import type { CommandSpec } from './args' import { findCommandSpec, isCommandGroup, supportsBrowserPageFlag } from './args' import { unknownCommandData } from './command-suggestion' 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 { const helpByFlag: Record = { agent: '--agent Launch a known TUI agent in the first terminal', 'base-branch': '--base-branch Base branch/ref to create the worktree from', command: '--command Command to run in the terminal on startup', comment: '--comment Comment stored in Orca metadata', cursor: '--cursor Line cursor from a previous read (returns only new output)', action: '--action Secondary accessibility action name', activate: '--activate Reveal the new worktree in the Orca app', app: '--app App name, bundle ID, or pid:N', direction: '--direction Direction: up|down|left|right for scroll, horizontal|vertical for split', 'display-name': '--display-name Override the Orca display name', 'element-index': '--element-index Element index from get-app-state', title: '--title Custom title for the terminal tab (omit to reset)', enter: '--enter Append Enter after sending text', force: '--force Force worktree removal when supported; does not force branch deletion', focus: '--focus Reveal the created terminal session in Orca', for: '--for exit|tui-idle Wait condition to satisfy', 'from-element-index': '--from-element-index Source element index from get-app-state', 'from-x': '--from-x Source window-local x coordinate', 'from-y': '--from-y Source window-local y coordinate', help: '--help Show this help message', 'include-visual-layouts': '--include-visual-layouts Include tab and pane topology in JSON output', interrupt: '--interrupt Send as an interrupt-style input when supported', id: '--id Identifier for a target item or permission', issue: '--issue Linked GitHub issue number', 'linear-issue': '--linear-issue Linked Linear issue identifier or URL; null clears on set', json: '--json Emit machine-readable JSON', key: '--key Key argument for this command', limit: '--limit Maximum number of rows to return', local: '--local Target the current project instead of the global install', skill: '--skill Bundled skill to act on; repeat for several', mode: '--mode Mode such as edit, diff, or both', model: '--model Provider model id for a new agent launch', effort: '--effort Reasoning effort for the selected model', 'mouse-button': '--mouse-button Mouse button: left, right, or middle', modifiers: '--modifiers Modifier keys held only for this click', name: '--name Name for the new worktree or automation', 'no-parent': '--no-parent Force no parent lineage for unrelated work', 'no-screenshot': '--no-screenshot Skip screenshot capture after the operation', pages: '--pages Number of scroll pages', 'parent-worktree': '--parent-worktree Parent worktree selector such as identity:, id:::, branch:, issue:, path:, or active/current', path: '--path Path argument for the command', prompt: '--prompt Prompt text for agent-backed commands', query: '--query Search text for matching refs', ref: '--ref Base ref to persist for the repo', repo: '--repo Repo selector such as id:, name:, or path:', 'restore-window': '--restore-window Bring the target app/window forward before the operation', session: '--session Snapshot namespace for a related computer-use workflow', setup: '--setup run|skip|inherit Setup policy for repo-defined setup hooks', terminal: '--terminal Runtime-issued terminal handle', text: '--text Text payload to send or type', 'text-stdin': '--text-stdin Read text payload from stdin', 'task-id': '--task-id Task id to include in orchestration payload JSON', 'task-title': '--task-title Concise title for an orchestration task', 'dispatch-id': '--dispatch-id Dispatch id to include in orchestration payload JSON', 'files-modified': '--files-modified Comma-separated files for orchestration payload JSON', 'report-path': '--report-path Report path to include in orchestration payload JSON', phase: '--phase Worker phase to include in orchestration payload JSON', 'timeout-ms': '--timeout-ms Maximum wait time before timing out', 'to-element-index': '--to-element-index Destination element index from get-app-state', 'to-x': '--to-x Destination window-local x coordinate', 'to-y': '--to-y Destination window-local y coordinate', worktree: '--worktree Worktree selector such as identity:, id:::, name:, branch:, issue:, path:, or active/current', workspace: '--workspace Existing worktree selector for automation runs', 'workspace-status': '--workspace-status Board status id (defaults: todo, in-progress, in-review, completed)', staged: '--staged Open staged source-control changes', provider: '--provider Agent id such as codex, claude, or gemini', 'source-context': '--source-context Explicit TaskSourceContext for automation task/provider data', trigger: '--trigger Automation schedule preset, cron, or RRULE', schedule: '--schedule Alias for --trigger', time: '--time Time used with daily/weekdays/weekly presets', day: '--day <0-6> Day used with weekly preset, Sunday=0', timezone: '--timezone IANA timezone for the automation', enabled: '--enabled Enable the automation', disabled: '--disabled Disable the automation', 'reuse-session': '--reuse-session Reuse the previous live session for existing-workspace runs', 'fresh-session': '--fresh-session Disable session reuse for future runs', 'workspace-mode': '--workspace-mode existing or new-per-run', 'missed-run-grace-minutes': '--missed-run-grace-minutes Missed-run grace window', 'value-stdin': '--value-stdin Read set-value payload from stdin', 'window-id': '--window-id Target a window id from list-windows', 'window-index': '--window-index Target a window index from list-windows', // Browser automation flags element: '--element Element ref from snapshot (e.g. e3)', url: '--url URL to navigate to', value: '--value Value to fill or select', input: '--input Text to type at current focus', expression: '--expression JavaScript expression to evaluate', amount: '--amount Scroll distance in pixels', index: '--index Tab index to switch to', page: '--page Stable browser page id from `orca tab list --json`', profile: '--profile Browser profile id', 'show-profile': '--show-profile Include tab profile in text output', 'no-ua-spoof': "--no-ua-spoof Keep Electron's native user agent", format: '--format Screenshot image format' } 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 helpByFlag[flag] ?? `--${flag}` }