mirror of
https://github.com/stablyai/orca.git
synced 2026-11-15 02:17:31 +00:00
* feat(cli): select and remove any saved agent account from `orca account` `orca account select` and `orca account rm` accepted only OpenCode and Devin. They now cover every provider with saved accounts — Claude, Codex, OpenCode, Devin and Antigravity — through the runtime methods the status-bar switcher and Settings already use (accounts.selectClaude, selectCodex/selectCodexForTarget, selectData, antigravitySelect and the matching remove methods). No runtime or wire change. `--account` takes an id, an email/label (case-insensitive) or `system`. A name shared by several accounts is refused with their ids rather than guessing which subscription to bill. `system` is refused for Antigravity (no system default), for Claude from the WSL bridge (the RPC has no target and would reset the Windows host), and always for `rm`. `orca account list` now prints each account's id and a `system` row, accepts `--agent` for every provider, and adds the Antigravity block when the host can read it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cli): assert the host refusal for Antigravity accounts from WSL The host rejects every client-selected WSL target for Antigravity account management, reads included, so the test now mirrors that refusal and checks that no select call is made instead of asserting an impossible success path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(cli): keep only the account-selection tests that guard this change Moves the guards into account.test.ts (the handler layer that owns them) and drops layer-duplicating, copy-pinning, Cartesian and mock-only cases per AGENTS.md test rules; un-exports resolveAccountId (test-only export). --------- Co-authored-by: Maxime AUBURTIN <m@hellomax.io> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
211 lines
8.7 KiB
TypeScript
211 lines
8.7 KiB
TypeScript
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)
|
|
? [...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} <command> [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} <command> --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 === 'terminal set-pane-title' && flag === 'title') {
|
|
return '--title <text> Custom title for one pane (pass "" to reset)'
|
|
}
|
|
if (command === 'linear issue' && flag === 'id') {
|
|
return '--id <id> Linear issue key, id, or URL'
|
|
}
|
|
if ((command === 'linear comment edit' || command === 'linear comment delete') && flag === 'id') {
|
|
return '--id <commentId> Linear comment id (a UUID)'
|
|
}
|
|
if (command === 'linear issue' && flag === 'workspace') {
|
|
return '--workspace <id> Connected Linear workspace id'
|
|
}
|
|
if (command === 'linear search' && flag === 'query') {
|
|
return '--query <text> Text to search across Linear issues'
|
|
}
|
|
if (command === 'linear search' && flag === 'workspace') {
|
|
return '--workspace <id|all> Connected Linear workspace id, or all'
|
|
}
|
|
if (command === 'linear list-issues' && flag === 'cursor') {
|
|
return '--cursor <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 <n> Max issues to return; omit to return every match'
|
|
}
|
|
if (command === 'artifacts list' && flag === 'cursor') {
|
|
return '--cursor <cursor> Opaque cursor returned by a previous artifacts page'
|
|
}
|
|
if (command === 'orchestration worker-read' && flag === 'cursor') {
|
|
return '--cursor <cursor> Opaque cursor returned by a previous worker-read page'
|
|
}
|
|
if (command === 'orchestration worker-list' && flag === 'cursor') {
|
|
return '--cursor <cursor> Opaque page cursor copied from nextCursor'
|
|
}
|
|
if (command === 'orchestration worker-list' && flag === 'terminal-state') {
|
|
return '--terminal-state <state> Terminal accounting filter: active, reclaimable, retained, release_pending, release_unknown, or released'
|
|
}
|
|
if (command.startsWith('orchestration ') && flag === 'full') {
|
|
return '--full Print the full record Orca returned instead of the concise view'
|
|
}
|
|
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 <id|all> Connected Linear workspace id, or all'
|
|
}
|
|
if (command.startsWith('linear ') && flag === 'workspace') {
|
|
return '--workspace <id> Connected Linear workspace id'
|
|
}
|
|
if (command.startsWith('linear ') && flag === 'body') {
|
|
return '--body <text> Linear comment or issue body'
|
|
}
|
|
if (command.startsWith('linear ') && flag === 'body-file') {
|
|
return '--body-file <path|-> Read Linear body from a file or stdin'
|
|
}
|
|
if (command.startsWith('linear ') && flag === 'write-id') {
|
|
return '--write-id <uuid> Retry id from linear_write_unconfirmed'
|
|
}
|
|
if (command.startsWith('linear ') && flag === 'to') {
|
|
return '--to <state> Exact Linear workflow state name'
|
|
}
|
|
if (command === 'linear comment add' && flag === 'reply-to') {
|
|
return '--reply-to <id> Comment id to reply to'
|
|
}
|
|
if (command === 'linear attach' && flag === 'url') {
|
|
return '--url <url> Absolute http(s) link to attach'
|
|
}
|
|
if (command === 'linear attach' && flag === 'title') {
|
|
return '--title <text> Attachment title'
|
|
}
|
|
if (command === 'linear create' && flag === 'title') {
|
|
return '--title <text> New Linear issue title'
|
|
}
|
|
if (command === 'linear create' && flag === 'team') {
|
|
return '--team <key> Linear team key'
|
|
}
|
|
if (command === 'linear create' && flag === 'parent') {
|
|
return '--parent <id> 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 <selector> Parent selector such as identity:<identity>, active/current, id:<repo-id>::<path>, branch:<branch>, issue:<number>, path:<path>, folder:<id>, or worktree:<worktreeId>'
|
|
}
|
|
if (command === 'orchestration task-create' && flag === 'task-title') {
|
|
return '--task-title <text> Concise title for the orchestration task'
|
|
}
|
|
if (command === 'orchestration task-create' && flag === 'display-name') {
|
|
return '--display-name <text> UI label shown for dispatched worker rows'
|
|
}
|
|
if (command === 'account add' && flag === 'agent') {
|
|
return '--agent <id> Account provider: claude, codex, opencode, or devin (default claude)'
|
|
}
|
|
if (command.startsWith('account ') && flag === 'agent') {
|
|
return '--agent <id> Account provider: claude, codex, opencode, devin, or antigravity'
|
|
}
|
|
if (command === 'chat rename' && flag === 'session') {
|
|
return '--session <id> Chat session id (defaults to the chat this runs in)'
|
|
}
|
|
if (command === 'chat rename' && flag === 'name') {
|
|
return '--name <text> New name for the chat'
|
|
}
|
|
if (flag === 'key' && command === 'computer hotkey') {
|
|
return '--key <key-combo> Modifier chord with one key, e.g. CmdOrCtrl+A'
|
|
}
|
|
if (flag === 'key' && command === 'computer press-key') {
|
|
return '--key <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}`
|
|
}
|