tencent cloud

Hooks Configuration Reference

Download
Focus Mode
Font Size
Last updated: 2026-09-30 18:41:47
AI-Translated

Overview

Hooks allow you to insert custom scripts or commands within the CodeBuddy Code session lifecycle to achieve advanced capabilities such as automated validation, environment initialization, and compliance checks.
Version requirements: This document targets the Hooks implementation provided in CodeBuddy Code v1.16.0 and later.
Feature Status: The Hook feature is currently in the Beta stage, and its interfaces and behaviors may be adjusted in future versions.

Feature Overview

Fully supports the Hook event family (27+ types), covering the tool lifecycle (PreToolUse / PostToolUse / PostToolUseFailure), sessions and subagents (SessionStart / SessionEnd / Stop / SubagentStart / SubagentStop / StopFailure), user interaction (UserPromptSubmit / Notification / PermissionRequest / PermissionDenied / Elicitation / ElicitationResult), context (PreCompact / PostCompact / InstructionsLoaded / ConfigChange), tasks and teams (TaskCreated / TaskCompleted / TeammateIdle), files and environment (FileChanged / CwdChanged / WorktreeCreate / WorktreeRemove), and startup/maintenance (Setup). For the complete event list, see the event table in the plugin reference.
Declare hooks directly in the frontmatter of a custom Agent or Skill, with the scope bound to the subagent lifecycle (see the section at the end of this document for details).
Supports regex-based matchers that can filter execution by tool name or event context.
Automatically inject context information such as session_id, session transcript files, and the current working directory.
Supports dual modes of exit codes and JSON output, providing clear decision semantics.
Supports graphical configuration through the CLI /hooks panel. All external modifications take effect only after review in the panel, ensuring security.
hook scripts are automatically terminated after a 60-second timeout without affecting the execution of other hooks.

Configuration

CodeBuddy Code hooks are stored in your settings file:
Scope
File path
Description
User level
~/.codebuddy/settings.json
Applicable to all projects
Project level
<project root>/.codebuddy/settings.json
Configuration shared with project members
Project-local
<project root>/.codebuddy/settings.local.json
Local uncommitted configuration
Enterprise policy
Policy file released through integration
Centrally managed by the enterprise
Configuration merge rules: hooks from different scopes are merged rather than overwritten. All matching hooks for the same event are executed in parallel.

Structure

Hooks are organized by matcher, and each matcher can have multiple hooks:
{
"hooks": {
"EventName": [
{
"matcher": "ToolPattern",
"hooks": [
{
"type": "command",
"command": "your-command-here"
}
]
}
]
}
}
Key fields:
matcher: A regular expression pattern for matching tool names, case-sensitive (applicable only to PreToolUse and PostToolUse).
Simple string matching: Write matches any tool name containing "Write" (such as Write and NotebookWrite).
Exact matching: use ^Write$ to match only the Write tool.
Multi-tool matching: Edit|Write or Web.*
Match all tools: the following three methods are equivalent.
Use the * character.
Use an empty string "".
Omit the matcher field.
hooks: An array of hooks to be executed when a pattern matches.
type: The Hook execution type - "command" for shell commands, or "prompt" for LLM-based evaluation.
command: (For type: "command") The shell command to execute. You can use the $CODEBUDDY_PROJECT_DIR environment variable. The command runs in the user's default shell ($SHELL) on macOS/Linux, and is forced to run in Git Bash on Windows (cmd.exe or PowerShell are not supported), so it must be compatible with bash syntax.
prompt: (For type: "prompt") The prompt sent to the LLM for evaluation (only supported for Stop, UserPromptSubmit, and PreToolUse events).
timeout: (Optional) The duration in seconds after which the specific hook is canceled.
For events that do not use a matcher (such as UserPromptSubmit, Stop, and SubagentStop), you can omit the matcher field:
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "python3 /path/to/prompt-validator.py"
}
]
}
]
}
}

Project-Specific Hook Scripts

You can use the CODEBUDDY_PROJECT_DIR environment variable (available only when CodeBuddy Code generates hook commands) to reference scripts stored in your project:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "\\"$CODEBUDDY_PROJECT_DIR\\"/.codebuddy/hooks/check-style.sh"
}
]
}
]
}
}
Note:
If your hook script is a Python file, explicitly invoke it with python3 instead of executing the .py file directly. This is because Git Bash may not correctly recognize the shebang line (#!/usr/bin/env python3) on Windows, even if the script contains it:
"command": "python3 \\"$CODEBUDDY_PROJECT_DIR\\"/.codebuddy/hooks/my_hook.py"

Plugin Hooks

Plugins can provide hooks that seamlessly integrate with user and project hooks. When a plugin is enabled, its hooks are automatically merged with your configuration.
How plugin hooks work:
Plugin hooks are defined in the plugin's hooks/hooks.json file, or in a file at a custom path provided by the hooks field.
When a plugin is enabled, its hooks are merged with user and project hooks.
Multiple hooks from different sources can respond to the same event.
Plugin hooks use the ${CODEBUDDY_PLUGIN_ROOT} environment variable to reference plugin files.
Example of plugin hook configuration:
{
"description": "Automatic code formatting",
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CODEBUDDY_PLUGIN_ROOT}/scripts/format.sh",
"timeout": 30
}
]
}
]
}
}

Prompt-Based Hooks

In addition to bash command hooks (type: "command"), CodeBuddy Code also supports prompt-based hooks (type: "prompt"), which use an LLM to evaluate whether to allow or block an operation.
Supported events: Currently, only the Stop, UserPromptSubmit, and PreToolUse events are supported.
Session-level shortcut: The built-in slash command /goal is an out-of-the-box wrapper for the prompt-based Stop hook. Simply enter /goal <condition> to keep CodeBuddy working until the condition is met, without writing hook configuration manually. If your decision logic can be expressed as conditional text, use /goal first. Only fall back to writing your own prompt hook in this section when you need more complex prompt orchestration or collaboration across multiple events.

How Prompt-Based hooks Work

Prompt-based hooks do not execute bash commands. Instead, they:
1. Send the hook input and your prompt to the fast small model (the small model bound to the lite slot, mapped separately by model provider).
2. The LLM responds with structured JSON containing the decision.
3. CodeBuddy Code handles the decision automatically.

Supported Events

Event
Purpose
Stop
Intelligently decide whether CodeBuddy should continue working
UserPromptSubmit
Use LLM to assist in verifying user prompts.
PreToolUse
Make context-aware permission decisions.

Comparison with Command Hooks

Feature
Command Hooks
Prompt Hooks
Execution Method
Run bash scripts.
Query the LLM.
Decision Logic
Implement in code.
LLM evaluates the context.
Setup Complexity
Requires a script file.
Only prompt configuration required.
Context Awareness
Limited by script logic.
Natural language understanding
Performance
Fast (local execution)
Slower (API call)
Use Cases
Deterministic rules
Context-aware decision-making

Configuration

{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Evaluate if CodeBuddy should stop: $ARGUMENTS. Check if all tasks are complete."
}
]
}
]
}
}
Field:
type: Must be "prompt"
prompt: The prompt text sent to the LLM.
Use $ARGUMENTS as a placeholder for the hook input JSON. It will be replaced directly.
If $ARGUMENTS does not exist, the input JSON is appended to the end of the prompt in the \\n\\nARGUMENTS:\\n{JSON} format.
timeout: (Optional) Timeout in seconds (default: 30 seconds)
continueOnBlock: (Optional) Whether to let the Agent continue working instead of stopping when the prompt hook returns ok: false. When set to true, the behavior is similar to /goal: the reason is injected into the conversation history, and the Agent keeps looping until the condition is met. This is only meaningful for Stop/SubagentStop events. The default is false (the Agent stops).

Response Modes

The LLM must respond with JSON containing the following:
{
"ok": true | false,
"reason": "Explanation for the decision", // Required when ok is false
"impossible": false // Optional, effective only for Stop events
}
Response field:
ok: true allows the operation, and false blocks it.
reason: Required when ok is false. The explanation displayed to CodeBuddy.
impossible: An optional boolean that is only meaningful for the Stop hook. {ok: false, impossible: true} indicates that the evaluator has determined that the goal is impossible to complete in the current session (the conditions are contradictory, required resources are unavailable, or the model has exhausted all reasonable attempts). CodeBuddy stops looping, and the UI displays the "unachievable" final state. A regular {ok: false} is still treated as "not achieved, continue working".
Semantics of reason injection into history: When the Stop hook returns {ok: false}, the reason text is not simply "displayed to CodeBuddy". Instead, it is injected into the conversation history as an internal user message with isMeta=true, allowing the main model to see the evaluator's perspective in the next round and precisely complete the missing parts. This is the core mechanism by which the prompt-based Stop hook drives multi-round iterative convergence (the /goal command relies on this chain under the hood).

Example: Intelligent Stop Hook

{
"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: Stop Hook for Continuous Work (continueOnBlock)

By setting continueOnBlock: true, the prompt Stop hook can drive the Agent to continue working when the condition is not met, similar to the effect of /goal:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Check if all tests pass and code is properly formatted. Context: $ARGUMENTS\\n\\nIf tests pass and code is clean, return {\\"ok\\": true}.\\nIf not, return {\\"ok\\": false, \\"reason\\": \\"describe what still needs to be fixed\\"}.",
"continueOnBlock": true,
"timeout": 30
}
]
}
]
}
}
When continueOnBlock is true:
ok: true → The Agent stops normally.
ok: false → reason is injected into the conversation history, and the Agent continues working until the condition is met.
ok: false, impossible: true → The Agent stops and displays "Goal unachievable".
When continueOnBlock is false (default):
ok: false → The Agent stops and does not continue looping.

Example: UserPromptSubmit Validation

{
"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

{
"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."
}
]
}
]
}
}

Tips

Specify in the prompt: Clearly describe what you want the LLM to evaluate.
Include decision criteria: List the factors that the LLM should consider.
Test your prompt: Verify that the LLM makes correct decisions for your use case.
Set an appropriate timeout: The default is 30 seconds, and it can be adjusted if needed.
For complex decisions: Command hooks are more suitable for simple, deterministic rules.

Hook Events

Event type

Event
Trigger Timing
matcher Field
Typical Scenario
PreToolUse
Before tool execution
Supported (tool name)
Validate commands, perform secondary approval, and record logs.
PostToolUse
After the tool is successfully executed
Supported
Automatically format, supplement context, and compress/replace tool results.
Notification
Permission request or no-input reminder for 60 seconds
Partially supported
Desktop notifications and IM notifications
UserPromptSubmit
When a user submits a message<br/>Note: Internal commands are not included
Not supported
Content review and context injection
Stop
When the main agent response ends
Not supported
Request to continue execution and append reminders
SubagentStop
When a subagent (TaskTool) ends
Not supported
Continue sub-task execution or provide supplementary instructions
PreCompact
Before context compression
Supported (manual/auto)
Preserve key information and prevent compression
SessionStart
When a session is created or resumed
Supported (startup/resume/clear/compact)
Environment initialization and variable injection
SessionEnd
When a session ends
Supported (clear/logout/prompt_input_exit/other)
Clean up resources and persist logs

PreToolUse

It runs after CodeBuddy creates the tool parameters and before the tool call is processed.

Common matchers:

Task - Subagent task
Bash - Shell command
Glob - File pattern matching
Grep - Content search
Read - File read
Edit - File edit
Write - File write
WebFetch, WebSearch - Web operations

PostToolUse

Run immediately after the tool completes successfully. Recognize the same matcher values as PreToolUse.

Notification

Runs when CodeBuddy Code sends a notification. Supports matchers to filter by notification type.
Common matchers (partially supported):
permission_prompt - Permission request from CodeBuddy Code
idle_prompt - When CodeBuddy is waiting for user input (after being idle for more than 60 seconds)
auth_success - Authentication success notification
elicitation_dialog - When CodeBuddy Code requires input guided by an MCP tool (not yet supported)
Example:
{
"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

It runs after the user submits a prompt and before CodeBuddy processes it. This allows you to add additional context based on the prompt/conversation, validate the prompt, or block certain types of prompts.

Stop

Runs when the main CodeBuddy Code agent finishes its response. It does not run if the stop was caused by user interruption.
Session-level shortcut: The built-in slash command /goal is a wrapper for the session-scoped prompt-based Stop hook. A single line of /goal <condition> registers a Stop hook that keeps CodeBuddy working until the condition is met, and automatically handles details such as three-state evaluation (met / not met, continue / cannot be met), turn counting, token statistics, and automatic recovery with /resume. When you need session-level "keep working until X", prefer /goal instead of writing hook configuration manually.

SubagentStop

Runs when a CodeBuddy Code subagent (Agent tool call) finishes its response.

PreCompact

Runs before CodeBuddy Code is about to perform a compaction operation.
Matchers:
manual - Invoked from /compact
auto - Invoked from automatic compaction (because the context window is full)

SessionStart

Runs when CodeBuddy Code starts a new session or resumes an existing session.
Matchers:
startup - Invoked from startup
resume - Invoked from --resume, --continue, or /resume
clear - Invoked from /clear
compact - Invoked from automatic or manual compaction

SessionEnd

Runs when a CodeBuddy Code session ends. Used to clean up tasks, record session statistics, or save session state.
The reason field will be one of the following:
clear - Clears the session using the /clear command
logout - Logs out the user
prompt_input_exit - Exits when the prompt input is visible.
other - Other exit reasons (including normal exit)

Hook Input

Hooks receive JSON data through stdin that contains session information and event-specific data:
{
// Common fields
"session_id": "string",
"transcript_path": "string", // Path to the conversation JSON
"cwd": "string", // Current working directory when the hook is invoked
"permission_mode": "string", // Current permission mode: "default", "plan", "acceptEdits", or "bypassPermissions"

// Event-specific fields
"hook_event_name": "string"
// ...
}

PreToolUse Input

{
"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

{
"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

{
"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

{
"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 and SubagentStop Input

stop_hook_active is true when CodeBuddy Code has already continued as a result of a stop hook.
{
"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 triggering, custom_instructions comes from the content that the user passes to /compact. For automatic triggering, custom_instructions is empty.
{
"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

{
"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

{
"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 can return output to CodeBuddy Code in two ways.

Simple Method: Exit Code

Hooks communicate status through exit codes, stdout, and stderr:
Exit code 0: Success. stdout is displayed to the user in transcript mode (CTRL-R), except for UserPromptSubmit and SessionStart, where stdout is added to the context.
Exit code 2: Blocking error. Message source priority: stdout (the reason/stopReason field in JSON or plain text) > stderr. That is, stderr is only a fallback and is passed to CodeBuddy only when stdout produces no output. Therefore, debug logs can be safely written to stderr without polluting the feedback message to the Agent.
Other exit codes: Non-blocking error. stderr is displayed to the user, and execution continues.

Behavior of Exit Code 2

Note:
In the following table, "displayed message" refers to the message obtained from stdout or stderr by priority (see the fallback description above).
Hook Event
Action
PreToolUse
Block tool invocation and display a message to CodeBuddy.
PostToolUse
Display a message to CodeBuddy (the tool has run, providing additional context); the tool result can be replaced with updatedToolOutput.
Notification
N/A. Only display a message to the user.
UserPromptSubmit
Block prompt processing, clear the prompt, and display a message only to the user.
Stop
Block the stop, display a message to CodeBuddy, and continue the conversation.
SubagentStop
Block the stop, display a message to the CodeBuddy subagent, and continue execution.
PreCompact
Block the compaction operation and display a message only to the user.
SessionStart
N/A. Only display a message to the user.
SessionEnd
N/A. Only display a message to the user.

Advanced Method: JSON Output

Hooks can return structured JSON in stdout to enable more complex control.

Common JSON Fields

All hook types can include these optional fields:
{
"continue": true, // Whether CodeBuddy continues after the hook executes (default: true)
"stopReason": "string", // The message displayed to CodeBuddy when continue is false
"reason": "string", // An alias for stopReason, and the two are equivalent.

"suppressOutput": true, // Hides stdout in transcript mode (default: false)
"systemMessage": "string" // An optional warning message displayed to the user (not passed to the Agent)
}
Message field description:
stopReason / reason: The message passed to the CodeBuddy Agent to explain why the operation was blocked.
systemMessage: A warning message displayed only to the user and not passed to the Agent.

PreToolUse Decision Control

PreToolUse hooks can control whether tool execution continues.
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow" | "deny" | "ask",
"permissionDecisionReason": "The reason description displayed in the permission dialog",
"modifiedInput": {
"field_to_modify": "new value"
}
}
}
"allow" bypasses the permission system and executes the tool directly.
"deny" blocks the tool call execution, and permissionDecisionReason is passed to the Agent.
"ask" prompts the user to confirm the tool call in the UI, and permissionDecisionReason is displayed in the confirmation dialog.
modifiedInput allows you to modify the input parameters of the tool before execution (partial field override).

PostToolUse Context Injection and Result Replacement

PostToolUse is triggered after tool execution completes. It cannot prevent the executed operation, but it can:
1. Append additional context to the Agent (additionalContext);
2. Replace the tool result that will be sent to the Agent (updatedToolOutput).
Append context (the original tool result is retained, and a prompt is appended after it):
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "Additional information provided to CodeBuddy, such as code compliance check results."
}
}
Replace tool result (replace the original tool output entirely with the return value before sending it to the Agent):
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"updatedToolOutput": "The streamlined tool output."
}
}
updatedToolOutput applies to all tools (both built-in tools and MCP tools).
Typical use case: compress lengthy tool output (such as oversized command logs or extremely long file content) to save context tokens. This type of "compression" hook relies on the replacement capability.
Difference from additionalContext: additionalContext appends (the result only becomes longer), while updatedToolOutput replaces (the result can become shorter).
Both can be returned at the same time: first replace with updatedToolOutput, and then append additionalContext to the replaced content.
For MCP tools, if the returned updatedToolOutput is an array, it is used directly as the MCP content array. Otherwise, it is wrapped as a single text block.
Note:
The decision: "block" field is deprecated. Since the tool has already finished executing, the operation cannot actually be "blocked" at this point.

UserPromptSubmit Decision Control

{
"continue": false, // Set to false to block prompt processing.
"reason": "The reason for blocking (displayed to the user only)",
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "Additional context injected into CodeBuddy."
}
}
Note:
The decision: "block" field is deprecated. Use continue: false instead.

Stop/SubagentStop Decision Control

{
"continue": false, // Set to false to prevent stopping and allow the Agent to continue working.
"reason": "The reason why the Agent needs to continue working."
}
Note:
Note: The decision: "block" field is deprecated. Use continue: false instead.

SessionStart Decision Control

{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "My additional context here"
}
}

Using MCP Tools

CodeBuddy Code hooks work seamlessly with Model Context Protocol (MCP) tools.

MCP Tool Naming

MCP tools follow the mcp__<server>__<tool> pattern, for example:
mcp__memory__create_entities - The entity creation tool of the Memory server.
mcp__filesystem__read_file - The file reading tool of the Filesystem server.
mcp__github__search_repositories - The search tool of the GitHub server.

Configuring Hooks for MCP Tools

You can target specific MCP tools or entire MCP servers:
{
"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 Considerations

Disclaimer

Use at your own risk: CodeBuddy Code hooks automatically execute arbitrary shell commands on your system. By using hooks, you acknowledge that:
You are fully responsible for the commands you configure.
Hooks can modify, delete, or access any file that your user account can access.
Malicious or poorly written hooks may cause data loss or system damage.
Tencent Cloud makes no warranties and assumes no liability for any damages resulting from the use of hooks.
You should thoroughly test hooks in a safe environment before using them in production.

Security Best Practices Tutorial

1. Validate and sanitize input - Never blindly trust input data.
2. Always quote shell variables - Use "$VAR" instead of $VAR
3. Prevent path traversal - Check for .. in file paths.
4. Use absolute paths - Specify the full path for scripts (use "$CODEBUDDY_PROJECT_DIR" for project paths).
5. Skip sensitive files - Avoid .env, .git/, keys, and similar items.

Security Configuration

Directly editing hooks in the settings file does not take effect immediately. CodeBuddy Code:
1. Capture a snapshot of hooks at startup.
2. Use this snapshot throughout the session.
3. If hooks are modified externally, a warning is issued.
4. You need to review changes in the /hooks menu before they can be applied.

Hook Execution Details

Timeout: The default execution limit is 60 seconds and can be configured per command.
Parallelization: All matching hooks run in parallel.
Deduplication: Multiple identical hook commands are automatically deduplicated.
Execute Shell:
macOS/Linux: Use the user's default shell (the $SHELL environment variable, typically bash or zsh), falling back to /bin/sh.
Windows: Git Bash is mandatory (cmd.exe or PowerShell are not supported). If Git Bash is not found, an error is reported prompting you to install Git for Windows. You can specify the bash.exe path using the CODEBUDDY_CODE_GIT_BASH_PATH environment variable.
You can override the default shell using the CODEBUDDY_CODE_SHELL environment variable (only POSIX shells are supported: bash, zsh, sh).
Environment: Run in the current directory using the CodeBuddy Code environment.
The CODEBUDDY_PROJECT_DIR environment variable contains the absolute path of the project root directory.
Input: JSON via stdin
Output:
PreToolUse/PostToolUse/Stop/SubagentStop: Show progress in the transcript (Ctrl-R).
Notification/SessionEnd: Logged only at the debug level (--debug).
UserPromptSubmit/SessionStart: stdout is added to CodeBuddy as context.

Debugging

Basic Troubleshooting

If your hooks are not working:
1. Check Configuration - Run /hooks to see whether your hook is registered.
2. Validate Syntax - Ensure that your JSON settings are valid.
3. Test Command - First, run the hook command manually.
4. Check Permissions - Ensure that the script is executable.
5. View Logs - Use codebuddy --debug to view hook execution details.
Common Issues:
Unescaped quotes - Use \\" in JSON strings.
Incorrect matcher - Check whether the tool name matches exactly (case-sensitive).
Command not found - Use the full path for the script.

Advanced Debugging

For complex hook issues:
1. Check hook Execution - Use codebuddy --debug to view detailed hook execution.
2. Validate JSON Schema - Use an external tool to test hook input/output.
3. Check Environment Variables - Verify that the CodeBuddy Code environment is correct.
4. Test Edge Cases - Try hooks with unusual file paths or inputs.
5. Monitor System Resources - Check for resource exhaustion during hook execution.
6. Use Structured Logging - Implement logging in your hook scripts.

Sample Debugging Output

Note: This feature is not supported yet.
Use codebuddy --debug to view hook execution details:
[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) and display:
Which hook is currently 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 also declare the hooks field directly in the YAML frontmatter of a custom Agent's .md file or a Skill's SKILL.md. This approach distributes Hooks together with Agents / Skills as an "atomic unit", and the scope automatically opens and closes with the subagent lifecycle without polluting the main session.

Field Format

The structure of the hooks field is exactly the same as in settings.json—grouped by event name, with several {matcher?, hooks[]} configurations under each event. The hook type supports four types: command / prompt / agent / http:
---
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: Skills support frontmatter hooks only with context: fork (the injection path has no clear lifecycle boundary and is not integrated). Custom Agents always support them.
Automatic registration/cleanup: When a subagent starts, frontmatter hooks are registered with the ScopedHookRegistry, and when the subagent exits, they are automatically unregistered. Hooks take effect only for that subagent's own tool calls and lifecycle events.
Stop → SubagentStop rewriting: Writing a Stop event in the frontmatter is automatically rewritten to SubagentStop—when a subagent completes, it does not trigger the main session's Stop. Writing Stop is intended to express the semantics of "the subagent ending itself."
Merging with global hooks: For the same event, frontmatter hooks are stacked (not overwritten) with those in settings.json and the plugin's hooks/hooks.json, and all are triggered in parallel.

Security Gate (allowUntrustedFrontmatterHooks)

frontmatter hooks can silently trigger Shell commands, so frontmatter hooks from non-built-in sources are not registered by default:
Source
Registered by Default or Not
Built-in Product agents/skills
✔ Allowed automatically
.codebuddy/agents/*.md (user/project-local agents)
✖ Rejected by default
.codebuddy/skills/SKILL.md (user/project-local skills)
✖ Rejected by default
Agents/skills distributed through the plugin marketplace
✖ Rejected by default
Plugin hooks/hooks.json (not frontmatter)
✔ Not restricted by the gate
To enable it, configure the following in ~/.codebuddy/settings.json:
{
"allowUntrustedFrontmatterHooks": true
}
When blocked by a gate, the CLI outputs a warning:
[AgentTask] Frontmatter hooks from skill 'xxx' skipped
(source not admin-trusted; enable `allowUntrustedFrontmatterHooks` in settings to allow)

Fault Tolerance and Diagnostics

Silently discard invalid definitions: When a single hook does not conform to the schema, only that hook is skipped without affecting the parsing of the entire hooks block. The warning includes event 'YYY' invalid: <detailed reason> for easier troubleshooting.
Unknown event name: It is skipped with a warning (unknown event 'XXX') and does not invalidate the entire frontmatter.
YAML completely broken: The log outputs Malformed YAML frontmatter in '<path>'.
Runtime debugging: After starting with CODEBUDDY_DEBUG=1, you can see registration logs such as [ScopedHookRegistry] registered N hook config(s) for scope '<sessionId>' (...) to confirm whether hooks are in place.
Note:
For a complete example of using frontmatter hooks in a Skill, see Skills documentation - Configuring Hooks in Skills.
This document helps you understand the Hook mechanism in CodeBuddy Code and how to configure it. For quick hands-on examples, see Hook Getting Started Guide.


Help and Support

Was this page helpful?

Help us improve! Rate your documentation experience in 5 mins.

Feedback