mirror of
https://github.com/stablyai/orca.git
synced 2026-09-23 16:02:24 +00:00
* 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>
47 lines
1.5 KiB
TypeScript
47 lines
1.5 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import type { CommandSpec } from './args'
|
|
import { HANDLER_COMMAND_KEYS } from './dispatch'
|
|
import { findRegistryParityGaps } from './registry-parity'
|
|
import { COMMAND_SPECS } from './specs'
|
|
|
|
describe('registry parity (live tree)', () => {
|
|
const handlerKeys = [...HANDLER_COMMAND_KEYS]
|
|
|
|
it('has a handler for every canonical spec path', () => {
|
|
const { specsWithoutHandler } = findRegistryParityGaps(COMMAND_SPECS, handlerKeys)
|
|
expect(specsWithoutHandler).toEqual([])
|
|
})
|
|
|
|
it('has a spec for every handler key', () => {
|
|
const { handlersWithoutSpec } = findRegistryParityGaps(COMMAND_SPECS, handlerKeys)
|
|
expect(handlersWithoutSpec).toEqual([])
|
|
})
|
|
})
|
|
|
|
describe('findRegistryParityGaps (fixtures)', () => {
|
|
const spec = (path: string[], aliases?: string[][]): CommandSpec => ({
|
|
path,
|
|
aliases,
|
|
summary: 's',
|
|
usage: 'u',
|
|
allowedFlags: []
|
|
})
|
|
|
|
it('flags a spec with no handler', () => {
|
|
const gaps = findRegistryParityGaps([spec(['foo', 'bar'])], [])
|
|
expect(gaps.specsWithoutHandler).toEqual(['foo bar'])
|
|
})
|
|
|
|
it('flags a handler with no spec', () => {
|
|
const gaps = findRegistryParityGaps([spec(['foo', 'bar'])], ['foo bar', 'ghost cmd'])
|
|
expect(gaps.handlersWithoutSpec).toEqual(['ghost cmd'])
|
|
})
|
|
|
|
it('does not require a handler for an alias path', () => {
|
|
const gaps = findRegistryParityGaps([spec(['foo', 'rm'], [['foo', 'remove']])], ['foo rm'])
|
|
expect(gaps.specsWithoutHandler).toEqual([])
|
|
expect(gaps.handlersWithoutSpec).toEqual([])
|
|
})
|
|
})
|