Skip to content

Hooks Integration

Abbado uses CLI hooks to get real-time visibility into what agents are doing. Instead of parsing terminal output, the CLI itself calls Abbado’s hook endpoint on every lifecycle event.

This is the foundation of Abbado’s dashboard features: status tracking, activity feed, notifications, and task progress all come from hooks.

When Abbado spawns a CLI session, it:

  1. Creates a hook script (hook.sh) that POSTs to POST /api/runs/{run-id}/hook
  2. Generates a hooks configuration (claude-hooks.json) that maps all CLI events to the hook script
  3. Launches the CLI with --settings <path-to-hooks-config>

The hook script is simple — it pipes stdin (the hook payload) to the Abbado API:

#!/bin/bash
RESPONSE=$(curl -s -X POST "http://127.0.0.1:3000/api/runs/<run-id>/hook" \
-H "Content-Type: application/json" -d @- 2>/dev/null)
echo "$RESPONSE"

Abbado registers handlers for all Claude Code hook events:

Trigger: User sends a prompt to the agent.

Abbado behavior: Marks the run as “running” if it was idle/queued. This is how Abbado knows the agent is actively working.

Trigger: Agent finishes responding to a prompt.

Abbado behavior: Marks the run as “paused” (idle). If notifications are enabled for this run, sends a Discord notification with the last assistant message.

Trigger: Agent encounters an error while processing.

Abbado behavior: Marks the run as “failed”. Sends a Discord notification with the error message.

Trigger: User quits the CLI or the session terminates.

Abbado behavior: Marks the run as “completed”. Sends a Discord notification if enabled.

Trigger: Agent is about to use a tool (Read, Edit, Bash, etc.).

Abbado behavior: Persists an agent.tool_call event with the tool name and input. This powers the real-time activity feed in the dashboard.

Trigger: A tool call completed successfully.

Abbado behavior: Persists an agent.tool_result event with the tool response.

Trigger: A tool call failed.

Abbado behavior: Persists an agent.tool_result event with the error message.

Trigger: Claude Code sends a notification (permission prompt, idle prompt, etc.).

Abbado behavior:

  • Persists an agent.needs_attention event for the frontend to show a badge
  • Sends a Discord notification for permission_prompt and idle_prompt types

Trigger: Agent needs permission to perform an action.

Abbado behavior: Does nothing — the user handles permissions directly in the terminal. Abbado does not auto-approve.

Trigger: Agent spawns or stops a subagent.

Abbado behavior: Persists agent.subagent_start / agent.subagent_stop events for the activity feed.

The generated hooks configuration file maps each event to the hook script:

{
"hooks": {
"UserPromptSubmit": [{ "hooks": [{ "type": "command", "command": "/path/to/hook.sh" }] }],
"Stop": [{ "hooks": [{ "type": "command", "command": "/path/to/hook.sh" }] }],
"StopFailure": [{ "hooks": [{ "type": "command", "command": "/path/to/hook.sh" }] }],
"SessionEnd": [{ "hooks": [{ "type": "command", "command": "/path/to/hook.sh" }] }],
"PreToolUse": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "/path/to/hook.sh" }] }],
"PostToolUse": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "/path/to/hook.sh" }] }],
"PostToolUseFailure": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "/path/to/hook.sh" }] }],
"Notification": [{ "hooks": [{ "type": "command", "command": "/path/to/hook.sh" }] }],
"PermissionRequest": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "/path/to/hook.sh" }] }],
"SubagentStart": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "/path/to/hook.sh" }] }],
"SubagentStop": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "/path/to/hook.sh" }] }]
}
}

For Codex CLI, hooks are set up differently. Instead of --settings, Codex uses its own hook system installed via abbado_adapters::codex::install_run_hooks. The hook endpoint response format includes a hookSpecificOutput wrapper for Codex compatibility.

CLI (claude/codex)
→ hook fires (e.g., PreToolUse)
→ hook.sh pipes JSON to POST /api/runs/{id}/hook
→ Abbado persists event to SQLite
→ Abbado broadcasts event via SSE
→ Frontend receives event and updates UI

All hook events are persisted in the database and available via the Events API.