gsd-pi 2.18.0 → 2.20.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 (289) hide show
  1. package/README.md +5 -1
  2. package/dist/cli.js +3 -3
  3. package/dist/onboarding.d.ts +3 -1
  4. package/dist/onboarding.js +77 -3
  5. package/dist/remote-questions-config.d.ts +1 -1
  6. package/dist/resources/extensions/google-search/index.ts +164 -47
  7. package/dist/resources/extensions/gsd/auto-dashboard.ts +14 -2
  8. package/dist/resources/extensions/gsd/auto-prompts.ts +148 -39
  9. package/dist/resources/extensions/gsd/auto-worktree.ts +93 -9
  10. package/dist/resources/extensions/gsd/auto.ts +690 -39
  11. package/dist/resources/extensions/gsd/captures.ts +384 -0
  12. package/dist/resources/extensions/gsd/commands.ts +654 -36
  13. package/dist/resources/extensions/gsd/complexity-classifier.ts +322 -0
  14. package/dist/resources/extensions/gsd/context-budget.ts +243 -0
  15. package/dist/resources/extensions/gsd/context-store.ts +195 -0
  16. package/dist/resources/extensions/gsd/dashboard-overlay.ts +51 -3
  17. package/dist/resources/extensions/gsd/db-writer.ts +341 -0
  18. package/dist/resources/extensions/gsd/debug-logger.ts +178 -0
  19. package/dist/resources/extensions/gsd/dispatch-guard.ts +0 -1
  20. package/dist/resources/extensions/gsd/docs/preferences-reference.md +54 -0
  21. package/dist/resources/extensions/gsd/doctor-proactive.ts +286 -0
  22. package/dist/resources/extensions/gsd/doctor.ts +283 -2
  23. package/dist/resources/extensions/gsd/export.ts +81 -2
  24. package/dist/resources/extensions/gsd/files.ts +39 -9
  25. package/dist/resources/extensions/gsd/git-service.ts +6 -0
  26. package/dist/resources/extensions/gsd/gsd-db.ts +752 -0
  27. package/dist/resources/extensions/gsd/guided-flow.ts +26 -1
  28. package/dist/resources/extensions/gsd/history.ts +0 -1
  29. package/dist/resources/extensions/gsd/index.ts +277 -1
  30. package/dist/resources/extensions/gsd/md-importer.ts +526 -0
  31. package/dist/resources/extensions/gsd/metrics.ts +84 -0
  32. package/dist/resources/extensions/gsd/model-cost-table.ts +65 -0
  33. package/dist/resources/extensions/gsd/model-router.ts +256 -0
  34. package/dist/resources/extensions/gsd/notifications.ts +0 -1
  35. package/dist/resources/extensions/gsd/post-unit-hooks.ts +72 -2
  36. package/dist/resources/extensions/gsd/preferences.ts +198 -150
  37. package/dist/resources/extensions/gsd/prompt-loader.ts +45 -9
  38. package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -5
  39. package/dist/resources/extensions/gsd/prompts/heal-skill.md +45 -0
  40. package/dist/resources/extensions/gsd/prompts/plan-slice.md +5 -1
  41. package/dist/resources/extensions/gsd/prompts/quick-task.md +48 -0
  42. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -0
  43. package/dist/resources/extensions/gsd/prompts/replan-slice.md +8 -0
  44. package/dist/resources/extensions/gsd/prompts/system.md +2 -1
  45. package/dist/resources/extensions/gsd/prompts/triage-captures.md +62 -0
  46. package/dist/resources/extensions/gsd/quick.ts +156 -0
  47. package/dist/resources/extensions/gsd/skill-discovery.ts +5 -3
  48. package/dist/resources/extensions/gsd/skill-health.ts +417 -0
  49. package/dist/resources/extensions/gsd/skill-telemetry.ts +127 -0
  50. package/dist/resources/extensions/gsd/state.ts +30 -0
  51. package/dist/resources/extensions/gsd/templates/preferences.md +1 -0
  52. package/dist/resources/extensions/gsd/tests/captures.test.ts +438 -0
  53. package/dist/resources/extensions/gsd/tests/complexity-classifier.test.ts +181 -0
  54. package/dist/resources/extensions/gsd/tests/context-budget.test.ts +283 -0
  55. package/dist/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
  56. package/dist/resources/extensions/gsd/tests/context-store.test.ts +462 -0
  57. package/dist/resources/extensions/gsd/tests/continue-here.test.ts +204 -0
  58. package/dist/resources/extensions/gsd/tests/dashboard-budget.test.ts +346 -0
  59. package/dist/resources/extensions/gsd/tests/db-writer.test.ts +602 -0
  60. package/dist/resources/extensions/gsd/tests/debug-logger.test.ts +185 -0
  61. package/dist/resources/extensions/gsd/tests/derive-state-db.test.ts +406 -0
  62. package/dist/resources/extensions/gsd/tests/dispatch-guard.test.ts +0 -1
  63. package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +22 -0
  64. package/dist/resources/extensions/gsd/tests/doctor-proactive.test.ts +244 -0
  65. package/dist/resources/extensions/gsd/tests/doctor-runtime.test.ts +303 -0
  66. package/dist/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -0
  67. package/dist/resources/extensions/gsd/tests/gsd-db.test.ts +353 -0
  68. package/dist/resources/extensions/gsd/tests/gsd-inspect.test.ts +125 -0
  69. package/dist/resources/extensions/gsd/tests/gsd-tools.test.ts +326 -0
  70. package/dist/resources/extensions/gsd/tests/integration-edge.test.ts +228 -0
  71. package/dist/resources/extensions/gsd/tests/integration-lifecycle.test.ts +277 -0
  72. package/dist/resources/extensions/gsd/tests/md-importer.test.ts +411 -0
  73. package/dist/resources/extensions/gsd/tests/metrics.test.ts +197 -0
  74. package/dist/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +144 -0
  75. package/dist/resources/extensions/gsd/tests/model-cost-table.test.ts +69 -0
  76. package/dist/resources/extensions/gsd/tests/model-isolation.test.ts +99 -0
  77. package/dist/resources/extensions/gsd/tests/model-router.test.ts +167 -0
  78. package/dist/resources/extensions/gsd/tests/parsers.test.ts +40 -0
  79. package/dist/resources/extensions/gsd/tests/post-unit-hooks.test.ts +41 -1
  80. package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +0 -1
  81. package/dist/resources/extensions/gsd/tests/preferences-hooks.test.ts +0 -1
  82. package/dist/resources/extensions/gsd/tests/preferences-mode.test.ts +110 -0
  83. package/dist/resources/extensions/gsd/tests/preferences-models.test.ts +0 -1
  84. package/dist/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +464 -0
  85. package/dist/resources/extensions/gsd/tests/prompt-db.test.ts +385 -0
  86. package/dist/resources/extensions/gsd/tests/remote-questions.test.ts +488 -1
  87. package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +17 -29
  88. package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +2 -8
  89. package/dist/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
  90. package/dist/resources/extensions/gsd/tests/skill-lifecycle.test.ts +126 -0
  91. package/dist/resources/extensions/gsd/tests/stop-auto-remote.test.ts +31 -8
  92. package/dist/resources/extensions/gsd/tests/token-savings.test.ts +366 -0
  93. package/dist/resources/extensions/gsd/tests/triage-dispatch.test.ts +224 -0
  94. package/dist/resources/extensions/gsd/tests/triage-resolution.test.ts +215 -0
  95. package/dist/resources/extensions/gsd/tests/unit-runtime.test.ts +25 -1
  96. package/dist/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +145 -0
  97. package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +290 -0
  98. package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +120 -0
  99. package/dist/resources/extensions/gsd/tests/visualizer-views.test.ts +478 -0
  100. package/dist/resources/extensions/gsd/tests/worktree-db-integration.test.ts +205 -0
  101. package/dist/resources/extensions/gsd/tests/worktree-db.test.ts +442 -0
  102. package/dist/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +165 -0
  103. package/dist/resources/extensions/gsd/triage-resolution.ts +200 -0
  104. package/dist/resources/extensions/gsd/triage-ui.ts +175 -0
  105. package/dist/resources/extensions/gsd/types.ts +29 -0
  106. package/dist/resources/extensions/gsd/undo.ts +0 -1
  107. package/dist/resources/extensions/gsd/unit-runtime.ts +5 -1
  108. package/dist/resources/extensions/gsd/visualizer-data.ts +505 -0
  109. package/dist/resources/extensions/gsd/visualizer-overlay.ts +337 -0
  110. package/dist/resources/extensions/gsd/visualizer-views.ts +755 -0
  111. package/dist/resources/extensions/gsd/worktree-command.ts +18 -0
  112. package/dist/resources/extensions/gsd/worktree-manager.ts +11 -4
  113. package/dist/resources/extensions/remote-questions/config.ts +4 -2
  114. package/dist/resources/extensions/remote-questions/discord-adapter.ts +35 -4
  115. package/dist/resources/extensions/remote-questions/format.ts +166 -14
  116. package/dist/resources/extensions/remote-questions/manager.ts +14 -4
  117. package/dist/resources/extensions/remote-questions/remote-command.ts +100 -4
  118. package/dist/resources/extensions/remote-questions/slack-adapter.ts +58 -2
  119. package/dist/resources/extensions/remote-questions/telegram-adapter.ts +161 -0
  120. package/dist/resources/extensions/remote-questions/types.ts +2 -1
  121. package/dist/resources/extensions/ttsr/ttsr-manager.ts +26 -0
  122. package/dist/resources/extensions/voice/index.ts +4 -3
  123. package/package.json +1 -1
  124. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  125. package/packages/pi-coding-agent/dist/core/agent-session.js +12 -1
  126. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  127. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  128. package/packages/pi-coding-agent/dist/core/extensions/loader.js +5 -0
  129. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  130. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts +6 -0
  131. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
  132. package/packages/pi-coding-agent/dist/core/lsp/client.js +25 -0
  133. package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
  134. package/packages/pi-coding-agent/dist/core/lsp/index.d.ts +2 -0
  135. package/packages/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -1
  136. package/packages/pi-coding-agent/dist/core/lsp/index.js +106 -3
  137. package/packages/pi-coding-agent/dist/core/lsp/index.js.map +1 -1
  138. package/packages/pi-coding-agent/dist/core/lsp/lsp.md +6 -0
  139. package/packages/pi-coding-agent/dist/core/lsp/types.d.ts +35 -0
  140. package/packages/pi-coding-agent/dist/core/lsp/types.d.ts.map +1 -1
  141. package/packages/pi-coding-agent/dist/core/lsp/types.js +6 -0
  142. package/packages/pi-coding-agent/dist/core/lsp/types.js.map +1 -1
  143. package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts +3 -1
  144. package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts.map +1 -1
  145. package/packages/pi-coding-agent/dist/core/lsp/utils.js +45 -0
  146. package/packages/pi-coding-agent/dist/core/lsp/utils.js.map +1 -1
  147. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +6 -0
  148. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  149. package/packages/pi-coding-agent/dist/core/settings-manager.js +43 -11
  150. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  151. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  152. package/packages/pi-coding-agent/dist/core/system-prompt.js +7 -1
  153. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  154. package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
  155. package/packages/pi-coding-agent/dist/core/tools/edit.js +5 -0
  156. package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
  157. package/packages/pi-coding-agent/dist/core/tools/index.d.ts +2 -0
  158. package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
  159. package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
  160. package/packages/pi-coding-agent/dist/core/tools/write.js +5 -0
  161. package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
  162. package/packages/pi-coding-agent/src/core/agent-session.ts +13 -1
  163. package/packages/pi-coding-agent/src/core/extensions/loader.ts +6 -0
  164. package/packages/pi-coding-agent/src/core/lsp/client.ts +26 -0
  165. package/packages/pi-coding-agent/src/core/lsp/index.ts +157 -2
  166. package/packages/pi-coding-agent/src/core/lsp/lsp.md +6 -0
  167. package/packages/pi-coding-agent/src/core/lsp/types.ts +53 -0
  168. package/packages/pi-coding-agent/src/core/lsp/utils.ts +56 -0
  169. package/packages/pi-coding-agent/src/core/settings-manager.ts +41 -11
  170. package/packages/pi-coding-agent/src/core/system-prompt.ts +7 -1
  171. package/packages/pi-coding-agent/src/core/tools/edit.ts +3 -0
  172. package/packages/pi-coding-agent/src/core/tools/write.ts +3 -0
  173. package/src/resources/extensions/google-search/index.ts +164 -47
  174. package/src/resources/extensions/gsd/auto-dashboard.ts +14 -2
  175. package/src/resources/extensions/gsd/auto-prompts.ts +148 -39
  176. package/src/resources/extensions/gsd/auto-worktree.ts +93 -9
  177. package/src/resources/extensions/gsd/auto.ts +690 -39
  178. package/src/resources/extensions/gsd/captures.ts +384 -0
  179. package/src/resources/extensions/gsd/commands.ts +654 -36
  180. package/src/resources/extensions/gsd/complexity-classifier.ts +322 -0
  181. package/src/resources/extensions/gsd/context-budget.ts +243 -0
  182. package/src/resources/extensions/gsd/context-store.ts +195 -0
  183. package/src/resources/extensions/gsd/dashboard-overlay.ts +51 -3
  184. package/src/resources/extensions/gsd/db-writer.ts +341 -0
  185. package/src/resources/extensions/gsd/debug-logger.ts +178 -0
  186. package/src/resources/extensions/gsd/dispatch-guard.ts +0 -1
  187. package/src/resources/extensions/gsd/docs/preferences-reference.md +54 -0
  188. package/src/resources/extensions/gsd/doctor-proactive.ts +286 -0
  189. package/src/resources/extensions/gsd/doctor.ts +283 -2
  190. package/src/resources/extensions/gsd/export.ts +81 -2
  191. package/src/resources/extensions/gsd/files.ts +39 -9
  192. package/src/resources/extensions/gsd/git-service.ts +6 -0
  193. package/src/resources/extensions/gsd/gsd-db.ts +752 -0
  194. package/src/resources/extensions/gsd/guided-flow.ts +26 -1
  195. package/src/resources/extensions/gsd/history.ts +0 -1
  196. package/src/resources/extensions/gsd/index.ts +277 -1
  197. package/src/resources/extensions/gsd/md-importer.ts +526 -0
  198. package/src/resources/extensions/gsd/metrics.ts +84 -0
  199. package/src/resources/extensions/gsd/model-cost-table.ts +65 -0
  200. package/src/resources/extensions/gsd/model-router.ts +256 -0
  201. package/src/resources/extensions/gsd/notifications.ts +0 -1
  202. package/src/resources/extensions/gsd/post-unit-hooks.ts +72 -2
  203. package/src/resources/extensions/gsd/preferences.ts +198 -150
  204. package/src/resources/extensions/gsd/prompt-loader.ts +45 -9
  205. package/src/resources/extensions/gsd/prompts/execute-task.md +3 -5
  206. package/src/resources/extensions/gsd/prompts/heal-skill.md +45 -0
  207. package/src/resources/extensions/gsd/prompts/plan-slice.md +5 -1
  208. package/src/resources/extensions/gsd/prompts/quick-task.md +48 -0
  209. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -0
  210. package/src/resources/extensions/gsd/prompts/replan-slice.md +8 -0
  211. package/src/resources/extensions/gsd/prompts/system.md +2 -1
  212. package/src/resources/extensions/gsd/prompts/triage-captures.md +62 -0
  213. package/src/resources/extensions/gsd/quick.ts +156 -0
  214. package/src/resources/extensions/gsd/skill-discovery.ts +5 -3
  215. package/src/resources/extensions/gsd/skill-health.ts +417 -0
  216. package/src/resources/extensions/gsd/skill-telemetry.ts +127 -0
  217. package/src/resources/extensions/gsd/state.ts +30 -0
  218. package/src/resources/extensions/gsd/templates/preferences.md +1 -0
  219. package/src/resources/extensions/gsd/tests/captures.test.ts +438 -0
  220. package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +181 -0
  221. package/src/resources/extensions/gsd/tests/context-budget.test.ts +283 -0
  222. package/src/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
  223. package/src/resources/extensions/gsd/tests/context-store.test.ts +462 -0
  224. package/src/resources/extensions/gsd/tests/continue-here.test.ts +204 -0
  225. package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +346 -0
  226. package/src/resources/extensions/gsd/tests/db-writer.test.ts +602 -0
  227. package/src/resources/extensions/gsd/tests/debug-logger.test.ts +185 -0
  228. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +406 -0
  229. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +0 -1
  230. package/src/resources/extensions/gsd/tests/dist-redirect.mjs +22 -0
  231. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +244 -0
  232. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +303 -0
  233. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -0
  234. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +353 -0
  235. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +125 -0
  236. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +326 -0
  237. package/src/resources/extensions/gsd/tests/integration-edge.test.ts +228 -0
  238. package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +277 -0
  239. package/src/resources/extensions/gsd/tests/md-importer.test.ts +411 -0
  240. package/src/resources/extensions/gsd/tests/metrics.test.ts +197 -0
  241. package/src/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +144 -0
  242. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +69 -0
  243. package/src/resources/extensions/gsd/tests/model-isolation.test.ts +99 -0
  244. package/src/resources/extensions/gsd/tests/model-router.test.ts +167 -0
  245. package/src/resources/extensions/gsd/tests/parsers.test.ts +40 -0
  246. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +41 -1
  247. package/src/resources/extensions/gsd/tests/preferences-git.test.ts +0 -1
  248. package/src/resources/extensions/gsd/tests/preferences-hooks.test.ts +0 -1
  249. package/src/resources/extensions/gsd/tests/preferences-mode.test.ts +110 -0
  250. package/src/resources/extensions/gsd/tests/preferences-models.test.ts +0 -1
  251. package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +464 -0
  252. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +385 -0
  253. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +488 -1
  254. package/src/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +17 -29
  255. package/src/resources/extensions/gsd/tests/resolve-ts.mjs +2 -8
  256. package/src/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
  257. package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +126 -0
  258. package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +31 -8
  259. package/src/resources/extensions/gsd/tests/token-savings.test.ts +366 -0
  260. package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +224 -0
  261. package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +215 -0
  262. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +25 -1
  263. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +145 -0
  264. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +290 -0
  265. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +120 -0
  266. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +478 -0
  267. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +205 -0
  268. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +442 -0
  269. package/src/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +165 -0
  270. package/src/resources/extensions/gsd/triage-resolution.ts +200 -0
  271. package/src/resources/extensions/gsd/triage-ui.ts +175 -0
  272. package/src/resources/extensions/gsd/types.ts +29 -0
  273. package/src/resources/extensions/gsd/undo.ts +0 -1
  274. package/src/resources/extensions/gsd/unit-runtime.ts +5 -1
  275. package/src/resources/extensions/gsd/visualizer-data.ts +505 -0
  276. package/src/resources/extensions/gsd/visualizer-overlay.ts +337 -0
  277. package/src/resources/extensions/gsd/visualizer-views.ts +755 -0
  278. package/src/resources/extensions/gsd/worktree-command.ts +18 -0
  279. package/src/resources/extensions/gsd/worktree-manager.ts +11 -4
  280. package/src/resources/extensions/remote-questions/config.ts +4 -2
  281. package/src/resources/extensions/remote-questions/discord-adapter.ts +35 -4
  282. package/src/resources/extensions/remote-questions/format.ts +166 -14
  283. package/src/resources/extensions/remote-questions/manager.ts +14 -4
  284. package/src/resources/extensions/remote-questions/remote-command.ts +100 -4
  285. package/src/resources/extensions/remote-questions/slack-adapter.ts +58 -2
  286. package/src/resources/extensions/remote-questions/telegram-adapter.ts +161 -0
  287. package/src/resources/extensions/remote-questions/types.ts +2 -1
  288. package/src/resources/extensions/ttsr/ttsr-manager.ts +26 -0
  289. package/src/resources/extensions/voice/index.ts +4 -3
@@ -0,0 +1,326 @@
1
+ // gsd-tools — Structured LLM tool tests
2
+ //
3
+ // Tests the three registered tools: gsd_save_decision, gsd_update_requirement, gsd_save_summary.
4
+ // Each tool is tested via direct function invocation against an in-memory DB.
5
+
6
+ import { createTestContext } from './test-helpers.ts';
7
+ import * as path from 'node:path';
8
+ import * as os from 'node:os';
9
+ import * as fs from 'node:fs';
10
+ import {
11
+ openDatabase,
12
+ closeDatabase,
13
+ isDbAvailable,
14
+ upsertRequirement,
15
+ getRequirementById,
16
+ getDecisionById,
17
+ _getAdapter,
18
+ insertArtifact,
19
+ } from '../gsd-db.ts';
20
+ import {
21
+ saveDecisionToDb,
22
+ updateRequirementInDb,
23
+ saveArtifactToDb,
24
+ nextDecisionId,
25
+ } from '../db-writer.ts';
26
+ import type { Requirement } from '../types.ts';
27
+
28
+ const { assertEq, assertTrue, assertMatch, report } = createTestContext();
29
+
30
+ // ═══════════════════════════════════════════════════════════════════════════
31
+ // Helpers
32
+ // ═══════════════════════════════════════════════════════════════════════════
33
+
34
+ function makeTmpDir(): string {
35
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-tools-'));
36
+ fs.mkdirSync(path.join(dir, '.gsd'), { recursive: true });
37
+ return dir;
38
+ }
39
+
40
+ function cleanupDir(dir: string): void {
41
+ try {
42
+ fs.rmSync(dir, { recursive: true, force: true });
43
+ } catch { /* swallow */ }
44
+ }
45
+
46
+ /**
47
+ * Simulate tool execute by calling the underlying DB functions directly.
48
+ * The actual tool registration happens in index.ts; here we test the
49
+ * execute logic pattern: check DB → call writer → return result.
50
+ */
51
+
52
+ // ═══════════════════════════════════════════════════════════════════════════
53
+ // gsd_save_decision tool tests
54
+ // ═══════════════════════════════════════════════════════════════════════════
55
+
56
+ console.log('\n── gsd_save_decision ──');
57
+
58
+ {
59
+ const tmpDir = makeTmpDir();
60
+ try {
61
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
62
+ openDatabase(dbPath);
63
+ assertTrue(isDbAvailable(), 'DB should be available after open');
64
+
65
+ // (a) Decision tool creates DB row + returns new ID
66
+ const result = await saveDecisionToDb(
67
+ {
68
+ scope: 'architecture',
69
+ decision: 'Use SQLite for metadata',
70
+ choice: 'SQLite',
71
+ rationale: 'Sync API fits the CLI model',
72
+ revisable: 'Yes',
73
+ when_context: 'M001',
74
+ },
75
+ tmpDir,
76
+ );
77
+
78
+ assertEq(result.id, 'D001', 'First decision should be D001');
79
+
80
+ // Verify DB row exists
81
+ const row = getDecisionById('D001');
82
+ assertTrue(row !== null, 'Decision D001 should exist in DB');
83
+ assertEq(row!.scope, 'architecture', 'Decision scope should match');
84
+ assertEq(row!.decision, 'Use SQLite for metadata', 'Decision text should match');
85
+ assertEq(row!.choice, 'SQLite', 'Decision choice should match');
86
+
87
+ // Verify DECISIONS.md was generated
88
+ const mdPath = path.join(tmpDir, '.gsd', 'DECISIONS.md');
89
+ assertTrue(fs.existsSync(mdPath), 'DECISIONS.md should be created');
90
+ const mdContent = fs.readFileSync(mdPath, 'utf-8');
91
+ assertTrue(mdContent.includes('D001'), 'DECISIONS.md should contain D001');
92
+ assertTrue(mdContent.includes('SQLite'), 'DECISIONS.md should contain choice');
93
+
94
+ // (e) Decision tool auto-assigns correct next ID
95
+ const result2 = await saveDecisionToDb(
96
+ {
97
+ scope: 'testing',
98
+ decision: 'Test runner',
99
+ choice: 'vitest',
100
+ rationale: 'Fast and ESM-native',
101
+ },
102
+ tmpDir,
103
+ );
104
+ assertEq(result2.id, 'D002', 'Second decision should be D002');
105
+
106
+ const result3 = await saveDecisionToDb(
107
+ {
108
+ scope: 'CI',
109
+ decision: 'CI platform',
110
+ choice: 'GitHub Actions',
111
+ rationale: 'Integrated with repo',
112
+ },
113
+ tmpDir,
114
+ );
115
+ assertEq(result3.id, 'D003', 'Third decision should be D003');
116
+
117
+ closeDatabase();
118
+ } finally {
119
+ cleanupDir(tmpDir);
120
+ }
121
+ }
122
+
123
+ // ═══════════════════════════════════════════════════════════════════════════
124
+ // gsd_update_requirement tool tests
125
+ // ═══════════════════════════════════════════════════════════════════════════
126
+
127
+ console.log('\n── gsd_update_requirement ──');
128
+
129
+ {
130
+ const tmpDir = makeTmpDir();
131
+ try {
132
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
133
+ openDatabase(dbPath);
134
+
135
+ // Seed a requirement
136
+ const seedReq: Requirement = {
137
+ id: 'R001',
138
+ class: 'functional',
139
+ status: 'active',
140
+ description: 'Must support SQLite storage',
141
+ why: 'Structured data needs',
142
+ source: 'design',
143
+ primary_owner: 'S03',
144
+ supporting_slices: '',
145
+ validation: '',
146
+ notes: '',
147
+ full_content: '',
148
+ superseded_by: null,
149
+ };
150
+ upsertRequirement(seedReq);
151
+
152
+ // (b) Requirement update tool modifies existing requirement
153
+ await updateRequirementInDb(
154
+ 'R001',
155
+ { status: 'validated', validation: 'Unit tests pass', notes: 'Verified in S06' },
156
+ tmpDir,
157
+ );
158
+
159
+ const updated = getRequirementById('R001');
160
+ assertTrue(updated !== null, 'R001 should still exist');
161
+ assertEq(updated!.status, 'validated', 'Status should be updated');
162
+ assertEq(updated!.validation, 'Unit tests pass', 'Validation should be updated');
163
+ assertEq(updated!.notes, 'Verified in S06', 'Notes should be updated');
164
+ // Original fields preserved
165
+ assertEq(updated!.description, 'Must support SQLite storage', 'Description should be preserved');
166
+ assertEq(updated!.primary_owner, 'S03', 'Primary owner should be preserved');
167
+
168
+ // Verify REQUIREMENTS.md was generated
169
+ const mdPath = path.join(tmpDir, '.gsd', 'REQUIREMENTS.md');
170
+ assertTrue(fs.existsSync(mdPath), 'REQUIREMENTS.md should be created');
171
+ const mdContent = fs.readFileSync(mdPath, 'utf-8');
172
+ assertTrue(mdContent.includes('R001'), 'REQUIREMENTS.md should contain R001');
173
+ assertTrue(mdContent.includes('validated'), 'REQUIREMENTS.md should reflect updated status');
174
+
175
+ // Updating non-existent requirement throws
176
+ let threwForMissing = false;
177
+ try {
178
+ await updateRequirementInDb('R999', { status: 'deferred' }, tmpDir);
179
+ } catch (err) {
180
+ threwForMissing = true;
181
+ assertTrue(
182
+ (err as Error).message.includes('R999'),
183
+ 'Error should mention the missing requirement ID',
184
+ );
185
+ }
186
+ assertTrue(threwForMissing, 'Should throw for non-existent requirement');
187
+
188
+ closeDatabase();
189
+ } finally {
190
+ cleanupDir(tmpDir);
191
+ }
192
+ }
193
+
194
+ // ═══════════════════════════════════════════════════════════════════════════
195
+ // gsd_save_summary tool tests
196
+ // ═══════════════════════════════════════════════════════════════════════════
197
+
198
+ console.log('\n── gsd_save_summary ──');
199
+
200
+ {
201
+ const tmpDir = makeTmpDir();
202
+ try {
203
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
204
+ openDatabase(dbPath);
205
+
206
+ // (c) Summary tool creates artifact row
207
+ await saveArtifactToDb(
208
+ {
209
+ path: 'milestones/M001/slices/S01/S01-SUMMARY.md',
210
+ artifact_type: 'SUMMARY',
211
+ content: '# S01 Summary\n\nThis is a test summary.',
212
+ milestone_id: 'M001',
213
+ slice_id: 'S01',
214
+ },
215
+ tmpDir,
216
+ );
217
+
218
+ // Verify artifact in DB
219
+ const adapter = _getAdapter();
220
+ assertTrue(adapter !== null, 'Adapter should be available');
221
+ const rows = adapter!.prepare(
222
+ "SELECT * FROM artifacts WHERE path = 'milestones/M001/slices/S01/S01-SUMMARY.md'",
223
+ ).all();
224
+ assertEq(rows.length, 1, 'Should have 1 artifact row');
225
+ assertEq(rows[0]['artifact_type'] as string, 'SUMMARY', 'Artifact type should be SUMMARY');
226
+ assertEq(rows[0]['milestone_id'] as string, 'M001', 'Milestone ID should match');
227
+ assertEq(rows[0]['slice_id'] as string, 'S01', 'Slice ID should match');
228
+
229
+ // Verify file was written to disk
230
+ const filePath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-SUMMARY.md');
231
+ assertTrue(fs.existsSync(filePath), 'Summary file should be written to disk');
232
+ const fileContent = fs.readFileSync(filePath, 'utf-8');
233
+ assertTrue(fileContent.includes('S01 Summary'), 'File should contain summary content');
234
+
235
+ // Test milestone-level artifact (no slice_id)
236
+ await saveArtifactToDb(
237
+ {
238
+ path: 'milestones/M001/M001-CONTEXT.md',
239
+ artifact_type: 'CONTEXT',
240
+ content: '# M001 Context\n\nContext notes.',
241
+ milestone_id: 'M001',
242
+ },
243
+ tmpDir,
244
+ );
245
+
246
+ const mFilePath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'M001-CONTEXT.md');
247
+ assertTrue(fs.existsSync(mFilePath), 'Milestone-level artifact file should be created');
248
+
249
+ // Test task-level artifact
250
+ await saveArtifactToDb(
251
+ {
252
+ path: 'milestones/M001/slices/S01/tasks/T01-SUMMARY.md',
253
+ artifact_type: 'SUMMARY',
254
+ content: '# T01 Summary\n\nTask summary.',
255
+ milestone_id: 'M001',
256
+ slice_id: 'S01',
257
+ task_id: 'T01',
258
+ },
259
+ tmpDir,
260
+ );
261
+
262
+ const tFilePath = path.join(tmpDir, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'tasks', 'T01-SUMMARY.md');
263
+ assertTrue(fs.existsSync(tFilePath), 'Task-level artifact file should be created');
264
+
265
+ closeDatabase();
266
+ } finally {
267
+ cleanupDir(tmpDir);
268
+ }
269
+ }
270
+
271
+ // ═══════════════════════════════════════════════════════════════════════════
272
+ // DB unavailable error paths
273
+ // ═══════════════════════════════════════════════════════════════════════════
274
+
275
+ console.log('\n── DB unavailable error paths ──');
276
+
277
+ {
278
+ // (d) All tools return isError when DB unavailable
279
+ // Close any open DB and don't open a new one
280
+ try { closeDatabase(); } catch { /* already closed */ }
281
+
282
+ // isDbAvailable() should return false
283
+ assertTrue(!isDbAvailable(), 'DB should be unavailable after close');
284
+
285
+ // nextDecisionId degrades gracefully
286
+ const fallbackId = await nextDecisionId();
287
+ assertEq(fallbackId, 'D001', 'nextDecisionId should return D001 when DB unavailable');
288
+ }
289
+
290
+ // ═══════════════════════════════════════════════════════════════════════════
291
+ // Tool result format verification
292
+ // ═══════════════════════════════════════════════════════════════════════════
293
+
294
+ console.log('\n── Tool result format ──');
295
+
296
+ {
297
+ const tmpDir = makeTmpDir();
298
+ try {
299
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
300
+ openDatabase(dbPath);
301
+
302
+ // Verify result follows AgentToolResult interface: {content: [{type: "text", text}], details}
303
+ const result = await saveDecisionToDb(
304
+ {
305
+ scope: 'format-test',
306
+ decision: 'Test format',
307
+ choice: 'TypeBox',
308
+ rationale: 'Schema validation',
309
+ },
310
+ tmpDir,
311
+ );
312
+
313
+ // The saveDecisionToDb returns {id} — the tool wrapping adds the AgentToolResult shape.
314
+ // Verify the raw function returns the expected shape.
315
+ assertTrue(typeof result.id === 'string', 'saveDecisionToDb should return {id: string}');
316
+ assertMatch(result.id, /^D\d{3}$/, 'ID should match DXXX pattern');
317
+
318
+ closeDatabase();
319
+ } finally {
320
+ cleanupDir(tmpDir);
321
+ }
322
+ }
323
+
324
+ // ═══════════════════════════════════════════════════════════════════════════
325
+
326
+ report();
@@ -0,0 +1,228 @@
1
+ // Integration Edge Case Tests
2
+ //
3
+ // Three scenarios that only had per-module coverage before:
4
+ // 1. Empty project — no markdown files → migration finds nothing → queries return empty
5
+ // 2. Partial migration — DECISIONS.md exists but no REQUIREMENTS.md → no crash
6
+ // 3. Fallback mode — _resetProvider → queries degrade → re-open restores
7
+ //
8
+ // Uses real module imports (no mocks), file-backed DBs, temp directories.
9
+
10
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
11
+ import { join } from 'node:path';
12
+ import { tmpdir } from 'node:os';
13
+
14
+ import { openDatabase, closeDatabase, isDbAvailable, _resetProvider } from '../gsd-db.ts';
15
+ import { migrateFromMarkdown } from '../md-importer.ts';
16
+ import {
17
+ queryDecisions,
18
+ queryRequirements,
19
+ formatDecisionsForPrompt,
20
+ formatRequirementsForPrompt,
21
+ } from '../context-store.ts';
22
+ import { createTestContext } from './test-helpers.ts';
23
+
24
+ const { assertEq, assertTrue, report } = createTestContext();
25
+
26
+ // ─── Fixture Helper ────────────────────────────────────────────────────────
27
+
28
+ function generateDecisionsMarkdown(count: number): string {
29
+ const lines: string[] = [
30
+ '# Decisions Register',
31
+ '',
32
+ '<!-- Append-only. Never edit or remove existing rows. -->',
33
+ '',
34
+ '| # | When | Scope | Decision | Choice | Rationale | Revisable? |',
35
+ '|---|------|-------|----------|--------|-----------|------------|',
36
+ ];
37
+
38
+ for (let i = 1; i <= count; i++) {
39
+ const id = `D${String(i).padStart(3, '0')}`;
40
+ const milestone = i <= 3 ? 'M001' : 'M002';
41
+ lines.push(`| ${id} | ${milestone}/S01 | testing | decision ${i} text | choice ${i} | rationale ${i} | yes |`);
42
+ }
43
+
44
+ return lines.join('\n');
45
+ }
46
+
47
+ // ═══════════════════════════════════════════════════════════════════════════
48
+ // Edge Case 1: Empty Project
49
+ // ═══════════════════════════════════════════════════════════════════════════
50
+
51
+ console.log('\n=== integration-edge: empty project ===');
52
+ {
53
+ const base = mkdtempSync(join(tmpdir(), 'gsd-int-edge-empty-'));
54
+ const gsdDir = join(base, '.gsd');
55
+ mkdirSync(gsdDir, { recursive: true });
56
+
57
+ const dbPath = join(gsdDir, 'test-edge-empty.db');
58
+
59
+ try {
60
+ // Open DB first so migrateFromMarkdown doesn't auto-create at default path
61
+ openDatabase(dbPath);
62
+ assertTrue(isDbAvailable(), 'empty: DB available after open');
63
+
64
+ // Migrate with no markdown files on disk
65
+ const result = migrateFromMarkdown(base);
66
+
67
+ assertEq(result.decisions, 0, 'empty: 0 decisions imported');
68
+ assertEq(result.requirements, 0, 'empty: 0 requirements imported');
69
+ assertEq(result.artifacts, 0, 'empty: 0 artifacts imported');
70
+
71
+ // Query decisions → empty array
72
+ const decisions = queryDecisions();
73
+ assertEq(decisions.length, 0, 'empty: queryDecisions returns empty array');
74
+
75
+ // Query requirements → empty array
76
+ const requirements = queryRequirements();
77
+ assertEq(requirements.length, 0, 'empty: queryRequirements returns empty array');
78
+
79
+ // Query with scope filters → still empty, no crash
80
+ const scopedDecisions = queryDecisions({ milestoneId: 'M001' });
81
+ assertEq(scopedDecisions.length, 0, 'empty: scoped queryDecisions returns empty');
82
+
83
+ const scopedRequirements = queryRequirements({ sliceId: 'S01' });
84
+ assertEq(scopedRequirements.length, 0, 'empty: scoped queryRequirements returns empty');
85
+
86
+ // Format empty results → empty strings
87
+ const formattedD = formatDecisionsForPrompt([]);
88
+ const formattedR = formatRequirementsForPrompt([]);
89
+ assertEq(formattedD, '', 'empty: formatDecisionsForPrompt returns empty string');
90
+ assertEq(formattedR, '', 'empty: formatRequirementsForPrompt returns empty string');
91
+
92
+ // Format with actual empty query results
93
+ const formattedD2 = formatDecisionsForPrompt(decisions);
94
+ const formattedR2 = formatRequirementsForPrompt(requirements);
95
+ assertEq(formattedD2, '', 'empty: format of empty query decisions is empty string');
96
+ assertEq(formattedR2, '', 'empty: format of empty query requirements is empty string');
97
+
98
+ closeDatabase();
99
+ } finally {
100
+ closeDatabase();
101
+ rmSync(base, { recursive: true, force: true });
102
+ }
103
+ }
104
+
105
+ // ═══════════════════════════════════════════════════════════════════════════
106
+ // Edge Case 2: Partial Migration (decisions only, no requirements)
107
+ // ═══════════════════════════════════════════════════════════════════════════
108
+
109
+ console.log('\n=== integration-edge: partial migration ===');
110
+ {
111
+ const base = mkdtempSync(join(tmpdir(), 'gsd-int-edge-partial-'));
112
+ const gsdDir = join(base, '.gsd');
113
+ mkdirSync(gsdDir, { recursive: true });
114
+
115
+ // Write DECISIONS.md but NOT REQUIREMENTS.md
116
+ const decisionsMarkdown = generateDecisionsMarkdown(6);
117
+ writeFileSync(join(gsdDir, 'DECISIONS.md'), decisionsMarkdown);
118
+
119
+ const dbPath = join(gsdDir, 'test-edge-partial.db');
120
+
121
+ try {
122
+ openDatabase(dbPath);
123
+ assertTrue(isDbAvailable(), 'partial: DB available after open');
124
+
125
+ const result = migrateFromMarkdown(base);
126
+
127
+ // Decisions imported, requirements skipped gracefully
128
+ assertTrue(result.decisions === 6, `partial: imported ${result.decisions} decisions, expected 6`);
129
+ assertEq(result.requirements, 0, 'partial: 0 requirements imported (no file)');
130
+
131
+ // Decisions queryable
132
+ const decisions = queryDecisions();
133
+ assertTrue(decisions.length === 6, `partial: queryDecisions returns 6 (got ${decisions.length})`);
134
+
135
+ const m001Decisions = queryDecisions({ milestoneId: 'M001' });
136
+ assertTrue(m001Decisions.length > 0, 'partial: M001 decisions non-empty');
137
+ assertTrue(m001Decisions.length < decisions.length, 'partial: M001 scope filters correctly');
138
+
139
+ // Requirements return empty — no crash
140
+ const requirements = queryRequirements();
141
+ assertEq(requirements.length, 0, 'partial: queryRequirements returns empty');
142
+
143
+ const scopedReqs = queryRequirements({ sliceId: 'S01' });
144
+ assertEq(scopedReqs.length, 0, 'partial: scoped queryRequirements returns empty');
145
+
146
+ // Format works on partial data
147
+ const formattedD = formatDecisionsForPrompt(m001Decisions);
148
+ assertTrue(formattedD.length > 0, 'partial: formatted decisions non-empty');
149
+
150
+ const formattedR = formatRequirementsForPrompt(requirements);
151
+ assertEq(formattedR, '', 'partial: formatted empty requirements is empty string');
152
+
153
+ closeDatabase();
154
+ } finally {
155
+ closeDatabase();
156
+ rmSync(base, { recursive: true, force: true });
157
+ }
158
+ }
159
+
160
+ // ═══════════════════════════════════════════════════════════════════════════
161
+ // Edge Case 3: Fallback Mode (_resetProvider)
162
+ // ═══════════════════════════════════════════════════════════════════════════
163
+
164
+ console.log('\n=== integration-edge: fallback mode ===');
165
+ {
166
+ const base = mkdtempSync(join(tmpdir(), 'gsd-int-edge-fallback-'));
167
+ const gsdDir = join(base, '.gsd');
168
+ mkdirSync(gsdDir, { recursive: true });
169
+
170
+ const decisionsMarkdown = generateDecisionsMarkdown(4);
171
+ writeFileSync(join(gsdDir, 'DECISIONS.md'), decisionsMarkdown);
172
+
173
+ const dbPath = join(gsdDir, 'test-edge-fallback.db');
174
+
175
+ try {
176
+ // Step 1: Open DB normally and verify it works
177
+ openDatabase(dbPath);
178
+ assertTrue(isDbAvailable(), 'fallback: DB available after open');
179
+
180
+ migrateFromMarkdown(base);
181
+ const before = queryDecisions();
182
+ assertTrue(before.length === 4, `fallback: 4 decisions before reset (got ${before.length})`);
183
+
184
+ // Step 2: Close and reset provider → DB unavailable
185
+ closeDatabase();
186
+ _resetProvider();
187
+ assertTrue(!isDbAvailable(), 'fallback: DB unavailable after _resetProvider');
188
+
189
+ // Step 3: Queries degrade gracefully (return empty, don't throw)
190
+ const degradedDecisions = queryDecisions();
191
+ assertEq(degradedDecisions.length, 0, 'fallback: queryDecisions returns empty when unavailable');
192
+
193
+ const degradedRequirements = queryRequirements();
194
+ assertEq(degradedRequirements.length, 0, 'fallback: queryRequirements returns empty when unavailable');
195
+
196
+ const degradedScopedD = queryDecisions({ milestoneId: 'M001' });
197
+ assertEq(degradedScopedD.length, 0, 'fallback: scoped queryDecisions returns empty when unavailable');
198
+
199
+ const degradedScopedR = queryRequirements({ sliceId: 'S01' });
200
+ assertEq(degradedScopedR.length, 0, 'fallback: scoped queryRequirements returns empty when unavailable');
201
+
202
+ // Format functions work on empty arrays (no crash)
203
+ const formattedD = formatDecisionsForPrompt(degradedDecisions);
204
+ assertEq(formattedD, '', 'fallback: format degraded decisions is empty');
205
+
206
+ const formattedR = formatRequirementsForPrompt(degradedRequirements);
207
+ assertEq(formattedR, '', 'fallback: format degraded requirements is empty');
208
+
209
+ // Step 4: Re-open DB → restores availability
210
+ openDatabase(dbPath);
211
+ assertTrue(isDbAvailable(), 'fallback: DB available after re-open');
212
+
213
+ // Data should be there from the file-backed DB (persisted by first open)
214
+ // But rows may need re-import since the DB was freshly opened from the file
215
+ migrateFromMarkdown(base);
216
+ const restored = queryDecisions();
217
+ assertTrue(restored.length === 4, `fallback: 4 decisions after re-open (got ${restored.length})`);
218
+
219
+ closeDatabase();
220
+ } finally {
221
+ closeDatabase();
222
+ rmSync(base, { recursive: true, force: true });
223
+ }
224
+ }
225
+
226
+ // ─── Report ────────────────────────────────────────────────────────────────
227
+
228
+ report();