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
@@ -18,7 +18,7 @@ Then research what this slice needs. Narrate key findings and surprises as you g
18
18
  2. **Skill Discovery ({{skillDiscoveryMode}}):**{{skillDiscoveryInstructions}}
19
19
  3. Explore relevant code for this slice's scope. For targeted exploration, use `rg`, `find`, and reads. For broad or unfamiliar subsystems, use `scout` to map the relevant area first.
20
20
  4. Use `resolve_library` / `get_library_docs` for unfamiliar libraries
21
- 5. Read the template at `~/.gsd/agent/extensions/gsd/templates/research.md`
21
+ 5. Use the **Research** output template from the inlined context above
22
22
  6. Write `{{outputPath}}`
23
23
 
24
24
  The slice directory already exists at `{{slicePath}}/`. Do NOT mkdir — just write the file.
@@ -27,11 +27,13 @@ import {
27
27
  resolveSliceFile,
28
28
  resolveTaskFile,
29
29
  resolveGsdRootFile,
30
+ gsdRoot,
30
31
  } from './paths.js';
31
32
  import { getActiveSliceBranch } from './worktree.js';
32
33
  import { milestoneIdSort, findMilestoneIds } from './guided-flow.js';
34
+ import { nativeBatchParseGsdFiles, type BatchParsedFile } from './native-parser-bridge.js';
33
35
 
34
- import { join } from 'path';
36
+ import { join, resolve } from 'path';
35
37
 
36
38
  // ─── Query Functions ───────────────────────────────────────────────────────
37
39
 
@@ -51,6 +53,27 @@ export function isMilestoneComplete(roadmap: Roadmap): boolean {
51
53
 
52
54
  // ─── State Derivation ──────────────────────────────────────────────────────
53
55
 
56
+ // ── deriveState memoization ─────────────────────────────────────────────────
57
+ // Cache the most recent deriveState() result keyed by basePath. Within a single
58
+ // dispatch cycle (~100ms window), repeated calls return the cached value instead
59
+ // of re-reading the entire .gsd/ tree from disk.
60
+
61
+ interface StateCache {
62
+ basePath: string;
63
+ result: GSDState;
64
+ timestamp: number;
65
+ }
66
+
67
+ const CACHE_TTL_MS = 100;
68
+ let _stateCache: StateCache | null = null;
69
+
70
+ /**
71
+ * Invalidate the deriveState() cache. Call this whenever planning files on disk
72
+ * may have changed (unit completion, merges, file writes).
73
+ */
74
+ export function invalidateStateCache(): void {
75
+ _stateCache = null;
76
+ }
54
77
 
55
78
  /**
56
79
  * Returns the ID of the first incomplete milestone, or null if all are complete.
@@ -77,10 +100,94 @@ export async function getActiveMilestoneId(basePath: string): Promise<string | n
77
100
  /**
78
101
  * Reconstruct GSD state from files on disk.
79
102
  * This is the source of truth — STATE.md is just a cache of this output.
103
+ *
104
+ * Uses native batch parsing when available: a single Rust call reads and parses
105
+ * every .md file under .gsd/, populating an in-memory cache that replaces all
106
+ * individual loadFile() calls during milestone/slice/task traversal.
107
+ * Falls back to sequential JS file reads when the native module is absent.
80
108
  */
81
109
  export async function deriveState(basePath: string): Promise<GSDState> {
110
+ // Return cached result if within the TTL window for the same basePath
111
+ if (
112
+ _stateCache &&
113
+ _stateCache.basePath === basePath &&
114
+ Date.now() - _stateCache.timestamp < CACHE_TTL_MS
115
+ ) {
116
+ return _stateCache.result;
117
+ }
118
+
119
+ const result = await _deriveStateImpl(basePath);
120
+ _stateCache = { basePath, result, timestamp: Date.now() };
121
+ return result;
122
+ }
123
+
124
+ async function _deriveStateImpl(basePath: string): Promise<GSDState> {
82
125
  const milestoneIds = findMilestoneIds(basePath);
83
- const requirements = parseRequirementCounts(await loadFile(resolveGsdRootFile(basePath, "REQUIREMENTS")));
126
+
127
+ // ── Batch-parse file cache ──────────────────────────────────────────────
128
+ // When the native Rust parser is available, read every .md file under .gsd/
129
+ // in one call and build an in-memory content map keyed by absolute path.
130
+ // This eliminates O(N) individual fs.readFile calls during traversal.
131
+ const fileContentCache = new Map<string, string>();
132
+ const gsdDir = gsdRoot(basePath);
133
+
134
+ const batchFiles = nativeBatchParseGsdFiles(gsdDir);
135
+ if (batchFiles) {
136
+ for (const f of batchFiles) {
137
+ // Reconstruct the full file content from parsed components so downstream
138
+ // parsers (parseRoadmap, parseSummary, etc.) receive the same input they
139
+ // expect from loadFile(). Files with frontmatter get it re-serialized;
140
+ // files without get just the body.
141
+ const absPath = resolve(gsdDir, f.path);
142
+ const hasMetadata = Object.keys(f.metadata).length > 0;
143
+ if (hasMetadata) {
144
+ // Re-serialize frontmatter as simple YAML key: value lines
145
+ const fmLines: string[] = ['---'];
146
+ for (const [key, value] of Object.entries(f.metadata)) {
147
+ if (Array.isArray(value)) {
148
+ if (value.length === 0) {
149
+ fmLines.push(`${key}: []`);
150
+ } else if (typeof value[0] === 'object' && value[0] !== null) {
151
+ fmLines.push(`${key}:`);
152
+ for (const obj of value) {
153
+ const entries = Object.entries(obj as Record<string, unknown>);
154
+ if (entries.length > 0) {
155
+ fmLines.push(` - ${entries[0][0]}: ${entries[0][1]}`);
156
+ for (let i = 1; i < entries.length; i++) {
157
+ fmLines.push(` ${entries[i][0]}: ${entries[i][1]}`);
158
+ }
159
+ }
160
+ }
161
+ } else {
162
+ fmLines.push(`${key}:`);
163
+ for (const item of value) {
164
+ fmLines.push(` - ${item}`);
165
+ }
166
+ }
167
+ } else {
168
+ fmLines.push(`${key}: ${value}`);
169
+ }
170
+ }
171
+ fmLines.push('---');
172
+ fileContentCache.set(absPath, fmLines.join('\n') + '\n\n' + f.body);
173
+ } else {
174
+ fileContentCache.set(absPath, f.body);
175
+ }
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Load file content from batch cache first, falling back to disk read.
181
+ * Resolves the path to absolute before cache lookup.
182
+ */
183
+ async function cachedLoadFile(path: string): Promise<string | null> {
184
+ const abs = resolve(path);
185
+ const cached = fileContentCache.get(abs);
186
+ if (cached !== undefined) return cached;
187
+ return loadFile(path);
188
+ }
189
+
190
+ const requirements = parseRequirementCounts(await cachedLoadFile(resolveGsdRootFile(basePath, "REQUIREMENTS")));
84
191
 
85
192
  if (milestoneIds.length === 0) {
86
193
  return {
@@ -99,25 +206,31 @@ export async function deriveState(basePath: string): Promise<GSDState> {
99
206
  };
100
207
  }
101
208
 
102
- // Pre-compute the set of complete milestone IDs for dependency checking.
103
- // This allows forward references (M002 depending on M003) to resolve correctly.
209
+ // ── Single-pass milestone scan ──────────────────────────────────────────
210
+ // Parse each milestone's roadmap once, caching results. First pass determines
211
+ // completeness for dependency resolution; second pass builds the registry.
212
+ // With the batch cache, all file reads hit memory instead of disk.
213
+
214
+ // Phase 1: Build roadmap cache and completeness set
215
+ const roadmapCache = new Map<string, Roadmap>();
104
216
  const completeMilestoneIds = new Set<string>();
217
+
105
218
  for (const mid of milestoneIds) {
106
219
  const rf = resolveMilestoneFile(basePath, mid, "ROADMAP");
107
- const rc = rf ? await loadFile(rf) : null;
220
+ const rc = rf ? await cachedLoadFile(rf) : null;
108
221
  if (!rc) {
109
- // No roadmap — milestone is complete if it has a summary
110
222
  const sf = resolveMilestoneFile(basePath, mid, "SUMMARY");
111
223
  if (sf) completeMilestoneIds.add(mid);
112
224
  continue;
113
225
  }
114
226
  const rmap = parseRoadmap(rc);
227
+ roadmapCache.set(mid, rmap);
115
228
  if (!isMilestoneComplete(rmap)) continue;
116
229
  const sf = resolveMilestoneFile(basePath, mid, "SUMMARY");
117
230
  if (sf) completeMilestoneIds.add(mid);
118
231
  }
119
232
 
120
- // Build the registry and locate the active milestone in a single pass.
233
+ // Phase 2: Build registry using cached roadmaps (no re-parsing or re-reading)
121
234
  const registry: MilestoneRegistryEntry[] = [];
122
235
  let activeMilestone: ActiveRef | null = null;
123
236
  let activeRoadmap: Roadmap | null = null;
@@ -125,13 +238,13 @@ export async function deriveState(basePath: string): Promise<GSDState> {
125
238
  let activeMilestoneHasDraft = false;
126
239
 
127
240
  for (const mid of milestoneIds) {
128
- const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
129
- const content = roadmapFile ? await loadFile(roadmapFile) : null;
130
- if (!content) {
241
+ const roadmap = roadmapCache.get(mid) ?? null;
242
+
243
+ if (!roadmap) {
131
244
  // No roadmap — check if a summary exists (completed milestone without roadmap)
132
245
  const summaryFile = resolveMilestoneFile(basePath, mid, "SUMMARY");
133
246
  if (summaryFile) {
134
- const summaryContent = await loadFile(summaryFile);
247
+ const summaryContent = await cachedLoadFile(summaryFile);
135
248
  const summaryTitle = summaryContent
136
249
  ? (parseSummary(summaryContent).title || mid)
137
250
  : mid;
@@ -157,7 +270,6 @@ export async function deriveState(basePath: string): Promise<GSDState> {
157
270
  continue;
158
271
  }
159
272
 
160
- const roadmap = parseRoadmap(content);
161
273
  const title = roadmap.title.replace(/^M\d+(?:-[a-z0-9]{6})?[^:]*:\s*/, '');
162
274
  const complete = isMilestoneComplete(roadmap);
163
275
 
@@ -176,7 +288,7 @@ export async function deriveState(basePath: string): Promise<GSDState> {
176
288
  } else if (!activeMilestoneFound) {
177
289
  // Check milestone-level dependencies before promoting to active
178
290
  const contextFile = resolveMilestoneFile(basePath, mid, "CONTEXT");
179
- const contextContent = contextFile ? await loadFile(contextFile) : null;
291
+ const contextContent = contextFile ? await cachedLoadFile(contextFile) : null;
180
292
  const deps = parseContextDependsOn(contextContent);
181
293
  const depsUnmet = deps.some(dep => !completeMilestoneIds.has(dep));
182
294
  if (depsUnmet) {
@@ -190,7 +302,7 @@ export async function deriveState(basePath: string): Promise<GSDState> {
190
302
  }
191
303
  } else {
192
304
  const contextFile2 = resolveMilestoneFile(basePath, mid, "CONTEXT");
193
- const contextContent2 = contextFile2 ? await loadFile(contextFile2) : null;
305
+ const contextContent2 = contextFile2 ? await cachedLoadFile(contextFile2) : null;
194
306
  const deps2 = parseContextDependsOn(contextContent2);
195
307
  registry.push({ id: mid, title, status: 'pending', ...(deps2.length > 0 ? { dependsOn: deps2 } : {}) });
196
308
  }
@@ -330,7 +442,7 @@ export async function deriveState(basePath: string): Promise<GSDState> {
330
442
 
331
443
  // Check if the slice has a plan
332
444
  const planFile = resolveSliceFile(basePath, activeMilestone.id, activeSlice.id, "PLAN");
333
- const slicePlanContent = planFile ? await loadFile(planFile) : null;
445
+ const slicePlanContent = planFile ? await cachedLoadFile(planFile) : null;
334
446
 
335
447
  if (!slicePlanContent) {
336
448
  return {
@@ -392,7 +504,7 @@ export async function deriveState(basePath: string): Promise<GSDState> {
392
504
  for (const ct of completedTasks) {
393
505
  const summaryFile = resolveTaskFile(basePath, activeMilestone.id, activeSlice.id, ct.id, "SUMMARY");
394
506
  if (!summaryFile) continue;
395
- const summaryContent = await loadFile(summaryFile);
507
+ const summaryContent = await cachedLoadFile(summaryFile);
396
508
  if (!summaryContent) continue;
397
509
  const summary = parseSummary(summaryContent);
398
510
  if (summary.frontmatter.blocker_discovered) {
@@ -432,8 +544,8 @@ export async function deriveState(basePath: string): Promise<GSDState> {
432
544
  const sDir = resolveSlicePath(basePath, activeMilestone.id, activeSlice.id);
433
545
  const continueFile = sDir ? resolveSliceFile(basePath, activeMilestone.id, activeSlice.id, "CONTINUE") : null;
434
546
  // Also check legacy continue.md
435
- const hasInterrupted = !!(continueFile && await loadFile(continueFile)) ||
436
- !!(sDir && await loadFile(join(sDir, "continue.md")));
547
+ const hasInterrupted = !!(continueFile && await cachedLoadFile(continueFile)) ||
548
+ !!(sDir && await cachedLoadFile(join(sDir, "continue.md")));
437
549
 
438
550
  return {
439
551
  activeMilestone,
@@ -1,4 +1,4 @@
1
- # {{milestoneId}}: {{milestoneTitle}} — Context
1
+ # {{milestoneId}}: {{milestoneTitle}}
2
2
 
3
3
  **Gathered:** {{date}}
4
4
  **Status:** Ready for planning
@@ -1,8 +1,8 @@
1
1
  # GSD State
2
2
 
3
- **Active Milestone:** {{milestoneId}} {{milestoneTitle}}
4
- **Active Slice:** {{sliceId}} {{sliceTitle}}
5
- **Active Task:** {{taskId}} {{taskTitle}}
3
+ **Active Milestone:** {{milestoneId}}: {{milestoneTitle}}
4
+ **Active Slice:** {{sliceId}}: {{sliceTitle}}
5
+ **Active Task:** {{taskId}}: {{taskTitle}}
6
6
  **Phase:** {{phase}}
7
7
  **Slice Branch:** {{activeBranch}}
8
8
  **Active Workspace:** {{activeWorkspace}}
@@ -0,0 +1,127 @@
1
+ // Tests for saveActivityLog performance behavior:
2
+ // - cache next sequence per activity directory instead of rescanning every save
3
+ // - skip rewriting identical snapshots for the same unit
4
+ // - recover safely if another writer creates the cached next sequence
5
+
6
+ import { existsSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from "node:fs";
7
+ import { tmpdir } from "node:os";
8
+ import { join } from "node:path";
9
+
10
+ import { saveActivityLog } from "../activity-log.ts";
11
+ import { createTestContext } from "./test-helpers.ts";
12
+
13
+ const { assertEq, assertTrue, report } = createTestContext();
14
+
15
+ let tmpDirs: string[] = [];
16
+
17
+ function createBaseDir(): string {
18
+ const dir = mkdtempSync(join(tmpdir(), "gsd-activity-save-test-"));
19
+ tmpDirs.push(dir);
20
+ return dir;
21
+ }
22
+
23
+ function activityDir(baseDir: string): string {
24
+ return join(baseDir, ".gsd", "activity");
25
+ }
26
+
27
+ function listActivityFiles(baseDir: string): string[] {
28
+ const dir = activityDir(baseDir);
29
+ return existsSync(dir) ? readdirSync(dir).sort() : [];
30
+ }
31
+
32
+ function createCtx(entries: unknown[]) {
33
+ return {
34
+ sessionManager: {
35
+ getEntries: () => entries,
36
+ },
37
+ };
38
+ }
39
+
40
+ function cleanup(): void {
41
+ for (const dir of tmpDirs) {
42
+ rmSync(dir, { recursive: true, force: true });
43
+ }
44
+ tmpDirs = [];
45
+ }
46
+
47
+ process.on("exit", cleanup);
48
+
49
+ async function main(): Promise<void> {
50
+ console.log("\n── (a) cache next sequence instead of rescanning every save");
51
+ {
52
+ const baseDir = createBaseDir();
53
+ saveActivityLog(createCtx([{ kind: "first", n: 1 }]) as any, baseDir, "execute-task", "M001/S01/T01");
54
+
55
+ writeFileSync(
56
+ join(activityDir(baseDir), "999-external-manual.jsonl"),
57
+ '{"external":true}\n',
58
+ "utf-8",
59
+ );
60
+
61
+ saveActivityLog(createCtx([{ kind: "second", n: 2 }]) as any, baseDir, "execute-task", "M001/S01/T02");
62
+
63
+ const files = listActivityFiles(baseDir);
64
+ assertTrue(files.includes("001-execute-task-M001-S01-T01.jsonl"), "(a) first save uses sequence 001");
65
+ assertTrue(files.includes("002-execute-task-M001-S01-T02.jsonl"), "(a) second save uses cached next sequence 002");
66
+ assertTrue(files.includes("999-external-manual.jsonl"), "(a) externally added file remains present");
67
+ assertTrue(!files.some(file => file.startsWith("1000-")), "(a) second save did not rescan and jump to sequence 1000");
68
+ }
69
+
70
+ console.log("\n── (b) skip rewriting identical snapshots for the same unit");
71
+ {
72
+ const baseDir = createBaseDir();
73
+ const ctx = createCtx([{ role: "assistant", content: "same snapshot" }]);
74
+
75
+ saveActivityLog(ctx as any, baseDir, "plan-slice", "M002/S01");
76
+ saveActivityLog(ctx as any, baseDir, "plan-slice", "M002/S01");
77
+
78
+ let files = listActivityFiles(baseDir);
79
+ assertEq(files.length, 1, "(b) identical repeated save writes only one activity file");
80
+ assertTrue(files[0] === "001-plan-slice-M002-S01.jsonl", "(b) the original sequence is preserved");
81
+
82
+ saveActivityLog(createCtx([{ role: "assistant", content: "changed snapshot" }]) as any, baseDir, "plan-slice", "M002/S01");
83
+ files = listActivityFiles(baseDir);
84
+ assertEq(files.length, 2, "(b) changed snapshot writes a new activity file");
85
+ assertTrue(files.includes("002-plan-slice-M002-S01.jsonl"), "(b) deduped save did not consume the next sequence");
86
+ }
87
+
88
+ console.log("\n── (c) recover if another writer creates the exact cached target file");
89
+ {
90
+ const baseDir = createBaseDir();
91
+ saveActivityLog(createCtx([{ turn: 1 }]) as any, baseDir, "execute-task", "M003/S02/T01");
92
+
93
+ writeFileSync(
94
+ join(activityDir(baseDir), "002-execute-task-M003-S02-T02.jsonl"),
95
+ '{"collision":true}\n',
96
+ "utf-8",
97
+ );
98
+
99
+ saveActivityLog(createCtx([{ turn: 2 }]) as any, baseDir, "execute-task", "M003/S02/T02");
100
+
101
+ const files = listActivityFiles(baseDir);
102
+ assertTrue(files.includes("002-execute-task-M003-S02-T02.jsonl"), "(c) exact collision file is preserved");
103
+ assertTrue(files.includes("003-execute-task-M003-S02-T02.jsonl"), "(c) logger rescans only on collision and advances to 003");
104
+ }
105
+
106
+ console.log("\n── (d) dedupe is tracked per unit, not just the last write in the directory");
107
+ {
108
+ const baseDir = createBaseDir();
109
+ const repeatedCtx = createCtx([{ role: "assistant", content: "same-for-unit-a" }]);
110
+
111
+ saveActivityLog(repeatedCtx as any, baseDir, "execute-task", "M004/S01/T01");
112
+ saveActivityLog(createCtx([{ role: "assistant", content: "other-unit" }]) as any, baseDir, "execute-task", "M004/S01/T02");
113
+ saveActivityLog(repeatedCtx as any, baseDir, "execute-task", "M004/S01/T01");
114
+
115
+ const files = listActivityFiles(baseDir);
116
+ assertEq(files.length, 2, "(d) interleaving another unit does not force a duplicate rewrite for unit A");
117
+ assertTrue(files.includes("001-execute-task-M004-S01-T01.jsonl"), "(d) original unit A snapshot is retained");
118
+ assertTrue(files.includes("002-execute-task-M004-S01-T02.jsonl"), "(d) unit B snapshot is retained");
119
+ }
120
+
121
+ report();
122
+ }
123
+
124
+ main().catch((error) => {
125
+ console.error(error);
126
+ process.exit(1);
127
+ });
@@ -3,6 +3,8 @@ import { join, dirname } from "node:path";
3
3
  import { tmpdir } from "node:os";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { createTestContext } from './test-helpers.ts';
6
+ import { clearPathCache } from '../paths.ts';
7
+ import { invalidateStateCache } from '../state.ts';
6
8
 
7
9
  // loadPrompt reads from ~/.gsd/agent/extensions/gsd/prompts/ (main checkout).
8
10
  // In a worktree the file may not exist there yet, so we resolve prompts
@@ -146,7 +148,7 @@ async function main(): Promise<void> {
146
148
  // ─── deriveState integration: completing-milestone dispatches correctly ─
147
149
  console.log("\n=== deriveState completing-milestone integration ===");
148
150
  {
149
- const { deriveState, isMilestoneComplete } = await import("../state.ts");
151
+ const { deriveState, isMilestoneComplete, invalidateStateCache } = await import("../state.ts");
150
152
  const { parseRoadmap } = await import("../files.ts");
151
153
 
152
154
  const base = createFixtureBase();
@@ -179,6 +181,8 @@ async function main(): Promise<void> {
179
181
 
180
182
  // Now add the summary and verify it transitions to complete
181
183
  writeMilestoneSummary(base, "M001", "# M001 Summary\n\nDone.");
184
+ clearPathCache();
185
+ invalidateStateCache();
182
186
  const stateAfter = await deriveState(base);
183
187
  assertEq(stateAfter.phase, "complete", "deriveState returns complete after summary exists");
184
188
  assertEq(stateAfter.registry[0]?.status, "complete", "registry shows complete status");
@@ -2,7 +2,7 @@ import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync, existsSync
2
2
  import { join } from "node:path";
3
3
  import { tmpdir } from "node:os";
4
4
 
5
- import { formatDoctorReport, runGSDDoctor, summarizeDoctorIssues, filterDoctorIssues, selectDoctorScope } from "../doctor.js";
5
+ import { formatDoctorReport, runGSDDoctor, summarizeDoctorIssues, filterDoctorIssues, selectDoctorScope, validateTitle } from "../doctor.js";
6
6
  import { createTestContext } from './test-helpers.ts';
7
7
 
8
8
  const { assertEq, assertTrue, report } = createTestContext();
@@ -471,6 +471,120 @@ Discovered an issue.
471
471
  rmSync(mhBase, { recursive: true, force: true });
472
472
  }
473
473
 
474
+ // ─── validateTitle: em dash and slash detection ────────────────────────
475
+ console.log("\n=== validateTitle: returns null for clean titles ===");
476
+ {
477
+ assertEq(validateTitle("Foundation"), null, "clean title passes");
478
+ assertEq(validateTitle("Build Core Systems"), null, "clean title with spaces passes");
479
+ assertEq(validateTitle("API v2 Integration"), null, "clean title with version passes");
480
+ assertEq(validateTitle(""), null, "empty title passes");
481
+ }
482
+
483
+ console.log("\n=== validateTitle: detects em dash ===");
484
+ {
485
+ const result = validateTitle("Foundation — Build Core");
486
+ assertTrue(result !== null, "detects em dash in title");
487
+ assertTrue(result!.includes("em/en dash"), "message mentions em/en dash");
488
+ }
489
+
490
+ console.log("\n=== validateTitle: detects en dash ===");
491
+ {
492
+ const result = validateTitle("Phase 1 – Phase 2");
493
+ assertTrue(result !== null, "detects en dash in title");
494
+ assertTrue(result!.includes("em/en dash"), "message mentions em/en dash for en dash");
495
+ }
496
+
497
+ console.log("\n=== validateTitle: detects forward slash ===");
498
+ {
499
+ const result = validateTitle("Client/Server");
500
+ assertTrue(result !== null, "detects forward slash in title");
501
+ assertTrue(result!.includes("forward slash"), "message mentions forward slash");
502
+ }
503
+
504
+ console.log("\n=== validateTitle: detects both em dash and slash ===");
505
+ {
506
+ const result = validateTitle("Client — Server/API");
507
+ assertTrue(result !== null, "detects both delimiters");
508
+ assertTrue(result!.includes("em/en dash"), "message mentions em/en dash");
509
+ assertTrue(result!.includes("forward slash"), "message mentions forward slash");
510
+ }
511
+
512
+ // ─── doctor detects delimiter_in_title for milestone ───────────────────
513
+ console.log("\n=== doctor detects em dash in milestone title ===");
514
+ {
515
+ const dtBase = mkdtempSync(join(tmpdir(), "gsd-doctor-dt-test-"));
516
+ const dtGsd = join(dtBase, ".gsd");
517
+ const dtMDir = join(dtGsd, "milestones", "M001");
518
+ const dtSDir = join(dtMDir, "slices", "S01");
519
+ const dtTDir = join(dtSDir, "tasks");
520
+ mkdirSync(dtTDir, { recursive: true });
521
+
522
+ // Roadmap with em dash in milestone title
523
+ writeFileSync(join(dtMDir, "M001-ROADMAP.md"), `# M001: Foundation — Build Core\n\n## Slices\n- [ ] **S01: Demo Slice** \`risk:low\` \`depends:[]\`\n > After this: demo works\n`);
524
+ writeFileSync(join(dtSDir, "S01-PLAN.md"), `# S01: Demo Slice\n\n**Goal:** Demo\n**Demo:** Demo\n\n## Tasks\n- [ ] **T01: Implement** \`est:10m\`\n Task.\n`);
525
+ writeFileSync(join(dtTDir, "T01-PLAN.md"), `# T01: Implement\n\n## Steps\n\n1. Do the thing.\n`);
526
+
527
+ const report = await runGSDDoctor(dtBase, { fix: false });
528
+ const dtIssues = report.issues.filter(i => i.code === "delimiter_in_title");
529
+ assertTrue(dtIssues.length >= 1, "detects delimiter_in_title for milestone with em dash");
530
+ const milestoneIssue = dtIssues.find(i => i.scope === "milestone");
531
+ assertTrue(milestoneIssue !== undefined, "delimiter issue has milestone scope");
532
+ assertEq(milestoneIssue?.severity, "warning", "delimiter issue has warning severity");
533
+ assertEq(milestoneIssue?.unitId, "M001", "delimiter issue unitId is M001");
534
+ assertTrue(milestoneIssue?.message?.includes("em/en dash") ?? false, "issue message mentions em/en dash");
535
+ assertEq(milestoneIssue?.fixable, false, "delimiter issue is not auto-fixable");
536
+
537
+ rmSync(dtBase, { recursive: true, force: true });
538
+ }
539
+
540
+ // ─── doctor detects delimiter_in_title for slice ────────────────────────
541
+ console.log("\n=== doctor detects em dash in slice title ===");
542
+ {
543
+ const dtBase = mkdtempSync(join(tmpdir(), "gsd-doctor-dt-slice-"));
544
+ const dtGsd = join(dtBase, ".gsd");
545
+ const dtMDir = join(dtGsd, "milestones", "M001");
546
+ const dtSDir = join(dtMDir, "slices", "S01");
547
+ const dtTDir = join(dtSDir, "tasks");
548
+ mkdirSync(dtTDir, { recursive: true });
549
+
550
+ // Roadmap with em dash in slice title (milestone title is clean)
551
+ writeFileSync(join(dtMDir, "M001-ROADMAP.md"), `# M001: Clean Milestone\n\n## Slices\n- [ ] **S01: Core — Foundation** \`risk:low\` \`depends:[]\`\n > After this: demo works\n`);
552
+ writeFileSync(join(dtSDir, "S01-PLAN.md"), `# S01: Core — Foundation\n\n**Goal:** Demo\n**Demo:** Demo\n\n## Tasks\n- [ ] **T01: Implement** \`est:10m\`\n Task.\n`);
553
+ writeFileSync(join(dtTDir, "T01-PLAN.md"), `# T01: Implement\n\n## Steps\n\n1. Do the thing.\n`);
554
+
555
+ const report = await runGSDDoctor(dtBase, { fix: false });
556
+ const dtIssues = report.issues.filter(i => i.code === "delimiter_in_title");
557
+ assertTrue(dtIssues.length >= 1, "detects delimiter_in_title for slice with em dash");
558
+ const sliceIssue = dtIssues.find(i => i.scope === "slice");
559
+ assertTrue(sliceIssue !== undefined, "delimiter issue has slice scope");
560
+ assertEq(sliceIssue?.severity, "warning", "slice delimiter issue has warning severity");
561
+ assertEq(sliceIssue?.unitId, "M001/S01", "slice delimiter issue unitId is M001/S01");
562
+
563
+ rmSync(dtBase, { recursive: true, force: true });
564
+ }
565
+
566
+ // ─── doctor does NOT flag clean titles ──────────────────────────────────
567
+ console.log("\n=== doctor does NOT flag milestone with clean title ===");
568
+ {
569
+ const dtBase = mkdtempSync(join(tmpdir(), "gsd-doctor-dt-clean-"));
570
+ const dtGsd = join(dtBase, ".gsd");
571
+ const dtMDir = join(dtGsd, "milestones", "M001");
572
+ const dtSDir = join(dtMDir, "slices", "S01");
573
+ const dtTDir = join(dtSDir, "tasks");
574
+ mkdirSync(dtTDir, { recursive: true });
575
+
576
+ // Roadmap with clean titles (no delimiters)
577
+ writeFileSync(join(dtMDir, "M001-ROADMAP.md"), `# M001: Foundation Build Core\n\n## Slices\n- [ ] **S01: Demo Slice** \`risk:low\` \`depends:[]\`\n > After this: demo works\n`);
578
+ writeFileSync(join(dtSDir, "S01-PLAN.md"), `# S01: Demo Slice\n\n**Goal:** Demo\n**Demo:** Demo\n\n## Tasks\n- [ ] **T01: Implement** \`est:10m\`\n Task.\n`);
579
+ writeFileSync(join(dtTDir, "T01-PLAN.md"), `# T01: Implement\n\n## Steps\n\n1. Do the thing.\n`);
580
+
581
+ const report = await runGSDDoctor(dtBase, { fix: false });
582
+ const dtIssues = report.issues.filter(i => i.code === "delimiter_in_title");
583
+ assertEq(dtIssues.length, 0, "no delimiter_in_title issues for clean titles");
584
+
585
+ rmSync(dtBase, { recursive: true, force: true });
586
+ }
587
+
474
588
  report();
475
589
  }
476
590
 
@@ -2,8 +2,8 @@ import { mkdtempSync, mkdirSync, rmSync, writeFileSync, existsSync } from "node:
2
2
  import { join } from "node:path";
3
3
  import { tmpdir } from "node:os";
4
4
 
5
- import { deriveState } from "../state.js";
6
- import { resolveMilestoneFile } from "../paths.js";
5
+ import { deriveState, invalidateStateCache } from "../state.js";
6
+ import { resolveMilestoneFile, clearPathCache } from "../paths.js";
7
7
 
8
8
  let passed = 0;
9
9
  let failed = 0;
@@ -40,6 +40,8 @@ assert(
40
40
  const contextPath = join(gsd, "milestones", "M001", "M001-CONTEXT.md");
41
41
  writeFileSync(contextPath, "# M001: Full Context\n\nDeep discussion output.\n");
42
42
 
43
+ clearPathCache();
44
+ invalidateStateCache();
43
45
  const state2 = await deriveState(tmpBase);
44
46
  assert(
45
47
  state2.phase === "pre-planning",
@@ -65,6 +67,8 @@ assert(
65
67
  );
66
68
 
67
69
  // Step 4: After cleanup, state is still pre-planning (CONTEXT.md exists)
70
+ clearPathCache();
71
+ invalidateStateCache();
68
72
  const state3 = await deriveState(tmpBase);
69
73
  assert(
70
74
  state3.phase === "pre-planning",