feat(examples): add production-ready commands, hooks, and comprehensive documentation
Added 3 production slash commands: - /pr: PR creation with complexity scoring and scope analysis - /release-notes: Generate release notes in 3 formats with migration detection - /sonarqube: Analyze SonarCloud quality issues for PRs Added 2 production hooks: - dangerous-actions-blocker.sh: PreToolUse security hook blocking destructive operations - notification.sh: Contextual macOS alerts with sound mappings Created comprehensive hooks documentation (examples/hooks/README.md) Improved README discoverability: - Moved "What's Inside" to line 24 for immediate visibility - Added DeepWiki interactive documentation explorer section - Added "Ready-to-Use Examples" section with command/hook tables Extended guide documentation: - Expanded bash mode (!) with 9 concrete examples - Documented file references (@) with usage patterns - Updated statistics: guide now 8,505 lines (+837 lines, +10.9%) All templates are fully generic with no project-specific references. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
2dd2744721
commit
96f0435291
10 changed files with 1698 additions and 19 deletions
151
examples/hooks/bash/dangerous-actions-blocker.sh
Executable file
151
examples/hooks/bash/dangerous-actions-blocker.sh
Executable file
|
|
@ -0,0 +1,151 @@
|
|||
#!/bin/bash
|
||||
# Hook: PreToolUse - Block dangerous actions
|
||||
# Exit 0 = allow, Exit 2 = block (stderr message shown to Claude)
|
||||
#
|
||||
# Place in: .claude/hooks/dangerous-actions-blocker.sh
|
||||
# Register in: .claude/settings.json under PreToolUse event
|
||||
|
||||
set -e
|
||||
|
||||
# Read JSON from stdin
|
||||
INPUT=$(cat)
|
||||
|
||||
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
|
||||
TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input // empty')
|
||||
|
||||
# === BASH: Dangerous commands ===
|
||||
if [[ "$TOOL_NAME" == "Bash" ]]; then
|
||||
COMMAND=$(echo "$TOOL_INPUT" | jq -r '.command // empty')
|
||||
|
||||
# Dangerous patterns
|
||||
DANGEROUS_PATTERNS=(
|
||||
"rm -rf /"
|
||||
"rm -rf ~"
|
||||
"rm -rf \$HOME"
|
||||
"dd if="
|
||||
"mkfs"
|
||||
":(){:|:&};:" # Fork bomb
|
||||
"> /dev/sda"
|
||||
"chmod -R 777 /"
|
||||
"chown -R"
|
||||
"sudo rm"
|
||||
"DROP DATABASE"
|
||||
"DROP TABLE"
|
||||
"--no-preserve-root"
|
||||
)
|
||||
|
||||
for pattern in "${DANGEROUS_PATTERNS[@]}"; do
|
||||
if [[ "$COMMAND" == *"$pattern"* ]]; then
|
||||
echo "BLOCKED: Dangerous command detected: '$pattern'" >&2
|
||||
exit 2
|
||||
fi
|
||||
done
|
||||
|
||||
# Block force push to main/master
|
||||
if echo "$COMMAND" | grep -qE "git push.*(-f|--force).*(main|master)"; then
|
||||
echo "BLOCKED: Force push to main/master is forbidden" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Block npm publish without confirmation
|
||||
if echo "$COMMAND" | grep -qE "npm publish|pnpm publish|yarn publish"; then
|
||||
echo "BLOCKED: Package publication requires manual confirmation" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Check for potential secrets in command
|
||||
SECRET_PATTERNS=(
|
||||
"password="
|
||||
"secret="
|
||||
"api_key="
|
||||
"apikey="
|
||||
"token="
|
||||
"aws_access_key"
|
||||
"aws_secret"
|
||||
"private_key"
|
||||
)
|
||||
|
||||
for pattern in "${SECRET_PATTERNS[@]}"; do
|
||||
if echo "$COMMAND" | grep -qi "$pattern"; then
|
||||
echo "BLOCKED: Potential secret detected in command: '$pattern'" >&2
|
||||
exit 2
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# === EDIT/WRITE: Sensitive files ===
|
||||
if [[ "$TOOL_NAME" == "Edit" || "$TOOL_NAME" == "Write" ]]; then
|
||||
FILE_PATH=$(echo "$TOOL_INPUT" | jq -r '.file_path // empty')
|
||||
|
||||
# Protected files
|
||||
PROTECTED_FILES=(
|
||||
".env"
|
||||
".env.local"
|
||||
".env.production"
|
||||
".env.development"
|
||||
"credentials.json"
|
||||
"serviceAccountKey.json"
|
||||
"id_rsa"
|
||||
"id_ed25519"
|
||||
"id_ecdsa"
|
||||
".npmrc"
|
||||
".pypirc"
|
||||
"secrets.yml"
|
||||
"secrets.yaml"
|
||||
)
|
||||
|
||||
FILENAME=$(basename "$FILE_PATH")
|
||||
for protected in "${PROTECTED_FILES[@]}"; do
|
||||
if [[ "$FILENAME" == "$protected" ]]; then
|
||||
echo "BLOCKED: Editing sensitive file '$FILENAME' is forbidden" >&2
|
||||
exit 2
|
||||
fi
|
||||
done
|
||||
|
||||
# Block editing outside project (with configurable exceptions)
|
||||
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
||||
CLAUDE_HOME="${HOME}/.claude"
|
||||
|
||||
# Allowed paths (configurable via environment variable)
|
||||
# Format: colon-separated paths - e.g., ALLOWED_PATHS="/custom/path:/other/path"
|
||||
EXTRA_ALLOWED="${ALLOWED_PATHS:-}"
|
||||
|
||||
# Check if path is allowed
|
||||
is_allowed=false
|
||||
|
||||
# Current project
|
||||
[[ "$FILE_PATH" == "$PROJECT_DIR"* ]] && is_allowed=true
|
||||
|
||||
# Claude Code directory (~/.claude/) - plans, logs, settings
|
||||
[[ "$FILE_PATH" == "$CLAUDE_HOME"* ]] && is_allowed=true
|
||||
|
||||
# Temporary files
|
||||
[[ "$FILE_PATH" == "/tmp"* ]] && is_allowed=true
|
||||
|
||||
# Additional configured paths
|
||||
if [[ -n "$EXTRA_ALLOWED" ]]; then
|
||||
IFS=':' read -ra EXTRA_PATHS <<< "$EXTRA_ALLOWED"
|
||||
for allowed_path in "${EXTRA_PATHS[@]}"; do
|
||||
[[ "$FILE_PATH" == "$allowed_path"* ]] && is_allowed=true
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ "$is_allowed" == "false" ]]; then
|
||||
echo "BLOCKED: Editing outside project is forbidden: $FILE_PATH" >&2
|
||||
echo "Allowed paths: $PROJECT_DIR, $CLAUDE_HOME, /tmp" >&2
|
||||
[[ -n "$EXTRA_ALLOWED" ]] && echo "Additional allowed: $EXTRA_ALLOWED" >&2
|
||||
exit 2
|
||||
fi
|
||||
fi
|
||||
|
||||
# === DELETE: Always warn ===
|
||||
if [[ "$TOOL_NAME" == "Bash" ]]; then
|
||||
COMMAND=$(echo "$TOOL_INPUT" | jq -r '.command // empty')
|
||||
if echo "$COMMAND" | grep -qE "rm -r|rmdir|unlink"; then
|
||||
# Warning but not blocking (exit 0)
|
||||
echo '{"systemMessage": "Warning: File deletion detected. Verify this is intentional."}'
|
||||
fi
|
||||
fi
|
||||
|
||||
# Allow by default
|
||||
exit 0
|
||||
62
examples/hooks/bash/notification.sh
Executable file
62
examples/hooks/bash/notification.sh
Executable file
|
|
@ -0,0 +1,62 @@
|
|||
#!/bin/bash
|
||||
# Hook: Notification - macOS alerts with contextual sounds
|
||||
# Plays different sound based on notification type
|
||||
#
|
||||
# Place in: .claude/hooks/notification.sh
|
||||
# Register in: .claude/settings.json under Notification event
|
||||
#
|
||||
# Note: macOS only - requires afplay and osascript
|
||||
|
||||
set -e
|
||||
|
||||
# Read JSON from stdin
|
||||
INPUT=$(cat)
|
||||
|
||||
# Extract message and title
|
||||
MESSAGE=$(echo "$INPUT" | jq -r '.message // "Claude Code Notification"')
|
||||
TITLE=$(echo "$INPUT" | jq -r '.title // "Claude Code"')
|
||||
|
||||
# Select sound based on context
|
||||
select_sound() {
|
||||
local msg="$1"
|
||||
local msg_lower=$(echo "$msg" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# Success / Completion
|
||||
if echo "$msg_lower" | grep -qE "(completed|terminé|fini|done|success|réussi|validé|finished)"; then
|
||||
echo "/System/Library/Sounds/Hero.aiff"
|
||||
return
|
||||
fi
|
||||
|
||||
# Error / Failure
|
||||
if echo "$msg_lower" | grep -qE "(error|erreur|failed|échec|échoué|problem|problème|failure)"; then
|
||||
echo "/System/Library/Sounds/Basso.aiff"
|
||||
return
|
||||
fi
|
||||
|
||||
# Waiting / Permission
|
||||
if echo "$msg_lower" | grep -qE "(waiting|attente|permission|approval|input|question|prompt)"; then
|
||||
echo "/System/Library/Sounds/Submarine.aiff"
|
||||
return
|
||||
fi
|
||||
|
||||
# Warning / Attention
|
||||
if echo "$msg_lower" | grep -qE "(warning|attention|caution|alert|avertissement)"; then
|
||||
echo "/System/Library/Sounds/Sosumi.aiff"
|
||||
return
|
||||
fi
|
||||
|
||||
# Default
|
||||
echo "/System/Library/Sounds/Ping.aiff"
|
||||
}
|
||||
|
||||
SOUND_FILE=$(select_sound "$MESSAGE")
|
||||
|
||||
# Play sound (in background to avoid blocking)
|
||||
if [[ -f "$SOUND_FILE" ]]; then
|
||||
afplay "$SOUND_FILE" &
|
||||
fi
|
||||
|
||||
# Display macOS notification
|
||||
osascript -e "display notification \"$MESSAGE\" with title \"$TITLE\" sound name \"\"" 2>/dev/null || true
|
||||
|
||||
exit 0
|
||||
Loading…
Add table
Add a link
Reference in a new issue