CLI Reference¶
The fasthooks CLI helps you create, install, and manage Claude Code hooks.
Installation¶
The CLI is included when you install fasthooks:
Commands¶
fasthooks init¶
Create a new hooks file with boilerplate code.
Options:
| Option | Short | Default | Description |
|---|---|---|---|
--path |
-p |
.claude/hooks.py |
Where to create the hooks file |
--force |
-f |
false |
Overwrite if file exists |
Example:
# Create default .claude/hooks.py
fasthooks init
# Create in custom location
fasthooks init --path my-hooks.py
# Overwrite existing file
fasthooks init --force
Generated file:
# /// script
# requires-python = ">=3.10"
# dependencies = []
# ///
"""
Claude Code hooks for this project.
Usage:
fasthooks install .claude/hooks.py
After installing, restart Claude Code for hooks to take effect.
"""
from fasthooks import HookApp, deny
app = HookApp()
@app.pre_tool("Bash")
def check_bash(event):
"""Example: block dangerous commands."""
if "rm -rf /" in event.command:
return deny("Blocked dangerous command")
# Return None to allow (default)
if __name__ == "__main__":
app.run()
fasthooks install¶
Register your hooks with Claude Code by updating settings.json.
Arguments:
| Argument | Required | Description |
|---|---|---|
path |
Yes | Path to your hooks.py file |
Options:
| Option | Short | Default | Description |
|---|---|---|---|
--scope |
-s |
project |
Where to install: project, user, or local |
--force |
-f |
false |
Reinstall even if already installed |
Example:
# Install to project scope (recommended for teams)
fasthooks install .claude/hooks.py
# Install to user scope (personal global hooks)
fasthooks install ~/.my-hooks/hooks.py --scope user
# Install to local scope (personal, not git-tracked)
fasthooks install .claude/hooks.py --scope local
# Reinstall after modifying hooks.py
fasthooks install .claude/hooks.py --force
What it does:
- Validates your hooks.py is importable (catches syntax errors)
- Discovers registered handlers (
@app.pre_tool,@app.on_stop, etc.) - Backs up existing settings.json
- Generates and merges hook configuration
- Creates a lock file to track the installation
Output:
✓ Validated .claude/hooks.py
✓ Found 3 handlers:
PreToolUse:Bash
PostToolUse:*
Stop
✓ Backed up .claude/settings.json → .claude/settings.json.bak
✓ Updated .claude/settings.json
✓ Created .claude/.fasthooks.lock
┌──────────────────────────────────────────────────────────────┐
│ Restart Claude Code to activate hooks. │
└──────────────────────────────────────────────────────────────┘
Restart Required
Claude Code does not hot-reload hooks. After installing or modifying hooks, you must restart Claude Code (exit and re-run claude) for changes to take effect.
fasthooks uninstall¶
Remove hooks from Claude Code.
Options:
| Option | Short | Default | Description |
|---|---|---|---|
--scope |
-s |
project |
Scope to uninstall from |
Example:
# Uninstall from project scope
fasthooks uninstall
# Uninstall from user scope
fasthooks uninstall --scope user
Output:
✓ Found installation in .claude/.fasthooks.lock
✓ Backed up .claude/settings.json → .claude/settings.json.bak
✓ Removed 3 hook entries
✓ Deleted .claude/.fasthooks.lock
┌──────────────────────────────────────────────────────────────┐
│ Restart Claude Code to deactivate hooks. │
└──────────────────────────────────────────────────────────────┘
fasthooks status¶
Show installation state and validate hooks.
Options:
| Option | Short | Default | Description |
|---|---|---|---|
--scope |
-s |
all | Check specific scope, or all if not specified |
Example:
Output:
╭──────────────────────────────── Hook Status ─────────────────────────────────╮
│ Project scope (.claude/settings.json) │
│ ✓ Installed: .claude/hooks.py │
│ ✓ Installed at: 2024-01-15 10:30:00 │
│ ✓ Handlers: PreToolUse:Bash, PostToolUse:*, Stop │
│ ✓ Hooks valid │
│ ✓ Settings in sync │
│ │
│ User scope (~/.claude/settings.json) │
│ ✗ Not installed │
│ │
│ Local scope (.claude/settings.local.json) │
│ ✗ Not installed │
╰──────────────────────────────────────────────────────────────────────────────╯
Detects issues:
- Import errors in hooks.py
- Handlers added/removed since install
- Settings.json out of sync
- Multiple scopes with hooks (warns about conflicts)
fasthooks studio¶
Launch the visual debugging UI for hooks. See exactly what your hooks receive, how they respond, and debug issues in real-time.
Options:
| Option | Default | Description |
|---|---|---|
--db |
~/.fasthooks/studio.db |
Path to SQLite database |
--host |
127.0.0.1 |
Host to bind server to |
--port |
5555 |
Port to bind server to |
--open |
false |
Open browser automatically |
--verbose |
false |
Enable debug logging |
Example:
# Start studio (opens at http://localhost:5555)
fasthooks studio
# Open browser automatically
fasthooks studio --open
# Use custom port
fasthooks studio --port 8080
# Point to specific database
fasthooks studio --db /path/to/studio.db
What you see:
The studio shows a conversation view similar to Claude Code's TUI, but with hook events inline:
👤 User: "check the logs"
🧠 Thinking: "Let me look at the log files..."
🔧 Bash(tail -f /var/log/app.log)
┌─ PreToolUse hooks ─────────────────────────────┐
│ check_paths → ✅ allow 0.3ms │
│ log_all → ✅ allow 0.1ms │
│ Total: 12.5ms │
└────────────────────────────────────────────────┘
📥 [log output here...]
Setting up observability:
For studio to capture hook events, add SQLiteObserver to your hooks:
from fasthooks import HookApp
from fasthooks.observability import SQLiteObserver
app = HookApp()
app.add_observer(SQLiteObserver()) # Writes to ~/.fasthooks/studio.db
@app.pre_tool("Bash")
def check_bash(event):
# Your handler logic
pass
app.run()
See the Observability Guide for more details.
Scopes¶
fasthooks supports three installation scopes:
| Scope | Settings File | Lock File | Use Case |
|---|---|---|---|
project |
.claude/settings.json |
.claude/.fasthooks.lock |
Team-shared hooks (git-tracked) |
user |
~/.claude/settings.json |
~/.claude/.fasthooks.lock |
Personal global hooks |
local |
.claude/settings.local.json |
.claude/.fasthooks.local.lock |
Personal project hooks (gitignored) |
Choosing a scope:
-
project (default): Best for team projects. The settings.json is committed to git, so everyone on the team gets the same hooks.
-
user: Best for personal productivity hooks you want everywhere. Applied to all projects.
-
local: Best for personal overrides on a specific project. Not git-tracked, won't affect teammates.
Multiple Scopes
If hooks are installed in multiple scopes, all of them run for each event. Use fasthooks status to check which scopes have hooks installed.
Common Workflows¶
Setting up hooks for a team project¶
# 1. Create hooks file
fasthooks init
# 2. Edit .claude/hooks.py with your handlers
# ...
# 3. Install to project scope (default)
fasthooks install .claude/hooks.py
# 4. Commit to git
git add .claude/hooks.py .claude/settings.json
git commit -m "Add project hooks"
# 5. Restart Claude Code
Adding personal global hooks¶
# 1. Create hooks file in your home directory
mkdir -p ~/.my-hooks
fasthooks init --path ~/.my-hooks/hooks.py
# 2. Edit with your personal handlers
# ...
# 3. Install to user scope
fasthooks install ~/.my-hooks/hooks.py --scope user
# 4. Restart Claude Code
Updating hooks after changes¶
# 1. Edit your hooks.py
# ...
# 2. Reinstall to update settings.json
fasthooks install .claude/hooks.py --force
# 3. Restart Claude Code
Checking installation status¶
# See what's installed across all scopes
fasthooks status
# If handlers changed, resync:
fasthooks install .claude/hooks.py --force
Troubleshooting¶
"Hooks not running"¶
- Did you restart Claude Code after installing?
- Run
fasthooks statusto check installation - Verify handlers are registered:
fasthooks install .claude/hooks.py --force
"Import error" during install¶
Your hooks.py has a syntax error or missing dependency. Fix it and try again:
"Already installed"¶
Use --force to reinstall:
"Multiple scopes" warning¶
You have hooks in multiple scopes. This is usually fine, but if you're seeing unexpected behavior:
# Check what's installed
fasthooks status
# Uninstall from scopes you don't need
fasthooks uninstall --scope local
Roadmap¶
Future CLI commands planned for v2:
| Command | Description |
|---|---|
fasthooks show-config |
Output settings.json snippet without writing (for CI/CD, debugging) |
fasthooks test |
Run hooks locally with mock events (quick smoke tests) |
show-config - Preview what install would write:
test - Test handlers without Claude Code:
fasthooks test .claude/hooks.py --event PreToolUse:Bash --input '{"command": "rm -rf /"}'
# Output: {"decision": "deny", "reason": "Dangerous command blocked"}
Have a feature request? Open an issue.