@opengsd/gsd-pi 1.2.0-dev.e8563f58 → 1.2.0-dev.fbdca60b

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 (432) hide show
  1. package/dist/cli-model-override.d.ts +15 -0
  2. package/dist/cli-model-override.js +21 -0
  3. package/dist/cli.js +1 -18
  4. package/dist/loader.js +6 -4
  5. package/dist/register-agent-bundles.d.ts +11 -2
  6. package/dist/register-agent-bundles.js +18 -4
  7. package/dist/resource-loader.d.ts +10 -5
  8. package/dist/resource-loader.js +121 -6
  9. package/dist/resources/.managed-resources-content-hash +1 -1
  10. package/dist/resources/extensions/ask-user-questions.js +3 -2
  11. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +447 -215
  12. package/dist/resources/extensions/claude-code-cli/turn-assembler.js +33 -1
  13. package/dist/resources/extensions/gsd/auto/closeout.js +215 -0
  14. package/dist/resources/extensions/gsd/auto/dispatch-history.js +21 -6
  15. package/dist/resources/extensions/gsd/auto/dispatch.js +365 -0
  16. package/dist/resources/extensions/gsd/auto/finalize.js +347 -0
  17. package/dist/resources/extensions/gsd/auto/loop.js +4 -1
  18. package/dist/resources/extensions/gsd/auto/milestone-lease-reclaim.js +56 -0
  19. package/dist/resources/extensions/gsd/auto/orchestrator.js +85 -15
  20. package/dist/resources/extensions/gsd/auto/phase-helpers.js +146 -0
  21. package/dist/resources/extensions/gsd/auto/phases.js +17 -2329
  22. package/dist/resources/extensions/gsd/auto/pre-dispatch.js +534 -0
  23. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  24. package/dist/resources/extensions/gsd/auto/unit-phase.js +694 -0
  25. package/dist/resources/extensions/gsd/auto/workflow-unit-dispatch.js +1 -1
  26. package/dist/resources/extensions/gsd/auto/worktree-safety-phase.js +125 -0
  27. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +3 -2
  28. package/dist/resources/extensions/gsd/auto-dispatch.js +11 -2
  29. package/dist/resources/extensions/gsd/auto-post-unit.js +18 -6
  30. package/dist/resources/extensions/gsd/auto-start.js +23 -3
  31. package/dist/resources/extensions/gsd/auto-unit-closeout.js +45 -21
  32. package/dist/resources/extensions/gsd/auto-verification.js +14 -2
  33. package/dist/resources/extensions/gsd/auto-worktree.js +15 -2
  34. package/dist/resources/extensions/gsd/auto.js +45 -2
  35. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +37 -7
  36. package/dist/resources/extensions/gsd/commands/context.js +16 -2
  37. package/dist/resources/extensions/gsd/commands-mcp-status.js +2 -2
  38. package/dist/resources/extensions/gsd/commands-workflow-templates.js +9 -2
  39. package/dist/resources/extensions/gsd/crash-recovery.js +8 -3
  40. package/dist/resources/extensions/gsd/db/engine.js +24 -6
  41. package/dist/resources/extensions/gsd/db/queries.js +30 -0
  42. package/dist/resources/extensions/gsd/db-migration-backup.js +51 -8
  43. package/dist/resources/extensions/gsd/db-transaction.js +27 -23
  44. package/dist/resources/extensions/gsd/db-writer.js +8 -17
  45. package/dist/resources/extensions/gsd/doctor-engine-checks.js +5 -5
  46. package/dist/resources/extensions/gsd/doctor-environment.js +256 -125
  47. package/dist/resources/extensions/gsd/gsd-db.js +15 -20
  48. package/dist/resources/extensions/gsd/guided-flow.js +93 -4
  49. package/dist/resources/extensions/gsd/health-widget.js +87 -28
  50. package/dist/resources/extensions/gsd/mcp-bridge.js +10 -0
  51. package/dist/resources/extensions/gsd/memory-relations.js +1 -1
  52. package/dist/resources/extensions/gsd/milestone-planning-persistence.js +2 -2
  53. package/dist/resources/extensions/gsd/milestone-reopen-events.js +3 -5
  54. package/dist/resources/extensions/gsd/milestone-settlement.js +2 -2
  55. package/dist/resources/extensions/gsd/notifications.js +12 -7
  56. package/dist/resources/extensions/gsd/projection-flush.js +7 -0
  57. package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  58. package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -2
  59. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  60. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  61. package/dist/resources/extensions/gsd/prompts/quick-task.md +1 -1
  62. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  63. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  64. package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  65. package/dist/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  66. package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
  67. package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  68. package/dist/resources/extensions/gsd/prompts/run-uat.md +3 -1
  69. package/dist/resources/extensions/gsd/prompts/triage-captures.md +1 -1
  70. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +1 -1
  71. package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -1
  72. package/dist/resources/extensions/gsd/roadmap-slices.js +25 -3
  73. package/dist/resources/extensions/gsd/session-lock.js +1 -1
  74. package/dist/resources/extensions/gsd/skill-activation.js +3 -6
  75. package/dist/resources/extensions/gsd/state.js +6 -2
  76. package/dist/resources/extensions/gsd/tool-contract.js +14 -3
  77. package/dist/resources/extensions/gsd/tool-surface-readiness.js +83 -31
  78. package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -2
  79. package/dist/resources/extensions/gsd/tools/complete-slice.js +2 -2
  80. package/dist/resources/extensions/gsd/tools/complete-task.js +65 -2
  81. package/dist/resources/extensions/gsd/tools/plan-slice.js +2 -2
  82. package/dist/resources/extensions/gsd/tools/plan-task.js +2 -2
  83. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +2 -2
  84. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +2 -2
  85. package/dist/resources/extensions/gsd/tools/reopen-slice.js +2 -2
  86. package/dist/resources/extensions/gsd/tools/reopen-task.js +2 -2
  87. package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -2
  88. package/dist/resources/extensions/gsd/unit-context-composer.js +1 -1
  89. package/dist/resources/extensions/gsd/unit-registry.js +34 -4
  90. package/dist/resources/extensions/gsd/verification-verdict.js +2 -1
  91. package/dist/resources/extensions/gsd/workflow-event-ledger.js +91 -0
  92. package/dist/resources/extensions/gsd/workflow-event-vocabulary.js +46 -0
  93. package/dist/resources/extensions/gsd/workflow-events.js +6 -18
  94. package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +2 -0
  95. package/dist/resources/extensions/gsd/workflow-mcp-readiness-cache.js +105 -0
  96. package/dist/resources/extensions/gsd/workflow-reconcile.js +21 -56
  97. package/dist/resources/extensions/gsd/worktree-manager.js +7 -1
  98. package/dist/resources/extensions/gsd/worktree-safety.js +28 -26
  99. package/dist/resources/extensions/gsd/worktree.js +8 -1
  100. package/dist/resources/extensions/mcp-client/manager.js +6 -1
  101. package/dist/resources/skills/create-skill/SKILL.md +3 -0
  102. package/dist/resources/skills/create-skill/references/skill-structure.md +1 -0
  103. package/dist/runtime-checks.d.ts +10 -0
  104. package/dist/runtime-checks.js +27 -0
  105. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  106. package/dist/web/standalone/.next/BUILD_ID +1 -1
  107. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  108. package/dist/web/standalone/.next/build-manifest.json +2 -2
  109. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  110. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  111. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  115. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  119. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  120. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  121. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  126. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  127. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  128. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  129. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  130. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  131. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  132. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  133. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  134. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  135. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  136. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  137. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  138. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  139. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  140. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  141. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  142. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
  143. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  144. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  145. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  146. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  147. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  148. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  149. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  150. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  151. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  152. package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
  153. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  154. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  155. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  156. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  157. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  158. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  159. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  160. package/dist/web/standalone/.next/server/app/index.html +1 -1
  161. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  162. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  163. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  164. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  165. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  166. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  167. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  168. package/dist/web/standalone/.next/server/chunks/{5942.js → 1128.js} +1 -1
  169. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  170. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  172. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  173. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  174. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  175. package/package.json +3 -3
  176. package/packages/cloud-mcp-gateway/package.json +2 -2
  177. package/packages/contracts/package.json +1 -1
  178. package/packages/daemon/package.json +4 -4
  179. package/packages/gsd-agent-core/dist/sdk.d.ts.map +1 -1
  180. package/packages/gsd-agent-core/dist/sdk.js +6 -4
  181. package/packages/gsd-agent-core/dist/sdk.js.map +1 -1
  182. package/packages/gsd-agent-core/package.json +5 -5
  183. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  184. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  185. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +10 -0
  186. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
  187. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +8 -0
  188. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  189. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +50 -6
  190. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  191. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts +2 -0
  192. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  193. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +34 -5
  194. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  195. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts +1 -0
  196. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  197. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +17 -0
  198. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
  199. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.d.ts.map +1 -1
  200. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js +4 -0
  201. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js.map +1 -1
  202. package/packages/gsd-agent-modes/package.json +7 -7
  203. package/packages/mcp-server/README.md +12 -3
  204. package/packages/mcp-server/dist/cli-runner.d.ts +40 -0
  205. package/packages/mcp-server/dist/cli-runner.d.ts.map +1 -0
  206. package/packages/mcp-server/dist/cli-runner.js +137 -0
  207. package/packages/mcp-server/dist/cli-runner.js.map +1 -0
  208. package/packages/mcp-server/dist/cli.js +2 -53
  209. package/packages/mcp-server/dist/cli.js.map +1 -1
  210. package/packages/mcp-server/dist/pid-registry.d.ts +46 -0
  211. package/packages/mcp-server/dist/pid-registry.d.ts.map +1 -0
  212. package/packages/mcp-server/dist/pid-registry.js +459 -0
  213. package/packages/mcp-server/dist/pid-registry.js.map +1 -0
  214. package/packages/mcp-server/dist/probe-mode.d.ts +4 -0
  215. package/packages/mcp-server/dist/probe-mode.d.ts.map +1 -0
  216. package/packages/mcp-server/dist/probe-mode.js +10 -0
  217. package/packages/mcp-server/dist/probe-mode.js.map +1 -0
  218. package/packages/mcp-server/dist/stdio-watchdog.d.ts +8 -0
  219. package/packages/mcp-server/dist/stdio-watchdog.d.ts.map +1 -0
  220. package/packages/mcp-server/dist/stdio-watchdog.js +40 -0
  221. package/packages/mcp-server/dist/stdio-watchdog.js.map +1 -0
  222. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  223. package/packages/mcp-server/dist/workflow-tools.js +62 -43
  224. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  225. package/packages/mcp-server/package.json +5 -5
  226. package/packages/native/package.json +1 -1
  227. package/packages/pi-agent-core/dist/agent-loop.js +43 -2
  228. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  229. package/packages/pi-agent-core/package.json +1 -1
  230. package/packages/pi-ai/package.json +1 -1
  231. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  232. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  233. package/packages/pi-coding-agent/dist/core/settings-manager.js +11 -0
  234. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  235. package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
  236. package/packages/pi-coding-agent/dist/theme/theme.js +45 -17
  237. package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
  238. package/packages/pi-coding-agent/package.json +7 -7
  239. package/packages/pi-tui/README.md +15 -0
  240. package/packages/pi-tui/dist/index.d.ts +2 -2
  241. package/packages/pi-tui/dist/index.d.ts.map +1 -1
  242. package/packages/pi-tui/dist/index.js +2 -2
  243. package/packages/pi-tui/dist/index.js.map +1 -1
  244. package/packages/pi-tui/dist/terminal-image.d.ts +33 -0
  245. package/packages/pi-tui/dist/terminal-image.d.ts.map +1 -1
  246. package/packages/pi-tui/dist/terminal-image.js +54 -2
  247. package/packages/pi-tui/dist/terminal-image.js.map +1 -1
  248. package/packages/pi-tui/dist/terminal.d.ts +12 -0
  249. package/packages/pi-tui/dist/terminal.d.ts.map +1 -1
  250. package/packages/pi-tui/dist/terminal.js +70 -25
  251. package/packages/pi-tui/dist/terminal.js.map +1 -1
  252. package/packages/pi-tui/dist/tui.d.ts +15 -0
  253. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  254. package/packages/pi-tui/dist/tui.js +106 -21
  255. package/packages/pi-tui/dist/tui.js.map +1 -1
  256. package/packages/pi-tui/dist/utils.d.ts.map +1 -1
  257. package/packages/pi-tui/dist/utils.js +110 -36
  258. package/packages/pi-tui/dist/utils.js.map +1 -1
  259. package/packages/pi-tui/package.json +2 -2
  260. package/packages/rpc-client/package.json +2 -2
  261. package/pkg/dist/theme/theme.d.ts.map +1 -1
  262. package/pkg/dist/theme/theme.js +45 -17
  263. package/pkg/dist/theme/theme.js.map +1 -1
  264. package/pkg/package.json +1 -1
  265. package/src/resources/extensions/ask-user-questions.ts +7 -2
  266. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +531 -226
  267. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +672 -7
  268. package/src/resources/extensions/claude-code-cli/turn-assembler.ts +38 -1
  269. package/src/resources/extensions/gsd/auto/closeout.ts +309 -0
  270. package/src/resources/extensions/gsd/auto/dispatch-history.ts +22 -6
  271. package/src/resources/extensions/gsd/auto/dispatch.ts +449 -0
  272. package/src/resources/extensions/gsd/auto/finalize.ts +445 -0
  273. package/src/resources/extensions/gsd/auto/loop.ts +4 -1
  274. package/src/resources/extensions/gsd/auto/milestone-lease-reclaim.ts +74 -0
  275. package/src/resources/extensions/gsd/auto/orchestrator.ts +95 -15
  276. package/src/resources/extensions/gsd/auto/phase-helpers.ts +199 -0
  277. package/src/resources/extensions/gsd/auto/phases.ts +58 -3022
  278. package/src/resources/extensions/gsd/auto/pre-dispatch.ts +704 -0
  279. package/src/resources/extensions/gsd/auto/session.ts +3 -0
  280. package/src/resources/extensions/gsd/auto/unit-phase.ts +910 -0
  281. package/src/resources/extensions/gsd/auto/workflow-unit-dispatch.ts +1 -1
  282. package/src/resources/extensions/gsd/auto/worktree-safety-phase.ts +149 -0
  283. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +10 -16
  284. package/src/resources/extensions/gsd/auto-dispatch.ts +11 -10
  285. package/src/resources/extensions/gsd/auto-post-unit.ts +21 -6
  286. package/src/resources/extensions/gsd/auto-start.ts +24 -4
  287. package/src/resources/extensions/gsd/auto-unit-closeout.ts +83 -28
  288. package/src/resources/extensions/gsd/auto-verification.ts +18 -2
  289. package/src/resources/extensions/gsd/auto-worktree.ts +15 -2
  290. package/src/resources/extensions/gsd/auto.ts +56 -2
  291. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +56 -6
  292. package/src/resources/extensions/gsd/commands/context.ts +16 -2
  293. package/src/resources/extensions/gsd/commands-mcp-status.ts +2 -2
  294. package/src/resources/extensions/gsd/commands-workflow-templates.ts +11 -4
  295. package/src/resources/extensions/gsd/crash-recovery.ts +10 -2
  296. package/src/resources/extensions/gsd/db/engine.ts +26 -6
  297. package/src/resources/extensions/gsd/db/queries.ts +29 -0
  298. package/src/resources/extensions/gsd/db-migration-backup.ts +56 -7
  299. package/src/resources/extensions/gsd/db-transaction.ts +37 -20
  300. package/src/resources/extensions/gsd/db-writer.ts +11 -19
  301. package/src/resources/extensions/gsd/doctor-engine-checks.ts +5 -4
  302. package/src/resources/extensions/gsd/doctor-environment.ts +267 -142
  303. package/src/resources/extensions/gsd/gsd-db.ts +15 -19
  304. package/src/resources/extensions/gsd/guided-flow.ts +145 -24
  305. package/src/resources/extensions/gsd/health-widget.ts +91 -27
  306. package/src/resources/extensions/gsd/mcp-bridge.ts +39 -0
  307. package/src/resources/extensions/gsd/memory-relations.ts +1 -1
  308. package/src/resources/extensions/gsd/milestone-planning-persistence.ts +2 -2
  309. package/src/resources/extensions/gsd/milestone-reopen-events.ts +3 -6
  310. package/src/resources/extensions/gsd/milestone-settlement.ts +2 -2
  311. package/src/resources/extensions/gsd/notifications.ts +13 -6
  312. package/src/resources/extensions/gsd/projection-flush.ts +20 -0
  313. package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  314. package/src/resources/extensions/gsd/prompts/execute-task.md +3 -2
  315. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  316. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  317. package/src/resources/extensions/gsd/prompts/quick-task.md +1 -1
  318. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  319. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  320. package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  321. package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  322. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  323. package/src/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  324. package/src/resources/extensions/gsd/prompts/run-uat.md +3 -1
  325. package/src/resources/extensions/gsd/prompts/triage-captures.md +1 -1
  326. package/src/resources/extensions/gsd/prompts/validate-milestone.md +1 -1
  327. package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -1
  328. package/src/resources/extensions/gsd/roadmap-slices.ts +28 -3
  329. package/src/resources/extensions/gsd/session-lock.ts +1 -1
  330. package/src/resources/extensions/gsd/skill-activation.ts +3 -6
  331. package/src/resources/extensions/gsd/state.ts +7 -1
  332. package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +1 -1
  333. package/src/resources/extensions/gsd/tests/auto-blocked-remediation-message.test.ts +1 -1
  334. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +206 -22
  335. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +6 -1
  336. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +76 -12
  337. package/src/resources/extensions/gsd/tests/auto-pause-double-entry-guard.test.ts +1 -1
  338. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +77 -1
  339. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +2 -1
  340. package/src/resources/extensions/gsd/tests/auto-remote-session-lock-cleanup.test.ts +65 -3
  341. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +236 -0
  342. package/src/resources/extensions/gsd/tests/auto-unit-closeout.test.ts +169 -1
  343. package/src/resources/extensions/gsd/tests/complete-task.test.ts +141 -5
  344. package/src/resources/extensions/gsd/tests/db-migration-backup.test.ts +68 -19
  345. package/src/resources/extensions/gsd/tests/db-transaction.test.ts +59 -0
  346. package/src/resources/extensions/gsd/tests/db-writer.test.ts +15 -4
  347. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +2 -1
  348. package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +62 -0
  349. package/src/resources/extensions/gsd/tests/discuss-routing-fixes.test.ts +12 -2
  350. package/src/resources/extensions/gsd/tests/dispatch-history.test.ts +55 -0
  351. package/src/resources/extensions/gsd/tests/dist-redirect.mjs +8 -0
  352. package/src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts +117 -91
  353. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +113 -0
  354. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +19 -0
  355. package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +18 -6
  356. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +15 -0
  357. package/src/resources/extensions/gsd/tests/integration/doctor-environment-async.test.ts +104 -0
  358. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +18 -0
  359. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +47 -16
  360. package/src/resources/extensions/gsd/tests/mcp-readiness-preflight.test.ts +205 -0
  361. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +6 -5
  362. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +1 -1
  363. package/src/resources/extensions/gsd/tests/milestone-report-path.test.ts +1 -1
  364. package/src/resources/extensions/gsd/tests/milestone-settlement.test.ts +92 -0
  365. package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +1 -1
  366. package/src/resources/extensions/gsd/tests/notifications.test.ts +64 -9
  367. package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +2 -2
  368. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +5 -0
  369. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +1 -1
  370. package/src/resources/extensions/gsd/tests/phases-terminal-complete-idempotent.test.ts +242 -0
  371. package/src/resources/extensions/gsd/tests/plan-gate-failed-doctor-heal-hint.test.ts +3 -3
  372. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +63 -2
  373. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +10 -2
  374. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -4
  375. package/src/resources/extensions/gsd/tests/remote-notification-from-desktop.test.ts +31 -81
  376. package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +68 -0
  377. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +26 -2
  378. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +170 -48
  379. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +20 -17
  380. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +7 -3
  381. package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +1 -1
  382. package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +17 -0
  383. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +4 -2
  384. package/src/resources/extensions/gsd/tests/tool-surface-readiness.test.ts +184 -10
  385. package/src/resources/extensions/gsd/tests/tool-unavailable-retry.test.ts +33 -0
  386. package/src/resources/extensions/gsd/tests/transport-gate-double-complete.test.ts +139 -0
  387. package/src/resources/extensions/gsd/tests/uok-audit-unified.test.ts +8 -0
  388. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
  389. package/src/resources/extensions/gsd/tests/verification-verdict.test.ts +2 -0
  390. package/src/resources/extensions/gsd/tests/workflow-events.test.ts +19 -0
  391. package/src/resources/extensions/gsd/tests/workflow-mcp-readiness-cache.test.ts +119 -0
  392. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +65 -2
  393. package/src/resources/extensions/gsd/tests/workflow-phase-contract-matrix.test.ts +332 -0
  394. package/src/resources/extensions/gsd/tests/workflow-reconcile.test.ts +20 -0
  395. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +92 -0
  396. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +1 -1
  397. package/src/resources/extensions/gsd/tests/worktree-project-root-degrade.test.ts +1 -1
  398. package/src/resources/extensions/gsd/tests/worktree-safety-phase.test.ts +100 -0
  399. package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +72 -0
  400. package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +22 -0
  401. package/src/resources/extensions/gsd/tests/worktree.test.ts +18 -0
  402. package/src/resources/extensions/gsd/tool-contract.ts +38 -3
  403. package/src/resources/extensions/gsd/tool-surface-readiness.ts +126 -19
  404. package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -2
  405. package/src/resources/extensions/gsd/tools/complete-slice.ts +2 -2
  406. package/src/resources/extensions/gsd/tools/complete-task.ts +90 -2
  407. package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -2
  408. package/src/resources/extensions/gsd/tools/plan-task.ts +2 -2
  409. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +2 -2
  410. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +2 -2
  411. package/src/resources/extensions/gsd/tools/reopen-slice.ts +2 -2
  412. package/src/resources/extensions/gsd/tools/reopen-task.ts +2 -2
  413. package/src/resources/extensions/gsd/tools/replan-slice.ts +2 -2
  414. package/src/resources/extensions/gsd/unit-context-composer.ts +1 -1
  415. package/src/resources/extensions/gsd/unit-registry.ts +34 -4
  416. package/src/resources/extensions/gsd/verification-verdict.ts +4 -2
  417. package/src/resources/extensions/gsd/workflow-event-ledger.ts +131 -0
  418. package/src/resources/extensions/gsd/workflow-event-vocabulary.ts +59 -0
  419. package/src/resources/extensions/gsd/workflow-events.ts +12 -20
  420. package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +2 -0
  421. package/src/resources/extensions/gsd/workflow-mcp-readiness-cache.ts +150 -0
  422. package/src/resources/extensions/gsd/workflow-reconcile.ts +29 -62
  423. package/src/resources/extensions/gsd/worktree-manager.ts +6 -1
  424. package/src/resources/extensions/gsd/worktree-safety.ts +41 -39
  425. package/src/resources/extensions/gsd/worktree.ts +7 -1
  426. package/src/resources/extensions/mcp-client/manager.ts +7 -1
  427. package/src/resources/skills/create-skill/SKILL.md +3 -0
  428. package/src/resources/skills/create-skill/references/skill-structure.md +1 -0
  429. package/dist/resources/skills/gsd-browser/SKILL.md +0 -41
  430. package/src/resources/skills/gsd-browser/SKILL.md +0 -41
  431. /package/dist/web/standalone/.next/static/{LDHRKiRBIVZmiuMjrL1Vy → 2T9IOdiiM3o3gZ4UbPi8E}/_buildManifest.js +0 -0
  432. /package/dist/web/standalone/.next/static/{LDHRKiRBIVZmiuMjrL1Vy → 2T9IOdiiM3o3gZ4UbPi8E}/_ssgManifest.js +0 -0
@@ -10,7 +10,7 @@
10
10
  */
11
11
 
12
12
  import { existsSync, readFileSync, statSync } from "node:fs";
13
- import { execSync } from "node:child_process";
13
+ import { execFile, execSync } from "node:child_process";
14
14
  import { join } from "node:path";
15
15
 
16
16
  import type { DoctorIssue, DoctorIssueCode } from "./doctor-types.js";
@@ -76,48 +76,47 @@ function commandExists(name: string, cwd: string): boolean {
76
76
  /**
77
77
  * Check that Node.js version meets the project's engines requirement.
78
78
  */
79
- function checkNodeVersion(basePath: string): EnvironmentCheckResult | null {
79
+ function readPackageEngineNode(basePath: string): string | null {
80
80
  const pkgPath = join(basePath, "package.json");
81
81
  if (!existsSync(pkgPath)) return null;
82
-
83
82
  try {
84
83
  const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
85
- const required = pkg.engines?.node;
86
- if (!required) return null;
87
-
88
- const currentVersion = tryExec("node --version", basePath);
89
- if (!currentVersion) {
90
- return { name: "node_version", status: "error", message: "Node.js not found in PATH" };
91
- }
92
-
93
- // Parse semver requirement (handles >=X.Y.Z format)
94
- const reqMatch = required.match(/>=?\s*(\d+)(?:\.(\d+))?/);
95
- if (!reqMatch) return null;
96
-
97
- const reqMajor = parseInt(reqMatch[1], 10);
98
- const reqMinor = parseInt(reqMatch[2] ?? "0", 10);
99
-
100
- const curMatch = currentVersion.match(/v?(\d+)\.(\d+)/);
101
- if (!curMatch) return null;
102
-
103
- const curMajor = parseInt(curMatch[1], 10);
104
- const curMinor = parseInt(curMatch[2], 10);
105
-
106
- if (curMajor < reqMajor || (curMajor === reqMajor && curMinor < reqMinor)) {
107
- return {
108
- name: "node_version",
109
- status: "warning",
110
- message: `Node.js ${currentVersion} does not meet requirement "${required}"`,
111
- detail: `Current: ${currentVersion}, Required: ${required}`,
112
- };
113
- }
114
-
115
- return { name: "node_version", status: "ok", message: `Node.js ${currentVersion}` };
84
+ return pkg.engines?.node ?? null;
116
85
  } catch {
117
86
  return null;
118
87
  }
119
88
  }
120
89
 
90
+ function buildNodeVersionResult(required: string, currentVersion: string | null): EnvironmentCheckResult | null {
91
+ if (!currentVersion) {
92
+ return { name: "node_version", status: "error", message: "Node.js not found in PATH" };
93
+ }
94
+ // Parse semver requirement (handles >=X.Y.Z format)
95
+ const reqMatch = required.match(/>=?\s*(\d+)(?:\.(\d+))?/);
96
+ if (!reqMatch) return null;
97
+ const reqMajor = parseInt(reqMatch[1], 10);
98
+ const reqMinor = parseInt(reqMatch[2] ?? "0", 10);
99
+ const curMatch = currentVersion.match(/v?(\d+)\.(\d+)/);
100
+ if (!curMatch) return null;
101
+ const curMajor = parseInt(curMatch[1], 10);
102
+ const curMinor = parseInt(curMatch[2], 10);
103
+ if (curMajor < reqMajor || (curMajor === reqMajor && curMinor < reqMinor)) {
104
+ return {
105
+ name: "node_version",
106
+ status: "warning",
107
+ message: `Node.js ${currentVersion} does not meet requirement "${required}"`,
108
+ detail: `Current: ${currentVersion}, Required: ${required}`,
109
+ };
110
+ }
111
+ return { name: "node_version", status: "ok", message: `Node.js ${currentVersion}` };
112
+ }
113
+
114
+ function checkNodeVersion(basePath: string): EnvironmentCheckResult | null {
115
+ const required = readPackageEngineNode(basePath);
116
+ if (!required) return null;
117
+ return buildNodeVersionResult(required, tryExec("node --version", basePath));
118
+ }
119
+
121
120
  /**
122
121
  * Check if node_modules exists and is not stale vs the lockfile.
123
122
  */
@@ -217,28 +216,19 @@ function checkEnvFiles(basePath: string): EnvironmentCheckResult | null {
217
216
  * Check for port conflicts on common dev server ports.
218
217
  * Only checks ports that appear in package.json scripts.
219
218
  */
220
- function checkPortConflicts(basePath: string): EnvironmentCheckResult[] {
221
- // Only run on macOS/Linux lsof is not available on Windows
222
- if (process.platform === "win32") return [];
223
-
224
- const results: EnvironmentCheckResult[] = [];
225
-
226
- // Try to detect ports from package.json scripts
219
+ // Detect the dev ports worth checking from package.json scripts, falling back to
220
+ // common defaults. Returns an empty set when there's no Node project (no
221
+ // package.json) or a parse failure — the caller then skips port checks entirely,
222
+ // avoiding false positives from system services (e.g. macOS AirPlay on 5000, #1381).
223
+ function collectPortsToCheck(basePath: string): Set<number> {
227
224
  const portsToCheck = new Set<number>();
228
225
  const pkgPath = join(basePath, "package.json");
229
-
230
- if (!existsSync(pkgPath)) {
231
- // No package.json — this isn't a Node.js project. Skip port checks
232
- // entirely to avoid false positives from system services (e.g., macOS
233
- // AirPlay Receiver on port 5000). (#1381)
234
- return [];
235
- }
226
+ if (!existsSync(pkgPath)) return portsToCheck;
236
227
 
237
228
  try {
238
229
  const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
239
230
  const scripts = pkg.scripts ?? {};
240
231
  const scriptText = Object.values(scripts).join(" ");
241
-
242
232
  // Look for --port NNNN, -p NNNN, PORT=NNNN, :NNNN patterns
243
233
  const portMatches = scriptText.matchAll(/(?:--port\s+|(?:^|[^a-z])PORT[=:]\s*|-p\s+|:)(\d{4,5})\b/gi);
244
234
  for (const m of portMatches) {
@@ -246,48 +236,66 @@ function checkPortConflicts(basePath: string): EnvironmentCheckResult[] {
246
236
  if (port >= 1024 && port <= 65535) portsToCheck.add(port);
247
237
  }
248
238
  } catch {
249
- // parse failed — skip port checks rather than using defaults
250
- return [];
239
+ return new Set();
251
240
  }
252
241
 
253
- // If no ports found in scripts, check common defaults.
254
- // Filter out port 5000 on macOS — AirPlay Receiver uses it by default (#1381).
255
242
  if (portsToCheck.size === 0) {
256
243
  for (const p of DEFAULT_DEV_PORTS) {
257
244
  if (p === 5000 && process.platform === "darwin") continue;
258
245
  portsToCheck.add(p);
259
246
  }
260
247
  }
248
+ return portsToCheck;
249
+ }
261
250
 
251
+ // Parse a single `lsof -nP -iTCP -sTCP:LISTEN` scan and report which of the
252
+ // requested ports are in use. Replaces the old per-port lsof loop (up to ~14
253
+ // system-wide socket scans, ~350ms) with one scan + an in-memory lookup.
254
+ function buildPortConflictResults(
255
+ lsofOut: string | null,
256
+ portsToCheck: Set<number>,
257
+ ): EnvironmentCheckResult[] {
258
+ const results: EnvironmentCheckResult[] = [];
259
+ const listening = new Map<number, { pid: string; command: string }>();
260
+ if (lsofOut) {
261
+ for (const line of lsofOut.split("\n")) {
262
+ // COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
263
+ // e.g. `node 12345 user 23u IPv4 0x.. 0t0 TCP *:3000 (LISTEN)`
264
+ const portMatch = line.match(/:(\d+)(?:\s+\(LISTEN\))?\s*$/);
265
+ if (!portMatch) continue;
266
+ const port = parseInt(portMatch[1], 10);
267
+ if (!Number.isFinite(port) || listening.has(port)) continue;
268
+ const cols = line.split(/\s+/);
269
+ listening.set(port, { command: cols[0] ?? "unknown", pid: cols[1] ?? "?" });
270
+ }
271
+ }
262
272
  for (const port of portsToCheck) {
263
- const result = tryExec(`lsof -i :${port} -sTCP:LISTEN -t`, basePath);
264
- if (result && result.length > 0) {
265
- // Get process name
266
- const nameResult = tryExec(`lsof -i :${port} -sTCP:LISTEN -Fp | head -2`, basePath);
267
- const processName = nameResult?.match(/p(\d+)\n?c?(.+)?/)?.[2] ?? "unknown";
268
-
273
+ const hit = listening.get(port);
274
+ if (hit) {
269
275
  results.push({
270
276
  name: "port_conflict",
271
277
  status: "warning",
272
- message: `Port ${port} is already in use by ${processName} (PID ${result.split("\n")[0]})`,
278
+ message: `Port ${port} is already in use by ${hit.command} (PID ${hit.pid})`,
273
279
  detail: `Kill the process or use a different port`,
274
280
  });
275
281
  }
276
282
  }
277
-
278
283
  return results;
279
284
  }
280
285
 
286
+ function checkPortConflicts(basePath: string): EnvironmentCheckResult[] {
287
+ // Only run on macOS/Linux — lsof is not available on Windows
288
+ if (process.platform === "win32") return [];
289
+ const portsToCheck = collectPortsToCheck(basePath);
290
+ if (portsToCheck.size === 0) return [];
291
+ return buildPortConflictResults(tryExec("lsof -nP -iTCP -sTCP:LISTEN", basePath), portsToCheck);
292
+ }
293
+
281
294
  /**
282
295
  * Check available disk space on the working directory partition.
283
296
  */
284
- function checkDiskSpace(basePath: string): EnvironmentCheckResult | null {
285
- // Only run on macOS/Linux
286
- if (process.platform === "win32") return null;
287
-
288
- const dfOutput = tryExec(`df -k "${basePath}" | tail -1`, basePath);
297
+ function buildDiskSpaceResult(dfOutput: string | null): EnvironmentCheckResult | null {
289
298
  if (!dfOutput) return null;
290
-
291
299
  try {
292
300
  // df output: filesystem blocks used avail capacity mount
293
301
  const parts = dfOutput.split(/\s+/);
@@ -306,32 +314,34 @@ function checkDiskSpace(basePath: string): EnvironmentCheckResult | null {
306
314
  detail: `Free up space — builds and git operations may fail`,
307
315
  };
308
316
  }
309
-
310
317
  if (availBytes < MIN_DISK_BYTES * 4) {
311
- return {
312
- name: "disk_space",
313
- status: "warning",
314
- message: `Disk space getting low: ${availGB}GB free`,
315
- };
318
+ return { name: "disk_space", status: "warning", message: `Disk space getting low: ${availGB}GB free` };
316
319
  }
317
-
318
320
  return { name: "disk_space", status: "ok", message: `${availGB}GB free` };
319
321
  } catch {
320
322
  return null;
321
323
  }
322
324
  }
323
325
 
326
+ function checkDiskSpace(basePath: string): EnvironmentCheckResult | null {
327
+ // Only run on macOS/Linux
328
+ if (process.platform === "win32") return null;
329
+ return buildDiskSpaceResult(tryExec(`df -k "${basePath}" | tail -1`, basePath));
330
+ }
331
+
324
332
  /**
325
333
  * Check if Docker is available when project has a Dockerfile.
326
334
  */
327
- function checkDocker(basePath: string): EnvironmentCheckResult | null {
328
- const hasDockerfile = existsSync(join(basePath, "Dockerfile")) ||
335
+ function hasDockerConfig(basePath: string): boolean {
336
+ return existsSync(join(basePath, "Dockerfile")) ||
329
337
  existsSync(join(basePath, "docker-compose.yml")) ||
330
338
  existsSync(join(basePath, "docker-compose.yaml")) ||
331
339
  existsSync(join(basePath, "compose.yml")) ||
332
340
  existsSync(join(basePath, "compose.yaml"));
341
+ }
333
342
 
334
- if (!hasDockerfile) return null;
343
+ function checkDocker(basePath: string): EnvironmentCheckResult | null {
344
+ if (!hasDockerConfig(basePath)) return null;
335
345
 
336
346
  if (!commandExists("docker", basePath)) {
337
347
  return {
@@ -357,78 +367,68 @@ function checkDocker(basePath: string): EnvironmentCheckResult | null {
357
367
  /**
358
368
  * Check for common project tools that should be available.
359
369
  */
360
- function checkProjectTools(basePath: string): EnvironmentCheckResult[] {
361
- const results: EnvironmentCheckResult[] = [];
362
- const pkgPath = join(basePath, "package.json");
363
-
364
- if (!existsSync(pkgPath)) return results;
370
+ interface ProjectToolSpec {
371
+ packageManager: string | null; // non-npm manager that must be on PATH, or null
372
+ needsTsc: boolean;
373
+ needsPython: boolean;
374
+ needsCargo: boolean;
375
+ needsGo: boolean;
376
+ }
365
377
 
378
+ // Decide which project tools to verify from package.json + marker files (pure fs).
379
+ // The actual `command -v` probes are left to the sync/async checkers so both share
380
+ // this logic.
381
+ function readProjectToolSpec(basePath: string): ProjectToolSpec | null {
382
+ const pkgPath = join(basePath, "package.json");
383
+ if (!existsSync(pkgPath)) return null;
366
384
  try {
367
385
  const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
368
- const allDeps = {
369
- ...(pkg.dependencies ?? {}),
370
- ...(pkg.devDependencies ?? {}),
371
- };
372
-
373
- // Check for package manager
374
- const packageManager = pkg.packageManager;
375
- if (packageManager) {
376
- const managerName = packageManager.split("@")[0];
377
- if (managerName && managerName !== "npm" && !commandExists(managerName, basePath)) {
378
- results.push({
379
- name: "package_manager",
380
- status: "warning",
381
- message: `Project requires ${managerName} but it's not installed`,
382
- detail: `Install with: npm install -g ${managerName}`,
383
- });
384
- }
385
- }
386
-
387
- // Check for TypeScript if it's a dependency
388
- if (allDeps["typescript"] && !existsSync(join(basePath, "node_modules", ".bin", "tsc"))) {
389
- results.push({
390
- name: "typescript",
391
- status: "warning",
392
- message: "TypeScript is a dependency but tsc is not available (run npm install)",
393
- });
394
- }
395
-
396
- // Check for Python if pyproject.toml or requirements.txt exists
397
- if (existsSync(join(basePath, "pyproject.toml")) || existsSync(join(basePath, "requirements.txt"))) {
398
- if (detectPythonExecutable() === null) {
399
- results.push({
400
- name: "python",
401
- status: "warning",
402
- message: "Project has Python config but python is not installed",
403
- });
404
- }
405
- }
406
-
407
- // Check for Rust if Cargo.toml exists
408
- if (existsSync(join(basePath, "Cargo.toml"))) {
409
- if (!commandExists("cargo", basePath)) {
410
- results.push({
411
- name: "cargo",
412
- status: "warning",
413
- message: "Project has Cargo.toml but cargo is not installed",
414
- });
415
- }
416
- }
417
-
418
- // Check for Go if go.mod exists
419
- if (existsSync(join(basePath, "go.mod"))) {
420
- if (!commandExists("go", basePath)) {
421
- results.push({
422
- name: "go",
423
- status: "warning",
424
- message: "Project has go.mod but go is not installed",
425
- });
426
- }
386
+ const allDeps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };
387
+ let packageManager: string | null = null;
388
+ if (pkg.packageManager) {
389
+ const managerName = String(pkg.packageManager).split("@")[0];
390
+ if (managerName && managerName !== "npm") packageManager = managerName;
427
391
  }
392
+ return {
393
+ packageManager,
394
+ needsTsc: Boolean(allDeps["typescript"]) && !existsSync(join(basePath, "node_modules", ".bin", "tsc")),
395
+ needsPython: existsSync(join(basePath, "pyproject.toml")) || existsSync(join(basePath, "requirements.txt")),
396
+ needsCargo: existsSync(join(basePath, "Cargo.toml")),
397
+ needsGo: existsSync(join(basePath, "go.mod")),
398
+ };
428
399
  } catch {
429
- // parse failed — skip
400
+ return null;
430
401
  }
402
+ }
431
403
 
404
+ function checkProjectTools(basePath: string): EnvironmentCheckResult[] {
405
+ const spec = readProjectToolSpec(basePath);
406
+ if (!spec) return [];
407
+ const results: EnvironmentCheckResult[] = [];
408
+ if (spec.packageManager && !commandExists(spec.packageManager, basePath)) {
409
+ results.push({
410
+ name: "package_manager",
411
+ status: "warning",
412
+ message: `Project requires ${spec.packageManager} but it's not installed`,
413
+ detail: `Install with: npm install -g ${spec.packageManager}`,
414
+ });
415
+ }
416
+ if (spec.needsTsc) {
417
+ results.push({
418
+ name: "typescript",
419
+ status: "warning",
420
+ message: "TypeScript is a dependency but tsc is not available (run npm install)",
421
+ });
422
+ }
423
+ if (spec.needsPython && detectPythonExecutable() === null) {
424
+ results.push({ name: "python", status: "warning", message: "Project has Python config but python is not installed" });
425
+ }
426
+ if (spec.needsCargo && !commandExists("cargo", basePath)) {
427
+ results.push({ name: "cargo", status: "warning", message: "Project has Cargo.toml but cargo is not installed" });
428
+ }
429
+ if (spec.needsGo && !commandExists("go", basePath)) {
430
+ results.push({ name: "go", status: "warning", message: "Project has go.mod but go is not installed" });
431
+ }
432
432
  return results;
433
433
  }
434
434
 
@@ -517,6 +517,131 @@ function checkTestHealth(basePath: string): EnvironmentCheckResult | null {
517
517
  * Run all environment health checks. Returns structured results for
518
518
  * integration with the doctor pipeline.
519
519
  */
520
+ // ── Async variants ─────────────────────────────────────────────────────────
521
+ // The always-on health widget refreshes off the first-paint path, but the sync
522
+ // `runEnvironmentChecks` blocks the event loop on its subprocess checks (~300ms,
523
+ // and again every 60s). These async siblings run the same checks via non-blocking
524
+ // `execFile` and fan the independent ones out concurrently, so the UI thread never
525
+ // stalls. The sync API above is unchanged for /gsd doctor and the dashboards.
526
+
527
+ function tryExecAsync(cmd: string, cwd: string): Promise<string | null> {
528
+ return new Promise((resolve) => {
529
+ const isWin = process.platform === "win32";
530
+ const child = execFile(
531
+ isWin ? "cmd" : "sh",
532
+ isWin ? ["/c", cmd] : ["-c", cmd],
533
+ { cwd, timeout: CMD_TIMEOUT, encoding: "utf-8" },
534
+ (err, stdout) => resolve(err ? null : String(stdout).trim()),
535
+ );
536
+ child.on("error", () => resolve(null));
537
+ });
538
+ }
539
+
540
+ async function commandExistsAsync(name: string, cwd: string): Promise<boolean> {
541
+ const whichCmd = process.platform === "win32" ? `where ${name}` : `command -v ${name}`;
542
+ return (await tryExecAsync(whichCmd, cwd)) !== null;
543
+ }
544
+
545
+ async function checkNodeVersionAsync(basePath: string): Promise<EnvironmentCheckResult | null> {
546
+ const required = readPackageEngineNode(basePath);
547
+ if (!required) return null;
548
+ const currentVersion = await tryExecAsync("node --version", basePath);
549
+ return buildNodeVersionResult(required, currentVersion);
550
+ }
551
+
552
+ async function checkPortConflictsAsync(basePath: string): Promise<EnvironmentCheckResult[]> {
553
+ if (process.platform === "win32") return [];
554
+ const portsToCheck = collectPortsToCheck(basePath);
555
+ if (portsToCheck.size === 0) return [];
556
+ const lsofOut = await tryExecAsync("lsof -nP -iTCP -sTCP:LISTEN", basePath);
557
+ return buildPortConflictResults(lsofOut, portsToCheck);
558
+ }
559
+
560
+ async function checkDiskSpaceAsync(basePath: string): Promise<EnvironmentCheckResult | null> {
561
+ if (process.platform === "win32") return null;
562
+ return buildDiskSpaceResult(await tryExecAsync(`df -k "${basePath}" | tail -1`, basePath));
563
+ }
564
+
565
+ async function checkDockerAsync(basePath: string): Promise<EnvironmentCheckResult | null> {
566
+ if (!hasDockerConfig(basePath)) return null;
567
+ if (!(await commandExistsAsync("docker", basePath))) {
568
+ return { name: "docker", status: "warning", message: "Project has Docker files but docker is not installed" };
569
+ }
570
+ const info = await tryExecAsync("docker info --format '{{.ServerVersion}}'", basePath);
571
+ if (!info) {
572
+ return {
573
+ name: "docker",
574
+ status: "warning",
575
+ message: "Docker is installed but daemon is not running",
576
+ detail: "Start Docker Desktop or the docker daemon",
577
+ };
578
+ }
579
+ return { name: "docker", status: "ok", message: `Docker ${info}` };
580
+ }
581
+
582
+ async function checkProjectToolsAsync(basePath: string): Promise<EnvironmentCheckResult[]> {
583
+ const spec = readProjectToolSpec(basePath);
584
+ if (!spec) return [];
585
+ const results: EnvironmentCheckResult[] = [];
586
+ if (spec.packageManager && !(await commandExistsAsync(spec.packageManager, basePath))) {
587
+ results.push({
588
+ name: "package_manager",
589
+ status: "warning",
590
+ message: `Project requires ${spec.packageManager} but it's not installed`,
591
+ detail: `Install with: npm install -g ${spec.packageManager}`,
592
+ });
593
+ }
594
+ if (spec.needsTsc) {
595
+ results.push({
596
+ name: "typescript",
597
+ status: "warning",
598
+ message: "TypeScript is a dependency but tsc is not available (run npm install)",
599
+ });
600
+ }
601
+ if (spec.needsPython && detectPythonExecutable() === null) {
602
+ results.push({ name: "python", status: "warning", message: "Project has Python config but python is not installed" });
603
+ }
604
+ if (spec.needsCargo && !(await commandExistsAsync("cargo", basePath))) {
605
+ results.push({ name: "cargo", status: "warning", message: "Project has Cargo.toml but cargo is not installed" });
606
+ }
607
+ if (spec.needsGo && !(await commandExistsAsync("go", basePath))) {
608
+ results.push({ name: "go", status: "warning", message: "Project has go.mod but go is not installed" });
609
+ }
610
+ return results;
611
+ }
612
+
613
+ /**
614
+ * Non-blocking equivalent of `runEnvironmentChecks` for the health-widget
615
+ * background refresh. Cheap fs checks run inline; the subprocess-backed checks
616
+ * fan out concurrently so the whole suite costs ~max(check), not the sum.
617
+ */
618
+ export async function runEnvironmentChecksAsync(basePath: string): Promise<EnvironmentCheckResult[]> {
619
+ const results: EnvironmentCheckResult[] = [];
620
+
621
+ // fs-only checks — already cheap and synchronous.
622
+ const depsCheck = checkDependenciesInstalled(basePath);
623
+ if (depsCheck) results.push(depsCheck);
624
+ const envCheck = checkEnvFiles(basePath);
625
+ if (envCheck) results.push(envCheck);
626
+
627
+ // subprocess-backed checks — run concurrently, off the event-loop critical path.
628
+ const [nodeCheck, portChecks, diskCheck, dockerCheck, toolChecks] = await Promise.all([
629
+ checkNodeVersionAsync(basePath),
630
+ checkPortConflictsAsync(basePath),
631
+ checkDiskSpaceAsync(basePath),
632
+ checkDockerAsync(basePath),
633
+ checkProjectToolsAsync(basePath),
634
+ ]);
635
+
636
+ if (nodeCheck) results.push(nodeCheck);
637
+ results.push(...portChecks);
638
+ if (diskCheck) results.push(diskCheck);
639
+ if (dockerCheck) results.push(dockerCheck);
640
+ results.push(...toolChecks);
641
+
642
+ return results;
643
+ }
644
+
520
645
  export function runEnvironmentChecks(basePath: string): EnvironmentCheckResult[] {
521
646
  const results: EnvironmentCheckResult[] = [];
522
647
 
@@ -3,13 +3,15 @@
3
3
  //
4
4
  // ─── Single-writer invariant ─────────────────────────────────────────────
5
5
  // Every write-SQL statement against `.gsd/gsd.db` lives behind a typed
6
- // wrapper in the single-writer layer (this file plus db/writers/*). Connection
7
- // ownership, lifecycle, schema/migrations and transaction primitives live in
8
- // db/engine.ts and are re-exported here for backward compatibility, so callers
9
- // keep importing from "./gsd-db.js".
6
+ // wrapper in the explicit writer allowlist: this compatibility barrel,
7
+ // db/writers/*, typed coordination/runtime writers, schema/migration helpers,
8
+ // and ADR backfill helpers. Connection ownership, lifecycle, schema/migrations,
9
+ // and transaction primitives live in db/engine.ts and are re-exported here for
10
+ // backward compatibility, so callers keep importing from "./gsd-db.js".
10
11
  //
11
12
  // `_getAdapter()` (re-exported from the engine) is retained for read-only
12
- // SELECTs in query modules. Do NOT use it for writes — add a wrapper here.
13
+ // SELECTs in query modules. Do NOT use it for writes — add or call a typed
14
+ // wrapper in the explicit writer layer.
13
15
  //
14
16
  // The separate `.gsd/unit-claims.db` (unit-ownership.ts) is an intentionally
15
17
  // independent store and is excluded from this invariant.
@@ -46,11 +48,11 @@ import { rowToSlice, rowToTask, type SliceRow, type TaskRow } from "./db-task-sl
46
48
  // primitives now live in the engine; re-export the full public surface so
47
49
  // existing `from "./gsd-db.js"` imports keep working.
48
50
  export * from "./db/engine.js";
49
- import { transaction, getDb, getDbOrNull } from "./db/engine.js";
51
+ import { immediateTransaction, transaction, getDb, getDbOrNull } from "./db/engine.js";
50
52
 
51
53
  // ─── Single Writer Layer re-exports ──────────────────────────────────────
52
- // Write subsystems live in db/writers/*; re-exported here so callers keep
53
- // importing from "./gsd-db.js".
54
+ // Domain write subsystems live in db/writers/*; re-exported here so callers
55
+ // keep importing from "./gsd-db.js".
54
56
  export * from "./db/writers/memory.js";
55
57
  export * from "./db/writers/reconcile.js";
56
58
  export * from "./db/writers/import-restore.js";
@@ -707,19 +709,14 @@ export function insertVerificationEvidence(e: {
707
709
 
708
710
 
709
711
  export function setMilestoneQueueOrder(order: string[]): void {
710
- if (!getDbOrNull()!) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
711
- getDbOrNull()!.exec("BEGIN IMMEDIATE");
712
- try {
713
- getDbOrNull()!.prepare("UPDATE milestones SET sequence = 0").run();
714
- const stmt = getDbOrNull()!.prepare("UPDATE milestones SET sequence = :sequence WHERE id = :id");
712
+ const db = getDb();
713
+ immediateTransaction(() => {
714
+ db.prepare("UPDATE milestones SET sequence = 0").run();
715
+ const stmt = db.prepare("UPDATE milestones SET sequence = :sequence WHERE id = :id");
715
716
  order.forEach((id, index) => {
716
717
  stmt.run({ ":id": id, ":sequence": index + 1 });
717
718
  });
718
- getDbOrNull()!.exec("COMMIT");
719
- } catch (err) {
720
- getDbOrNull()!.exec("ROLLBACK");
721
- throw err;
722
- }
719
+ });
723
720
  }
724
721
 
725
722
  function getMilestoneStatusForUpdate(milestoneId: string): string | null {
@@ -1442,4 +1439,3 @@ export function upsertQualityGate(g: {
1442
1439
  ":evaluated_at": g.evaluatedAt,
1443
1440
  });
1444
1441
  }
1445
-