Skip to main content

Hooks reference

Every event, field and exit code. To get started, see Hooks.

Configuration​

{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.abacusai/hooks/guard.sh",
"if": "Bash(git *)",
"timeout": 30,
"statusMessage": "Checking the command"
}
]
}
]
},
"disableAllHooks": false
}

Hooks are read from ~/.abacusai/config.json, from <project>/.abacusai/config.json in a trusted workspace, and from each --settings <file>. Hooks from every file run. Identical handlers matched by one event run once. "disableAllHooks": true in any of these files turns every hook off.

Handler fields​

FieldDefaultWhat it does
type"command", the only type that runs
commandThe command line, run by shell. With args, the program to run directly, with no shell.
argsArguments for command, which then runs without a shell
shellbashbash or powershell
timeout600Seconds before the hook is stopped
statusMessageShown while the hook runs
ifA permission rule the tool call must match. See The if field.
oncefalseRun at most once per session
asyncfalseRun in the background. Its output decides nothing.
onFailurecontinueblock treats a failed hook (crash, timeout, invalid JSON) as a block, so the hook fails closed

Matchers​

MatcherMatches
absent, "" or *Everything
Letters, digits, _, -, spaces, | and , onlyAn exact name, or a list: Bash, Edit|Write
Anything elseA JavaScript regular expression: mcp__github__.*, ^(Read|Grep)$

What the matcher is compared with depends on the event:

EventMatcher is compared with
PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDeniedThe tool name
SessionStartsource: startup, resume, clear, compact, fork
SessionEndreason: clear, resume, logout, prompt_input_exit, other
Notificationnotification_type: permission_prompt, agent_needs_input, idle_prompt
StopFailureerror
SubagentStart, SubagentStopThe agent type on a local provider. On Abacus.AI models only * matches.
PreCompact, PostCompacttrigger: manual or auto
ConfigChangesource: user_settings or project_settings
PostModelSwitchThe model id switched to
UserPromptSubmit, Stop, CwdChangedNothing. The matcher is ignored.

The if field​

if is one permission rule, such as Bash(git commit *) or Edit(src/**). The hook runs only for calls the rule matches. Shell rules see every part of a compound command, so Bash(git commit *) also matches cd repo && git commit -m x. if applies to tool events only.

Events​

EventWhen it firesCan block
PreToolUseBefore a tool runsYes
PermissionRequestWhen a permission prompt is about to showYes, with JSON
PermissionDeniedWhen Auto mode refuses a callNo
PostToolUseAfter a tool succeedsFeedback only
PostToolUseFailureAfter a tool failsFeedback only
UserPromptSubmitBefore a prompt is sent, including queued and steered messagesYes
StopWhen the agent finishes a turn. Not after you stop it or after an error.Yes, the agent keeps working
StopFailureWhen a turn ends with an errorNo
SubagentStartWhen a subagent startsNo
SubagentStopWhen a subagent finishesOn a local provider
SessionStartWhen a conversation starts, resumes, is cleared, forked or compactedNo
SessionEndWhen a conversation is cleared, switched away from, signed out of or closedNo
NotificationWhen a prompt is waiting for you, or a minute after a turn ends with no replyNo
PreCompactBefore /compactYes
PostCompactAfter a compactionNo
CwdChangedWhen the session's working directory changesNo
ConfigChangeWhen your user or project settings file changesNo
PostModelSwitchWhen the model changes inside a conversationNo

On Abacus.AI models​

Conversations on Abacus.AI models run the agent on Abacus.AI's servers, and your machine runs the tools it is asked to run. Hooks act on what your machine does, so:

  • Tools the server runs itself, such as web search and web fetch, fire no hooks. Shell commands, file tools, search, MCP tools, to-dos, plan approval, questions to you and background tasks are all hooked.
  • Context from SessionStart, UserPromptSubmit and PostModelSwitch hooks is not sent to the model. The conversation shows a warning. Blocking a prompt and setting the title still work.
  • SubagentStart and SubagentStop only observe. The subagent has already run.

On a local provider every tool is hooked and all of the above works.

Input​

Each hook gets one JSON object on stdin.

Common fields​

FieldValue
session_idThe conversation id
transcript_pathPath to a JSONL transcript of the conversation in Claude Code's format
cwdThe workspace folder
permission_modedefault (Supervise), acceptEdits, plan, auto, dontAsk or bypassPermissions (Unsupervised)
hook_event_nameThe event, such as PreToolUse

Per-event fields​

EventFields beyond the common ones
PreToolUsetool_name, tool_input, tool_use_id
PostToolUseSame, plus tool_response, duration_ms
PostToolUseFailureSame as PreToolUse, plus error, is_interrupt
PermissionRequestSame as PreToolUse, plus permission_suggestions
PermissionDeniedSame as PreToolUse, plus reason
UserPromptSubmitprompt
SessionStartsource, model
SessionEndreason
Notificationmessage, title, notification_type
Stopstop_hook_active, last_assistant_message, background_tasks
StopFailureerror, error_details, last_assistant_message
SubagentStartagent_id, agent_type
SubagentStopStop's fields, plus agent_id, agent_type
PreCompacttrigger
PostCompacttrigger, compact_summary
CwdChangedold_cwd, new_cwd
ConfigChangesource, file_path
PostModelSwitchfrom_model, to_model, requested_model, source

Tool names​

Hooks see Claude Code's tool names and input shapes. Matchers, if and permission rules use the same names.

ToolHooks seetool_input
Shell commandBashcommand, timeout, run_in_background
Read a fileReadfile_path, offset, limit
Write a fileWritefile_path, content
Edit a fileEditfile_path, old_string, new_string, replace_all
Delete a file or folderBashcommand: rm -- '<path>', or rm -rf -- '<path>' for a folder
Search contents, find filesGrep, GlobThe tool's arguments
MCP toolmcp__<server>__<tool>The MCP tool's arguments
To-do list, plan approval, question to youTodoWrite, ExitPlanMode, AskUserQuestionThe tool's arguments
Background tasksTaskOutput, TaskStop, ScheduleWakeupThe tool's arguments

file_path is always absolute. Because a delete reaches hooks as an rm command, a guard on Bash(rm *) covers it too.

Exit codes​

Exit codeResult
0Success. If stdout is one JSON object, it is the hook's output. Otherwise stdout is added as context for SessionStart, UserPromptSubmit and PostModelSwitch, and ignored elsewhere.
2Block. stderr is the reason.
Anything elseA failure. You see Hook failed and the work carries on, unless the hook sets "onFailure": "block".

What exit 2 does depends on the event:

EventEffect
PreToolUseThe tool does not run. The agent is told why.
PostToolUse, PostToolUseFailureThe tool already ran. The reason is added to its result.
UserPromptSubmitThe prompt is not sent and goes back to the composer
Stop, SubagentStopThe agent keeps working, with the reason as its next instruction
PreCompact/compact is cancelled
Other eventsNothing is blocked. stderr is shown as a warning. A PermissionRequest hook answers with JSON, not exit 2.

JSON output​

Exit 0 and print one JSON object for more control than an exit code gives.

Fields for every event​

FieldWhat it does
continuefalse stops the turn. On Stop and SubagentStop it lets the agent stop.
stopReasonShown when continue is false
systemMessageA note shown to you, not to the agent
decision, reason"decision": "block" with a reason, on the events where exit 2 blocks
hookSpecificOutputThe event's own fields, below

Per event​

EventhookSpecificOutput fields
PreToolUsepermissionDecision (allow, deny or ask), permissionDecisionReason, updatedInput, additionalContext
PostToolUseupdatedToolOutput, updatedMCPToolOutput, additionalContext
PostToolUseFailureadditionalContext
PermissionRequestdecision: behavior (allow or deny), updatedInput, updatedPermissions, message, interrupt
PermissionDeniedretry
UserPromptSubmit, SessionStartadditionalContext, sessionTitle
Stop, SubagentStop, SubagentStart, PostModelSwitchadditionalContext

The other events only observe.

What the decisions do​

OutputEffect
denyBlocks the call, like exit 2
allowSkips the permission prompt. It does not override a deny or ask rule, and does not approve a write to a protected file, the removal of a critical folder such as your home folder, or an MCP tool whose server requires your confirmation. Those still ask.
askShows a permission prompt with your reason, in every mode. In Don't ask mode the call is declined unless a PermissionRequest hook allows it.
Several hooks disagreeDeny beats ask, and ask beats allow
updatedInputReplaces the tool's input. Include every field, since a field you leave out is dropped. The new input is checked against your permission rules again.
updatedToolOutputReplaces the result the agent reads
additionalContextOn a tool event, added to the tool's result. On Stop, continues the turn.
PermissionRequest decisionallow answers the prompt for you and deny refuses the call. interrupt: true also stops the turn.
retry: trueOn PermissionDenied, tells the agent it may try again
sessionTitleRenames the conversation

A Stop hook can keep the agent going eight times in a row. After that the agent stops and you are told.

A PermissionRequest hook that allows a call can also change permissions with updatedPermissions: add, remove or replace rules, add or remove directories, or set the mode. Each entry's destination decides whether the change lasts for the session only (session) or is also saved to <project>/.abacusai/config.json (projectSettings or localSettings) or ~/.abacusai/config.json (userSettings). See Permissions for what a rule can and cannot approve.

Text a hook returns, such as a reason or context, is capped at 10,000 characters.

Running​

Hooks matched by one event run in parallel, and the call waits for all of them. Each runs in the workspace folder. A hook gets 600 seconds unless it sets timeout, and stopping the turn stops its hooks too.

Environment variableValue
CLAUDE_PROJECT_DIRThe workspace folder
ABACUS_PROJECT_DIRThe same

Hooks otherwise inherit the environment the CLI or extension runs in, minus ABACUS_API_KEY and ROUTELLM_API_KEY.

In print mode the same hooks run, under the same trust rule.

With --output-format text or json, blocks, failures and hook notes are written to stderr. If a UserPromptSubmit hook blocks the prompt, it is not sent, the json result has subtype: "prompt_blocked", and the process exits with code 2.

With --output-format stream-json, each hook run is a pair of system lines, hook_started and then hook_response:

{"type":"system","subtype":"hook_started","hook_id":"5f…","hook_name":"PreToolUse:Bash","hook_event":"PreToolUse","tool_call_id":"t1","run":{"status":"running"},"session_id":"c1","uuid":"…"}
{"type":"system","subtype":"hook_response","hook_id":"5f…","hook_name":"PreToolUse:Bash","hook_event":"PreToolUse","tool_call_id":"t1","outcome":"error","exit_code":2,"stderr":"rm is not allowed\n","run":{"status":"blocked","duration_ms":41,"entries":[{"kind":"feedback","text":"rm is not allowed"}]},"session_id":"c1","uuid":"…"}
FieldValue
outcomesuccess, error (exit 2 or a crash) or cancelled (timed out or stopped)
output, stdout, stderr, exit_codeThe raw run
run.statusrunning, then completed, blocked, failed or stopped
run.entriesWhat the hook said: warning, feedback (a block's reason), stop or error

Unlike Claude Code, which writes hook lines only with --include-hook-events, these lines are always written. See Scripting and CI for the full protocol.

Differences from Claude Code​

The format is Claude Code's: the settings shape, matchers, if, stdin fields, exit codes and JSON output. What differs:

Claude CodeHere
Hooks in ~/.claude/settings.json, .claude/settings.json and .claude/settings.local.jsonHooks in .abacusai/config.json files. Copy yours with /import.
command, http, mcp_tool, prompt and agent handlerscommand only. Other types are skipped with a warning.
Hooks from plugins, and from skill and subagent frontmatterNot loaded. A hook that uses CLAUDE_PLUGIN_ROOT needs the variable set in its command.
PreToolUse can return deferdefer is ignored and the normal permission check applies
allowManagedHooksOnly and other managed hook policyNot available
Every tool call is hookedTools the Abacus.AI server runs itself are not. See On Abacus.AI models.

These Claude Code events are accepted in your config but never fire, and a warning lists any you use: PostToolBatch, UserPromptExpansion, PreModelSwitch, InstructionsLoaded, DirectoryAdded, WorktreeCreate, WorktreeRemove, FileChanged, MessageDisplay, TaskCreated, TaskCompleted, TeammateIdle, Setup, Elicitation and ElicitationResult.

Sources: Claude Code's Hooks reference and hooks guide.

For further help, reach out at support@abacus.ai.