cc-discipline 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/init.sh +1 -0
- package/lib/doctor.sh +2 -2
- package/lib/status.sh +2 -1
- package/package.json +1 -1
- package/templates/.claude/hooks/git-guard.sh +62 -0
- package/templates/.claude/settings.json +9 -0
package/init.sh
CHANGED
|
@@ -280,6 +280,7 @@ cp "$SCRIPT_DIR/templates/.claude/hooks/post-error-remind.sh" .claude/hooks/
|
|
|
280
280
|
cp "$SCRIPT_DIR/templates/.claude/hooks/streak-breaker.sh" .claude/hooks/
|
|
281
281
|
cp "$SCRIPT_DIR/templates/.claude/hooks/session-start.sh" .claude/hooks/
|
|
282
282
|
cp "$SCRIPT_DIR/templates/.claude/hooks/phase-gate.sh" .claude/hooks/
|
|
283
|
+
cp "$SCRIPT_DIR/templates/.claude/hooks/git-guard.sh" .claude/hooks/
|
|
283
284
|
cp "$SCRIPT_DIR/templates/.claude/hooks/action-counter.sh" .claude/hooks/
|
|
284
285
|
chmod +x .claude/hooks/*.sh
|
|
285
286
|
|
package/lib/doctor.sh
CHANGED
|
@@ -44,7 +44,7 @@ done
|
|
|
44
44
|
# 3. Hooks
|
|
45
45
|
echo ""
|
|
46
46
|
echo "Hooks:"
|
|
47
|
-
for hook in pre-edit-guard streak-breaker post-error-remind session-start phase-gate action-counter; do
|
|
47
|
+
for hook in pre-edit-guard streak-breaker post-error-remind session-start phase-gate action-counter git-guard; do
|
|
48
48
|
if [ -f ".claude/hooks/${hook}.sh" ]; then
|
|
49
49
|
if [ -x ".claude/hooks/${hook}.sh" ]; then
|
|
50
50
|
ok "${hook}.sh"
|
|
@@ -62,7 +62,7 @@ echo "Hook registration:"
|
|
|
62
62
|
if [ -f ".claude/settings.json" ]; then
|
|
63
63
|
if command -v jq &>/dev/null; then
|
|
64
64
|
CONTENT=$(cat .claude/settings.json)
|
|
65
|
-
for hook in pre-edit-guard streak-breaker post-error-remind session-start phase-gate action-counter; do
|
|
65
|
+
for hook in pre-edit-guard streak-breaker post-error-remind session-start phase-gate action-counter git-guard; do
|
|
66
66
|
if echo "$CONTENT" | grep -q "$hook"; then
|
|
67
67
|
ok "${hook} registered"
|
|
68
68
|
else
|
package/lib/status.sh
CHANGED
|
@@ -51,8 +51,9 @@ HOOKS=""
|
|
|
51
51
|
[ -f ".claude/hooks/session-start.sh" ] && HOOKS="${HOOKS}session-start "
|
|
52
52
|
[ -f ".claude/hooks/phase-gate.sh" ] && HOOKS="${HOOKS}phase-gate "
|
|
53
53
|
[ -f ".claude/hooks/action-counter.sh" ] && HOOKS="${HOOKS}action-counter "
|
|
54
|
+
[ -f ".claude/hooks/git-guard.sh" ] && HOOKS="${HOOKS}git-guard "
|
|
54
55
|
HOOK_COUNT=$(echo "$HOOKS" | wc -w | tr -d ' ')
|
|
55
|
-
echo -e "${GREEN}${HOOK_COUNT}/
|
|
56
|
+
echo -e "${GREEN}${HOOK_COUNT}/7${NC} (${HOOKS% })"
|
|
56
57
|
|
|
57
58
|
# Agents
|
|
58
59
|
echo -n "Agents: "
|
package/package.json
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# cc-discipline: Guard against destructive git commands
|
|
3
|
+
# PreToolUse on Bash — blocks git checkout/restore/reset --hard/clean -f without confirmation.
|
|
4
|
+
|
|
5
|
+
INPUT=$(cat)
|
|
6
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""' 2>/dev/null)
|
|
7
|
+
|
|
8
|
+
if [ "$TOOL_NAME" != "Bash" ]; then
|
|
9
|
+
exit 0
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""' 2>/dev/null)
|
|
13
|
+
|
|
14
|
+
# Normalize: collapse whitespace, trim
|
|
15
|
+
CMD_NORM=$(echo "$CMD" | tr '\n' ' ' | sed 's/ */ /g')
|
|
16
|
+
|
|
17
|
+
BLOCKED=""
|
|
18
|
+
SUGGESTION=""
|
|
19
|
+
|
|
20
|
+
# git checkout . / git checkout -- <file> (discard working tree changes)
|
|
21
|
+
# But allow: git checkout <branch>, git checkout -b <branch>
|
|
22
|
+
if echo "$CMD_NORM" | grep -qE 'git\s+checkout\s+(\.|--\s)'; then
|
|
23
|
+
BLOCKED="git checkout (discards uncommitted changes)"
|
|
24
|
+
SUGGESTION="git stash"
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# git restore . / git restore <file> (without --staged)
|
|
28
|
+
if echo "$CMD_NORM" | grep -qE 'git\s+restore\s' && ! echo "$CMD_NORM" | grep -qE 'git\s+restore\s+--staged'; then
|
|
29
|
+
BLOCKED="git restore (discards uncommitted changes)"
|
|
30
|
+
SUGGESTION="git stash"
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# git reset --hard
|
|
34
|
+
if echo "$CMD_NORM" | grep -qE 'git\s+reset\s+--hard'; then
|
|
35
|
+
BLOCKED="git reset --hard (destroys all uncommitted changes)"
|
|
36
|
+
SUGGESTION="git stash && git reset"
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# git clean -f / -fd / -fx
|
|
40
|
+
if echo "$CMD_NORM" | grep -qE 'git\s+clean\s+-[a-z]*f'; then
|
|
41
|
+
BLOCKED="git clean -f (permanently deletes untracked files)"
|
|
42
|
+
SUGGESTION="git stash --include-untracked"
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# git branch -D (force delete unmerged branch)
|
|
46
|
+
if echo "$CMD_NORM" | grep -qE 'git\s+branch\s+-D\s'; then
|
|
47
|
+
BLOCKED="git branch -D (deletes branch even if not merged)"
|
|
48
|
+
SUGGESTION="git branch -d (safe delete, fails if not merged)"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# git push --force / -f (to main/master)
|
|
52
|
+
if echo "$CMD_NORM" | grep -qE 'git\s+push\s+.*(-f|--force)' && echo "$CMD_NORM" | grep -qE '(main|master)'; then
|
|
53
|
+
BLOCKED="git push --force to main/master (rewrites shared history)"
|
|
54
|
+
SUGGESTION="git push --force-with-lease"
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
if [ -n "$BLOCKED" ]; then
|
|
58
|
+
echo "GIT GUARD: Blocked destructive command: $BLOCKED. This command can permanently destroy uncommitted work. Before running this: (1) Check git status and git diff to see what would be lost. (2) If changes should be kept, run: $SUGGESTION first. (3) If you are certain the changes should be discarded, tell the user what will be lost and ask for explicit confirmation." >&2
|
|
59
|
+
exit 2
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
exit 0
|