cc-discipline 2.10.3 → 2.12.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/bin/cli.js CHANGED
@@ -1,147 +1,147 @@
1
- #!/usr/bin/env node
2
- // cc-discipline CLI entry point (cross-platform)
3
- // Detects platform and spawns bash with correct paths
4
-
5
- const { execSync, spawnSync } = require('child_process');
6
- const path = require('path');
7
- const fs = require('fs');
8
-
9
- const PKG_DIR = path.resolve(__dirname, '..');
10
- const args = process.argv.slice(2);
11
-
12
- // Find bash executable
13
- function findBash() {
14
- // Unix: bash is always available
15
- if (process.platform !== 'win32') return 'bash';
16
-
17
- // Windows: try common Git Bash locations
18
- const candidates = [
19
- 'C:\\Program Files\\Git\\bin\\bash.exe',
20
- 'C:\\Program Files (x86)\\Git\\bin\\bash.exe',
21
- process.env.PROGRAMFILES + '\\Git\\bin\\bash.exe',
22
- ];
23
-
24
- for (const candidate of candidates) {
25
- if (fs.existsSync(candidate)) return candidate;
26
- }
27
-
28
- // Try PATH
29
- try {
30
- execSync('bash --version', { stdio: 'ignore' });
31
- return 'bash';
32
- } catch (e) {
33
- console.error('Error: bash not found. Please install Git for Windows (https://git-scm.com/download/win)');
34
- console.error('Git Bash is required to run cc-discipline on Windows.');
35
- process.exit(1);
36
- }
37
- }
38
-
39
- const bash = findBash();
40
-
41
- // Convert Windows path to Unix-style for bash
42
- function toUnixPath(p) {
43
- if (process.platform !== 'win32') return p;
44
- // C:\Users\foo → /c/Users/foo
45
- return p.replace(/\\/g, '/').replace(/^([A-Za-z]):/, (_, drive) => '/' + drive.toLowerCase());
46
- }
47
-
48
- // Route subcommands
49
- const command = args[0] || 'init';
50
- const restArgs = args.slice(1);
51
-
52
- let script;
53
- let scriptArgs;
54
-
55
- switch (command) {
56
- case 'init':
57
- script = path.join(PKG_DIR, 'init.sh');
58
- scriptArgs = restArgs;
59
- break;
60
- case 'upgrade':
61
- script = path.join(PKG_DIR, 'init.sh');
62
- scriptArgs = ['--auto', ...restArgs];
63
- break;
64
- case 'status':
65
- script = path.join(PKG_DIR, 'lib', 'status.sh');
66
- scriptArgs = [];
67
- break;
68
- case 'doctor':
69
- script = path.join(PKG_DIR, 'lib', 'doctor.sh');
70
- scriptArgs = [];
71
- break;
72
- case 'add-stack':
73
- if (restArgs.length === 0) {
74
- console.log('Usage: cc-discipline add-stack <numbers>');
75
- console.log(' e.g.: cc-discipline add-stack 3 4');
76
- process.exit(1);
77
- }
78
- script = path.join(PKG_DIR, 'init.sh');
79
- scriptArgs = ['--stack', restArgs.join(' '), '--no-global'];
80
- break;
81
- case 'remove-stack':
82
- script = path.join(PKG_DIR, 'lib', 'stack-remove.sh');
83
- scriptArgs = restArgs;
84
- break;
85
- case '-v':
86
- case '--version':
87
- case 'version':
88
- const pkg = require(path.join(PKG_DIR, 'package.json'));
89
- console.log(`cc-discipline v${pkg.version}`);
90
- process.exit(0);
91
- case '-h':
92
- case '--help':
93
- case 'help':
94
- const ver = require(path.join(PKG_DIR, 'package.json')).version;
95
- console.log(`cc-discipline v${ver} — Discipline framework for Claude Code
96
-
97
- Usage: cc-discipline <command> [options]
98
-
99
- Commands:
100
- init [options] Install discipline into current project (default)
101
- upgrade Upgrade rules/hooks (shortcut for init --auto)
102
- add-stack <numbers> Add stack rules (e.g., add-stack 3 4)
103
- remove-stack <numbers> Remove stack rules
104
- status Show installed version, stacks, and hooks
105
- doctor Check installation integrity
106
- version Show version
107
-
108
- Init options:
109
- --auto Non-interactive with defaults
110
- --stack <choices> Stack selection: 1-7, space-separated
111
- --name <name> Project name (default: directory name)
112
- --global Install global rules to ~/.claude/CLAUDE.md
113
- --no-global Skip global rules install
114
-
115
- Stacks:
116
- 1=RTL 2=Embedded 3=Python 4=JS/TS 5=Mobile 6=Fullstack 7=General
117
-
118
- Examples:
119
- npx cc-discipline # Interactive setup
120
- npx cc-discipline init --auto # Non-interactive defaults
121
- npx cc-discipline init --auto --stack "3 4" # Python + JS/TS
122
- npx cc-discipline upgrade # Upgrade to latest
123
- npx cc-discipline status # Check what's installed
124
- npx cc-discipline doctor # Diagnose issues`);
125
- process.exit(0);
126
- default:
127
- console.error(`Unknown command: ${command}`);
128
- console.error("Run 'cc-discipline --help' for usage");
129
- process.exit(1);
130
- }
131
-
132
- // Run the bash script
133
- const unixScript = toUnixPath(script);
134
- const pkgVersion = require(path.join(PKG_DIR, 'package.json')).version;
135
- const env = {
136
- ...process.env,
137
- CC_DISCIPLINE_PKG_DIR: process.platform === 'win32' ? toUnixPath(PKG_DIR) : PKG_DIR,
138
- CC_DISCIPLINE_VERSION: pkgVersion,
139
- };
140
-
141
- const result = spawnSync(bash, [unixScript, ...scriptArgs], {
142
- stdio: 'inherit',
143
- env,
144
- cwd: process.cwd(),
145
- });
146
-
147
- process.exit(result.status || 0);
1
+ #!/usr/bin/env node
2
+ // cc-discipline CLI entry point (cross-platform)
3
+ // Detects platform and spawns bash with correct paths
4
+
5
+ const { execSync, spawnSync } = require('child_process');
6
+ const path = require('path');
7
+ const fs = require('fs');
8
+
9
+ const PKG_DIR = path.resolve(__dirname, '..');
10
+ const args = process.argv.slice(2);
11
+
12
+ // Find bash executable
13
+ function findBash() {
14
+ // Unix: bash is always available
15
+ if (process.platform !== 'win32') return 'bash';
16
+
17
+ // Windows: try common Git Bash locations
18
+ const candidates = [
19
+ 'C:\\Program Files\\Git\\bin\\bash.exe',
20
+ 'C:\\Program Files (x86)\\Git\\bin\\bash.exe',
21
+ process.env.PROGRAMFILES + '\\Git\\bin\\bash.exe',
22
+ ];
23
+
24
+ for (const candidate of candidates) {
25
+ if (fs.existsSync(candidate)) return candidate;
26
+ }
27
+
28
+ // Try PATH
29
+ try {
30
+ execSync('bash --version', { stdio: 'ignore' });
31
+ return 'bash';
32
+ } catch (e) {
33
+ console.error('Error: bash not found. Please install Git for Windows (https://git-scm.com/download/win)');
34
+ console.error('Git Bash is required to run cc-discipline on Windows.');
35
+ process.exit(1);
36
+ }
37
+ }
38
+
39
+ const bash = findBash();
40
+
41
+ // Convert Windows path to Unix-style for bash
42
+ function toUnixPath(p) {
43
+ if (process.platform !== 'win32') return p;
44
+ // C:\Users\foo → /c/Users/foo
45
+ return p.replace(/\\/g, '/').replace(/^([A-Za-z]):/, (_, drive) => '/' + drive.toLowerCase());
46
+ }
47
+
48
+ // Route subcommands
49
+ const command = args[0] || 'init';
50
+ const restArgs = args.slice(1);
51
+
52
+ let script;
53
+ let scriptArgs;
54
+
55
+ switch (command) {
56
+ case 'init':
57
+ script = path.join(PKG_DIR, 'init.sh');
58
+ scriptArgs = restArgs;
59
+ break;
60
+ case 'upgrade':
61
+ script = path.join(PKG_DIR, 'init.sh');
62
+ scriptArgs = ['--auto', ...restArgs];
63
+ break;
64
+ case 'status':
65
+ script = path.join(PKG_DIR, 'lib', 'status.sh');
66
+ scriptArgs = [];
67
+ break;
68
+ case 'doctor':
69
+ script = path.join(PKG_DIR, 'lib', 'doctor.sh');
70
+ scriptArgs = [];
71
+ break;
72
+ case 'add-stack':
73
+ if (restArgs.length === 0) {
74
+ console.log('Usage: cc-discipline add-stack <numbers>');
75
+ console.log(' e.g.: cc-discipline add-stack 3 4');
76
+ process.exit(1);
77
+ }
78
+ script = path.join(PKG_DIR, 'init.sh');
79
+ scriptArgs = ['--stack', restArgs.join(' '), '--no-global'];
80
+ break;
81
+ case 'remove-stack':
82
+ script = path.join(PKG_DIR, 'lib', 'stack-remove.sh');
83
+ scriptArgs = restArgs;
84
+ break;
85
+ case '-v':
86
+ case '--version':
87
+ case 'version':
88
+ const pkg = require(path.join(PKG_DIR, 'package.json'));
89
+ console.log(`cc-discipline v${pkg.version}`);
90
+ process.exit(0);
91
+ case '-h':
92
+ case '--help':
93
+ case 'help':
94
+ const ver = require(path.join(PKG_DIR, 'package.json')).version;
95
+ console.log(`cc-discipline v${ver} — Discipline framework for Claude Code
96
+
97
+ Usage: cc-discipline <command> [options]
98
+
99
+ Commands:
100
+ init [options] Install discipline into current project (default)
101
+ upgrade Upgrade rules/hooks (shortcut for init --auto)
102
+ add-stack <numbers> Add stack rules (e.g., add-stack 3 4)
103
+ remove-stack <numbers> Remove stack rules
104
+ status Show installed version, stacks, and hooks
105
+ doctor Check installation integrity
106
+ version Show version
107
+
108
+ Init options:
109
+ --auto Non-interactive with defaults
110
+ --stack <choices> Stack selection: 1-7, space-separated
111
+ --name <name> Project name (default: directory name)
112
+ --global Install global rules to ~/.claude/CLAUDE.md
113
+ --no-global Skip global rules install
114
+
115
+ Stacks:
116
+ 1=RTL 2=Embedded 3=Python 4=JS/TS 5=Mobile 6=Fullstack 7=General
117
+
118
+ Examples:
119
+ npx cc-discipline # Interactive setup
120
+ npx cc-discipline init --auto # Non-interactive defaults
121
+ npx cc-discipline init --auto --stack "3 4" # Python + JS/TS
122
+ npx cc-discipline upgrade # Upgrade to latest
123
+ npx cc-discipline status # Check what's installed
124
+ npx cc-discipline doctor # Diagnose issues`);
125
+ process.exit(0);
126
+ default:
127
+ console.error(`Unknown command: ${command}`);
128
+ console.error("Run 'cc-discipline --help' for usage");
129
+ process.exit(1);
130
+ }
131
+
132
+ // Run the bash script
133
+ const unixScript = toUnixPath(script);
134
+ const pkgVersion = require(path.join(PKG_DIR, 'package.json')).version;
135
+ const env = {
136
+ ...process.env,
137
+ CC_DISCIPLINE_PKG_DIR: process.platform === 'win32' ? toUnixPath(PKG_DIR) : PKG_DIR,
138
+ CC_DISCIPLINE_VERSION: pkgVersion,
139
+ };
140
+
141
+ const result = spawnSync(bash, [unixScript, ...scriptArgs], {
142
+ stdio: 'inherit',
143
+ env,
144
+ cwd: process.cwd(),
145
+ });
146
+
147
+ process.exit(result.status || 0);
package/init.sh CHANGED
@@ -365,20 +365,13 @@ cp "$SCRIPT_DIR/templates/.claude/agents/investigator.md" .claude/agents/
365
365
 
366
366
  # ─── Install skills ───
367
367
  echo -e "${GREEN}Installing skills...${NC}"
368
- cp -r "$SCRIPT_DIR/templates/.claude/skills/commit" .claude/skills/
369
- cp -r "$SCRIPT_DIR/templates/.claude/skills/self-check" .claude/skills/
370
- cp -r "$SCRIPT_DIR/templates/.claude/skills/evaluate" .claude/skills/
371
- cp -r "$SCRIPT_DIR/templates/.claude/skills/think" .claude/skills/
372
- cp -r "$SCRIPT_DIR/templates/.claude/skills/retro" .claude/skills/
373
- cp -r "$SCRIPT_DIR/templates/.claude/skills/summary" .claude/skills/
374
- cp -r "$SCRIPT_DIR/templates/.claude/skills/investigate" .claude/skills/
375
- echo " ✓ /commit — smart commit (test → update memory → commit)"
376
- echo " ✓ /self-check — periodic discipline check (use with /loop 10m /self-check)"
377
- echo " ✓ /evaluate — evaluate external review/advice against codebase context"
378
- echo " ✓ /think — stop and think before coding (ask → propose → wait)"
379
- echo " ✓ /retro — post-task retrospective (project + framework feedback)"
380
- echo " ✓ /summary — write high-quality compact option before /compact"
381
- echo " ✓ /investigate — multi-agent cross-investigation and proposal review"
368
+ # Install every skill directory under templates/ — no per-skill enumeration,
369
+ # so adding a new skill needs zero changes here.
370
+ for skill_dir in "$SCRIPT_DIR"/templates/.claude/skills/*/; do
371
+ [ -d "$skill_dir" ] || continue
372
+ cp -r "$skill_dir" .claude/skills/
373
+ echo " ✓ /$(basename "$skill_dir")"
374
+ done
382
375
 
383
376
  # ─── Handle CLAUDE.md ───
384
377
  if [ ! -f "CLAUDE.md" ]; then
@@ -534,13 +527,7 @@ if [ "$INSTALL_MODE" = "fresh" ]; then
534
527
  echo -e " ${GREEN}.claude/rules/${NC} ← Auto-injected rules"
535
528
  echo -e " ${GREEN}.claude/hooks/${NC} ← 7 discipline hooks (edit guard, streak breaker, git guard, phase gate, action counter, error remind, session start)"
536
529
  echo -e " ${GREEN}.claude/agents/${NC} ← Reviewer & investigator subagents"
537
- echo -e " ${GREEN}.claude/skills/commit/${NC} /commit smart commit"
538
- echo -e " ${GREEN}.claude/skills/self-check/${NC} ← /self-check periodic discipline check"
539
- echo -e " ${GREEN}.claude/skills/evaluate/${NC} ← /evaluate assess external review advice"
540
- echo -e " ${GREEN}.claude/skills/think/${NC} ← /think stop and think before coding"
541
- echo -e " ${GREEN}.claude/skills/retro/${NC} ← /retro post-task retrospective"
542
- echo -e " ${GREEN}.claude/skills/summary/${NC} ← /summary before compacting"
543
- echo -e " ${GREEN}.claude/skills/investigate/${NC} ← /investigate multi-agent cross-investigation"
530
+ echo -e " ${GREEN}.claude/skills/${NC} Skills (run 'npx cc-discipline status' to list)"
544
531
  echo -e " ${GREEN}.claude/settings.json${NC} ← Hooks configuration"
545
532
  echo -e " ${GREEN}docs/progress.md${NC} ← Progress log (maintained by Claude)"
546
533
  echo -e " ${GREEN}docs/debug-log.md${NC} ← Debug log (maintained by Claude)"
@@ -556,13 +543,7 @@ else
556
543
  echo -e " ${GREEN}.claude/rules/${NC} ← Discipline rules installed/updated"
557
544
  echo -e " ${GREEN}.claude/hooks/${NC} ← Hook scripts installed/updated"
558
545
  echo -e " ${GREEN}.claude/agents/${NC} ← Subagents installed/updated"
559
- echo -e " ${GREEN}.claude/skills/commit/${NC} /commit skill installed/updated"
560
- echo -e " ${GREEN}.claude/skills/self-check/${NC} ← /self-check discipline check installed"
561
- echo -e " ${GREEN}.claude/skills/evaluate/${NC} ← /evaluate external review assessment"
562
- echo -e " ${GREEN}.claude/skills/think/${NC} ← /think stop and think before coding"
563
- echo -e " ${GREEN}.claude/skills/retro/${NC} ← /retro post-task retrospective"
564
- echo -e " ${GREEN}.claude/skills/summary/${NC} ← /summary before compacting"
565
- echo -e " ${GREEN}.claude/skills/investigate/${NC} ← /investigate multi-agent cross-investigation"
546
+ echo -e " ${GREEN}.claude/skills/${NC} Skills installed/updated (run 'npx cc-discipline status' to list)"
566
547
  if [ ! -f "$BACKUP_DIR/settings.json" ] || [ -f ".claude/.cc-discipline-settings-template.json" ]; then
567
548
  echo -e " ${YELLOW}.claude/settings.json${NC} ← See notes above"
568
549
  else
package/lib/doctor.sh CHANGED
@@ -95,13 +95,13 @@ done
95
95
  # 6. Skills
96
96
  echo ""
97
97
  echo "Skills:"
98
- for skill in commit self-check evaluate think retro summary investigate; do
99
- if [ -d ".claude/skills/${skill}" ]; then
100
- ok "/${skill}"
101
- else
102
- warn "Missing /${skill} skill (optional)"
103
- fi
98
+ SKILL_FOUND=0
99
+ for skill_dir in .claude/skills/*/; do
100
+ [ -d "$skill_dir" ] || continue
101
+ ok "/$(basename "$skill_dir")"
102
+ SKILL_FOUND=$((SKILL_FOUND + 1))
104
103
  done
104
+ [ "$SKILL_FOUND" -eq 0 ] && warn "No skills installed (optional)"
105
105
 
106
106
  # 7. jq
107
107
  echo ""
package/lib/status.sh CHANGED
@@ -66,15 +66,12 @@ echo -e "${GREEN}${AGENT_COUNT}/2${NC} (${AGENTS% })"
66
66
  # Skills
67
67
  echo -n "Skills: "
68
68
  SKILLS=""
69
- [ -d ".claude/skills/commit" ] && SKILLS="${SKILLS}/commit "
70
- [ -d ".claude/skills/self-check" ] && SKILLS="${SKILLS}/self-check "
71
- [ -d ".claude/skills/evaluate" ] && SKILLS="${SKILLS}/evaluate "
72
- [ -d ".claude/skills/think" ] && SKILLS="${SKILLS}/think "
73
- [ -d ".claude/skills/retro" ] && SKILLS="${SKILLS}/retro "
74
- [ -d ".claude/skills/summary" ] && SKILLS="${SKILLS}/summary "
75
- [ -d ".claude/skills/investigate" ] && SKILLS="${SKILLS}/investigate "
69
+ for skill_dir in .claude/skills/*/; do
70
+ [ -d "$skill_dir" ] || continue
71
+ SKILLS="${SKILLS}/$(basename "$skill_dir") "
72
+ done
76
73
  SKILL_COUNT=$(echo "$SKILLS" | wc -w | tr -d ' ')
77
- echo -e "${GREEN}${SKILL_COUNT}/7${NC} (${SKILLS% })"
74
+ echo -e "${GREEN}${SKILL_COUNT}${NC} (${SKILLS% })"
78
75
 
79
76
  # Settings
80
77
  echo -n "Settings: "
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-discipline",
3
- "version": "2.10.3",
3
+ "version": "2.12.0",
4
4
  "description": "Discipline framework for Claude Code — rules, hooks, and agents that keep AI on track",
5
5
  "bin": {
6
6
  "cc-discipline": "bin/cli.js"
@@ -3,13 +3,18 @@
3
3
  # Counts action-type tool calls per session, injects self-check every N actions.
4
4
  # PreToolUse on Edit|Write|MultiEdit|Bash|Agent — additionalContext injection.
5
5
 
6
- THRESHOLD=25
7
-
8
6
  INPUT=$(cat)
9
- SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"' 2>/dev/null)
10
- if [ -z "$SESSION_ID" ] || [ "$SESSION_ID" = "null" ]; then
11
- SESSION_ID="unknown"
7
+ # Extract session_id with a grep fallback for when jq is unavailable (e.g.
8
+ # Windows Git Bash). Without this fallback, every session collapsed to the
9
+ # literal "unknown" and shared ONE never-resetting global counter — which
10
+ # permanently disabled the early-action phase check (count never restarted
11
+ # at 1) and made the periodic reflection fire off a global tally. (fixed 2026-06-05)
12
+ if command -v jq &>/dev/null; then
13
+ SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty' 2>/dev/null)
14
+ else
15
+ SESSION_ID=$(echo "$INPUT" | grep -o '"session_id":\s*"[^"]*"' | head -1 | sed 's/"session_id":\s*"//;s/"//')
12
16
  fi
17
+ SESSION_ID="${SESSION_ID:-unknown}"
13
18
 
14
19
  COUNT_DIR="/tmp/cc-discipline-${SESSION_ID}"
15
20
  COUNT_FILE="${COUNT_DIR}/action-count"
@@ -29,14 +34,29 @@ fi
29
34
 
30
35
  # Progress.md staleness check: every 50 actions, check if progress.md was updated recently
31
36
  if [ $((COUNT % 50)) -eq 0 ]; then
32
- CWD=$(echo "$INPUT" | jq -r '.cwd // ""' 2>/dev/null)
37
+ if command -v jq &>/dev/null; then
38
+ CWD=$(echo "$INPUT" | jq -r '.cwd // ""' 2>/dev/null)
39
+ else
40
+ CWD=$(echo "$INPUT" | sed -n -E 's/.*"cwd"[[:space:]]*:[[:space:]]*"([^"]*)".*/\1/p' | head -1 | sed 's/[\][\]/\//g')
41
+ fi
33
42
  PROGRESS_FILE=""
34
43
  [ -f "$CWD/docs/progress.md" ] && PROGRESS_FILE="$CWD/docs/progress.md"
35
44
  [ -z "$PROGRESS_FILE" ] && [ -f "docs/progress.md" ] && PROGRESS_FILE="docs/progress.md"
36
45
  if [ -n "$PROGRESS_FILE" ]; then
37
- # Check if file was modified in the last 30 minutes
46
+ # Check if file was modified in the last 30 minutes.
47
+ # GNU stat (Linux, Windows Git Bash) FIRST, BSD stat (macOS) second —
48
+ # this order matters and the reverse is broken: in GNU stat, `-f` is not
49
+ # BSD's "format" flag, it means "show filesystem status". It therefore
50
+ # SUCCEEDS on Git Bash and prints a multi-line filesystem dump, so the
51
+ # `||` fallback never ran and $((NOW - FILE_MOD)) died with "syntax error
52
+ # in expression". `stat -c` is simply an invalid option on macOS, so it
53
+ # fails cleanly and falls through. The numeric guard below makes the
54
+ # failure mode silent either way. (fixed 2026-07-30)
38
55
  if command -v stat &>/dev/null; then
39
- FILE_MOD=$(stat -f %m "$PROGRESS_FILE" 2>/dev/null || stat -c %Y "$PROGRESS_FILE" 2>/dev/null)
56
+ FILE_MOD=$(stat -c %Y "$PROGRESS_FILE" 2>/dev/null || stat -f %m "$PROGRESS_FILE" 2>/dev/null)
57
+ case "$FILE_MOD" in
58
+ ''|*[!0-9]*) FILE_MOD="" ;;
59
+ esac
40
60
  NOW=$(date +%s)
41
61
  if [ -n "$FILE_MOD" ] && [ $((NOW - FILE_MOD)) -gt 1800 ]; then
42
62
  STALE_MIN=$(( (NOW - FILE_MOD) / 60 ))
@@ -49,10 +69,14 @@ JSONEOF
49
69
  fi
50
70
  fi
51
71
 
52
- if [ $((COUNT % THRESHOLD)) -eq 0 ]; then
53
- cat <<JSONEOF
54
- {"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":"Periodic reflection (#${COUNT} actions): Take a moment to check in. ALIGNMENT: (1) Am I still serving the user's current direction? They may have pivoted — follow their latest intent. (2) Am I making changes the user asked for, and only those? QUALITY: (3) Are all my 'verified' claims backed by actual execution output? If not, correct that now. (4) If executing a plan, does what I delivered match what the step asked for? Check acceptance criteria. (5) Am I maintaining quality, or taking shortcuts to avoid difficulty? If tempted to cut corners, ask the user instead. PROGRESS: (6) Am I progressing or circling? (fixing the same area repeatedly suggests a deeper issue worth stepping back to find) (7) Is docs/progress.md up to date? If auto-compact happened now, could a fresh session resume from it? (8) Did I break a task into subtasks and skip some? If the analysis context is fresh, finishing them now is cheaper than rebuilding later. WHAT'S WORKING: (9) Note one thing going well — a good approach, a clean fix, or effective tool use. (10) Any friction from hooks or rules since last check? Note it for /retro. If any answer is concerning, pause and report to the user before continuing."}}
55
- JSONEOF
56
- fi
72
+ # The 25-action, 10-question "Periodic reflection" block was removed 2026-07-30.
73
+ # Anthropic's Opus 5 guidance: the model verifies its own work without being told
74
+ # to, and "legacy harness scaffolding that adds separate verification steps"
75
+ # causes over-verification — it compounds with the model's own behavior and adds
76
+ # cost with no quality gain. The block also duplicated rules already injected
77
+ # every session (00-core, 03, 06, 07), at ~299 tokens per firing. The remaining
78
+ # checks above (early-action phase check, progress.md staleness) are kept: both
79
+ # came out of the 112-session insights analysis in v2.4.0 and target failure
80
+ # modes we actually measured, not generic self-review.
57
81
 
58
82
  exit 0
@@ -3,17 +3,46 @@
3
3
  # PreToolUse on Bash — blocks git checkout/restore/reset --hard/clean -f without confirmation.
4
4
 
5
5
  INPUT=$(cat)
6
- TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""' 2>/dev/null)
7
6
 
8
- if [ "$TOOL_NAME" != "Bash" ]; then
9
- exit 0
7
+ # Extract tool_name and command. MUST have a jq-or-grep fallback: jq is absent
8
+ # on Windows Git Bash, and the previous jq-only version left TOOL_NAME empty
9
+ # there, so the `!= "Bash"` check below exited 0 immediately and EVERY guard in
10
+ # this file was dead code. Destructive-git protection never ran on Windows.
11
+ # (fixed 2026-07-30 — verified with jq absent, see docs/progress.md)
12
+ if command -v jq &>/dev/null; then
13
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""' 2>/dev/null)
14
+ CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""' 2>/dev/null)
15
+ else
16
+ TOOL_NAME=$(echo "$INPUT" | sed -n -E 's/.*"tool_name"[[:space:]]*:[[:space:]]*"([^"]*)".*/\1/p' | head -1)
17
+ # The [{,] anchor makes this pick the real "command" field rather than an
18
+ # escaped \"command\" nested inside it (happens when piping JSON to a hook).
19
+ # Unescape \n and \" so line-spanning commands still match the patterns below.
20
+ CMD=$(echo "$INPUT" | sed -n -E 's/.*[{,][[:space:]]*"command"[[:space:]]*:[[:space:]]*"(.*)".*/\1/p' \
21
+ | sed 's/[\]n/ /g; s/[\]"/"/g')
22
+ # If the command could not be isolated, scan the whole payload instead of
23
+ # giving up. For a destructive-git guard the failure directions are not
24
+ # symmetric: a spurious confirmation prompt costs one turn, a miss costs the
25
+ # user's uncommitted work. Fail loud.
26
+ [ -z "$CMD" ] && CMD="$INPUT"
10
27
  fi
11
28
 
12
- CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""' 2>/dev/null)
29
+ # Only gate on tool_name when it actually resolved — an unresolvable tool_name
30
+ # must not silently disable the guard (that was the original bug).
31
+ if [ -n "$TOOL_NAME" ] && [ "$TOOL_NAME" != "Bash" ]; then
32
+ exit 0
33
+ fi
13
34
 
14
35
  # Normalize: collapse whitespace, trim
15
36
  CMD_NORM=$(echo "$CMD" | tr '\n' ' ' | sed 's/ */ /g')
16
37
 
38
+ # This guard matches command TEXT, so a command that merely quotes a destructive
39
+ # command trips it. The one recurring legitimate case is feeding JSON into one of
40
+ # these hooks to test them (the workflow documented in CLAUDE.md). Exempt it —
41
+ # piping a payload to a hook script never touches the working tree.
42
+ if echo "$CMD_NORM" | grep -qE 'hooks[/\\][a-z-]+\.sh'; then
43
+ exit 0
44
+ fi
45
+
17
46
  BLOCKED=""
18
47
  SUGGESTION=""
19
48
 
@@ -29,11 +29,25 @@ if command -v jq &>/dev/null; then
29
29
  if [ -z "$OUTPUT" ]; then
30
30
  OUTPUT=$(echo "$RAW_INPUT" | jq -r '.tool_response.output // empty' 2>/dev/null)
31
31
  fi
32
+ else
33
+ # jq is absent on Windows Git Bash. Extract the same two fields with sed.
34
+ # Unescape \n so the line-anchored patterns below (^ERROR, ^Traceback, ...)
35
+ # still work.
36
+ TOOL_NAME=$(echo "$RAW_INPUT" | sed -n -E 's/.*"tool_name"[[:space:]]*:[[:space:]]*"([^"]*)".*/\1/p' | head -1)
37
+ OUTPUT=$(echo "$RAW_INPUT" | sed -n -E 's/.*[{,][[:space:]]*"(error|output)"[[:space:]]*:[[:space:]]*"(.*)".*/\2/p' | head -1 \
38
+ | sed 's/[\]n/\
39
+ /g; s/[\]"/"/g')
32
40
  fi
33
41
 
34
- # Fallback if jq unavailable
42
+ # NEVER match against the raw JSON envelope. The command text lives in there
43
+ # too, so `grep -rn "permission denied" logs/` used to flag itself — and with
44
+ # tool_name unresolved the non-Bash early-exit below never fired either, so this
45
+ # hook lectured on clean Read calls. Both were the old `OUTPUT="$RAW_INPUT"`
46
+ # fallback. If the tool's own output can't be isolated, stay silent: a false
47
+ # positive here costs a turn and misleads, so silence is the safe direction.
48
+ # (fixed 2026-07-30 — verified with jq absent, see docs/progress.md)
35
49
  if [ -z "$OUTPUT" ]; then
36
- OUTPUT="$RAW_INPUT"
50
+ exit 0
37
51
  fi
38
52
 
39
53
  # --- Early exits for known non-error situations ---
@@ -97,14 +111,14 @@ fi
97
111
  # --- Emit reminder if error detected ---
98
112
 
99
113
  if [ "$HAS_ERROR" = true ]; then
100
- REMINDER="Error encountered debugging checklist: 1. Resist modifying code immediately 2. Fully understand the error message 3. List >=3 possible causes — label them hypotheses, not root cause 4. Record in docs/debug-log.md 5. Eliminate >=2 hypotheses with evidence before identifying root cause 6. Only then fix. Important: after seeing one error, the first explanation is a hypothesis, not a conclusion. Say 'possible cause' until you have elimination evidence."
114
+ # Keep this to ONE line. The hypothesis discipline itself lives in the
115
+ # always-injected rules (00-core §6, 01-debugging) — re-injecting the full
116
+ # ">=3 causes / record in debug-log / eliminate >=2" ritual on every error
117
+ # duplicated them and pushed toward over-verification. Trimmed 2026-07-30.
118
+ REMINDER="Error detected: the first explanation that comes to mind is a hypothesis, not the root cause. Read the actual error before changing code."
101
119
 
102
120
  if [ "$ERROR_TYPE" = "test" ]; then
103
- REMINDER="$REMINDER Note: Test failure — before changing the test, first determine if it's a code bug or an outdated test."
104
- fi
105
-
106
- if [ "$ERROR_TYPE" = "crash" ]; then
107
- REMINDER="$REMINDER Note: Crash/segfault — may involve memory issues."
121
+ REMINDER="$REMINDER Test failure — decide whether the code is wrong or the test is outdated before touching either."
108
122
  fi
109
123
 
110
124
  echo "$REMINDER" >&2
@@ -51,34 +51,54 @@ if [ -n "$DEBUG_LOG" ]; then
51
51
  CONFIRMED=$(echo "$CLEAN" | grep -c '| confirmed |' 2>/dev/null) || CONFIRMED=0
52
52
 
53
53
  if [ "$PENDING" -gt "$CONFIRMED" ] 2>/dev/null; then
54
- cat >&2 <<EOF
55
- docs/debug-log.md has $((PENDING - CONFIRMED)) unverified hypotheses.
56
- Please complete the debugging process (verify or eliminate hypotheses) before editing source code.
57
- If you have confirmed the root cause, update debug-log.md first.
58
- EOF
59
- exit 2
54
+ # Downgraded from a hard block (exit 2) to a note, 2026-07-30.
55
+ # As a hard block this combined with 01-debugging Phase 2 to form a trap:
56
+ # follow the rule, write 3 hypotheses into debug-log.md, and you were then
57
+ # forbidden from editing source until 3 were marked confirmed — including
58
+ # for unrelated planned work. It is the "legacy harness scaffolding that
59
+ # adds separate verification steps" Anthropic's Opus 5 guidance calls out.
60
+ # The hypothesis discipline itself stays in rules 00-core §6 / 01-debugging
61
+ # (56 measured wrong-approach incidents); only the enforcement is relaxed.
62
+ cat <<JSONEOF
63
+ {"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":"DEBUG-LOG NOTE: docs/debug-log.md has $((PENDING - CONFIRMED)) hypotheses still marked pending. If this edit is the fix for one of them, update its status first so the log stays truthful. If this edit is unrelated work, carry on."}}
64
+ JSONEOF
65
+ exit 0
60
66
  fi
61
67
  fi
62
68
 
63
69
  # ─── Large diff warning ───
64
- HAS_JQ=false
65
- command -v jq &>/dev/null && HAS_JQ=true
66
-
67
- if [ "$HAS_JQ" = true ]; then
68
- NEW_STRING=$(echo "$INPUT" | jq -r '.tool_input.new_string // ""')
69
- OLD_STRING=$(echo "$INPUT" | jq -r '.tool_input.old_string // ""')
70
- NEW_LINES=$(echo "$NEW_STRING" | wc -l | tr -d ' ')
71
- OLD_LINES=$(echo "$OLD_STRING" | wc -l | tr -d ' ')
72
- DIFF_LINES=$((NEW_LINES > OLD_LINES ? NEW_LINES : OLD_LINES))
73
- if [ "$DIFF_LINES" -gt 200 ]; then
74
- echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"additionalContext\":\"LARGE EDIT NOTE: This edit involves ${DIFF_LINES} lines. Consider: is this the minimal change needed? Could it be broken into smaller, more focused edits?\"}}"
75
- exit 0
76
- fi
70
+ # Counts `content` too, not just new_string/old_string: Write sends `content`, so
71
+ # the previous version silently never fired on a large Write. Has a no-jq path so
72
+ # the "every jq read has an else" invariant holds repo-wide (see CLAUDE.md).
73
+ if command -v jq &>/dev/null; then
74
+ NEW_LINES=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // ""' | wc -l | tr -d ' ')
75
+ OLD_LINES=$(echo "$INPUT" | jq -r '.tool_input.old_string // ""' | wc -l | tr -d ' ')
76
+ else
77
+ # Newlines arrive as the two-character sequence \n inside the JSON string.
78
+ count_lines() {
79
+ c=$(echo "$INPUT" | sed -n -E "s/.*[{,][[:space:]]*\"$1\"[[:space:]]*:[[:space:]]*\"(.*)\".*/\1/p" \
80
+ | grep -o '[\]n' | wc -l | tr -d ' ')
81
+ echo $((c + 1))
82
+ }
83
+ NEW_LINES=$(count_lines new_string)
84
+ [ "$NEW_LINES" -le 1 ] && NEW_LINES=$(count_lines content)
85
+ OLD_LINES=$(count_lines old_string)
86
+ fi
87
+ DIFF_LINES=$((NEW_LINES > OLD_LINES ? NEW_LINES : OLD_LINES))
88
+ if [ "$DIFF_LINES" -gt 200 ]; then
89
+ echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"additionalContext\":\"LARGE EDIT NOTE: This edit involves ${DIFF_LINES} lines. Consider: is this the minimal change needed? Could it be broken into smaller, more focused edits?\"}}"
90
+ exit 0
77
91
  fi
78
92
 
79
93
  # ─── New tool/script detection ───
80
94
  # When creating a script file via Write, remind to register in CLAUDE.md
81
- TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
95
+ # jq-or-sed: the previous jq-only line left TOOL_NAME empty on Windows Git Bash,
96
+ # so this detection never fired there. (fixed 2026-07-30)
97
+ if command -v jq &>/dev/null; then
98
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)
99
+ else
100
+ TOOL_NAME=$(echo "$INPUT" | sed -n -E 's/.*"tool_name"[[:space:]]*:[[:space:]]*"([^"]*)".*/\1/p' | head -1)
101
+ fi
82
102
  if [ "$TOOL_NAME" = "Write" ] && echo "$BASENAME" | grep -qiE "\.(sh|py|js|ts|rb|pl)$"; then
83
103
  # Check if file already exists (new file = tool creation)
84
104
  FULL_PATH="$FILE_PATH"
@@ -90,11 +110,9 @@ JSONEOF
90
110
  fi
91
111
  fi
92
112
 
93
- # ─── Bug-fix sanity check ───
94
- # Inject a lightweight reminder on source file edits:
95
- # "If this is a bug fix, have you eliminated alternative hypotheses?"
96
- # Uses additionalContext (non-blocking) so it doesn't slow down normal edits.
97
- cat <<JSONEOF
98
- {"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":"If this edit is a bug fix: have you listed >=3 possible causes and eliminated >=2 with evidence? Thorough elimination before fixing prevents wasted cycles. Use 'possible cause' until elimination evidence confirms the root cause."}}
99
- JSONEOF
113
+ # Normal source edit: stay silent (exit 0, no output).
114
+ # The "list >=3 causes before a bug fix" guidance already lives in the
115
+ # always-injected rules (00-core §6, 01-debugging), the hard debug-log block
116
+ # above, and post-error-remind. Re-injecting it on EVERY edit fired even on
117
+ # planned feature/registration work — redundant noise, removed 2026-06-05.
100
118
  exit 0
@@ -4,9 +4,15 @@
4
4
  # stdout → context (Claude can see and act on it)
5
5
  # Fires on: startup, resume, clear, compact
6
6
 
7
- # Reset action counter for this session
7
+ # Reset action counter for this session.
8
+ # jq-or-sed: jq is absent on Windows Git Bash, and the previous jq-only version
9
+ # left SESSION_ID empty there, so the reset below never ran. (fixed 2026-07-30)
8
10
  INPUT=$(cat)
9
- SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"' 2>/dev/null)
11
+ if command -v jq &>/dev/null; then
12
+ SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"' 2>/dev/null)
13
+ else
14
+ SESSION_ID=$(echo "$INPUT" | sed -n -E 's/.*"session_id"[[:space:]]*:[[:space:]]*"([^"]*)".*/\1/p' | head -1)
15
+ fi
10
16
  if [ -n "$SESSION_ID" ] && [ "$SESSION_ID" != "unknown" ]; then
11
17
  rm -f "/tmp/cc-discipline-${SESSION_ID}/action-count"
12
18
  fi
@@ -31,14 +37,14 @@ Verify project status by reading files or asking — don't assume beyond what is
31
37
  EOF
32
38
  fi
33
39
 
40
+ # Only the skill pointer is injected here. The four rule restatements that used
41
+ # to follow it (pre-edit checks, 3-failure rule, confirm-before-implementing,
42
+ # verify project state) were removed 2026-07-30: all four are already in the
43
+ # rules injected every session (02, 00-core §4, 05, 07 §4a), so repeating them
44
+ # here bought nothing. Skills are the one thing rules don't announce.
34
45
  cat <<'EOF'
35
46
 
36
- Reminders:
37
- - /self-check available for periodic monitoring. For complex tasks: /loop 10m /self-check
38
- - Before editing: root cause identified? scope respected? change recorded?
39
- - 3 consecutive failures → pause and regroup with the user
40
- - Confirm the approach with the user before starting implementation
41
- - Verify project state (phase, status, dependencies) by reading files or asking
47
+ /self-check is available for periodic monitoring — for long tasks: /loop 10m /self-check
42
48
  EOF
43
49
 
44
50
  exit 0
@@ -4,8 +4,8 @@
4
4
  # Pauses for reflection when the pattern suggests circling
5
5
  #
6
6
  # Design:
7
- # - Source code files (.java/.ts/.py/.go etc): warn at 3, stop at 5
8
- # - Config/doc files (.md/.json/.yaml/.xml etc): warn at 6, stop at 10
7
+ # - Source code files (.java/.ts/.py/.go etc): warn at 6, stop at 10
8
+ # - Config/doc files (.md/.json/.yaml/.xml etc): warn at 10, stop at 16
9
9
  # - docs/ directory: exempt (always allow)
10
10
  #
11
11
  # Exit 0 + no output = silent allow
@@ -41,13 +41,20 @@ fi
41
41
  BASENAME=$(basename "$FILE_PATH")
42
42
 
43
43
  if echo "$BASENAME" | grep -qiE "\.(md|json|yaml|yml|toml|xml|cfg|ini|properties|gitignore)$"; then
44
- # Config/doc files: higher thresholds (many independent sections to fill)
44
+ # Config/doc files: higher thresholds (many independent sections to fill).
45
+ # Raised 6/10 -> 10/16 on 2026-07-30 to keep this tier above the source tier
46
+ # after source was raised to 6/10.
47
+ WARN_THRESHOLD=10
48
+ STOP_THRESHOLD=16
49
+ else
50
+ # Source code files: repeated edits may indicate circling.
51
+ # Raised from 3/5 to 6/10 on 2026-07-30. Opus 5 completes multi-file features
52
+ # and larger refactors end-to-end rather than in one pass per file, so 5 edits
53
+ # to one source file is now common in legitimate feature work — the old
54
+ # thresholds fired on progress, not on circling. Circling that is actually
55
+ # worth interrupting still shows up well before 10.
45
56
  WARN_THRESHOLD=6
46
57
  STOP_THRESHOLD=10
47
- else
48
- # Source code files: strict thresholds (repeated edits may indicate circling)
49
- WARN_THRESHOLD=3
50
- STOP_THRESHOLD=5
51
58
  fi
52
59
 
53
60
  # Counter logic
@@ -5,7 +5,7 @@ description: "Core working principles — auto-injected before all operations"
5
5
 
6
6
  ## Core Principles
7
7
 
8
- 1. **Understand before acting** — Before modifying any file, state: what you're changing, why, and the expected impact
8
+ 1. **Understand before acting** — Know what you're changing, why, and what it affects before you edit. State the reasoning when it isn't evident from the change itself; don't narrate routine edits
9
9
  2. **Don't lock onto the first explanation** — After finding a suspected cause, list >=2 alternative hypotheses before acting
10
10
  3. **Minimal change, minimal complexity** — No large-scale refactors unless explicitly requested. When proposing solutions, prefer the simplest approach that meets requirements. If a lightweight solution exists, choose it over a heavyweight one unless the user asks for more.
11
11
  4. **3 consecutive failures → pause and regroup** — Report current state, attempted solutions, and points of confusion. Fresh perspective from the user often unblocks what repetition cannot.
@@ -9,14 +9,3 @@ Before modifying this file, confirm each of the following:
9
9
  - [ ] **I know how to verify after the change** — Per 07-integrity: run it, paste output, or mark unverified
10
10
 
11
11
  If any item is uncertain, resolving it first will make the edit smoother and avoid rework.
12
-
13
- ## Post-Edit Checklist
14
-
15
- After writing or modifying code, before running it:
16
-
17
- - [ ] **Syntax check** — Does the code compile/parse without errors? (e.g., `python -m py_compile`, `tsc --noEmit`, `go vet`)
18
- - [ ] **Obvious errors** — No undefined variables, no wrong function signatures, no missing imports?
19
- - [ ] **API correctness** — Are function/method calls using the right arguments, types, and return values? If unsure, read the API docs or source first.
20
- - [ ] **Edge cases in your changes** — Did you handle empty inputs, None/null, off-by-one, etc.?
21
-
22
- A 10-second syntax check catches errors that would otherwise cost 10-minute debug cycles. Always worth it.
@@ -5,13 +5,12 @@
5
5
  - During debugging → update `docs/debug-log.md` (hypotheses, evidence, elimination results)
6
6
  - When making architectural decisions → record the decision and reasoning in progress.md
7
7
 
8
- ### Parallel Execution
9
- - **Don't do everything single-threaded.** When a task has independent parts, use subagents or tasks to work on them in parallel. Examples:
10
- - Investigating 3 debug hypotheses spawn 3 agents, each verifies one
11
- - Researching multiple files/modules one agent per area, summarize results
12
- - Independent subtasks in a plan parallel agents for non-dependent steps
13
- - **Keep the main conversation for decisions, not research.** Subagents do the reading and exploring; the main conversation synthesizes and decides.
14
- - **When to parallelize:** if two subtasks don't depend on each other's output, they should run concurrently, not sequentially.
8
+ ### Delegation
9
+ - **Delegate for isolation and genuine parallelism — not by default.** A subagent earns its cost when the work is sizeable, genuinely independent, and would otherwise flood the main conversation: a wide multi-file investigation, one agent per area of a broad survey.
10
+ - **Work directly** on single-file edits, short sequences of tool calls, and anything where you need to carry context across steps. If you can finish it in a handful of tool calls, don't delegate it.
11
+ - **Never delegate verification.** Don't spawn agents to double-check or re-verify your own work.
12
+ - **Keep spawn counts low.** If one subagent can do the job, use one rather than several.
13
+ - **Keep the main conversation for decisions.** When you do delegate research, the subagent reads and reports; the main conversation synthesizes and decides.
15
14
 
16
15
  ### Compact Strategy
17
16
  - Avoid proactively suggesting compacting or warning about "context running low." The system auto-compacts when context hits 0% — there is no advance warning, and you cannot see the percentage. With 200K-1M context, most sessions never hit the limit. The urge to say "this session is getting long" is understandable in a long session, but it's not based on information you have access to — the system will handle it. Focus on the work.
@@ -77,16 +77,3 @@ When you discover wrong information in memory, docs, or prior output:
77
77
  1. Correct it now, not "next time"
78
78
  2. Note the correction and why, to prevent recurrence
79
79
  3. If wrong information was already sent externally, alert the user
80
-
81
- ---
82
-
83
- ## Pre-action Checklist
84
-
85
- Before any significant action, verify:
86
-
87
- - [ ] Are my assumptions verified, or inferred from names/context?
88
- - [ ] Are my "verified" claims actually backed by execution output?
89
- - [ ] Am I quoting tool output verbatim, or have I altered it?
90
- - [ ] Is the external information I'm referencing current?
91
- - [ ] Does this content go external? Has the user reviewed it?
92
- - [ ] Is there anything I wrote confidently but am actually unsure about?
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: finish
3
+ description: Drive a task to completion with the quality bar — solid, comprehensive, fully tested; "not run" never counts as done. Use when you're ready to execute to the end (standalone, or as the handoff after /think approval).
4
+ ---
5
+
6
+ You are in **finish mode**: the task is understood and approved — now drive it to completion without stopping early. This is the opposite stance from /think (which aligns and waits). Here you execute, persist, and hold a high quality bar.
7
+
8
+ ## Step 1: Lock the completion conditions
9
+
10
+ State explicitly — in one short block — what "done" means for this task:
11
+ - The concrete deliverables (what must exist / work when finished)
12
+ - The quality bar below, applied to each
13
+ - Anything you'll treat as "blocked, must report" rather than silently skip
14
+
15
+ If invoked standalone (no prior /think) and the scope is genuinely unclear, ask ONE tight round of questions, then commit. Don't turn finish mode into a planning session — the point is to execute.
16
+
17
+ ## Step 2: The quality bar (definition of done)
18
+
19
+ Hold all of these. They are the standard, not aspirations:
20
+
21
+ - **扎实 (solid)** — Fix the root cause, not symptoms. Handle edge cases (empty / null / error paths). No TODO, stub, or workaround left standing in as "the solution."
22
+ - **全面 (comprehensive)** — Cover the full scope, not just the happy path. Update related call sites, docs, and `docs/progress.md`. Don't leave half the task for "later."
23
+ - **完备测试 (fully tested)** — Write tests AND run them. Per 07-integrity §2, "verified" requires actual execution output — paste the command and result. Untested code is not done.
24
+ - **诚实 (honest)** — If something is blocked by an external resource, mark it "⚠️ code ready, verification pending: [reason]" — never ✅ it. Distinguish done from blocked.
25
+
26
+ ## Step 3: Drive
27
+
28
+ Keep going until the completion conditions are met. While driving:
29
+ - Make reasonable decisions and keep moving — note them, don't stop to ask about trivia.
30
+ - The discipline rules still apply: 3 consecutive failures → pause and regroup; scope changes → re-align; anything irreversible or outward-facing → confirm first.
31
+ - Don't declare partial success to exit early. If you feel the urge to stop before the bar is met, that urge is the signal to push through or report the specific blocker — not to lower the bar.
32
+
33
+ ## Step 4: Report on completion
34
+
35
+ When the completion conditions are met, give a short close-out:
36
+
37
+ ```
38
+ FINISH — [task]
39
+ Done: [each deliverable + how it was verified — paste key command output]
40
+ Quality bar: 扎实 [✓/note] · 全面 [✓/note] · 完备测试 [✓/note]
41
+ Blocked (if any): [item — why, what's needed]
42
+ ```
43
+
44
+ If any part is blocked rather than done, say so plainly. A truthful "90% done, X blocked on Y" beats a false ✅.
@@ -3,11 +3,11 @@ name: retro
3
3
  description: Find friction, remove friction. Quick post-task review that makes this project's workflow smoother and feeds improvements back to cc-discipline.
4
4
  ---
5
5
 
6
- Find friction. Remove friction. That's it.
6
+ Find friction. Remove friction. Record what the rules actually caught.
7
7
 
8
8
  ## What to do
9
9
 
10
- Quickly scan what just happened — `git log --oneline -10` and any hook triggers you remember. Then output **only friction and insights**, in this format:
10
+ Quickly scan what just happened — `git log --oneline -10` and any hook triggers you remember. Then output in this format:
11
11
 
12
12
  ```
13
13
  RETRO — [date]
@@ -15,6 +15,9 @@ RETRO — [date]
15
15
  Friction:
16
16
  - [what got in the way] → fix: [specific actionable change]
17
17
 
18
+ Saves:
19
+ - [rule/hook] caught [the specific real problem] — without it: [what would have shipped]
20
+
18
21
  Insights:
19
22
  - [something learned that should survive this session]
20
23
 
@@ -23,18 +26,24 @@ Framework:
23
26
  ```
24
27
 
25
28
  Rules:
26
- - **Only friction** — Don't list what went well. Smooth things don't need attention.
27
- - **Only actionable** — Every friction item must have a "→ fix:" with a concrete change (adjust a threshold, add to CLAUDE.md, update memory, exempt a path).
29
+ - **Only actionable friction** — Every friction item must have a "→ fix:" with a concrete change (adjust a threshold, add to CLAUDE.md, update memory, exempt a path).
28
30
  - **Only new** — Don't repeat friction that's already been addressed or recorded in memory.
29
31
  - **Be specific** — "streak-breaker was annoying" is not useful. "streak-breaker triggered 3x on config.yaml during template fill → fix: add config.yaml to docs/ exempt path, or raise config threshold to 10" is useful.
30
32
  - **Framework items are rare** — Most friction is project-specific. Only flag framework issues if the same problem would hit other projects too.
31
- - **Keep it short** — 3-5 items max. If you can't find friction, say "no friction found" and move on. An empty retro is a good sign.
33
+ - **Keep it short** — 3-5 items per section max. If you can't find friction, say "no friction found" and move on. An empty friction list is a good sign.
34
+
35
+ Saves — read this before filling that section in:
36
+ - **A save is not "what went well."** Log a save only when a rule or hook *changed the outcome*: it caught a real mistake, blocked a real loss, or stopped a wrong turn that was already in motion. "The rules kept me disciplined" is not a save. "git-guard blocked `git reset --hard` while 40 min of uncommitted work was in the tree" is.
37
+ - **"no saves" is a real answer, and it is data.** Write it plainly rather than manufacturing one.
38
+ - **Why this section exists:** friction is visible and saves are invisible — a rule that works silently suppresses the very failure it was written for, so the only naturally measurable signal is its cost. With cost-only data every rule eventually looks like pure overhead and gets cut, including the ones that are still load-bearing. The save log is what makes it possible to judge, later and with evidence, whether a given rule still earns its place.
32
39
 
33
40
  ## After output
34
41
 
35
- Present the items. User decides:
42
+ Append both Friction and Saves to a `## Rule Ledger` section in `docs/progress.md` (create the section at the end of the file if it isn't there yet), one dated line each. This accumulates the record across sessions — a single retro proves nothing, twenty of them decide which rules stay.
43
+
44
+ Then present the items. User decides:
36
45
  - "fix it" → apply the changes
37
46
  - "remember it" → write to memory via /commit
38
47
  - "skip" → move on
39
48
 
40
- Do not auto-apply. Do not pad. Do not turn this into a report.
49
+ Do not auto-apply fixes. Do not pad. Do not turn this into a report. (Appending to the ledger is not a fix — do that without asking.)
@@ -2,6 +2,7 @@
2
2
  name: self-check
3
3
  description: Periodic self-check — reflect on alignment, progress, and quality. Use with /loop for continuous monitoring.
4
4
  disable-model-invocation: true
5
+ disallowed-tools: AskUserQuestion
5
6
  ---
6
7
 
7
8
  Pause and honestly answer every question below.
@@ -59,7 +60,7 @@ Pause and honestly answer every question below.
59
60
  - **Gotchas** — what went wrong or was surprising
60
61
  - **Verification** — how it was confirmed working (test output, manual check)
61
62
 
62
- If any of the above are stale or incomplete: **update docs/progress.md now before continuing.** This takes 2 minutes and saves hours of re-discovery after compact.
63
+ If any of the above are stale or incomplete: **update docs/progress.md now, automatically — don't ask for permission.** Keeping progress.md current is always-correct maintenance, not a decision that needs sign-off. Just do it, then note "updated now" in the status line. This takes 2 minutes and saves hours of re-discovery after compact.
63
64
 
64
65
  ## 6. Am I using the project's scaffolding?
65
66
 
@@ -105,7 +106,7 @@ Going well: [one thing]
105
106
  Issues found: [list, or "none"]
106
107
  ```
107
108
 
108
- If any issues were found, pause and report to the user before continuing.
109
+ If any issues were found, pause and report to the user before continuing. (Routine progress.md updates from §5 don't count as "issues" — you already made them silently; just report "updated now". Reserve the pause for alignment, rigor, or scope problems that genuinely need the user.)
109
110
 
110
111
  ## Reminder
111
112
 
@@ -73,7 +73,7 @@ Rules:
73
73
  - Flag risks or unknowns you've spotted
74
74
  - **Every step must have acceptance criteria** — "done when" must be observable and verifiable, not vague. Bad: "done when refactored". Good: "done when 3 methods extracted, each ≤20 lines, all tests pass"
75
75
  - **Simplicity bias** — If a simple approach meets all requirements, list it first and recommend it. Do NOT lead with complex/elegant solutions unless the user signals they want that. "Simple but sufficient" beats "powerful but overkill".
76
- - **Mark parallel opportunities** — In the steps, annotate which can run concurrently (via subagents/tasks) and which have dependencies. Don't default to sequential when parallel is possible.
76
+ - **Note real dependencies** — Mark which steps depend on which. Don't annotate steps for parallel delegation by default; see 03-context-mgmt "Delegation" for when a subagent is actually warranted.
77
77
 
78
78
  ## Step 4: Self-review
79
79
 
@@ -106,3 +106,12 @@ When the user confirms an approach, transition to implementation. You now have:
106
106
  - Known risks flagged upfront
107
107
 
108
108
  Carry these forward. If scope changes during implementation, pause and re-align rather than silently expanding.
109
+
110
+ ## Downstream: what the user appended
111
+
112
+ The user may steer what happens after alignment by appending a directive to the invocation:
113
+
114
+ - **`/think and plan`** — after alignment, produce the plan (in plan mode, present it for approval via ExitPlanMode).
115
+ - **`/think and finish`** (or `and goal`) — after the user approves the approach, transition into **/finish**: drive to completion with its quality bar (solid, comprehensive, fully tested). Do NOT start before approval — 05-phase-discipline still applies; "and finish" declares the post-approval execution stance, it does not bypass the gate.
116
+
117
+ If no directive was appended, stop and wait as usual (Step 5).
@@ -70,3 +70,25 @@
70
70
  | # | Decision | Reason | Impact Scope | Date |
71
71
  |---|----------|--------|-------------|------|
72
72
  | | | | | |
73
+
74
+ ---
75
+
76
+ ## Rule Ledger
77
+
78
+ <!--
79
+ Appended by /retro. Two columns of evidence about the discipline rules themselves:
80
+
81
+ - SAVE — a rule or hook changed the outcome: caught a real mistake, blocked a
82
+ real loss, stopped a wrong turn already in motion.
83
+ - FRICTION — a rule or hook got in the way and cost time for no benefit.
84
+
85
+ Why keep this: friction is visible, saves are invisible. A rule that works
86
+ silently suppresses the very failure it was written for, so cost is the only
87
+ signal that shows up on its own. Judging rules on cost alone eventually cuts the
88
+ load-bearing ones. This ledger is the counterweight — with enough dated entries,
89
+ "should this rule stay?" becomes a lookup instead of a guess.
90
+ -->
91
+
92
+ | Date | Kind | Rule / Hook | What happened |
93
+ |------|------|-------------|---------------|
94
+ | | | | |