gsd-pi 2.44.0-dev.62b5d6c → 2.44.0-dev.73f2fd5

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 (326) hide show
  1. package/README.md +30 -12
  2. package/dist/resources/extensions/gsd/auto/infra-errors.js +3 -0
  3. package/dist/resources/extensions/gsd/auto/phases.js +36 -36
  4. package/dist/resources/extensions/gsd/auto-prompts.js +24 -1
  5. package/dist/resources/extensions/gsd/auto-start.js +10 -0
  6. package/dist/resources/extensions/gsd/auto-timers.js +57 -3
  7. package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -0
  8. package/dist/resources/extensions/gsd/auto-worktree.js +9 -6
  9. package/dist/resources/extensions/gsd/auto.js +30 -3
  10. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +156 -0
  11. package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -12
  12. package/dist/resources/extensions/gsd/commands/catalog.js +6 -1
  13. package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
  14. package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
  15. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
  16. package/dist/resources/extensions/gsd/commands-mcp-status.js +187 -0
  17. package/dist/resources/extensions/gsd/db-writer.js +34 -16
  18. package/dist/resources/extensions/gsd/doctor.js +8 -0
  19. package/dist/resources/extensions/gsd/git-service.js +8 -3
  20. package/dist/resources/extensions/gsd/gsd-db.js +12 -1
  21. package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
  22. package/dist/resources/extensions/gsd/preferences.js +9 -1
  23. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
  24. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  25. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  26. package/dist/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  27. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  28. package/dist/resources/extensions/gsd/provider-error-pause.js +7 -0
  29. package/dist/resources/extensions/gsd/state.js +19 -2
  30. package/dist/resources/extensions/gsd/tools/plan-slice.js +1 -0
  31. package/dist/resources/extensions/gsd/tools/plan-task.js +1 -0
  32. package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -0
  33. package/dist/resources/extensions/gsd/tools/validate-milestone.js +88 -0
  34. package/dist/resources/extensions/gsd/worktree-resolver.js +6 -0
  35. package/dist/resources/extensions/mcp-client/index.js +14 -0
  36. package/dist/web/standalone/.next/BUILD_ID +1 -1
  37. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  38. package/dist/web/standalone/.next/build-manifest.json +2 -2
  39. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  40. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  41. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  49. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/index.html +1 -1
  57. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  64. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  65. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  66. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  67. package/package.json +1 -1
  68. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
  69. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  70. package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
  71. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  72. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
  73. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  74. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
  75. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  76. package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
  77. package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
  78. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
  79. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
  80. package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
  81. package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
  82. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +11 -0
  83. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  84. package/packages/pi-coding-agent/dist/core/model-registry.js +20 -1
  85. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  86. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
  87. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  88. package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
  89. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  90. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  91. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  92. package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
  93. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  94. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
  95. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
  96. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  97. package/packages/pi-coding-agent/dist/main.js +17 -0
  98. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  99. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
  100. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
  101. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
  102. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
  103. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  104. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  105. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +8 -1
  106. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  107. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  108. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  109. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
  110. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  111. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
  112. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
  113. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
  114. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +1 -0
  115. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  116. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +4 -1
  117. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  118. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
  119. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  120. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +13 -2
  121. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  122. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  123. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
  124. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  125. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  126. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -3
  127. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  128. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
  129. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
  130. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
  131. package/packages/pi-coding-agent/src/core/auth-storage.ts +15 -1
  132. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
  133. package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
  134. package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
  135. package/packages/pi-coding-agent/src/core/model-registry.ts +21 -1
  136. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
  137. package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
  138. package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
  139. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
  140. package/packages/pi-coding-agent/src/main.ts +19 -0
  141. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
  142. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
  143. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  144. package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
  145. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
  146. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
  147. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
  148. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
  149. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
  150. package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
  151. package/src/resources/extensions/gsd/auto/phases.ts +45 -48
  152. package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
  153. package/src/resources/extensions/gsd/auto-start.ts +14 -0
  154. package/src/resources/extensions/gsd/auto-timers.ts +64 -3
  155. package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
  156. package/src/resources/extensions/gsd/auto-worktree.ts +9 -6
  157. package/src/resources/extensions/gsd/auto.ts +37 -3
  158. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +148 -0
  159. package/src/resources/extensions/gsd/bootstrap/system-context.ts +48 -11
  160. package/src/resources/extensions/gsd/commands/catalog.ts +6 -1
  161. package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
  162. package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
  163. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
  164. package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
  165. package/src/resources/extensions/gsd/db-writer.ts +39 -17
  166. package/src/resources/extensions/gsd/doctor.ts +7 -1
  167. package/src/resources/extensions/gsd/git-service.ts +6 -2
  168. package/src/resources/extensions/gsd/gsd-db.ts +16 -1
  169. package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
  170. package/src/resources/extensions/gsd/preferences.ts +11 -1
  171. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
  172. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  173. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  174. package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  175. package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  176. package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
  177. package/src/resources/extensions/gsd/state.ts +19 -1
  178. package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
  179. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
  180. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
  181. package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
  182. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
  183. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +465 -523
  184. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
  185. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
  186. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +533 -656
  187. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
  188. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
  189. package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
  190. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
  191. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
  192. package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
  193. package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
  194. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +38 -59
  195. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +228 -263
  196. package/src/resources/extensions/gsd/tests/complete-task.test.ts +250 -302
  197. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
  198. package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
  199. package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
  200. package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
  201. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
  202. package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
  203. package/src/resources/extensions/gsd/tests/db-writer.test.ts +465 -416
  204. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
  205. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +68 -83
  206. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +210 -181
  207. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
  208. package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
  209. package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
  210. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
  211. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +164 -180
  212. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
  213. package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
  214. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +27 -29
  215. package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
  216. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +54 -75
  217. package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +21 -32
  218. package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
  219. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +38 -44
  220. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
  221. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
  222. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +54 -60
  223. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
  224. package/src/resources/extensions/gsd/tests/doctor.test.ts +104 -134
  225. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
  226. package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
  227. package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
  228. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
  229. package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
  230. package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +30 -42
  231. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +198 -206
  232. package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
  233. package/src/resources/extensions/gsd/tests/git-service.test.ts +285 -388
  234. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
  235. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
  236. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
  237. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
  238. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +81 -103
  239. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
  240. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
  241. package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
  242. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -102
  243. package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
  244. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
  245. package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
  246. package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
  247. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
  248. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
  249. package/src/resources/extensions/gsd/tests/knowledge.test.ts +89 -0
  250. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +150 -194
  251. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
  252. package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
  253. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
  254. package/src/resources/extensions/gsd/tests/memory-store.test.ts +80 -93
  255. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
  256. package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
  257. package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +83 -93
  258. package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
  259. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
  260. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
  261. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +87 -96
  262. package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +125 -164
  263. package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
  264. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
  265. package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
  266. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
  267. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
  268. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
  269. package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
  270. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
  271. package/src/resources/extensions/gsd/tests/parsers.test.ts +546 -611
  272. package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
  273. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
  274. package/src/resources/extensions/gsd/tests/preferences.test.ts +27 -0
  275. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +11 -7
  276. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
  277. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
  278. package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
  279. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
  280. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
  281. package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
  282. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
  283. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
  284. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
  285. package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
  286. package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
  287. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
  288. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
  289. package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
  290. package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
  291. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
  292. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +19 -26
  293. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
  294. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
  295. package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
  296. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
  297. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
  298. package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
  299. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
  300. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +10 -11
  301. package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
  302. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
  303. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
  304. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
  305. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
  306. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
  307. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
  308. package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
  309. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
  310. package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
  311. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
  312. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
  313. package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
  314. package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
  315. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
  316. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
  317. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
  318. package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
  319. package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -0
  320. package/src/resources/extensions/gsd/tools/plan-task.ts +2 -0
  321. package/src/resources/extensions/gsd/tools/replan-slice.ts +3 -0
  322. package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
  323. package/src/resources/extensions/gsd/worktree-resolver.ts +7 -0
  324. package/src/resources/extensions/mcp-client/index.ts +20 -0
  325. /package/dist/web/standalone/.next/static/{fOnWQBjWXMKUs4bqTg530 → kxxAA66bah_yhPYqLBHE2}/_buildManifest.js +0 -0
  326. /package/dist/web/standalone/.next/static/{fOnWQBjWXMKUs4bqTg530 → kxxAA66bah_yhPYqLBHE2}/_ssgManifest.js +0 -0
@@ -66,7 +66,7 @@ function createFixture(): string {
66
66
  return base;
67
67
  }
68
68
 
69
- test("dispatch uat targets last completed slice, not activeSlice (#1693)", async () => {
69
+ test("dispatch uat targets last completed slice, not activeSlice (#1693)", async (t) => {
70
70
  const base = createFixture();
71
71
  invalidateStateCache();
72
72
 
@@ -88,31 +88,29 @@ test("dispatch uat targets last completed slice, not activeSlice (#1693)", async
88
88
  },
89
89
  } as any;
90
90
 
91
- try {
92
- await dispatchDirectPhase(ctx, pi, "uat", base);
93
-
94
- // Should have dispatched (sendMessage called)
95
- assert.ok(sentPrompt, "sendMessage should have been called with a prompt");
96
-
97
- // The dispatch notification should reference M001/S01 (completed), not M001/S02 (active)
98
- const dispatchNotification = notifications.find(n => n.message.startsWith("Dispatching"));
99
- assert.ok(dispatchNotification, "dispatch notification should be present");
100
- assert.match(
101
- dispatchNotification.message,
102
- /M001\/S01/,
103
- "dispatch should target completed slice S01, not active slice S02",
104
- );
105
- assert.doesNotMatch(
106
- dispatchNotification.message,
107
- /M001\/S02/,
108
- "dispatch should NOT target active (next incomplete) slice S02",
109
- );
110
- } finally {
111
- rmSync(base, { recursive: true, force: true });
112
- }
91
+ t.after(() => rmSync(base, { recursive: true, force: true }));
92
+
93
+ await dispatchDirectPhase(ctx, pi, "uat", base);
94
+
95
+ // Should have dispatched (sendMessage called)
96
+ assert.ok(sentPrompt, "sendMessage should have been called with a prompt");
97
+
98
+ // The dispatch notification should reference M001/S01 (completed), not M001/S02 (active)
99
+ const dispatchNotification = notifications.find(n => n.message.startsWith("Dispatching"));
100
+ assert.ok(dispatchNotification, "dispatch notification should be present");
101
+ assert.match(
102
+ dispatchNotification.message,
103
+ /M001\/S01/,
104
+ "dispatch should target completed slice S01, not active slice S02",
105
+ );
106
+ assert.doesNotMatch(
107
+ dispatchNotification.message,
108
+ /M001\/S02/,
109
+ "dispatch should NOT target active (next incomplete) slice S02",
110
+ );
113
111
  });
114
112
 
115
- test("dispatch uat warns when no completed slices exist", async () => {
113
+ test("dispatch uat warns when no completed slices exist", async (t) => {
116
114
  const base = mkdtempSync(join(tmpdir(), "gsd-dispatch-uat-none-"));
117
115
  invalidateStateCache();
118
116
 
@@ -164,13 +162,11 @@ test("dispatch uat warns when no completed slices exist", async () => {
164
162
  },
165
163
  } as any;
166
164
 
167
- try {
168
- await dispatchDirectPhase(ctx, pi, "uat", base);
165
+ t.after(() => rmSync(base, { recursive: true, force: true }));
166
+
167
+ await dispatchDirectPhase(ctx, pi, "uat", base);
169
168
 
170
- const warning = notifications.find(n => n.level === "warning");
171
- assert.ok(warning, "should show a warning notification");
172
- assert.match(warning.message, /no completed slices/, "warning should mention no completed slices");
173
- } finally {
174
- rmSync(base, { recursive: true, force: true });
175
- }
169
+ const warning = notifications.find(n => n.level === "warning");
170
+ assert.ok(warning, "should show a warning notification");
171
+ assert.match(warning.message, /no completed slices/, "warning should mention no completed slices");
176
172
  });
@@ -56,35 +56,33 @@ Done.
56
56
  `);
57
57
  }
58
58
 
59
- test("doctor does not report any reconciliation issue codes", async () => {
59
+ test("doctor does not report any reconciliation issue codes", async (t) => {
60
60
  const tmp = makeTmp("no-reconciliation");
61
- try {
62
- buildScaffold(tmp);
63
-
64
- const report = await runGSDDoctor(tmp, { fix: true, fixLevel: "task" });
65
-
66
- const REMOVED_CODES = [
67
- "task_done_missing_summary",
68
- "task_summary_without_done_checkbox",
69
- "all_tasks_done_missing_slice_summary",
70
- "all_tasks_done_missing_slice_uat",
71
- "all_tasks_done_roadmap_not_checked",
72
- "slice_checked_missing_summary",
73
- "slice_checked_missing_uat",
74
- ];
75
-
76
- const codes = report.issues.map(i => i.code);
77
- for (const removed of REMOVED_CODES) {
78
- assert.ok(!codes.includes(removed as any), `should NOT report removed code: ${removed}`);
79
- }
80
-
81
- // No summary or UAT stubs should be created
82
- const sliceSummaryPath = join(tmp, ".gsd", "milestones", "M001", "slices", "S01", "S01-SUMMARY.md");
83
- assert.ok(!existsSync(sliceSummaryPath), "should NOT have created summary stub");
84
-
85
- const sliceUatPath = join(tmp, ".gsd", "milestones", "M001", "slices", "S01", "S01-UAT.md");
86
- assert.ok(!existsSync(sliceUatPath), "should NOT have created UAT stub");
87
- } finally {
88
- rmSync(tmp, { recursive: true, force: true });
61
+ t.after(() => rmSync(tmp, { recursive: true, force: true }));
62
+
63
+ buildScaffold(tmp);
64
+
65
+ const report = await runGSDDoctor(tmp, { fix: true, fixLevel: "task" });
66
+
67
+ const REMOVED_CODES = [
68
+ "task_done_missing_summary",
69
+ "task_summary_without_done_checkbox",
70
+ "all_tasks_done_missing_slice_summary",
71
+ "all_tasks_done_missing_slice_uat",
72
+ "all_tasks_done_roadmap_not_checked",
73
+ "slice_checked_missing_summary",
74
+ "slice_checked_missing_uat",
75
+ ];
76
+
77
+ const codes = report.issues.map(i => i.code);
78
+ for (const removed of REMOVED_CODES) {
79
+ assert.ok(!codes.includes(removed as any), `should NOT report removed code: ${removed}`);
89
80
  }
81
+
82
+ // No summary or UAT stubs should be created
83
+ const sliceSummaryPath = join(tmp, ".gsd", "milestones", "M001", "slices", "S01", "S01-SUMMARY.md");
84
+ assert.ok(!existsSync(sliceSummaryPath), "should NOT have created summary stub");
85
+
86
+ const sliceUatPath = join(tmp, ".gsd", "milestones", "M001", "slices", "S01", "S01-UAT.md");
87
+ assert.ok(!existsSync(sliceUatPath), "should NOT have created UAT stub");
90
88
  });
@@ -12,7 +12,7 @@ import { join } from "node:path";
12
12
  import { tmpdir } from "node:os";
13
13
  import { runGSDDoctor } from "../doctor.js";
14
14
 
15
- test("doctor fix=true sanitizes em-dash in milestone title", async () => {
15
+ test("doctor fix=true sanitizes em-dash in milestone title", async (t) => {
16
16
  const tmpBase = mkdtempSync(join(tmpdir(), "gsd-doctor-delim-"));
17
17
  const gsd = join(tmpBase, ".gsd");
18
18
  const mDir = join(gsd, "milestones", "M001");
@@ -34,33 +34,31 @@ test("doctor fix=true sanitizes em-dash in milestone title", async () => {
34
34
  writeFileSync(join(sDir, "S01-PLAN.md"), `# S01: Initial Setup\n\n## Tasks\n- [ ] **T01: Scaffold** \`est:15m\`\n`);
35
35
  writeFileSync(join(tDir, "T01-PLAN.md"), "# T01: Scaffold\n");
36
36
 
37
- try {
38
- // Run doctor with fix=true
39
- const report = await runGSDDoctor(tmpBase, { fix: true });
40
-
41
- // The em-dash should have been replaced
42
- const fixed = readFileSync(join(mDir, "M001-ROADMAP.md"), "utf-8");
43
- const h1 = fixed.split("\n").find(l => l.startsWith("# "))!;
44
- assert.ok(h1, "H1 line should exist");
45
- assert.ok(!h1.includes("\u2014"), "em-dash should be replaced");
46
- assert.ok(!h1.includes("\u2013"), "en-dash should be replaced");
47
- assert.ok(h1.includes("-"), "should contain ASCII hyphen as replacement");
48
-
49
- // Should have recorded the fix
50
- assert.ok(
51
- report.fixesApplied.some(f => f.includes("sanitized")),
52
- `fixesApplied should mention sanitization, got: ${JSON.stringify(report.fixesApplied)}`,
53
- );
54
-
55
- // The issue should NOT appear in the report (it was fixed)
56
- const delimIssues = report.issues.filter(i => i.code === "delimiter_in_title" && i.unitId === "M001");
57
- assert.equal(delimIssues.length, 0, "fixed issue should not appear in issues list");
58
- } finally {
59
- rmSync(tmpBase, { recursive: true, force: true });
60
- }
37
+ t.after(() => rmSync(tmpBase, { recursive: true, force: true }));
38
+
39
+ // Run doctor with fix=true
40
+ const report = await runGSDDoctor(tmpBase, { fix: true });
41
+
42
+ // The em-dash should have been replaced
43
+ const fixed = readFileSync(join(mDir, "M001-ROADMAP.md"), "utf-8");
44
+ const h1 = fixed.split("\n").find(l => l.startsWith("# "))!;
45
+ assert.ok(h1, "H1 line should exist");
46
+ assert.ok(!h1.includes("\u2014"), "em-dash should be replaced");
47
+ assert.ok(!h1.includes("\u2013"), "en-dash should be replaced");
48
+ assert.ok(h1.includes("-"), "should contain ASCII hyphen as replacement");
49
+
50
+ // Should have recorded the fix
51
+ assert.ok(
52
+ report.fixesApplied.some(f => f.includes("sanitized")),
53
+ `fixesApplied should mention sanitization, got: ${JSON.stringify(report.fixesApplied)}`,
54
+ );
55
+
56
+ // The issue should NOT appear in the report (it was fixed)
57
+ const delimIssues = report.issues.filter(i => i.code === "delimiter_in_title" && i.unitId === "M001");
58
+ assert.equal(delimIssues.length, 0, "fixed issue should not appear in issues list");
61
59
  });
62
60
 
63
- test("doctor fix=false still reports delimiter_in_title as warning", async () => {
61
+ test("doctor fix=false still reports delimiter_in_title as warning", async (t) => {
64
62
  const tmpBase = mkdtempSync(join(tmpdir(), "gsd-doctor-delim-nf-"));
65
63
  const gsd = join(tmpBase, ".gsd");
66
64
  const mDir = join(gsd, "milestones", "M001");
@@ -72,16 +70,14 @@ test("doctor fix=false still reports delimiter_in_title as warning", async () =>
72
70
  writeFileSync(join(sDir, "S01-PLAN.md"), `# S01: Setup\n\n## Tasks\n- [ ] **T01: Init** \`est:10m\`\n`);
73
71
  writeFileSync(join(tDir, "T01-PLAN.md"), "# T01: Init\n");
74
72
 
75
- try {
76
- const report = await runGSDDoctor(tmpBase, { fix: false });
77
- const delimIssues = report.issues.filter(i => i.code === "delimiter_in_title");
78
- assert.ok(delimIssues.length > 0, "should report delimiter_in_title as issue when fix=false");
79
- assert.equal(delimIssues[0].severity, "warning");
80
-
81
- // File should be unchanged
82
- const content = readFileSync(join(mDir, "M001-ROADMAP.md"), "utf-8");
83
- assert.ok(content.includes("\u2014"), "file should not be modified when fix=false");
84
- } finally {
85
- rmSync(tmpBase, { recursive: true, force: true });
86
- }
73
+ t.after(() => rmSync(tmpBase, { recursive: true, force: true }));
74
+
75
+ const report = await runGSDDoctor(tmpBase, { fix: false });
76
+ const delimIssues = report.issues.filter(i => i.code === "delimiter_in_title");
77
+ assert.ok(delimIssues.length > 0, "should report delimiter_in_title as issue when fix=false");
78
+ assert.equal(delimIssues[0].severity, "warning");
79
+
80
+ // File should be unchanged
81
+ const content = readFileSync(join(mDir, "M001-ROADMAP.md"), "utf-8");
82
+ assert.ok(content.includes("\u2014"), "file should not be modified when fix=false");
87
83
  });
@@ -1,13 +1,11 @@
1
+ import { describe, test } from 'node:test';
2
+ import assert from 'node:assert/strict';
1
3
  import { mkdtempSync, mkdirSync, rmSync, writeFileSync, existsSync } from "node:fs";
2
4
  import { join } from "node:path";
3
5
  import { tmpdir } from "node:os";
4
6
 
5
7
  import { runGSDDoctor } from "../doctor.js";
6
8
  import { formatDoctorReportJson } from "../doctor-format.js";
7
- import { createTestContext } from "./test-helpers.ts";
8
-
9
- const { assertEq, assertTrue, assertMatch, report } = createTestContext();
10
-
11
9
  // ── Helpers ─────────────────────────────────────────────────────────────────
12
10
 
13
11
  function makeBase(): { base: string; gsd: string; mDir: string } {
@@ -30,41 +28,38 @@ function writeSlice(mDir: string, sliceId: string, planContent: string): string
30
28
  return sDir;
31
29
  }
32
30
 
33
- async function main(): Promise<void> {
31
+ describe('doctor-enhancements', async () => {
34
32
  // ── 1. Circular dependency detection ──────────────────────────────────────
35
- console.log("\n=== circular dependency detection ===");
36
- {
33
+ test('circular dependency detection', async () => {
37
34
  const { base, mDir } = makeBase();
38
35
  writeRoadmap(mDir, `# M001: Circular Test\n\n## Slices\n- [ ] **S01: Slice A** \`risk:low\` \`depends:[S02]\`\n > After this: done\n- [ ] **S02: Slice B** \`risk:low\` \`depends:[S01]\`\n > After this: done\n`);
39
36
  writeSlice(mDir, "S01", "# S01: Slice A\n\n**Goal:** A\n**Demo:** A\n\n## Tasks\n- [ ] **T01: Task** `est:10m`\n Pending.\n");
40
37
  writeSlice(mDir, "S02", "# S02: Slice B\n\n**Goal:** B\n**Demo:** B\n\n## Tasks\n- [ ] **T01: Task** `est:10m`\n Pending.\n");
41
38
 
42
39
  const result = await runGSDDoctor(base, { fix: false });
43
- assertTrue(
40
+ assert.ok(
44
41
  result.issues.some(i => i.code === "circular_slice_dependency"),
45
42
  "detects circular dependency S01 → S02 → S01",
46
43
  );
47
44
  rmSync(base, { recursive: true, force: true });
48
- }
45
+ });
49
46
 
50
47
  // ── 2. Duplicate task IDs ──────────────────────────────────────────────────
51
- console.log("\n=== duplicate task IDs ===");
52
- {
48
+ test('duplicate task IDs', async () => {
53
49
  const { base, mDir } = makeBase();
54
50
  writeRoadmap(mDir, `# M001: Dup Test\n\n## Slices\n- [ ] **S01: Slice** \`risk:low\` \`depends:[]\`\n > After this: done\n`);
55
51
  writeSlice(mDir, "S01", "# S01: Slice\n\n**Goal:** G\n**Demo:** D\n\n## Tasks\n- [ ] **T01: First** `est:10m`\n Task one.\n- [ ] **T01: Duplicate** `est:10m`\n Task dup.\n");
56
52
 
57
53
  const result = await runGSDDoctor(base, { fix: false });
58
- assertTrue(
54
+ assert.ok(
59
55
  result.issues.some(i => i.code === "duplicate_task_id"),
60
56
  "detects duplicate task ID T01",
61
57
  );
62
58
  rmSync(base, { recursive: true, force: true });
63
- }
59
+ });
64
60
 
65
61
  // ── 3. Orphaned slice directory ──────────────────────────────────────────
66
- console.log("\n=== orphaned slice directory ===");
67
- {
62
+ test('orphaned slice directory', async () => {
68
63
  const { base, mDir } = makeBase();
69
64
  writeRoadmap(mDir, `# M001: Orphan Test\n\n## Slices\n- [ ] **S01: Slice** \`risk:low\` \`depends:[]\`\n > After this: done\n`);
70
65
  writeSlice(mDir, "S01", "# S01: Slice\n\n**Goal:** G\n**Demo:** D\n\n## Tasks\n- [ ] **T01: Task** `est:10m`\n Pending.\n");
@@ -72,16 +67,15 @@ async function main(): Promise<void> {
72
67
  mkdirSync(join(mDir, "slices", "S99"), { recursive: true });
73
68
 
74
69
  const result = await runGSDDoctor(base, { fix: false });
75
- assertTrue(
70
+ assert.ok(
76
71
  result.issues.some(i => i.code === "orphaned_slice_directory" && i.message.includes("S99")),
77
72
  "detects orphaned slice directory S99",
78
73
  );
79
74
  rmSync(base, { recursive: true, force: true });
80
- }
75
+ });
81
76
 
82
77
  // ── 4. Task file not in plan ───────────────────────────────────────────────
83
- console.log("\n=== task file not in plan ===");
84
- {
78
+ test('task file not in plan', async () => {
85
79
  const { base, mDir } = makeBase();
86
80
  writeRoadmap(mDir, `# M001: Extra Task Test\n\n## Slices\n- [ ] **S01: Slice** \`risk:low\` \`depends:[]\`\n > After this: done\n`);
87
81
  const sDir = writeSlice(mDir, "S01", "# S01: Slice\n\n**Goal:** G\n**Demo:** D\n\n## Tasks\n- [x] **T01: Task** `est:10m`\n Done.\n");
@@ -91,16 +85,15 @@ async function main(): Promise<void> {
91
85
  writeFileSync(join(sDir, "tasks", "T99-SUMMARY.md"), "---\nstatus: done\n---\n# T99\nExtra.\n");
92
86
 
93
87
  const result = await runGSDDoctor(base, { fix: false });
94
- assertTrue(
88
+ assert.ok(
95
89
  result.issues.some(i => i.code === "task_file_not_in_plan" && i.message.includes("T99")),
96
90
  "detects task summary T99 not in plan",
97
91
  );
98
92
  rmSync(base, { recursive: true, force: true });
99
- }
93
+ });
100
94
 
101
95
  // ── 5. Stale REPLAN file ────────────────────────────────────────────────────
102
- console.log("\n=== stale REPLAN detection ===");
103
- {
96
+ test('stale REPLAN detection', async () => {
104
97
  const { base, mDir } = makeBase();
105
98
  writeRoadmap(mDir, `# M001: Replan Test\n\n## Slices\n- [ ] **S01: Slice** \`risk:low\` \`depends:[]\`\n > After this: done\n`);
106
99
  const sDir = writeSlice(mDir, "S01", "# S01: Slice\n\n**Goal:** G\n**Demo:** D\n\n## Tasks\n- [x] **T01: Task** `est:10m`\n Done.\n");
@@ -109,16 +102,15 @@ async function main(): Promise<void> {
109
102
  writeFileSync(join(sDir, "S01-REPLAN.md"), "# S01 REPLAN\nSomething changed.\n");
110
103
 
111
104
  const result = await runGSDDoctor(base, { fix: false });
112
- assertTrue(
105
+ assert.ok(
113
106
  result.issues.some(i => i.code === "stale_replan_file"),
114
107
  "detects stale REPLAN when all tasks are done",
115
108
  );
116
109
  rmSync(base, { recursive: true, force: true });
117
- }
110
+ });
118
111
 
119
112
  // ── 6. Metrics ledger corrupt ───────────────────────────────────────────────
120
- console.log("\n=== metrics ledger corrupt ===");
121
- {
113
+ test('metrics ledger corrupt', async () => {
122
114
  const { base, gsd, mDir } = makeBase();
123
115
  writeRoadmap(mDir, `# M001: Metrics Test\n\n## Slices\n- [ ] **S01: Slice** \`risk:low\` \`depends:[]\`\n > After this: done\n`);
124
116
  writeSlice(mDir, "S01", "# S01: Slice\n\n**Goal:** G\n**Demo:** D\n\n## Tasks\n- [ ] **T01: Task** `est:10m`\n Pending.\n");
@@ -126,16 +118,15 @@ async function main(): Promise<void> {
126
118
  writeFileSync(join(gsd, "metrics.json"), '{"version":2,"data":[]}');
127
119
 
128
120
  const result = await runGSDDoctor(base, { fix: false });
129
- assertTrue(
121
+ assert.ok(
130
122
  result.issues.some(i => i.code === "metrics_ledger_corrupt"),
131
123
  "detects corrupt metrics ledger (version != 1)",
132
124
  );
133
125
  rmSync(base, { recursive: true, force: true });
134
- }
126
+ });
135
127
 
136
128
  // ── 7. Large planning file ──────────────────────────────────────────────────
137
- console.log("\n=== large planning file ===");
138
- {
129
+ test('large planning file', async () => {
139
130
  const { base, mDir } = makeBase();
140
131
  writeRoadmap(mDir, `# M001: Large File Test\n\n## Slices\n- [ ] **S01: Slice** \`risk:low\` \`depends:[]\`\n > After this: done\n`);
141
132
  const sDir = writeSlice(mDir, "S01", "# S01: Slice\n\n**Goal:** G\n**Demo:** D\n\n## Tasks\n- [ ] **T01: Task** `est:10m`\n Pending.\n");
@@ -144,16 +135,15 @@ async function main(): Promise<void> {
144
135
  writeFileSync(join(sDir, "BIGFILE.md"), bigContent);
145
136
 
146
137
  const result = await runGSDDoctor(base, { fix: false });
147
- assertTrue(
138
+ assert.ok(
148
139
  result.issues.some(i => i.code === "large_planning_file"),
149
140
  "detects large planning file over 100KB",
150
141
  );
151
142
  rmSync(base, { recursive: true, force: true });
152
- }
143
+ });
153
144
 
154
145
  // ── 8. Future timestamp ─────────────────────────────────────────────────────
155
- console.log("\n=== future timestamp ===");
156
- {
146
+ test('future timestamp', async () => {
157
147
  const { base, mDir } = makeBase();
158
148
  writeRoadmap(mDir, `# M001: Timestamp Test\n\n## Slices\n- [ ] **S01: Slice** \`risk:low\` \`depends:[]\`\n > After this: done\n`);
159
149
  const sDir = writeSlice(mDir, "S01", "# S01: Slice\n\n**Goal:** G\n**Demo:** D\n\n## Tasks\n- [x] **T01: Task** `est:10m`\n Done.\n");
@@ -165,16 +155,15 @@ async function main(): Promise<void> {
165
155
  );
166
156
 
167
157
  const result = await runGSDDoctor(base, { fix: false });
168
- assertTrue(
158
+ assert.ok(
169
159
  result.issues.some(i => i.code === "future_timestamp"),
170
160
  "detects future completed_at timestamp",
171
161
  );
172
162
  rmSync(base, { recursive: true, force: true });
173
- }
163
+ });
174
164
 
175
165
  // ── 9. JSON output format ───────────────────────────────────────────────────
176
- console.log("\n=== JSON output format ===");
177
- {
166
+ test('JSON output format', async () => {
178
167
  const { base, mDir } = makeBase();
179
168
  writeRoadmap(mDir, `# M001: JSON Test\n\n## Slices\n- [ ] **S01: Slice** \`risk:low\` \`depends:[]\`\n > After this: done\n`);
180
169
  writeSlice(mDir, "S01", "# S01: Slice\n\n**Goal:** G\n**Demo:** D\n\n## Tasks\n- [ ] **T01: Task** `est:10m`\n Pending.\n");
@@ -189,19 +178,18 @@ async function main(): Promise<void> {
189
178
  parsed = null;
190
179
  }
191
180
 
192
- assertTrue(parsed !== null, "formatDoctorReportJson produces valid JSON");
193
- assertTrue(typeof (parsed as Record<string, unknown>)?.ok === "boolean", "JSON has ok field");
194
- assertTrue(Array.isArray((parsed as Record<string, unknown>)?.issues), "JSON has issues array");
195
- assertTrue(Array.isArray((parsed as Record<string, unknown>)?.fixesApplied), "JSON has fixesApplied array");
196
- assertTrue(typeof (parsed as Record<string, unknown>)?.generatedAt === "string", "JSON has generatedAt field");
197
- assertTrue(typeof (parsed as Record<string, unknown>)?.summary === "object", "JSON has summary object");
181
+ assert.ok(parsed !== null, "formatDoctorReportJson produces valid JSON");
182
+ assert.ok(typeof (parsed as Record<string, unknown>)?.ok === "boolean", "JSON has ok field");
183
+ assert.ok(Array.isArray((parsed as Record<string, unknown>)?.issues), "JSON has issues array");
184
+ assert.ok(Array.isArray((parsed as Record<string, unknown>)?.fixesApplied), "JSON has fixesApplied array");
185
+ assert.ok(typeof (parsed as Record<string, unknown>)?.generatedAt === "string", "JSON has generatedAt field");
186
+ assert.ok(typeof (parsed as Record<string, unknown>)?.summary === "object", "JSON has summary object");
198
187
 
199
188
  rmSync(base, { recursive: true, force: true });
200
- }
189
+ });
201
190
 
202
191
  // ── 10. Dry-run mode ────────────────────────────────────────────────────────
203
- console.log("\n=== dry-run mode ===");
204
- {
192
+ test('dry-run mode', async () => {
205
193
  const { base, mDir } = makeBase();
206
194
  writeRoadmap(mDir, `# M001: Dry Run Test\n\n## Slices\n- [ ] **S01: Slice** \`risk:low\` \`depends:[]\`\n > After this: done\n`);
207
195
  writeSlice(mDir, "S01", "# S01: Slice\n\n**Goal:** G\n**Demo:** D\n\n## Tasks\n- [ ] **T01: Task** `est:10m`\n Pending.\n");
@@ -209,32 +197,30 @@ async function main(): Promise<void> {
209
197
  const result = await runGSDDoctor(base, { fix: true, dryRun: true });
210
198
  // dry-run with fix:true still runs the doctor; shouldFix() returns false
211
199
  // so no reconciliation fixes are applied through that path
212
- assertTrue(result.issues !== undefined, "dry-run still produces issue list");
213
- assertTrue(Array.isArray(result.fixesApplied), "dry-run report has fixesApplied array");
200
+ assert.ok(result.issues !== undefined, "dry-run still produces issue list");
201
+ assert.ok(Array.isArray(result.fixesApplied), "dry-run report has fixesApplied array");
214
202
 
215
203
  rmSync(base, { recursive: true, force: true });
216
- }
204
+ });
217
205
 
218
206
  // ── 11. Per-check timing ─────────────────────────────────────────────────────
219
- console.log("\n=== per-check timing ===");
220
- {
207
+ test('per-check timing', async () => {
221
208
  const { base, mDir } = makeBase();
222
209
  writeRoadmap(mDir, `# M001: Timing Test\n\n## Slices\n- [ ] **S01: Slice** \`risk:low\` \`depends:[]\`\n > After this: done\n`);
223
210
  writeSlice(mDir, "S01", "# S01: Slice\n\n**Goal:** G\n**Demo:** D\n\n## Tasks\n- [ ] **T01: Task** `est:10m`\n Pending.\n");
224
211
 
225
212
  const result = await runGSDDoctor(base, { fix: false });
226
- assertTrue(result.timing !== undefined, "report includes timing");
227
- assertTrue(typeof result.timing?.git === "number", "timing.git is a number");
228
- assertTrue(typeof result.timing?.runtime === "number", "timing.runtime is a number");
229
- assertTrue(typeof result.timing?.environment === "number", "timing.environment is a number");
230
- assertTrue(typeof result.timing?.gsdState === "number", "timing.gsdState is a number");
213
+ assert.ok(result.timing !== undefined, "report includes timing");
214
+ assert.ok(typeof result.timing?.git === "number", "timing.git is a number");
215
+ assert.ok(typeof result.timing?.runtime === "number", "timing.runtime is a number");
216
+ assert.ok(typeof result.timing?.environment === "number", "timing.environment is a number");
217
+ assert.ok(typeof result.timing?.gsdState === "number", "timing.gsdState is a number");
231
218
 
232
219
  rmSync(base, { recursive: true, force: true });
233
- }
220
+ });
234
221
 
235
222
  // ── 12. Doctor history ───────────────────────────────────────────────────────
236
- console.log("\n=== doctor history ===");
237
- {
223
+ test('doctor history', async () => {
238
224
  const { base, gsd, mDir } = makeBase();
239
225
  writeRoadmap(mDir, `# M001: History Test\n\n## Slices\n- [ ] **S01: Slice** \`risk:low\` \`depends:[]\`\n > After this: done\n`);
240
226
  writeSlice(mDir, "S01", "# S01: Slice\n\n**Goal:** G\n**Demo:** D\n\n## Tasks\n- [ ] **T01: Task** `est:10m`\n Pending.\n");
@@ -242,23 +228,16 @@ async function main(): Promise<void> {
242
228
  await runGSDDoctor(base, { fix: false });
243
229
 
244
230
  const historyPath = join(gsd, "doctor-history.jsonl");
245
- assertTrue(existsSync(historyPath), "doctor-history.jsonl is created after run");
231
+ assert.ok(existsSync(historyPath), "doctor-history.jsonl is created after run");
246
232
 
247
233
  const { readDoctorHistory } = await import("../doctor.js");
248
234
  const history = await readDoctorHistory(base);
249
- assertTrue(history.length >= 1, "history has at least one entry");
250
- assertTrue(typeof history[0]?.ts === "string", "history entry has ts field");
251
- assertTrue(typeof history[0]?.ok === "boolean", "history entry has ok field");
252
- assertTrue(typeof history[0]?.errors === "number", "history entry has errors count");
253
- assertTrue(Array.isArray(history[0]?.codes), "history entry has codes array");
235
+ assert.ok(history.length >= 1, "history has at least one entry");
236
+ assert.ok(typeof history[0]?.ts === "string", "history entry has ts field");
237
+ assert.ok(typeof history[0]?.ok === "boolean", "history entry has ok field");
238
+ assert.ok(typeof history[0]?.errors === "number", "history entry has errors count");
239
+ assert.ok(Array.isArray(history[0]?.codes), "history entry has codes array");
254
240
 
255
241
  rmSync(base, { recursive: true, force: true });
256
- }
257
-
258
- report();
259
- }
260
-
261
- main().catch(err => {
262
- console.error(err);
263
- process.exit(1);
242
+ });
264
243
  });