Documentation Index
Fetch the complete documentation index at: /llms.txt
Use this file to discover all available pages before exploring further.
Skip to main content
Configuration fields:
Matcher pattern examples:
For session events (
Use PreToolUse output control to block tools.
Example: Extracting tool-specific parameters
Example: Using
Example: Using
Example: Using
Example Stop event:
Available for: All event types
How to use:
Available for: PreToolUse and PostToolUse only
How to use:
Available for: Stop event only
How to use:
Warning message (exit code 0):
Example: Block dangerous command
Example: Provide context to agent
Example: Prevent stop
Example: Inject context
Metadata flags:
Hook Input (Stop event with all metadata options):
Hook Input (PostToolUse event with metadata - MCP tool):
Hook Input (PostToolUse event with file_changes - file edit):
Hook script (with metadata options):
Configuration:
Block specific MCP server:
Configuration:
2. Test your hook script:
3. Validate JSON output:
Check logs for:
Save event data for inspection:
Hooks
Hooks
Intercept and control tool execution with custom scripts
Overview
Hooks allow you to intercept tool execution at specific lifecycle events and run custom scripts. This enables powerful workflows like:- Security auditing - Block dangerous commands or log sensitive file access
- Policy enforcement - Enforce coding standards or restrict production access
- Logging - Track tool usage for compliance and analytics
- Integration - Connect with external systems and workflows
Configuration
Hooks are configured insettings.json files. Augment loads hook configuration from multiple locations, allowing system-wide policies, user preferences, and project-specific hooks to coexist.
Settings File Locations
Settings are listed below from highest to lowest precedence:| Location | Platform | Supported By | Description |
|---|---|---|---|
/etc/augment/settings.json | Linux/macOS | CLI, VSCode, IntelliJ | System-wide settings for enterprise/admin policies (immutable) |
C:\ProgramData\Augment\settings.json | Windows | CLI, VSCode, IntelliJ | System-wide settings for enterprise/admin policies (immutable) |
<workspace>/.augment/settings.local.json | All | CLI | Personal project overrides — not committed to source control |
<workspace>/.augment/settings.json | All | CLI | Shared project settings — committed to source control |
~/.augment/settings.json | All | CLI, VSCode, IntelliJ | User-level settings (lowest precedence) |
System-level settings (
/etc/augment/settings.json) take the highest precedence and cannot be overridden by
any other configuration source. Project-scoped settings (.augment/settings.json and .augment/settings.local.json)
allow repositories to ship hook configurations that apply automatically to all contributors.Use
<workspace>/.augment/settings.json to define hooks that all contributors should use — for example,
security auditing or policy enforcement hooks. Use <workspace>/.augment/settings.local.json for personal
overrides that shouldn’t be committed to source control.PreToolUse, PostToolUse, and Stop hooks run synchronously - the agent waits for them to
complete before proceeding. Use appropriate timeouts to prevent long delays. Note that only
PreToolUse can block tool execution; Stop can block the agent from finishing (via decision: "block"), but PostToolUse cannot block anything.Structure
Hooks are organized by event type with optional matchers:{
"hooks": {
"PreToolUse": [
{
"matcher": "launch-process",
"hooks": [
{
"type": "command",
"command": "/etc/augment/hooks/validate-command.sh",
"timeout": 5000
}
]
}
]
}
}
| Field | Description |
|---|---|
matcher | Pattern to match tool names (case-sensitive). Supports regex patterns. Optional for PreToolUse and PostToolUse (defaults to ".*" to match all tools). Not used for session events (SessionStart, SessionEnd, Stop). |
hooks | Array of hook handlers to execute when the pattern matches. |
type | Hook execution type - currently only "command" is supported. |
command | Path to the script to execute (must use a supported script extension: .ps1, .cmd, .bat, or .sh). |
timeout | (Optional) Timeout in milliseconds (default: 60000ms). |
metadata | (Optional) Configuration for additional context fields - see Hook Metadata. |
| Pattern | Description |
|---|---|
"launch-process" | Match a specific tool |
"str-replace-editor|save-file" | Match multiple tools using regex OR |
".*" | Match all tools |
"mcp:*" | Match all MCP tools (special case) |
"mcp:.*_my-server$" | Match any tool from a specific MCP server |
SessionStart, SessionEnd, Stop) that don’t require matchers:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "/path/to/setup-script.sh"
}
]
}
]
}
}
Script Requirements
Hook scripts must:- Use a supported script extension -
.ps1,.cmd,.bat, or.sh - Be executable - Run
chmod +x your-hook.sh(Unix.shscripts only) - Have a valid shebang - First line must specify the interpreter (Unix
.shscripts only)
| Extension | How it’s executed |
|---|---|
.sh | Executed directly on Unix; the shebang line selects the interpreter |
.ps1 | Dispatched to powershell.exe -Command on Windows |
.bat, .cmd | Dispatched to cmd.exe /c on Windows |
Shebang-based interpreter selection (Python, Node.js, Ruby, etc.) only applies to
.sh scripts on Unix.
.ps1 files are always run with PowerShell, and .bat/.cmd files are always run with cmd.exe — a shebang inside those files is ignored.#!/usr/bin/env python3
# Python script with .sh extension on Unix - uses Python interpreter via shebang
import sys, json
event_data = json.load(sys.stdin)
# ... your Python code
Hook Events
PreToolUse
Runs before a tool executes. Can block tool execution. Common tool names:| Tool | Description |
|---|---|
launch-process | Shell commands |
view | File reading |
str-replace-editor | File editing |
save-file | File writing |
remove-files | File deletion |
web-fetch | Fetch web content |
web-search | Web search |
codebase-retrieval | Codebase search |
github-api | GitHub integration |
linear | Linear integration |
PostToolUse
Runs immediately after a tool completes. Can provide feedback to the agent but cannot block execution. Recognizes the same tool names as PreToolUse. Includestool_output and tool_error in the event data.
Stop
Runs when the agent stops responding, whether from normal completion or user interrupt. Theagent_stop_cause field indicates why the agent stopped (e.g., "end_turn" or "interrupted"). Can block the agent from stopping (useful for requiring tests before completion).
SessionStart
Runs when Auggie starts a new session. Useful for:- Loading development context (git status, open issues)
- Installing dependencies
- Setting up environment variables
SessionEnd
Runs when an Auggie session ends. Useful for:- Cleanup tasks
- Logging session statistics
- Saving session state
Hook Input
Hooks receive event data via stdin as a JSON object. The structure varies by event type, but all events share common base fields.Common Fields (All Events)
These fields are present in every hook event:| Field | Type | Description |
|---|---|---|
hook_event_name | string | The type of event: "PreToolUse", "PostToolUse", "Stop", "SessionStart", "SessionEnd", "Notification" |
conversation_id | string | Unique identifier for the current conversation |
workspace_roots | string[] | List of workspace root directories (usually contains one path) |
Event-Specific Fields
PreToolUse / PostToolUse Events
Tool events include information about the tool being executed:| Field | Type | Availability | Description |
|---|---|---|---|
tool_name | string | Always | Name of the tool (e.g., "launch-process", "str-replace-editor"). Use to filter which tools your hook applies to. |
tool_input | object | Always | Input parameters passed to the tool. Critical for security hooks - extract and validate specific parameters. |
tool_output | string? | PostToolUse only | Output returned by the tool (if successful). Use for auditing or providing context to the agent. |
tool_error | string? | PostToolUse only | Error message if tool execution failed. Use to detect failures and inject troubleshooting tips. |
file_changes | object[]? | PostToolUse only | File changes for save-file, str-replace-editor, remove-files. Includes path, changeType, content, and oldContent. |
is_mcp_tool | boolean | Always | true if the tool is provided by an MCP server, false for built-in tools. |
#!/usr/bin/env bash
EVENT_DATA=$(cat)
TOOL_NAME=$(echo "$EVENT_DATA" | jq -r '.tool_name')
if [[ "$TOOL_NAME" == "launch-process" ]]; then
# Extract the command being executed
COMMAND=$(echo "$EVENT_DATA" | jq -r '.tool_input.command')
echo "Command: $COMMAND" >&2
# Check for dangerous patterns
if echo "$COMMAND" | grep -qE "rm -rf|sudo|curl.*sh"; then
echo "Blocked dangerous command" >&2
exit 2
fi
fi
exit 0
#!/usr/bin/env python3
import sys, json
event_data = json.load(sys.stdin)
if event_data.get('tool_name') == 'str-replace-editor':
path = event_data.get('tool_input', {}).get('path', '')
if any(p in path for p in ['.env', 'secrets', 'credentials']):
print(f"[AUDIT] Sensitive file: {path}", file=sys.stderr)
sys.exit(0)
tool_output for context (PostToolUse)
#!/usr/bin/env bash
EVENT_DATA=$(cat)
TOOL_NAME=$(echo "$EVENT_DATA" | jq -r '.tool_name')
TOOL_OUTPUT=$(echo "$EVENT_DATA" | jq -r '.tool_output // ""')
if [[ "$TOOL_NAME" == "launch-process" ]] && echo "$TOOL_OUTPUT" | grep -q "test.*passed"; then
# Tests passed - inject success context
cat << EOF
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "All tests passed successfully!"
}
}
EOF
fi
exit 0
#!/usr/bin/env python3
import sys
import json
event_data = json.load(sys.stdin)
tool_name = event_data.get('tool_name', '')
tool_output = event_data.get('tool_output', '')
if tool_name == 'launch-process' and 'test' in tool_output and 'passed' in tool_output:
# Tests passed - inject success context
output = {
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "All tests passed successfully!"
}
}
print(json.dumps(output))
sys.exit(0)
tool_error for troubleshooting (PostToolUse)
#!/usr/bin/env bash
EVENT_DATA=$(cat)
TOOL_ERROR=$(echo "$EVENT_DATA" | jq -r '.tool_error // ""')
if [[ -n "$TOOL_ERROR" ]] && echo "$TOOL_ERROR" | grep -q "permission denied"; then
# Inject troubleshooting tip
cat << EOF
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "Permission denied error detected. Try running with appropriate permissions or check file ownership."
}
}
EOF
fi
exit 0
#!/usr/bin/env python3
import sys
import json
event_data = json.load(sys.stdin)
tool_error = event_data.get('tool_error', '')
if tool_error and 'permission denied' in tool_error.lower():
output = {
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "Permission denied error detected. Try with appropriate permissions."
}
}
print(json.dumps(output))
sys.exit(0)
file_changes for audit logging (PostToolUse)
The file_changes field is populated for file-modifying tools and includes the old content for edits:
{
"hook_event_name": "PostToolUse",
"conversation_id": "conv-xyz789",
"tool_name": "str-replace-editor",
"tool_input": {
"path": "src/auth.ts",
"old_str_1": "...",
"new_str_1": "..."
},
"file_changes": [
{
"path": "src/auth.ts",
"changeType": "edit",
"content": "// New code content here...",
"oldContent": "// Old code that was replaced..."
}
]
}
#!/usr/bin/env bash
EVENT_DATA=$(cat)
echo "$EVENT_DATA" | jq -c '.file_changes[]?' | while read -r change; do
echo "[AUDIT] $(echo $change | jq -r '.changeType'): $(echo $change | jq -r '.path')" >&2
done
exit 0
SessionStart / SessionEnd Events
Session events have no additional fields beyond the common base fields. Example SessionStart event:{
"hook_event_name": "SessionStart",
"conversation_id": "conv-xyz789",
"workspace_roots": ["/Users/username/project"]
}
Stop Event
Stop events include information about why the agent stopped:| Field | Type | Description |
|---|---|---|
agent_stop_cause | string | Why the agent stopped: "end_turn", "interrupted", "max_iterations", or "error" |
{
"hook_event_name": "Stop",
"conversation_id": "conv-xyz789",
"workspace_roots": ["/Users/username/project"],
"agent_stop_cause": "end_turn"
}
Metadata-Based Fields
When hooks declare metadata options in their configuration, additional fields are included in the event data:context field (when includeUserContext: true)
Available for: All event types
{
"context": {
"userEmail": "user@example.com",
"modelName": "Claude Opus 4.5",
"toolVersion": "0.6.0",
"timestamp": "2025-01-15T10:30:00-08:00"
}
}
userEmail: Identify which user triggered the hook. Use for user-specific policies or analytics.modelName: AI model display name (e.g., “Claude Opus 4.5”, “Sonnet-3.7”). Use for model-specific behavior.toolVersion: CLI or VSCode extension version (e.g., “0.6.0”). Use for debugging or version-specific behavior.timestamp: ISO 8601 timestamp. Use for auditing and analytics.
#!/usr/bin/env bash
EVENT_DATA=$(cat)
USER_EMAIL=$(echo "$EVENT_DATA" | jq -r '.context.userEmail // ""')
TOOL_NAME=$(echo "$EVENT_DATA" | jq -r '.tool_name')
COMMAND=$(echo "$EVENT_DATA" | jq -r '.tool_input.command // ""')
# Only allow deployments for specific users
if [[ "$TOOL_NAME" == "launch-process" ]] && echo "$COMMAND" | grep -q "deploy"; then
if [[ "$USER_EMAIL" != "admin@example.com" ]]; then
echo "Only admins can deploy" >&2
exit 2
fi
fi
exit 0
#!/usr/bin/env python3
import sys
import json
event_data = json.load(sys.stdin)
user_email = event_data.get('context', {}).get('userEmail', '')
# Only allow deployments for specific users
tool_name = event_data.get('tool_name', '')
command = event_data.get('tool_input', {}).get('command', '')
if tool_name == 'launch-process' and 'deploy' in command:
if user_email != 'admin@example.com':
print("Only admins can deploy", file=sys.stderr)
sys.exit(2)
sys.exit(0)
mcp_metadata field (when includeMCPMetadata: true)
Available for: PreToolUse and PostToolUse only
{
"mcp_metadata": {
"timestamp": "2025-01-15T10:30:00-08:00",
"mcpDecision": "yes",
"mcpTotalToolsCount": 215,
"mcpExecutedToolName": "search_my-server",
"mcpExecutedToolServerName": "my-server",
"mcpExecutedToolServerToolsCount": 6
}
}
timestamp: ISO 8601 timestamp. Use for auditing and analytics.mcpDecision: Whether this is an MCP tool ("yes") or native tool ("no").mcpTotalToolsCount: Total MCP tools available across all servers.mcpExecutedToolName: Full MCP tool name (e.g.,"search_my-server").mcpExecutedToolServerName: MCP server name (e.g.,"my-server").mcpExecutedToolServerToolsCount: Tools available from the executed server.
#!/usr/bin/env bash
EVENT_DATA=$(cat)
MCP_DECISION=$(echo "$EVENT_DATA" | jq -r '.mcp_metadata.mcpDecision // "no"')
MCP_TOTAL=$(echo "$EVENT_DATA" | jq -r '.mcp_metadata.mcpTotalToolsCount // 0')
# Block if too many MCP tools are enabled (security concern)
if [[ "$MCP_DECISION" == "yes" ]] && [[ "$MCP_TOTAL" -gt 100 ]]; then
echo "Too many MCP tools enabled ($MCP_TOTAL). Contact admin." >&2
exit 2
fi
exit 0
#!/usr/bin/env python3
# /etc/augment/hooks/mcp-limit.sh (Python via shebang)
import sys
import json
event_data = json.load(sys.stdin)
mcp_metadata = event_data.get('mcp_metadata', {})
mcp_decision = mcp_metadata.get('mcpDecision', 'no')
mcp_total = mcp_metadata.get('mcpTotalToolsCount', 0)
# Block if too many MCP tools are enabled (security concern)
if mcp_decision == 'yes' and mcp_total > 100:
print(f"Too many MCP tools enabled ({mcp_total}). Contact admin.", file=sys.stderr)
sys.exit(2)
sys.exit(0)
conversation field (when includeConversationData: true)
Available for: Stop event only
{
"conversation": {
"timestamp": "2025-01-15T10:30:00-08:00",
"userPrompt": "Add error handling to the login function",
"agentTextResponse": "I'll add comprehensive error handling...",
"agentCodeResponse": [
{
"path": "src/auth/login.ts",
"changeType": "edit",
"content": "export function login() { try { ... } catch (e) { ... } }"
}
]
}
}
timestamp: ISO 8601 timestamp. Use for auditing.userPrompt: The user’s original request.agentTextResponse: Agent’s explanation of what it did.agentCodeResponse: Array of file changes. Each entry has:path: File path modifiedchangeType:"edit","create", or"delete"content: New content (for edit/create only)
#!/usr/bin/env bash
EVENT_DATA=$(cat)
# Extract code changes
CODE_RESPONSE=$(echo "$EVENT_DATA" | jq -r '.conversation.agentCodeResponse // []')
# Check if any test files were modified
TEST_FILES=$(echo "$CODE_RESPONSE" | jq -r '.[] | select(.path | test("test|spec")) | .path')
if [[ -z "$TEST_FILES" ]]; then
# No test files modified - block stop
cat << EOF
{
"hookSpecificOutput": {
"hookEventName": "Stop",
"decision": "block",
"reason": "Please add or update tests before finishing"
}
}
EOF
exit 0
fi
exit 0
#!/usr/bin/env python3
import sys
import json
import re
event_data = json.load(sys.stdin)
conversation = event_data.get('conversation', {})
code_response = conversation.get('agentCodeResponse', [])
# Check if any test files were modified
test_files = [
change['path'] for change in code_response
if re.search(r'test|spec', change.get('path', ''))
]
if not test_files:
# No test files modified - block stop
output = {
"hookSpecificOutput": {
"hookEventName": "Stop",
"decision": "block",
"reason": "Please add or update tests before finishing"
}
}
print(json.dumps(output))
sys.exit(0)
sys.exit(0)
Reading Hook Input
#!/usr/bin/env bash
# Read entire JSON from stdin
EVENT_DATA=$(cat)
# Extract fields using jq
HOOK_EVENT=$(echo "$EVENT_DATA" | jq -r '.hook_event_name')
TOOL_NAME=$(echo "$EVENT_DATA" | jq -r '.tool_name // ""')
CONV_ID=$(echo "$EVENT_DATA" | jq -r '.conversation_id')
echo "Event: $HOOK_EVENT, Tool: $TOOL_NAME, Conversation: $CONV_ID"
#!/usr/bin/env python3
import sys
import json
event_data = json.load(sys.stdin)
hook_event = event_data['hook_event_name']
tool_name = event_data.get('tool_name', '')
conv_id = event_data['conversation_id']
print(f"Event: {hook_event}, Tool: {tool_name}, Conversation: {conv_id}")
Hook Output and Communication
Hooks communicate results through exit codes and output streams. The behavior depends on the exit code and the hook event type.Exit Codes
- Exit code 0: Success - Hook completed successfully
- Exit code 2: Blocking error - Prevents tool execution (PreToolUse only)
- Other exit codes: Non-blocking error - Logged but execution continues
Output Streams
- stdout: Standard output from the hook
- stderr: Error output from the hook
Communication Matrix
The following table shows how hook output is handled based on exit code and event type:| Exit Code | Event Type | Output Stream | Shown To | Behavior |
|---|---|---|---|---|
| 2 | PreToolUse | stderr | Agent | Blocks tool execution, agent sees why it was blocked |
| 2 | SessionStart | stderr | User | Hook failed at startup, user needs to fix configuration |
| 0 | PreToolUse | stderr | User | Warning message shown to user |
| 0 | PreToolUse | stdout | User | Success message shown to user |
| 0 | PostToolUse | stderr | User | Warning message shown to user |
| 0 | PostToolUse | stdout | User | Success message shown to user |
| 0 | SessionStart | stdout | Agent | Inject context at session start |
| 0 | SessionEnd | stdout | User | Show completion message |
| Other | Any | stderr | User | Error logged, execution continues |
Key Principle: Exit code 2 with PreToolUse blocks the tool and shows stderr to the agent (so
it knows why). Exit code 0 shows output to the user. SessionStart stdout is special - it injects
context for the agent.
PreToolUse Output
Blocking a tool (exit code 2):#!/usr/bin/env bash
EVENT_DATA=$(cat)
COMMAND=$(echo "$EVENT_DATA" | jq -r '.tool_input.command // ""')
if echo "$COMMAND" | grep -qE "rm -rf|sudo"; then
echo "Blocked dangerous command: $COMMAND" >&2
exit 2 # Blocks tool, stderr shown to agent
fi
exit 0 # Allow tool to proceed
#!/usr/bin/env python3
import sys
import json
import re
event_data = json.load(sys.stdin)
command = event_data.get('tool_input', {}).get('command', '')
if re.search(r'rm -rf|sudo', command):
print(f"Blocked dangerous command: {command}", file=sys.stderr)
sys.exit(2) # Blocks tool, stderr shown to agent
sys.exit(0) # Allow tool to proceed
#!/usr/bin/env bash
EVENT_DATA=$(cat)
FILE_PATH=$(echo "$EVENT_DATA" | jq -r '.tool_input.path // ""')
if echo "$FILE_PATH" | grep -qE "\.env|secrets"; then
echo "Warning: Accessing sensitive file: $FILE_PATH" >&2
fi
exit 0 # Allow tool but show warning
#!/usr/bin/env python3
import sys
import json
import re
event_data = json.load(sys.stdin)
file_path = event_data.get('tool_input', {}).get('path', '')
if re.search(r'\.env|secrets', file_path):
print(f"Warning: Accessing sensitive file: {file_path}", file=sys.stderr)
sys.exit(0) # Allow tool but show warning
SessionStart Output
SessionStart hooks can inject context for the agent by writing to stdout:#!/usr/bin/env bash
# Load current issues from issue tracker
ISSUES=$(curl -s https://api.example.com/issues)
# Output to stdout - this will be injected as context for the agent
echo "Current open issues:"
echo "$ISSUES"
exit 0
#!/usr/bin/env python3
import sys
import requests
# Load current issues from issue tracker
response = requests.get('https://api.example.com/issues')
issues = response.text
# Output to stdout - this will be injected as context for the agent
print("Current open issues:")
print(issues)
sys.exit(0)
Hook Output Reference
Hooks can return structured output to control execution and communicate with the agent or user. Output is provided via stdout (as JSON) and stderr (for error messages).Exit Codes
| Exit Code | Meaning | Behavior |
|---|---|---|
0 | Success | Hook completed successfully. Tool execution continues (unless JSON output blocks it). |
2 | Blocking Error | PreToolUse only: Blocks tool execution. Stderr message is shown to both user and agent. |
| Other | Non-blocking Error | Hook failed, but tool execution continues. Stderr shown in verbose mode. |
JSON Output Format
Hooks can return JSON on stdout (exit code 0 only) to provide structured control:{
"continue": true,
"stopReason": "Optional reason if continue=false",
"suppressOutput": false,
"systemMessage": "Message shown to user",
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Security policy violation"
}
}
Common JSON Fields (All Events)
These fields can be returned by any hook:| Field | Type | Description | Destination |
|---|---|---|---|
continue | boolean | If false, stops execution (overrides exit code 0) | - |
stopReason | string | Reason shown when continue=false | User |
suppressOutput | boolean | If true, hides stdout from verbose mode | - |
systemMessage | string | Warning or informational message | User |
Event-Specific JSON Output
PreToolUse Output
PreToolUse hooks can control tool execution:| Field | Type | Description | Destination |
|---|---|---|---|
permissionDecision | "deny" | Block tool execution | - |
permissionDecisionReason | string | Reason for blocking the tool | Agent and User |
Currently only
permissionDecision: "deny" is supported. The "allow" and "ask" values will be
implemented in a future release.{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Command contains 'rm -rf' which is not allowed"
}
}
Modifying tool input via hooks (
updatedInput) is not yet supported. This feature is planned for a future release.PostToolUse Output
PostToolUse hooks can provide additional context to the agent:| Field | Type | Description | Destination |
|---|---|---|---|
decision | "block" | Blocks agent with reason | - |
reason | string | Reason for blocking (required if decision=“block”) | Agent |
additionalContext | string | Additional context for the agent to consider | Agent |
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "The command modified 3 files in the authentication module"
}
}
Stop Output
Stop hooks can prevent the agent from finishing:| Field | Type | Description | Destination |
|---|---|---|---|
decision | "block" | Prevents stop | - |
reason | string | Reason for blocking stop (required if decision=“block”) | Agent |
{
"hookSpecificOutput": {
"hookEventName": "Stop",
"decision": "block",
"reason": "Please run tests before finishing"
}
}
SessionStart Output
SessionStart hooks can inject context for the agent:| Field | Type | Description | Destination |
|---|---|---|---|
additionalContext | string | Context to inject at session start | Agent |
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "Current sprint: Sprint 23. Focus: Authentication refactor"
}
}
Output Routing
Different output goes to different destinations: To Agent (injected into conversation):- Exit code 2 stderr (PreToolUse, PostToolUse, Stop events)
permissionDecisionReason(when decision=“deny”)reason(when decision=“block”)additionalContext- SessionStart stdout or
additionalContext
- Exit code 2 stderr (SessionStart, SessionEnd events)
systemMessagepermissionDecisionReason(when decision=“allow” or “ask”)- Plain stdout (in verbose mode, for non-SessionStart events)
Complete Output Examples
#!/usr/bin/env bash
EVENT_DATA=$(cat)
COMMAND=$(echo "$EVENT_DATA" | jq -r '.tool_input.command // ""')
if echo "$COMMAND" | grep -qE "rm -rf"; then
echo "Blocked dangerous command: $COMMAND" >&2
exit 2 # Blocks tool, stderr shown to agent and user
fi
exit 0
#!/usr/bin/env python3
import sys
import json
import re
event_data = json.load(sys.stdin)
command = event_data.get('tool_input', {}).get('command', '')
if re.search(r'rm -rf', command):
print(f"Blocked dangerous command: {command}", file=sys.stderr)
sys.exit(2) # Blocks tool, stderr shown to agent and user
sys.exit(0)
#!/usr/bin/env bash
EVENT_DATA=$(cat)
COMMAND=$(echo "$EVENT_DATA" | jq -r '.tool_input.command // ""')
if echo "$COMMAND" | grep -qE "rm -rf"; then
cat << EOF
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Command contains 'rm -rf' which violates security policy"
}
}
EOF
exit 0
fi
exit 0
#!/usr/bin/env python3
import sys
import json
import re
event_data = json.load(sys.stdin)
command = event_data.get('tool_input', {}).get('command', '')
if re.search(r'rm -rf', command):
output = {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Command contains 'rm -rf' which violates security policy"
}
}
print(json.dumps(output))
sys.exit(0)
sys.exit(0)
Hook Metadata (Configuration)
Hook metadata options are configuration-level flags that control what data is included in the hook input. They provide a privacy-first, opt-in model for accessing conversation data, MCP metadata, and user context.Available Metadata Options
Metadata options are specified in the hook configuration (not in hook output):{
"hooks": {
"PreToolUse": [
{
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "/etc/augment/hooks/analytics.sh"
}
],
"metadata": {
"includeConversationData": true,
"includeMCPMetadata": true,
"includeUserContext": false
}
}
]
}
}
-
includeUserContext: Include user and environment context in hook input- Adds
contextfield to hook event data - Available for: All event types (PreToolUse, PostToolUse, Stop, SessionStart, SessionEnd)
- Fields included:
userEmail- User’s email address (e.g., “user@example.com”)modelName- Model name being used (e.g., “Claude Opus 4.5”)timestamp- ISO 8601 timestamp (e.g., “2025-01-15T10:30:00-08:00”)
- Default:
false(user context excluded)
- Adds
-
includeMCPMetadata: Include MCP-specific metadata in hook input- Adds
mcp_metadatafield to hook event data - Available for: PreToolUse and PostToolUse only
- Fields included:
mcpDecision- Whether MCP tools were used (“yes” | “no”)mcpTotalToolsCount- Total number of MCP tools available across all serversmcpExecutedToolName- Name of the executed MCP tool (if any)mcpExecutedToolServerName- Server name of the executed MCP tool (if any)mcpExecutedToolServerToolsCount- Number of tools from the executed server (if any)
- Default:
false(MCP metadata excluded)
- Adds
-
includeConversationData: Include conversation fields in hook input- Adds
conversationfield to hook event data - Available for: Stop event only
- Fields included:
userPrompt- The user’s prompt/message that triggered the agent responseagentTextResponse- Agent’s text response (markdown format)agentCodeResponse- Array of file changes made by the agent, each containing:path- File path relative to workspace rootchangeType- Type of change (“edit” | “create” | “delete”)content- File content after the change (undefined for deletions)
- Default:
false(conversation data excluded for privacy)
- Adds
| Metadata Option | PreToolUse | PostToolUse | Stop | SessionStart | SessionEnd |
|---|---|---|---|---|---|
includeUserContext | ✓ | ✓ | ✓ | ✓ | ✓ |
includeMCPMetadata | ✓ | ✓ | - | - | - |
includeConversationData | - | - | ✓ | - | - |
When to Use Metadata Options
UseincludeUserContext when:
- Implementing user-specific rate limiting
- Building per-user analytics
- Tracking model usage by user
- Implementing user-based access controls
- Logging user activity for compliance
includeMCPMetadata when:
- Monitoring MCP tool usage patterns
- Debugging MCP integration issues
- Building MCP usage analytics
- Rate limiting MCP tool calls
- Tracking which MCP servers are being used
includeConversationData when:
- Building analytics dashboards that track user interactions
- Implementing compliance logging for audit trails
- Analyzing agent response quality
- Tracking code changes made by the agent
- Capturing complete conversation context for debugging
Hook Input with Metadata
When metadata options are enabled, the hook receives additional fields in the event data. Here’s an example showing all three options enabled: Configuration:{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "/etc/augment/hooks/analytics.sh"
}
],
"metadata": {
"includeUserContext": true,
"includeConversationData": true
}
}
]
}
}
{
"hook_event_name": "Stop",
"conversation_id": "conv-xyz789",
"workspace_roots": ["/Users/username/project"],
"agent_stop_cause": "end_turn",
"context": {
"userEmail": "user@example.com",
"modelName": "Claude Opus 4.5",
"toolVersion": "0.6.0",
"timestamp": "2025-01-15T10:30:00-08:00"
},
"conversation": {
"timestamp": "2025-01-15T10:30:00-08:00",
"userPrompt": "Add error handling to the login function",
"agentTextResponse": "I'll add comprehensive error handling...",
"agentCodeResponse": [{ "path": "src/auth/login.ts", "changeType": "edit" }]
}
}
{
"hook_event_name": "PostToolUse",
"conversation_id": "conv-xyz789",
"workspace_roots": ["/Users/username/project"],
"tool_name": "search_example-server",
"tool_input": { "query": "latest documentation" },
"tool_output": "Found 5 results...",
"is_mcp_tool": true,
"context": {
"userEmail": "user@example.com",
"modelName": "Claude Opus 4.5",
"toolVersion": "0.6.0",
"timestamp": "2025-01-15T10:30:00-08:00"
},
"mcp_metadata": {
"timestamp": "2025-01-15T10:30:00-08:00",
"mcpDecision": "yes",
"mcpTotalToolsCount": 15,
"mcpExecutedToolName": "search",
"mcpExecutedToolServerName": "example-server",
"mcpExecutedToolServerToolsCount": 5
}
}
{
"hook_event_name": "PostToolUse",
"conversation_id": "conv-xyz789",
"workspace_roots": ["/Users/username/project"],
"tool_name": "str-replace-editor",
"tool_input": {
"path": "src/auth.ts",
"old_str_1": "...",
"new_str_1": "..."
},
"tool_output": "File edited successfully",
"is_mcp_tool": false,
"file_changes": [
{
"path": "src/auth.ts",
"changeType": "edit",
"content": "// New content...",
"oldContent": "// Old content that was replaced..."
}
],
"context": {
"userEmail": "user@example.com",
"modelName": "Claude Opus 4.5",
"toolVersion": "0.6.0",
"timestamp": "2025-01-15T10:30:00-08:00"
}
}
Privacy Considerations
Metadata options follow a privacy-first, opt-in model:- Default deny: All sensitive data is excluded by default
- Explicit opt-in: Hooks must explicitly request metadata options
- Minimal access: Request only the options you need
- Audit trail: Metadata usage should be logged for compliance
When using metadata options that include user data (
includeConversationData,
includeUserContext), ensure your hook scripts: - Handle sensitive data securely - Comply with
privacy regulations (GDPR, CCPA, etc.) - Implement appropriate data retention policies - Use
encryption for data at rest and in transitExample: Analytics Hook with Metadata
Configuration:{
"hooks": {
"PostToolUse": [
{
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "/etc/augment/hooks/analytics.sh"
}
],
"metadata": {
"includeUserContext": true,
"includeMCPMetadata": true
}
}
]
}
}
#!/usr/bin/env bash
# /etc/augment/hooks/analytics.sh
EVENT_DATA=$(cat)
# Extract user context (available because includeUserContext=true)
USER_EMAIL=$(echo "$EVENT_DATA" | jq -r '.context.userEmail // ""')
MODEL_NAME=$(echo "$EVENT_DATA" | jq -r '.context.modelName // ""')
# Extract MCP metadata (available because includeMCPMetadata=true)
MCP_DECISION=$(echo "$EVENT_DATA" | jq -r '.mcp_metadata.mcpDecision // "no"')
MCP_SERVER=$(echo "$EVENT_DATA" | jq -r '.mcp_metadata.mcpExecutedToolServerName // ""')
# Log analytics
echo "User: $USER_EMAIL, Model: $MODEL_NAME, MCP: $MCP_DECISION, Server: $MCP_SERVER"
exit 0
#!/usr/bin/env python3
# /etc/augment/hooks/analytics.sh (Python via shebang)
import sys
import json
event_data = json.load(sys.stdin)
# Extract user context
context = event_data.get('context', {})
user_email = context.get('userEmail', '')
model_name = context.get('modelName', '')
# Extract MCP metadata
mcp = event_data.get('mcp_metadata', {})
mcp_decision = mcp.get('mcpDecision', 'no')
mcp_server = mcp.get('mcpExecutedToolServerName', '')
print(f"User: {user_email}, Model: {model_name}, MCP: {mcp_decision}")
sys.exit(0)
Working with MCP Tools
Hooks work seamlessly with Model Context Protocol (MCP) tools. MCP tools have special naming and metadata that you can use in hooks.MCP Tool Naming
MCP tools follow the naming pattern{toolName}_{serverName}. For example:
search_my-server- “search” tool from “my-server”query_database-server- “query” tool from “database-server”read_filesystem-mcp- “read” tool from “filesystem-mcp”
Both tool names and server names can contain underscores. The server name is always the suffix after the last underscore that matches the known server name. For reliable matching, enable
includeMCPMetadata: true on the hook configuration and read mcp_metadata.mcpExecutedToolServerName from the event JSON rather than parsing the tool name.MCP Tool Matchers
Use themcp: prefix to match MCP tools. The pattern after mcp: is a regex matched against the full tool name (toolName_serverName):
"mcp:*"- Match ALL MCP tools (special case)"mcp:.*_my-server$"- Match any tool frommy-server(use$anchor)"mcp:^search_my-server$"- Match exact toolsearchfrommy-server"mcp:^search_.*"- Matchsearchtool from any server
The
mcp: prefix ensures only MCP tools are matched. The pattern is a standard regex applied to
the full tool name. Use the $ anchor when matching server names to avoid partial matches.MCP Hook Examples
Log MCP tool usage:#!/usr/bin/env bash
EVENT_DATA=$(cat)
SERVER=$(echo "$EVENT_DATA" | jq -r '.mcp_metadata.mcpExecutedToolServerName // ""')
echo "[MCP] Server: $SERVER, Tool: $(echo "$EVENT_DATA" | jq -r '.tool_name')" >&2
exit 0
{
"hooks": {
"PreToolUse": [
{
"matcher": "mcp:*",
"hooks": [
{
"type": "command",
"command": "/etc/augment/hooks/log-mcp-tools.sh",
"timeout": 5000
}
]
}
]
}
}
#!/usr/bin/env bash
EVENT_DATA=$(cat)
MCP_SERVER=$(echo "$EVENT_DATA" | jq -r '.mcp_metadata.mcpExecutedToolServerName // ""')
if [ "$MCP_SERVER" = "blocked-server" ]; then
echo "This MCP server is blocked by security policy" >&2
exit 2
fi
exit 0
{
"hooks": {
"PreToolUse": [
{
"matcher": "mcp:.*_blocked-server$",
"hooks": [
{
"type": "command",
"command": "/etc/augment/hooks/block-mcp-server.sh",
"timeout": 5000
}
]
}
]
}
}
Examples
For ready-to-use hook examples, see Hooks Examples.Debugging Hooks
Environment Variables
Hooks have access to these environment variables during execution:| Variable | Description | Example |
|---|---|---|
AUGMENT_PROJECT_DIR | First workspace root directory (or process.cwd() if empty) | /Users/username/project |
AUGMENT_CONVERSATION_ID | Current conversation ID | conv-xyz789 |
AUGMENT_HOOK_EVENT | Event type | PreToolUse |
AUGMENT_TOOL_NAME | Tool name (PreToolUse/PostToolUse only) | launch-process |
#!/usr/bin/env bash
echo "Project: $AUGMENT_PROJECT_DIR, Event: $AUGMENT_HOOK_EVENT" >&2
exit 0
Testing Hooks Locally
1. Create a test event file:cat > test-event.json << 'EOF'
{
"hook_event_name": "PreToolUse",
"conversation_id": "test-conv",
"workspace_roots": ["/Users/username/project"],
"tool_name": "launch-process",
"tool_input": { "command": "git status" }
}
EOF
# Test your hook script (works for both bash and Python via shebang)
cat test-event.json | /etc/augment/hooks/my-hook.sh
echo "Exit code: $?"
# Test and validate JSON output
OUTPUT=$(cat test-event.json | /etc/augment/hooks/my-hook.sh)
echo "$OUTPUT" | jq . # Validates JSON syntax
Viewing Hook Execution Logs
Hooks log to the Augment logger. To see hook execution details: CLI:# Run with debug-level logging
auggie --log-level debug "your prompt here"
[HookExecutor]- Hook execution details[HookManager]- Hook matching and routing[hook-output-router]- Output routing decisions
Common Debugging Techniques
Log to stderr (won’t affect hook output):#!/usr/bin/env bash
EVENT_DATA=$(cat)
echo "[DEBUG] $(echo "$EVENT_DATA" | jq -r '.hook_event_name')" >&2
exit 0
#!/usr/bin/env bash
cat > /tmp/hook-debug.json
exit 0
3. Validate hook matcher patterns
To test if your matcher pattern works:# Test regex pattern in bash
TOOL_NAME="search_my-server"
PATTERN=".*_my-server$"
if [[ "$TOOL_NAME" =~ $PATTERN ]]; then
echo "Pattern matches!"
else
echo "Pattern does not match"
fi
# Test regex pattern in Python
import re
tool_name = "search_my-server"
pattern = r".*_my-server$"
if re.match(pattern, tool_name):
print("Pattern matches!")
else:
print("Pattern does not match")
Performance Tips
- Keep hooks fast - Hooks run synchronously and block tool execution
- Cache expensive operations - Store results in files or environment variables
- Use early returns - Exit as soon as you know the result
- Avoid network calls - Network requests add latency
- Minimize JSON parsing - Parse only the fields you need
#!/usr/bin/env bash
EVENT_DATA=$(cat)
# Early return if not a launch-process tool
TOOL_NAME=$(echo "$EVENT_DATA" | jq -r '.tool_name')
if [[ "$TOOL_NAME" != "launch-process" ]]; then
exit 0 # Not interested, return immediately
fi
# Only parse command if we got here
COMMAND=$(echo "$EVENT_DATA" | jq -r '.tool_input.command')
# Check command
if echo "$COMMAND" | grep -qE "rm -rf"; then
echo "Blocked dangerous command" >&2
exit 2
fi
exit 0
#!/usr/bin/env python3
import sys
import json
import re
event_data = json.load(sys.stdin)
# Early return if not a launch-process tool
tool_name = event_data.get('tool_name', '')
if tool_name != 'launch-process':
sys.exit(0) # Not interested, return immediately
# Only parse command if we got here
command = event_data.get('tool_input', {}).get('command', '')
# Check command
if re.search(r'rm -rf', command):
print("Blocked dangerous command", file=sys.stderr)
sys.exit(2)
sys.exit(0)
Troubleshooting
Hook not being triggered
Hook not being triggered
Check:
- Matcher pattern - Verify your regex pattern matches the tool name
- Event type - Ensure you’re listening to the correct event (PreToolUse vs PostToolUse)
- Settings file location - Verify hooks are in the correct settings file (project vs user vs system)
- Hook path - Ensure the hook script path is correct and the file exists
- File permissions - Make sure the hook script is executable (
chmod +x hook.sh)
# Check if hook file exists and is executable
ls -la /etc/augment/hooks/my-hook.sh
# Test matcher pattern
echo "launch-process" | grep -E "launch-process|str-replace-editor" # Should match
echo "view" | grep -E "launch-process|str-replace-editor" # Should not match
Hook execution timeout
Hook execution timeout
Hooks have a 60-second timeout by default. If your hook takes longer:
- Optimize the hook - Make it faster
- Run async operations - Don’t wait for slow operations
- Use background jobs - Start long-running tasks in the background
#!/usr/bin/env bash
EVENT_DATA=$(cat)
# Start long-running task in background (don't wait)
(
# Long-running operation here
sleep 300
) &
# Return immediately
exit 0
JSON parsing errors
JSON parsing errors
Common issues:
- Invalid JSON syntax - Use
jqto validate - Missing quotes - Ensure all strings are quoted
- Trailing commas - JSON doesn’t allow trailing commas
- Special characters - Escape special characters in strings
# Test JSON output
cat << 'EOF' | jq .
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny"
}
}
EOF
Hook blocks but shouldn't
Hook blocks but shouldn't
Check:
- Exit code - Make sure you’re returning
exit 0for success - JSON output - Verify
permissionDecisionis not set to"deny" - stderr output - Ensure you’re not writing errors to stderr with exit code 2
#!/usr/bin/env bash
EVENT_DATA=$(cat)
# Explicitly allow
echo "Allowing tool execution" >&2 # Debug message
exit 0 # Success - don't block
Hook errors in logs
Hook errors in logs
Check the Auggie logs for hook execution errors:
# View recent logs
tail -f ~/.augment/logs/auggie.log | grep -i hook
Testing hooks locally
Testing hooks locally
Test your hook script manually:
# Create test event data
echo '{
"hook_event_name": "PreToolUse",
"tool_name": "launch-process",
"tool_input": {"command": "ls -la"}
}' | /etc/augment/hooks/your-hook.sh
# Check exit code
echo $?
Security Considerations
USE AT YOUR OWN RISK: Hooks execute arbitrary shell commands on your system automatically. By using hooks, you acknowledge that:
- You are solely responsible for the commands you configure
- Hooks can modify, delete, or access any files your user account can access
- Malicious or poorly written hooks can cause data loss or system damage
- You should thoroughly test hooks in a safe environment before production use
Security Best Practices
- Validate and sanitize inputs - Never trust input data blindly
- Always quote shell variables - Use
"$VAR"not$VAR - Block path traversal - Check for
..in file paths - Use absolute paths - Specify full paths for scripts
- Skip sensitive files - Avoid
.env,.git/, keys, etc. - Set appropriate timeouts - Prevent hooks from hanging indefinitely
- Test thoroughly - Test hooks with various inputs before deploying
Best Practices
- Keep hooks fast: Hooks should complete quickly (< 1 second) to avoid slowing down the agent
- Use timeouts: Always set reasonable timeouts to prevent hanging
- Handle errors gracefully: Use exit code 0 for non-critical errors to avoid blocking the agent
- Log appropriately: Use stderr for user-facing messages, log files for detailed audit trails
- Test thoroughly: Test hooks with various tool inputs before deploying
- Version control: Keep hook scripts in version control with your project
- Document behavior: Add comments explaining what each hook does and why
Limitations
- Hooks currently only support command execution (webhooks planned for future)
- PostToolUse hooks cannot modify tool output (read-only)
- Hooks cannot access the agent’s conversation history directly
- Maximum timeout is enforced to prevent indefinite blocking
- Hook execution is sequential, not parallel
- Modifying tool input via PreToolUse hooks (
updatedInput) is not yet implemented — only blocking tools withpermissionDecision: "deny"is currently supported
Hook Execution Details
- Timeout: 60-second execution limit by default, configurable per command
- Execution order: Hooks execute in the order they are defined
- Environment: Runs in current directory with Auggie’s environment
- Input: JSON via stdin
- Output:
- PreToolUse/PostToolUse/Stop: Progress shown in logs
- SessionStart: stdout added as context for agent
- SessionEnd: Logged to debug only
Related Documentation
- Hooks Examples - Ready-to-use hook examples
- MCP (Model Context Protocol) - External tool integration
- Permissions - Tool permission system
- Rules & Guidelines - Custom rules and guidelines
- Custom Commands - Create custom CLI commands
Assistant
Responses are generated using AI and may contain mistakes.
