Files
orca/src/cli/agent-context.test.ts
T
e2b4bc2c2c feat(cli): make the CLI self-correcting and self-describing for agents (#6303)
* feat(cli): make the CLI self-correcting and self-describing for agents

Agents build a generalized model of how CLIs work and apply it to every
tool. When orca diverged — `rm` where git uses `remove` — a reasonable
first guess (`orca worktree remove`) dead-ended on a bare "Unknown
command" with no path forward. This makes the CLI degrade gracefully when
the orca-cli skill isn't loaded in context.

- First-class CommandSpec.aliases, resolved to the canonical path before
  dispatch (no new handler registrations). `worktree remove`/`delete` now
  resolve to `rm`; the ad-hoc `terminal focus` duplicate spec/handler is
  migrated onto the mechanism.
- Did-you-mean suggestions on unknown commands and unknown flags, ranked
  by edit distance over the live registry, surfaced in both stderr and
  --json error.data (reusing the existing nextSteps channel).
- `orca agent-context [--json]`: a versioned, machine-readable dump of the
  command schema. Pure local read (no RPC), so it works over SSH and when
  the app isn't running.
- CI guards: specs<->handlers parity, and a vocabulary policy that fails
  on new off-policy deletion/read verbs (existing ones grandfathered).

* Address PR review feedback (#6303)

- agent-context now emits each command's effective flag set (globals +
  conditional --page), not just allowedFlags, so the schema no longer
  under-reports --json/--help. Shared as effectiveAllowedFlags() between
  validation and the schema.
- Collision check now covers alias paths too, so a duplicate alias that
  would silently shadow a real command fails the build.

* fix(cli): harden agent recovery and introspection

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-10 19:17:01 -07:00

94 lines
3.2 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import type { CommandSpec } from './args'
import { buildAgentContext, formatAgentContextSummary } from './agent-context'
import { COMMAND_SPECS } from './specs'
describe('buildAgentContext', () => {
const specs: CommandSpec[] = [
{
path: ['worktree', 'rm'],
aliases: [
['worktree', 'remove'],
['worktree', 'delete']
],
summary: 'Remove a worktree',
usage: 'orca worktree rm',
allowedFlags: ['worktree', 'force']
},
{
path: ['agent-context'],
summary: 'Print the schema',
usage: 'orca agent-context',
allowedFlags: []
}
]
it('emits a versioned envelope with a command count', () => {
const schema = buildAgentContext(specs)
expect(schema.schemaVersion).toBe(1)
expect(schema.commandCount).toBe(2)
expect(schema.commands).toHaveLength(2)
})
it('includes resolved aliases for a command', () => {
const schema = buildAgentContext(specs)
const rm = schema.commands.find((command) => command.command === 'worktree rm')
expect(rm?.aliases).toEqual([
['worktree', 'remove'],
['worktree', 'delete']
])
})
it('reports effective flags including globals, not just allowedFlags', () => {
const schema = buildAgentContext(specs)
const rm = schema.commands.find((command) => command.command === 'worktree rm')
expect(rm?.flags).toContain('worktree')
expect(rm?.flags).toContain('force')
expect(rm?.flags).toContain('json')
expect(rm?.flags).toContain('help')
})
it('orders commands deterministically', () => {
const schema = buildAgentContext(specs)
expect(schema.commands.map((command) => command.command)).toEqual([
'agent-context',
'worktree rm'
])
})
it('defaults optional fields to empty arrays', () => {
const schema = buildAgentContext(specs)
const agentContext = schema.commands.find((command) => command.command === 'agent-context')
expect(agentContext?.aliases).toEqual([])
expect(agentContext?.examples).toEqual([])
})
})
describe('agent-context over the live registry', () => {
it('exposes the worktree rm command with its remove/delete aliases', () => {
const schema = buildAgentContext(COMMAND_SPECS)
const rm = schema.commands.find((command) => command.command === 'worktree rm')
expect(rm).toBeDefined()
expect(rm?.aliases).toContainEqual(['worktree', 'remove'])
})
it('human summary count matches the command count', () => {
const schema = buildAgentContext(COMMAND_SPECS)
expect(formatAgentContextSummary(schema)).toContain(`${schema.commandCount} commands`)
})
it('does not advertise browser page targeting for local discovery', () => {
const schema = buildAgentContext(COMMAND_SPECS)
const agentContext = schema.commands.find((command) => command.command === 'agent-context')
expect(agentContext?.flags).not.toContain('page')
})
it('marks raw passthrough commands without synthesizing Orca flags', () => {
const schema = buildAgentContext(COMMAND_SPECS)
const claudeTeams = schema.commands.find((command) => command.command === 'claude-teams')
expect(claudeTeams?.argumentMode).toBe('passthrough')
expect(claudeTeams?.flags).toEqual([])
})
})