const SUBCOMMAND_HELP: Record = { config: [ "Usage: sf config", "", "Re-run the interactive setup wizard to configure:", " - LLM provider (Anthropic, OpenAI, Google, OpenRouter, Ollama, LM Studio, etc.)", " - Web search provider (Brave, Tavily, built-in)", " - Remote questions (Discord, Slack, Telegram)", " - Tool API keys (Context7, Jina, Groq)", "", "All steps are skippable and can be changed later with /login or /search-provider.", "", "For detailed provider setup instructions (OpenRouter, Ollama, LM Studio, vLLM,", "and other OpenAI-compatible endpoints), see docs/providers.md.", ].join("\n"), update: [ "Usage: sf update", "", "Update SF to the latest version.", "", "Equivalent to: npm install -g singularity-forge@latest", ].join("\n"), sessions: [ "Usage: sf sessions", " sf sessions --all", "", "List saved sessions and interactively pick one to resume. Shows date,", "message count, and a preview of the first message for each session.", "", "Sessions are stored per-directory by default. Use --all to list sessions", "across all projects.", "", " sf sessions List sessions for the current directory", " sf sessions --all List sessions across all projects", "", "Compare with --continue (-c) which always resumes the most recent session.", ].join("\n"), install: [ "Usage: sf install [-l, --local]", "", "Install a package/extension source and run post-install validation (dependency checks, setup).", "", "Examples:", " sf install npm:@foo/bar", " sf install git:github.com/user/repo", " sf install https://github.com/user/repo", " sf install ./local/path", ].join("\n"), remove: [ "Usage: sf remove [-l, --local]", "", "Remove an installed package source and its settings entry.", ].join("\n"), list: [ "Usage: sf list", "", "List installed package sources from user and project settings.", ].join("\n"), logs: [ "Usage: sf logs tail|follow [--source notif|session|activity|audit] [--severity level]", "", "Follow a merged live stream from .sf/notifications.jsonl, the latest", "headless session JSONL, the latest .sf/activity JSONL, and .sf/audit-log.jsonl.", "", "Examples:", " sf logs tail", " sf logs follow --source audit", " sf logs tail --severity error", ].join("\n"), status: [ "Usage: sf status --live [--watch]", "", "Show a printable aggregate project view: current milestone, slice, task,", "phase, next dispatch, recent events, session cost, and current model.", "", "Examples:", " sf status --live", " sf status --live --watch", " sf dash --watch", ].join("\n"), dash: ["Usage: sf dash [--watch]", "", "Alias for sf status --live."].join( "\n", ), stats: [ "Usage: sf stats models [--unit-type ] [--since ]", "", "Summarize Bayesian model outcome rows from .sf/sf.db.", "", "Examples:", " sf stats models --since 7d", " sf stats models --unit-type execute-task --since 24h", ].join("\n"), worktree: [ "Usage: sf worktree [args]", "", "Manage isolated git worktrees for parallel work streams.", "", "Commands:", " list List worktrees with status (files changed, commits, dirty)", " merge [name] Squash-merge a worktree into main and clean up", " clean Remove all worktrees that have been merged or are empty", " remove Remove a worktree (--force to remove with unmerged changes)", "", "The -w flag creates/resumes worktrees for interactive sessions:", " sf -w Auto-name a new worktree, or resume the only active one", " sf -w my-feature Create or resume a named worktree", "", "Lifecycle:", " 1. sf -w Create worktree, start session inside it", " 2. (work normally) All changes happen on the worktree branch", " 3. Ctrl+C Exit — dirty work is auto-committed", " 4. sf -w Resume where you left off", " 5. sf worktree merge Squash-merge into main when done", "", "Examples:", " sf -w Start in a new auto-named worktree", ' sf -w auth-refactor Create/resume "auth-refactor" worktree', " sf worktree list See all worktrees and their status", " sf worktree merge auth-refactor Merge and clean up", " sf worktree clean Remove all merged/empty worktrees", " sf worktree remove old-branch Remove a specific worktree", " sf worktree remove old-branch --force Remove even with unmerged changes", ].join("\n"), graph: [ "Usage: sf graph [options]", "", "Manage the SF project knowledge graph. Reads .sf/ artifacts and builds", "a queryable graph of milestones, slices, tasks, rules, patterns, and lessons.", "", "Subcommands:", " build Parse .sf/ artifacts (STATE.md, milestone ROADMAPs, slice PLANs,", " KNOWLEDGE.md) and write .sf/graphs/graph.json atomically.", " query Search graph nodes by term (BFS from seed matches, budget-trimmed).", " Returns matching nodes and reachable edges within the token budget.", " status Show whether graph.json exists, its age, node/edge counts, and", " whether it is stale (built more than 24 hours ago).", " diff Compare current graph.json with .last-build-snapshot.json.", " Returns added, removed, and changed nodes and edges.", "", "Examples:", " sf graph build Build the graph from .sf/ artifacts", " sf graph status Check graph age and node/edge counts", ' sf graph query auth Find nodes related to "auth"', " sf graph diff Show changes since last snapshot", ].join("\n"), plan: [ "Usage: sf plan ", "", "Manage SF milestone planning artifacts and human docs exports.", "", "Commands:", " promote Copy a file from .sf/ to docs/plans/, docs/adr/, or docs/specs/", " list List milestone and slice files in .sf/", " diff Compare .sf/ state with promoted docs/ artifacts", " specs Generate, diff, or check docs/specs exports from .sf state", "", "See docs/plans/README.md, docs/adr/README.md, and docs/specs/README.md for conventions.", ].join("\n"), schedule: [ "Usage: sf schedule [args]", "", "Manage time-bound reminders and deferred work items.", "Entries are stored as versioned append-only JSONL in .sf/schedule.jsonl (project)", "or ~/.sf/schedule.jsonl (global). No daemon required — items surface on pull.", "", "Commands:", " add --in [--kind ] [--scope ] ", " --at <ISO-date> Schedule by absolute date instead", " list [--due] [--all] [--json] [--scope <scope>]", " done <id> Mark a scheduled item as done", " cancel <id> Cancel a scheduled item", " snooze <id> --by <duration> Postpone by relative time", " run <id> Execute a scheduled item (show reminder or run command)", "", "Duration format: <number><unit> where unit is w(weeks), d(days), h(hours), m(minutes).", " e.g. 30m, 4h, 2d, 1w", "", "Scope: project (default, stored in .sf/schedule.jsonl) or global (~/.sf/schedule.jsonl).", "", "Kinds: reminder, milestone_check, review_due, review, audit, recurring, command", "", "Examples:", ' sf schedule add --in 2w "Review feature adoption metrics"', ' sf schedule add --at 2026-06-01T09:00:00Z --kind audit "Audit ADR-007"', " sf schedule list --due", " sf schedule snooze 01ARZ3ND --by 1d", " sf schedule done 01ARZ3ND", ].join("\n"), key: [ "Usage: sf key <subcommand> [args]", "", "Manage provider API keys stored in ~/.sf/agent/auth.json.", "", "Credentials for SF runtime live in ~/.sf/agent/auth.json.", "SF does NOT read API keys from environment variables at runtime —", "env is only used during initial provider setup. Use `sf key set`", "to rotate keys. auth.json is always the authoritative source.", "", "Subcommands:", " set <provider> <api-key> Set or rotate the API key for a provider", " get <provider> Show key status (last 4 chars only, never full key)", " remove <provider> [--yes] Remove the credential (prompts unless --yes)", " list List all providers with credential status", "", "Examples:", " sf key set anthropic sk-ant-abc123 Set the Anthropic API key", " sf key set xiaomi tp-abc123 Set the Xiaomi API key", " sf key get anthropic Show masked key for anthropic", " sf key remove anthropic Remove anthropic credential (confirms)", " sf key remove anthropic --yes Remove without prompting", " sf key list List all providers", "", "Security:", " - Keys are never echoed in full. Only the last 4 characters are shown.", " - All writes go through AuthStorage (file-locked, chmod 600).", " - sf key remove always asks for confirmation unless --yes is passed.", ].join("\n"), headless: [ "Usage: sf headless [flags] [command] [args...]", "", "Machine surface for direct SF commands. Runs the same SF flow without rendering the TUI.", "", "Flags:", " --timeout N Overall timeout in ms (default: 300000)", " --json JSONL event stream to stdout (alias for --output-format stream-json)", " --output-format <fmt> Output format: text (default), json (structured result), stream-json (JSONL events)", " --bare Minimal context: skip CLAUDE.md, AGENTS.md, user settings, user skills", " --resume <id> Resume a prior headless session by ID", " --model ID Override model", " --supervised Forward interactive UI requests to orchestrator via stdout/stdin", " --response-timeout N Timeout (ms) for orchestrator response (default: 30000)", " --answers <path> Pre-supply answers and secrets (JSON file)", " --events <types> Filter JSONL output to specific event types (comma-separated)", "", "Commands:", " autonomous Run all queued product units continuously", " next Run one unit", " status Show progress dashboard", " new-milestone Create a milestone from a specification document", " query Machine snapshot: JSON state + next dispatch + costs (no LLM)", " usage Live LLM-provider usage snapshot (today: gemini-cli tier + per-model quota)", " reflect Assemble reflection corpus + render prompt for cross-corpus pattern analysis (--json for raw, --run to dispatch to gemini-cli, --model <id> to override)", " triage Render canonical self-feedback triage prompt (--list/--json inspect, --run writes decisions, --apply runs triage-decider -> review-code)", " complete-slice Mark a slice complete out-of-band: complete-slice <M>/<S> [--reason <txt>]", " skip-slice Mark a slice skipped out-of-band (placeholder/migration cleanup)", " complete-milestone Mark a milestone complete out-of-band: complete-milestone <M> [--reason <txt>]", " feedback add File a self_feedback entry (--summary <txt> --severity low|medium|high|critical [--kind <k>] [--evidence <t>] [--suggested-fix <t>] [--milestone M --slice S --task T] [--impact-score N] [--effort-estimate N] [--blocking] [--purpose <fragment>])", " feedback list List self_feedback entries [--unresolved] [--severity <s>] [--purpose <fragment>] [--json]", " feedback resolve Resolve an entry: feedback resolve <id> --reason <txt> [--evidence-kind human-clear|agent-fix|...]", "", "new-milestone flags:", " --context <path> Path to spec/PRD file (use '-' for stdin)", " --context-text <txt> Inline specification text", " --autonomous Start autonomous mode after milestone creation", " --verbose Show tool calls in progress output", " --skip-pdd-check Bypass the ADR-0000 PDD-fields gate (migration escape hatch)", "", "Output formats:", " text Human-readable progress on stderr (default)", " json Collect events silently, emit one structured result on stdout at exit", " stream-json Stream JSONL events to stdout in real time (same as --json)", "", "Examples:", " sf headless Show this help", " sf headless autonomous Run autonomous mode through the machine surface", " sf headless next Run one unit", " sf headless --output-format json autonomous Structured JSON result on stdout", " sf headless --json status Machine-readable JSONL stream", " sf headless --timeout 60000 autonomous Run autonomous with 1-minute timeout", " sf headless --bare autonomous Minimal context (CI/ecosystem use)", " sf headless --resume abc123 autonomous Resume a prior session", " sf headless new-milestone --context spec.md Create milestone from file", " cat spec.md | sf headless new-milestone --context - From stdin", " sf headless new-milestone --context spec.md --autonomous Create + run autonomously", " sf headless --supervised autonomous Supervised orchestrator mode", " sf headless --answers answers.json autonomous With pre-supplied answers", " sf headless --events agent_end,extension_ui_request autonomous Filtered event stream", " sf headless query Instant machine JSON state snapshot", " sf headless status uok UOK gate health table (last 24h)", " sf headless status uok --json UOK gate health as JSON", " sf headless triage --list Self-feedback queue digest (impact↓ effort↑ ts↑)", " sf headless triage | sf-some-model Pipe triage prompt to any model", " sf headless triage --run Dispatch triage to default model + write decisions", " sf headless triage --apply Apply via triage-decider, then gate with review-code", " sf headless reflect Render reflection prompt for piping", " sf headless reflect --run Dispatch reflection + write report", " sf headless complete-slice M010/S03 Flip M010/S03 to status=complete (idempotent)", " sf headless skip-slice M003/S01 --reason \"migration placeholder\" Mark placeholder slice skipped", " sf headless complete-milestone M010 Flip milestone to status=complete", " sf headless feedback add --severity high --summary \"30K truncate drops the why\" File self-feedback", " sf headless feedback add --summary \"...\" --purpose \"M015 vision: ...\" Anchor to a purpose (ADR-0000)", " sf headless feedback list --unresolved Pending self-feedback entries", " sf headless feedback list --purpose \"M015 vision\" Triage by purpose anchor", " sf headless feedback resolve sf-mp4xxx --reason \"shipped in 7b85a6\" Resolve an entry", "", "Exit codes: 0 = success, 1 = error/timeout, 10 = blocked, 11 = cancelled", ].join("\n"), }; // Alias: `sf wt --help` → same as `sf worktree --help` SUBCOMMAND_HELP["wt"] = SUBCOMMAND_HELP["worktree"]; export function printHelp(version: string): void { process.stdout.write(`SF v${version} — Singularity Forge\n\n`); process.stdout.write("Usage: sf [options] [message...]\n\n"); process.stdout.write("Options:\n"); process.stdout.write( " --mode <text|json|rpc> Session I/O mode: text/json print format or RPC protocol\n", ); process.stdout.write(" --print, -p Single-shot print mode\n"); process.stdout.write( " --continue, -c Resume the most recent session\n", ); process.stdout.write( " --worktree, -w [name] Start in an isolated worktree (auto-named if omitted)\n", ); process.stdout.write( " --model <id> Override model (e.g. provider/model-id)\n", ); process.stdout.write( " --no-session Disable session persistence\n", ); process.stdout.write( " --extension <path> Load additional extension\n", ); process.stdout.write(" --tools <a,b,c> Restrict available tools\n"); process.stdout.write( " --list-models [search] List available models and exit\n", ); process.stdout.write( " --discover Force live verification for filtered --list-models\n", ); process.stdout.write(" --version, -v Print version and exit\n"); process.stdout.write(" --help, -h Print this help and exit\n"); process.stdout.write("\nSubcommands:\n"); process.stdout.write(" config Re-run the setup wizard\n"); process.stdout.write( " install <source> Install a package/extension source\n", ); process.stdout.write( " remove <source> Remove an installed package source\n", ); process.stdout.write( " list List installed package sources\n", ); process.stdout.write( " update Update SF to the latest version\n", ); process.stdout.write( " sessions List and resume a past session\n", ); process.stdout.write(" logs tail|follow Follow merged SF logs\n"); process.stdout.write( " status --live Show aggregate project status\n", ); process.stdout.write(" dash Alias for status --live\n"); process.stdout.write(" stats models Summarize model outcomes\n"); process.stdout.write( " worktree <cmd> Manage worktrees (list, merge, clean, remove)\n", ); process.stdout.write( " autonomous [args] Run autonomous mode through the machine surface (pipeable)\n", ); process.stdout.write( " headless [cmd] [args] Machine surface for direct SF commands\n", ); process.stdout.write( " graph <subcommand> Manage knowledge graph (build, query, status, diff)\n", ); process.stdout.write( " plan <cmd> Manage SF planning artifacts and docs exports\n", ); process.stdout.write( " schedule <cmd> Manage time-bound reminders (add, list, done, cancel, snooze, run)\n", ); process.stdout.write( " key <set|get|remove|list> Manage provider API keys in auth.json\n", ); process.stdout.write( "\nRun sf <subcommand> --help for subcommand-specific help.\n", ); } export function printSubcommandHelp( subcommand: string, version: string, ): boolean { const help = SUBCOMMAND_HELP[subcommand]; if (!help) return false; process.stdout.write(`SF v${version} — Singularity Forge\n\n`); process.stdout.write(help + "\n"); return true; }