mirror of
https://github.com/stablyai/orca.git
synced 2026-09-23 08:02:31 +00:00
* skills: rewrite the seven non-orchestration guides to one outcome-first standard
Every guide leads with Result / Done / Safe failure, states conditions instead of case lists, keeps one done bar and one autonomy envelope, and loads references at the point of use via `skills get <topic> --full`. orca-cli drops from 424 to 260 always-loaded lines with three references; orca-per-workspace-env from 794 to 397 with five.
Defects fixed in shipped guides: `emulator camera` (no such command), iOS `permissions` (backend refuses it), Android pane described as in development, `relayGracePeriodSeconds: 0` documented as immediate teardown (it is unbounded), doctor `ok: true` hiding `warn`, an SSH exemplar setting both `jumpHost` and `proxyCommand`, a provisioned-root fetch from `origin`, and the Linear unconfirmed-write rule keyed on four verbs when ten emit it.
The resolver ladder, placeholder rule, and older-binary fallback shared by every installable SKILL.md now come from one skill-stubs/_shared/cli-resolution.md fragment composed by the generator, which also bundles per-guide references into --full. New guards: every ORCA invocation and flag resolves against COMMAND_SPECS, descriptions carry no angle-bracket tokens, reference routing is checked both ways, and an always-loaded size ratchet (300 lines) that guides may leave but never join.
* skills: address review on the SSH recipe and the parity guard
- ssh-host create script: route the bootstrap ssh through the chosen jump host or proxy command, refuse both at once, use StrictHostKeyChecking=accept-new instead of a blind ssh-keyscan append, and pass gh_token/project_root/repo_url/repo_ref to the remote bash via printf %q so a quote in a value cannot break out of the command.
- per-workspace-env envelope: the step-10 workspace test the user asked for is no longer forbidden by the same paragraph.
- linear guides: name the full verb, ORCA linear list-issues.
- parity guard: a prefix reference such as ORCA linear --help or ORCA emulator --webcam now has its flags checked against every command under that prefix; only an exact path or an explicit ... was checked before.
* skills: tighten prose in the seven rewritten guides
Shorter outcome spines, one idea per sentence, no restated rationale after a rule. No rule, command, or pinned phrase changes; 47 net lines fewer across the guides and references.
* skills: route orca-cli and per-workspace-env gates through --reference
Both guides told agents to load --full at a gate because the per-reference
selector did not exist when they were written. Now that main serves
`skills get <topic> --reference references/<file>.md`, load only the
named file and keep --full as the fallback for an older CLI, matching the
orchestration kernel.
* skills: drop outcome-spine boilerplate from the CLI-wrapper guides
The Result/Done/Safe-failure preambles and Next Action closers restated
rules the body already carries. Agents stop fine without them, and for
a CLI wrapper the command surface is the guide. Keeps the one substantive
rule computer-use's Done block added (never report unverified as success)
inside Action Rules. orchestration and per-workspace-env keep theirs:
those are multi-step workflows where the done bar is load-bearing.
(cherry picked from commit 44a74baf73)
* skills: trim the guides and stubs to what agents actually need
- Drop the Result/Done/Safe-failure preambles and Next Action closers from
the six CLI-wrapper guides; the one substantive rule (never report an
unverified computer-use action as success) moves into Action Rules.
- Drop the 'guide may be stale, trust --help' lines: the guide is served by
the binary that runs the commands, so it cannot be stale relative to it.
- Drop the status --json / open --json preflight from every guide; the stub
no-guessing paragraph now says to start Orca only when a command reports
it is not running.
- Cut the ORCA placeholder paragraph in each guide to one line that points
back at the stub's resolution.
- Trim the orchestration, orca-cli, and computer-use descriptions to trigger
phrases plus one line of scope.
- Remove the older-binary fallback section from every stub (and its two
shared blocks); a binary without skills get gets one sentence.
- Remove the guide size ratchet test.
* skills: apply independent review cleanup
* skills: clarify guide loading and Linear command discovery
* skills: harden environment recipe examples
* test: complete branch rename journal doubles
* skills: clarify custom Codex launch and refresh model example
* test: deduplicate journal fix now present on main
190 lines
6.4 KiB
TypeScript
190 lines
6.4 KiB
TypeScript
import { readdirSync, readFileSync } from 'node:fs'
|
|
import { join, relative, resolve } from 'node:path'
|
|
import { describe, expect, it } from 'vitest'
|
|
import { CLI_GLOBAL_FLAGS } from '../shared/cli-argument-boundary'
|
|
import { specPaths } from './command-spec'
|
|
import { COMMAND_SPECS } from './specs'
|
|
|
|
// Why: a guide is the version-matched surface for the binary that shipped it, so a command
|
|
// path or flag it names must exist in COMMAND_SPECS. `orca emulator camera --webcam` was
|
|
// documented for months without ever existing (#16904 review C1).
|
|
|
|
// Why __dirname: it works under both Vitest and the CommonJS tsc emit that build:cli type-checks
|
|
// this file against; import.meta.dirname does not (TS1470).
|
|
const projectDir = resolve(__dirname, '..', '..')
|
|
const guideRoot = join(projectDir, 'skill-guides')
|
|
const MAX_COMMAND_DEPTH = 3
|
|
|
|
type Invocation = { file: string; line: number; text: string }
|
|
|
|
function guideFiles(directory: string): string[] {
|
|
return readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
const full = join(directory, entry.name)
|
|
if (entry.isDirectory()) {
|
|
return guideFiles(full)
|
|
}
|
|
return entry.isFile() && entry.name.endsWith('.md') ? [full] : []
|
|
})
|
|
}
|
|
|
|
/**
|
|
* The invocation span is the command text only — never the surrounding prose or table cell.
|
|
* `skill-guides/orca-emulator.md` describes serve-sim's own `--detach` in a Notes column beside
|
|
* an `ORCA ...` cell, and that is correct prose a line-scoped check would flag.
|
|
*/
|
|
function invocationSpans(contents: string, file: string): Invocation[] {
|
|
const found: Invocation[] = []
|
|
let inFence = false
|
|
contents.split(/\r?\n/u).forEach((line, index) => {
|
|
if (/^\s*(?:```|~~~)/u.test(line)) {
|
|
inFence = !inFence
|
|
return
|
|
}
|
|
const spans = inFence ? [line] : [...line.matchAll(/`([^`]+)`/gu)].map((match) => match[1])
|
|
for (const span of spans) {
|
|
const starts = [...span.matchAll(/\bORCA\b/gu)].map((match) => match.index)
|
|
starts.forEach((start, position) => {
|
|
found.push({
|
|
file,
|
|
line: index + 1,
|
|
text: span.slice(start, starts[position + 1] ?? span.length).trim()
|
|
})
|
|
})
|
|
}
|
|
})
|
|
return found
|
|
}
|
|
|
|
/** Blank out quoted values so a nested `--model` inside `--command "codex --model ..."` is not read as a flag. */
|
|
function maskQuotedValues(text: string): string {
|
|
let masked = ''
|
|
let quote: string | null = null
|
|
for (const character of text) {
|
|
if (quote) {
|
|
masked += character === quote ? character : ' '
|
|
if (character === quote) {
|
|
quote = null
|
|
}
|
|
} else if (character === '"' || character === "'") {
|
|
quote = character
|
|
masked += character
|
|
} else {
|
|
masked += character
|
|
}
|
|
}
|
|
return masked
|
|
}
|
|
|
|
const specByPath = new Map<string, (typeof COMMAND_SPECS)[number]>()
|
|
const pathPrefixes = new Set<string>()
|
|
for (const spec of COMMAND_SPECS) {
|
|
for (const path of specPaths(spec)) {
|
|
specByPath.set(path.join(' '), spec)
|
|
for (let length = 1; length < path.length; length += 1) {
|
|
pathPrefixes.add(path.slice(0, length).join(' '))
|
|
}
|
|
}
|
|
}
|
|
|
|
function longestKnownPrefix(tokens: string[]): string | null {
|
|
for (let length = tokens.length; length >= 1; length -= 1) {
|
|
const candidate = tokens.slice(0, length).join(' ')
|
|
if (specByPath.has(candidate) || pathPrefixes.has(candidate)) {
|
|
return candidate
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
function allowedFlagsFor(prefix: string): Set<string> {
|
|
const exact = specByPath.get(prefix)
|
|
const flags = new Set<string>(CLI_GLOBAL_FLAGS)
|
|
const specs = exact
|
|
? [exact]
|
|
: COMMAND_SPECS.filter((spec) =>
|
|
specPaths(spec).some((path) => path.join(' ').startsWith(`${prefix} `))
|
|
)
|
|
for (const spec of specs) {
|
|
for (const flag of spec.allowedFlags) {
|
|
flags.add(flag)
|
|
}
|
|
}
|
|
return flags
|
|
}
|
|
|
|
function describeFailure(invocation: Invocation, detail: string): string {
|
|
const location = `${relative(projectDir, invocation.file)}:${invocation.line}`
|
|
return `${location}: ${detail}\n ${invocation.text}`
|
|
}
|
|
|
|
function parityFailures(invocation: Invocation): string[] {
|
|
const masked = maskQuotedValues(invocation.text).replace(/\s#.*$/u, '')
|
|
const tokens: string[] = []
|
|
for (const token of masked.slice('ORCA'.length).trim().split(/\s+/u)) {
|
|
if (!/^[a-z][a-z0-9-]*$/u.test(token) || tokens.length === MAX_COMMAND_DEPTH) {
|
|
break
|
|
}
|
|
tokens.push(token)
|
|
}
|
|
if (tokens.length === 0) {
|
|
return []
|
|
}
|
|
|
|
const failures: string[] = []
|
|
let command: string | null = null
|
|
for (let length = tokens.length; length >= 1 && command === null; length -= 1) {
|
|
const candidate = tokens.slice(0, length).join(' ')
|
|
if (specByPath.has(candidate)) {
|
|
command = candidate
|
|
}
|
|
}
|
|
if (command === null) {
|
|
// A prefix reference such as `ORCA emulator ...` or `ORCA linear --help` names no exact
|
|
// path, but its flags still have to belong to some command under that prefix.
|
|
if (pathPrefixes.has(tokens.join(' '))) {
|
|
command = tokens.join(' ')
|
|
}
|
|
}
|
|
if (command === null) {
|
|
failures.push(
|
|
describeFailure(invocation, `no COMMAND_SPECS path or alias for "${tokens.join(' ')}"`)
|
|
)
|
|
command = longestKnownPrefix(tokens)
|
|
if (command === null) {
|
|
return failures
|
|
}
|
|
}
|
|
|
|
const allowed = allowedFlagsFor(command)
|
|
for (const match of masked.matchAll(/--([a-z][a-z0-9-]*)/gu)) {
|
|
if (!allowed.has(match[1])) {
|
|
failures.push(describeFailure(invocation, `--${match[1]} is not a flag of "${command}"`))
|
|
}
|
|
}
|
|
return failures
|
|
}
|
|
|
|
describe('skill guides only name commands and flags the CLI defines', () => {
|
|
const invocations = guideFiles(guideRoot).flatMap((file) =>
|
|
invocationSpans(readFileSync(file, 'utf8'), file)
|
|
)
|
|
|
|
it('extracts a nonempty invocation corpus across guides and references', () => {
|
|
expect(invocations.length).toBeGreaterThan(150)
|
|
expect(new Set(invocations.map((invocation) => invocation.file)).size).toBeGreaterThan(8)
|
|
})
|
|
|
|
it('checks extracted ORCA command paths and flags against COMMAND_SPECS', () => {
|
|
expect(invocations.flatMap(parityFailures)).toEqual([])
|
|
})
|
|
|
|
it('checks flags on a prefix reference against every command under it', () => {
|
|
const at = (text: string) => parityFailures({ file: 'x.md', line: 1, text })
|
|
expect(at('ORCA emulator ...')).toEqual([])
|
|
expect(at('ORCA linear --help')).toEqual([])
|
|
expect(at('ORCA emulator --webcam')).toEqual([
|
|
expect.stringContaining('--webcam is not a flag of "emulator"')
|
|
])
|
|
})
|
|
})
|