{"id":51721972,"url":"https://github.com/0xgosu/herdr-auto-pilot","last_synced_at":"2026-07-20T10:00:39.869Z","repository":{"id":370580423,"uuid":"1295060983","full_name":"0xGosu/herdr-auto-pilot","owner":"0xGosu","description":"A Herdr plugin that will automatically prompt the running AI Coding CLI on-behalf of you via Herdr API. The plugin has training mode which learn from your actions and also has guards to prevent dangerous/malicious actions to be performed. Once it has been trained with enough interaction you can let it run on Full-Self Prompting mode (FSP)","archived":false,"fork":false,"pushed_at":"2026-07-17T13:18:50.000Z","size":2207,"stargazers_count":2,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-17T13:25:20.769Z","etag":null,"topics":["herdr-plugin"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/0xGosu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-09T10:25:12.000Z","updated_at":"2026-07-17T11:49:32.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/0xGosu/herdr-auto-pilot","commit_stats":null,"previous_names":["0xgosu/herdr-auto-pilot"],"tags_count":113,"template":false,"template_full_name":null,"purl":"pkg:github/0xGosu/herdr-auto-pilot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xGosu%2Fherdr-auto-pilot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xGosu%2Fherdr-auto-pilot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xGosu%2Fherdr-auto-pilot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xGosu%2Fherdr-auto-pilot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/0xGosu","download_url":"https://codeload.github.com/0xGosu/herdr-auto-pilot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xGosu%2Fherdr-auto-pilot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35682457,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"ssl_error","status_checked_at":"2026-07-20T02:08:09.736Z","response_time":111,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["herdr-plugin"],"created_at":"2026-07-17T13:00:28.737Z","updated_at":"2026-07-20T10:00:39.861Z","avatar_url":"https://github.com/0xGosu.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Herd Auto Prompter\n\n**Keep your [Herdr](https://herdr.dev) coding agents unblocked, hands-free.**\n\nHerd Auto Prompter is a Herdr plugin that watches every agent session in your\nherd, detects when an agent needs input — finished a step, waiting on an\napproval, stuck on a multiple-choice question, or stalled on an error — and\nautomatically supplies the next prompt or the correct response, *the way you\nwould*. It learns from your own past decisions in a supervised shadow mode,\ncan follow task lists you explicitly configure, and can optionally consult an\nLLM/agent CLI. Autonomous actions must clear the applicable confidence and\nsafety gates; uncertain ones escalate to you. Everything it does is audited\nand correctable.\n\n\u003e [!IMPORTANT]\n\u003e **Agent compatibility:** Herd Auto Prompter is developed and extensively\n\u003e tested against **Claude Code** (`claude`) and the **OpenAI Codex CLI**\n\u003e (`codex`). These are the primary supported coding-agent types.\n\u003e\n\u003e Other coding agents are best-effort and have not received the same level of\n\u003e integration and regression testing. Their status events, prompts, menus, and\n\u003e terminal UI formats may differ, so agents such as **OpenCode** (`opencode`),\n\u003e **Antigravity CLI** (`agy`), and other types may be misclassified or may not\n\u003e work reliably. Keep shadow mode and conservative confidence thresholds in\n\u003e place while evaluating an additional agent type, and review its audit trail\n\u003e before allowing autonomous actions.\n\n- **Learned rules, not guesses** — every action taken from a learned rule\n  traces back to your confirmed decisions. Explicit task sources and the\n  opt-in LLM helper are separate, clearly audited paths.\n- **Confidence-gated** — learned rules and optional LLM suggestions use their\n  own configured thresholds; below the applicable threshold they escalate.\n- **Safety first** — never-auto patterns (force-push, destructive ops,\n  deploys, credential changes, …), a global pause/kill switch, a runaway-loop\n  guard, and an error-retry ceiling all veto automation.\n- **Local by default** — learning data, history, and the audit log live in\n  SQLite on your machine. Hap sends no telemetry and makes no cloud call of\n  its own; an optional CLI you configure may use its provider's service.\n\n## Quickstart\n\nRequires: Herdr ≥ 0.7.0 and `curl`. **No Go toolchain needed** — the install\nstep downloads the prebuilt binary for your platform (Linux/macOS,\namd64/arm64) from the matching GitHub Release and verifies it against the\npublished SHA256SUMS. (Building from source instead needs Go ≥ 1.24; see\nDevelopment.)\n\n```sh\nherdr plugin install 0xGosu/herdr-auto-pilot\n```\n\nPin a release (recommended for reproducible installs), or install\nnon-interactively:\n\n```sh\nherdr plugin install 0xGosu/herdr-auto-pilot --ref v0.4.0\nherdr plugin install 0xGosu/herdr-auto-pilot --yes\n```\n\nThe monitoring daemon starts automatically when an agent appears in the herd.\nUse the following recommended setup to make the **Auto Prompter** pane (TUI)\nand its CLI convenient to access from the host machine.\n\n### Open the pane with a hotkey (recommended)\n\nHerdr supports custom command keybindings, and the Auto Prompter pane can be\nopened from the CLI. Add this recommended binding to\n`~/.config/herdr/config.toml`:\n\n```toml\n[[keys.command]]\nkey = \"prefix+a\"\ntype = \"shell\"\ncommand = \"herdr plugin pane open --plugin herd-auto-prompter --entrypoint control\"\ndescription = \"Open Auto Prompter pane\"\n```\n\nThen apply it with `herdr server reload-config` (no restart needed). Now\n`ctrl+b` (Herdr's default prefix) followed by `a` opens the pane.\n\nNotes:\n\n- The pane opens as a tab (the placement declared in the plugin manifest);\n  override with `--placement split|overlay|zoomed` in the command if you prefer.\n- `prefix+a` is unused by Herdr's default bindings. Direct (no-prefix) chords\n  like `key = \"ctrl+alt+a\"` also work — ctrl+letter, function keys, and\n  explicit modified chords are the most reliable.\n\n### Make the CLI available from any shell\n\nOpen the **Auto Prompter** pane with the hotkey above, switch to the **Config**\ntab, select **Create /usr/local/bin/hap symlink to this running binary** under\n**Quick Shortcuts**, and press `enter` to confirm. This makes `hap` available\nfrom any shell (provided `/usr/local/bin` is on your `PATH`).\n\nAfter creating the symlink, you can also launch the TUI directly from any Bash\nshell instead of opening the Herdr pane with the hotkey:\n\n```sh\nhap tui\n```\n\nEverything the TUI does is also a CLI verb on the same binary:\n\n```sh\nhap status         # from any shell after creating the symlink above\nhap escalations\nhap pause          # global kill switch\n```\n\nRun from any shell, `hap` operates on the same instance the daemon uses:\nit honors the `HERDR_PLUGIN_CONFIG_DIR`/`HERDR_PLUGIN_STATE_DIR` env vars\nHerdr injects, and without them auto-detects Herdr's plugin directories\n(`~/.config/herdr/plugins/config/herd-auto-prompter`,\n`~/.local/state/herdr/plugins/herd-auto-prompter`). Only when neither\nexists — the plugin isn't installed — does it fall back to standalone\ndirs (`~/.config/herd-auto-prompter`, `~/.local/state/herd-auto-prompter`).\n\nTo see exactly where those resolved — handy for tailing the daemon log,\ninspecting the DB, or hand-editing `config.toml` — ask `hap`. These are\nread-only, need no daemon, and print the bare path so they compose in\nscripts:\n\n```sh\nhap state-dir            # state dir (DB, logs, socket, lock, match-index)\nhap config path          # the config.toml path (printed even before it exists)\nhap paths                # both, labeled\ncd \"$(hap state-dir)\"    # e.g. jump into the state dir\n```\n\n## Architecture\n\nHerd Auto Prompter is one Go binary (`hap`) used in several roles: a\nlong-running monitoring daemon, a TUI hosted in a Herdr pane, equivalent CLI\ncommands, an internal embedding worker, and an MCP server for optional LLM\nconsults. Herdr remains the source of truth for workspaces, panes, agents,\nstatus events, screen contents, and keyboard input. Hap never attaches to or\nsends input directly to an agent process; it observes and controls the agent's\npane through Herdr.\n\n```mermaid\nflowchart TB\n    subgraph HR[\"Herdr\"]\n        direction LR\n        A[\"Coding agent needs input\"] --\u003e H[\"Status and screen events\"]\n    end\n\n    H --\u003e M\n\n    subgraph HP[\"Auto Prompter\"]\n        direction LR\n        M[\"Monitor and\u003cbr/\u003eclassify\"] --\u003e D[\"Match a learned choice\u003cbr/\u003eor get an AI suggestion\"]\n        D --\u003e G{\"Pass safety and\u003cbr/\u003econfidence checks?\"}\n    end\n\n    subgraph LS[\"Local data\"]\n        direction LR\n        C[\"Config and\u003cbr/\u003esafety rules\"]\n        L[(\"Learned choices,\u003cbr/\u003ecorrections, and audit history\")]\n    end\n\n    AI[\"Optional AI helper\"] -.-\u003e D\n    C --\u003e G\n    L --\u003e D\n    G --\u003e|Yes| R[\"Herdr sends the response\u003cbr/\u003eand the agent continues\"]\n    G --\u003e|No| F[\"Escalation appears\u003cbr/\u003ein the TUI / CLI\"]\n    F --\u003e O[\"You review,\u003cbr/\u003econfirm, or correct\"]\n    O --\u003e|Confirmed response| R\n```\n\nIn the diagram, **Auto Prompter** includes the background daemon and the\nuser-facing TUI/CLI. The **optional AI helper** is any configured local LLM or\nagent CLI, and it can only propose a response. **Local data** covers\n`config.toml` plus the SQLite learning and audit database.\n\n### Runtime flow\n\n1. **Herdr starts the plugin daemon.** The plugin manifest registers hooks for\n   `workspace.created` and `pane.agent_detected`. A hook runs\n   `hap daemon --ensure`, which starts one version-checked, lock-guarded daemon\n   for the whole herd and returns immediately.\n2. **Herdr reports status and screen events.** The daemon subscribes to Herdr's\n   local event socket for pane discovery and agent-status changes. When an\n   agent becomes idle, blocked, done, or otherwise needs attention, the daemon\n   waits for the agent interface to paint, then asks Herdr to read the pane\n   and its metadata. Reconnects replay existing panes, so agents that predate\n   the daemon are reconciled too.\n3. **Auto Prompter monitors, classifies, and matches.** It classifies the\n   captured situation as idle, approval, choice, or error; masks volatile text\n   into a stable signature; and looks for a learned rule. Semantic lookup runs\n   in the isolated `hap embed-worker`, with BM25 and exact matching as\n   fallbacks.\n4. **Every proposed response passes confidence and safety checks.** Learned\n   confidence, graduation state, optional task sources, and any AI suggestion\n   feed the same decision pipeline. Before delivery, every result passes the\n   kill switch, never-auto patterns, suspected-irreversible check, rate/retry\n   ceilings, audit-before-action rule, and live-pane staleness check.\n5. **Herdr sends an approved response.** Hap calls Herdr's CLI to send text\n   and Enter, or individual keystrokes for numbered and multi-tab forms. Herdr\n   injects that input into the agent pane; the agent resumes, and later status\n   changes begin the cycle again. A `@noop` decision is audited but sends no\n   input.\n6. **Uncertain decisions become escalations for you.** The daemon writes an\n   audit row and escalation, then asks Herdr to show a notification. The TUI\n   and CLI read the same local state. Confirming, correcting, dismissing,\n   pausing, or changing config updates that local state. A confirmed/corrected\n   reply is a human-initiated action, so the frontend records it and sends it\n   through Herdr directly; it then sends a lightweight nudge to the daemon,\n   which reloads the new learning/config state without restarting.\n\n### Optional AI helper path\n\nFor an unknown situation, the daemon may launch the configured local\nLLM/agent CLI. Consults attach a short-lived `hap mcp` server: the model reads\nthe staged context with `get_context` and returns a structured decision with\n`submit_decision`. Rewrites and idle-task generation are one-shot commands\nwhose stdout is treated as a proposed result. In every case the daemon — not\nthe model or MCP process — owns the final decision, runs the proposal through\nthe same confidence, safety, and staleness checks, and either sends through\nHerdr or escalates.\n\nSQLite stores learned choices as signatures and decisions, along with audit\nrecords, escalations, user corrections, and safety counters. `config.toml`\nremains the hand-editable source for thresholds, safety rules, task sources,\nembedding, LLM, and TUI settings. Both live under the local plugin directories\ndescribed in Quickstart; no learning state is stored in Herdr or in an agent's\ncontext.\n\n## How learned rules work (shadow mode)\n\nA learned rule never acts on a situation before you have taught it. Explicit\ntask sources and opt-in LLM auto-actions are separate paths; they use their own\ngates and remain visible in the same audit trail.\n\n1. **Observe.** When an agent needs input, the plugin classifies the\n   situation (idle / approval / choice / error), fingerprints it into a\n   *situation signature* (volatile stuff like paths, hashes, and timestamps\n   is masked), and — in shadow mode — **escalates with a suggestion**.\n   Claude's AskUserQuestion and Codex `request_user_input` MCQ forms classify\n   as `choice`. A Claude **multi-tab** form (plan-mode question series,\n   `← ☐ … ✔ Submit →` header) is first\n   swept tab-by-tab with arrow keystrokes so the escalation, the signature,\n   and the LLM consult see **all** questions, not just the focused one. Its\n   answer is a digit series, one digit per tab including Submit (e.g.\n   `1 2 3 2 1`). Delivery adapts per tab: a digit commits a plain option, but\n   on Claude's preview layout it only moves the caret, so hap re-reads the live\n   form and presses Enter only after verifying the intended option is selected.\n   Unexpected transitions fail closed, and a series that doesn't match the tab\n   count is never partially delivered. Codex question series are likewise\n   swept and aggregated, but their answer series contains one digit per\n   question (no Submit pseudo-option); delivery verifies every live question\n   transition and explicitly submits the completed form.\n2. **Confirm or correct.** In the TUI's *Escalations* tab press `enter` to\n   confirm the suggestion (and send it), or `c` to type the correct\n   response — `v` shows the full record (trigger, rationale, LLM output,\n   agent type, and the **matched rule** — the exact learned signature this\n   situation resolved to, with its mode/streak/confidence/top action, or\n   \"none yet\" for a first sighting) when the list line is truncated; it\n   works on the *Agents*, *Audit*, and *Rules* tabs too, and pressing\n   `tab`/`shift+tab` inside the detail view switches tabs directly (no\n   `esc` needed). Escalation and audit list rows carry compact `rule=` and\n   agent-type columns; the CLI `escalations`/`audit` listings show the\n   same. From the CLI: `confirm \u003cid\u003e --send` or\n   `resolve \u003cid\u003e --action TEXT --send`. Escalations you don't want to\n   answer can be **deleted**: `space` marks one or more rows, `x` deletes\n   the marked (or selected) ones right away (no confirmation — dismissing\n   is safe, nothing is sent or learned), and `X` prunes everything older\n   than an age you pick (default 360 minutes). Deleting dismisses without\n   responding — the audit row is kept as `dismissed`. Deleting a learned\n   rule still asks for confirmation, and audit entries can't be deleted\n   individually (only the full clear-data reset removes them). CLI:\n\n   `hap capture \u003cagent-name-or-pane-id\u003e` explicitly re-runs the daemon's\n   normal delayed capture pipeline for a live `blocked`, `idle`, or `done`\n   agent. This is useful for testing or re-reading a pane after a daemon\n   restart; classification, MCQ sweeping, safety gates, duplicate handling,\n   automation, and auditing are identical to a real Herdr status event, so a\n   learned or LLM-approved response may be sent.\n\n   `dismiss \u003cid\u003e...` and `escalations prune [minutes]`. Long lists scroll\n   with the cursor and show a `… N more` line when rows are clipped, and\n   `/` opens an incremental search on the *Agents*, *Tasks*, *Escalations*,\n   *Audit*, and *Rules* tabs — case-insensitive substring over the visible\n   columns. `esc`/`enter` closes the search input keeping the filter,\n   backspacing the query to empty clears it, and while typing, every\n   printable key goes into the query — action keys like `q`, `y`, and `x`\n   can't fire mid-search. Action outcomes (confirm, resolve, delete, …)\n   stay pinned in a status area (`✓`/`✗` plus timestamp) until the next\n   mutating action starts, so results remain readable without lingering\n   beside the next operation. Detail\n   views always open at the top. Captured situations are collapsed to their\n   title plus a trailing preview (three lines normally; ten for Escalations'\n   Current Situation), and Audit's LLM output uses the same three-line\n   preview. Press `v` again to expand or collapse all previews. Expanded\n   content still retains its newest trailing lines when\n   `tui.max_content_height` caps it (`0` keeps the full content).\n3. **Graduate.** After **2 consecutive consistent confirmations** by default\n   (configurable from 1–10) *and* confidence above the per-situation threshold,\n   that signature becomes autonomous: next time, the plugin acts on its own\n   and logs it. Confirmations carry extra confidence weight (3× by default),\n   because an explicit operator answer is stronger evidence than an automated\n   observation.\n4. **Stay in control.** Correct any automated decision post-hoc (TUI *Audit*\n   tab or `resolve \u003caudit-id\u003e --action ...`). The correction is recorded and\n   immediately affects the rule's live confidence gate, but graduation is\n   permanent: it does not silently change an autonomous rule back to shadow.\n   To retrain one from a clean confidence/streak boundary, select it on the TUI\n   *Rules* tab and press `0`, or run `hap signatures reset \u003cprefix\u003e --yes`.\n   Reset keeps the audit and decision history (and the learned answer), but\n   excludes pre-reset decisions from confidence and graduation; the rule must\n   earn the configured confirmations again.\n\nWhen the first operator response creates a rule, earlier LLM-only guesses for\nthat signature remain in history for audit but do not seed the new rule's\nconfidence. The rule begins with the operator evidence that actually taught it.\n\n### Inspecting what it has learned\n\nEvery learned signature is visible on the TUI's *Rules* tab and via the\n`signatures` CLI (alias `sigs`): mode, confirmation streak toward\ngraduation, confidence, and the action it learned. The Rules view and\n`--min-conf` filter recompute live confidence from the current post-reset\ndecision history rather than using a stale stored snapshot. In rule, audit,\nand escalation confidence fields, a `-` means that item has not been scored\nyet; it is not a measured `0.00`. Press `enter`/`v` for\nthe full record — including the **original situation**, the pane snapshot\nfirst captured for the rule, so you can see exactly what a rule answers,\nnot just the action it sends (rules learned before this feature pick it\nup on their next sighting) — plus recent decisions and last audit\ncontext. The list shows each rule's full signature id untruncated, ready\nto copy into the CLI. `f`\nfilters by mode (composing with `/` search), and `x` deletes a signature\nyou no longer trust — deletion erases\nits decision history too (audit rows are kept), so it re-learns from\nscratch. Signatures are addressed by unique prefix, git-style:\n\n```sh\nhap signatures                      # list (--type, --mode, --agent-type, --min-conf)\nhap signatures show approval:9f2c   # full detail by unique prefix\nhap signatures reset approval:9f2c --yes # shadow + fresh streak/confidence; history kept\nhap signatures delete approval:9f2c --yes\n```\n\n## Configuration\n\nConfig lives in the plugin config dir (`herdr plugin config-dir\nherd-auto-prompter`) as hand-editable TOML; edits apply live (the daemon is\nnudged, or picks them up on the next event). A complete annotated sample\ncovering every section (including `[safety]`, `[llm]`, and `[tui]`) ships\nat [`sample/config.toml`](sample/config.toml) — copy it in and tune. The\nhighlights:\n\n```toml\n[confidence_thresholds]\nminimum = 0.50             # variance guard: minimum learned-action agreement\nidle = 0.65\napproval = 0.70\nchoice = 0.70\nerror = 0.75\ninferred_task_bar = 0.60   # higher bar for tasks inferred from pane history\n\n[learning]\ngraduation_n = 2           # consecutive confirmations to graduate (1-10)\nconfirmation_weight = 3.0  # confidence weight for an operator confirmation (\u003e=1)\n\n[limits]\nmax_consecutive_auto_prompts = 10  # per agent, without human interaction\nmax_auto_prompts_per_minute = 5    # per agent\nmax_error_retries = 2              # per error signature\n\n# Semantic rule matching: situations are matched to learned rules by\n# embedding their masked salient content (llama.cpp, MiniLM by default) in an\n# isolated worker and vector-searching stored signatures, so a paraphrased\n# prompt reuses the rule instead of re-learning from zero. Worker/model\n# failures degrade to normalized BM25 text matching, then exact hashes.\n[embedding]\ndisabled = false\nmodel_path = \"\"            # \"\" = bundled \u003cplugin\u003e/models/all-minilm-l6-v2-q8_0.gguf; any .gguf works\nsimilarity_threshold = 0.90 # min cosine similarity to reuse a learned signature\nbm25_min_score = 0.35       # min normalized BM25 similarity for the text fallback, (0,1]\ngpu_layers = 0              # inert in official builds (GPU backends compiled out)\nmodel_context_window = 0    # 0 = bundled-model default (512 tokens); input is\n                            # truncated below this limit before embedding\n# pane_salient_chars = 500  # fallback signature window for idle/unclassified\n                            # situations (trailing N characters of pane content).\n                            # Changing it re-keys idle/unclassified rules once,\n                            # so they re-learn; structured approval/choice/error\n                            # rules are unaffected.\n\n# TUI appearance. `theme` picks a named palette: default, dark, light,\n# high-contrast. Empty or unknown names resolve to default — the exact\n# original look — so existing setups see no change.\n[tui]\nmax_content_width = 0       # cap variable-width list columns; 0 = full width\nmax_content_height = 0      # expanded long-field lines; 0 = unlimited (collapsed previews use short tails)\ntheme = \"high-contrast\"\n\n# Optional per-role color overrides, layered on top of the theme; unset\n# roles inherit the theme's value. Values are terminal color strings\n# lipgloss accepts: 256-color codes (\"205\") or hex (\"#ff5faf\"). Roles:\n# title, section, error, ok, paused, running, warn, help. Edited in config.toml\n# only (the TUI shows them read-only).\n[tui.palette]\ntitle = \"205\"\nerror = \"#ff5f5f\"\n\n# Point agents/workspaces at a task list so idle agents get the next\n# unchecked item. Without a declared source, the plugin falls back to\n# inferring the next task from the agent's own native todo rendering — never\n# free-form prose — held to the higher inferred_task_bar. If neither source\n# exists, an optional llm.task_generate_command can propose tasks for you to\n# approve. Inference is agent-type-specific: currently only `claude` is\n# supported (Claude Code's ✔/■/□ todo widget; the in-progress item wins,\n# else the first\n# pending one). Other agent types skip inference entirely and escalate.\n#\n# The prompt sent to the agent is rendered from a template. The default steers\n# the agent to manage its list through the hap CLI with its own name pre-filled\n# (and a --path fallback for sources that aren't name-addressable):\n#   \"Your next task is {next_task_content}. Prefer the hap CLI to manage your\n#    tasks: `hap task {agent_name} list` to view them, `hap task {agent_name}\n#    start \u003cn\u003e` to mark one in-progress when you begin working on it, and\n#    `hap task {agent_name} done \u003cn\u003e` to mark it complete as you go (if that\n#    name isn't recognized, use `--path {task_list_path}` in place of\n#    `{agent_name}`).\"\n# When every item is checked off, the templated prompt is never sent: the\n# plugin escalates a confirmable @noop suggestion (\"No more pending tasks\")\n# instead — unless BOTH llm.task_generate_command and\n# llm.task_generate_command_start are configured, in which case it generates\n# more tasks for the agent instead of escalating (see \"Suggesting tasks when\n# no source exists\" below; the same generation flow refills an exhausted\n# source, always via task_generate_command since a list already exists).\n[[task_sources]]\nagent = \"brave-otter\" # agent short name, pane id, or type (\"\" = any)\nworkspace = \"\"        # workspace name; \"\" or \"*\" = any, \"*\" wildcards work\n                      # (\"codex-*\" = starts with, \"*-vscode3\" = ends with)\npath = \"/home/me/project/docs/tasks.md\"\n# Optional per-source prompt format ({next_task_content}, {task_list_path}, {agent_name}, {cwd}):\nnext_task_template = \"Your next task is {next_task_content}. Read the full tasks list at {task_list_path}. Verify task dependencies before starting. When there is no task available, focus on improving the test coverage of this project.\"\n# When an [llm].command is configured, each determined task is first reviewed by\n# the LLM before it is sent (see \"Reviewing tasks before they are sent\" below).\n# Default: on. Opt this source out with:\n# llm_review = false\n```\n\nThe former `[thresholds]` table is accepted for compatibility. Loading it\npreserves its values, and the next config save rewrites it as\n`[confidence_thresholds]`.\n\n### Agent short names\n\nEvery monitored agent automatically gets a short friendly two-word name\n(e.g. `brave-otter`) the moment it appears in the herd — on detection, not\non its first blocked prompt — because pane ids like `w6:p1` are not\noperator-friendly. The TUI's agent detail (`v`) also shows exactly where\nthe agent lives: workspace, tab, and pane, each with its number, label,\nand id, plus the matching **task source** (if any). From that detail view,\n`t` jumps to the *Tasks* tab at this agent's source, where its checklist —\nand the source entry itself — can be managed. Use the name in task-source\nselectors, and rename agents to whatever fits your workflow:\n\n```sh\nhap agents                      # short name, pane id, type, status, automation\nhap rename brave-otter backend-dev\nhap disable backend-dev         # stop automation for only this agent\nhap enable backend-dev          # allow automation again\nhap task-source --agent backend-dev ./docs/backend-tasks.md\nhap task-source --agent backend-dev --template 'Do this next: {next_task_content} (full list: {task_list_path})' ./docs/backend-tasks.md\n```\n\n(Or in the TUI: select the agent and press `n` to rename it, `x` to disable\nit behind a `Y/n` confirmation, or `e` to enable it again. A disabled live\nagent remains in the list with `DISABLED` in its status column. HAP never\nperforms autonomous pane actions for it: would-be actions are audited as\n`denied` with rationale `[agent_disabled]`, while would-be escalations are\nwritten directly as `dismissed` with the same tag and never enter the pending\nqueue.)\n\n### The Tasks tab\n\nThe TUI's *Tasks* tab aggregates the checklist items of **every** configured\ntask source into one list — a header row per source (with the live agent it\ncurrently feeds, if any) and its checklist items underneath, done and pending\nalike. Long source paths are display-truncated to their tail (`…/dir/file.md`,\nthe file name always preserved); the full path stays searchable and shows in\nthe task detail view. It's the same checklist state the `hap task` CLI edits,\nso changes made either way stay in sync (the daemon re-reads the file live on\neach idle event).\n\nManage items without leaving the pane:\n\n- `enter`/`y` — send the pending task under the cursor to the live agent its\n  source feeds, rendered through the source's next-task template, behind a\n  `Y/n` confirmation. Only a truly pending `[ ]` task on a **cleanly idle**\n  agent qualifies — done (`[x]`) and in-progress (`[-]`) tasks, and\n  working/blocked agents, are refused (the daemon's own idle-only rule). The\n  agent is re-checked idle *at the moment of delivery*, not just when the\n  question is asked, so a confirmation left open while the agent picks up\n  work refuses rather than interrupting it. The task is marked `[-]` in\n  progress **before** it is delivered, which is what keeps the daemon from\n  handing the same item out again; a delivery that fails returns it to\n  `[ ]`. The CLI twin is `hap task \u003cagent\u003e send \u003cn\u003e [--yes]`.\n- `v` — open a task's detail view: full multi-line text, status, the\n  source's full path and selectors, and the live agents it feeds. `enter`/\n  `y`, `e`, `x`, and `f` keep working inside the detail, acting on the item\n  shown.\n- `a` — add a task to the source under the cursor\n- `e` — edit the text of the task under the cursor\n- `d` — toggle a task done/undone\n- `x` — delete a task; on a **source's header row**, retire the whole\n  source (see below)\n- `space` — mark a run of tasks, so `d`/`x` act on all of them at once\n  (with nothing marked, they act on the row under the cursor)\n- `f` — focus the live agent this source feeds, in herdr\n- `/` — incremental search over the visible columns\n\nThe add and edit prompts accept multi-line task text: **Shift+Enter inserts\na line break** (Ctrl+J works on terminals that can't report Shift+Enter) and\nthe input box expands one line per break; **Enter submits**, Esc cancels.\nA task always stays ONE checklist line: line breaks are stored as the\nliteral two-character sequence `\\n` in tasks.md (hand-written `\\n` works\ntoo) and are converted back to real newlines when the task is sent to an\nagent — which means backslash-n in task text always reads as a line break,\nnever as those two literal characters. The edit prompt decodes stored `\\n`\nback into real lines, and the detail view shows the task as the agent will\nreceive it.\n\nThe manual send is independent of the daemon, but marking the sent task\n`[-]` keeps the two in step: the daemon's own idle-time declared-task flow\nonly ever picks the first still-pending `[ ]` item.\n\nEdits are guarded against a checklist that changed underneath you: an action\ncaptured against a row aborts (rather than mutating the wrong line) if that\ntask's text no longer matches when the write runs.\n\n**Retiring a whole source.** Pressing `x` on a source's *header row* removes\nits `[[task_sources]]` entry, behind a `y/n` confirmation — but only once the\nsource can no longer be serving anyone: either **no live agent matches** its\nselectors, or **every task in it is finished**. A source that still feeds a\nlive agent and still has unfinished work refuses, naming the agent and what's\nleft. In-progress `[-]` tasks count as unfinished, so a source can't be pulled\nout from under an agent that is mid-task. Both *unknowns* refuse too, since\nneither is evidence of safety: an agent list herdr won't answer isn't an empty\nherd, and a checklist that won't read isn't an empty checklist. (A source no\nlive agent matches stays retirable whatever its file says — that's what keeps\na broken entry cleanable from this tab.) Removal takes the config entry\nonly — **the checklist file stays on disk**, since sources are often\nhand-written docs hap never created; re-adding the source brings the list back\nuntouched. (With items marked via `space`, `x` still deletes those items — the\nselection wins.) To retire a source the guard refuses, use the *Config* tab's\n`x` or `hap task-source remove \u003cindex\u003e`, which are unguarded by design. To\npoint an agent at a source in the first place, use `hap task-source add` or\nthe *Config* tab's `t`.\n\n### Suggesting tasks when no source exists, or a source runs out (optional)\n\nIf an idle agent has neither a matching `[[task_sources]]` entry nor an\ninferable native todo, `llm.task_generate_command` can run a one-shot local\nCLI to propose one or more next tasks. This is opt-in: without the command,\nthe safe default remains a `no_task_source` escalation and hap invents\nnothing.\n\nThe same generation flow also refills a declared `[[task_sources]]` entry\nonce its checklist is fully checked off — but only when BOTH\n`llm.task_generate_command` and `llm.task_generate_command_start` are\nconfigured (stricter than the no-source case above, since it replaces content\nin a source that already had operator-relevant tasks). Without both commands\nset, an exhausted source escalates `task_source_exhausted` — a confirmable\n@noop suggestion (\"No more pending tasks\") — instead of generating or sending\nthe old templated \"none\" prompt.\n\nRefill is capped per source by `max_tasks` (default **20**): once a source's\nfile holds more than that many checklist items (done, in-progress, and pending\ncounted alike) and its pending items are exhausted, the daemon logs a warning\n(\"Maximum number of tasks reached for agent … — clean up the task list to make\nroom for new tasks\") and **skips** generation for that agent instead of piling\nmore onto an already-long list. The **same cap also gates manual creation** —\nadding tasks (the Tasks tab's `a`, or `hap task … add`) to a registered source\nis rejected once it would push the list past `max_tasks` — so a hand-added list\ncan't grow past what the daemon would then refuse to refill. Prune the checklist\n(or raise `max_tasks` on the `[[task_sources]]` entry) to resume. Sending the\nremaining pending items of a source under its cap is unaffected, and a `--path`\nfile that isn't a registered `[[task_sources]]` entry is never capped.\n\nThe command's stdout may be plain lines or a Markdown list/checklist. Hap\nnormalizes it and surfaces it as an escalation; it never auto-accepts a\ngenerated task. Confirming the suggestion creates\n`\u003cstate-dir\u003e/tasks/\u003cagent-name\u003e.md`, marks the first task in progress,\nregisters the file as that agent's task source, and sends only the first task.\nLater idle events consume the remaining tasks through the normal declared-task\nflow. Dismiss it with `x`; if generation failed or timed out, press `l` to\nretry. Suggestions are dropped or refused if the agent has started working, or\nnow has a task source with a real pending item, in the meantime.\n\n```toml\n[llm]\ntask_generate_command = [\n  \"claude\", \"-p\",\n  \"Suggest concrete next tasks, most important first. Reply with only the tasks, one per line.\\n\\nAgent: {agent_name}\\nCwd: {cwd}\\n\\nScreen:\\n{pane_excerpt}\",\n  \"--model\", \"haiku\",\n]\n# Optional first generation for each agent this daemon lifetime:\n# task_generate_command_start = [ ... ]\n# task_generate_timeout_seconds = 60  # omitted: inherits timeout_seconds\n```\n\nAvailable placeholders are `{self}`, `{agent_name}`, `{agent_type}`,\n`{pane_excerpt}`, and `{cwd}`. The first-generation state is tracked\nindependently from LLM consults and rewrites, and only applies to the\nno-source-at-all case: `task_generate_command_start` bootstraps a list from\nnothing, so refilling an already-exhausted declared source is never treated\nas \"first\" — it always uses `task_generate_command`. (These keys were renamed\nfrom `generate_task_command*`; the old spellings no longer load, so update an\nexisting config.)\n\n### Task source info in every consult\n\nWhenever an agent has a matching `[[task_sources]]` entry, `get_context`\ncarries `task_list_path` (the checklist file), `pending_task_count` (how many\nitems are still unchecked, `[ ]`) with `next_pending_task` (a truncated\npreview of the first, only when at least one is pending), and\n`in_progress_task_count` (how many items are marked `[-]` — this may be the\ntask the agent is currently working on) with `first_in_progress_task` (a\ntruncated preview of the first, only when at least one is in progress). This\nis included on **every** LLM consult for that agent\n(approval, choice, error, or idle), not just the pre-send task review below,\nso the LLM always knows the\nagent's backlog state.\n\n### Reviewing tasks before they are sent (optional)\n\nWhen an `[llm].command` is configured, each task determined from a\n`[[task_sources]]` entry for an idle agent is first **reviewed** by that LLM\nbefore it is sent. Using the same two MCP tools as an ordinary consult\n(`get_context` / `submit_decision`), the LLM sees the live pane plus the queued\ntask (`proposed_task` / `current_task`), the checklist path (`task_list_path`),\nand every remaining item (`pending_tasks`) — so it can also **pick a different\npending task** when the pane shows the current one is already done — and decides\nwhat to send:\n\n- **Send as-is** — `submit_decision` with `recommend_action` `@next_task:declared`;\n  the daemon sends the queued task verbatim (no copying, no paraphrase drift).\n- **Send edited / a different task** — `recommend_action` set to the literal\n  instruction text (an edit, or the next unfinished item from `pending_tasks`\n  when the current one is already done).\n- **Decline** — `submit_decision` with `recommend_action` `@noop` (the agent is\n  still busy, the task is already done, or the pane shows it should not run).\n\nBoth outcomes follow `auto_act_confidence_threshold` exactly like any consult,\nand are re-gated by the same safety controls (kill switch, never-auto patterns,\nirreversible heuristic, rate limits):\n\n- **Confident (`confident_score ≥ threshold`)** — the LLM's resolution is applied\n  automatically: a send delivers the task, a decline silently skips it.\n- **Low-confidence (`\u003c threshold`)** — surfaced for you to confirm. The\n  escalation's suggestion is the LLM's exact recommendation (the possibly-edited\n  task, or *no reply* for a decline), and the **original** queued task and the\n  LLM's reasoning appear in the escalation detail.\n\nBecause the default threshold is `999` (never auto-act), every review is\nsurfaced for confirmation until you lower `auto_act_confidence_threshold`.\n\nFor learning, every accepted task-review send is stored symbolically as\n`@next_task:declared`, whether the LLM approved the queued task verbatim,\ncopied it, edited it, or chose another pending item. The pane still receives\nthe real task text. This teaches the reusable decision “send the next declared\ntask” instead of treating each task's one-off wording as a different action;\nan operator confirmation of a low-confidence review learns the same symbolic\naction. The sentinel is rejected if an LLM submits it outside a task review\nthat actually carries a proposed task.\n\nThis is **on by default** whenever the LLM command exists; set\n`llm_review = false` on a `[[task_sources]]` entry to keep the plain\ndeclared-task flow for that source.\n\n### Never-auto patterns\n\nIrreversible operations are **never** automated, regardless of confidence.\nThe shipped seed covers force-pushes, destructive filesystem/database ops,\ndeploys/publishes, credential changes, and broader suspected-irreversible\nlanguage. The strict and heuristic seed rules are both controlled by\n`safety.disable_never_auto_seed_patterns` and are regression-tested in\nCI against a maintained corpus of irreversible-operation prompts\n(`internal/domain/testdata/irreversible_corpus.txt`). Extend it with your own\nregex patterns:\n\n```toml\n[safety]\nnever_auto_patterns = ['(?i)restart\\s+the\\s+payment\\s+service']\n```\n\n(The pre-rename key `allowlist_patterns` still loads as a deprecated alias —\npatterns are merged with a warning, and the next config save rewrites the\nfile under the new key.)\n\nThe pre-rename boolean `safety.disable_seed` also still loads with a warning;\nthe next config save rewrites it as\n`safety.disable_never_auto_seed_patterns`.\n\nor `hap rules add '\u003cregex\u003e'` / `rules remove \u003cindex\u003e`, or press `a`/`x` on\nthe TUI's *Config* tab — which also lists the supported scalar config fields,\nadds/removes task sources (`t`/`x`), and clears learned data (`X`).\nSimple fields — numbers, booleans, and the `tui.theme` enum, including\n`llm.pane_excerpt_chars`, `llm.task_generate_timeout_seconds`,\n`embedding.model_context_window`, `safety.disable_never_auto_seed_patterns`,\n`tui.max_content_width` / `tui.max_content_height`, and `tui.terminal_bell`\n(on by default — rings the terminal bell on a new escalation, and when the\nkill switch is paused by a *different* process than the TUI you're in) — edit\ninline (`enter`) or via `hap config set \u003ckey\u003e \u003cvalue\u003e`. Free-text fields (`llm.command`,\n`llm.command_start`, `llm.rewrite_command`, `llm.rewrite_command_start`,\n`llm.rewrite_fallback_template`, `llm.task_generate_command`,\n`llm.task_generate_command_start`, `embedding.model_path`) show read-only in\nthe TUI, because a one-line\nprompt mangles quoted argv values — edit them in `config.toml` or with\n`config set`, which accepts every listed scalar key. Scoped never-auto rules\nand `[[capture_delay]]` rules also display read-only on the tab. The\n`[tui.palette]` overrides are edited directly in `config.toml`. Capture delays show the built-in defaults (10000\nms first event / 2000 ms after) when none are configured, and long values are\ntruncated to one line — the full value lives in `config.toml`. Prompts that\n*look* destructive\nbut match no pattern are escalated by a suspected-irreversible heuristic\nrather than automated. The heuristic needs corroboration to fire — a\ndestructive verb aimed at a data/infrastructure target, explicit no-undo\nlanguage, and the like — so everyday prompts (\"remove the unused import\")\ndon't trip it. It scans only the actionable region (the pending dialog near\nthe pane bottom, or the next-task prompt about to be sent when idle), so an\nagent merely *talking about* destructive operations in its narration isn't\nflagged, and the escalation rationale names the indicator and the text it\nmatched. Add operator regexes to `never_auto_patterns` in `[safety]` (all\nagents), or scope a pattern to specific agent types:\n\n```toml\n[[safety.never_auto_rules]]\npattern = '(?i)compact\\s+the\\s+conversation'\nagent_types = [\"codex\", \"agy\"]   # \"*\" or omit for all agent types\n```\n\nThe legacy `irreversible_indicators` and `[[safety.indicator_rules]]` settings\nstill load with warnings and migrate to these unified never-auto forms on the\nnext config save.\n\n### Daemon and semantic-matching health\n\n`hap status` and the TUI share the same health assessment. They report a\nstale or hung daemon, a runtime-degraded embedder, crash-looping, and the\ncrash-loop breaker's auto-disable/give-up states. The detached daemon's stderr\nis captured at `\u003cstate-dir\u003e/daemon.stderr.log` (rotated at 256 KiB); an\nerror-severity TUI banner offers `!` to open the last 16 KiB in a scrollable\ndetail view. The same path appears in `hap status` and `hap state-dir` makes it\neasy to locate.\n\nLlama.cpp runs in a persistent `hap embed-worker` child rather than inside the\ndaemon. A native abort or stalled embedding call therefore kills/restarts the\nworker and degrades semantic matching to BM25/exact matching after repeated\nfailures while the monitoring daemon stays alive. The outer crash-loop breaker\nis still a final safeguard: clustered daemon restarts first latch embeddings\noff, then stop respawning if the daemon continues to crash. Changing any\n`[embedding]` setting clears that latch and retries.\n\nEmbedding input is token-truncated before it reaches the model. The bundled\nMiniLM uses `model_context_window = 0` (resolved to 512 positions); custom\nmodels can set their real limit explicitly. Positive values below 256 are\nclamped to 256. Never configure a value above the model's actual position\nlimit, because llama.cpp can abort the worker when that limit is exceeded.\nThe fallback idle/unclassified signature window defaults to 500 characters.\n\n### Local LLM fallback (optional)\n\nWhen no confident learned rule applies, the plugin can consult a local\nLLM/agent CLI you already have installed. The model receives context and\nsubmits its suggestion through the plugin's own MCP server\n(`hap mcp` — tools `get_context` and `submit_decision`); its\nstdout is captured for audit only. `submit_decision` enforces a\nper-situation contract: `approval`/`choice` listing options must be\nanswered with `select_options` (the explicit answer: 1-based option\nnumbers — `[2]` for a single menu, one integer per tab for a multi-tab\nform; a menu-less prompt such as a bare y/n takes `recommend_action`\nliteral text instead), while `idle`/`error` require `recommend_action`\n(the literal reply text) and reject `select_options`;\n`recommend_action \"@noop\"` (\"no reply needed\") is accepted for any\nsituation, and a `confident_score` (0-100) is shown on the\nescalation entry so you can weigh the suggestion. Example for Claude\nCode:\n\n```toml\n[llm]\n# Claude Code: the prompt belongs immediately after -p (the plugin\n# auto-repairs a prompt misplaced after other flags — see below).\ncommand = [\n  \"claude\", \"-p\",\n  \"Use the hap MCP tools: call get_context, decide what the operator would answer — or whether no reply is needed — then call submit_decision (select_options for multiple-choice, recommend_action '@noop' to do nothing).\",\n  \"--mcp-config\", '{\"mcpServers\":{\"hap\":{\"command\":\"{self}\",\"args\":[\"mcp\"],\"env\":{\"HAP_REQUEST_ID\":\"{request_id}\"}}}}',\n  \"--allowedTools\", \"mcp__hap__get_context,mcp__hap__submit_decision\",\n]\ntimeout_seconds = 120\nauto_act_confidence_threshold = 999   # auto-act only when the LLM's confidence (0-100) is \u003e= this; 999 = never (surface for your confirmation)\npane_excerpt_chars = 5000   # pane excerpt size in the consult context (default 5000)\n```\n\nAn optional **`command_start`** runs *instead of* `command` on an agent's\n**first consult** — the first time the plugin needs the LLM for that agent\nthis daemon lifetime. Every later consult uses `command`. A genuinely new\nagent almost always starts in a new pane, so it primes on its own; a herdr\nsubscriber reconnect does **not** re-fire it, so a long-running agent's\nkickoff prompt won't repeat mid-session. It takes the same placeholders and\nMCP flow as `command`; use it for a priming/kickoff prompt or a stronger\nmodel on the first touch. Omitting it (or leaving it empty) reuses\n`command`, so the feature is opt-in — and `command_start` alone never\nenables the fallback (`command` is what gates it):\n\n```toml\n[llm]\ncommand       = [ \"claude\", \"-p\", \"...ongoing consult prompt...\", \"--model\", \"haiku\" ]\ncommand_start = [ \"claude\", \"-p\", \"...first-touch kickoff prompt...\", \"--model\", \"opus\" ]\n```\n\nThe preferred template also has a one-shot **fast-fail fallback**. If it\nexits with an error in under one second without staging a decision, hap tries\nthe other template (`command` ↔ `command_start`) once. This works in both\ndirections, so a failed first-touch command can fall back to the ongoing one,\nand a failed ongoing/resume command can try the start form. Timeouts, clean\nexits without `submit_decision`, cancelled runs, and absent or identical\nalternates are not retried automatically.\n\n`get_context` hands the model the classified situation (type, options,\npermission verb, error summary), a pane excerpt (the last\n`pane_excerpt_chars` characters, read deeper than the classification\nsnapshot), the agent's herdr location (`workspace_id`, `tab_id`,\n`pane_id`, `agent_id`), and the pane's working directory (`cwd`,\n`foreground_cwd` — advisory: a deleted directory carries a\n`\" (deleted)\"` suffix and either may be empty). The location ids let the\nmodel run its own read-only `herdr` queries (`herdr pane read \u003cpane_id\u003e`,\n`herdr pane get \u003cpane_id\u003e`, ...) — to allow that with Claude Code, extend\nthe tool allowlist, e.g.:\n\n```toml\n\"--allowedTools\", \"mcp__hap__get_context,mcp__hap__submit_decision,Bash(herdr pane read:*),Bash(herdr pane get:*)\",\n```\n\nOpenAI Codex CLI (MCP server passed inline via `-c` overrides; `exec` is\nrequired for headless runs — the plugin inserts it if you forget). Codex's\napproval policy auto-denies MCP tool calls in headless mode, so the bypass\nflag is required; hap's own safety controls still re-gate every submission\nbefore anything reaches an agent:\n\n```toml\n[llm]\ncommand = [\n  \"codex\", \"exec\", \"--skip-git-repo-check\",\n  \"--dangerously-bypass-approvals-and-sandbox\",\n  \"-c\", 'mcp_servers.hap.command=\"{self}\"',\n  \"-c\", 'mcp_servers.hap.args=[\"mcp\"]',\n  \"-c\", 'mcp_servers.hap.env.HAP_REQUEST_ID=\"{request_id}\"',\n  \"-c\", 'mcp_servers.hap.env.HAP_DB_PATH=\"{db}\"',\n  \"-c\", 'mcp_servers.hap.env.HAP_CONTROL_PATH=\"{control}\"',\n  \"Use the hap MCP tools: call get_context, decide what the operator would answer — or whether no reply is needed — then call submit_decision (select_options for multiple-choice, recommend_action '@noop' to do nothing). Do not run any other commands.\",\n]\ntimeout_seconds = 180\n```\n\n(The `HAP_DB_PATH`/`HAP_CONTROL_PATH` entries matter: codex launches MCP\nservers with a sanitized environment, so the hap server must be told its\ndatabase explicitly.)\n\nAntigravity CLI (`agy`) has no per-invocation MCP flag — register hap once\nin `~/.gemini/config/mcp_config.json` with the database path in `env` (the\nhap MCP tools default to the current pending request, so no request id is\nneeded):\n\n```json\n{\"mcpServers\": {\"hap\": {\"command\": \"/path/to/plugin/bin/hap\", \"args\": [\"mcp\"],\n  \"env\": {\"HAP_DB_PATH\": \"~/.local/state/herdr/plugins/herd-auto-prompter/herd-auto-prompter.db\"}}}}\n```\n\n```toml\n[llm]\n# agy, like claude, wants the prompt immediately after --print\n# (auto-repaired if misplaced).\ncommand = [\n  \"agy\", \"--print\",\n  \"Use the hap MCP tools: call get_context, decide what the operator would answer — or whether no reply is needed — then call submit_decision (select_options for multiple-choice, recommend_action '@noop' to do nothing).\",\n  \"--dangerously-skip-permissions\",\n]\ntimeout_seconds = 180\n```\n\nPlaceholders: `{self}` (this plugin binary), `{request_id}`, `{db}`,\n`{control}`, `{agent_name}` (the agent's short name). Common\nmisconfigurations of known CLIs are auto-repaired at\nlaunch (claude/agy: prompt moved next to `-p`/`--print`; codex: missing\n`exec` inserted) — an unrecognized shape is left untouched. Every LLM\nsuggestion is re-gated through the same never-auto patterns, kill switch, and rate\nguards; it auto-acts only when the LLM's self-reported confidence meets\n`auto_act_confidence_threshold` (0-100; default 999 = never) and the action\ndoesn't contradict your learned history — otherwise the suggestion is surfaced\nfor you to confirm. On timeout, CLI failure, or no submission the situation\nescalates. For a retryable failed/timed-out consult, press `l` on its TUI\nescalation; hap refreshes the agent's live pane and status before re-running\nthe consult, and disables retry while another consult is already in flight.\n\nThe model can also submit `recommend_action: \"@noop\"` (also accepted: `noop`,\n`no_op`, `no-op`) to say **no reply is needed** — the agent finished or is\nonly reporting status, and any prompt would just nudge it into another\nround trip. A noop is recorded in the audit trail and learned like any\nother decision (an accepted \"do nothing\" escalation graduates into a rule\nthat silently stands down), but nothing is ever sent to the pane. Note: a\nlearned idle noop suppresses task sends for that signature until you\ncorrect it or delete the signature.\n\n### LLM rewrite of literal replies (optional)\n\nWhen a learned rule resolves to **literal free text** — an idle next-task\nprompt, an error retry command, a free-text approval reply — the plugin can\npass that text through a one-shot LLM CLI to adapt it to what's actually on\nthe agent's screen before sending. Unlike the consult fallback there is no\nMCP round-trip: the CLI is run once and its **stdout is the rewritten\ntext**.\n\n```toml\n[llm]\nrewrite_command = [\n  \"claude\", \"-p\",\n  \"Rewrite this instruction for the coding agent given its current screen. Reply with ONLY the rewritten text.\\n\\nInstruction: {text}\\n\\nScreen:\\n{pane_excerpt}\",\n  \"--model\", \"haiku\",\n]\nrewrite_timeout_seconds = 30   # omitted: inherits timeout_seconds\n# Wraps the original when the rewrite fails (never blocks the send):\nrewrite_fallback_template = \"You must act based on the following: {original_text}\"\n# Optional: runs instead of rewrite_command on the agent's FIRST rewrite\n# (same first-interaction boundary as command_start, tracked independently;\n# empty inherits rewrite_command):\n# rewrite_command_start = [ \"claude\", \"-p\", \"...first-rewrite prompt...\", \"--model\", \"haiku\" ]\n```\n\nAs with consult commands, a rewrite command that exits with an error in under\none second is retried once with the other configured rewrite template\n(`rewrite_command` ↔ `rewrite_command_start`). Timeouts and clean empty or\noversized output use the normal rewrite fallback instead of retrying.\n\nPlaceholders in `rewrite_command`: `{text}` (the literal reply a rule\nresolved to), `{situation_type}`, `{agent_type}`, `{agent_name}` (the\nagent's short name), `{pane_excerpt}` (last `pane_excerpt_chars` characters\nof the live pane). The same CLI auto-repair as `llm.command` applies (on the\nraw template, before substitution). No shell is involved — each element is\none argv entry — but `{text}` and `{pane_excerpt}` carry untrusted pane\ncontent: embed them inside a prompt string (as in the example) rather than as\nstandalone argv elements, so a value starting with `-` can never be parsed as\na flag; the same values are also available as `HAP_REWRITE_TEXT` /\n`HAP_SITUATION_TYPE` / `HAP_AGENT_TYPE` / `HAP_AGENT_NAME` env vars.\n\nInvariants:\n\n- **Numbered-menu answers are never rewritten** — a mapped digit reaches\n  the menu untouched. Only literal free text goes through the rewriter.\n- **A rewrite failure never blocks the send**: on error, timeout, or empty\n  output the original text is delivered wrapped in\n  `rewrite_fallback_template` (`{original_text}`, `{agent_name}`\n  placeholders; empty or `{original_text}`-less templates fall back to the\n  built-in default).\n- **Safety controls still apply to the rewritten text**: output matching\n  the never-auto patterns or the irreversible-operation heuristic is\n  discarded in favor of the wrapped original; if even that trips, the\n  situation escalates instead of sending. Kill switch, rate guard, and a\n  staleness re-check (the pane must still show the same situation) run\n  again at delivery time.\n- **Learning is unaffected**: decision history records the original\n  learned action, never the rewritten text, so rule confidence and the\n  variance guard keep working.\n\n#### Troubleshooting the fallback\n\n- **Escalations citing `not found in PATH`** — the daemon inherits herdr's\n  environment, which can be narrower than your shell's; make sure the CLI\n  is reachable from a non-login shell or use an absolute path in\n  `llm.command`.\n- **Escalations citing `ENOENT: Bun could not find a file` (≤ v0.1.10)** —\n  the daemon was started from a workspace directory that has since been\n  deleted, which kills the Bun-built `claude` CLI at startup. Fixed in\n  v0.1.11; upgrading also requires replacing the running daemon (below).\n- **Upgrades not taking effect** — the daemon is a singleton that outlives\n  binary upgrades. Since v0.1.13, `hap daemon --ensure` (fired by herdr's\n  event hooks) detects the version mismatch and replaces the old daemon\n  automatically; `hap status` shows the running daemon's version and flags\n  a stale one. On older versions run `pkill -f 'hap daemon'` once after\n  upgrading.\n\n## Pause/kill switch \u0026 audit\n\n- `pause` / `resume` (CLI, TUI `p`/`r`, or Herdr plugin actions) toggle a\n  global kill switch. It takes effect within a second — the daemon re-reads\n  the latest kill event on every decision — and the full pause/resume history\n  is kept for audit.\n- Every automated action **and** every escalation writes an audit record:\n  trigger, situation, action or escalation reason, confidence, rationale, and\n  (for LLM decisions) captured output. `audit` / the *Audit* tab shows it;\n  corrections keep their lineage to the original decision.\n- Escalations whose target is no longer present in an authoritative Herdr\n  agent snapshot are written directly as `dismissed` with `[agent_not_live]`.\n  Disabled agents use `[agent_disabled]`; their suppressed autonomous actions\n  are written as `denied`. These lifecycle outcomes remain visible without\n  notifying the operator or creating a stale pending escalation.\n- `clear-data --yes` resets all learned history and audit data (it never\n  leaves your machine in the first place).\n\n### Wiping plugin data\n\nTwo levels, depending on how much you want gone:\n\n- **Reset learned data (the supported path):**\n\n  ```sh\n  hap clear-data --yes\n  ```\n\n  This empties every learning-related table in the SQLite database\n  (signatures, decisions, audit log, corrections, rate/retry counters, LLM\n  requests and decisions) and nudges the running daemon to reload — no\n  restart needed. The `--yes` is mandatory; without it the command refuses.\n  Your configuration (thresholds, never-auto rules, task sources) is kept.\n\n- **Full factory reset (everything, including config):** there's no single\n  CLI verb for this — stop the daemon and delete the plugin's two\n  directories:\n\n  ```sh\n  pkill -f \"hap daemon\" 2\u003e/dev/null                          # stop the daemon\n  rm -rf ~/.local/state/herdr/plugins/herd-auto-prompter     # DB, log, socket, lock\n  rm -rf ~/.config/herdr/plugins/config/herd-auto-prompter   # config.toml\n  ```\n\n  Both directories are recreated fresh automatically — the daemon restarts\n  on the next `pane.agent_detected`/`workspace.created` event, or\n  immediately via `hap daemon --ensure`.\n\nPrefer `clear-data` unless you also want your config gone; it's the only\npath that keeps the daemon running through the wipe.\n\n## Development\n\n```sh\ngo build ./...        # build\ngo test ./...         # unit, golden, safety-invariant, concurrency, integration\n\n# develop against your local checkout: linking skips the release-download\n# build step, so build the binary yourself first\ngo build -o bin/hap ./cmd/hap\nherdr plugin link .\n```\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md). The specification this plugin\nimplements lives in [`docs/specs/herd-auto-prompter/`](docs/specs/herd-auto-prompter/).\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0xgosu%2Fherdr-auto-pilot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F0xgosu%2Fherdr-auto-pilot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0xgosu%2Fherdr-auto-pilot/lists"}