refactor(plugin): remove restrictive hooks, clean up orphaned scripts

Removed:
- prefer-core.sh: blocked raw go/php commands unnecessarily
- post-commit-check.sh: noisy warnings after every commit
- block-docs.sh: blocked writing specs and RFCs
- capture-context.sh, extract-actionables.sh, pr-created.sh,
  suggest-compact.sh: orphaned scripts not referenced by any hook

Kept:
- go-format.sh, php-format.sh: auto-format after edits (helpful)
- check-debug.sh: warns about dd()/fmt.Print* (lightweight)
- session-start.sh, pre-compact.sh, session-save.sh: essential

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-03-16 05:37:52 +00:00
parent cce41faa39
commit 88e5fc6f49
8 changed files with 0 additions and 327 deletions

View file

@ -1,18 +1,6 @@
{
"$schema": "https://claude.ai/schemas/hooks.json",
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/prefer-core.sh"
}
],
"description": "Block destructive commands (rm -rf, sed -i, xargs rm) and enforce core CLI"
},
],
"PostToolUse": [
{
"matcher": "tool == \"Edit\" && tool_input.file_path matches \"\\.go$\"",
@ -43,16 +31,6 @@
}
],
"description": "Warn about debug statements (dd, dump, fmt.Println)"
},
{
"matcher": "tool == \"Bash\" && tool_input.command matches \"^git commit\"",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/post-commit-check.sh"
}
],
"description": "Warn about uncommitted work after git commit"
}
],
"Stop": [

View file

@ -1,18 +1,6 @@
{
"$schema": "https://claude.ai/schemas/hooks.json",
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/prefer-core.sh"
}
],
"description": "Block destructive commands (rm -rf, sed -i, xargs rm) and enforce core CLI"
},
],
"PostToolUse": [
{
"matcher": "tool == \"Edit\" && tool_input.file_path matches \"\\.go$\"",
@ -43,16 +31,6 @@
}
],
"description": "Warn about debug statements (dd, dump, fmt.Println)"
},
{
"matcher": "tool == \"Bash\" && tool_input.command matches \"^git commit\"",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/post-commit-check.sh"
}
],
"description": "Warn about uncommitted work after git commit"
}
],
"PreCompact": [

View file

@ -1,108 +0,0 @@
#!/bin/bash
# PreToolUse hook: Block dangerous commands, enforce core CLI
#
# BLOCKS:
# - Raw go commands (use core go *)
# - Destructive patterns (sed -i, xargs rm, etc.)
# - Mass file operations (rm -rf, mv/cp with wildcards)
#
# This prevents "efficient shortcuts" that nuke codebases
read -r input
full_command=$(echo "$input" | jq -r '.tool_input.command // empty')
# Strip heredoc content — only check the actual command, not embedded text
# This prevents false positives from code/docs inside heredocs
command=$(echo "$full_command" | sed -n '1p')
if echo "$command" | grep -qE "<<\s*['\"]?[A-Z_]+"; then
# First line has heredoc marker — only check the command portion before <<
command=$(echo "$command" | sed -E 's/\s*<<.*$//')
fi
# For multi-line commands joined with && or ;, check each segment
# But still only the first line (not heredoc body)
# === HARD BLOCKS - Never allow these ===
# Block rm -rf, rm -r (except for known safe paths like node_modules, vendor, .cache)
# Allow git rm -r (safe — git tracks everything, easily reversible)
if echo "$command" | grep -qE 'rm\s+(-[a-zA-Z]*r[a-zA-Z]*|-[a-zA-Z]*f[a-zA-Z]*r|--recursive)'; then
# git rm -r is safe — everything is tracked and recoverable
if echo "$command" | grep -qE 'git\s+rm\s'; then
: # allow git rm through
# Allow only specific safe directories for raw rm
elif ! echo "$command" | grep -qE 'rm\s+(-rf|-r)\s+(node_modules|vendor|\.cache|dist|build|__pycache__|\.pytest_cache|/tmp/)'; then
echo '{"decision": "block", "message": "BLOCKED: Recursive delete is not allowed. Delete files individually or ask the user to run this command."}'
exit 0
fi
fi
# Block mv/cp with dangerous wildcards (e.g. `cp * /tmp`, `mv ./* /dest`)
# Allow specific file copies that happen to use glob in a for loop or path
if echo "$command" | grep -qE '(mv|cp)\s+(\.\/)?\*\s'; then
echo '{"decision": "block", "message": "BLOCKED: Mass file move/copy with bare wildcards is not allowed. Copy files individually."}'
exit 0
fi
# Block xargs with rm, mv, cp (mass operations)
if echo "$command" | grep -qE 'xargs\s+.*(rm|mv|cp)'; then
echo '{"decision": "block", "message": "BLOCKED: xargs with file operations is not allowed. Too risky for mass changes."}'
exit 0
fi
# Block find -exec with rm, mv, cp
if echo "$command" | grep -qE 'find\s+.*-exec\s+.*(rm|mv|cp)'; then
echo '{"decision": "block", "message": "BLOCKED: find -exec with file operations is not allowed. Too risky for mass changes."}'
exit 0
fi
# Block sed -i on LOCAL files only (allow on remote via ssh/docker exec)
if echo "$command" | grep -qE '^sed\s+(-[a-zA-Z]*i|--in-place)'; then
echo '{"decision": "block", "message": "BLOCKED: sed -i (in-place edit) on local files. Use the Edit tool."}'
exit 0
fi
# Block grep -l piped to destructive commands only (not head, wc, etc.)
if echo "$command" | grep -qE 'grep\s+.*-l.*\|\s*(xargs|sed|rm|mv)'; then
echo '{"decision": "block", "message": "BLOCKED: grep -l piped to destructive commands. Too risky."}'
exit 0
fi
# Block perl -i on local files
if echo "$command" | grep -qE '^perl\s+-[a-zA-Z]*i'; then
echo '{"decision": "block", "message": "BLOCKED: In-place file editing with perl. Use the Edit tool."}'
exit 0
fi
# === REQUIRE CORE CLI ===
# Suggest core CLI for common go commands, but don't block
# go work sync, go mod edit, go get, go install, go list etc. have no core wrapper
case "$command" in
"go test"*|"go build"*|"go fmt"*|"go vet"*)
echo '{"decision": "block", "message": "Use `core go test`, `core build`, `core go fmt --fix`, `core go vet`. Raw go commands bypass quality checks."}'
exit 0
;;
esac
# Allow all other go commands (go mod tidy, go work sync, go get, go run, etc.)
# Block raw php commands
case "$command" in
"php artisan serve"*|"./vendor/bin/pest"*|"./vendor/bin/pint"*|"./vendor/bin/phpstan"*)
echo '{"decision": "block", "message": "Use `core php dev`, `core php test`, `core php fmt`, `core php analyse`. Raw php commands are not allowed."}'
exit 0
;;
"composer test"*|"composer lint"*)
echo '{"decision": "block", "message": "Use `core php test` or `core php fmt`. Raw composer commands are not allowed."}'
exit 0
;;
esac
# Block golangci-lint directly
if echo "$command" | grep -qE '^golangci-lint'; then
echo '{"decision": "block", "message": "Use `core go lint` instead of golangci-lint directly."}'
exit 0
fi
# === APPROVED ===
echo '{"decision": "approve"}'

View file

@ -1,44 +0,0 @@
#!/bin/bash
# Capture context facts from tool output or conversation
# Called by PostToolUse hooks to extract actionable items
#
# Stores in ~/.claude/sessions/context.json as:
# [{"fact": "...", "source": "core go qa", "ts": 1234567890}, ...]
CONTEXT_FILE="${HOME}/.claude/sessions/context.json"
TIMESTAMP=$(date '+%s')
THREE_HOURS=10800
mkdir -p "${HOME}/.claude/sessions"
# Initialize if missing or stale
if [[ -f "$CONTEXT_FILE" ]]; then
FIRST_TS=$(jq -r '.[0].ts // 0' "$CONTEXT_FILE" 2>/dev/null)
NOW=$(date '+%s')
AGE=$((NOW - FIRST_TS))
if [[ $AGE -gt $THREE_HOURS ]]; then
echo "[]" > "$CONTEXT_FILE"
fi
else
echo "[]" > "$CONTEXT_FILE"
fi
# Read input (fact and source passed as args or stdin)
FACT="${1:-}"
SOURCE="${2:-manual}"
if [[ -z "$FACT" ]]; then
# Try reading from stdin
read -r FACT
fi
if [[ -n "$FACT" ]]; then
# Append to context (keep last 20 items)
jq --arg fact "$FACT" --arg source "$SOURCE" --argjson ts "$TIMESTAMP" \
'. + [{"fact": $fact, "source": $source, "ts": $ts}] | .[-20:]' \
"$CONTEXT_FILE" > "${CONTEXT_FILE}.tmp" && mv "${CONTEXT_FILE}.tmp" "$CONTEXT_FILE"
echo "[Context] Saved: $FACT" >&2
fi
exit 0

View file

@ -1,34 +0,0 @@
#!/bin/bash
# Extract actionable items from core CLI output
# Called PostToolUse on Bash commands that run core
read -r input
COMMAND=$(echo "$input" | jq -r '.tool_input.command // empty')
OUTPUT=$(echo "$input" | jq -r '.tool_output.output // empty')
CONTEXT_SCRIPT="$(dirname "$0")/capture-context.sh"
# Extract actionables from specific core commands
case "$COMMAND" in
"core go qa"*|"core go test"*|"core go lint"*)
# Extract error/warning lines
echo "$OUTPUT" | grep -E "^(ERROR|WARN|FAIL|---)" | head -5 | while read -r line; do
"$CONTEXT_SCRIPT" "$line" "core go"
done
;;
"core php test"*|"core php analyse"*)
# Extract PHP errors
echo "$OUTPUT" | grep -E "^(FAIL|Error|×)" | head -5 | while read -r line; do
"$CONTEXT_SCRIPT" "$line" "core php"
done
;;
"core build"*)
# Extract build errors
echo "$OUTPUT" | grep -E "^(error|cannot|undefined)" | head -5 | while read -r line; do
"$CONTEXT_SCRIPT" "$line" "core build"
done
;;
esac
# Pass through
echo "$input"

View file

@ -1,51 +0,0 @@
#!/bin/bash
# Post-commit hook: Check for uncommitted work that might get lost
#
# After committing task-specific files, check if there's other work
# in the repo that should be committed or stashed
read -r input
COMMAND=$(echo "$input" | jq -r '.tool_input.command // empty')
# Only run after git commit
if ! echo "$COMMAND" | grep -qE '^git commit'; then
echo "$input"
exit 0
fi
# Check for remaining uncommitted changes
UNSTAGED=$(git diff --name-only 2>/dev/null | wc -l | tr -d ' ')
STAGED=$(git diff --cached --name-only 2>/dev/null | wc -l | tr -d ' ')
UNTRACKED=$(git ls-files --others --exclude-standard 2>/dev/null | wc -l | tr -d ' ')
TOTAL=$((UNSTAGED + STAGED + UNTRACKED))
if [[ $TOTAL -gt 0 ]]; then
echo "" >&2
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
echo "[PostCommit] WARNING: Uncommitted work remains" >&2
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
if [[ $UNSTAGED -gt 0 ]]; then
echo " Modified (unstaged): $UNSTAGED files" >&2
git diff --name-only 2>/dev/null | head -5 | sed 's/^/ /' >&2
[[ $UNSTAGED -gt 5 ]] && echo " ... and $((UNSTAGED - 5)) more" >&2
fi
if [[ $STAGED -gt 0 ]]; then
echo " Staged (not committed): $STAGED files" >&2
git diff --cached --name-only 2>/dev/null | head -5 | sed 's/^/ /' >&2
fi
if [[ $UNTRACKED -gt 0 ]]; then
echo " Untracked: $UNTRACKED files" >&2
git ls-files --others --exclude-standard 2>/dev/null | head -5 | sed 's/^/ /' >&2
[[ $UNTRACKED -gt 5 ]] && echo " ... and $((UNTRACKED - 5)) more" >&2
fi
echo "" >&2
echo "Consider: commit these, stash them, or confirm they're intentionally left" >&2
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" >&2
fi
echo "$input"

View file

@ -1,18 +0,0 @@
#!/bin/bash
# Log PR URL and provide review command after PR creation
read -r input
COMMAND=$(echo "$input" | jq -r '.tool_input.command // empty')
OUTPUT=$(echo "$input" | jq -r '.tool_output.output // empty')
if [[ "$COMMAND" == *"gh pr create"* ]]; then
PR_URL=$(echo "$OUTPUT" | grep -oE 'https://github.com/[^/]+/[^/]+/pull/[0-9]+' | head -1)
if [[ -n "$PR_URL" ]]; then
REPO=$(echo "$PR_URL" | sed -E 's|https://github.com/([^/]+/[^/]+)/pull/[0-9]+|\1|')
PR_NUM=$(echo "$PR_URL" | sed -E 's|.*/pull/([0-9]+)|\1|')
echo "[Hook] PR created: $PR_URL" >&2
echo "[Hook] To review: gh pr review $PR_NUM --repo $REPO" >&2
fi
fi
echo "$input"

View file

@ -1,28 +0,0 @@
#!/bin/bash
# Suggest /compact at logical intervals to manage context window
# Tracks tool calls per session, suggests compaction every 50 calls
SESSION_ID="${CLAUDE_SESSION_ID:-$$}"
COUNTER_FILE="/tmp/claude-tool-count-${SESSION_ID}"
THRESHOLD="${COMPACT_THRESHOLD:-50}"
# Read or initialize counter
if [[ -f "$COUNTER_FILE" ]]; then
COUNT=$(($(cat "$COUNTER_FILE") + 1))
else
COUNT=1
fi
echo "$COUNT" > "$COUNTER_FILE"
# Suggest compact at threshold
if [[ $COUNT -eq $THRESHOLD ]]; then
echo "[Compact] ${THRESHOLD} tool calls - consider /compact if transitioning phases" >&2
fi
# Suggest at intervals after threshold
if [[ $COUNT -gt $THRESHOLD ]] && [[ $((COUNT % 25)) -eq 0 ]]; then
echo "[Compact] ${COUNT} tool calls - good checkpoint for /compact" >&2
fi
exit 0