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,297 @@
1
+ // GSD Extension — Hook Engine Tests (Post-Unit, Pre-Dispatch, State Persistence)
2
+ // Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
3
+
4
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync, existsSync, readFileSync } from "node:fs";
5
+ import { join } from "node:path";
6
+ import { tmpdir } from "node:os";
7
+ import { createTestContext } from "./test-helpers.ts";
8
+ import {
9
+ checkPostUnitHooks,
10
+ getActiveHook,
11
+ resetHookState,
12
+ isRetryPending,
13
+ consumeRetryTrigger,
14
+ resolveHookArtifactPath,
15
+ runPreDispatchHooks,
16
+ persistHookState,
17
+ restoreHookState,
18
+ clearPersistedHookState,
19
+ getHookStatus,
20
+ formatHookStatus,
21
+ } from "../post-unit-hooks.ts";
22
+
23
+ const { assertEq, assertTrue, assertMatch, report } = createTestContext();
24
+
25
+ // ─── Fixture Helpers ───────────────────────────────────────────────────────
26
+
27
+ function createFixtureBase(): string {
28
+ const base = mkdtempSync(join(tmpdir(), "gsd-hook-test-"));
29
+ mkdirSync(join(base, ".gsd", "M001", "slices", "S01", "tasks"), { recursive: true });
30
+ return base;
31
+ }
32
+
33
+ // ═══════════════════════════════════════════════════════════════════════════
34
+ // Phase 1: Post-Unit Hook Tests
35
+ // ═══════════════════════════════════════════════════════════════════════════
36
+
37
+ // ─── resolveHookArtifactPath ───────────────────────────────────────────────
38
+
39
+ console.log("\n=== resolveHookArtifactPath ===");
40
+
41
+ {
42
+ const base = "/project";
43
+
44
+ // Task-level
45
+ const taskPath = resolveHookArtifactPath(base, "M001/S01/T01", "REVIEW-PASS.md");
46
+ assertEq(
47
+ taskPath,
48
+ join(base, ".gsd", "M001", "slices", "S01", "tasks", "T01-REVIEW-PASS.md"),
49
+ "task-level artifact path",
50
+ );
51
+
52
+ // Slice-level
53
+ const slicePath = resolveHookArtifactPath(base, "M001/S01", "REVIEW-PASS.md");
54
+ assertEq(
55
+ slicePath,
56
+ join(base, ".gsd", "M001", "slices", "S01", "REVIEW-PASS.md"),
57
+ "slice-level artifact path",
58
+ );
59
+
60
+ // Milestone-level
61
+ const milestonePath = resolveHookArtifactPath(base, "M001", "REVIEW-PASS.md");
62
+ assertEq(
63
+ milestonePath,
64
+ join(base, ".gsd", "M001", "REVIEW-PASS.md"),
65
+ "milestone-level artifact path",
66
+ );
67
+ }
68
+
69
+ // ─── resetHookState ────────────────────────────────────────────────────────
70
+
71
+ console.log("\n=== resetHookState ===");
72
+
73
+ {
74
+ resetHookState();
75
+ assertEq(getActiveHook(), null, "no active hook after reset");
76
+ assertTrue(!isRetryPending(), "no retry pending after reset");
77
+ assertEq(consumeRetryTrigger(), null, "no retry trigger after reset");
78
+ }
79
+
80
+ // ─── checkPostUnitHooks with no hooks configured ───────────────────────────
81
+
82
+ console.log("\n=== No hooks configured ===");
83
+
84
+ {
85
+ resetHookState();
86
+ const base = createFixtureBase();
87
+ try {
88
+ const result = checkPostUnitHooks("execute-task", "M001/S01/T01", base);
89
+ assertEq(result, null, "returns null when no hooks configured");
90
+ } finally {
91
+ rmSync(base, { recursive: true, force: true });
92
+ }
93
+ }
94
+
95
+ // ─── Hook units don't trigger hooks (no hook-on-hook) ──────────────────────
96
+
97
+ console.log("\n=== Hook-on-hook prevention ===");
98
+
99
+ {
100
+ resetHookState();
101
+ const base = createFixtureBase();
102
+ try {
103
+ const result = checkPostUnitHooks("hook/code-review", "M001/S01/T01", base);
104
+ assertEq(result, null, "hook units don't trigger other hooks");
105
+ } finally {
106
+ rmSync(base, { recursive: true, force: true });
107
+ }
108
+ }
109
+
110
+ // ─── consumeRetryTrigger clears state ──────────────────────────────────────
111
+
112
+ console.log("\n=== consumeRetryTrigger clears state ===");
113
+
114
+ {
115
+ resetHookState();
116
+ assertEq(consumeRetryTrigger(), null, "no trigger initially");
117
+ assertTrue(!isRetryPending(), "no retry initially");
118
+ }
119
+
120
+ // ─── Variable substitution in prompts ──────────────────────────────────────
121
+
122
+ console.log("\n=== Variable substitution ===");
123
+
124
+ {
125
+ const base = "/project";
126
+
127
+ // 3-part ID
128
+ const path3 = resolveHookArtifactPath(base, "M002/S03/T05", "result.md");
129
+ assertTrue(path3.includes("M002"), "3-part ID extracts milestoneId");
130
+ assertTrue(path3.includes("S03"), "3-part ID extracts sliceId");
131
+ assertTrue(path3.includes("T05"), "3-part ID extracts taskId");
132
+
133
+ // 2-part ID
134
+ const path2 = resolveHookArtifactPath(base, "M002/S03", "result.md");
135
+ assertTrue(path2.includes("M002"), "2-part ID extracts milestoneId");
136
+ assertTrue(path2.includes("S03"), "2-part ID extracts sliceId");
137
+
138
+ // 1-part ID
139
+ const path1 = resolveHookArtifactPath(base, "M002", "result.md");
140
+ assertTrue(path1.includes("M002"), "1-part ID extracts milestoneId");
141
+ }
142
+
143
+ // ═══════════════════════════════════════════════════════════════════════════
144
+ // Phase 2: Pre-Dispatch Hook Tests
145
+ // ═══════════════════════════════════════════════════════════════════════════
146
+
147
+ console.log("\n=== Pre-dispatch: no hooks configured ===");
148
+
149
+ {
150
+ const base = createFixtureBase();
151
+ try {
152
+ const result = runPreDispatchHooks("execute-task", "M001/S01/T01", "original prompt", base);
153
+ assertEq(result.action, "proceed", "proceeds when no hooks");
154
+ assertEq(result.prompt, "original prompt", "prompt unchanged");
155
+ assertEq(result.firedHooks.length, 0, "no hooks fired");
156
+ } finally {
157
+ rmSync(base, { recursive: true, force: true });
158
+ }
159
+ }
160
+
161
+ console.log("\n=== Pre-dispatch: hook units bypass ===");
162
+
163
+ {
164
+ const base = createFixtureBase();
165
+ try {
166
+ const result = runPreDispatchHooks("hook/review", "M001/S01/T01", "hook prompt", base);
167
+ assertEq(result.action, "proceed", "hook units always proceed");
168
+ assertEq(result.prompt, "hook prompt", "hook prompt unchanged");
169
+ assertEq(result.firedHooks.length, 0, "no hooks fired for hook units");
170
+ } finally {
171
+ rmSync(base, { recursive: true, force: true });
172
+ }
173
+ }
174
+
175
+ // ═══════════════════════════════════════════════════════════════════════════
176
+ // Phase 3: State Persistence Tests
177
+ // ═══════════════════════════════════════════════════════════════════════════
178
+
179
+ console.log("\n=== State persistence: persist and restore ===");
180
+
181
+ {
182
+ const base = createFixtureBase();
183
+ try {
184
+ resetHookState();
185
+
186
+ // Persist empty state
187
+ persistHookState(base);
188
+ const filePath = join(base, ".gsd", "hook-state.json");
189
+ assertTrue(existsSync(filePath), "hook-state.json created");
190
+
191
+ const content = JSON.parse(readFileSync(filePath, "utf-8"));
192
+ assertEq(typeof content.savedAt, "string", "savedAt is a string");
193
+ assertEq(Object.keys(content.cycleCounts).length, 0, "empty cycle counts");
194
+ } finally {
195
+ rmSync(base, { recursive: true, force: true });
196
+ }
197
+ }
198
+
199
+ console.log("\n=== State persistence: restore from disk ===");
200
+
201
+ {
202
+ const base = createFixtureBase();
203
+ try {
204
+ resetHookState();
205
+
206
+ // Write a state file with some cycle counts
207
+ const stateFile = join(base, ".gsd", "hook-state.json");
208
+ writeFileSync(stateFile, JSON.stringify({
209
+ cycleCounts: {
210
+ "review/execute-task/M001/S01/T01": 2,
211
+ "simplify/execute-task/M001/S01/T02": 1,
212
+ },
213
+ savedAt: new Date().toISOString(),
214
+ }), "utf-8");
215
+
216
+ // Restore
217
+ restoreHookState(base);
218
+
219
+ // Verify by persisting and reading back
220
+ persistHookState(base);
221
+ const restored = JSON.parse(readFileSync(stateFile, "utf-8"));
222
+ assertEq(restored.cycleCounts["review/execute-task/M001/S01/T01"], 2, "cycle count restored for review");
223
+ assertEq(restored.cycleCounts["simplify/execute-task/M001/S01/T02"], 1, "cycle count restored for simplify");
224
+ } finally {
225
+ rmSync(base, { recursive: true, force: true });
226
+ }
227
+ }
228
+
229
+ console.log("\n=== State persistence: clear ===");
230
+
231
+ {
232
+ const base = createFixtureBase();
233
+ try {
234
+ resetHookState();
235
+
236
+ // Write then clear
237
+ const stateFile = join(base, ".gsd", "hook-state.json");
238
+ writeFileSync(stateFile, JSON.stringify({
239
+ cycleCounts: { "review/execute-task/M001/S01/T01": 3 },
240
+ savedAt: new Date().toISOString(),
241
+ }), "utf-8");
242
+
243
+ clearPersistedHookState(base);
244
+
245
+ const cleared = JSON.parse(readFileSync(stateFile, "utf-8"));
246
+ assertEq(Object.keys(cleared.cycleCounts).length, 0, "cycle counts cleared");
247
+ } finally {
248
+ rmSync(base, { recursive: true, force: true });
249
+ }
250
+ }
251
+
252
+ console.log("\n=== State persistence: restore handles missing file ===");
253
+
254
+ {
255
+ const base = createFixtureBase();
256
+ try {
257
+ resetHookState();
258
+ // Should not throw
259
+ restoreHookState(base);
260
+ assertEq(getActiveHook(), null, "no active hook after restore from missing file");
261
+ } finally {
262
+ rmSync(base, { recursive: true, force: true });
263
+ }
264
+ }
265
+
266
+ console.log("\n=== State persistence: restore handles corrupt file ===");
267
+
268
+ {
269
+ const base = createFixtureBase();
270
+ try {
271
+ resetHookState();
272
+ writeFileSync(join(base, ".gsd", "hook-state.json"), "not json", "utf-8");
273
+ // Should not throw
274
+ restoreHookState(base);
275
+ assertEq(getActiveHook(), null, "no active hook after corrupt restore");
276
+ } finally {
277
+ rmSync(base, { recursive: true, force: true });
278
+ }
279
+ }
280
+
281
+ // ═══════════════════════════════════════════════════════════════════════════
282
+ // Phase 3: Hook Status Reporting Tests
283
+ // ═══════════════════════════════════════════════════════════════════════════
284
+
285
+ console.log("\n=== Hook status: no hooks ===");
286
+
287
+ {
288
+ resetHookState();
289
+ const entries = getHookStatus();
290
+ // No preferences file = no hooks
291
+ assertEq(entries.length, 0, "no entries when no hooks configured");
292
+
293
+ const formatted = formatHookStatus();
294
+ assertMatch(formatted, /No hooks configured/, "status message says no hooks");
295
+ }
296
+
297
+ report();
@@ -0,0 +1,226 @@
1
+ // GSD Extension — Hook Preferences Parsing Tests (Post-Unit + Pre-Dispatch)
2
+ // Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
3
+
4
+ import { createTestContext } from "./test-helpers.ts";
5
+
6
+ const { assertEq, assertTrue, report } = createTestContext();
7
+
8
+ // ═══════════════════════════════════════════════════════════════════════════
9
+ // Phase 1: Post-Unit Hook Config Tests
10
+ // ═══════════════════════════════════════════════════════════════════════════
11
+
12
+ console.log("\n=== Post-unit hook config validation ===");
13
+
14
+ {
15
+ const validHook = {
16
+ name: "test-hook",
17
+ after: ["execute-task"],
18
+ prompt: "Test prompt",
19
+ max_cycles: 2,
20
+ model: "claude-sonnet-4-6",
21
+ artifact: "TEST-RESULT.md",
22
+ retry_on: "TEST-ISSUES.md",
23
+ enabled: true,
24
+ };
25
+
26
+ assertEq(validHook.name, "test-hook", "valid hook has name");
27
+ assertEq(validHook.after.length, 1, "valid hook has one after entry");
28
+ assertEq(validHook.after[0], "execute-task", "valid hook triggers after execute-task");
29
+ assertTrue(validHook.max_cycles! <= 10, "max_cycles within limit");
30
+ assertTrue(validHook.max_cycles! >= 1, "max_cycles above minimum");
31
+ }
32
+
33
+ console.log("\n=== max_cycles clamping ===");
34
+
35
+ {
36
+ const clampedHigh = Math.max(1, Math.min(10, Math.round(15)));
37
+ assertEq(clampedHigh, 10, "max_cycles above 10 clamped to 10");
38
+
39
+ const clampedLow = Math.max(1, Math.min(10, Math.round(0)));
40
+ assertEq(clampedLow, 1, "max_cycles below 1 clamped to 1");
41
+
42
+ const clampedNeg = Math.max(1, Math.min(10, Math.round(-5)));
43
+ assertEq(clampedNeg, 1, "negative max_cycles clamped to 1");
44
+
45
+ const normal = Math.max(1, Math.min(10, Math.round(3)));
46
+ assertEq(normal, 3, "normal max_cycles passes through");
47
+ }
48
+
49
+ console.log("\n=== Post-unit hook merging ===");
50
+
51
+ {
52
+ const baseHooks = [
53
+ { name: "review", after: ["execute-task"], prompt: "base prompt" },
54
+ { name: "lint", after: ["plan-slice"], prompt: "lint code" },
55
+ ];
56
+
57
+ const overrideHooks = [
58
+ { name: "review", after: ["execute-task", "complete-slice"], prompt: "override prompt" },
59
+ { name: "security", after: ["execute-task"], prompt: "security check" },
60
+ ];
61
+
62
+ const merged = [...baseHooks];
63
+ for (const hook of overrideHooks) {
64
+ const idx = merged.findIndex(h => h.name === hook.name);
65
+ if (idx >= 0) {
66
+ merged[idx] = hook;
67
+ } else {
68
+ merged.push(hook);
69
+ }
70
+ }
71
+
72
+ assertEq(merged.length, 3, "merged has 3 hooks");
73
+ assertEq(merged[0].prompt, "override prompt", "review hook was overridden");
74
+ assertEq(merged[0].after.length, 2, "overridden review has 2 after entries");
75
+ assertEq(merged[1].name, "lint", "lint kept from base");
76
+ assertEq(merged[2].name, "security", "security added from override");
77
+ }
78
+
79
+ // ═══════════════════════════════════════════════════════════════════════════
80
+ // Phase 2: Pre-Dispatch Hook Config Tests
81
+ // ═══════════════════════════════════════════════════════════════════════════
82
+
83
+ console.log("\n=== Pre-dispatch hook config shape ===");
84
+
85
+ {
86
+ const modifyHook = {
87
+ name: "inject-context",
88
+ before: ["execute-task"],
89
+ action: "modify" as const,
90
+ prepend: "Remember to follow coding conventions.",
91
+ append: "Run tests after making changes.",
92
+ enabled: true,
93
+ };
94
+
95
+ assertEq(modifyHook.name, "inject-context", "modify hook has name");
96
+ assertEq(modifyHook.action, "modify", "action is modify");
97
+ assertTrue(!!modifyHook.prepend, "has prepend text");
98
+ assertTrue(!!modifyHook.append, "has append text");
99
+ }
100
+
101
+ {
102
+ const skipHook = {
103
+ name: "skip-research",
104
+ before: ["research-slice"],
105
+ action: "skip" as const,
106
+ skip_if: "RESEARCH-DONE.md",
107
+ enabled: true,
108
+ };
109
+
110
+ assertEq(skipHook.action, "skip", "action is skip");
111
+ assertEq(skipHook.skip_if, "RESEARCH-DONE.md", "has skip condition");
112
+ }
113
+
114
+ {
115
+ const replaceHook = {
116
+ name: "custom-planning",
117
+ before: ["plan-slice"],
118
+ action: "replace" as const,
119
+ prompt: "Use custom planning approach for {sliceId}",
120
+ unit_type: "custom-plan",
121
+ model: "claude-opus-4-6",
122
+ enabled: true,
123
+ };
124
+
125
+ assertEq(replaceHook.action, "replace", "action is replace");
126
+ assertTrue(!!replaceHook.prompt, "replace hook has prompt");
127
+ assertEq(replaceHook.unit_type, "custom-plan", "has unit_type override");
128
+ }
129
+
130
+ console.log("\n=== Pre-dispatch action validation ===");
131
+
132
+ {
133
+ const validActions = new Set(["modify", "skip", "replace"]);
134
+ assertTrue(validActions.has("modify"), "modify is valid");
135
+ assertTrue(validActions.has("skip"), "skip is valid");
136
+ assertTrue(validActions.has("replace"), "replace is valid");
137
+ assertTrue(!validActions.has("delete"), "delete is not valid");
138
+ assertTrue(!validActions.has(""), "empty string is not valid");
139
+ }
140
+
141
+ console.log("\n=== Pre-dispatch hook merging ===");
142
+
143
+ {
144
+ const baseHooks = [
145
+ { name: "inject", before: ["execute-task"], action: "modify" as const, prepend: "base" },
146
+ ];
147
+
148
+ const overrideHooks = [
149
+ { name: "inject", before: ["execute-task"], action: "modify" as const, prepend: "override" },
150
+ { name: "gate", before: ["plan-slice"], action: "skip" as const },
151
+ ];
152
+
153
+ const merged = [...baseHooks];
154
+ for (const hook of overrideHooks) {
155
+ const idx = merged.findIndex(h => h.name === hook.name);
156
+ if (idx >= 0) {
157
+ merged[idx] = hook;
158
+ } else {
159
+ merged.push(hook);
160
+ }
161
+ }
162
+
163
+ assertEq(merged.length, 2, "merged has 2 pre-dispatch hooks");
164
+ assertEq(merged[0].prepend, "override", "inject hook overridden");
165
+ assertEq(merged[1].name, "gate", "gate hook added");
166
+ }
167
+
168
+ // ═══════════════════════════════════════════════════════════════════════════
169
+ // Known unit types validation
170
+ // ═══════════════════════════════════════════════════════════════════════════
171
+
172
+ console.log("\n=== Known unit types ===");
173
+
174
+ {
175
+ const knownUnitTypes = new Set([
176
+ "research-milestone", "plan-milestone", "research-slice", "plan-slice",
177
+ "execute-task", "complete-slice", "replan-slice", "reassess-roadmap",
178
+ "run-uat", "fix-merge", "complete-milestone",
179
+ ]);
180
+
181
+ assertTrue(knownUnitTypes.has("execute-task"), "execute-task is known");
182
+ assertTrue(knownUnitTypes.has("complete-slice"), "complete-slice is known");
183
+ assertTrue(knownUnitTypes.has("plan-slice"), "plan-slice is known");
184
+ assertTrue(!knownUnitTypes.has("hook/review"), "hook types are not in known set");
185
+ assertTrue(!knownUnitTypes.has("invalid-type"), "invalid types are not in known set");
186
+ }
187
+
188
+ // ═══════════════════════════════════════════════════════════════════════════
189
+ // Preferences YAML format verification
190
+ // ═══════════════════════════════════════════════════════════════════════════
191
+
192
+ console.log("\n=== Preferences YAML format ===");
193
+
194
+ {
195
+ const prefsContent = [
196
+ "---",
197
+ "version: 1",
198
+ "post_unit_hooks:",
199
+ " - name: code-review",
200
+ " after:",
201
+ " - execute-task",
202
+ " prompt: Review the changes",
203
+ " max_cycles: 3",
204
+ " artifact: REVIEW-PASS.md",
205
+ " retry_on: REVIEW-ISSUES.md",
206
+ "pre_dispatch_hooks:",
207
+ " - name: inject-conventions",
208
+ " before:",
209
+ " - execute-task",
210
+ " action: modify",
211
+ " append: Follow project coding conventions",
212
+ " - name: custom-research",
213
+ " before:",
214
+ " - research-slice",
215
+ " action: replace",
216
+ " prompt: Custom research prompt",
217
+ "---",
218
+ ].join("\n");
219
+
220
+ assertTrue(prefsContent.includes("post_unit_hooks:"), "has post_unit_hooks key");
221
+ assertTrue(prefsContent.includes("pre_dispatch_hooks:"), "has pre_dispatch_hooks key");
222
+ assertTrue(prefsContent.includes("action: modify"), "has modify action");
223
+ assertTrue(prefsContent.includes("action: replace"), "has replace action");
224
+ }
225
+
226
+ report();
@@ -67,6 +67,18 @@ async function main(): Promise<void> {
67
67
  assertEq('M001-abc123: Title'.replace(TITLE_STRIP_RE, ''), 'Title', 'strips M001-abc123: Title → Title');
68
68
  assertEq('M042-z9a8b7: Dashboard'.replace(TITLE_STRIP_RE, ''), 'Dashboard', 'strips M042-z9a8b7: Dashboard');
69
69
 
70
+ // Em dash in title — current format (M001: Title) correctly preserves em dash in title body
71
+ assertEq(
72
+ 'M001: Foundation — Build Core'.replace(TITLE_STRIP_RE, ''),
73
+ 'Foundation — Build Core',
74
+ 'strips M001: prefix and preserves em dash in title body',
75
+ );
76
+ assertEq(
77
+ 'M001-abc123: Foundation — Build Core'.replace(TITLE_STRIP_RE, ''),
78
+ 'Foundation — Build Core',
79
+ 'strips M001-abc123: prefix and preserves em dash in title body (unique format)',
80
+ );
81
+
70
82
  // Edge case: dash-style separator (M001 — Title: Subtitle preserves colon in body)
71
83
  assertEq(
72
84
  'M001 — Unique Milestone IDs: Foo'.replace(TITLE_STRIP_RE, ''),
@@ -8,6 +8,7 @@ import {
8
8
  readUnitRuntimeRecord,
9
9
  writeUnitRuntimeRecord,
10
10
  } from "../unit-runtime.ts";
11
+ import { clearPathCache } from '../paths.ts';
11
12
  import { createTestContext } from './test-helpers.ts';
12
13
 
13
14
  const { assertEq, assertTrue, report } = createTestContext();
@@ -48,6 +49,7 @@ console.log("\n=== execute-task durability inspection ===");
48
49
  "utf-8",
49
50
  );
50
51
  writeFileSync(join(base, ".gsd", "STATE.md"), "## Next Action\nExecute T10 for S02: next thing\n", "utf-8");
52
+ clearPathCache();
51
53
 
52
54
  status = await inspectExecuteTaskDurability(base, "M100/S02/T09");
53
55
  assertEq(status!.summaryExists, true, "summary found after write");
@@ -128,6 +130,7 @@ console.log("\n=== must-haves: partially mentioned in summary ===");
128
130
  );
129
131
  writeFileSync(join(mhBase, ".gsd", "STATE.md"), "## Next Action\nExecute T02 for S02: next thing\n", "utf-8");
130
132
 
133
+ clearPathCache();
131
134
  const status = await inspectExecuteTaskDurability(mhBase, "M200/S02/T01");
132
135
  assertTrue(status !== null, "mh-partial: status exists");
133
136
  assertEq(status!.mustHaveCount, 3, "mh-partial: mustHaveCount is 3");
@@ -155,6 +158,7 @@ console.log("\n=== must-haves: no task plan file ===");
155
158
  );
156
159
  writeFileSync(join(mhBase, ".gsd", "STATE.md"), "## Next Action\nExecute T02 for S03: next thing\n", "utf-8");
157
160
 
161
+ clearPathCache();
158
162
  const status = await inspectExecuteTaskDurability(mhBase, "M200/S03/T01");
159
163
  assertTrue(status !== null, "mh-noplan: status exists");
160
164
  assertEq(status!.mustHaveCount, 0, "mh-noplan: mustHaveCount is 0 when no task plan");
@@ -179,6 +183,7 @@ console.log("\n=== must-haves: present but no summary file ===");
179
183
  );
180
184
  writeFileSync(join(mhBase, ".gsd", "STATE.md"), "## Next Action\nExecute T01 for S04: build parser\n", "utf-8");
181
185
 
186
+ clearPathCache();
182
187
  const status = await inspectExecuteTaskDurability(mhBase, "M200/S04/T01");
183
188
  assertTrue(status !== null, "mh-nosummary: status exists");
184
189
  assertEq(status!.mustHaveCount, 2, "mh-nosummary: mustHaveCount is 2");
@@ -210,6 +215,7 @@ console.log("\n=== must-haves: substring matching (no backtick tokens) ===");
210
215
  );
211
216
  writeFileSync(join(mhBase, ".gsd", "STATE.md"), "## Next Action\nExecute T02 for S05: next thing\n", "utf-8");
212
217
 
218
+ clearPathCache();
213
219
  const status = await inspectExecuteTaskDurability(mhBase, "M200/S05/T01");
214
220
  assertTrue(status !== null, "mh-substr: status exists");
215
221
  assertEq(status!.mustHaveCount, 3, "mh-substr: mustHaveCount is 3");
@@ -79,7 +79,7 @@ writeFileSync(
79
79
  "utf-8",
80
80
  );
81
81
  run("git add .", base);
82
- run("git commit -m 'chore: init'", base);
82
+ run('git commit -m "chore: init"', base);
83
83
 
84
84
  async function main(): Promise<void> {
85
85
  // ── Verify main tree baseline ──────────────────────────────────────────────
@@ -124,7 +124,7 @@ async function main(): Promise<void> {
124
124
  console.log("\n=== Work and merge slice in worktree ===");
125
125
  writeFileSync(join(wt.path, "feature.txt"), "new feature\n", "utf-8");
126
126
  run("git add .", wt.path);
127
- run("git commit -m 'feat: add feature'", wt.path);
127
+ run('git commit -m "feat: add feature"', wt.path);
128
128
 
129
129
  // switchToMain should go to worktree/alpha, NOT main
130
130
  switchToMain(wt.path);
@@ -150,7 +150,7 @@ async function main(): Promise<void> {
150
150
 
151
151
  writeFileSync(join(wt.path, "feature2.txt"), "second feature\n", "utf-8");
152
152
  run("git add .", wt.path);
153
- run("git commit -m 'feat: add feature 2'", wt.path);
153
+ run('git commit -m "feat: add feature 2"', wt.path);
154
154
 
155
155
  switchToMain(wt.path);
156
156
  const merge2 = mergeSliceToMain(wt.path, "M001", "S02", "Second");
@@ -167,7 +167,7 @@ async function main(): Promise<void> {
167
167
 
168
168
  writeFileSync(join(base, "main-feature.txt"), "main work\n", "utf-8");
169
169
  run("git add .", base);
170
- run("git commit -m 'feat: main work'", base);
170
+ run('git commit -m "feat: main work"', base);
171
171
 
172
172
  switchToMain(base);
173
173
  assertEq(getCurrentBranch(base), "main", "main tree switchToMain goes to main");
@@ -23,8 +23,8 @@ function run(command: string, cwd: string): string {
23
23
  // Set up a test repo
24
24
  const base = mkdtempSync(join(tmpdir(), "gsd-worktree-mgr-test-"));
25
25
  run("git init -b main", base);
26
- run("git config user.name 'Pi Test'", base);
27
- run("git config user.email 'pi@example.com'", base);
26
+ run('git config user.name "Pi Test"', base);
27
+ run('git config user.email "pi@example.com"', base);
28
28
 
29
29
  // Create initial project structure
30
30
  mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
@@ -35,7 +35,7 @@ writeFileSync(
35
35
  "utf-8",
36
36
  );
37
37
  run("git add .", base);
38
- run("git commit -m 'chore: init'", base);
38
+ run('git commit -m "chore: init"', base);
39
39
 
40
40
  async function main(): Promise<void> {
41
41
  console.log("\n=== worktreeBranchName ===");
@@ -95,7 +95,7 @@ async function main(): Promise<void> {
95
95
  "utf-8",
96
96
  );
97
97
  run("git add .", wtPath);
98
- run("git commit -m 'feat: add M002 and update M001'", wtPath);
98
+ run('git commit -m "feat: add M002 and update M001"', wtPath);
99
99
 
100
100
  console.log("\n=== diffWorktreeGSD ===");
101
101
  const diff = diffWorktreeGSD(base, "feature-x");