Initial release v3.3.8
- Hook system for customization - Documentation and installation scripts - Multi-platform support via GitHub releases - Binaries available for Windows, Linux (x64/ARM64), macOS (Intel/Apple Silicon) Generated with Claude Code via Happy
This commit is contained in:
@@ -0,0 +1,787 @@
|
||||
# Hooks reference
|
||||
|
||||
> This page provides reference documentation for implementing hooks in Claude Code.
|
||||
|
||||
<Tip>
|
||||
For a quickstart guide with examples, see [Get started with Claude Code hooks](/en/docs/claude-code/hooks-guide).
|
||||
</Tip>
|
||||
|
||||
## Configuration
|
||||
|
||||
Claude Code hooks are configured in your [settings files](/en/docs/claude-code/settings):
|
||||
|
||||
* `~/.claude/settings.json` - User settings
|
||||
* `.claude/settings.json` - Project settings
|
||||
* `.claude/settings.local.json` - Local project settings (not committed)
|
||||
* Enterprise managed policy settings
|
||||
|
||||
### Structure
|
||||
|
||||
Hooks are organized by matchers, where each matcher can have multiple hooks:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"EventName": [
|
||||
{
|
||||
"matcher": "ToolPattern",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "your-command-here"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* **matcher**: Pattern to match tool names, case-sensitive (only applicable for
|
||||
`PreToolUse` and `PostToolUse`)
|
||||
* Simple strings match exactly: `Write` matches only the Write tool
|
||||
* Supports regex: `Edit|Write` or `Notebook.*`
|
||||
* Use `*` to match all tools. You can also use empty string (`""`) or leave
|
||||
`matcher` blank.
|
||||
* **hooks**: Array of commands to execute when the pattern matches
|
||||
* `type`: Currently only `"command"` is supported
|
||||
* `command`: The bash command to execute (can use `$CLAUDE_PROJECT_DIR`
|
||||
environment variable)
|
||||
* `timeout`: (Optional) How long a command should run, in seconds, before
|
||||
canceling that specific command.
|
||||
|
||||
For events like `UserPromptSubmit`, `Notification`, `Stop`, and `SubagentStop`
|
||||
that don't use matchers, you can omit the matcher field:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"UserPromptSubmit": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "/path/to/prompt-validator.py"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Project-Specific Hook Scripts
|
||||
|
||||
You can use the environment variable `CLAUDE_PROJECT_DIR` (only available when
|
||||
Claude Code spawns the hook command) to reference scripts stored in your project,
|
||||
ensuring they work regardless of Claude's current directory:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/check-style.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Hook Events
|
||||
|
||||
### PreToolUse
|
||||
|
||||
Runs after Claude creates tool parameters and before processing the tool call.
|
||||
|
||||
**Common matchers:**
|
||||
|
||||
* `Task` - Subagent tasks (see [subagents documentation](/en/docs/claude-code/sub-agents))
|
||||
* `Bash` - Shell commands
|
||||
* `Glob` - File pattern matching
|
||||
* `Grep` - Content search
|
||||
* `Read` - File reading
|
||||
* `Edit`, `MultiEdit` - File editing
|
||||
* `Write` - File writing
|
||||
* `WebFetch`, `WebSearch` - Web operations
|
||||
|
||||
### PostToolUse
|
||||
|
||||
Runs immediately after a tool completes successfully.
|
||||
|
||||
Recognizes the same matcher values as PreToolUse.
|
||||
|
||||
### Notification
|
||||
|
||||
Runs when Claude Code sends notifications. Notifications are sent when:
|
||||
|
||||
1. Claude needs your permission to use a tool. Example: "Claude needs your
|
||||
permission to use Bash"
|
||||
2. The prompt input has been idle for at least 60 seconds. "Claude is waiting
|
||||
for your input"
|
||||
|
||||
### UserPromptSubmit
|
||||
|
||||
Runs when the user submits a prompt, before Claude processes it. This allows you
|
||||
to add additional context based on the prompt/conversation, validate prompts, or
|
||||
block certain types of prompts.
|
||||
|
||||
### Stop
|
||||
|
||||
Runs when the main Claude Code agent has finished responding. Does not run if
|
||||
the stoppage occurred due to a user interrupt.
|
||||
|
||||
### SubagentStop
|
||||
|
||||
Runs when a Claude Code subagent (Task tool call) has finished responding.
|
||||
|
||||
### SessionEnd
|
||||
|
||||
Runs when a Claude Code session ends. Useful for cleanup tasks, logging session
|
||||
statistics, or saving session state.
|
||||
|
||||
The `reason` field in the hook input will be one of:
|
||||
|
||||
* `clear` - Session cleared with /clear command
|
||||
* `logout` - User logged out
|
||||
* `prompt_input_exit` - User exited while prompt input was visible
|
||||
* `other` - Other exit reasons
|
||||
|
||||
### PreCompact
|
||||
|
||||
Runs before Claude Code is about to run a compact operation.
|
||||
|
||||
**Matchers:**
|
||||
|
||||
* `manual` - Invoked from `/compact`
|
||||
* `auto` - Invoked from auto-compact (due to full context window)
|
||||
|
||||
### SessionStart
|
||||
|
||||
Runs when Claude Code starts a new session or resumes an existing session (which
|
||||
currently does start a new session under the hood). Useful for loading in
|
||||
development context like existing issues or recent changes to your codebase.
|
||||
|
||||
**Matchers:**
|
||||
|
||||
* `startup` - Invoked from startup
|
||||
* `resume` - Invoked from `--resume`, `--continue`, or `/resume`
|
||||
* `clear` - Invoked from `/clear`
|
||||
|
||||
## Hook Input
|
||||
|
||||
Hooks receive JSON data via stdin containing session information and
|
||||
event-specific data:
|
||||
|
||||
```typescript
|
||||
{
|
||||
// Common fields
|
||||
session_id: string
|
||||
transcript_path: string // Path to conversation JSON
|
||||
cwd: string // The current working directory when the hook is invoked
|
||||
|
||||
// Event-specific fields
|
||||
hook_event_name: string
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### PreToolUse Input
|
||||
|
||||
The exact schema for `tool_input` depends on the tool.
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "abc123",
|
||||
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
|
||||
"cwd": "/Users/...",
|
||||
"hook_event_name": "PreToolUse",
|
||||
"tool_name": "Write",
|
||||
"tool_input": {
|
||||
"file_path": "/path/to/file.txt",
|
||||
"content": "file content"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### PostToolUse Input
|
||||
|
||||
The exact schema for `tool_input` and `tool_response` depends on the tool.
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "abc123",
|
||||
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
|
||||
"cwd": "/Users/...",
|
||||
"hook_event_name": "PostToolUse",
|
||||
"tool_name": "Write",
|
||||
"tool_input": {
|
||||
"file_path": "/path/to/file.txt",
|
||||
"content": "file content"
|
||||
},
|
||||
"tool_response": {
|
||||
"filePath": "/path/to/file.txt",
|
||||
"success": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Notification Input
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "abc123",
|
||||
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
|
||||
"cwd": "/Users/...",
|
||||
"hook_event_name": "Notification",
|
||||
"message": "Task completed successfully"
|
||||
}
|
||||
```
|
||||
|
||||
### UserPromptSubmit Input
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "abc123",
|
||||
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
|
||||
"cwd": "/Users/...",
|
||||
"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 Claude Code is already continuing as a result of
|
||||
a stop hook. Check this value or process the transcript to prevent Claude Code
|
||||
from running indefinitely.
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "abc123",
|
||||
"transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
|
||||
"hook_event_name": "Stop",
|
||||
"stop_hook_active": true
|
||||
}
|
||||
```
|
||||
|
||||
### PreCompact Input
|
||||
|
||||
For `manual`, `custom_instructions` comes from what the user passes into
|
||||
`/compact`. For `auto`, `custom_instructions` is empty.
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "abc123",
|
||||
"transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
|
||||
"hook_event_name": "PreCompact",
|
||||
"trigger": "manual",
|
||||
"custom_instructions": ""
|
||||
}
|
||||
```
|
||||
|
||||
### SessionStart Input
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "abc123",
|
||||
"transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
|
||||
"hook_event_name": "SessionStart",
|
||||
"source": "startup"
|
||||
}
|
||||
```
|
||||
|
||||
### SessionEnd Input
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "abc123",
|
||||
"transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
|
||||
"cwd": "/Users/...",
|
||||
"hook_event_name": "SessionEnd",
|
||||
"reason": "exit"
|
||||
}
|
||||
```
|
||||
|
||||
## Hook Output
|
||||
|
||||
There are two ways for hooks to return output back to Claude Code. The output
|
||||
communicates whether to block and any feedback that should be shown to Claude
|
||||
and the user.
|
||||
|
||||
### Simple: Exit Code
|
||||
|
||||
Hooks communicate status through exit codes, stdout, and stderr:
|
||||
|
||||
* **Exit code 0**: Success. `stdout` is shown 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. `stderr` is fed back to Claude to process
|
||||
automatically. See per-hook-event behavior below.
|
||||
* **Other exit codes**: Non-blocking error. `stderr` is shown to the user and
|
||||
execution continues.
|
||||
|
||||
<Warning>
|
||||
Reminder: Claude Code does not see stdout if the exit code is 0, except for
|
||||
the `UserPromptSubmit` hook where stdout is injected as context.
|
||||
</Warning>
|
||||
|
||||
#### Exit Code 2 Behavior
|
||||
|
||||
| Hook Event | Behavior |
|
||||
| ------------------ | ------------------------------------------------------------------ |
|
||||
| `PreToolUse` | Blocks the tool call, shows stderr to Claude |
|
||||
| `PostToolUse` | Shows stderr to Claude (tool already ran) |
|
||||
| `Notification` | N/A, shows stderr to user only |
|
||||
| `UserPromptSubmit` | Blocks prompt processing, erases prompt, shows stderr to user only |
|
||||
| `Stop` | Blocks stoppage, shows stderr to Claude |
|
||||
| `SubagentStop` | Blocks stoppage, shows stderr to Claude subagent |
|
||||
| `PreCompact` | N/A, shows stderr to user only |
|
||||
| `SessionStart` | N/A, shows stderr to user only |
|
||||
| `SessionEnd` | N/A, shows stderr to user only |
|
||||
|
||||
### Advanced: JSON Output
|
||||
|
||||
Hooks can return structured JSON in `stdout` for more sophisticated control:
|
||||
|
||||
#### Common JSON Fields
|
||||
|
||||
All hook types can include these optional fields:
|
||||
|
||||
```json
|
||||
{
|
||||
"continue": true, // Whether Claude should continue after hook execution (default: true)
|
||||
"stopReason": "string", // Message shown when continue is false
|
||||
|
||||
"suppressOutput": true, // Hide stdout from transcript mode (default: false)
|
||||
"systemMessage": "string" // Optional warning message shown to the user
|
||||
}
|
||||
```
|
||||
|
||||
If `continue` is false, Claude stops processing after the hooks run.
|
||||
|
||||
* For `PreToolUse`, this is different from `"permissionDecision": "deny"`, which
|
||||
only blocks a specific tool call and provides automatic feedback to Claude.
|
||||
* For `PostToolUse`, this is different from `"decision": "block"`, which
|
||||
provides automated feedback to Claude.
|
||||
* For `UserPromptSubmit`, this prevents the prompt from being processed.
|
||||
* For `Stop` and `SubagentStop`, this takes precedence over any
|
||||
`"decision": "block"` output.
|
||||
* In all cases, `"continue" = false` takes precedence over any
|
||||
`"decision": "block"` output.
|
||||
|
||||
`stopReason` accompanies `continue` with a reason shown to the user, not shown
|
||||
to Claude.
|
||||
|
||||
#### `PreToolUse` Decision Control
|
||||
|
||||
`PreToolUse` hooks can control whether a tool call proceeds.
|
||||
|
||||
* `"allow"` bypasses the permission system. `permissionDecisionReason` is shown
|
||||
to the user but not to Claude.
|
||||
* `"deny"` prevents the tool call from executing. `permissionDecisionReason` is
|
||||
shown to Claude.
|
||||
* `"ask"` asks the user to confirm the tool call in the UI.
|
||||
`permissionDecisionReason` is shown to the user but not to Claude.
|
||||
|
||||
```json
|
||||
{
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "PreToolUse",
|
||||
"permissionDecision": "allow" | "deny" | "ask",
|
||||
"permissionDecisionReason": "My reason here"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<Note>
|
||||
The `decision` and `reason` fields are deprecated for PreToolUse hooks.
|
||||
Use `hookSpecificOutput.permissionDecision` and
|
||||
`hookSpecificOutput.permissionDecisionReason` instead. The deprecated fields
|
||||
`"approve"` and `"block"` map to `"allow"` and `"deny"` respectively.
|
||||
</Note>
|
||||
|
||||
#### `PostToolUse` Decision Control
|
||||
|
||||
`PostToolUse` hooks can provide feedback to Claude after tool execution.
|
||||
|
||||
* `"block"` automatically prompts Claude with `reason`.
|
||||
* `undefined` does nothing. `reason` is ignored.
|
||||
* `"hookSpecificOutput.additionalContext"` adds context for Claude to consider.
|
||||
|
||||
```json
|
||||
{
|
||||
"decision": "block" | undefined,
|
||||
"reason": "Explanation for decision",
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "PostToolUse",
|
||||
"additionalContext": "Additional information for Claude"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### `UserPromptSubmit` Decision Control
|
||||
|
||||
`UserPromptSubmit` hooks can control whether a user prompt is processed.
|
||||
|
||||
* `"block"` prevents the prompt from being processed. The submitted prompt is
|
||||
erased from context. `"reason"` is shown to the user but not added to context.
|
||||
* `undefined` allows the prompt to proceed normally. `"reason"` is ignored.
|
||||
* `"hookSpecificOutput.additionalContext"` adds the string to the context if not
|
||||
blocked.
|
||||
|
||||
```json
|
||||
{
|
||||
"decision": "block" | undefined,
|
||||
"reason": "Explanation for decision",
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "UserPromptSubmit",
|
||||
"additionalContext": "My additional context here"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### `Stop`/`SubagentStop` Decision Control
|
||||
|
||||
`Stop` and `SubagentStop` hooks can control whether Claude must continue.
|
||||
|
||||
* `"block"` prevents Claude from stopping. You must populate `reason` for Claude
|
||||
to know how to proceed.
|
||||
* `undefined` allows Claude to stop. `reason` is ignored.
|
||||
|
||||
```json
|
||||
{
|
||||
"decision": "block" | undefined,
|
||||
"reason": "Must be provided when Claude is blocked from stopping"
|
||||
}
|
||||
```
|
||||
|
||||
#### `SessionStart` Decision Control
|
||||
|
||||
`SessionStart` hooks allow you to load in context at the start of a session.
|
||||
|
||||
* `"hookSpecificOutput.additionalContext"` adds the string to the context.
|
||||
* Multiple hooks' `additionalContext` values are concatenated.
|
||||
|
||||
```json
|
||||
{
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "SessionStart",
|
||||
"additionalContext": "My additional context here"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### `SessionEnd` Decision Control
|
||||
|
||||
`SessionEnd` hooks run when a session ends. They cannot block session termination
|
||||
but can perform cleanup tasks.
|
||||
|
||||
#### Exit Code Example: Bash Command Validation
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
|
||||
# Define validation rules as a list of (regex pattern, message) tuples
|
||||
VALIDATION_RULES = [
|
||||
(
|
||||
r"\bgrep\b(?!.*\|)",
|
||||
"Use 'rg' (ripgrep) instead of 'grep' for better performance and features",
|
||||
),
|
||||
(
|
||||
r"\bfind\s+\S+\s+-name\b",
|
||||
"Use 'rg --files | rg pattern' or 'rg --files -g pattern' instead of 'find -name' for better performance",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def validate_command(command: str) -> list[str]:
|
||||
issues = []
|
||||
for pattern, message in VALIDATION_RULES:
|
||||
if re.search(pattern, command):
|
||||
issues.append(message)
|
||||
return issues
|
||||
|
||||
|
||||
try:
|
||||
input_data = json.load(sys.stdin)
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
tool_name = input_data.get("tool_name", "")
|
||||
tool_input = input_data.get("tool_input", {})
|
||||
command = tool_input.get("command", "")
|
||||
|
||||
if tool_name != "Bash" or not command:
|
||||
sys.exit(1)
|
||||
|
||||
# Validate the command
|
||||
issues = validate_command(command)
|
||||
|
||||
if issues:
|
||||
for message in issues:
|
||||
print(f"• {message}", file=sys.stderr)
|
||||
# Exit code 2 blocks tool call and shows stderr to Claude
|
||||
sys.exit(2)
|
||||
```
|
||||
|
||||
#### JSON Output Example: UserPromptSubmit to Add Context and Validation
|
||||
|
||||
<Note>
|
||||
For `UserPromptSubmit` hooks, you can inject context using either method:
|
||||
|
||||
* Exit code 0 with stdout: Claude sees the context (special case for `UserPromptSubmit`)
|
||||
* JSON output: Provides more control over the behavior
|
||||
</Note>
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
import json
|
||||
import sys
|
||||
import re
|
||||
import datetime
|
||||
|
||||
# Load input from stdin
|
||||
try:
|
||||
input_data = json.load(sys.stdin)
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
prompt = input_data.get("prompt", "")
|
||||
|
||||
# Check for sensitive patterns
|
||||
sensitive_patterns = [
|
||||
(r"(?i)\b(password|secret|key|token)\s*[:=]", "Prompt contains potential secrets"),
|
||||
]
|
||||
|
||||
for pattern, message in sensitive_patterns:
|
||||
if re.search(pattern, prompt):
|
||||
# Use JSON output to block with a specific reason
|
||||
output = {
|
||||
"decision": "block",
|
||||
"reason": f"Security policy violation: {message}. Please rephrase your request without sensitive information."
|
||||
}
|
||||
print(json.dumps(output))
|
||||
sys.exit(0)
|
||||
|
||||
# Add current time to context
|
||||
context = f"Current time: {datetime.datetime.now()}"
|
||||
print(context)
|
||||
|
||||
"""
|
||||
The following is also equivalent:
|
||||
print(json.dumps({
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "UserPromptSubmit",
|
||||
"additionalContext": context,
|
||||
},
|
||||
}))
|
||||
"""
|
||||
|
||||
# Allow the prompt to proceed with the additional context
|
||||
sys.exit(0)
|
||||
```
|
||||
|
||||
#### JSON Output Example: PreToolUse with Approval
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
import json
|
||||
import sys
|
||||
|
||||
# Load input from stdin
|
||||
try:
|
||||
input_data = json.load(sys.stdin)
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
tool_name = input_data.get("tool_name", "")
|
||||
tool_input = input_data.get("tool_input", {})
|
||||
|
||||
# Example: Auto-approve file reads for documentation files
|
||||
if tool_name == "Read":
|
||||
file_path = tool_input.get("file_path", "")
|
||||
if file_path.endswith((".md", ".mdx", ".txt", ".json")):
|
||||
# Use JSON output to auto-approve the tool call
|
||||
output = {
|
||||
"decision": "approve",
|
||||
"reason": "Documentation file auto-approved",
|
||||
"suppressOutput": True # Don't show in transcript mode
|
||||
}
|
||||
print(json.dumps(output))
|
||||
sys.exit(0)
|
||||
|
||||
# For other cases, let the normal permission flow proceed
|
||||
sys.exit(0)
|
||||
```
|
||||
|
||||
## Working with MCP Tools
|
||||
|
||||
Claude Code hooks work seamlessly with
|
||||
[Model Context Protocol (MCP) tools](/en/docs/claude-code/mcp). When MCP servers
|
||||
provide tools, they appear with a special naming pattern that you can match in
|
||||
your hooks.
|
||||
|
||||
### MCP Tool Naming
|
||||
|
||||
MCP tools follow the pattern `mcp__<server>__<tool>`, for example:
|
||||
|
||||
* `mcp__memory__create_entities` - Memory server's create entities tool
|
||||
* `mcp__filesystem__read_file` - Filesystem server's read file tool
|
||||
* `mcp__github__search_repositories` - GitHub server's search tool
|
||||
|
||||
### Configuring Hooks for MCP Tools
|
||||
|
||||
You can target specific MCP tools or entire MCP servers:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "mcp__memory__.*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": "mcp__.*__write.*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "/home/user/scripts/validate-mcp-write.py"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
<Tip>
|
||||
For practical examples including code formatting, notifications, and file protection, see [More Examples](/en/docs/claude-code/hooks-guide#more-examples) in the get started guide.
|
||||
</Tip>
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Disclaimer
|
||||
|
||||
**USE AT YOUR OWN RISK**: Claude Code 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
|
||||
* Anthropic provides no warranty and assumes no liability for any damages
|
||||
resulting from hook usage
|
||||
* You should thoroughly test hooks in a safe environment before production use
|
||||
|
||||
Always review and understand any hook commands before adding them to your
|
||||
configuration.
|
||||
|
||||
### Security Best Practices
|
||||
|
||||
Here are some key practices for writing more secure hooks:
|
||||
|
||||
1. **Validate and sanitize inputs** - Never trust input data blindly
|
||||
2. **Always quote shell variables** - Use `"$VAR"` not `$VAR`
|
||||
3. **Block path traversal** - Check for `..` in file paths
|
||||
4. **Use absolute paths** - Specify full paths for scripts (use
|
||||
`$CLAUDE_PROJECT_DIR` for the project path)
|
||||
5. **Skip sensitive files** - Avoid `.env`, `.git/`, keys, etc.
|
||||
|
||||
### Configuration Safety
|
||||
|
||||
Direct edits to hooks in settings files don't take effect immediately. Claude
|
||||
Code:
|
||||
|
||||
1. Captures a snapshot of hooks at startup
|
||||
2. Uses this snapshot throughout the session
|
||||
3. Warns if hooks are modified externally
|
||||
4. Requires review in `/hooks` menu for changes to apply
|
||||
|
||||
This prevents malicious hook modifications from affecting your current session.
|
||||
|
||||
## Hook Execution Details
|
||||
|
||||
* **Timeout**: 60-second execution limit by default, configurable per command.
|
||||
* A timeout for an individual command does not affect the other commands.
|
||||
* **Parallelization**: All matching hooks run in parallel
|
||||
* **Deduplication**: Multiple identical hook commands are deduplicated automatically
|
||||
* **Environment**: Runs in current directory with Claude Code's environment
|
||||
* The `CLAUDE_PROJECT_DIR` environment variable is available and contains the
|
||||
absolute path to the project root directory (where Claude Code was started)
|
||||
* **Input**: JSON via stdin
|
||||
* **Output**:
|
||||
* PreToolUse/PostToolUse/Stop/SubagentStop: Progress shown in transcript (Ctrl-R)
|
||||
* Notification/SessionEnd: Logged to debug only (`--debug`)
|
||||
* UserPromptSubmit/SessionStart: stdout added as context for Claude
|
||||
|
||||
## Debugging
|
||||
|
||||
### Basic Troubleshooting
|
||||
|
||||
If your hooks aren't working:
|
||||
|
||||
1. **Check configuration** - Run `/hooks` to see if your hook is registered
|
||||
2. **Verify syntax** - Ensure your JSON settings are valid
|
||||
3. **Test commands** - Run hook commands manually first
|
||||
4. **Check permissions** - Make sure scripts are executable
|
||||
5. **Review logs** - Use `claude --debug` to see hook execution details
|
||||
|
||||
Common issues:
|
||||
|
||||
* **Quotes not escaped** - Use `\"` inside JSON strings
|
||||
* **Wrong matcher** - Check tool names match exactly (case-sensitive)
|
||||
* **Command not found** - Use full paths for scripts
|
||||
|
||||
### Advanced Debugging
|
||||
|
||||
For complex hook issues:
|
||||
|
||||
1. **Inspect hook execution** - Use `claude --debug` to see detailed hook
|
||||
execution
|
||||
2. **Validate JSON schemas** - Test hook input/output with external tools
|
||||
3. **Check environment variables** - Verify Claude Code's 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
|
||||
|
||||
### Debug Output Example
|
||||
|
||||
Use `claude --debug` to see 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) showing:
|
||||
|
||||
* Which hook is running
|
||||
* Command being executed
|
||||
* Success/failure status
|
||||
* Output or error messages
|
||||
@@ -0,0 +1,202 @@
|
||||
# Status line configuration
|
||||
|
||||
> Create a custom status line for Claude Code to display contextual information
|
||||
|
||||
Make Claude Code your own with a custom status line that displays at the bottom of the Claude Code interface, similar to how terminal prompts (PS1) work in shells like Oh-my-zsh.
|
||||
|
||||
## Create a custom status line
|
||||
|
||||
You can either:
|
||||
|
||||
* Run `/statusline` to ask Claude Code to help you set up a custom status line. By default, it will try to reproduce your terminal's prompt, but you can provide additional instructions about the behavior you want to Claude Code, such as `/statusline show the model name in orange`
|
||||
|
||||
* Directly add a `statusLine` command to your `.claude/settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"statusLine": {
|
||||
"type": "command",
|
||||
"command": "~/.claude/statusline.sh",
|
||||
"padding": 0 // Optional: set to 0 to let status line go to edge
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## How it Works
|
||||
|
||||
* The status line is updated when the conversation messages update
|
||||
* Updates run at most every 300ms
|
||||
* The first line of stdout from your command becomes the status line text
|
||||
* ANSI color codes are supported for styling your status line
|
||||
* Claude Code passes contextual information about the current session (model, directories, etc.) as JSON to your script via stdin
|
||||
|
||||
## JSON Input Structure
|
||||
|
||||
Your status line command receives structured data via stdin in JSON format:
|
||||
|
||||
```json
|
||||
{
|
||||
"hook_event_name": "Status",
|
||||
"session_id": "abc123...",
|
||||
"transcript_path": "/path/to/transcript.json",
|
||||
"cwd": "/current/working/directory",
|
||||
"model": {
|
||||
"id": "claude-opus-4-1",
|
||||
"display_name": "Opus"
|
||||
},
|
||||
"workspace": {
|
||||
"current_dir": "/current/working/directory",
|
||||
"project_dir": "/original/project/directory"
|
||||
},
|
||||
"version": "1.0.80",
|
||||
"output_style": {
|
||||
"name": "default"
|
||||
},
|
||||
"cost": {
|
||||
"total_cost_usd": 0.01234,
|
||||
"total_duration_ms": 45000,
|
||||
"total_api_duration_ms": 2300,
|
||||
"total_lines_added": 156,
|
||||
"total_lines_removed": 23
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Example Scripts
|
||||
|
||||
### Simple Status Line
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Read JSON input from stdin
|
||||
input=$(cat)
|
||||
|
||||
# Extract values using jq
|
||||
MODEL_DISPLAY=$(echo "$input" | jq -r '.model.display_name')
|
||||
CURRENT_DIR=$(echo "$input" | jq -r '.workspace.current_dir')
|
||||
|
||||
echo "[$MODEL_DISPLAY] 📁 ${CURRENT_DIR##*/}"
|
||||
```
|
||||
|
||||
### Git-Aware Status Line
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Read JSON input from stdin
|
||||
input=$(cat)
|
||||
|
||||
# Extract values using jq
|
||||
MODEL_DISPLAY=$(echo "$input" | jq -r '.model.display_name')
|
||||
CURRENT_DIR=$(echo "$input" | jq -r '.workspace.current_dir')
|
||||
|
||||
# Show git branch if in a git repo
|
||||
GIT_BRANCH=""
|
||||
if git rev-parse --git-dir > /dev/null 2>&1; then
|
||||
BRANCH=$(git branch --show-current 2>/dev/null)
|
||||
if [ -n "$BRANCH" ]; then
|
||||
GIT_BRANCH=" | 🌿 $BRANCH"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "[$MODEL_DISPLAY] 📁 ${CURRENT_DIR##*/}$GIT_BRANCH"
|
||||
```
|
||||
|
||||
### Python Example
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Read JSON from stdin
|
||||
data = json.load(sys.stdin)
|
||||
|
||||
# Extract values
|
||||
model = data['model']['display_name']
|
||||
current_dir = os.path.basename(data['workspace']['current_dir'])
|
||||
|
||||
# Check for git branch
|
||||
git_branch = ""
|
||||
if os.path.exists('.git'):
|
||||
try:
|
||||
with open('.git/HEAD', 'r') as f:
|
||||
ref = f.read().strip()
|
||||
if ref.startswith('ref: refs/heads/'):
|
||||
git_branch = f" | 🌿 {ref.replace('ref: refs/heads/', '')}"
|
||||
except:
|
||||
pass
|
||||
|
||||
print(f"[{model}] 📁 {current_dir}{git_branch}")
|
||||
```
|
||||
|
||||
### Node.js Example
|
||||
|
||||
```javascript
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Read JSON from stdin
|
||||
let input = '';
|
||||
process.stdin.on('data', chunk => input += chunk);
|
||||
process.stdin.on('end', () => {
|
||||
const data = JSON.parse(input);
|
||||
|
||||
// Extract values
|
||||
const model = data.model.display_name;
|
||||
const currentDir = path.basename(data.workspace.current_dir);
|
||||
|
||||
// Check for git branch
|
||||
let gitBranch = '';
|
||||
try {
|
||||
const headContent = fs.readFileSync('.git/HEAD', 'utf8').trim();
|
||||
if (headContent.startsWith('ref: refs/heads/')) {
|
||||
gitBranch = ` | 🌿 ${headContent.replace('ref: refs/heads/', '')}`;
|
||||
}
|
||||
} catch (e) {
|
||||
// Not a git repo or can't read HEAD
|
||||
}
|
||||
|
||||
console.log(`[${model}] 📁 ${currentDir}${gitBranch}`);
|
||||
});
|
||||
```
|
||||
|
||||
### Helper Function Approach
|
||||
|
||||
For more complex bash scripts, you can create helper functions:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Read JSON input once
|
||||
input=$(cat)
|
||||
|
||||
# Helper functions for common extractions
|
||||
get_model_name() { echo "$input" | jq -r '.model.display_name'; }
|
||||
get_current_dir() { echo "$input" | jq -r '.workspace.current_dir'; }
|
||||
get_project_dir() { echo "$input" | jq -r '.workspace.project_dir'; }
|
||||
get_version() { echo "$input" | jq -r '.version'; }
|
||||
get_cost() { echo "$input" | jq -r '.cost.total_cost_usd'; }
|
||||
get_duration() { echo "$input" | jq -r '.cost.total_duration_ms'; }
|
||||
get_lines_added() { echo "$input" | jq -r '.cost.total_lines_added'; }
|
||||
get_lines_removed() { echo "$input" | jq -r '.cost.total_lines_removed'; }
|
||||
|
||||
# Use the helpers
|
||||
MODEL=$(get_model_name)
|
||||
DIR=$(get_current_dir)
|
||||
echo "[$MODEL] 📁 ${DIR##*/}"
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
* Keep your status line concise - it should fit on one line
|
||||
* Use emojis (if your terminal supports them) and colors to make information scannable
|
||||
* Use `jq` for JSON parsing in Bash (see examples above)
|
||||
* Test your script by running it manually with mock JSON input: `echo '{"model":{"display_name":"Test"},"workspace":{"current_dir":"/test"}}' | ./statusline.sh`
|
||||
* Consider caching expensive operations (like git status) if needed
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
* If your status line doesn't appear, check that your script is executable (`chmod +x`)
|
||||
* Ensure your script outputs to stdout (not stderr)
|
||||
@@ -0,0 +1,173 @@
|
||||
# Claude Code Hook Configuration Documentation
|
||||
|
||||
**LOCKED by @docs-agent | Change to 🔑 to allow @docs-agent edits**
|
||||
|
||||
## Official Documentation Reference
|
||||
|
||||
- **Source**: Claude Code Hooks API Documentation
|
||||
- **Version**: v2025
|
||||
- **Last Verified**: 2025-08-31
|
||||
- **Official URL**: https://docs.anthropic.com/en/docs/claude-code/hooks
|
||||
|
||||
## Hook Configuration Structure
|
||||
|
||||
### Two Categories of Hooks
|
||||
|
||||
Claude Code hooks are divided into two distinct categories with different configuration structures:
|
||||
|
||||
#### 1. Tool-Related Hooks
|
||||
These hooks are triggered in relation to tool usage and require a `matcher` field:
|
||||
- `PreToolUse`: Executed before a tool is invoked
|
||||
- `PostToolUse`: Executed after a tool completes
|
||||
|
||||
**Configuration Structure:**
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Edit|MultiEdit|Write",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "/path/to/script.js",
|
||||
"timeout": 60000
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. Non-Tool Hooks
|
||||
These hooks are triggered by system events and **MUST NOT** have a `matcher` or `pattern` field:
|
||||
- `PreCompact`: Before conversation compaction
|
||||
- `SessionStart`: When a new session begins
|
||||
- `SessionEnd`: When a session ends
|
||||
- `UserPromptSubmit`: When user submits a prompt
|
||||
- `Notification`: For system notifications
|
||||
- `Stop`: When Claude is stopping
|
||||
- `SubagentStop`: When a subagent is stopping
|
||||
|
||||
**Configuration Structure:**
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "/path/to/script.js",
|
||||
"timeout": 30000
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Common Configuration Mistakes
|
||||
|
||||
### ❌ INCORRECT: Adding `pattern` field to non-tool hooks
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreCompact": [
|
||||
{
|
||||
"pattern": "*", // WRONG: Non-tool hooks don't use patterns
|
||||
"hooks": [...]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ✅ CORRECT: Non-tool hooks without matcher
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreCompact": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "/path/to/pre-compact.js",
|
||||
"timeout": 180000
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Hook Field Reference
|
||||
|
||||
### Common Fields (All Hooks)
|
||||
- `type`: Always `"command"` for external scripts
|
||||
- `command`: Absolute path to the executable script
|
||||
- `timeout`: Optional timeout in milliseconds (default: 60000)
|
||||
|
||||
### Tool Hook Specific
|
||||
- `matcher`: Regex pattern to match tool names
|
||||
- Example: `"Edit|MultiEdit|Write"`
|
||||
- Example: `"mcp__.*__write.*"`
|
||||
- Example: `"Bash"`
|
||||
|
||||
### Environment Variables Available to Hooks
|
||||
- `$CLAUDE_PROJECT_DIR`: Project root directory
|
||||
- Standard environment variables from the shell
|
||||
|
||||
## Hook Input/Output
|
||||
|
||||
### Input (via stdin)
|
||||
All hooks receive JSON input with common fields:
|
||||
```json
|
||||
{
|
||||
"session_id": "string",
|
||||
"transcript_path": "string",
|
||||
"cwd": "string",
|
||||
"hook_event_name": "string",
|
||||
// Additional event-specific fields
|
||||
}
|
||||
```
|
||||
|
||||
### Output Options
|
||||
Hooks can output:
|
||||
1. **Plain text** (stdout): Added as context
|
||||
2. **JSON** (stdout): Structured response for decisions
|
||||
3. **Exit codes**:
|
||||
- `0`: Success, continue normally
|
||||
- `1`: General error
|
||||
- `2`: Block operation (for PreToolUse)
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
### File Locations
|
||||
- User settings: `~/.claude/settings.json`
|
||||
- Project settings: `./.claude/settings.json`
|
||||
- Local settings: `./.claude/settings.local.json`
|
||||
|
||||
### Settings Precedence (Highest to Lowest)
|
||||
1. Enterprise managed policies
|
||||
2. Command line arguments
|
||||
3. Local project settings
|
||||
4. Shared project settings
|
||||
5. User settings
|
||||
|
||||
## Cross-References
|
||||
|
||||
- Code Implementation: `/Users/alexnewman/Scripts/claude-mem/src/commands/install.ts:263-320`
|
||||
- Hook Files: `/Users/alexnewman/Scripts/claude-mem/hooks/`
|
||||
- User Guide: `/Users/alexnewman/Scripts/claude-mem/README-npm.md`
|
||||
|
||||
## Version History
|
||||
|
||||
- **2025-08-31**: Fixed hook configuration to remove incorrect `pattern` field from non-tool hooks
|
||||
- **2025-08-31**: Documented official hook structure requirements per Claude Code API
|
||||
|
||||
---
|
||||
*This documentation is maintained by @docs-agent and verified against official Anthropic documentation.*
|
||||
@@ -0,0 +1,127 @@
|
||||
# Claude Code Hook Response Format Documentation
|
||||
## Source: Official Claude Code Docs v2025
|
||||
## Last Verified: 2025-08-31
|
||||
|
||||
## Common Hook Response Fields
|
||||
|
||||
All hooks can return these common fields:
|
||||
|
||||
```json
|
||||
{
|
||||
"continue": true, // Whether Claude should continue (default: true)
|
||||
"stopReason": "string", // Message shown when continue is false
|
||||
"suppressOutput": true, // Hide stdout from transcript (default: false)
|
||||
"systemMessage": "string" // Optional warning message shown to user
|
||||
}
|
||||
```
|
||||
|
||||
## Hook-Specific Response Formats
|
||||
|
||||
### PreCompact Hook
|
||||
**IMPORTANT**: PreCompact does NOT support `hookSpecificOutput`
|
||||
|
||||
```json
|
||||
{
|
||||
"continue": true,
|
||||
"suppressOutput": true
|
||||
}
|
||||
```
|
||||
|
||||
### SessionStart Hook
|
||||
SessionStart DOES support `hookSpecificOutput`:
|
||||
|
||||
```json
|
||||
{
|
||||
"continue": true,
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "SessionStart",
|
||||
"additionalContext": "Context string to add to session"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### PreToolUse Hook
|
||||
```json
|
||||
{
|
||||
"continue": true,
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "PreToolUse",
|
||||
"permissionDecision": "allow" | "deny" | "ask",
|
||||
"permissionDecisionReason": "Reason for decision"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### PostToolUse Hook
|
||||
```json
|
||||
{
|
||||
"decision": "block", // Optional - blocks further processing
|
||||
"reason": "Explanation",
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "PostToolUse",
|
||||
"additionalContext": "Additional information for Claude"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### UserPromptSubmit Hook
|
||||
```json
|
||||
{
|
||||
"decision": "block", // Optional - blocks the prompt
|
||||
"reason": "Security policy violation",
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "UserPromptSubmit",
|
||||
"additionalContext": "Additional context for the prompt"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Exit Codes
|
||||
|
||||
- `0`: Success - hook executed successfully
|
||||
- `1`: Error - shown to user with stdout
|
||||
- `2`: Error - shown to Claude with stderr
|
||||
|
||||
## Common Mistakes to Avoid
|
||||
|
||||
### \u274c INCORRECT: Using wrong field names
|
||||
```javascript
|
||||
// WRONG
|
||||
{
|
||||
"decision": "block", // \u274c Wrong field
|
||||
"reason": "Error message" // \u274c Wrong field
|
||||
}
|
||||
```
|
||||
|
||||
### \u2705 CORRECT: Using official field names
|
||||
```javascript
|
||||
// RIGHT
|
||||
{
|
||||
"continue": false,
|
||||
"stopReason": "Error message"
|
||||
}
|
||||
```
|
||||
|
||||
### \u274c INCORRECT: Adding hookSpecificOutput to PreCompact
|
||||
```javascript
|
||||
// WRONG - PreCompact doesn't support this
|
||||
{
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "PreCompact",
|
||||
"status": "success"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### \u2705 CORRECT: Simple response for PreCompact
|
||||
```javascript
|
||||
// RIGHT
|
||||
{
|
||||
"continue": true,
|
||||
"suppressOutput": true
|
||||
}
|
||||
```
|
||||
|
||||
## References
|
||||
- Official Docs: https://docs.anthropic.com/en/docs/claude-code/hooks
|
||||
- Hook Examples: https://docs.anthropic.com/en/docs/claude-code/hooks-guide
|
||||
@@ -0,0 +1,175 @@
|
||||
# Claude Code Hooks Configuration Documentation
|
||||
## Source: Official Claude Code Docs v2025
|
||||
## Last Verified: 2025-08-31
|
||||
|
||||
## Hook Configuration Structure
|
||||
|
||||
### For Tool-Based Hooks (PreToolUse, PostToolUse)
|
||||
These hooks use the `matcher` field to match tool patterns:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "ToolPattern", // Required for tool hooks
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "your-command-here",
|
||||
"timeout": 60000 // Optional, in milliseconds
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### For Non-Tool Hooks (PreCompact, SessionStart, etc.)
|
||||
These hooks DO NOT use matcher/pattern fields:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreCompact": [
|
||||
{
|
||||
// NO matcher or pattern field!
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "/path/to/script.js",
|
||||
"timeout": 180000
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Available Hook Events
|
||||
|
||||
### Tool-Related Hooks (use matcher)
|
||||
- **PreToolUse**: Before tool execution
|
||||
- **PostToolUse**: After tool execution
|
||||
|
||||
### System Event Hooks (no matcher)
|
||||
- **PreCompact**: Before conversation compaction
|
||||
- **SessionStart**: When session begins
|
||||
- **SessionEnd**: When session ends (not in official docs)
|
||||
- **UserPromptSubmit**: When user submits prompt
|
||||
- **Notification**: When Claude needs user input
|
||||
- **Stop**: When stop is requested
|
||||
- **SubagentStop**: When subagent stop is requested
|
||||
|
||||
## Hook Payload Structure
|
||||
|
||||
### Common Fields (all hooks)
|
||||
```json
|
||||
{
|
||||
"session_id": "string",
|
||||
"transcript_path": "string",
|
||||
"hook_event_name": "string",
|
||||
"cwd": "string" // Current working directory
|
||||
}
|
||||
```
|
||||
|
||||
### PreCompact Specific
|
||||
```json
|
||||
{
|
||||
"hook_event_name": "PreCompact",
|
||||
"trigger": "manual" | "auto",
|
||||
"custom_instructions": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### SessionStart Specific
|
||||
```json
|
||||
{
|
||||
"hook_event_name": "SessionStart",
|
||||
"source": "startup" | "compact" | "vscode" | "web"
|
||||
}
|
||||
```
|
||||
|
||||
### PreToolUse/PostToolUse Specific
|
||||
```json
|
||||
{
|
||||
"tool_name": "string",
|
||||
"tool_input": { /* tool specific */ },
|
||||
"tool_response": { /* PostToolUse only */ }
|
||||
}
|
||||
```
|
||||
|
||||
## Common Configuration Mistakes
|
||||
|
||||
### \u274c INCORRECT: Using 'pattern' for non-tool hooks
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreCompact": [{
|
||||
"pattern": "*", // \u274c WRONG - non-tool hooks don't use this
|
||||
"hooks": [...]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### \u2705 CORRECT: No matcher for non-tool hooks
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreCompact": [{
|
||||
// No pattern or matcher field
|
||||
"hooks": [...]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### \u274c INCORRECT: Wrong matcher field name
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [{
|
||||
"pattern": "Bash", // \u274c WRONG field name
|
||||
"hooks": [...]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### \u2705 CORRECT: Using 'matcher' for tool hooks
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [{
|
||||
"matcher": "Bash", // \u2705 Correct field name
|
||||
"hooks": [...]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Matcher Patterns for Tool Hooks
|
||||
|
||||
- **Exact match**: `"Bash"` - matches only Bash tool
|
||||
- **Multiple tools**: `"Edit|MultiEdit|Write"` - matches any of these
|
||||
- **MCP tools**: `"mcp__memory__.*"` - matches all memory server tools
|
||||
- **All tools**: `"*"` - matches everything
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Hooks have access to:
|
||||
- `$CLAUDE_PROJECT_DIR` - Project root directory
|
||||
|
||||
## Settings File Locations
|
||||
|
||||
1. **User settings**: `~/.claude/settings.json`
|
||||
2. **Project settings**: `./.claude/settings.json`
|
||||
3. **Local settings**: `./.claude/settings.local.json`
|
||||
4. **Managed settings**: `/Library/Application Support/ClaudeCode/managed-settings.json`
|
||||
|
||||
## References
|
||||
- Official Docs: https://docs.anthropic.com/en/docs/claude-code/hooks
|
||||
- Hook Guide: https://docs.anthropic.com/en/docs/claude-code/hooks-guide
|
||||
@@ -0,0 +1,133 @@
|
||||
# MCP Configuration Documentation
|
||||
## Source: Official Claude Code Docs v2025
|
||||
## Last Verified: 2025-08-31
|
||||
|
||||
## MCP Configuration File Locations
|
||||
|
||||
### User Scope
|
||||
- **File**: `~/.claude.json`
|
||||
- **Purpose**: User-wide MCP servers available across all projects
|
||||
- **Persistence**: Persists across projects
|
||||
- **Example Path**: `/Users/username/.claude.json`
|
||||
|
||||
### Project Scope
|
||||
- **File**: `./.mcp.json`
|
||||
- **Purpose**: Project-specific servers for team collaboration
|
||||
- **Persistence**: Checked into version control
|
||||
- **Example Path**: `/path/to/project/.mcp.json`
|
||||
|
||||
### Local Scope
|
||||
- **Status**: Not officially documented
|
||||
- **Implementation**: Currently uses `~/.claude.json` (may need revision)
|
||||
|
||||
## Configuration Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"server-name": {
|
||||
"command": "command-to-run",
|
||||
"args": ["arg1", "arg2"],
|
||||
"env": {
|
||||
"ENV_VAR": "value"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Example Configurations
|
||||
|
||||
### Memory Server (stdio)
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"claude-mem": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "@modelcontextprotocol/server-memory"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### HTTP Server
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"api-server": {
|
||||
"type": "sse",
|
||||
"url": "${API_BASE_URL:-https://api.example.com}/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${API_KEY}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Environment Variable Expansion
|
||||
|
||||
MCP configs support environment variable expansion:
|
||||
- `${VAR}` - Direct expansion
|
||||
- `${VAR:-default}` - With fallback value
|
||||
|
||||
Applicable fields:
|
||||
- `command`
|
||||
- `args`
|
||||
- `env`
|
||||
- `url`
|
||||
- `headers`
|
||||
|
||||
## CLI Commands
|
||||
|
||||
```bash
|
||||
# Add a server
|
||||
claude mcp add <name> <command> [args...]
|
||||
|
||||
# Add with scope
|
||||
claude mcp add <name> --scope project /path/to/server
|
||||
claude mcp add <name> --scope user /path/to/server
|
||||
|
||||
# List servers
|
||||
claude mcp list
|
||||
|
||||
# Get server details
|
||||
claude mcp get <name>
|
||||
|
||||
# Remove server
|
||||
claude mcp remove <name>
|
||||
|
||||
# Check status (within Claude Code)
|
||||
/mcp
|
||||
```
|
||||
|
||||
## Tool Naming Convention
|
||||
|
||||
MCP tools follow the pattern: `mcp__<serverName>__<toolName>`
|
||||
|
||||
Example:
|
||||
- Server: `claude-mem`
|
||||
- Tool: `create_entities`
|
||||
- Full name: `mcp__claude_mem__create_entities`
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Tool Permissions**: Must explicitly allow MCP tools via `--allowedTools`
|
||||
2. **Server Trust**: Only use MCP servers from trusted sources
|
||||
3. **Credential Management**: Use environment variables for sensitive data
|
||||
4. **Audit Trail**: MCP operations can be monitored via hooks
|
||||
|
||||
## Common Issues
|
||||
|
||||
### Issue: MCP server not connecting
|
||||
**Solution**: Check that the command and args are correct, and npx is in PATH
|
||||
|
||||
### Issue: Tools not available
|
||||
**Solution**: Ensure server is in allowed list and properly configured
|
||||
|
||||
### Issue: Configuration not loading
|
||||
**Solution**: Verify JSON syntax and file location
|
||||
|
||||
## References
|
||||
- Official Docs: https://docs.anthropic.com/en/docs/claude-code/mcp
|
||||
- MCP Protocol: https://modelcontextprotocol.io/
|
||||
@@ -0,0 +1,82 @@
|
||||
# SessionStart Hook Documentation
|
||||
|
||||
## Official Documentation Reference
|
||||
- **Source**: https://docs.anthropic.com/en/docs/claude-code/hooks#sessionstart
|
||||
- **Last Verified**: 2025-08-31
|
||||
- **Version**: Claude Code v2025
|
||||
|
||||
## Hook Payload Structure
|
||||
|
||||
The SessionStart hook receives the following JSON payload via stdin:
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "string",
|
||||
"transcript_path": "string",
|
||||
"hook_event_name": "SessionStart",
|
||||
"source": "startup" | "compact" | "vscode" | "web"
|
||||
}
|
||||
```
|
||||
|
||||
### Field Descriptions
|
||||
|
||||
- **session_id**: Unique identifier for the Claude Code session
|
||||
- **transcript_path**: Path to the conversation transcript JSONL file
|
||||
- **hook_event_name**: Always "SessionStart" for this hook
|
||||
- **source**: Indicates how the session was initiated:
|
||||
- `"startup"`: New session started normally (should load context)
|
||||
- `"compact"`: Session started after compaction (may skip context)
|
||||
- `"vscode"`: Session initiated from VS Code extension
|
||||
- `"web"`: Session initiated from web interface
|
||||
|
||||
## Response Format
|
||||
|
||||
The hook should output JSON in the following format to add context:
|
||||
|
||||
```json
|
||||
{
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "SessionStart",
|
||||
"additionalContext": "string"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Response Fields
|
||||
|
||||
- **hookSpecificOutput**: Container for hook-specific output
|
||||
- **hookEventName**: Must be "SessionStart"
|
||||
- **additionalContext**: String content to add to the session context
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
### Context Loading Strategy
|
||||
|
||||
The hook should determine whether to load context based on the `source` field:
|
||||
|
||||
1. **For "startup" source**: Load full context from memory
|
||||
2. **For "compact" source**: Skip or load minimal context (session continuing after compaction)
|
||||
3. **For "vscode"/"web" sources**: Load context as appropriate
|
||||
|
||||
### Error Handling
|
||||
|
||||
- If context loading fails, exit silently (exit code 0)
|
||||
- Do not break the session start with errors
|
||||
- Log errors to separate log file if needed
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
### Incorrect Field Check (FIXED)
|
||||
**Wrong**: Checking `payload.reason === 'continue'`
|
||||
**Correct**: Checking `payload.source === 'compact'`
|
||||
|
||||
The payload does not have a `reason` field. The `source` field indicates the session initiation context.
|
||||
|
||||
## Code Location
|
||||
- **File**: `/Users/alexnewman/Scripts/claude-mem/hooks/session-start.js`
|
||||
- **Line**: 53-66 (field check and documentation)
|
||||
|
||||
## Cross-References
|
||||
- General Hooks Documentation: [docs/claude-code/hooks.md](./hooks.md)
|
||||
- Hook Response Formats: [docs/claude-code/hook-responses.md](./hook-responses.md)
|
||||
- MCP Configuration: [docs/claude-code/mcp-configuration.md](./mcp-configuration.md)
|
||||
Reference in New Issue
Block a user