From b3847f936e2ec412fa2d939fac395b9dc02b0bca Mon Sep 17 00:00:00 2001 From: Neil Date: Mon, 14 Sep 2026 11:51:30 -0700 Subject: [PATCH] fix(native-chat): suggest verified OMP terminal commands Co-authored-by: Nafisul Haque <100821672+nafisul-haque@users.noreply.github.com> --- ...le-native-chat-send-classification.test.ts | 7 +++ .../native-chat-slash-commands.omp.test.ts | 60 +++++++++++++++++++ src/shared/native-chat-slash-commands.ts | 32 +++++++++- 3 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 src/shared/native-chat-slash-commands.omp.test.ts diff --git a/mobile/src/session/mobile-native-chat-send-classification.test.ts b/mobile/src/session/mobile-native-chat-send-classification.test.ts index 55b9863efe9..8ad61d01c61 100644 --- a/mobile/src/session/mobile-native-chat-send-classification.test.ts +++ b/mobile/src/session/mobile-native-chat-send-classification.test.ts @@ -2,6 +2,13 @@ import { describe, expect, it } from 'vitest' import { classifyMobileNativeChatSend } from './mobile-native-chat-send-classification' describe('classifyMobileNativeChatSend', () => { + it('recognizes OMP selectors and context commands without claiming generic help', () => { + expect(classifyMobileNativeChatSend('omp', '/switch')).toBe('command') + expect(classifyMobileNativeChatSend('omp', '/compact focus on tests')).toBe('command') + expect(classifyMobileNativeChatSend('omp', '/help')).toBe('unknown-token') + expect(classifyMobileNativeChatSend('omp', '/smol')).toBe('unknown-token') + }) + it('classifies catalog commands per agent', () => { expect(classifyMobileNativeChatSend('claude', '/clear')).toBe('command') expect(classifyMobileNativeChatSend('claude', '/compact')).toBe('command') diff --git a/src/shared/native-chat-slash-commands.omp.test.ts b/src/shared/native-chat-slash-commands.omp.test.ts new file mode 100644 index 00000000000..8282f9da796 --- /dev/null +++ b/src/shared/native-chat-slash-commands.omp.test.ts @@ -0,0 +1,60 @@ +import { describe, expect, it } from 'vitest' +import { + getNativeChatAgentProfile, + getVerifiedNativeChatCommands +} from './native-chat-agent-profiles' +import { + applySlashSuggestion, + classifyNativeChatSend, + filterSlashCommands, + getAgentSlashCommands, + sessionSlashCommandSuggestions, + slashCommandDispatchText +} from './native-chat-slash-commands' + +describe('OMP terminal command catalog', () => { + it('offers the verified model, planning and context commands without generic help', () => { + const commands = getAgentSlashCommands('omp') + const names = commands.map((command) => command.name) + expect(names).toEqual( + expect.arrayContaining(['model', 'switch', 'plan', 'compact', 'context', 'usage']) + ) + expect(names).not.toContain('help') + expect(getVerifiedNativeChatCommands('omp')).toEqual(commands) + expect(getNativeChatAgentProfile('omp')).toBeNull() + expect(new Set(names).size).toBe(names.length) + expect(getAgentSlashCommands('pi').map((command) => command.name)).toEqual(['clear', 'help']) + }) + + it('completes arguments separately from picker command dispatch', () => { + const matches = filterSlashCommands(getAgentSlashCommands('omp'), 'MOD') + expect(matches).toHaveLength(1) + const command = matches[0] + if (!command) { + throw new Error('Expected an OMP model suggestion') + } + expect(command.name).toBe('model') + expect(applySlashSuggestion(command)).toBe('/model ') + expect(slashCommandDispatchText(command)).toBe('/model') + }) + + it('classifies known OMP commands as terminal actions, preserving unknown and prose paths', () => { + const commands = getAgentSlashCommands('omp') + expect(classifyNativeChatSend('/compact focus on tests', commands, null, null)).toBe('command') + expect(classifyNativeChatSend('/model', commands, null, null)).toBe('command') + expect(classifyNativeChatSend('/help', commands, null, null)).toBe('unknown-token') + expect(classifyNativeChatSend(' /model', commands, null, null)).toBe('chat') + expect(classifyNativeChatSend('/plan', commands, '/plan', '/')).toBe('chat') + }) + + it('lets a session command report replace the curated set and descriptions', () => { + expect( + sessionSlashCommandSuggestions('omp', [ + { name: 'model', kind: 'command', description: 'Host model selector' }, + { name: 'custom', kind: 'command' }, + { name: 'plan', kind: 'skill' } + ]) + ).toEqual([{ name: 'model', description: 'Host model selector' }, { name: 'custom' }]) + expect(sessionSlashCommandSuggestions('omp', [])).toEqual([]) + }) +}) diff --git a/src/shared/native-chat-slash-commands.ts b/src/shared/native-chat-slash-commands.ts index 6360ca05f83..d650c9b0f3a 100644 --- a/src/shared/native-chat-slash-commands.ts +++ b/src/shared/native-chat-slash-commands.ts @@ -82,10 +82,40 @@ const CODEX_COMMANDS: readonly SlashCommandSuggestion[] = [ { name: 'subagents', description: 'Switch the active agent thread' } ] +// OMP built-in registry; interactive commands still execute in its terminal. +const OMP_COMMANDS: readonly SlashCommandSuggestion[] = [ + { name: 'model', description: 'Open the model selector in Terminal' }, + { + name: 'switch', + description: 'Open the temporary model selector in Terminal' + }, + { name: 'plan', description: 'Toggle plan mode' }, + { name: 'compact', description: 'Compact conversation context' }, + { name: 'clear', description: 'Clear context while keeping the session' }, + { name: 'new', description: 'Start a new session' }, + { name: 'resume', description: 'Resume a session; without arguments, choose in Terminal' }, + { name: 'fork', description: 'Fork from a previous message in Terminal' }, + { name: 'branch', description: 'Rewind to a previous message in Terminal' }, + { name: 'tree', description: 'Browse the session tree in Terminal' }, + { name: 'session', description: 'Show session information and controls' }, + { name: 'rename', description: 'Rename the session' }, + { name: 'context', description: 'Show estimated context usage' }, + { name: 'usage', description: 'Show provider usage and limits' }, + { name: 'fast', description: 'Toggle priority service tier' }, + { name: 'tools', description: 'Show tools visible to the agent' }, + { name: 'jobs', description: 'Show background jobs' }, + { name: 'git', description: 'Open the Git viewer in Terminal' }, + { name: 'export', description: 'Export the session to HTML' }, + { name: 'settings', description: 'Open settings in Terminal' }, + { name: 'extensions', description: 'Open the extension dashboard in Terminal' }, + { name: 'hotkeys', description: 'Show keyboard shortcuts in Terminal' } +] + const COMMANDS_BY_AGENT: Partial> = { claude: CLAUDE_COMMANDS, openclaude: CLAUDE_COMMANDS, - codex: CODEX_COMMANDS + codex: CODEX_COMMANDS, + omp: OMP_COMMANDS } /** Known slash commands for an agent, falling back to a small common set so the