gsd-pi 2.10.12 → 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (504) hide show
  1. package/dist/bundled-extension-paths.d.ts +2 -0
  2. package/dist/bundled-extension-paths.js +10 -0
  3. package/dist/loader.js +45 -28
  4. package/dist/onboarding.js +100 -0
  5. package/dist/resource-loader.js +12 -3
  6. package/dist/resources/GSD-WORKFLOW.md +661 -0
  7. package/dist/resources/agents/researcher.md +29 -0
  8. package/dist/resources/agents/scout.md +56 -0
  9. package/dist/resources/agents/worker.md +31 -0
  10. package/dist/resources/extensions/ask-user-questions.ts +289 -0
  11. package/dist/resources/extensions/async-jobs/async-bash-tool.ts +211 -0
  12. package/dist/resources/extensions/async-jobs/await-tool.ts +101 -0
  13. package/dist/resources/extensions/async-jobs/cancel-job-tool.ts +34 -0
  14. package/dist/resources/extensions/async-jobs/index.ts +133 -0
  15. package/dist/resources/extensions/async-jobs/job-manager.ts +211 -0
  16. package/dist/resources/extensions/bg-shell/index.ts +3179 -0
  17. package/dist/resources/extensions/browser-tools/BROWSER-TOOLS-V2-PROPOSAL.md +1277 -0
  18. package/dist/resources/extensions/browser-tools/capture.ts +165 -0
  19. package/dist/resources/extensions/browser-tools/core.d.ts +205 -0
  20. package/dist/resources/extensions/browser-tools/core.js +1057 -0
  21. package/dist/resources/extensions/browser-tools/evaluate-helpers.ts +184 -0
  22. package/dist/resources/extensions/browser-tools/index.ts +51 -0
  23. package/dist/resources/extensions/browser-tools/lifecycle.ts +270 -0
  24. package/dist/resources/extensions/browser-tools/package.json +24 -0
  25. package/dist/resources/extensions/browser-tools/refs.ts +264 -0
  26. package/dist/resources/extensions/browser-tools/settle.ts +197 -0
  27. package/dist/resources/extensions/browser-tools/state.ts +408 -0
  28. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +652 -0
  29. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +614 -0
  30. package/dist/resources/extensions/browser-tools/tools/assertions.ts +342 -0
  31. package/dist/resources/extensions/browser-tools/tools/forms.ts +801 -0
  32. package/dist/resources/extensions/browser-tools/tools/inspection.ts +492 -0
  33. package/dist/resources/extensions/browser-tools/tools/intent.ts +614 -0
  34. package/dist/resources/extensions/browser-tools/tools/interaction.ts +865 -0
  35. package/dist/resources/extensions/browser-tools/tools/navigation.ts +232 -0
  36. package/dist/resources/extensions/browser-tools/tools/pages.ts +303 -0
  37. package/dist/resources/extensions/browser-tools/tools/refs.ts +541 -0
  38. package/dist/resources/extensions/browser-tools/tools/screenshot.ts +83 -0
  39. package/dist/resources/extensions/browser-tools/tools/session.ts +400 -0
  40. package/dist/resources/extensions/browser-tools/tools/wait.ts +247 -0
  41. package/dist/resources/extensions/browser-tools/utils.ts +660 -0
  42. package/dist/resources/extensions/context7/index.ts +428 -0
  43. package/dist/resources/extensions/context7/package.json +11 -0
  44. package/dist/resources/extensions/get-secrets-from-user.ts +625 -0
  45. package/dist/resources/extensions/google-search/index.ts +336 -0
  46. package/dist/resources/extensions/google-search/package.json +9 -0
  47. package/dist/resources/extensions/gsd/activity-log.ts +116 -0
  48. package/dist/resources/extensions/gsd/auto.ts +3732 -0
  49. package/dist/resources/extensions/gsd/commands.ts +544 -0
  50. package/dist/resources/extensions/gsd/crash-recovery.ts +105 -0
  51. package/dist/resources/extensions/gsd/dashboard-overlay.ts +564 -0
  52. package/dist/resources/extensions/gsd/dispatch-guard.ts +78 -0
  53. package/dist/resources/extensions/gsd/docs/preferences-reference.md +278 -0
  54. package/dist/resources/extensions/gsd/doctor.ts +766 -0
  55. package/dist/resources/extensions/gsd/exit-command.ts +18 -0
  56. package/dist/resources/extensions/gsd/files.ts +857 -0
  57. package/dist/resources/extensions/gsd/git-service.ts +904 -0
  58. package/dist/resources/extensions/gsd/gitignore.ts +200 -0
  59. package/dist/resources/extensions/gsd/guided-flow.ts +1054 -0
  60. package/dist/resources/extensions/gsd/index.ts +735 -0
  61. package/dist/resources/extensions/gsd/metrics.ts +374 -0
  62. package/dist/resources/extensions/gsd/migrate/command.ts +218 -0
  63. package/dist/resources/extensions/gsd/migrate/index.ts +42 -0
  64. package/dist/resources/extensions/gsd/migrate/parser.ts +323 -0
  65. package/dist/resources/extensions/gsd/migrate/parsers.ts +624 -0
  66. package/dist/resources/extensions/gsd/migrate/preview.ts +48 -0
  67. package/dist/resources/extensions/gsd/migrate/transformer.ts +346 -0
  68. package/dist/resources/extensions/gsd/migrate/types.ts +370 -0
  69. package/dist/resources/extensions/gsd/migrate/validator.ts +55 -0
  70. package/dist/resources/extensions/gsd/migrate/writer.ts +539 -0
  71. package/dist/resources/extensions/gsd/native-git-bridge.ts +181 -0
  72. package/dist/resources/extensions/gsd/native-parser-bridge.ts +135 -0
  73. package/dist/resources/extensions/gsd/observability-validator.ts +408 -0
  74. package/dist/resources/extensions/gsd/package.json +11 -0
  75. package/dist/resources/extensions/gsd/paths.ts +339 -0
  76. package/dist/resources/extensions/gsd/post-unit-hooks.ts +449 -0
  77. package/dist/resources/extensions/gsd/preferences.ts +988 -0
  78. package/dist/resources/extensions/gsd/prompt-loader.ts +92 -0
  79. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +25 -0
  80. package/dist/resources/extensions/gsd/prompts/complete-slice.md +27 -0
  81. package/dist/resources/extensions/gsd/prompts/discuss.md +237 -0
  82. package/dist/resources/extensions/gsd/prompts/doctor-heal.md +29 -0
  83. package/dist/resources/extensions/gsd/prompts/execute-task.md +63 -0
  84. package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +3 -0
  85. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +5 -0
  86. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +61 -0
  87. package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +3 -0
  88. package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +29 -0
  89. package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +3 -0
  90. package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +13 -0
  91. package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -0
  92. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +78 -0
  93. package/dist/resources/extensions/gsd/prompts/plan-slice.md +55 -0
  94. package/dist/resources/extensions/gsd/prompts/queue.md +100 -0
  95. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +48 -0
  96. package/dist/resources/extensions/gsd/prompts/replan-slice.md +39 -0
  97. package/dist/resources/extensions/gsd/prompts/research-milestone.md +37 -0
  98. package/dist/resources/extensions/gsd/prompts/research-slice.md +28 -0
  99. package/dist/resources/extensions/gsd/prompts/review-migration.md +66 -0
  100. package/dist/resources/extensions/gsd/prompts/run-uat.md +109 -0
  101. package/dist/resources/extensions/gsd/prompts/system.md +190 -0
  102. package/dist/resources/extensions/gsd/prompts/worktree-merge.md +123 -0
  103. package/dist/resources/extensions/gsd/roadmap-slices.ts +50 -0
  104. package/dist/resources/extensions/gsd/session-forensics.ts +500 -0
  105. package/dist/resources/extensions/gsd/skill-discovery.ts +137 -0
  106. package/dist/resources/extensions/gsd/state.ts +569 -0
  107. package/dist/resources/extensions/gsd/templates/context.md +76 -0
  108. package/dist/resources/extensions/gsd/templates/decisions.md +8 -0
  109. package/dist/resources/extensions/gsd/templates/milestone-summary.md +73 -0
  110. package/dist/resources/extensions/gsd/templates/plan.md +131 -0
  111. package/dist/resources/extensions/gsd/templates/preferences.md +23 -0
  112. package/dist/resources/extensions/gsd/templates/project.md +31 -0
  113. package/dist/resources/extensions/gsd/templates/reassessment.md +28 -0
  114. package/dist/resources/extensions/gsd/templates/requirements.md +81 -0
  115. package/dist/resources/extensions/gsd/templates/research.md +46 -0
  116. package/dist/resources/extensions/gsd/templates/roadmap.md +118 -0
  117. package/dist/resources/extensions/gsd/templates/secrets-manifest.md +22 -0
  118. package/dist/resources/extensions/gsd/templates/slice-context.md +58 -0
  119. package/dist/resources/extensions/gsd/templates/slice-summary.md +99 -0
  120. package/dist/resources/extensions/gsd/templates/state.md +19 -0
  121. package/dist/resources/extensions/gsd/templates/task-plan.md +52 -0
  122. package/dist/resources/extensions/gsd/templates/task-summary.md +57 -0
  123. package/dist/resources/extensions/gsd/templates/uat.md +54 -0
  124. package/dist/resources/extensions/gsd/tests/activity-log-prune.test.ts +297 -0
  125. package/dist/resources/extensions/gsd/tests/activity-log-save.test.ts +127 -0
  126. package/dist/resources/extensions/gsd/tests/auto-draft-pause.test.ts +109 -0
  127. package/dist/resources/extensions/gsd/tests/auto-preflight.test.ts +45 -0
  128. package/dist/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +196 -0
  129. package/dist/resources/extensions/gsd/tests/auto-supervisor.test.mjs +53 -0
  130. package/dist/resources/extensions/gsd/tests/collect-from-manifest.test.ts +469 -0
  131. package/dist/resources/extensions/gsd/tests/complete-milestone.test.ts +200 -0
  132. package/dist/resources/extensions/gsd/tests/cost-projection.test.ts +134 -0
  133. package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +312 -0
  134. package/dist/resources/extensions/gsd/tests/derive-state-draft.test.ts +299 -0
  135. package/dist/resources/extensions/gsd/tests/derive-state.test.ts +660 -0
  136. package/dist/resources/extensions/gsd/tests/discuss-prompt.test.ts +27 -0
  137. package/dist/resources/extensions/gsd/tests/dispatch-guard.test.ts +91 -0
  138. package/dist/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +170 -0
  139. package/dist/resources/extensions/gsd/tests/doctor.test.ts +594 -0
  140. package/dist/resources/extensions/gsd/tests/draft-promotion.test.ts +169 -0
  141. package/dist/resources/extensions/gsd/tests/exit-command.test.ts +50 -0
  142. package/dist/resources/extensions/gsd/tests/git-service.test.ts +1788 -0
  143. package/dist/resources/extensions/gsd/tests/idle-recovery.test.ts +577 -0
  144. package/dist/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +580 -0
  145. package/dist/resources/extensions/gsd/tests/manifest-status.test.ts +283 -0
  146. package/dist/resources/extensions/gsd/tests/metrics-io.test.ts +176 -0
  147. package/dist/resources/extensions/gsd/tests/metrics.test.ts +188 -0
  148. package/dist/resources/extensions/gsd/tests/migrate-command.test.ts +369 -0
  149. package/dist/resources/extensions/gsd/tests/migrate-parser.test.ts +757 -0
  150. package/dist/resources/extensions/gsd/tests/migrate-transformer.test.ts +635 -0
  151. package/dist/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +414 -0
  152. package/dist/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +297 -0
  153. package/dist/resources/extensions/gsd/tests/migrate-writer.test.ts +398 -0
  154. package/dist/resources/extensions/gsd/tests/must-have-parser.test.ts +291 -0
  155. package/dist/resources/extensions/gsd/tests/network-error-fallback.test.ts +54 -0
  156. package/dist/resources/extensions/gsd/tests/next-milestone-id.test.ts +66 -0
  157. package/dist/resources/extensions/gsd/tests/orphaned-branch.test.ts +353 -0
  158. package/dist/resources/extensions/gsd/tests/parsers.test.ts +1664 -0
  159. package/dist/resources/extensions/gsd/tests/plan-milestone.test.ts +133 -0
  160. package/dist/resources/extensions/gsd/tests/plan-quality-validator.test.ts +363 -0
  161. package/dist/resources/extensions/gsd/tests/post-unit-hooks.test.ts +297 -0
  162. package/dist/resources/extensions/gsd/tests/preferences-hooks.test.ts +226 -0
  163. package/dist/resources/extensions/gsd/tests/queue-draft-detection.test.ts +126 -0
  164. package/dist/resources/extensions/gsd/tests/reassess-prompt.test.ts +142 -0
  165. package/dist/resources/extensions/gsd/tests/regex-hardening.test.ts +281 -0
  166. package/dist/resources/extensions/gsd/tests/remote-questions.test.ts +155 -0
  167. package/dist/resources/extensions/gsd/tests/remote-status.test.ts +99 -0
  168. package/dist/resources/extensions/gsd/tests/replan-slice.test.ts +493 -0
  169. package/dist/resources/extensions/gsd/tests/requirements.test.ts +106 -0
  170. package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +35 -0
  171. package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +11 -0
  172. package/dist/resources/extensions/gsd/tests/roadmap-slices.test.ts +41 -0
  173. package/dist/resources/extensions/gsd/tests/run-uat.test.ts +316 -0
  174. package/dist/resources/extensions/gsd/tests/secure-env-collect.test.ts +185 -0
  175. package/dist/resources/extensions/gsd/tests/smart-entry-draft.test.ts +123 -0
  176. package/dist/resources/extensions/gsd/tests/test-helpers.ts +61 -0
  177. package/dist/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +219 -0
  178. package/dist/resources/extensions/gsd/tests/unit-runtime.test.ts +234 -0
  179. package/dist/resources/extensions/gsd/tests/workspace-index.test.ts +75 -0
  180. package/dist/resources/extensions/gsd/tests/worktree-integration.test.ts +234 -0
  181. package/dist/resources/extensions/gsd/tests/worktree-manager.test.ts +141 -0
  182. package/dist/resources/extensions/gsd/tests/worktree.test.ts +597 -0
  183. package/dist/resources/extensions/gsd/tests/write-gate.test.ts +122 -0
  184. package/dist/resources/extensions/gsd/types.ts +296 -0
  185. package/dist/resources/extensions/gsd/unit-runtime.ts +184 -0
  186. package/dist/resources/extensions/gsd/workspace-index.ts +189 -0
  187. package/dist/resources/extensions/gsd/worktree-command.ts +845 -0
  188. package/dist/resources/extensions/gsd/worktree-manager.ts +459 -0
  189. package/dist/resources/extensions/gsd/{worktree.js → worktree.ts} +92 -61
  190. package/dist/resources/extensions/mac-tools/index.ts +852 -0
  191. package/dist/resources/extensions/mac-tools/swift-cli/Package.swift +22 -0
  192. package/dist/resources/extensions/mac-tools/swift-cli/Sources/main.swift +1318 -0
  193. package/dist/resources/extensions/mcporter/index.ts +429 -0
  194. package/dist/resources/extensions/remote-questions/config.ts +81 -0
  195. package/dist/resources/extensions/remote-questions/discord-adapter.ts +128 -0
  196. package/dist/resources/extensions/remote-questions/format.ts +163 -0
  197. package/dist/resources/extensions/remote-questions/manager.ts +192 -0
  198. package/dist/resources/extensions/remote-questions/remote-command.ts +361 -0
  199. package/dist/resources/extensions/remote-questions/slack-adapter.ts +92 -0
  200. package/dist/resources/extensions/remote-questions/status.ts +31 -0
  201. package/dist/resources/extensions/remote-questions/store.ts +77 -0
  202. package/dist/resources/extensions/remote-questions/types.ts +75 -0
  203. package/dist/resources/extensions/search-the-web/cache.ts +78 -0
  204. package/dist/resources/extensions/search-the-web/command-search-provider.ts +99 -0
  205. package/dist/resources/extensions/search-the-web/format.ts +258 -0
  206. package/dist/resources/extensions/search-the-web/http.ts +238 -0
  207. package/dist/resources/extensions/search-the-web/index.ts +65 -0
  208. package/dist/resources/extensions/search-the-web/native-search.ts +165 -0
  209. package/dist/resources/extensions/search-the-web/provider.ts +136 -0
  210. package/dist/resources/extensions/search-the-web/tavily.ts +116 -0
  211. package/dist/resources/extensions/search-the-web/tool-fetch-page.ts +581 -0
  212. package/dist/resources/extensions/search-the-web/tool-llm-context.ts +620 -0
  213. package/dist/resources/extensions/search-the-web/tool-search.ts +635 -0
  214. package/dist/resources/extensions/search-the-web/url-utils.ts +91 -0
  215. package/dist/resources/extensions/shared/bundled-extension-paths.ts +11 -0
  216. package/dist/resources/extensions/shared/confirm-ui.ts +126 -0
  217. package/dist/resources/extensions/shared/interview-ui.ts +613 -0
  218. package/dist/resources/extensions/shared/next-action-ui.ts +197 -0
  219. package/dist/resources/extensions/shared/progress-widget.ts +282 -0
  220. package/dist/resources/extensions/shared/terminal.ts +23 -0
  221. package/dist/resources/extensions/shared/thinking-widget.ts +107 -0
  222. package/dist/resources/extensions/shared/ui.ts +400 -0
  223. package/dist/resources/extensions/shared/wizard-ui.ts +551 -0
  224. package/dist/resources/extensions/slash-commands/audit.ts +88 -0
  225. package/dist/resources/extensions/slash-commands/clear.ts +10 -0
  226. package/dist/resources/extensions/slash-commands/create-extension.ts +297 -0
  227. package/dist/resources/extensions/slash-commands/create-slash-command.ts +234 -0
  228. package/dist/resources/extensions/slash-commands/index.ts +12 -0
  229. package/dist/resources/extensions/subagent/agents.ts +126 -0
  230. package/dist/resources/extensions/subagent/index.ts +1072 -0
  231. package/dist/resources/extensions/subagent/isolation.ts +498 -0
  232. package/dist/resources/extensions/ttsr/index.ts +163 -0
  233. package/dist/resources/extensions/ttsr/rule-loader.ts +121 -0
  234. package/dist/resources/extensions/ttsr/ttsr-interrupt.md +6 -0
  235. package/dist/resources/extensions/ttsr/ttsr-manager.ts +430 -0
  236. package/dist/resources/extensions/universal-config/discovery.ts +78 -0
  237. package/dist/resources/extensions/universal-config/format.ts +160 -0
  238. package/dist/resources/extensions/universal-config/index.ts +118 -0
  239. package/dist/resources/extensions/universal-config/package.json +11 -0
  240. package/dist/resources/extensions/universal-config/scanners.ts +579 -0
  241. package/dist/resources/extensions/universal-config/tests/discovery.test.ts +111 -0
  242. package/dist/resources/extensions/universal-config/tests/format.test.ts +111 -0
  243. package/dist/resources/extensions/universal-config/tests/scanners.test.ts +438 -0
  244. package/dist/resources/extensions/universal-config/tools.ts +60 -0
  245. package/dist/resources/extensions/universal-config/types.ts +116 -0
  246. package/dist/resources/extensions/voice/index.ts +271 -0
  247. package/dist/resources/extensions/voice/speech-recognizer.py +504 -0
  248. package/dist/resources/extensions/voice/speech-recognizer.swift +154 -0
  249. package/dist/resources/skills/debug-like-expert/SKILL.md +231 -0
  250. package/dist/resources/skills/debug-like-expert/references/debugging-mindset.md +253 -0
  251. package/dist/resources/skills/debug-like-expert/references/hypothesis-testing.md +373 -0
  252. package/dist/resources/skills/debug-like-expert/references/investigation-techniques.md +337 -0
  253. package/dist/resources/skills/debug-like-expert/references/verification-patterns.md +425 -0
  254. package/dist/resources/skills/debug-like-expert/references/when-to-research.md +361 -0
  255. package/dist/resources/skills/frontend-design/SKILL.md +45 -0
  256. package/dist/resources/skills/github-workflows/SKILL.md +92 -0
  257. package/dist/resources/skills/github-workflows/references/gh/SKILL.md +255 -0
  258. package/dist/resources/skills/github-workflows/references/gh/references/issue-stories.md +204 -0
  259. package/dist/resources/skills/github-workflows/references/gh/references/labels.md +170 -0
  260. package/dist/resources/skills/github-workflows/references/gh/references/milestones.md +158 -0
  261. package/dist/resources/skills/github-workflows/references/gh/references/projects-v2.md +177 -0
  262. package/dist/resources/skills/github-workflows/references/gh/scripts/experiment_cleanup.py +191 -0
  263. package/dist/resources/skills/github-workflows/references/gh/scripts/github_project_setup.py +799 -0
  264. package/dist/resources/skills/github-workflows/references/gh/tests/__init__.py +0 -0
  265. package/dist/resources/skills/github-workflows/references/gh/tests/test_github_project_setup.py +608 -0
  266. package/dist/resources/skills/swiftui/SKILL.md +208 -0
  267. package/dist/resources/skills/swiftui/references/animations.md +921 -0
  268. package/dist/resources/skills/swiftui/references/architecture.md +1561 -0
  269. package/dist/resources/skills/swiftui/references/layout-system.md +1186 -0
  270. package/dist/resources/skills/swiftui/references/navigation.md +1492 -0
  271. package/dist/resources/skills/swiftui/references/networking-async.md +214 -0
  272. package/dist/resources/skills/swiftui/references/performance.md +1706 -0
  273. package/dist/resources/skills/swiftui/references/platform-integration.md +204 -0
  274. package/dist/resources/skills/swiftui/references/state-management.md +1443 -0
  275. package/dist/resources/skills/swiftui/references/swiftdata.md +297 -0
  276. package/dist/resources/skills/swiftui/references/testing-debugging.md +247 -0
  277. package/dist/resources/skills/swiftui/references/uikit-appkit-interop.md +218 -0
  278. package/dist/resources/skills/swiftui/workflows/add-feature.md +191 -0
  279. package/dist/resources/skills/swiftui/workflows/build-new-app.md +311 -0
  280. package/dist/resources/skills/swiftui/workflows/debug-swiftui.md +192 -0
  281. package/dist/resources/skills/swiftui/workflows/optimize-performance.md +197 -0
  282. package/dist/resources/skills/swiftui/workflows/ship-app.md +203 -0
  283. package/dist/resources/skills/swiftui/workflows/write-tests.md +235 -0
  284. package/dist/wizard.js +1 -0
  285. package/package.json +8 -6
  286. package/packages/native/dist/fd/index.d.ts +1 -0
  287. package/packages/native/dist/fd/index.js +1 -0
  288. package/packages/native/dist/grep/index.d.ts +2 -1
  289. package/packages/native/dist/grep/index.js +1 -0
  290. package/packages/native/src/__tests__/fd.test.mjs +35 -0
  291. package/packages/native/src/__tests__/grep.test.mjs +26 -9
  292. package/packages/native/src/fd/index.ts +1 -0
  293. package/packages/native/src/grep/index.ts +3 -2
  294. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  295. package/packages/pi-agent-core/dist/agent-loop.js +169 -55
  296. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  297. package/packages/pi-agent-core/dist/agent.d.ts +13 -1
  298. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  299. package/packages/pi-agent-core/dist/agent.js +16 -0
  300. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  301. package/packages/pi-agent-core/dist/types.d.ts +91 -1
  302. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  303. package/packages/pi-agent-core/dist/types.js.map +1 -1
  304. package/packages/pi-agent-core/src/agent-loop.ts +273 -63
  305. package/packages/pi-agent-core/src/agent.ts +24 -0
  306. package/packages/pi-agent-core/src/types.ts +98 -0
  307. package/packages/pi-ai/dist/env-api-keys.js +2 -0
  308. package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
  309. package/packages/pi-ai/dist/models.generated.d.ts +314 -0
  310. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  311. package/packages/pi-ai/dist/models.generated.js +236 -0
  312. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  313. package/packages/pi-ai/dist/providers/google-gemini-cli.d.ts.map +1 -1
  314. package/packages/pi-ai/dist/providers/google-gemini-cli.js +6 -0
  315. package/packages/pi-ai/dist/providers/google-gemini-cli.js.map +1 -1
  316. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  317. package/packages/pi-ai/dist/providers/openai-completions.js +2 -0
  318. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  319. package/packages/pi-ai/dist/types.d.ts +1 -1
  320. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  321. package/packages/pi-ai/dist/types.js.map +1 -1
  322. package/packages/pi-ai/src/env-api-keys.ts +2 -0
  323. package/packages/pi-ai/src/models.generated.ts +236 -0
  324. package/packages/pi-ai/src/providers/google-gemini-cli.ts +8 -0
  325. package/packages/pi-ai/src/providers/openai-completions.ts +3 -0
  326. package/packages/pi-ai/src/types.ts +2 -1
  327. package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
  328. package/packages/pi-coding-agent/dist/cli/args.js +1 -0
  329. package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
  330. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +26 -0
  331. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  332. package/packages/pi-coding-agent/dist/core/agent-session.js +137 -22
  333. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  334. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +20 -0
  335. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  336. package/packages/pi-coding-agent/dist/core/auth-storage.js +43 -0
  337. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  338. package/packages/pi-coding-agent/dist/core/fallback-resolver.d.ts +51 -0
  339. package/packages/pi-coding-agent/dist/core/fallback-resolver.d.ts.map +1 -0
  340. package/packages/pi-coding-agent/dist/core/fallback-resolver.js +119 -0
  341. package/packages/pi-coding-agent/dist/core/fallback-resolver.js.map +1 -0
  342. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.d.ts +2 -0
  343. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.d.ts.map +1 -0
  344. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js +183 -0
  345. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js.map +1 -0
  346. package/packages/pi-coding-agent/dist/core/index.d.ts +1 -0
  347. package/packages/pi-coding-agent/dist/core/index.d.ts.map +1 -1
  348. package/packages/pi-coding-agent/dist/core/index.js +1 -0
  349. package/packages/pi-coding-agent/dist/core/index.js.map +1 -1
  350. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  351. package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
  352. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  353. package/packages/pi-coding-agent/dist/core/session-manager.d.ts +12 -1
  354. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  355. package/packages/pi-coding-agent/dist/core/session-manager.js +37 -1
  356. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  357. package/packages/pi-coding-agent/dist/core/session-manager.test.d.ts +2 -0
  358. package/packages/pi-coding-agent/dist/core/session-manager.test.d.ts.map +1 -0
  359. package/packages/pi-coding-agent/dist/core/session-manager.test.js +61 -0
  360. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -0
  361. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +20 -0
  362. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  363. package/packages/pi-coding-agent/dist/core/settings-manager.js +47 -0
  364. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  365. package/packages/pi-coding-agent/dist/core/tools/find.d.ts.map +1 -1
  366. package/packages/pi-coding-agent/dist/core/tools/find.js +1 -0
  367. package/packages/pi-coding-agent/dist/core/tools/find.js.map +1 -1
  368. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  369. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +6 -15
  370. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  371. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts +18 -1
  372. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  373. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +46 -4
  374. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
  375. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  376. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +25 -2
  377. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  378. package/packages/pi-coding-agent/src/cli/args.ts +1 -0
  379. package/packages/pi-coding-agent/src/core/agent-session.ts +167 -22
  380. package/packages/pi-coding-agent/src/core/auth-storage.ts +45 -0
  381. package/packages/pi-coding-agent/src/core/fallback-resolver.test.ts +229 -0
  382. package/packages/pi-coding-agent/src/core/fallback-resolver.ts +165 -0
  383. package/packages/pi-coding-agent/src/core/index.ts +1 -0
  384. package/packages/pi-coding-agent/src/core/model-resolver.ts +1 -0
  385. package/packages/pi-coding-agent/src/core/session-manager.test.ts +65 -0
  386. package/packages/pi-coding-agent/src/core/session-manager.ts +51 -1
  387. package/packages/pi-coding-agent/src/core/settings-manager.ts +66 -0
  388. package/packages/pi-coding-agent/src/core/tools/find.ts +1 -0
  389. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +6 -16
  390. package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +49 -4
  391. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +28 -2
  392. package/packages/pi-tui/dist/autocomplete.d.ts.map +1 -1
  393. package/packages/pi-tui/dist/autocomplete.js +3 -4
  394. package/packages/pi-tui/dist/autocomplete.js.map +1 -1
  395. package/packages/pi-tui/dist/components/editor.d.ts +7 -0
  396. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  397. package/packages/pi-tui/dist/components/editor.js +57 -50
  398. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  399. package/packages/pi-tui/src/autocomplete.ts +3 -5
  400. package/packages/pi-tui/src/components/editor.ts +70 -49
  401. package/pkg/dist/core/export-html/ansi-to-html.d.ts +22 -0
  402. package/pkg/dist/core/export-html/ansi-to-html.d.ts.map +1 -0
  403. package/pkg/dist/core/export-html/ansi-to-html.js +249 -0
  404. package/pkg/dist/core/export-html/ansi-to-html.js.map +1 -0
  405. package/pkg/dist/core/export-html/index.d.ts +37 -0
  406. package/pkg/dist/core/export-html/index.d.ts.map +1 -0
  407. package/pkg/dist/core/export-html/index.js +223 -0
  408. package/pkg/dist/core/export-html/index.js.map +1 -0
  409. package/pkg/dist/core/export-html/template.css +971 -0
  410. package/pkg/dist/core/export-html/template.html +54 -0
  411. package/pkg/dist/core/export-html/template.js +1583 -0
  412. package/pkg/dist/core/export-html/tool-renderer.d.ts +38 -0
  413. package/pkg/dist/core/export-html/tool-renderer.d.ts.map +1 -0
  414. package/pkg/dist/core/export-html/tool-renderer.js +70 -0
  415. package/pkg/dist/core/export-html/tool-renderer.js.map +1 -0
  416. package/pkg/dist/core/export-html/vendor/highlight.min.js +1213 -0
  417. package/pkg/dist/core/export-html/vendor/marked.min.js +6 -0
  418. package/scripts/postinstall.js +30 -17
  419. package/src/resources/extensions/bg-shell/index.ts +51 -7
  420. package/src/resources/extensions/gsd/activity-log.ts +61 -14
  421. package/src/resources/extensions/gsd/auto.ts +288 -19
  422. package/src/resources/extensions/gsd/commands.ts +9 -3
  423. package/src/resources/extensions/gsd/dashboard-overlay.ts +55 -12
  424. package/src/resources/extensions/gsd/docs/preferences-reference.md +25 -0
  425. package/src/resources/extensions/gsd/doctor.ts +60 -3
  426. package/src/resources/extensions/gsd/files.ts +45 -1
  427. package/src/resources/extensions/gsd/git-service.ts +46 -41
  428. package/src/resources/extensions/gsd/guided-flow.ts +81 -9
  429. package/src/resources/extensions/gsd/index.ts +62 -1
  430. package/src/resources/extensions/gsd/native-git-bridge.ts +181 -0
  431. package/src/resources/extensions/gsd/paths.ts +35 -4
  432. package/src/resources/extensions/gsd/post-unit-hooks.ts +449 -0
  433. package/src/resources/extensions/gsd/preferences.ts +232 -1
  434. package/src/resources/extensions/gsd/prompt-loader.ts +45 -3
  435. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  436. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -3
  437. package/src/resources/extensions/gsd/prompts/discuss.md +10 -8
  438. package/src/resources/extensions/gsd/prompts/execute-task.md +4 -2
  439. package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +3 -1
  440. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -1
  441. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
  442. package/src/resources/extensions/gsd/prompts/guided-execute-task.md +3 -1
  443. package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +4 -2
  444. package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +3 -1
  445. package/src/resources/extensions/gsd/prompts/guided-research-slice.md +3 -1
  446. package/src/resources/extensions/gsd/prompts/plan-milestone.md +9 -12
  447. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -3
  448. package/src/resources/extensions/gsd/prompts/queue.md +3 -1
  449. package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  450. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  451. package/src/resources/extensions/gsd/state.ts +130 -18
  452. package/src/resources/extensions/gsd/templates/context.md +1 -1
  453. package/src/resources/extensions/gsd/templates/state.md +3 -3
  454. package/src/resources/extensions/gsd/tests/activity-log-save.test.ts +127 -0
  455. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +5 -1
  456. package/src/resources/extensions/gsd/tests/doctor.test.ts +115 -1
  457. package/src/resources/extensions/gsd/tests/draft-promotion.test.ts +6 -2
  458. package/src/resources/extensions/gsd/tests/git-service.test.ts +35 -35
  459. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +8 -8
  460. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +4 -1
  461. package/src/resources/extensions/gsd/tests/network-error-fallback.test.ts +54 -0
  462. package/src/resources/extensions/gsd/tests/orphaned-branch.test.ts +5 -5
  463. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +297 -0
  464. package/src/resources/extensions/gsd/tests/preferences-hooks.test.ts +226 -0
  465. package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +12 -0
  466. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +6 -0
  467. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +4 -4
  468. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +4 -4
  469. package/src/resources/extensions/gsd/tests/worktree.test.ts +19 -19
  470. package/src/resources/extensions/gsd/types.ts +109 -0
  471. package/src/resources/extensions/gsd/worktree-manager.ts +72 -13
  472. package/src/resources/extensions/gsd/worktree.ts +5 -4
  473. package/src/resources/extensions/search-the-web/command-search-provider.ts +8 -4
  474. package/src/resources/extensions/search-the-web/native-search.ts +32 -23
  475. package/src/resources/extensions/search-the-web/provider.ts +19 -2
  476. package/src/resources/extensions/search-the-web/tool-fetch-page.ts +62 -0
  477. package/src/resources/extensions/search-the-web/tool-llm-context.ts +62 -3
  478. package/src/resources/extensions/search-the-web/tool-search.ts +62 -3
  479. package/src/resources/extensions/shared/bundled-extension-paths.ts +11 -0
  480. package/src/resources/extensions/subagent/index.ts +2 -1
  481. package/src/resources/skills/github-workflows/SKILL.md +5 -0
  482. package/dist/resources/extensions/gsd/git-service.d.ts +0 -225
  483. package/dist/resources/extensions/gsd/git-service.js +0 -756
  484. package/dist/resources/extensions/gsd/preferences.d.ts +0 -122
  485. package/dist/resources/extensions/gsd/preferences.js +0 -616
  486. package/dist/resources/extensions/gsd/worktree.d.ts +0 -106
  487. package/dist/resources/extensions/remote-questions/config.d.ts +0 -14
  488. package/dist/resources/extensions/remote-questions/config.js +0 -68
  489. package/dist/resources/extensions/remote-questions/discord-adapter.d.ts +0 -17
  490. package/dist/resources/extensions/remote-questions/discord-adapter.js +0 -118
  491. package/dist/resources/extensions/remote-questions/format.d.ts +0 -38
  492. package/dist/resources/extensions/remote-questions/format.js +0 -119
  493. package/dist/resources/extensions/remote-questions/manager.d.ts +0 -23
  494. package/dist/resources/extensions/remote-questions/manager.js +0 -161
  495. package/dist/resources/extensions/remote-questions/remote-command.d.ts +0 -6
  496. package/dist/resources/extensions/remote-questions/remote-command.js +0 -358
  497. package/dist/resources/extensions/remote-questions/slack-adapter.d.ts +0 -15
  498. package/dist/resources/extensions/remote-questions/slack-adapter.js +0 -82
  499. package/dist/resources/extensions/remote-questions/status.d.ts +0 -9
  500. package/dist/resources/extensions/remote-questions/status.js +0 -25
  501. package/dist/resources/extensions/remote-questions/store.d.ts +0 -11
  502. package/dist/resources/extensions/remote-questions/store.js +0 -66
  503. package/dist/resources/extensions/remote-questions/types.d.ts +0 -68
  504. package/dist/resources/extensions/remote-questions/types.js +0 -4
@@ -0,0 +1,1054 @@
1
+ /**
2
+ * GSD Guided Flow — Smart Entry Wizard
3
+ *
4
+ * One function: showSmartEntry(). Reads state from disk, shows a contextual
5
+ * wizard via showNextAction(), and dispatches through GSD-WORKFLOW.md.
6
+ * No execution state, no hooks, no tools — the LLM does the rest.
7
+ */
8
+
9
+ import type { ExtensionAPI, ExtensionContext, ExtensionCommandContext } from "@gsd/pi-coding-agent";
10
+ import { showNextAction } from "../shared/next-action-ui.js";
11
+ import { loadFile, parseRoadmap } from "./files.js";
12
+ import { loadPrompt, inlineTemplate } from "./prompt-loader.js";
13
+ import { deriveState } from "./state.js";
14
+ import { startAuto } from "./auto.js";
15
+ import { readCrashLock, clearLock, formatCrashInfo } from "./crash-recovery.js";
16
+ import { listUnitRuntimeRecords, clearUnitRuntimeRecord } from "./unit-runtime.js";
17
+ import { resolveExpectedArtifactPath } from "./auto.js";
18
+ import {
19
+ gsdRoot, milestonesDir, resolveMilestoneFile, resolveMilestonePath,
20
+ resolveSliceFile, resolveSlicePath, resolveGsdRootFile, relGsdRootFile,
21
+ relMilestoneFile, relSliceFile, relSlicePath,
22
+ } from "./paths.js";
23
+ import { randomInt } from "node:crypto";
24
+ import { join } from "node:path";
25
+ import { readFileSync, existsSync, mkdirSync, readdirSync, rmSync, unlinkSync } from "node:fs";
26
+ import { execSync, execFileSync } from "node:child_process";
27
+ import { ensureGitignore, ensurePreferences, untrackRuntimeFiles } from "./gitignore.js";
28
+ import { loadEffectiveGSDPreferences } from "./preferences.js";
29
+ import { showConfirm } from "../shared/confirm-ui.js";
30
+
31
+ // ─── Auto-start after discuss ─────────────────────────────────────────────────
32
+
33
+ /** Stashed context + flag for auto-starting after discuss phase completes */
34
+ let pendingAutoStart: {
35
+ ctx: ExtensionCommandContext;
36
+ pi: ExtensionAPI;
37
+ basePath: string;
38
+ milestoneId: string; // the milestone being discussed
39
+ step?: boolean; // preserve step mode through discuss → auto transition
40
+ } | null = null;
41
+
42
+ /** Returns the milestoneId being discussed, or null if no discussion is active */
43
+ export function getDiscussionMilestoneId(): string | null {
44
+ return pendingAutoStart?.milestoneId ?? null;
45
+ }
46
+
47
+ /** Called from agent_end to check if auto-mode should start after discuss */
48
+ export function checkAutoStartAfterDiscuss(): boolean {
49
+ if (!pendingAutoStart) return false;
50
+
51
+ const { ctx, pi, basePath, milestoneId, step } = pendingAutoStart;
52
+
53
+ // Don't fire until the discuss phase has actually produced a context file
54
+ // for the milestone being discussed. agent_end fires after every LLM turn,
55
+ // including the initial "What do you want to build?" response — we need to
56
+ // wait for the full conversation to complete and the LLM to write CONTEXT.md.
57
+ const contextFile = resolveMilestoneFile(basePath, milestoneId, "CONTEXT");
58
+ if (!contextFile) return false; // no context yet — keep waiting
59
+
60
+ // Draft promotion cleanup: if a CONTEXT-DRAFT.md exists alongside the new
61
+ // CONTEXT.md, delete the draft — it's been consumed by the discussion.
62
+ try {
63
+ const draftFile = resolveMilestoneFile(basePath, milestoneId, "CONTEXT-DRAFT");
64
+ if (draftFile) unlinkSync(draftFile);
65
+ } catch { /* non-fatal — stale draft doesn't break anything, CONTEXT.md wins */ }
66
+
67
+ pendingAutoStart = null;
68
+ startAuto(ctx, pi, basePath, false, { step }).catch(() => {});
69
+ return true;
70
+ }
71
+
72
+ // ─── Types ────────────────────────────────────────────────────────────────────
73
+
74
+ type UIContext = ExtensionContext;
75
+
76
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
77
+
78
+ /**
79
+ * Read GSD-WORKFLOW.md and dispatch it to the LLM with a contextual note.
80
+ * This is the only way the wizard triggers work — everything else is the LLM's job.
81
+ */
82
+ function dispatchWorkflow(pi: ExtensionAPI, note: string, customType = "gsd-run"): void {
83
+ const workflowPath = process.env.GSD_WORKFLOW_PATH ?? join(process.env.HOME ?? "~", ".pi", "GSD-WORKFLOW.md");
84
+ const workflow = readFileSync(workflowPath, "utf-8");
85
+
86
+ pi.sendMessage(
87
+ {
88
+ customType,
89
+ content: `Read the following GSD workflow protocol and execute exactly.\n\n${workflow}\n\n## Your Task\n\n${note}`,
90
+ display: false,
91
+ },
92
+ { triggerTurn: true },
93
+ );
94
+ }
95
+
96
+ /**
97
+ * Build the discuss-and-plan prompt for a new milestone.
98
+ * Used by all three "new milestone" paths (first ever, no active, all complete).
99
+ */
100
+ function buildDiscussPrompt(nextId: string, preamble: string, _basePath: string): string {
101
+ const milestoneRel = `.gsd/milestones/${nextId}`;
102
+ const inlinedTemplates = [
103
+ inlineTemplate("project", "Project"),
104
+ inlineTemplate("requirements", "Requirements"),
105
+ inlineTemplate("context", "Context"),
106
+ inlineTemplate("roadmap", "Roadmap"),
107
+ inlineTemplate("decisions", "Decisions"),
108
+ ].join("\n\n---\n\n");
109
+ return loadPrompt("discuss", {
110
+ milestoneId: nextId,
111
+ preamble,
112
+ contextPath: `${milestoneRel}/${nextId}-CONTEXT.md`,
113
+ roadmapPath: `${milestoneRel}/${nextId}-ROADMAP.md`,
114
+ inlinedTemplates,
115
+ });
116
+ }
117
+
118
+ export function findMilestoneIds(basePath: string): string[] {
119
+ const dir = milestonesDir(basePath);
120
+ try {
121
+ return readdirSync(dir, { withFileTypes: true })
122
+ .filter((d) => d.isDirectory())
123
+ .map((d) => {
124
+ const match = d.name.match(/^(M\d+(?:-[a-z0-9]{6})?)/);
125
+ return match ? match[1] : d.name;
126
+ })
127
+ .sort(milestoneIdSort);
128
+ } catch {
129
+ return [];
130
+ }
131
+ }
132
+
133
+ // ─── Milestone ID primitives ────────────────────────────────────────────────
134
+
135
+ /** Matches both classic `M001` and unique `M001-abc123` formats (anchored). */
136
+ export const MILESTONE_ID_RE = /^M\d{3}(?:-[a-z0-9]{6})?$/;
137
+
138
+ /** Extract the trailing sequential number from a milestone ID. Returns 0 for non-matches. */
139
+ export function extractMilestoneSeq(id: string): number {
140
+ const m = id.match(/^M(\d{3})(?:-[a-z0-9]{6})?$/);
141
+ return m ? parseInt(m[1], 10) : 0;
142
+ }
143
+
144
+ /** Structured parse of a milestone ID into optional suffix and sequence number. */
145
+ export function parseMilestoneId(id: string): { suffix?: string; num: number } {
146
+ const m = id.match(/^M(\d{3})(?:-([a-z0-9]{6}))?$/);
147
+ if (!m) return { num: 0 };
148
+ return {
149
+ ...(m[2] ? { suffix: m[2] } : {}),
150
+ num: parseInt(m[1], 10),
151
+ };
152
+ }
153
+
154
+ /** Comparator for sorting milestone IDs by sequential number. */
155
+ export function milestoneIdSort(a: string, b: string): number {
156
+ return extractMilestoneSeq(a) - extractMilestoneSeq(b);
157
+ }
158
+
159
+ /** Generate a 6-char lowercase `[a-z0-9]` suffix using crypto.randomInt(). */
160
+ export function generateMilestoneSuffix(): string {
161
+ const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
162
+ let result = "";
163
+ for (let i = 0; i < 6; i++) {
164
+ result += chars[randomInt(36)];
165
+ }
166
+ return result;
167
+ }
168
+
169
+ /** Return the highest numeric suffix among milestone IDs (0 when the list is empty or has no numeric IDs). */
170
+ export function maxMilestoneNum(milestoneIds: string[]): number {
171
+ return milestoneIds.reduce((max, id) => {
172
+ const num = extractMilestoneSeq(id);
173
+ return num > max ? num : max;
174
+ }, 0);
175
+ }
176
+
177
+ /** Derive the next milestone ID from existing IDs using max-based approach to avoid collisions after deletions. */
178
+ export function nextMilestoneId(milestoneIds: string[], uniqueEnabled?: boolean): string {
179
+ const seq = String(maxMilestoneNum(milestoneIds) + 1).padStart(3, "0");
180
+ if (uniqueEnabled) {
181
+ return `M${seq}-${generateMilestoneSuffix()}`;
182
+ }
183
+ return `M${seq}`;
184
+ }
185
+
186
+ // ─── Queue ─────────────────────────────────────────────────────────────────────
187
+
188
+ /**
189
+ * Queue future milestones via conversational intake.
190
+ *
191
+ * Safe to run while auto-mode is executing — only writes to future milestone
192
+ * directories (which auto-mode won't touch until it reaches them) and appends
193
+ * to project.md / queue.md.
194
+ *
195
+ * The flow:
196
+ * 1. Build context about all existing milestones (complete, active, pending)
197
+ * 2. Dispatch the queue prompt — LLM discusses with the user, assesses scope
198
+ * 3. LLM writes CONTEXT.md files for new milestones (no roadmaps — JIT)
199
+ * 4. Auto-mode picks them up naturally when it advances past current work
200
+ *
201
+ * Root durable artifacts use uppercase names like PROJECT.md and QUEUE.md.
202
+ */
203
+ export async function showQueue(
204
+ ctx: ExtensionCommandContext,
205
+ pi: ExtensionAPI,
206
+ basePath: string,
207
+ ): Promise<void> {
208
+ // ── Ensure .gsd/ exists ─────────────────────────────────────────────
209
+ const gsd = gsdRoot(basePath);
210
+ if (!existsSync(gsd)) {
211
+ ctx.ui.notify("No GSD project found. Run /gsd to start one first.", "warning");
212
+ return;
213
+ }
214
+
215
+ const state = await deriveState(basePath);
216
+ const milestoneIds = findMilestoneIds(basePath);
217
+
218
+ if (milestoneIds.length === 0) {
219
+ ctx.ui.notify("No milestones exist yet. Run /gsd to create the first one.", "warning");
220
+ return;
221
+ }
222
+
223
+ // ── Build existing milestones context for the prompt ────────────────
224
+ const existingContext = await buildExistingMilestonesContext(basePath, milestoneIds, state);
225
+
226
+ // ── Determine next milestone ID ─────────────────────────────────────
227
+ const uniqueEnabled = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
228
+ const nextId = nextMilestoneId(milestoneIds, uniqueEnabled);
229
+ const nextIdPlus1 = nextMilestoneId([...milestoneIds, nextId], uniqueEnabled);
230
+
231
+ // ── Build preamble ──────────────────────────────────────────────────
232
+ const activePart = state.activeMilestone
233
+ ? `Currently executing: ${state.activeMilestone.id} — ${state.activeMilestone.title} (phase: ${state.phase}).`
234
+ : "No milestone currently active.";
235
+
236
+ const pendingCount = state.registry.filter(m => m.status === "pending").length;
237
+ const completeCount = state.registry.filter(m => m.status === "complete").length;
238
+
239
+ const preamble = [
240
+ `Queuing new work onto an existing GSD project.`,
241
+ activePart,
242
+ `${completeCount} milestone(s) complete, ${pendingCount} pending.`,
243
+ `Next available milestone ID: ${nextId}.`,
244
+ ].join(" ");
245
+
246
+ // ── Dispatch the queue prompt ───────────────────────────────────────
247
+ const queueInlinedTemplates = inlineTemplate("context", "Context");
248
+ const prompt = loadPrompt("queue", {
249
+ preamble,
250
+ nextId,
251
+ nextIdPlus1,
252
+ existingMilestonesContext: existingContext,
253
+ inlinedTemplates: queueInlinedTemplates,
254
+ });
255
+
256
+ pi.sendMessage(
257
+ {
258
+ customType: "gsd-queue",
259
+ content: prompt,
260
+ display: false,
261
+ },
262
+ { triggerTurn: true },
263
+ );
264
+ }
265
+
266
+ /**
267
+ * Build a context block describing all existing milestones for the queue prompt.
268
+ * Gives the LLM enough information to dedup, sequence, and dependency-check.
269
+ */
270
+ export async function buildExistingMilestonesContext(
271
+ basePath: string,
272
+ milestoneIds: string[],
273
+ state: import("./types.js").GSDState,
274
+ ): Promise<string> {
275
+ const sections: string[] = [];
276
+
277
+ // Include PROJECT.md if it exists — it has the milestone sequence and project description
278
+ const projectPath = resolveGsdRootFile(basePath, "PROJECT");
279
+ if (existsSync(projectPath)) {
280
+ const projectContent = await loadFile(projectPath);
281
+ if (projectContent) {
282
+ sections.push(`### Project Overview\nSource: \`${relGsdRootFile("PROJECT")}\`\n\n${projectContent.trim()}`);
283
+ }
284
+ }
285
+
286
+ // Include DECISIONS.md if it exists — architectural decisions inform new milestone scoping
287
+ const decisionsPath = resolveGsdRootFile(basePath, "DECISIONS");
288
+ if (existsSync(decisionsPath)) {
289
+ const decisionsContent = await loadFile(decisionsPath);
290
+ if (decisionsContent) {
291
+ sections.push(`### Decisions Register\nSource: \`${relGsdRootFile("DECISIONS")}\`\n\n${decisionsContent.trim()}`);
292
+ }
293
+ }
294
+
295
+ // For each milestone, include context and status
296
+ for (const mid of milestoneIds) {
297
+ const registryEntry = state.registry.find(m => m.id === mid);
298
+ const status = registryEntry?.status ?? "unknown";
299
+ const title = registryEntry?.title ?? mid;
300
+
301
+ const parts: string[] = [];
302
+ parts.push(`### ${mid}: ${title}\n**Status:** ${status}`);
303
+
304
+ // Include context file — this is the primary content for understanding scope
305
+ const contextFile = resolveMilestoneFile(basePath, mid, "CONTEXT");
306
+ if (contextFile) {
307
+ const content = await loadFile(contextFile);
308
+ if (content) {
309
+ parts.push(`\n**Context:**\n${content.trim()}`);
310
+ }
311
+ } else {
312
+ // No full CONTEXT.md — check for CONTEXT-DRAFT.md (draft seed from prior discussion)
313
+ const draftFile = resolveMilestoneFile(basePath, mid, "CONTEXT-DRAFT");
314
+ if (draftFile) {
315
+ const draftContent = await loadFile(draftFile);
316
+ if (draftContent) {
317
+ parts.push(`\n**Draft context available:**\n${draftContent.trim()}`);
318
+ }
319
+ }
320
+ }
321
+
322
+ // For completed milestones, include the summary if it exists
323
+ if (status === "complete") {
324
+ const summaryFile = resolveMilestoneFile(basePath, mid, "SUMMARY");
325
+ if (summaryFile) {
326
+ const content = await loadFile(summaryFile);
327
+ if (content) {
328
+ parts.push(`\n**Summary:**\n${content.trim()}`);
329
+ }
330
+ }
331
+ }
332
+
333
+ // For active/pending milestones, include the roadmap if it exists
334
+ // (shows what's planned but not yet built)
335
+ if (status === "active" || status === "pending") {
336
+ const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
337
+ if (roadmapFile) {
338
+ const content = await loadFile(roadmapFile);
339
+ if (content) {
340
+ parts.push(`\n**Roadmap:**\n${content.trim()}`);
341
+ }
342
+ }
343
+ }
344
+
345
+ sections.push(parts.join("\n"));
346
+ }
347
+
348
+ // Include queue log if it exists — shows what's been queued before
349
+ const queuePath = resolveGsdRootFile(basePath, "QUEUE");
350
+ if (existsSync(queuePath)) {
351
+ const queueContent = await loadFile(queuePath);
352
+ if (queueContent) {
353
+ sections.push(`### Previous Queue Entries\nSource: \`${relGsdRootFile("QUEUE")}\`\n\n${queueContent.trim()}`);
354
+ }
355
+ }
356
+
357
+ return sections.join("\n\n---\n\n");
358
+ }
359
+
360
+ // ─── Discuss Flow ─────────────────────────────────────────────────────────────
361
+
362
+ /**
363
+ * Build a rich inlined-context prompt for discussing a specific slice.
364
+ * Preloads roadmap, milestone context, research, decisions, and completed
365
+ * slice summaries so the agent can ask grounded UX/behaviour questions
366
+ * without wasting a turn reading files.
367
+ */
368
+ async function buildDiscussSlicePrompt(
369
+ mid: string,
370
+ sid: string,
371
+ sTitle: string,
372
+ base: string,
373
+ ): Promise<string> {
374
+ const inlined: string[] = [];
375
+
376
+ // Roadmap — always included so the agent sees surrounding slices
377
+ const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
378
+ const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
379
+ const roadmapContent = roadmapPath ? await loadFile(roadmapPath) : null;
380
+ if (roadmapContent) {
381
+ inlined.push(`### Milestone Roadmap\nSource: \`${roadmapRel}\`\n\n${roadmapContent.trim()}`);
382
+ }
383
+
384
+ // Milestone context — understanding the full milestone intent
385
+ const contextPath = resolveMilestoneFile(base, mid, "CONTEXT");
386
+ const contextRel = relMilestoneFile(base, mid, "CONTEXT");
387
+ const contextContent = contextPath ? await loadFile(contextPath) : null;
388
+ if (contextContent) {
389
+ inlined.push(`### Milestone Context\nSource: \`${contextRel}\`\n\n${contextContent.trim()}`);
390
+ }
391
+
392
+ // Milestone research — technical grounding
393
+ const researchPath = resolveMilestoneFile(base, mid, "RESEARCH");
394
+ const researchRel = relMilestoneFile(base, mid, "RESEARCH");
395
+ const researchContent = researchPath ? await loadFile(researchPath) : null;
396
+ if (researchContent) {
397
+ inlined.push(`### Milestone Research\nSource: \`${researchRel}\`\n\n${researchContent.trim()}`);
398
+ }
399
+
400
+ // Decisions — architectural context that constrains this slice
401
+ const decisionsPath = resolveGsdRootFile(base, "DECISIONS");
402
+ if (existsSync(decisionsPath)) {
403
+ const decisionsContent = await loadFile(decisionsPath);
404
+ if (decisionsContent) {
405
+ inlined.push(`### Decisions Register\nSource: \`${relGsdRootFile("DECISIONS")}\`\n\n${decisionsContent.trim()}`);
406
+ }
407
+ }
408
+
409
+ // Completed slice summaries — what was already built that this slice builds on
410
+ if (roadmapContent) {
411
+ const roadmap = parseRoadmap(roadmapContent);
412
+ for (const s of roadmap.slices) {
413
+ if (!s.done || s.id === sid) continue;
414
+ const summaryPath = resolveSliceFile(base, mid, s.id, "SUMMARY");
415
+ const summaryRel = relSliceFile(base, mid, s.id, "SUMMARY");
416
+ const summaryContent = summaryPath ? await loadFile(summaryPath) : null;
417
+ if (summaryContent) {
418
+ inlined.push(`### ${s.id} Summary (completed)\nSource: \`${summaryRel}\`\n\n${summaryContent.trim()}`);
419
+ }
420
+ }
421
+ }
422
+
423
+ const inlinedContext = inlined.length > 0
424
+ ? `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`
425
+ : `## Inlined Context\n\n_(no context files found yet — go in blind and ask broad questions)_`;
426
+
427
+ const sliceDirPath = `.gsd/milestones/${mid}/slices/${sid}`;
428
+ const sliceContextPath = `${sliceDirPath}/${sid}-CONTEXT.md`;
429
+
430
+ const inlinedTemplates = inlineTemplate("slice-context", "Slice Context");
431
+ return loadPrompt("guided-discuss-slice", {
432
+ milestoneId: mid,
433
+ sliceId: sid,
434
+ sliceTitle: sTitle,
435
+ inlinedContext,
436
+ sliceDirPath,
437
+ contextPath: sliceContextPath,
438
+ projectRoot: base,
439
+ inlinedTemplates,
440
+ });
441
+ }
442
+
443
+ /**
444
+ * /gsd discuss — show a picker of non-done slices and run a slice interview.
445
+ * Loops back to the picker after each discussion so the user can chain
446
+ * multiple slice interviews in one session.
447
+ */
448
+ export async function showDiscuss(
449
+ ctx: ExtensionCommandContext,
450
+ pi: ExtensionAPI,
451
+ basePath: string,
452
+ ): Promise<void> {
453
+ // Guard: no .gsd/ project
454
+ if (!existsSync(join(basePath, ".gsd"))) {
455
+ ctx.ui.notify("No GSD project found. Run /gsd to start one first.", "warning");
456
+ return;
457
+ }
458
+
459
+ const state = await deriveState(basePath);
460
+
461
+ // Guard: no active milestone
462
+ if (!state.activeMilestone) {
463
+ ctx.ui.notify("No active milestone. Run /gsd to create one first.", "warning");
464
+ return;
465
+ }
466
+
467
+ const mid = state.activeMilestone.id;
468
+ const milestoneTitle = state.activeMilestone.title;
469
+
470
+ // Guard: no roadmap yet
471
+ const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
472
+ const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null;
473
+ if (!roadmapContent) {
474
+ ctx.ui.notify("No roadmap yet for this milestone. Run /gsd to plan first.", "warning");
475
+ return;
476
+ }
477
+
478
+ const roadmap = parseRoadmap(roadmapContent);
479
+ const pendingSlices = roadmap.slices.filter(s => !s.done);
480
+
481
+ if (pendingSlices.length === 0) {
482
+ ctx.ui.notify("All slices are complete — nothing to discuss.", "info");
483
+ return;
484
+ }
485
+
486
+ // Loop: show picker, dispatch discuss, repeat until "not_yet"
487
+ while (true) {
488
+ const actions = pendingSlices.map((s, i) => ({
489
+ id: s.id,
490
+ label: `${s.id}: ${s.title}`,
491
+ description: state.activeSlice?.id === s.id ? "active slice" : "upcoming",
492
+ recommended: i === 0,
493
+ }));
494
+
495
+ const choice = await showNextAction(ctx as any, {
496
+ title: "GSD — Discuss a slice",
497
+ summary: [
498
+ `${mid}: ${milestoneTitle}`,
499
+ "Pick a slice to interview. Context file will be written when done.",
500
+ ],
501
+ actions,
502
+ notYetMessage: "Run /gsd discuss when ready.",
503
+ });
504
+
505
+ if (choice === "not_yet") return;
506
+
507
+ const chosen = pendingSlices.find(s => s.id === choice);
508
+ if (!chosen) return;
509
+
510
+ const prompt = await buildDiscussSlicePrompt(mid, chosen.id, chosen.title, basePath);
511
+ dispatchWorkflow(pi, prompt, "gsd-discuss");
512
+
513
+ // Wait for the discuss session to finish, then loop back to the picker
514
+ await ctx.waitForIdle();
515
+ }
516
+ }
517
+
518
+ // ─── Smart Entry Point ────────────────────────────────────────────────────────
519
+
520
+ /**
521
+ * The one wizard. Reads state, shows contextual options, dispatches into the workflow doc.
522
+ */
523
+ /**
524
+ * Self-heal: scan runtime records and clear stale ones left behind when
525
+ * auto-mode crashed mid-unit. auto.ts has its own selfHealRuntimeRecords()
526
+ * but guided-flow (manual /gsd mode) never called it — meaning stale records
527
+ * persisted until the next /gsd auto run. This ensures the wizard always
528
+ * starts from a clean state regardless of how the previous session ended.
529
+ */
530
+ function selfHealRuntimeRecords(basePath: string, ctx: ExtensionContext): { cleared: number } {
531
+ try {
532
+ const records = listUnitRuntimeRecords(basePath);
533
+ let cleared = 0;
534
+ for (const record of records) {
535
+ const { unitType, unitId, phase } = record;
536
+ // Clear records whose expected artifact already exists (completed but not cleaned up)
537
+ const artifactPath = resolveExpectedArtifactPath(unitType, unitId, basePath);
538
+ if (artifactPath && existsSync(artifactPath)) {
539
+ clearUnitRuntimeRecord(basePath, unitType, unitId);
540
+ cleared++;
541
+ continue;
542
+ }
543
+ // Clear records stuck in dispatched or timeout phase (process died mid-unit)
544
+ if (phase === "dispatched" || phase === "timeout") {
545
+ clearUnitRuntimeRecord(basePath, unitType, unitId);
546
+ cleared++;
547
+ }
548
+ }
549
+ if (cleared > 0) {
550
+ ctx.ui.notify(`Self-heal: cleared ${cleared} stale runtime record(s) from a previous session.`, "info");
551
+ }
552
+ return { cleared };
553
+ } catch {
554
+ // Non-fatal — self-heal should never block the wizard
555
+ return { cleared: 0 };
556
+ }
557
+ }
558
+
559
+ export async function showSmartEntry(
560
+ ctx: ExtensionCommandContext,
561
+ pi: ExtensionAPI,
562
+ basePath: string,
563
+ options?: { step?: boolean },
564
+ ): Promise<void> {
565
+ const stepMode = options?.step;
566
+
567
+ // ── Ensure git repo exists — GSD needs it for branch-per-slice ──────
568
+ try {
569
+ execSync("git rev-parse --git-dir", { cwd: basePath, stdio: "pipe" });
570
+ } catch {
571
+ const mainBranch = loadEffectiveGSDPreferences()?.preferences?.git?.main_branch || "main";
572
+ execFileSync("git", ["init", "-b", mainBranch], { cwd: basePath, stdio: "pipe" });
573
+ }
574
+
575
+ // ── Ensure .gitignore has baseline patterns ──────────────────────────
576
+ ensureGitignore(basePath);
577
+ untrackRuntimeFiles(basePath);
578
+
579
+ // ── No GSD project OR no milestone → Create first/next milestone ────
580
+ if (!existsSync(join(basePath, ".gsd"))) {
581
+ // Bootstrap .gsd/ silently — the user wants a milestone, not to "init"
582
+ const gsd = gsdRoot(basePath);
583
+ mkdirSync(join(gsd, "milestones"), { recursive: true });
584
+
585
+ // ── Create PREFERENCES.md template ────────────────────────────────
586
+ ensurePreferences(basePath);
587
+ try {
588
+ execSync("git add -A .gsd .gitignore && git commit -m 'chore: init gsd'", {
589
+ cwd: basePath,
590
+ stdio: "pipe",
591
+ });
592
+ } catch {
593
+ // nothing to commit — that's fine
594
+ }
595
+ }
596
+
597
+ // ── Self-heal stale runtime records from crashed auto-mode sessions ──
598
+ selfHealRuntimeRecords(basePath, ctx);
599
+
600
+ // Check for crash from previous auto-mode session
601
+ const crashLock = readCrashLock(basePath);
602
+ if (crashLock) {
603
+ clearLock(basePath);
604
+ const resume = await showNextAction(ctx as any, {
605
+ title: "GSD — Interrupted Session Detected",
606
+ summary: [formatCrashInfo(crashLock)],
607
+ actions: [
608
+ { id: "resume", label: "Resume with /gsd auto", description: "Pick up where it left off", recommended: true },
609
+ { id: "continue", label: "Continue manually", description: "Open the wizard as normal" },
610
+ ],
611
+ });
612
+ if (resume === "resume") {
613
+ await startAuto(ctx, pi, basePath, false);
614
+ return;
615
+ }
616
+ }
617
+
618
+ const state = await deriveState(basePath);
619
+
620
+ if (!state.activeMilestone) {
621
+ // Guard: if a discuss session is already in flight, don't re-inject the prompt.
622
+ // Both /gsd and /gsd auto reach this branch when no milestone exists yet.
623
+ // Without this guard, every subsequent /gsd call overwrites pendingAutoStart
624
+ // and fires another dispatchWorkflow, resetting the conversation mid-interview.
625
+ if (pendingAutoStart) {
626
+ ctx.ui.notify("Discussion already in progress — answer the question above to continue.", "info");
627
+ return;
628
+ }
629
+
630
+ const milestoneIds = findMilestoneIds(basePath);
631
+ const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
632
+ const nextId = nextMilestoneId(milestoneIds, uniqueMilestoneIds);
633
+ const isFirst = milestoneIds.length === 0;
634
+
635
+ if (isFirst) {
636
+ // First ever — skip wizard, just ask directly
637
+ pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
638
+ dispatchWorkflow(pi, buildDiscussPrompt(nextId,
639
+ `New project, milestone ${nextId}. Do NOT read or explore .gsd/ — it's empty scaffolding.`,
640
+ basePath
641
+ ));
642
+ } else {
643
+ const choice = await showNextAction(ctx as any, {
644
+ title: "GSD — Get Shit Done",
645
+ summary: ["No active milestone."],
646
+ actions: [
647
+ {
648
+ id: "new_milestone",
649
+ label: "Create next milestone",
650
+ description: "Define what to build next.",
651
+ recommended: true,
652
+ },
653
+ ],
654
+ notYetMessage: "Run /gsd when ready.",
655
+ });
656
+
657
+ if (choice === "new_milestone") {
658
+ pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
659
+ dispatchWorkflow(pi, buildDiscussPrompt(nextId,
660
+ `New milestone ${nextId}.`,
661
+ basePath
662
+ ));
663
+ }
664
+ }
665
+ return;
666
+ }
667
+
668
+ const milestoneId = state.activeMilestone.id;
669
+ const milestoneTitle = state.activeMilestone.title;
670
+
671
+ // ── All milestones complete → New milestone ──────────────────────────
672
+ if (state.phase === "complete") {
673
+ const choice = await showNextAction(ctx as any, {
674
+ title: `GSD — ${milestoneId}: ${milestoneTitle}`,
675
+ summary: ["All milestones complete."],
676
+ actions: [
677
+ {
678
+ id: "new_milestone",
679
+ label: "Start new milestone",
680
+ description: "Define and plan the next milestone.",
681
+ recommended: true,
682
+ },
683
+ {
684
+ id: "status",
685
+ label: "View status",
686
+ description: "Review what was built.",
687
+ },
688
+ ],
689
+ notYetMessage: "Run /gsd when ready.",
690
+ });
691
+
692
+ if (choice === "new_milestone") {
693
+ const milestoneIds = findMilestoneIds(basePath);
694
+ const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
695
+ const nextId = nextMilestoneId(milestoneIds, uniqueMilestoneIds);
696
+
697
+ pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
698
+ dispatchWorkflow(pi, buildDiscussPrompt(nextId,
699
+ `New milestone ${nextId}.`,
700
+ basePath
701
+ ));
702
+ } else if (choice === "status") {
703
+ const { fireStatusViaCommand } = await import("./commands.js");
704
+ await fireStatusViaCommand(ctx);
705
+ }
706
+ return;
707
+ }
708
+
709
+ // ── Draft milestone — needs discussion before planning ────────────────
710
+ if (state.phase === "needs-discussion") {
711
+ const draftFile = resolveMilestoneFile(basePath, milestoneId, "CONTEXT-DRAFT");
712
+ const draftContent = draftFile ? await loadFile(draftFile) : null;
713
+
714
+ const choice = await showNextAction(ctx as any, {
715
+ title: `GSD — ${milestoneId}: ${milestoneTitle}`,
716
+ summary: ["This milestone has a draft context from a prior discussion.", "It needs a dedicated discussion before auto-planning can begin."],
717
+ actions: [
718
+ {
719
+ id: "discuss_draft",
720
+ label: "Discuss from draft",
721
+ description: "Continue where the prior discussion left off — seed material is loaded automatically.",
722
+ recommended: true,
723
+ },
724
+ {
725
+ id: "discuss_fresh",
726
+ label: "Start fresh discussion",
727
+ description: "Discard the draft and start a new discussion from scratch.",
728
+ },
729
+ {
730
+ id: "skip_milestone",
731
+ label: "Skip — create new milestone",
732
+ description: "Leave this milestone as-is and start something new.",
733
+ },
734
+ ],
735
+ notYetMessage: "Run /gsd when ready to discuss this milestone.",
736
+ });
737
+
738
+ if (choice === "discuss_draft") {
739
+ const discussMilestoneTemplates = inlineTemplate("context", "Context");
740
+ const basePrompt = loadPrompt("guided-discuss-milestone", {
741
+ milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates,
742
+ });
743
+ const seed = draftContent
744
+ ? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
745
+ : basePrompt;
746
+ pendingAutoStart = { ctx, pi, basePath, milestoneId, step: stepMode };
747
+ dispatchWorkflow(pi, seed, "gsd-discuss");
748
+ } else if (choice === "discuss_fresh") {
749
+ const discussMilestoneTemplates = inlineTemplate("context", "Context");
750
+ pendingAutoStart = { ctx, pi, basePath, milestoneId, step: stepMode };
751
+ dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
752
+ milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates,
753
+ }), "gsd-discuss");
754
+ } else if (choice === "skip_milestone") {
755
+ const milestoneIds = findMilestoneIds(basePath);
756
+ const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
757
+ const nextId = nextMilestoneId(milestoneIds, uniqueMilestoneIds);
758
+ pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
759
+ dispatchWorkflow(pi, buildDiscussPrompt(nextId,
760
+ `New milestone ${nextId}.`,
761
+ basePath
762
+ ));
763
+ }
764
+ return;
765
+ }
766
+
767
+ // ── No active slice ──────────────────────────────────────────────────
768
+ if (!state.activeSlice) {
769
+ const roadmapFile = resolveMilestoneFile(basePath, milestoneId, "ROADMAP");
770
+ const hasRoadmap = !!(roadmapFile && await loadFile(roadmapFile));
771
+
772
+ if (!hasRoadmap) {
773
+ // No roadmap → discuss or plan
774
+ const contextFile = resolveMilestoneFile(basePath, milestoneId, "CONTEXT");
775
+ const hasContext = !!(contextFile && await loadFile(contextFile));
776
+
777
+ const actions = [
778
+ {
779
+ id: "plan",
780
+ label: "Create roadmap",
781
+ description: hasContext
782
+ ? "Context captured. Decompose into slices with a boundary map."
783
+ : "Decompose the milestone into slices with a boundary map.",
784
+ recommended: true,
785
+ },
786
+ ...(!hasContext ? [{
787
+ id: "discuss",
788
+ label: "Discuss first",
789
+ description: "Capture decisions on gray areas before planning.",
790
+ }] : []),
791
+ {
792
+ id: "skip_milestone",
793
+ label: "Skip — create new milestone",
794
+ description: "Leave this milestone on disk and start a fresh one.",
795
+ },
796
+ {
797
+ id: "discard_milestone",
798
+ label: "Discard this milestone",
799
+ description: "Delete the milestone directory and start over.",
800
+ },
801
+ ];
802
+
803
+ const choice = await showNextAction(ctx as any, {
804
+ title: `GSD — ${milestoneId}: ${milestoneTitle}`,
805
+ summary: [hasContext ? "Context captured. Ready to create roadmap." : "New milestone — no roadmap yet."],
806
+ actions,
807
+ notYetMessage: "Run /gsd when ready.",
808
+ });
809
+
810
+ if (choice === "plan") {
811
+ const planMilestoneTemplates = [
812
+ inlineTemplate("roadmap", "Roadmap"),
813
+ inlineTemplate("plan", "Slice Plan"),
814
+ inlineTemplate("task-plan", "Task Plan"),
815
+ inlineTemplate("secrets-manifest", "Secrets Manifest"),
816
+ ].join("\n\n---\n\n");
817
+ const secretsOutputPath = relMilestoneFile(basePath, milestoneId, "SECRETS");
818
+ dispatchWorkflow(pi, loadPrompt("guided-plan-milestone", {
819
+ milestoneId, milestoneTitle, secretsOutputPath, inlinedTemplates: planMilestoneTemplates,
820
+ }));
821
+ } else if (choice === "discuss") {
822
+ const discussMilestoneTemplates = inlineTemplate("context", "Context");
823
+ dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
824
+ milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates,
825
+ }));
826
+ } else if (choice === "skip_milestone") {
827
+ const milestoneIds = findMilestoneIds(basePath);
828
+ const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
829
+ const nextId = nextMilestoneId(milestoneIds, uniqueMilestoneIds);
830
+ pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
831
+ dispatchWorkflow(pi, buildDiscussPrompt(nextId,
832
+ `New milestone ${nextId}.`,
833
+ basePath
834
+ ));
835
+ } else if (choice === "discard_milestone") {
836
+ const mDir = resolveMilestonePath(basePath, milestoneId);
837
+ if (!mDir) return;
838
+ const confirmed = await showConfirm(ctx as any, {
839
+ title: "Discard milestone?",
840
+ message: `This will permanently delete ${milestoneId} and all its contents.`,
841
+ confirmLabel: "Discard",
842
+ declineLabel: "Cancel",
843
+ });
844
+ if (confirmed) {
845
+ rmSync(mDir, { recursive: true, force: true });
846
+ return showSmartEntry(ctx, pi, basePath, options);
847
+ }
848
+ }
849
+ } else {
850
+ // Roadmap exists — either blocked or ready for auto
851
+ const actions = [
852
+ {
853
+ id: "auto",
854
+ label: "Go auto",
855
+ description: "Execute everything automatically until milestone complete.",
856
+ recommended: true,
857
+ },
858
+ {
859
+ id: "status",
860
+ label: "View status",
861
+ description: "See milestone progress and blockers.",
862
+ },
863
+ ];
864
+
865
+ const choice = await showNextAction(ctx as any, {
866
+ title: `GSD — ${milestoneId}: ${milestoneTitle}`,
867
+ summary: ["Roadmap exists. Ready to execute."],
868
+ actions,
869
+ notYetMessage: "Run /gsd status for details.",
870
+ });
871
+
872
+ if (choice === "auto") {
873
+ await startAuto(ctx, pi, basePath, false);
874
+ } else if (choice === "status") {
875
+ const { fireStatusViaCommand } = await import("./commands.js");
876
+ await fireStatusViaCommand(ctx);
877
+ }
878
+ }
879
+ return;
880
+ }
881
+
882
+ const sliceId = state.activeSlice.id;
883
+ const sliceTitle = state.activeSlice.title;
884
+
885
+ // ── Slice needs planning ─────────────────────────────────────────────
886
+ if (state.phase === "planning") {
887
+ const contextFile = resolveSliceFile(basePath, milestoneId, sliceId, "CONTEXT");
888
+ const researchFile = resolveSliceFile(basePath, milestoneId, sliceId, "RESEARCH");
889
+ const hasContext = !!(contextFile && await loadFile(contextFile));
890
+ const hasResearch = !!(researchFile && await loadFile(researchFile));
891
+
892
+ const actions = [
893
+ {
894
+ id: "plan",
895
+ label: `Plan ${sliceId}`,
896
+ description: `Decompose "${sliceTitle}" into tasks with must-haves.`,
897
+ recommended: true,
898
+ },
899
+ ...(!hasContext ? [{
900
+ id: "discuss",
901
+ label: `Discuss ${sliceId} first`,
902
+ description: "Capture context and decisions for this slice.",
903
+ }] : []),
904
+ ...(!hasResearch ? [{
905
+ id: "research",
906
+ label: `Research ${sliceId} first`,
907
+ description: "Scout codebase and relevant docs.",
908
+ }] : []),
909
+ {
910
+ id: "status",
911
+ label: "View status",
912
+ description: "See milestone progress.",
913
+ },
914
+ ];
915
+
916
+ const summaryParts = [];
917
+ if (hasContext) summaryParts.push("context ✓");
918
+ if (hasResearch) summaryParts.push("research ✓");
919
+ const summaryLine = summaryParts.length > 0
920
+ ? `${sliceId}: ${sliceTitle} (${summaryParts.join(", ")})`
921
+ : `${sliceId}: ${sliceTitle} — ready for planning.`;
922
+
923
+ const choice = await showNextAction(ctx as any, {
924
+ title: `GSD — ${milestoneId} / ${sliceId}: ${sliceTitle}`,
925
+ summary: [summaryLine],
926
+ actions,
927
+ notYetMessage: "Run /gsd when ready.",
928
+ });
929
+
930
+ if (choice === "plan") {
931
+ const planSliceTemplates = [
932
+ inlineTemplate("plan", "Slice Plan"),
933
+ inlineTemplate("task-plan", "Task Plan"),
934
+ ].join("\n\n---\n\n");
935
+ dispatchWorkflow(pi, loadPrompt("guided-plan-slice", {
936
+ milestoneId, sliceId, sliceTitle, inlinedTemplates: planSliceTemplates,
937
+ }));
938
+ } else if (choice === "discuss") {
939
+ dispatchWorkflow(pi, await buildDiscussSlicePrompt(milestoneId, sliceId, sliceTitle, basePath));
940
+ } else if (choice === "research") {
941
+ const researchTemplates = inlineTemplate("research", "Research");
942
+ dispatchWorkflow(pi, loadPrompt("guided-research-slice", {
943
+ milestoneId, sliceId, sliceTitle, inlinedTemplates: researchTemplates,
944
+ }));
945
+ } else if (choice === "status") {
946
+ const { fireStatusViaCommand } = await import("./commands.js");
947
+ await fireStatusViaCommand(ctx);
948
+ }
949
+ return;
950
+ }
951
+
952
+ // ── All tasks done → Complete slice ──────────────────────────────────
953
+ if (state.phase === "summarizing") {
954
+ const choice = await showNextAction(ctx as any, {
955
+ title: `GSD — ${milestoneId} / ${sliceId}: ${sliceTitle}`,
956
+ summary: ["All tasks complete. Ready for slice summary."],
957
+ actions: [
958
+ {
959
+ id: "complete",
960
+ label: `Complete ${sliceId}`,
961
+ description: "Write slice summary, UAT, mark done, and squash-merge to main.",
962
+ recommended: true,
963
+ },
964
+ {
965
+ id: "status",
966
+ label: "View status",
967
+ description: "Review tasks before completing.",
968
+ },
969
+ ],
970
+ notYetMessage: "Run /gsd when ready.",
971
+ });
972
+
973
+ if (choice === "complete") {
974
+ const completeSliceTemplates = [
975
+ inlineTemplate("slice-summary", "Slice Summary"),
976
+ inlineTemplate("uat", "UAT"),
977
+ ].join("\n\n---\n\n");
978
+ dispatchWorkflow(pi, loadPrompt("guided-complete-slice", {
979
+ milestoneId, sliceId, sliceTitle, inlinedTemplates: completeSliceTemplates,
980
+ }));
981
+ } else if (choice === "status") {
982
+ const { fireStatusViaCommand } = await import("./commands.js");
983
+ await fireStatusViaCommand(ctx);
984
+ }
985
+ return;
986
+ }
987
+
988
+ // ── Active task → Execute ────────────────────────────────────────────
989
+ if (state.activeTask) {
990
+ const taskId = state.activeTask.id;
991
+ const taskTitle = state.activeTask.title;
992
+
993
+ const continueFile = resolveSliceFile(basePath, milestoneId, sliceId, "CONTINUE");
994
+ const sDir = resolveSlicePath(basePath, milestoneId, sliceId);
995
+ const hasInterrupted = !!(continueFile && await loadFile(continueFile)) ||
996
+ !!(sDir && await loadFile(join(sDir, "continue.md")));
997
+
998
+ const choice = await showNextAction(ctx as any, {
999
+ title: `GSD — ${milestoneId} / ${sliceId}: ${sliceTitle}`,
1000
+ summary: [
1001
+ hasInterrupted
1002
+ ? `Resuming: ${taskId} — ${taskTitle}`
1003
+ : `Next: ${taskId} — ${taskTitle}`,
1004
+ ],
1005
+ actions: [
1006
+ {
1007
+ id: "execute",
1008
+ label: hasInterrupted ? `Resume ${taskId}` : `Execute ${taskId}`,
1009
+ description: hasInterrupted
1010
+ ? "Continue from where you left off."
1011
+ : `Start working on "${taskTitle}".`,
1012
+ recommended: true,
1013
+ },
1014
+ {
1015
+ id: "auto",
1016
+ label: "Go auto",
1017
+ description: "Execute this and all remaining tasks automatically.",
1018
+ },
1019
+ {
1020
+ id: "status",
1021
+ label: "View status",
1022
+ description: "See slice progress before starting.",
1023
+ },
1024
+ ],
1025
+ notYetMessage: "Run /gsd when ready.",
1026
+ });
1027
+
1028
+ if (choice === "auto") {
1029
+ await startAuto(ctx, pi, basePath, false);
1030
+ return;
1031
+ }
1032
+
1033
+ if (choice === "execute") {
1034
+ if (hasInterrupted) {
1035
+ dispatchWorkflow(pi, loadPrompt("guided-resume-task", {
1036
+ milestoneId, sliceId,
1037
+ }));
1038
+ } else {
1039
+ const executeTaskTemplates = inlineTemplate("task-summary", "Task Summary");
1040
+ dispatchWorkflow(pi, loadPrompt("guided-execute-task", {
1041
+ milestoneId, sliceId, taskId, taskTitle, inlinedTemplates: executeTaskTemplates,
1042
+ }));
1043
+ }
1044
+ } else if (choice === "status") {
1045
+ const { fireStatusViaCommand } = await import("./commands.js");
1046
+ await fireStatusViaCommand(ctx);
1047
+ }
1048
+ return;
1049
+ }
1050
+
1051
+ // ── Fallback: show status ────────────────────────────────────────────
1052
+ const { fireStatusViaCommand } = await import("./commands.js");
1053
+ await fireStatusViaCommand(ctx);
1054
+ }