Claude Code Hooks (Go Implementation)
Claude Code hooks implemented in Go for better performance, zero runtime dependencies, and native integration with the Nix ecosystem.
Migrated from TypeScript/Bun implementation in dots/.claude/hooks/.
Hooks Included
1. claude-hooks-initialize-session
Event: SessionStart
What it does:
- Detects and skips subagent sessions
- Sets terminal tab title to “Claude Ready”
- Logs session start to
~/.config/claude/history/sessions/YYYY-MM/YYYY-MM-DD_session-log.txt - Implements debouncing to prevent duplicate triggers (2 second window)
2. claude-hooks-capture-tool-output
Event: PostToolUse
What it does:
- Captures outputs from interesting tools (Bash, Edit, Write, Read, Task, NotebookEdit, Skill, SlashCommand)
- Logs to JSONL files:
~/.config/claude/history/tool-outputs/YYYY-MM/YYYY-MM-DD_tool-outputs.jsonl - Silent failure - doesn’t disrupt workflow if logging fails
- Automatically creates directory structure
3. claude-hooks-validate-docs
Event: Manual/Pre-commit
What it does:
- Scans all markdown files for internal links
- Checks if linked files exist
- Reports broken links with file:line numbers
- Exit code 0 if valid, 1 if broken links found
- Skips external URLs, anchors, and mailto links
4. claude-hooks-save-session
Event: SessionEnd
What it does:
- Prompts user to save session summary when ending a Claude Code session
- Skips subagent sessions (silent)
- Displays a message asking if session should be saved
- Works with
/save-sessionslash command for creating session entries - Saves to
~/.config/claude/history/sessions/YYYY-MM/YYYY-MM-DD-HHMMSS_SESSION_description.md
5. claude-hooks-validate-git-push
Event: PreToolUse (via extensions)
What it does:
- Validates git commands to prevent dangerous operations
- Blocks
git add -A,git add --all, andgit add .to prevent adding unintended files - Blocks git push commands without explicit refspecs (e.g.,
git push origin main) - Warns about pushes to protected branches (main/master)
- Exit code 0 if command is safe, non-zero if command should be blocked
Architecture
tools/claude-hooks/
├── cmd/
│ ├── capture-tool-output/main.go # PostToolUse hook
│ ├── initialize-session/main.go # SessionStart hook
│ ├── save-session/main.go # SessionEnd hook
│ └── validate-docs/main.go # Documentation validator
├── internal/
│ └── paths/paths.go # Shared path utilities
├── default.nix # Nix package definition
├── go.mod # Go module definition
├── go.sum # Go dependencies
├── setup-hooks.sh # Configuration script
└── README.md # This file
Installation
Via Nix (Recommended)
-
Build the package:
nix build .#claude-hooks -
Install to your profile:
nix profile install .#claude-hooks -
Or add to home-manager (in
home/common/dev/default.nixor similar):home.packages = with pkgs; [ claude-hooks ]; -
Configure hooks (run setup script):
./tools/claude-hooks/setup-hooks.shOr manually update
~/.claude/settings.json:{ "hooks": { "SessionStart": [ { "hooks": [ { "type": "command", "command": "claude-hooks-initialize-session" } ] } ], "PostToolUse": [ { "hooks": [ { "type": "command", "command": "claude-hooks-capture-tool-output" } ] } ], "SessionEnd": [ { "hooks": [ { "type": "command", "command": "claude-hooks-save-session" } ] } ] } } -
Enable the session-manager plugin (for
/save-sessioncommand):# Symlink the plugin if not already linked ln -s ~/src/home/dots/.claude/plugins/session-manager ~/.claude/plugins/session-manager
Manual Build (without Nix)
cd tools/claude-hooks
# Build all hooks
go build -o bin/claude-hooks-capture-tool-output ./cmd/capture-tool-output
go build -o bin/claude-hooks-initialize-session ./cmd/initialize-session
go build -o bin/claude-hooks-save-session ./cmd/save-session
go build -o bin/claude-hooks-validate-docs ./cmd/validate-docs
# Copy to PATH
sudo cp bin/* /usr/local/bin/
# Then run setup script
./setup-hooks.sh
Development
Running hooks directly with go run
cd tools/claude-hooks
# Test initialize-session
go run ./cmd/initialize-session
# Test capture-tool-output (requires JSON input)
echo '{"tool_name":"Bash","tool_input":{},"tool_response":{},"conversation_id":"test"}' | \
go run ./cmd/capture-tool-output
# Test validate-docs
go run ./cmd/validate-docs
Testing
# Run all tests
go test ./...
# Test with verbose output
go test -v ./internal/paths
# Test specific package
go test ./cmd/validate-docs
Updating vendorHash
If you add/update Go dependencies:
cd tools/claude-hooks
go mod tidy
nix build .#claude-hooks 2>&1 | grep "got:" | awk '{print $2}'
# Copy the hash to default.nix vendorHash field
Advantages over TypeScript/Bun
- Zero runtime dependencies - Compiled to native binary, no Bun required
- Faster execution - Native code vs interpreted JavaScript
- Easier distribution - Single binary per hook
- Cross-compilation - Build for any architecture from any platform
- Nix integration - Proper package management and reproducible builds
- Type safety - Compile-time checks without runtime overhead
- Standard library - Excellent built-in support for JSON, file I/O, paths
Migration Notes
The Go implementation is functionally equivalent to the TypeScript version with these improvements:
- Performance: Faster startup and execution
- Reliability: Compile-time type checking
- Portability: Works on all systems where Go binaries run
- Integration: Native Nix packaging for declarative installation
The old TypeScript hooks in dots/.claude/hooks/ can be removed after verifying the Go versions work correctly.
Troubleshooting
Hook not running:
- Check
~/.claude/settings.jsonsyntax - Verify binaries are in PATH:
which claude-hooks-initialize-session - Check stderr output in terminal
- Verify hooks are executable:
ls -la $(which claude-hooks-initialize-session)
Permission errors:
- Ensure directories exist:
mkdir -p ~/.config/claude/history/{sessions,tool-outputs} - Check write permissions on
~/.config/claude/history
Debugging:
- Hooks write errors to stderr - check terminal output
- Run hooks manually to test:
claude-hooks-initialize-session - Check log files:
ls -la ~/.config/claude/history/
Build errors:
- Run
go mod tidyto update dependencies - Check Go version:
go version(requires Go 1.23+) - Verify vendorHash in
default.nixis correct
Environment Variables
CLAUDE_DIR- Override default~/.config/claudedirectory (optional)CLAUDE_PROJECT_DIR- Set by Claude Code (used for subagent detection)CLAUDE_AGENT_TYPE- Set by Claude Code for subagents (used for detection)
License
Same as the rest of the home repository.