gsd-pi 2.82.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (555) hide show
  1. package/README.md +51 -32
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/GSD-WORKFLOW.md +10 -1
  4. package/dist/resources/extensions/browser-tools/tools/screenshot.js +1 -0
  5. package/dist/resources/extensions/browser-tools/tools/zoom.js +1 -0
  6. package/dist/resources/extensions/claude-code-cli/partial-builder.js +2 -1
  7. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +1 -1
  8. package/dist/resources/extensions/cmux/index.js +5 -0
  9. package/dist/resources/extensions/gsd/auto/infra-errors.js +9 -3
  10. package/dist/resources/extensions/gsd/auto/loop.js +19 -6
  11. package/dist/resources/extensions/gsd/auto/orchestrator.js +124 -6
  12. package/dist/resources/extensions/gsd/auto/phases.js +90 -31
  13. package/dist/resources/extensions/gsd/auto/session.js +4 -0
  14. package/dist/resources/extensions/gsd/auto/workflow-kernel.js +3 -0
  15. package/dist/resources/extensions/gsd/auto/workflow-memory-pressure.js +12 -0
  16. package/dist/resources/extensions/gsd/auto-dashboard.js +66 -1
  17. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +1 -0
  18. package/dist/resources/extensions/gsd/auto-dispatch.js +20 -19
  19. package/dist/resources/extensions/gsd/auto-model-selection.js +2 -0
  20. package/dist/resources/extensions/gsd/auto-post-unit.js +246 -133
  21. package/dist/resources/extensions/gsd/auto-prompts.js +13 -5
  22. package/dist/resources/extensions/gsd/auto-recovery.js +71 -14
  23. package/dist/resources/extensions/gsd/auto-start.js +87 -14
  24. package/dist/resources/extensions/gsd/auto-verification.js +45 -26
  25. package/dist/resources/extensions/gsd/auto-worktree.js +176 -10
  26. package/dist/resources/extensions/gsd/auto.js +178 -63
  27. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +31 -7
  28. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +10 -9
  29. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +9 -2
  30. package/dist/resources/extensions/gsd/bootstrap/subagent-input.js +21 -9
  31. package/dist/resources/extensions/gsd/bootstrap/system-context.js +55 -12
  32. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +17 -3
  33. package/dist/resources/extensions/gsd/clean-root-preflight.js +170 -8
  34. package/dist/resources/extensions/gsd/commands/catalog.js +10 -1
  35. package/dist/resources/extensions/gsd/commands/handlers/core.js +39 -1
  36. package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
  37. package/dist/resources/extensions/gsd/commands-bootstrap.js +5 -0
  38. package/dist/resources/extensions/gsd/commands-handlers.js +15 -2
  39. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +7 -2
  40. package/dist/resources/extensions/gsd/commands-verdict.js +139 -0
  41. package/dist/resources/extensions/gsd/context-store.js +112 -0
  42. package/dist/resources/extensions/gsd/crash-recovery.js +43 -5
  43. package/dist/resources/extensions/gsd/db/milestone-leases.js +24 -0
  44. package/dist/resources/extensions/gsd/db/unit-dispatches.js +3 -2
  45. package/dist/resources/extensions/gsd/db-writer.js +150 -84
  46. package/dist/resources/extensions/gsd/dispatch-guard.js +2 -2
  47. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  48. package/dist/resources/extensions/gsd/doctor-git-checks.js +87 -7
  49. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +28 -11
  50. package/dist/resources/extensions/gsd/doctor.js +2 -28
  51. package/dist/resources/extensions/gsd/export-html.js +27 -425
  52. package/dist/resources/extensions/gsd/forensics.js +3 -3
  53. package/dist/resources/extensions/gsd/git-service.js +45 -3
  54. package/dist/resources/extensions/gsd/gsd-db.js +21 -6
  55. package/dist/resources/extensions/gsd/guided-flow-queue.js +4 -3
  56. package/dist/resources/extensions/gsd/guided-flow.js +101 -116
  57. package/dist/resources/extensions/gsd/guided-unit-context.js +23 -0
  58. package/dist/resources/extensions/gsd/knowledge-backfill.js +144 -0
  59. package/dist/resources/extensions/gsd/knowledge-capture.js +136 -0
  60. package/dist/resources/extensions/gsd/knowledge-parser.js +154 -0
  61. package/dist/resources/extensions/gsd/knowledge-projection.js +210 -0
  62. package/dist/resources/extensions/gsd/markdown-renderer.js +16 -9
  63. package/dist/resources/extensions/gsd/md-importer.js +1 -1
  64. package/dist/resources/extensions/gsd/memory-backfill.js +73 -17
  65. package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +222 -0
  66. package/dist/resources/extensions/gsd/migrate/command.js +5 -0
  67. package/dist/resources/extensions/gsd/migrate/parsers.js +10 -0
  68. package/dist/resources/extensions/gsd/migrate/preview.js +9 -0
  69. package/dist/resources/extensions/gsd/migrate/transformer.js +51 -4
  70. package/dist/resources/extensions/gsd/migrate/writer.js +11 -1
  71. package/dist/resources/extensions/gsd/migration-auto-check.js +12 -17
  72. package/dist/resources/extensions/gsd/milestone-actions.js +11 -4
  73. package/dist/resources/extensions/gsd/native-git-bridge.js +48 -12
  74. package/dist/resources/extensions/gsd/paths.js +4 -0
  75. package/dist/resources/extensions/gsd/pending-auto-start.js +52 -0
  76. package/dist/resources/extensions/gsd/post-execution-checks.js +73 -2
  77. package/dist/resources/extensions/gsd/pre-execution-checks.js +28 -1
  78. package/dist/resources/extensions/gsd/prompt-loader.js +1 -1
  79. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  80. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  81. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +8 -8
  82. package/dist/resources/extensions/gsd/prompts/discuss.md +9 -9
  83. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +4 -4
  84. package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +3 -3
  85. package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -4
  86. package/dist/resources/extensions/gsd/prompts/queue.md +4 -4
  87. package/dist/resources/extensions/gsd/prompts/refine-slice.md +2 -2
  88. package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  89. package/dist/resources/extensions/gsd/prompts/system.md +2 -2
  90. package/dist/resources/extensions/gsd/provider-switch-observer.js +146 -0
  91. package/dist/resources/extensions/gsd/queue-reorder-ui.js +30 -13
  92. package/dist/resources/extensions/gsd/smart-entry-routing.js +36 -0
  93. package/dist/resources/extensions/gsd/state-reconciliation/drift/merge-state.js +6 -1
  94. package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +9 -14
  95. package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +19 -24
  96. package/dist/resources/extensions/gsd/state.js +3 -3
  97. package/dist/resources/extensions/gsd/status-guards.js +11 -0
  98. package/dist/resources/extensions/gsd/templates/knowledge.md +2 -2
  99. package/dist/resources/extensions/gsd/templates/plan.md +9 -5
  100. package/dist/resources/extensions/gsd/templates/task-plan.md +10 -2
  101. package/dist/resources/extensions/gsd/tools/complete-milestone.js +6 -8
  102. package/dist/resources/extensions/gsd/tools/complete-slice.js +6 -8
  103. package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -1
  104. package/dist/resources/extensions/gsd/tools/plan-slice.js +87 -14
  105. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +119 -0
  106. package/dist/resources/extensions/gsd/unit-context-manifest.js +32 -10
  107. package/dist/resources/extensions/gsd/validation.js +23 -1
  108. package/dist/resources/extensions/gsd/verification-gate.js +68 -7
  109. package/dist/resources/extensions/gsd/verification-verdict.js +26 -0
  110. package/dist/resources/extensions/gsd/workflow-mcp.js +17 -1
  111. package/dist/resources/extensions/gsd/workflow-projections.js +6 -8
  112. package/dist/resources/extensions/gsd/worktree-lifecycle.js +54 -10
  113. package/dist/resources/extensions/gsd/worktree-manager.js +1 -1
  114. package/dist/resources/extensions/shared/html-shell.js +388 -0
  115. package/dist/resources/extensions/subagent/index.js +448 -78
  116. package/dist/resources/extensions/subagent/launch.js +77 -0
  117. package/dist/resources/extensions/subagent/run-store.js +148 -0
  118. package/dist/resources/extensions/ttsr/ttsr-manager.js +3 -1
  119. package/dist/resources/extensions/visual-brief/artifact-policy.js +29 -0
  120. package/dist/resources/extensions/visual-brief/extension-manifest.json +8 -0
  121. package/dist/resources/extensions/visual-brief/index.js +5 -0
  122. package/dist/resources/extensions/visual-brief/page-contract.js +124 -0
  123. package/dist/resources/extensions/visual-brief/prompts.js +140 -0
  124. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  125. package/dist/web/standalone/.next/BUILD_ID +1 -1
  126. package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
  127. package/dist/web/standalone/.next/build-manifest.json +3 -3
  128. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  129. package/dist/web/standalone/.next/react-loadable-manifest.json +5 -5
  130. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  132. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  134. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  136. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  137. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  139. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  140. package/dist/web/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  141. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  142. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  143. package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -7
  144. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -7
  145. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  146. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -5
  147. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  148. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  149. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -5
  150. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  152. package/dist/web/standalone/.next/server/app/index.html +1 -1
  153. package/dist/web/standalone/.next/server/app/index.rsc +4 -7
  154. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  155. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -7
  156. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  157. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -5
  158. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -5
  159. package/dist/web/standalone/.next/server/app/page.js +2 -2
  160. package/dist/web/standalone/.next/server/app/page.js.nft.json +1 -1
  161. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
  163. package/dist/web/standalone/.next/server/chunks/4266.js +2 -0
  164. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  166. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  168. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  169. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  170. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  171. package/dist/web/standalone/.next/static/chunks/2973.33f26573894b6153.js +2 -0
  172. package/dist/web/standalone/.next/static/chunks/8359.65b24fac92188a6b.js +10 -0
  173. package/dist/web/standalone/.next/static/chunks/9441.ff70bb53f6835771.js +1 -0
  174. package/dist/web/standalone/.next/static/chunks/app/layout-b23b3f6858dc6dc8.js +1 -0
  175. package/dist/web/standalone/.next/static/chunks/{webpack-de742b64187e13fe.js → webpack-855d616060cb6e59.js} +1 -1
  176. package/dist/web/standalone/.next/static/css/746ee28c929d1880.css +1 -0
  177. package/package.json +6 -5
  178. package/packages/contracts/dist/rpc.test.js +7 -0
  179. package/packages/contracts/dist/rpc.test.js.map +1 -1
  180. package/packages/contracts/dist/workflow.d.ts +21 -0
  181. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  182. package/packages/contracts/dist/workflow.js +24 -0
  183. package/packages/contracts/dist/workflow.js.map +1 -1
  184. package/packages/contracts/src/rpc.test.ts +8 -0
  185. package/packages/contracts/src/workflow.ts +24 -0
  186. package/packages/daemon/package.json +2 -2
  187. package/packages/mcp-server/README.md +14 -3
  188. package/packages/mcp-server/dist/workflow-tools.d.ts +0 -3
  189. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  190. package/packages/mcp-server/dist/workflow-tools.js +80 -0
  191. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  192. package/packages/mcp-server/package.json +2 -2
  193. package/packages/mcp-server/src/workflow-tools-parity.test.ts +244 -0
  194. package/packages/mcp-server/src/workflow-tools.test.ts +23 -1
  195. package/packages/mcp-server/src/workflow-tools.ts +168 -0
  196. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  197. package/packages/native/package.json +1 -1
  198. package/packages/native/tsconfig.json +2 -1
  199. package/packages/native/tsconfig.tsbuildinfo +1 -1
  200. package/packages/pi-agent-core/package.json +1 -1
  201. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  202. package/packages/pi-ai/dist/index.d.ts +2 -2
  203. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  204. package/packages/pi-ai/dist/index.js +1 -1
  205. package/packages/pi-ai/dist/index.js.map +1 -1
  206. package/packages/pi-ai/dist/providers/google-gemini-cli.d.ts.map +1 -1
  207. package/packages/pi-ai/dist/providers/google-gemini-cli.js +5 -0
  208. package/packages/pi-ai/dist/providers/google-gemini-cli.js.map +1 -1
  209. package/packages/pi-ai/dist/providers/google-gemini-cli.test.d.ts +2 -0
  210. package/packages/pi-ai/dist/providers/google-gemini-cli.test.d.ts.map +1 -0
  211. package/packages/pi-ai/dist/providers/google-gemini-cli.test.js +41 -0
  212. package/packages/pi-ai/dist/providers/google-gemini-cli.test.js.map +1 -0
  213. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
  214. package/packages/pi-ai/dist/providers/openai-codex-responses.js +82 -1
  215. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  216. package/packages/pi-ai/dist/providers/openai-codex-responses.test.d.ts +2 -0
  217. package/packages/pi-ai/dist/providers/openai-codex-responses.test.d.ts.map +1 -0
  218. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js +52 -0
  219. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js.map +1 -0
  220. package/packages/pi-ai/dist/providers/simple-options.d.ts +2 -4
  221. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  222. package/packages/pi-ai/dist/providers/simple-options.js +5 -6
  223. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  224. package/packages/pi-ai/dist/providers/simple-options.test.d.ts +2 -0
  225. package/packages/pi-ai/dist/providers/simple-options.test.d.ts.map +1 -0
  226. package/packages/pi-ai/dist/providers/simple-options.test.js +50 -0
  227. package/packages/pi-ai/dist/providers/simple-options.test.js.map +1 -0
  228. package/packages/pi-ai/dist/providers/transform-messages.d.ts +11 -0
  229. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  230. package/packages/pi-ai/dist/providers/transform-messages.js +20 -0
  231. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  232. package/packages/pi-ai/package.json +1 -1
  233. package/packages/pi-ai/src/index.ts +7 -2
  234. package/packages/pi-ai/src/providers/google-gemini-cli.test.ts +49 -0
  235. package/packages/pi-ai/src/providers/google-gemini-cli.ts +7 -0
  236. package/packages/pi-ai/src/providers/openai-codex-responses.test.ts +63 -0
  237. package/packages/pi-ai/src/providers/openai-codex-responses.ts +91 -1
  238. package/packages/pi-ai/src/providers/simple-options.test.ts +60 -0
  239. package/packages/pi-ai/src/providers/simple-options.ts +5 -6
  240. package/packages/pi-ai/src/providers/transform-messages.ts +24 -0
  241. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  242. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.d.ts +2 -0
  243. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.d.ts.map +1 -0
  244. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.js +66 -0
  245. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.js.map +1 -0
  246. package/packages/pi-coding-agent/dist/core/agent-session.js +1 -1
  247. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  248. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +44 -3
  249. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  250. package/packages/pi-coding-agent/dist/core/sdk.js +1 -1
  251. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  252. package/packages/pi-coding-agent/dist/core/system-prompt.js +4 -4
  253. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  254. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  255. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +24 -6
  256. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  257. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  258. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +71 -97
  259. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  260. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-ordering.test.js +25 -1
  261. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-ordering.test.js.map +1 -1
  262. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +2 -0
  263. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  264. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +24 -10
  265. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  266. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.d.ts +2 -0
  267. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.d.ts.map +1 -0
  268. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.js +17 -0
  269. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.js.map +1 -0
  270. package/packages/pi-coding-agent/package.json +1 -1
  271. package/packages/pi-coding-agent/src/core/agent-session-thinking-level.test.ts +79 -0
  272. package/packages/pi-coding-agent/src/core/agent-session.ts +1 -1
  273. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +53 -3
  274. package/packages/pi-coding-agent/src/core/sdk.ts +1 -1
  275. package/packages/pi-coding-agent/src/core/system-prompt.ts +4 -4
  276. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +23 -7
  277. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +75 -102
  278. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-ordering.test.ts +30 -1
  279. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +29 -10
  280. package/packages/pi-coding-agent/src/tests/system-prompt-file-safety.test.ts +22 -0
  281. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  282. package/packages/pi-tui/dist/__tests__/terminal.test.d.ts +2 -0
  283. package/packages/pi-tui/dist/__tests__/terminal.test.d.ts.map +1 -0
  284. package/packages/pi-tui/dist/__tests__/terminal.test.js +103 -0
  285. package/packages/pi-tui/dist/__tests__/terminal.test.js.map +1 -0
  286. package/packages/pi-tui/dist/terminal.d.ts +2 -0
  287. package/packages/pi-tui/dist/terminal.d.ts.map +1 -1
  288. package/packages/pi-tui/dist/terminal.js +12 -0
  289. package/packages/pi-tui/dist/terminal.js.map +1 -1
  290. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  291. package/packages/pi-tui/dist/tui.js +5 -0
  292. package/packages/pi-tui/dist/tui.js.map +1 -1
  293. package/packages/pi-tui/package.json +1 -1
  294. package/packages/pi-tui/src/__tests__/terminal.test.ts +121 -0
  295. package/packages/pi-tui/src/terminal.ts +11 -0
  296. package/packages/pi-tui/src/tui.ts +6 -0
  297. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  298. package/packages/rpc-client/package.json +1 -1
  299. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  300. package/pkg/package.json +1 -1
  301. package/src/resources/GSD-WORKFLOW.md +10 -1
  302. package/src/resources/extensions/browser-tools/tools/screenshot.ts +1 -0
  303. package/src/resources/extensions/browser-tools/tools/zoom.ts +1 -0
  304. package/src/resources/extensions/claude-code-cli/partial-builder.ts +2 -1
  305. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +1 -1
  306. package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +19 -2
  307. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +9 -0
  308. package/src/resources/extensions/cmux/index.ts +6 -0
  309. package/src/resources/extensions/gsd/auto/contracts.ts +59 -16
  310. package/src/resources/extensions/gsd/auto/infra-errors.ts +9 -3
  311. package/src/resources/extensions/gsd/auto/loop.ts +22 -6
  312. package/src/resources/extensions/gsd/auto/orchestrator.ts +129 -6
  313. package/src/resources/extensions/gsd/auto/phases.ts +104 -38
  314. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  315. package/src/resources/extensions/gsd/auto/workflow-kernel.ts +5 -1
  316. package/src/resources/extensions/gsd/auto/workflow-memory-pressure.ts +13 -0
  317. package/src/resources/extensions/gsd/auto-dashboard.ts +72 -1
  318. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +1 -0
  319. package/src/resources/extensions/gsd/auto-dispatch.ts +21 -19
  320. package/src/resources/extensions/gsd/auto-model-selection.ts +2 -1
  321. package/src/resources/extensions/gsd/auto-post-unit.ts +279 -144
  322. package/src/resources/extensions/gsd/auto-prompts.ts +13 -5
  323. package/src/resources/extensions/gsd/auto-recovery.ts +74 -11
  324. package/src/resources/extensions/gsd/auto-start.ts +94 -12
  325. package/src/resources/extensions/gsd/auto-verification.ts +58 -36
  326. package/src/resources/extensions/gsd/auto-worktree.ts +193 -10
  327. package/src/resources/extensions/gsd/auto.ts +187 -61
  328. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +42 -7
  329. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +10 -9
  330. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +10 -2
  331. package/src/resources/extensions/gsd/bootstrap/subagent-input.ts +19 -7
  332. package/src/resources/extensions/gsd/bootstrap/system-context.ts +58 -15
  333. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +20 -4
  334. package/src/resources/extensions/gsd/clean-root-preflight.ts +174 -8
  335. package/src/resources/extensions/gsd/commands/catalog.ts +10 -1
  336. package/src/resources/extensions/gsd/commands/handlers/core.ts +42 -1
  337. package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
  338. package/src/resources/extensions/gsd/commands-bootstrap.ts +10 -0
  339. package/src/resources/extensions/gsd/commands-handlers.ts +19 -2
  340. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +8 -3
  341. package/src/resources/extensions/gsd/commands-verdict.ts +202 -0
  342. package/src/resources/extensions/gsd/context-store.ts +120 -1
  343. package/src/resources/extensions/gsd/crash-recovery.ts +44 -4
  344. package/src/resources/extensions/gsd/db/milestone-leases.ts +26 -0
  345. package/src/resources/extensions/gsd/db/unit-dispatches.ts +4 -3
  346. package/src/resources/extensions/gsd/db-writer.ts +167 -84
  347. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -2
  348. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  349. package/src/resources/extensions/gsd/doctor-git-checks.ts +89 -7
  350. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +25 -13
  351. package/src/resources/extensions/gsd/doctor-types.ts +3 -0
  352. package/src/resources/extensions/gsd/doctor.ts +2 -27
  353. package/src/resources/extensions/gsd/export-html.ts +27 -427
  354. package/src/resources/extensions/gsd/forensics.ts +3 -3
  355. package/src/resources/extensions/gsd/git-service.ts +51 -4
  356. package/src/resources/extensions/gsd/gsd-db.ts +21 -6
  357. package/src/resources/extensions/gsd/guided-flow-queue.ts +4 -3
  358. package/src/resources/extensions/gsd/guided-flow.ts +134 -133
  359. package/src/resources/extensions/gsd/guided-unit-context.ts +30 -0
  360. package/src/resources/extensions/gsd/knowledge-backfill.ts +164 -0
  361. package/src/resources/extensions/gsd/knowledge-capture.ts +160 -0
  362. package/src/resources/extensions/gsd/knowledge-parser.ts +174 -0
  363. package/src/resources/extensions/gsd/knowledge-projection.ts +241 -0
  364. package/src/resources/extensions/gsd/markdown-renderer.ts +16 -9
  365. package/src/resources/extensions/gsd/md-importer.ts +1 -1
  366. package/src/resources/extensions/gsd/memory-backfill.ts +89 -17
  367. package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +277 -0
  368. package/src/resources/extensions/gsd/migrate/command.ts +5 -0
  369. package/src/resources/extensions/gsd/migrate/parsers.ts +11 -0
  370. package/src/resources/extensions/gsd/migrate/preview.ts +10 -0
  371. package/src/resources/extensions/gsd/migrate/transformer.ts +58 -4
  372. package/src/resources/extensions/gsd/migrate/writer.ts +14 -1
  373. package/src/resources/extensions/gsd/migration-auto-check.ts +15 -23
  374. package/src/resources/extensions/gsd/milestone-actions.ts +10 -4
  375. package/src/resources/extensions/gsd/native-git-bridge.ts +54 -12
  376. package/src/resources/extensions/gsd/paths.ts +5 -0
  377. package/src/resources/extensions/gsd/pending-auto-start.ts +79 -0
  378. package/src/resources/extensions/gsd/post-execution-checks.ts +87 -2
  379. package/src/resources/extensions/gsd/pre-execution-checks.ts +32 -1
  380. package/src/resources/extensions/gsd/prompt-loader.ts +1 -1
  381. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  382. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  383. package/src/resources/extensions/gsd/prompts/discuss-headless.md +8 -8
  384. package/src/resources/extensions/gsd/prompts/discuss.md +9 -9
  385. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +4 -4
  386. package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +3 -3
  387. package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -4
  388. package/src/resources/extensions/gsd/prompts/queue.md +4 -4
  389. package/src/resources/extensions/gsd/prompts/refine-slice.md +2 -2
  390. package/src/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  391. package/src/resources/extensions/gsd/prompts/system.md +2 -2
  392. package/src/resources/extensions/gsd/provider-switch-observer.ts +185 -0
  393. package/src/resources/extensions/gsd/queue-reorder-ui.ts +31 -13
  394. package/src/resources/extensions/gsd/smart-entry-routing.ts +77 -0
  395. package/src/resources/extensions/gsd/state-reconciliation/drift/merge-state.ts +8 -1
  396. package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +12 -15
  397. package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +17 -25
  398. package/src/resources/extensions/gsd/state.ts +3 -3
  399. package/src/resources/extensions/gsd/status-guards.ts +13 -0
  400. package/src/resources/extensions/gsd/templates/knowledge.md +2 -2
  401. package/src/resources/extensions/gsd/templates/plan.md +9 -5
  402. package/src/resources/extensions/gsd/templates/task-plan.md +10 -2
  403. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +71 -0
  404. package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +116 -0
  405. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +131 -0
  406. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +487 -4
  407. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +151 -12
  408. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +53 -2
  409. package/src/resources/extensions/gsd/tests/auto-post-unit-step-message.test.ts +18 -6
  410. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +91 -6
  411. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +4 -4
  412. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +1 -0
  413. package/src/resources/extensions/gsd/tests/auto-stop-notification.test.ts +20 -0
  414. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +69 -1
  415. package/src/resources/extensions/gsd/tests/brief-command.test.ts +89 -0
  416. package/src/resources/extensions/gsd/tests/browser-tools-compatibility-declarations.test.ts +62 -0
  417. package/src/resources/extensions/gsd/tests/checkout-branch-stash-guard.test.ts +87 -0
  418. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +107 -2
  419. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +11 -2
  420. package/src/resources/extensions/gsd/tests/closeout-git-deferral.test.ts +16 -0
  421. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +378 -0
  422. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +4 -1
  423. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +5 -9
  424. package/src/resources/extensions/gsd/tests/complete-task.test.ts +3 -1
  425. package/src/resources/extensions/gsd/tests/context-store-decisions-from-memories.test.ts +312 -0
  426. package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +86 -2
  427. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +2 -0
  428. package/src/resources/extensions/gsd/tests/db-authority-regression.test.ts +208 -0
  429. package/src/resources/extensions/gsd/tests/db-writer.test.ts +13 -8
  430. package/src/resources/extensions/gsd/tests/decisions-projection-from-memories.test.ts +453 -0
  431. package/src/resources/extensions/gsd/tests/decisions-stop-table-writes.test.ts +348 -0
  432. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +59 -2
  433. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +66 -0
  434. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +27 -0
  435. package/src/resources/extensions/gsd/tests/doctor-empty-worktree.test.ts +65 -0
  436. package/src/resources/extensions/gsd/tests/evidence-cross-ref.test.ts +38 -0
  437. package/src/resources/extensions/gsd/tests/export-html-enhancements.test.ts +8 -0
  438. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +8 -4
  439. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +11 -0
  440. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +11 -7
  441. package/src/resources/extensions/gsd/tests/gsdroot-worktree-detection.test.ts +5 -2
  442. package/src/resources/extensions/gsd/tests/guided-discuss-project-prompt-rendering.test.ts +2 -0
  443. package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +106 -0
  444. package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +59 -11
  445. package/src/resources/extensions/gsd/tests/guided-flow.test.ts +21 -0
  446. package/src/resources/extensions/gsd/tests/guided-tool-contract.test.ts +65 -0
  447. package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +7 -7
  448. package/src/resources/extensions/gsd/tests/hook-model-resolution.test.ts +5 -0
  449. package/src/resources/extensions/gsd/tests/infra-error.test.ts +2 -2
  450. package/src/resources/extensions/gsd/tests/infra-errors-cooldown.test.ts +9 -0
  451. package/src/resources/extensions/gsd/tests/integration/doctor-git.test.ts +44 -0
  452. package/src/resources/extensions/gsd/tests/integration/doctor-runtime.test.ts +20 -0
  453. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +112 -1
  454. package/src/resources/extensions/gsd/tests/integration/integration-lifecycle.test.ts +13 -5
  455. package/src/resources/extensions/gsd/tests/integration/migrate-command.test.ts +48 -3
  456. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +6 -1
  457. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +46 -0
  458. package/src/resources/extensions/gsd/tests/knowledge-backfill-projection.test.ts +323 -0
  459. package/src/resources/extensions/gsd/tests/knowledge-capture.test.ts +242 -0
  460. package/src/resources/extensions/gsd/tests/knowledge.test.ts +47 -2
  461. package/src/resources/extensions/gsd/tests/load-knowledge-block-rules-only.test.ts +209 -0
  462. package/src/resources/extensions/gsd/tests/memory-consolidation-scanner.test.ts +316 -0
  463. package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +179 -0
  464. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +5 -1
  465. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +24 -1
  466. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +6 -1
  467. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +26 -18
  468. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +63 -2
  469. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +121 -1
  470. package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +55 -1
  471. package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +29 -5
  472. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +2 -1
  473. package/src/resources/extensions/gsd/tests/plan-milestone-sketch-render.test.ts +157 -0
  474. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +26 -0
  475. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +2 -0
  476. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +251 -2
  477. package/src/resources/extensions/gsd/tests/plan-task.test.ts +17 -0
  478. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +79 -1
  479. package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +86 -0
  480. package/src/resources/extensions/gsd/tests/post-unit-git-failure.test.ts +1 -1
  481. package/src/resources/extensions/gsd/tests/post-unit-state-rebuild.test.ts +84 -0
  482. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +53 -0
  483. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +59 -0
  484. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +8 -0
  485. package/src/resources/extensions/gsd/tests/prompt-loader.test.ts +23 -0
  486. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +37 -1
  487. package/src/resources/extensions/gsd/tests/provider-switch-observer.test.ts +252 -0
  488. package/src/resources/extensions/gsd/tests/quality-gates.test.ts +6 -0
  489. package/src/resources/extensions/gsd/tests/queue-reorder-ui.test.ts +54 -0
  490. package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +89 -2
  491. package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +2 -3
  492. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +16 -4
  493. package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +10 -0
  494. package/src/resources/extensions/gsd/tests/smart-entry-routing.test.ts +113 -0
  495. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +53 -2
  496. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +6 -0
  497. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +119 -23
  498. package/src/resources/extensions/gsd/tests/status-guards.test.ts +13 -1
  499. package/src/resources/extensions/gsd/tests/stuck-state-via-db.test.ts +64 -1
  500. package/src/resources/extensions/gsd/tests/summary-render-parity.test.ts +7 -3
  501. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +103 -7
  502. package/src/resources/extensions/gsd/tests/validate-milestone-stuck-guard.test.ts +29 -2
  503. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +110 -1
  504. package/src/resources/extensions/gsd/tests/verification-verdict.test.ts +78 -0
  505. package/src/resources/extensions/gsd/tests/workflow-kernel.test.ts +7 -0
  506. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +19 -1
  507. package/src/resources/extensions/gsd/tests/workflow-memory-pressure.test.ts +21 -1
  508. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +1 -1
  509. package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +39 -0
  510. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +64 -12
  511. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +25 -0
  512. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +54 -0
  513. package/src/resources/extensions/gsd/tools/complete-milestone.ts +8 -10
  514. package/src/resources/extensions/gsd/tools/complete-slice.ts +6 -8
  515. package/src/resources/extensions/gsd/tools/plan-milestone.ts +5 -1
  516. package/src/resources/extensions/gsd/tools/plan-slice.ts +97 -12
  517. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +135 -0
  518. package/src/resources/extensions/gsd/types.ts +1 -1
  519. package/src/resources/extensions/gsd/unit-context-manifest.ts +47 -11
  520. package/src/resources/extensions/gsd/validation.ts +23 -1
  521. package/src/resources/extensions/gsd/verification-gate.ts +78 -6
  522. package/src/resources/extensions/gsd/verification-verdict.ts +47 -0
  523. package/src/resources/extensions/gsd/workflow-logger.ts +4 -0
  524. package/src/resources/extensions/gsd/workflow-mcp.ts +18 -1
  525. package/src/resources/extensions/gsd/workflow-projections.ts +6 -8
  526. package/src/resources/extensions/gsd/worktree-lifecycle.ts +61 -10
  527. package/src/resources/extensions/gsd/worktree-manager.ts +1 -1
  528. package/src/resources/extensions/shared/html-shell.ts +412 -0
  529. package/src/resources/extensions/subagent/index.ts +567 -103
  530. package/src/resources/extensions/subagent/launch.ts +131 -0
  531. package/src/resources/extensions/subagent/run-store.ts +218 -0
  532. package/src/resources/extensions/subagent/tests/launch.test.ts +115 -0
  533. package/src/resources/extensions/subagent/tests/run-store.test.ts +111 -0
  534. package/src/resources/extensions/ttsr/ttsr-manager.ts +5 -1
  535. package/src/resources/extensions/visual-brief/artifact-policy.ts +41 -0
  536. package/src/resources/extensions/visual-brief/extension-manifest.json +8 -0
  537. package/src/resources/extensions/visual-brief/index.ts +8 -0
  538. package/src/resources/extensions/visual-brief/page-contract.ts +136 -0
  539. package/src/resources/extensions/visual-brief/prompts.ts +183 -0
  540. package/src/resources/extensions/visual-brief/tests/visual-brief.test.ts +212 -0
  541. package/dist/web/standalone/.next/server/chunks/5822.js +0 -2
  542. package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +0 -1
  543. package/dist/web/standalone/.next/static/chunks/8359.e059d86b255fce1c.js +0 -10
  544. package/dist/web/standalone/.next/static/chunks/9441.1081da1125d1764f.js +0 -1
  545. package/dist/web/standalone/.next/static/chunks/app/layout-9ecfd95f343793f0.js +0 -1
  546. package/dist/web/standalone/.next/static/css/54ec2745c1da488b.css +0 -1
  547. package/dist/web/standalone/.next/static/css/de70bee13400563f.css +0 -1
  548. package/dist/web/standalone/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
  549. package/dist/web/standalone/.next/static/media/747892c23ea88013-s.woff2 +0 -0
  550. package/dist/web/standalone/.next/static/media/8d697b304b401681-s.woff2 +0 -0
  551. package/dist/web/standalone/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
  552. package/dist/web/standalone/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
  553. package/dist/web/standalone/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
  554. /package/dist/web/standalone/.next/static/{S44UQTFCUdA44dkjfYt6S → qoMxZh-xuwuvpFW0x0k01}/_buildManifest.js +0 -0
  555. /package/dist/web/standalone/.next/static/{S44UQTFCUdA44dkjfYt6S → qoMxZh-xuwuvpFW0x0k01}/_ssgManifest.js +0 -0
@@ -1,6 +1,6 @@
1
1
  import { clearParseCache } from "../files.js";
2
2
  import { isClosedStatus } from "../status-guards.js";
3
- import { isNonEmptyString, validateStringArray } from "../validation.js";
3
+ import { isNonEmptyString, validateStringArray, validateTitle } from "../validation.js";
4
4
  import {
5
5
  transaction,
6
6
  getMilestone,
@@ -148,6 +148,8 @@ function validateSlices(value: unknown): PlanMilestoneSliceInput[] {
148
148
  if (seen.has(sliceId)) throw new Error(`slices[${index}].sliceId must be unique`);
149
149
  seen.add(sliceId);
150
150
  if (!isNonEmptyString(title)) throw new Error(`slices[${index}].title must be a non-empty string`);
151
+ const titleIssue = validateTitle(title);
152
+ if (titleIssue) throw new Error(`slices[${index}].title is invalid: ${titleIssue}`);
151
153
  if (!isNonEmptyString(risk)) throw new Error(`slices[${index}].risk must be a non-empty string`);
152
154
  if (!Array.isArray(depends) || depends.some((item) => !isNonEmptyString(item))) {
153
155
  throw new Error(`slices[${index}].depends must be an array of non-empty strings`);
@@ -190,6 +192,8 @@ function validateParams(params: PlanMilestoneParams): PlanMilestoneParams {
190
192
  if (!isNonEmptyString(params?.milestoneId)) throw new Error("milestoneId is required");
191
193
  if (!isNonEmptyString(params?.title)) throw new Error("title is required");
192
194
  if (!isNonEmptyString(params?.vision)) throw new Error("vision is required");
195
+ const milestoneTitleIssue = validateTitle(params.title);
196
+ if (milestoneTitleIssue) throw new Error(`title is invalid: ${milestoneTitleIssue}`);
193
197
 
194
198
  return {
195
199
  ...params,
@@ -1,3 +1,5 @@
1
+ import { existsSync, rmSync } from "node:fs";
2
+ import { join, relative } from "node:path";
1
3
  import { clearParseCache } from "../files.js";
2
4
  import { isClosedStatus, isDeferredStatus } from "../status-guards.js";
3
5
  import { isNonEmptyString, validateStringArray } from "../validation.js";
@@ -5,11 +7,15 @@ import {
5
7
  transaction,
6
8
  getMilestone,
7
9
  getSlice,
10
+ getSliceTasks,
8
11
  insertTask,
9
12
  upsertSlicePlanning,
10
13
  upsertTaskPlanning,
11
14
  insertGateRow,
12
15
  updateSliceStatus,
16
+ setSliceSketchFlag,
17
+ deleteTask,
18
+ deleteArtifactByPath,
13
19
  } from "../gsd-db.js";
14
20
  import type { GateId } from "../types.js";
15
21
  import { invalidateStateCache } from "../state.js";
@@ -19,6 +25,9 @@ import { writeManifest } from "../workflow-manifest.js";
19
25
  import { appendEvent } from "../workflow-events.js";
20
26
  import { logWarning } from "../workflow-logger.js";
21
27
  import { validatePlanningPathScope } from "../planning-path-scope.js";
28
+ import { checkFilePathConsistency, checkTaskOrdering } from "../pre-execution-checks.js";
29
+ import type { TaskRow } from "../db-task-slice-rows.js";
30
+ import { buildTaskFileName, gsdProjectionRoot } from "../paths.js";
22
31
 
23
32
  export interface PlanSliceTaskInput {
24
33
  taskId: string;
@@ -86,16 +95,10 @@ function validateTasks(value: unknown): PlanSliceTaskInput[] {
86
95
  if (!isNonEmptyString(title)) throw new Error(`tasks[${index}].title must be a non-empty string`);
87
96
  if (!isNonEmptyString(description)) throw new Error(`tasks[${index}].description must be a non-empty string`);
88
97
  if (!isNonEmptyString(estimate)) throw new Error(`tasks[${index}].estimate must be a non-empty string`);
89
- if (!Array.isArray(files) || files.some((item) => !isNonEmptyString(item))) {
90
- throw new Error(`tasks[${index}].files must be an array of non-empty strings`);
91
- }
98
+ const validatedFiles = validateStringArray(files, `tasks[${index}].files`);
92
99
  if (!isNonEmptyString(verify)) throw new Error(`tasks[${index}].verify must be a non-empty string`);
93
- if (!Array.isArray(inputs) || inputs.some((item) => !isNonEmptyString(item))) {
94
- throw new Error(`tasks[${index}].inputs must be an array of non-empty strings`);
95
- }
96
- if (!Array.isArray(expectedOutput) || expectedOutput.some((item) => !isNonEmptyString(item))) {
97
- throw new Error(`tasks[${index}].expectedOutput must be an array of non-empty strings`);
98
- }
100
+ const validatedInputs = validateStringArray(inputs, `tasks[${index}].inputs`);
101
+ const validatedExpectedOutput = validateStringArray(expectedOutput, `tasks[${index}].expectedOutput`);
99
102
  if (observabilityImpact !== undefined && !isNonEmptyString(observabilityImpact)) {
100
103
  throw new Error(`tasks[${index}].observabilityImpact must be a non-empty string when provided`);
101
104
  }
@@ -105,10 +108,10 @@ function validateTasks(value: unknown): PlanSliceTaskInput[] {
105
108
  title,
106
109
  description,
107
110
  estimate,
108
- files,
111
+ files: validatedFiles,
109
112
  verify,
110
- inputs,
111
- expectedOutput,
113
+ inputs: validatedInputs,
114
+ expectedOutput: validatedExpectedOutput,
112
115
  observabilityImpact: typeof observabilityImpact === "string" ? observabilityImpact : "",
113
116
  };
114
117
  });
@@ -131,6 +134,56 @@ function validateParams(params: PlanSliceParams): PlanSliceParams {
131
134
  };
132
135
  }
133
136
 
137
+ function toTaskRows(params: PlanSliceParams): TaskRow[] {
138
+ return params.tasks.map((task, index) => ({
139
+ milestone_id: params.milestoneId,
140
+ slice_id: params.sliceId,
141
+ id: task.taskId,
142
+ title: task.title,
143
+ status: "pending",
144
+ one_liner: "",
145
+ narrative: "",
146
+ verification_result: "",
147
+ duration: "",
148
+ completed_at: null,
149
+ blocker_discovered: false,
150
+ deviations: "",
151
+ known_issues: "",
152
+ key_files: [],
153
+ key_decisions: [],
154
+ full_summary_md: "",
155
+ description: task.description,
156
+ estimate: task.estimate,
157
+ files: task.files,
158
+ verify: task.verify,
159
+ inputs: task.inputs,
160
+ expected_output: task.expectedOutput,
161
+ observability_impact: task.observabilityImpact ?? "",
162
+ full_plan_md: task.fullPlanMd ?? "",
163
+ sequence: index + 1,
164
+ blocker_source: "",
165
+ escalation_pending: 0,
166
+ escalation_awaiting_review: 0,
167
+ escalation_artifact_path: null,
168
+ escalation_override_applied_at: null,
169
+ }));
170
+ }
171
+
172
+ function validateTaskPathsBeforePersist(params: PlanSliceParams, basePath: string): string | null {
173
+ const taskRows = toTaskRows(params);
174
+ const checks = [
175
+ ...checkFilePathConsistency(taskRows, basePath),
176
+ ...checkTaskOrdering(taskRows, basePath),
177
+ ];
178
+ const blocking = checks.filter((check) => !check.passed && check.blocking);
179
+
180
+ if (blocking.length === 0) return null;
181
+
182
+ return blocking
183
+ .map((check) => `[${check.category}] ${check.target}: ${check.message}`)
184
+ .join("\n");
185
+ }
186
+
134
187
  export async function handlePlanSlice(
135
188
  rawParams: PlanSliceParams,
136
189
  basePath: string,
@@ -154,10 +207,16 @@ export async function handlePlanSlice(
154
207
  return { error: `validation failed: ${pathScopeError}` };
155
208
  }
156
209
 
210
+ const pathError = validateTaskPathsBeforePersist(params, basePath);
211
+ if (pathError) {
212
+ return { error: `pre-execution validation failed:\n${pathError}` };
213
+ }
214
+
157
215
  // ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
158
216
  // Guards must be inside the transaction so the state they check cannot
159
217
  // change between the read and the write (#2723).
160
218
  let guardError: string | null = null;
219
+ let omittedTaskIds: string[] = [];
161
220
 
162
221
  try {
163
222
  transaction(() => {
@@ -181,9 +240,23 @@ export async function handlePlanSlice(
181
240
  return;
182
241
  }
183
242
 
243
+ const newTaskIds = new Set(params.tasks.map((task) => task.taskId));
244
+ const existingTasks = getSliceTasks(params.milestoneId, params.sliceId);
245
+ omittedTaskIds = existingTasks
246
+ .filter((task) => !newTaskIds.has(task.id))
247
+ .map((task) => task.id);
248
+
249
+ for (const task of existingTasks) {
250
+ if (!newTaskIds.has(task.id) && isClosedStatus(task.status)) {
251
+ guardError = `cannot remove completed task ${task.id}`;
252
+ return;
253
+ }
254
+ }
255
+
184
256
  if (isDeferredStatus(parentSlice.status)) {
185
257
  updateSliceStatus(params.milestoneId, params.sliceId, "pending");
186
258
  }
259
+ setSliceSketchFlag(params.milestoneId, params.sliceId, false);
187
260
 
188
261
  upsertSlicePlanning(params.milestoneId, params.sliceId, {
189
262
  goal: params.goal,
@@ -193,6 +266,10 @@ export async function handlePlanSlice(
193
266
  observabilityImpact: params.observabilityImpact,
194
267
  });
195
268
 
269
+ for (const taskId of omittedTaskIds) {
270
+ deleteTask(params.milestoneId, params.sliceId, taskId);
271
+ }
272
+
196
273
  for (const task of params.tasks) {
197
274
  insertTask({
198
275
  id: task.taskId,
@@ -237,6 +314,14 @@ export async function handlePlanSlice(
237
314
  }
238
315
 
239
316
  try {
317
+ const tasksDir = join(gsdProjectionRoot(basePath), "milestones", params.milestoneId, "slices", params.sliceId, "tasks");
318
+ for (const taskId of omittedTaskIds) {
319
+ const taskPlanPath = join(tasksDir, buildTaskFileName(taskId, "PLAN"));
320
+ if (existsSync(taskPlanPath)) rmSync(taskPlanPath, { force: true });
321
+ const artifactPath = relative(gsdProjectionRoot(basePath), taskPlanPath).replace(/\\/g, "/");
322
+ deleteArtifactByPath(artifactPath);
323
+ }
324
+
240
325
  const renderResult = await renderPlanFromDb(basePath, params.milestoneId, params.sliceId);
241
326
  invalidateStateCache();
242
327
  clearParseCache();
@@ -1,3 +1,6 @@
1
+ // Project/App: GSD-2
2
+ // File Purpose: Adapts shared GSD workflow handlers for MCP executor calls.
3
+
1
4
  import { ensureDbOpen } from "../bootstrap/dynamic-tools.js";
2
5
  import { sanitizeCompleteMilestoneParams } from "../bootstrap/sanitize-complete-milestone.js";
3
6
  import { loadWriteGateSnapshot, shouldBlockContextArtifactSaveInSnapshot, shouldBlockRootArtifactSaveInSnapshot } from "../bootstrap/write-gate.js";
@@ -25,6 +28,12 @@ import type { PlanSliceParams } from "./plan-slice.js";
25
28
  import { handlePlanSlice } from "./plan-slice.js";
26
29
  import type { ReplanSliceParams } from "./replan-slice.js";
27
30
  import { handleReplanSlice } from "./replan-slice.js";
31
+ import type { ReopenMilestoneParams } from "./reopen-milestone.js";
32
+ import { handleReopenMilestone } from "./reopen-milestone.js";
33
+ import type { ReopenSliceParams } from "./reopen-slice.js";
34
+ import { handleReopenSlice } from "./reopen-slice.js";
35
+ import type { ReopenTaskParams } from "./reopen-task.js";
36
+ import { handleReopenTask } from "./reopen-task.js";
28
37
  import type { ReassessRoadmapParams } from "./reassess-roadmap.js";
29
38
  import { handleReassessRoadmap } from "./reassess-roadmap.js";
30
39
  import type { ValidateMilestoneParams } from "./validate-milestone.js";
@@ -311,6 +320,9 @@ export type SliceCompleteExecutorParams = CompleteSliceParams;
311
320
  export type PlanMilestoneExecutorParams = PlanMilestoneParams;
312
321
  export type PlanSliceExecutorParams = PlanSliceParams;
313
322
  export type ReplanSliceExecutorParams = ReplanSliceParams;
323
+ export type ReopenTaskExecutorParams = ReopenTaskParams;
324
+ export type ReopenSliceExecutorParams = ReopenSliceParams;
325
+ export type ReopenMilestoneExecutorParams = ReopenMilestoneParams;
314
326
  export type ValidateMilestoneExecutorParams = ValidateMilestoneParams;
315
327
  export type ReassessRoadmapExecutorParams = ReassessRoadmapParams;
316
328
 
@@ -371,6 +383,129 @@ export async function executeTaskComplete(
371
383
  }
372
384
  }
373
385
 
386
+ export async function executeTaskReopen(
387
+ params: ReopenTaskExecutorParams,
388
+ basePath: string = process.cwd(),
389
+ ): Promise<ToolExecutionResult> {
390
+ const dbAvailable = await ensureDbOpen(basePath);
391
+ if (!dbAvailable) {
392
+ return {
393
+ content: [{ type: "text", text: "Error: GSD database is not available. Cannot reopen task." }],
394
+ details: { operation: "reopen_task", error: "db_unavailable" },
395
+ isError: true,
396
+ };
397
+ }
398
+ try {
399
+ const result = await handleReopenTask(params, basePath);
400
+ if ("error" in result) {
401
+ return {
402
+ content: [{ type: "text", text: `Error reopening task: ${result.error}` }],
403
+ details: { operation: "reopen_task", error: result.error },
404
+ isError: true,
405
+ };
406
+ }
407
+ return {
408
+ content: [{ type: "text", text: `Reopened task ${result.taskId} (${result.sliceId}/${result.milestoneId})` }],
409
+ details: {
410
+ operation: "reopen_task",
411
+ taskId: result.taskId,
412
+ sliceId: result.sliceId,
413
+ milestoneId: result.milestoneId,
414
+ },
415
+ };
416
+ } catch (err) {
417
+ const msg = err instanceof Error ? err.message : String(err);
418
+ logError("tool", `reopen_task tool failed: ${msg}`, { tool: "gsd_task_reopen", error: String(err) });
419
+ return {
420
+ content: [{ type: "text", text: `Error reopening task: ${msg}` }],
421
+ details: { operation: "reopen_task", error: msg },
422
+ isError: true,
423
+ };
424
+ }
425
+ }
426
+
427
+ export async function executeSliceReopen(
428
+ params: ReopenSliceExecutorParams,
429
+ basePath: string = process.cwd(),
430
+ ): Promise<ToolExecutionResult> {
431
+ const dbAvailable = await ensureDbOpen(basePath);
432
+ if (!dbAvailable) {
433
+ return {
434
+ content: [{ type: "text", text: "Error: GSD database is not available. Cannot reopen slice." }],
435
+ details: { operation: "reopen_slice", error: "db_unavailable" },
436
+ isError: true,
437
+ };
438
+ }
439
+ try {
440
+ const result = await handleReopenSlice(params, basePath);
441
+ if ("error" in result) {
442
+ return {
443
+ content: [{ type: "text", text: `Error reopening slice: ${result.error}` }],
444
+ details: { operation: "reopen_slice", error: result.error },
445
+ isError: true,
446
+ };
447
+ }
448
+ return {
449
+ content: [{ type: "text", text: `Reopened slice ${result.sliceId} (${result.milestoneId})` }],
450
+ details: {
451
+ operation: "reopen_slice",
452
+ sliceId: result.sliceId,
453
+ milestoneId: result.milestoneId,
454
+ tasksReset: result.tasksReset,
455
+ },
456
+ };
457
+ } catch (err) {
458
+ const msg = err instanceof Error ? err.message : String(err);
459
+ logError("tool", `reopen_slice tool failed: ${msg}`, { tool: "gsd_slice_reopen", error: String(err) });
460
+ return {
461
+ content: [{ type: "text", text: `Error reopening slice: ${msg}` }],
462
+ details: { operation: "reopen_slice", error: msg },
463
+ isError: true,
464
+ };
465
+ }
466
+ }
467
+
468
+ export async function executeMilestoneReopen(
469
+ params: ReopenMilestoneExecutorParams,
470
+ basePath: string = process.cwd(),
471
+ ): Promise<ToolExecutionResult> {
472
+ const dbAvailable = await ensureDbOpen(basePath);
473
+ if (!dbAvailable) {
474
+ return {
475
+ content: [{ type: "text", text: "Error: GSD database is not available. Cannot reopen milestone." }],
476
+ details: { operation: "reopen_milestone", error: "db_unavailable" },
477
+ isError: true,
478
+ };
479
+ }
480
+ try {
481
+ const result = await handleReopenMilestone(params, basePath);
482
+ if ("error" in result) {
483
+ return {
484
+ content: [{ type: "text", text: `Error reopening milestone: ${result.error}` }],
485
+ details: { operation: "reopen_milestone", error: result.error },
486
+ isError: true,
487
+ };
488
+ }
489
+ return {
490
+ content: [{ type: "text", text: `Reopened milestone ${result.milestoneId}` }],
491
+ details: {
492
+ operation: "reopen_milestone",
493
+ milestoneId: result.milestoneId,
494
+ slicesReset: result.slicesReset,
495
+ tasksReset: result.tasksReset,
496
+ },
497
+ };
498
+ } catch (err) {
499
+ const msg = err instanceof Error ? err.message : String(err);
500
+ logError("tool", `reopen_milestone tool failed: ${msg}`, { tool: "gsd_milestone_reopen", error: String(err) });
501
+ return {
502
+ content: [{ type: "text", text: `Error reopening milestone: ${msg}` }],
503
+ details: { operation: "reopen_milestone", error: msg },
504
+ isError: true,
505
+ };
506
+ }
507
+ }
508
+
374
509
  export async function executeSliceComplete(
375
510
  params: SliceCompleteExecutorParams,
376
511
  basePath: string = process.cwd(),
@@ -106,7 +106,7 @@ export interface AuditWarning {
106
106
  export interface VerificationResult {
107
107
  passed: boolean; // true if all checks passed (or no checks discovered)
108
108
  checks: VerificationCheck[]; // per-command results
109
- discoverySource: "preference" | "task-plan" | "package-json" | "none";
109
+ discoverySource: "preference" | "task-plan" | "package-json" | "python-project" | "none";
110
110
  timestamp: number; // Date.now() at gate start
111
111
  runtimeErrors?: RuntimeError[]; // optional — populated by captureRuntimeErrors()
112
112
  auditWarnings?: AuditWarning[]; // optional — populated by runDependencyAudit()
@@ -132,6 +132,9 @@ export type ContextModePolicy =
132
132
  * the explicit `allowedPathGlobs` set; Bash safe-allowlist;
133
133
  * no subagents. Reserved for rewrite-docs, which legitimately
134
134
  * edits project markdown outside .gsd/.
135
+ * - "verification"
136
+ * — Read tools + Bash for verification commands, writes
137
+ * restricted to .gsd/**, no subagents.
135
138
  *
136
139
  * The allowlist for "docs" is declared per-manifest rather than hardcoded so
137
140
  * projects with non-standard doc layouts can extend it without forking the
@@ -143,7 +146,8 @@ export type ToolsPolicy =
143
146
  | { readonly mode: "read-only" }
144
147
  | { readonly mode: "planning" }
145
148
  | { readonly mode: "planning-dispatch"; readonly allowedSubagents: readonly string[] }
146
- | { readonly mode: "docs"; readonly allowedPathGlobs: readonly string[] };
149
+ | { readonly mode: "docs"; readonly allowedPathGlobs: readonly string[] }
150
+ | { readonly mode: "verification" };
147
151
 
148
152
  // ─── Computed-artifact registry (#4924 v2 contract) ───────────────────────
149
153
 
@@ -288,6 +292,7 @@ const COMMON_BUDGET_SMALL = 250_000; // ~65K tokens
288
292
 
289
293
  const TOOLS_ALL: ToolsPolicy = { mode: "all" };
290
294
  const TOOLS_PLANNING: ToolsPolicy = { mode: "planning" };
295
+ const TOOLS_VERIFICATION: ToolsPolicy = { mode: "verification" };
291
296
  // Like TOOLS_PLANNING but permits dispatch to read-only recon/planning
292
297
  // specialists. Runtime-enforced by write-gate.ts before the subagent tool runs.
293
298
  const TOOLS_PLANNING_DISPATCH_RECON: ToolsPolicy = {
@@ -406,8 +411,8 @@ export const UNIT_MANIFESTS: Record<UnitType, UnitContextManifest> = {
406
411
  contextMode: "verification",
407
412
  // planning-dispatch: validation is a verification-fan-out unit. It reads
408
413
  // the milestone surface and dispatches reviewer/security/tester subagents
409
- // to report findings without touching user source. Mirrors
410
- // complete-milestone's policy. Write isolation to .gsd/ is preserved.
414
+ // to report findings without touching user source. Write isolation to
415
+ // .gsd/ is preserved.
411
416
  tools: TOOLS_PLANNING_DISPATCH_REVIEW,
412
417
  artifacts: {
413
418
  inline: ["roadmap", "slice-summary", "slice-uat", "requirements", "decisions", "templates"],
@@ -423,11 +428,9 @@ export const UNIT_MANIFESTS: Record<UnitType, UnitContextManifest> = {
423
428
  codebaseMap: false,
424
429
  preferences: "active-only",
425
430
  contextMode: "verification",
426
- // planning-dispatch: completion is a high-leverage place to fan out to
427
- // reviewer / security / tester subagents. They read the diff and report
428
- // findings; they do not write user source. Write isolation to .gsd/ is
429
- // preserved.
430
- tools: TOOLS_PLANNING_DISPATCH_REVIEW,
431
+ // Milestone closeout must run unrestricted shell verification commands
432
+ // against the final diff before recording completion.
433
+ tools: TOOLS_ALL,
431
434
  artifacts: {
432
435
  // #4780 landed slice-summary as excerpt for this unit; phase 2 of
433
436
  // the architecture will read this manifest as the source of truth
@@ -447,7 +450,9 @@ export const UNIT_MANIFESTS: Record<UnitType, UnitContextManifest> = {
447
450
  codebaseMap: true,
448
451
  preferences: "active-only",
449
452
  contextMode: "research",
450
- tools: TOOLS_PLANNING,
453
+ // Multi-slice research dispatches use the research-slice unit contract to
454
+ // fan out scout subagents that write .gsd research artifacts.
455
+ tools: TOOLS_PLANNING_DISPATCH_RECON,
451
456
  artifacts: {
452
457
  inline: ["roadmap", "milestone-research", "dependency-summaries", "templates"],
453
458
  excerpt: [],
@@ -587,7 +592,7 @@ export const UNIT_MANIFESTS: Record<UnitType, UnitContextManifest> = {
587
592
  codebaseMap: false,
588
593
  preferences: "active-only",
589
594
  contextMode: "verification",
590
- tools: TOOLS_PLANNING,
595
+ tools: TOOLS_VERIFICATION,
591
596
  artifacts: {
592
597
  // Phase 3 migration (#4782): manifest matches today's actual
593
598
  // buildRunUatPrompt inlining. Prior phase-1 entry listed
@@ -606,7 +611,9 @@ export const UNIT_MANIFESTS: Record<UnitType, UnitContextManifest> = {
606
611
  codebaseMap: false,
607
612
  preferences: "active-only",
608
613
  contextMode: "verification",
609
- tools: TOOLS_PLANNING,
614
+ // Gate evaluation fans out tester-style subagents, which read the slice
615
+ // plan and report via the DB-backed gate-result tool.
616
+ tools: TOOLS_PLANNING_DISPATCH_REVIEW,
610
617
  artifacts: {
611
618
  inline: ["slice-plan", "prior-task-summaries"],
612
619
  excerpt: [],
@@ -734,3 +741,32 @@ export const UNIT_MANIFESTS: Record<UnitType, UnitContextManifest> = {
734
741
  export function resolveManifest(unitType: string): UnitContextManifest | null {
735
742
  return (UNIT_MANIFESTS as Record<string, UnitContextManifest>)[unitType] ?? null;
736
743
  }
744
+
745
+ export interface SubagentPermissionContract {
746
+ readonly allowed: boolean;
747
+ readonly allowedSubagents: readonly string[];
748
+ readonly toolsMode: ToolsPolicy["mode"] | "unknown";
749
+ }
750
+
751
+ export function compileSubagentPermissionContract(
752
+ policy: ToolsPolicy | null | undefined,
753
+ ): SubagentPermissionContract {
754
+ if (!policy) {
755
+ return { allowed: false, allowedSubagents: [], toolsMode: "unknown" };
756
+ }
757
+ if (policy.mode === "all") {
758
+ return { allowed: true, allowedSubagents: ["*"], toolsMode: policy.mode };
759
+ }
760
+ if (policy.mode === "planning-dispatch") {
761
+ return {
762
+ allowed: true,
763
+ allowedSubagents: [...policy.allowedSubagents],
764
+ toolsMode: policy.mode,
765
+ };
766
+ }
767
+ return { allowed: false, allowedSubagents: [], toolsMode: policy.mode };
768
+ }
769
+
770
+ export function resolveSubagentPermissionContract(unitType: string): SubagentPermissionContract {
771
+ return compileSubagentPermissionContract(resolveManifest(unitType)?.tools);
772
+ }
@@ -7,6 +7,27 @@ export function isNonEmptyString(value: unknown): value is string {
7
7
  return typeof value === "string" && value.trim().length > 0;
8
8
  }
9
9
 
10
+ /**
11
+ * Characters that are used as delimiters in GSD state management documents
12
+ * and should not appear in milestone or slice titles.
13
+ */
14
+ const TITLE_DELIMITER_RE = /[\u2014\u2013\/]/; // em dash, en dash, forward slash
15
+
16
+ /**
17
+ * Check whether a milestone or slice title contains characters that conflict
18
+ * with GSD's state document delimiter conventions.
19
+ * Returns a human-readable description of the problem, or null if the title is safe.
20
+ */
21
+ export function validateTitle(title: string): string | null {
22
+ if (TITLE_DELIMITER_RE.test(title)) {
23
+ const found: string[] = [];
24
+ if (/[\u2014\u2013]/.test(title)) found.push("em/en dash (\u2014 or \u2013)");
25
+ if (/\//.test(title)) found.push("forward slash (/)");
26
+ return `title contains ${found.join(" and ")}, which conflict with GSD state document delimiters`;
27
+ }
28
+ return null;
29
+ }
30
+
10
31
  /**
11
32
  * Validate that `value` is an array of non-empty strings.
12
33
  * Throws with a message referencing `field` on failure.
@@ -14,7 +35,8 @@ export function isNonEmptyString(value: unknown): value is string {
14
35
  */
15
36
  export function validateStringArray(value: unknown, field: string): string[] {
16
37
  if (!Array.isArray(value)) {
17
- throw new Error(`${field} must be an array`);
38
+ const received = value === null ? "null" : typeof value;
39
+ throw new Error(`${field} must be an array of strings, not ${received}`);
18
40
  }
19
41
  if (value.some((item) => !isNonEmptyString(item))) {
20
42
  throw new Error(`${field} must contain only non-empty strings`);
@@ -4,7 +4,7 @@
4
4
  // First non-empty source wins.
5
5
 
6
6
  import { spawnSync, type SpawnSyncReturns } from "node:child_process";
7
- import { existsSync, readFileSync } from "node:fs";
7
+ import { existsSync, readFileSync, readdirSync, type Dirent } from "node:fs";
8
8
  import { join, basename } from "node:path";
9
9
  import type { AuditWarning, RuntimeError, VerificationCheck, VerificationResult } from "./types.js";
10
10
  import { DEFAULT_COMMAND_TIMEOUT_MS } from "./constants.js";
@@ -44,7 +44,8 @@ const PACKAGE_SCRIPT_KEYS = ["typecheck", "lint", "test"] as const;
44
44
  * 1. Explicit preference commands
45
45
  * 2. Task plan verify field (split on &&)
46
46
  * 3. package.json scripts (typecheck, lint, test)
47
- * 4. None found
47
+ * 4. Python pytest project markers
48
+ * 5. None found
48
49
  */
49
50
  export function discoverCommands(options: DiscoverCommandsOptions): DiscoveredCommands {
50
51
  // 1. Preference commands
@@ -91,10 +92,67 @@ export function discoverCommands(options: DiscoverCommandsOptions): DiscoveredCo
91
92
  }
92
93
  }
93
94
 
94
- // 4. Nothing found
95
+ const pythonCommand = discoverPythonPytestCommand(options.cwd);
96
+ if (pythonCommand) {
97
+ return { commands: [pythonCommand], source: "python-project" };
98
+ }
99
+
100
+ // 5. Nothing found
95
101
  return { commands: [], source: "none" };
96
102
  }
97
103
 
104
+ function discoverPythonPytestCommand(cwd: string): string | null {
105
+ const hasPythonTestFiles = hasPythonTests(join(cwd, "tests"));
106
+ const hasPytestConfig = existsSync(join(cwd, "pytest.ini"));
107
+ const pyprojectPath = join(cwd, "pyproject.toml");
108
+ const hasPyproject = existsSync(pyprojectPath);
109
+
110
+ if (!hasPythonTestFiles && !hasPytestConfig && !hasPyproject) {
111
+ return null;
112
+ }
113
+
114
+ if (hasPytestConfig || hasPythonTestFiles) {
115
+ return "python3 -m pytest";
116
+ }
117
+
118
+ try {
119
+ const pyproject = readFileSync(pyprojectPath, "utf-8");
120
+ if (
121
+ pyproject.includes("[tool.pytest]") ||
122
+ pyproject.includes("[tool.pytest.") ||
123
+ pyproject.includes("[pytest]") ||
124
+ pyproject.includes("[tool:pytest]")
125
+ ) {
126
+ return "python3 -m pytest";
127
+ }
128
+ } catch {
129
+ // Ignore unreadable pyproject.toml and fall through.
130
+ }
131
+
132
+ return null;
133
+ }
134
+
135
+ function hasPythonTests(dir: string): boolean {
136
+ let entries: Dirent[];
137
+ try {
138
+ entries = readdirSync(dir, { withFileTypes: true });
139
+ } catch {
140
+ return false;
141
+ }
142
+
143
+ for (const entry of entries) {
144
+ const path = join(dir, entry.name);
145
+ if (entry.isDirectory() && hasPythonTests(path)) {
146
+ return true;
147
+ }
148
+ if (entry.isFile() && /^test_.*\.py$|^.*_test\.py$/.test(entry.name)) {
149
+ return true;
150
+ }
151
+ }
152
+
153
+ return false;
154
+ }
155
+
98
156
  // ─── Failure Context Formatting ──────────────────────────────────────────────
99
157
 
100
158
  /** Maximum chars of stderr to include per failed check in failure context. */
@@ -144,7 +202,7 @@ export function formatFailureContext(result: VerificationResult): string {
144
202
  // ─── Gate Execution ─────────────────────────────────────────────────────────
145
203
 
146
204
  /** Characters that indicate shell injection when found in a command string. */
147
- const SHELL_INJECTION_PATTERN = /[;|`]|\$\(/;
205
+ const SHELL_INJECTION_PATTERN = /[;|`<>]|\$\(/;
148
206
 
149
207
  /**
150
208
  * Known executable first-tokens that are safe to run.
@@ -182,6 +240,7 @@ const KNOWN_COMMAND_PREFIXES = new Set([
182
240
  * Heuristics (any true → prose-like):
183
241
  * 1. First token starts with an uppercase letter and the string has 4+ words
184
242
  * 2. String contains commas followed by spaces (prose clause structure)
243
+ * 3. First token has no ASCII letters or digits and the string has 4+ words
185
244
  */
186
245
  export function isLikelyCommand(cmd: string): boolean {
187
246
  const trimmed = cmd.trim();
@@ -208,6 +267,9 @@ export function isLikelyCommand(cmd: string): boolean {
208
267
  // First token has uppercase letters and no path separators → prose
209
268
  if (/[A-Z]/.test(firstToken) && !firstToken.includes("/")) return false;
210
269
 
270
+ // Non-ASCII prose with multiple words should not be executed as a command.
271
+ if (!/[A-Za-z0-9]/.test(firstToken) && tokens.length >= 4) return false;
272
+
211
273
  return true;
212
274
  }
213
275
 
@@ -215,9 +277,19 @@ export function isLikelyCommand(cmd: string): boolean {
215
277
  * Validate a command string for obvious shell injection patterns.
216
278
  * Returns the command unchanged if safe, or null if suspicious.
217
279
  */
280
+ export function validateVerificationCommand(cmd: string): { ok: true } | { ok: false; reason: string } {
281
+ if (SHELL_INJECTION_PATTERN.test(cmd)) {
282
+ return { ok: false, reason: "contains shell control syntax such as pipes, redirects, semicolons, backticks, or command substitution" };
283
+ }
284
+ if (!isLikelyCommand(cmd)) {
285
+ return { ok: false, reason: "does not look like a runnable command" };
286
+ }
287
+ return { ok: true };
288
+ }
289
+
218
290
  function sanitizeCommand(cmd: string): string | null {
219
- if (SHELL_INJECTION_PATTERN.test(cmd)) return null;
220
- if (!isLikelyCommand(cmd)) return null;
291
+ const validation = validateVerificationCommand(cmd);
292
+ if (!validation.ok) return null;
221
293
  return cmd;
222
294
  }
223
295