worktree creation and cleanupEvent | Trigger Timing |
PreToolUse | Before tool execution |
PostToolUse | After tool execution succeeds |
UserPromptSubmit | When a user submits a message |
Stop | When the main Agent response ends |
SubagentStop | When the sub-Agent ends |
PreCompact | Before context compression |
WorktreeCreate | When an isolated worktree is created |
WorktreeRemove | When an isolated worktree is deleted |
unstable_Checkpoint | When a checkpoint is automatically created after file modification |
hooks option. Each event can have multiple matchers, and each matcher can have multiple hook callbacks.import { query } from '@tencent-ai/agent-sdk';const q = query({prompt: 'Help me analyze the code',options: {model: 'deepseek-v3.1',hooks: {PreToolUse: [{matcher: 'Bash', // Matches only the Bash toolhooks: [async (input, toolUseId, ctx) => {console.log('About to execute:', input);return { continue: true };}],timeout: 5000 // Timeout in milliseconds}]}}});
from codebuddy_agent_sdk import query, CodeBuddyAgentOptions, HookMatcherasync def pre_tool_hook(input_data, tool_use_id, context):print(f"About to execute: {input_data}")return {"continue_": True}options = CodeBuddyAgentOptions(model="deepseek-v3.1",hooks={"PreToolUse": [HookMatcher(matcher="Bash", # Matches only the Bash toolhooks=[pre_tool_hook],timeout=5.0 # Timeout in seconds)]})async for msg in query(prompt="Help me analyze the code", options=options):print(msg)
Field | Type | Description |
matcher | string | Matching mode, supports regular expressions. * or an empty string matches all. |
hooks | HookCallback[] | Array of callback functions |
timeout | number | Timeout (in milliseconds for TypeScript, in seconds for Python) |
"Bash" matches only the Bash tool."Edit|Write" matches Edit or Write."*" or "" matches all tools."mcp__.*" matches all MCP tools.hooks: {PreToolUse: [{matcher: 'Bash',hooks: [async (input, toolUseId, ctx) => {const command = input.command as string;// Block dangerous commandsif (command.includes('rm -rf')) {return {decision: 'block',reason: 'Dangerous command blocked'};}return { continue: true };}]}]}
async def pre_bash_hook(input_data, tool_use_id, context):command = input_data.get("command", "")# Block dangerous commandsif "rm -rf" in command:return {"decision": "block","reason": "Dangerous command blocked"}return {"continue_": True}hooks = {"PreToolUse": [HookMatcher(matcher="Bash", hooks=[pre_bash_hook])]}
hooks: {PostToolUse: [{matcher: 'Write|Edit',hooks: [async (input, toolUseId) => {console.log(`File modified: ${input.file_path}`);// Log the modificationawait logFileChange(input.file_path);return { continue: true };}]}]}
async def post_write_hook(input_data, tool_use_id, context):print(f"File modified: {input_data.get('file_path')}")# Log the modificationawait log_file_change(input_data.get("file_path"))return {"continue_": True}hooks = {"PostToolUse": [HookMatcher(matcher="Write|Edit", hooks=[post_write_hook])]}
hooks: {UserPromptSubmit: [{hooks: [async (input) => {const prompt = input.prompt as string;// Sensitive word checkif (containsSensitiveWords(prompt)) {return {decision: 'block',reason: 'Message contains sensitive content'};}return { continue: true };}]}]}
async def prompt_check_hook(input_data, tool_use_id, context):prompt = input_data.get("prompt", "")# Sensitive word checkif contains_sensitive_words(prompt):return {"decision": "block","reason": "Message contains sensitive content"}return {"continue_": True}hooks = {"UserPromptSubmit": [HookMatcher(hooks=[prompt_check_hook])]}
hooks: {Stop: [{hooks: [async (input) => {// Check whether the task is actually completeif (!isTaskComplete()) {return {decision: 'block',reason: 'Task not completed, please continue'};}return { continue: true };}]}]}
async def stop_hook(input_data, tool_use_id, context):# Check whether the task is actually completeif not is_task_complete():return {"decision": "block","reason": "Task not completed, please continue"}return {"continue_": True}hooks = {"Stop": [HookMatcher(hooks=[stop_hook])]}
import type { CheckpointHookInput } from '@tencent-ai/agent-sdk';hooks: {unstable_Checkpoint: [{hooks: [async (input) => {const checkpointInput = input as CheckpointHookInput;const checkpoint = checkpointInput.checkpoint;console.log('File change checkpoint:', {id: checkpoint.id,label: checkpoint.label,files: checkpoint.fileChangeStats?.files,additions: checkpoint.fileChangeStats?.additions,deletions: checkpoint.fileChangeStats?.deletions});// Access file snapshotsfor (const [filePath, version] of Object.entries(checkpoint.fileSnapshots)) {console.log(` ${filePath} - version ${version.version}`);}return { continue: true };}]}]}
async def checkpoint_hook(input_data, tool_use_id, context):checkpoint = input_data.get("checkpoint", {})file_change_stats = checkpoint.get("fileChangeStats", {})print(f"File change checkpoint:")print(f" ID: {checkpoint.get('id')}")print(f" Label: {checkpoint.get('label')}")print(f" Files: {file_change_stats.get('files', [])}")print(f" Additions: +{file_change_stats.get('additions', 0)} lines")print(f" Deletions: -{file_change_stats.get('deletions', 0)} lines")# Access file snapshotsfor file_path, version in checkpoint.get("fileSnapshots", {}).items():print(f" {file_path} - version {version.get('version')}")return {"continue_": True}hooks = {"unstable_Checkpoint": [HookMatcher(hooks=[checkpoint_hook])]}
id: Unique identifier of the checkpointlabel: A human-readable label (usually a user prompt)createdAt: Creation timestampfileSnapshots: A mapping from file paths to version informationfilePath: Absolute path of the fileversion: Version numberbackupFileName: Backup file namebackupTime: Backup timestampfileChangeStats: File change statisticsfiles: A list of changed file pathsadditions: Number of added linesdeletions: Number of deleted lines{"session_id": "abc123","cwd": "/path/to/project","permission_mode": "default","hook_event_name": "PreToolUse"}
{"tool_name": "Bash","tool_input": {"command": "ls -la"}}
{"prompt": "Help me write a function"}
{"stop_hook_active": false}
{"hook_event_name": "WorktreeCreate","session_id": "abc123","cwd": "/path/to/project","transcript_path": "/path/to/transcript.jsonl","name": "feature-auth"}
{"hook_event_name": "WorktreeRemove","session_id": "abc123","cwd": "/path/to/project","transcript_path": "/path/to/transcript.jsonl","worktree_path": "/tmp/codebuddy-worktrees/feature-auth"}
{"checkpoint": {"id": "ckpt_abc123","label": "Help me write a function","createdAt": 1705920000000,"fileSnapshots": {"/path/to/file.ts": {"filePath": "/path/to/file.ts","version": 1,"backupFileName": "file.ts.v1.backup","backupTime": 1705920000000}},"fileChangeStats": {"files": ["/path/to/file.ts"],"additions": 10,"deletions": 2}}}
Field | Type | Description |
continue / continue_ | boolean | Whether to continue execution (default: true) |
decision | 'block' | Set to 'block' to block the operation. |
reason | string | Reason for blocking |
stopReason | string | Stop message displayed when continue is false |
suppressOutput | boolean | Suppress output |
return {continue: true,hookSpecificOutput: {hookEventName: 'PreToolUse',updatedInput: {command: `echo "Security check passed" && ${input.command}`}}};
return {"continue_": True,"hookSpecificOutput": {"hookEventName": "PreToolUse","updatedInput": {"command": f'echo "Security check passed" && {input_data["command"]}'}}}
additionalContext), or use updatedToolOutput to replace the tool result that will be sent to the Agent (effective for all tools, commonly used to compress verbose output to save tokens):return {continue: true,hookSpecificOutput: {hookEventName: 'PostToolUse',// Replace the tool result (the result may become shorter). You can also use additionalContext to append information (the result will only become longer).updatedToolOutput: compress(input.tool_response)}};
return {"continue_": True,"hookSpecificOutput": {"hookEventName": "PostToolUse",# Replace the tool result (the result may become shorter). You can also use additionalContext to append information (the result will only become longer)."updatedToolOutput": compress(input_data.get("tool_response"))}}
import { query } from '@tencent-ai/agent-sdk';import * as fs from 'fs';const logFile = '/tmp/bash-audit.log';const q = query({prompt: 'Help me clean up temporary files',options: {model: 'deepseek-v3.1',hooks: {PreToolUse: [{matcher: 'Bash',hooks: [async (input, toolUseId) => {const command = input.command as string;const timestamp = new Date().toISOString();// Log the commandfs.appendFileSync(logFile, `${timestamp} [PRE] ${command}\\n`);// Check for dangerous commandsconst dangerous = ['rm -rf /', 'mkfs', ':(){:|:&};:'];for (const d of dangerous) {if (command.includes(d)) {return {decision: 'block',reason: `Dangerous command blocked: ${d}`};}}return { continue: true };}]}],PostToolUse: [{matcher: 'Bash',hooks: [async (input, toolUseId) => {const command = input.command as string;const timestamp = new Date().toISOString();// Log execution completionfs.appendFileSync(logFile, `${timestamp} [POST] ${command} - Done\\n`);return { continue: true };}]}]}}});for await (const message of q) {console.log(message);}
import asynciofrom datetime import datetimefrom codebuddy_agent_sdk import query, CodeBuddyAgentOptions, HookMatcherlog_file = "/tmp/bash-audit.log"async def pre_bash_hook(input_data, tool_use_id, context):command = input_data.get("command", "")timestamp = datetime.now().isoformat()# Log the commandwith open(log_file, "a") as f:f.write(f"{timestamp} [PRE] {command}\\n")# Check for dangerous commandsdangerous = ["rm -rf /", "mkfs", ":(){:|:&};:"]for d in dangerous:if d in command:return {"decision": "block","reason": f"Dangerous command blocked: {d}"}return {"continue_": True}async def post_bash_hook(input_data, tool_use_id, context):command = input_data.get("command", "")timestamp = datetime.now().isoformat()# Log execution completionwith open(log_file, "a") as f:f.write(f"{timestamp} [POST] {command} - Done\\n")return {"continue_": True}async def main():options = CodeBuddyAgentOptions(model="deepseek-v3.1",hooks={"PreToolUse": [HookMatcher(matcher="Bash", hooks=[pre_bash_hook])],"PostToolUse": [HookMatcher(matcher="Bash", hooks=[post_bash_hook])]})async for message in query(prompt="Help me clean up temporary files", options=options):print(message)asyncio.run(main())
hooks: {PreToolUse: [{matcher: 'Write|Edit',hooks: [async (input) => {const filePath = input.file_path as string;// Only allow modifications to the src directoryif (!filePath.startsWith('/path/to/project/src/')) {return {decision: 'block',reason: `Modifying files outside the src directory is not allowed: ${filePath}`};}// Do not modify configuration filesif (filePath.endsWith('.env') || filePath.includes('.git/')) {return {decision: 'block',reason: 'Modifying sensitive files is not allowed'};}return { continue: true };}]}]}
async def file_scope_hook(input_data, tool_use_id, context):file_path = input_data.get("file_path", "")# Only allow modifications to the src directoryif not file_path.startswith("/path/to/project/src/"):return {"decision": "block","reason": f"Modifying files outside the src directory is not allowed: {file_path}"}# Do not modify configuration filesif file_path.endswith(".env") or ".git/" in file_path:return {"decision": "block","reason": "Modifying sensitive files is not allowed"}return {"continue_": True}hooks = {"PreToolUse": [HookMatcher(matcher="Write|Edit", hooks=[file_scope_hook])]}
import { query, type CheckpointHookInput } from '@tencent-ai/agent-sdk';import * as fs from 'fs';const changeLog = '/tmp/file-changes.log';const q = query({prompt: 'Refactor the src/utils.ts file',options: {model: 'deepseek-v3.1',hooks: {unstable_Checkpoint: [{hooks: [async (input) => {const checkpointInput = input as CheckpointHookInput;const checkpoint = checkpointInput.checkpoint;const stats = checkpoint.fileChangeStats;if (!stats) return { continue: true };// Log file changesconst timestamp = new Date().toISOString();const logEntry = `[${timestamp}] Checkpoint ${checkpoint.id}Label: ${checkpoint.label}Files: ${stats.files.join(', ')}Changes: +${stats.additions}/-${stats.deletions}Snapshots: ${Object.keys(checkpoint.fileSnapshots).length} files`;fs.appendFileSync(changeLog, logEntry);// If the changes are too large, remind the userif (stats.additions + stats.deletions > 100) {console.warn('Large amount of code changes, review is recommended');}return { continue: true };}]}]}}});for await (const message of q) {console.log(message);}
import asynciofrom datetime import datetimefrom codebuddy_agent_sdk import query, CodeBuddyAgentOptions, HookMatcherchange_log = "/tmp/file-changes.log"async def checkpoint_tracker(input_data, tool_use_id, context):checkpoint = input_data.get("checkpoint", {})stats = checkpoint.get("fileChangeStats")if not stats:return {"continue_": True}# Log file changestimestamp = datetime.now().isoformat()log_entry = f"""[{timestamp}] Checkpoint {checkpoint.get('id')}Label: {checkpoint.get('label')}Files: {', '.join(stats.get('files', []))}Changes: +{stats.get('additions', 0)}/-{stats.get('deletions', 0)}Snapshots: {len(checkpoint.get('fileSnapshots', {}))} files"""with open(change_log, "a") as f:f.write(log_entry)# If the changes are too large, remind the usertotal_changes = stats.get("additions", 0) + stats.get("deletions", 0)if total_changes > 100:print("⚠️ Large amount of code changes, review is recommended")return {"continue_": True}async def main():options = CodeBuddyAgentOptions(model="deepseek-v3.1",hooks={"unstable_Checkpoint": [HookMatcher(hooks=[checkpoint_tracker])]})async for message in query(prompt="Refactor the src/utils.ts file", options=options):print(message)asyncio.run(main())
Was this page helpful?
You can also Contact sales or Submit a Ticket for help.
Help us improve! Rate your documentation experience in 5 mins.
Feedback