cc-discipline 2.12.3 → 2.13.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/README.md +19 -12
- package/README.zh-CN.md +16 -13
- package/init.sh +100 -9
- package/package.json +1 -1
- package/templates/.claude/hooks/pre-edit-guard.sh +8 -19
- package/templates/.claude/skills/coplan/SKILL.md +45 -0
- package/templates/.claude/skills/self-check/SKILL.md +12 -2
- package/templates/.claude/skills/think/SKILL.md +1 -2
- package/templates/docs/progress.md +1 -1
- package/templates/.claude/skills/finish/SKILL.md +0 -44
- package/templates/.claude/skills/retro/SKILL.md +0 -49
package/README.md
CHANGED
|
@@ -17,9 +17,11 @@ Markdown rules help, but Claude can ignore them. **Hooks can't be ignored** —
|
|
|
17
17
|
|
|
18
18
|
### Hooks (the enforcer)
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
Seven shell scripts run automatically before and after Claude's actions. Two of them can stop an operation outright — `git-guard.sh` on destructive git commands, and `streak-breaker.sh` once a file passes its edit ceiling. The rest inject context rather than block. The three below are the ones worth seeing in action:
|
|
21
21
|
|
|
22
|
-
**`streak-breaker.sh`** — Tracks per-file edit counts.
|
|
22
|
+
**`streak-breaker.sh`** — Tracks per-file edit counts. Source files warn at 6 and **hard-block at 10**; config and docs warn at 10 and block at 16. Confirming that a run of edits is planned doubles both thresholds for that file. Forces Claude to stop and find the root cause instead of endlessly patching.
|
|
23
|
+
|
|
24
|
+
> Raised from 3/5 on 2026-07-30. Newer models complete multi-file features end to end rather than one pass per file, so five edits to one source file is now common in legitimate work — the old thresholds fired on progress, not on circling.
|
|
23
25
|
|
|
24
26
|
Here's what it looks like in practice:
|
|
25
27
|
|
|
@@ -34,25 +36,26 @@ Claude: The regex wasn't enough, let me also add length check.
|
|
|
34
36
|
|
|
35
37
|
Claude: That broke the existing tests, let me adjust.
|
|
36
38
|
[Edit src/auth.py — fix test compatibility]
|
|
37
|
-
⚠️ EDIT NOTE: File src/auth.py has been edited
|
|
39
|
+
⚠️ EDIT NOTE: File src/auth.py has been edited 6 times.
|
|
38
40
|
Quick check: are these edits building toward a goal,
|
|
39
41
|
or fixing previous edits?
|
|
40
42
|
|
|
41
43
|
Claude: Let me try a different approach to the validation.
|
|
42
44
|
[Edit src/auth.py — rewrite validation layer]
|
|
45
|
+
[… three more edits …]
|
|
43
46
|
|
|
44
47
|
Claude: Almost there, one more adjustment...
|
|
45
48
|
[Edit src/auth.py — blocked]
|
|
46
49
|
|
|
47
50
|
🛑 EDIT CHECKPOINT
|
|
48
|
-
File src/auth.py has been edited
|
|
51
|
+
File src/auth.py has been edited 10 times this session.
|
|
49
52
|
Reflect:
|
|
50
53
|
1. Are these edits progressing toward a goal,
|
|
51
54
|
or fixing previous edits?
|
|
52
55
|
2. If fixing previous edits: pause and look
|
|
53
56
|
for the root cause.
|
|
54
57
|
|
|
55
|
-
Claude: You're right. Looking at all
|
|
58
|
+
Claude: You're right. Looking at all 10 edits, I was patching
|
|
56
59
|
symptoms. The real issue is that the User model's
|
|
57
60
|
validate() method is called before sanitization.
|
|
58
61
|
Let me fix it at the source instead.
|
|
@@ -62,7 +65,9 @@ Without the hook, Claude would keep editing `auth.py` indefinitely — each fix
|
|
|
62
65
|
|
|
63
66
|
---
|
|
64
67
|
|
|
65
|
-
**`pre-edit-guard.sh`** —
|
|
68
|
+
**`pre-edit-guard.sh`** — Before a source edit, checks `docs/debug-log.md` for hypotheses still marked pending, flags unusually large diffs, and reminds you to register newly created scripts. These are context notes, not blocks.
|
|
69
|
+
|
|
70
|
+
> Downgraded from a hard block on 2026-07-30. Combined with the debugging rules it had become a trap: write three hypotheses into the log as the rules ask, and you were then barred from editing source until three were confirmed — including for unrelated planned work. The hypothesis discipline stays in the rules; only the enforcement was relaxed.
|
|
66
71
|
|
|
67
72
|
```
|
|
68
73
|
You: "The API is returning 500 errors on /users endpoint"
|
|
@@ -73,11 +78,12 @@ Claude: Let me investigate.
|
|
|
73
78
|
|
|
74
79
|
Claude: I think it's a database connection issue. Let me fix
|
|
75
80
|
the connection pool config.
|
|
76
|
-
[Edit src/db.py
|
|
81
|
+
[Edit src/db.py]
|
|
77
82
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
83
|
+
DEBUG-LOG NOTE: docs/debug-log.md has 2 hypotheses
|
|
84
|
+
still marked pending. If this edit is the fix for one
|
|
85
|
+
of them, update its status first so the log stays
|
|
86
|
+
truthful. If this edit is unrelated work, carry on.
|
|
81
87
|
|
|
82
88
|
Claude: Right, I haven't verified my hypothesis yet.
|
|
83
89
|
Let me check the actual error logs first.
|
|
@@ -188,8 +194,9 @@ CLAUDE.md # Your project info (you fill this in)
|
|
|
188
194
|
|
|
189
195
|
```bash
|
|
190
196
|
# In .claude/hooks/streak-breaker.sh
|
|
191
|
-
WARN_THRESHOLD=
|
|
192
|
-
STOP_THRESHOLD=
|
|
197
|
+
WARN_THRESHOLD=6 # Warn after N edits to same source file
|
|
198
|
+
STOP_THRESHOLD=10 # Hard block after N edits
|
|
199
|
+
# Config and doc files use 10/16; a confirmed run doubles both
|
|
193
200
|
```
|
|
194
201
|
|
|
195
202
|
**Add your own rules:**
|
package/README.zh-CN.md
CHANGED
|
@@ -84,7 +84,7 @@ your-project/
|
|
|
84
84
|
│ │ └── mobile.md # 移动端
|
|
85
85
|
│ ├── hooks/
|
|
86
86
|
│ │ ├── pre-edit-guard.sh # 编辑前纪律检查
|
|
87
|
-
│ │ ├── streak-breaker.sh #
|
|
87
|
+
│ │ ├── streak-breaker.sh # 打地鼠检测(源码同文件编辑≥10次硬停止)
|
|
88
88
|
│ │ └── post-error-remind.sh # 错误后调试流程提醒
|
|
89
89
|
│ ├── agents/
|
|
90
90
|
│ │ ├── reviewer.md # 代码审查员(独立 context)
|
|
@@ -127,8 +127,8 @@ Hooks 是在 Claude 执行操作前/后自动运行的 shell 脚本。**exit 2
|
|
|
127
127
|
|
|
128
128
|
| Hook | 触发时机 | 作用 |
|
|
129
129
|
|------|---------|------|
|
|
130
|
-
| `streak-breaker.sh` | 编辑文件前 |
|
|
131
|
-
| `pre-edit-guard.sh` | 编辑文件前 |
|
|
130
|
+
| `streak-breaker.sh` | 编辑文件前 | 源码同文件编辑 ≥6 次警告、**≥10 次硬停止**;配置与文档为 10 / 16。用户确认为计划内改动后,两个阈值翻倍 |
|
|
131
|
+
| `pre-edit-guard.sh` | 编辑文件前 | 检查 debug-log 中仍标 pending 的假设、超大 diff、新建脚本 —— **均为上下文提示,不阻断**(2026-07-30 从硬阻断降级) |
|
|
132
132
|
| `post-error-remind.sh` | 执行命令后 | 检测到错误输出时提醒遵守调试纪律 |
|
|
133
133
|
|
|
134
134
|
**实际效果演示:**
|
|
@@ -146,22 +146,23 @@ Claude: 正则不够,再加个长度检查。
|
|
|
146
146
|
|
|
147
147
|
Claude: 把之前的测试搞挂了,调整一下。
|
|
148
148
|
[编辑 src/auth.py — 修复测试兼容性]
|
|
149
|
-
⚠️ 提示:auth.py 已被编辑
|
|
149
|
+
⚠️ 提示:auth.py 已被编辑 6 次。
|
|
150
150
|
检查一下:这些修改是在推进目标,还是在修前面的修改?
|
|
151
151
|
|
|
152
152
|
Claude: 换个思路重写验证层。
|
|
153
153
|
[编辑 src/auth.py — 重写验证逻辑]
|
|
154
|
+
[… 中间还有三次编辑 …]
|
|
154
155
|
|
|
155
156
|
Claude: 差不多了,再调一下...
|
|
156
157
|
[编辑 src/auth.py — 被阻止]
|
|
157
158
|
|
|
158
159
|
🛑 编辑检查点
|
|
159
|
-
文件 auth.py 本次会话已被编辑
|
|
160
|
+
文件 auth.py 本次会话已被编辑 10 次。
|
|
160
161
|
反思:
|
|
161
162
|
1. 这些修改是在推进目标,还是在修之前的修改?
|
|
162
163
|
2. 如果是后者:停下来,寻找根本原因。
|
|
163
164
|
|
|
164
|
-
Claude: 你说得对。回顾这
|
|
165
|
+
Claude: 你说得对。回顾这 10 次编辑,我一直在修症状。
|
|
165
166
|
真正的问题是 User 模型的 validate() 在
|
|
166
167
|
sanitization 之前被调用了。我应该从源头修。
|
|
167
168
|
```
|
|
@@ -170,7 +171,7 @@ Claude: 你说得对。回顾这 5 次编辑,我一直在修症状。
|
|
|
170
171
|
|
|
171
172
|
---
|
|
172
173
|
|
|
173
|
-
**pre-edit-guard** —
|
|
174
|
+
**pre-edit-guard** — 在跳过诊断直接改代码时提示(2026-07-30 起为提示,不阻断):
|
|
174
175
|
|
|
175
176
|
```
|
|
176
177
|
你: "API 的 /users 接口返回 500 错误"
|
|
@@ -180,11 +181,12 @@ Claude: 我来排查。
|
|
|
180
181
|
→ 500 Internal Server Error
|
|
181
182
|
|
|
182
183
|
Claude: 我觉得是数据库连接池的问题,改一下配置。
|
|
183
|
-
[编辑 src/db.py
|
|
184
|
+
[编辑 src/db.py]
|
|
184
185
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
186
|
+
DEBUG-LOG 提示:docs/debug-log.md 中有 2 个假设
|
|
187
|
+
仍标记为 pending。如果这次编辑就是其中某条的修复,
|
|
188
|
+
请先更新它的状态,让日志保持真实;
|
|
189
|
+
如果是无关的工作,继续即可。
|
|
188
190
|
|
|
189
191
|
Claude: 对,我还没验证我的假设。先看看错误日志。
|
|
190
192
|
[执行: tail -50 logs/error.log]
|
|
@@ -262,8 +264,9 @@ EOF
|
|
|
262
264
|
|
|
263
265
|
编辑 `.claude/hooks/streak-breaker.sh`:
|
|
264
266
|
```bash
|
|
265
|
-
WARN_THRESHOLD=
|
|
266
|
-
STOP_THRESHOLD=
|
|
267
|
+
WARN_THRESHOLD=6 # 同一源码文件编辑 N 次后警告
|
|
268
|
+
STOP_THRESHOLD=10 # 同一源码文件编辑 N 次后硬停止
|
|
269
|
+
# 配置与文档文件为 10/16;用户确认为计划内改动后两者翻倍
|
|
267
270
|
```
|
|
268
271
|
|
|
269
272
|
### 添加新的子代理
|
package/init.sh
CHANGED
|
@@ -394,13 +394,52 @@ echo -e "${GREEN}Installing skills...${NC}"
|
|
|
394
394
|
SKILLS_MANIFEST=".claude/.cc-discipline-skills.manifest"
|
|
395
395
|
NEW_MANIFEST=$(mktemp 2>/dev/null || echo ".claude/.skills-manifest.tmp")
|
|
396
396
|
PRESERVED_SKILLS=""
|
|
397
|
-
|
|
397
|
+
RETIRED_SKILLS=""
|
|
398
|
+
RETIRED_PARTIAL=""
|
|
399
|
+
KEPT_RETIRED=""
|
|
400
|
+
# Snapshot the manifest before the install loop overwrites it. The retirement
|
|
401
|
+
# pass further down needs the OLD record to tell "we installed this" apart from
|
|
402
|
+
# "the user wrote it themselves".
|
|
403
|
+
OLD_MANIFEST=$(mktemp 2>/dev/null || echo ".claude/.skills-manifest.old")
|
|
404
|
+
[ -f "$SKILLS_MANIFEST" ] && cp "$SKILLS_MANIFEST" "$OLD_MANIFEST"
|
|
405
|
+
|
|
406
|
+
# Emit "<algo>:<digest>", never a bare digest. The fallback chain below resolves
|
|
407
|
+
# differently depending on what is installed, so a manifest that stores only the
|
|
408
|
+
# digest silently mis-compares the moment tool availability changes — the same
|
|
409
|
+
# class of environment drift that made the jq-only reads fail. When that happens
|
|
410
|
+
# every recorded hash mismatches, every retired skill looks user-modified, and
|
|
411
|
+
# the retirement pass does nothing. (fixed 2026-08-28, before 2.13.0 shipped.)
|
|
412
|
+
# sha256sum and `shasum -a 256` produce identical digests, so both record sha256.
|
|
398
413
|
_cc_hash() {
|
|
399
|
-
if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | cut -d' ' -f1
|
|
400
|
-
elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "$1" | cut -d' ' -f1
|
|
401
|
-
elif command -v md5sum >/dev/null 2>&1; then md5sum "$1" | cut -d' ' -f1
|
|
402
|
-
elif command -v md5 >/dev/null 2>&1; then md5 -q "$1"
|
|
403
|
-
else cksum "$1" | cut -d' ' -f1; fi
|
|
414
|
+
if command -v sha256sum >/dev/null 2>&1; then echo "sha256:$(sha256sum "$1" | cut -d' ' -f1)"
|
|
415
|
+
elif command -v shasum >/dev/null 2>&1; then echo "sha256:$(shasum -a 256 "$1" | cut -d' ' -f1)"
|
|
416
|
+
elif command -v md5sum >/dev/null 2>&1; then echo "md5:$(md5sum "$1" | cut -d' ' -f1)"
|
|
417
|
+
elif command -v md5 >/dev/null 2>&1; then echo "md5:$(md5 -q "$1")"
|
|
418
|
+
else echo "cksum:$(cksum "$1" | cut -d' ' -f1)"; fi
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
# True when manifest entry $2 still describes the current content of file $1.
|
|
422
|
+
# Handles the "<algo>:<digest>" form written from 2.13.0 and the bare digest
|
|
423
|
+
# written by 2.12.2/2.12.3: for a legacy entry the algorithm is unknown, so try
|
|
424
|
+
# each available one and accept any match rather than calling the file modified.
|
|
425
|
+
_cc_hash_matches() {
|
|
426
|
+
_f="$1"; _rec="$2"
|
|
427
|
+
[ -n "$_rec" ] || return 1
|
|
428
|
+
case "$_rec" in
|
|
429
|
+
*:*) [ "$(_cc_hash "$_f")" = "$_rec" ] && return 0; return 1 ;;
|
|
430
|
+
esac
|
|
431
|
+
for _algo in sha256sum shasum md5sum md5 cksum; do
|
|
432
|
+
command -v "$_algo" >/dev/null 2>&1 || continue
|
|
433
|
+
case "$_algo" in
|
|
434
|
+
sha256sum) _got=$(sha256sum "$_f" | cut -d' ' -f1) ;;
|
|
435
|
+
shasum) _got=$(shasum -a 256 "$_f" | cut -d' ' -f1) ;;
|
|
436
|
+
md5sum) _got=$(md5sum "$_f" | cut -d' ' -f1) ;;
|
|
437
|
+
md5) _got=$(md5 -q "$_f") ;;
|
|
438
|
+
cksum) _got=$(cksum "$_f" | cut -d' ' -f1) ;;
|
|
439
|
+
esac
|
|
440
|
+
[ "$_got" = "$_rec" ] && return 0
|
|
441
|
+
done
|
|
442
|
+
return 1
|
|
404
443
|
}
|
|
405
444
|
|
|
406
445
|
for skill_dir in "$SCRIPT_DIR"/templates/.claude/skills/*/; do
|
|
@@ -419,11 +458,10 @@ for skill_dir in "$SCRIPT_DIR"/templates/.claude/skills/*/; do
|
|
|
419
458
|
if [ ! -f "$dst" ]; then
|
|
420
459
|
cp "$src" "$dst" # new file — nothing to protect
|
|
421
460
|
else
|
|
422
|
-
disk_hash=$(_cc_hash "$dst")
|
|
423
461
|
recorded=$(grep "^$rel " "$SKILLS_MANIFEST" 2>/dev/null | head -1 | cut -d' ' -f2)
|
|
424
|
-
if
|
|
462
|
+
if _cc_hash_matches "$dst" "$tpl_hash"; then
|
|
425
463
|
: # already current
|
|
426
|
-
elif
|
|
464
|
+
elif _cc_hash_matches "$dst" "$recorded"; then
|
|
427
465
|
cp "$src" "$dst" # pristine older version — safe
|
|
428
466
|
elif [ -z "$recorded" ]; then
|
|
429
467
|
# No manifest yet (first run on an existing install) and the file
|
|
@@ -449,6 +487,59 @@ done
|
|
|
449
487
|
|
|
450
488
|
[ -f "$NEW_MANIFEST" ] && mv "$NEW_MANIFEST" "$SKILLS_MANIFEST"
|
|
451
489
|
|
|
490
|
+
# ─── Retire skills that upstream removed ───
|
|
491
|
+
# The install loop only ever copies, so a skill deleted from templates/ (as
|
|
492
|
+
# /finish and /retro were in v2.13.0) would otherwise sit in every existing
|
|
493
|
+
# install forever. Same conffile rule as the overwrite guard above: delete only
|
|
494
|
+
# what we installed and the user never touched. Anything modified is kept and
|
|
495
|
+
# reported. An install with no prior manifest is left alone entirely — without
|
|
496
|
+
# the old hashes there is no way to tell our copy from the user's own work.
|
|
497
|
+
if [ -s "$OLD_MANIFEST" ]; then
|
|
498
|
+
for old_skill in $(cut -d' ' -f1 "$OLD_MANIFEST" | cut -d'/' -f1 | sort -u); do
|
|
499
|
+
[ -d "$SCRIPT_DIR/templates/.claude/skills/$old_skill" ] && continue
|
|
500
|
+
[ -d ".claude/skills/$old_skill" ] || continue
|
|
501
|
+
skill_modified=false
|
|
502
|
+
while read -r rel rec_hash; do
|
|
503
|
+
case "$rel" in "$old_skill"/*) ;; *) continue ;; esac
|
|
504
|
+
[ -f ".claude/skills/$rel" ] || continue
|
|
505
|
+
_cc_hash_matches ".claude/skills/$rel" "$rec_hash" || skill_modified=true
|
|
506
|
+
done < "$OLD_MANIFEST"
|
|
507
|
+
if [ "$skill_modified" = true ]; then
|
|
508
|
+
KEPT_RETIRED="$KEPT_RETIRED $old_skill"
|
|
509
|
+
# Carry the old entries into the new manifest. Without this the skill
|
|
510
|
+
# never appears in a manifest again, so a later upgrade cannot tell
|
|
511
|
+
# our copy from the user's and it is frozen in place permanently.
|
|
512
|
+
grep "^$old_skill/" "$OLD_MANIFEST" >> "$SKILLS_MANIFEST" 2>/dev/null
|
|
513
|
+
else
|
|
514
|
+
# Delete only the files the manifest recorded, then the directory if
|
|
515
|
+
# that leaves it empty. A blanket rm -rf would also destroy files a
|
|
516
|
+
# user put inside the skill directory that we never installed and
|
|
517
|
+
# therefore cannot account for.
|
|
518
|
+
while read -r rel _; do
|
|
519
|
+
case "$rel" in "$old_skill"/*) ;; *) continue ;; esac
|
|
520
|
+
rm -f ".claude/skills/$rel"
|
|
521
|
+
done < "$OLD_MANIFEST"
|
|
522
|
+
if rmdir ".claude/skills/$old_skill" 2>/dev/null; then
|
|
523
|
+
RETIRED_SKILLS="$RETIRED_SKILLS $old_skill"
|
|
524
|
+
else
|
|
525
|
+
RETIRED_PARTIAL="$RETIRED_PARTIAL $old_skill"
|
|
526
|
+
fi
|
|
527
|
+
fi
|
|
528
|
+
done
|
|
529
|
+
fi
|
|
530
|
+
rm -f "$OLD_MANIFEST" 2>/dev/null
|
|
531
|
+
|
|
532
|
+
if [ -n "$RETIRED_SKILLS" ]; then
|
|
533
|
+
echo -e " ${YELLOW}Retired upstream, removed:${RETIRED_SKILLS}${NC}"
|
|
534
|
+
fi
|
|
535
|
+
if [ -n "$RETIRED_PARTIAL" ]; then
|
|
536
|
+
echo -e " ${YELLOW}Retired upstream; our files removed but directory kept (it holds files we did not install):${RETIRED_PARTIAL}${NC}"
|
|
537
|
+
fi
|
|
538
|
+
if [ -n "$KEPT_RETIRED" ]; then
|
|
539
|
+
echo -e " ${YELLOW}Retired upstream but kept — you modified them:${KEPT_RETIRED}${NC}"
|
|
540
|
+
echo -e " ${YELLOW}Delete by hand once you no longer need them.${NC}"
|
|
541
|
+
fi
|
|
542
|
+
|
|
452
543
|
if [ -n "$PRESERVED_SKILLS" ]; then
|
|
453
544
|
echo -e " ${YELLOW}Locally modified skills were not overwritten:${PRESERVED_SKILLS}${NC}"
|
|
454
545
|
echo -e " ${YELLOW}Compare with: diff .claude/skills/<name>/SKILL.md{,.new}${NC}"
|
package/package.json
CHANGED
|
@@ -90,25 +90,14 @@ if [ "$DIFF_LINES" -gt 200 ]; then
|
|
|
90
90
|
exit 0
|
|
91
91
|
fi
|
|
92
92
|
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
#
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
else
|
|
100
|
-
|
|
101
|
-
fi
|
|
102
|
-
if [ "$TOOL_NAME" = "Write" ] && echo "$BASENAME" | grep -qiE "\.(sh|py|js|ts|rb|pl)$"; then
|
|
103
|
-
# Check if file already exists (new file = tool creation)
|
|
104
|
-
FULL_PATH="$FILE_PATH"
|
|
105
|
-
if [ ! -f "$FULL_PATH" ]; then
|
|
106
|
-
cat <<JSONEOF
|
|
107
|
-
{"hookSpecificOutput":{"hookEventName":"PreToolUse","additionalContext":"NEW SCRIPT DETECTED: You are creating $BASENAME. If this is a reusable tool/helper, register it in CLAUDE.md under 'Project Tools' now (path, purpose, usage, date) — capturing it while context is fresh saves time later."}}
|
|
108
|
-
JSONEOF
|
|
109
|
-
exit 0
|
|
110
|
-
fi
|
|
111
|
-
fi
|
|
93
|
+
# The new-script registration reminder was removed 2026-08-28. It fired 2,170
|
|
94
|
+
# times in four weeks across two machines (~50 tokens each), almost entirely on
|
|
95
|
+
# one-off transform scripts nobody would ever register. The measured correlation
|
|
96
|
+
# with a growing Project Tools section was confounded: /commit carries the same
|
|
97
|
+
# instruction, and a "does the heading exist" gate was disproven because
|
|
98
|
+
# templates/CLAUDE.md ships that heading so every generated project has it.
|
|
99
|
+
# Removing the branch also drops the TOOL_NAME extraction, which nothing else
|
|
100
|
+
# used, cutting two subprocess calls from the hottest hook.
|
|
112
101
|
|
|
113
102
|
# Normal source edit: stay silent (exit 0, no output).
|
|
114
103
|
# The "list >=3 causes before a bug fix" guidance already lives in the
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: coplan
|
|
3
|
+
description: Write the plan under discussion to docs/current-plan.md so Codex can review it against the real code. Stacks after /think.
|
|
4
|
+
when_to_use: When a plan is worth a second opinion before building it — "coplan", "落个方案", "写下来给 codex 看", "把方案存一下" — or whenever /coplan is stacked after /think.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Write the plan under discussion to `docs/current-plan.md`, then stop.
|
|
8
|
+
|
|
9
|
+
This skill does not produce the plan and does not decide anything. It takes whatever is on the table — the approaches from `/think`, or a plan just described in conversation — and puts it where Codex can read it. Codex runs read-only from the repo root, so a plan **on disk** is one it can check **against the actual code**. That is the entire point: a plan pasted into a prompt can only be judged on its own terms, while a plan in the tree can be judged against what is really there.
|
|
10
|
+
|
|
11
|
+
If there is no plan on the table yet — no approaches from `/think`, nothing described in conversation — **say so and stop**. Point at `/think` instead. Do not invent a plan just to have something to write: a fabricated plan sent for review wastes a Codex run and produces confident feedback on something nobody proposed.
|
|
12
|
+
|
|
13
|
+
## What to write
|
|
14
|
+
|
|
15
|
+
**Overwrite the file completely.** One path, current version only — every earlier version survives in `git log -p docs/current-plan.md`, so nothing is lost and the directory never fills with dated files.
|
|
16
|
+
|
|
17
|
+
Include, in this order:
|
|
18
|
+
|
|
19
|
+
1. **Task** — what is being asked, in a sentence or two, framed the way the user framed it rather than the way you re-scoped it.
|
|
20
|
+
2. **Approaches** — two or three. For each: how it works, what it costs, what it gives up. If there is genuinely only one reasonable approach, say so and say why. Do not invent alternatives to fill the section.
|
|
21
|
+
3. **Recommendation** — which one, and the reason. A recommendation, not a survey.
|
|
22
|
+
4. **Assumptions to verify** — the highest-value section for a reviewer that has the repo. Every claim the plan rests on that you have *not* checked: "assumes this is only called from X", "assumes the migration runs before Y", "assumes no other caller depends on the old shape". Codex can open the code and tell you which of these are false. **A plan usually fails here, not in its logic.**
|
|
23
|
+
5. **Risks and unknowns** — what could go wrong, and what you could not determine.
|
|
24
|
+
6. **Out of scope** — what this deliberately does not touch, so a reviewer does not flag choices as omissions.
|
|
25
|
+
|
|
26
|
+
Write it for someone who has the codebase but was not in this conversation. No "as discussed above", no pronouns pointing at chat history.
|
|
27
|
+
|
|
28
|
+
## Then stop
|
|
29
|
+
|
|
30
|
+
Report the path and hand the review command over verbatim:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
Plan written to docs/current-plan.md
|
|
34
|
+
|
|
35
|
+
To have Codex review it:
|
|
36
|
+
/codex:rescue 只评审方案不要修改任何文件,方案见 docs/current-plan.md
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Do not run the review yourself** — it costs time and quota, and a small plan does not need one. That call belongs to the user. **Do not start implementing either**: writing a plan down is not approval to build it.
|
|
40
|
+
|
|
41
|
+
## When stacked after /think
|
|
42
|
+
|
|
43
|
+
`/think /coplan <task>` is the intended pairing. `/think` produces the approaches and stops for the user's choice; `/coplan` puts them on disk on the way past.
|
|
44
|
+
|
|
45
|
+
Write the file with the approaches **still open**. The review is meant to inform the choice, not rubber-stamp one already made — so do not collapse the section down to your recommendation just because you have one.
|
|
@@ -86,9 +86,18 @@ Keep this list curated — remove tools that are obsolete.
|
|
|
86
86
|
|
|
87
87
|
(Empty — fill in as the project develops scaffolding worth checking periodically.)
|
|
88
88
|
|
|
89
|
-
## 7.
|
|
89
|
+
## 7. Did the rules change anything this time?
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
Most checks have neither — **"none this time" is the normal answer and it is data.** Never manufacture one.
|
|
92
|
+
|
|
93
|
+
- **Save** — a rule or hook *changed the outcome*: caught a real mistake, blocked a real loss, stopped a wrong turn already in motion. "The rules kept me disciplined" is not a save; "git-guard blocked a hard reset with 40 min of uncommitted work in the tree" is.
|
|
94
|
+
- **Friction** — a rule or hook cost time for no benefit. Only log it with a concrete `→ fix:` (raise a threshold, exempt a path, reword a check). "X was annoying" is not actionable.
|
|
95
|
+
|
|
96
|
+
If either is present, append one dated line each to a `## Rule Ledger` table in `docs/progress.md` (create the section at the end if it isn't there), then note it in the status line. **Do this automatically — recording an observation is not a change that needs sign-off.**
|
|
97
|
+
|
|
98
|
+
Saves get logged because they are invisible: a rule that works suppresses the very failure it was written for, so only its cost surfaces on its own. Judge on cost alone and every load-bearing rule eventually looks like overhead.
|
|
99
|
+
|
|
100
|
+
If there was no save, note one thing that went well instead — recognizing what works reinforces it.
|
|
92
101
|
|
|
93
102
|
## 8. Status report
|
|
94
103
|
|
|
@@ -102,6 +111,7 @@ On track: yes/no/drifted
|
|
|
102
111
|
Progressing: yes/circling
|
|
103
112
|
Progress recorded: yes/updated now/no
|
|
104
113
|
Scaffolding: [used/skipped/n/a]
|
|
114
|
+
Ledger: [save / friction appended, or "none"]
|
|
105
115
|
Going well: [one thing]
|
|
106
116
|
Issues found: [list, or "none"]
|
|
107
117
|
```
|
|
@@ -93,7 +93,7 @@ If self-review reveals issues, revise your proposals before presenting. Don't sh
|
|
|
93
93
|
|
|
94
94
|
Do not:
|
|
95
95
|
- Start coding "while waiting"
|
|
96
|
-
- Create files or scaffolding
|
|
96
|
+
- Create files or scaffolding — **except** writing the plan itself to disk, which is not implementation. `/coplan` does exactly that and is designed to stack here.
|
|
97
97
|
- Run exploratory commands beyond what's needed for Step 0-3
|
|
98
98
|
- Say "I'll go ahead and start with Approach A" — that's the user's call
|
|
99
99
|
|
|
@@ -112,6 +112,5 @@ Carry these forward. If scope changes during implementation, pause and re-align
|
|
|
112
112
|
The user may steer what happens after alignment by appending a directive to the invocation:
|
|
113
113
|
|
|
114
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
115
|
|
|
117
116
|
If no directive was appended, stop and wait as usual (Step 5).
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
## Rule Ledger
|
|
77
77
|
|
|
78
78
|
<!--
|
|
79
|
-
Appended by /
|
|
79
|
+
Appended by /self-check §7. Two columns of evidence about the discipline rules themselves:
|
|
80
80
|
|
|
81
81
|
- SAVE — a rule or hook changed the outcome: caught a real mistake, blocked a
|
|
82
82
|
real loss, stopped a wrong turn already in motion.
|
|
@@ -1,44 +0,0 @@
|
|
|
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 ✅.
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: retro
|
|
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
|
-
---
|
|
5
|
-
|
|
6
|
-
Find friction. Remove friction. Record what the rules actually caught.
|
|
7
|
-
|
|
8
|
-
## What to do
|
|
9
|
-
|
|
10
|
-
Quickly scan what just happened — `git log --oneline -10` and any hook triggers you remember. Then output in this format:
|
|
11
|
-
|
|
12
|
-
```
|
|
13
|
-
RETRO — [date]
|
|
14
|
-
|
|
15
|
-
Friction:
|
|
16
|
-
- [what got in the way] → fix: [specific actionable change]
|
|
17
|
-
|
|
18
|
-
Saves:
|
|
19
|
-
- [rule/hook] caught [the specific real problem] — without it: [what would have shipped]
|
|
20
|
-
|
|
21
|
-
Insights:
|
|
22
|
-
- [something learned that should survive this session]
|
|
23
|
-
|
|
24
|
-
Framework:
|
|
25
|
-
- [cc-discipline improvement, if any — skip if none]
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
Rules:
|
|
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).
|
|
30
|
-
- **Only new** — Don't repeat friction that's already been addressed or recorded in memory.
|
|
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.
|
|
32
|
-
- **Framework items are rare** — Most friction is project-specific. Only flag framework issues if the same problem would hit other projects too.
|
|
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.
|
|
39
|
-
|
|
40
|
-
## After output
|
|
41
|
-
|
|
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:
|
|
45
|
-
- "fix it" → apply the changes
|
|
46
|
-
- "remember it" → write to memory via /commit
|
|
47
|
-
- "skip" → move on
|
|
48
|
-
|
|
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.)
|