34 lines
1.8 KiB
Bash
Executable File
34 lines
1.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# PreToolUse hook: edits in code dirs (any src/ by default, or .claude/code-paths) require a plan for this branch or a .bounded marker.
|
|
set -euo pipefail
|
|
input=$(cat)
|
|
read -r cwd fpath < <(printf '%s' "$input" | python3 -c '
|
|
import sys,json
|
|
d=json.load(sys.stdin); ti=d.get("tool_input",{}) or {}
|
|
print(d.get("cwd",""), ti.get("file_path") or ti.get("path") or "")' 2>/dev/null || echo " ")
|
|
[ -n "$cwd" ] && [ -n "$fpath" ] || exit 0
|
|
root=$(git -C "$cwd" rev-parse --show-toplevel 2>/dev/null || true)
|
|
[ -n "$root" ] || exit 0
|
|
# Защищаемые пути. По умолчанию — любой каталог src/ на любой глубине (src/, api/src/, packages/*/src/).
|
|
# Проект может переопределить списком префиксов (относительно корня, по одному на строку) в .claude/code-paths.
|
|
rel=${fpath#"$root"/}
|
|
protected=0
|
|
if [ -f "$root/.claude/code-paths" ]; then
|
|
while IFS= read -r p; do
|
|
p=${p%%#*}; p=${p## }; p=${p%% }; [ -n "$p" ] || continue
|
|
case "$rel" in "${p%/}"/*) protected=1; break ;; esac
|
|
done < "$root/.claude/code-paths"
|
|
else
|
|
case "/$rel" in */src/*) protected=1 ;; esac
|
|
fi
|
|
[ "$protected" = 1 ] || exit 0
|
|
[ -f "$root/.bounded" ] && exit 0
|
|
branch=$(git -C "$cwd" branch --show-current 2>/dev/null || true)
|
|
if ls "$root"/docs/superpowers/plans/*.md >/dev/null 2>&1; then
|
|
if [ -z "$branch" ] || grep -lq -- "$branch" "$root"/docs/superpowers/plans/*.md 2>/dev/null; then exit 0; fi
|
|
# any plan exists but none names this branch: still allow if only one plan (single-feature repo)
|
|
[ "$(ls "$root"/docs/superpowers/plans/*.md | wc -l)" -eq 1 ] && exit 0
|
|
fi
|
|
echo "BLOCKED: editing $fpath without a plan. Run superpowers:brainstorming; for a bounded change create '$root/.bounded' first." >&2
|
|
exit 2
|