devlog-tracker 0.22.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.
Files changed (120) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +234 -0
  3. package/bin/devlog-tracker.js +51 -0
  4. package/cli/init.js +55 -0
  5. package/cli/init.test.js +37 -0
  6. package/cli/merge-hooks.js +60 -0
  7. package/cli/merge-hooks.test.js +187 -0
  8. package/cli/platforms/codex.js +13 -0
  9. package/cli/platforms/cursor.js +13 -0
  10. package/cli/status.js +14 -0
  11. package/cli/status.test.js +33 -0
  12. package/cli/vendor.js +37 -0
  13. package/cli/vendor.test.js +84 -0
  14. package/codex/hooks/on-pre-tool.sh +19 -0
  15. package/codex/hooks/on-session-end.sh +31 -0
  16. package/codex/hooks/on-session-start.sh +37 -0
  17. package/codex/hooks/on-stop.sh +18 -0
  18. package/codex/hooks/on-user-prompt-submit.sh +15 -0
  19. package/codex/hooks/project-dir.sh +13 -0
  20. package/codex/hooks/test-adapters.sh +98 -0
  21. package/codex/hooks.json +54 -0
  22. package/commands/checkpoint.md +17 -0
  23. package/commands/clean.md +50 -0
  24. package/commands/compact.md +24 -0
  25. package/commands/continue.md +30 -0
  26. package/commands/keep.md +100 -0
  27. package/commands/lessons-drift.md +19 -0
  28. package/commands/lessons-off.md +16 -0
  29. package/commands/lessons-on.md +20 -0
  30. package/commands/lessons.md +20 -0
  31. package/commands/overview.md +32 -0
  32. package/commands/pause.md +20 -0
  33. package/commands/resume.md +18 -0
  34. package/commands/segment-watch.md +21 -0
  35. package/commands/span.md +13 -0
  36. package/commands/start.md +19 -0
  37. package/commands/status.md +14 -0
  38. package/cursor/hooks/on-pre-tool.sh +24 -0
  39. package/cursor/hooks/on-session-end.sh +11 -0
  40. package/cursor/hooks/on-session-start.sh +15 -0
  41. package/cursor/hooks/on-stop.sh +44 -0
  42. package/cursor/hooks/on-submit-prompt.sh +28 -0
  43. package/cursor/hooks/on-tool-failure.sh +16 -0
  44. package/cursor/hooks/project-dir.sh +10 -0
  45. package/cursor/hooks/test-adapters.sh +201 -0
  46. package/cursor/hooks.json +26 -0
  47. package/hooks/scripts/await-open.sh +23 -0
  48. package/hooks/scripts/checkpoint-set.sh +44 -0
  49. package/hooks/scripts/clean-devlog.sh +69 -0
  50. package/hooks/scripts/close-open-round.sh +186 -0
  51. package/hooks/scripts/compact-devlog.sh +72 -0
  52. package/hooks/scripts/detect-pending-question.sh +31 -0
  53. package/hooks/scripts/devlog-lock.sh +38 -0
  54. package/hooks/scripts/devlog-md.sh +222 -0
  55. package/hooks/scripts/devlog-path.sh +70 -0
  56. package/hooks/scripts/enforce-devlog.sh +629 -0
  57. package/hooks/scripts/files-snapshot.sh +70 -0
  58. package/hooks/scripts/json-field.sh +76 -0
  59. package/hooks/scripts/keep-move.sh +155 -0
  60. package/hooks/scripts/kept-list.sh +40 -0
  61. package/hooks/scripts/lessons-append.sh +93 -0
  62. package/hooks/scripts/lessons-drift-set.sh +42 -0
  63. package/hooks/scripts/lessons-off.sh +15 -0
  64. package/hooks/scripts/lessons-on.sh +21 -0
  65. package/hooks/scripts/lessons-read.sh +60 -0
  66. package/hooks/scripts/on-session-end.sh +14 -0
  67. package/hooks/scripts/on-stop-failure.sh +18 -0
  68. package/hooks/scripts/on-tool-failure.sh +25 -0
  69. package/hooks/scripts/pause-devlog.sh +13 -0
  70. package/hooks/scripts/redact-prompt.sh +19 -0
  71. package/hooks/scripts/resume-devlog.sh +41 -0
  72. package/hooks/scripts/round-start.sh +333 -0
  73. package/hooks/scripts/run-tests.sh +26 -0
  74. package/hooks/scripts/segment-watch-set.sh +47 -0
  75. package/hooks/scripts/segment-watch.sh +197 -0
  76. package/hooks/scripts/session-start-devlog.sh +157 -0
  77. package/hooks/scripts/span-close.sh +9 -0
  78. package/hooks/scripts/span-open.sh +21 -0
  79. package/hooks/scripts/start-devlog.sh +24 -0
  80. package/hooks/scripts/status-devlog.sh +43 -0
  81. package/hooks/scripts/tests/test-await-open.sh +112 -0
  82. package/hooks/scripts/tests/test-branch-scoped-integration.sh +108 -0
  83. package/hooks/scripts/tests/test-checkpoint-set.sh +36 -0
  84. package/hooks/scripts/tests/test-clean-devlog.sh +160 -0
  85. package/hooks/scripts/tests/test-cli-init-e2e.sh +37 -0
  86. package/hooks/scripts/tests/test-close-open-round.sh +325 -0
  87. package/hooks/scripts/tests/test-compact-devlog.sh +62 -0
  88. package/hooks/scripts/tests/test-devlog-lock.sh +37 -0
  89. package/hooks/scripts/tests/test-devlog-md.sh +530 -0
  90. package/hooks/scripts/tests/test-devlog-path.sh +118 -0
  91. package/hooks/scripts/tests/test-enforce-devlog-files.sh +233 -0
  92. package/hooks/scripts/tests/test-enforce-devlog-handoff-order.sh +223 -0
  93. package/hooks/scripts/tests/test-enforce-devlog-workspace.sh +333 -0
  94. package/hooks/scripts/tests/test-enforce-devlog.sh +1290 -0
  95. package/hooks/scripts/tests/test-files-snapshot.sh +142 -0
  96. package/hooks/scripts/tests/test-json-field.sh +57 -0
  97. package/hooks/scripts/tests/test-keep-move.sh +127 -0
  98. package/hooks/scripts/tests/test-kept-list.sh +80 -0
  99. package/hooks/scripts/tests/test-lessons-append.sh +138 -0
  100. package/hooks/scripts/tests/test-lessons-drift-set.sh +36 -0
  101. package/hooks/scripts/tests/test-lessons-on-off.sh +75 -0
  102. package/hooks/scripts/tests/test-lessons-read.sh +97 -0
  103. package/hooks/scripts/tests/test-on-interrupt.sh +186 -0
  104. package/hooks/scripts/tests/test-redact-prompt.sh +27 -0
  105. package/hooks/scripts/tests/test-resume-devlog.sh +35 -0
  106. package/hooks/scripts/tests/test-round-start.sh +825 -0
  107. package/hooks/scripts/tests/test-segment-watch-set.sh +75 -0
  108. package/hooks/scripts/tests/test-segment-watch.sh +468 -0
  109. package/hooks/scripts/tests/test-session-start-devlog.sh +508 -0
  110. package/hooks/scripts/tests/test-start-pause-devlog.sh +84 -0
  111. package/hooks/scripts/tests/test-status-span.sh +50 -0
  112. package/hooks/scripts/tests/test-workspace-snapshot.sh +130 -0
  113. package/hooks/scripts/workspace-snapshot.sh +69 -0
  114. package/package.json +37 -0
  115. package/skills/devlog-tracker/SKILL.md +411 -0
  116. package/skills/devlog-tracker/references/checkpoint-mode.md +42 -0
  117. package/skills/devlog-tracker/references/lessons-mode.md +32 -0
  118. package/skills/devlog-tracker/references/reply-fold.md +129 -0
  119. package/skills/devlog-tracker/references/round-segments.md +55 -0
  120. package/skills/devlog-tracker/references/span-mode.md +65 -0
@@ -0,0 +1,333 @@
1
+ #!/usr/bin/env bash
2
+ # Self-check for enforce-devlog.sh's #### 工作區 machine-verify (Phase 1,
3
+ # docs/design/devlog-as-ssot-assessment.md). Separate file from
4
+ # test-enforce-devlog.sh to avoid growing that suite further; still
5
+ # auto-discovered by run-tests.sh's tests/test-*.sh glob.
6
+ set -uo pipefail
7
+
8
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
9
+ TMP_ROOT="$(mktemp -d)"
10
+ trap 'rm -rf "$TMP_ROOT"' EXIT
11
+
12
+ export CLAUDE_PROJECT_DIR="$TMP_ROOT"
13
+ DEVLOG_DIR="$TMP_ROOT/.devlog"
14
+ mkdir -p "$DEVLOG_DIR"
15
+ touch "$DEVLOG_DIR/.enabled"
16
+
17
+ git -C "$TMP_ROOT" init -q -b main
18
+ git -C "$TMP_ROOT" config user.email test@example.com
19
+ git -C "$TMP_ROOT" config user.name test
20
+ echo '.devlog/' > "$TMP_ROOT/.gitignore"
21
+ git -C "$TMP_ROOT" add .gitignore
22
+ git -C "$TMP_ROOT" commit -q -m init
23
+ HASH="$(git -C "$TMP_ROOT" rev-parse --short HEAD)"
24
+
25
+ FAIL=0
26
+ assert_exit() {
27
+ local desc="$1" expected="$2" actual="$3"
28
+ if [ "$actual" -eq "$expected" ]; then
29
+ echo "PASS: $desc"
30
+ else
31
+ echo "FAIL: $desc (expected exit $expected, got $actual)"
32
+ FAIL=1
33
+ fi
34
+ }
35
+
36
+ # Round-current split: enforce-devlog.sh validates .round-current.md now
37
+ # (not devlog.md), and merges it into devlog.md on success. One assertion
38
+ # per "should succeed" case exercises that merge.
39
+ assert_round_merged() {
40
+ local desc="$1" marker="${2:-fixture}"
41
+ if [ -f "$DEVLOG_DIR/.round-current.md" ]; then
42
+ echo "FAIL: $desc (.round-current.md should be merged away)"
43
+ FAIL=1
44
+ elif ! grep -q "$marker" "$DEVLOG_DIR/devlog.md" 2>/dev/null; then
45
+ echo "FAIL: $desc (devlog.md missing merged content)"
46
+ FAIL=1
47
+ else
48
+ echo "PASS: $desc (round-current merged into devlog.md)"
49
+ fi
50
+ }
51
+
52
+ write_round() {
53
+ # $1 = workspace body (multi-line ok); empty string omits the section.
54
+ # Writes into .round-current.md — the file enforce-devlog.sh now validates
55
+ # (round-start.sh already opened a skeleton there; this overwrites it).
56
+ local ws="$1"
57
+ {
58
+ echo "## Round 1 — 2026-09-10T00:00:00+08:00"
59
+ echo ""
60
+ echo "### Summary"
61
+ echo "fixture"
62
+ echo ""
63
+ echo "### Reply"
64
+ echo "fixture reply."
65
+ echo ""
66
+ echo "### Handoff"
67
+ if [ -n "$ws" ]; then
68
+ echo "#### 工作區"
69
+ printf '%s\n' "$ws"
70
+ fi
71
+ echo "#### 現況"
72
+ echo "fixture"
73
+ echo "#### 完成條件"
74
+ echo "observable done via test."
75
+ echo "#### 下一步"
76
+ echo "edit hooks/scripts/enforce-devlog.sh"
77
+ echo ""
78
+ echo "### Status"
79
+ echo "IN_PROGRESS"
80
+ } > "$DEVLOG_DIR/.round-current.md"
81
+ }
82
+
83
+ # --- exact match -> allowed --------------------------------------------------
84
+ bash "$SCRIPT_DIR/round-start.sh" < /dev/null
85
+ write_round "main @ ${HASH},工作樹乾淨"
86
+ echo '{}' | bash "$SCRIPT_DIR/enforce-devlog.sh" >/dev/null 2>&1
87
+ assert_exit "workspace matches live clean git state -> allowed" 0 $?
88
+ assert_round_merged "workspace matches live clean git state"
89
+
90
+ # --- missing section -> blocked, message shows exact expected text ----------
91
+ bash "$SCRIPT_DIR/round-start.sh" < /dev/null
92
+ write_round ""
93
+ MSG="$(echo '{}' | bash "$SCRIPT_DIR/enforce-devlog.sh" 2>&1)"
94
+ assert_exit "workspace section missing on IN_PROGRESS -> blocked" 2 $?
95
+ case "$MSG" in
96
+ *"跟目前 git 狀態不符"*) echo "PASS: message explains the mismatch" ;;
97
+ *) echo "FAIL: message should explain the mismatch, got: $MSG"; FAIL=1 ;;
98
+ esac
99
+ case "$MSG" in
100
+ *"main @ ${HASH},工作樹乾淨"*) echo "PASS: message includes the exact expected snapshot" ;;
101
+ *) echo "FAIL: message should include the exact expected snapshot, got: $MSG"; FAIL=1 ;;
102
+ esac
103
+
104
+ # --- stale hash -> blocked ---------------------------------------------------
105
+ bash "$SCRIPT_DIR/round-start.sh" < /dev/null
106
+ write_round "main @ 0000000,工作樹乾淨"
107
+ echo '{}' | bash "$SCRIPT_DIR/enforce-devlog.sh" >/dev/null 2>&1
108
+ assert_exit "workspace with stale hash -> blocked" 2 $?
109
+
110
+ # --- dirty tree, exact two-line match -> allowed -----------------------------
111
+ echo change >> "$TMP_ROOT/a.txt"
112
+ bash "$SCRIPT_DIR/round-start.sh" < /dev/null
113
+ write_round "main @ ${HASH}
114
+ 未提交:a.txt"
115
+ echo '{}' | bash "$SCRIPT_DIR/enforce-devlog.sh" >/dev/null 2>&1
116
+ assert_exit "dirty tree, exact two-line workspace -> allowed" 0 $?
117
+ assert_round_merged "dirty tree, exact two-line workspace"
118
+ git -C "$TMP_ROOT" checkout -q -- a.txt 2>/dev/null || rm -f "$TMP_ROOT/a.txt"
119
+
120
+ # --- BLOCKED status is also enforced -----------------------------------------
121
+ bash "$SCRIPT_DIR/round-start.sh" < /dev/null
122
+ {
123
+ echo "## Round 2 — 2026-09-10T00:05:00+08:00"
124
+ echo ""
125
+ echo "### Summary"
126
+ echo "fixture"
127
+ echo ""
128
+ echo "### Reply"
129
+ echo "fixture reply."
130
+ echo ""
131
+ echo "### Handoff"
132
+ echo "#### 現況"
133
+ echo "缺使用者提供的 token(出現即繼續)"
134
+ echo "#### 完成條件"
135
+ echo "缺件已到且可觀察條件達成。"
136
+ echo "#### 下一步"
137
+ echo "等使用者提供 token 後接著改 hooks/foo.sh"
138
+ echo ""
139
+ echo "### Status"
140
+ echo "BLOCKED"
141
+ } > "$DEVLOG_DIR/.round-current.md"
142
+ echo '{}' | bash "$SCRIPT_DIR/enforce-devlog.sh" >/dev/null 2>&1
143
+ assert_exit "BLOCKED with missing workspace -> blocked" 2 $?
144
+
145
+ # --- DONE is unaffected (check only applies to IN_PROGRESS/BLOCKED) ---------
146
+ bash "$SCRIPT_DIR/round-start.sh" < /dev/null
147
+ {
148
+ echo "## Round 3 — 2026-09-10T00:10:00+08:00"
149
+ echo ""
150
+ echo "### Summary"
151
+ echo "fixture"
152
+ echo ""
153
+ echo "### Reply"
154
+ echo "fixture reply."
155
+ echo ""
156
+ echo "### Handoff"
157
+ echo "#### 現況"
158
+ echo "fixture"
159
+ echo ""
160
+ echo "### Status"
161
+ echo "DONE"
162
+ } > "$DEVLOG_DIR/.round-current.md"
163
+ echo '{}' | bash "$SCRIPT_DIR/enforce-devlog.sh" >/dev/null 2>&1
164
+ assert_exit "DONE with no workspace section -> allowed (check does not apply)" 0 $?
165
+ assert_round_merged "DONE with no workspace section"
166
+
167
+ # --- fenced example quoting #### 工作區 before the real section -> ignored,
168
+ # extractor still finds the real (unfenced) 工作區 body -------------------
169
+ bash "$SCRIPT_DIR/round-start.sh" < /dev/null
170
+ {
171
+ echo "## Round 4 — 2026-09-10T00:15:00+08:00"
172
+ echo ""
173
+ echo "### Summary"
174
+ echo "fixture"
175
+ echo ""
176
+ echo "### Reply"
177
+ echo "fixture reply."
178
+ echo ""
179
+ echo "### Handoff"
180
+ echo "#### 決策"
181
+ echo "範例格式:"
182
+ echo '```markdown'
183
+ echo "#### 工作區"
184
+ echo "main @ 0000000,工作樹乾淨"
185
+ echo '```'
186
+ echo "真正決策內容"
187
+ echo "#### 工作區"
188
+ echo "main @ ${HASH},工作樹乾淨"
189
+ echo "#### 現況"
190
+ echo "fixture"
191
+ echo "#### 完成條件"
192
+ echo "observable done via test."
193
+ echo "#### 下一步"
194
+ echo "edit hooks/scripts/enforce-devlog.sh"
195
+ echo ""
196
+ echo "### Status"
197
+ echo "IN_PROGRESS"
198
+ } > "$DEVLOG_DIR/.round-current.md"
199
+ echo '{}' | bash "$SCRIPT_DIR/enforce-devlog.sh" >/dev/null 2>&1
200
+ assert_exit "fenced example quoting #### 工作區 before the real section -> allowed" 0 $?
201
+ assert_round_merged "fenced example quoting #### 工作區 before the real section"
202
+
203
+ # --- unterminated fence in an earlier subsection -> fail-open, not a false
204
+ # block (final-review Fix 1). A ``` fence that never closes (odd fence-marker
205
+ # count) must never make a present, filled-in #### 下一步 register as missing.
206
+ # Pre-fix, the odd fence opened by #### 現況's (malformed) example got
207
+ # `fence` stuck at 1 for the rest of the round, so handoff_subsection_body()
208
+ # could no longer even find the "#### 下一步" heading — even though #### 工作區
209
+ # above it is exactly correct and #### 下一步 below it has real content.
210
+ bash "$SCRIPT_DIR/round-start.sh" < /dev/null
211
+ {
212
+ echo "## Round 5 — 2026-09-10T00:20:00+08:00"
213
+ echo ""
214
+ echo "### Summary"
215
+ echo "fixture"
216
+ echo ""
217
+ echo "### Reply"
218
+ echo "fixture reply."
219
+ echo ""
220
+ echo "### Handoff"
221
+ echo "#### 工作區"
222
+ echo "main @ ${HASH},工作樹乾淨"
223
+ echo "#### 現況"
224
+ echo "一段沒收尾的範例:"
225
+ echo '```markdown'
226
+ echo "沒收尾內容,一路吃到這個 Round 結尾"
227
+ echo "#### 完成條件"
228
+ echo "observable done via test."
229
+ echo "#### 下一步"
230
+ echo "edit hooks/scripts/enforce-devlog.sh"
231
+ echo ""
232
+ echo "### Status"
233
+ echo "IN_PROGRESS"
234
+ } > "$DEVLOG_DIR/.round-current.md"
235
+ echo '{}' | bash "$SCRIPT_DIR/enforce-devlog.sh" >/dev/null 2>&1
236
+ assert_exit "unterminated fence in 現況 -> fail-open, 下一步 still recognized" 0 $?
237
+ assert_round_merged "unterminated fence in 現況"
238
+
239
+ # --- DONE with a non-empty #### 檔案 (claims files were touched/committed)
240
+ # IS now checked: a DONE round that reports file changes but has no/wrong
241
+ # #### 工作區 is exactly the "已 commit 完成" false-claim case
242
+ # devlog-as-ssot-assessment.md flags as uncaught — machine-verify it the
243
+ # same way IN_PROGRESS/BLOCKED already are. A trivial DONE round with no
244
+ # #### 檔案 stays exempt (previous test above), matching SKILL.md's
245
+ # "瑣碎輪只留現況一句(沒有工作區)" convention untouched.
246
+ #
247
+ # The #### 檔案 body below is just "尚未 commit:" with no claimed paths —
248
+ # this suite only cares about triggering the 工作區 check via a non-empty
249
+ # #### 檔案, not about 檔案 content itself (that's
250
+ # test-enforce-devlog-files.sh's job, Phase 4). Any grammar-valid,
251
+ # always-passing body works here. ------------------------------------------
252
+ bash "$SCRIPT_DIR/round-start.sh" < /dev/null
253
+ {
254
+ echo "## Round 6 — 2026-09-10T00:25:00+08:00"
255
+ echo ""
256
+ echo "### Summary"
257
+ echo "fixture"
258
+ echo ""
259
+ echo "### Reply"
260
+ echo "fixture reply."
261
+ echo ""
262
+ echo "### Handoff"
263
+ echo "#### 檔案"
264
+ echo "尚未 commit:"
265
+ echo "#### 現況"
266
+ echo "fixture"
267
+ echo ""
268
+ echo "### Status"
269
+ echo "DONE"
270
+ } > "$DEVLOG_DIR/.round-current.md"
271
+ MSG_DONE="$(echo '{}' | bash "$SCRIPT_DIR/enforce-devlog.sh" 2>&1)"
272
+ assert_exit "DONE with #### 檔案 but no #### 工作區 -> blocked" 2 $?
273
+ case "$MSG_DONE" in
274
+ *"main @ ${HASH},工作樹乾淨"*) echo "PASS: DONE block message includes exact expected snapshot" ;;
275
+ *) echo "FAIL: DONE block message should include exact expected snapshot, got: $MSG_DONE"; FAIL=1 ;;
276
+ esac
277
+
278
+ bash "$SCRIPT_DIR/round-start.sh" < /dev/null
279
+ {
280
+ echo "## Round 6 — 2026-09-10T00:25:00+08:00"
281
+ echo ""
282
+ echo "### Summary"
283
+ echo "fixture"
284
+ echo ""
285
+ echo "### Reply"
286
+ echo "fixture reply."
287
+ echo ""
288
+ echo "### Handoff"
289
+ echo "#### 檔案"
290
+ echo "尚未 commit:"
291
+ echo "#### 工作區"
292
+ echo "main @ 0000000,工作樹乾淨"
293
+ echo "#### 現況"
294
+ echo "fixture"
295
+ echo ""
296
+ echo "### Status"
297
+ echo "DONE"
298
+ } > "$DEVLOG_DIR/.round-current.md"
299
+ echo '{}' | bash "$SCRIPT_DIR/enforce-devlog.sh" >/dev/null 2>&1
300
+ assert_exit "DONE with #### 檔案 and stale 工作區 hash -> blocked" 2 $?
301
+
302
+ bash "$SCRIPT_DIR/round-start.sh" < /dev/null
303
+ {
304
+ echo "## Round 6 — 2026-09-10T00:25:00+08:00"
305
+ echo ""
306
+ echo "### Summary"
307
+ echo "fixture"
308
+ echo ""
309
+ echo "### Reply"
310
+ echo "fixture reply."
311
+ echo ""
312
+ echo "### Handoff"
313
+ echo "#### 檔案"
314
+ echo "尚未 commit:"
315
+ echo "#### 工作區"
316
+ echo "main @ ${HASH},工作樹乾淨"
317
+ echo "#### 現況"
318
+ echo "fixture"
319
+ echo ""
320
+ echo "### Status"
321
+ echo "DONE"
322
+ } > "$DEVLOG_DIR/.round-current.md"
323
+ echo '{}' | bash "$SCRIPT_DIR/enforce-devlog.sh" >/dev/null 2>&1
324
+ assert_exit "DONE with #### 檔案 and matching 工作區 -> allowed" 0 $?
325
+ assert_round_merged "DONE with #### 檔案 and matching 工作區"
326
+
327
+ if [ "$FAIL" -eq 0 ]; then
328
+ echo "All checks passed."
329
+ exit 0
330
+ else
331
+ echo "Some checks FAILED."
332
+ exit 1
333
+ fi