|
| 1 | +#!/bin/bash |
| 2 | +# block_bash_with_instructions.sh - Blocks specific bash commands and provides alternative instructions |
| 3 | +# |
| 4 | +# This hook intercepts Bash tool use calls and blocks commands that match |
| 5 | +# specific patterns, providing alternative instructions to the agent. |
| 6 | +# |
| 7 | +# Usage: Registered as a PreToolUse hook in .claude/settings.json |
| 8 | +# |
| 9 | +# Input (stdin): JSON from Claude Code hook system containing tool_name and tool_input |
| 10 | +# Output (stdout): JSON response with error message if blocked |
| 11 | +# Exit codes: |
| 12 | +# 0 - Success (allow action) |
| 13 | +# 2 - Blocking error (prevent action with message) |
| 14 | + |
| 15 | +set -e |
| 16 | + |
| 17 | +# ============================================================================= |
| 18 | +# BLOCKED COMMANDS CONFIGURATION |
| 19 | +# ============================================================================= |
| 20 | +# Format: Each entry is a regex pattern followed by a delimiter (|||) and instructions |
| 21 | +# The regex is matched against the full bash command |
| 22 | +# Add new blocked commands here: |
| 23 | + |
| 24 | +BLOCKED_COMMANDS=( |
| 25 | + 'git[[:space:]]+commit|||All commits must be done via the `/commit` skill. Do not use git commit directly. Instead, run `/commit` to start the commit workflow which includes code review, testing, and linting before committing.' |
| 26 | +) |
| 27 | + |
| 28 | +# ============================================================================= |
| 29 | +# HOOK LOGIC - DO NOT MODIFY BELOW UNLESS NECESSARY |
| 30 | +# ============================================================================= |
| 31 | + |
| 32 | +# Read stdin into variable |
| 33 | +HOOK_INPUT="" |
| 34 | +if [ ! -t 0 ]; then |
| 35 | + HOOK_INPUT=$(cat) |
| 36 | +fi |
| 37 | + |
| 38 | +# Exit early if no input |
| 39 | +if [ -z "${HOOK_INPUT}" ]; then |
| 40 | + exit 0 |
| 41 | +fi |
| 42 | + |
| 43 | +# Extract tool_name from input |
| 44 | +TOOL_NAME=$(echo "${HOOK_INPUT}" | jq -r '.tool_name // empty' 2>/dev/null) |
| 45 | + |
| 46 | +# Only process Bash tool calls |
| 47 | +if [ "${TOOL_NAME}" != "Bash" ]; then |
| 48 | + exit 0 |
| 49 | +fi |
| 50 | + |
| 51 | +# Extract the command from tool_input |
| 52 | +COMMAND=$(echo "${HOOK_INPUT}" | jq -r '.tool_input.command // empty' 2>/dev/null) |
| 53 | + |
| 54 | +# Exit if no command |
| 55 | +if [ -z "${COMMAND}" ]; then |
| 56 | + exit 0 |
| 57 | +fi |
| 58 | + |
| 59 | +# Check each blocked pattern |
| 60 | +for entry in "${BLOCKED_COMMANDS[@]}"; do |
| 61 | + # Split entry by delimiter |
| 62 | + pattern="${entry%%|||*}" |
| 63 | + instructions="${entry##*|||}" |
| 64 | + |
| 65 | + # Check if command matches pattern (using extended regex) |
| 66 | + if echo "${COMMAND}" | grep -qE "${pattern}"; then |
| 67 | + # Output error message as JSON |
| 68 | + cat << EOF |
| 69 | +{"error": "${instructions}"} |
| 70 | +EOF |
| 71 | + exit 2 |
| 72 | + fi |
| 73 | +done |
| 74 | + |
| 75 | +# Command is allowed |
| 76 | +exit 0 |
0 commit comments