gsd-pi 2.67.0-dev.a5b1d8f → 2.67.0-dev.fe39184

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 (191) hide show
  1. package/README.md +41 -31
  2. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +121 -8
  3. package/dist/resources/extensions/gsd/auto/phases.js +17 -0
  4. package/dist/resources/extensions/gsd/auto/session.js +6 -0
  5. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +12 -0
  6. package/dist/resources/extensions/gsd/auto-start.js +12 -0
  7. package/dist/resources/extensions/gsd/auto.js +27 -0
  8. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +11 -435
  9. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +1 -4
  10. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +7 -64
  11. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +88 -8
  12. package/dist/resources/extensions/gsd/commands/catalog.js +2 -1
  13. package/dist/resources/extensions/gsd/commands/handlers/core.js +39 -25
  14. package/dist/resources/extensions/gsd/commands/index.js +8 -1
  15. package/dist/resources/extensions/gsd/commands-mcp-status.js +43 -7
  16. package/dist/resources/extensions/gsd/guided-flow.js +16 -0
  17. package/dist/resources/extensions/gsd/init-wizard.js +37 -0
  18. package/dist/resources/extensions/gsd/mcp-project-config.js +83 -0
  19. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +508 -0
  20. package/dist/resources/extensions/gsd/workflow-logger.js +18 -3
  21. package/dist/resources/extensions/gsd/workflow-mcp.js +261 -0
  22. package/dist/web/standalone/.next/BUILD_ID +1 -1
  23. package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
  24. package/dist/web/standalone/.next/build-manifest.json +3 -3
  25. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  26. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  27. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  28. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  36. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/index.html +1 -1
  44. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
  51. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  52. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  53. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  54. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  55. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  56. package/dist/web/standalone/.next/static/chunks/6502.5dcdcf1e1432e20d.js +9 -0
  57. package/dist/web/standalone/.next/static/chunks/{webpack-b49b09f97429b5d0.js → webpack-42a66876b763aa26.js} +1 -1
  58. package/package.json +4 -2
  59. package/packages/mcp-server/README.md +38 -0
  60. package/packages/mcp-server/dist/cli.d.ts +9 -0
  61. package/packages/mcp-server/dist/cli.d.ts.map +1 -0
  62. package/packages/mcp-server/dist/cli.js +58 -0
  63. package/packages/mcp-server/dist/cli.js.map +1 -0
  64. package/packages/mcp-server/dist/index.d.ts +20 -0
  65. package/packages/mcp-server/dist/index.d.ts.map +1 -0
  66. package/packages/mcp-server/dist/index.js +14 -0
  67. package/packages/mcp-server/dist/index.js.map +1 -0
  68. package/packages/mcp-server/dist/readers/captures.d.ts +25 -0
  69. package/packages/mcp-server/dist/readers/captures.d.ts.map +1 -0
  70. package/packages/mcp-server/dist/readers/captures.js +67 -0
  71. package/packages/mcp-server/dist/readers/captures.js.map +1 -0
  72. package/packages/mcp-server/dist/readers/doctor-lite.d.ts +20 -0
  73. package/packages/mcp-server/dist/readers/doctor-lite.d.ts.map +1 -0
  74. package/packages/mcp-server/dist/readers/doctor-lite.js +173 -0
  75. package/packages/mcp-server/dist/readers/doctor-lite.js.map +1 -0
  76. package/packages/mcp-server/dist/readers/index.d.ts +14 -0
  77. package/packages/mcp-server/dist/readers/index.d.ts.map +1 -0
  78. package/packages/mcp-server/dist/readers/index.js +10 -0
  79. package/packages/mcp-server/dist/readers/index.js.map +1 -0
  80. package/packages/mcp-server/dist/readers/knowledge.d.ts +18 -0
  81. package/packages/mcp-server/dist/readers/knowledge.d.ts.map +1 -0
  82. package/packages/mcp-server/dist/readers/knowledge.js +82 -0
  83. package/packages/mcp-server/dist/readers/knowledge.js.map +1 -0
  84. package/packages/mcp-server/dist/readers/metrics.d.ts +32 -0
  85. package/packages/mcp-server/dist/readers/metrics.d.ts.map +1 -0
  86. package/packages/mcp-server/dist/readers/metrics.js +74 -0
  87. package/packages/mcp-server/dist/readers/metrics.js.map +1 -0
  88. package/packages/mcp-server/dist/readers/paths.d.ts +42 -0
  89. package/packages/mcp-server/dist/readers/paths.d.ts.map +1 -0
  90. package/packages/mcp-server/dist/readers/paths.js +199 -0
  91. package/packages/mcp-server/dist/readers/paths.js.map +1 -0
  92. package/packages/mcp-server/dist/readers/roadmap.d.ts +26 -0
  93. package/packages/mcp-server/dist/readers/roadmap.d.ts.map +1 -0
  94. package/packages/mcp-server/dist/readers/roadmap.js +194 -0
  95. package/packages/mcp-server/dist/readers/roadmap.js.map +1 -0
  96. package/packages/mcp-server/dist/readers/state.d.ts +43 -0
  97. package/packages/mcp-server/dist/readers/state.d.ts.map +1 -0
  98. package/packages/mcp-server/dist/readers/state.js +184 -0
  99. package/packages/mcp-server/dist/readers/state.js.map +1 -0
  100. package/packages/mcp-server/dist/server.d.ts +28 -0
  101. package/packages/mcp-server/dist/server.d.ts.map +1 -0
  102. package/packages/mcp-server/dist/server.js +319 -0
  103. package/packages/mcp-server/dist/server.js.map +1 -0
  104. package/packages/mcp-server/dist/session-manager.d.ts +54 -0
  105. package/packages/mcp-server/dist/session-manager.d.ts.map +1 -0
  106. package/packages/mcp-server/dist/session-manager.js +284 -0
  107. package/packages/mcp-server/dist/session-manager.js.map +1 -0
  108. package/packages/mcp-server/dist/types.d.ts +61 -0
  109. package/packages/mcp-server/dist/types.d.ts.map +1 -0
  110. package/packages/mcp-server/dist/types.js +11 -0
  111. package/packages/mcp-server/dist/types.js.map +1 -0
  112. package/packages/mcp-server/dist/workflow-tools.d.ts +9 -0
  113. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -0
  114. package/packages/mcp-server/dist/workflow-tools.js +532 -0
  115. package/packages/mcp-server/dist/workflow-tools.js.map +1 -0
  116. package/packages/mcp-server/src/server.ts +6 -2
  117. package/packages/mcp-server/src/workflow-tools.test.ts +976 -0
  118. package/packages/mcp-server/src/workflow-tools.ts +997 -0
  119. package/packages/mcp-server/tsconfig.json +1 -1
  120. package/packages/pi-agent-core/dist/agent-loop.js +14 -6
  121. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  122. package/packages/pi-agent-core/src/agent-loop.test.ts +53 -0
  123. package/packages/pi-agent-core/src/agent-loop.ts +20 -6
  124. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.d.ts +2 -0
  125. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.d.ts.map +1 -0
  126. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +28 -0
  127. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -0
  128. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  129. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  130. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +17 -12
  131. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  132. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  133. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +19 -0
  134. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  135. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +54 -0
  136. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +18 -12
  137. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +21 -0
  138. package/packages/rpc-client/dist/index.d.ts +10 -0
  139. package/packages/rpc-client/dist/index.d.ts.map +1 -0
  140. package/packages/rpc-client/dist/index.js +9 -0
  141. package/packages/rpc-client/dist/index.js.map +1 -0
  142. package/packages/rpc-client/dist/jsonl.d.ts +17 -0
  143. package/packages/rpc-client/dist/jsonl.d.ts.map +1 -0
  144. package/packages/rpc-client/dist/jsonl.js +54 -0
  145. package/packages/rpc-client/dist/jsonl.js.map +1 -0
  146. package/packages/rpc-client/dist/rpc-client.d.ts +259 -0
  147. package/packages/rpc-client/dist/rpc-client.d.ts.map +1 -0
  148. package/packages/rpc-client/dist/rpc-client.js +541 -0
  149. package/packages/rpc-client/dist/rpc-client.js.map +1 -0
  150. package/packages/rpc-client/dist/rpc-client.test.d.ts +2 -0
  151. package/packages/rpc-client/dist/rpc-client.test.d.ts.map +1 -0
  152. package/packages/rpc-client/dist/rpc-client.test.js +477 -0
  153. package/packages/rpc-client/dist/rpc-client.test.js.map +1 -0
  154. package/packages/rpc-client/dist/rpc-types.d.ts +566 -0
  155. package/packages/rpc-client/dist/rpc-types.d.ts.map +1 -0
  156. package/packages/rpc-client/dist/rpc-types.js +12 -0
  157. package/packages/rpc-client/dist/rpc-types.js.map +1 -0
  158. package/scripts/ensure-workspace-builds.cjs +2 -0
  159. package/scripts/link-workspace-packages.cjs +21 -14
  160. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +157 -8
  161. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +182 -0
  162. package/src/resources/extensions/gsd/auto/phases.ts +25 -0
  163. package/src/resources/extensions/gsd/auto/session.ts +6 -0
  164. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +20 -0
  165. package/src/resources/extensions/gsd/auto-start.ts +15 -1
  166. package/src/resources/extensions/gsd/auto.ts +29 -1
  167. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +22 -435
  168. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +1 -5
  169. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +7 -72
  170. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +122 -6
  171. package/src/resources/extensions/gsd/commands/catalog.ts +2 -1
  172. package/src/resources/extensions/gsd/commands/handlers/core.ts +53 -26
  173. package/src/resources/extensions/gsd/commands/index.ts +7 -1
  174. package/src/resources/extensions/gsd/commands-mcp-status.ts +53 -7
  175. package/src/resources/extensions/gsd/guided-flow.ts +24 -0
  176. package/src/resources/extensions/gsd/init-wizard.ts +40 -0
  177. package/src/resources/extensions/gsd/mcp-project-config.ts +128 -0
  178. package/src/resources/extensions/gsd/tests/auto-project-root-env.test.ts +29 -0
  179. package/src/resources/extensions/gsd/tests/core-overlay-fallback.test.ts +101 -0
  180. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +66 -0
  181. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +85 -0
  182. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +15 -0
  183. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +16 -0
  184. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +500 -0
  185. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +625 -0
  186. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +629 -0
  187. package/src/resources/extensions/gsd/workflow-logger.ts +19 -3
  188. package/src/resources/extensions/gsd/workflow-mcp.ts +320 -0
  189. package/dist/web/standalone/.next/static/chunks/6502.b804e48b7919f55e.js +0 -9
  190. /package/dist/web/standalone/.next/static/{NllX5BEOLdTXS9ypf1i3i → gbSATDX4Jt2ufxzUr5nYm}/_buildManifest.js +0 -0
  191. /package/dist/web/standalone/.next/static/{NllX5BEOLdTXS9ypf1i3i → gbSATDX4Jt2ufxzUr5nYm}/_ssgManifest.js +0 -0
@@ -241,6 +241,29 @@ const s = new AutoSession();
241
241
  /** Throttle STATE.md rebuilds — at most once per 30 seconds */
242
242
  const STATE_REBUILD_MIN_INTERVAL_MS = 30_000;
243
243
 
244
+ function captureProjectRootEnv(projectRoot: string): void {
245
+ if (!s.projectRootEnvCaptured) {
246
+ s.hadProjectRootEnv = Object.prototype.hasOwnProperty.call(process.env, "GSD_PROJECT_ROOT");
247
+ s.previousProjectRootEnv = process.env.GSD_PROJECT_ROOT ?? null;
248
+ s.projectRootEnvCaptured = true;
249
+ }
250
+ process.env.GSD_PROJECT_ROOT = projectRoot;
251
+ }
252
+
253
+ function restoreProjectRootEnv(): void {
254
+ if (!s.projectRootEnvCaptured) return;
255
+
256
+ if (s.hadProjectRootEnv && s.previousProjectRootEnv !== null) {
257
+ process.env.GSD_PROJECT_ROOT = s.previousProjectRootEnv;
258
+ } else {
259
+ delete process.env.GSD_PROJECT_ROOT;
260
+ }
261
+
262
+ s.previousProjectRootEnv = null;
263
+ s.hadProjectRootEnv = false;
264
+ s.projectRootEnvCaptured = false;
265
+ }
266
+
244
267
  export function shouldUseWorktreeIsolation(): boolean {
245
268
  const prefs = loadEffectiveGSDPreferences()?.preferences?.git;
246
269
  if (prefs?.isolation === "worktree") return true;
@@ -542,6 +565,7 @@ function handleLostSessionLock(
542
565
  s.active = false;
543
566
  s.paused = false;
544
567
  clearUnitTimeout();
568
+ restoreProjectRootEnv();
545
569
  deregisterSigtermHandler();
546
570
  clearCmuxSidebar(loadEffectiveGSDPreferences()?.preferences);
547
571
  const base = lockBase();
@@ -577,6 +601,7 @@ function cleanupAfterLoopExit(ctx: ExtensionContext): void {
577
601
  s.currentUnit = null;
578
602
  s.active = false;
579
603
  clearUnitTimeout();
604
+ restoreProjectRootEnv();
580
605
 
581
606
  // Clear crash lock and release session lock so the next `/gsd next` does
582
607
  // not see a stale lock with the current PID and treat it as a "remote"
@@ -846,6 +871,7 @@ export async function stopAuto(
846
871
  ctx?.ui.setStatus("gsd-auto", undefined);
847
872
  ctx?.ui.setWidget("gsd-progress", undefined);
848
873
  ctx?.ui.setFooter(undefined);
874
+ restoreProjectRootEnv();
849
875
 
850
876
  // Reset all session state in one call
851
877
  s.reset();
@@ -934,6 +960,7 @@ export async function pauseAuto(
934
960
 
935
961
  s.active = false;
936
962
  s.paused = true;
963
+ restoreProjectRootEnv();
937
964
  s.pendingVerificationRetry = null;
938
965
  s.verificationRetryCount.clear();
939
966
  ctx?.ui.setStatus("gsd-auto", "paused");
@@ -1305,6 +1332,7 @@ export async function startAuto(
1305
1332
  );
1306
1333
  logCmuxEvent(loadEffectiveGSDPreferences()?.preferences, s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "progress");
1307
1334
 
1335
+ captureProjectRootEnv(s.originalBasePath || s.basePath);
1308
1336
  await autoLoop(ctx, pi, s, buildLoopDeps());
1309
1337
  cleanupAfterLoopExit(ctx);
1310
1338
  return;
@@ -1329,6 +1357,7 @@ export async function startAuto(
1329
1357
  );
1330
1358
  if (!ready) return;
1331
1359
 
1360
+ captureProjectRootEnv(s.originalBasePath || s.basePath);
1332
1361
  try {
1333
1362
  syncCmuxSidebar(loadEffectiveGSDPreferences()?.preferences, await deriveState(s.basePath));
1334
1363
  } catch (err) {
@@ -1569,4 +1598,3 @@ export {
1569
1598
  buildLoopRemediationSteps,
1570
1599
  } from "./auto-recovery.js";
1571
1600
  export { resolveExpectedArtifactPath } from "./auto-artifact-paths.js";
1572
-
@@ -8,15 +8,18 @@ import { ensureDbOpen } from "./dynamic-tools.js";
8
8
  import { StringEnum } from "@gsd/pi-ai";
9
9
  import { logError } from "../workflow-logger.js";
10
10
  import { getErrorMessage } from "../error-utils.js";
11
- import { shouldBlockContextArtifactSave } from "./write-gate.js";
12
-
13
- const SUPPORTED_SUMMARY_ARTIFACT_TYPES = ["SUMMARY", "RESEARCH", "CONTEXT", "ASSESSMENT", "CONTEXT-DRAFT"] as const;
14
-
15
- export function isSupportedSummaryArtifactType(
16
- artifactType: string,
17
- ): artifactType is (typeof SUPPORTED_SUMMARY_ARTIFACT_TYPES)[number] {
18
- return (SUPPORTED_SUMMARY_ARTIFACT_TYPES as readonly string[]).includes(artifactType);
19
- }
11
+ import {
12
+ executeCompleteMilestone,
13
+ executePlanMilestone,
14
+ executePlanSlice,
15
+ executeReplanSlice,
16
+ executeReassessRoadmap,
17
+ executeSaveGateResult,
18
+ executeSliceComplete,
19
+ executeSummarySave,
20
+ executeTaskComplete,
21
+ executeValidateMilestone,
22
+ } from "../tools/workflow-tool-executors.js";
20
23
 
21
24
  /**
22
25
  * Register an alias tool that shares the same execute function as its canonical counterpart.
@@ -286,63 +289,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
286
289
  // ─── gsd_summary_save (formerly gsd_save_summary) ──────────────────────
287
290
 
288
291
  const summarySaveExecute = async (_toolCallId: string, params: any, _signal: AbortSignal | undefined, _onUpdate: unknown, _ctx: unknown) => {
289
- const dbAvailable = await ensureDbOpen();
290
- if (!dbAvailable) {
291
- return {
292
- content: [{ type: "text" as const, text: "Error: GSD database is not available. Cannot save artifact." }],
293
- details: { operation: "save_summary", error: "db_unavailable" } as any,
294
- };
295
- }
296
- if (!isSupportedSummaryArtifactType(params.artifact_type)) {
297
- return {
298
- content: [{ type: "text" as const, text: `Error: Invalid artifact_type "${params.artifact_type}". Must be one of: ${SUPPORTED_SUMMARY_ARTIFACT_TYPES.join(", ")}` }],
299
- details: { operation: "save_summary", error: "invalid_artifact_type" } as any,
300
- };
301
- }
302
- const contextGuard = shouldBlockContextArtifactSave(
303
- params.artifact_type,
304
- params.milestone_id ?? null,
305
- params.slice_id ?? null,
306
- );
307
- if (contextGuard.block) {
308
- return {
309
- content: [{ type: "text" as const, text: `Error saving artifact: ${contextGuard.reason ?? "context write blocked"}` }],
310
- details: { operation: "save_summary", error: "context_write_blocked" } as any,
311
- };
312
- }
313
- try {
314
- let relativePath: string;
315
- if (params.task_id && params.slice_id) {
316
- relativePath = `milestones/${params.milestone_id}/slices/${params.slice_id}/tasks/${params.task_id}-${params.artifact_type}.md`;
317
- } else if (params.slice_id) {
318
- relativePath = `milestones/${params.milestone_id}/slices/${params.slice_id}/${params.slice_id}-${params.artifact_type}.md`;
319
- } else {
320
- relativePath = `milestones/${params.milestone_id}/${params.milestone_id}-${params.artifact_type}.md`;
321
- }
322
- const { saveArtifactToDb } = await import("../db-writer.js");
323
- await saveArtifactToDb(
324
- {
325
- path: relativePath,
326
- artifact_type: params.artifact_type,
327
- content: params.content,
328
- milestone_id: params.milestone_id,
329
- slice_id: params.slice_id,
330
- task_id: params.task_id,
331
- },
332
- process.cwd(),
333
- );
334
- return {
335
- content: [{ type: "text" as const, text: `Saved ${params.artifact_type} artifact to ${relativePath}` }],
336
- details: { operation: "save_summary", path: relativePath, artifact_type: params.artifact_type } as any,
337
- };
338
- } catch (err) {
339
- const msg = err instanceof Error ? err.message : String(err);
340
- logError("tool", `gsd_summary_save tool failed: ${msg}`, { tool: "gsd_summary_save", error: String(err) });
341
- return {
342
- content: [{ type: "text" as const, text: `Error saving artifact: ${msg}` }],
343
- details: { operation: "save_summary", error: msg } as any,
344
- };
345
- }
292
+ return executeSummarySave(params, process.cwd());
346
293
  };
347
294
 
348
295
  const summarySaveTool = {
@@ -475,38 +422,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
475
422
  // ─── gsd_plan_milestone (gsd_milestone_plan alias) ─────────────────────
476
423
 
477
424
  const planMilestoneExecute = async (_toolCallId: string, params: any, _signal: AbortSignal | undefined, _onUpdate: unknown, _ctx: unknown) => {
478
- const dbAvailable = await ensureDbOpen();
479
- if (!dbAvailable) {
480
- return {
481
- content: [{ type: "text" as const, text: "Error: GSD database is not available. Cannot plan milestone." }],
482
- details: { operation: "plan_milestone", error: "db_unavailable" } as any,
483
- };
484
- }
485
- try {
486
- const { handlePlanMilestone } = await import("../tools/plan-milestone.js");
487
- const result = await handlePlanMilestone(params, process.cwd());
488
- if ("error" in result) {
489
- return {
490
- content: [{ type: "text" as const, text: `Error planning milestone: ${result.error}` }],
491
- details: { operation: "plan_milestone", error: result.error } as any,
492
- };
493
- }
494
- return {
495
- content: [{ type: "text" as const, text: `Planned milestone ${result.milestoneId}` }],
496
- details: {
497
- operation: "plan_milestone",
498
- milestoneId: result.milestoneId,
499
- roadmapPath: result.roadmapPath,
500
- } as any,
501
- };
502
- } catch (err) {
503
- const msg = err instanceof Error ? err.message : String(err);
504
- logError("tool", `plan_milestone tool failed: ${msg}`, { tool: "gsd_plan_milestone", error: String(err) });
505
- return {
506
- content: [{ type: "text" as const, text: `Error planning milestone: ${msg}` }],
507
- details: { operation: "plan_milestone", error: msg } as any,
508
- };
509
- }
425
+ return executePlanMilestone(params, process.cwd());
510
426
  };
511
427
 
512
428
  const planMilestoneTool = {
@@ -568,40 +484,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
568
484
  // ─── gsd_plan_slice (gsd_slice_plan alias) ─────────────────────────────
569
485
 
570
486
  const planSliceExecute = async (_toolCallId: string, params: any, _signal: AbortSignal | undefined, _onUpdate: unknown, _ctx: unknown) => {
571
- const dbAvailable = await ensureDbOpen();
572
- if (!dbAvailable) {
573
- return {
574
- content: [{ type: "text" as const, text: "Error: GSD database is not available. Cannot plan slice." }],
575
- details: { operation: "plan_slice", error: "db_unavailable" } as any,
576
- };
577
- }
578
- try {
579
- const { handlePlanSlice } = await import("../tools/plan-slice.js");
580
- const result = await handlePlanSlice(params, process.cwd());
581
- if ("error" in result) {
582
- return {
583
- content: [{ type: "text" as const, text: `Error planning slice: ${result.error}` }],
584
- details: { operation: "plan_slice", error: result.error } as any,
585
- };
586
- }
587
- return {
588
- content: [{ type: "text" as const, text: `Planned slice ${result.sliceId} (${result.milestoneId})` }],
589
- details: {
590
- operation: "plan_slice",
591
- milestoneId: result.milestoneId,
592
- sliceId: result.sliceId,
593
- planPath: result.planPath,
594
- taskPlanPaths: result.taskPlanPaths,
595
- } as any,
596
- };
597
- } catch (err) {
598
- const msg = err instanceof Error ? err.message : String(err);
599
- logError("tool", `plan_slice tool failed: ${msg}`, { tool: "gsd_plan_slice", error: String(err) });
600
- return {
601
- content: [{ type: "text" as const, text: `Error planning slice: ${msg}` }],
602
- details: { operation: "plan_slice", error: msg } as any,
603
- };
604
- }
487
+ return executePlanSlice(params, process.cwd());
605
488
  };
606
489
 
607
490
  const planSliceTool = {
@@ -717,46 +600,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
717
600
  // ─── gsd_task_complete (gsd_complete_task alias) ────────────────────────
718
601
 
719
602
  const taskCompleteExecute = async (_toolCallId: string, params: any, _signal: AbortSignal | undefined, _onUpdate: unknown, _ctx: unknown) => {
720
- const dbAvailable = await ensureDbOpen();
721
- if (!dbAvailable) {
722
- return {
723
- content: [{ type: "text" as const, text: "Error: GSD database is not available. Cannot complete task." }],
724
- details: { operation: "complete_task", error: "db_unavailable" } as any,
725
- };
726
- }
727
- try {
728
- // Coerce string items to objects for verificationEvidence (#3541).
729
- const coerced = { ...params };
730
- coerced.verificationEvidence = (params.verificationEvidence ?? []).map((v: any) =>
731
- typeof v === "string" ? { command: v, exitCode: -1, verdict: "unknown (coerced from string)", durationMs: 0 } : v,
732
- );
733
-
734
- const { handleCompleteTask } = await import("../tools/complete-task.js");
735
- const result = await handleCompleteTask(coerced, process.cwd());
736
- if ("error" in result) {
737
- return {
738
- content: [{ type: "text" as const, text: `Error completing task: ${result.error}` }],
739
- details: { operation: "complete_task", error: result.error } as any,
740
- };
741
- }
742
- return {
743
- content: [{ type: "text" as const, text: `Completed task ${result.taskId} (${result.sliceId}/${result.milestoneId})` }],
744
- details: {
745
- operation: "complete_task",
746
- taskId: result.taskId,
747
- sliceId: result.sliceId,
748
- milestoneId: result.milestoneId,
749
- summaryPath: result.summaryPath,
750
- } as any,
751
- };
752
- } catch (err) {
753
- const msg = err instanceof Error ? err.message : String(err);
754
- logError("tool", `complete_task tool failed: ${msg}`, { tool: "gsd_task_complete", error: String(err) });
755
- return {
756
- content: [{ type: "text" as const, text: `Error completing task: ${msg}` }],
757
- details: { operation: "complete_task", error: msg } as any,
758
- };
759
- }
603
+ return executeTaskComplete(params, process.cwd());
760
604
  };
761
605
 
762
606
  const taskCompleteTool = {
@@ -809,86 +653,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
809
653
  // ─── gsd_slice_complete (gsd_complete_slice alias) ─────────────────────
810
654
 
811
655
  const sliceCompleteExecute = async (_toolCallId: string, params: any, _signal: AbortSignal | undefined, _onUpdate: unknown, _ctx: unknown) => {
812
- const dbAvailable = await ensureDbOpen();
813
- if (!dbAvailable) {
814
- return {
815
- content: [{ type: "text" as const, text: "Error: GSD database is not available. Cannot complete slice." }],
816
- details: { operation: "complete_slice", error: "db_unavailable" } as any,
817
- };
818
- }
819
- try {
820
- // Coerce string items to objects for fields where LLMs sometimes pass
821
- // plain strings instead of the expected { key, value } shape (#3541).
822
- // Parses "key — value" or "key - value" format when possible.
823
- const splitPair = (s: string): [string, string] => {
824
- const m = s.match(/^(.+?)\s*(?:—|-)\s+(.+)$/);
825
- return m ? [m[1].trim(), m[2].trim()] : [s.trim(), ""];
826
- };
827
- const coerced = { ...params };
828
- // Coerce simple string-array fields: LLMs sometimes pass a plain string
829
- // instead of a single-element array (#3585).
830
- const wrapArray = (v: any): any[] =>
831
- v == null ? [] : Array.isArray(v) ? v : [v];
832
- coerced.provides = wrapArray(params.provides);
833
- coerced.keyFiles = wrapArray(params.keyFiles);
834
- coerced.keyDecisions = wrapArray(params.keyDecisions);
835
- coerced.patternsEstablished = wrapArray(params.patternsEstablished);
836
- coerced.observabilitySurfaces = wrapArray(params.observabilitySurfaces);
837
- coerced.requirementsSurfaced = wrapArray(params.requirementsSurfaced);
838
- coerced.drillDownPaths = wrapArray(params.drillDownPaths);
839
- coerced.affects = wrapArray(params.affects);
840
- coerced.filesModified = wrapArray(params.filesModified).map((f: any) => {
841
- if (typeof f !== "string") return f;
842
- const [path, description] = splitPair(f);
843
- return { path, description };
844
- });
845
- coerced.requires = wrapArray(params.requires).map((r: any) => {
846
- if (typeof r !== "string") return r;
847
- const [slice, provides] = splitPair(r);
848
- return { slice, provides };
849
- });
850
- coerced.requirementsAdvanced = wrapArray(params.requirementsAdvanced).map((r: any) => {
851
- if (typeof r !== "string") return r;
852
- const [id, how] = splitPair(r);
853
- return { id, how };
854
- });
855
- coerced.requirementsValidated = wrapArray(params.requirementsValidated).map((r: any) => {
856
- if (typeof r !== "string") return r;
857
- const [id, proof] = splitPair(r);
858
- return { id, proof };
859
- });
860
- coerced.requirementsInvalidated = wrapArray(params.requirementsInvalidated).map((r: any) => {
861
- if (typeof r !== "string") return r;
862
- const [id, what] = splitPair(r);
863
- return { id, what };
864
- });
865
-
866
- const { handleCompleteSlice } = await import("../tools/complete-slice.js");
867
- const result = await handleCompleteSlice(coerced, process.cwd());
868
- if ("error" in result) {
869
- return {
870
- content: [{ type: "text" as const, text: `Error completing slice: ${result.error}` }],
871
- details: { operation: "complete_slice", error: result.error } as any,
872
- };
873
- }
874
- return {
875
- content: [{ type: "text" as const, text: `Completed slice ${result.sliceId} (${result.milestoneId})` }],
876
- details: {
877
- operation: "complete_slice",
878
- sliceId: result.sliceId,
879
- milestoneId: result.milestoneId,
880
- summaryPath: result.summaryPath,
881
- uatPath: result.uatPath,
882
- } as any,
883
- };
884
- } catch (err) {
885
- const msg = err instanceof Error ? err.message : String(err);
886
- logError("tool", `complete_slice tool failed: ${msg}`, { tool: "gsd_slice_complete", error: String(err) });
887
- return {
888
- content: [{ type: "text" as const, text: `Error completing slice: ${msg}` }],
889
- details: { operation: "complete_slice", error: msg } as any,
890
- };
891
- }
656
+ return executeSliceComplete(params, process.cwd());
892
657
  };
893
658
 
894
659
  const sliceCompleteTool = {
@@ -1073,42 +838,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
1073
838
  // ─── gsd_complete_milestone ────────────────────────────────────────────
1074
839
 
1075
840
  const milestoneCompleteExecute = async (_toolCallId: string, params: any, _signal: AbortSignal | undefined, _onUpdate: unknown, _ctx: unknown) => {
1076
- const dbAvailable = await ensureDbOpen();
1077
- if (!dbAvailable) {
1078
- return {
1079
- content: [{ type: "text" as const, text: "Error: GSD database is not available. Cannot complete milestone." }],
1080
- details: { operation: "complete_milestone", error: "db_unavailable" } as any,
1081
- };
1082
- }
1083
- try {
1084
- // ── Input sanitization: normalize markdown parameters (#3013) ──────
1085
- const { sanitizeCompleteMilestoneParams } = await import("./sanitize-complete-milestone.js");
1086
- const sanitized = sanitizeCompleteMilestoneParams(params);
1087
-
1088
- const { handleCompleteMilestone } = await import("../tools/complete-milestone.js");
1089
- const result = await handleCompleteMilestone(sanitized, process.cwd());
1090
- if ("error" in result) {
1091
- return {
1092
- content: [{ type: "text" as const, text: `Error completing milestone: ${result.error}` }],
1093
- details: { operation: "complete_milestone", error: result.error } as any,
1094
- };
1095
- }
1096
- return {
1097
- content: [{ type: "text" as const, text: `Completed milestone ${result.milestoneId}. Summary written to ${result.summaryPath}` }],
1098
- details: {
1099
- operation: "complete_milestone",
1100
- milestoneId: result.milestoneId,
1101
- summaryPath: result.summaryPath,
1102
- } as any,
1103
- };
1104
- } catch (err) {
1105
- const msg = err instanceof Error ? err.message : String(err);
1106
- logError("tool", `complete_milestone tool failed: ${msg}`, { tool: "gsd_complete_milestone", error: String(err) });
1107
- return {
1108
- content: [{ type: "text" as const, text: `Error completing milestone: ${msg}` }],
1109
- details: { operation: "complete_milestone", error: msg } as any,
1110
- };
1111
- }
841
+ return executeCompleteMilestone(params, process.cwd());
1112
842
  };
1113
843
 
1114
844
  const milestoneCompleteTool = {
@@ -1150,39 +880,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
1150
880
  // ─── gsd_validate_milestone (gsd_milestone_validate alias) ─────────────
1151
881
 
1152
882
  const milestoneValidateExecute = async (_toolCallId: string, params: any, _signal: AbortSignal | undefined, _onUpdate: unknown, _ctx: unknown) => {
1153
- const dbAvailable = await ensureDbOpen();
1154
- if (!dbAvailable) {
1155
- return {
1156
- content: [{ type: "text" as const, text: "Error: GSD database is not available. Cannot validate milestone." }],
1157
- details: { operation: "validate_milestone", error: "db_unavailable" } as any,
1158
- };
1159
- }
1160
- try {
1161
- const { handleValidateMilestone } = await import("../tools/validate-milestone.js");
1162
- const result = await handleValidateMilestone(params, process.cwd());
1163
- if ("error" in result) {
1164
- return {
1165
- content: [{ type: "text" as const, text: `Error validating milestone: ${result.error}` }],
1166
- details: { operation: "validate_milestone", error: result.error } as any,
1167
- };
1168
- }
1169
- return {
1170
- content: [{ type: "text" as const, text: `Validated milestone ${result.milestoneId} — verdict: ${result.verdict}. Written to ${result.validationPath}` }],
1171
- details: {
1172
- operation: "validate_milestone",
1173
- milestoneId: result.milestoneId,
1174
- verdict: result.verdict,
1175
- validationPath: result.validationPath,
1176
- } as any,
1177
- };
1178
- } catch (err) {
1179
- const msg = err instanceof Error ? err.message : String(err);
1180
- logError("tool", `validate_milestone tool failed: ${msg}`, { tool: "gsd_validate_milestone", error: String(err) });
1181
- return {
1182
- content: [{ type: "text" as const, text: `Error validating milestone: ${msg}` }],
1183
- details: { operation: "validate_milestone", error: msg } as any,
1184
- };
1185
- }
883
+ return executeValidateMilestone(params, process.cwd());
1186
884
  };
1187
885
 
1188
886
  const milestoneValidateTool = {
@@ -1219,40 +917,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
1219
917
  // ─── gsd_replan_slice (gsd_slice_replan alias) ─────────────────────────
1220
918
 
1221
919
  const replanSliceExecute = async (_toolCallId: string, params: any, _signal: AbortSignal | undefined, _onUpdate: unknown, _ctx: unknown) => {
1222
- const dbAvailable = await ensureDbOpen();
1223
- if (!dbAvailable) {
1224
- return {
1225
- content: [{ type: "text" as const, text: "Error: GSD database is not available. Cannot replan slice." }],
1226
- details: { operation: "replan_slice", error: "db_unavailable" } as any,
1227
- };
1228
- }
1229
- try {
1230
- const { handleReplanSlice } = await import("../tools/replan-slice.js");
1231
- const result = await handleReplanSlice(params, process.cwd());
1232
- if ("error" in result) {
1233
- return {
1234
- content: [{ type: "text" as const, text: `Error replanning slice: ${result.error}` }],
1235
- details: { operation: "replan_slice", error: result.error } as any,
1236
- };
1237
- }
1238
- return {
1239
- content: [{ type: "text" as const, text: `Replanned slice ${result.sliceId} (${result.milestoneId})` }],
1240
- details: {
1241
- operation: "replan_slice",
1242
- milestoneId: result.milestoneId,
1243
- sliceId: result.sliceId,
1244
- replanPath: result.replanPath,
1245
- planPath: result.planPath,
1246
- } as any,
1247
- };
1248
- } catch (err) {
1249
- const msg = err instanceof Error ? err.message : String(err);
1250
- logError("tool", `replan_slice tool failed: ${msg}`, { tool: "gsd_replan_slice", error: String(err) });
1251
- return {
1252
- content: [{ type: "text" as const, text: `Error replanning slice: ${msg}` }],
1253
- details: { operation: "replan_slice", error: msg } as any,
1254
- };
1255
- }
920
+ return executeReplanSlice(params, process.cwd());
1256
921
  };
1257
922
 
1258
923
  const replanSliceTool = {
@@ -1299,40 +964,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
1299
964
  // ─── gsd_reassess_roadmap (gsd_roadmap_reassess alias) ─────────────────
1300
965
 
1301
966
  const reassessRoadmapExecute = async (_toolCallId: string, params: any, _signal: AbortSignal | undefined, _onUpdate: unknown, _ctx: unknown) => {
1302
- const dbAvailable = await ensureDbOpen();
1303
- if (!dbAvailable) {
1304
- return {
1305
- content: [{ type: "text" as const, text: "Error: GSD database is not available. Cannot reassess roadmap." }],
1306
- details: { operation: "reassess_roadmap", error: "db_unavailable" } as any,
1307
- };
1308
- }
1309
- try {
1310
- const { handleReassessRoadmap } = await import("../tools/reassess-roadmap.js");
1311
- const result = await handleReassessRoadmap(params, process.cwd());
1312
- if ("error" in result) {
1313
- return {
1314
- content: [{ type: "text" as const, text: `Error reassessing roadmap: ${result.error}` }],
1315
- details: { operation: "reassess_roadmap", error: result.error } as any,
1316
- };
1317
- }
1318
- return {
1319
- content: [{ type: "text" as const, text: `Reassessed roadmap for milestone ${result.milestoneId} after ${result.completedSliceId}` }],
1320
- details: {
1321
- operation: "reassess_roadmap",
1322
- milestoneId: result.milestoneId,
1323
- completedSliceId: result.completedSliceId,
1324
- assessmentPath: result.assessmentPath,
1325
- roadmapPath: result.roadmapPath,
1326
- } as any,
1327
- };
1328
- } catch (err) {
1329
- const msg = err instanceof Error ? err.message : String(err);
1330
- logError("tool", `reassess_roadmap tool failed: ${msg}`, { tool: "gsd_reassess_roadmap", error: String(err) });
1331
- return {
1332
- content: [{ type: "text" as const, text: `Error reassessing roadmap: ${msg}` }],
1333
- details: { operation: "reassess_roadmap", error: msg } as any,
1334
- };
1335
- }
967
+ return executeReassessRoadmap(params, process.cwd());
1336
968
  };
1337
969
 
1338
970
  const reassessRoadmapTool = {
@@ -1387,52 +1019,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
1387
1019
  // ─── gsd_save_gate_result ──────────────────────────────────────────────
1388
1020
 
1389
1021
  const saveGateResultExecute = async (_toolCallId: string, params: any, _signal: AbortSignal | undefined, _onUpdate: unknown, _ctx: unknown) => {
1390
- const dbAvailable = await ensureDbOpen();
1391
- if (!dbAvailable) {
1392
- return {
1393
- content: [{ type: "text" as const, text: "Error: GSD database is not available." }],
1394
- details: { operation: "save_gate_result", error: "db_unavailable" } as any,
1395
- };
1396
- }
1397
- const validGates = ["Q3", "Q4", "Q5", "Q6", "Q7", "Q8"];
1398
- if (!validGates.includes(params.gateId)) {
1399
- return {
1400
- content: [{ type: "text" as const, text: `Error: Invalid gateId "${params.gateId}". Must be one of: ${validGates.join(", ")}` }],
1401
- details: { operation: "save_gate_result", error: "invalid_gate_id" } as any,
1402
- };
1403
- }
1404
- const validVerdicts = ["pass", "flag", "omitted"];
1405
- if (!validVerdicts.includes(params.verdict)) {
1406
- return {
1407
- content: [{ type: "text" as const, text: `Error: Invalid verdict "${params.verdict}". Must be one of: ${validVerdicts.join(", ")}` }],
1408
- details: { operation: "save_gate_result", error: "invalid_verdict" } as any,
1409
- };
1410
- }
1411
- try {
1412
- const { saveGateResult } = await import("../gsd-db.js");
1413
- const { invalidateStateCache } = await import("../state.js");
1414
- saveGateResult({
1415
- milestoneId: params.milestoneId,
1416
- sliceId: params.sliceId,
1417
- gateId: params.gateId,
1418
- taskId: params.taskId ?? "",
1419
- verdict: params.verdict,
1420
- rationale: params.rationale,
1421
- findings: params.findings ?? "",
1422
- });
1423
- invalidateStateCache();
1424
- return {
1425
- content: [{ type: "text" as const, text: `Gate ${params.gateId} result saved: verdict=${params.verdict}` }],
1426
- details: { operation: "save_gate_result", gateId: params.gateId, verdict: params.verdict } as any,
1427
- };
1428
- } catch (err) {
1429
- const msg = err instanceof Error ? err.message : String(err);
1430
- logError("tool", `gsd_save_gate_result failed: ${msg}`, { tool: "gsd_save_gate_result", error: String(err) });
1431
- return {
1432
- content: [{ type: "text" as const, text: `Error saving gate result: ${msg}` }],
1433
- details: { operation: "save_gate_result", error: msg } as any,
1434
- };
1435
- }
1022
+ return executeSaveGateResult(params, process.cwd());
1436
1023
  };
1437
1024
 
1438
1025
  const saveGateResultTool = {
@@ -75,12 +75,9 @@ export function resolveProjectRootDbPath(basePath: string): string {
75
75
  return join(basePath, ".gsd", "gsd.db");
76
76
  }
77
77
 
78
- export async function ensureDbOpen(): Promise<boolean> {
78
+ export async function ensureDbOpen(basePath: string = process.cwd()): Promise<boolean> {
79
79
  try {
80
80
  const db = await import("../gsd-db.js");
81
- if (db.isDbAvailable()) return true;
82
-
83
- const basePath = process.cwd();
84
81
  const dbPath = resolveProjectRootDbPath(basePath);
85
82
  const gsdDir = join(basePath, ".gsd");
86
83
 
@@ -194,4 +191,3 @@ export function registerDynamicTools(pi: ExtensionAPI): void {
194
191
  },
195
192
  } as any);
196
193
  }
197
-