Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions .claude/hooks/block_bash_with_instructions.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/bin/bash
# block_bash_with_instructions.sh - Blocks specific bash commands and provides alternative instructions
#
# This hook intercepts Bash tool use calls and blocks commands that match
# specific patterns, providing alternative instructions to the agent.
#
# Usage: Registered as a PreToolUse hook in .claude/settings.json
#
# Input (stdin): JSON from Claude Code hook system containing tool_name and tool_input
# Output (stdout): JSON response with error message if blocked
# Exit codes:
# 0 - Success (allow action)
# 2 - Blocking error (prevent action with message)

set -e

# =============================================================================
# BLOCKED COMMANDS CONFIGURATION
# =============================================================================
# Format: Each entry is a regex pattern followed by a delimiter (|||) and instructions
# The regex is matched against the full bash command
# Add new blocked commands here:

BLOCKED_COMMANDS=(
'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.'
)

# =============================================================================
# HOOK LOGIC - DO NOT MODIFY BELOW UNLESS NECESSARY
# =============================================================================

# Read stdin into variable
HOOK_INPUT=""
if [ ! -t 0 ]; then
HOOK_INPUT=$(cat)
fi

# Exit early if no input
if [ -z "${HOOK_INPUT}" ]; then
exit 0
fi

# Extract tool_name from input
TOOL_NAME=$(echo "${HOOK_INPUT}" | jq -r '.tool_name // empty' 2>/dev/null)

# Only process Bash tool calls
if [ "${TOOL_NAME}" != "Bash" ]; then
exit 0
fi

# Extract the command from tool_input
COMMAND=$(echo "${HOOK_INPUT}" | jq -r '.tool_input.command // empty' 2>/dev/null)

# Exit if no command
if [ -z "${COMMAND}" ]; then
exit 0
fi

# Check each blocked pattern
for entry in "${BLOCKED_COMMANDS[@]}"; do
# Split entry by delimiter
pattern="${entry%%|||*}"
instructions="${entry##*|||}"

# Check if command matches pattern (using extended regex)
if echo "${COMMAND}" | grep -qE "${pattern}"; then
# Output error message as JSON
cat << EOF
{"error": "${instructions}"}
EOF
exit 2
fi
done

# Command is allowed
exit 0
4 changes: 4 additions & 0 deletions .claude/hooks/commit_job_git_commit.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
# commit_job_git_commit.sh - Wrapper for git commit invoked via the /commit skill

exec git commit "$@"
14 changes: 13 additions & 1 deletion .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,22 @@
"Read(./.deepwork/**)",
"Edit(./.deepwork/**)",
"Write(./.deepwork/**)",
"Bash(deepwork:*)"
"Bash(deepwork:*)",
"Bash(.claude/hooks/commit_job_git_commit.sh:*)"
]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/block_bash_with_instructions.sh"
}
]
}
],
"SessionStart": [
{
"matcher": "",
Expand Down
3 changes: 2 additions & 1 deletion .deepwork/jobs/commit/steps/commit_and_push.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ Check the list of changed files against what was modified during this session, e
- The style of recent commits
- Conventional commit format if the project uses it

**IMPORTANT:** Use the commit job script (not `git commit` directly):
```bash
git commit -m "commit message here"
.claude/hooks/commit_job_git_commit.sh -m "commit message here"
```

7. **Push to remote**
Expand Down