clis:
  claude:
    promptArg: last-arg
    # `ay send` delivers the body as one bracketed paste (DECSET 2004) instead
    # of a keystroke burst this CLI would have to segment by arrival timing.
    # Without it the leading part of a long body is swallowed and the model sees
    # only a tail — nondeterministically, so no length rule makes a sender safe.
    # Measured before enabling: every claude agent log carries ESC[?2004h.
    bracketedPaste: true
    systemPrompt: --append-system-prompt
    # yesArgs: appended when `-y` / --yes is passed. The per-CLI "yolo" flag.
    yesArgs:
      - --dangerously-skip-permissions
    install:
      powershell: 'powershell -Command "irm https://claude.ai/install.ps1 | iex"'
      bash: "curl -fsSL https://claude.ai/install.sh | bash"
      npm: "npm i -g @anthropic-ai/claude-code@latest"
    ready:
      - '\? for shortcuts'
      - ' Try "'
      - '^\? for shortcuts'
      - "^>[  ]"
      - "──────────+"
    working:
      - esc to interrupt
      - to run in background
    # Wedge watchdog: a frozen state that repaints no `working` marker at all
    # (observed: claude wedged mid-"Compacting conversation…" at 0% CPU for
    # days — screen matched neither ready, working nor needsInput). After this
    # many seconds of total PTY silence in that no-marker state, recover via
    # the same Esc → force-restart ladder as the spinner stall watchdog.
    # Only enabled for CLIs whose ready markers reliably identify "parked at
    # prompt"; 0/absent disables.
    wedgeTimeoutSecs: 1800
    # needsInput: the agent is blocked on an interactive selection menu it did NOT
    # auto-resolve — an AskUserQuestion / a permission prompt whose options aren't
    # the auto-`enter`ed "Yes". The cursor `❯` sits on a numbered option. Surfaced
    # by `ay ls` / `ay status` as the `needs_input` state (distinct from idle/done)
    # so an orchestrator can tell "waiting for me to answer" from "finished".
    needsInput:
      - pattern: '❯ ?\d+\.'
        flags: m
    typingRespond:
      "1\n":
        - '│ Do you want to use this API key\?'
      # Trust dialog, current layout: un-numbered, and the cursor starts on
      # "No, exit" (Enter alone would quit the CLI). Press Down so the cursor
      # lands on "Yes, I trust this folder"; the matching `enter` pattern below
      # then confirms it once the screen has moved. Single-line on purpose —
      # the TS runtime matches typingRespond per rendered line.
      "\u001b[B":
        - pattern: '❯ ?No, exit'
          flags: m
    enter:
      # Trust dialog, legacy numbered layout (cursor already on Yes).
      - pattern: ' > 1\. Yes, I trust this folder'
        flags: m
      # Trust dialog, current layout — after the Down above moved the cursor.
      - pattern: '❯ ?Yes, I trust this folder'
        flags: m
      - pattern: '❯ ?1\. ?Dark mode ?✔'
        flags: m
      - pattern: '❯ ?1\. ?Auto \(match terminal\)'
        flags: m
      - pattern: '❯ ?1\. ?Yes'
        flags: m
      - pattern: '^.{0,4} ?1\. ?Dark mode ?✔'
        flags: m
      - pattern: '^.{0,4} ?1\. ?Auto \(match terminal\)'
        flags: m
      - pattern: '^.{0,4} ?1\. ?Yes'
        flags: m
      # Large-session resume menu on `claude --continue` ("Resume from summary
      # (recommended) / Resume full session / Don't ask again"). The default ❯
      # sits on option 1 (summary); auto-accept it so an unattended restart —
      # including the no-output watchdog's kill+--continue recovery — doesn't
      # re-wedge waiting on this prompt.
      - pattern: '❯ ?1\. ?Resume'
        flags: m
      - pattern: '^.{0,4} ?1\. ?Resume'
        flags: m
      - pattern: Press Enter to continue
        flags: m
    fatal:
      - "^error: unknown option"
    # Recoverable API errors: instead of exiting, agent-yes types "retry" with
    # exponential backoff (8s, 16, 32 … capped) for up to 8h (claude's usage
    # limit window is ~5h). Checked before `fatal`, so these never kill the run.
    autoRetry:
      - pattern: "API Error.*Overloaded"
        flags: i
      # The CLI's transient status toast is a line that is ONLY "Overloaded"
      # (optionally a leading bullet/mark). Whole-line anchored: a task title or
      # prose merely mentioning Overloaded must not arm a spurious retry —
      # rendered task lists / summaries sit on screen next to a ready prompt,
      # which is exactly the arm condition.
      - pattern: '^\s*[·•●⎿✗×]?\s*Overloaded\.?\s*$'
        flags: m
      # "API Error: Response stalled mid-stream. The response above may be
      # incomplete." — the streamed response wedged and the client aborted
      # mid-token. Transient + server-side like Overloaded, so retry with
      # backoff instead of dying. Anchored on the literal "API Error:" chrome
      # the CLI emits, so an agent merely *discussing* a stalled mid-stream
      # (docs, commits, this very feature's PR) can't self-trigger a retry —
      # only the real error banner does. `[\s\S]{0,40}` spans the prefix→phrase
      # gap across a line-wrap; `\s+`/`\s*` tolerate wraps inside the phrase.
      - pattern: 'API Error:[\s\S]{0,40}Response\s+stalled\s+mid-\s*stream'
        flags: i
      # "API Error: Connection closed mid-response. The response above may be
      # incomplete." — the HTTP/SSE connection dropped before the response
      # finished. Same class as the stalled-mid-stream case (transient, the
      # partial answer is already on screen), so retry with backoff instead of
      # wedging. Same "API Error:" chrome anchor keeps an agent merely
      # *discussing* a dropped connection from self-triggering a retry;
      # `[\s\S]{0,40}` spans the prefix→phrase gap across a line-wrap and
      # `\s+`/`\s*` tolerate wraps inside the phrase.
      - pattern: 'API Error:[\s\S]{0,40}Connection\s+closed\s+mid-\s*response'
        flags: i
      # "API Error: Stream idle timeout - no chunks received." — the streamed
      # response went idle (no chunks arrived within the client's window) and the
      # client gave up. Same transient/server-side class as the stalled/closed
      # cases above, so retry with backoff. Same "API Error:" chrome anchor keeps
      # an agent merely *discussing* it from self-triggering; `[\s\S]{0,40}` spans
      # the prefix→phrase gap across a line-wrap and `\s+` tolerates wraps.
      - pattern: 'API Error:[\s\S]{0,40}Stream\s+idle\s+timeout'
        flags: i
      # Any 5xx API error (e.g. "API Error: 529 Overloaded", "API Error: 503 …",
      # or a 529 rendered as a raw JSON error blob) is server-side + transient —
      # retry with backoff regardless of the wording. Anchored to "API Error" so
      # a stray "529"/"500" in normal output can't trigger a spurious retry.
      - pattern: 'API Error.{0,4}5\d\d'
        flags: i
      # 429 / rate-limit: anchored to the "API Error" chrome. A bare
      # `rate.?limit` here caused real spurious retries — an on-screen task
      # list ("client ignores rate-limit drop") or an agent's own summary
      # (INGEST_RATE_LIMIT_PER_MIN) armed the loop and typed "retry" into a
      # perfectly healthy session.
      - pattern: "API Error.{0,4}429"
        flags: i
      - pattern: 'API Error[\s\S]{0,60}rate.?limit'
        flags: i
      - Claude usage limit reached
      - hit your usage limit
      # Session/5-hour limit banners keep the distinctive "… limit reached"
      # phrase; a bare "session limit" matched ordinary prose.
      - pattern: "(session|5-hour) limit reached"
        flags: i
    restoreArgs:
      - --continue
    # resumeCommand (optional): a regex scraped from the agent's log by
    # `ay restart` to resume the exact session when a CLI prints a resume command.
    # Capture group 1 is the argument string (whitespace-split) appended to the
    # relaunched `agent-yes --cli=<cli> …`. The claude family has no printed resume
    # command — it resumes via restoreArgs (--continue) above — so it's left unset.
    # Example for a CLI that prints one:
    #   resumeCommand:
    #     pattern: 'To resume, run:.*?(--resume \S+)'
    #     flags: i
    restartWithoutContinueArg:
      - No conversation found to continue
    exitCommands:
      - /exit
    bunx: true
    defaultArgs: []

  # GLM (Z.AI) — runs the `claude` binary against Z.AI's Anthropic-compatible
  # endpoint, so it inherits every claude marker (ready/working/enter/autoRetry…)
  # while talking to GLM models. Set ZAI_API_KEY (from
  # https://z.ai/manage-apikey/apikey-list) before launching. Override the model
  # via ~/.claude/settings.json (ANTHROPIC_DEFAULT_*_MODEL) or by exporting the
  # same env vars; unset → Z.AI's default (GLM-4.7). See
  # https://docs.z.ai/devpack/tool/claude
  glm:
    binary: claude
    # ${VAR} entries expand against the launching environment at spawn time; an
    # entry whose variable is unset is skipped (so it can't blank out an
    # inherited value). Static values are passed through verbatim.
    env:
      ANTHROPIC_BASE_URL: https://api.z.ai/api/anthropic
      ANTHROPIC_AUTH_TOKEN: ${ZAI_API_KEY}
      API_TIMEOUT_MS: "3000000"
    promptArg: last-arg
    systemPrompt: --append-system-prompt
    yesArgs:
      - --dangerously-skip-permissions
    install:
      powershell: 'powershell -Command "irm https://claude.ai/install.ps1 | iex"'
      bash: "curl -fsSL https://claude.ai/install.sh | bash"
      npm: "npm i -g @anthropic-ai/claude-code@latest"
    ready:
      - '\? for shortcuts'
      - ' Try "'
      - '^\? for shortcuts'
      - "^>[  ]"
      - "──────────+"
    working:
      - esc to interrupt
      - to run in background
    # Same wedge watchdog as claude (this CLI runs the claude binary) — see
    # the claude section for rationale.
    wedgeTimeoutSecs: 1800
    needsInput:
      - pattern: '❯ ?\d+\.'
        flags: m
    typingRespond:
      "1\n":
        - '│ Do you want to use this API key\?'
      # Trust dialog, current layout: un-numbered, and the cursor starts on
      # "No, exit" (Enter alone would quit the CLI). Press Down so the cursor
      # lands on "Yes, I trust this folder"; the matching `enter` pattern below
      # then confirms it once the screen has moved. Single-line on purpose —
      # the TS runtime matches typingRespond per rendered line.
      "\u001b[B":
        - pattern: '❯ ?No, exit'
          flags: m
    enter:
      # Trust dialog, legacy numbered layout (cursor already on Yes).
      - pattern: ' > 1\. Yes, I trust this folder'
        flags: m
      # Trust dialog, current layout — after the Down above moved the cursor.
      - pattern: '❯ ?Yes, I trust this folder'
        flags: m
      - pattern: '❯ ?1\. ?Dark mode ?✔'
        flags: m
      - pattern: '❯ ?1\. ?Auto \(match terminal\)'
        flags: m
      - pattern: '❯ ?1\. ?Yes'
        flags: m
      - pattern: '^.{0,4} ?1\. ?Dark mode ?✔'
        flags: m
      - pattern: '^.{0,4} ?1\. ?Auto \(match terminal\)'
        flags: m
      - pattern: '^.{0,4} ?1\. ?Yes'
        flags: m
      # Large-session resume menu on `claude --continue` ("Resume from summary
      # (recommended) / Resume full session / Don't ask again"). The default ❯
      # sits on option 1 (summary); auto-accept it so an unattended restart —
      # including the no-output watchdog's kill+--continue recovery — doesn't
      # re-wedge waiting on this prompt.
      - pattern: '❯ ?1\. ?Resume'
        flags: m
      - pattern: '^.{0,4} ?1\. ?Resume'
        flags: m
      - pattern: Press Enter to continue
        flags: m
    fatal:
      - "^error: unknown option"
    autoRetry:
      - pattern: "API Error.*Overloaded"
        flags: i
      # Whole-line-only toast match — see the claude group for why a bare
      # `Overloaded` substring is dangerous (task lists / prose arm retries).
      - pattern: '^\s*[·•●⎿✗×]?\s*Overloaded\.?\s*$'
        flags: m
      - pattern: 'API Error.{0,4}5\d\d'
        flags: i
      # 429 / rate-limit: anchored to the "API Error" chrome. A bare
      # `rate.?limit` here caused real spurious retries — an on-screen task
      # list ("client ignores rate-limit drop") or an agent's own summary
      # (INGEST_RATE_LIMIT_PER_MIN) armed the loop and typed "retry" into a
      # perfectly healthy session.
      - pattern: "API Error.{0,4}429"
        flags: i
      - pattern: 'API Error[\s\S]{0,60}rate.?limit'
        flags: i
      - Claude usage limit reached
      - hit your usage limit
      # Session/5-hour limit banners keep the distinctive "… limit reached"
      # phrase; a bare "session limit" matched ordinary prose.
      - pattern: "(session|5-hour) limit reached"
        flags: i
    restoreArgs:
      - --continue
    # resumeCommand (optional): a regex scraped from the agent's log by
    # `ay restart` to resume the exact session when a CLI prints a resume command.
    # Capture group 1 is the argument string (whitespace-split) appended to the
    # relaunched `agent-yes --cli=<cli> …`. The claude family has no printed resume
    # command — it resumes via restoreArgs (--continue) above — so it's left unset.
    # Example for a CLI that prints one:
    #   resumeCommand:
    #     pattern: 'To resume, run:.*?(--resume \S+)'
    #     flags: i
    restartWithoutContinueArg:
      - No conversation found to continue
    exitCommands:
      - /exit
    bunx: true
    defaultArgs: []

  # Pi — minimal multi-provider coding agent (https://github.com/earendil-works/pi).
  # Interactive TUI: a bordered input box with a footer status line showing the
  # context-usage gauge (e.g. "0.0%/272k") + model. Pi has NO tool-permission
  # popups by design (run it in a container), so there's no "yolo" flag to map
  # `-y` to — yesArgs is intentionally omitted. Pass the provider/model via
  # PI_* env vars or --provider/--model; default provider is google.
  pi:
    binary: pi
    promptArg: last-arg
    systemPrompt: --append-system-prompt
    help: https://github.com/earendil-works/pi
    install:
      npm: "npm install -g --ignore-scripts @earendil-works/pi-coding-agent"
    # The footer context-usage gauge is re-rendered every time pi returns to the
    # prompt, so it doubles as the "ready for input" marker. Idle detection also
    # requires output quiescence, so a streaming response (gauge still updating)
    # is not mis-read as idle.
    ready:
      - '%/\d+k'
      - clear/exit
      - "──────────+"
    fatal:
      - "^error: unknown option"
    restoreArgs:
      - --continue
    exitCommands:
      - /quit
    defaultArgs: []

  # Antigravity CLI (`agy`) — Google's Gemini-backed terminal coding agent
  # (https://antigravity.google/docs/cli/). Its TUI is claude-shaped: a `>`
  # input box between two rules, `? for shortcuts` in the footer while parked at
  # the prompt, `esc to cancel` while generating, a first-launch "Do you trust
  # the contents of this project?" dialog with the cursor `>` already on "Yes",
  # and numbered tool-permission menus with the cursor on "1. Yes, run command".
  # All markers below were read off agy 1.2.4's PTY output.
  agy:
    binary: agy
    # A bare positional is NOT a prompt and `--prompt` is print mode (runs once,
    # non-interactive); `-i` (--prompt-interactive) runs the prompt and then
    # stays in the session, which is what an `ay send`-able agent needs.
    promptArg: -i
    # agy's PTY output carries ESC[?2004h, so `ay send` can deliver one framed
    # paste instead of a keystroke burst.
    bracketedPaste: true
    yesArgs:
      - --dangerously-skip-permissions
    help: https://antigravity.google/docs/cli/
    install:
      powershell: 'powershell -Command "irm https://antigravity.google/cli/install.ps1 | iex"'
      bash: "curl -fsSL https://antigravity.google/cli/install.sh | bash"
      windows: "winget install Google.AntigravityCLI"
    ready:
      - '\? for shortcuts'
      - "^>[  ]"
      - "──────────+"
    working:
      - esc to cancel
      - 'Generating\.\.\.'
      - 'Running command\.\.\.'
    wedgeTimeoutSecs: 1800
    # A permission/selection menu the `enter` list below did NOT auto-resolve —
    # the cursor `>` sits on a numbered option other than the auto-accepted "1.".
    needsInput:
      - pattern: '^ {0,4}> ?\d+\.'
        flags: m
    enter:
      # First-launch trust dialog; cursor starts on "Yes, I trust this folder".
      - pattern: '^ {0,4}> ?Yes, I trust this folder'
        flags: m
      # Tool permission menu ("1. Yes, run command" / "1. Yes, ..."); the
      # cursor starts on option 1.
      - pattern: '^ {0,4}> ?1\. ?Yes'
        flags: m
      - pattern: Press Enter to continue
        flags: m
    fatal:
      - "^flag provided but not defined"
    restoreArgs:
      - --continue
    exitCommands:
      - /exit
    defaultArgs: []

  codex:
    promptArg: first-arg
    # Verified the same way as claude's — codex's PTY log carries ESC[?2004h,
    # so it honours a framed paste. That is the claim being made here; codex was
    # NOT observed splitting an unframed burst the way claude does. Sending one
    # message as one paste is right either way. See ts/bracketedPaste.ts.
    bracketedPaste: true
    # yesArgs: `-y` / --yes maps to codex's own "yolo" flag. Codex rejects
    # claude's --dangerously-skip-permissions outright, and its bwrap sandbox
    # cannot initialize inside an already-sandboxed/containerized environment
    # ("bwrap: Failed to make / slave: Permission denied"), blocking command
    # exec AND apply_patch before they run. --dangerously-bypass-approvals-and
    # -sandbox skips both the approval prompts and the sandbox wrapper, which is
    # exactly codex's documented mode for "environments that are externally
    # sandboxed" (the agent-yes-in-a-container case). Invoke as `ay -y codex
    # "<task>"` — agent-yes's own `-y` must precede the cli name (everything
    # after it is forwarded verbatim to codex via trailing_var_arg).
    yesArgs:
      - --dangerously-bypass-approvals-and-sandbox
    install:
      npm: "npm install -g @openai/codex@latest"
    updateAvailable:
      - "^✨⬆️ Update available!"
    ready:
      - ⏎ send
      - "› "
      - '\? for shortcuts'
    working:
      # codex shows "• Working (… • esc to interrupt)" while streaming/running.
      # (Approval dialogs say "esc to cancel", so this only marks real work.)
      - esc to interrupt
    # needsInput: a codex selection menu the agent is blocked on (cursor `›`/`>` on
    # a numbered option). The bare `› ` idle prompt has no digit so it won't match.
    needsInput:
      - pattern: '[›>] ?\d+\.'
        flags: m
    enter:
      # codex highlights the selected option with "›" (U+203A), NOT ASCII ">".
      # Match the affirmative first option (Yes / Approve / Allow) when it is the
      # highlighted choice. The "[›>]" class keeps matching the old ASCII bullet
      # too, in case an older/newer codex reverts the glyph.
      - pattern: '[›>] ?1\. ?Yes'
        flags: m
      - pattern: '[›>] ?1\. ?Approve'
        flags: m
      - pattern: '[›>] ?1\. ?Allow'
        flags: m
    fatal:
      - "Error: The cursor position could not be read within"
    defaultArgs:
      - --search
    noEOL: true

  # codex-ds — the `codex` binary pointed at DeepSeek V4 Pro over OpenRouter
  # instead of the built-in OpenAI models. Everything the provider needs rides
  # in `defaultArgs` as `-c` overrides, so this works against a stock codex
  # install with no `~/.codex/*.config.toml` of your own; the only external
  # requirement is `OPENROUTER_API_KEY` in the environment (same shape as glm's
  # ZAI_API_KEY). Get one at https://openrouter.ai/keys.
  #
  # Two codex-version constraints are baked into the override:
  #   - `wire_api` must be "responses"; codex >= 0.147 removed the "chat"
  #     wire protocol, and OpenRouter serves POST /api/v1/responses.
  #   - the provider table is passed inline rather than as a named profile,
  #     because codex >= 0.147 rejects a `[profiles.X]` table in config.toml
  #     and wants it in a separate `<X>.config.toml`.
  #
  # All drive markers below are copied from the `codex` block — same binary,
  # same TUI — because per-CLI config does not inherit.
  codex-ds:
    binary: codex
    promptArg: first-arg
    defaultArgs:
      - -c
      - model_providers.openrouter={name="OpenRouter",base_url="https://openrouter.ai/api/v1",env_key="OPENROUTER_API_KEY",wire_api="responses"}
      - -c
      - model_provider="openrouter"
      - -c
      - model="deepseek/deepseek-v4-pro-0813"
      - --search
    yesArgs:
      - --dangerously-bypass-approvals-and-sandbox
    install:
      npm: "npm install -g @openai/codex@latest"
    help: https://openrouter.ai/deepseek/deepseek-v4-pro
    updateAvailable:
      - "^✨⬆️ Update available!"
    ready:
      - ⏎ send
      - "› "
      - '\? for shortcuts'
    working:
      - esc to interrupt
    needsInput:
      - pattern: '[›>] ?\d+\.'
        flags: m
    enter:
      - pattern: '[›>] ?1\. ?Yes'
        flags: m
      - pattern: '[›>] ?1\. ?Approve'
        flags: m
      - pattern: '[›>] ?1\. ?Allow'
        flags: m
    fatal:
      - "Error: The cursor position could not be read within"
    noEOL: true

  # codex-ds-direct — same idea as codex-ds, but pointed straight at DeepSeek's
  # own endpoint instead of going through OpenRouter. DeepSeek gained native
  # Responses API support in the 2026-08-13 V4 Pro update
  # (https://api-docs.deepseek.com/guides/responses_api/), so no translating
  # proxy is needed on this path either — one less hop and no reseller margin.
  # Requires `DEEPSEEK_API_KEY`; get one at https://platform.deepseek.com.
  #
  # Model id differs from the OpenRouter route: DeepSeek's own /models lists
  # exactly `deepseek-v4-flash` and `deepseek-v4-pro` — there are no dated
  # snapshot ids here, so this uses the plain `deepseek-v4-pro`.
  #
  # Drive markers are copied from the `codex` block, as with codex-ds.
  codex-ds-direct:
    binary: codex
    promptArg: first-arg
    defaultArgs:
      - -c
      - model_providers.deepseek={name="DeepSeek",base_url="https://api.deepseek.com",env_key="DEEPSEEK_API_KEY",wire_api="responses"}
      - -c
      - model_provider="deepseek"
      - -c
      - model="deepseek-v4-pro"
      - --search
    yesArgs:
      - --dangerously-bypass-approvals-and-sandbox
    install:
      npm: "npm install -g @openai/codex@latest"
    help: https://api-docs.deepseek.com/guides/responses_api/
    updateAvailable:
      - "^✨⬆️ Update available!"
    ready:
      - ⏎ send
      - "› "
      - '\? for shortcuts'
    working:
      - esc to interrupt
    needsInput:
      - pattern: '[›>] ?\d+\.'
        flags: m
    enter:
      - pattern: '[›>] ?1\. ?Yes'
        flags: m
      - pattern: '[›>] ?1\. ?Approve'
        flags: m
      - pattern: '[›>] ?1\. ?Allow'
        flags: m
    fatal:
      - "Error: The cursor position could not be read within"
    noEOL: true

  qwen:
    install:
      npm: "npm install -g @qwen-code/qwen-code@latest"
    version: qwen --version

  grok:
    install:
      npm: "npm install -g @vibe-kit/grok-cli@latest"
    ready:
      - "^  │ ❯ +"
    enter:
      - "^   1. Yes"

  copilot:
    promptArg: -i
    install:
      npm: "npm install -g @github/copilot"
    ready:
      - "^ +> "
      - Ctrl\+c Exit
    enter:
      - " │ ❯ +1. Yes, proceed"
      - " ❯ +1. Yes"
    system: "IMPORTANT: USE TOOLS TO RESEARCH/EXPLORE/WORKAROUND your self, except you need approve on DESTRUCTIVE OPERATIONS, DONT ASK QUESTIONS ON USERS REQUEST, JUST SOLVE IT."
    fatal: []

  cursor:
    install:
      bash: open https://cursor.com/ja/docs/cli/installation
    binary: cursor-agent
    bunx: true
    ready:
      - / commands
    enter:
      - '→ Run \(once\) \(y\) \(enter\)'
      - '▶ \[a\] Trust this workspace'
    fatal:
      - "Error: You've hit your usage limit"

  auggie:
    help: https://docs.augmentcode.com/cli/overview
    install:
      npm: "npm install -g @augmentcode/auggie"
    promptArg: first-arg
    ready:
      - " > "
      - '\? to show shortcuts'
    typingRespond:
      "y\n":
        - '\[Y\] Enable indexing - Unlock full workspace understanding'
    enter: []
    fatal: []

  amp:
    help: https://ampcode.com/
    install:
      bash: "curl -fsSL https://ampcode.com/install.sh | bash"
      npm: "npm i -g @sourcegraph/amp"
    enter:
      - "^.{0,4} Approve "

  opencode:
    help: https://opencode.ai/
    # opencode's positional arg is a PROJECT DIRECTORY, not a prompt — passing
    # one makes it try to `cd` into it and die. The prompt goes via --prompt,
    # which starts the TUI with the message already sent.
    promptArg: --prompt
    install:
      bash: "curl -fsSL https://opencode.ai/install | bash"
      npm: "npm i -g opencode-ai"
    enter:
      # Permission confirm dialog ("△ Always allow — This will allow the
      # following patterns until OpenCode is restarted"). Focus defaults to the
      # Confirm button and the footer advertises "enter confirm" (verified live
      # on OpenCode 1.18.15: a bare Enter accepted and the run resumed).
      # Anchored on the dialog chrome — the "Always allow" heading plus the
      # button row whose same line carries the "⇆ select  enter confirm"
      # footer — so prose merely mentioning Confirm/Cancel can't trigger it.
      # [\s\S]{0,4000} spans the heading→button gap across the wide TUI rows.
      - pattern: 'Always allow[\s\S]{0,4000}Confirm\s+Cancel[^\n]*⇆ select\s+enter confirm'
    # Any other OpenCode confirm dialog (one the enter list doesn't auto-accept)
    # still surfaces as needs_input instead of hanging silently.
    needsInput:
      - pattern: 'Confirm\s+Cancel[^\n]*⇆ select\s+enter confirm'
        flags: m
    ready: []

  # DeepSeek Harness (dsh-legacy) — the original DeepSeek Harness CLI
  # (https://github.com/deepseek-ai/deepseek-harness). `dsh --profile tui`
  # boots a persistent interactive session: the ready prompt is `❯ `, tool
  # approval is a numbered `1. yes / 2. no` menu auto-answered with `1`, and the
  # initial task rides as the positional arg. Reachable as `ay dsh-legacy …`.
  dsh-legacy:
    promptArg: last-arg
    defaultArgs:
      - --profile
      - tui
    install:
      npm: "npm install -g @deepseek-ai/dsh@latest"
    help: https://github.com/deepseek-ai/deepseek-harness
    ready:
      - "❯ "
    typingRespond:
      "1\n":
        - '1\. yes'
    working: []
    enter: []
    fatal: []

  # DeepSeek Harness TUI — the `❯` prompt line driveable as a plain interactive
  # agent (same shell pattern as `bash`: no spinner/menu markers, `promptArg:
  # typed` sends the initial command by typing it into the session once ready).
  dsh:
    binary: dsh-tui
    promptArg: typed
    ready:
      - pattern: "❯"
        flags: m
    working: []
    enter: []
    fatal: []
    exitCommands:
      - exit
  # Alias — `ay dsh-tui` launches the dsh-tui binary (same drive pattern as dsh).
  dsh-tui:
    binary: dsh-tui
    promptArg: typed
    ready:
      - pattern: "❯"
        flags: m
    working: []
    enter: []
    fatal: []
    exitCommands:
      - exit

  # Bare interactive shells. Unlike the agent CLIs above, these aren't AI tools
  # — they're a plain login shell you can drive from the console and launch
  # other agents inside (e.g. `ay bash 'claude'`). `promptArg: typed` means any
  # initial command is TYPED into the shell after its prompt is ready (see the
  # run loop), rather than passed as an argv — so `bash -c` never runs-and-exits
  # and the session stays interactive. No `working`/`enter` markers: a shell has
  # no spinner and no auto-acceptable menus. The `ready` regex anchors on the
  # prompt suffix so the typed command lands at the prompt instead of waiting on
  # the force-ready fallback; with no match it still force-readies after 10s.
  bash:
    promptArg: typed
    ready:
      - pattern: "[$#] $"
        flags: m
    working: []
    enter: []
    fatal: []
    exitCommands:
      - exit

  # Windows only (cmd.exe is absent on macOS/Linux, so `ay cmd` there just
  # reports command-not-found). Prompt looks like `C:\Users\me>`.
  cmd:
    promptArg: typed
    ready:
      - pattern: '^[A-Za-z]:\\.*>'
        flags: m
    working: []
    enter: []
    fatal: []
    exitCommands:
      - exit

  # Windows only (Windows PowerShell 5.1). Prompt looks like `PS C:\Users\me>`.
  powershell:
    promptArg: typed
    ready:
      - pattern: "^PS .*>"
        flags: m
    working: []
    enter: []
    fatal: []
    exitCommands:
      - exit
