diff --git a/packages/cli/src/config/settingsSchema.ts b/packages/cli/src/config/settingsSchema.ts index a596e793..439bc5d9 100644 --- a/packages/cli/src/config/settingsSchema.ts +++ b/packages/cli/src/config/settingsSchema.ts @@ -194,6 +194,16 @@ const SETTINGS_SCHEMA = { { value: 'ru', label: 'Русский (Russian)' }, ], }, + terminalBell: { + type: 'boolean', + label: 'Terminal Bell', + category: 'General', + requiresRestart: false, + default: true, + description: + 'Play terminal bell sound when response completes or needs approval.', + showInDialog: true, + }, }, }, output: { diff --git a/packages/cli/src/i18n/locales/en.js b/packages/cli/src/i18n/locales/en.js index c2217757..dec11869 100644 --- a/packages/cli/src/i18n/locales/en.js +++ b/packages/cli/src/i18n/locales/en.js @@ -867,6 +867,7 @@ export default { // Exit Screen / Stats // ============================================================================ 'Agent powering down. Goodbye!': 'Agent powering down. Goodbye!', + 'To continue this session, run': 'To continue this session, run', 'Interaction Summary': 'Interaction Summary', 'Session ID:': 'Session ID:', 'Tool Calls:': 'Tool Calls:', diff --git a/packages/cli/src/i18n/locales/zh.js b/packages/cli/src/i18n/locales/zh.js index adeb85f1..dc00d068 100644 --- a/packages/cli/src/i18n/locales/zh.js +++ b/packages/cli/src/i18n/locales/zh.js @@ -820,6 +820,7 @@ export default { // Exit Screen / Stats // ============================================================================ 'Agent powering down. Goodbye!': 'Qwen Code 正在关闭,再见!', + 'To continue this session, run': '要继续此会话,请运行', 'Interaction Summary': '交互摘要', 'Session ID:': '会话 ID:', 'Tool Calls:': '工具调用:', diff --git a/packages/cli/src/services/BuiltinCommandLoader.test.ts b/packages/cli/src/services/BuiltinCommandLoader.test.ts index 9d649b2f..7c8e6fc5 100644 --- a/packages/cli/src/services/BuiltinCommandLoader.test.ts +++ b/packages/cli/src/services/BuiltinCommandLoader.test.ts @@ -58,7 +58,6 @@ vi.mock('../ui/commands/authCommand.js', () => ({ authCommand: {} })); vi.mock('../ui/commands/bugCommand.js', () => ({ bugCommand: {} })); vi.mock('../ui/commands/clearCommand.js', () => ({ clearCommand: {} })); vi.mock('../ui/commands/compressCommand.js', () => ({ compressCommand: {} })); -vi.mock('../ui/commands/corgiCommand.js', () => ({ corgiCommand: {} })); vi.mock('../ui/commands/docsCommand.js', () => ({ docsCommand: {} })); vi.mock('../ui/commands/editorCommand.js', () => ({ editorCommand: {} })); vi.mock('../ui/commands/extensionsCommand.js', () => ({ diff --git a/packages/cli/src/services/BuiltinCommandLoader.ts b/packages/cli/src/services/BuiltinCommandLoader.ts index 100fbef9..d3877a8a 100644 --- a/packages/cli/src/services/BuiltinCommandLoader.ts +++ b/packages/cli/src/services/BuiltinCommandLoader.ts @@ -15,7 +15,6 @@ import { bugCommand } from '../ui/commands/bugCommand.js'; import { clearCommand } from '../ui/commands/clearCommand.js'; import { compressCommand } from '../ui/commands/compressCommand.js'; import { copyCommand } from '../ui/commands/copyCommand.js'; -import { corgiCommand } from '../ui/commands/corgiCommand.js'; import { docsCommand } from '../ui/commands/docsCommand.js'; import { directoryCommand } from '../ui/commands/directoryCommand.js'; import { editorCommand } from '../ui/commands/editorCommand.js'; @@ -63,7 +62,6 @@ export class BuiltinCommandLoader implements ICommandLoader { clearCommand, compressCommand, copyCommand, - corgiCommand, docsCommand, directoryCommand, editorCommand, diff --git a/packages/cli/src/test-utils/mockCommandContext.ts b/packages/cli/src/test-utils/mockCommandContext.ts index eced7df1..fd825b9d 100644 --- a/packages/cli/src/test-utils/mockCommandContext.ts +++ b/packages/cli/src/test-utils/mockCommandContext.ts @@ -56,7 +56,6 @@ export const createMockCommandContext = ( pendingItem: null, setPendingItem: vi.fn(), loadHistory: vi.fn(), - toggleCorgiMode: vi.fn(), toggleVimEnabled: vi.fn(), extensionsUpdateState: new Map(), setExtensionsUpdateState: vi.fn(), diff --git a/packages/cli/src/ui/AppContainer.tsx b/packages/cli/src/ui/AppContainer.tsx index edda3d4d..ff16c53d 100644 --- a/packages/cli/src/ui/AppContainer.tsx +++ b/packages/cli/src/ui/AppContainer.tsx @@ -136,7 +136,6 @@ export const AppContainer = (props: AppContainerProps) => { const { settings, config, initializationResult } = props; const historyManager = useHistory(); useMemoryMonitor(historyManager); - const [corgiMode, setCorgiMode] = useState(false); const [debugMessage, setDebugMessage] = useState(''); const [quittingMessages, setQuittingMessages] = useState< HistoryItem[] | null @@ -485,7 +484,6 @@ export const AppContainer = (props: AppContainerProps) => { }, 100); }, setDebugMessage, - toggleCorgiMode: () => setCorgiMode((prev) => !prev), dispatchExtensionStateUpdate, addConfirmUpdateExtensionRequest, openSubagentCreateDialog, @@ -498,7 +496,6 @@ export const AppContainer = (props: AppContainerProps) => { openSettingsDialog, openModelDialog, setDebugMessage, - setCorgiMode, dispatchExtensionStateUpdate, openPermissionsDialog, openApprovalModeDialog, @@ -945,6 +942,7 @@ export const AppContainer = (props: AppContainerProps) => { isFocused, streamingState, elapsedTime, + settings, }); // Dialog close functionality @@ -1218,7 +1216,6 @@ export const AppContainer = (props: AppContainerProps) => { qwenAuthState, editorError, isEditorDialogOpen, - corgiMode, debugMessage, quittingMessages, isSettingsDialogOpen, @@ -1309,7 +1306,6 @@ export const AppContainer = (props: AppContainerProps) => { qwenAuthState, editorError, isEditorDialogOpen, - corgiMode, debugMessage, quittingMessages, isSettingsDialogOpen, diff --git a/packages/cli/src/ui/commands/corgiCommand.test.ts b/packages/cli/src/ui/commands/corgiCommand.test.ts deleted file mode 100644 index 3c25e8cd..00000000 --- a/packages/cli/src/ui/commands/corgiCommand.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @license - * Copyright 2025 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { corgiCommand } from './corgiCommand.js'; -import { type CommandContext } from './types.js'; -import { createMockCommandContext } from '../../test-utils/mockCommandContext.js'; - -describe('corgiCommand', () => { - let mockContext: CommandContext; - - beforeEach(() => { - mockContext = createMockCommandContext(); - vi.spyOn(mockContext.ui, 'toggleCorgiMode'); - }); - - it('should call the toggleCorgiMode function on the UI context', async () => { - if (!corgiCommand.action) { - throw new Error('The corgi command must have an action.'); - } - - await corgiCommand.action(mockContext, ''); - - expect(mockContext.ui.toggleCorgiMode).toHaveBeenCalledTimes(1); - }); - - it('should have the correct name and description', () => { - expect(corgiCommand.name).toBe('corgi'); - expect(corgiCommand.description).toBe('Toggles corgi mode.'); - }); -}); diff --git a/packages/cli/src/ui/commands/corgiCommand.ts b/packages/cli/src/ui/commands/corgiCommand.ts deleted file mode 100644 index 2da6ad3e..00000000 --- a/packages/cli/src/ui/commands/corgiCommand.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @license - * Copyright 2025 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import { CommandKind, type SlashCommand } from './types.js'; - -export const corgiCommand: SlashCommand = { - name: 'corgi', - description: 'Toggles corgi mode.', - hidden: true, - kind: CommandKind.BUILT_IN, - action: (context, _args) => { - context.ui.toggleCorgiMode(); - }, -}; diff --git a/packages/cli/src/ui/commands/types.ts b/packages/cli/src/ui/commands/types.ts index a2a352cb..f2ec2173 100644 --- a/packages/cli/src/ui/commands/types.ts +++ b/packages/cli/src/ui/commands/types.ts @@ -64,8 +64,6 @@ export interface CommandContext { * @param history The array of history items to load. */ loadHistory: UseHistoryManagerReturn['loadHistory']; - /** Toggles a special display mode. */ - toggleCorgiMode: () => void; toggleVimEnabled: () => Promise; setGeminiMdFileCount: (count: number) => void; reloadCommands: () => void; diff --git a/packages/cli/src/ui/components/Composer.test.tsx b/packages/cli/src/ui/components/Composer.test.tsx index 084cd746..d660d704 100644 --- a/packages/cli/src/ui/components/Composer.test.tsx +++ b/packages/cli/src/ui/components/Composer.test.tsx @@ -120,7 +120,6 @@ const createMockUIState = (overrides: Partial = {}): UIState => }, branchName: 'main', debugMessage: '', - corgiMode: false, errorCount: 0, nightly: false, isTrustedFolder: true, @@ -183,6 +182,7 @@ describe('Composer', () => { const { lastFrame } = renderComposer(uiState, settings); + // Smoke check that the Footer renders when enabled. expect(lastFrame()).toContain('Footer'); }); @@ -200,7 +200,6 @@ describe('Composer', () => { it('passes correct props to Footer including vim mode when enabled', async () => { const uiState = createMockUIState({ branchName: 'feature-branch', - corgiMode: true, errorCount: 2, sessionStats: { sessionId: 'test-session', diff --git a/packages/cli/src/ui/components/Footer.tsx b/packages/cli/src/ui/components/Footer.tsx index 776817a6..71f278df 100644 --- a/packages/cli/src/ui/components/Footer.tsx +++ b/packages/cli/src/ui/components/Footer.tsx @@ -33,7 +33,6 @@ export const Footer: React.FC = () => { debugMode, branchName, debugMessage, - corgiMode, errorCount, showErrorDetails, promptTokenCount, @@ -45,7 +44,6 @@ export const Footer: React.FC = () => { debugMode: config.getDebugMode(), branchName: uiState.branchName, debugMessage: uiState.debugMessage, - corgiMode: uiState.corgiMode, errorCount: uiState.errorCount, showErrorDetails: uiState.showErrorDetails, promptTokenCount: uiState.sessionStats.lastPromptTokenCount, @@ -153,16 +151,6 @@ export const Footer: React.FC = () => { {showMemoryUsage && } - {corgiMode && ( - - | - - - - `) - - - )} {!showErrorDetails && errorCount > 0 && ( | diff --git a/packages/cli/src/ui/components/SessionSummaryDisplay.test.tsx b/packages/cli/src/ui/components/SessionSummaryDisplay.test.tsx index 766e851a..19aa3af8 100644 --- a/packages/cli/src/ui/components/SessionSummaryDisplay.test.tsx +++ b/packages/cli/src/ui/components/SessionSummaryDisplay.test.tsx @@ -20,16 +20,21 @@ vi.mock('../contexts/SessionContext.js', async (importOriginal) => { const useSessionStatsMock = vi.mocked(SessionContext.useSessionStats); -const renderWithMockedStats = (metrics: SessionMetrics) => { +const renderWithMockedStats = ( + metrics: SessionMetrics, + sessionId: string = 'test-session-id-12345', + promptCount: number = 5, +) => { useSessionStatsMock.mockReturnValue({ stats: { + sessionId, sessionStartTime: new Date(), metrics, lastPromptTokenCount: 0, - promptCount: 5, + promptCount, }, - getPromptCount: () => 5, + getPromptCount: () => promptCount, startNewPrompt: vi.fn(), }); @@ -70,6 +75,38 @@ describe('', () => { const output = lastFrame(); expect(output).toContain('Agent powering down. Goodbye!'); + expect(output).toContain('To continue this session, run'); + expect(output).toContain('qwen --resume test-session-id-12345'); expect(output).toMatchSnapshot(); }); + + it('does not show resume message when there are no messages', () => { + const metrics: SessionMetrics = { + models: {}, + tools: { + totalCalls: 0, + totalSuccess: 0, + totalFail: 0, + totalDurationMs: 0, + totalDecisions: { accept: 0, reject: 0, modify: 0 }, + byName: {}, + }, + files: { + totalLinesAdded: 0, + totalLinesRemoved: 0, + }, + }; + + // Pass promptCount = 0 to simulate no messages + const { lastFrame } = renderWithMockedStats( + metrics, + 'test-session-id-12345', + 0, + ); + const output = lastFrame(); + + expect(output).toContain('Agent powering down. Goodbye!'); + expect(output).not.toContain('To continue this session, run'); + expect(output).not.toContain('qwen --resume'); + }); }); diff --git a/packages/cli/src/ui/components/SessionSummaryDisplay.tsx b/packages/cli/src/ui/components/SessionSummaryDisplay.tsx index c8d79e0e..c38edc75 100644 --- a/packages/cli/src/ui/components/SessionSummaryDisplay.tsx +++ b/packages/cli/src/ui/components/SessionSummaryDisplay.tsx @@ -5,7 +5,10 @@ */ import type React from 'react'; +import { Box, Text } from 'ink'; import { StatsDisplay } from './StatsDisplay.js'; +import { useSessionStats } from '../contexts/SessionContext.js'; +import { theme } from '../semantic-colors.js'; import { t } from '../../i18n/index.js'; interface SessionSummaryDisplayProps { @@ -14,9 +17,28 @@ interface SessionSummaryDisplayProps { export const SessionSummaryDisplay: React.FC = ({ duration, -}) => ( - -); +}) => { + const { stats } = useSessionStats(); + + // Only show the resume message if there were messages in the session + const hasMessages = stats.promptCount > 0; + + return ( + <> + + {hasMessages && ( + + + {t('To continue this session, run')}{' '} + + qwen --resume {stats.sessionId} + + + + )} + + ); +}; diff --git a/packages/cli/src/ui/components/__snapshots__/SessionSummaryDisplay.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/SessionSummaryDisplay.test.tsx.snap index 7c925f72..dfa39ba8 100644 --- a/packages/cli/src/ui/components/__snapshots__/SessionSummaryDisplay.test.tsx.snap +++ b/packages/cli/src/ui/components/__snapshots__/SessionSummaryDisplay.test.tsx.snap @@ -6,7 +6,7 @@ exports[` > renders the summary display with a title 1` │ Agent powering down. Goodbye! │ │ │ │ Interaction Summary │ -│ Session ID: │ +│ Session ID: test-session-id-12345 │ │ Tool Calls: 0 ( ✓ 0 x 0 ) │ │ Success Rate: 0.0% │ │ Code Changes: +42 -15 │ @@ -26,5 +26,7 @@ exports[` > renders the summary display with a title 1` │ │ │ » Tip: For a full token breakdown, run \`/stats model\`. │ │ │ -╰──────────────────────────────────────────────────────────────────────────────────────────────────╯" +╰──────────────────────────────────────────────────────────────────────────────────────────────────╯ + +To continue this session, run qwen --resume test-session-id-12345" `; diff --git a/packages/cli/src/ui/contexts/UIStateContext.tsx b/packages/cli/src/ui/contexts/UIStateContext.tsx index ac2f5f10..62e54204 100644 --- a/packages/cli/src/ui/contexts/UIStateContext.tsx +++ b/packages/cli/src/ui/contexts/UIStateContext.tsx @@ -54,7 +54,6 @@ export interface UIState { qwenAuthState: QwenAuthState; editorError: string | null; isEditorDialogOpen: boolean; - corgiMode: boolean; debugMessage: string; quittingMessages: HistoryItem[] | null; isSettingsDialogOpen: boolean; diff --git a/packages/cli/src/ui/hooks/slashCommandProcessor.test.ts b/packages/cli/src/ui/hooks/slashCommandProcessor.test.ts index 55fec0c3..42ce4099 100644 --- a/packages/cli/src/ui/hooks/slashCommandProcessor.test.ts +++ b/packages/cli/src/ui/hooks/slashCommandProcessor.test.ts @@ -153,7 +153,6 @@ describe('useSlashCommandProcessor', () => { openModelDialog: mockOpenModelDialog, quit: mockSetQuittingMessages, setDebugMessage: vi.fn(), - toggleCorgiMode: vi.fn(), }, ), ); @@ -909,7 +908,6 @@ describe('useSlashCommandProcessor', () => { vi.fn(), // openThemeDialog mockOpenAuthDialog, vi.fn(), // openEditorDialog - vi.fn(), // toggleCorgiMode mockSetQuittingMessages, vi.fn(), // openSettingsDialog vi.fn(), // openModelSelectionDialog diff --git a/packages/cli/src/ui/hooks/slashCommandProcessor.ts b/packages/cli/src/ui/hooks/slashCommandProcessor.ts index 553accb7..6439c934 100644 --- a/packages/cli/src/ui/hooks/slashCommandProcessor.ts +++ b/packages/cli/src/ui/hooks/slashCommandProcessor.ts @@ -68,7 +68,6 @@ interface SlashCommandProcessorActions { openApprovalModeDialog: () => void; quit: (messages: HistoryItem[]) => void; setDebugMessage: (message: string) => void; - toggleCorgiMode: () => void; dispatchExtensionStateUpdate: (action: ExtensionUpdateAction) => void; addConfirmUpdateExtensionRequest: (request: ConfirmationRequest) => void; openSubagentCreateDialog: () => void; @@ -206,7 +205,6 @@ export const useSlashCommandProcessor = ( setDebugMessage: actions.setDebugMessage, pendingItem, setPendingItem, - toggleCorgiMode: actions.toggleCorgiMode, toggleVimEnabled, setGeminiMdFileCount, reloadCommands, diff --git a/packages/cli/src/ui/hooks/useAttentionNotifications.test.ts b/packages/cli/src/ui/hooks/useAttentionNotifications.test.ts index 1475aa52..e8beb86f 100644 --- a/packages/cli/src/ui/hooks/useAttentionNotifications.test.ts +++ b/packages/cli/src/ui/hooks/useAttentionNotifications.test.ts @@ -15,6 +15,23 @@ import { LONG_TASK_NOTIFICATION_THRESHOLD_SECONDS, useAttentionNotifications, } from './useAttentionNotifications.js'; +import type { LoadedSettings } from '../../config/settings.js'; + +const mockSettings: LoadedSettings = { + merged: { + general: { + terminalBell: true, + }, + }, +} as LoadedSettings; + +const mockSettingsDisabled: LoadedSettings = { + merged: { + general: { + terminalBell: false, + }, + }, +} as LoadedSettings; vi.mock('../../utils/attentionNotification.js', () => ({ notifyTerminalAttention: vi.fn(), @@ -40,6 +57,7 @@ describe('useAttentionNotifications', () => { isFocused: true, streamingState: StreamingState.Idle, elapsedTime: 0, + settings: mockSettings, ...props, }, }, @@ -53,11 +71,13 @@ describe('useAttentionNotifications', () => { isFocused: false, streamingState: StreamingState.WaitingForConfirmation, elapsedTime: 0, + settings: mockSettings, }, }); expect(mockedNotify).toHaveBeenCalledWith( AttentionNotificationReason.ToolApproval, + { enabled: true }, ); }); @@ -72,6 +92,7 @@ describe('useAttentionNotifications', () => { isFocused: false, streamingState: StreamingState.WaitingForConfirmation, elapsedTime: 0, + settings: mockSettings, }, }); @@ -86,6 +107,7 @@ describe('useAttentionNotifications', () => { isFocused: false, streamingState: StreamingState.Responding, elapsedTime: LONG_TASK_NOTIFICATION_THRESHOLD_SECONDS + 5, + settings: mockSettings, }, }); @@ -94,11 +116,13 @@ describe('useAttentionNotifications', () => { isFocused: false, streamingState: StreamingState.Idle, elapsedTime: 0, + settings: mockSettings, }, }); expect(mockedNotify).toHaveBeenCalledWith( AttentionNotificationReason.LongTaskComplete, + { enabled: true }, ); }); @@ -110,6 +134,7 @@ describe('useAttentionNotifications', () => { isFocused: true, streamingState: StreamingState.Responding, elapsedTime: LONG_TASK_NOTIFICATION_THRESHOLD_SECONDS + 2, + settings: mockSettings, }, }); @@ -118,6 +143,7 @@ describe('useAttentionNotifications', () => { isFocused: true, streamingState: StreamingState.Idle, elapsedTime: 0, + settings: mockSettings, }, }); @@ -135,6 +161,7 @@ describe('useAttentionNotifications', () => { isFocused: false, streamingState: StreamingState.Responding, elapsedTime: 5, + settings: mockSettings, }, }); @@ -143,9 +170,30 @@ describe('useAttentionNotifications', () => { isFocused: false, streamingState: StreamingState.Idle, elapsedTime: 0, + settings: mockSettings, }, }); expect(mockedNotify).not.toHaveBeenCalled(); }); + + it('does not notify when terminalBell setting is disabled', () => { + const { rerender } = render({ + settings: mockSettingsDisabled, + }); + + rerender({ + hookProps: { + isFocused: false, + streamingState: StreamingState.WaitingForConfirmation, + elapsedTime: 0, + settings: mockSettingsDisabled, + }, + }); + + expect(mockedNotify).toHaveBeenCalledWith( + AttentionNotificationReason.ToolApproval, + { enabled: false }, + ); + }); }); diff --git a/packages/cli/src/ui/hooks/useAttentionNotifications.ts b/packages/cli/src/ui/hooks/useAttentionNotifications.ts index e632c827..7c5cd043 100644 --- a/packages/cli/src/ui/hooks/useAttentionNotifications.ts +++ b/packages/cli/src/ui/hooks/useAttentionNotifications.ts @@ -10,6 +10,7 @@ import { notifyTerminalAttention, AttentionNotificationReason, } from '../../utils/attentionNotification.js'; +import type { LoadedSettings } from '../../config/settings.js'; export const LONG_TASK_NOTIFICATION_THRESHOLD_SECONDS = 20; @@ -17,13 +18,16 @@ interface UseAttentionNotificationsOptions { isFocused: boolean; streamingState: StreamingState; elapsedTime: number; + settings: LoadedSettings; } export const useAttentionNotifications = ({ isFocused, streamingState, elapsedTime, + settings, }: UseAttentionNotificationsOptions) => { + const terminalBellEnabled = settings?.merged?.general?.terminalBell ?? true; const awaitingNotificationSentRef = useRef(false); const respondingElapsedRef = useRef(0); @@ -33,14 +37,16 @@ export const useAttentionNotifications = ({ !isFocused && !awaitingNotificationSentRef.current ) { - notifyTerminalAttention(AttentionNotificationReason.ToolApproval); + notifyTerminalAttention(AttentionNotificationReason.ToolApproval, { + enabled: terminalBellEnabled, + }); awaitingNotificationSentRef.current = true; } if (streamingState !== StreamingState.WaitingForConfirmation || isFocused) { awaitingNotificationSentRef.current = false; } - }, [isFocused, streamingState]); + }, [isFocused, streamingState, terminalBellEnabled]); useEffect(() => { if (streamingState === StreamingState.Responding) { @@ -53,11 +59,13 @@ export const useAttentionNotifications = ({ respondingElapsedRef.current >= LONG_TASK_NOTIFICATION_THRESHOLD_SECONDS; if (wasLongTask && !isFocused) { - notifyTerminalAttention(AttentionNotificationReason.LongTaskComplete); + notifyTerminalAttention(AttentionNotificationReason.LongTaskComplete, { + enabled: terminalBellEnabled, + }); } // Reset tracking for next task respondingElapsedRef.current = 0; return; } - }, [streamingState, elapsedTime, isFocused]); + }, [streamingState, elapsedTime, isFocused, terminalBellEnabled]); }; diff --git a/packages/cli/src/ui/noninteractive/nonInteractiveUi.ts b/packages/cli/src/ui/noninteractive/nonInteractiveUi.ts index fc75924a..77929333 100644 --- a/packages/cli/src/ui/noninteractive/nonInteractiveUi.ts +++ b/packages/cli/src/ui/noninteractive/nonInteractiveUi.ts @@ -20,7 +20,6 @@ export function createNonInteractiveUI(): CommandContext['ui'] { loadHistory: (_newHistory) => {}, pendingItem: null, setPendingItem: (_item) => {}, - toggleCorgiMode: () => {}, toggleVimEnabled: async () => false, setGeminiMdFileCount: (_count) => {}, reloadCommands: () => {}, diff --git a/packages/cli/src/utils/attentionNotification.ts b/packages/cli/src/utils/attentionNotification.ts index 26dc2a25..e166444f 100644 --- a/packages/cli/src/utils/attentionNotification.ts +++ b/packages/cli/src/utils/attentionNotification.ts @@ -13,6 +13,7 @@ export enum AttentionNotificationReason { export interface TerminalNotificationOptions { stream?: Pick; + enabled?: boolean; } const TERMINAL_BELL = '\u0007'; @@ -28,6 +29,11 @@ export function notifyTerminalAttention( _reason: AttentionNotificationReason, options: TerminalNotificationOptions = {}, ): boolean { + // Check if terminal bell is enabled (default true for backwards compatibility) + if (options.enabled === false) { + return false; + } + const stream = options.stream ?? process.stdout; if (!stream?.write || stream.isTTY === false) { return false; diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index b507c9c5..073f2aa1 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -38,7 +38,6 @@ "src/ui/commands/clearCommand.test.ts", "src/ui/commands/compressCommand.test.ts", "src/ui/commands/copyCommand.test.ts", - "src/ui/commands/corgiCommand.test.ts", "src/ui/commands/docsCommand.test.ts", "src/ui/commands/editorCommand.test.ts", "src/ui/commands/extensionsCommand.test.ts",