cliHelpText function

String cliHelpText(
  1. String version
)

The full --help output printed by the fa executable. version is threaded through from the executable (_version in bin/fah.dart) — the single source of truth shared with --version — and rendered in the header line.

Implementation

String cliHelpText(String version) =>
    '''
fa — flutter_agent_harness CLI agent v$version

USAGE
  fa [options]                          interactive REPL
  fa [options] "fix the tests"          headless: run one prompt and exit
  fa [options] -p "fix the tests"       headless, prompt used verbatim
  fa [options] notes.md "summarize"     headless, existing file as prompt source

INVOCATION
  Interactive REPL: no prompt arguments. Type a message to run the agent;
  /commands manage the session (see REPL COMMANDS below). While a run is
  streaming, typed input steers the agent; Ctrl-C aborts the run, Ctrl-C
  while idle exits.

  Headless mode: a prompt via positional arguments (joined with spaces) or
  -p/--prompt runs a single non-interactive prompt and exits. The response
  streams to stdout; tool indicators and notices go to stderr (stdout stays
  pipeable). Nothing is ever prompted interactively — approval/ask prompts
  are denied per the non-interactive rule. The session persists like a
  normal REPL turn (including auto-compaction). Exit codes: 0 ok,
  1 provider error, 130 aborted (Ctrl-C).

  File-as-prompt: a first positional naming an EXISTING file is the prompt
  source — text files (.md, .markdown, .txt) are inlined as the prompt, any
  other file is attached as a path reference the agent can open with its
  tools; trailing text appends as the instruction. A path that does not
  exist is treated as plain prompt text. -p text is always used verbatim
  (never a file).

OPTIONS
  -p, --prompt <text>          Run a single headless prompt and exit
  --model <id>                 Model id (default per provider, see PROVIDERS)
  --provider <kind>            openai-completions | anthropic | google
                               (default: openai-completions, via OpenRouter)
  --base-url <url>             Override the provider API base URL
  --mode <name>                Initial mode: code | architect | review
  --system-prompt <text>       Override the system prompt for this run
                               (verbatim; beats the config prompts: section
                               and the built-in mode prompts)
  --system-prompt-file <path>  Same, but read from a Markdown file
                               (frontmatter stripped; ~ expanded; relative to
                               the current directory). Cannot be combined
                               with --system-prompt.
  --vision-model <id>          Enable the inspect_image tool with this vision
                               model (e.g. gpt-4o, openai/gpt-4o)
  --vision-base-url <url>      Override the vision provider base URL
  --transcribe-model <id>      Enable the transcribe_audio tool with this
                               transcription model (default: whisper-1)
  --transcribe-base-url <url>  Override the transcription endpoint base URL
  --plugin <name>              Enable a built-in plugin (repeatable):
                               inspect_image, transcribe_audio
  --prompt-template-dir <path> Add a prompt template directory (repeatable)
  --cwd <dir>                  Working directory (default: current directory)
  --session-root <dir>         Session storage root (default: ~/.fah/sessions)
  --session <name>             Resume or create a named session for this cwd
  --help, -h                   Show this help
  --version                    Print the version

QUICK COMMANDS
  update                       Download the latest release binary and swap
                               it in (pub-global installs re-activate)
  uninstall                    Remove the binary and its PATH entry after a
                               y/N confirmation; ~/.fah (sessions, config)
                               is kept unless a second confirmation says yes

PROVIDERS AND API KEYS
  openai-completions (default)
      Key: OPENROUTER_API_KEY (fallback OPENAI_API_KEY)
      Default model: anthropic/claude-sonnet-4 @ https://openrouter.ai/api/v1
  anthropic
      Key: ANTHROPIC_API_KEY
      Default model: claude-sonnet-4-5 @ https://api.anthropic.com
  google
      Key: GOOGLE_API_KEY
      Default model: gemini-2.5-pro @
      https://generativelanguage.googleapis.com/v1beta

  Custom endpoints: --provider openai-completions --base-url <url> talks to
  any OpenAI-compatible server — a local Ollama (http://localhost:11434/v1),
  Ollama Cloud (https://ollama.com/v1), vLLM, etc. Pick the model with
  --model. The API key is optional there: local servers (llama.cpp, Ollama,
  LM Studio) need none, and no Authorization header is sent without one.

  In the REPL, /provider [name] [baseUrl] [token] switches the provider and
  endpoint live (openrouter, openai, anthropic, google, or a saved custom
  provider by name): without a token the key resolves per below; an explicit
  token is persisted in the OS secure store when one is available — under an
  endpoint-scoped name (FA_KEY_<HOST>, the same scheme custom providers
  use), never the shared env name, so a key written for one endpoint cannot
  be picked up by another. /provider custom starts a guided setup: pick the
  api type from a menu (openai-like / anthropic-like / google-like), enter
  the base URL (Enter applies the shown default), optionally a key, then the
  model — from the endpoint's /models list when it has one, typed manually
  otherwise. The provider is saved (customProviders: in ~/.fah/config.yaml)
  and listed first in the /provider picker, remembering its last-used
  model; /provider-edit re-runs the wizard for the active provider.

  Secure key storage: keys resolve in order — a genuine environment value
  of the catalog env names, then the endpoint-scoped secure-store entry
  (FA_KEY_<HOST>, or FA_KEY_<HOST>_<NAME> for a saved custom provider —
  several accounts on the same endpoint keep separate keys), then legacy
  env-name store entries written by older versions. The store is the macOS
  Keychain, Secret Service on Linux (secret-tool/libsecret; unavailable on
  headless hosts), or the Windows Credential Locker. /key shows per key
  where the value comes from (never the values), /key set <NAME> <value>
  stores, /key delete <NAME> removes. Rotation stacks (NAME_2, ...) stay
  env-only.

  Vision: VISION_API_KEY for --vision-model (defaults to the main key).
  Transcription: TRANSCRIBE_API_KEY for --transcribe-model (defaults to the
  main key).
  Web search: keyless DuckDuckGo works out of the box; BRAVE_API_KEY and
  TAVILY_API_KEY add those providers to the search chain.
  Key rotation: stack numbered suffixes on any key (OPENROUTER_API_KEY_2,
  OPENROUTER_API_KEY_3, ...) — the model-roles resolver rotates them on
  rate limits.

MODEL ROLES (~/.fah/config.yaml)
  The optional roles: section pins intent-based roles (default, smol, slow,
  plan) to ordered fallback chains. On 429/quota the run rotates stacked
  keys for free, retries the entry with backoff, then fails over to the next
  chain entry — every step announced, never silent. The smol role backs
  compaction summaries; modelOverrides: scopes chains to path prefixes;
  retry: tunes the backoff policy. Example:

    roles:
      default:
        - openrouter/anthropic/claude-sonnet-4
        - provider: openai
          model: gpt-4o
          apiKeyName: OPENAI_API_KEY   # optional; also baseUrl,
                                       # contextWindow, maxTokens
      smol:
        - openrouter/openai/gpt-4o-mini
    modelOverrides:
      - path: ~/work/acme
        roles:
          plan:
            - anthropic/claude-opus-4-5
    retry:
      retriesPerEntry: 2               # + baseDelayMs, maxBackoffMs,
                                       #   maxWaitMs, keyBackoffMs

  With no roles: section the CLI runs the single --provider/--model pair.
  /model lists the resolved roles and chains.

MODELS CONFIG (~/.fah/config.yaml)
  The optional models: section carries per-slot media model overrides (the
  same slot schema the Flutter app uses — a slot without an override falls
  back to the main connection) and named custom model definitions:

    models:
      slots:                       # imageGeneration, audioTts,
                                   # musicGeneration, videoGeneration,
                                   # vision, transcription
        vision:
          providerKind: openai-completions
          baseUrl: https://api.openai.com/v1
          modelId: gpt-4o
          apiKeyName: OPENAI_API_KEY   # optional; key NAME, never the value
      custom:                      # /model <name> switch targets
        fast:
          provider: openai             # catalog provider name
          baseUrl: https://api.openai.com/v1
          model: gpt-4o-mini
          contextWindow: 128000        # optional; also maxTokens, input

  In the REPL, /models config shows the effective configuration,
  /models set <slot> <model> [baseUrl] pins a media slot (the base URL
  defaults to the main connection's endpoint), /models remove <slot>
  returns a slot to the main connection — both persisted into the models:
  section. /model <custom-name> switches provider, endpoint, and model in
  one step using a models.custom definition.

PROMPTS
  Modes: the system prompt comes from the active mode — code (default),
  architect, review. Select with --mode or switch live with /mode, /code,
  /architect, /review.

  Prompt templates: Markdown files in .fah/prompts/, ~/.fah/prompts/, and
  every --prompt-template-dir become /name commands; /name args expand \$1,
  \$@, \$ARGUMENTS, \${1:-default}, \${@:2} in the template body.

  Prompt overrides: the prompts: section of ~/.fah/config.yaml replaces
  built-in prompts by name. A value is a FILE when it starts with /, ~/,
  ./, ../ or ends in .md/.markdown/.txt (~ expands, relative paths resolve
  against --cwd, frontmatter stripped, missing files are a hard error);
  anything else is inline text. Names:

    system                    alias for cli/mode_code
    cli/mode_code             base CLI system prompt (default mode)
    cli/mode_architect        architect mode system prompt
    cli/mode_review           review mode system prompt
    compaction/summary_system system prompt of the summarization call
    compaction/summary        first-summary instructions
    compaction/summary_update summary-update instructions
    compaction/turn_prefix    split-turn prefix instructions

  Example:

    prompts:
      system: ~/prompts/my_system.md
      cli/mode_review: "You are a terse reviewer."
      compaction/summary: ./prompts/summary.md

  Resolution order: --system-prompt[-file] flag > config prompts: override
  > built-in prompt. Mode prompts may use {{cwd}} (substituted with the
  working directory) — overrides too.

APPROVALS
  Tool calls are gated by capability tier (read < write < exec). Modes:
    always-ask   prompt for every write/exec call
    write        auto-approve read+write, prompt for exec
    yolo         auto-approve everything (default) — except critical bash
                 patterns (e.g. rm -rf /, force pushes), which still prompt
  /approval [mode] shows or sets the mode (persisted); /allow <tool>
  always-allows one tool (persisted). Prompt answers: y = once, n = deny,
  a = always for that tool. Non-interactive runs (headless or piped stdin)
  cannot prompt: prompt-policy calls are denied with a reason.

SESSIONS AND COMPACTION
  Every run (REPL or headless) appends to a JSONL session under the session
  root (--session-root, default ~/.fah/sessions), laid out per working
  directory. Start with --session <name> or use /session, /session-new,
  /rename-session, and /sessions to manage named sessions. /reset starts a
  fresh session; /stats shows token/cost totals.

  When the context nears the model's window the history is auto-compacted:
  older messages are summarized (via the smol role when configured) and
  replaced by the summary; /compact does it on demand. Compaction prompts
  are overridable (see PROMPTS).

  The checkpoint and rewind tools let the agent mark the session before an
  exploratory detour and later prune the transcript back to the mark,
  keeping a report of what it learned.

  TTSR stream rules abort a streaming run on regex matches in the model
  output, inject the rule body as a hidden reminder, and retry. Rules come
  from the ttsr: section of ~/.fah/config.yaml and from .fah/rules.yaml in
  the project (project rules win name clashes):

    ttsr:
      enabled: true                    # contextMode, repeatMode, repeatGap,
                                       # maxInjectionsPerTurn, retryDelayMs
      rules:
        - name: no-console-log
          pattern: "console\\\\.log\\\\("
          body: Do not use console.log; use the project logger.

TOOLS
  read             read files (line ranges, hashline tags, zip/tar members,
                   SQLite db:table targets)
  write            write whole files
  edit             exact-match edits or hashline patches
  ls               list directories
  bash             run shell commands (exec tier; critical patterns prompt)
  web_search       search the web (DuckDuckGo keyless; Brave/Tavily keyed)
  web_fetch        fetch a page rendered as Markdown
  lsp              diagnostics/definition/references/rename via language
                   servers (project server map: .fah/lsp.json)
  task             spawn subagents — parallel batches, or background jobs
                   (background: true) whose results re-enter the conversation
                   as async-result messages; monitor with /tasks
  ask              ask the user structured questions mid-run
  checkpoint       mark the session for a later rewind
  rewind           prune the transcript back to a checkpoint
  inspect_image    analyze images (via --vision-model or the plugin)
  transcribe_audio transcribe audio (via --transcribe-model or the plugin)

PLUGINS
  --plugin <name> enables a built-in plugin (repeatable): inspect_image,
  transcribe_audio. The project file .fah/packages.yaml enables and
  configures plugins per project (same built-in names).

REPL COMMANDS
  /exit              quit
  /reset             start a new session
  /compact           summarize history to free context
  /stats             show token and cost totals
  /tasks [cancel <id>] list (or cancel) background agents
  /skills            list discovered skills; /skill:<name> [args] invokes one

SKILLS AND CONTEXT FILES
  Skills are SKILL.md files with YAML frontmatter (name, description),
  discovered from .fah/skills and .agents/skills under the project and the
  home directory (project wins name clashes). Only metadata enters the
  system prompt — the agent loads the body with the read tool when the
  task matches (progressive disclosure). AGENTS.md, CLAUDE.md, GOAL.md and
  DESIGN.md found from the working directory up to the git root (plus
  ~/.fah/AGENTS.md) are merged into the system prompt, closest last, with
  a 32 KiB leaf-first budget.
  /model [id|?|N]    show model/roles, pick from known models, or switch
                     (a models.custom definition name switches provider,
                     endpoint, and model in one step)
  /models [filter]   list known models for the current provider;
  /models config     show the effective models: configuration (media slot
                     overrides + custom model definitions)
  /models set <slot> <model> [baseUrl]
                     pin a media slot to a model (persisted); the base URL
                     defaults to the main connection's endpoint
  /models remove <slot>
                     drop a media slot override (persisted; the slot falls
                     back to the main connection)
  /model-edit [contextWindow|maxTokens <n>]
                     show or override the active model's token limits for
                     this session (an endpoint-reported window from /models
                     wins over the 200k catalog default; persist per chain
                     via roles yaml contextWindow:/maxTokens:)
  /provider [name] [baseUrl] [token] | custom
                     show or switch the provider/endpoint (token optional,
                     saved to the OS secure store when available); custom is
                     a guided setup that saves the provider (api type, url,
                     key, model)
  /provider-edit     edit the active provider via the guided setup
  /key [set|delete]  manage API keys in the OS secure store
  /mode [name]       show or switch the active mode
  /session [name]    show current or switch/create a named session
  /session-new <n>   create a new named session
  /sessions          list named sessions for the current directory
  /resume            switch to the most recent session
  /rename-session <n> rename the current session
  /approval [mode]   show or set tool approval (always-ask|write|yolo)
  /allow [tool]      always-allow a tool (or list them)
  /code              switch to coding mode
  /architect         switch to architect mode
  /review            switch to review mode
  /help              in-REPL command summary
  !<command>         run a shell command directly
  /<template> args   expand a prompt template (see PROMPTS)
  While a run is streaming, typed input steers the agent; Ctrl-C aborts.

CONFIGURATION FILES
  ~/.fah/config.yaml   user preferences: provider, model, baseUrl, mode,
                       approvalMode, allowedTools, plus the prompts:, roles:,
                       modelOverrides:, retry:, ttsr:, and models: sections.
                       Invalid roles/ttsr/prompts/models sections fail
                       loudly at startup.
  .fah/packages.yaml   project plugin configuration
  .fah/rules.yaml      project TTSR stream rules
  .fah/lsp.json        project LSP server map
  .fah/prompts/        project prompt templates (~/.fah/prompts/ for user)
  ~/.fah/sessions/     session storage root
''';