gsd-pi 2.80.0-dev.c5f2443b3 → 2.80.0-dev.e146beb20

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 (342) hide show
  1. package/dist/resources/.managed-resources-content-hash +1 -1
  2. package/dist/resources/GSD-WORKFLOW.md +2 -2
  3. package/dist/resources/extensions/github-sync/templates.js +39 -8
  4. package/dist/resources/extensions/gsd/auto/loop.js +48 -10
  5. package/dist/resources/extensions/gsd/auto/phases.js +37 -30
  6. package/dist/resources/extensions/gsd/auto/run-unit.js +19 -15
  7. package/dist/resources/extensions/gsd/auto-dashboard.js +51 -15
  8. package/dist/resources/extensions/gsd/auto-dispatch.js +10 -0
  9. package/dist/resources/extensions/gsd/auto-post-unit.js +10 -10
  10. package/dist/resources/extensions/gsd/auto-prompts.js +111 -1
  11. package/dist/resources/extensions/gsd/auto-recovery.js +154 -8
  12. package/dist/resources/extensions/gsd/auto-start.js +2 -3
  13. package/dist/resources/extensions/gsd/auto.js +9 -1
  14. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +15 -1
  15. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -1
  16. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +129 -1
  17. package/dist/resources/extensions/gsd/clean-root-preflight.js +42 -4
  18. package/dist/resources/extensions/gsd/commands/dispatcher.js +5 -0
  19. package/dist/resources/extensions/gsd/commands-extract-learnings.js +17 -12
  20. package/dist/resources/extensions/gsd/crash-recovery.js +56 -10
  21. package/dist/resources/extensions/gsd/custom-workflow-engine.js +22 -2
  22. package/dist/resources/extensions/gsd/db-base-schema.js +14 -0
  23. package/dist/resources/extensions/gsd/db-migration-steps.js +16 -0
  24. package/dist/resources/extensions/gsd/detection.js +106 -0
  25. package/dist/resources/extensions/gsd/graph.js +9 -3
  26. package/dist/resources/extensions/gsd/gsd-db.js +102 -2
  27. package/dist/resources/extensions/gsd/guided-flow.js +49 -12
  28. package/dist/resources/extensions/gsd/planning-path-scope.js +26 -0
  29. package/dist/resources/extensions/gsd/pr-evidence.js +57 -16
  30. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +7 -8
  31. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  32. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  33. package/dist/resources/extensions/gsd/safety/evidence-collector.js +10 -2
  34. package/dist/resources/extensions/gsd/tools/plan-slice.js +9 -0
  35. package/dist/resources/extensions/gsd/tools/plan-task.js +9 -0
  36. package/dist/resources/extensions/gsd/unit-runtime.js +11 -0
  37. package/dist/resources/extensions/gsd/working-output-messages.js +64 -0
  38. package/dist/resources/extensions/gsd/worktree-manager.js +16 -14
  39. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  40. package/dist/web/standalone/.next/BUILD_ID +1 -1
  41. package/dist/web/standalone/.next/app-path-routes-manifest.json +16 -16
  42. package/dist/web/standalone/.next/build-manifest.json +3 -3
  43. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  44. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  45. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  46. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  54. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  62. package/dist/web/standalone/.next/server/app/index.html +1 -1
  63. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app-paths-manifest.json +16 -16
  70. package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
  71. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  72. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  73. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  74. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  75. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  76. package/dist/web/standalone/.next/static/chunks/{8336.6f6f30e410419aff.js → 8336.631939fb583761fa.js} +1 -1
  77. package/dist/web/standalone/.next/static/chunks/{webpack-d82dbee6356c1733.js → webpack-0481f1221120a7c6.js} +1 -1
  78. package/package.json +10 -6
  79. package/packages/contracts/package.json +1 -1
  80. package/packages/pi-ai/dist/models/fake-model.d.ts +12 -0
  81. package/packages/pi-ai/dist/models/fake-model.d.ts.map +1 -0
  82. package/packages/pi-ai/dist/models/fake-model.js +27 -0
  83. package/packages/pi-ai/dist/models/fake-model.js.map +1 -0
  84. package/packages/pi-ai/dist/models/index.d.ts.map +1 -1
  85. package/packages/pi-ai/dist/models/index.js +8 -0
  86. package/packages/pi-ai/dist/models/index.js.map +1 -1
  87. package/packages/pi-ai/dist/providers/fake.d.ts +42 -0
  88. package/packages/pi-ai/dist/providers/fake.d.ts.map +1 -0
  89. package/packages/pi-ai/dist/providers/fake.js +319 -0
  90. package/packages/pi-ai/dist/providers/fake.js.map +1 -0
  91. package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
  92. package/packages/pi-ai/dist/providers/register-builtins.js +24 -0
  93. package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
  94. package/packages/pi-ai/src/models/fake-model.ts +30 -0
  95. package/packages/pi-ai/src/models/index.ts +9 -0
  96. package/packages/pi-ai/src/providers/fake.ts +376 -0
  97. package/packages/pi-ai/src/providers/register-builtins.ts +23 -0
  98. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  99. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +74 -0
  100. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  101. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts +15 -0
  102. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts.map +1 -0
  103. package/packages/pi-coding-agent/dist/core/db-snapshot.js +66 -0
  104. package/packages/pi-coding-agent/dist/core/db-snapshot.js.map +1 -0
  105. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts +2 -0
  106. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts.map +1 -0
  107. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js +24 -0
  108. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js.map +1 -0
  109. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -0
  110. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  111. package/packages/pi-coding-agent/dist/core/extensions/runner.js +14 -1
  112. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  113. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +97 -0
  114. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  115. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  116. package/packages/pi-coding-agent/dist/core/model-registry.js +5 -0
  117. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  118. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +4 -0
  119. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  120. package/packages/pi-coding-agent/dist/core/settings-manager.js +8 -0
  121. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  122. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  123. package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
  124. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  125. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js +6 -4
  126. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js.map +1 -1
  127. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +54 -15
  128. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  129. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +26 -0
  130. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -0
  131. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +112 -0
  132. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -0
  133. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts +2 -0
  134. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts.map +1 -0
  135. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +51 -0
  136. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -0
  137. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  138. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  139. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  140. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +10 -9
  141. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  142. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +3 -0
  143. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  144. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +11 -0
  145. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  146. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +7 -6
  147. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
  148. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +16 -0
  149. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  150. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +106 -17
  151. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  152. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  153. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +60 -1
  154. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  155. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +40 -1
  156. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
  157. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +1 -0
  158. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  159. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
  160. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  161. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  162. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
  163. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  164. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +23 -0
  165. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  166. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  167. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +20 -0
  168. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  169. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts +2 -0
  170. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts.map +1 -0
  171. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js +79 -0
  172. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js.map +1 -0
  173. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  174. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  175. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js +13 -0
  176. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  177. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
  178. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  179. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +18 -1
  180. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  181. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  182. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +36 -27
  183. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  184. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts +11 -0
  185. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts.map +1 -0
  186. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js +18 -0
  187. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js.map +1 -0
  188. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts +2 -0
  189. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts.map +1 -0
  190. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +48 -0
  191. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -0
  192. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts +2 -0
  193. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts.map +1 -0
  194. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js +10 -0
  195. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js.map +1 -0
  196. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts.map +1 -1
  197. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js +3 -2
  198. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js.map +1 -1
  199. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +87 -0
  200. package/packages/pi-coding-agent/src/core/db-snapshot.test.ts +32 -0
  201. package/packages/pi-coding-agent/src/core/db-snapshot.ts +66 -0
  202. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +108 -0
  203. package/packages/pi-coding-agent/src/core/extensions/runner.ts +16 -1
  204. package/packages/pi-coding-agent/src/core/model-registry.ts +4 -0
  205. package/packages/pi-coding-agent/src/core/settings-manager.ts +12 -0
  206. package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
  207. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.ts +7 -5
  208. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +78 -15
  209. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +59 -0
  210. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +160 -0
  211. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +1 -0
  212. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +10 -9
  213. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  214. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +10 -9
  215. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +118 -17
  216. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +43 -1
  217. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +75 -1
  218. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +1 -0
  219. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -1
  220. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +25 -0
  221. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.test.ts +95 -0
  222. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +24 -1
  223. package/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.ts +13 -0
  224. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +32 -2
  225. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +36 -27
  226. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +65 -0
  227. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.ts +29 -0
  228. package/packages/pi-coding-agent/src/resources/extensions/memory/storage-safety-guard.test.ts +14 -0
  229. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.ts +3 -2
  230. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  231. package/packages/pi-tui/dist/__tests__/style.test.d.ts +2 -0
  232. package/packages/pi-tui/dist/__tests__/style.test.d.ts.map +1 -0
  233. package/packages/pi-tui/dist/__tests__/style.test.js +63 -0
  234. package/packages/pi-tui/dist/__tests__/style.test.js.map +1 -0
  235. package/packages/pi-tui/dist/__tests__/tui.test.js +24 -3
  236. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  237. package/packages/pi-tui/dist/index.d.ts +1 -0
  238. package/packages/pi-tui/dist/index.d.ts.map +1 -1
  239. package/packages/pi-tui/dist/index.js +2 -0
  240. package/packages/pi-tui/dist/index.js.map +1 -1
  241. package/packages/pi-tui/dist/style.d.ts +41 -0
  242. package/packages/pi-tui/dist/style.d.ts.map +1 -0
  243. package/packages/pi-tui/dist/style.js +158 -0
  244. package/packages/pi-tui/dist/style.js.map +1 -0
  245. package/packages/pi-tui/dist/tui.d.ts +0 -1
  246. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  247. package/packages/pi-tui/dist/tui.js +3 -8
  248. package/packages/pi-tui/dist/tui.js.map +1 -1
  249. package/packages/pi-tui/src/__tests__/style.test.ts +76 -0
  250. package/packages/pi-tui/src/__tests__/tui.test.ts +29 -3
  251. package/packages/pi-tui/src/index.ts +9 -0
  252. package/packages/pi-tui/src/style.ts +225 -0
  253. package/packages/pi-tui/src/tui.ts +3 -8
  254. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  255. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
  256. package/pkg/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  257. package/pkg/dist/modes/interactive/theme/theme-schema.js +13 -0
  258. package/pkg/dist/modes/interactive/theme/theme-schema.js.map +1 -1
  259. package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
  260. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  261. package/pkg/dist/modes/interactive/theme/theme.js +18 -1
  262. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  263. package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  264. package/pkg/dist/modes/interactive/theme/themes.js +36 -27
  265. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  266. package/src/resources/GSD-WORKFLOW.md +2 -2
  267. package/src/resources/extensions/github-sync/templates.ts +38 -8
  268. package/src/resources/extensions/github-sync/tests/inline-code.test.ts +66 -0
  269. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  270. package/src/resources/extensions/gsd/auto/loop.ts +67 -18
  271. package/src/resources/extensions/gsd/auto/phases.ts +42 -28
  272. package/src/resources/extensions/gsd/auto/run-unit.ts +24 -14
  273. package/src/resources/extensions/gsd/auto-dashboard.ts +57 -8
  274. package/src/resources/extensions/gsd/auto-dispatch.ts +17 -0
  275. package/src/resources/extensions/gsd/auto-post-unit.ts +10 -10
  276. package/src/resources/extensions/gsd/auto-prompts.ts +116 -1
  277. package/src/resources/extensions/gsd/auto-recovery.ts +153 -7
  278. package/src/resources/extensions/gsd/auto-start.ts +7 -6
  279. package/src/resources/extensions/gsd/auto.ts +12 -1
  280. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -1
  281. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +17 -1
  282. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +135 -1
  283. package/src/resources/extensions/gsd/clean-root-preflight.ts +41 -3
  284. package/src/resources/extensions/gsd/commands/dispatcher.ts +6 -0
  285. package/src/resources/extensions/gsd/commands-extract-learnings.ts +17 -12
  286. package/src/resources/extensions/gsd/crash-recovery.ts +67 -10
  287. package/src/resources/extensions/gsd/custom-workflow-engine.ts +24 -1
  288. package/src/resources/extensions/gsd/db-base-schema.ts +15 -0
  289. package/src/resources/extensions/gsd/db-migration-steps.ts +17 -0
  290. package/src/resources/extensions/gsd/detection.ts +128 -0
  291. package/src/resources/extensions/gsd/graph.ts +12 -5
  292. package/src/resources/extensions/gsd/gsd-db.ts +119 -1
  293. package/src/resources/extensions/gsd/guided-flow.ts +49 -12
  294. package/src/resources/extensions/gsd/planning-path-scope.ts +35 -0
  295. package/src/resources/extensions/gsd/pr-evidence.ts +63 -5
  296. package/src/resources/extensions/gsd/prompts/complete-milestone.md +7 -8
  297. package/src/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
  298. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  299. package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -2
  300. package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +7 -1
  301. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +33 -0
  302. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +84 -5
  303. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +170 -1
  304. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +88 -2
  305. package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +9 -0
  306. package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +55 -0
  307. package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +22 -0
  308. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +112 -6
  309. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +40 -2
  310. package/src/resources/extensions/gsd/tests/db-migration-steps.integration.test.ts +428 -0
  311. package/src/resources/extensions/gsd/tests/db-schema-metadata.test.ts +2 -2
  312. package/src/resources/extensions/gsd/tests/detection.test.ts +140 -0
  313. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-basic.md +52 -0
  314. package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-empty-optionals.md +42 -0
  315. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +55 -0
  316. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +60 -0
  317. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +10 -0
  318. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +44 -0
  319. package/src/resources/extensions/gsd/tests/has-pending-deep-stage.test.ts +33 -1
  320. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +50 -0
  321. package/src/resources/extensions/gsd/tests/plan-task.test.ts +21 -0
  322. package/src/resources/extensions/gsd/tests/pr-evidence-equivalence.test.ts +102 -0
  323. package/src/resources/extensions/gsd/tests/pr-evidence-hardening.test.ts +165 -0
  324. package/src/resources/extensions/gsd/tests/right-sized-workflow-prompts.test.ts +192 -0
  325. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +29 -0
  326. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +38 -0
  327. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +46 -2
  328. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +7 -0
  329. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
  330. package/src/resources/extensions/gsd/tests/working-output-messages.test.ts +93 -0
  331. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +37 -6
  332. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +7 -0
  333. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +9 -2
  334. package/src/resources/extensions/gsd/tests/worktree-write-gate.test.ts +179 -0
  335. package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -0
  336. package/src/resources/extensions/gsd/tools/plan-task.ts +10 -0
  337. package/src/resources/extensions/gsd/unit-runtime.ts +14 -0
  338. package/src/resources/extensions/gsd/working-output-messages.ts +120 -0
  339. package/src/resources/extensions/gsd/worktree-manager.ts +15 -4
  340. package/packages/contracts/tsconfig.tsbuildinfo +0 -1
  341. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → y73quA-XdLo9n41nxphjW}/_buildManifest.js +0 -0
  342. /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → y73quA-XdLo9n41nxphjW}/_ssgManifest.js +0 -0
@@ -0,0 +1,165 @@
1
+ // Project/App: GSD-2
2
+ // File Purpose: Hardening tests for buildPrEvidence — HTML-comment stripping, fake commit-trailer removal, and per-item length capping.
3
+
4
+ import test from "node:test";
5
+ import assert from "node:assert/strict";
6
+
7
+ import { buildPrEvidence, type PrEvidenceInput } from "../pr-evidence.ts";
8
+
9
+ test("pr-evidence hardening: strips HTML comments from summaries", () => {
10
+ const evidence = buildPrEvidence({
11
+ milestoneId: "M001",
12
+ summaries: ["visible<!-- hidden secret -->tail"],
13
+ });
14
+ assert.ok(!evidence.body.includes("<!--"), "raw <!-- must not appear");
15
+ assert.ok(!evidence.body.includes("hidden secret"), "comment contents must be stripped");
16
+ assert.ok(evidence.body.includes("visibletail"), "non-comment text must remain");
17
+ });
18
+
19
+ test("pr-evidence hardening: removes Co-Authored-By trailer from why", () => {
20
+ const evidence = buildPrEvidence({
21
+ milestoneId: "M001",
22
+ why: "Real reason here.\nCo-Authored-By: Evil <e@evil.com>\nMore reason.",
23
+ });
24
+ assert.ok(!evidence.body.includes("Evil <e@evil.com>"));
25
+ assert.ok(!/Co-Authored-By:/i.test(evidence.body));
26
+ assert.ok(evidence.body.includes("Real reason here."));
27
+ assert.ok(evidence.body.includes("More reason."));
28
+ });
29
+
30
+ test("pr-evidence hardening: removes Signed-off-by trailer from how", () => {
31
+ const evidence = buildPrEvidence({
32
+ milestoneId: "M001",
33
+ how: "Step one.\nSigned-off-by: Forged <f@x.com>\nStep two.",
34
+ });
35
+ assert.ok(!evidence.body.includes("Forged <f@x.com>"));
36
+ assert.ok(!/Signed-off-by:/i.test(evidence.body));
37
+ assert.ok(evidence.body.includes("Step one."));
38
+ assert.ok(evidence.body.includes("Step two."));
39
+ });
40
+
41
+ test("pr-evidence hardening: trailer-name match is case-insensitive", () => {
42
+ const evidence = buildPrEvidence({
43
+ milestoneId: "M001",
44
+ why: "ok\nco-authored-by: lower <l@l.com>\nSIGNED-OFF-BY: upper <u@u.com>\nend",
45
+ });
46
+ assert.ok(!evidence.body.includes("lower <l@l.com>"));
47
+ assert.ok(!evidence.body.includes("upper <u@u.com>"));
48
+ assert.ok(evidence.body.includes("ok"));
49
+ assert.ok(evidence.body.includes("end"));
50
+ });
51
+
52
+ test("pr-evidence hardening: caps oversize summaries item with truncation suffix", () => {
53
+ const big = "A".repeat(5 * 1024); // 5 KB
54
+ const evidence = buildPrEvidence({
55
+ milestoneId: "M001",
56
+ summaries: [big],
57
+ });
58
+ // Find the truncated A-block in the body and assert it is bounded.
59
+ const lines = evidence.body.split("\n");
60
+ const longLine = lines.find((l) => l.startsWith("AAAA"));
61
+ assert.ok(longLine, "expected truncated A-line in body");
62
+ assert.ok(longLine!.endsWith(" … [truncated]"), "must end with truncation suffix");
63
+ assert.ok(
64
+ Buffer.byteLength(longLine!, "utf8") <= 2048,
65
+ `truncated item must be within 2 KB cap, got ${Buffer.byteLength(longLine!, "utf8")}`,
66
+ );
67
+ });
68
+
69
+ test("pr-evidence hardening: HTML comment split across summary items is preserved literally", () => {
70
+ // Documented behavior: each item is sanitized independently. A comment
71
+ // that begins in one item and closes in the next is NOT joined, so the
72
+ // open/close markers remain as literal text. This is intentional — joining
73
+ // items before sanitizing would let an attacker straddle items to inject
74
+ // an aligned comment that hides the second item from rendered view.
75
+ const evidence = buildPrEvidence({
76
+ milestoneId: "M001",
77
+ summaries: ["first item ends <!--", "--> second item begins"],
78
+ });
79
+ // The literal markers survive because each item was sanitized alone.
80
+ assert.ok(evidence.body.includes("<!--"), "open marker preserved as literal");
81
+ assert.ok(evidence.body.includes("-->"), "close marker preserved as literal");
82
+ assert.ok(evidence.body.includes("first item ends"));
83
+ assert.ok(evidence.body.includes("second item begins"));
84
+ });
85
+
86
+ test("pr-evidence hardening: clean input is byte-identical to pre-hardening output", () => {
87
+ // This test is the contract that protects the golden fixtures: the
88
+ // sanitizer must be a true no-op for well-formed input. If this fails,
89
+ // there is a bug in the sanitizer (not in the goldens).
90
+ const cleanInput: PrEvidenceInput = {
91
+ milestoneId: "M001",
92
+ milestoneTitle: "Authentication",
93
+ changeType: "feat",
94
+ linkedIssue: "Closes #123",
95
+ summaries: ["### S01\nImplemented login flow."],
96
+ blockers: ["Awaiting design review"],
97
+ roadmapItems: ["- [x] **S01: Login**"],
98
+ metrics: ["**Units executed:** 3"],
99
+ testsRun: ["npm test", "npm run typecheck:extensions"],
100
+ why: "Users need to authenticate before accessing protected resources.",
101
+ how: "Added password hash check and session token issuance.",
102
+ rollbackNotes: ["Revert the merge commit."],
103
+ };
104
+
105
+ const expected = [
106
+ "## TL;DR",
107
+ "",
108
+ "**What:** Ship milestone M001 - Authentication",
109
+ "**Why:** Users need to authenticate before accessing protected resources.",
110
+ "**How:** Added password hash check and session token issuance.",
111
+ "",
112
+ "## What",
113
+ "",
114
+ "### S01\nImplemented login flow.",
115
+ "",
116
+ "## Blockers",
117
+ "",
118
+ "- Awaiting design review",
119
+ "",
120
+ "## Why",
121
+ "",
122
+ "Users need to authenticate before accessing protected resources.",
123
+ "",
124
+ "## How",
125
+ "",
126
+ "Added password hash check and session token issuance.",
127
+ "",
128
+ "## Linked Issue",
129
+ "",
130
+ "Closes #123",
131
+ "",
132
+ "## Roadmap",
133
+ "",
134
+ "- [x] **S01: Login**",
135
+ "",
136
+ "## Metrics",
137
+ "",
138
+ "- **Units executed:** 3",
139
+ "",
140
+ "## Tests Run",
141
+ "",
142
+ "- npm test",
143
+ "- npm run typecheck:extensions",
144
+ "",
145
+ "## Change Type",
146
+ "",
147
+ "- [x] `feat` - New feature or capability",
148
+ "- [ ] `fix` - Bug fix",
149
+ "- [ ] `refactor` - Code restructuring",
150
+ "- [ ] `test` - Adding or updating tests",
151
+ "- [ ] `docs` - Documentation only",
152
+ "- [ ] `chore` - Build, CI, or tooling changes",
153
+ "",
154
+ "## Rollback And Compatibility",
155
+ "",
156
+ "- Revert the merge commit.",
157
+ "",
158
+ "## AI Assistance Disclosure",
159
+ "",
160
+ "This PR was prepared with AI assistance.",
161
+ ].join("\n");
162
+
163
+ const actual = buildPrEvidence(cleanInput).body;
164
+ assert.equal(actual, expected, "clean input must produce byte-identical output (sanitizer is no-op)");
165
+ });
@@ -0,0 +1,192 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { execFileSync } from "node:child_process";
4
+ import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
5
+ import { join } from "node:path";
6
+ import { tmpdir } from "node:os";
7
+
8
+ import { buildCompleteMilestonePrompt, buildPlanMilestonePrompt } from "../auto-prompts.ts";
9
+
10
+ function git(cwd: string, args: string[]): string {
11
+ return execFileSync("git", args, {
12
+ cwd,
13
+ stdio: ["ignore", "pipe", "pipe"],
14
+ encoding: "utf-8",
15
+ env: { ...process.env, GIT_AUTHOR_NAME: "Test User", GIT_AUTHOR_EMAIL: "test@example.com", GIT_COMMITTER_NAME: "Test User", GIT_COMMITTER_EMAIL: "test@example.com" },
16
+ }).trim();
17
+ }
18
+
19
+ function makeRepo(files: Record<string, string>): string {
20
+ const base = mkdtempSync(join(tmpdir(), "gsd-right-size-"));
21
+ git(base, ["init", "-b", "main"]);
22
+ mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
23
+ writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-CONTEXT.md"), "# Context\n\nTest milestone.");
24
+ for (const [path, content] of Object.entries(files)) {
25
+ const abs = join(base, path);
26
+ mkdirSync(join(abs, ".."), { recursive: true });
27
+ writeFileSync(abs, content);
28
+ }
29
+ git(base, ["add", "."]);
30
+ git(base, ["commit", "-m", "init"]);
31
+ return base;
32
+ }
33
+
34
+ function writeCompleteMilestoneFiles(base: string, validation: string): void {
35
+ const dir = join(base, ".gsd", "milestones", "M001");
36
+ mkdirSync(join(dir, "slices", "S01"), { recursive: true });
37
+ writeFileSync(join(dir, "M001-ROADMAP.md"), "# M001\n\n## Slices\n- [x] **S01: One** `risk:low` `depends:[]`\n > Done\n");
38
+ writeFileSync(join(dir, "M001-VALIDATION.md"), validation);
39
+ writeFileSync(join(dir, "slices", "S01", "S01-SUMMARY.md"), "# S01 Summary\n\n**Verification:** passed\n");
40
+ }
41
+
42
+ function validationMetadata(): string {
43
+ return [
44
+ "validation_metadata:",
45
+ " covered_artifacts:",
46
+ " - `.gsd/milestones/M001/M001-VALIDATION.md`",
47
+ " - `.gsd/milestones/M001/M001-ROADMAP.md`",
48
+ " - `.gsd/milestones/M001/slices/S01/S01-SUMMARY.md`",
49
+ ].join("\n");
50
+ }
51
+
52
+ test("plan-milestone prompt includes tiny untyped project classification and one-slice guidance", async () => {
53
+ const base = makeRepo({ "index.html": "<!doctype html>\n<title>Test</title>\n" });
54
+ try {
55
+ const prompt = await buildPlanMilestonePrompt("M001", "Polish static page", base, "minimal");
56
+ assert.match(prompt, /\*\*Kind:\*\* untyped-existing/);
57
+ assert.match(prompt, /\*\*Content files:\*\* 1/);
58
+ assert.match(prompt, /`index\.html`/);
59
+ assert.match(prompt, /Prefer exactly one slice/);
60
+ } finally {
61
+ rmSync(base, { recursive: true, force: true });
62
+ }
63
+ });
64
+
65
+ test("plan-milestone prompt includes small untyped project 1-2 slice guidance", async () => {
66
+ const base = makeRepo({
67
+ "index.html": "html",
68
+ "README.md": "readme",
69
+ "styles.css": "body {}",
70
+ });
71
+ try {
72
+ const prompt = await buildPlanMilestonePrompt("M001", "Polish static files", base, "minimal");
73
+ assert.match(prompt, /\*\*Kind:\*\* untyped-existing/);
74
+ assert.match(prompt, /\*\*Content files:\*\* 3/);
75
+ assert.match(prompt, /Prefer 1-2 slices/);
76
+ } finally {
77
+ rmSync(base, { recursive: true, force: true });
78
+ }
79
+ });
80
+
81
+ test("plan-milestone prompt keeps normal guidance for typed projects", async () => {
82
+ const base = makeRepo({
83
+ "package.json": "{\"scripts\":{\"test\":\"node --test\"}}\n",
84
+ "src/index.js": "console.log('ok');\n",
85
+ });
86
+ try {
87
+ const prompt = await buildPlanMilestonePrompt("M001", "Update app", base, "minimal");
88
+ assert.match(prompt, /\*\*Kind:\*\* typed-existing/);
89
+ assert.match(prompt, /Use normal ecosystem-aware planning guidance/);
90
+ assert.doesNotMatch(prompt, /Prefer exactly one slice/);
91
+ } finally {
92
+ rmSync(base, { recursive: true, force: true });
93
+ }
94
+ });
95
+
96
+ test("workflow docs no longer contain blanket 4-10 slice guidance", () => {
97
+ const docs = readFileSync(join(process.cwd(), "src", "resources", "GSD-WORKFLOW.md"), "utf-8");
98
+ assert.doesNotMatch(docs, /4-10 slices/);
99
+ assert.match(docs, /1-10 slices/);
100
+ assert.match(docs, /single-file/);
101
+ });
102
+
103
+ test("prompt templates carry right-sized planning and closeout mode guidance", () => {
104
+ const planTemplate = readFileSync(join(process.cwd(), "src", "resources", "extensions", "gsd", "prompts", "plan-milestone.md"), "utf-8");
105
+ const completeTemplate = readFileSync(join(process.cwd(), "src", "resources", "extensions", "gsd", "prompts", "complete-milestone.md"), "utf-8");
106
+
107
+ assert.match(planTemplate, /Use 1-10 slices, sized to the work/);
108
+ assert.match(planTemplate, /tiny\/single-file\/static work should usually be one slice/);
109
+ assert.match(planTemplate, /untyped-existing/);
110
+ assert.match(completeTemplate, /Closeout Review Mode/);
111
+ assert.match(completeTemplate, /passing validation artifact is present/);
112
+ assert.doesNotMatch(completeTemplate, /^### Delegate Review Work/m);
113
+ });
114
+
115
+ test("complete-milestone prompt trusts passing validation artifact", async () => {
116
+ const base = makeRepo({ "index.html": "<!doctype html>\n<title>Test</title>\n" });
117
+ try {
118
+ writeCompleteMilestoneFiles(base, `---\nverdict: pass\nremediation_round: 0\n---\n\n# Validation\n${validationMetadata()}\n\nAll checks passed.`);
119
+ const prompt = await buildCompleteMilestonePrompt("M001", "Polish static page", base, "minimal");
120
+ assert.match(prompt, /Passing Validation Artifact/);
121
+ assert.match(prompt, /Treat it as authoritative/);
122
+ assert.match(prompt, /Do not delegate fresh reviewer\/security\/tester audits/);
123
+ assert.match(prompt, /All checks passed/);
124
+ } finally {
125
+ rmSync(base, { recursive: true, force: true });
126
+ }
127
+ });
128
+
129
+ test("complete-milestone prompt trusts centralized markdown body pass verdict", async () => {
130
+ const base = makeRepo({ "index.html": "<!doctype html>\n<title>Test</title>\n" });
131
+ try {
132
+ writeCompleteMilestoneFiles(base, `# Validation\n\n**Verdict:** PASS\n\n${validationMetadata()}\n\nAll checks passed.`);
133
+ const prompt = await buildCompleteMilestonePrompt("M001", "Polish static page", base, "minimal");
134
+ assert.match(prompt, /Passing Validation Artifact/);
135
+ assert.match(prompt, /Treat it as authoritative/);
136
+ assert.match(prompt, /Do not delegate fresh reviewer\/security\/tester audits/);
137
+ } finally {
138
+ rmSync(base, { recursive: true, force: true });
139
+ }
140
+ });
141
+
142
+ test("complete-milestone prompt does not trust stale pass validation without metadata", async () => {
143
+ const base = makeRepo({ "index.html": "<!doctype html>\n<title>Test</title>\n" });
144
+ try {
145
+ writeCompleteMilestoneFiles(base, "---\nverdict: pass\nremediation_round: 0\n---\n\n# Validation\nAll checks passed.");
146
+ const prompt = await buildCompleteMilestonePrompt("M001", "Polish static page", base, "minimal");
147
+ assert.match(prompt, /Validation Requires Attention/);
148
+ assert.match(prompt, /missing freshness metadata/);
149
+ assert.doesNotMatch(prompt, /Passing Validation Artifact/);
150
+ } finally {
151
+ rmSync(base, { recursive: true, force: true });
152
+ }
153
+ });
154
+
155
+ test("complete-milestone prompt does not trust pass validation missing current summary coverage", async () => {
156
+ const base = makeRepo({ "index.html": "<!doctype html>\n<title>Test</title>\n" });
157
+ try {
158
+ writeCompleteMilestoneFiles(base, [
159
+ "---",
160
+ "verdict: pass",
161
+ "remediation_round: 0",
162
+ "---",
163
+ "",
164
+ "# Validation",
165
+ "validation_metadata:",
166
+ " covered_artifacts:",
167
+ " - `.gsd/milestones/M001/M001-VALIDATION.md`",
168
+ " - `.gsd/milestones/M001/M001-ROADMAP.md`",
169
+ "",
170
+ "All checks passed.",
171
+ ].join("\n"));
172
+ const prompt = await buildCompleteMilestonePrompt("M001", "Polish static page", base, "minimal");
173
+ assert.match(prompt, /Validation Requires Attention/);
174
+ assert.match(prompt, /does not cover current milestone artifacts/);
175
+ assert.doesNotMatch(prompt, /Passing Validation Artifact/);
176
+ } finally {
177
+ rmSync(base, { recursive: true, force: true });
178
+ }
179
+ });
180
+
181
+ test("complete-milestone prompt keeps deeper review path without passing validation", async () => {
182
+ const base = makeRepo({ "index.html": "<!doctype html>\n<title>Test</title>\n" });
183
+ try {
184
+ writeCompleteMilestoneFiles(base, "---\nverdict: needs-attention\nremediation_round: 0\n---\n\n# Validation\nFix gaps.");
185
+ const prompt = await buildCompleteMilestonePrompt("M001", "Polish static page", base, "minimal");
186
+ assert.match(prompt, /Validation Requires Attention/);
187
+ assert.match(prompt, /verdict `needs-attention`/);
188
+ assert.match(prompt, /Use `subagent` for review work needing fresh context/i);
189
+ } finally {
190
+ rmSync(base, { recursive: true, force: true });
191
+ }
192
+ });
@@ -144,6 +144,18 @@ test("safety-harness-bug2-race: bash evidence survives mid-unit reset between to
144
144
  assert.ok(bash[0].outputSnippet.includes("found"), "output snippet captured");
145
145
  });
146
146
 
147
+ test("safety-harness: gsd_exec counts as execution evidence", () => {
148
+ resetEvidence();
149
+
150
+ recordToolCall("tc-exec-1", "gsd_exec", { command: "grep -n render index.html" });
151
+ recordToolResult("tc-exec-1", "gsd_exec", "Command exited with code 0\n1:render\n", false);
152
+
153
+ const bash = getEvidence().filter((e): e is BashEvidence => e.kind === "bash");
154
+ assert.equal(bash.length, 1, "gsd_exec must be tracked as execution evidence");
155
+ assert.equal(bash[0].command, "grep -n render index.html");
156
+ assert.equal(bash[0].exitCode, 0);
157
+ });
158
+
147
159
  // ─── Bug 3: git diff HEAD~1 scope check ─────────────────────────────────────
148
160
 
149
161
  test("safety-harness-bug3: validateFileChanges works on initial commit (no HEAD~1)", (t) => {
@@ -237,3 +249,20 @@ test("safety-harness-bug3: validateFileChanges works on merge commit", (t) => {
237
249
  // Must produce a valid result without throwing
238
250
  assert.ok(audit !== null, "audit must be produced for merge commit repo");
239
251
  });
252
+
253
+ test("safety-harness: planned changed file avoids unexpected-file warning", (t) => {
254
+ const base = mkdtempSync(join(tmpdir(), "gsd-planned-file-"));
255
+ t.after(() => rmSync(base, { recursive: true, force: true }));
256
+
257
+ execFileSync("git", ["init"], { cwd: base });
258
+ execFileSync("git", ["config", "user.email", "test@example.com"], { cwd: base });
259
+ execFileSync("git", ["config", "user.name", "Test User"], { cwd: base });
260
+ writeFileSync(join(base, "index.html"), "<main></main>\n");
261
+ execFileSync("git", ["add", "index.html"], { cwd: base });
262
+ execFileSync("git", ["commit", "-m", "add static app"], { cwd: base });
263
+
264
+ const audit = validateFileChanges(base, [], ["index.html"]);
265
+ assert.ok(audit !== null, "audit must be produced");
266
+ assert.deepEqual(audit!.unexpectedFiles, [], "planned index.html must not be unexpected");
267
+ assert.deepEqual(audit!.missingFiles, [], "planned index.html must not be missing");
268
+ });
@@ -47,6 +47,9 @@ test("guided-flow complete branch offers a chooser for next milestone or status"
47
47
  const branchChunk = guidedFlowSource.slice(branchIdx, nextBranchIdx === -1 ? branchIdx + 1600 : nextBranchIdx);
48
48
 
49
49
  assert.match(branchChunk, /showNextAction\(/, "complete branch should present a chooser");
50
+ assert.match(branchChunk, /id:\s*"quick_task"/, "complete branch should offer quick task before milestone planning");
51
+ assert.match(branchChunk, /Do a small bounded task without opening a milestone/, "quick task action should explain that it avoids milestones");
52
+ assert.match(branchChunk, /recommended:\s*true/, "quick task action should be the recommended complete-state action");
50
53
  assert.match(branchChunk, /findMilestoneIds\(basePath\)/, "complete branch should compute the next milestone id");
51
54
  assert.match(
52
55
  branchChunk,
@@ -56,6 +59,41 @@ test("guided-flow complete branch offers a chooser for next milestone or status"
56
59
  assert.match(branchChunk, /dispatchWorkflow\(pi, await prepareAndBuildDiscussPrompt\(/, "complete branch should dispatch the prepared discuss prompt");
57
60
  });
58
61
 
62
+ test("guided-flow quick task choices prompt for text and route through /gsd quick", () => {
63
+ const guidedFlowSource = readFileSync(join(import.meta.dirname, "..", "guided-flow.ts"), "utf-8");
64
+
65
+ assert.match(
66
+ guidedFlowSource,
67
+ /async function runQuickTaskChoice\([\s\S]*ctx\.ui\.input\("Quick task"[\s\S]*await import\("\.\/quick\.js"\)[\s\S]*await handleQuick\(task,\s*ctx,\s*pi\)/,
68
+ "quick task chooser should prompt for task text and route through handleQuick",
69
+ );
70
+
71
+ const notifyOnlyPattern = /if \(choice === "quick_task"\) \{\s*ctx\.ui\.notify\("Run \/gsd quick <task>/;
72
+ assert.doesNotMatch(
73
+ guidedFlowSource,
74
+ notifyOnlyPattern,
75
+ "quick task chooser must not merely print instructions after selection",
76
+ );
77
+
78
+ const quickChoiceCalls = guidedFlowSource.match(/if \(choice === "quick_task"\) \{\s*await runQuickTaskChoice\(ctx,\s*pi\);/g) ?? [];
79
+ assert.equal(quickChoiceCalls.length, 3, "all guided-flow quick task choices should dispatch the helper");
80
+ });
81
+
82
+ test("dispatcher routes multi-word freeform /gsd input through /gsd do", () => {
83
+ const dispatcherSource = readFileSync(join(import.meta.dirname, "..", "commands", "dispatcher.ts"), "utf-8");
84
+
85
+ assert.match(
86
+ dispatcherSource,
87
+ /if\s*\(trimmed\.includes\(" "\)\)\s*\{[\s\S]*handleDo\(trimmed,\s*ctx,\s*pi\)/,
88
+ "dispatcher should treat multi-word unknown input as natural-language /gsd do work",
89
+ );
90
+ assert.match(
91
+ dispatcherSource,
92
+ /Unknown: \/gsd/,
93
+ "single-token unknown commands should still report the normal unknown-command warning",
94
+ );
95
+ });
96
+
59
97
  test("guided-flow needs-discussion skip branch opens the project DB before reserving a new milestone", () => {
60
98
  const guidedFlowSource = readFileSync(join(import.meta.dirname, "..", "guided-flow.ts"), "utf-8");
61
99
  const laterDbOpenIdx = guidedFlowSource.indexOf("// Ensure DB is open before querying slices (#2560).");
@@ -3,6 +3,8 @@ import assert from "node:assert/strict";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { resolve } from "node:path";
5
5
 
6
+ import { _withDetachedAutoKeepaliveForTest } from "../auto.ts";
7
+
6
8
  const gsdDir = resolve(import.meta.dirname, "..");
7
9
 
8
10
  function readGsdFile(relativePath: string): string {
@@ -102,8 +104,8 @@ test("startAutoDetached reports failures asynchronously (#3733)", () => {
102
104
  "auto.ts should export startAutoDetached",
103
105
  );
104
106
  assert.ok(
105
- autoSrc.includes("void startAuto(ctx, pi, base, verboseMode, options).catch"),
106
- "startAutoDetached should launch startAuto without awaiting it",
107
+ autoSrc.includes("void withDetachedAutoKeepalive(startAuto(ctx, pi, base, verboseMode, options)).catch"),
108
+ "startAutoDetached should launch startAuto without awaiting it and keep the process alive",
107
109
  );
108
110
  assert.ok(
109
111
  autoSrc.includes("ctx.ui.notify(`Auto-start failed: ${message}`, \"error\")"),
@@ -111,6 +113,48 @@ test("startAutoDetached reports failures asynchronously (#3733)", () => {
111
113
  );
112
114
  });
113
115
 
116
+ test("detached auto-start keeps a ref'ed handle until the run settles", async () => {
117
+ const originalSetInterval = globalThis.setInterval;
118
+ const originalClearInterval = globalThis.clearInterval;
119
+ let intervalCreated = false;
120
+ let intervalCleared = false;
121
+ let createdHandle: NodeJS.Timeout | undefined;
122
+ let resolveRun!: () => void;
123
+ const run = new Promise<void>((resolve) => {
124
+ resolveRun = resolve;
125
+ });
126
+
127
+ globalThis.setInterval = ((handler: TimerHandler, timeout?: number, ...args: unknown[]) => {
128
+ intervalCreated = true;
129
+ assert.equal(timeout, 30_000);
130
+ void handler;
131
+ void args;
132
+ createdHandle = originalSetInterval(() => {}, 1_000_000);
133
+ assert.equal(createdHandle.hasRef(), true, "detached auto keepalive must be ref'ed");
134
+ return createdHandle;
135
+ }) as unknown as typeof setInterval;
136
+
137
+ globalThis.clearInterval = ((handle?: NodeJS.Timeout | number | string) => {
138
+ if (handle === createdHandle) intervalCleared = true;
139
+ return originalClearInterval(handle);
140
+ }) as unknown as typeof clearInterval;
141
+
142
+ try {
143
+ const heldRun = _withDetachedAutoKeepaliveForTest(run);
144
+ assert.equal(intervalCreated, true, "keepalive interval should start immediately");
145
+ assert.equal(intervalCleared, false, "keepalive should remain active while auto-mode is running");
146
+
147
+ resolveRun();
148
+ await heldRun;
149
+
150
+ assert.equal(intervalCleared, true, "keepalive interval should clear when auto-mode settles");
151
+ } finally {
152
+ if (createdHandle) originalClearInterval(createdHandle);
153
+ globalThis.setInterval = originalSetInterval;
154
+ globalThis.clearInterval = originalClearInterval;
155
+ }
156
+ });
157
+
114
158
  test("detached auto-start preserves milestone lock across pause/stop cleanup (#3733)", () => {
115
159
  const autoSrc = readGsdFile("auto.ts");
116
160
  const sessionSrc = readGsdFile("auto/session.ts");
@@ -5,6 +5,7 @@ import {
5
5
  clearUnitRuntimeRecord,
6
6
  formatExecuteTaskRecoveryStatus,
7
7
  inspectExecuteTaskDurability,
8
+ isInFlightRuntimePhase,
8
9
  readUnitRuntimeRecord,
9
10
  writeUnitRuntimeRecord,
10
11
  } from "../unit-runtime.ts";
@@ -22,6 +23,12 @@ writeFileSync(
22
23
  "utf-8",
23
24
  );
24
25
 
26
+ console.log("\n=== in-flight runtime phases ===");
27
+ {
28
+ assert.equal(isInFlightRuntimePhase("crashed"), true, "crashed records remain recoverable");
29
+ assert.equal(isInFlightRuntimePhase("finalized"), false, "finalized records are terminal");
30
+ }
31
+
25
32
  console.log("\n=== runtime record write/read/update ===");
26
33
  {
27
34
  const first = writeUnitRuntimeRecord(base, "execute-task", "M100/S02/T09", 1000, { phase: "dispatched" });
@@ -118,7 +118,7 @@ test("guided flow checks pending deep setup before plan-v2 gate", () => {
118
118
  const source = readFileSync(join(gsdDir, "guided-flow.ts"), "utf-8");
119
119
  const showSmartEntryIdx = source.indexOf("export async function showSmartEntry");
120
120
  assert.notEqual(showSmartEntryIdx, -1);
121
- const deepIdx = source.indexOf("hasPendingDeepStage(prefs, basePath)", showSmartEntryIdx);
121
+ const deepIdx = source.indexOf("shouldRunDeepProjectSetup(state, prefs, basePath)", showSmartEntryIdx);
122
122
  const planIdx = source.indexOf("runPlanV2Gate(ctx, basePath, state)", showSmartEntryIdx);
123
123
  assert.ok(
124
124
  deepIdx > -1 && planIdx > -1 && deepIdx < planIdx,
@@ -0,0 +1,93 @@
1
+ // GSD-2 + src/resources/extensions/gsd/tests/working-output-messages.test.ts - Regression coverage for user-facing working-state message quality.
2
+
3
+ import test from "node:test";
4
+ import assert from "node:assert/strict";
5
+
6
+ import {
7
+ evaluateWorkingOutputMessage,
8
+ evaluateWorkingOutputMessages,
9
+ formatAutoUnitWorkingMessage,
10
+ } from "../working-output-messages.ts";
11
+
12
+ test("auto unit loader messages name the active work", () => {
13
+ assert.equal(
14
+ formatAutoUnitWorkingMessage("research-milestone", "M001"),
15
+ "Researching M001: waiting for provider response",
16
+ );
17
+ assert.equal(
18
+ evaluateWorkingOutputMessage({
19
+ surface: "loader",
20
+ message: formatAutoUnitWorkingMessage("research-milestone", "M001"),
21
+ context: { unitType: "research-milestone", unitId: "M001", health: "waiting" },
22
+ }).length,
23
+ 0,
24
+ );
25
+ });
26
+
27
+ test("generic working messages are flagged", () => {
28
+ const findings = evaluateWorkingOutputMessage({
29
+ surface: "loader",
30
+ message: "Working...",
31
+ context: { health: "waiting" },
32
+ });
33
+
34
+ assert.deepEqual(findings.map(f => f.code), ["generic-working-message"]);
35
+ });
36
+
37
+ test("dashboard messages cannot claim healthy progress while recovering", () => {
38
+ const findings = evaluateWorkingOutputMessage({
39
+ surface: "dashboard",
40
+ message: "GSD AUTO Progressing well",
41
+ context: { health: "recovering", recoveryAttempts: 1 },
42
+ });
43
+
44
+ assert.deepEqual(findings.map(f => f.code), ["misleading-healthy-message"]);
45
+ });
46
+
47
+ test("pre-roadmap zero-slice counters are flagged", () => {
48
+ const findings = evaluateWorkingOutputMessage({
49
+ surface: "dashboard",
50
+ message: "0/0 slices",
51
+ context: { health: "waiting" },
52
+ });
53
+
54
+ assert.deepEqual(findings.map(f => f.code), ["fake-zero-progress"]);
55
+ });
56
+
57
+ test("stalled/provider-error/timeout messages need an action", () => {
58
+ const noAction = evaluateWorkingOutputMessage({
59
+ surface: "notification",
60
+ message: "Provider stalled after 2m",
61
+ context: { health: "stalled" },
62
+ });
63
+ assert.deepEqual(noAction.map(f => f.code), ["missing-action"]);
64
+
65
+ const withAction = evaluateWorkingOutputMessage({
66
+ surface: "notification",
67
+ message: "Provider stalled after 2m. Type /gsd stop or wait for retry.",
68
+ context: { health: "stalled" },
69
+ });
70
+ assert.equal(withAction.length, 0);
71
+ });
72
+
73
+ test("current fixed research working surfaces pass the audit", () => {
74
+ const findings = evaluateWorkingOutputMessages([
75
+ {
76
+ surface: "loader",
77
+ message: "Researching M001: waiting for provider response",
78
+ context: { unitType: "research-milestone", unitId: "M001", health: "waiting" },
79
+ },
80
+ {
81
+ surface: "dashboard",
82
+ message: "Recovering\nretry 1 after idle stall",
83
+ context: { unitType: "research-milestone", unitId: "M001", health: "recovering", recoveryAttempts: 1 },
84
+ },
85
+ {
86
+ surface: "notification",
87
+ message: "Auto-mode stopped - User requested stop.",
88
+ context: { health: "stopped" },
89
+ },
90
+ ]);
91
+
92
+ assert.equal(findings.length, 0);
93
+ });