gsd-pi 2.66.1 → 2.67.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 (272) hide show
  1. package/dist/claude-cli-check.d.ts +8 -0
  2. package/dist/claude-cli-check.js +36 -0
  3. package/dist/cli.js +40 -0
  4. package/dist/onboarding.js +19 -2
  5. package/dist/resources/extensions/ask-user-questions.js +79 -11
  6. package/dist/resources/extensions/claude-code-cli/partial-builder.js +4 -3
  7. package/dist/resources/extensions/claude-code-cli/readiness.js +63 -12
  8. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +10 -3
  9. package/dist/resources/extensions/gsd/auto/loop.js +13 -1
  10. package/dist/resources/extensions/gsd/auto/phases.js +22 -3
  11. package/dist/resources/extensions/gsd/auto/run-unit.js +10 -2
  12. package/dist/resources/extensions/gsd/auto/session.js +1 -1
  13. package/dist/resources/extensions/gsd/auto-dashboard.js +65 -15
  14. package/dist/resources/extensions/gsd/auto-dispatch.js +30 -28
  15. package/dist/resources/extensions/gsd/auto-model-selection.js +12 -3
  16. package/dist/resources/extensions/gsd/auto-prompts.js +173 -25
  17. package/dist/resources/extensions/gsd/auto-recovery.js +11 -12
  18. package/dist/resources/extensions/gsd/auto.js +13 -1
  19. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +32 -1
  20. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +18 -6
  21. package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +5 -0
  22. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +59 -5
  23. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +8 -5
  24. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +186 -14
  25. package/dist/resources/extensions/gsd/codebase-generator.js +4 -0
  26. package/dist/resources/extensions/gsd/commands/handlers/core.js +3 -3
  27. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +10 -4
  28. package/dist/resources/extensions/gsd/context-store.js +134 -2
  29. package/dist/resources/extensions/gsd/custom-workflow-engine.js +3 -1
  30. package/dist/resources/extensions/gsd/detection.js +6 -0
  31. package/dist/resources/extensions/gsd/files.js +19 -2
  32. package/dist/resources/extensions/gsd/guided-flow.js +12 -8
  33. package/dist/resources/extensions/gsd/index.js +1 -1
  34. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +2 -0
  35. package/dist/resources/extensions/gsd/parsers-legacy.js +3 -1
  36. package/dist/resources/extensions/gsd/preferences.js +6 -1
  37. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  38. package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +7 -7
  39. package/dist/resources/extensions/gsd/prompts/discuss.md +3 -3
  40. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -3
  41. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
  42. package/dist/resources/extensions/gsd/prompts/rethink.md +6 -2
  43. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  44. package/dist/resources/extensions/gsd/prompts/triage-captures.md +1 -1
  45. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +4 -4
  46. package/dist/resources/extensions/gsd/prompts/worktree-merge.md +3 -1
  47. package/dist/resources/extensions/gsd/safety/file-change-validator.js +2 -1
  48. package/dist/resources/extensions/gsd/state.js +2 -1
  49. package/dist/resources/extensions/gsd/visualizer-overlay.js +27 -26
  50. package/dist/resources/extensions/gsd/workflow-reconcile.js +46 -7
  51. package/dist/resources/extensions/remote-questions/manager.js +8 -0
  52. package/dist/resources/extensions/shared/interview-ui.js +10 -0
  53. package/dist/web/standalone/.next/BUILD_ID +1 -1
  54. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  55. package/dist/web/standalone/.next/build-manifest.json +2 -2
  56. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  57. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  58. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  66. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/index.html +1 -1
  74. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  81. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  82. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  83. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  84. package/package.json +1 -1
  85. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  86. package/packages/pi-ai/dist/providers/anthropic-shared.js +4 -3
  87. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  88. package/packages/pi-ai/dist/utils/json-parse.d.ts.map +1 -1
  89. package/packages/pi-ai/dist/utils/json-parse.js +11 -1
  90. package/packages/pi-ai/dist/utils/json-parse.js.map +1 -1
  91. package/packages/pi-ai/dist/utils/repair-tool-json.d.ts.map +1 -1
  92. package/packages/pi-ai/dist/utils/repair-tool-json.js +60 -1
  93. package/packages/pi-ai/dist/utils/repair-tool-json.js.map +1 -1
  94. package/packages/pi-ai/dist/utils/tests/json-parse.test.d.ts +2 -0
  95. package/packages/pi-ai/dist/utils/tests/json-parse.test.d.ts.map +1 -0
  96. package/packages/pi-ai/dist/utils/tests/json-parse.test.js +14 -0
  97. package/packages/pi-ai/dist/utils/tests/json-parse.test.js.map +1 -0
  98. package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js +10 -0
  99. package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js.map +1 -1
  100. package/packages/pi-ai/src/providers/anthropic-shared.ts +4 -3
  101. package/packages/pi-ai/src/utils/json-parse.ts +11 -1
  102. package/packages/pi-ai/src/utils/repair-tool-json.ts +69 -1
  103. package/packages/pi-ai/src/utils/tests/json-parse.test.ts +17 -0
  104. package/packages/pi-ai/src/utils/tests/repair-tool-json.test.ts +13 -0
  105. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +3 -0
  106. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  107. package/packages/pi-coding-agent/dist/core/agent-session.js +1 -0
  108. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  109. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts +16 -0
  110. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  111. package/packages/pi-coding-agent/dist/core/retry-handler.js +58 -1
  112. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  113. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +58 -0
  114. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  115. package/packages/pi-coding-agent/dist/core/sdk.d.ts +3 -0
  116. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  117. package/packages/pi-coding-agent/dist/core/sdk.js +1 -0
  118. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  119. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.d.ts +2 -0
  120. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.d.ts.map +1 -0
  121. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js +17 -0
  122. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js.map +1 -0
  123. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  124. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +2 -1
  125. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  126. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts +1 -0
  127. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  128. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +9 -2
  129. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
  130. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
  131. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +2 -1
  132. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
  133. package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  134. package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.js +2 -1
  135. package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  136. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +2 -2
  137. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  138. package/packages/pi-coding-agent/package.json +1 -1
  139. package/packages/pi-coding-agent/src/core/agent-session.ts +4 -0
  140. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +69 -0
  141. package/packages/pi-coding-agent/src/core/retry-handler.ts +66 -1
  142. package/packages/pi-coding-agent/src/core/sdk.ts +5 -0
  143. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/provider-display-name.test.ts +18 -0
  144. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +2 -1
  145. package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +11 -2
  146. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +2 -1
  147. package/packages/pi-coding-agent/src/modes/interactive/components/scoped-models-selector.ts +2 -1
  148. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +2 -2
  149. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +13 -0
  150. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  151. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.d.ts +2 -0
  152. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.d.ts.map +1 -0
  153. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +35 -0
  154. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -0
  155. package/packages/pi-tui/dist/__tests__/tui.test.d.ts +2 -0
  156. package/packages/pi-tui/dist/__tests__/tui.test.d.ts.map +1 -0
  157. package/packages/pi-tui/dist/__tests__/tui.test.js +43 -0
  158. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -0
  159. package/packages/pi-tui/dist/autocomplete.d.ts.map +1 -1
  160. package/packages/pi-tui/dist/autocomplete.js +9 -7
  161. package/packages/pi-tui/dist/autocomplete.js.map +1 -1
  162. package/packages/pi-tui/dist/components/__tests__/editor.test.d.ts +2 -0
  163. package/packages/pi-tui/dist/components/__tests__/editor.test.d.ts.map +1 -0
  164. package/packages/pi-tui/dist/components/__tests__/editor.test.js +54 -0
  165. package/packages/pi-tui/dist/components/__tests__/editor.test.js.map +1 -0
  166. package/packages/pi-tui/dist/components/editor.d.ts +3 -1
  167. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  168. package/packages/pi-tui/dist/components/editor.js +14 -3
  169. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  170. package/packages/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
  171. package/packages/pi-tui/dist/stdin-buffer.js +6 -0
  172. package/packages/pi-tui/dist/stdin-buffer.js.map +1 -1
  173. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  174. package/packages/pi-tui/dist/tui.js +8 -0
  175. package/packages/pi-tui/dist/tui.js.map +1 -1
  176. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +15 -0
  177. package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +43 -0
  178. package/packages/pi-tui/src/__tests__/tui.test.ts +50 -0
  179. package/packages/pi-tui/src/autocomplete.ts +9 -7
  180. package/packages/pi-tui/src/components/__tests__/editor.test.ts +64 -0
  181. package/packages/pi-tui/src/components/editor.ts +14 -3
  182. package/packages/pi-tui/src/stdin-buffer.ts +7 -0
  183. package/packages/pi-tui/src/tui.ts +9 -0
  184. package/pkg/package.json +1 -1
  185. package/src/resources/extensions/ask-user-questions.ts +103 -11
  186. package/src/resources/extensions/claude-code-cli/partial-builder.ts +4 -3
  187. package/src/resources/extensions/claude-code-cli/readiness.ts +67 -12
  188. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +12 -3
  189. package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +17 -0
  190. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +18 -0
  191. package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -1
  192. package/src/resources/extensions/gsd/auto/loop.ts +14 -1
  193. package/src/resources/extensions/gsd/auto/phases.ts +27 -4
  194. package/src/resources/extensions/gsd/auto/run-unit.ts +14 -2
  195. package/src/resources/extensions/gsd/auto/session.ts +1 -1
  196. package/src/resources/extensions/gsd/auto-dashboard.ts +76 -16
  197. package/src/resources/extensions/gsd/auto-dispatch.ts +36 -35
  198. package/src/resources/extensions/gsd/auto-model-selection.ts +12 -3
  199. package/src/resources/extensions/gsd/auto-prompts.ts +195 -25
  200. package/src/resources/extensions/gsd/auto-recovery.ts +15 -15
  201. package/src/resources/extensions/gsd/auto.ts +12 -1
  202. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +34 -1
  203. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +27 -6
  204. package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +6 -0
  205. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +67 -6
  206. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +11 -8
  207. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +209 -16
  208. package/src/resources/extensions/gsd/codebase-generator.ts +4 -0
  209. package/src/resources/extensions/gsd/commands/handlers/core.ts +6 -6
  210. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +11 -4
  211. package/src/resources/extensions/gsd/context-store.ts +167 -2
  212. package/src/resources/extensions/gsd/custom-workflow-engine.ts +3 -1
  213. package/src/resources/extensions/gsd/detection.ts +6 -0
  214. package/src/resources/extensions/gsd/files.ts +21 -2
  215. package/src/resources/extensions/gsd/guided-flow.ts +15 -8
  216. package/src/resources/extensions/gsd/index.ts +6 -0
  217. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +2 -0
  218. package/src/resources/extensions/gsd/parsers-legacy.ts +3 -1
  219. package/src/resources/extensions/gsd/preferences.ts +6 -1
  220. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  221. package/src/resources/extensions/gsd/prompts/discuss-prepared.md +7 -7
  222. package/src/resources/extensions/gsd/prompts/discuss.md +3 -3
  223. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -3
  224. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
  225. package/src/resources/extensions/gsd/prompts/rethink.md +6 -2
  226. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  227. package/src/resources/extensions/gsd/prompts/triage-captures.md +1 -1
  228. package/src/resources/extensions/gsd/prompts/validate-milestone.md +4 -4
  229. package/src/resources/extensions/gsd/prompts/worktree-merge.md +3 -1
  230. package/src/resources/extensions/gsd/safety/file-change-validator.ts +4 -1
  231. package/src/resources/extensions/gsd/state.ts +2 -1
  232. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +52 -1
  233. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +50 -2
  234. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +21 -7
  235. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +48 -0
  236. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
  237. package/src/resources/extensions/gsd/tests/context-store.test.ts +176 -0
  238. package/src/resources/extensions/gsd/tests/core-overlay-fallback.test.ts +44 -0
  239. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +7 -1
  240. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +31 -0
  241. package/src/resources/extensions/gsd/tests/decision-scope-cascade.test.ts +370 -0
  242. package/src/resources/extensions/gsd/tests/detection.test.ts +37 -0
  243. package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +50 -0
  244. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +35 -0
  245. package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +34 -0
  246. package/src/resources/extensions/gsd/tests/health-widget.test.ts +45 -0
  247. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +53 -13
  248. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +2 -2
  249. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +3 -3
  250. package/src/resources/extensions/gsd/tests/measurement.test.ts +531 -0
  251. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +3 -4
  252. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +21 -0
  253. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +71 -2
  254. package/src/resources/extensions/gsd/tests/parsers.test.ts +25 -0
  255. package/src/resources/extensions/gsd/tests/preferences.test.ts +20 -0
  256. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +8 -1
  257. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +60 -0
  258. package/src/resources/extensions/gsd/tests/queue-execution-guard.test.ts +9 -0
  259. package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +19 -0
  260. package/src/resources/extensions/gsd/tests/register-shortcuts.test.ts +73 -0
  261. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +98 -0
  262. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +2 -2
  263. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +26 -0
  264. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +59 -0
  265. package/src/resources/extensions/gsd/tests/workflow-reconcile.test.ts +91 -0
  266. package/src/resources/extensions/gsd/tests/write-gate.test.ts +210 -35
  267. package/src/resources/extensions/gsd/visualizer-overlay.ts +31 -27
  268. package/src/resources/extensions/gsd/workflow-reconcile.ts +59 -8
  269. package/src/resources/extensions/remote-questions/manager.ts +9 -0
  270. package/src/resources/extensions/shared/interview-ui.ts +13 -0
  271. /package/dist/web/standalone/.next/static/{y5P0reMrCMs-4-gswdawm → DFZllMYDbO0OwyS6FSvm5}/_buildManifest.js +0 -0
  272. /package/dist/web/standalone/.next/static/{y5P0reMrCMs-4-gswdawm → DFZllMYDbO0OwyS6FSvm5}/_ssgManifest.js +0 -0
@@ -8,6 +8,7 @@
8
8
  import { getCurrentBranch } from "./worktree.js";
9
9
  import { getActiveHook } from "./post-unit-hooks.js";
10
10
  import { getLedger, getProjectTotals } from "./metrics.js";
11
+ import { getErrorMessage } from "./error-utils.js";
11
12
  import { isDbAvailable, getMilestoneSlices, getSliceTasks } from "./gsd-db.js";
12
13
  import { formatShortcut } from "./files.js";
13
14
  import { readFileSync, writeFileSync, existsSync } from "node:fs";
@@ -17,7 +18,7 @@ import { makeUI } from "../shared/tui.js";
17
18
  import { GLYPH, INDENT } from "../shared/mod.js";
18
19
  import { computeProgressScore } from "./progress-score.js";
19
20
  import { getActiveWorktreeName } from "./worktree-command.js";
20
- import { loadEffectiveGSDPreferences, getGlobalGSDPreferencesPath } from "./preferences.js";
21
+ import { getGlobalGSDPreferencesPath, getProjectGSDPreferencesPath, parsePreferencesMarkdown, } from "./preferences.js";
21
22
  import { resolveServiceTierIcon, getEffectiveServiceTier } from "./service-tier.js";
22
23
  import { parseUnitId } from "./unit-id.js";
23
24
  import { formatRtkSavingsLabel, getRtkSessionSavings, } from "../shared/rtk-session-stats.js";
@@ -293,26 +294,68 @@ export const hideFooter = () => ({
293
294
  const WIDGET_MODES = ["full", "small", "min", "off"];
294
295
  let widgetMode = "full";
295
296
  let widgetModeInitialized = false;
297
+ let widgetModePreferencePath = null;
298
+ function safeReadTextFile(path) {
299
+ try {
300
+ if (!existsSync(path))
301
+ return null;
302
+ return readFileSync(path, "utf-8");
303
+ }
304
+ catch {
305
+ return null;
306
+ }
307
+ }
308
+ function readWidgetModeFromFile(path) {
309
+ const raw = safeReadTextFile(path);
310
+ if (!raw)
311
+ return undefined;
312
+ const prefs = parsePreferencesMarkdown(raw);
313
+ const saved = prefs?.widget_mode;
314
+ if (saved && WIDGET_MODES.includes(saved)) {
315
+ return saved;
316
+ }
317
+ return undefined;
318
+ }
319
+ function resolveWidgetModePreferencePath(projectPath = getProjectGSDPreferencesPath(), globalPath = getGlobalGSDPreferencesPath()) {
320
+ if (readWidgetModeFromFile(projectPath)) {
321
+ return projectPath;
322
+ }
323
+ if (readWidgetModeFromFile(globalPath)) {
324
+ return globalPath;
325
+ }
326
+ if (safeReadTextFile(projectPath) !== null)
327
+ return projectPath;
328
+ if (safeReadTextFile(globalPath) !== null)
329
+ return globalPath;
330
+ return getGlobalGSDPreferencesPath();
331
+ }
296
332
  /** Load widget mode from preferences (once). */
297
- function ensureWidgetModeLoaded() {
333
+ function ensureWidgetModeLoaded(projectPath, globalPath) {
298
334
  if (widgetModeInitialized)
299
335
  return;
300
336
  widgetModeInitialized = true;
301
337
  try {
302
- const loaded = loadEffectiveGSDPreferences();
303
- const saved = loaded?.preferences?.widget_mode;
338
+ const resolvedProjectPath = projectPath ?? getProjectGSDPreferencesPath();
339
+ const resolvedGlobalPath = globalPath ?? getGlobalGSDPreferencesPath();
340
+ const saved = readWidgetModeFromFile(resolvedProjectPath) ?? readWidgetModeFromFile(resolvedGlobalPath);
304
341
  if (saved && WIDGET_MODES.includes(saved)) {
305
342
  widgetMode = saved;
306
343
  }
344
+ widgetModePreferencePath = resolveWidgetModePreferencePath(resolvedProjectPath, resolvedGlobalPath);
307
345
  }
308
346
  catch (err) { /* non-fatal — use default */
309
- logWarning("dashboard", `operation failed: ${err instanceof Error ? err.message : String(err)}`);
347
+ logWarning("dashboard", `operation failed: ${getErrorMessage(err)}`);
348
+ widgetModePreferencePath = getGlobalGSDPreferencesPath();
310
349
  }
311
350
  }
312
- /** Persist widget mode to global preferences YAML. */
313
- function persistWidgetMode(mode) {
351
+ /**
352
+ * Persist widget mode to the preference file that owns the effective value.
353
+ * Project-scoped widget_mode wins over global; if neither scope defines it,
354
+ * we prefer an existing project preferences file and otherwise fall back to
355
+ * the global preferences file.
356
+ */
357
+ function persistWidgetMode(mode, prefsPath = widgetModePreferencePath ?? resolveWidgetModePreferencePath()) {
314
358
  try {
315
- const prefsPath = getGlobalGSDPreferencesPath();
316
359
  let content = "";
317
360
  if (existsSync(prefsPath)) {
318
361
  content = readFileSync(prefsPath, "utf-8");
@@ -332,23 +375,30 @@ function persistWidgetMode(mode) {
332
375
  }
333
376
  }
334
377
  /** Cycle to the next widget mode. Returns the new mode. */
335
- export function cycleWidgetMode() {
336
- ensureWidgetModeLoaded();
378
+ export function cycleWidgetMode(projectPath, globalPath) {
379
+ ensureWidgetModeLoaded(projectPath, globalPath);
337
380
  const idx = WIDGET_MODES.indexOf(widgetMode);
338
381
  widgetMode = WIDGET_MODES[(idx + 1) % WIDGET_MODES.length];
339
- persistWidgetMode(widgetMode);
382
+ persistWidgetMode(widgetMode, widgetModePreferencePath ?? resolveWidgetModePreferencePath(projectPath, globalPath));
340
383
  return widgetMode;
341
384
  }
342
385
  /** Set widget mode directly. */
343
- export function setWidgetMode(mode) {
386
+ export function setWidgetMode(mode, projectPath, globalPath) {
387
+ ensureWidgetModeLoaded(projectPath, globalPath);
344
388
  widgetMode = mode;
345
- persistWidgetMode(widgetMode);
389
+ persistWidgetMode(widgetMode, widgetModePreferencePath ?? resolveWidgetModePreferencePath(projectPath, globalPath));
346
390
  }
347
391
  /** Get current widget mode. */
348
- export function getWidgetMode() {
349
- ensureWidgetModeLoaded();
392
+ export function getWidgetMode(projectPath, globalPath) {
393
+ ensureWidgetModeLoaded(projectPath, globalPath);
350
394
  return widgetMode;
351
395
  }
396
+ /** Test-only reset for widget mode caching. */
397
+ export function _resetWidgetModeForTests() {
398
+ widgetMode = "full";
399
+ widgetModeInitialized = false;
400
+ widgetModePreferencePath = null;
401
+ }
352
402
  export function updateProgressWidget(ctx, unitType, unitId, state, accessors, tierBadge) {
353
403
  if (!ctx.hasUI)
354
404
  return;
@@ -291,34 +291,8 @@ export const DISPATCH_RULES = [
291
291
  },
292
292
  },
293
293
  {
294
- name: "planning (no research, not S01) research-slice",
295
- match: async ({ state, mid, midTitle, basePath, prefs }) => {
296
- if (state.phase !== "planning")
297
- return null;
298
- // Phase skip: skip research when preference or profile says so
299
- if (prefs?.phases?.skip_research || prefs?.phases?.skip_slice_research)
300
- return null;
301
- if (!state.activeSlice)
302
- return missingSliceStop(mid, state.phase);
303
- const sid = state.activeSlice.id;
304
- const sTitle = state.activeSlice.title;
305
- const researchFile = resolveSliceFile(basePath, mid, sid, "RESEARCH");
306
- if (researchFile)
307
- return null; // has research, fall through
308
- // Skip slice research for S01 when milestone research already exists —
309
- // the milestone research already covers the same ground for the first slice.
310
- const milestoneResearchFile = resolveMilestoneFile(basePath, mid, "RESEARCH");
311
- if (milestoneResearchFile && sid === "S01")
312
- return null; // fall through to plan-slice
313
- return {
314
- action: "dispatch",
315
- unitType: "research-slice",
316
- unitId: `${mid}/${sid}`,
317
- prompt: await buildResearchSlicePrompt(mid, midTitle, sid, sTitle, basePath),
318
- };
319
- },
320
- },
321
- {
294
+ // Keep this rule before the single-slice research rule so the multi-slice
295
+ // path wins whenever 2+ slices are ready.
322
296
  name: "planning (multiple slices need research) → parallel-research-slices",
323
297
  match: async ({ state, mid, midTitle, basePath, prefs }) => {
324
298
  if (state.phase !== "planning")
@@ -360,6 +334,34 @@ export const DISPATCH_RULES = [
360
334
  };
361
335
  },
362
336
  },
337
+ {
338
+ name: "planning (no research, not S01) → research-slice",
339
+ match: async ({ state, mid, midTitle, basePath, prefs }) => {
340
+ if (state.phase !== "planning")
341
+ return null;
342
+ // Phase skip: skip research when preference or profile says so
343
+ if (prefs?.phases?.skip_research || prefs?.phases?.skip_slice_research)
344
+ return null;
345
+ if (!state.activeSlice)
346
+ return missingSliceStop(mid, state.phase);
347
+ const sid = state.activeSlice.id;
348
+ const sTitle = state.activeSlice.title;
349
+ const researchFile = resolveSliceFile(basePath, mid, sid, "RESEARCH");
350
+ if (researchFile)
351
+ return null; // has research, fall through
352
+ // Skip slice research for S01 when milestone research already exists —
353
+ // the milestone research already covers the same ground for the first slice.
354
+ const milestoneResearchFile = resolveMilestoneFile(basePath, mid, "RESEARCH");
355
+ if (milestoneResearchFile && sid === "S01")
356
+ return null; // fall through to plan-slice
357
+ return {
358
+ action: "dispatch",
359
+ unitType: "research-slice",
360
+ unitId: `${mid}/${sid}`,
361
+ prompt: await buildResearchSlicePrompt(mid, midTitle, sid, sTitle, basePath),
362
+ };
363
+ },
364
+ },
363
365
  {
364
366
  name: "planning → plan-slice",
365
367
  match: async ({ state, mid, midTitle, basePath }) => {
@@ -252,8 +252,17 @@ export function resolveModelId(modelId, availableModels, currentProvider) {
252
252
  return undefined;
253
253
  if (candidates.length === 1)
254
254
  return candidates[0];
255
- // Extension / CLI-wrapper providers that should never win bare-ID resolution
256
- // when a first-class API provider also offers the same model.
255
+ // When the user's current provider is claude-code (set by startup migration
256
+ // or explicit selection), honour it for bare IDs. Routing back to anthropic
257
+ // would undo the migration and hit the third-party subscription block (#3772).
258
+ if (currentProvider === "claude-code") {
259
+ const ccMatch = candidates.find(m => m.provider === "claude-code");
260
+ if (ccMatch)
261
+ return ccMatch;
262
+ }
263
+ // Extension / CLI-wrapper providers that should not win bare-ID resolution
264
+ // when a first-class API provider also offers the same model AND the user
265
+ // has not explicitly chosen the extension provider.
257
266
  const EXTENSION_PROVIDERS = new Set(["claude-code"]);
258
267
  // Prefer currentProvider only when it is a first-class API provider
259
268
  if (currentProvider && !EXTENSION_PROVIDERS.has(currentProvider)) {
@@ -274,7 +283,7 @@ export function resolveModelId(modelId, availableModels, currentProvider) {
274
283
  * Uses case-insensitive matching with alias support to prevent fail-open on
275
284
  * provider naming variations (e.g. "copilot" vs "github-copilot").
276
285
  */
277
- const FLAT_RATE_PROVIDERS = new Set(["github-copilot", "copilot"]);
286
+ const FLAT_RATE_PROVIDERS = new Set(["github-copilot", "copilot", "claude-code"]);
278
287
  export function isFlatRateProvider(provider) {
279
288
  return FLAT_RATE_PROVIDERS.has(provider.toLowerCase());
280
289
  }
@@ -219,7 +219,12 @@ export async function inlineGsdRootFile(base, filename, label) {
219
219
  // ─── DB-Aware Inline Helpers ──────────────────────────────────────────────
220
220
  /**
221
221
  * Inline decisions with optional milestone scoping from the DB.
222
- * Falls back to filesystem via inlineGsdRootFile when DB unavailable or empty.
222
+ * Falls back to filesystem via inlineGsdRootFile only when DB is unavailable.
223
+ *
224
+ * Cascade logic (R005):
225
+ * 1. Query with { milestoneId, scope } if scope provided
226
+ * 2. If empty AND scope was provided, retry with { milestoneId } only (drop scope)
227
+ * 3. If still empty, return null (intentional per D020)
223
228
  */
224
229
  export async function inlineDecisionsFromDb(base, milestoneId, scope, level) {
225
230
  const inlineLevel = level ?? resolveInlineLevel();
@@ -227,7 +232,12 @@ export async function inlineDecisionsFromDb(base, milestoneId, scope, level) {
227
232
  const { isDbAvailable } = await import("./gsd-db.js");
228
233
  if (isDbAvailable()) {
229
234
  const { queryDecisions, formatDecisionsForPrompt } = await import("./context-store.js");
230
- const decisions = queryDecisions({ milestoneId, scope });
235
+ // First query: try with both milestoneId and scope (if scope provided)
236
+ let decisions = queryDecisions({ milestoneId, scope });
237
+ // Cascade: if empty AND scope was provided, retry without scope
238
+ if (decisions.length === 0 && scope) {
239
+ decisions = queryDecisions({ milestoneId });
240
+ }
231
241
  if (decisions.length > 0) {
232
242
  // Use compact format for non-full levels to save ~35% tokens
233
243
  const formatted = inlineLevel !== "full"
@@ -235,24 +245,27 @@ export async function inlineDecisionsFromDb(base, milestoneId, scope, level) {
235
245
  : formatDecisionsForPrompt(decisions);
236
246
  return `### Decisions\nSource: \`.gsd/DECISIONS.md\`\n\n${formatted}`;
237
247
  }
248
+ // DB available but cascade returned empty — intentional per D020, don't fall back to file
249
+ return null;
238
250
  }
239
251
  }
240
252
  catch (err) {
241
253
  logWarning("prompt", `inlineDecisionsFromDb failed: ${err instanceof Error ? err.message : String(err)}`);
242
254
  }
255
+ // DB unavailable — fall back to filesystem
243
256
  return inlineGsdRootFile(base, "decisions.md", "Decisions");
244
257
  }
245
258
  /**
246
- * Inline requirements with optional slice scoping from the DB.
259
+ * Inline requirements with optional milestone and slice scoping from the DB.
247
260
  * Falls back to filesystem via inlineGsdRootFile when DB unavailable or empty.
248
261
  */
249
- export async function inlineRequirementsFromDb(base, sliceId, level) {
262
+ export async function inlineRequirementsFromDb(base, milestoneId, sliceId, level) {
250
263
  const inlineLevel = level ?? resolveInlineLevel();
251
264
  try {
252
265
  const { isDbAvailable } = await import("./gsd-db.js");
253
266
  if (isDbAvailable()) {
254
267
  const { queryRequirements, formatRequirementsForPrompt } = await import("./context-store.js");
255
- const requirements = queryRequirements({ sliceId });
268
+ const requirements = queryRequirements({ milestoneId, sliceId });
256
269
  if (requirements.length > 0) {
257
270
  // Use compact format for non-full levels to save ~40% tokens
258
271
  const formatted = inlineLevel !== "full"
@@ -287,6 +300,117 @@ export async function inlineProjectFromDb(base) {
287
300
  }
288
301
  return inlineGsdRootFile(base, "project.md", "Project");
289
302
  }
303
+ // ─── Stopwords for keyword extraction ─────────────────────────────────────
304
+ const STOPWORDS = new Set(['of', 'the', 'and', 'a', 'for', '+', '-', 'to', 'in', 'on', 'with', 'is', 'as', 'by']);
305
+ // Generic words that don't provide meaningful scope differentiation
306
+ const GENERIC_WORDS = new Set([
307
+ 'setup', 'integration', 'implementation', 'testing', 'test', 'tests',
308
+ 'config', 'configuration', 'init', 'initial', 'basic', 'core',
309
+ 'main', 'primary', 'final', 'complete', 'finish', 'end',
310
+ 'start', 'begin', 'first', 'last', 'update', 'updates',
311
+ 'fix', 'fixes', 'add', 'adds', 'remove', 'removes',
312
+ 'create', 'creates', 'build', 'builds', 'deploy', 'deployment',
313
+ 'refactor', 'refactoring', 'cleanup', 'polish', 'review',
314
+ // Process/activity words that describe what you're doing, not what domain
315
+ 'hardening', 'validation', 'verification', 'optimization',
316
+ 'improvement', 'enhancement', 'infrastructure',
317
+ ]);
318
+ // Pattern to match slice/milestone/task IDs (e.g., S01, M001, T03)
319
+ const UNIT_ID_PATTERN = /^[smt]\d+$/i;
320
+ /**
321
+ * Derive a scope keyword from slice title and optional description.
322
+ * Returns the most specific noun (first non-generic keyword) for decision scoping.
323
+ *
324
+ * Examples:
325
+ * - "Auth Middleware & Protected Route" → "auth"
326
+ * - "Database & User Model Setup" → "database"
327
+ * - "Integration Testing" → undefined (too generic)
328
+ * - "API Rate Limiting" → "api"
329
+ *
330
+ * @param sliceTitle - The slice title
331
+ * @param sliceDescription - Optional roadmap description (demo text)
332
+ * @returns A single lowercase keyword or undefined if no meaningful scope
333
+ */
334
+ export function deriveSliceScope(sliceTitle, sliceDescription) {
335
+ // Combine title and description for keyword extraction
336
+ const combinedText = sliceDescription
337
+ ? `${sliceTitle} ${sliceDescription}`
338
+ : sliceTitle;
339
+ // Extract all words, lowercase, remove punctuation
340
+ const words = combinedText
341
+ .split(/[\s&+,;:|/\\()-]+/)
342
+ .map(w => w.toLowerCase().replace(/[^a-z0-9]/g, ''))
343
+ .filter(w => w.length >= 2);
344
+ // Find the first word that is:
345
+ // 1. Not a stopword
346
+ // 2. Not a generic word
347
+ // 3. Not a unit ID (S01, M001, T03)
348
+ // 4. At least 3 characters (meaningful scope)
349
+ for (const word of words) {
350
+ if (STOPWORDS.has(word))
351
+ continue;
352
+ if (GENERIC_WORDS.has(word))
353
+ continue;
354
+ if (UNIT_ID_PATTERN.test(word))
355
+ continue;
356
+ if (word.length < 3)
357
+ continue;
358
+ return word;
359
+ }
360
+ return undefined;
361
+ }
362
+ /**
363
+ * Extract keywords from a slice title for scoped knowledge queries.
364
+ * Splits on whitespace, filters stopwords, lowercases.
365
+ * Example: 'KNOWLEDGE scoping + roadmap excerpt' → ['knowledge', 'scoping', 'roadmap', 'excerpt']
366
+ */
367
+ function extractKeywords(title) {
368
+ return title
369
+ .split(/\s+/)
370
+ .map(w => w.toLowerCase().replace(/[^a-z0-9]/g, ''))
371
+ .filter(w => w.length > 0 && !STOPWORDS.has(w));
372
+ }
373
+ /**
374
+ * Inline scoped KNOWLEDGE.md content based on keywords from slice title.
375
+ * Reads KNOWLEDGE.md, filters to sections matching keywords, formats with header.
376
+ * Returns null if no KNOWLEDGE.md exists or no sections match.
377
+ */
378
+ export async function inlineKnowledgeScoped(base, keywords) {
379
+ const knowledgePath = resolveGsdRootFile(base, "KNOWLEDGE");
380
+ if (!existsSync(knowledgePath))
381
+ return null;
382
+ const content = await loadFile(knowledgePath);
383
+ if (!content)
384
+ return null;
385
+ // Import queryKnowledge from context-store
386
+ const { queryKnowledge } = await import("./context-store.js");
387
+ const scoped = await queryKnowledge(content, keywords);
388
+ // Return null if no sections matched (empty string from queryKnowledge)
389
+ if (!scoped)
390
+ return null;
391
+ return `### Project Knowledge (scoped)\nSource: \`${relGsdRootFile("KNOWLEDGE")}\`\n\n${scoped.trim()}`;
392
+ }
393
+ /**
394
+ * Inline a roadmap excerpt for a specific slice.
395
+ * Reads full roadmap, extracts minimal excerpt with header + predecessor + target row.
396
+ * Returns null if roadmap doesn't exist or slice not found.
397
+ */
398
+ export async function inlineRoadmapExcerpt(base, mid, sid) {
399
+ const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
400
+ if (!roadmapPath || !existsSync(roadmapPath))
401
+ return null;
402
+ const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
403
+ const content = await loadFile(roadmapPath);
404
+ if (!content)
405
+ return null;
406
+ // Import formatRoadmapExcerpt from context-store
407
+ const { formatRoadmapExcerpt } = await import("./context-store.js");
408
+ const excerpt = formatRoadmapExcerpt(content, sid, roadmapRel);
409
+ // Return null if slice not found in roadmap
410
+ if (!excerpt)
411
+ return null;
412
+ return `### Milestone Roadmap (excerpt)\nSource: \`${roadmapRel}\`\n\n${excerpt}`;
413
+ }
290
414
  // ─── Skill Activation & Discovery ─────────────────────────────────────────
291
415
  function normalizeSkillReference(ref) {
292
416
  const normalized = ref.replace(/\\/g, "/").trim();
@@ -772,7 +896,7 @@ export async function buildResearchMilestonePrompt(mid, midTitle, base) {
772
896
  const projectInline = await inlineProjectFromDb(base);
773
897
  if (projectInline)
774
898
  inlined.push(projectInline);
775
- const requirementsInline = await inlineRequirementsFromDb(base);
899
+ const requirementsInline = await inlineRequirementsFromDb(base, mid);
776
900
  if (requirementsInline)
777
901
  inlined.push(requirementsInline);
778
902
  const decisionsInline = await inlineDecisionsFromDb(base, mid);
@@ -823,7 +947,7 @@ export async function buildPlanMilestonePrompt(mid, midTitle, base, level) {
823
947
  const projectInline = await inlineProjectFromDb(base);
824
948
  if (projectInline)
825
949
  inlined.push(projectInline);
826
- const requirementsInline = await inlineRequirementsFromDb(base, undefined, inlineLevel);
950
+ const requirementsInline = await inlineRequirementsFromDb(base, mid, undefined, inlineLevel);
827
951
  if (requirementsInline)
828
952
  inlined.push(requirementsInline);
829
953
  const decisionsInline = await inlineDecisionsFromDb(base, mid, undefined, inlineLevel);
@@ -884,7 +1008,15 @@ export async function buildResearchSlicePrompt(mid, _midTitle, sid, sTitle, base
884
1008
  const sliceContextPath = resolveSliceFile(base, mid, sid, "CONTEXT");
885
1009
  const sliceContextRel = relSliceFile(base, mid, sid, "CONTEXT");
886
1010
  const inlined = [];
887
- inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
1011
+ // Use roadmap excerpt instead of full roadmap for context reduction
1012
+ const roadmapExcerptRS = await inlineRoadmapExcerpt(base, mid, sid);
1013
+ if (roadmapExcerptRS) {
1014
+ inlined.push(roadmapExcerptRS);
1015
+ }
1016
+ else {
1017
+ // Fall back to full roadmap if excerpt fails
1018
+ inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
1019
+ }
888
1020
  const contextInline = await inlineFileOptional(contextPath, contextRel, "Milestone Context");
889
1021
  if (contextInline)
890
1022
  inlined.push(contextInline);
@@ -894,13 +1026,17 @@ export async function buildResearchSlicePrompt(mid, _midTitle, sid, sTitle, base
894
1026
  const researchInline = await inlineFileOptional(milestoneResearchPath, milestoneResearchRel, "Milestone Research");
895
1027
  if (researchInline)
896
1028
  inlined.push(researchInline);
897
- const decisionsInline = await inlineDecisionsFromDb(base, mid);
1029
+ // Derive scope from slice title for decision filtering (R005)
1030
+ const derivedScope = deriveSliceScope(sTitle);
1031
+ const decisionsInline = await inlineDecisionsFromDb(base, mid, derivedScope);
898
1032
  if (decisionsInline)
899
1033
  inlined.push(decisionsInline);
900
- const requirementsInline = await inlineRequirementsFromDb(base, sid);
1034
+ const requirementsInline = await inlineRequirementsFromDb(base, mid, sid);
901
1035
  if (requirementsInline)
902
1036
  inlined.push(requirementsInline);
903
- const knowledgeInlineRS = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
1037
+ // Use scoped knowledge based on slice title keywords
1038
+ const keywords = extractKeywords(sTitle);
1039
+ const knowledgeInlineRS = await inlineKnowledgeScoped(base, keywords);
904
1040
  if (knowledgeInlineRS)
905
1041
  inlined.push(knowledgeInlineRS);
906
1042
  inlined.push(inlineTemplate("research", "Research"));
@@ -944,7 +1080,15 @@ export async function buildPlanSlicePrompt(mid, _midTitle, sid, sTitle, base, le
944
1080
  const researchSliceAnchor = readPhaseAnchor(base, mid, "research-slice");
945
1081
  if (researchSliceAnchor)
946
1082
  inlined.push(formatAnchorForPrompt(researchSliceAnchor));
947
- inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
1083
+ // Use roadmap excerpt instead of full roadmap for context reduction
1084
+ const roadmapExcerptPS = await inlineRoadmapExcerpt(base, mid, sid);
1085
+ if (roadmapExcerptPS) {
1086
+ inlined.push(roadmapExcerptPS);
1087
+ }
1088
+ else {
1089
+ // Fall back to full roadmap if excerpt fails
1090
+ inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
1091
+ }
948
1092
  const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
949
1093
  if (sliceCtxInline)
950
1094
  inlined.push(sliceCtxInline);
@@ -952,14 +1096,18 @@ export async function buildPlanSlicePrompt(mid, _midTitle, sid, sTitle, base, le
952
1096
  if (researchInline)
953
1097
  inlined.push(researchInline);
954
1098
  if (inlineLevel !== "minimal") {
955
- const decisionsInline = await inlineDecisionsFromDb(base, mid, undefined, inlineLevel);
1099
+ // Derive scope from slice title for decision filtering (R005)
1100
+ const derivedScopePS = deriveSliceScope(sTitle);
1101
+ const decisionsInline = await inlineDecisionsFromDb(base, mid, derivedScopePS, inlineLevel);
956
1102
  if (decisionsInline)
957
1103
  inlined.push(decisionsInline);
958
- const requirementsInline = await inlineRequirementsFromDb(base, sid, inlineLevel);
1104
+ const requirementsInline = await inlineRequirementsFromDb(base, mid, sid, inlineLevel);
959
1105
  if (requirementsInline)
960
1106
  inlined.push(requirementsInline);
961
1107
  }
962
- const knowledgeInlinePS = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
1108
+ // Use scoped knowledge based on slice title keywords
1109
+ const keywordsPS = extractKeywords(sTitle);
1110
+ const knowledgeInlinePS = await inlineKnowledgeScoped(base, keywordsPS);
963
1111
  if (knowledgeInlinePS)
964
1112
  inlined.push(knowledgeInlinePS);
965
1113
  inlined.push(inlineTemplate("plan", "Slice Plan"));
@@ -1117,7 +1265,7 @@ export async function buildCompleteSlicePrompt(mid, _midTitle, sid, sTitle, base
1117
1265
  inlined.push(sliceCtxInline);
1118
1266
  inlined.push(await inlineFile(slicePlanPath, slicePlanRel, "Slice Plan"));
1119
1267
  if (inlineLevel !== "minimal") {
1120
- const requirementsInline = await inlineRequirementsFromDb(base, sid, inlineLevel);
1268
+ const requirementsInline = await inlineRequirementsFromDb(base, mid, sid, inlineLevel);
1121
1269
  if (requirementsInline)
1122
1270
  inlined.push(requirementsInline);
1123
1271
  }
@@ -1195,7 +1343,7 @@ export async function buildCompleteMilestonePrompt(mid, midTitle, base, level) {
1195
1343
  }
1196
1344
  // Inline root GSD files (skip for minimal — completion can read these if needed)
1197
1345
  if (inlineLevel !== "minimal") {
1198
- const requirementsInline = await inlineRequirementsFromDb(base, undefined, inlineLevel);
1346
+ const requirementsInline = await inlineRequirementsFromDb(base, mid, undefined, inlineLevel);
1199
1347
  if (requirementsInline)
1200
1348
  inlined.push(requirementsInline);
1201
1349
  const decisionsInline = await inlineDecisionsFromDb(base, mid, undefined, inlineLevel);
@@ -1262,7 +1410,7 @@ export async function buildValidateMilestonePrompt(mid, midTitle, base, level) {
1262
1410
  catch (err) {
1263
1411
  logWarning("prompt", `buildValidateMilestonePrompt verification classes lookup failed: ${err instanceof Error ? err.message : String(err)}`);
1264
1412
  }
1265
- // Inline all slice summaries and UAT results
1413
+ // Inline all slice summaries and assessment results
1266
1414
  let valSliceIds = [];
1267
1415
  try {
1268
1416
  const { isDbAvailable, getMilestoneSlices } = await import("./gsd-db.js");
@@ -1288,11 +1436,11 @@ export async function buildValidateMilestonePrompt(mid, midTitle, base, level) {
1288
1436
  const summaryPath = resolveSliceFile(base, mid, sid, "SUMMARY");
1289
1437
  const summaryRel = relSliceFile(base, mid, sid, "SUMMARY");
1290
1438
  inlined.push(await inlineFile(summaryPath, summaryRel, `${sid} Summary`));
1291
- const uatPath = resolveSliceFile(base, mid, sid, "UAT");
1292
- const uatRel = relSliceFile(base, mid, sid, "UAT");
1293
- const uatInline = await inlineFileOptional(uatPath, uatRel, `${sid} UAT Result`);
1294
- if (uatInline)
1295
- inlined.push(uatInline);
1439
+ const assessmentPath = resolveSliceFile(base, mid, sid, "ASSESSMENT");
1440
+ const assessmentRel = relSliceFile(base, mid, sid, "ASSESSMENT");
1441
+ const assessmentInline = await inlineFileOptional(assessmentPath, assessmentRel, `${sid} Assessment`);
1442
+ if (assessmentInline)
1443
+ inlined.push(assessmentInline);
1296
1444
  }
1297
1445
  // Aggregate unresolved follow-ups and known limitations across slices
1298
1446
  const outstandingItems = [];
@@ -1324,7 +1472,7 @@ export async function buildValidateMilestonePrompt(mid, midTitle, base, level) {
1324
1472
  }
1325
1473
  // Inline root GSD files
1326
1474
  if (inlineLevel !== "minimal") {
1327
- const requirementsInline = await inlineRequirementsFromDb(base, undefined, inlineLevel);
1475
+ const requirementsInline = await inlineRequirementsFromDb(base, mid, undefined, inlineLevel);
1328
1476
  if (requirementsInline)
1329
1477
  inlined.push(requirementsInline);
1330
1478
  const decisionsInline = await inlineDecisionsFromDb(base, mid, undefined, inlineLevel);
@@ -1487,7 +1635,7 @@ export async function buildReassessRoadmapPrompt(mid, midTitle, completedSliceId
1487
1635
  const projectInline = await inlineProjectFromDb(base);
1488
1636
  if (projectInline)
1489
1637
  inlined.push(projectInline);
1490
- const requirementsInline = await inlineRequirementsFromDb(base, undefined, inlineLevel);
1638
+ const requirementsInline = await inlineRequirementsFromDb(base, mid, undefined, inlineLevel);
1491
1639
  if (requirementsInline)
1492
1640
  inlined.push(requirementsInline);
1493
1641
  const decisionsInline = await inlineDecisionsFromDb(base, mid, undefined, inlineLevel);
@@ -10,7 +10,7 @@ import { parseUnitId } from "./unit-id.js";
10
10
  import { appendEvent } from "./workflow-events.js";
11
11
  import { clearParseCache } from "./files.js";
12
12
  import { parseRoadmap as parseLegacyRoadmap, parsePlan as parseLegacyPlan } from "./parsers-legacy.js";
13
- import { isDbAvailable, getTask, getSlice, getSliceTasks, updateTaskStatus, updateSliceStatus } from "./gsd-db.js";
13
+ import { isDbAvailable, getTask, getSlice, getSliceTasks, getPendingGates, updateTaskStatus, updateSliceStatus } from "./gsd-db.js";
14
14
  import { isValidationTerminal } from "./state.js";
15
15
  import { getErrorMessage } from "./error-utils.js";
16
16
  import { logWarning, logError } from "./workflow-logger.js";
@@ -198,8 +198,7 @@ export function verifyExpectedArtifact(unitType, unitId, base) {
198
198
  if (gateIds.length === 0)
199
199
  return true;
200
200
  try {
201
- const { getPendingGates: getPending } = require("./gsd-db.js");
202
- const pending = getPending(mid, sid, "slice");
201
+ const pending = getPendingGates(mid, sid, "slice");
203
202
  const pendingIds = new Set(pending.map((g) => g.gate_id));
204
203
  // All dispatched gates must no longer be pending
205
204
  for (const gid of gateIds) {
@@ -454,9 +453,8 @@ function abortAndResetMerge(basePath, hasMergeHead, squashMsgPath) {
454
453
  /**
455
454
  * Detect leftover merge state from a prior session and reconcile it.
456
455
  * If MERGE_HEAD or SQUASH_MSG exists, check whether conflicts are resolved.
457
- * If resolved: finalize the commit. If still conflicted: abort and reset.
458
- *
459
- * Returns true if state was dirty and re-derivation is needed.
456
+ * If resolved: finalize the commit. If only .gsd conflicts remain: auto-resolve.
457
+ * If code conflicts remain: fail safe without modifying the worktree.
460
458
  */
461
459
  export function reconcileMergeState(basePath, ctx) {
462
460
  const mergeHeadPath = join(basePath, ".git", "MERGE_HEAD");
@@ -464,7 +462,7 @@ export function reconcileMergeState(basePath, ctx) {
464
462
  const hasMergeHead = existsSync(mergeHeadPath);
465
463
  const hasSquashMsg = existsSync(squashMsgPath);
466
464
  if (!hasMergeHead && !hasSquashMsg)
467
- return false;
465
+ return "clean";
468
466
  const conflictedFiles = nativeConflictFiles(basePath);
469
467
  if (conflictedFiles.length === 0) {
470
468
  // All conflicts resolved — finalize the merge/squash commit
@@ -481,7 +479,7 @@ export function reconcileMergeState(basePath, ctx) {
481
479
  catch (err) {
482
480
  const errorMessage = getErrorMessage(err);
483
481
  ctx.ui.notify(`Failed to finalize leftover merge/squash commit: ${errorMessage}`, "error");
484
- return false;
482
+ return "blocked";
485
483
  }
486
484
  }
487
485
  else {
@@ -515,12 +513,13 @@ export function reconcileMergeState(basePath, ctx) {
515
513
  }
516
514
  }
517
515
  else {
518
- // Code conflicts present — abort and reset
519
- abortAndResetMerge(basePath, hasMergeHead, squashMsgPath);
520
- ctx.ui.notify("Detected leftover merge state with unresolved conflicts cleaned up. Re-deriving state.", "warning");
516
+ // Code conflicts present — fail safe and preserve any manual resolution
517
+ // work instead of discarding it with merge --abort/reset --hard.
518
+ ctx.ui.notify("Detected leftover merge state with unresolved code conflicts. Auto-mode will pause without modifying the worktree so manual conflict resolution is preserved.", "error");
519
+ return "blocked";
521
520
  }
522
521
  }
523
- return true;
522
+ return "reconciled";
524
523
  }
525
524
  // ─── Loop Remediation ─────────────────────────────────────────────────────────
526
525
  /**
@@ -906,7 +906,19 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
906
906
  s.active = true;
907
907
  s.verbose = verboseMode;
908
908
  s.stepMode = requestedStepMode;
909
- s.cmdCtx = ctx;
909
+ // Preserve the original cmdCtx (ExtensionCommandContext with newSession)
910
+ // when resuming from a provider-error pause. The resume callback receives
911
+ // an ExtensionContext (from the agent_end hook) which lacks newSession —
912
+ // using it would crash runUnit with "newSession is not a function".
913
+ // Only override if the new ctx actually has newSession (user-initiated resume).
914
+ if ("newSession" in ctx && typeof ctx.newSession === "function") {
915
+ s.cmdCtx = ctx;
916
+ }
917
+ else if (!s.cmdCtx) {
918
+ // No saved cmdCtx — this shouldn't happen, but handle gracefully
919
+ s.cmdCtx = ctx;
920
+ }
921
+ // else: keep existing s.cmdCtx which has the real newSession
910
922
  s.basePath = base;
911
923
  setLogBasePath(base);
912
924
  if (!s.autoStartTime || s.autoStartTime <= 0)