# Configure Development Hooks for This Project
Use this prompt to set up Claude Code hooks tailored to a project's tech stack. Run it after `/tech-plan` has produced architecture specs (or after you've manually chosen your stack) and before `/init-project` or first feature work.
---
## Prompt
Analyze this project's tech stack and configure Claude Code hooks that prevent dangerous actions and protect sensitive files. Also recommend a pre-commit hook setup for code quality. The output is a working `.claude/settings.json` (or updates to an existing one) plus any hook scripts the configuration references.
### Process (7 steps)
**Step 1: Load Tech Stack Context**
Locate the project's technology decisions in this order:
- `docs/specs/architecture.md` — tech stack, languages, frameworks
- `CLAUDE.md` — any existing conventions or tool preferences
- `package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, or equivalent — actual dependencies
- `.claude/settings.json` — any existing hooks to preserve
If no tech stack is identifiable, ask the user to specify: primary language, framework, package manager, test runner, and linter/formatter.
**Step 2: Identify Applicable Hook Categories**
For the detected stack, determine which hook categories apply. Present the list to the user with recommendations marked.
| Category | Purpose | Mechanism |
|----------|---------|-----------|
| **Safety Guards** | Block dangerous shell commands before execution | Claude Code hook (PreToolUse → Bash) |
| **File Protection** | Prevent modification of sensitive/generated files | Claude Code hook (PreToolUse → Edit\|Write) |
| **Dependency Safety** | Guard against risky package operations | Claude Code hook (PreToolUse → Bash) |
| **Code Quality** | Formatting and linting on commit | Git pre-commit hook (not a Claude Code hook) |
Safety Guards and File Protection are non-negotiable. Ask the user about the others.
**Step 3: Configure Safety Hooks (PreToolUse)**
These are the most important hooks. They use `PreToolUse` with matcher `Bash` to inspect commands before execution and block dangerous ones.
**Mandatory patterns to block** (adapt regex to the project's stack):
| Pattern | Why | Regex |
|---------|-----|-------|
| Destructive file ops | Prevent mass deletion | `rm\s+(-rf\|--recursive)` on paths outside the project |
| Database destruction | Prevent data loss | `DROP\s+(TABLE\|DATABASE\|SCHEMA)`, `TRUNCATE`, `DELETE\s+FROM\s+\w+\s*;` (no WHERE) |
| Git force operations | Prevent history rewrite | `git\s+push\s+.*--force(?!-with-lease)`, `git\s+reset\s+--hard` |
| Env/secrets exposure | Prevent credential leaks | `cat\s+.*\.env`, `echo\s+.*\$(.*API_KEY\|SECRET\|TOKEN\|PASSWORD)` |
| System-level commands | Prevent system damage | `sudo\s+`, `chmod\s+777`, `chown`, commands writing outside project root |
| Package registry publish | Prevent accidental publish | `npm\s+publish`, `pip\s+upload`, `cargo\s+publish`, `twine\s+upload` |
| Process killing | Prevent collateral damage | `kill\s+-9`, `killall`, `pkill` |
| Network exfiltration | Prevent data leaks | `curl\s+.*-d\|--data`, `wget\s+.*--post` sending to unknown hosts |
**Stack-specific patterns to add:**
- **Node/JS**: Block `npx` with unknown packages, `npm link` to system
- **Python**: Block `pip install` without `--target` or venv, `python -c` with eval/exec on external input
- **Docker**: Block `docker rm -f`, `docker system prune`, `docker push` to non-project registries
- **Database (any)**: Block `migrate` or `seed` commands against production connection strings
For each blocked pattern:
1. Exit code 2 with stderr message explaining what was blocked and why
2. The stderr message should suggest the safe alternative
**Step 4: Configure File Protection Hooks (PreToolUse)**
Create a `PreToolUse` hook with matcher `Edit|Write` that prevents modification of:
| File Pattern | Why |
|-------------|-----|
| `.env*` | Secrets — should never be modified by automation |
| `*.lock` / `*-lock.*` | Lock files — only package managers should touch these |
| `.git/*` | Git internals |
| `dist/` / `build/` / `out/` | Generated output — edit source, not artifacts |
| `node_modules/` / `venv/` / `target/` | Dependencies — never edit directly |
| Files matching `.gitignore` patterns | Anything git ignores is probably generated or sensitive |
Let the user add project-specific protected paths (e.g., migration files, generated API clients).
**Step 5: Write Hook Scripts**
Create all hook scripts in `.claude/hooks/` with this structure:
```
.claude/hooks/
├── block-dangerous-cmds.sh # PreToolUse: Bash → safety guard
└── protect-files.sh # PreToolUse: Edit|Write → file protection
```
Each script must:
- Read JSON from stdin using `jq`
- Be executable (`chmod +x`)
- Handle missing fields gracefully (not all tool calls have all fields)
- Exit 0 for success/pass-through, exit 2 for block
- Include a comment header explaining what it does
Guard against shell profile noise — hook scripts should not source `.bashrc`/`.zshrc`. Use `#!/usr/bin/env bash` with explicit PATH if tools need to be found.
**Step 6: Assemble settings.json and Pre-commit Config**
**Claude Code hooks** — write `.claude/settings.json` merging any existing settings with:
```json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/block-dangerous-cmds.sh",
"timeout": 10,
"statusMessage": "Checking command safety..."
}
]
},
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/protect-files.sh",
"timeout": 5,
"statusMessage": "Checking file protection..."
}
]
}
]
}
}
```
**Git pre-commit hook for code quality** — set up formatting and linting as a git pre-commit hook instead of a Claude Code hook. This runs once at commit time rather than on every edit, avoiding wasted tokens.
Use the `pre-commit` framework (`https://pre-commit.com`). Write a `.pre-commit-config.yaml` appropriate to the stack:
| Stack | Formatter Hook | Linter Hook |
|-------|---------------|-------------|
| Python | `ruff format` via `ruff-pre-commit` | `ruff check --fix` via `ruff-pre-commit` |
| TypeScript/Node | `prettier` via `mirrors-prettier` | `eslint` via local hook |
| Rust | `rustfmt` via local hook | `clippy` via local hook |
| Go | `gofmt` via local hook | `golangci-lint` via local hook |
Example for Python:
```yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.10 # check for latest version
hooks:
- id: ruff
args: [check, --fix]
- id: ruff-format
```
Install with `pre-commit install` and document in CLAUDE.md so Claude knows the project uses pre-commit hooks.
**Step 7: Output Summary**
```
## Hooks Configured: [Project Name]
### Safety Hooks (Claude Code — PreToolUse)
- block-dangerous-cmds.sh → Bash — [N] dangerous patterns covered
- [list key patterns blocked]
- protect-files.sh → Edit|Write — [N] protected patterns
- [list protected paths]
### Code Quality (Git Pre-commit)
- [formatter]: runs on commit via pre-commit framework
- [linter]: runs on commit via pre-commit framework
### Files Created
- .claude/settings.json — [created/updated]
- .claude/hooks/block-dangerous-cmds.sh
- .claude/hooks/protect-files.sh
- .pre-commit-config.yaml
### Testing Your Hooks
To verify safety hooks work, try these (they should be blocked):
- `[example dangerous command for this stack]`
- `[example protected file edit for this stack]`
### What's NOT Covered
- [Any categories the user declined]
- [Any limitations of the current setup]
### Suggested Next Steps
- `/establish-patterns` — prove architecture patterns
- `/init-project` — scaffold the repo
- Review blocked-command list and add project-specific patterns
```
---
## Hook Design Reference
### Exit Code Behavior
| Exit Code | Meaning |
|-----------|---------|
| `0` | Success — JSON stdout parsed, execution continues |
| `2` | Block — stderr shown to Claude, action prevented |
| Other | Non-blocking error — logged in debug mode only |
### Reading Input in Hook Scripts
All hook scripts receive JSON on stdin. Key fields:
```bash
#!/usr/bin/env bash
INPUT=$(cat)
# For Bash commands (PreToolUse, matcher: Bash)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')
# For file operations (PreToolUse, matcher: Edit|Write)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
```
### Blocking with Explanation (PreToolUse)
```bash
# Block and tell Claude why + what to do instead
jq -n --arg reason "DROP TABLE blocked. Use migrations to modify schema." '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: $reason
}
}'
```
### Performance Considerations
- Keep hook timeouts short (5-10s for safety checks)
- Use file extension checks early to skip irrelevant files fast
---
## After Running This Prompt
The project will have:
- `.claude/settings.json` with safety and file protection hooks
- `.claude/hooks/` directory with executable scripts
- `.pre-commit-config.yaml` for formatting/linting at commit time
- Safety guards that prevent dangerous commands regardless of permission mode
Commit `.claude/settings.json`, `.claude/hooks/`, and `.pre-commit-config.yaml` to the repo so the entire team benefits.