@opengsd/gsd-pi 1.0.2-dev.e9a1b49 → 1.0.2-dev.fb7ddf1

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 (490) hide show
  1. package/README.md +63 -12
  2. package/dist/headless-answers.js +2 -1
  3. package/dist/headless-events.d.ts +1 -0
  4. package/dist/headless-events.js +8 -1
  5. package/dist/onboarding.js +22 -3
  6. package/dist/resource-loader.d.ts +7 -0
  7. package/dist/resource-loader.js +44 -9
  8. package/dist/resources/.managed-resources-content-hash +1 -1
  9. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +34 -11
  10. package/dist/resources/extensions/context7/index.js +12 -2
  11. package/dist/resources/extensions/get-secrets-from-user.js +16 -16
  12. package/dist/resources/extensions/google-cli/index.js +30 -0
  13. package/dist/resources/extensions/google-cli/models.js +55 -0
  14. package/dist/resources/extensions/google-cli/package.json +11 -0
  15. package/dist/resources/extensions/google-cli/readiness.js +12 -0
  16. package/dist/resources/extensions/google-cli/stream-adapter.js +191 -0
  17. package/dist/resources/extensions/gsd/auto/loop.js +81 -1
  18. package/dist/resources/extensions/gsd/auto/orchestrator.js +4 -2
  19. package/dist/resources/extensions/gsd/auto/phases.js +38 -1
  20. package/dist/resources/extensions/gsd/auto/run-unit.js +8 -0
  21. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  22. package/dist/resources/extensions/gsd/auto-dispatch.js +17 -7
  23. package/dist/resources/extensions/gsd/auto-post-unit.js +65 -16
  24. package/dist/resources/extensions/gsd/auto-prompts.js +5 -236
  25. package/dist/resources/extensions/gsd/auto-recovery.js +10 -5
  26. package/dist/resources/extensions/gsd/auto-start.js +232 -49
  27. package/dist/resources/extensions/gsd/auto-tool-tracking.js +2 -1
  28. package/dist/resources/extensions/gsd/auto-verification.js +14 -2
  29. package/dist/resources/extensions/gsd/auto-worktree.js +36 -55
  30. package/dist/resources/extensions/gsd/auto.js +40 -2
  31. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +4 -3
  32. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +7 -2
  33. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +39 -5
  34. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +107 -27
  35. package/dist/resources/extensions/gsd/bootstrap/system-context.js +3 -27
  36. package/dist/resources/extensions/gsd/bootstrap/tool-search-shim.js +4 -4
  37. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +1 -1
  38. package/dist/resources/extensions/gsd/closeout-recovery.js +7 -1
  39. package/dist/resources/extensions/gsd/commands/handlers/auto.js +9 -1
  40. package/dist/resources/extensions/gsd/commands-handlers.js +3 -0
  41. package/dist/resources/extensions/gsd/commands-usage.js +105 -1
  42. package/dist/resources/extensions/gsd/config-overlay.js +20 -14
  43. package/dist/resources/extensions/gsd/context-overlay.js +22 -16
  44. package/dist/resources/extensions/gsd/dashboard-overlay.js +10 -23
  45. package/dist/resources/extensions/gsd/doctor-engine-checks.js +87 -0
  46. package/dist/resources/extensions/gsd/doctor-git-checks.js +70 -5
  47. package/dist/resources/extensions/gsd/doctor-providers.js +54 -24
  48. package/dist/resources/extensions/gsd/doctor.js +7 -2
  49. package/dist/resources/extensions/gsd/git-conflict-state.js +26 -1
  50. package/dist/resources/extensions/gsd/guided-flow.js +5 -6
  51. package/dist/resources/extensions/gsd/key-manager.js +45 -13
  52. package/dist/resources/extensions/gsd/mcp-filter.js +57 -18
  53. package/dist/resources/extensions/gsd/mcp-project-config.js +15 -9
  54. package/dist/resources/extensions/gsd/migration-auto-check.js +5 -1
  55. package/dist/resources/extensions/gsd/milestone-actions.js +3 -0
  56. package/dist/resources/extensions/gsd/milestone-reopen-events.js +28 -0
  57. package/dist/resources/extensions/gsd/notification-overlay.js +8 -9
  58. package/dist/resources/extensions/gsd/parallel-merge.js +6 -4
  59. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +15 -13
  60. package/dist/resources/extensions/gsd/post-execution-checks.js +5 -4
  61. package/dist/resources/extensions/gsd/preferences-skills.js +11 -4
  62. package/dist/resources/extensions/gsd/preferences.js +14 -2
  63. package/dist/resources/extensions/gsd/prompt-loader.js +2 -0
  64. package/dist/resources/extensions/gsd/prompts/discuss.md +4 -2
  65. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  66. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  67. package/dist/resources/extensions/gsd/prompts/system.md +3 -20
  68. package/dist/resources/extensions/gsd/queue-reorder-ui.js +28 -18
  69. package/dist/resources/extensions/gsd/repo-identity.js +36 -6
  70. package/dist/resources/extensions/gsd/repository-registry.js +3 -1
  71. package/dist/resources/extensions/gsd/safety/evidence-collector.js +13 -6
  72. package/dist/resources/extensions/gsd/skill-activation.js +233 -0
  73. package/dist/resources/extensions/gsd/skill-catalog.data.js +820 -0
  74. package/dist/resources/extensions/gsd/skill-catalog.install.js +179 -0
  75. package/dist/resources/extensions/gsd/skill-catalog.js +5 -1028
  76. package/dist/resources/extensions/gsd/skill-discovery.js +121 -79
  77. package/dist/resources/extensions/gsd/skill-scope.js +52 -0
  78. package/dist/resources/extensions/gsd/skill-telemetry.js +6 -39
  79. package/dist/resources/extensions/gsd/skills/gsd-headless/SKILL.md +1 -1
  80. package/dist/resources/extensions/gsd/skills.js +60 -0
  81. package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +351 -0
  82. package/dist/resources/extensions/gsd/state-reconciliation/index.js +41 -0
  83. package/dist/resources/extensions/gsd/state-reconciliation/registry.js +4 -0
  84. package/dist/resources/extensions/gsd/tools/complete-task.js +9 -0
  85. package/dist/resources/extensions/gsd/tools/exec-tool.js +42 -8
  86. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +63 -2
  87. package/dist/resources/extensions/gsd/tui/render-kit.js +51 -0
  88. package/dist/resources/extensions/gsd/unit-context-manifest.js +35 -26
  89. package/dist/resources/extensions/gsd/user-input-boundary.js +1 -1
  90. package/dist/resources/extensions/gsd/vision-ask.js +22 -0
  91. package/dist/resources/extensions/gsd/visualizer-overlay.js +8 -36
  92. package/dist/resources/extensions/gsd/worktree-lifecycle.js +24 -3
  93. package/dist/resources/extensions/gsd/worktree-post-create-hook.js +117 -0
  94. package/dist/resources/extensions/gsd/worktree-state-projection.js +29 -0
  95. package/dist/resources/extensions/search-the-web/native-search.js +57 -8
  96. package/dist/resources/extensions/shared/confirm-ui.js +9 -6
  97. package/dist/resources/extensions/shared/dialog-frame.js +42 -0
  98. package/dist/resources/extensions/shared/interview-ui.js +42 -30
  99. package/dist/resources/extensions/shared/next-action-ui.js +6 -6
  100. package/dist/resources/extensions/subagent/index.js +8 -15
  101. package/dist/resources/shared/package-manager-detection.js +36 -0
  102. package/dist/resources/skills/agent-browser/SKILL.md +1 -1
  103. package/dist/resources/skills/api-design/SKILL.md +1 -1
  104. package/dist/resources/skills/code-optimizer/SKILL.md +6 -11
  105. package/dist/resources/skills/create-gsd-extension/SKILL.md +1 -1
  106. package/dist/resources/skills/create-mcp-server/SKILL.md +1 -1
  107. package/dist/resources/skills/create-skill/references/gsd-skill-ecosystem.md +1 -1
  108. package/dist/resources/skills/create-skill/workflows/verify-skill.md +2 -10
  109. package/dist/resources/skills/debug-like-expert/references/when-to-research.md +1 -5
  110. package/dist/resources/skills/decompose-into-slices/SKILL.md +3 -3
  111. package/dist/resources/skills/dependency-upgrade/SKILL.md +1 -1
  112. package/dist/resources/skills/forensics/SKILL.md +2 -2
  113. package/dist/resources/skills/grill-me/SKILL.md +1 -1
  114. package/dist/resources/skills/handoff/SKILL.md +1 -1
  115. package/dist/resources/skills/make-interfaces-feel-better/SKILL.md +1 -1
  116. package/dist/resources/skills/observability/SKILL.md +1 -1
  117. package/dist/resources/skills/security-review/SKILL.md +1 -1
  118. package/dist/resources/skills/spike-wrap-up/SKILL.md +1 -1
  119. package/dist/resources/skills/tdd/SKILL.md +1 -1
  120. package/dist/resources/skills/write-docs/SKILL.md +1 -1
  121. package/dist/resources/skills/write-milestone-brief/SKILL.md +1 -1
  122. package/dist/update-check.d.ts +6 -2
  123. package/dist/update-check.js +7 -3
  124. package/dist/web/standalone/.next/BUILD_ID +1 -1
  125. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  126. package/dist/web/standalone/.next/build-manifest.json +2 -2
  127. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  128. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  129. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  130. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  131. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  132. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  134. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  136. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  137. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  139. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  140. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  141. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  142. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  143. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  144. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  145. package/dist/web/standalone/.next/server/app/index.html +1 -1
  146. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  147. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  148. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  149. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  150. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  151. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  152. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  153. package/dist/web/standalone/.next/server/chunks/1834.js +2 -2
  154. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  156. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  157. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  158. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  159. package/dist/worktree-cli.d.ts +0 -2
  160. package/dist/worktree-cli.js +21 -9
  161. package/package.json +5 -2
  162. package/packages/cloud-mcp-gateway/bin/gsd-cloud-mcp-gateway.js +14 -0
  163. package/packages/cloud-mcp-gateway/package.json +4 -3
  164. package/packages/contracts/dist/rpc.test.js +5 -0
  165. package/packages/contracts/dist/rpc.test.js.map +1 -1
  166. package/packages/contracts/dist/workflow.d.ts +15 -0
  167. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  168. package/packages/contracts/dist/workflow.js +16 -0
  169. package/packages/contracts/dist/workflow.js.map +1 -1
  170. package/packages/contracts/dist/workflow.test.js +1 -0
  171. package/packages/contracts/dist/workflow.test.js.map +1 -1
  172. package/packages/contracts/package.json +1 -1
  173. package/packages/daemon/package.json +4 -4
  174. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts +1 -0
  175. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts.map +1 -1
  176. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js +22 -8
  177. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js.map +1 -1
  178. package/packages/gsd-agent-core/package.json +5 -5
  179. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts +12 -0
  180. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts.map +1 -0
  181. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js +45 -0
  182. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js.map +1 -0
  183. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts +3 -2
  184. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  185. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js +11 -11
  186. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js.map +1 -1
  187. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts +3 -3
  188. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  189. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js +13 -11
  190. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js.map +1 -1
  191. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts +3 -3
  192. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  193. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js +12 -10
  194. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js.map +1 -1
  195. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts +1 -0
  196. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts.map +1 -1
  197. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js +1 -0
  198. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js.map +1 -1
  199. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts +1 -1
  200. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  201. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js +2 -2
  202. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js.map +1 -1
  203. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts +6 -1
  204. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  205. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js +9 -6
  206. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  207. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  208. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +3 -1
  209. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  210. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  211. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +0 -2
  212. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  213. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts +1 -0
  214. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts.map +1 -1
  215. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js +1 -0
  216. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js.map +1 -1
  217. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  218. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +2 -1
  219. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
  220. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts +3 -0
  221. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
  222. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +144 -2
  223. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
  224. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.d.ts.map +1 -1
  225. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js +2 -14
  226. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js.map +1 -1
  227. package/packages/gsd-agent-modes/package.json +7 -7
  228. package/packages/mcp-server/bin/gsd-mcp-server.js +14 -0
  229. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  230. package/packages/mcp-server/dist/server.js +7 -1
  231. package/packages/mcp-server/dist/server.js.map +1 -1
  232. package/packages/mcp-server/dist/workflow-tools.d.ts +13 -1
  233. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  234. package/packages/mcp-server/dist/workflow-tools.js +47 -8
  235. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  236. package/packages/mcp-server/package.json +5 -4
  237. package/packages/native/package.json +1 -1
  238. package/packages/pi-agent-core/dist/agent-loop.js +16 -14
  239. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  240. package/packages/pi-agent-core/dist/harness/skills.d.ts.map +1 -1
  241. package/packages/pi-agent-core/dist/harness/skills.js +6 -0
  242. package/packages/pi-agent-core/dist/harness/skills.js.map +1 -1
  243. package/packages/pi-agent-core/dist/harness/system-prompt.d.ts +7 -0
  244. package/packages/pi-agent-core/dist/harness/system-prompt.d.ts.map +1 -1
  245. package/packages/pi-agent-core/dist/harness/system-prompt.js +7 -0
  246. package/packages/pi-agent-core/dist/harness/system-prompt.js.map +1 -1
  247. package/packages/pi-agent-core/package.json +1 -1
  248. package/packages/pi-ai/bin/pi-ai.js +14 -0
  249. package/packages/pi-ai/dist/models.generated.d.ts +48 -206
  250. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  251. package/packages/pi-ai/dist/models.generated.js +73 -226
  252. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  253. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  254. package/packages/pi-ai/dist/providers/anthropic.js +50 -0
  255. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  256. package/packages/pi-ai/dist/providers/openai-responses-shared.d.ts.map +1 -1
  257. package/packages/pi-ai/dist/providers/openai-responses-shared.js +28 -4
  258. package/packages/pi-ai/dist/providers/openai-responses-shared.js.map +1 -1
  259. package/packages/pi-ai/dist/types.d.ts +2 -0
  260. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  261. package/packages/pi-ai/dist/types.js.map +1 -1
  262. package/packages/pi-ai/dist/utils/tests/tool-search-shim.test.js +29 -1
  263. package/packages/pi-ai/dist/utils/tests/tool-search-shim.test.js.map +1 -1
  264. package/packages/pi-ai/dist/utils/tool-search-shim.d.ts +4 -1
  265. package/packages/pi-ai/dist/utils/tool-search-shim.d.ts.map +1 -1
  266. package/packages/pi-ai/dist/utils/tool-search-shim.js +58 -10
  267. package/packages/pi-ai/dist/utils/tool-search-shim.js.map +1 -1
  268. package/packages/pi-ai/dist/utils/tool-shims.d.ts +1 -1
  269. package/packages/pi-ai/dist/utils/tool-shims.d.ts.map +1 -1
  270. package/packages/pi-ai/dist/utils/tool-shims.js.map +1 -1
  271. package/packages/pi-ai/package.json +3 -2
  272. package/packages/pi-coding-agent/README.md +1 -1
  273. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts +2 -2
  274. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts.map +1 -1
  275. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.js.map +1 -1
  276. package/packages/pi-coding-agent/dist/core/extensions/loader.js +1 -1
  277. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  278. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  279. package/packages/pi-coding-agent/dist/core/extensions/runner.js +8 -2
  280. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  281. package/packages/pi-coding-agent/dist/core/skills.d.ts +3 -0
  282. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  283. package/packages/pi-coding-agent/dist/core/skills.js +3 -0
  284. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  285. package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
  286. package/packages/pi-coding-agent/dist/core/tools/edit.js +5 -7
  287. package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
  288. package/packages/pi-coding-agent/dist/core/tools/read.d.ts +2 -2
  289. package/packages/pi-coding-agent/dist/core/tools/read.d.ts.map +1 -1
  290. package/packages/pi-coding-agent/dist/core/tools/read.js +5 -4
  291. package/packages/pi-coding-agent/dist/core/tools/read.js.map +1 -1
  292. package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
  293. package/packages/pi-coding-agent/dist/core/tools/write.js +0 -1
  294. package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
  295. package/packages/pi-coding-agent/package.json +8 -8
  296. package/packages/pi-tui/package.json +1 -1
  297. package/packages/rpc-client/package.json +2 -2
  298. package/pkg/package.json +1 -1
  299. package/scripts/install/deps.js +10 -0
  300. package/scripts/install/detect-existing.js +17 -3
  301. package/scripts/install/npm-global.js +103 -33
  302. package/scripts/install.js +1 -0
  303. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +36 -11
  304. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +86 -19
  305. package/src/resources/extensions/context7/index.ts +15 -2
  306. package/src/resources/extensions/get-secrets-from-user.ts +17 -16
  307. package/src/resources/extensions/google-cli/index.ts +34 -0
  308. package/src/resources/extensions/google-cli/models.ts +57 -0
  309. package/src/resources/extensions/google-cli/package.json +11 -0
  310. package/src/resources/extensions/google-cli/readiness.ts +15 -0
  311. package/src/resources/extensions/google-cli/stream-adapter.ts +245 -0
  312. package/src/resources/extensions/gsd/auto/loop.ts +96 -1
  313. package/src/resources/extensions/gsd/auto/orchestrator.ts +4 -2
  314. package/src/resources/extensions/gsd/auto/phases.ts +47 -1
  315. package/src/resources/extensions/gsd/auto/run-unit.ts +10 -0
  316. package/src/resources/extensions/gsd/auto/session.ts +3 -0
  317. package/src/resources/extensions/gsd/auto-dispatch.ts +31 -11
  318. package/src/resources/extensions/gsd/auto-post-unit.ts +101 -18
  319. package/src/resources/extensions/gsd/auto-prompts.ts +4 -284
  320. package/src/resources/extensions/gsd/auto-recovery.ts +10 -7
  321. package/src/resources/extensions/gsd/auto-start.ts +307 -56
  322. package/src/resources/extensions/gsd/auto-tool-tracking.ts +3 -1
  323. package/src/resources/extensions/gsd/auto-verification.ts +18 -2
  324. package/src/resources/extensions/gsd/auto-worktree.ts +47 -57
  325. package/src/resources/extensions/gsd/auto.ts +50 -2
  326. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +4 -3
  327. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +9 -4
  328. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +42 -5
  329. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +124 -25
  330. package/src/resources/extensions/gsd/bootstrap/system-context.ts +3 -28
  331. package/src/resources/extensions/gsd/bootstrap/tool-search-shim.ts +4 -4
  332. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +1 -1
  333. package/src/resources/extensions/gsd/closeout-recovery.ts +6 -1
  334. package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -1
  335. package/src/resources/extensions/gsd/commands-handlers.ts +2 -0
  336. package/src/resources/extensions/gsd/commands-usage.ts +110 -5
  337. package/src/resources/extensions/gsd/config-overlay.ts +19 -16
  338. package/src/resources/extensions/gsd/context-overlay.ts +24 -19
  339. package/src/resources/extensions/gsd/dashboard-overlay.ts +14 -27
  340. package/src/resources/extensions/gsd/doctor-engine-checks.ts +99 -0
  341. package/src/resources/extensions/gsd/doctor-git-checks.ts +72 -5
  342. package/src/resources/extensions/gsd/doctor-providers.ts +55 -27
  343. package/src/resources/extensions/gsd/doctor-types.ts +2 -0
  344. package/src/resources/extensions/gsd/doctor.ts +7 -2
  345. package/src/resources/extensions/gsd/git-conflict-state.ts +25 -1
  346. package/src/resources/extensions/gsd/guided-flow.ts +5 -6
  347. package/src/resources/extensions/gsd/key-manager.ts +57 -14
  348. package/src/resources/extensions/gsd/mcp-filter.ts +64 -17
  349. package/src/resources/extensions/gsd/mcp-project-config.ts +24 -9
  350. package/src/resources/extensions/gsd/migration-auto-check.ts +6 -0
  351. package/src/resources/extensions/gsd/milestone-actions.ts +2 -0
  352. package/src/resources/extensions/gsd/milestone-reopen-events.ts +28 -0
  353. package/src/resources/extensions/gsd/notification-overlay.ts +12 -11
  354. package/src/resources/extensions/gsd/parallel-merge.ts +6 -4
  355. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +16 -12
  356. package/src/resources/extensions/gsd/post-execution-checks.ts +7 -4
  357. package/src/resources/extensions/gsd/preferences-skills.ts +11 -4
  358. package/src/resources/extensions/gsd/preferences.ts +17 -2
  359. package/src/resources/extensions/gsd/prompt-loader.ts +2 -0
  360. package/src/resources/extensions/gsd/prompts/discuss.md +4 -2
  361. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  362. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  363. package/src/resources/extensions/gsd/prompts/system.md +3 -20
  364. package/src/resources/extensions/gsd/queue-reorder-ui.ts +29 -20
  365. package/src/resources/extensions/gsd/repo-identity.ts +35 -7
  366. package/src/resources/extensions/gsd/repository-registry.ts +3 -1
  367. package/src/resources/extensions/gsd/safety/evidence-collector.ts +13 -6
  368. package/src/resources/extensions/gsd/skill-activation.ts +292 -0
  369. package/src/resources/extensions/gsd/skill-catalog.data.ts +858 -0
  370. package/src/resources/extensions/gsd/skill-catalog.install.ts +205 -0
  371. package/src/resources/extensions/gsd/skill-catalog.ts +16 -1087
  372. package/src/resources/extensions/gsd/skill-discovery.ts +134 -78
  373. package/src/resources/extensions/gsd/skill-scope.ts +63 -0
  374. package/src/resources/extensions/gsd/skill-telemetry.ts +6 -40
  375. package/src/resources/extensions/gsd/skills/gsd-headless/SKILL.md +1 -1
  376. package/src/resources/extensions/gsd/skills.ts +75 -0
  377. package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +499 -0
  378. package/src/resources/extensions/gsd/state-reconciliation/index.ts +40 -0
  379. package/src/resources/extensions/gsd/state-reconciliation/registry.ts +8 -0
  380. package/src/resources/extensions/gsd/state-reconciliation/types.ts +30 -0
  381. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +328 -2
  382. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +75 -0
  383. package/src/resources/extensions/gsd/tests/auto-post-unit-artifact-diagnostic.test.ts +28 -2
  384. package/src/resources/extensions/gsd/tests/auto-post-unit-evidence-crossref-4909.test.ts +30 -0
  385. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +41 -0
  386. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +24 -0
  387. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +436 -0
  388. package/src/resources/extensions/gsd/tests/auto-worktree-untracked-content.test.ts +53 -0
  389. package/src/resources/extensions/gsd/tests/closeout-recovery.test.ts +15 -0
  390. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +31 -0
  391. package/src/resources/extensions/gsd/tests/commands-context.test.ts +5 -3
  392. package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +15 -2
  393. package/src/resources/extensions/gsd/tests/commands-usage.test.ts +97 -0
  394. package/src/resources/extensions/gsd/tests/complete-slice-reopen-handoff.test.ts +40 -3
  395. package/src/resources/extensions/gsd/tests/context-chart.test.ts +9 -0
  396. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +64 -0
  397. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +25 -0
  398. package/src/resources/extensions/gsd/tests/discord-invite-links.test.ts +1 -0
  399. package/src/resources/extensions/gsd/tests/discuss-prompt.test.ts +4 -2
  400. package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +1 -1
  401. package/src/resources/extensions/gsd/tests/doctor-empty-worktree.test.ts +71 -1
  402. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +105 -0
  403. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +101 -1
  404. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +30 -0
  405. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +6 -0
  406. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +22 -3
  407. package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +43 -0
  408. package/src/resources/extensions/gsd/tests/interactive-tool-idle-exemption.test.ts +8 -0
  409. package/src/resources/extensions/gsd/tests/key-manager.test.ts +23 -4
  410. package/src/resources/extensions/gsd/tests/mcp-filter.test.ts +19 -1
  411. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +24 -0
  412. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +56 -1
  413. package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +6 -1
  414. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +70 -10
  415. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +7 -1
  416. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +9 -0
  417. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +127 -10
  418. package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +44 -0
  419. package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +93 -0
  420. package/src/resources/extensions/gsd/tests/project-relocation-recovery.test.ts +101 -0
  421. package/src/resources/extensions/gsd/tests/queue-reorder-ui.test.ts +46 -0
  422. package/src/resources/extensions/gsd/tests/register-extension-guard.test.ts +116 -11
  423. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +27 -0
  424. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +30 -1
  425. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +4 -0
  426. package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +111 -0
  427. package/src/resources/extensions/gsd/tests/skill-scope-auto.test.ts +67 -0
  428. package/src/resources/extensions/gsd/tests/skills.test.ts +55 -0
  429. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +13 -2
  430. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +303 -0
  431. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +67 -1
  432. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +24 -1
  433. package/src/resources/extensions/gsd/tests/tui-border-assertions.ts +28 -0
  434. package/src/resources/extensions/gsd/tests/tui-render-kit.test.ts +14 -0
  435. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +18 -0
  436. package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +26 -0
  437. package/src/resources/extensions/gsd/tests/vision-ask.test.ts +23 -0
  438. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -1
  439. package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +133 -0
  440. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +82 -0
  441. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +16 -1
  442. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +28 -0
  443. package/src/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +141 -1
  444. package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +38 -1
  445. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +10 -0
  446. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +45 -1
  447. package/src/resources/extensions/gsd/tools/complete-task.ts +9 -0
  448. package/src/resources/extensions/gsd/tools/exec-tool.ts +42 -10
  449. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +82 -5
  450. package/src/resources/extensions/gsd/tui/render-kit.ts +82 -0
  451. package/src/resources/extensions/gsd/unit-context-manifest.ts +37 -26
  452. package/src/resources/extensions/gsd/user-input-boundary.ts +1 -1
  453. package/src/resources/extensions/gsd/vision-ask.ts +28 -0
  454. package/src/resources/extensions/gsd/visualizer-overlay.ts +12 -40
  455. package/src/resources/extensions/gsd/worktree-lifecycle.ts +37 -2
  456. package/src/resources/extensions/gsd/worktree-post-create-hook.ts +127 -0
  457. package/src/resources/extensions/gsd/worktree-state-projection.ts +33 -0
  458. package/src/resources/extensions/search-the-web/native-search.ts +60 -8
  459. package/src/resources/extensions/shared/confirm-ui.ts +8 -12
  460. package/src/resources/extensions/shared/dialog-frame.ts +71 -0
  461. package/src/resources/extensions/shared/interview-ui.ts +43 -42
  462. package/src/resources/extensions/shared/next-action-ui.ts +6 -6
  463. package/src/resources/extensions/shared/tests/confirm-ui.test.ts +57 -0
  464. package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +163 -0
  465. package/src/resources/extensions/shared/tests/next-action-ui-hasui.test.ts +55 -0
  466. package/src/resources/extensions/subagent/index.ts +8 -15
  467. package/src/resources/shared/package-manager-detection.ts +39 -0
  468. package/src/resources/skills/agent-browser/SKILL.md +1 -1
  469. package/src/resources/skills/api-design/SKILL.md +1 -1
  470. package/src/resources/skills/code-optimizer/SKILL.md +6 -11
  471. package/src/resources/skills/create-gsd-extension/SKILL.md +1 -1
  472. package/src/resources/skills/create-mcp-server/SKILL.md +1 -1
  473. package/src/resources/skills/create-skill/references/gsd-skill-ecosystem.md +1 -1
  474. package/src/resources/skills/create-skill/workflows/verify-skill.md +2 -10
  475. package/src/resources/skills/debug-like-expert/references/when-to-research.md +1 -5
  476. package/src/resources/skills/decompose-into-slices/SKILL.md +3 -3
  477. package/src/resources/skills/dependency-upgrade/SKILL.md +1 -1
  478. package/src/resources/skills/forensics/SKILL.md +2 -2
  479. package/src/resources/skills/grill-me/SKILL.md +1 -1
  480. package/src/resources/skills/handoff/SKILL.md +1 -1
  481. package/src/resources/skills/make-interfaces-feel-better/SKILL.md +1 -1
  482. package/src/resources/skills/observability/SKILL.md +1 -1
  483. package/src/resources/skills/security-review/SKILL.md +1 -1
  484. package/src/resources/skills/spike-wrap-up/SKILL.md +1 -1
  485. package/src/resources/skills/tdd/SKILL.md +1 -1
  486. package/src/resources/skills/write-docs/SKILL.md +1 -1
  487. package/src/resources/skills/write-milestone-brief/SKILL.md +1 -1
  488. package/dist/tsconfig.extensions.tsbuildinfo +0 -1
  489. /package/dist/web/standalone/.next/static/{BEjZM0MLHLibeMFbjtMol → tH1tnDYt1E0hK9Ien73Z0}/_buildManifest.js +0 -0
  490. /package/dist/web/standalone/.next/static/{BEjZM0MLHLibeMFbjtMol → tH1tnDYt1E0hK9Ien73Z0}/_ssgManifest.js +0 -0
@@ -15,16 +15,15 @@ import { delimiter, join } from "node:path";
15
15
  import { AuthStorage } from "@gsd/pi-coding-agent";
16
16
  import { getEnvApiKey } from "@gsd/pi-ai";
17
17
  import { loadEffectiveGSDPreferences } from "./preferences.js";
18
- import { getAuthPath, PROVIDER_REGISTRY } from "./key-manager.js";
18
+ import { getAuthPath, PROVIDER_REGISTRY, supportsBrowserOAuth } from "./key-manager.js";
19
19
  import { homedir } from "node:os";
20
20
  // ── Provider routing constants ────────────────────────────────────────────────
21
21
  /**
22
22
  * Providers that use external CLI authentication (not API keys).
23
- * These are always considered "found" the host CLI handles auth.
23
+ * When explicitly selected, the provider's own CLI/session owns auth.
24
24
  */
25
25
  const CLI_AUTH_PROVIDERS = new Set([
26
26
  "claude-code",
27
- "openai-codex",
28
27
  "google-gemini-cli",
29
28
  "google-antigravity",
30
29
  ]);
@@ -126,23 +125,26 @@ function collectConfiguredModelProviders() {
126
125
  * Used for lightweight binary-presence checks (PATH scan, no subprocess).
127
126
  */
128
127
  const CLI_BINARY_MAP = {
129
- "claude-code": "claude",
130
- "openai-codex": "codex",
131
- "google-gemini-cli": "gemini",
132
- "google-antigravity": "antigravity",
128
+ "claude-code": ["claude", "claude-code"],
129
+ "google-gemini-cli": ["gemini"],
130
+ "google-antigravity": ["agy"],
133
131
  };
132
+ const CLI_AUTH_PATH_CHECK_PROVIDERS = new Set([
133
+ "google-gemini-cli",
134
+ "google-antigravity",
135
+ ]);
134
136
  /**
135
137
  * Check if a CLI provider's binary exists anywhere in PATH.
136
138
  * Fast filesystem scan — no subprocess, no network, sub-1ms.
137
139
  */
138
140
  function isCliBinaryInPath(providerId) {
139
- const binary = CLI_BINARY_MAP[providerId];
140
- if (!binary)
141
+ const binaries = CLI_BINARY_MAP[providerId];
142
+ if (!binaries)
141
143
  return false;
142
144
  const pathDirs = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
143
145
  // On Windows, command shims are commonly installed as .cmd/.exe/.bat/.com.
144
146
  // Scan PATHEXT candidates in addition to the bare binary name.
145
- const executableNames = [binary];
147
+ const executableNames = [...binaries];
146
148
  if (process.platform === "win32") {
147
149
  const rawPathExt = process.env.PATHEXT
148
150
  ?.split(";")
@@ -150,10 +152,12 @@ function isCliBinaryInPath(providerId) {
150
152
  .filter(Boolean) ?? [];
151
153
  const normalizedPathExt = rawPathExt.map(ext => ext.startsWith(".") ? ext.toLowerCase() : `.${ext.toLowerCase()}`);
152
154
  const defaultExt = [".exe", ".cmd", ".bat", ".com"];
153
- for (const ext of [...normalizedPathExt, ...defaultExt]) {
154
- const candidate = `${binary}${ext}`;
155
- if (!executableNames.includes(candidate))
156
- executableNames.push(candidate);
155
+ for (const binary of binaries) {
156
+ for (const ext of [...normalizedPathExt, ...defaultExt]) {
157
+ const candidate = `${binary}${ext}`;
158
+ if (!executableNames.includes(candidate))
159
+ executableNames.push(candidate);
160
+ }
157
161
  }
158
162
  }
159
163
  return pathDirs.some(dir => executableNames.some(name => existsSync(join(dir, name))));
@@ -185,11 +189,6 @@ function hasModelsJsonApiKey(providerId) {
185
189
  }
186
190
  function resolveKey(providerId) {
187
191
  const info = PROVIDER_REGISTRY.find(p => p.id === providerId);
188
- // claude-code never stores credentials in auth.json — GSD delegates entirely to
189
- // the local CLI binary. Presence of the binary in PATH is the only signal.
190
- if (providerId === "claude-code") {
191
- return { found: isCliBinaryInPath("claude-code"), source: "env", backedOff: false };
192
- }
193
192
  if (providerId === "anthropic-vertex" && process.env.ANTHROPIC_VERTEX_PROJECT_ID) {
194
193
  return { found: true, source: "env", backedOff: false };
195
194
  }
@@ -201,7 +200,16 @@ function resolveKey(providerId) {
201
200
  const creds = auth.getCredentialsForProvider(providerId);
202
201
  if (creds.length > 0) {
203
202
  // Filter out empty placeholder keys (from skipped onboarding)
204
- const hasRealKey = creds.some(c => c.type === "oauth" || (c.type === "api_key" && c.key));
203
+ const hasRealKey = creds.some(c => {
204
+ if (c.type === "oauth")
205
+ return true;
206
+ if (c.type !== "api_key")
207
+ return false;
208
+ const key = c.key?.trim();
209
+ if (!key)
210
+ return false;
211
+ return !(CLI_AUTH_PROVIDERS.has(providerId) && key === "cli");
212
+ });
205
213
  if (hasRealKey) {
206
214
  return {
207
215
  found: true,
@@ -229,6 +237,11 @@ function resolveKey(providerId) {
229
237
  if (hasModelsJsonApiKey(providerId)) {
230
238
  return { found: true, source: "models.json", backedOff: false };
231
239
  }
240
+ // Cross-provider routes can use a local CLI when it is installed. Explicit
241
+ // external CLI provider selections are handled in checkLlmProviders() below.
242
+ if (CLI_AUTH_PROVIDERS.has(providerId) && isCliBinaryInPath(providerId)) {
243
+ return { found: true, source: "env", backedOff: false };
244
+ }
232
245
  return { found: false, source: "none", backedOff: false };
233
246
  }
234
247
  // ── Individual check groups ────────────────────────────────────────────────────
@@ -236,15 +249,32 @@ function checkLlmProviders() {
236
249
  const required = collectConfiguredModelProviders();
237
250
  const results = [];
238
251
  for (const providerId of required) {
239
- // CLI-authenticated providers don't need API keys skip key check
252
+ // CLI-authenticated providers don't need API keys. The provider's own
253
+ // request path validates CLI sessions when it is used.
240
254
  if (CLI_AUTH_PROVIDERS.has(providerId)) {
241
255
  const info = PROVIDER_REGISTRY.find(p => p.id === providerId);
256
+ const label = info?.label ?? providerId;
257
+ if (CLI_AUTH_PATH_CHECK_PROVIDERS.has(providerId) && !isCliBinaryInPath(providerId)) {
258
+ const binaries = CLI_BINARY_MAP[providerId]?.map(binary => `\`${binary}\``).join(" or ");
259
+ results.push({
260
+ name: providerId,
261
+ label,
262
+ category: "llm",
263
+ status: "error",
264
+ message: `${label} — CLI not found`,
265
+ detail: binaries
266
+ ? `Install ${label} and ensure ${binaries} is on PATH`
267
+ : `Install ${label} and ensure its CLI is on PATH`,
268
+ required: true,
269
+ });
270
+ continue;
271
+ }
242
272
  results.push({
243
273
  name: providerId,
244
- label: info?.label ?? providerId,
274
+ label,
245
275
  category: "llm",
246
276
  status: "ok",
247
- message: `${info?.label ?? providerId} — CLI auth (no key needed)`,
277
+ message: `${label} — CLI auth (no key needed)`,
248
278
  required: true,
249
279
  });
250
280
  continue;
@@ -282,7 +312,7 @@ function checkLlmProviders() {
282
312
  message: `${label} — not configured`,
283
313
  detail: providerId === "anthropic-vertex"
284
314
  ? "Set ANTHROPIC_VERTEX_PROJECT_ID and authenticate with Google ADC"
285
- : info?.hasOAuth
315
+ : info && supportsBrowserOAuth(info)
286
316
  ? `Run /gsd keys to authenticate`
287
317
  : `Set ${envVar} or run /gsd keys`,
288
318
  required: true,
@@ -306,7 +306,7 @@ export async function runGSDDoctor(basePath, options) {
306
306
  return false;
307
307
  return true;
308
308
  };
309
- const prefs = loadEffectiveGSDPreferences();
309
+ const prefs = loadEffectiveGSDPreferences(basePath);
310
310
  if (prefs) {
311
311
  const prefIssues = validatePreferenceShape(prefs.preferences);
312
312
  for (const issue of prefIssues) {
@@ -480,13 +480,18 @@ export async function runGSDDoctor(basePath, options) {
480
480
  catch {
481
481
  continue;
482
482
  }
483
+ if (entry === "parallel-research")
484
+ continue;
483
485
  if (!knownSliceIds.has(entry)) {
486
+ const quarantineExample = `.gsd/quarantine/milestones/${milestoneId}/slices/${entry}-manual-review`;
484
487
  issues.push({
485
488
  severity: "warning",
486
489
  code: "orphaned_slice_directory",
487
490
  scope: "milestone",
488
491
  unitId: milestoneId,
489
- message: `Directory "${entry}" exists in ${milestoneId}/slices/ but is not referenced in the roadmap`,
492
+ message: `Directory "${entry}" exists in ${milestoneId}/slices/ but is not referenced in the roadmap or DB. ` +
493
+ `Review it; if stale, move or delete it. To preserve it, move it under ${quarantineExample}. ` +
494
+ "If it contains work to keep, copy or merge that content into a DB-backed slice before resuming.",
490
495
  file: `${relMilestonePath(basePath, milestoneId)}/slices/${entry}`,
491
496
  fixable: false,
492
497
  });
@@ -2,7 +2,7 @@
2
2
  // File Purpose: Detect and reconcile unresolved Git conflict state before automation runs.
3
3
  import { spawnSync } from "node:child_process";
4
4
  import { existsSync } from "node:fs";
5
- import { join } from "node:path";
5
+ import { dirname, join, resolve } from "node:path";
6
6
  import { autoResolveSafeConflictPaths } from "./git-conflict-resolve.js";
7
7
  import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
8
8
  import { abortAndReset } from "./git-self-heal.js";
@@ -10,6 +10,23 @@ import { logWarning } from "./workflow-logger.js";
10
10
  function splitZeroDelimited(output) {
11
11
  return output.split("\0").filter(Boolean);
12
12
  }
13
+ function hasGitMarker(basePath) {
14
+ try {
15
+ let dir = resolve(basePath);
16
+ for (let i = 0; i < 30; i++) {
17
+ if (existsSync(join(dir, ".git")))
18
+ return true;
19
+ const parent = dirname(dir);
20
+ if (parent === dir)
21
+ break;
22
+ dir = parent;
23
+ }
24
+ }
25
+ catch {
26
+ // Fall through to the git probes, which will report unknown on failure.
27
+ }
28
+ return false;
29
+ }
13
30
  export function listUnmergedGitPaths(basePath) {
14
31
  try {
15
32
  const output = spawnSync("git", ["diff", "--name-only", "--diff-filter=U", "-z"], {
@@ -57,6 +74,14 @@ export function listMergeStateBlockers(basePath) {
57
74
  return MERGE_STATE_MARKERS.filter((marker) => existsSync(join(gitDir, marker)));
58
75
  }
59
76
  export function probeGitConflictState(basePath) {
77
+ if (!hasGitMarker(basePath)) {
78
+ return {
79
+ status: "clean",
80
+ unmerged: [],
81
+ checkFailures: [],
82
+ mergeStateBlockers: [],
83
+ };
84
+ }
60
85
  const unmerged = listUnmergedGitPaths(basePath);
61
86
  if (unmerged === null) {
62
87
  return {
@@ -934,7 +934,7 @@ async function dispatchWorkflow(pi, note, customType = "gsd-run", ctx, unitType,
934
934
  if (unitType)
935
935
  setGuidedUnitContext(projectRoot, unitType);
936
936
  try {
937
- pi.sendMessage({
937
+ await pi.sendMessage({
938
938
  customType,
939
939
  content: buildWorkflowDispatchContent({ workflow, workflowPath, task: note }),
940
940
  display: false,
@@ -946,10 +946,9 @@ async function dispatchWorkflow(pi, note, customType = "gsd-run", ctx, unitType,
946
946
  }
947
947
  }
948
948
  finally {
949
- // Restore full tool set after the message is queued. The LLM turn has
950
- // already captured the scoped set restoring prevents the narrowed
951
- // tools from leaking into subsequent dispatches (#3628). The finally
952
- // block ensures restoration even if sendMessage throws.
949
+ // Restore full tool/skill surface after the turn completes. Awaiting
950
+ // sendMessage ensures scoped skills stay in _baseSystemPrompt through
951
+ // before_agent_start (#3628, skill token savings).
953
952
  restoreGsdWorkflowTools(pi, savedTools);
954
953
  }
955
954
  }
@@ -1045,7 +1044,7 @@ function buildHeadlessDiscussPrompt(nextId, seedContext, _basePath) {
1045
1044
  * Run preparation phase if enabled, then build the discuss prompt.
1046
1045
  * Preparation analyzes the codebase and prior context, injecting the results
1047
1046
  * as supplementary context into the standard discuss template. The discuss
1048
- * template drives the conversation (asks "What's the vision?" first), while
1047
+ * template drives the conversation with a variable vision opener, while
1049
1048
  * the preparation briefs give the agent grounding in the existing codebase.
1050
1049
  *
1051
1050
  * @param ctx - Extension command context with UI for progress notifications
@@ -12,18 +12,18 @@ import { getErrorMessage } from "./error-utils.js";
12
12
  import { gsdHome } from "./gsd-home.js";
13
13
  export const PROVIDER_REGISTRY = [
14
14
  // LLM Providers
15
- { id: "anthropic", label: "Anthropic (Claude)", category: "llm", envVar: "ANTHROPIC_API_KEY", prefixes: ["sk-ant-"], hasOAuth: true, dashboardUrl: "console.anthropic.com" },
15
+ { id: "anthropic", label: "Anthropic (Claude)", category: "llm", envVar: "ANTHROPIC_API_KEY", prefixes: ["sk-ant-"], authMode: "apiKey", dashboardUrl: "console.anthropic.com" },
16
16
  // Claude Code CLI: routes through the local `claude` binary — no API key,
17
17
  // authentication is handled by the CLI's own OAuth flow.
18
18
  // Referenced by doctor-providers.ts, auto-model-selection.ts, and others;
19
19
  // must be in the canonical registry so all consumers see the same catalog.
20
20
  // See: https://github.com/open-gsd/gsd-pi/issues/4541
21
- { id: "claude-code", label: "Claude Code CLI", category: "llm", hasOAuth: true },
21
+ { id: "claude-code", label: "Claude Code CLI", category: "llm", authMode: "externalCli" },
22
22
  { id: "openai", label: "OpenAI", category: "llm", envVar: "OPENAI_API_KEY", prefixes: ["sk-"], dashboardUrl: "platform.openai.com/api-keys" },
23
- { id: "github-copilot", label: "GitHub Copilot", category: "llm", envVar: "GITHUB_TOKEN", hasOAuth: true },
24
- { id: "openai-codex", label: "ChatGPT Plus/Pro (Codex)", category: "llm", hasOAuth: true },
25
- { id: "google-gemini-cli", label: "Google Gemini CLI", category: "llm", hasOAuth: true },
26
- { id: "google-antigravity", label: "Antigravity", category: "llm", hasOAuth: true },
23
+ { id: "github-copilot", label: "GitHub Copilot", category: "llm", envVar: "GITHUB_TOKEN", authMode: "browserOAuth" },
24
+ { id: "openai-codex", label: "ChatGPT Plus/Pro (Codex)", category: "llm", authMode: "browserOAuth" },
25
+ { id: "google-gemini-cli", label: "Google Gemini CLI", category: "llm", authMode: "externalCli" },
26
+ { id: "google-antigravity", label: "Antigravity", category: "llm", authMode: "externalCli" },
27
27
  { id: "google", label: "Google (Gemini)", category: "llm", envVar: "GEMINI_API_KEY", dashboardUrl: "aistudio.google.com/apikey" },
28
28
  { id: "groq", label: "Groq", category: "llm", envVar: "GROQ_API_KEY", dashboardUrl: "console.groq.com" },
29
29
  { id: "xai", label: "xAI (Grok)", category: "llm", envVar: "XAI_API_KEY", dashboardUrl: "console.x.ai" },
@@ -49,6 +49,20 @@ export const PROVIDER_REGISTRY = [
49
49
  { id: "telegram_bot", label: "Telegram Bot", category: "remote", envVar: "TELEGRAM_BOT_TOKEN" },
50
50
  ];
51
51
  // ─── Utilities ──────────────────────────────────────────────────────────────────
52
+ export function getProviderAuthMode(provider) {
53
+ return provider.authMode ?? "apiKey";
54
+ }
55
+ export function supportsBrowserOAuth(provider) {
56
+ return getProviderAuthMode(provider) === "browserOAuth";
57
+ }
58
+ export function supportsStoredApiKey(provider) {
59
+ const mode = getProviderAuthMode(provider);
60
+ if (mode === "externalCli" || mode === "cloudIdentity" || mode === "none")
61
+ return false;
62
+ if (mode === "browserOAuth")
63
+ return Boolean(provider.envVar || provider.prefixes?.length);
64
+ return true;
65
+ }
52
66
  /**
53
67
  * Mask an API key for display: show first 4 + last 4 chars.
54
68
  * Keys shorter than 12 chars show only first 2 + last 2.
@@ -79,11 +93,14 @@ export function formatDuration(ms) {
79
93
  /**
80
94
  * Describe a credential's type and status.
81
95
  */
82
- export function describeCredential(cred) {
96
+ export function describeCredential(cred, provider) {
83
97
  if (cred.type === "api_key") {
84
98
  const apiCred = cred;
85
99
  if (!apiCred.key)
86
100
  return "empty key";
101
+ if (apiCred.key === "cli" && provider && getProviderAuthMode(provider) === "externalCli") {
102
+ return "external CLI";
103
+ }
87
104
  return `API key (${maskKey(apiCred.key)})`;
88
105
  }
89
106
  if (cred.type === "oauth") {
@@ -129,7 +146,7 @@ export function getAllKeyStatuses(auth) {
129
146
  const firstCred = creds[0];
130
147
  const desc = creds.length > 1
131
148
  ? `${creds.length} keys (round-robin)`
132
- : describeCredential(firstCred);
149
+ : describeCredential(firstCred, provider);
133
150
  return {
134
151
  provider,
135
152
  configured: true,
@@ -236,9 +253,20 @@ export async function handleAddKey(providerArg, ctx, auth) {
236
253
  return false;
237
254
  provider = PROVIDER_REGISTRY[idx];
238
255
  }
239
- // If OAuth is available, offer choice
240
- if (provider.hasOAuth) {
241
- const methods = ["API key", "Browser login (OAuth)"];
256
+ const authMode = getProviderAuthMode(provider);
257
+ if (authMode === "externalCli") {
258
+ ctx.ui.notify(`${provider.label} is authenticated by its own local CLI. ` +
259
+ `Sign in with that CLI first, then run /login to activate it in GSD.`, "info");
260
+ return false;
261
+ }
262
+ if (authMode === "cloudIdentity") {
263
+ ctx.ui.notify(`${provider.label} uses cloud identity credentials. Configure the provider's cloud CLI or environment, then restart GSD.`, "info");
264
+ return false;
265
+ }
266
+ if (supportsBrowserOAuth(provider)) {
267
+ const methods = supportsStoredApiKey(provider)
268
+ ? ["API token/key", "Browser login (OAuth)"]
269
+ : ["Browser login (OAuth)"];
242
270
  const method = await ctx.ui.select(`${provider.label} — how do you want to authenticate?`, methods);
243
271
  if (!method || typeof method !== "string")
244
272
  return false;
@@ -248,6 +276,10 @@ export async function handleAddKey(providerArg, ctx, auth) {
248
276
  return false;
249
277
  }
250
278
  }
279
+ if (!supportsStoredApiKey(provider)) {
280
+ ctx.ui.notify(`${provider.label} does not accept a GSD-stored API key. Use /login.`, "info");
281
+ return false;
282
+ }
251
283
  // API key input
252
284
  const input = await ctx.ui.input(`API key for ${provider.label}:`, provider.envVar ? `or set ${provider.envVar} env var` : "paste your key here");
253
285
  if (input === null || input === undefined)
@@ -309,7 +341,7 @@ export async function handleRemoveKey(providerArg, ctx, auth) {
309
341
  }
310
342
  // Multi-key handling
311
343
  if (creds.length > 1) {
312
- const options = creds.map((c, i) => `[${i + 1}] ${describeCredential(c)}`);
344
+ const options = creds.map((c, i) => `[${i + 1}] ${describeCredential(c, provider)}`);
313
345
  options.push("Remove all");
314
346
  const choice = await ctx.ui.select(`${provider.label} has ${creds.length} keys. Remove which?`, options);
315
347
  if (!choice || typeof choice !== "string")
@@ -330,7 +362,7 @@ export async function handleRemoveKey(providerArg, ctx, auth) {
330
362
  }
331
363
  }
332
364
  else {
333
- const confirmed = await ctx.ui.confirm("Remove key?", `Remove ${describeCredential(creds[0])} for ${provider.label}?`);
365
+ const confirmed = await ctx.ui.confirm("Remove key?", `Remove ${describeCredential(creds[0], provider)} for ${provider.label}?`);
334
366
  if (!confirmed)
335
367
  return false;
336
368
  auth.remove(provider.id);
@@ -1,29 +1,68 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
  import { resolveModelMcpConfig } from "./preferences-mcp.js";
4
- export function discoverMcpServerNames(projectDir) {
4
+ function isRecord(value) {
5
+ return !!value && typeof value === "object" && !Array.isArray(value);
6
+ }
7
+ function readJsonFile(path, ignoreParseErrors = false) {
8
+ if (!existsSync(path))
9
+ return undefined;
10
+ try {
11
+ return JSON.parse(readFileSync(path, "utf-8"));
12
+ }
13
+ catch (err) {
14
+ if (!ignoreParseErrors)
15
+ throw err;
16
+ return undefined;
17
+ }
18
+ }
19
+ function collectServerEntries(servers) {
20
+ if (!isRecord(servers))
21
+ return [];
22
+ return Object.entries(servers).map(([name, config]) => ({ name, config }));
23
+ }
24
+ export function discoverMcpServers(projectDir) {
5
25
  const mcpJsonPath = resolve(projectDir, ".mcp.json");
6
26
  const settingsPath = resolve(projectDir, ".claude", "settings.json");
7
- let mcpJsonServers = [];
8
- if (existsSync(mcpJsonPath)) {
9
- const raw = readFileSync(mcpJsonPath, "utf-8");
10
- const parsed = JSON.parse(raw);
11
- mcpJsonServers = Object.keys(parsed.mcpServers ?? {});
27
+ const mcpJson = readJsonFile(mcpJsonPath);
28
+ const settings = readJsonFile(settingsPath, true);
29
+ const seen = new Set();
30
+ const discovered = [];
31
+ for (const entry of [
32
+ ...collectServerEntries(mcpJson?.mcpServers),
33
+ ...collectServerEntries(mcpJson?.servers),
34
+ ...collectServerEntries(settings?.mcpServers),
35
+ ]) {
36
+ if (seen.has(entry.name))
37
+ continue;
38
+ seen.add(entry.name);
39
+ discovered.push(entry);
12
40
  }
13
- let settingsServers = [];
14
- if (existsSync(settingsPath)) {
15
- try {
16
- const raw = readFileSync(settingsPath, "utf-8");
17
- const parsed = JSON.parse(raw);
18
- if (parsed.mcpServers) {
19
- settingsServers = Object.keys(parsed.mcpServers);
20
- }
21
- }
22
- catch {
23
- // settings.json parse errors are silently ignored
41
+ return discovered;
42
+ }
43
+ function isWorkflowMcpServerConfig(config) {
44
+ if (!isRecord(config))
45
+ return false;
46
+ const env = config.env;
47
+ if (isRecord(env)) {
48
+ if (typeof env.GSD_WORKFLOW_PROJECT_ROOT === "string"
49
+ || typeof env.GSD_WORKFLOW_EXECUTORS_MODULE === "string"
50
+ || typeof env.GSD_WORKFLOW_WRITE_GATE_MODULE === "string"
51
+ || typeof env.GSD_PERSIST_WRITE_GATE_STATE === "string") {
52
+ return true;
24
53
  }
25
54
  }
26
- return [...new Set([...mcpJsonServers, ...settingsServers])];
55
+ const command = typeof config.command === "string" ? config.command : "";
56
+ if (command.includes("gsd-mcp-server"))
57
+ return true;
58
+ const args = Array.isArray(config.args) ? config.args.filter((arg) => typeof arg === "string") : [];
59
+ return args.some((arg) => arg.includes("gsd-mcp-server") || arg.includes("packages/mcp-server"));
60
+ }
61
+ export function discoverWorkflowMcpServerName(projectDir) {
62
+ return discoverMcpServers(projectDir).find((server) => isWorkflowMcpServerConfig(server.config))?.name;
63
+ }
64
+ export function discoverMcpServerNames(projectDir) {
65
+ return discoverMcpServers(projectDir).map((server) => server.name);
27
66
  }
28
67
  export function computeMcpDisallowedTools(modelId, mcpConfig, discoveredServers, workflowServerName) {
29
68
  if (!mcpConfig)
@@ -20,6 +20,9 @@ export function resolveBundledGsdCliPath(env = process.env) {
20
20
  return null;
21
21
  }
22
22
  export function buildProjectWorkflowMcpServerConfig(projectRoot, env = process.env) {
23
+ return buildProjectWorkflowMcpServerSpec(projectRoot, env).server;
24
+ }
25
+ function buildProjectWorkflowMcpServerSpec(projectRoot, env = process.env) {
23
26
  const resolvedProjectRoot = resolve(projectRoot);
24
27
  const gsdCliPath = resolveBundledGsdCliPath(env);
25
28
  const launch = detectWorkflowMcpLaunchConfig(resolvedProjectRoot, {
@@ -30,10 +33,13 @@ export function buildProjectWorkflowMcpServerConfig(projectRoot, env = process.e
30
33
  throw new Error("Unable to resolve the GSD workflow MCP server. Build this checkout or install gsd-mcp-server on PATH.");
31
34
  }
32
35
  return {
33
- command: launch.command,
34
- ...(launch.args && launch.args.length > 0 ? { args: launch.args } : {}),
35
- ...(launch.cwd ? { cwd: launch.cwd } : {}),
36
- ...(launch.env ? { env: launch.env } : {}),
36
+ serverName: launch.name || GSD_WORKFLOW_MCP_SERVER_NAME,
37
+ server: {
38
+ command: launch.command,
39
+ ...(launch.args && launch.args.length > 0 ? { args: launch.args } : {}),
40
+ ...(launch.cwd ? { cwd: launch.cwd } : {}),
41
+ ...(launch.env ? { env: launch.env } : {}),
42
+ },
37
43
  };
38
44
  }
39
45
  function readExistingConfig(configPath) {
@@ -53,20 +59,20 @@ export function ensureProjectWorkflowMcpConfig(projectRoot, env = process.env) {
53
59
  assertSafeDirectory(resolvedProjectRoot);
54
60
  const configPath = resolve(resolvedProjectRoot, ".mcp.json");
55
61
  const existing = readExistingConfig(configPath);
56
- const desiredServer = buildProjectWorkflowMcpServerConfig(resolvedProjectRoot, env);
62
+ const { serverName, server: desiredServer } = buildProjectWorkflowMcpServerSpec(resolvedProjectRoot, env);
57
63
  const previousServers = existing.mcpServers ?? {};
58
64
  const nextServers = {
59
65
  ...previousServers,
60
- [GSD_WORKFLOW_MCP_SERVER_NAME]: desiredServer,
66
+ [serverName]: desiredServer,
61
67
  };
62
68
  const alreadyPresent = existsSync(configPath);
63
- const unchanged = JSON.stringify(previousServers[GSD_WORKFLOW_MCP_SERVER_NAME] ?? null)
69
+ const unchanged = JSON.stringify(previousServers[serverName] ?? null)
64
70
  === JSON.stringify(desiredServer)
65
71
  && existing.mcpServers !== undefined;
66
72
  if (unchanged) {
67
73
  return {
68
74
  configPath,
69
- serverName: GSD_WORKFLOW_MCP_SERVER_NAME,
75
+ serverName,
70
76
  status: "unchanged",
71
77
  };
72
78
  }
@@ -77,7 +83,7 @@ export function ensureProjectWorkflowMcpConfig(projectRoot, env = process.env) {
77
83
  writeFileSync(configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf-8");
78
84
  return {
79
85
  configPath,
80
- serverName: GSD_WORKFLOW_MCP_SERVER_NAME,
86
+ serverName,
81
87
  status: alreadyPresent ? "updated" : "created",
82
88
  };
83
89
  }
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readdirSync, readFileSync } from "node:fs";
2
2
  import { ensureDbOpen } from "./bootstrap/dynamic-tools.js";
3
- import { getAllMilestones, getMilestoneSlices, getSliceTasks, isDbAvailable, } from "./gsd-db.js";
3
+ import { getAllMilestones, getMilestoneSlices, getSliceTasks, isDbAvailable, refreshOpenDatabaseFromDisk, } from "./gsd-db.js";
4
4
  import { parsePlan, parseRoadmap } from "./parsers-legacy.js";
5
5
  import { milestonesDir, resolveMilestoneFile, resolveSliceFile, } from "./paths.js";
6
6
  function zeroCounts() {
@@ -63,6 +63,10 @@ export async function checkMarkdownHierarchyAgainstDb(basePath) {
63
63
  if (!opened || !isDbAvailable()) {
64
64
  throw new Error(`failed to open or create the GSD database at ${basePath}`);
65
65
  }
66
+ // The markdown projections may have just been written by a workflow/MCP
67
+ // server in another process. Reopen before comparing so startup does not
68
+ // warn from a stale long-lived SQLite handle.
69
+ refreshOpenDatabaseFromDisk();
66
70
  const beforeDb = countDbHierarchy();
67
71
  if (sameCounts(markdown, beforeDb)) {
68
72
  return { action: "none", reason: "in-sync", markdown, beforeDb, afterDb: beforeDb };
@@ -149,6 +149,9 @@ export function discardMilestone(basePath, milestoneId) {
149
149
  logWarning("engine", `discardMilestone DB cleanup failed for ${milestoneId}: ${err.message}`);
150
150
  }
151
151
  }
152
+ else {
153
+ logWarning("engine", `discardMilestone DB cleanup skipped for ${milestoneId}: database unavailable`);
154
+ }
152
155
  invalidateAllCaches();
153
156
  return true;
154
157
  }
@@ -0,0 +1,28 @@
1
+ import { join } from "node:path";
2
+ import { gsdRoot } from "./paths.js";
3
+ import { readEvents } from "./workflow-events.js";
4
+ export function latestExplicitReopenAt(basePath, milestoneId) {
5
+ const root = gsdRoot(basePath);
6
+ const candidates = [
7
+ join(root, "event-log.jsonl"),
8
+ join(root, `event-log-${milestoneId}.jsonl.archived`),
9
+ ];
10
+ let latest = null;
11
+ for (const file of candidates) {
12
+ for (const event of readEvents(file)) {
13
+ const eventMilestoneId = event.params.milestoneId;
14
+ if (event.cmd !== "reopen-milestone" || eventMilestoneId !== milestoneId)
15
+ continue;
16
+ if (!latest || event.ts > latest)
17
+ latest = event.ts;
18
+ }
19
+ }
20
+ return latest;
21
+ }
22
+ export function isAfter(value, cutoff) {
23
+ if (!cutoff)
24
+ return true;
25
+ if (!value)
26
+ return true;
27
+ return Date.parse(value) > Date.parse(cutoff);
28
+ }
@@ -3,7 +3,7 @@
3
3
  import { truncateToWidth, visibleWidth, matchesKey, Key } from "@gsd/pi-tui";
4
4
  import { readNotifications, markAllRead, clearNotifications, onNotificationStoreChange, } from "./notification-store.js";
5
5
  import { formattedShortcutPair } from "./shortcut-defs.js";
6
- import { padRightVisible, renderFrame, renderKeyHints, rightAlign, wrapVisibleText, } from "./tui/render-kit.js";
6
+ import { padRightVisible, renderDialogFrame, renderKeyHints, rightAlign, wrapVisibleText, } from "./tui/render-kit.js";
7
7
  const FILTER_CYCLE = ["all", "error", "warning", "success", "info"];
8
8
  const OVERLAY_WIDTH = "58%";
9
9
  const OVERLAY_MIN_WIDTH = 68;
@@ -163,12 +163,16 @@ export class GSDNotificationOverlay {
163
163
  const terminalRows = process.stdout.rows || 32;
164
164
  const availableRows = Math.max(1, terminalRows - OVERLAY_MARGIN.top - OVERLAY_MARGIN.bottom);
165
165
  const overlayRows = Math.min(availableRows, Math.max(1, Math.floor((terminalRows * OVERLAY_MAX_HEIGHT_PERCENT) / 100)));
166
- const maxVisibleRows = Math.max(5, overlayRows - 2);
166
+ const maxVisibleRows = Math.max(5, overlayRows - 4);
167
167
  const visibleContentRows = Math.min(content.length, maxVisibleRows);
168
168
  const maxScroll = Math.max(0, content.length - visibleContentRows);
169
169
  this.scrollOffset = Math.min(this.scrollOffset, maxScroll);
170
170
  const visibleContent = content.slice(this.scrollOffset, this.scrollOffset + visibleContentRows);
171
- const lines = renderFrame(this.theme, visibleContent, width);
171
+ const footer = renderKeyHints(this.theme, ["↑/↓ scroll", "f filter", "c clear", `Esc/${formattedShortcutPair("notifications")} close`], Math.max(1, width - 4));
172
+ const lines = renderDialogFrame(this.theme, "Notifications", visibleContent, width, {
173
+ footer,
174
+ scroll: { offset: this.scrollOffset, visibleRows: visibleContentRows, totalRows: content.length },
175
+ });
172
176
  this.cachedWidth = width;
173
177
  this.cachedLines = lines;
174
178
  return lines;
@@ -211,8 +215,6 @@ export class GSDNotificationOverlay {
211
215
  };
212
216
  const blank = () => row("");
213
217
  const hr = () => row(th.fg("dim", "─".repeat(contentWidth)));
214
- // Header
215
- const title = th.fg("accent", th.bold("Notifications"));
216
218
  const filterLabel = this.filter === "all"
217
219
  ? th.fg("dim", "all")
218
220
  : th.fg(this.filter === "error" ? "error"
@@ -220,11 +222,8 @@ export class GSDNotificationOverlay {
220
222
  : this.filter === "success" ? "success"
221
223
  : "dim", this.filter);
222
224
  const count = `${this.filteredEntries.length} entries`;
223
- lines.push(row(rightAlign(`${title} ${th.fg("dim", "filter:")} ${filterLabel}`, th.fg("dim", count), contentWidth)));
225
+ lines.push(row(rightAlign(`${th.fg("dim", "filter:")} ${filterLabel}`, th.fg("dim", count), contentWidth)));
224
226
  lines.push(hr());
225
- // Controls
226
- const closeShortcut = formattedShortcutPair("notifications");
227
- lines.push(row(renderKeyHints(th, ["↑/↓ scroll", "f filter", "c clear", `Esc/${closeShortcut} close`], contentWidth)));
228
227
  lines.push(blank());
229
228
  // Entries
230
229
  const filtered = this.filteredEntries;