diff --git a/packages/cli/src/i18n/locales/de.js b/packages/cli/src/i18n/locales/de.js index 2208c8d8b..4ee340c17 100644 --- a/packages/cli/src/i18n/locales/de.js +++ b/packages/cli/src/i18n/locales/de.js @@ -1971,6 +1971,6 @@ export default { verbose: 'ausführlich', 'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).': 'Vollständige Tool-Ausgabe und Denkprozess im ausführlichen Modus anzeigen (mit Strg+O umschalten).', - 'Press Ctrl+O to show full tool output': - 'Strg+O für vollständige Tool-Ausgabe drücken', + 'Press Ctrl+O to toggle verbose mode': + 'Strg+O zum Umschalten des ausführlichen Modus drücken', }; diff --git a/packages/cli/src/i18n/locales/en.js b/packages/cli/src/i18n/locales/en.js index 4e7c2409a..85e774265 100644 --- a/packages/cli/src/i18n/locales/en.js +++ b/packages/cli/src/i18n/locales/en.js @@ -2011,6 +2011,5 @@ export default { verbose: 'verbose', 'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).': 'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).', - 'Press Ctrl+O to show full tool output': - 'Press Ctrl+O to show full tool output', + 'Press Ctrl+O to toggle verbose mode': 'Press Ctrl+O to toggle verbose mode', }; diff --git a/packages/cli/src/i18n/locales/ja.js b/packages/cli/src/i18n/locales/ja.js index 3781b39e0..7cd46c92a 100644 --- a/packages/cli/src/i18n/locales/ja.js +++ b/packages/cli/src/i18n/locales/ja.js @@ -1463,5 +1463,5 @@ export default { verbose: '詳細', 'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).': '詳細モードで完全なツール出力と思考を表示します(Ctrl+O で切り替え)。', - 'Press Ctrl+O to show full tool output': 'Ctrl+O で完全なツール出力を表示', + 'Press Ctrl+O to toggle verbose mode': 'Ctrl+O で詳細モードを切り替え', }; diff --git a/packages/cli/src/i18n/locales/pt.js b/packages/cli/src/i18n/locales/pt.js index 5d1901f2d..a9b28e3c1 100644 --- a/packages/cli/src/i18n/locales/pt.js +++ b/packages/cli/src/i18n/locales/pt.js @@ -1961,6 +1961,6 @@ export default { verbose: 'detalhado', 'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).': 'Mostrar saída completa da ferramenta e raciocínio no modo detalhado (alternar com Ctrl+O).', - 'Press Ctrl+O to show full tool output': - 'Pressione Ctrl+O para exibir a saída completa da ferramenta', + 'Press Ctrl+O to toggle verbose mode': + 'Pressione Ctrl+O para alternar o modo detalhado', }; diff --git a/packages/cli/src/i18n/locales/ru.js b/packages/cli/src/i18n/locales/ru.js index 9ea41ec03..fb76e60f1 100644 --- a/packages/cli/src/i18n/locales/ru.js +++ b/packages/cli/src/i18n/locales/ru.js @@ -1968,6 +1968,6 @@ export default { verbose: 'подробный', 'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).': 'Показывать полный вывод инструментов и процесс рассуждений в подробном режиме (переключить с помощью Ctrl+O).', - 'Press Ctrl+O to show full tool output': - 'Нажмите Ctrl+O для показа полного вывода инструментов', + 'Press Ctrl+O to toggle verbose mode': + 'Нажмите Ctrl+O для переключения подробного режима', }; diff --git a/packages/cli/src/i18n/locales/zh.js b/packages/cli/src/i18n/locales/zh.js index c23242437..884ca321e 100644 --- a/packages/cli/src/i18n/locales/zh.js +++ b/packages/cli/src/i18n/locales/zh.js @@ -1816,5 +1816,5 @@ export default { verbose: '详细', 'Show full tool output and thinking in verbose mode (toggle with Ctrl+O).': '详细模式下显示完整工具输出和思考过程(Ctrl+O 切换)。', - 'Press Ctrl+O to show full tool output': '按 Ctrl+O 查看详细工具调用结果', + 'Press Ctrl+O to toggle verbose mode': '按 Ctrl+O 切换详细模式', }; diff --git a/packages/cli/src/ui/AppContainer.test.tsx b/packages/cli/src/ui/AppContainer.test.tsx index 07397989a..026f52977 100644 --- a/packages/cli/src/ui/AppContainer.test.tsx +++ b/packages/cli/src/ui/AppContainer.test.tsx @@ -243,6 +243,7 @@ describe('AppContainer State Management', () => { addMessage: vi.fn(), clearQueue: vi.fn(), getQueuedMessagesText: vi.fn().mockReturnValue(''), + drainQueue: vi.fn().mockReturnValue([]), }); mockedUseAutoAcceptIndicator.mockReturnValue(false); mockedUseGitBranchName.mockReturnValue('main'); @@ -455,6 +456,7 @@ describe('AppContainer State Management', () => { addMessage: mockQueueMessage, clearQueue: vi.fn(), getQueuedMessagesText: vi.fn().mockReturnValue(''), + drainQueue: vi.fn().mockReturnValue([]), }); render( diff --git a/packages/cli/src/ui/AppContainer.tsx b/packages/cli/src/ui/AppContainer.tsx index 29ea1c756..176d2eacb 100644 --- a/packages/cli/src/ui/AppContainer.tsx +++ b/packages/cli/src/ui/AppContainer.tsx @@ -776,24 +776,22 @@ export const AppContainer = (props: AppContainerProps) => { disabled: agentViewState.activeView !== 'main', }); - const { messageQueue, addMessage, clearQueue, getQueuedMessagesText } = - useMessageQueue({ - isConfigInitialized, - streamingState, - submitQuery, - }); + const { + messageQueue, + addMessage, + clearQueue, + getQueuedMessagesText, + drainQueue, + } = useMessageQueue({ + isConfigInitialized, + streamingState, + submitQuery, + }); // Bridge message queue to mid-turn drain via ref. - // Sync ref on every render so the drain callback always reads latest state. - const messageQueueRef = useRef(messageQueue); - messageQueueRef.current = messageQueue; - midTurnDrainRef.current = () => { - const queue = messageQueueRef.current; - if (queue.length === 0) return []; - messageQueueRef.current = []; - clearQueue(); - return [...queue]; - }; + // drainQueue reads from the synchronous queueRef inside useMessageQueue, + // so it always sees the latest state even between renders. + midTurnDrainRef.current = drainQueue; // Callback for handling final submit (must be after addMessage from useMessageQueue) const handleFinalSubmit = useCallback( diff --git a/packages/cli/src/ui/components/messages/CompactToolGroupDisplay.tsx b/packages/cli/src/ui/components/messages/CompactToolGroupDisplay.tsx index cf870f0be..62d580434 100644 --- a/packages/cli/src/ui/components/messages/CompactToolGroupDisplay.tsx +++ b/packages/cli/src/ui/components/messages/CompactToolGroupDisplay.tsx @@ -101,7 +101,7 @@ export const CompactToolGroupDisplay: React.FC< {/* Hint line */} - {t('Press Ctrl+O to show full tool output')} + {t('Press Ctrl+O to toggle verbose mode')} ); diff --git a/packages/cli/src/ui/hooks/useStatusLine.ts b/packages/cli/src/ui/hooks/useStatusLine.ts index 1f5ede315..06a4d038d 100644 --- a/packages/cli/src/ui/hooks/useStatusLine.ts +++ b/packages/cli/src/ui/hooks/useStatusLine.ts @@ -175,6 +175,8 @@ export function useStatusLine(): { const { currentModel, branchName } = uiState; const totalToolCalls = uiState.sessionStats.metrics.tools.totalCalls; const totalLinesAdded = uiState.sessionStats.metrics.files.totalLinesAdded; + const totalLinesRemoved = + uiState.sessionStats.metrics.files.totalLinesRemoved; const effectiveVim = vimEnabled ? vimMode : undefined; const prevStateRef = useRef<{ promptTokenCount: number; @@ -183,6 +185,7 @@ export function useStatusLine(): { branchName: string | undefined; totalToolCalls: number; totalLinesAdded: number; + totalLinesRemoved: number; }>({ promptTokenCount: lastPromptTokenCount, currentModel, @@ -190,6 +193,7 @@ export function useStatusLine(): { branchName, totalToolCalls, totalLinesAdded, + totalLinesRemoved, }); // Guard: when true, the mount effect has already called doUpdate so the @@ -216,8 +220,15 @@ export function useStatusLine(): { cfg.getContentGeneratorConfig()?.contextWindowSize || 0; const usedPercentage = contextWindowSize > 0 - ? Math.round((stats.lastPromptTokenCount / contextWindowSize) * 1000) / - 10 + ? Math.min( + 100, + Math.max( + 0, + Math.round( + (stats.lastPromptTokenCount / contextWindowSize) * 1000, + ) / 10, + ), + ) : 0; let totalInputTokens = 0; @@ -238,9 +249,15 @@ export function useStatusLine(): { used_percentage: usedPercentage, remaining_percentage: contextWindowSize > 0 - ? Math.round( - (1 - stats.lastPromptTokenCount / contextWindowSize) * 1000, - ) / 10 + ? Math.min( + 100, + Math.max( + 0, + Math.round( + (1 - stats.lastPromptTokenCount / contextWindowSize) * 1000, + ) / 10, + ), + ) : 100, current_usage: stats.lastPromptTokenCount, total_input_tokens: totalInputTokens, @@ -256,7 +273,7 @@ export function useStatusLine(): { }), metrics: buildMetricsPayload(m), ...(vimEnabledRef.current && { - vim: { mode: vimModeRef.current ?? 'INSERT' }, + vim: { mode: vimModeRef.current }, }), }; @@ -328,7 +345,8 @@ export function useStatusLine(): { effectiveVim !== prev.effectiveVim || branchName !== prev.branchName || totalToolCalls !== prev.totalToolCalls || - totalLinesAdded !== prev.totalLinesAdded + totalLinesAdded !== prev.totalLinesAdded || + totalLinesRemoved !== prev.totalLinesRemoved ) { prev.promptTokenCount = lastPromptTokenCount; prev.currentModel = currentModel; @@ -336,6 +354,7 @@ export function useStatusLine(): { prev.branchName = branchName; prev.totalToolCalls = totalToolCalls; prev.totalLinesAdded = totalLinesAdded; + prev.totalLinesRemoved = totalLinesRemoved; scheduleUpdate(); } }, [ @@ -346,6 +365,7 @@ export function useStatusLine(): { branchName, totalToolCalls, totalLinesAdded, + totalLinesRemoved, scheduleUpdate, ]); @@ -379,6 +399,7 @@ export function useStatusLine(): { genRef.current++; if (debounceRef.current !== undefined) { clearTimeout(debounceRef.current); + debounceRef.current = undefined; } }; // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/packages/core/src/subagents/builtin-agents.ts b/packages/core/src/subagents/builtin-agents.ts index a97ca24eb..747ffd509 100644 --- a/packages/core/src/subagents/builtin-agents.ts +++ b/packages/core/src/subagents/builtin-agents.ts @@ -104,7 +104,12 @@ Notes: name: 'statusline-setup', description: "Use this agent to configure the user's Qwen Code status line setting.", - tools: [ToolNames.READ_FILE, ToolNames.EDIT, ToolNames.ASK_USER_QUESTION], + tools: [ + ToolNames.READ_FILE, + ToolNames.WRITE_FILE, + ToolNames.EDIT, + ToolNames.ASK_USER_QUESTION, + ], color: 'orange', systemPrompt: `You are a status line setup agent for Qwen Code. Your job is to create or update the statusLine command in the user's Qwen Code settings. @@ -115,7 +120,12 @@ When asked to convert the user's shell PS1 configuration, follow these steps: - ~/.bash_profile - ~/.profile -2. Extract the PS1 value using this regex pattern: /(?:^|\\n)\\s*(?:export\\s+)?PS1\\s*=\\s*["']([^"']+)["']/m +2. Look for PS1 assignments. PS1 may be quoted or unquoted, e.g.: + - PS1="\\u@\\h:\\w\\$ " + - PS1='\\u@\\h:\\w\\$ ' + - PS1=\\u@\\h:\\w\\$ + - export PS1="..." + If there are multiple PS1 assignments, use the last one (it takes effect). 3. Convert PS1 escape sequences to shell commands: - \\u → $(whoami) @@ -130,8 +140,10 @@ When asked to convert the user's shell PS1 configuration, follow these steps: - \\@ → $(date +%I:%M%p) - \\# → # - \\! → ! + - \\[ and \\] → (remove — these are readline non-printing markers, not needed in the status line) + - \\e or \\033 → (ANSI escape — strip the entire color sequence including \\e[...m) -4. When using ANSI color codes, be sure to use \`printf\`. Do not remove colors. Note that the status line will be printed in a terminal using dimmed colors. +4. Strip ANSI color/escape sequences from the PS1 output. The status line already renders in dimmed color, so PS1 colors are not useful and can produce garbled output. 5. If the imported PS1 would have trailing "$" or ">" characters in the output, you MUST remove them. @@ -198,8 +210,6 @@ How to use the statusLine command: } Make sure to preserve any existing "ui" settings (theme, etc.) when updating. -4. If ~/.qwen/settings.json is a symlink, update the target file instead. - Guidelines: - The status line only displays the first line of stdout — ensure commands produce exactly one line of output - Preserve existing settings when updating