import type { CommandSpec } from './args' import { findCommandSpec, isCommandGroup, supportsBrowserPageFlag } from './args' import { unknownCommandData } from './command-suggestion' import { FLAG_HELP_TEXT } from './flag-help-text' import { formatSkillsCommandFlagHelp } from './skills-command-flag-help' 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(commandPath)) { console.log(formatGroupHelp(specs, commandPath[0])) 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[], group: string): string { const groupSpecs = specs.filter((spec) => spec.path[0] === group && spec.hidden !== true) const lines = [`orca ${group}`, '', `Usage: orca ${group} [options]`, '', 'Commands:'] for (const spec of groupSpecs) { lines.push(` ${spec.path.slice(1).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 skillsHelp = formatSkillsCommandFlagHelp(command, flag) if (skillsHelp) { return skillsHelp } 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 or codex (default claude)' } 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 { if (flag === 'current') { return '--current Use the current Orca worktree linked Linear issue' } if (flag === 'comments') { return '--comments Include threaded Linear comments' } if (flag === 'children') { return '--children Include recursive child issues' } if (flag === 'depth') { return '--depth Child issue depth for --children/--full' } if (flag === 'attachments') { return '--attachments Include attachment metadata and URLs' } if (flag === 'relations') { return '--relations Include blocking, related, and duplicate links' } if (flag === 'activity') { return '--activity Include issue field-change history' } if (flag === 'full') { return '--full Include all supported V1 issue context within caps' } return FLAG_HELP_TEXT[flag] ?? `--${flag}` }