A Rust CLI tool that integrates with Claude Code using hooks to provide intelligent command suggestions, semantic directory aliasing, command history tracking, and security pattern detection.
- π― Command Intelligence - Automatically map commands to your preferred alternatives (npm β bun, curl β wget)
- π Directory Aliasing - Use natural language like "docs" or "project_docs" instead of typing full paths
- π Command History - Track all commands Claude runs in a SQLite database with session tracking
- π Security Patterns - 27 built-in patterns detect dangerous code across 10+ languages
- β‘ Fast & Lightweight - Rust-based with ~1-5ms hook response time
You: "What files are in my docs?" Claude: "I'll check your docs directory at /Users/you/Documents/Documentation"
Claude tries: npm install
Tool suggests: bun install
Claude runs: bun install automatically
Claude tries to write: eval(userInput)
Tool warns: Security risk detected
Claude: Finds a safer alternative
/plugin marketplace add sirmews/claude-hook-advisor
/plugin install claude-hook-advisor@sirmewsIncludes automatic hook setup and slash commands (/history, /history-failures, /history-search).
cargo install claude-hook-advisor
claude-hook-advisor --install-hooksgit clone https://github.com/sirmews/claude-hook-advisor.git
cd claude-hook-advisor
make install- Install via plugin marketplace (recommended) or cargo
- Create
.claude-hook-advisor.tomlin your project root:
# Command mappings - map any command to your preferred tool
[commands]
npm = "bun"
yarn = "bun"
curl = "wget --verbose"
# Directory aliases - use natural language in conversations
[semantic_directories]
"project docs" = "~/Documents/my-project/docs"
"test data" = "~/Documents/test-data"That's it! Start a Claude Code conversation and the hooks work automatically. Security patterns are enabled by default with no configuration needed.
π Optional: Enable Command History Tracking
Add to your .claude-hook-advisor.toml:
[command_history]
enabled = true
log_file = "~/.claude-hook-advisor/bash-history.db"View history:
claude-hook-advisor --history # Recent commands
claude-hook-advisor --history --failures # Only failed commands
claude-hook-advisor --history --pattern git # Filter by patternπ Optional: Disable Noisy Security Patterns
Security patterns are enabled by default. To disable specific patterns:
[security_pattern_overrides]
swift_force_unwrap = false # Common in Swift code
eval_injection = false # If working on a REPL
rust_unsafe_block = false # For systems programmingRun claude-hook-advisor --install-hooks to automatically configure hooks, or manually add to .claude/settings.json:
{
"hooks": {
"PreToolUse": { "Bash": "claude-hook-advisor --hook" },
"UserPromptSubmit": { ".*": "claude-hook-advisor --hook" },
"PostToolUse": { "Bash": "claude-hook-advisor --hook" }
}
}How It Works
Three hooks work together to provide comprehensive functionality:
The Flow:
- Command Detection: When Claude Code tries to run a Bash command, the hook receives JSON input
- Configuration Loading: The tool loads
.claude-hook-advisor.tomlfrom the current directory - Pattern Matching: Matches only the primary command at the start of the line
- Suggestion Generation: If a match is found, returns a blocking response with the suggested replacement
- Claude Integration: Claude receives the suggestion and automatically retries with the correct command
Smart Matching:
- Start-of-line matching ensures only primary commands are replaced
npm installβbun installβnpx npmstays unchanged (npm is not the primary command) βnpm-checkstays unchanged (different command) β- Preserves command arguments:
npm install --saveβbun install --saveβ
The Flow:
- Text Analysis: Scans user prompts for semantic directory references (e.g., "docs", "project_docs")
- Pattern Recognition: Uses regex to detect directory aliases in natural language
- Path Expansion: Expands tilde (~) to user home directory
- Path Resolution: Converts semantic references to canonical filesystem paths
- Security Validation: Performs path canonicalization to prevent traversal attacks
Security Features:
- Path canonicalization prevents
../../../etc/passwdattacks - Only resolves to configured directories
- Validates paths exist before resolution
The Flow:
- Execution Tracking: Receives command results with success/failure data
- Database Logging: Stores commands in SQLite with full metadata
- Session Tracking: Links commands to Claude Code sessions
Failure Detection: Uses a clever two-hook approach:
- PreToolUse: Logs every command Claude attempts with status="pending"
- PostToolUse: Updates status to "success" when commands complete
- Commands remaining "pending" = failed (PostToolUse never fired)
This workaround handles the limitation where Claude Code doesn't send PostToolUse events for failed commands.
Command History Tracking - Detailed Guide
Enable in your .claude-hook-advisor.toml:
[command_history]
enabled = true
log_file = "~/.claude-hook-advisor/bash-history.db"The PostToolUse hook (installed via --install-hooks) will automatically start logging. No restart needed!
# Show recent commands
claude-hook-advisor --history
# Show last 50 commands
claude-hook-advisor --history --limit 50
# Show only failed commands
claude-hook-advisor --history --failures
# Show git commands only
claude-hook-advisor --history --pattern git
# Show commands from a specific session
claude-hook-advisor --history --session abc123Each command record includes:
- Timestamp: When the command was executed
- Command: The exact command that ran
- Status: Success (β) or Failed (β) - automatically tracked
- Exit code: Success (0) or failure code
- Working directory: Where the command was executed
- Session ID: Link commands to Claude Code sessions
Command History (5 records)
================================================================================
2025-11-10T14:30:22Z β
Command: git status
CWD: /home/user/my-project
Session: abc123-def456
2025-11-10T14:30:25Z β
Command: cargo test
CWD: /home/user/my-project
Session: abc123-def456
2025-11-10T14:30:30Z β FAILED
Command: npm test
CWD: /home/user/my-project
Session: abc123-def456
- Track failures: Automatically identify which commands failed
- Debugging: "Which commands failed in this session?" -
--history --failures - Retrieve commands: "What was that complex curl command Claude ran yesterday?"
- Audit trail: Track all command attempts for compliance
- Learning: See what commands Claude tries and which ones work
Security Pattern Detection - Complete Reference
27 built-in security patterns automatically detect dangerous code patterns when Claude edits files using Edit, Write, or MultiEdit tools. Enabled by default with no configuration needed.
When Claude tries to edit a file, the PreToolUse hook:
- Checks the file path against glob patterns (e.g.,
.github/workflows/*.yml) - Scans the content for dangerous substrings (e.g.,
eval(,dangerouslySetInnerHTML) - Blocks the operation if a pattern matches and shows a security warning
- Tracks warnings per-session so each warning is only shown once
Claude sees the warning and either finds a safer alternative, asks for your guidance, or explains why the risky operation is needed.
eval_injection: Detectseval()usage that can execute arbitrary codenew_function_injection: Detectsnew Function()code injection risksinnerHTML_xss: DetectsinnerHTMLXSS vulnerabilitiesreact_dangerously_set_html: Detects ReactdangerouslySetInnerHTMLXSS risksdocument_write_xss: Detectsdocument.write()XSS attackschild_process_exec: Detects command injection viaexec()/execSync()
python_eval: Detectseval()arbitrary code executionpython_exec: Detectsexec()arbitrary code executionpickle_deserialization: Detects unsafepickle.load()usageos_system_injection: Detects command injection viaos.system()
sql_injection: Detects SQL injection via string interpolationsql_string_format: Detects SQL injection via format strings
rust_unsafe_block: Detectsunsafe {}blocks that bypass safetyrust_command_injection: Detects shell command usage that could allow injection
go_command_injection: Detects shell command injection risksgo_sql_injection: Detects SQL injection viafmt.Sprintf
swift_force_unwrap: Detects force unwrap!that can cause crashesswift_unsafe_operations: Detects unsafe pointer operationsswift_nspredicate_format: Detects NSPredicate injection vulnerabilities
java_runtime_exec: Detects command injection viaRuntime.exec()java_deserialization: Detects unsafe deserialization
php_eval: Detectseval()arbitrary code executionphp_unserialize: Detects object injection viaunserialize()
ruby_eval: Detectseval()variants (eval, instance_eval, class_eval)ruby_yaml_load: Detects arbitrary code execution viaYAML.load
github_actions_workflow: Detects workflow injection in.ymlfilesgithub_actions_workflow_yaml: Detects workflow injection in.yamlfiles
When Claude tries to write code with eval():
β οΈ Security Warning: eval() executes arbitrary code and is a major security risk.
Consider using JSON.parse() for data parsing or alternative design patterns that
don't require code evaluation. Only use eval() if you truly need to evaluate
arbitrary code.
All patterns are enabled by default. To disable specific patterns:
[security_pattern_overrides]
swift_force_unwrap = false # Common in Swift code
eval_injection = false # If working on a REPL
python_eval = false
rust_unsafe_block = false # For systems programmingAll pattern names:
github_actions_workflow github_actions_workflow_yaml
eval_injection new_function_injection
react_dangerously_set_html document_write_xss
innerHTML_xss child_process_exec
pickle_deserialization os_system_injection
python_eval python_exec
sql_injection sql_string_format
rust_unsafe_block rust_command_injection
go_command_injection go_sql_injection
swift_force_unwrap swift_unsafe_operations
swift_nspredicate_format java_runtime_exec
java_deserialization php_eval
php_unserialize ruby_eval
ruby_yaml_load
Configuration Examples & Use Cases
Node.js project (prefer bun):
[commands]
npm = "bun"
yarn = "bun"
npx = "bunx"Python project (prefer uv):
[commands]
pip = "uv pip"
"pip install" = "uv add"General tool preferences:
[commands]
curl = "wget --verbose"
cat = "bat"
ls = "eza"Comprehensive project setup:
# Command mappings
[commands]
npm = "bun"
yarn = "bun"
npx = "bunx"
# Semantic directory aliases
[semantic_directories]
"project docs" = "~/Documents/Documentation/my-project"
"central docs" = "~/Documents/Documentation"
"test data" = "~/Documents/test-data"
# Command history
[command_history]
enabled = true
log_file = "~/.claude-hook-advisor/bash-history.db"
# Disable noisy security patterns for this project
[security_pattern_overrides]
swift_force_unwrap = false- Package Manager Consistency: Enforce use of
buninstead ofnpm/yarn - Tool Preferences: Replace
curlwithwget,catwithbat, etc. - Project Standards: Ensure consistent tooling across team members
- Legacy Migration: Gradually move from old tools to new ones
- Documentation Management: Use "docs" instead of typing full paths
- Project Organization: Reference "project_docs", "central_docs" naturally
- Team Collaboration: Shared semantic directory references across team members
- Workflow Automation: Natural language directory references in Claude conversations
The tool looks for configuration files in this order:
- Custom path specified with
-c/--configflag .claude-hook-advisor.tomlin current directory- If no config found, allows all commands (no mappings)
Testing & Development
Test hooks without Claude Code:
# Test directory resolution
echo '{"session_id":"test","hook_event_name":"UserPromptSubmit","prompt":"check docs"}' | claude-hook-advisor --hook
# Test command mapping
echo '{"session_id":"test","hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"npm install"}}' | claude-hook-advisor --hook
# Test PostToolUse hook
echo '{"session_id":"test","hook_event_name":"PostToolUse","tool_name":"Bash","tool_input":{"command":"bun install"},"tool_response":{"exit_code":0}}' | claude-hook-advisor --hookmake test # Run unit tests
make build # Build in debug mode
make release # Build in release mode
make lint # Run clippy linting
make fmt # Format code with rustfmt
make clean # Clean build artifacts
make help # Show all available targets# Run unit tests
make test
# Test with example npm command
make run-example
# Manual testing - Command mapping (PreToolUse)
echo '{"session_id":"test","transcript_path":"","cwd":"","hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"yarn start"}}' | ./target/debug/claude-hook-advisor --hook
# Manual testing - Directory detection (UserPromptSubmit)
echo '{"session_id":"test","hook_event_name":"UserPromptSubmit","prompt":"check the docs directory"}' | ./target/debug/claude-hook-advisor --hook- Startup time: ~1-5ms per hook call
- Memory usage: ~2-3MB per process
- File watching: Configuration is loaded on each hook call (no caching)
- Path resolution: Uses filesystem canonicalization for security
Troubleshooting Guide
Add RUST_LOG=debug to your Claude Code settings for detailed logging:
{
"hooks": {
"UserPromptSubmit": { ".*": "RUST_LOG=debug claude-hook-advisor --hook" },
"PreToolUse": { "Bash": "RUST_LOG=debug claude-hook-advisor --hook" },
"PostToolUse": { "Bash": "RUST_LOG=debug claude-hook-advisor --hook" }
}
}Debug output shows:
- Configuration file loading
- Pattern matching details
- Path resolution steps
- Variable substitution
- Security validation
Problem: No hook messages appear in Claude Code conversations
Solutions:
- Verify hook installation: Check
.claude/settings.jsonor.claude/settings.local.json - Ensure binary is in PATH:
which claude-hook-advisor - Test manually:
echo '{"session_id":"test","hook_event_name":"UserPromptSubmit","prompt":"check docs"}' | claude-hook-advisor --hook
- Verify hooks are configured:
{ "hooks": { "UserPromptSubmit": { ".*": "claude-hook-advisor --hook" } } }
Problem: Directory aliases don't resolve to expected paths
Solutions:
- Check configuration file exists:
ls .claude-hook-advisor.toml - Verify alias configuration:
[semantic_directories] docs = "~/Documents/Documentation"
- Test resolution:
echo '{"session_id":"test","hook_event_name":"UserPromptSubmit","prompt":"check docs"}' | claude-hook-advisor --hook
- Ensure path exists on filesystem
- Check file permissions:
ls -la .claude-hook-advisor.toml
Problem: Commands still run with original tool instead of mapped replacement
Solutions:
- Verify command mapping in config:
[commands] npm = "bun"
- Test mapping:
echo '{"session_id":"test","hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"npm install"}}' | claude-hook-advisor --hook
- Remember: Commands only match at the start of the line (by design):
- β
npm installβbun install - β
npx npmstays unchanged (npm is not the primary command) - β
npm-checkstays unchanged (different command)
- β
Problem: Hook fails with permission errors
Solutions:
- Make binary executable:
chmod +x ~/.cargo/bin/claude-hook-advisor - Check file ownership:
ls -la ~/.cargo/bin/claude-hook-advisor - Verify PATH includes
~/.cargo/bin:echo $PATH
Problem: Commands aren't being saved to the database
Solutions:
- Verify command history is enabled in config:
[command_history] enabled = true log_file = "~/.claude-hook-advisor/bash-history.db"
- Check PostToolUse hook is installed
- Verify log file location is writable
- Check database file permissions
When working correctly, you'll see these messages in Claude Code:
Directory Resolution:
<user-prompt-submit-hook>Directory reference 'docs' resolved to: /Users/you/Documents/Documentation</user-prompt-submit-hook>
Command Suggestions:
<pre-tool-use-hook>Command 'npm' mapped to 'bun'. Suggested: bun install</pre-tool-use-hook>
Execution Tracking:
<post-tool-use-hook>Command 'bun install' completed successfully (exit code: 0)</post-tool-use-hook>
If you find this tool useful, consider supporting its development:
The security pattern detection feature was inspired by Claude Code itself. While building this tool, I noticed how Claude Code implements its own security checks and validation patterns to protect users from dangerous operations. This inspired me to bring similar protective capabilities to custom hooks, allowing the community to extend Claude Code's safety mechanisms in their own workflows.