Initial commit
This commit is contained in:
566
commands/create-hook.md
Normal file
566
commands/create-hook.md
Normal file
@@ -0,0 +1,566 @@
|
||||
---
|
||||
description: Create and configure Claude Code hooks with reference documentation
|
||||
argument-hint: [hook-type] [matcher] [command]
|
||||
---
|
||||
|
||||
# /create-hook
|
||||
|
||||
## Purpose
|
||||
Create and configure Claude Code hooks with reference documentation and interactive guidance.
|
||||
|
||||
## Contract
|
||||
**Inputs:**
|
||||
- `$1` — HOOK_TYPE (optional: PreToolUse, PostToolUse, UserPromptSubmit, Notification, Stop, SubagentStop, PreCompact, SessionStart, SessionEnd)
|
||||
- `$2` — MATCHER (optional: tool name pattern, e.g., "Bash", "Edit|Write", "*")
|
||||
- `$3` — COMMAND (optional: shell command to execute)
|
||||
|
||||
**Outputs:** `STATUS=<OK|FAIL> HOOK_FILE=<path>`
|
||||
|
||||
## Instructions
|
||||
|
||||
1. **Detect common use cases (hybrid approach):**
|
||||
- Scan user's request for keywords: "eslint", "prettier", "format", "lint", "typescript", "test", "commit"
|
||||
- If detected, ask: "I can set up a production-ready [TOOL] hook. Would you like to use the template or create a custom hook?"
|
||||
- If template chosen: Generate external script file in `.claude/hooks/` + settings.json entry
|
||||
- If custom or no match: Fall back to current workflow (steps 2-3)
|
||||
|
||||
2. **Determine hook configuration mode:**
|
||||
- If no arguments provided: Show interactive menu of hook types with examples
|
||||
- If HOOK_TYPE provided: Guide user through creating that specific hook
|
||||
- If all arguments provided: Create hook directly
|
||||
|
||||
3. **Validate inputs:**
|
||||
- HOOK_TYPE must be one of the valid hook events
|
||||
- MATCHER should be a valid tool name or pattern
|
||||
- COMMAND should be a valid shell command or path to external script
|
||||
- For external scripts: Ensure `.claude/hooks/` directory exists
|
||||
|
||||
4. **Reference documentation:**
|
||||
Review the Claude Code hooks documentation for best practices and examples:
|
||||
|
||||
**Hook Events:**
|
||||
- `PreToolUse`: Runs before tool calls (can block them)
|
||||
- `PostToolUse`: Runs after tool calls complete
|
||||
- `UserPromptSubmit`: Runs when user submits a prompt
|
||||
- `Notification`: Runs when Claude Code sends notifications
|
||||
- `Stop`: Runs when Claude Code finishes responding
|
||||
- `SubagentStop`: Runs when subagent tasks complete
|
||||
- `PreCompact`: Runs before compact operations
|
||||
- `SessionStart`: Runs when session starts/resumes
|
||||
- `SessionEnd`: Runs when session ends
|
||||
|
||||
5. **Production-Ready Script Templates:**
|
||||
|
||||
When user requests common integrations, generate these external scripts in `.claude/hooks/`:
|
||||
|
||||
**ESLint Auto-Fix (`run-eslint.sh`):**
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# Auto-fix JavaScript/TypeScript files with ESLint after edits
|
||||
set -euo pipefail
|
||||
|
||||
# Extract file path from Claude's JSON payload
|
||||
file_path="$(jq -r '.tool_input.file_path // ""')"
|
||||
|
||||
# Only process JS/TS files
|
||||
[[ "$file_path" =~ \.(js|jsx|ts|tsx)$ ]] || exit 0
|
||||
|
||||
# Auto-detect package manager (prefer project's lock file)
|
||||
if command -v pnpm >/dev/null 2>&1 && [ -f "pnpm-lock.yaml" ]; then
|
||||
PM="pnpm exec"
|
||||
elif command -v yarn >/dev/null 2>&1 && [ -f "yarn.lock" ]; then
|
||||
PM="yarn"
|
||||
else
|
||||
PM="npx"
|
||||
fi
|
||||
|
||||
# Run ESLint with auto-fix from project root
|
||||
cd "$CLAUDE_PROJECT_DIR" && $PM eslint --fix "$file_path"
|
||||
```
|
||||
|
||||
**Prettier Format (`run-prettier.sh`):**
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# Format files with Prettier after edits
|
||||
set -euo pipefail
|
||||
|
||||
file_path="$(jq -r '.tool_input.file_path // ""')"
|
||||
|
||||
# Skip non-formattable files
|
||||
[[ "$file_path" =~ \.(js|jsx|ts|tsx|json|css|scss|md|html|yml|yaml)$ ]] || exit 0
|
||||
|
||||
# Auto-detect package manager
|
||||
if command -v pnpm >/dev/null 2>&1 && [ -f "pnpm-lock.yaml" ]; then
|
||||
PM="pnpm exec"
|
||||
elif command -v yarn >/dev/null 2>&1 && [ -f "yarn.lock" ]; then
|
||||
PM="yarn"
|
||||
else
|
||||
PM="npx"
|
||||
fi
|
||||
|
||||
cd "$CLAUDE_PROJECT_DIR" && $PM prettier --write "$file_path"
|
||||
```
|
||||
|
||||
**TypeScript Type Check (`run-typescript.sh`):**
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# Run TypeScript type checker on TS/TSX file edits
|
||||
set -euo pipefail
|
||||
|
||||
file_path="$(jq -r '.tool_input.file_path // ""')"
|
||||
|
||||
# Only process TypeScript files
|
||||
[[ "$file_path" =~ \.(ts|tsx)$ ]] || exit 0
|
||||
|
||||
# Auto-detect package manager
|
||||
if command -v pnpm >/dev/null 2>&1 && [ -f "pnpm-lock.yaml" ]; then
|
||||
PM="pnpm exec"
|
||||
elif command -v yarn >/dev/null 2>&1 && [ -f "yarn.lock" ]; then
|
||||
PM="yarn"
|
||||
else
|
||||
PM="npx"
|
||||
fi
|
||||
|
||||
# Run tsc --noEmit to check types without emitting files
|
||||
cd "$CLAUDE_PROJECT_DIR" && $PM tsc --noEmit --pretty
|
||||
```
|
||||
|
||||
**Run Affected Tests (`run-tests.sh`):**
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# Run tests for modified files
|
||||
set -euo pipefail
|
||||
|
||||
file_path="$(jq -r '.tool_input.file_path // ""')"
|
||||
|
||||
# Only run tests for source files (not test files themselves)
|
||||
[[ "$file_path" =~ \.(test|spec)\.(js|ts|jsx|tsx)$ ]] && exit 0
|
||||
[[ "$file_path" =~ \.(js|jsx|ts|tsx)$ ]] || exit 0
|
||||
|
||||
# Auto-detect test runner and package manager
|
||||
if [ -f "vitest.config.ts" ] || [ -f "vitest.config.js" ]; then
|
||||
TEST_CMD="vitest related --run"
|
||||
elif [ -f "jest.config.js" ] || [ -f "jest.config.ts" ]; then
|
||||
TEST_CMD="jest --findRelatedTests"
|
||||
else
|
||||
exit 0 # No test runner configured
|
||||
fi
|
||||
|
||||
if command -v pnpm >/dev/null 2>&1 && [ -f "pnpm-lock.yaml" ]; then
|
||||
PM="pnpm exec"
|
||||
elif command -v yarn >/dev/null 2>&1 && [ -f "yarn.lock" ]; then
|
||||
PM="yarn"
|
||||
else
|
||||
PM="npx"
|
||||
fi
|
||||
|
||||
cd "$CLAUDE_PROJECT_DIR" && $PM $TEST_CMD "$file_path"
|
||||
```
|
||||
|
||||
**Commit Message Validation (`validate-commit.sh`):**
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# Validate commit messages follow conventional commits format
|
||||
set -euo pipefail
|
||||
|
||||
# Extract commit message from tool input
|
||||
commit_msg="$(jq -r '.tool_input // ""')"
|
||||
|
||||
# Check for conventional commit format: type(scope): message
|
||||
if ! echo "$commit_msg" | grep -qE '^(feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert)(\(.+\))?: .+'; then
|
||||
echo "ERROR: Commit message must follow conventional commits format"
|
||||
echo "Expected: type(scope): description"
|
||||
echo "Got: $commit_msg"
|
||||
exit 2 # Block the commit
|
||||
fi
|
||||
```
|
||||
|
||||
**Corresponding settings.json entries:**
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Edit|Write",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"comment": "Auto-fix with ESLint",
|
||||
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/run-eslint.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
6. **Best Practices for Hook Development:**
|
||||
|
||||
**Always use shell safety headers:**
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail # Exit on error, undefined vars, pipe failures
|
||||
```
|
||||
|
||||
**Extract data from Claude's JSON payload:**
|
||||
```bash
|
||||
# File path (Edit/Write tools)
|
||||
file_path="$(jq -r '.tool_input.file_path // ""')"
|
||||
|
||||
# Command (Bash tool)
|
||||
command="$(jq -r '.tool_input.command // ""')"
|
||||
|
||||
# Tool name
|
||||
tool_name="$(jq -r '.tool_name // ""')"
|
||||
|
||||
# Full tool input
|
||||
tool_input="$(jq -r '.tool_input' | jq -c .)"
|
||||
```
|
||||
|
||||
**Use environment variables provided by Claude:**
|
||||
```bash
|
||||
$CLAUDE_PROJECT_DIR # Project root directory
|
||||
$CLAUDE_USER_DIR # User's ~/.claude directory
|
||||
$CLAUDE_SESSION_ID # Current session identifier
|
||||
```
|
||||
|
||||
**Efficient file extension filtering:**
|
||||
```bash
|
||||
# Good: Use bash regex matching
|
||||
[[ "$file_path" =~ \.(ts|tsx)$ ]] || exit 0
|
||||
|
||||
# Avoid: Spawning grep subprocess
|
||||
echo "$file_path" | grep -q '\.ts$' || exit 0
|
||||
```
|
||||
|
||||
**Package manager auto-detection pattern:**
|
||||
```bash
|
||||
# Check lock files to match project's package manager
|
||||
if command -v pnpm >/dev/null 2>&1 && [ -f "pnpm-lock.yaml" ]; then
|
||||
PM="pnpm exec"
|
||||
elif command -v yarn >/dev/null 2>&1 && [ -f "yarn.lock" ]; then
|
||||
PM="yarn"
|
||||
else
|
||||
PM="npx" # Fallback to npx
|
||||
fi
|
||||
```
|
||||
|
||||
**Exit codes matter:**
|
||||
```bash
|
||||
exit 0 # Success: Allow operation to continue
|
||||
exit 1 # Error: Log error but don't block
|
||||
exit 2 # Block: Prevent operation in PreToolUse hooks
|
||||
```
|
||||
|
||||
**Performance considerations:**
|
||||
- Avoid heavy operations in tight loops (e.g., don't run full test suite on every file edit)
|
||||
- Use file extension checks to skip irrelevant files early
|
||||
- Consider async/background execution for slow operations
|
||||
- Cache results when possible (e.g., dependency checks)
|
||||
|
||||
**When to use external scripts vs inline commands:**
|
||||
- **External scripts** (`.claude/hooks/*.sh`): Complex logic, multiple steps, reusable patterns
|
||||
- **Inline commands**: Simple one-liners, quick jq filters, logging
|
||||
|
||||
Example inline command:
|
||||
```json
|
||||
"command": "jq -r '.tool_input.command' >> ~/.claude/command-log.txt"
|
||||
```
|
||||
|
||||
7. **Common use cases and examples (updated with best practices):**
|
||||
|
||||
**Logging Bash commands:**
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [{
|
||||
"matcher": "Bash",
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"comment": "Log all Bash commands with descriptions",
|
||||
"command": "set -euo pipefail; cmd=$(jq -r '.tool_input.command // \"\"'); desc=$(jq -r '.tool_input.description // \"No description\"'); echo \"[$(date -u +%Y-%m-%dT%H:%M:%SZ)] $cmd - $desc\" >> \"$CLAUDE_USER_DIR/bash-command-log.txt\""
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Auto-format TypeScript files with Prettier:**
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [{
|
||||
"matcher": "Edit|Write",
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"comment": "Auto-format TypeScript files after edits",
|
||||
"command": "set -euo pipefail; file_path=$(jq -r '.tool_input.file_path // \"\"'); [[ \"$file_path\" =~ \\.(ts|tsx)$ ]] || exit 0; cd \"$CLAUDE_PROJECT_DIR\" && npx prettier --write \"$file_path\""
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Block sensitive file edits:**
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [{
|
||||
"matcher": "Edit|Write",
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"comment": "Prevent edits to sensitive files",
|
||||
"command": "set -euo pipefail; file_path=$(jq -r '.tool_input.file_path // \"\"'); if [[ \"$file_path\" =~ \\.(env|secrets|credentials) ]] || [[ \"$file_path\" == *\"package-lock.json\" ]] || [[ \"$file_path\" == *.git/* ]]; then echo \"ERROR: Cannot edit sensitive file: $file_path\" >&2; exit 2; fi"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Desktop notifications:**
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"Notification": [{
|
||||
"matcher": "*",
|
||||
"hooks": [{
|
||||
"type": "command",
|
||||
"comment": "Send desktop notifications",
|
||||
"command": "if command -v notify-send >/dev/null 2>&1; then notify-send 'Claude Code' 'Awaiting your input'; elif command -v osascript >/dev/null 2>&1; then osascript -e 'display notification \"Awaiting your input\" with title \"Claude Code\"'; fi"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
8. **Generic Patterns Reference:**
|
||||
|
||||
**Pattern: Extract file path and check multiple extensions**
|
||||
```bash
|
||||
set -euo pipefail
|
||||
file_path=$(jq -r '.tool_input.file_path // ""')
|
||||
[[ "$file_path" =~ \.(js|ts|jsx|tsx|py|go|rs)$ ]] || exit 0
|
||||
# Your command here
|
||||
```
|
||||
|
||||
**Pattern: Process multiple files from array**
|
||||
```bash
|
||||
set -euo pipefail
|
||||
jq -r '.tool_input.files[]?' | while IFS= read -r file; do
|
||||
[[ -f "$file" ]] && echo "Processing: $file"
|
||||
# Your processing logic here
|
||||
done
|
||||
```
|
||||
|
||||
**Pattern: Conditional execution based on directory**
|
||||
```bash
|
||||
set -euo pipefail
|
||||
file_path=$(jq -r '.tool_input.file_path // ""')
|
||||
# Only process files in src/ directory
|
||||
[[ "$file_path" =~ ^src/ ]] || exit 0
|
||||
# Your command here
|
||||
```
|
||||
|
||||
**Pattern: Extract and validate Bash command**
|
||||
```bash
|
||||
set -euo pipefail
|
||||
cmd=$(jq -r '.tool_input.command // ""')
|
||||
# Block dangerous commands
|
||||
if [[ "$cmd" =~ (rm -rf|mkfs|dd|:(){:|:&};:) ]]; then
|
||||
echo "ERROR: Dangerous command blocked" >&2
|
||||
exit 2
|
||||
fi
|
||||
```
|
||||
|
||||
**Pattern: Background/async execution**
|
||||
```bash
|
||||
set -euo pipefail
|
||||
file_path=$(jq -r '.tool_input.file_path // ""')
|
||||
# Run slow operation in background, don't block Claude
|
||||
(cd "$CLAUDE_PROJECT_DIR" && npm run build "$file_path" &> /tmp/build.log) &
|
||||
```
|
||||
|
||||
**Pattern: Conditional tool execution**
|
||||
```bash
|
||||
set -euo pipefail
|
||||
tool_name=$(jq -r '.tool_name // ""')
|
||||
case "$tool_name" in
|
||||
Edit|Write)
|
||||
file_path=$(jq -r '.tool_input.file_path // ""')
|
||||
echo "File modified: $file_path"
|
||||
;;
|
||||
Bash)
|
||||
cmd=$(jq -r '.tool_input.command // ""')
|
||||
echo "Command executed: $cmd"
|
||||
;;
|
||||
esac
|
||||
```
|
||||
|
||||
**Pattern: Multi-tool chain with error handling**
|
||||
```bash
|
||||
set -euo pipefail
|
||||
file_path=$(jq -r '.tool_input.file_path // ""')
|
||||
[[ "$file_path" =~ \.(ts|tsx)$ ]] || exit 0
|
||||
|
||||
cd "$CLAUDE_PROJECT_DIR"
|
||||
|
||||
# Run linter
|
||||
if ! npx eslint --fix "$file_path" 2>/dev/null; then
|
||||
echo "Warning: ESLint failed" >&2
|
||||
fi
|
||||
|
||||
# Run formatter (always runs even if linter fails)
|
||||
npx prettier --write "$file_path" 2>/dev/null || true
|
||||
```
|
||||
|
||||
**Pattern: Cache validation results**
|
||||
```bash
|
||||
set -euo pipefail
|
||||
file_path=$(jq -r '.tool_input.file_path // ""')
|
||||
cache_file="/tmp/claude-hook-cache-$(echo \"$file_path\" | md5sum | cut -d' ' -f1)"
|
||||
|
||||
# Check cache freshness
|
||||
if [[ -f "$cache_file" ]] && [[ "$cache_file" -nt "$file_path" ]]; then
|
||||
cat "$cache_file"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Run expensive validation
|
||||
result=$(npx tsc --noEmit "$file_path" 2>&1)
|
||||
echo "$result" > "$cache_file"
|
||||
echo "$result"
|
||||
```
|
||||
|
||||
**Pattern: Cross-platform compatibility**
|
||||
```bash
|
||||
set -euo pipefail
|
||||
|
||||
# Detect OS and use appropriate commands
|
||||
case "$(uname -s)" in
|
||||
Darwin*)
|
||||
# macOS
|
||||
osascript -e 'display notification "Build complete"'
|
||||
;;
|
||||
Linux*)
|
||||
# Linux
|
||||
notify-send "Build complete"
|
||||
;;
|
||||
MINGW*|MSYS*|CYGWIN*)
|
||||
# Windows
|
||||
powershell -Command "New-BurntToastNotification -Text 'Build complete'"
|
||||
;;
|
||||
esac
|
||||
```
|
||||
|
||||
9. **Security considerations:**
|
||||
- Hooks run automatically with your environment's credentials
|
||||
- Always review hook implementation before registering
|
||||
- Be cautious with hooks that execute external commands
|
||||
- Avoid hardcoding sensitive data in hook commands
|
||||
- Use exit code 2 to block operations in PreToolUse hooks
|
||||
|
||||
10. **Hook creation workflow:**
|
||||
|
||||
**For common tools (ESLint, Prettier, TypeScript, Tests):**
|
||||
1. Detect tool name from user request keywords
|
||||
2. Ask user: "I can set up a production-ready [TOOL] hook. Use template or create custom?"
|
||||
3. If template:
|
||||
- Create `.claude/hooks/` directory if needed
|
||||
- Generate appropriate script file (e.g., `run-eslint.sh`)
|
||||
- Make script executable (`chmod +x`)
|
||||
- Add settings.json entry with `$CLAUDE_PROJECT_DIR` reference
|
||||
- Verify package manager and dependencies exist
|
||||
4. Provide setup summary and next steps
|
||||
|
||||
**For custom hooks:**
|
||||
1. Determine appropriate hook event for the use case
|
||||
2. Define matcher pattern (specific tool name, regex like "Edit|Write", or "*" for all)
|
||||
3. Write shell command that processes JSON input via stdin
|
||||
4. Always start with `set -euo pipefail` for safety
|
||||
5. Use `$CLAUDE_PROJECT_DIR` and other environment variables
|
||||
6. Test hook command independently before registering
|
||||
7. Choose storage location:
|
||||
- `.claude/settings.json` - Project-specific, committed to git
|
||||
- `.claude/settings.local.json` - Project-specific, not committed
|
||||
- `~/.claude/settings.json` - User-wide, all projects
|
||||
8. Register hook and reload with `/hooks` command
|
||||
|
||||
11. **Debugging hooks:**
|
||||
- Run `claude --debug` to see hook execution logs
|
||||
- Use `/hooks` command to verify hook registration
|
||||
- Check hook configuration in settings files
|
||||
- Test hook command standalone with sample JSON:
|
||||
```bash
|
||||
echo '{"tool_name":"Edit","tool_input":{"file_path":"test.ts"}}' | .claude/hooks/run-eslint.sh
|
||||
```
|
||||
- Review Claude Code output for hook execution errors
|
||||
- Use `jq` to inspect JSON data passed to hooks
|
||||
- Check exit codes (0=success/continue, 1=error/log, 2=block operation)
|
||||
- Verify file permissions (`chmod +x` for external scripts)
|
||||
- Check that `$CLAUDE_PROJECT_DIR` resolves correctly
|
||||
|
||||
12. **Output:**
|
||||
|
||||
**Template mode:**
|
||||
- Create external script file in `.claude/hooks/[script-name].sh`
|
||||
- Generate settings.json configuration snippet
|
||||
- Make script executable
|
||||
- Print: `STATUS=OK HOOK_FILE=.claude/hooks/[script-name].sh SETTINGS=.claude/settings.json`
|
||||
- Show: Next steps (reload hooks, test the hook)
|
||||
|
||||
**Interactive/Direct mode:**
|
||||
- Guide user through hook creation with prompts
|
||||
- Show the complete JSON configuration to add
|
||||
- Provide instructions for registering the hook
|
||||
- Print: `STATUS=OK HOOK_FILE=~/.claude/settings.json` or `STATUS=OK HOOK_FILE=.claude/settings.local.json`
|
||||
- Remind user to run `/hooks` to reload configuration
|
||||
|
||||
## Constraints
|
||||
- External scripts must be executable (`chmod +x`)
|
||||
- Hooks must be valid shell commands or script paths
|
||||
- JSON structure must follow hooks schema
|
||||
- PreToolUse hooks can block operations with exit code 2
|
||||
- Hooks should be idempotent and handle errors gracefully
|
||||
- Consider performance impact of hooks in tight loops
|
||||
- Always use `set -euo pipefail` in bash scripts for safety
|
||||
- Use `$CLAUDE_PROJECT_DIR` for project-relative paths
|
||||
- Test hooks standalone before deploying
|
||||
|
||||
## Examples
|
||||
|
||||
**Template mode (recommended for common tools):**
|
||||
```bash
|
||||
# User: "Set up ESLint to run on file edits"
|
||||
/create-hook
|
||||
# Detects "ESLint", offers template
|
||||
# → Creates .claude/hooks/run-eslint.sh
|
||||
# → Adds PostToolUse hook to settings.json
|
||||
# → Makes script executable
|
||||
# → STATUS=OK HOOK_FILE=.claude/hooks/run-eslint.sh
|
||||
```
|
||||
|
||||
**Interactive mode:**
|
||||
```bash
|
||||
/create-hook
|
||||
# Shows menu of:
|
||||
# 1. Common tools (ESLint, Prettier, TypeScript, Tests)
|
||||
# 2. Hook types (PreToolUse, PostToolUse, etc.)
|
||||
# 3. Custom hook creation
|
||||
```
|
||||
|
||||
**Guided mode:**
|
||||
```bash
|
||||
/create-hook PostToolUse
|
||||
# Guides through creating a PostToolUse hook
|
||||
# Asks for: matcher, command/script, storage location
|
||||
```
|
||||
|
||||
**Direct mode:**
|
||||
```bash
|
||||
/create-hook PreToolUse "Bash" "set -euo pipefail; cmd=\$(jq -r '.tool_input.command'); echo \"[\$(date -Iseconds)] \$cmd\" >> \"\$CLAUDE_USER_DIR/bash.log\""
|
||||
# Creates hook configuration directly with best practices
|
||||
```
|
||||
|
||||
## Reference
|
||||
For complete documentation, see: https://docs.claude.com/en/hooks
|
||||
Reference in New Issue
Block a user