@opengsd/gsd-pi 1.1.1-dev.74e8dd1 → 1.1.1-dev.9bb7453

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 (259) hide show
  1. package/dist/cli.js +3 -2
  2. package/dist/help-text.js +10 -6
  3. package/dist/resources/.managed-resources-content-hash +1 -1
  4. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +495 -0
  5. package/dist/resources/extensions/browser-tools/engine/selection.js +16 -0
  6. package/dist/resources/extensions/browser-tools/extension-manifest.json +2 -2
  7. package/dist/resources/extensions/browser-tools/index.js +57 -9
  8. package/dist/resources/extensions/browser-tools/package.json +5 -1
  9. package/dist/resources/extensions/gsd/auto/orchestrator.js +0 -1
  10. package/dist/resources/extensions/gsd/auto-dashboard.js +77 -13
  11. package/dist/resources/extensions/gsd/auto-dispatch.js +5 -0
  12. package/dist/resources/extensions/gsd/auto-post-unit.js +21 -3
  13. package/dist/resources/extensions/gsd/auto-prompts.js +59 -22
  14. package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
  15. package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
  16. package/dist/resources/extensions/gsd/auto.js +9 -2
  17. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +4 -4
  18. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +9 -5
  19. package/dist/resources/extensions/gsd/browser-evidence.js +29 -2
  20. package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -2
  21. package/dist/resources/extensions/gsd/commands-handlers.js +76 -11
  22. package/dist/resources/extensions/gsd/commands-mcp-status.js +2 -1
  23. package/dist/resources/extensions/gsd/dashboard-overlay.js +21 -7
  24. package/dist/resources/extensions/gsd/docs/preferences-reference.md +8 -0
  25. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +2 -2
  26. package/dist/resources/extensions/gsd/escalation.js +4 -4
  27. package/dist/resources/extensions/gsd/forensics.js +74 -2
  28. package/dist/resources/extensions/gsd/gsd-db.js +5 -2
  29. package/dist/resources/extensions/gsd/guided-flow.js +29 -68
  30. package/dist/resources/extensions/gsd/mcp-project-config.js +9 -76
  31. package/dist/resources/extensions/gsd/memory-store.js +4 -1
  32. package/dist/resources/extensions/gsd/post-unit-hooks.js +9 -0
  33. package/dist/resources/extensions/gsd/preferences-validation.js +39 -0
  34. package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
  35. package/dist/resources/extensions/gsd/prompts/forensics.md +61 -1
  36. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
  37. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
  38. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  39. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
  40. package/dist/resources/extensions/gsd/prompts/run-uat.md +40 -22
  41. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
  42. package/dist/resources/extensions/gsd/rule-registry.js +428 -52
  43. package/dist/resources/extensions/gsd/state.js +2 -2
  44. package/dist/resources/extensions/gsd/templates/plan.md +3 -1
  45. package/dist/resources/extensions/gsd/tools/complete-slice.js +15 -1
  46. package/dist/resources/extensions/gsd/tools/complete-task.js +11 -1
  47. package/dist/resources/extensions/gsd/tools/validate-milestone.js +46 -16
  48. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +51 -14
  49. package/dist/resources/extensions/gsd/verdict-parser.js +59 -15
  50. package/dist/resources/extensions/gsd/verification-gate.js +72 -1
  51. package/dist/resources/extensions/shared/gsd-browser-cli.js +145 -0
  52. package/dist/rtk.d.ts +7 -1
  53. package/dist/rtk.js +27 -11
  54. package/dist/update-check.d.ts +15 -1
  55. package/dist/update-check.js +87 -12
  56. package/dist/update-cmd.d.ts +1 -0
  57. package/dist/update-cmd.js +53 -2
  58. package/dist/web/standalone/.next/BUILD_ID +1 -1
  59. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  60. package/dist/web/standalone/.next/build-manifest.json +2 -2
  61. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  62. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  79. package/dist/web/standalone/.next/server/app/index.html +1 -1
  80. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  87. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  88. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  90. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  91. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  92. package/package.json +4 -2
  93. package/packages/cloud-mcp-gateway/package.json +2 -2
  94. package/packages/contracts/package.json +1 -1
  95. package/packages/daemon/package.json +4 -4
  96. package/packages/gsd-agent-core/dist/agent-session.d.ts +9 -0
  97. package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
  98. package/packages/gsd-agent-core/dist/agent-session.js +32 -0
  99. package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
  100. package/packages/gsd-agent-core/dist/index.d.ts +1 -0
  101. package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
  102. package/packages/gsd-agent-core/dist/index.js +1 -0
  103. package/packages/gsd-agent-core/dist/index.js.map +1 -1
  104. package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts +2 -0
  105. package/packages/gsd-agent-core/dist/session/agent-session-compaction.d.ts.map +1 -1
  106. package/packages/gsd-agent-core/dist/session/agent-session-compaction.js +8 -2
  107. package/packages/gsd-agent-core/dist/session/agent-session-compaction.js.map +1 -1
  108. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +7 -0
  109. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
  110. package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
  111. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
  112. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +69 -1
  113. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
  114. package/packages/gsd-agent-core/dist/turn-latency.d.ts +47 -0
  115. package/packages/gsd-agent-core/dist/turn-latency.d.ts.map +1 -0
  116. package/packages/gsd-agent-core/dist/turn-latency.js +123 -0
  117. package/packages/gsd-agent-core/dist/turn-latency.js.map +1 -0
  118. package/packages/gsd-agent-core/package.json +6 -6
  119. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts +21 -0
  120. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.d.ts.map +1 -0
  121. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js +213 -0
  122. package/packages/gsd-agent-modes/dist/modes/interactive/components/__prototype__/gsd-widget-prototype.js.map +1 -0
  123. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  124. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +20 -0
  125. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  126. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  127. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js +7 -1
  128. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  129. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.d.ts.map +1 -1
  130. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js +6 -0
  131. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-command-handlers.js.map +1 -1
  132. package/packages/gsd-agent-modes/package.json +7 -7
  133. package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -1
  134. package/packages/mcp-server/dist/remote-questions.js +23 -9
  135. package/packages/mcp-server/dist/remote-questions.js.map +1 -1
  136. package/packages/mcp-server/dist/workflow-tools.js +2 -2
  137. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  138. package/packages/mcp-server/package.json +3 -3
  139. package/packages/native/package.json +1 -1
  140. package/packages/pi-agent-core/dist/agent-loop.js +38 -0
  141. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  142. package/packages/pi-agent-core/dist/agent.d.ts +5 -1
  143. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  144. package/packages/pi-agent-core/dist/agent.js +2 -0
  145. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  146. package/packages/pi-agent-core/dist/types.d.ts +3 -0
  147. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  148. package/packages/pi-agent-core/dist/types.js.map +1 -1
  149. package/packages/pi-agent-core/package.json +1 -1
  150. package/packages/pi-ai/dist/api-registry.d.ts +2 -0
  151. package/packages/pi-ai/dist/api-registry.d.ts.map +1 -1
  152. package/packages/pi-ai/dist/api-registry.js +23 -0
  153. package/packages/pi-ai/dist/api-registry.js.map +1 -1
  154. package/packages/pi-ai/dist/models.generated.d.ts +68 -0
  155. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  156. package/packages/pi-ai/dist/models.generated.js +72 -4
  157. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  158. package/packages/pi-ai/dist/stream.js +6 -6
  159. package/packages/pi-ai/dist/stream.js.map +1 -1
  160. package/packages/pi-ai/package.json +1 -1
  161. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  162. package/packages/pi-coding-agent/dist/core/model-registry.js +2 -2
  163. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  164. package/packages/pi-coding-agent/package.json +7 -7
  165. package/packages/pi-tui/package.json +1 -1
  166. package/packages/rpc-client/package.json +2 -2
  167. package/pkg/package.json +1 -1
  168. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +579 -0
  169. package/src/resources/extensions/browser-tools/engine/selection.ts +19 -0
  170. package/src/resources/extensions/browser-tools/extension-manifest.json +2 -2
  171. package/src/resources/extensions/browser-tools/index.ts +60 -9
  172. package/src/resources/extensions/browser-tools/package.json +5 -1
  173. package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +35 -0
  174. package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +33 -0
  175. package/src/resources/extensions/gsd/auto/orchestrator.ts +0 -1
  176. package/src/resources/extensions/gsd/auto-dashboard.ts +82 -14
  177. package/src/resources/extensions/gsd/auto-dispatch.ts +5 -0
  178. package/src/resources/extensions/gsd/auto-post-unit.ts +28 -2
  179. package/src/resources/extensions/gsd/auto-prompts.ts +93 -15
  180. package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -0
  181. package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
  182. package/src/resources/extensions/gsd/auto.ts +12 -2
  183. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +4 -4
  184. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +9 -5
  185. package/src/resources/extensions/gsd/browser-evidence.ts +26 -2
  186. package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -2
  187. package/src/resources/extensions/gsd/commands-handlers.ts +76 -11
  188. package/src/resources/extensions/gsd/commands-mcp-status.ts +2 -1
  189. package/src/resources/extensions/gsd/dashboard-overlay.ts +28 -7
  190. package/src/resources/extensions/gsd/docs/preferences-reference.md +8 -0
  191. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +2 -2
  192. package/src/resources/extensions/gsd/escalation.ts +4 -4
  193. package/src/resources/extensions/gsd/forensics.ts +99 -5
  194. package/src/resources/extensions/gsd/gsd-db.ts +5 -2
  195. package/src/resources/extensions/gsd/guided-flow.ts +90 -82
  196. package/src/resources/extensions/gsd/mcp-project-config.ts +13 -78
  197. package/src/resources/extensions/gsd/memory-store.ts +4 -1
  198. package/src/resources/extensions/gsd/post-unit-hooks.ts +14 -1
  199. package/src/resources/extensions/gsd/preferences-validation.ts +36 -0
  200. package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
  201. package/src/resources/extensions/gsd/prompts/forensics.md +61 -1
  202. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +3 -1
  203. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +3 -1
  204. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  205. package/src/resources/extensions/gsd/prompts/reactive-execute.md +3 -1
  206. package/src/resources/extensions/gsd/prompts/run-uat.md +40 -22
  207. package/src/resources/extensions/gsd/prompts/validate-milestone.md +3 -3
  208. package/src/resources/extensions/gsd/rule-registry.ts +558 -58
  209. package/src/resources/extensions/gsd/rule-types.ts +2 -0
  210. package/src/resources/extensions/gsd/state.ts +2 -2
  211. package/src/resources/extensions/gsd/templates/plan.md +3 -1
  212. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +105 -4
  213. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +37 -0
  214. package/src/resources/extensions/gsd/tests/browser-evidence.test.ts +142 -0
  215. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +30 -0
  216. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +42 -0
  217. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +45 -0
  218. package/src/resources/extensions/gsd/tests/deep-planning-mode-dispatch.test.ts +53 -0
  219. package/src/resources/extensions/gsd/tests/discuss-milestone-structured-questions.test.ts +31 -0
  220. package/src/resources/extensions/gsd/tests/doctor-runtime-checks.test.ts +27 -0
  221. package/src/resources/extensions/gsd/tests/escalation.test.ts +16 -27
  222. package/src/resources/extensions/gsd/tests/forensics-issue-routing.test.ts +20 -0
  223. package/src/resources/extensions/gsd/tests/forensics-prompt-rendering.test.ts +3 -0
  224. package/src/resources/extensions/gsd/tests/forensics-tool-scope.test.ts +69 -0
  225. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +40 -1
  226. package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +86 -0
  227. package/src/resources/extensions/gsd/tests/guided-flow.test.ts +12 -9
  228. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +4 -4
  229. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +66 -10
  230. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +32 -0
  231. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +2 -0
  232. package/src/resources/extensions/gsd/tests/memory-maintenance.test.ts +39 -8
  233. package/src/resources/extensions/gsd/tests/new-milestone-discuss-routing.test.ts +3 -3
  234. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +9 -0
  235. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +157 -0
  236. package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +179 -0
  237. package/src/resources/extensions/gsd/tests/preferences.test.ts +29 -0
  238. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +43 -1
  239. package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +14 -0
  240. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +7 -8
  241. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +75 -0
  242. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +100 -0
  243. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +139 -0
  244. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +19 -0
  245. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +7 -1
  246. package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +6 -3
  247. package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +133 -0
  248. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +51 -0
  249. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +130 -0
  250. package/src/resources/extensions/gsd/tools/complete-slice.ts +14 -1
  251. package/src/resources/extensions/gsd/tools/complete-task.ts +20 -2
  252. package/src/resources/extensions/gsd/tools/validate-milestone.ts +46 -15
  253. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +63 -15
  254. package/src/resources/extensions/gsd/types.ts +69 -5
  255. package/src/resources/extensions/gsd/verdict-parser.ts +54 -13
  256. package/src/resources/extensions/gsd/verification-gate.ts +87 -1
  257. package/src/resources/extensions/shared/gsd-browser-cli.ts +172 -0
  258. /package/dist/web/standalone/.next/static/{eRWf-RI9bzbrwEurm_3uI → jBtwT9v1u2lUA3UEOy_ZH}/_buildManifest.js +0 -0
  259. /package/dist/web/standalone/.next/static/{eRWf-RI9bzbrwEurm_3uI → jBtwT9v1u2lUA3UEOy_ZH}/_ssgManifest.js +0 -0
@@ -5,6 +5,8 @@
5
5
  * handleRunHook, handleUpdate, handleSkillHealth
6
6
  */
7
7
  import { existsSync, readFileSync, mkdirSync } from "node:fs";
8
+ import { execFileSync } from "node:child_process";
9
+ import { createRequire } from "node:module";
8
10
  import { join, resolve as resolvePath, sep } from "node:path";
9
11
  import { homedir } from "node:os";
10
12
  import { deriveState } from "./state.js";
@@ -20,7 +22,10 @@ import { loadPrompt } from "./prompt-loader.js";
20
22
  import { isPnpmInstall } from "../../shared/package-manager-detection.js";
21
23
  import { buildDoctorHealIssuePayload, buildDoctorHealSummary, buildWorkflowDispatchContent, } from "./workflow-protocol.js";
22
24
  import { restoreGsdWorkflowTools, scopeGsdWorkflowToolsForDispatch, } from "./bootstrap/register-hooks.js";
25
+ const GSD_PI_PACKAGE = "@opengsd/gsd-pi";
26
+ const GSD_BROWSER_PACKAGE = "@opengsd/gsd-browser";
23
27
  const UPDATE_REGISTRY_URL = "https://registry.npmjs.org/@opengsd%2fgsd-pi/latest";
28
+ const BROWSER_UPDATE_REGISTRY_URL = "https://registry.npmjs.org/@opengsd%2fgsd-browser/latest";
24
29
  const UPDATE_FETCH_TIMEOUT_MS = 5000;
25
30
  // Detects a bun-installed gsd via `process.argv[1]`. Mirrors isBunInstall in
26
31
  // src/update-check.ts — duplicated because tsconfig.resources.json rootDir
@@ -47,11 +52,11 @@ function resolveInstallCommand(pkg) {
47
52
  return `pnpm add -g ${pkg}`;
48
53
  return `npm install -g ${pkg}`;
49
54
  }
50
- async function fetchLatestVersionForCommand() {
55
+ async function fetchLatestVersionForCommand(registryUrl = UPDATE_REGISTRY_URL) {
51
56
  const controller = new AbortController();
52
57
  const timeout = setTimeout(() => controller.abort(), UPDATE_FETCH_TIMEOUT_MS);
53
58
  try {
54
- const res = await fetch(UPDATE_REGISTRY_URL, { signal: controller.signal });
59
+ const res = await fetch(registryUrl, { signal: controller.signal });
55
60
  if (!res.ok)
56
61
  return null;
57
62
  const data = (await res.json());
@@ -65,6 +70,19 @@ async function fetchLatestVersionForCommand() {
65
70
  clearTimeout(timeout);
66
71
  }
67
72
  }
73
+ function resolveInstalledPackageVersionForCommand(packageName) {
74
+ try {
75
+ const requireFromHere = createRequire(import.meta.url);
76
+ const packageJsonPath = requireFromHere.resolve(`${packageName}/package.json`);
77
+ const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
78
+ return typeof pkg.version === "string" && pkg.version.trim().length > 0
79
+ ? pkg.version.trim().replace(/^v/, "")
80
+ : null;
81
+ }
82
+ catch {
83
+ return null;
84
+ }
85
+ }
68
86
  export function dispatchDoctorHeal(pi, scope, reportText, structuredIssues) {
69
87
  const workflowPath = process.env.GSD_WORKFLOW_PATH ?? join(gsdHome(), "agent", "GSD-WORKFLOW.md");
70
88
  const workflow = readFileSync(workflowPath, "utf-8");
@@ -386,27 +404,74 @@ function compareSemverLocal(a, b) {
386
404
  }
387
405
  return 0;
388
406
  }
389
- export async function handleUpdate(ctx) {
407
+ function formatCommandVersion(version) {
408
+ return version ? `v${version}` : "unknown";
409
+ }
410
+ function pickHigherVersionForCommand(a, b) {
411
+ if (!a)
412
+ return b;
413
+ if (!b)
414
+ return a;
415
+ return compareSemverLocal(a, b) >= 0 ? a : b;
416
+ }
417
+ // Mirrors resolveGsdBrowserPathVersion in src/update-check.ts — duplicated because
418
+ // tsconfig.resources.json rootDir prevents importing from src/.
419
+ function resolveGsdBrowserPathVersionForCommand(env = process.env) {
420
+ const explicit = env.GSD_BROWSER_PATH_VERSION?.trim();
421
+ if (explicit)
422
+ return explicit.match(/\b(\d+\.\d+\.\d+)\b/)?.[1] ?? null;
423
+ try {
424
+ const out = execFileSync("gsd-browser", ["--version"], {
425
+ encoding: "utf-8",
426
+ env,
427
+ stdio: ["ignore", "pipe", "ignore"],
428
+ timeout: 2000,
429
+ });
430
+ return out.match(/\b(\d+\.\d+\.\d+)\b/)?.[1] ?? null;
431
+ }
432
+ catch {
433
+ return null;
434
+ }
435
+ }
436
+ export async function handleUpdate(ctx, args = "") {
390
437
  const { execSync } = await import("node:child_process");
391
- const NPM_PACKAGE = "@opengsd/gsd-pi";
392
- const current = process.env.GSD_VERSION || "0.0.0";
393
- ctx.ui.notify(`Current version: v${current}\nChecking npm registry...`, "info");
394
- const latest = await fetchLatestVersionForCommand();
438
+ const target = args.trim();
439
+ const browserUpdate = target === "browser" || target === "gsd-browser";
440
+ if (target && !browserUpdate) {
441
+ ctx.ui.notify("Usage: /gsd update [browser]", "warning");
442
+ return;
443
+ }
444
+ const NPM_PACKAGE = browserUpdate ? GSD_BROWSER_PACKAGE : GSD_PI_PACKAGE;
445
+ const registryUrl = browserUpdate ? BROWSER_UPDATE_REGISTRY_URL : UPDATE_REGISTRY_URL;
446
+ const bundledVersion = browserUpdate
447
+ ? resolveInstalledPackageVersionForCommand(GSD_BROWSER_PACKAGE)
448
+ : null;
449
+ const current = browserUpdate
450
+ ? pickHigherVersionForCommand(bundledVersion, resolveGsdBrowserPathVersionForCommand())
451
+ : process.env.GSD_VERSION || "0.0.0";
452
+ const label = browserUpdate ? "gsd-browser version" : "version";
453
+ ctx.ui.notify(`Current ${label}: ${formatCommandVersion(current)}\nChecking npm registry...`, "info");
454
+ const latest = await fetchLatestVersionForCommand(registryUrl);
395
455
  if (!latest) {
396
456
  ctx.ui.notify("Failed to reach npm registry. Check your network connection.", "error");
397
457
  return;
398
458
  }
399
- if (compareSemverLocal(latest, current) <= 0) {
400
- ctx.ui.notify(`Already up to date (v${current}).`, "info");
459
+ if (current && compareSemverLocal(latest, current) <= 0) {
460
+ ctx.ui.notify(`Already up to date (${formatCommandVersion(current)}).`, "info");
401
461
  return;
402
462
  }
403
- ctx.ui.notify(`Updating: v${current} → v${latest}...`, "info");
463
+ ctx.ui.notify(`Updating: ${formatCommandVersion(current)} → v${latest}...`, "info");
404
464
  const installCmd = resolveInstallCommand(`${NPM_PACKAGE}@latest`);
405
465
  try {
406
466
  execSync(installCmd, {
407
467
  stdio: ["ignore", "pipe", "ignore"],
408
468
  });
409
- ctx.ui.notify(`Updated to v${latest}. Restart your GSD session to use the new version.`, "info");
469
+ const newPathVersion = browserUpdate ? resolveGsdBrowserPathVersionForCommand() : null;
470
+ const pathReady = !browserUpdate || (!!newPathVersion && compareSemverLocal(newPathVersion, latest) >= 0);
471
+ ctx.ui.notify(browserUpdate
472
+ ? `Updated gsd-browser to v${latest}. Restart your GSD session to use the new browser automation version.` +
473
+ (pathReady ? "" : "\nNote: Ensure the npm global bin directory is on your PATH so MCP automation uses the updated binary.")
474
+ : `Updated to v${latest}. Restart your GSD session to use the new version.`, "info");
410
475
  }
411
476
  catch {
412
477
  ctx.ui.notify(`Update failed. Try manually: ${installCmd}`, "error");
@@ -33,7 +33,8 @@ export function formatMcpInitResult(status, configPath, targetPath) {
33
33
  `Project: ${targetPath}`,
34
34
  `Config: ${configPath}`,
35
35
  "",
36
- "MCP-capable clients can now load the GSD workflow MCP server from this folder.",
36
+ "MCP-capable clients can now load the GSD workflow and gsd-browser MCP servers from this folder.",
37
+ "Pi Providers use the managed gsd-browser engine directly; this project config is for External MCP Clients.",
37
38
  "Restart or reconnect any client that already has this project open.",
38
39
  ].join("\n");
39
40
  }
@@ -62,6 +62,8 @@ export class GSDDashboardOverlay {
62
62
  refreshInFlight = null;
63
63
  disposed = false;
64
64
  resizeHandler = null;
65
+ cachedMetrics = null;
66
+ lastSeenUnitCount = -1;
65
67
  constructor(tui, theme, onClose) {
66
68
  this.tui = tui;
67
69
  this.theme = theme;
@@ -105,7 +107,8 @@ export class GSDDashboardOverlay {
105
107
  return;
106
108
  this.dashData = getAutoDashboardData();
107
109
  const nextIdentity = this.computeDashboardIdentity(this.dashData);
108
- if (initial || nextIdentity !== this.loadedDashboardIdentity) {
110
+ const identityChanged = initial || nextIdentity !== this.loadedDashboardIdentity;
111
+ if (identityChanged) {
109
112
  const loaded = await this.loadData();
110
113
  if (this.disposed)
111
114
  return;
@@ -116,7 +119,9 @@ export class GSDDashboardOverlay {
116
119
  if (initial) {
117
120
  this.loading = false;
118
121
  }
119
- this.invalidate();
122
+ if (identityChanged) {
123
+ this.invalidate();
124
+ }
120
125
  this.tui.requestRender();
121
126
  }
122
127
  async loadData() {
@@ -392,7 +397,7 @@ export class GSDDashboardOverlay {
392
397
  }
393
398
  const ledger = getLedger();
394
399
  if (ledger && ledger.units.length > 0) {
395
- const totals = getProjectTotals(ledger.units);
400
+ const { totals, promptStats, phases, slices, models } = this.ensureMetricsCache(ledger.units);
396
401
  lines.push(blank());
397
402
  lines.push(hr());
398
403
  lines.push(row(th.fg("text", th.bold("Cost & Usage"))));
@@ -424,7 +429,6 @@ export class GSDDashboardOverlay {
424
429
  }
425
430
  lines.push(row(budgetParts.join(` ${th.fg("dim", "·")} `)));
426
431
  }
427
- const promptStats = getPromptSizeStats(ledger.units);
428
432
  if (promptStats) {
429
433
  const promptParts = [
430
434
  `${th.fg("dim", "avg prompt:")} ${th.fg("text", formatCharCount(promptStats.averagePromptChars))}`,
@@ -435,7 +439,6 @@ export class GSDDashboardOverlay {
435
439
  }
436
440
  lines.push(row(promptParts.join(` ${th.fg("dim", "·")} `)));
437
441
  }
438
- const phases = aggregateByPhase(ledger.units);
439
442
  if (phases.length > 0) {
440
443
  lines.push(blank());
441
444
  lines.push(row(th.fg("dim", "By Phase")));
@@ -446,7 +449,6 @@ export class GSDDashboardOverlay {
446
449
  lines.push(row(joinColumns(left, right, contentWidth)));
447
450
  }
448
451
  }
449
- const slices = aggregateBySlice(ledger.units);
450
452
  if (slices.length > 0) {
451
453
  lines.push(blank());
452
454
  lines.push(row(th.fg("dim", "By Slice")));
@@ -475,7 +477,6 @@ export class GSDDashboardOverlay {
475
477
  }
476
478
  }
477
479
  }
478
- const models = aggregateByModel(ledger.units);
479
480
  if (models.length >= 1) {
480
481
  lines.push(blank());
481
482
  lines.push(row(th.fg("dim", "By Model")));
@@ -534,6 +535,19 @@ export class GSDDashboardOverlay {
534
535
  const bar = th.fg(color, "█".repeat(filled)) + th.fg("dim", "░".repeat(Math.max(0, barWidth - filled)));
535
536
  return `${th.fg("dim", labelText)}${" ".repeat(gap)}${bar}${" ".repeat(gap)}${th.fg("dim", rightText)}`;
536
537
  }
538
+ ensureMetricsCache(units) {
539
+ if (!this.cachedMetrics || units.length !== this.lastSeenUnitCount) {
540
+ this.cachedMetrics = {
541
+ totals: getProjectTotals(units),
542
+ promptStats: getPromptSizeStats(units),
543
+ phases: aggregateByPhase(units),
544
+ slices: aggregateBySlice(units),
545
+ models: aggregateByModel(units),
546
+ };
547
+ this.lastSeenUnitCount = units.length;
548
+ }
549
+ return this.cachedMetrics;
550
+ }
537
551
  invalidate() {
538
552
  this.cachedWidth = undefined;
539
553
  this.cachedLines = undefined;
@@ -305,10 +305,18 @@ This config sets a parent workspace with two child repositories. The implicit `p
305
305
  - `max_cycles`: number — max times this hook fires per trigger (default: 1, max: 10).
306
306
  - `model`: string — optional model override.
307
307
  - `artifact`: string — expected output file name (relative to task/slice dir). Hook is skipped if file already exists (idempotent).
308
+ - `criticality`: `"advisory"` or `"blocking"` — advisory preserves current best-effort behavior; blocking requires clean hook completion plus a valid outcome verdict before auto-mode advances. Default: `"advisory"`.
308
309
  - `retry_on`: string — if this file is produced instead of the artifact, re-run the trigger unit then re-run hooks.
310
+ - `on_block`: object — optional routing for blocking findings:
311
+ - `action`: `"retry-unit"`, `"retry-task"`, `"queue-task"`, `"queue-slice"`, or `"pause"`.
312
+ - `artifact`: string — optional compatibility artifact for retry routing.
309
313
  - `agent`: string — agent definition file to use for hook execution.
310
314
  - `enabled`: boolean — toggle without removing (default: `true`).
311
315
 
316
+ Blocking hook artifacts must begin with YAML frontmatter containing either `verdict` or `outcome.verdict`.
317
+ Supported verdicts are `pass`, `advisory`, `needs-rework`, `needs-remediation`, and `needs-attention`.
318
+ `pass` and `advisory` continue; `needs-rework` retries the trigger unit when routed with `retry-unit`/`retry-task`; `needs-remediation` and `needs-attention` pause with recovery guidance.
319
+
312
320
  - `pre_dispatch_hooks`: array — hooks that fire before a unit is dispatched. Each entry has:
313
321
  - `name`: string — unique hook identifier.
314
322
  - `before`: string[] — unit types to intercept.
@@ -258,14 +258,14 @@ export async function checkRuntimeHealth(basePath, issues, fixesApplied, shouldF
258
258
  catch {
259
259
  count = MAX_UAT_ATTEMPTS + 1;
260
260
  }
261
- if (count <= MAX_UAT_ATTEMPTS)
261
+ if (count < MAX_UAT_ATTEMPTS)
262
262
  continue;
263
263
  issues.push({
264
264
  severity: "warning",
265
265
  code: "uat_retry_exhausted",
266
266
  scope: "slice",
267
267
  unitId: `${mid}/${sid}`,
268
- message: `run-uat for ${mid}/${sid} exhausted ${count - 1} retry attempt(s) without an ASSESSMENT verdict. Reset the retry counter after fixing the underlying UAT/tool issue, then rerun /gsd auto.`,
268
+ message: `run-uat for ${mid}/${sid} exhausted ${count} attempt(s) without an ASSESSMENT verdict. Reset the retry counter after fixing the underlying UAT/tool issue, then rerun /gsd auto.`,
269
269
  file: `.gsd/runtime/${fileName}`,
270
270
  fixable: true,
271
271
  });
@@ -140,13 +140,13 @@ export function readEscalationArtifact(path) {
140
140
  }
141
141
  // ─── Detection ────────────────────────────────────────────────────────────
142
142
  /**
143
- * Returns the task id of the first task with an un-resolved pause-escalation
144
- * (escalation_pending=1, not yet respondedAt). awaiting_review slices are NOT
145
- * returned they don't pause the loop.
143
+ * Returns the task id of the first task with an unresolved escalation.
144
+ * `continueWithDefault=true` artifacts keep the awaiting_review flag for
145
+ * compatibility, but still pause dispatch until the user explicitly responds.
146
146
  */
147
147
  export function detectPendingEscalation(tasks, basePath) {
148
148
  for (const t of tasks) {
149
- if (t.escalation_pending !== 1)
149
+ if (t.escalation_pending !== 1 && t.escalation_awaiting_review !== 1)
150
150
  continue;
151
151
  if (!t.escalation_artifact_path)
152
152
  continue;
@@ -32,6 +32,68 @@ import { isInteractiveCommandContext, notifyForensicsNeedsInteractiveMenu, } fro
32
32
  import { ensurePreferencesFile, serializePreferencesToFrontmatter } from "./commands-prefs-wizard.js";
33
33
  import { summarizeWorktreeTelemetry, percentile } from "./worktree-telemetry.js";
34
34
  import { homedir } from "node:os";
35
+ // ─── Filing Tool Scope ───────────────────────────────────────────────────────
36
+ const FORENSICS_FILING_TOOLS = ["bash", "write"];
37
+ function uniqueAppend(base, additions) {
38
+ const seen = new Set(base);
39
+ const next = [...base];
40
+ for (const name of additions) {
41
+ if (seen.has(name))
42
+ continue;
43
+ seen.add(name);
44
+ next.push(name);
45
+ }
46
+ return next;
47
+ }
48
+ function sameOrderedTools(a, b) {
49
+ return a.length === b.length && a.every((name, index) => name === b[index]);
50
+ }
51
+ function getRegisteredToolNames(pi, fallback) {
52
+ if (typeof pi.getAllTools === "function") {
53
+ return pi.getAllTools().map((tool) => tool.name);
54
+ }
55
+ return [...fallback];
56
+ }
57
+ export function createForensicsToolScope(pi) {
58
+ const savedTools = [...pi.getActiveTools()];
59
+ const registeredTools = new Set([...getRegisteredToolNames(pi, savedTools), ...savedTools]);
60
+ const availableFilingTools = FORENSICS_FILING_TOOLS.filter((name) => registeredTools.has(name));
61
+ const missingFilingTools = FORENSICS_FILING_TOOLS.filter((name) => !registeredTools.has(name));
62
+ const activeToolsForTurn = uniqueAppend(savedTools, availableFilingTools);
63
+ return {
64
+ savedTools,
65
+ activeToolsForTurn,
66
+ availableFilingTools,
67
+ missingFilingTools,
68
+ toolsChanged: !sameOrderedTools(savedTools, activeToolsForTurn),
69
+ };
70
+ }
71
+ export function applyForensicsToolScope(pi, scope) {
72
+ if (scope.toolsChanged)
73
+ pi.setActiveTools(scope.activeToolsForTurn);
74
+ }
75
+ export function restoreForensicsToolScope(pi, scope) {
76
+ if (scope.toolsChanged)
77
+ pi.setActiveTools(scope.savedTools);
78
+ }
79
+ export function buildForensicsToolingSection(scope) {
80
+ const available = new Set(scope.availableFilingTools);
81
+ const requested = scope.availableFilingTools.length
82
+ ? scope.availableFilingTools.map((name) => `\`${name}\``).join(", ")
83
+ : "none";
84
+ const statusFor = (name) => available.has(name)
85
+ ? `- \`${name}\`: available for this queued forensics turn`
86
+ : `- \`${name}\`: unavailable in this host session`;
87
+ return `
88
+ ## Filing Tool Availability
89
+
90
+ For this queued forensic turn, the extension requested the registered filing tools: ${requested}.
91
+
92
+ ${FORENSICS_FILING_TOOLS.map(statusFor).join("\n")}
93
+
94
+ If \`bash\` is available, use the GitHub duplicate-check and issue-creation protocols below. If \`bash\` is unavailable, do not attempt duplicate-check or issue-creation tool calls with another tool; provide the paste-once shell script fallback instead.
95
+ `;
96
+ }
35
97
  // ─── Duplicate Detection ──────────────────────────────────────────────────────
36
98
  const DEDUP_PROMPT_SECTION = `
37
99
  ## Pre-Investigation: Duplicate Check (REQUIRED)
@@ -42,6 +104,8 @@ Before reading GSD source code or performing deep analysis, you MUST search for
42
104
 
43
105
  Use keywords from the user's problem description and the anomaly summaries in the forensic report above.
44
106
 
107
+ If \`bash\` is unavailable in the Filing Tool Availability section, do not attempt live duplicate-search tool calls. Say the live duplicate search must be run by the user, continue the source investigation, and include the duplicate-search commands in the paste-once fallback when issue filing is accepted.
108
+
45
109
  1. **Search closed issues** for similar keywords:
46
110
  \`\`\`
47
111
  gh issue list --repo open-gsd/gsd-pi --state closed --search "<keywords from root cause>" --limit 20
@@ -151,16 +215,24 @@ export async function handleForensics(args, ctx, pi) {
151
215
  gsdSourceDir = fallback;
152
216
  }
153
217
  const forensicData = formatReportForPrompt(report);
218
+ const toolScope = createForensicsToolScope(pi);
154
219
  const content = loadPrompt("forensics", {
155
220
  problemDescription,
156
221
  forensicData,
157
222
  gsdSourceDir,
158
223
  dedupSection,
224
+ toolingSection: buildForensicsToolingSection(toolScope),
159
225
  });
160
226
  ctx.ui.notify(`Forensic report saved: ${relative(basePath, savedPath)}`, "info");
161
227
  ctx.ui.setStatus("gsd-forensics", "running");
162
- pi.sendMessage({ customType: "gsd-forensics", content, display: false }, { triggerTurn: true });
163
- ctx.ui.setStatus("gsd-forensics", undefined);
228
+ try {
229
+ applyForensicsToolScope(pi, toolScope);
230
+ await pi.sendMessage({ customType: "gsd-forensics", content, display: false }, { triggerTurn: true });
231
+ }
232
+ finally {
233
+ restoreForensicsToolScope(pi, toolScope);
234
+ ctx.ui.setStatus("gsd-forensics", undefined);
235
+ }
164
236
  // Persist forensics context so follow-up turns can re-inject it (#2941)
165
237
  writeForensicsMarker(basePath, savedPath, content);
166
238
  }
@@ -1290,7 +1290,7 @@ export function setTaskEscalationPending(milestoneId, sliceId, taskId, artifactP
1290
1290
  escalation_artifact_path = :path
1291
1291
  WHERE milestone_id = :mid AND slice_id = :sid AND id = :tid`).run({ ":path": artifactPath, ":mid": milestoneId, ":sid": sliceId, ":tid": taskId });
1292
1292
  }
1293
- /** Set awaiting-review state (artifact exists but continueWithDefault=true, no pause). Mutually exclusive with pending. */
1293
+ /** Set awaiting-review state (artifact exists and requires explicit user review). Mutually exclusive with pending. */
1294
1294
  export function setTaskEscalationAwaitingReview(milestoneId, sliceId, taskId, artifactPath) {
1295
1295
  if (!currentDb)
1296
1296
  throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
@@ -2578,7 +2578,10 @@ export function decayMemoriesBefore(cutoffTs, now) {
2578
2578
  throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
2579
2579
  currentDb.prepare(`UPDATE memories
2580
2580
  SET confidence = MAX(0.1, confidence - 0.1), updated_at = :now
2581
- WHERE superseded_by IS NULL AND updated_at < :cutoff AND confidence > 0.1`).run({ ":now": now, ":cutoff": cutoffTs });
2581
+ WHERE superseded_by IS NULL
2582
+ AND updated_at < :cutoff
2583
+ AND confidence > 0.1
2584
+ AND (structured_fields IS NULL OR structured_fields NOT LIKE '%"sourceDecisionId"%')`).run({ ":now": now, ":cutoff": cutoffTs });
2582
2585
  }
2583
2586
  export function supersedeLowestRankedMemories(limit, now) {
2584
2587
  if (!currentDb)
@@ -115,15 +115,10 @@ async function runQuickTaskChoice(ctx, pi) {
115
115
  }
116
116
  async function dispatchDiscussForNextMilestoneWithBacklog(ctx, pi, basePath, nextId) {
117
117
  const backlogContext = buildRequirementsBacklogDiscussContext(nextId);
118
- const discussMilestoneTemplates = inlineTemplate("context", "Context");
119
118
  const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
120
- const basePrompt = loadPrompt("guided-discuss-milestone", {
121
- workingDirectory: basePath,
122
- milestoneId: nextId,
123
- milestoneTitle: `New milestone ${nextId}`,
124
- inlinedTemplates: discussMilestoneTemplates,
125
- structuredQuestionsAvailable,
119
+ const basePrompt = await buildDiscussMilestonePrompt(nextId, `New milestone ${nextId}`, basePath, structuredQuestionsAvailable, {
126
120
  commitInstruction: buildDocsCommitInstruction(`docs(${nextId}): milestone context from discuss`),
121
+ includeContextMode: false,
127
122
  fastPathInstruction: [
128
123
  "> **Requirements backlog active.**",
129
124
  "> Map unmapped active requirements to this milestone before finalizing context.",
@@ -1054,7 +1049,7 @@ function buildHeadlessDiscussPrompt(nextId, seedContext, _basePath) {
1054
1049
  * @param basePath - Root directory of the project
1055
1050
  * @returns The discuss prompt string
1056
1051
  */
1057
- async function buildDiscussPreparationContext(ctx, basePath, mode = "greenfield") {
1052
+ async function buildDiscussPreparationContext(ctx, basePath, mode = "greenfield", skipPriorContext = false) {
1058
1053
  const prefs = loadEffectiveGSDPreferences()?.preferences ?? {};
1059
1054
  if (prefs.discuss_preparation === false)
1060
1055
  return "";
@@ -1071,7 +1066,7 @@ async function buildDiscussPreparationContext(ctx, basePath, mode = "greenfield"
1071
1066
  const parts = [];
1072
1067
  if (codebaseBrief)
1073
1068
  parts.push(`### Codebase Brief\n\n${codebaseBrief}`);
1074
- if (priorContextBrief)
1069
+ if (priorContextBrief && !skipPriorContext)
1075
1070
  parts.push(`### Prior Context Brief\n\n${priorContextBrief}`);
1076
1071
  if (parts.length === 0)
1077
1072
  return "";
@@ -1103,17 +1098,11 @@ async function dispatchNewMilestoneDiscuss(ctx, pi, basePath, nextId, stepMode,
1103
1098
  await dispatchWorkflow(pi, prompt, "gsd-run", ctx, "discuss-milestone", { basePath });
1104
1099
  return;
1105
1100
  }
1106
- const preparationContext = await buildDiscussPreparationContext(ctx, basePath, "milestone");
1107
- const discussMilestoneTemplates = inlineTemplate("context", "Context");
1101
+ const preparationContext = await buildDiscussPreparationContext(ctx, basePath, "milestone", true);
1108
1102
  const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
1109
- let prompt = loadPrompt("guided-discuss-milestone", {
1110
- workingDirectory: basePath,
1111
- milestoneId: nextId,
1112
- milestoneTitle: `New milestone ${nextId}`,
1113
- inlinedTemplates: discussMilestoneTemplates,
1114
- structuredQuestionsAvailable,
1103
+ let prompt = await buildDiscussMilestonePrompt(nextId, `New milestone ${nextId}`, basePath, structuredQuestionsAvailable, {
1115
1104
  commitInstruction: buildDocsCommitInstruction(`docs(${nextId}): milestone context from discuss`),
1116
- fastPathInstruction: "",
1105
+ includeContextMode: false,
1117
1106
  });
1118
1107
  if (preparationContext)
1119
1108
  prompt += preparationContext;
@@ -1364,8 +1353,6 @@ export async function showDiscuss(ctx, pi, basePath, options) {
1364
1353
  // Special case: milestone is in needs-discussion phase (has CONTEXT-DRAFT.md but no roadmap yet).
1365
1354
  // Route to the draft discussion flow instead of erroring — the discussion IS how the roadmap gets created.
1366
1355
  if (state.phase === "needs-discussion") {
1367
- const draftFile = resolveMilestoneFile(basePath, mid, "CONTEXT-DRAFT");
1368
- const draftContent = draftFile ? await loadFile(draftFile) : null;
1369
1356
  const choice = await showNextAction(ctx, {
1370
1357
  title: `GSD — ${mid}: ${milestoneTitle}`,
1371
1358
  summary: ["This milestone has a draft context from a prior discussion.", "It needs a dedicated discussion before auto-planning can begin."],
@@ -1390,30 +1377,23 @@ export async function showDiscuss(ctx, pi, basePath, options) {
1390
1377
  notYetMessage: "Run /gsd discuss when ready to discuss this milestone.",
1391
1378
  });
1392
1379
  if (choice === "discuss_draft") {
1393
- const discussMilestoneTemplates = inlineTemplate("context", "Context");
1394
1380
  const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
1395
- const basePrompt = loadPrompt("guided-discuss-milestone", {
1396
- workingDirectory: basePath,
1397
- milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
1381
+ const seed = await buildDiscussMilestonePrompt(mid, milestoneTitle, basePath, structuredQuestionsAvailable, {
1398
1382
  commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
1399
- fastPathInstruction: "",
1383
+ includeContextMode: false,
1400
1384
  });
1401
- const seed = draftContent
1402
- ? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
1403
- : basePrompt;
1404
1385
  setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: mid, step: true });
1405
1386
  await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone", { basePath });
1406
1387
  }
1407
1388
  else if (choice === "discuss_fresh") {
1408
- const discussMilestoneTemplates = inlineTemplate("context", "Context");
1409
1389
  const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
1410
- setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: mid, step: true });
1411
- await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
1412
- workingDirectory: basePath,
1413
- milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
1390
+ const prompt = await buildDiscussMilestonePrompt(mid, milestoneTitle, basePath, structuredQuestionsAvailable, {
1414
1391
  commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
1415
- fastPathInstruction: "",
1416
- }), "gsd-discuss", ctx, "discuss-milestone", { basePath });
1392
+ includeContextMode: false,
1393
+ includeDraftSeed: false,
1394
+ });
1395
+ setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: mid, step: true });
1396
+ await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-milestone", { basePath });
1417
1397
  }
1418
1398
  else if (choice === "skip_milestone") {
1419
1399
  const { ensureDbOpen } = await import("./bootstrap/dynamic-tools.js");
@@ -1631,20 +1611,12 @@ async function dispatchDiscussForMilestone(ctx, pi, basePath, mid, milestoneTitl
1631
1611
  "> Ask only questions where the answer would materially change scope.",
1632
1612
  ].join("\n")
1633
1613
  : "";
1634
- const discussMilestoneTemplates = inlineTemplate("context", "Context");
1635
1614
  const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
1636
- const basePrompt = loadPrompt("guided-discuss-milestone", {
1637
- workingDirectory: basePath,
1638
- milestoneId: mid,
1639
- milestoneTitle,
1640
- inlinedTemplates: discussMilestoneTemplates,
1641
- structuredQuestionsAvailable,
1615
+ const prompt = await buildDiscussMilestonePrompt(mid, milestoneTitle, basePath, structuredQuestionsAvailable, {
1642
1616
  commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
1617
+ includeContextMode: false,
1643
1618
  fastPathInstruction,
1644
1619
  });
1645
- const prompt = draftContent
1646
- ? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
1647
- : basePrompt;
1648
1620
  await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-milestone", { basePath });
1649
1621
  }
1650
1622
  // ─── Smart Entry Point ────────────────────────────────────────────────────────
@@ -2122,8 +2094,6 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
2122
2094
  }
2123
2095
  // ── Draft milestone — needs discussion before planning ────────────────
2124
2096
  if (state.phase === "needs-discussion") {
2125
- const draftFile = resolveMilestoneFile(basePath, milestoneId, "CONTEXT-DRAFT");
2126
- const draftContent = draftFile ? await loadFile(draftFile) : null;
2127
2097
  const choice = await showNextAction(ctx, {
2128
2098
  title: `GSD — ${milestoneId}: ${milestoneTitle}`,
2129
2099
  summary: ["This milestone has a draft context from a prior discussion.", "It needs a dedicated discussion before auto-planning can begin."],
@@ -2148,30 +2118,23 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
2148
2118
  notYetMessage: "Run /gsd when ready to discuss this milestone.",
2149
2119
  });
2150
2120
  if (choice === "discuss_draft") {
2151
- const discussMilestoneTemplates = inlineTemplate("context", "Context");
2152
2121
  const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
2153
- const basePrompt = loadPrompt("guided-discuss-milestone", {
2154
- workingDirectory: basePath,
2155
- milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
2122
+ const seed = await buildDiscussMilestonePrompt(milestoneId, milestoneTitle, basePath, structuredQuestionsAvailable, {
2156
2123
  commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
2157
- fastPathInstruction: "",
2124
+ includeContextMode: false,
2158
2125
  });
2159
- const seed = draftContent
2160
- ? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
2161
- : basePrompt;
2162
2126
  setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
2163
2127
  await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone", { basePath });
2164
2128
  }
2165
2129
  else if (choice === "discuss_fresh") {
2166
- const discussMilestoneTemplates = inlineTemplate("context", "Context");
2167
2130
  const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
2168
- setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
2169
- await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
2170
- workingDirectory: basePath,
2171
- milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
2131
+ const prompt = await buildDiscussMilestonePrompt(milestoneId, milestoneTitle, basePath, structuredQuestionsAvailable, {
2172
2132
  commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
2173
- fastPathInstruction: "",
2174
- }), "gsd-discuss", ctx, "discuss-milestone", { basePath });
2133
+ includeContextMode: false,
2134
+ includeDraftSeed: false,
2135
+ });
2136
+ setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
2137
+ await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-milestone", { basePath });
2175
2138
  }
2176
2139
  else if (choice === "skip_milestone") {
2177
2140
  const milestoneIds = findMilestoneIds(basePath);
@@ -2279,14 +2242,12 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
2279
2242
  await dispatchWorkflow(pi, await buildPlanMilestonePrompt(milestoneId, milestoneTitle, basePath), "gsd-run", ctx, "plan-milestone", { basePath });
2280
2243
  }
2281
2244
  else if (choice === "discuss") {
2282
- const discussMilestoneTemplates = inlineTemplate("context", "Context");
2283
2245
  const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
2284
- await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
2285
- workingDirectory: basePath,
2286
- milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
2246
+ const prompt = await buildDiscussMilestonePrompt(milestoneId, milestoneTitle, basePath, structuredQuestionsAvailable, {
2287
2247
  commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
2288
- fastPathInstruction: "",
2289
- }), "gsd-run", ctx, "discuss-milestone", { basePath });
2248
+ includeContextMode: false,
2249
+ });
2250
+ await dispatchWorkflow(pi, prompt, "gsd-run", ctx, "discuss-milestone", { basePath });
2290
2251
  }
2291
2252
  else if (choice === "skip_milestone") {
2292
2253
  const milestoneIds = findMilestoneIds(basePath);