@opengsd/gsd-pi 1.3.0-dev.65546769 → 1.3.0-dev.738160b9

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 (488) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +17 -4
  3. package/dist/headless-answers.js +1 -1
  4. package/dist/headless-ui.d.ts +1 -1
  5. package/dist/headless-ui.js +1 -1
  6. package/dist/headless.js +1 -1
  7. package/dist/resources/.managed-resources-content-hash +1 -1
  8. package/dist/resources/extensions/claude-code-cli/models.js +9 -0
  9. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +11 -2
  10. package/dist/resources/extensions/google-cli/stream-adapter.js +82 -15
  11. package/dist/resources/extensions/gsd/artifact-verification.js +427 -0
  12. package/dist/resources/extensions/gsd/auto/orchestrator.js +12 -3
  13. package/dist/resources/extensions/gsd/auto/session.js +11 -0
  14. package/dist/resources/extensions/gsd/auto/verification-retry-policy.js +1 -1
  15. package/dist/resources/extensions/gsd/auto-artifact-paths.js +28 -1
  16. package/dist/resources/extensions/gsd/auto-budget.js +5 -1
  17. package/dist/resources/extensions/gsd/auto-dispatch.js +34 -20
  18. package/dist/resources/extensions/gsd/auto-post-unit.js +1 -1
  19. package/dist/resources/extensions/gsd/auto-prompts.js +29 -14
  20. package/dist/resources/extensions/gsd/auto-recovery.js +9 -510
  21. package/dist/resources/extensions/gsd/auto-runtime-state.js +4 -5
  22. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +3 -3
  23. package/dist/resources/extensions/gsd/auto-verification.js +5 -2
  24. package/dist/resources/extensions/gsd/auto.js +19 -10
  25. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +103 -13
  26. package/dist/resources/extensions/gsd/bootstrap/core-session-tools.js +39 -0
  27. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +11 -5
  28. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +2 -0
  29. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
  30. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +41 -21
  31. package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -19
  32. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +96 -10
  33. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +1 -1
  34. package/dist/resources/extensions/gsd/closeout-consistency-gate.js +3 -1
  35. package/dist/resources/extensions/gsd/commands/handlers/auto.js +1 -1
  36. package/dist/resources/extensions/gsd/commands-context.js +19 -1
  37. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +17 -11
  38. package/dist/resources/extensions/gsd/commands-usage.js +1 -1
  39. package/dist/resources/extensions/gsd/commands-worktree.js +12 -10
  40. package/dist/resources/extensions/gsd/dashboard-overlay.js +32 -3
  41. package/dist/resources/extensions/gsd/db/engine.js +4 -1
  42. package/dist/resources/extensions/gsd/db/queries.js +60 -0
  43. package/dist/resources/extensions/gsd/db-memory-fts-schema.js +34 -0
  44. package/dist/resources/extensions/gsd/db-workspace.js +55 -3
  45. package/dist/resources/extensions/gsd/delegation-policy.js +1 -1
  46. package/dist/resources/extensions/gsd/docs/preferences-reference.md +2 -2
  47. package/dist/resources/extensions/gsd/doctor-engine-checks.js +192 -4
  48. package/dist/resources/extensions/gsd/doctor-providers.js +92 -8
  49. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +3 -0
  50. package/dist/resources/extensions/gsd/exec-sandbox.js +53 -11
  51. package/dist/resources/extensions/gsd/flat-phase-migration.js +45 -0
  52. package/dist/resources/extensions/gsd/forensics.js +2 -32
  53. package/dist/resources/extensions/gsd/git-service.js +61 -4
  54. package/dist/resources/extensions/gsd/gitignore.js +4 -1
  55. package/dist/resources/extensions/gsd/guided-flow-queue.js +66 -5
  56. package/dist/resources/extensions/gsd/health-widget.js +55 -29
  57. package/dist/resources/extensions/gsd/json-persistence.js +20 -0
  58. package/dist/resources/extensions/gsd/knowledge-backfill.js +18 -11
  59. package/dist/resources/extensions/gsd/layout-policy.js +3 -1
  60. package/dist/resources/extensions/gsd/markdown-renderer.js +8 -9
  61. package/dist/resources/extensions/gsd/memory-backfill.js +28 -21
  62. package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +44 -21
  63. package/dist/resources/extensions/gsd/metrics.js +4 -4
  64. package/dist/resources/extensions/gsd/migrate-external.js +11 -2
  65. package/dist/resources/extensions/gsd/migration-auto-check.js +22 -0
  66. package/dist/resources/extensions/gsd/milestone-ids.js +32 -2
  67. package/dist/resources/extensions/gsd/milestone-implementation-evidence.js +26 -20
  68. package/dist/resources/extensions/gsd/orphan-stash-audit.js +92 -16
  69. package/dist/resources/extensions/gsd/prompts/code-review.md +6 -4
  70. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +2 -2
  71. package/dist/resources/extensions/gsd/prompts/plan-slice.md +3 -3
  72. package/dist/resources/extensions/gsd/quick.js +45 -2
  73. package/dist/resources/extensions/gsd/root-write-leak-guard.js +7 -1
  74. package/dist/resources/extensions/gsd/safety/destructive-guard.js +8 -1
  75. package/dist/resources/extensions/gsd/safety/evidence-collector.js +16 -1
  76. package/dist/resources/extensions/gsd/safety/file-change-validator.js +5 -2
  77. package/dist/resources/extensions/gsd/session-forensics.js +11 -1
  78. package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
  79. package/dist/resources/extensions/gsd/state/derive/cache.js +28 -0
  80. package/dist/resources/extensions/gsd/state/derive/db-open.js +39 -0
  81. package/dist/resources/extensions/gsd/state/derive/from-db.js +452 -0
  82. package/dist/resources/extensions/gsd/state/derive/index.js +75 -0
  83. package/dist/resources/extensions/gsd/state/derive/interrupted-work.js +21 -0
  84. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +45 -2
  85. package/dist/resources/extensions/gsd/state-reconciliation/index.js +48 -23
  86. package/dist/resources/extensions/gsd/state-reconciliation/registry.js +32 -28
  87. package/dist/resources/extensions/gsd/state.js +12 -611
  88. package/dist/resources/extensions/gsd/tools/complete-milestone.js +4 -2
  89. package/dist/resources/extensions/gsd/tools/complete-slice.js +2 -2
  90. package/dist/resources/extensions/gsd/tools/complete-task.js +43 -14
  91. package/dist/resources/extensions/gsd/tools/exec-tool.js +7 -2
  92. package/dist/resources/extensions/gsd/tools/plan-slice.js +59 -44
  93. package/dist/resources/extensions/gsd/tools/plan-task.js +28 -5
  94. package/dist/resources/extensions/gsd/undo.js +43 -6
  95. package/dist/resources/extensions/gsd/unit-context-composer.js +23 -7
  96. package/dist/resources/extensions/gsd/unit-registry.js +36 -6
  97. package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +33 -3
  98. package/dist/resources/extensions/gsd/validation-block-guard.js +9 -4
  99. package/dist/resources/extensions/gsd/verification-gate.js +7 -0
  100. package/dist/resources/extensions/gsd/workflow-manifest.js +126 -13
  101. package/dist/resources/extensions/gsd/workflow-projections.js +19 -14
  102. package/dist/resources/extensions/gsd/workspace-git-preflight.js +30 -1
  103. package/dist/resources/extensions/gsd/worktree-manager.js +44 -2
  104. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  105. package/dist/web/standalone/.next/BUILD_ID +1 -1
  106. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  107. package/dist/web/standalone/.next/build-manifest.json +3 -3
  108. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  109. package/dist/web/standalone/.next/react-loadable-manifest.json +9 -9
  110. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  112. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  115. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  119. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  121. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  126. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  127. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  128. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/index.html +1 -1
  130. package/dist/web/standalone/.next/server/app/index.rsc +2 -2
  131. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  132. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
  133. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  134. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  136. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  138. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  139. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  142. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  143. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  144. package/dist/web/standalone/.next/static/chunks/{2659.b7b129ee6a769448.js → 2659.58e950899a9bb82f.js} +1 -1
  145. package/dist/web/standalone/.next/static/chunks/2772.a7c1fcc69a4685ef.js +1 -0
  146. package/dist/web/standalone/.next/static/chunks/{3616.3c60753b8ffcbd2e.js → 3616.61a2af74bb8833c8.js} +1 -1
  147. package/dist/web/standalone/.next/static/chunks/{4283.8e446784528ed9dc.js → 4283.d0d9e0a955e441cb.js} +1 -1
  148. package/dist/web/standalone/.next/static/chunks/{5826.a46ecdd1cfe8dabc.js → 5826.5421d66c72b9f34e.js} +1 -1
  149. package/dist/web/standalone/.next/static/chunks/{8785.481aa5869991b760.js → 8785.e29b3134cab1d153.js} +1 -1
  150. package/dist/web/standalone/.next/static/chunks/8937.640dc9c2aaa1dfad.js +10 -0
  151. package/dist/web/standalone/.next/static/chunks/app/{page-6644fc6ee8ca1247.js → page-72a856634ad14c10.js} +1 -1
  152. package/dist/web/standalone/.next/static/chunks/webpack-9c401904f87ded16.js +1 -0
  153. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  154. package/package.json +1 -1
  155. package/packages/cloud-mcp-gateway/package.json +2 -2
  156. package/packages/contracts/dist/workflow.d.ts +1 -0
  157. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  158. package/packages/contracts/dist/workflow.js +2 -0
  159. package/packages/contracts/dist/workflow.js.map +1 -1
  160. package/packages/contracts/package.json +1 -1
  161. package/packages/daemon/package.json +4 -4
  162. package/packages/gsd-agent-core/dist/agent-session.d.ts +1 -0
  163. package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
  164. package/packages/gsd-agent-core/dist/agent-session.js +3 -0
  165. package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
  166. package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts +41 -0
  167. package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts.map +1 -0
  168. package/packages/gsd-agent-core/dist/extension-ui-snapshot.js +62 -0
  169. package/packages/gsd-agent-core/dist/extension-ui-snapshot.js.map +1 -0
  170. package/packages/gsd-agent-core/dist/index.d.ts +2 -0
  171. package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
  172. package/packages/gsd-agent-core/dist/index.js +2 -0
  173. package/packages/gsd-agent-core/dist/index.js.map +1 -1
  174. package/packages/gsd-agent-core/dist/session/agent-session-events.js +1 -1
  175. package/packages/gsd-agent-core/dist/session/agent-session-events.js.map +1 -1
  176. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +1 -0
  177. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
  178. package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
  179. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts +5 -0
  180. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
  181. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +60 -3
  182. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
  183. package/packages/gsd-agent-core/dist/transcript-store.d.ts +58 -0
  184. package/packages/gsd-agent-core/dist/transcript-store.d.ts.map +1 -0
  185. package/packages/gsd-agent-core/dist/transcript-store.js +132 -0
  186. package/packages/gsd-agent-core/dist/transcript-store.js.map +1 -0
  187. package/packages/gsd-agent-core/package.json +5 -5
  188. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +2 -0
  189. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  190. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -11
  191. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  192. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts +4 -0
  193. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts.map +1 -0
  194. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js +7 -0
  195. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js.map +1 -0
  196. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts +3 -24
  197. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  198. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +26 -829
  199. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  200. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts +58 -0
  201. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts.map +1 -0
  202. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js +312 -0
  203. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js.map +1 -0
  204. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts +31 -0
  205. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts.map +1 -0
  206. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js +130 -0
  207. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js.map +1 -0
  208. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts +15 -0
  209. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts.map +1 -0
  210. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js +258 -0
  211. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js.map +1 -0
  212. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts +13 -0
  213. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts.map +1 -0
  214. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js +118 -0
  215. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js.map +1 -0
  216. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts +9 -0
  217. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  218. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js +1 -1
  219. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  220. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts +54 -0
  221. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts.map +1 -0
  222. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js +20 -0
  223. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js.map +1 -0
  224. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts +4 -1
  225. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  226. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +9 -0
  227. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
  228. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts +56 -0
  229. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts.map +1 -0
  230. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js +44 -0
  231. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js.map +1 -0
  232. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts +5 -0
  233. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts.map +1 -0
  234. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js +77 -0
  235. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js.map +1 -0
  236. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.d.ts +2 -0
  237. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  238. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js +23 -5
  239. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js.map +1 -1
  240. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  241. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js +18 -0
  242. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js.map +1 -1
  243. package/packages/gsd-agent-modes/package.json +7 -7
  244. package/packages/mcp-server/README.md +1 -1
  245. package/packages/mcp-server/dist/server.d.ts +1 -1
  246. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  247. package/packages/mcp-server/dist/server.js +3 -3
  248. package/packages/mcp-server/dist/server.js.map +1 -1
  249. package/packages/mcp-server/dist/workflow-tools.d.ts +13 -1
  250. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  251. package/packages/mcp-server/dist/workflow-tools.js +35 -21
  252. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  253. package/packages/mcp-server/package.json +4 -4
  254. package/packages/native/package.json +1 -1
  255. package/packages/pi-agent-core/dist/agent-loop.js +3 -2
  256. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  257. package/packages/pi-agent-core/package.json +1 -1
  258. package/packages/pi-ai/dist/models.generated.d.ts +46 -0
  259. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  260. package/packages/pi-ai/dist/models.generated.js +38 -0
  261. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  262. package/packages/pi-ai/package.json +1 -1
  263. package/packages/pi-coding-agent/README.md +3 -2
  264. package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts +9 -0
  265. package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts.map +1 -0
  266. package/packages/pi-coding-agent/dist/core/session-manager-context.js +94 -0
  267. package/packages/pi-coding-agent/dist/core/session-manager-context.js.map +1 -0
  268. package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts +8 -0
  269. package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts.map +1 -0
  270. package/packages/pi-coding-agent/dist/core/session-manager-list.js +244 -0
  271. package/packages/pi-coding-agent/dist/core/session-manager-list.js.map +1 -0
  272. package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts +12 -0
  273. package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts.map +1 -0
  274. package/packages/pi-coding-agent/dist/core/session-manager-migration.js +84 -0
  275. package/packages/pi-coding-agent/dist/core/session-manager-migration.js.map +1 -0
  276. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts +135 -0
  277. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts.map +1 -0
  278. package/packages/pi-coding-agent/dist/core/session-manager-types.js +2 -0
  279. package/packages/pi-coding-agent/dist/core/session-manager-types.js.map +1 -0
  280. package/packages/pi-coding-agent/dist/core/session-manager.d.ts +6 -154
  281. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  282. package/packages/pi-coding-agent/dist/core/session-manager.js +22 -459
  283. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  284. package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts +75 -75
  285. package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts.map +1 -1
  286. package/packages/pi-coding-agent/dist/theme/theme-schema.js +1 -1
  287. package/packages/pi-coding-agent/dist/theme/theme-schema.js.map +1 -1
  288. package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
  289. package/packages/pi-coding-agent/dist/theme/theme.js +11 -7
  290. package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
  291. package/packages/pi-coding-agent/package.json +7 -7
  292. package/packages/pi-tui/package.json +2 -2
  293. package/packages/rpc-client/package.json +2 -2
  294. package/pkg/dist/theme/theme-schema.d.ts +75 -75
  295. package/pkg/dist/theme/theme-schema.d.ts.map +1 -1
  296. package/pkg/dist/theme/theme-schema.js +1 -1
  297. package/pkg/dist/theme/theme-schema.js.map +1 -1
  298. package/pkg/dist/theme/theme.d.ts.map +1 -1
  299. package/pkg/dist/theme/theme.js +11 -7
  300. package/pkg/dist/theme/theme.js.map +1 -1
  301. package/pkg/package.json +1 -1
  302. package/src/resources/extensions/claude-code-cli/models.ts +9 -0
  303. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +20 -2
  304. package/src/resources/extensions/claude-code-cli/tests/index.test.ts +14 -0
  305. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +80 -0
  306. package/src/resources/extensions/google-cli/stream-adapter.ts +106 -19
  307. package/src/resources/extensions/gsd/artifact-verification.ts +464 -0
  308. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  309. package/src/resources/extensions/gsd/auto/orchestrator.ts +25 -11
  310. package/src/resources/extensions/gsd/auto/session.ts +14 -0
  311. package/src/resources/extensions/gsd/auto/verification-retry-policy.ts +3 -1
  312. package/src/resources/extensions/gsd/auto-artifact-paths.ts +47 -1
  313. package/src/resources/extensions/gsd/auto-budget.ts +12 -1
  314. package/src/resources/extensions/gsd/auto-dispatch.ts +35 -24
  315. package/src/resources/extensions/gsd/auto-post-unit.ts +1 -1
  316. package/src/resources/extensions/gsd/auto-prompts.ts +41 -15
  317. package/src/resources/extensions/gsd/auto-recovery.ts +13 -511
  318. package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -5
  319. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +3 -2
  320. package/src/resources/extensions/gsd/auto-verification.ts +5 -1
  321. package/src/resources/extensions/gsd/auto.ts +23 -8
  322. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +125 -12
  323. package/src/resources/extensions/gsd/bootstrap/core-session-tools.ts +44 -0
  324. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +11 -5
  325. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +2 -0
  326. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
  327. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +52 -21
  328. package/src/resources/extensions/gsd/bootstrap/system-context.ts +52 -18
  329. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +104 -10
  330. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +1 -1
  331. package/src/resources/extensions/gsd/closeout-consistency-gate.ts +2 -1
  332. package/src/resources/extensions/gsd/commands/handlers/auto.ts +1 -1
  333. package/src/resources/extensions/gsd/commands-context.ts +18 -1
  334. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +15 -10
  335. package/src/resources/extensions/gsd/commands-usage.ts +1 -1
  336. package/src/resources/extensions/gsd/commands-worktree.ts +12 -10
  337. package/src/resources/extensions/gsd/dashboard-overlay.ts +32 -3
  338. package/src/resources/extensions/gsd/db/engine.ts +8 -1
  339. package/src/resources/extensions/gsd/db/queries.ts +79 -0
  340. package/src/resources/extensions/gsd/db-memory-fts-schema.ts +45 -0
  341. package/src/resources/extensions/gsd/db-workspace.ts +61 -3
  342. package/src/resources/extensions/gsd/delegation-policy.ts +1 -1
  343. package/src/resources/extensions/gsd/docs/preferences-reference.md +2 -2
  344. package/src/resources/extensions/gsd/doctor-engine-checks.ts +233 -5
  345. package/src/resources/extensions/gsd/doctor-providers.ts +103 -9
  346. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +3 -0
  347. package/src/resources/extensions/gsd/doctor-types.ts +3 -0
  348. package/src/resources/extensions/gsd/exec-sandbox.ts +56 -11
  349. package/src/resources/extensions/gsd/flat-phase-migration.ts +44 -0
  350. package/src/resources/extensions/gsd/forensics.ts +2 -33
  351. package/src/resources/extensions/gsd/git-service.ts +69 -5
  352. package/src/resources/extensions/gsd/gitignore.ts +4 -1
  353. package/src/resources/extensions/gsd/guided-flow-queue.ts +89 -4
  354. package/src/resources/extensions/gsd/health-widget.ts +69 -32
  355. package/src/resources/extensions/gsd/json-persistence.ts +20 -0
  356. package/src/resources/extensions/gsd/knowledge-backfill.ts +16 -10
  357. package/src/resources/extensions/gsd/layout-policy.ts +2 -1
  358. package/src/resources/extensions/gsd/markdown-renderer.ts +8 -11
  359. package/src/resources/extensions/gsd/memory-backfill.ts +26 -20
  360. package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +51 -19
  361. package/src/resources/extensions/gsd/metrics.ts +4 -4
  362. package/src/resources/extensions/gsd/migrate-external.ts +8 -2
  363. package/src/resources/extensions/gsd/migration-auto-check.ts +23 -0
  364. package/src/resources/extensions/gsd/milestone-ids.ts +31 -2
  365. package/src/resources/extensions/gsd/milestone-implementation-evidence.ts +35 -21
  366. package/src/resources/extensions/gsd/orphan-stash-audit.ts +108 -20
  367. package/src/resources/extensions/gsd/prompts/code-review.md +6 -4
  368. package/src/resources/extensions/gsd/prompts/plan-milestone.md +2 -2
  369. package/src/resources/extensions/gsd/prompts/plan-slice.md +3 -3
  370. package/src/resources/extensions/gsd/quick.ts +43 -2
  371. package/src/resources/extensions/gsd/root-write-leak-guard.ts +13 -1
  372. package/src/resources/extensions/gsd/safety/destructive-guard.ts +9 -1
  373. package/src/resources/extensions/gsd/safety/evidence-collector.ts +16 -1
  374. package/src/resources/extensions/gsd/safety/file-change-validator.ts +8 -2
  375. package/src/resources/extensions/gsd/session-forensics.ts +11 -1
  376. package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
  377. package/src/resources/extensions/gsd/state/derive/cache.ts +46 -0
  378. package/src/resources/extensions/gsd/state/derive/db-open.ts +45 -0
  379. package/src/resources/extensions/gsd/state/derive/from-db.ts +561 -0
  380. package/src/resources/extensions/gsd/state/derive/index.ts +104 -0
  381. package/src/resources/extensions/gsd/state/derive/interrupted-work.ts +31 -0
  382. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +81 -7
  383. package/src/resources/extensions/gsd/state-reconciliation/index.ts +50 -24
  384. package/src/resources/extensions/gsd/state-reconciliation/registry.ts +43 -28
  385. package/src/resources/extensions/gsd/state.ts +32 -732
  386. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +4 -3
  387. package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +98 -1
  388. package/src/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +14 -5
  389. package/src/resources/extensions/gsd/tests/auto-pause-double-entry-guard.test.ts +36 -0
  390. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +182 -4
  391. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +146 -1
  392. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +30 -0
  393. package/src/resources/extensions/gsd/tests/commands-context.test.ts +26 -0
  394. package/src/resources/extensions/gsd/tests/commands-gsd-core.test.ts +1 -0
  395. package/src/resources/extensions/gsd/tests/commands-worktree-clean.test.ts +80 -0
  396. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +28 -0
  397. package/src/resources/extensions/gsd/tests/complete-slice-reopen-handoff.test.ts +69 -0
  398. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +11 -0
  399. package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +48 -8
  400. package/src/resources/extensions/gsd/tests/complete-task.test.ts +75 -0
  401. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +55 -2
  402. package/src/resources/extensions/gsd/tests/db-memory-fts-schema.test.ts +58 -3
  403. package/src/resources/extensions/gsd/tests/decisions-projection-from-memories.test.ts +51 -0
  404. package/src/resources/extensions/gsd/tests/destructive-guard.test.ts +44 -0
  405. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +143 -0
  406. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +26 -1
  407. package/src/resources/extensions/gsd/tests/doctor-forensics-db-open-regression.test.ts +70 -2
  408. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +107 -0
  409. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +239 -2
  410. package/src/resources/extensions/gsd/tests/exec-graceful-kill.test.ts +38 -0
  411. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +16 -0
  412. package/src/resources/extensions/gsd/tests/exec-tool.test.ts +45 -1
  413. package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +40 -0
  414. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +37 -2
  415. package/src/resources/extensions/gsd/tests/forensics-error-filter.test.ts +88 -0
  416. package/src/resources/extensions/gsd/tests/gitignore-bg-shell-runtime.test.ts +4 -0
  417. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +42 -0
  418. package/src/resources/extensions/gsd/tests/health-widget.test.ts +268 -3
  419. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +130 -3
  420. package/src/resources/extensions/gsd/tests/integration/queue-active-milestone-context-budget.test.ts +93 -0
  421. package/src/resources/extensions/gsd/tests/integration/quick-branch-lifecycle.test.ts +56 -9
  422. package/src/resources/extensions/gsd/tests/json-persistence-atomic.test.ts +47 -0
  423. package/src/resources/extensions/gsd/tests/knowledge-backfill-projection.test.ts +35 -0
  424. package/src/resources/extensions/gsd/tests/knowledge-cold-start.test.ts +14 -0
  425. package/src/resources/extensions/gsd/tests/memory-consolidation-scanner.test.ts +78 -0
  426. package/src/resources/extensions/gsd/tests/memory-store.test.ts +48 -0
  427. package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +31 -1
  428. package/src/resources/extensions/gsd/tests/metrics-prune-cache-invalidation.test.ts +6 -0
  429. package/src/resources/extensions/gsd/tests/metrics.test.ts +1 -0
  430. package/src/resources/extensions/gsd/tests/migrate-external-worktree.test.ts +57 -0
  431. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +70 -0
  432. package/src/resources/extensions/gsd/tests/orchestrator-logs.test.ts +43 -1
  433. package/src/resources/extensions/gsd/tests/orphan-stash-audit.test.ts +91 -7
  434. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +26 -0
  435. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +4 -1
  436. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +1 -1
  437. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +10 -0
  438. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +103 -4
  439. package/src/resources/extensions/gsd/tests/plan-task.test.ts +44 -1
  440. package/src/resources/extensions/gsd/tests/post-mutation-hook.test.ts +4 -2
  441. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +54 -1
  442. package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +50 -14
  443. package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +58 -0
  444. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +8 -6
  445. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +195 -1
  446. package/src/resources/extensions/gsd/tests/read-uat-gate-verdict.test.ts +185 -0
  447. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +87 -0
  448. package/src/resources/extensions/gsd/tests/register-hooks-loop-guard-auto.test.ts +103 -0
  449. package/src/resources/extensions/gsd/tests/root-write-leak-guard.test.ts +20 -1
  450. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +15 -3
  451. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +56 -0
  452. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +191 -0
  453. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +208 -0
  454. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +32 -3
  455. package/src/resources/extensions/gsd/tests/undo.test.ts +74 -0
  456. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +193 -14
  457. package/src/resources/extensions/gsd/tests/unmerged-milestone-guard.test.ts +25 -0
  458. package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +38 -0
  459. package/src/resources/extensions/gsd/tests/validation-block-guard.test.ts +79 -0
  460. package/src/resources/extensions/gsd/tests/verification-retry-policy.test.ts +69 -4
  461. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +66 -0
  462. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +96 -15
  463. package/src/resources/extensions/gsd/tests/workflow-mcp-preflight.test.ts +1 -0
  464. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +8 -4
  465. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +6 -5
  466. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +151 -2
  467. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +39 -0
  468. package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -2
  469. package/src/resources/extensions/gsd/tools/complete-slice.ts +2 -2
  470. package/src/resources/extensions/gsd/tools/complete-task.ts +53 -15
  471. package/src/resources/extensions/gsd/tools/exec-tool.ts +7 -3
  472. package/src/resources/extensions/gsd/tools/plan-slice.ts +60 -47
  473. package/src/resources/extensions/gsd/tools/plan-task.ts +30 -5
  474. package/src/resources/extensions/gsd/undo.ts +47 -6
  475. package/src/resources/extensions/gsd/unit-context-composer.ts +33 -7
  476. package/src/resources/extensions/gsd/unit-registry.ts +36 -6
  477. package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +41 -5
  478. package/src/resources/extensions/gsd/validation-block-guard.ts +13 -7
  479. package/src/resources/extensions/gsd/verification-gate.ts +8 -0
  480. package/src/resources/extensions/gsd/workflow-manifest.ts +141 -7
  481. package/src/resources/extensions/gsd/workflow-projections.ts +20 -14
  482. package/src/resources/extensions/gsd/workspace-git-preflight.ts +31 -0
  483. package/src/resources/extensions/gsd/worktree-manager.ts +41 -1
  484. package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +0 -1
  485. package/dist/web/standalone/.next/static/chunks/796.e0bdc932325d7e03.js +0 -10
  486. package/dist/web/standalone/.next/static/chunks/webpack-f46ea08200a0227e.js +0 -1
  487. /package/dist/web/standalone/.next/static/{BTKtGFF1Y-hvVJEGhBRo9 → 2A_WqqCS-EoYZxN9_v3d2}/_buildManifest.js +0 -0
  488. /package/dist/web/standalone/.next/static/{BTKtGFF1Y-hvVJEGhBRo9 → 2A_WqqCS-EoYZxN9_v3d2}/_ssgManifest.js +0 -0
@@ -11,7 +11,7 @@ import { spawn } from "node:child_process";
11
11
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
12
12
  import { randomUUID } from "node:crypto";
13
13
  import { resolve } from "node:path";
14
- import { killProcessTree, SIGKILL_GRACE_MS, HARD_DEADLINE_MS } from "@gsd/pi-coding-agent";
14
+ import { getShellConfig, killProcessTree, SIGKILL_GRACE_MS, HARD_DEADLINE_MS } from "@gsd/pi-coding-agent";
15
15
  const ALWAYS_FORWARD_ENV = ["PATH", "HOME"];
16
16
  // SIGKILL_GRACE_MS / HARD_DEADLINE_MS are imported from @gsd/pi-coding-agent
17
17
  // (shell.ts) — the single source of truth for the graceful-kill timing ladder —
@@ -60,7 +60,13 @@ function clampTimeout(request, opts) {
60
60
  function resolveCommand(runtime) {
61
61
  switch (runtime) {
62
62
  case "bash":
63
- return { cmd: "bash", args: ["-c"] };
63
+ try {
64
+ const { shell, args } = getShellConfig();
65
+ return { cmd: shell, args };
66
+ }
67
+ catch {
68
+ return { cmd: "bash", args: ["-c"] };
69
+ }
64
70
  case "node":
65
71
  return { cmd: process.execPath, args: ["-e"] };
66
72
  case "python":
@@ -180,10 +186,22 @@ export function runExecSandbox(request, opts) {
180
186
  const effectiveGraceMs = opts.kill_grace_ms ?? SIGKILL_GRACE_MS;
181
187
  const effectiveForceResolveDelay = opts.force_resolve_delay_ms ?? (effectiveGraceMs + HARD_DEADLINE_MS);
182
188
  let timedOut = false;
189
+ let aborted = false;
183
190
  let settled = false;
191
+ let killInitiated = false;
192
+ let timer;
184
193
  let forceResolveTimer;
185
- const timer = setTimeout(() => {
186
- timedOut = true;
194
+ let abortListener;
195
+ const removeAbortListener = () => {
196
+ if (opts.signal && abortListener) {
197
+ opts.signal.removeEventListener("abort", abortListener);
198
+ abortListener = undefined;
199
+ }
200
+ };
201
+ const initiateKill = () => {
202
+ if (killInitiated)
203
+ return;
204
+ killInitiated = true;
187
205
  // killProcessTree handles both platforms and kills the whole tree: on Unix
188
206
  // it signals the process group (SIGTERM -> grace -> SIGKILL); on Windows it
189
207
  // force-kills the tree via taskkill /F /T. Using child.kill("SIGTERM") here
@@ -201,14 +219,14 @@ export function runExecSandbox(request, opts) {
201
219
  finalize(null, "SIGKILL", true);
202
220
  }, effectiveForceResolveDelay);
203
221
  forceResolveTimer.unref?.();
204
- }, timeoutMs);
205
- timer.unref?.();
222
+ };
206
223
  const finalize = (exitCode, signal, forceResolved = false) => {
207
224
  if (settled)
208
225
  return;
209
226
  settled = true;
210
227
  clearTimeout(timer);
211
228
  clearTimeout(forceResolveTimer);
229
+ removeAbortListener();
212
230
  const duration = Date.now() - started;
213
231
  const stdoutBuf = Buffer.concat(stdoutChunks);
214
232
  const stderrBuf = Buffer.concat(stderrChunks);
@@ -219,17 +237,20 @@ export function runExecSandbox(request, opts) {
219
237
  const digestBody = tail(stdoutBuf, opts.digest_chars);
220
238
  const digest = digestBody.length > 0
221
239
  ? digestBody
222
- : timedOut
223
- ? "[no stdout — timed out]"
224
- : stderrBuf.length > 0
225
- ? `[no stdout — tail of stderr]\n${tail(stderrBuf, opts.digest_chars)}`
226
- : "[no output]";
240
+ : aborted
241
+ ? "[no stdout — aborted]"
242
+ : timedOut
243
+ ? "[no stdout — timed out]"
244
+ : stderrBuf.length > 0
245
+ ? `[no stdout — tail of stderr]\n${tail(stderrBuf, opts.digest_chars)}`
246
+ : "[no output]";
227
247
  const result = {
228
248
  id,
229
249
  runtime: request.runtime,
230
250
  exit_code: exitCode,
231
251
  signal,
232
252
  timed_out: timedOut,
253
+ aborted,
233
254
  force_resolved: forceResolved,
234
255
  duration_ms: duration,
235
256
  stdout_bytes: stdoutBytes,
@@ -244,6 +265,26 @@ export function runExecSandbox(request, opts) {
244
265
  writeMeta(metaPath, result, request, now);
245
266
  resolveP(result);
246
267
  };
268
+ timer = setTimeout(() => {
269
+ timedOut = true;
270
+ initiateKill();
271
+ }, timeoutMs);
272
+ timer.unref?.();
273
+ if (opts.signal) {
274
+ abortListener = () => {
275
+ if (settled || timedOut)
276
+ return;
277
+ aborted = true;
278
+ clearTimeout(timer);
279
+ initiateKill();
280
+ };
281
+ if (opts.signal.aborted) {
282
+ abortListener();
283
+ }
284
+ else {
285
+ opts.signal.addEventListener("abort", abortListener, { once: true });
286
+ }
287
+ }
247
288
  child.on("error", (err) => {
248
289
  const message = err instanceof Error ? err.message : String(err);
249
290
  const line = `child error: ${message}\n`;
@@ -274,6 +315,7 @@ function writeMeta(path, result, request, now) {
274
315
  exit_code: result.exit_code,
275
316
  signal: result.signal,
276
317
  timed_out: result.timed_out,
318
+ aborted: result.aborted === true,
277
319
  force_resolved: result.force_resolved,
278
320
  duration_ms: result.duration_ms,
279
321
  stdout_bytes: result.stdout_bytes,
@@ -59,6 +59,51 @@ export function needsFlatPhaseMigration(basePath) {
59
59
  // Resume an interrupted migration where the legacy tree was renamed aside.
60
60
  return hasLegacyMilestoneSubdirs(legacyMigratingPath(basePath));
61
61
  }
62
+ /** Retention window before flat-phase migration backups are auto-pruned. */
63
+ export const FLAT_PHASE_BACKUP_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
64
+ /**
65
+ * Remove stale flat-phase migration backups after the retention window.
66
+ * Only runs when migration is complete (.gsd/phases/ exists, legacy layout gone).
67
+ * Returns the number of migrate-* directories removed.
68
+ */
69
+ export function pruneStaleFlatPhaseBackups(basePath) {
70
+ if (needsFlatPhaseMigration(basePath))
71
+ return 0;
72
+ const phasesPath = join(basePath, ".gsd", LAYOUT_SEGMENTS.level1);
73
+ if (!existsSync(phasesPath))
74
+ return 0;
75
+ const backupRoot = join(basePath, ".gsd-backups");
76
+ if (!existsSync(backupRoot))
77
+ return 0;
78
+ const now = Date.now();
79
+ let removed = 0;
80
+ for (const entry of readdirSync(backupRoot)) {
81
+ if (!entry.startsWith("migrate-"))
82
+ continue;
83
+ const dirPath = join(backupRoot, entry);
84
+ try {
85
+ const st = statSync(dirPath);
86
+ if (!st.isDirectory())
87
+ continue;
88
+ if (now - st.mtimeMs < FLAT_PHASE_BACKUP_RETENTION_MS)
89
+ continue;
90
+ rmSync(dirPath, { recursive: true, force: true });
91
+ removed++;
92
+ }
93
+ catch {
94
+ // Non-fatal: leave backup for manual recovery.
95
+ }
96
+ }
97
+ try {
98
+ if (readdirSync(backupRoot).length === 0) {
99
+ rmSync(backupRoot, { recursive: true, force: true });
100
+ }
101
+ }
102
+ catch {
103
+ // Non-fatal.
104
+ }
105
+ return removed;
106
+ }
62
107
  /**
63
108
  * Migrate from legacy nested .gsd/milestones/ to flat-phase .gsd/phases/.
64
109
  *
@@ -22,8 +22,7 @@ import { deriveState } from "./state.js";
22
22
  import { isAutoActive } from "./auto.js";
23
23
  import { loadPrompt } from "./prompt-loader.js";
24
24
  import { gsdRoot } from "./paths.js";
25
- import { isDbAvailable, getAllMilestones, getMilestoneSlices, getSliceTasks } from "./gsd-db.js";
26
- import { isClosedStatus } from "./status-guards.js";
25
+ import { isDbAvailable, getHierarchyCompletionCounts } from "./gsd-db.js";
27
26
  import { formatDuration } from "../shared/format-utils.js";
28
27
  import { getAutoWorktreePath } from "./auto-worktree.js";
29
28
  import { clearGSDPreferencesCache, loadEffectiveGSDPreferences, loadGlobalGSDPreferences, getGlobalGSDPreferencesPath } from "./preferences.js";
@@ -593,36 +592,7 @@ function loadCompletedKeys(basePath) {
593
592
  function getDbCompletionCounts() {
594
593
  if (!isDbAvailable())
595
594
  return null;
596
- const milestones = getAllMilestones();
597
- let completedMilestones = 0;
598
- let totalSlices = 0;
599
- let completedSlices = 0;
600
- let totalTasks = 0;
601
- let completedTasks = 0;
602
- for (const m of milestones) {
603
- if (isClosedStatus(m.status))
604
- completedMilestones++;
605
- const slices = getMilestoneSlices(m.id);
606
- for (const s of slices) {
607
- totalSlices++;
608
- if (isClosedStatus(s.status))
609
- completedSlices++;
610
- const tasks = getSliceTasks(m.id, s.id);
611
- for (const t of tasks) {
612
- totalTasks++;
613
- if (isClosedStatus(t.status))
614
- completedTasks++;
615
- }
616
- }
617
- }
618
- return {
619
- milestones: completedMilestones,
620
- milestonesTotal: milestones.length,
621
- slices: completedSlices,
622
- slicesTotal: totalSlices,
623
- tasks: completedTasks,
624
- tasksTotal: totalTasks,
625
- };
595
+ return getHierarchyCompletionCounts();
626
596
  }
627
597
  // ─── Anomaly Detectors ───────────────────────────────────────────────────────
628
598
  /**
@@ -208,6 +208,7 @@ export class MergeConflictError extends GSDError {
208
208
  */
209
209
  export const RUNTIME_EXCLUSION_PATHS = [
210
210
  ".gsd-worktrees/",
211
+ ".gsd-backups/**",
211
212
  ".gsd/activity/",
212
213
  ".gsd/audit/",
213
214
  ".gsd/forensics/",
@@ -225,6 +226,7 @@ export const RUNTIME_EXCLUSION_PATHS = [
225
226
  ".gsd/event-log.jsonl",
226
227
  ".gsd/DISCUSSION-MANIFEST.json",
227
228
  ];
229
+ const runtimeFilesCleanedUpRepos = new Set();
228
230
  // ─── Integration Branch Metadata ───────────────────────────────────────────
229
231
  /**
230
232
  * Path to the milestone metadata file that stores the integration branch.
@@ -565,7 +567,8 @@ export class GitServiceImpl {
565
567
  // and the worktree is torn down. This prevents a mid-execution behavioral
566
568
  // discontinuity where the first half of a milestone has .gsd/ artifacts
567
569
  // committed but the second half doesn't (#1326).
568
- if (!this._runtimeFilesCleanedUp) {
570
+ const cleanupRepoKey = resolve(this.basePath);
571
+ if (!runtimeFilesCleanedUpRepos.has(cleanupRepoKey)) {
569
572
  let cleaned = false;
570
573
  for (const exclusion of RUNTIME_EXCLUSION_PATHS) {
571
574
  const removed = nativeRmCached(this.basePath, [exclusion]);
@@ -575,7 +578,7 @@ export class GitServiceImpl {
575
578
  if (cleaned) {
576
579
  nativeCommit(this.basePath, "chore: untrack .gsd/ runtime files from git index", { allowEmpty: false });
577
580
  }
578
- this._runtimeFilesCleanedUp = true;
581
+ runtimeFilesCleanedUpRepos.add(cleanupRepoKey);
579
582
  }
580
583
  // Stage everything using pathspec exclusions so excluded paths are never
581
584
  // hashed by git. The old approach of `git add -A` followed by unstaging
@@ -683,8 +686,6 @@ export class GitServiceImpl {
683
686
  return false;
684
687
  }
685
688
  }
686
- /** Tracks whether runtime file cleanup has run this session. */
687
- _runtimeFilesCleanedUp = false;
688
689
  /**
689
690
  * Stage files (smart staging) and commit.
690
691
  * Returns the commit message string on success, or null if nothing to commit.
@@ -1027,6 +1028,7 @@ function collectRepositoryDirtyStatus(basePath) {
1027
1028
  const preferences = loadEffectiveGSDPreferences(basePath)?.preferences;
1028
1029
  const registry = createRepositoryRegistryFromPreferences(basePath, preferences);
1029
1030
  const dirtyByRepository = {};
1031
+ const registeredRoots = new Set(registry.repositories.map((repo) => resolve(repo.root)));
1030
1032
  for (const repo of registry.repositories) {
1031
1033
  try {
1032
1034
  dirtyByRepository[repo.id] = runGit(repo.root, ["status", "--porcelain"]).length > 0;
@@ -1036,8 +1038,63 @@ function collectRepositoryDirtyStatus(basePath) {
1036
1038
  dirtyByRepository[repo.id] = nativeHasChanges(repo.root);
1037
1039
  }
1038
1040
  }
1041
+ for (const repoRoot of findUndeclaredNestedGitRepositories(registry.projectRoot, registeredRoots)) {
1042
+ let dirty = false;
1043
+ try {
1044
+ dirty = runGit(repoRoot, ["status", "--porcelain"]).length > 0;
1045
+ }
1046
+ catch {
1047
+ dirty = nativeHasChanges(repoRoot);
1048
+ }
1049
+ if (!dirty)
1050
+ continue;
1051
+ const relPath = relative(registry.projectRoot, repoRoot).split(sep).join("/") || repoRoot;
1052
+ const label = `${relPath} (undeclared)`;
1053
+ dirtyByRepository[label] = true;
1054
+ logWarning("engine", `undeclared dirty nested git repo ${relPath}; declare it under workspace.repositories or commit manually`, { file: "git-service.ts" });
1055
+ }
1039
1056
  return dirtyByRepository;
1040
1057
  }
1058
+ const UNDECLARED_NESTED_REPO_SCAN_MAX_DEPTH = 4;
1059
+ const UNDECLARED_NESTED_REPO_SKIP_DIRS = new Set([
1060
+ ".git",
1061
+ ".gsd",
1062
+ ".gsd-worktrees",
1063
+ "node_modules",
1064
+ "dist",
1065
+ "dist-test",
1066
+ ]);
1067
+ function findUndeclaredNestedGitRepositories(projectRoot, registeredRoots) {
1068
+ const found = [];
1069
+ function visit(dir, depth) {
1070
+ if (depth >= UNDECLARED_NESTED_REPO_SCAN_MAX_DEPTH)
1071
+ return;
1072
+ let entries;
1073
+ try {
1074
+ entries = readdirSync(dir, { withFileTypes: true });
1075
+ }
1076
+ catch {
1077
+ return;
1078
+ }
1079
+ for (const entry of entries) {
1080
+ if (!entry.isDirectory())
1081
+ continue;
1082
+ if (UNDECLARED_NESTED_REPO_SKIP_DIRS.has(entry.name))
1083
+ continue;
1084
+ const child = join(dir, entry.name);
1085
+ const childRoot = resolve(child);
1086
+ const hasGitDir = existsSync(join(child, ".git"));
1087
+ const isRegistered = registeredRoots.has(childRoot);
1088
+ if (hasGitDir && !isRegistered) {
1089
+ found.push(childRoot);
1090
+ continue;
1091
+ }
1092
+ visit(child, depth + 1);
1093
+ }
1094
+ }
1095
+ visit(projectRoot, 0);
1096
+ return found;
1097
+ }
1041
1098
  function runPerRepositoryCommitAction(args) {
1042
1099
  const preferences = loadEffectiveGSDPreferences(args.basePath)?.preferences;
1043
1100
  const registry = createRepositoryRegistryFromPreferences(args.basePath, preferences);
@@ -22,8 +22,9 @@ import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
22
22
  * With external state (symlink), these are a no-op in most cases,
23
23
  * but retained for backwards compatibility during migration.
24
24
  */
25
- const GSD_RUNTIME_PATTERNS = [
25
+ export const GSD_RUNTIME_PATTERNS = [
26
26
  ".gsd-worktrees/",
27
+ ".gsd-backups/",
27
28
  ".gsd/activity/",
28
29
  ".gsd/audit/",
29
30
  ".gsd/forensics/",
@@ -48,6 +49,8 @@ const BASELINE_PATTERNS = [
48
49
  ".gsd",
49
50
  // Worktree container sibling — NOT covered by the ".gsd" pattern above.
50
51
  ".gsd-worktrees/",
52
+ // Flat-phase migration snapshots — NOT covered by the ".gsd" pattern above.
53
+ ".gsd-backups/",
51
54
  ".gsd-id",
52
55
  ".mcp.json",
53
56
  ".bg-shell/",
@@ -12,7 +12,7 @@ import { loadFile } from "./files.js";
12
12
  import { loadPrompt, inlineTemplate } from "./prompt-loader.js";
13
13
  import { deriveState } from "./state.js";
14
14
  import { invalidateAllCaches } from "./cache.js";
15
- import { gsdRoot, resolveMilestoneFile, resolveGsdRootFile, relGsdRootFile, } from "./paths.js";
15
+ import { gsdRoot, resolveMilestoneFile, resolveGsdRootFile, relGsdRootFile, relMilestoneFile, } from "./paths.js";
16
16
  import { readFileSync, writeFileSync, existsSync } from "node:fs";
17
17
  import { atomicWriteSync } from "./atomic-write.js";
18
18
  import { nativeAddPaths, nativeCommit } from "./native-git-bridge.js";
@@ -20,6 +20,9 @@ import { loadEffectiveGSDPreferences } from "./preferences.js";
20
20
  import { saveQueueOrder } from "./queue-order.js";
21
21
  import { findMilestoneIds, nextMilestoneId } from "./milestone-ids.js";
22
22
  import { isFutureMilestoneStatus } from "./status-guards.js";
23
+ const QUEUE_ARTIFACT_EXCERPT_MAX_CHARS = 20_000;
24
+ const QUEUE_EXISTING_MILESTONES_CONTEXT_MAX_CHARS = 120_000;
25
+ const QUEUE_CONTEXT_SECTION_SEPARATOR = "\n\n---\n\n";
23
26
  // ─── Queue Entry Point ──────────────────────────────────────────────────────
24
27
  /**
25
28
  * Queue future milestones via conversational intake.
@@ -215,7 +218,7 @@ export async function buildExistingMilestonesContext(basePath, milestoneIds, sta
215
218
  if (contextFile) {
216
219
  const content = await loadFile(contextFile);
217
220
  if (content) {
218
- parts.push(`\n**Context:**\n${content.trim()}`);
221
+ parts.push(`\n**Context:**\n${summarizeArtifactForQueue(content, relMilestoneFile(basePath, mid, "CONTEXT"))}`);
219
222
  }
220
223
  }
221
224
  else {
@@ -224,7 +227,7 @@ export async function buildExistingMilestonesContext(basePath, milestoneIds, sta
224
227
  if (draftFile) {
225
228
  const draftContent = await loadFile(draftFile);
226
229
  if (draftContent) {
227
- parts.push(`\n**Draft context available:**\n${draftContent.trim()}`);
230
+ parts.push(`\n**Draft context available:**\n${summarizeArtifactForQueue(draftContent, relMilestoneFile(basePath, mid, "CONTEXT-DRAFT"))}`);
228
231
  }
229
232
  }
230
233
  }
@@ -235,7 +238,7 @@ export async function buildExistingMilestonesContext(basePath, milestoneIds, sta
235
238
  if (roadmapFile) {
236
239
  const content = await loadFile(roadmapFile);
237
240
  if (content) {
238
- parts.push(`\n**Roadmap:**\n${content.trim()}`);
241
+ parts.push(`\n**Roadmap:**\n${summarizeArtifactForQueue(content, relMilestoneFile(basePath, mid, "ROADMAP"))}`);
239
242
  }
240
243
  }
241
244
  }
@@ -249,7 +252,65 @@ export async function buildExistingMilestonesContext(basePath, milestoneIds, sta
249
252
  sections.push(`### Previous Queue Entries\nSource: \`${relGsdRootFile("QUEUE")}\`\n\n${queueContent.trim()}`);
250
253
  }
251
254
  }
252
- return sections.join("\n\n---\n\n");
255
+ return capExistingMilestonesContext(sections);
256
+ }
257
+ function summarizeArtifactForQueue(content, sourcePath, cap = QUEUE_ARTIFACT_EXCERPT_MAX_CHARS) {
258
+ const trimmed = content.trim();
259
+ if (trimmed.length <= cap) {
260
+ return `Source: \`${sourcePath}\`\n\n${trimmed}`;
261
+ }
262
+ const excerpt = trimmed.slice(0, cap).trimEnd();
263
+ const omittedChars = trimmed.length - excerpt.length;
264
+ return [
265
+ `Source: \`${sourcePath}\``,
266
+ "",
267
+ excerpt,
268
+ "",
269
+ `[Truncated ${omittedChars} chars. Read \`${sourcePath}\` for full content.]`,
270
+ ].join("\n");
271
+ }
272
+ function capExistingMilestonesContext(sections, cap = QUEUE_EXISTING_MILESTONES_CONTEXT_MAX_CHARS) {
273
+ const fullContext = sections.join(QUEUE_CONTEXT_SECTION_SEPARATOR);
274
+ if (fullContext.length <= cap)
275
+ return fullContext;
276
+ const notice = `[Existing milestones context truncated to ${cap} chars. Read source paths in this prompt or the corresponding .gsd artifacts for full details.]`;
277
+ const noticeSuffix = `${QUEUE_CONTEXT_SECTION_SEPARATOR}${notice}`;
278
+ const selected = [];
279
+ for (const section of sections) {
280
+ const candidate = [...selected, section].join(QUEUE_CONTEXT_SECTION_SEPARATOR) + noticeSuffix;
281
+ if (candidate.length <= cap) {
282
+ selected.push(section);
283
+ continue;
284
+ }
285
+ break;
286
+ }
287
+ if (selected.length === sections.length) {
288
+ return selected.join(QUEUE_CONTEXT_SECTION_SEPARATOR) + noticeSuffix;
289
+ }
290
+ const compactTail = sections.slice(selected.length).map(compactSectionForQueueBudget);
291
+ const hybrid = [...selected, ...compactTail].join(QUEUE_CONTEXT_SECTION_SEPARATOR) + noticeSuffix;
292
+ if (hybrid.length <= cap)
293
+ return hybrid;
294
+ const compact = sections.map(compactSectionForQueueBudget);
295
+ const compactContext = compact.join(QUEUE_CONTEXT_SECTION_SEPARATOR) + noticeSuffix;
296
+ if (compactContext.length <= cap)
297
+ return compactContext;
298
+ return `${compactContext.slice(0, Math.max(0, cap - notice.length - 2)).trimEnd()}\n\n${notice}`;
299
+ }
300
+ function compactSectionForQueueBudget(section) {
301
+ const lines = section.split("\n");
302
+ const compact = [];
303
+ if (lines[0])
304
+ compact.push(lines[0]);
305
+ const statusLine = lines.find(line => line.startsWith("**Status:**"));
306
+ if (statusLine)
307
+ compact.push(statusLine);
308
+ const sourceLines = lines.filter(line => line.startsWith("Source: `"));
309
+ if (sourceLines.length > 0) {
310
+ compact.push("", "**Sources:**", ...sourceLines);
311
+ compact.push("", "[Artifact excerpts omitted due to total queue/rethink context budget.]");
312
+ }
313
+ return compact.join("\n");
253
314
  }
254
315
  // ─── Internal Helpers ───────────────────────────────────────────────────────
255
316
  /**
@@ -1,29 +1,61 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Always-on ambient health signal rendered below the editor.
3
- import { runProviderChecks, summariseProviderIssues } from "./doctor-providers.js";
3
+ import { execFile } from "node:child_process";
4
+ import { runProviderChecks, runProviderChecksAsync, summariseProviderIssues } from "./doctor-providers.js";
4
5
  import { runEnvironmentChecks, runEnvironmentChecksAsync } from "./doctor-environment.js";
5
6
  import { loadEffectiveGSDPreferences } from "./preferences.js";
6
- import { nativeIsRepo, nativeLastCommitEpoch, nativeGetCurrentBranch, nativeCommitSubject } from "./native-git-bridge.js";
7
+ import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
7
8
  import { loadLedgerFromDisk, getProjectTotals } from "./metrics.js";
8
9
  import { projectRoot } from "./commands/context.js";
9
10
  import { buildHealthLines, detectHealthWidgetProjectState, } from "./health-widget-core.js";
10
11
  export const HEALTH_WIDGET_ACTIVE_HINTS = " /gsd auto to run · /gsd status to inspect · /gsd report for snapshots · /gsd notifications for history · /gsd help";
12
+ const LAST_COMMIT_LOOKUP_TIMEOUT_MS = 3_000;
13
+ const REFRESH_INTERVAL_MS = 60_000;
14
+ const PROJECT_STATE_CACHE_TTL_MS = REFRESH_INTERVAL_MS;
11
15
  // ── Data loader ────────────────────────────────────────────────────────────────
12
- // Last-commit lookup is subprocess-backed (native-git-bridge → git spawns),
13
- // so it is treated like the other expensive checks: skipped on first paint,
14
- // run only by the background refresh.
15
- function loadLastCommitInfo(basePath) {
16
+ const projectStateCache = new Map();
17
+ export function getCachedProjectState(basePath, force) {
18
+ const now = Date.now();
19
+ const cached = projectStateCache.get(basePath);
20
+ if (!force && cached && now - cached.computedAt <= PROJECT_STATE_CACHE_TTL_MS) {
21
+ return cached.state;
22
+ }
23
+ const state = detectHealthWidgetProjectState(basePath);
24
+ projectStateCache.set(basePath, { state, computedAt: now });
25
+ return state;
26
+ }
27
+ function runHealthWidgetGit(basePath, args) {
28
+ return new Promise((resolve) => {
29
+ const child = execFile("git", args, {
30
+ cwd: basePath,
31
+ timeout: LAST_COMMIT_LOOKUP_TIMEOUT_MS,
32
+ encoding: "utf-8",
33
+ env: GIT_NO_PROMPT_ENV,
34
+ }, (err, stdout) => resolve(err ? null : String(stdout).trimEnd()));
35
+ child.on("error", () => resolve(null));
36
+ });
37
+ }
38
+ async function loadLastCommitInfoAsync(basePath) {
16
39
  try {
17
- if (nativeIsRepo(basePath)) {
18
- const branch = nativeGetCurrentBranch(basePath);
19
- const epoch = nativeLastCommitEpoch(basePath, branch || "HEAD");
20
- if (epoch > 0) {
21
- return { epoch, message: nativeCommitSubject(basePath, branch || "HEAD") || null };
22
- }
40
+ if ((await runHealthWidgetGit(basePath, ["rev-parse", "--git-dir"])) === null) {
41
+ return { epoch: null, message: null };
23
42
  }
43
+ const branch = await runHealthWidgetGit(basePath, ["branch", "--show-current"]);
44
+ const ref = branch || "HEAD";
45
+ const raw = await runHealthWidgetGit(basePath, ["log", "-1", "--format=%ct%x00%s", ref]);
46
+ if (!raw)
47
+ return { epoch: null, message: null };
48
+ const separator = raw.indexOf("\0");
49
+ const epochText = separator >= 0 ? raw.slice(0, separator) : raw;
50
+ const epoch = parseInt(epochText.trim(), 10) || 0;
51
+ if (epoch <= 0)
52
+ return { epoch: null, message: null };
53
+ const message = separator >= 0 ? raw.slice(separator + 1).trim() : "";
54
+ return { epoch, message: message || null };
55
+ }
56
+ catch {
57
+ return { epoch: null, message: null };
24
58
  }
25
- catch { /* non-fatal */ }
26
- return { epoch: null, message: null };
27
59
  }
28
60
  function loadHealthWidgetData(basePath, options) {
29
61
  // `includeChecks` gates the expensive subprocess-backed checks (provider +
@@ -38,7 +70,7 @@ function loadHealthWidgetData(basePath, options) {
38
70
  let environmentWarningCount = 0;
39
71
  let lastCommitEpoch = null;
40
72
  let lastCommitMessage = null;
41
- const projectState = detectHealthWidgetProjectState(basePath);
73
+ const projectState = getCachedProjectState(basePath, options?.forceProjectState);
42
74
  try {
43
75
  const prefs = loadEffectiveGSDPreferences();
44
76
  budgetCeiling = prefs?.preferences?.budget_ceiling;
@@ -66,12 +98,6 @@ function loadHealthWidgetData(basePath, options) {
66
98
  }
67
99
  catch { /* non-fatal */ }
68
100
  }
69
- // ── Last commit info ── (git spawns — gated like the other expensive checks)
70
- if (includeChecks) {
71
- const commit = loadLastCommitInfo(basePath);
72
- lastCommitEpoch = commit.epoch;
73
- lastCommitMessage = commit.message;
74
- }
75
101
  return {
76
102
  projectState,
77
103
  budgetCeiling,
@@ -85,17 +111,15 @@ function loadHealthWidgetData(basePath, options) {
85
111
  };
86
112
  }
87
113
  // Non-blocking variant used by the widget's background refresh: the cheap fields
88
- // come from the synchronous snapshot, then provider + environment checks are
89
- // layered in off the event-loop critical path (env checks run concurrently via
90
- // runEnvironmentChecksAsync). Keeps the always-on widget from stalling the UI on
91
- // its initial enrichment or its 60s refresh.
114
+ // come from the synchronous snapshot, then provider, environment, and last-commit
115
+ // checks are layered in off the event-loop critical path.
92
116
  async function loadHealthWidgetDataAsync(basePath) {
93
117
  const data = loadHealthWidgetData(basePath, { includeChecks: false });
94
118
  let providerIssue = data.providerIssue;
95
119
  let environmentErrorCount = 0;
96
120
  let environmentWarningCount = 0;
97
121
  try {
98
- providerIssue = summariseProviderIssues(runProviderChecks());
122
+ providerIssue = summariseProviderIssues(await runProviderChecksAsync());
99
123
  }
100
124
  catch { /* non-fatal */ }
101
125
  try {
@@ -108,7 +132,7 @@ async function loadHealthWidgetDataAsync(basePath) {
108
132
  }
109
133
  }
110
134
  catch { /* non-fatal */ }
111
- const commit = loadLastCommitInfo(basePath);
135
+ const commit = await loadLastCommitInfoAsync(basePath);
112
136
  return {
113
137
  ...data,
114
138
  providerIssue,
@@ -120,7 +144,6 @@ async function loadHealthWidgetDataAsync(basePath) {
120
144
  };
121
145
  }
122
146
  // ── Widget init ────────────────────────────────────────────────────────────────
123
- const REFRESH_INTERVAL_MS = 60_000;
124
147
  /**
125
148
  * Initialize the always-on gsd-health widget (belowEditor).
126
149
  * Call once from the extension entry point after context is available.
@@ -129,13 +152,16 @@ export function initHealthWidget(ctx) {
129
152
  if (!ctx.hasUI)
130
153
  return;
131
154
  const basePath = projectRoot();
155
+ // Re-init must reflect filesystem changes immediately; the TTL cache is for
156
+ // interval refreshes, not this one-off synchronous paint.
157
+ projectStateCache.delete(basePath);
132
158
  // String-array fallback — used in RPC mode (factory is a no-op there).
133
159
  // Skip the expensive provider/environment doctor checks here: this runs
134
160
  // synchronously on the interactive-startup path, where running them would
135
161
  // block first paint by ~0.9s (lsof/docker probes, otherwise run again
136
162
  // immediately by the factory below). The factory's async refresh fills in
137
163
  // real health once the screen is up.
138
- const initialData = loadHealthWidgetData(basePath, { includeChecks: false });
164
+ const initialData = loadHealthWidgetData(basePath, { includeChecks: false, forceProjectState: true });
139
165
  ctx.ui.setWidget("gsd-health", buildHealthLines(initialData), { placement: "belowEditor" });
140
166
  // Factory-based widget for TUI mode — replaces the string-array above
141
167
  ctx.ui.setWidget("gsd-health", (_tui, _theme) => {
@@ -56,6 +56,26 @@ export function saveJsonFile(filePath, data) {
56
56
  // Non-fatal — don't let persistence failures break operation
57
57
  }
58
58
  }
59
+ /**
60
+ * Save a JSON file atomically using compact serialization.
61
+ *
62
+ * Intended for machine-only ledgers where pretty-printing has no consumer.
63
+ * Keeps the same write-tmp-rename safety and non-fatal error behavior as
64
+ * saveJsonFile.
65
+ */
66
+ export function saveJsonFileCompact(filePath, data) {
67
+ try {
68
+ mkdirSync(dirname(filePath), { recursive: true });
69
+ // Use randomized tmp suffix to prevent concurrent-write data loss
70
+ const tmp = `${filePath}.tmp.${randomBytes(4).toString("hex")}`;
71
+ writeFileSync(tmp, JSON.stringify(data) + "\n", "utf-8");
72
+ renameSync(tmp, filePath);
73
+ // No cleanup needed — renameSync atomically removes tmp on success
74
+ }
75
+ catch {
76
+ // Non-fatal — don't let persistence failures break operation
77
+ }
78
+ }
59
79
  /**
60
80
  * Write a JSON file atomically (write to .tmp, then rename).
61
81
  * Creates parent directories as needed. Non-fatal on error.