gsd-pi 2.67.0-dev.1cd1e0f → 2.67.0-dev.2367d7e

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 (257) hide show
  1. package/README.md +1 -1
  2. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +155 -70
  3. package/dist/resources/extensions/gsd/auto/phases.js +17 -0
  4. package/dist/resources/extensions/gsd/auto/session.js +10 -0
  5. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +12 -0
  6. package/dist/resources/extensions/gsd/auto-dispatch.js +1 -1
  7. package/dist/resources/extensions/gsd/auto-start.js +16 -30
  8. package/dist/resources/extensions/gsd/auto-worktree.js +62 -15
  9. package/dist/resources/extensions/gsd/auto.js +121 -59
  10. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +11 -435
  11. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +1 -4
  12. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +7 -64
  13. package/dist/resources/extensions/gsd/bootstrap/system-context.js +7 -2
  14. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +88 -8
  15. package/dist/resources/extensions/gsd/commands/catalog.js +2 -1
  16. package/dist/resources/extensions/gsd/commands/handlers/core.js +39 -25
  17. package/dist/resources/extensions/gsd/commands/index.js +8 -1
  18. package/dist/resources/extensions/gsd/commands-mcp-status.js +43 -7
  19. package/dist/resources/extensions/gsd/doctor-git-checks.js +4 -4
  20. package/dist/resources/extensions/gsd/doctor-proactive.js +3 -3
  21. package/dist/resources/extensions/gsd/doctor.js +8 -4
  22. package/dist/resources/extensions/gsd/gsd-db.js +11 -0
  23. package/dist/resources/extensions/gsd/guided-flow.js +56 -31
  24. package/dist/resources/extensions/gsd/init-wizard.js +37 -0
  25. package/dist/resources/extensions/gsd/interrupted-session.js +146 -0
  26. package/dist/resources/extensions/gsd/mcp-project-config.js +83 -0
  27. package/dist/resources/extensions/gsd/state.js +7 -2
  28. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +508 -0
  29. package/dist/resources/extensions/gsd/workflow-logger.js +18 -3
  30. package/dist/resources/extensions/gsd/workflow-mcp.js +261 -0
  31. package/dist/web/standalone/.next/BUILD_ID +1 -1
  32. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  33. package/dist/web/standalone/.next/build-manifest.json +3 -3
  34. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  35. package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
  36. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  37. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  38. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  46. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  47. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/index.html +1 -1
  55. package/dist/web/standalone/.next/server/app/index.rsc +2 -2
  56. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  57. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
  58. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  62. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  63. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  64. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  65. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  66. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  67. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  68. package/dist/web/standalone/.next/static/chunks/2826.821e01b07d92e948.js +9 -0
  69. package/dist/web/standalone/.next/static/chunks/app/{page-0c485498795110d6.js → page-f1e30ab6bb269149.js} +1 -1
  70. package/dist/web/standalone/.next/static/chunks/{webpack-b49b09f97429b5d0.js → webpack-6e4d7e9a4f57bed4.js} +1 -1
  71. package/package.json +4 -2
  72. package/packages/mcp-server/README.md +38 -0
  73. package/packages/mcp-server/dist/cli.d.ts +9 -0
  74. package/packages/mcp-server/dist/cli.d.ts.map +1 -0
  75. package/packages/mcp-server/dist/cli.js +58 -0
  76. package/packages/mcp-server/dist/cli.js.map +1 -0
  77. package/packages/mcp-server/dist/index.d.ts +20 -0
  78. package/packages/mcp-server/dist/index.d.ts.map +1 -0
  79. package/packages/mcp-server/dist/index.js +14 -0
  80. package/packages/mcp-server/dist/index.js.map +1 -0
  81. package/packages/mcp-server/dist/readers/captures.d.ts +25 -0
  82. package/packages/mcp-server/dist/readers/captures.d.ts.map +1 -0
  83. package/packages/mcp-server/dist/readers/captures.js +67 -0
  84. package/packages/mcp-server/dist/readers/captures.js.map +1 -0
  85. package/packages/mcp-server/dist/readers/doctor-lite.d.ts +20 -0
  86. package/packages/mcp-server/dist/readers/doctor-lite.d.ts.map +1 -0
  87. package/packages/mcp-server/dist/readers/doctor-lite.js +173 -0
  88. package/packages/mcp-server/dist/readers/doctor-lite.js.map +1 -0
  89. package/packages/mcp-server/dist/readers/index.d.ts +14 -0
  90. package/packages/mcp-server/dist/readers/index.d.ts.map +1 -0
  91. package/packages/mcp-server/dist/readers/index.js +10 -0
  92. package/packages/mcp-server/dist/readers/index.js.map +1 -0
  93. package/packages/mcp-server/dist/readers/knowledge.d.ts +18 -0
  94. package/packages/mcp-server/dist/readers/knowledge.d.ts.map +1 -0
  95. package/packages/mcp-server/dist/readers/knowledge.js +82 -0
  96. package/packages/mcp-server/dist/readers/knowledge.js.map +1 -0
  97. package/packages/mcp-server/dist/readers/metrics.d.ts +32 -0
  98. package/packages/mcp-server/dist/readers/metrics.d.ts.map +1 -0
  99. package/packages/mcp-server/dist/readers/metrics.js +74 -0
  100. package/packages/mcp-server/dist/readers/metrics.js.map +1 -0
  101. package/packages/mcp-server/dist/readers/paths.d.ts +42 -0
  102. package/packages/mcp-server/dist/readers/paths.d.ts.map +1 -0
  103. package/packages/mcp-server/dist/readers/paths.js +199 -0
  104. package/packages/mcp-server/dist/readers/paths.js.map +1 -0
  105. package/packages/mcp-server/dist/readers/roadmap.d.ts +26 -0
  106. package/packages/mcp-server/dist/readers/roadmap.d.ts.map +1 -0
  107. package/packages/mcp-server/dist/readers/roadmap.js +194 -0
  108. package/packages/mcp-server/dist/readers/roadmap.js.map +1 -0
  109. package/packages/mcp-server/dist/readers/state.d.ts +43 -0
  110. package/packages/mcp-server/dist/readers/state.d.ts.map +1 -0
  111. package/packages/mcp-server/dist/readers/state.js +184 -0
  112. package/packages/mcp-server/dist/readers/state.js.map +1 -0
  113. package/packages/mcp-server/dist/server.d.ts +28 -0
  114. package/packages/mcp-server/dist/server.d.ts.map +1 -0
  115. package/packages/mcp-server/dist/server.js +319 -0
  116. package/packages/mcp-server/dist/server.js.map +1 -0
  117. package/packages/mcp-server/dist/session-manager.d.ts +54 -0
  118. package/packages/mcp-server/dist/session-manager.d.ts.map +1 -0
  119. package/packages/mcp-server/dist/session-manager.js +284 -0
  120. package/packages/mcp-server/dist/session-manager.js.map +1 -0
  121. package/packages/mcp-server/dist/types.d.ts +61 -0
  122. package/packages/mcp-server/dist/types.d.ts.map +1 -0
  123. package/packages/mcp-server/dist/types.js +11 -0
  124. package/packages/mcp-server/dist/types.js.map +1 -0
  125. package/packages/mcp-server/dist/workflow-tools.d.ts +9 -0
  126. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -0
  127. package/packages/mcp-server/dist/workflow-tools.js +532 -0
  128. package/packages/mcp-server/dist/workflow-tools.js.map +1 -0
  129. package/packages/mcp-server/src/server.ts +6 -2
  130. package/packages/mcp-server/src/workflow-tools.test.ts +976 -0
  131. package/packages/mcp-server/src/workflow-tools.ts +997 -0
  132. package/packages/mcp-server/tsconfig.json +1 -1
  133. package/packages/pi-agent-core/dist/agent-loop.js +14 -6
  134. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  135. package/packages/pi-agent-core/src/agent-loop.test.ts +53 -0
  136. package/packages/pi-agent-core/src/agent-loop.ts +20 -6
  137. package/packages/pi-coding-agent/dist/core/contextual-tips.d.ts +43 -0
  138. package/packages/pi-coding-agent/dist/core/contextual-tips.d.ts.map +1 -0
  139. package/packages/pi-coding-agent/dist/core/contextual-tips.js +208 -0
  140. package/packages/pi-coding-agent/dist/core/contextual-tips.js.map +1 -0
  141. package/packages/pi-coding-agent/dist/core/contextual-tips.test.d.ts +2 -0
  142. package/packages/pi-coding-agent/dist/core/contextual-tips.test.d.ts.map +1 -0
  143. package/packages/pi-coding-agent/dist/core/contextual-tips.test.js +227 -0
  144. package/packages/pi-coding-agent/dist/core/contextual-tips.test.js.map +1 -0
  145. package/packages/pi-coding-agent/dist/core/index.d.ts +1 -0
  146. package/packages/pi-coding-agent/dist/core/index.d.ts.map +1 -1
  147. package/packages/pi-coding-agent/dist/core/index.js +1 -0
  148. package/packages/pi-coding-agent/dist/core/index.js.map +1 -1
  149. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.d.ts +2 -0
  150. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.d.ts.map +1 -0
  151. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +28 -0
  152. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -0
  153. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  154. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  155. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +17 -12
  156. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  157. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  158. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +19 -0
  159. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  160. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts +4 -0
  161. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  162. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +14 -0
  163. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  164. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
  165. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  166. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +15 -12
  167. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  168. package/packages/pi-coding-agent/src/core/contextual-tips.test.ts +259 -0
  169. package/packages/pi-coding-agent/src/core/contextual-tips.ts +232 -0
  170. package/packages/pi-coding-agent/src/core/index.ts +2 -0
  171. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +54 -0
  172. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +18 -12
  173. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +21 -0
  174. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +19 -0
  175. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +19 -15
  176. package/packages/rpc-client/dist/index.d.ts +10 -0
  177. package/packages/rpc-client/dist/index.d.ts.map +1 -0
  178. package/packages/rpc-client/dist/index.js +9 -0
  179. package/packages/rpc-client/dist/index.js.map +1 -0
  180. package/packages/rpc-client/dist/jsonl.d.ts +17 -0
  181. package/packages/rpc-client/dist/jsonl.d.ts.map +1 -0
  182. package/packages/rpc-client/dist/jsonl.js +54 -0
  183. package/packages/rpc-client/dist/jsonl.js.map +1 -0
  184. package/packages/rpc-client/dist/rpc-client.d.ts +259 -0
  185. package/packages/rpc-client/dist/rpc-client.d.ts.map +1 -0
  186. package/packages/rpc-client/dist/rpc-client.js +541 -0
  187. package/packages/rpc-client/dist/rpc-client.js.map +1 -0
  188. package/packages/rpc-client/dist/rpc-client.test.d.ts +2 -0
  189. package/packages/rpc-client/dist/rpc-client.test.d.ts.map +1 -0
  190. package/packages/rpc-client/dist/rpc-client.test.js +477 -0
  191. package/packages/rpc-client/dist/rpc-client.test.js.map +1 -0
  192. package/packages/rpc-client/dist/rpc-types.d.ts +566 -0
  193. package/packages/rpc-client/dist/rpc-types.d.ts.map +1 -0
  194. package/packages/rpc-client/dist/rpc-types.js +12 -0
  195. package/packages/rpc-client/dist/rpc-types.js.map +1 -0
  196. package/scripts/ensure-workspace-builds.cjs +2 -0
  197. package/scripts/link-workspace-packages.cjs +21 -14
  198. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +193 -93
  199. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +173 -79
  200. package/src/resources/extensions/gsd/auto/phases.ts +25 -0
  201. package/src/resources/extensions/gsd/auto/session.ts +10 -0
  202. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +20 -0
  203. package/src/resources/extensions/gsd/auto-dispatch.ts +1 -1
  204. package/src/resources/extensions/gsd/auto-start.ts +23 -55
  205. package/src/resources/extensions/gsd/auto-worktree.ts +59 -15
  206. package/src/resources/extensions/gsd/auto.ts +133 -64
  207. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +22 -435
  208. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +1 -5
  209. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +7 -72
  210. package/src/resources/extensions/gsd/bootstrap/system-context.ts +8 -2
  211. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +122 -6
  212. package/src/resources/extensions/gsd/commands/catalog.ts +2 -1
  213. package/src/resources/extensions/gsd/commands/handlers/core.ts +53 -26
  214. package/src/resources/extensions/gsd/commands/index.ts +7 -1
  215. package/src/resources/extensions/gsd/commands-mcp-status.ts +53 -7
  216. package/src/resources/extensions/gsd/doctor-git-checks.ts +4 -4
  217. package/src/resources/extensions/gsd/doctor-proactive.ts +3 -3
  218. package/src/resources/extensions/gsd/doctor.ts +9 -5
  219. package/src/resources/extensions/gsd/gsd-db.ts +12 -0
  220. package/src/resources/extensions/gsd/guided-flow.ts +66 -36
  221. package/src/resources/extensions/gsd/init-wizard.ts +40 -0
  222. package/src/resources/extensions/gsd/interrupted-session.ts +224 -0
  223. package/src/resources/extensions/gsd/mcp-project-config.ts +128 -0
  224. package/src/resources/extensions/gsd/state.ts +7 -1
  225. package/src/resources/extensions/gsd/tests/auto-project-root-env.test.ts +29 -0
  226. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +668 -2
  227. package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +14 -4
  228. package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +21 -0
  229. package/src/resources/extensions/gsd/tests/core-overlay-fallback.test.ts +101 -0
  230. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +380 -2
  231. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +66 -0
  232. package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +30 -0
  233. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +12 -0
  234. package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +2 -2
  235. package/src/resources/extensions/gsd/tests/integration/doctor-fixlevel.test.ts +52 -1
  236. package/src/resources/extensions/gsd/tests/integration/doctor-git.test.ts +2 -9
  237. package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +0 -33
  238. package/src/resources/extensions/gsd/tests/integration/merge-cwd-restore.test.ts +169 -0
  239. package/src/resources/extensions/gsd/tests/interrupted-session-auto.test.ts +146 -0
  240. package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +136 -0
  241. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +85 -0
  242. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +15 -0
  243. package/src/resources/extensions/gsd/tests/verification-operational-gate.test.ts +11 -0
  244. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +16 -0
  245. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +500 -0
  246. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +625 -0
  247. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +629 -0
  248. package/src/resources/extensions/gsd/workflow-logger.ts +19 -3
  249. package/src/resources/extensions/gsd/workflow-mcp.ts +320 -0
  250. package/dist/web/standalone/.next/static/chunks/6502.b804e48b7919f55e.js +0 -9
  251. package/packages/pi-coding-agent/dist/modes/interactive/provider-auth-setup.d.ts +0 -13
  252. package/packages/pi-coding-agent/dist/modes/interactive/provider-auth-setup.d.ts.map +0 -1
  253. package/packages/pi-coding-agent/dist/modes/interactive/provider-auth-setup.js +0 -27
  254. package/packages/pi-coding-agent/dist/modes/interactive/provider-auth-setup.js.map +0 -1
  255. package/packages/pi-coding-agent/src/modes/interactive/provider-auth-setup.ts +0 -40
  256. /package/dist/web/standalone/.next/static/{PHqEommYRR8CRn3i84CGM → WMDT_0C0XDkBKtsAI_AX4}/_buildManifest.js +0 -0
  257. /package/dist/web/standalone/.next/static/{PHqEommYRR8CRn3i84CGM → WMDT_0C0XDkBKtsAI_AX4}/_ssgManifest.js +0 -0
@@ -0,0 +1,629 @@
1
+ import { ensureDbOpen } from "../bootstrap/dynamic-tools.js";
2
+ import { sanitizeCompleteMilestoneParams } from "../bootstrap/sanitize-complete-milestone.js";
3
+ import { loadWriteGateSnapshot, shouldBlockContextArtifactSaveInSnapshot } from "../bootstrap/write-gate.js";
4
+ import {
5
+ getMilestone,
6
+ getSliceStatusSummary,
7
+ getSliceTaskCounts,
8
+ _getAdapter,
9
+ saveGateResult,
10
+ } from "../gsd-db.js";
11
+ import { saveArtifactToDb } from "../db-writer.js";
12
+ import type { CompleteMilestoneParams } from "./complete-milestone.js";
13
+ import { handleCompleteMilestone } from "./complete-milestone.js";
14
+ import { handleCompleteTask } from "./complete-task.js";
15
+ import type { CompleteSliceParams } from "../types.js";
16
+ import { handleCompleteSlice } from "./complete-slice.js";
17
+ import type { PlanMilestoneParams } from "./plan-milestone.js";
18
+ import { handlePlanMilestone } from "./plan-milestone.js";
19
+ import type { PlanSliceParams } from "./plan-slice.js";
20
+ import { handlePlanSlice } from "./plan-slice.js";
21
+ import type { ReplanSliceParams } from "./replan-slice.js";
22
+ import { handleReplanSlice } from "./replan-slice.js";
23
+ import type { ReassessRoadmapParams } from "./reassess-roadmap.js";
24
+ import { handleReassessRoadmap } from "./reassess-roadmap.js";
25
+ import type { ValidateMilestoneParams } from "./validate-milestone.js";
26
+ import { handleValidateMilestone } from "./validate-milestone.js";
27
+ import { logError, logWarning } from "../workflow-logger.js";
28
+ import { invalidateStateCache } from "../state.js";
29
+
30
+ export const SUPPORTED_SUMMARY_ARTIFACT_TYPES = ["SUMMARY", "RESEARCH", "CONTEXT", "ASSESSMENT", "CONTEXT-DRAFT"] as const;
31
+
32
+ export function isSupportedSummaryArtifactType(
33
+ artifactType: string,
34
+ ): artifactType is (typeof SUPPORTED_SUMMARY_ARTIFACT_TYPES)[number] {
35
+ return (SUPPORTED_SUMMARY_ARTIFACT_TYPES as readonly string[]).includes(artifactType);
36
+ }
37
+
38
+ export interface ToolExecutionResult {
39
+ content: Array<{ type: "text"; text: string }>;
40
+ details: Record<string, unknown>;
41
+ }
42
+
43
+ export interface SummarySaveParams {
44
+ milestone_id: string;
45
+ slice_id?: string;
46
+ task_id?: string;
47
+ artifact_type: string;
48
+ content: string;
49
+ }
50
+
51
+ export async function executeSummarySave(
52
+ params: SummarySaveParams,
53
+ basePath: string = process.cwd(),
54
+ ): Promise<ToolExecutionResult> {
55
+ const dbAvailable = await ensureDbOpen(basePath);
56
+ if (!dbAvailable) {
57
+ return {
58
+ content: [{ type: "text", text: "Error: GSD database is not available. Cannot save artifact." }],
59
+ details: { operation: "save_summary", error: "db_unavailable" },
60
+ };
61
+ }
62
+ if (!isSupportedSummaryArtifactType(params.artifact_type)) {
63
+ return {
64
+ content: [{ type: "text", text: `Error: Invalid artifact_type "${params.artifact_type}". Must be one of: ${SUPPORTED_SUMMARY_ARTIFACT_TYPES.join(", ")}` }],
65
+ details: { operation: "save_summary", error: "invalid_artifact_type" },
66
+ };
67
+ }
68
+ const contextGuard = shouldBlockContextArtifactSaveInSnapshot(
69
+ loadWriteGateSnapshot(basePath),
70
+ params.artifact_type,
71
+ params.milestone_id ?? null,
72
+ params.slice_id ?? null,
73
+ );
74
+ if (contextGuard.block) {
75
+ return {
76
+ content: [{ type: "text", text: `Error saving artifact: ${contextGuard.reason ?? "context write blocked"}` }],
77
+ details: { operation: "save_summary", error: "context_write_blocked" },
78
+ };
79
+ }
80
+ try {
81
+ let relativePath: string;
82
+ if (params.task_id && params.slice_id) {
83
+ relativePath = `milestones/${params.milestone_id}/slices/${params.slice_id}/tasks/${params.task_id}-${params.artifact_type}.md`;
84
+ } else if (params.slice_id) {
85
+ relativePath = `milestones/${params.milestone_id}/slices/${params.slice_id}/${params.slice_id}-${params.artifact_type}.md`;
86
+ } else {
87
+ relativePath = `milestones/${params.milestone_id}/${params.milestone_id}-${params.artifact_type}.md`;
88
+ }
89
+
90
+ await saveArtifactToDb(
91
+ {
92
+ path: relativePath,
93
+ artifact_type: params.artifact_type,
94
+ content: params.content,
95
+ milestone_id: params.milestone_id,
96
+ slice_id: params.slice_id,
97
+ task_id: params.task_id,
98
+ },
99
+ basePath,
100
+ );
101
+ return {
102
+ content: [{ type: "text", text: `Saved ${params.artifact_type} artifact to ${relativePath}` }],
103
+ details: { operation: "save_summary", path: relativePath, artifact_type: params.artifact_type },
104
+ };
105
+ } catch (err) {
106
+ const msg = err instanceof Error ? err.message : String(err);
107
+ logError("tool", `gsd_summary_save tool failed: ${msg}`, { tool: "gsd_summary_save", error: String(err) });
108
+ return {
109
+ content: [{ type: "text", text: `Error saving artifact: ${msg}` }],
110
+ details: { operation: "save_summary", error: msg },
111
+ };
112
+ }
113
+ }
114
+
115
+ type VerificationEvidenceInput =
116
+ | {
117
+ command: string;
118
+ exitCode: number;
119
+ verdict: string;
120
+ durationMs: number;
121
+ }
122
+ | string;
123
+
124
+ export interface TaskCompleteParams {
125
+ taskId: string;
126
+ sliceId: string;
127
+ milestoneId: string;
128
+ oneLiner: string;
129
+ narrative: string;
130
+ verification: string;
131
+ deviations?: string;
132
+ knownIssues?: string;
133
+ keyFiles?: string[];
134
+ keyDecisions?: string[];
135
+ blockerDiscovered?: boolean;
136
+ verificationEvidence?: VerificationEvidenceInput[];
137
+ }
138
+
139
+ export type CompleteMilestoneExecutorParams = Partial<CompleteMilestoneParams> & Record<string, unknown>;
140
+ export type SliceCompleteExecutorParams = CompleteSliceParams;
141
+ export type PlanMilestoneExecutorParams = PlanMilestoneParams;
142
+ export type PlanSliceExecutorParams = PlanSliceParams;
143
+ export type ReplanSliceExecutorParams = ReplanSliceParams;
144
+ export type ValidateMilestoneExecutorParams = ValidateMilestoneParams;
145
+ export type ReassessRoadmapExecutorParams = ReassessRoadmapParams;
146
+
147
+ export interface SaveGateResultParams {
148
+ milestoneId: string;
149
+ sliceId: string;
150
+ gateId: string;
151
+ taskId?: string;
152
+ verdict: "pass" | "flag" | "omitted";
153
+ rationale: string;
154
+ findings?: string;
155
+ }
156
+
157
+ export async function executeTaskComplete(
158
+ params: TaskCompleteParams,
159
+ basePath: string = process.cwd(),
160
+ ): Promise<ToolExecutionResult> {
161
+ const dbAvailable = await ensureDbOpen(basePath);
162
+ if (!dbAvailable) {
163
+ return {
164
+ content: [{ type: "text", text: "Error: GSD database is not available. Cannot complete task." }],
165
+ details: { operation: "complete_task", error: "db_unavailable" },
166
+ };
167
+ }
168
+ try {
169
+ const coerced = { ...params };
170
+ coerced.verificationEvidence = (params.verificationEvidence ?? []).map((v) =>
171
+ typeof v === "string" ? { command: v, exitCode: -1, verdict: "unknown (coerced from string)", durationMs: 0 } : v,
172
+ );
173
+
174
+ const result = await handleCompleteTask(coerced as any, basePath);
175
+ if ("error" in result) {
176
+ return {
177
+ content: [{ type: "text", text: `Error completing task: ${result.error}` }],
178
+ details: { operation: "complete_task", error: result.error },
179
+ };
180
+ }
181
+ return {
182
+ content: [{ type: "text", text: `Completed task ${result.taskId} (${result.sliceId}/${result.milestoneId})` }],
183
+ details: {
184
+ operation: "complete_task",
185
+ taskId: result.taskId,
186
+ sliceId: result.sliceId,
187
+ milestoneId: result.milestoneId,
188
+ summaryPath: result.summaryPath,
189
+ },
190
+ };
191
+ } catch (err) {
192
+ const msg = err instanceof Error ? err.message : String(err);
193
+ logError("tool", `complete_task tool failed: ${msg}`, { tool: "gsd_task_complete", error: String(err) });
194
+ return {
195
+ content: [{ type: "text", text: `Error completing task: ${msg}` }],
196
+ details: { operation: "complete_task", error: msg },
197
+ };
198
+ }
199
+ }
200
+
201
+ export async function executeSliceComplete(
202
+ params: SliceCompleteExecutorParams,
203
+ basePath: string = process.cwd(),
204
+ ): Promise<ToolExecutionResult> {
205
+ const dbAvailable = await ensureDbOpen(basePath);
206
+ if (!dbAvailable) {
207
+ return {
208
+ content: [{ type: "text", text: "Error: GSD database is not available. Cannot complete slice." }],
209
+ details: { operation: "complete_slice", error: "db_unavailable" },
210
+ };
211
+ }
212
+ try {
213
+ const splitPair = (s: string): [string, string] => {
214
+ const m = s.match(/^(.+?)\s*(?:—|-)\s+(.+)$/);
215
+ return m ? [m[1].trim(), m[2].trim()] : [s.trim(), ""];
216
+ };
217
+ const wrapArray = (v: unknown): unknown[] =>
218
+ v == null ? [] : Array.isArray(v) ? v : [v];
219
+
220
+ const coerced = { ...params } as CompleteSliceParams & Record<string, unknown>;
221
+ coerced.provides = wrapArray(params.provides) as string[];
222
+ coerced.keyFiles = wrapArray(params.keyFiles) as string[];
223
+ coerced.keyDecisions = wrapArray(params.keyDecisions) as string[];
224
+ coerced.patternsEstablished = wrapArray(params.patternsEstablished) as string[];
225
+ coerced.observabilitySurfaces = wrapArray(params.observabilitySurfaces) as string[];
226
+ coerced.requirementsSurfaced = wrapArray(params.requirementsSurfaced) as string[];
227
+ coerced.drillDownPaths = wrapArray(params.drillDownPaths) as string[];
228
+ coerced.affects = wrapArray(params.affects) as string[];
229
+ coerced.filesModified = wrapArray(params.filesModified).map((f) => {
230
+ if (typeof f !== "string") return f;
231
+ const [path, description] = splitPair(f);
232
+ return { path, description };
233
+ }) as Array<{ path: string; description: string }>;
234
+ coerced.requires = wrapArray(params.requires).map((r) => {
235
+ if (typeof r !== "string") return r;
236
+ const [slice, provides] = splitPair(r);
237
+ return { slice, provides };
238
+ }) as Array<{ slice: string; provides: string }>;
239
+ coerced.requirementsAdvanced = wrapArray(params.requirementsAdvanced).map((r) => {
240
+ if (typeof r !== "string") return r;
241
+ const [id, how] = splitPair(r);
242
+ return { id, how };
243
+ }) as Array<{ id: string; how: string }>;
244
+ coerced.requirementsValidated = wrapArray(params.requirementsValidated).map((r) => {
245
+ if (typeof r !== "string") return r;
246
+ const [id, proof] = splitPair(r);
247
+ return { id, proof };
248
+ }) as Array<{ id: string; proof: string }>;
249
+ coerced.requirementsInvalidated = wrapArray(params.requirementsInvalidated).map((r) => {
250
+ if (typeof r !== "string") return r;
251
+ const [id, what] = splitPair(r);
252
+ return { id, what };
253
+ }) as Array<{ id: string; what: string }>;
254
+
255
+ const result = await handleCompleteSlice(coerced as CompleteSliceParams, basePath);
256
+ if ("error" in result) {
257
+ return {
258
+ content: [{ type: "text", text: `Error completing slice: ${result.error}` }],
259
+ details: { operation: "complete_slice", error: result.error },
260
+ };
261
+ }
262
+ return {
263
+ content: [{ type: "text", text: `Completed slice ${result.sliceId} (${result.milestoneId})` }],
264
+ details: {
265
+ operation: "complete_slice",
266
+ sliceId: result.sliceId,
267
+ milestoneId: result.milestoneId,
268
+ summaryPath: result.summaryPath,
269
+ uatPath: result.uatPath,
270
+ },
271
+ };
272
+ } catch (err) {
273
+ const msg = err instanceof Error ? err.message : String(err);
274
+ logError("tool", `complete_slice tool failed: ${msg}`, { tool: "gsd_slice_complete", error: String(err) });
275
+ return {
276
+ content: [{ type: "text", text: `Error completing slice: ${msg}` }],
277
+ details: { operation: "complete_slice", error: msg },
278
+ };
279
+ }
280
+ }
281
+
282
+ export async function executeCompleteMilestone(
283
+ params: CompleteMilestoneExecutorParams,
284
+ basePath: string = process.cwd(),
285
+ ): Promise<ToolExecutionResult> {
286
+ const dbAvailable = await ensureDbOpen(basePath);
287
+ if (!dbAvailable) {
288
+ return {
289
+ content: [{ type: "text", text: "Error: GSD database is not available. Cannot complete milestone." }],
290
+ details: { operation: "complete_milestone", error: "db_unavailable" },
291
+ };
292
+ }
293
+ try {
294
+ const sanitized = sanitizeCompleteMilestoneParams(params);
295
+ const result = await handleCompleteMilestone(sanitized, basePath);
296
+ if ("error" in result) {
297
+ return {
298
+ content: [{ type: "text", text: `Error completing milestone: ${result.error}` }],
299
+ details: { operation: "complete_milestone", error: result.error },
300
+ };
301
+ }
302
+ return {
303
+ content: [{ type: "text", text: `Completed milestone ${result.milestoneId}. Summary written to ${result.summaryPath}` }],
304
+ details: {
305
+ operation: "complete_milestone",
306
+ milestoneId: result.milestoneId,
307
+ summaryPath: result.summaryPath,
308
+ },
309
+ };
310
+ } catch (err) {
311
+ const msg = err instanceof Error ? err.message : String(err);
312
+ logError("tool", `complete_milestone tool failed: ${msg}`, { tool: "gsd_complete_milestone", error: String(err) });
313
+ return {
314
+ content: [{ type: "text", text: `Error completing milestone: ${msg}` }],
315
+ details: { operation: "complete_milestone", error: msg },
316
+ };
317
+ }
318
+ }
319
+
320
+ export async function executeValidateMilestone(
321
+ params: ValidateMilestoneExecutorParams,
322
+ basePath: string = process.cwd(),
323
+ ): Promise<ToolExecutionResult> {
324
+ const dbAvailable = await ensureDbOpen(basePath);
325
+ if (!dbAvailable) {
326
+ return {
327
+ content: [{ type: "text", text: "Error: GSD database is not available. Cannot validate milestone." }],
328
+ details: { operation: "validate_milestone", error: "db_unavailable" },
329
+ };
330
+ }
331
+ try {
332
+ const result = await handleValidateMilestone(params, basePath);
333
+ if ("error" in result) {
334
+ return {
335
+ content: [{ type: "text", text: `Error validating milestone: ${result.error}` }],
336
+ details: { operation: "validate_milestone", error: result.error },
337
+ };
338
+ }
339
+ return {
340
+ content: [{ type: "text", text: `Validated milestone ${result.milestoneId} — verdict: ${result.verdict}. Written to ${result.validationPath}` }],
341
+ details: {
342
+ operation: "validate_milestone",
343
+ milestoneId: result.milestoneId,
344
+ verdict: result.verdict,
345
+ validationPath: result.validationPath,
346
+ },
347
+ };
348
+ } catch (err) {
349
+ const msg = err instanceof Error ? err.message : String(err);
350
+ logError("tool", `validate_milestone tool failed: ${msg}`, { tool: "gsd_validate_milestone", error: String(err) });
351
+ return {
352
+ content: [{ type: "text", text: `Error validating milestone: ${msg}` }],
353
+ details: { operation: "validate_milestone", error: msg },
354
+ };
355
+ }
356
+ }
357
+
358
+ export async function executeReassessRoadmap(
359
+ params: ReassessRoadmapExecutorParams,
360
+ basePath: string = process.cwd(),
361
+ ): Promise<ToolExecutionResult> {
362
+ const dbAvailable = await ensureDbOpen(basePath);
363
+ if (!dbAvailable) {
364
+ return {
365
+ content: [{ type: "text", text: "Error: GSD database is not available. Cannot reassess roadmap." }],
366
+ details: { operation: "reassess_roadmap", error: "db_unavailable" },
367
+ };
368
+ }
369
+ try {
370
+ const result = await handleReassessRoadmap(params, basePath);
371
+ if ("error" in result) {
372
+ return {
373
+ content: [{ type: "text", text: `Error reassessing roadmap: ${result.error}` }],
374
+ details: { operation: "reassess_roadmap", error: result.error },
375
+ };
376
+ }
377
+ return {
378
+ content: [{ type: "text", text: `Reassessed roadmap for milestone ${result.milestoneId} after ${result.completedSliceId}` }],
379
+ details: {
380
+ operation: "reassess_roadmap",
381
+ milestoneId: result.milestoneId,
382
+ completedSliceId: result.completedSliceId,
383
+ assessmentPath: result.assessmentPath,
384
+ roadmapPath: result.roadmapPath,
385
+ },
386
+ };
387
+ } catch (err) {
388
+ const msg = err instanceof Error ? err.message : String(err);
389
+ logError("tool", `reassess_roadmap tool failed: ${msg}`, { tool: "gsd_reassess_roadmap", error: String(err) });
390
+ return {
391
+ content: [{ type: "text", text: `Error reassessing roadmap: ${msg}` }],
392
+ details: { operation: "reassess_roadmap", error: msg },
393
+ };
394
+ }
395
+ }
396
+
397
+ export async function executeSaveGateResult(
398
+ params: SaveGateResultParams,
399
+ basePath: string = process.cwd(),
400
+ ): Promise<ToolExecutionResult> {
401
+ const dbAvailable = await ensureDbOpen(basePath);
402
+ if (!dbAvailable) {
403
+ return {
404
+ content: [{ type: "text", text: "Error: GSD database is not available." }],
405
+ details: { operation: "save_gate_result", error: "db_unavailable" },
406
+ };
407
+ }
408
+
409
+ const validGates = ["Q3", "Q4", "Q5", "Q6", "Q7", "Q8"];
410
+ if (!validGates.includes(params.gateId)) {
411
+ return {
412
+ content: [{ type: "text", text: `Error: Invalid gateId "${params.gateId}". Must be one of: ${validGates.join(", ")}` }],
413
+ details: { operation: "save_gate_result", error: "invalid_gate_id" },
414
+ };
415
+ }
416
+
417
+ const validVerdicts = ["pass", "flag", "omitted"];
418
+ if (!validVerdicts.includes(params.verdict)) {
419
+ return {
420
+ content: [{ type: "text", text: `Error: Invalid verdict "${params.verdict}". Must be one of: ${validVerdicts.join(", ")}` }],
421
+ details: { operation: "save_gate_result", error: "invalid_verdict" },
422
+ };
423
+ }
424
+
425
+ try {
426
+ saveGateResult({
427
+ milestoneId: params.milestoneId,
428
+ sliceId: params.sliceId,
429
+ gateId: params.gateId,
430
+ taskId: params.taskId ?? "",
431
+ verdict: params.verdict,
432
+ rationale: params.rationale,
433
+ findings: params.findings ?? "",
434
+ });
435
+ invalidateStateCache();
436
+ return {
437
+ content: [{ type: "text", text: `Gate ${params.gateId} result saved: verdict=${params.verdict}` }],
438
+ details: { operation: "save_gate_result", gateId: params.gateId, verdict: params.verdict },
439
+ };
440
+ } catch (err) {
441
+ const msg = err instanceof Error ? err.message : String(err);
442
+ logError("tool", `gsd_save_gate_result failed: ${msg}`, { tool: "gsd_save_gate_result", error: String(err) });
443
+ return {
444
+ content: [{ type: "text", text: `Error saving gate result: ${msg}` }],
445
+ details: { operation: "save_gate_result", error: msg },
446
+ };
447
+ }
448
+ }
449
+
450
+ export async function executePlanMilestone(
451
+ params: PlanMilestoneExecutorParams,
452
+ basePath: string = process.cwd(),
453
+ ): Promise<ToolExecutionResult> {
454
+ const dbAvailable = await ensureDbOpen(basePath);
455
+ if (!dbAvailable) {
456
+ return {
457
+ content: [{ type: "text", text: "Error: GSD database is not available. Cannot plan milestone." }],
458
+ details: { operation: "plan_milestone", error: "db_unavailable" },
459
+ };
460
+ }
461
+ try {
462
+ const result = await handlePlanMilestone(params, basePath);
463
+ if ("error" in result) {
464
+ return {
465
+ content: [{ type: "text", text: `Error planning milestone: ${result.error}` }],
466
+ details: { operation: "plan_milestone", error: result.error },
467
+ };
468
+ }
469
+ return {
470
+ content: [{ type: "text", text: `Planned milestone ${result.milestoneId}` }],
471
+ details: {
472
+ operation: "plan_milestone",
473
+ milestoneId: result.milestoneId,
474
+ roadmapPath: result.roadmapPath,
475
+ },
476
+ };
477
+ } catch (err) {
478
+ const msg = err instanceof Error ? err.message : String(err);
479
+ logError("tool", `plan_milestone tool failed: ${msg}`, { tool: "gsd_plan_milestone", error: String(err) });
480
+ return {
481
+ content: [{ type: "text", text: `Error planning milestone: ${msg}` }],
482
+ details: { operation: "plan_milestone", error: msg },
483
+ };
484
+ }
485
+ }
486
+
487
+ export async function executePlanSlice(
488
+ params: PlanSliceExecutorParams,
489
+ basePath: string = process.cwd(),
490
+ ): Promise<ToolExecutionResult> {
491
+ const dbAvailable = await ensureDbOpen(basePath);
492
+ if (!dbAvailable) {
493
+ return {
494
+ content: [{ type: "text", text: "Error: GSD database is not available. Cannot plan slice." }],
495
+ details: { operation: "plan_slice", error: "db_unavailable" },
496
+ };
497
+ }
498
+ try {
499
+ const result = await handlePlanSlice(params, basePath);
500
+ if ("error" in result) {
501
+ return {
502
+ content: [{ type: "text", text: `Error planning slice: ${result.error}` }],
503
+ details: { operation: "plan_slice", error: result.error },
504
+ };
505
+ }
506
+ return {
507
+ content: [{ type: "text", text: `Planned slice ${result.sliceId} (${result.milestoneId})` }],
508
+ details: {
509
+ operation: "plan_slice",
510
+ milestoneId: result.milestoneId,
511
+ sliceId: result.sliceId,
512
+ planPath: result.planPath,
513
+ taskPlanPaths: result.taskPlanPaths,
514
+ },
515
+ };
516
+ } catch (err) {
517
+ const msg = err instanceof Error ? err.message : String(err);
518
+ logError("tool", `plan_slice tool failed: ${msg}`, { tool: "gsd_plan_slice", error: String(err) });
519
+ return {
520
+ content: [{ type: "text", text: `Error planning slice: ${msg}` }],
521
+ details: { operation: "plan_slice", error: msg },
522
+ };
523
+ }
524
+ }
525
+
526
+ export async function executeReplanSlice(
527
+ params: ReplanSliceExecutorParams,
528
+ basePath: string = process.cwd(),
529
+ ): Promise<ToolExecutionResult> {
530
+ const dbAvailable = await ensureDbOpen(basePath);
531
+ if (!dbAvailable) {
532
+ return {
533
+ content: [{ type: "text", text: "Error: GSD database is not available. Cannot replan slice." }],
534
+ details: { operation: "replan_slice", error: "db_unavailable" },
535
+ };
536
+ }
537
+ try {
538
+ const result = await handleReplanSlice(params, basePath);
539
+ if ("error" in result) {
540
+ return {
541
+ content: [{ type: "text", text: `Error replanning slice: ${result.error}` }],
542
+ details: { operation: "replan_slice", error: result.error },
543
+ };
544
+ }
545
+ return {
546
+ content: [{ type: "text", text: `Replanned slice ${result.sliceId} (${result.milestoneId})` }],
547
+ details: {
548
+ operation: "replan_slice",
549
+ milestoneId: result.milestoneId,
550
+ sliceId: result.sliceId,
551
+ replanPath: result.replanPath,
552
+ planPath: result.planPath,
553
+ },
554
+ };
555
+ } catch (err) {
556
+ const msg = err instanceof Error ? err.message : String(err);
557
+ logError("tool", `replan_slice tool failed: ${msg}`, { tool: "gsd_replan_slice", error: String(err) });
558
+ return {
559
+ content: [{ type: "text", text: `Error replanning slice: ${msg}` }],
560
+ details: { operation: "replan_slice", error: msg },
561
+ };
562
+ }
563
+ }
564
+
565
+ export interface MilestoneStatusParams {
566
+ milestoneId: string;
567
+ }
568
+
569
+ export async function executeMilestoneStatus(
570
+ params: MilestoneStatusParams,
571
+ basePath: string = process.cwd(),
572
+ ): Promise<ToolExecutionResult> {
573
+ try {
574
+ const dbAvailable = await ensureDbOpen(basePath);
575
+ if (!dbAvailable) {
576
+ return {
577
+ content: [{ type: "text", text: "Error: GSD database is not available." }],
578
+ details: { operation: "milestone_status", error: "db_unavailable" },
579
+ };
580
+ }
581
+
582
+ const adapter = _getAdapter()!;
583
+ adapter.exec("BEGIN");
584
+ try {
585
+ const milestone = getMilestone(params.milestoneId);
586
+ if (!milestone) {
587
+ adapter.exec("COMMIT");
588
+ return {
589
+ content: [{ type: "text", text: `Milestone ${params.milestoneId} not found in database.` }],
590
+ details: { operation: "milestone_status", milestoneId: params.milestoneId, found: false },
591
+ };
592
+ }
593
+
594
+ const sliceStatuses = getSliceStatusSummary(params.milestoneId);
595
+ const slices = sliceStatuses.map((s) => ({
596
+ id: s.id,
597
+ status: s.status,
598
+ taskCounts: getSliceTaskCounts(params.milestoneId, s.id),
599
+ }));
600
+
601
+ adapter.exec("COMMIT");
602
+
603
+ const result = {
604
+ milestoneId: milestone.id,
605
+ title: milestone.title,
606
+ status: milestone.status,
607
+ createdAt: milestone.created_at,
608
+ completedAt: milestone.completed_at,
609
+ sliceCount: slices.length,
610
+ slices,
611
+ };
612
+
613
+ return {
614
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
615
+ details: { operation: "milestone_status", milestoneId: milestone.id, sliceCount: slices.length },
616
+ };
617
+ } catch (txErr) {
618
+ try { adapter.exec("ROLLBACK"); } catch { /* swallow */ }
619
+ throw txErr;
620
+ }
621
+ } catch (err) {
622
+ const msg = err instanceof Error ? err.message : String(err);
623
+ logWarning("tool", `gsd_milestone_status tool failed: ${msg}`);
624
+ return {
625
+ content: [{ type: "text", text: `Error querying milestone status: ${msg}` }],
626
+ details: { operation: "milestone_status", error: msg },
627
+ };
628
+ }
629
+ }
@@ -67,6 +67,7 @@ export interface LogEntry {
67
67
  const MAX_BUFFER = 100;
68
68
  let _buffer: LogEntry[] = [];
69
69
  let _auditBasePath: string | null = null;
70
+ let _stderrEnabled = true;
70
71
 
71
72
  /**
72
73
  * Set the base path for persistent audit log writes.
@@ -77,6 +78,16 @@ export function setLogBasePath(basePath: string): void {
77
78
  _auditBasePath = basePath;
78
79
  }
79
80
 
81
+ /**
82
+ * Enable or disable immediate stderr writes for workflow logs.
83
+ * Returns the previous setting so callers can restore it.
84
+ */
85
+ export function setStderrLoggingEnabled(enabled: boolean): boolean {
86
+ const previous = _stderrEnabled;
87
+ _stderrEnabled = enabled;
88
+ return previous;
89
+ }
90
+
80
91
  // ─── Public API ─────────────────────────────────────────────────────────
81
92
 
82
93
  /**
@@ -245,7 +256,7 @@ function _push(
245
256
  // Always forward to stderr so terminal watchers see it (see module header for policy)
246
257
  const prefix = severity === "error" ? "ERROR" : "WARN";
247
258
  const ctxStr = context ? ` ${JSON.stringify(context)}` : "";
248
- process.stderr.write(`[gsd:${component}] ${prefix}: ${message}${ctxStr}\n`);
259
+ _writeStderr(`[gsd:${component}] ${prefix}: ${message}${ctxStr}\n`);
249
260
 
250
261
  // Persist to notification store (both warnings and errors)
251
262
  try {
@@ -255,7 +266,7 @@ function _push(
255
266
  "workflow-logger",
256
267
  );
257
268
  } catch (notifErr) {
258
- process.stderr.write(`[gsd:workflow-logger] notification-store append failed: ${(notifErr as Error).message}\n`);
269
+ _writeStderr(`[gsd:workflow-logger] notification-store append failed: ${(notifErr as Error).message}\n`);
259
270
  }
260
271
 
261
272
  // Buffer for auto-loop to drain
@@ -275,11 +286,16 @@ function _push(
275
286
  appendFileSync(join(auditDir, "audit-log.jsonl"), JSON.stringify(sanitized) + "\n", "utf-8");
276
287
  } catch (auditErr) {
277
288
  // Best-effort — never let audit write failures bubble up
278
- process.stderr.write(`[gsd:audit] failed to persist log entry: ${(auditErr as Error).message}\n`);
289
+ _writeStderr(`[gsd:audit] failed to persist log entry: ${(auditErr as Error).message}\n`);
279
290
  }
280
291
  }
281
292
  }
282
293
 
294
+ function _writeStderr(message: string): void {
295
+ if (!_stderrEnabled) return;
296
+ process.stderr.write(message);
297
+ }
298
+
283
299
  /**
284
300
  * Sanitize a log entry before persisting to the audit JSONL file.
285
301
  * Strips potentially sensitive context (raw paths, cwd, full error text)