Agent hooks

The built-in hooks extension wires each coding agent's lifecycle hooks to thurbox-cli session signal so every session reports its state back to Thurbox. The session list then shows, at a glance, which agents are working , blocked , done , or idle — a colored dot per row, rolled up per repo group.

On by default. Unlike other extensions , hooks ships embedded in the binary and is auto-activated on first run — the default agent's hook is pre-configured with zero setup. It installs through the ordinary extension machinery (it just lives inside Thurbox instead of being fetched).

Overview #

The hook command is always thurbox-cli session signal --state <working|blocked|done|idle> , suffixed || true so it can never break the agent. Identity comes from the injected $THURBOX_SESSION environment variable, so a hook passes no ids . The reported state is persisted on the session row and picked up by the TUI within a tick (via PRAGMA data_version ), so a hook firing headlessly is honored too.

The five states #

State Colour Glyph Meaning
Working yellow animated braille spinner (⠋⠙⠹…) the agent is actively running
Blocked red the agent needs input or approval
Done blue a turn just finished; shown until you switch away
Idle green acknowledged, never active, or at rest
Error red reserved for a crashed agent (not derived yet)

A Done session becomes Idle once you move focus off it (you've acknowledged it); a Working session that goes quiet for 10 s is treated as Idle so an interrupted turn never spins forever. Repo groups roll up to their most-urgent member ( Blocked > Error > Working > Done > Idle ), and the status colours are tunable theme keys.

Per-agent coverage #

How much each agent can report depends on the lifecycle surface its CLI exposes — Thurbox maps whatever hooks an agent offers, and the dot is accurate for the edges that agent can express:

Agent idle working blocked done
claude
opencode
codex *
antigravity
aider
copilot *
vibe *
pi *
omp *

* Experimental — the hook schema for codex, copilot, vibe, pi, and omp is newer or not exhaustively verified against the live CLI. If event names differ, the fix is a one-file edit to the extension payload (no code change), and an unrecognized hook is a fail-open no-op ( || true ) so the agent is never broken.

How each agent is wired #

  • claude — a managed settings file passed via --settings (claude merges it with your own settings, never clobbering them). SessionStart → idle, UserPromptSubmit / PreToolUse → working, Stop → done, and Notification → blocked only for permission/approval prompts (an idle nudge is ignored so a session never flips to a false red).
  • opencode — a plugin dropped into ~/.config/opencode/plugin/ (only when opencode is installed). session.created → idle, chat.message → working, permission.asked → blocked, session.idle → done.
  • codex (experimental) — codex's claude-shaped hooks.json is JSON-merged into ~/.codex/hooks.json (reversibly; uninstall prunes exactly our entries). SessionStart → idle, UserPromptSubmit / PreToolUse → working, Stop → done. No blocked — codex's top-level hooks have no permission event. This replaced the older notify override (which reported done only).
  • antigravity — the agy CLI loads hooks from its shared ~/.gemini/settings.json , so Thurbox JSON-merges its entries in without clobbering your settings. agy adopted claude's hook schema (verified against agy 1.0.9), so the mapping mirrors claude: SessionStart → idle, PreToolUse → working, Stop → done, and Notification → blocked (permission/approval prompts only). It has no UserPromptSubmit , so working is signaled at the first tool call.
  • aider--notifications-command reports the only edge aider exposes: blocked (waiting for input).
  • vibe (experimental) — Mistral Vibe loads hooks from ~/.vibe/hooks.toml ; Thurbox drops a managed file in (only when vibe is installed). Verified against vibe 2.21.0: pre_tool → working, post_agent → done. No blocked — vibe's only hook types are pre_tool / post_tool / post_agent (no permission event), so a tool awaiting approval reads as working. If you already maintain your own hooks.toml the write is refused (never clobbered) — vibe simply goes unreported.
  • copilot (experimental) — GitHub Copilot CLI loads hooks from its own ~/.copilot/hooks/ dir, so Thurbox drops a managed standalone file in (only when copilot is installed), never touching your other hook files. sessionStart → idle, userPromptSubmitted / preToolUse → working, agentStop → done, and notification matched to permission_prompt → blocked. Both bash and powershell commands ship, so status works on Windows too.
  • pi (experimental) — the pi.dev CLI auto-discovers TypeScript extensions from ~/.pi/agent/extensions/ , so Thurbox drops a managed one in (only when pi is installed). session_start → idle, agent_start / tool_execution_start → working, agent_end → done, and a structured ask_user_question tool call → blocked (pi has no permission hook, so a turn that ends by asking in prose signals done, not blocked).
  • omp (experimental) — Oh My Pi is Pi-compatible and likewise auto-discovers TypeScript extensions, from ~/.omp/agent/extensions/ , so Thurbox drops a managed one in (only when omp is installed). It mirrors pi's extension but maps OMP's structured user-question tool — named ask — to blocked (it recognizes both ask and pi's ask_user_question ). Verified against OMP 17.0.6.

These use three manifest capabilities — [[agent_patches]] (append args to an agent), [[external_files]] (place a file in an agent's own config dir), and [[config_merges]] (reversibly deep-merge JSON into an agent's shared config). Each is fully reversible: uninstall removes exactly what was added, matched by the session signal marker.

Where the config lives #

The wiring is applied only to agents Thurbox launches — it never edits your own global agent config (e.g. your personal ~/.claude/settings.json ). For claude the managed hooks file is passed with --settings , which claude merges on top of your own settings: inside a Thurbox session both your hooks and Thurbox's fire, while a plain claude outside Thurbox sees only your own. The other agents are wired by a reversible merge into — or a managed file dropped in — their own config dir.

Agent On-disk location How it's applied
claude ~/.config/thurbox/hooks/claude.json --settings flag on the claude agent (claude merges it)
aider — (no file) --notifications-command flag on the aider agent
opencode ~/.config/opencode/plugin/thurbox-status.js managed plugin file
codex ~/.codex/hooks.json reversible JSON-merge of our entries
vibe ~/.vibe/hooks.toml managed file (refused if you already have one)
copilot ~/.copilot/hooks/thurbox-status.json managed standalone file
antigravity ~/.gemini/settings.json reversible JSON-merge of our entries
pi ~/.pi/agent/extensions/thurbox-status.ts managed extension file (refused if you already have one)
omp ~/.omp/agent/extensions/thurbox-status.ts managed extension file (refused if you already have one)

The home dir is ~/.config/thurbox/hooks for a release build and ~/.config/thurbox-dev/hooks for a dev build, so the two stay isolated.

Inspect or customize. To see exactly what Thurbox installed, read the file for the agent above (e.g. cat ~/.config/thurbox/hooks/claude.json ). The injected --settings / --notifications-command flags themselves live in the claude / aider entries of ~/.config/thurbox/agents.toml . You can hand-edit a managed file, but self-heal rewrites it from the embedded payload on the next TUI start / heartbeat tick — so to keep a change, either deactivate the extension and wire the hook yourself, or edit the payload source under extensions/hooks/ and reinstall.

Opt out #

Hooks is the one extension that's on without you asking. Turn it off (and back on):

bash
thurbox-cli extension deactivate hooks   # remove the wiring; won't come back
thurbox-cli extension activate hooks     # re-enable it

Deactivating records an opt-out flag, so startup self-heal won't resurrect it. All the per-agent wiring is reversible, so opting out leaves your agent configs exactly as they were.

Files #

See extensions/hooks for the README.md , the extension.toml manifest, and the per-agent payloads ( claude.json , codex-hooks.json , antigravity-hooks.json , opencode-status.js , vibe-hooks.toml , copilot-hooks.json ).