diff --git a/packages/cli/src/ui/hooks/useStatusLine.test.ts b/packages/cli/src/ui/hooks/useStatusLine.test.ts index c2e3713dbc..08af6ce9c9 100644 --- a/packages/cli/src/ui/hooks/useStatusLine.test.ts +++ b/packages/cli/src/ui/hooks/useStatusLine.test.ts @@ -69,6 +69,10 @@ const mockConfig = { getTargetDir: vi.fn(() => '/test/dir'), getModel: vi.fn(() => 'test-model'), getModelDisplayName: vi.fn(() => 'Test Model'), + getModelsConfig: vi.fn(() => ({ + getModelDisplayName: (model: string) => + model === 'main-model' ? 'Main Model' : 'Test Model', + })), getCliVersion: vi.fn(() => '1.0.0'), getContentGeneratorConfig: vi.fn(getMockContentGeneratorConfig), }; @@ -191,6 +195,7 @@ describe('useStatusLine', () => { mockUIState.sessionStats.metrics.files.totalLinesRemoved = 0; mockVimMode.vimEnabled = false; mockVimMode.vimMode = 'INSERT'; + mockConfig.getModelDisplayName.mockReturnValue('Test Model'); mockConfig.getContentGeneratorConfig.mockReturnValue({ contextWindowSize: 131072, }); @@ -215,6 +220,27 @@ describe('useStatusLine', () => { ]); }); + it('falls back to config model display name when the preset current model is empty', () => { + mockUIState.currentModel = ''; + + const { result } = renderHook(() => useStatusLine()); + + expect(result.current.lines).toEqual([ + '\u279c dir \u00b7 git:(main) \u00b7 Test Model \u00b7 131.1k Context 0.1% used', + ]); + }); + + it('renders the session model when config is scoped to a fast subagent', () => { + mockUIState.currentModel = 'main-model'; + mockConfig.getModelDisplayName.mockReturnValue('Fast Model'); + + const { result } = renderHook(() => useStatusLine()); + + expect(result.current.lines).toEqual([ + '\u279c dir \u00b7 git:(main) \u00b7 Main Model \u00b7 131.1k Context 0.1% used', + ]); + }); + it('renders no lines when statusLine is explicitly null (opt-out)', () => { setStatusLineConfig(null); const { result } = renderHook(() => useStatusLine()); @@ -715,6 +741,17 @@ describe('useStatusLine', () => { const input = JSON.parse(stdinWrittenData); expect(input.model.display_name).toBe('Test Model'); }); + + it('sends the session model when config is scoped to a fast subagent', () => { + mockUIState.currentModel = 'main-model'; + mockConfig.getModelDisplayName.mockReturnValue('Fast Model'); + setStatusLineConfig({ type: 'command', command: 'cat' }); + + renderHook(() => useStatusLine()); + + const input = JSON.parse(stdinWrittenData); + expect(input.model.display_name).toBe('Main Model'); + }); }); // --- Stale generation handling --- diff --git a/packages/cli/src/ui/hooks/useStatusLine.ts b/packages/cli/src/ui/hooks/useStatusLine.ts index d928683b3b..338a489c1c 100644 --- a/packages/cli/src/ui/hooks/useStatusLine.ts +++ b/packages/cli/src/ui/hooks/useStatusLine.ts @@ -389,6 +389,24 @@ export function useStatusLine(): { const doUpdate = useCallback(() => { const preset = statusLinePresetRef.current; + const cmd = statusLineCommandRef.current; + if (!preset && !cmd) { + clearPullRequestLookup(); + setOutput([]); + return; + } + + const ui = uiStateRef.current; + const cfg = configRef.current; + const stats = ui.sessionStats; + const m = stats.metrics; + const contentGeneratorConfig = cfg.getContentGeneratorConfig(); + const contextWindowSize = contentGeneratorConfig?.contextWindowSize || 0; + const modelDisplayName = ui.currentModel + ? cfg.getModelsConfig().getModelDisplayName(ui.currentModel) + : cfg.getModelDisplayName(); + const { totalInputTokens, totalOutputTokens } = aggregateModelTokens(m); + if (preset) { if (activeChildRef.current) { activeChildRef.current.kill(); @@ -396,21 +414,13 @@ export function useStatusLine(): { generationRef.current++; } - const ui = uiStateRef.current; - const cfg = configRef.current; - const stats = ui.sessionStats; - const m = stats.metrics; const currentDir = cfg.getTargetDir(); ensurePullRequestNumber(preset, currentDir, ui.branchName); - const { totalInputTokens, totalOutputTokens } = aggregateModelTokens(m); - - const contentGeneratorConfig = cfg.getContentGeneratorConfig(); - const contextWindowSize = contentGeneratorConfig?.contextWindowSize || 0; const data = buildStatusLinePresetData({ sessionId: stats.sessionId, version: cfg.getCliVersion(), - modelDisplayName: cfg.getModelDisplayName(), + modelDisplayName, reasoning: contentGeneratorConfig?.reasoning, currentDir, branch: ui.branchName, @@ -429,19 +439,6 @@ export function useStatusLine(): { clearPullRequestLookup(); - const cmd = statusLineCommandRef.current; - if (!cmd) { - setOutput([]); - return; - } - - const ui = uiStateRef.current; - const cfg = configRef.current; - const stats = ui.sessionStats; - const m = stats.metrics; - - const contextWindowSize = - cfg.getContentGeneratorConfig()?.contextWindowSize || 0; const usedPercentage = contextWindowSize > 0 ? Math.min( @@ -455,13 +452,11 @@ export function useStatusLine(): { ) : 0; - const { totalInputTokens, totalOutputTokens } = aggregateModelTokens(m); - const input: StatusLineCommandInput = { session_id: stats.sessionId, version: cfg.getCliVersion() || 'unknown', model: { - display_name: cfg.getModelDisplayName(), + display_name: modelDisplayName, }, context_window: { context_window_size: contextWindowSize, @@ -511,7 +506,7 @@ export function useStatusLine(): { let child: ChildProcess; try { child = exec( - cmd, + cmd!, { cwd: cfg.getTargetDir(), timeout: 5000, maxBuffer: 1024 * 10 }, (error, stdout) => { if (gen !== generationRef.current) return; // stale