mirror of
https://github.com/coleam00/context-engineering-intro.git
synced 2025-12-29 16:14:56 +00:00
Finishing up Claude Code guide
This commit is contained in:
109
claude-code-full-guide/.claude/hooks/README.md
Normal file
109
claude-code-full-guide/.claude/hooks/README.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# Claude Code Hooks Examples
|
||||
|
||||
This directory contains example hooks for Claude Code that demonstrate how to add deterministic behavior to your AI coding workflow.
|
||||
|
||||
## What are Hooks?
|
||||
|
||||
Hooks are user-defined shell commands that execute at specific points in Claude Code's lifecycle. They provide control over Claude's behavior, ensuring certain actions always happen rather than relying on the AI to choose to run them.
|
||||
|
||||
## Files in this Directory
|
||||
|
||||
1. **format-after-edit.sh** - A PostToolUse hook that automatically formats code after file edits
|
||||
2. **example-hook-config.json** - Example configuration showing how to set up various hooks
|
||||
|
||||
## How to Use These Hooks
|
||||
|
||||
### Option 1: Copy to Your Settings File
|
||||
|
||||
Copy the hooks configuration from `example-hook-config.json` to your Claude Code settings:
|
||||
|
||||
**Project-specific** (`.claude/settings.json`):
|
||||
```bash
|
||||
# Create settings file if it doesn't exist
|
||||
touch .claude/settings.json
|
||||
|
||||
# Add hooks configuration from example-hook-config.json
|
||||
```
|
||||
|
||||
**User-wide** (`~/.claude/settings.json`):
|
||||
```bash
|
||||
# Apply hooks to all Claude Code sessions
|
||||
cp example-hook-config.json ~/.claude/settings.json
|
||||
```
|
||||
|
||||
### Option 2: Use Individual Hooks
|
||||
|
||||
1. Copy the hook script to your project:
|
||||
```bash
|
||||
cp format-after-edit.sh /your/project/.claude/hooks/
|
||||
chmod +x /your/project/.claude/hooks/format-after-edit.sh
|
||||
```
|
||||
|
||||
2. Add to your settings.json:
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Edit|Write|MultiEdit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": ".claude/hooks/format-after-edit.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Available Hook Events
|
||||
|
||||
- **PreToolUse**: Before tool execution (can block tools)
|
||||
- **PostToolUse**: After successful tool completion
|
||||
- **UserPromptSubmit**: When user submits a prompt
|
||||
- **SubagentStop**: When a subagent completes
|
||||
- **Stop**: When main agent finishes responding
|
||||
- **Notification**: During system notifications
|
||||
- **PreCompact**: Before context compaction
|
||||
- **SessionStart**: At session initialization
|
||||
|
||||
## Creating Your Own Hooks
|
||||
|
||||
1. Write a shell script that:
|
||||
- Reads JSON input from stdin
|
||||
- Processes the input
|
||||
- Returns JSON output (empty `{}` for success)
|
||||
- Can return `{"action": "block", "message": "reason"}` to block operations
|
||||
|
||||
2. Make it executable:
|
||||
```bash
|
||||
chmod +x your-hook.sh
|
||||
```
|
||||
|
||||
3. Add to settings.json with appropriate matcher and event
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- Hooks execute arbitrary shell commands
|
||||
- Always validate and sanitize inputs
|
||||
- Use full paths to avoid PATH manipulation
|
||||
- Be careful with file operations
|
||||
- Test hooks thoroughly before deployment
|
||||
|
||||
## Debugging Hooks
|
||||
|
||||
Run Claude Code with debug flag to see hook execution:
|
||||
```bash
|
||||
claude --debug
|
||||
```
|
||||
|
||||
This will show:
|
||||
- Which hooks are triggered
|
||||
- Input/output for each hook
|
||||
- Any errors or issues
|
||||
|
||||
## Integration with Subagents
|
||||
|
||||
The example configuration includes a hook that integrates with the validation-gates subagent, demonstrating how hooks and subagents can work together for a more robust development workflow.
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Edit|Write|MultiEdit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": ".claude/hooks/format-after-edit.sh",
|
||||
"description": "Automatically format code after file edits"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bash -c 'input=$(cat); cmd=$(echo \"$input\" | jq -r \".tool_input.command // empty\"); if [[ \"$cmd\" =~ (rm|delete).*(\\*|\\.env|credentials|secret) ]]; then echo \"{\\\"action\\\": \\\"block\\\", \\\"message\\\": \\\"Dangerous command blocked: $cmd\\\"}\"; else echo \"{}\"; fi'",
|
||||
"description": "Block dangerous bash commands"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"SubagentStop": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bash -c 'input=$(cat); agent=$(echo \"$input\" | jq -r \".subagent_name // empty\"); if [[ \"$agent\" == \"validation-gates\" ]]; then echo \"Validation gates completed. Running final checks...\" >&2; fi; echo \"{}\"'",
|
||||
"description": "Log when validation-gates subagent completes"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"UserPromptSubmit": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bash -c 'input=$(cat); prompt=$(echo \"$input\" | jq -r \".prompt // empty\"); if [[ \"$prompt\" =~ (test|validate|check) ]] && [[ ! \"$prompt\" =~ (skip|no|without).*(test|validation) ]]; then echo \"Reminder: Use the validation-gates subagent to ensure comprehensive testing.\" >&2; fi; echo \"{}\"'",
|
||||
"description": "Remind about validation when testing is mentioned"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
70
claude-code-full-guide/.claude/hooks/format-after-edit.sh
Normal file
70
claude-code-full-guide/.claude/hooks/format-after-edit.sh
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/bin/bash
|
||||
# PostToolUse hook: Automatically format code after file edits
|
||||
# This hook runs after Edit, Write, or MultiEdit tools to ensure consistent formatting
|
||||
|
||||
# Read the JSON input from stdin
|
||||
input=$(cat)
|
||||
|
||||
# Extract tool name and file path from the input
|
||||
tool_name=$(echo "$input" | jq -r '.tool_name // empty')
|
||||
file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
|
||||
|
||||
# Only process if it's a file editing tool and we have a file path
|
||||
if [[ "$tool_name" =~ ^(Edit|Write|MultiEdit)$ ]] && [[ -n "$file_path" ]]; then
|
||||
# Determine file extension
|
||||
extension="${file_path##*.}"
|
||||
|
||||
# Format based on file type
|
||||
case "$extension" in
|
||||
js|jsx|ts|tsx)
|
||||
# JavaScript/TypeScript files
|
||||
if command -v prettier &> /dev/null; then
|
||||
echo "Formatting $file_path with Prettier..." >&2
|
||||
prettier --write "$file_path" 2>/dev/null || true
|
||||
elif command -v npx &> /dev/null; then
|
||||
echo "Formatting $file_path with npx prettier..." >&2
|
||||
npx prettier --write "$file_path" 2>/dev/null || true
|
||||
fi
|
||||
;;
|
||||
py)
|
||||
# Python files
|
||||
if command -v black &> /dev/null; then
|
||||
echo "Formatting $file_path with Black..." >&2
|
||||
black "$file_path" 2>/dev/null || true
|
||||
elif command -v ruff &> /dev/null; then
|
||||
echo "Formatting $file_path with Ruff..." >&2
|
||||
ruff format "$file_path" 2>/dev/null || true
|
||||
fi
|
||||
;;
|
||||
go)
|
||||
# Go files
|
||||
if command -v gofmt &> /dev/null; then
|
||||
echo "Formatting $file_path with gofmt..." >&2
|
||||
gofmt -w "$file_path" 2>/dev/null || true
|
||||
fi
|
||||
;;
|
||||
rs)
|
||||
# Rust files
|
||||
if command -v rustfmt &> /dev/null; then
|
||||
echo "Formatting $file_path with rustfmt..." >&2
|
||||
rustfmt "$file_path" 2>/dev/null || true
|
||||
fi
|
||||
;;
|
||||
json)
|
||||
# JSON files
|
||||
if command -v jq &> /dev/null; then
|
||||
echo "Formatting $file_path with jq..." >&2
|
||||
# Format JSON with jq (careful with large files)
|
||||
if [[ $(stat -f%z "$file_path" 2>/dev/null || stat -c%s "$file_path" 2>/dev/null) -lt 1048576 ]]; then
|
||||
jq . "$file_path" > "$file_path.tmp" && mv "$file_path.tmp" "$file_path" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# Log formatting completion
|
||||
echo "Post-edit formatting completed for $file_path" >&2
|
||||
fi
|
||||
|
||||
# Always return success to avoid blocking the tool
|
||||
echo "{}"
|
||||
25
claude-code-full-guide/.claude/hooks/log-tool-usage.sh
Normal file
25
claude-code-full-guide/.claude/hooks/log-tool-usage.sh
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
# PostToolUse hook: Log all tool usage for tracking and debugging
|
||||
# This hook runs after any tool execution to maintain an audit log
|
||||
|
||||
# Read the JSON input from stdin
|
||||
input=$(cat)
|
||||
|
||||
# Extract tool name and basic info
|
||||
tool_name=$(echo "$input" | jq -r '.tool_name // "unknown"')
|
||||
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
|
||||
# Create logs directory if it doesn't exist
|
||||
mkdir -p .claude/logs
|
||||
|
||||
# Log the tool usage
|
||||
echo "[$timestamp] Tool used: $tool_name" >> .claude/logs/tool-usage.log
|
||||
|
||||
# Optionally, you can add more detailed logging
|
||||
if [[ "$tool_name" =~ ^(Edit|Write|MultiEdit)$ ]]; then
|
||||
file_path=$(echo "$input" | jq -r '.tool_input.file_path // "unknown"')
|
||||
echo "[$timestamp] File operation: $tool_name on $file_path" >> .claude/logs/file-operations.log
|
||||
fi
|
||||
|
||||
# Always return success to avoid blocking tools
|
||||
echo "{}"
|
||||
Reference in New Issue
Block a user