merge main branch functionalities

This commit is contained in:
koalazf.99
2025-08-02 14:47:37 +08:00
parent b69b2ce376
commit 23f6ae8513
30 changed files with 1378 additions and 420 deletions

View File

@@ -55,6 +55,7 @@ describe('clearCommand', () => {
expect(mockContext.ui.setDebugMessage).toHaveBeenCalledTimes(1);
expect(mockResetChat).toHaveBeenCalledTimes(1);
expect(mockContext.session.resetSession).toHaveBeenCalledTimes(1);
expect(uiTelemetryService.resetLastPromptTokenCount).toHaveBeenCalledTimes(
1,
);
@@ -64,6 +65,8 @@ describe('clearCommand', () => {
const setDebugMessageOrder = (mockContext.ui.setDebugMessage as Mock).mock
.invocationCallOrder[0];
const resetChatOrder = mockResetChat.mock.invocationCallOrder[0];
const resetSessionOrder = (mockContext.session.resetSession as Mock).mock
.invocationCallOrder[0];
const resetTelemetryOrder = (
uiTelemetryService.resetLastPromptTokenCount as Mock
).mock.invocationCallOrder[0];
@@ -73,6 +76,8 @@ describe('clearCommand', () => {
expect(setDebugMessageOrder).toBeLessThan(resetChatOrder);
expect(resetChatOrder).toBeLessThan(resetTelemetryOrder);
expect(resetTelemetryOrder).toBeLessThan(clearOrder);
expect(resetChatOrder).toBeLessThan(resetSessionOrder);
expect(resetSessionOrder).toBeLessThan(clearOrder);
});
it('should not attempt to reset chat if config service is not available', async () => {
@@ -92,6 +97,7 @@ describe('clearCommand', () => {
'Clearing terminal.',
);
expect(mockResetChat).not.toHaveBeenCalled();
expect(nullConfigContext.session.resetSession).toHaveBeenCalledTimes(1);
expect(uiTelemetryService.resetLastPromptTokenCount).toHaveBeenCalledTimes(
1,
);

View File

@@ -24,6 +24,7 @@ export const clearCommand: SlashCommand = {
}
uiTelemetryService.resetLastPromptTokenCount();
context.session.resetSession();
context.ui.clear();
},
};

View File

@@ -15,10 +15,10 @@ import { MessageType } from '../types.js';
export const docsCommand: SlashCommand = {
name: 'docs',
description: 'open full Gemini CLI documentation in your browser',
description: 'open full Qwen Code documentation in your browser',
kind: CommandKind.BUILT_IN,
action: async (context: CommandContext): Promise<void> => {
const docsUrl = 'https://goo.gle/gemini-cli-docs';
const docsUrl = 'https://github.com/QwenLM/qwen-code';
if (process.env.SANDBOX && process.env.SANDBOX !== 'sandbox-exec') {
context.ui.addItem(

View File

@@ -13,7 +13,7 @@ import { MessageType } from '../types.js';
export const toolsCommand: SlashCommand = {
name: 'tools',
description: 'list available Gemini CLI tools',
description: 'list available Qwen Codetools',
kind: CommandKind.BUILT_IN,
action: async (context: CommandContext, args?: string): Promise<void> => {
const subCommand = args?.trim();
@@ -40,7 +40,7 @@ export const toolsCommand: SlashCommand = {
// Filter out MCP tools by checking for the absence of a serverName property
const geminiTools = tools.filter((tool) => !('serverName' in tool));
let message = 'Available Gemini CLI tools:\n\n';
let message = 'Available Qwen Code tools:\n\n';
if (geminiTools.length > 0) {
geminiTools.forEach((tool) => {

View File

@@ -63,6 +63,7 @@ export interface CommandContext {
// Session-specific data
session: {
stats: SessionStatsState;
resetSession: () => void;
/** A transient list of shell commands the user has approved for this session. */
sessionShellAllowlist: Set<string>;
};

View File

@@ -50,6 +50,7 @@ interface SessionStatsContextValue {
stats: SessionStatsState;
startNewPrompt: () => void;
getPromptCount: () => number;
resetSession: () => void;
}
// --- Context Definition ---
@@ -109,13 +110,23 @@ export const SessionStatsProvider: React.FC<{ children: React.ReactNode }> = ({
[stats.promptCount],
);
const resetSession = useCallback(() => {
setStats({
sessionStartTime: new Date(),
metrics: uiTelemetryService.getMetrics(),
lastPromptTokenCount: uiTelemetryService.getLastPromptTokenCount(),
promptCount: 0,
});
}, []);
const value = useMemo(
() => ({
stats,
startNewPrompt,
getPromptCount,
resetSession,
}),
[stats, startNewPrompt, getPromptCount],
[stats, startNewPrompt, getPromptCount, resetSession],
);
return (

View File

@@ -162,6 +162,7 @@ export const useSlashCommandProcessor = (
session: {
stats: session.stats,
sessionShellAllowlist,
resetSession: session.resetSession,
},
}),
[
@@ -174,6 +175,7 @@ export const useSlashCommandProcessor = (
clearItems,
refreshStatic,
session.stats,
session.resetSession,
onDebugMessage,
pendingCompressionItemRef,
setPendingCompressionItem,

View File

@@ -511,6 +511,23 @@ export const useGeminiStream = (
[addItem, config],
);
const handleSessionTokenLimitExceededEvent = useCallback(
(value: { currentTokens: number; limit: number; message: string }) =>
addItem(
{
type: 'error',
text:
`🚫 Session token limit exceeded: ${value.currentTokens.toLocaleString()} tokens > ${value.limit.toLocaleString()} limit.\n\n` +
`💡 Solutions:\n` +
` • Start a new session: Use /clear command\n` +
` • Increase limit: Add "sessionTokenLimit": (e.g., 128000) to your settings.json\n` +
` • Compress history: Use /compress command to compress history`,
},
Date.now(),
),
[addItem],
);
const handleLoopDetectedEvent = useCallback(() => {
addItem(
{
@@ -560,6 +577,9 @@ export const useGeminiStream = (
case ServerGeminiEventType.MaxSessionTurns:
handleMaxSessionTurnsEvent();
break;
case ServerGeminiEventType.SessionTokenLimitExceeded:
handleSessionTokenLimitExceededEvent(event.value);
break;
case ServerGeminiEventType.Finished:
handleFinishedEvent(
event as ServerGeminiFinishedEvent,
@@ -591,6 +611,7 @@ export const useGeminiStream = (
handleChatCompressionEvent,
handleFinishedEvent,
handleMaxSessionTurnsEvent,
handleSessionTokenLimitExceededEvent,
],
);

View File

@@ -34,7 +34,7 @@ export async function checkForUpdates(): Promise<string | null> {
notifier.update &&
semver.gt(notifier.update.latest, notifier.update.current)
) {
return `Gemini CLI update available! ${notifier.update.current}${notifier.update.latest}\nRun npm install -g ${packageJson.name} to update`;
return `Qwen Code update available! ${notifier.update.current}${notifier.update.latest}\nRun npm install -g ${packageJson.name} to update`;
}
return null;