Finishing up Claude Code guide

This commit is contained in:
Cole Medin
2025-08-05 11:59:18 -05:00
parent 828234dd67
commit 5597e61d36
10 changed files with 877 additions and 224 deletions

View 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.

View File

@@ -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"
}
]
}
]
}
}

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

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