Hooks Reference
Version requirement: This guide targets the Claude Code Hooks–compatible implementation shipped with CodeBuddy Code v1.16.0 and later. Feature status: Hooks are currently in Beta; APIs and runtime behavior may evolve.
Hooks let you inject custom scripts or commands into every stage of a CodeBuddy Code session so you can automate validation, bootstrap environments, run compliance checks, and more. Our implementation is fully compatible with the Claude Code Hooks specification, including event names, payload schemas, and safety guarantees.
Feature Overview
- Full support for the hook event family (27+ events), covering tool lifecycle (
PreToolUse/PostToolUse/PostToolUseFailure), session and sub-agent (SessionStart/SessionEnd/Stop/SubagentStart/SubagentStop/StopFailure), user interaction (UserPromptSubmit/Notification/PermissionRequest/PermissionDenied/Elicitation/ElicitationResult), context (PreCompact/PostCompact/InstructionsLoaded/ConfigChange), task and team (TaskCreated/TaskCompleted/TeammateIdle), file and environment (FileChanged/CwdChanged/WorktreeCreate/WorktreeRemove), and bootstrap/maintenance (Setup). For the full event list, see the event table in the plugin reference. - Declare hooks directly in the frontmatter of a custom Agent / Skill, with scope bound to the subagent lifecycle (see the section at the end of this document).
- Regex-based matchers to route execution by tool name or event context.
- Automatic injection of
session_id, transcript paths, working directory, and other context. - Dual signaling via exit codes and JSON payloads to mirror Claude Code semantics.
/hooksCLI panel for reviewing and approving any configuration changes before they take effect, ensuring safety.- Individual hook processes time out after 60 seconds so one slow script will not block the rest.
Configuration
CodeBuddy Code stores hook configuration inside your settings files:
| Scope | Path | Notes |
|---|---|---|
| User | ~/.codebuddy/settings.json | Applies to every project |
| Project | <project-root>/.codebuddy/settings.json | Shared with the whole repo |
| Project local | <project-root>/.codebuddy/settings.local.json | Local-only overrides (not committed) |
| Enterprise policy | Distributed policy bundle | Managed centrally by your org |
Merge behavior: Hooks from different scopes are merged, not overwritten. All matching hooks for the same event run in parallel.
Structure
Hooks are grouped by matcher, and each matcher may contain multiple hook definitions:
json
{
"hooks": {
"EventName": [
{
"matcher": "ToolPattern",
"hooks": [
{
"type": "command",
"command": "your-command-here"
}
]
}
]
}
}Key fields:
- matcher – Regex pattern for tool names, case-sensitive (only for
PreToolUseandPostToolUse).- Simple string match:
Writematches any tool name containing "Write" (e.g.,Write,NotebookWrite). - Exact match: Use
^Write$to match only the Write tool. - Multiple tools:
Edit|WriteorWeb.*. - Match all tools: Any of the following are equivalent:
- Use
* - Use empty string
"" - Omit the
matcherfield
- Use
- Simple string match:
- hooks – Array executed when the matcher hits.
- type – Execution mode. Use
"command"for shell commands, or"prompt"for LLM-based evaluation. - command – (type =
command) Shell command to run.$CODEBUDDY_PROJECT_DIRis available. Executed using the user's default shell ($SHELL) on macOS/Linux, and Git Bash is enforced on Windows (cmd.exe and PowerShell are not supported), so commands must be compatible with bash syntax. - prompt – (type =
prompt) Prompt text sent to the LLM for evaluation (only supportsStop,UserPromptSubmit, andPreToolUseevents). - timeout – Optional timeout in seconds for the specific hook.
- type – Execution mode. Use
For events that do not use matchers (UserPromptSubmit, Stop, SubagentStop, etc.) you can omit the field entirely:
json
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "python3 /path/to/prompt-validator.py"
}
]
}
]
}
}Project-Scoped Hook Scripts
Use the CODEBUDDY_PROJECT_DIR environment variable (available while CodeBuddy builds the hook command) to reference scripts stored in the repo:
json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "\"$CODEBUDDY_PROJECT_DIR\"/.codebuddy/hooks/check-style.sh"
}
]
}
]
}
}Tip: If your hook script is a Python file, explicitly use
python3to invoke it instead of directly executing the.pyfile. This is because even if the script includes a shebang line (#!/usr/bin/env python3), Git Bash on Windows may not correctly recognize it:json"command": "python3 \"$CODEBUDDY_PROJECT_DIR\"/.codebuddy/hooks/my_hook.py"
Plugin Hooks
Plugins can ship hooks that merge seamlessly with user or project settings. When you enable a plugin, its hooks are merged automatically.
How plugin hooks work:
- Defined inside
hooks/hooks.jsonwithin the plugin bundle, or via a custom path referenced from thehooksfield. - Activated hooks merge with user and project hooks.
- Multiple hook sources can all respond to the same event.
${CODEBUDDY_PLUGIN_ROOT}points to the plugin directory for script references.
Example:
json
{
"description": "Automatic code formatting",
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CODEBUDDY_PLUGIN_ROOT}/scripts/format.sh",
"timeout": 30
}
]
}
]
}
}Prompt-Based Hooks
Besides shell commands (type: "command"), CodeBuddy Code also supports prompt-based hooks (type: "prompt") that use an LLM to evaluate whether to allow or block an action.
Supported events: Currently only
Stop,UserPromptSubmit, andPreToolUseevents are supported.
How Prompt Hooks Work
Instead of running a bash command, the hook:
- Sends the hook input plus your prompt to a fast LLM (Haiku).
- Receives a structured JSON decision.
- Lets CodeBuddy Code enforce that decision.
Supported Events
| Event | Use Case |
|---|---|
Stop | Intelligently decide whether CodeBuddy should continue working |
UserPromptSubmit | Use LLM to assist in validating user prompts |
PreToolUse | Make context-aware permission decisions |
Comparison with Command Hooks
| Feature | Command Hooks | Prompt Hooks |
|---|---|---|
| Execution | Runs bash scripts | Queries LLM |
| Decision logic | You implement in code | LLM evaluates context |
| Setup complexity | Requires script files | Just configure a prompt |
| Context awareness | Limited by script logic | Natural language understanding |
| Performance | Fast (local execution) | Slower (API call) |
| Best for | Deterministic rules | Context-aware decisions |
Configuration
json
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Evaluate if CodeBuddy should stop: $ARGUMENTS. Check if all tasks are complete."
}
]
}
]
}
}Fields:
- type – Must be
"prompt". - prompt – Text sent to the LLM. Use
$ARGUMENTSas a placeholder for the hook input JSON, which will be directly replaced. When$ARGUMENTSis not present, the input JSON is appended in the format\n\nARGUMENTS:\n{JSON}. - timeout – Optional timeout in seconds (default: 30s).
Response Schema
The LLM must return JSON:
jsonc
{
"ok": true | false,
"reason": "Explanation for the decision" // Required when ok is false
}Response fields:
ok:trueto allow the operation,falseto block itreason: Required whenokisfalse, explanation shown to CodeBuddy
Example: Smart Stop Hook
json
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "You are evaluating whether CodeBuddy should stop working. Context: $ARGUMENTS\n\nAnalyze the conversation and determine if:\n1. All user-requested tasks are complete\n2. Any errors need to be addressed\n3. Follow-up work is needed\n\nRespond with JSON: {\"ok\": true} to allow stopping, or {\"ok\": false, \"reason\": \"your explanation\"} to continue working.",
"timeout": 30
}
]
}
]
}
}Example: UserPromptSubmit Validation
json
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Evaluate if this user prompt is safe and appropriate. Input: $ARGUMENTS\n\nCheck if:\n- The prompt contains sensitive information (passwords, secrets)\n- The request is clear and actionable\n- Any security concerns exist\n\nReturn: {\"ok\": true} to allow, or {\"ok\": false, \"reason\": \"explanation\"} to block."
}
]
}
]
}
}Example: PreToolUse Permission Decision
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "prompt",
"prompt": "Evaluate if this bash command should be allowed. Input: $ARGUMENTS\n\nCheck if:\n- The command is safe and non-destructive\n- It doesn't access sensitive files or directories\n- It aligns with the user's stated goals\n\nReturn: {\"ok\": true} to allow, or {\"ok\": false, \"reason\": \"explanation\"} to deny."
}
]
}
]
}
}Best Practices
- Be specific in your prompts: Clearly describe what you want the LLM to evaluate
- Include decision criteria: List the factors the LLM should consider
- Test your prompts: Verify the LLM makes correct decisions for your use cases
- Set appropriate timeouts: Default is 30 seconds, adjust if needed
- Use for complex decisions: Command hooks are better suited for simple, deterministic rules
Hook Events
Event Matrix
| Event | Trigger | Matcher support | Typical use cases |
|---|---|---|---|
PreToolUse | Before a tool executes | Yes (tool name) | Command validation, approvals, logging |
PostToolUse | After a tool succeeds | Yes | Auto-formatting, context enrichment |
Notification | Permission prompts or 60s idle reminders | Partial | Desktop alerts, IM pings |
UserPromptSubmit | User message submitted (internal commands excluded) | No | Content review, context injection |
Stop | Main agent reply finishes | No | Force continuation, add reminders |
SubagentStop | Sub-agent (Task tool) completes | No | Extend or annotate sub-tasks |
PreCompact | Before context compaction | Yes (manual / auto) | Preserve key info, avoid lossy compression |
SessionStart | Session creation or resume | Yes (startup / resume / clear / compact) | Env bootstrap, variable injection |
SessionEnd | Session terminated | Yes (clear / logout / prompt_input_exit / other) | Cleanup, log persistence |
PreToolUse
Runs after CodeBuddy builds tool arguments but before executing the tool.
Common matchers:
Task– Sub-agent tasksBash– Shell commandsGlob– File globbingGrep– Content searchRead– File readsEdit– File editsWrite– File writesWebFetch,WebSearch– Web operations
PostToolUse
Runs immediately after a tool succeeds. Uses the same matcher values as PreToolUse.
Notification
Runs when CodeBuddy emits a notification. Matchers filter by notification type.
Supported matchers (partial):
permission_prompt– Permission requests from CodeBuddy Codeidle_prompt– When CodeBuddy is waiting for user input (after idle > 60 seconds)auth_success– Auth success notificationelicitation_dialog– When CodeBuddy Code needs MCP tool–elicited input (not yet supported)
Example:
json
{
"hooks": {
"Notification": [
{
"matcher": "permission_prompt",
"hooks": [
{
"type": "command",
"command": "/path/to/permission-alert.sh"
}
]
},
{
"matcher": "idle_prompt",
"hooks": [
{
"type": "command",
"command": "/path/to/idle-notification.sh"
}
]
}
]
}
}UserPromptSubmit
Runs after the user submits a prompt but before CodeBuddy processes it. Useful for injecting context, validating, or blocking certain prompts.
Stop
Runs when the primary CodeBuddy agent finishes responding (skipped if the user manually interrupts).
SubagentStop
Runs when a CodeBuddy sub-agent (Task tool) finishes.
PreCompact
Runs before CodeBuddy compacts conversation context.
Matchers:
manual– Triggered via/compactauto– Triggered automatically when the context window fills up
SessionStart
Runs when CodeBuddy starts a new session or resumes an existing one.
Matchers:
startup– Fresh startresume– Triggered by--resume,--continue, or/resumeclear– Triggered by/clearcompact– Triggered by auto/manual compaction
SessionEnd
Runs when a session closes—use it to free resources or persist logs.
reason field values:
clear– Session cleared via/clearlogout– User signed outprompt_input_exit– User exited while the prompt input box was visibleother– Any other exit reason (including normal exit)
Hook Input
Each hook receives JSON over stdin containing session metadata plus event-specific fields:
jsonc
{
// Common fields
"session_id": "string",
"transcript_path": "string", // Path to the conversation JSON
"cwd": "string", // Working directory when the hook runs
"permission_mode": "string", // "default", "plan", "acceptEdits", or "bypassPermissions"
// Event-specific properties
"hook_event_name": "string"
// ...
}PreToolUse Input
json
{
"session_id": "abc123",
"transcript_path": "/Users/.../.codebuddy/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "PreToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "/path/to/file.txt",
"content": "file content"
}
}PostToolUse Input
json
{
"session_id": "abc123",
"transcript_path": "/Users/.../.codebuddy/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "PostToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "/path/to/file.txt",
"content": "file content"
},
"tool_response": {
"filePath": "/path/to/file.txt",
"success": true
}
}Notification Input
json
{
"session_id": "abc123",
"transcript_path": "/Users/.../.codebuddy/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "Notification",
"message": "CodeBuddy needs your permission to use Bash",
"notification_type": "permission_prompt"
}UserPromptSubmit Input
json
{
"session_id": "abc123",
"transcript_path": "/Users/.../.codebuddy/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "UserPromptSubmit",
"prompt": "Write a function to calculate the factorial of a number"
}Stop & SubagentStop Input
stop_hook_active becomes true when a stop hook has already resumed execution.
json
{
"session_id": "abc123",
"transcript_path": "/Users/xxx/.codebuddy/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"permission_mode": "default",
"hook_event_name": "Stop",
"stop_hook_active": true
}PreCompact Input
For manual triggers, custom_instructions comes from the /compact command. Auto triggers leave it empty.
json
{
"session_id": "abc123",
"transcript_path": "/Users/xxx/.codebuddy/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"permission_mode": "default",
"hook_event_name": "PreCompact",
"trigger": "manual",
"custom_instructions": ""
}SessionStart Input
json
{
"session_id": "abc123",
"transcript_path": "/Users/xxx/.codebuddy/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"permission_mode": "default",
"hook_event_name": "SessionStart",
"source": "startup"
}SessionEnd Input
json
{
"session_id": "abc123",
"transcript_path": "/Users/xxx/.codebuddy/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "SessionEnd",
"reason": "other"
}Hook Output
Hooks communicate back to CodeBuddy Code in two ways.
Basic Mode: Exit Codes
Use exit status, stdout, and stderr to convey results:
- Exit code 0 – Success. Stdout appears in transcript mode (Ctrl+R), except for
UserPromptSubmitandSessionStart, where stdout is appended to the context. - Exit code 2 – Blocking error. Message source priority: stdout (JSON
reason/stopReasonfield or plain text) > stderr. Stderr is only a fallback – it is only passed to CodeBuddy when stdout has no output. Therefore, debug logs can safely be written to stderr without polluting the feedback message to the Agent. - Other exit codes – Non-blocking errors. Stderr is shown to the user and execution proceeds.
Exit Code 2 Behavior
Note: "message" in the table below refers to the message retrieved from stdout or stderr based on priority (see fallback explanation above).
| Event | Effect |
|---|---|
| PreToolUse | Blocks the tool call and surfaces message to Agent |
| PostToolUse | Surfaces message to Agent (tool already ran, used for context injection) |
| Notification | N/A – message shown to the user |
| UserPromptSubmit | Blocks the prompt, clears it, message shown to user only |
| Stop | Blocks the stop action, surfaces message to Agent and continues conversation |
| SubagentStop | Blocks the sub-agent stop, surfaces message to sub-agent and continues execution |
| PreCompact | Blocks compaction, message shown to user only |
| SessionStart | N/A – message shown to the user |
| SessionEnd | N/A – message shown to the user |
Advanced Mode: JSON Output
Write structured JSON to stdout for granular control.
Common JSON Fields
All hook types may include these optional fields:
jsonc
{
"continue": true, // Whether CodeBuddy proceeds after the hook (default true)
"stopReason": "string", // Message shown to CodeBuddy when continue is false
"reason": "string", // Alias for stopReason, both are equivalent
"suppressOutput": true, // Hide stdout in transcript mode (default false)
"systemMessage": "string" // Optional warning shown to user only (not passed to Agent)
}Message field notes:
stopReason/reason: Message passed to CodeBuddy Agent explaining why the operation was blockedsystemMessage: Warning shown to user only, not passed to Agent
PreToolUse Decision Control
PreToolUse hooks can control whether the tool call proceeds.
jsonc
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow" | "deny" | "ask",
"permissionDecisionReason": "Reason shown in permission dialog",
"modifiedInput": {
"field_to_modify": "new value"
}
}
}"allow"– Bypass the permission system and execute the tool directly"deny"– Block the tool call,permissionDecisionReasonis passed to Agent"ask"– Force the UI to prompt the user,permissionDecisionReasonis shown in the confirmation dialogmodifiedInput– Mutate tool arguments before execution (partial field override)
PostToolUse Context Injection
PostToolUse fires after the tool has executed, so it cannot truly "block" the operation. Instead, use it to inject additional context to the Agent.
jsonc
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "Additional information for CodeBuddy, e.g., code review results"
}
}Note: The
decision: "block"field is deprecated. Since the tool has already executed, blocking has no effect.
UserPromptSubmit Decision Control
jsonc
{
"continue": false, // Set to false to block prompt processing
"reason": "Block reason (shown to user only)",
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "Additional context injected to CodeBuddy"
}
}Note: The
decision: "block"field is deprecated. Usecontinue: falseinstead.
Stop / SubagentStop Decision Control
jsonc
{
"continue": false, // Set to false to prevent stopping, Agent continues working
"reason": "Tell the Agent why it needs to continue working"
}Note: The
decision: "block"field is deprecated. Usecontinue: falseinstead.
SessionStart Decision Control
jsonc
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "My additional context here"
}
}Using MCP Tools
Hooks integrate seamlessly with MCP (Model Context Protocol) tools.
MCP Tool Naming
MCP tool names follow the pattern mcp__<server>__<tool>, for example:
mcp__memory__create_entities–create_entitieson the Memory servermcp__filesystem__read_file–read_fileon the Filesystem servermcp__github__search_repositories– GitHub search tool
Configuring Hooks for MCP Tools
Target an individual tool or an entire MCP server:
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "mcp__memory__.*",
"hooks": [
{
"type": "command",
"command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"
}
]
},
{
"matcher": "mcp__.*__write.*",
"hooks": [
{
"type": "command",
"command": "python3 /home/user/scripts/validate-mcp-write.py"
}
]
}
]
}
}Security Notes
Disclaimer
Use at your own risk. Hooks run arbitrary shell commands on your machine. By enabling them you acknowledge:
- You are solely responsible for the commands you configure.
- Hooks can read, modify, or delete anything your user can access.
- Malicious or buggy hooks can cause data loss or system damage.
- Tencent Cloud offers no warranty and is not liable for any damages.
- Always test hooks in a safe environment before rolling them into production.
Best Practices
- Validate and sanitize input – Never trust incoming data blindly.
- Quote shell variables – Use
"$VAR", not$VAR. - Prevent path traversal – Guard against
..in file paths. - Use absolute paths – Especially for scripts (or
"$CODEBUDDY_PROJECT_DIR"). - Skip sensitive files – Avoid touching
.env,.git/, secrets, etc.
Configuration Safety
Editing the settings file does not hot-load hooks. CodeBuddy Code:
- Captures a snapshot at startup.
- Uses that snapshot for the whole session.
- Warns if the file changes externally.
- Requires review in the
/hookspanel before changes apply.
Execution Details
- Timeout – 60 seconds by default; override per hook.
- Parallelism – All matching hooks run in parallel.
- Deduplication – Identical commands are coalesced.
- Execution Shell:
- macOS/Linux: Uses the user's default shell (
$SHELLenvironment variable, typically bash or zsh), falls back to/bin/sh. - Windows: Git Bash is enforced (cmd.exe and PowerShell are not supported). If Git Bash is not found, an error will prompt you to install Git for Windows. The
CODEBUDDY_CODE_GIT_BASH_PATHenvironment variable can be used to specify the bash.exe path. - The default shell can be overridden via the
CODEBUDDY_CODE_SHELLenvironment variable (only POSIX shells are supported: bash, zsh, sh).
- macOS/Linux: Uses the user's default shell (
- Environment – Runs inside the current working directory with CodeBuddy's environment.
CODEBUDDY_PROJECT_DIRexposes the absolute project root.
- Input – JSON over stdin.
- Output –
- PreToolUse / PostToolUse / Stop / SubagentStop: progress visible in transcript mode (Ctrl+R).
- Notification / SessionEnd: logged only when
--debugis enabled. - UserPromptSubmit / SessionStart: stdout is appended to the conversation context.
Debugging
Basic Troubleshooting
If hooks are not firing:
- Check configuration – Run
/hooksto confirm registration. - Validate JSON – Ensure the settings file parses.
- Test commands – Execute the script manually first.
- Verify permissions – Scripts must be executable.
- Inspect logs – Launch
codebuddy --debugfor hook traces.
Common mistakes:
- Unescaped quotes – Use
\"inside JSON strings. - Incorrect matcher – Tool names are case-sensitive.
- Command not found – Provide full paths.
Advanced Debugging
For tricky issues:
- Trace execution –
codebuddy --debugshows hook scheduling and output. - Validate schemas – Use external tools to simulate hook input/output.
- Check env vars – Confirm CodeBuddy exposes the expected environment.
- Test edge cases – Try unusual paths or payloads.
- Monitor resources – Ensure hooks are not exhausting CPU/RAM.
- Emit structured logs – Add logging inside your scripts.
Debug Output Example
Note: This feature is not yet supported.
codebuddy --debug prints hook execution like:
text
[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Getting matching hook commands for PostToolUse with query: Write
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 hooks for query "Write"
[DEBUG] Found 1 hook commands to execute
[DEBUG] Executing hook command: <Your command> with timeout 60000ms
[DEBUG] Hook command completed with status 0: <Your stdout>Progress messages appear in transcript mode (Ctrl+R), showing:
- Which hook is running
- The command being executed
- Success/failure status
- Output or error messages
Agent / Skill Frontmatter Hooks
In addition to configuring hooks globally in ~/.codebuddy/settings.json, you can declare a hooks field directly in the YAML frontmatter of a custom Agent's .md file or a Skill's SKILL.md. This way, hooks are distributed together with the Agent / Skill as an "atomic unit"; the scope automatically opens and closes with the subagent lifecycle, and does not pollute the main session.
Field Format
The hooks field has exactly the same structure as in settings.json — grouped by event name, each event containing several {matcher?, hooks[]} configurations. The hook type supports four kinds: command / prompt / agent / http:
yaml
---
name: my-reviewer
description: Code reviewer with pre-tool-use guard
hooks:
PreToolUse:
- matcher: Bash
hooks:
- type: command
command: ./guard.sh
once: true
SubagentStop:
- hooks:
- type: command
command: echo "reviewer finished"
- type: http
url: https://example.com/notify
method: POST
---Lifecycle and Scope
- Scope restriction: Skills only support frontmatter hooks when
context: fork(the injection path lacks a clear lifecycle boundary, so it is not wired in); custom Agents always support them. - Auto register/cleanup: When a subagent starts, frontmatter hooks are registered into
ScopedHookRegistry, and are automatically deregistered when the subagent exits. Hooks only take effect for that subagent's own tool calls / lifecycle events. Stop→SubagentStoprewrite: Writing aStopevent in the frontmatter is automatically rewritten asSubagentStop— the main session'sStopis not triggered when the subagent completes; writingStopis intended to express "the subagent itself ends" semantics.- Merged with global hooks: Under the same event, frontmatter hooks stack (do not override) with
settings.json/ pluginhooks/hooks.json, and all are triggered in parallel.
Safety Gate (allowUntrustedFrontmatterHooks)
Frontmatter hooks can silently trigger shell commands, so frontmatter hooks from non-builtin sources are not registered by default:
| Source | Registered by default |
|---|---|
| Product-builtin Agent / Skill | ✅ Auto-allowed |
.codebuddy/agents/*.md (user/project local Agent) | ❌ Denied by default |
.codebuddy/skills/SKILL.md (user/project local Skill) | ❌ Denied by default |
| Agent / Skill distributed via plugin marketplace | ❌ Denied by default |
Plugin hooks/hooks.json (not frontmatter) | ✅ Not subject to the gate |
To enable, set the following in ~/.codebuddy/settings.json:
json
{
"allowUntrustedFrontmatterHooks": true
}When blocked by the gate, the CLI emits a warning:
[AgentTask] Frontmatter hooks from skill 'xxx' skipped
(source not admin-trusted; enable `allowUntrustedFrontmatterHooks` in settings to allow)Fault Tolerance and Diagnostics
- Silently drop invalid definitions: When a single hook does not match the schema, only that entry is skipped without affecting the rest of hooks parsing; the warning includes
event 'YYY' invalid: <detailed reason>for easy debugging. - Unknown event names: Skipped with a warning (
unknown event 'XXX'); does not invalidate the entire frontmatter. - Completely broken YAML: The log emits
Malformed YAML frontmatter in '<path>'. - Runtime debugging: Launch with
CODEBUDDY_DEBUG=1to see registration logs like[ScopedHookRegistry] registered N hook config(s) for scope '<sessionId>' (...), confirming hooks are in place.
For a complete example of using frontmatter hooks in a Skill, see Skills documentation – Configuring Hooks in a Skill.
With this guide you should have a complete understanding of how hooks work inside CodeBuddy Code and how to configure them safely. For a hands-on walkthrough, continue with the Hooks Getting Started Guide.