import type { CommandSpec } from './args' import { findCommandSpec, isCommandGroup, matches, supportsBrowserPageFlag } from './args' import { unknownCommandData } from './command-suggestion' import { formatCommandScopedFlagHelp } from './command-scoped-flag-help' import { FLAG_HELP_TEXT } from './flag-help-text' import { ROOT_HELP_TEXT_PRIMARY } from './root-help-text-primary' import { ROOT_HELP_TEXT_SECONDARY } from './root-help-text-secondary' const ROOT_HELP_TEXT = [ROOT_HELP_TEXT_PRIMARY, ROOT_HELP_TEXT_SECONDARY].join('\n') export function printHelp(specs: CommandSpec[], commandPath: string[] = []): void { const exactSpec = findCommandSpec(specs, commandPath) if (exactSpec) { console.log(formatCommandHelp(exactSpec)) return } if (isCommandGroup(specs, commandPath)) { console.log(formatGroupHelp(specs, commandPath)) return } if (commandPath.length > 0) { const { nextSteps } = unknownCommandData(specs, commandPath) const recovery = nextSteps.map((step) => `Next step: ${step}`).join('\n') console.log(`Unknown command: ${commandPath.join(' ')}${recovery ? `\n${recovery}` : ''}\n`) } console.log(ROOT_HELP_TEXT) } export function formatCommandHelp(spec: CommandSpec): string { const lines = [`orca ${spec.path.join(' ')}`, '', `Usage: ${spec.usage}`, '', spec.summary] const displayedFlags = spec.argumentMode === 'passthrough' ? [] : supportsBrowserPageFlag(spec.path) ? [...spec.allowedFlags, 'page'] : spec.allowedFlags if (displayedFlags.length > 0) { lines.push('', 'Options:') for (const flag of displayedFlags) { lines.push(` ${formatCommandFlagHelp(flag, spec.path)}`) } } if (spec.notes && spec.notes.length > 0) { lines.push('', 'Notes:') for (const note of spec.notes) { lines.push(` ${note}`) } } if (spec.examples && spec.examples.length > 0) { lines.push('', 'Examples:') for (const example of spec.examples) { lines.push(` $ ${example}`) } } return lines.join('\n') } export function formatGroupHelp(specs: CommandSpec[], groupPath: string[]): string { const group = groupPath.join(' ') const lines = [`orca ${group}`, '', `Usage: orca ${group} [options]`, '', 'Commands:'] for (const spec of specs) { if ( spec.hidden === true || spec.path.length <= groupPath.length || !matches(spec.path.slice(0, groupPath.length), groupPath) ) { continue } lines.push(` ${spec.path.slice(groupPath.length).join(' ').padEnd(18)} ${spec.summary}`) } lines.push('', `Run \`orca ${group} --help\` for command-specific usage.`) return lines.join('\n') } function formatCommandFlagHelp(flag: string, commandPath: string[]): string { const command = commandPath.join(' ') const scopedHelp = formatCommandScopedFlagHelp(command, flag) if (scopedHelp) { return scopedHelp } if (command === 'terminal close' && flag === 'tab') { return '--tab Close the whole tab and wait for durable persistence' } if (command === 'linear issue' && flag === 'id') { return '--id Linear issue key, id, or URL' } if (command === 'linear issue' && flag === 'workspace') { return '--workspace Connected Linear workspace id' } if (command === 'linear search' && flag === 'query') { return '--query Text to search across Linear issues' } if (command === 'linear search' && flag === 'workspace') { return '--workspace Connected Linear workspace id, or all' } if (command === 'linear list-issues' && flag === 'cursor') { return '--cursor Opaque cursor from a previous list-issues page; issued cursors bind the workspace, raw Linear cursors need --workspace' } if (command === 'linear list-issues' && flag === 'priority') { return '--priority <0-4> 0=none, 1=urgent, 2=high, 3=medium, 4=low' } if (command === 'linear list-issues' && flag === 'limit') { return '--limit Max issues to return; omit to return every match' } if (command === 'artifacts list' && flag === 'cursor') { return '--cursor Opaque cursor returned by a previous artifacts page' } if (command === 'orchestration worker-read' && flag === 'cursor') { return '--cursor Opaque cursor returned by a previous worker-read page' } if (command === 'orchestration worker-list' && flag === 'cursor') { return '--cursor Opaque page cursor copied from page.nextCursor' } if (command === 'orchestration worker-list' && flag === 'terminal-state') { return '--terminal-state Terminal accounting filter: active, reclaimable, retained, release_pending, release_unknown, or released' } if (command === 'skills get' && flag === 'full') { return '--full Print the full guide with bundled references' } if (command === 'orchestration worker-list' && flag === 'include-remote') { return '--include-remote Include connected-server worker observations' } if (command === 'linear list-issues' && flag === 'workspace') { return '--workspace Connected Linear workspace id, or all' } if (command.startsWith('linear ') && flag === 'workspace') { return '--workspace Connected Linear workspace id' } if (command.startsWith('linear ') && flag === 'body') { return '--body Linear comment or issue body' } if (command.startsWith('linear ') && flag === 'body-file') { return '--body-file Read Linear body from a file or stdin' } if (command.startsWith('linear ') && flag === 'write-id') { return '--write-id Retry id from linear_write_unconfirmed' } if (command.startsWith('linear ') && flag === 'to') { return '--to Exact Linear workflow state name' } if (command === 'linear comment add' && flag === 'reply-to') { return '--reply-to Comment id to reply to' } if (command === 'linear attach' && flag === 'url') { return '--url Absolute http(s) link to attach' } if (command === 'linear attach' && flag === 'title') { return '--title Attachment title' } if (command === 'linear create' && flag === 'title') { return '--title New Linear issue title' } if (command === 'linear create' && flag === 'team') { return '--team Linear team key' } if (command === 'linear create' && flag === 'parent') { return '--parent Parent Linear issue key, id, or URL' } if (command === 'linear create' && flag === 'parent-current') { return '--parent-current Use the current linked issue as parent' } if (command === 'worktree create' && flag === 'parent-worktree') { return '--parent-worktree Parent selector such as identity:, active/current, id:::, branch:, issue:, path:, folder:, or worktree:' } if (command === 'orchestration task-create' && flag === 'task-title') { return '--task-title Concise title for the orchestration task' } if (command === 'orchestration task-create' && flag === 'display-name') { return '--display-name UI label shown for dispatched worker rows' } // Why: the shared --agent help describes launching a TUI agent in a terminal, // which is the wrong meaning here — this selects the account provider. if (command === 'account add' && flag === 'agent') { return '--agent Account provider: claude, codex, opencode, or devin (default claude)' } if (command.startsWith('account ') && flag === 'agent') { return '--agent Account provider: opencode or devin' } if (flag === 'key' && command === 'computer hotkey') { return '--key Modifier chord with one key, e.g. CmdOrCtrl+A' } if (flag === 'key' && command === 'computer press-key') { return '--key Single key, e.g. Return, Escape, Tab, Left, or PageUp' } return formatFlagHelp(flag) } export function formatFlagHelp(flag: string): string { return FLAG_HELP_TEXT[flag] ?? `--${flag}` }