gsd-pi 2.67.0-dev.1cd1e0f → 2.67.0-dev.2142d3e

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 (74) hide show
  1. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +3 -0
  2. package/dist/resources/extensions/gsd/auto/phases.js +17 -0
  3. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +12 -0
  4. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +11 -435
  5. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +1 -4
  6. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +7 -64
  7. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +88 -8
  8. package/dist/resources/extensions/gsd/commands/handlers/core.js +38 -24
  9. package/dist/resources/extensions/gsd/commands/index.js +8 -1
  10. package/dist/resources/extensions/gsd/guided-flow.js +16 -0
  11. package/dist/resources/extensions/gsd/init-wizard.js +34 -0
  12. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +508 -0
  13. package/dist/resources/extensions/gsd/workflow-logger.js +18 -3
  14. package/dist/resources/extensions/gsd/workflow-mcp.js +190 -0
  15. package/dist/web/standalone/.next/BUILD_ID +1 -1
  16. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  17. package/dist/web/standalone/.next/build-manifest.json +2 -2
  18. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  19. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  20. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  21. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  22. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  23. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  24. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  25. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  28. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/index.html +1 -1
  36. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  43. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  44. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  45. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  46. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  47. package/package.json +1 -1
  48. package/packages/mcp-server/README.md +38 -0
  49. package/packages/mcp-server/src/server.ts +6 -2
  50. package/packages/mcp-server/src/workflow-tools.test.ts +976 -0
  51. package/packages/mcp-server/src/workflow-tools.ts +986 -0
  52. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +3 -0
  53. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +121 -0
  54. package/src/resources/extensions/gsd/auto/phases.ts +25 -0
  55. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +20 -0
  56. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +22 -435
  57. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +1 -5
  58. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +7 -72
  59. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +122 -6
  60. package/src/resources/extensions/gsd/commands/handlers/core.ts +52 -25
  61. package/src/resources/extensions/gsd/commands/index.ts +7 -1
  62. package/src/resources/extensions/gsd/guided-flow.ts +24 -0
  63. package/src/resources/extensions/gsd/init-wizard.ts +34 -0
  64. package/src/resources/extensions/gsd/tests/core-overlay-fallback.test.ts +101 -0
  65. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +66 -0
  66. package/src/resources/extensions/gsd/tests/init-bootstrap-completeness.test.ts +121 -0
  67. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +16 -0
  68. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +301 -0
  69. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +625 -0
  70. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +629 -0
  71. package/src/resources/extensions/gsd/workflow-logger.ts +19 -3
  72. package/src/resources/extensions/gsd/workflow-mcp.ts +233 -0
  73. /package/dist/web/standalone/.next/static/{PHqEommYRR8CRn3i84CGM → xR6qurkuYSvyjBjRyJLxG}/_buildManifest.js +0 -0
  74. /package/dist/web/standalone/.next/static/{PHqEommYRR8CRn3i84CGM → xR6qurkuYSvyjBjRyJLxG}/_ssgManifest.js +0 -0
@@ -9,6 +9,7 @@
9
9
  import { EventStream } from "@gsd/pi-ai";
10
10
  import { execSync } from "node:child_process";
11
11
  import { PartialMessageBuilder, ZERO_USAGE, mapUsage } from "./partial-builder.js";
12
+ import { buildWorkflowMcpServers } from "../gsd/workflow-mcp.js";
12
13
  // ---------------------------------------------------------------------------
13
14
  // Stream factory
14
15
  // ---------------------------------------------------------------------------
@@ -197,6 +198,7 @@ export function buildFinalClaudeCodeContent(blocks, lastThinkingContent, lastTex
197
198
  * beta flags, and other configuration without mocking the full SDK.
198
199
  */
199
200
  export function buildSdkOptions(modelId, prompt) {
201
+ const mcpServers = buildWorkflowMcpServers();
200
202
  return {
201
203
  pathToClaudeCodeExecutable: getClaudePath(),
202
204
  model: modelId,
@@ -207,6 +209,7 @@ export function buildSdkOptions(modelId, prompt) {
207
209
  allowDangerouslySkipPermissions: true,
208
210
  settingSources: ["project"],
209
211
  systemPrompt: { type: "preset", preset: "claude_code" },
212
+ ...(mcpServers ? { mcpServers } : {}),
210
213
  betas: modelId.includes("sonnet") ? ["context-1m-2025-08-07"] : [],
211
214
  };
212
215
  }
@@ -27,6 +27,7 @@ import { isDbAvailable, getMilestoneSlices } from "../gsd-db.js";
27
27
  import { resetEvidence } from "../safety/evidence-collector.js";
28
28
  import { createCheckpoint, cleanupCheckpoint, rollbackToCheckpoint } from "../safety/git-checkpoint.js";
29
29
  import { resolveSafetyHarnessConfig } from "../safety/safety-harness.js";
30
+ import { getWorkflowTransportSupportError, getRequiredWorkflowToolsForAutoUnit, } from "../workflow-mcp.js";
30
31
  // ─── generateMilestoneReport ──────────────────────────────────────────────────
31
32
  /**
32
33
  * Resolve the base path for milestone reports.
@@ -886,6 +887,22 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
886
887
  s.currentDispatchedModelId = s.currentUnitModel
887
888
  ? `${s.currentUnitModel.provider ?? ""}/${s.currentUnitModel.id ?? ""}`
888
889
  : null;
890
+ const compatibilityError = getWorkflowTransportSupportError(s.currentUnitModel?.provider ?? ctx.model?.provider, getRequiredWorkflowToolsForAutoUnit(unitType), {
891
+ projectRoot: s.basePath,
892
+ surface: "auto-mode",
893
+ unitType,
894
+ authMode: s.currentUnitModel?.provider
895
+ ? ctx.modelRegistry.getProviderAuthMode(s.currentUnitModel.provider)
896
+ : ctx.model?.provider
897
+ ? ctx.modelRegistry.getProviderAuthMode(ctx.model.provider)
898
+ : undefined,
899
+ baseUrl: s.currentUnitModel?.baseUrl ?? ctx.model?.baseUrl,
900
+ });
901
+ if (compatibilityError) {
902
+ ctx.ui.notify(compatibilityError, "error");
903
+ await deps.stopAuto(ctx, pi, compatibilityError);
904
+ return { action: "break", reason: "workflow-capability" };
905
+ }
889
906
  // Progress widget + preconditions — deferred to after model selection so the
890
907
  // widget's first render tick shows the correct model (#2899).
891
908
  deps.updateProgressWidget(ctx, unitType, unitId, state);
@@ -10,6 +10,7 @@ import { resolveMilestoneFile, resolveSliceFile, relSliceFile, } from "./paths.j
10
10
  import { buildResearchSlicePrompt, buildResearchMilestonePrompt, buildPlanSlicePrompt, buildPlanMilestonePrompt, buildExecuteTaskPrompt, buildCompleteSlicePrompt, buildCompleteMilestonePrompt, buildReassessRoadmapPrompt, buildRunUatPrompt, buildReplanSlicePrompt, } from "./auto-prompts.js";
11
11
  import { loadEffectiveGSDPreferences } from "./preferences.js";
12
12
  import { pauseAuto } from "./auto.js";
13
+ import { getWorkflowTransportSupportError, getRequiredWorkflowToolsForAutoUnit, } from "./workflow-mcp.js";
13
14
  export async function dispatchDirectPhase(ctx, pi, phase, base) {
14
15
  const state = await deriveState(base);
15
16
  const mid = state.activeMilestone?.id;
@@ -202,6 +203,17 @@ export async function dispatchDirectPhase(ctx, pi, phase, base) {
202
203
  ctx.ui.notify(`Unknown phase "${phase}". Valid phases: research, plan, execute, complete, reassess, uat, replan.`, "warning");
203
204
  return;
204
205
  }
206
+ const compatibilityError = getWorkflowTransportSupportError(ctx.model?.provider, getRequiredWorkflowToolsForAutoUnit(unitType), {
207
+ projectRoot: base,
208
+ surface: "direct phase dispatch",
209
+ unitType,
210
+ authMode: ctx.model?.provider ? ctx.modelRegistry.getProviderAuthMode(ctx.model.provider) : undefined,
211
+ baseUrl: ctx.model?.baseUrl,
212
+ });
213
+ if (compatibilityError) {
214
+ ctx.ui.notify(compatibilityError, "error");
215
+ return;
216
+ }
205
217
  ctx.ui.notify(`Dispatching ${unitType} for ${unitId}...`, "info");
206
218
  const result = await ctx.newSession();
207
219
  if (result.cancelled) {
@@ -5,11 +5,7 @@ import { loadEffectiveGSDPreferences } from "../preferences.js";
5
5
  import { ensureDbOpen } from "./dynamic-tools.js";
6
6
  import { StringEnum } from "@gsd/pi-ai";
7
7
  import { logError } from "../workflow-logger.js";
8
- import { shouldBlockContextArtifactSave } from "./write-gate.js";
9
- const SUPPORTED_SUMMARY_ARTIFACT_TYPES = ["SUMMARY", "RESEARCH", "CONTEXT", "ASSESSMENT", "CONTEXT-DRAFT"];
10
- export function isSupportedSummaryArtifactType(artifactType) {
11
- return SUPPORTED_SUMMARY_ARTIFACT_TYPES.includes(artifactType);
12
- }
8
+ import { executeCompleteMilestone, executePlanMilestone, executePlanSlice, executeReplanSlice, executeReassessRoadmap, executeSaveGateResult, executeSliceComplete, executeSummarySave, executeTaskComplete, executeValidateMilestone, } from "../tools/workflow-tool-executors.js";
13
9
  /**
14
10
  * Register an alias tool that shares the same execute function as its canonical counterpart.
15
11
  * The alias description and promptGuidelines direct the LLM to prefer the canonical name.
@@ -272,59 +268,7 @@ export function registerDbTools(pi) {
272
268
  registerAlias(pi, requirementSaveTool, "gsd_save_requirement", "gsd_requirement_save");
273
269
  // ─── gsd_summary_save (formerly gsd_save_summary) ──────────────────────
274
270
  const summarySaveExecute = async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
275
- const dbAvailable = await ensureDbOpen();
276
- if (!dbAvailable) {
277
- return {
278
- content: [{ type: "text", text: "Error: GSD database is not available. Cannot save artifact." }],
279
- details: { operation: "save_summary", error: "db_unavailable" },
280
- };
281
- }
282
- if (!isSupportedSummaryArtifactType(params.artifact_type)) {
283
- return {
284
- content: [{ type: "text", text: `Error: Invalid artifact_type "${params.artifact_type}". Must be one of: ${SUPPORTED_SUMMARY_ARTIFACT_TYPES.join(", ")}` }],
285
- details: { operation: "save_summary", error: "invalid_artifact_type" },
286
- };
287
- }
288
- const contextGuard = shouldBlockContextArtifactSave(params.artifact_type, params.milestone_id ?? null, params.slice_id ?? null);
289
- if (contextGuard.block) {
290
- return {
291
- content: [{ type: "text", text: `Error saving artifact: ${contextGuard.reason ?? "context write blocked"}` }],
292
- details: { operation: "save_summary", error: "context_write_blocked" },
293
- };
294
- }
295
- try {
296
- let relativePath;
297
- if (params.task_id && params.slice_id) {
298
- relativePath = `milestones/${params.milestone_id}/slices/${params.slice_id}/tasks/${params.task_id}-${params.artifact_type}.md`;
299
- }
300
- else if (params.slice_id) {
301
- relativePath = `milestones/${params.milestone_id}/slices/${params.slice_id}/${params.slice_id}-${params.artifact_type}.md`;
302
- }
303
- else {
304
- relativePath = `milestones/${params.milestone_id}/${params.milestone_id}-${params.artifact_type}.md`;
305
- }
306
- const { saveArtifactToDb } = await import("../db-writer.js");
307
- await saveArtifactToDb({
308
- path: relativePath,
309
- artifact_type: params.artifact_type,
310
- content: params.content,
311
- milestone_id: params.milestone_id,
312
- slice_id: params.slice_id,
313
- task_id: params.task_id,
314
- }, process.cwd());
315
- return {
316
- content: [{ type: "text", text: `Saved ${params.artifact_type} artifact to ${relativePath}` }],
317
- details: { operation: "save_summary", path: relativePath, artifact_type: params.artifact_type },
318
- };
319
- }
320
- catch (err) {
321
- const msg = err instanceof Error ? err.message : String(err);
322
- logError("tool", `gsd_summary_save tool failed: ${msg}`, { tool: "gsd_summary_save", error: String(err) });
323
- return {
324
- content: [{ type: "text", text: `Error saving artifact: ${msg}` }],
325
- details: { operation: "save_summary", error: msg },
326
- };
327
- }
271
+ return executeSummarySave(params, process.cwd());
328
272
  };
329
273
  const summarySaveTool = {
330
274
  name: "gsd_summary_save",
@@ -452,39 +396,7 @@ export function registerDbTools(pi) {
452
396
  registerAlias(pi, milestoneGenerateIdTool, "gsd_generate_milestone_id", "gsd_milestone_generate_id");
453
397
  // ─── gsd_plan_milestone (gsd_milestone_plan alias) ─────────────────────
454
398
  const planMilestoneExecute = async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
455
- const dbAvailable = await ensureDbOpen();
456
- if (!dbAvailable) {
457
- return {
458
- content: [{ type: "text", text: "Error: GSD database is not available. Cannot plan milestone." }],
459
- details: { operation: "plan_milestone", error: "db_unavailable" },
460
- };
461
- }
462
- try {
463
- const { handlePlanMilestone } = await import("../tools/plan-milestone.js");
464
- const result = await handlePlanMilestone(params, process.cwd());
465
- if ("error" in result) {
466
- return {
467
- content: [{ type: "text", text: `Error planning milestone: ${result.error}` }],
468
- details: { operation: "plan_milestone", error: result.error },
469
- };
470
- }
471
- return {
472
- content: [{ type: "text", text: `Planned milestone ${result.milestoneId}` }],
473
- details: {
474
- operation: "plan_milestone",
475
- milestoneId: result.milestoneId,
476
- roadmapPath: result.roadmapPath,
477
- },
478
- };
479
- }
480
- catch (err) {
481
- const msg = err instanceof Error ? err.message : String(err);
482
- logError("tool", `plan_milestone tool failed: ${msg}`, { tool: "gsd_plan_milestone", error: String(err) });
483
- return {
484
- content: [{ type: "text", text: `Error planning milestone: ${msg}` }],
485
- details: { operation: "plan_milestone", error: msg },
486
- };
487
- }
399
+ return executePlanMilestone(params, process.cwd());
488
400
  };
489
401
  const planMilestoneTool = {
490
402
  name: "gsd_plan_milestone",
@@ -541,41 +453,7 @@ export function registerDbTools(pi) {
541
453
  registerAlias(pi, planMilestoneTool, "gsd_milestone_plan", "gsd_plan_milestone");
542
454
  // ─── gsd_plan_slice (gsd_slice_plan alias) ─────────────────────────────
543
455
  const planSliceExecute = async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
544
- const dbAvailable = await ensureDbOpen();
545
- if (!dbAvailable) {
546
- return {
547
- content: [{ type: "text", text: "Error: GSD database is not available. Cannot plan slice." }],
548
- details: { operation: "plan_slice", error: "db_unavailable" },
549
- };
550
- }
551
- try {
552
- const { handlePlanSlice } = await import("../tools/plan-slice.js");
553
- const result = await handlePlanSlice(params, process.cwd());
554
- if ("error" in result) {
555
- return {
556
- content: [{ type: "text", text: `Error planning slice: ${result.error}` }],
557
- details: { operation: "plan_slice", error: result.error },
558
- };
559
- }
560
- return {
561
- content: [{ type: "text", text: `Planned slice ${result.sliceId} (${result.milestoneId})` }],
562
- details: {
563
- operation: "plan_slice",
564
- milestoneId: result.milestoneId,
565
- sliceId: result.sliceId,
566
- planPath: result.planPath,
567
- taskPlanPaths: result.taskPlanPaths,
568
- },
569
- };
570
- }
571
- catch (err) {
572
- const msg = err instanceof Error ? err.message : String(err);
573
- logError("tool", `plan_slice tool failed: ${msg}`, { tool: "gsd_plan_slice", error: String(err) });
574
- return {
575
- content: [{ type: "text", text: `Error planning slice: ${msg}` }],
576
- details: { operation: "plan_slice", error: msg },
577
- };
578
- }
456
+ return executePlanSlice(params, process.cwd());
579
457
  };
580
458
  const planSliceTool = {
581
459
  name: "gsd_plan_slice",
@@ -682,44 +560,7 @@ export function registerDbTools(pi) {
682
560
  registerAlias(pi, planTaskTool, "gsd_task_plan", "gsd_plan_task");
683
561
  // ─── gsd_task_complete (gsd_complete_task alias) ────────────────────────
684
562
  const taskCompleteExecute = async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
685
- const dbAvailable = await ensureDbOpen();
686
- if (!dbAvailable) {
687
- return {
688
- content: [{ type: "text", text: "Error: GSD database is not available. Cannot complete task." }],
689
- details: { operation: "complete_task", error: "db_unavailable" },
690
- };
691
- }
692
- try {
693
- // Coerce string items to objects for verificationEvidence (#3541).
694
- const coerced = { ...params };
695
- coerced.verificationEvidence = (params.verificationEvidence ?? []).map((v) => typeof v === "string" ? { command: v, exitCode: -1, verdict: "unknown (coerced from string)", durationMs: 0 } : v);
696
- const { handleCompleteTask } = await import("../tools/complete-task.js");
697
- const result = await handleCompleteTask(coerced, process.cwd());
698
- if ("error" in result) {
699
- return {
700
- content: [{ type: "text", text: `Error completing task: ${result.error}` }],
701
- details: { operation: "complete_task", error: result.error },
702
- };
703
- }
704
- return {
705
- content: [{ type: "text", text: `Completed task ${result.taskId} (${result.sliceId}/${result.milestoneId})` }],
706
- details: {
707
- operation: "complete_task",
708
- taskId: result.taskId,
709
- sliceId: result.sliceId,
710
- milestoneId: result.milestoneId,
711
- summaryPath: result.summaryPath,
712
- },
713
- };
714
- }
715
- catch (err) {
716
- const msg = err instanceof Error ? err.message : String(err);
717
- logError("tool", `complete_task tool failed: ${msg}`, { tool: "gsd_task_complete", error: String(err) });
718
- return {
719
- content: [{ type: "text", text: `Error completing task: ${msg}` }],
720
- details: { operation: "complete_task", error: msg },
721
- };
722
- }
563
+ return executeTaskComplete(params, process.cwd());
723
564
  };
724
565
  const taskCompleteTool = {
725
566
  name: "gsd_task_complete",
@@ -764,90 +605,7 @@ export function registerDbTools(pi) {
764
605
  registerAlias(pi, taskCompleteTool, "gsd_complete_task", "gsd_task_complete");
765
606
  // ─── gsd_slice_complete (gsd_complete_slice alias) ─────────────────────
766
607
  const sliceCompleteExecute = async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
767
- const dbAvailable = await ensureDbOpen();
768
- if (!dbAvailable) {
769
- return {
770
- content: [{ type: "text", text: "Error: GSD database is not available. Cannot complete slice." }],
771
- details: { operation: "complete_slice", error: "db_unavailable" },
772
- };
773
- }
774
- try {
775
- // Coerce string items to objects for fields where LLMs sometimes pass
776
- // plain strings instead of the expected { key, value } shape (#3541).
777
- // Parses "key — value" or "key - value" format when possible.
778
- const splitPair = (s) => {
779
- const m = s.match(/^(.+?)\s*(?:—|-)\s+(.+)$/);
780
- return m ? [m[1].trim(), m[2].trim()] : [s.trim(), ""];
781
- };
782
- const coerced = { ...params };
783
- // Coerce simple string-array fields: LLMs sometimes pass a plain string
784
- // instead of a single-element array (#3585).
785
- const wrapArray = (v) => v == null ? [] : Array.isArray(v) ? v : [v];
786
- coerced.provides = wrapArray(params.provides);
787
- coerced.keyFiles = wrapArray(params.keyFiles);
788
- coerced.keyDecisions = wrapArray(params.keyDecisions);
789
- coerced.patternsEstablished = wrapArray(params.patternsEstablished);
790
- coerced.observabilitySurfaces = wrapArray(params.observabilitySurfaces);
791
- coerced.requirementsSurfaced = wrapArray(params.requirementsSurfaced);
792
- coerced.drillDownPaths = wrapArray(params.drillDownPaths);
793
- coerced.affects = wrapArray(params.affects);
794
- coerced.filesModified = wrapArray(params.filesModified).map((f) => {
795
- if (typeof f !== "string")
796
- return f;
797
- const [path, description] = splitPair(f);
798
- return { path, description };
799
- });
800
- coerced.requires = wrapArray(params.requires).map((r) => {
801
- if (typeof r !== "string")
802
- return r;
803
- const [slice, provides] = splitPair(r);
804
- return { slice, provides };
805
- });
806
- coerced.requirementsAdvanced = wrapArray(params.requirementsAdvanced).map((r) => {
807
- if (typeof r !== "string")
808
- return r;
809
- const [id, how] = splitPair(r);
810
- return { id, how };
811
- });
812
- coerced.requirementsValidated = wrapArray(params.requirementsValidated).map((r) => {
813
- if (typeof r !== "string")
814
- return r;
815
- const [id, proof] = splitPair(r);
816
- return { id, proof };
817
- });
818
- coerced.requirementsInvalidated = wrapArray(params.requirementsInvalidated).map((r) => {
819
- if (typeof r !== "string")
820
- return r;
821
- const [id, what] = splitPair(r);
822
- return { id, what };
823
- });
824
- const { handleCompleteSlice } = await import("../tools/complete-slice.js");
825
- const result = await handleCompleteSlice(coerced, process.cwd());
826
- if ("error" in result) {
827
- return {
828
- content: [{ type: "text", text: `Error completing slice: ${result.error}` }],
829
- details: { operation: "complete_slice", error: result.error },
830
- };
831
- }
832
- return {
833
- content: [{ type: "text", text: `Completed slice ${result.sliceId} (${result.milestoneId})` }],
834
- details: {
835
- operation: "complete_slice",
836
- sliceId: result.sliceId,
837
- milestoneId: result.milestoneId,
838
- summaryPath: result.summaryPath,
839
- uatPath: result.uatPath,
840
- },
841
- };
842
- }
843
- catch (err) {
844
- const msg = err instanceof Error ? err.message : String(err);
845
- logError("tool", `complete_slice tool failed: ${msg}`, { tool: "gsd_slice_complete", error: String(err) });
846
- return {
847
- content: [{ type: "text", text: `Error completing slice: ${msg}` }],
848
- details: { operation: "complete_slice", error: msg },
849
- };
850
- }
608
+ return executeSliceComplete(params, process.cwd());
851
609
  };
852
610
  const sliceCompleteTool = {
853
611
  name: "gsd_slice_complete",
@@ -1004,42 +762,7 @@ export function registerDbTools(pi) {
1004
762
  });
1005
763
  // ─── gsd_complete_milestone ────────────────────────────────────────────
1006
764
  const milestoneCompleteExecute = async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
1007
- const dbAvailable = await ensureDbOpen();
1008
- if (!dbAvailable) {
1009
- return {
1010
- content: [{ type: "text", text: "Error: GSD database is not available. Cannot complete milestone." }],
1011
- details: { operation: "complete_milestone", error: "db_unavailable" },
1012
- };
1013
- }
1014
- try {
1015
- // ── Input sanitization: normalize markdown parameters (#3013) ──────
1016
- const { sanitizeCompleteMilestoneParams } = await import("./sanitize-complete-milestone.js");
1017
- const sanitized = sanitizeCompleteMilestoneParams(params);
1018
- const { handleCompleteMilestone } = await import("../tools/complete-milestone.js");
1019
- const result = await handleCompleteMilestone(sanitized, process.cwd());
1020
- if ("error" in result) {
1021
- return {
1022
- content: [{ type: "text", text: `Error completing milestone: ${result.error}` }],
1023
- details: { operation: "complete_milestone", error: result.error },
1024
- };
1025
- }
1026
- return {
1027
- content: [{ type: "text", text: `Completed milestone ${result.milestoneId}. Summary written to ${result.summaryPath}` }],
1028
- details: {
1029
- operation: "complete_milestone",
1030
- milestoneId: result.milestoneId,
1031
- summaryPath: result.summaryPath,
1032
- },
1033
- };
1034
- }
1035
- catch (err) {
1036
- const msg = err instanceof Error ? err.message : String(err);
1037
- logError("tool", `complete_milestone tool failed: ${msg}`, { tool: "gsd_complete_milestone", error: String(err) });
1038
- return {
1039
- content: [{ type: "text", text: `Error completing milestone: ${msg}` }],
1040
- details: { operation: "complete_milestone", error: msg },
1041
- };
1042
- }
765
+ return executeCompleteMilestone(params, process.cwd());
1043
766
  };
1044
767
  const milestoneCompleteTool = {
1045
768
  name: "gsd_complete_milestone",
@@ -1076,40 +799,7 @@ export function registerDbTools(pi) {
1076
799
  registerAlias(pi, milestoneCompleteTool, "gsd_milestone_complete", "gsd_complete_milestone");
1077
800
  // ─── gsd_validate_milestone (gsd_milestone_validate alias) ─────────────
1078
801
  const milestoneValidateExecute = async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
1079
- const dbAvailable = await ensureDbOpen();
1080
- if (!dbAvailable) {
1081
- return {
1082
- content: [{ type: "text", text: "Error: GSD database is not available. Cannot validate milestone." }],
1083
- details: { operation: "validate_milestone", error: "db_unavailable" },
1084
- };
1085
- }
1086
- try {
1087
- const { handleValidateMilestone } = await import("../tools/validate-milestone.js");
1088
- const result = await handleValidateMilestone(params, process.cwd());
1089
- if ("error" in result) {
1090
- return {
1091
- content: [{ type: "text", text: `Error validating milestone: ${result.error}` }],
1092
- details: { operation: "validate_milestone", error: result.error },
1093
- };
1094
- }
1095
- return {
1096
- content: [{ type: "text", text: `Validated milestone ${result.milestoneId} — verdict: ${result.verdict}. Written to ${result.validationPath}` }],
1097
- details: {
1098
- operation: "validate_milestone",
1099
- milestoneId: result.milestoneId,
1100
- verdict: result.verdict,
1101
- validationPath: result.validationPath,
1102
- },
1103
- };
1104
- }
1105
- catch (err) {
1106
- const msg = err instanceof Error ? err.message : String(err);
1107
- logError("tool", `validate_milestone tool failed: ${msg}`, { tool: "gsd_validate_milestone", error: String(err) });
1108
- return {
1109
- content: [{ type: "text", text: `Error validating milestone: ${msg}` }],
1110
- details: { operation: "validate_milestone", error: msg },
1111
- };
1112
- }
802
+ return executeValidateMilestone(params, process.cwd());
1113
803
  };
1114
804
  const milestoneValidateTool = {
1115
805
  name: "gsd_validate_milestone",
@@ -1141,41 +831,7 @@ export function registerDbTools(pi) {
1141
831
  registerAlias(pi, milestoneValidateTool, "gsd_milestone_validate", "gsd_validate_milestone");
1142
832
  // ─── gsd_replan_slice (gsd_slice_replan alias) ─────────────────────────
1143
833
  const replanSliceExecute = async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
1144
- const dbAvailable = await ensureDbOpen();
1145
- if (!dbAvailable) {
1146
- return {
1147
- content: [{ type: "text", text: "Error: GSD database is not available. Cannot replan slice." }],
1148
- details: { operation: "replan_slice", error: "db_unavailable" },
1149
- };
1150
- }
1151
- try {
1152
- const { handleReplanSlice } = await import("../tools/replan-slice.js");
1153
- const result = await handleReplanSlice(params, process.cwd());
1154
- if ("error" in result) {
1155
- return {
1156
- content: [{ type: "text", text: `Error replanning slice: ${result.error}` }],
1157
- details: { operation: "replan_slice", error: result.error },
1158
- };
1159
- }
1160
- return {
1161
- content: [{ type: "text", text: `Replanned slice ${result.sliceId} (${result.milestoneId})` }],
1162
- details: {
1163
- operation: "replan_slice",
1164
- milestoneId: result.milestoneId,
1165
- sliceId: result.sliceId,
1166
- replanPath: result.replanPath,
1167
- planPath: result.planPath,
1168
- },
1169
- };
1170
- }
1171
- catch (err) {
1172
- const msg = err instanceof Error ? err.message : String(err);
1173
- logError("tool", `replan_slice tool failed: ${msg}`, { tool: "gsd_replan_slice", error: String(err) });
1174
- return {
1175
- content: [{ type: "text", text: `Error replanning slice: ${msg}` }],
1176
- details: { operation: "replan_slice", error: msg },
1177
- };
1178
- }
834
+ return executeReplanSlice(params, process.cwd());
1179
835
  };
1180
836
  const replanSliceTool = {
1181
837
  name: "gsd_replan_slice",
@@ -1214,41 +870,7 @@ export function registerDbTools(pi) {
1214
870
  registerAlias(pi, replanSliceTool, "gsd_slice_replan", "gsd_replan_slice");
1215
871
  // ─── gsd_reassess_roadmap (gsd_roadmap_reassess alias) ─────────────────
1216
872
  const reassessRoadmapExecute = async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
1217
- const dbAvailable = await ensureDbOpen();
1218
- if (!dbAvailable) {
1219
- return {
1220
- content: [{ type: "text", text: "Error: GSD database is not available. Cannot reassess roadmap." }],
1221
- details: { operation: "reassess_roadmap", error: "db_unavailable" },
1222
- };
1223
- }
1224
- try {
1225
- const { handleReassessRoadmap } = await import("../tools/reassess-roadmap.js");
1226
- const result = await handleReassessRoadmap(params, process.cwd());
1227
- if ("error" in result) {
1228
- return {
1229
- content: [{ type: "text", text: `Error reassessing roadmap: ${result.error}` }],
1230
- details: { operation: "reassess_roadmap", error: result.error },
1231
- };
1232
- }
1233
- return {
1234
- content: [{ type: "text", text: `Reassessed roadmap for milestone ${result.milestoneId} after ${result.completedSliceId}` }],
1235
- details: {
1236
- operation: "reassess_roadmap",
1237
- milestoneId: result.milestoneId,
1238
- completedSliceId: result.completedSliceId,
1239
- assessmentPath: result.assessmentPath,
1240
- roadmapPath: result.roadmapPath,
1241
- },
1242
- };
1243
- }
1244
- catch (err) {
1245
- const msg = err instanceof Error ? err.message : String(err);
1246
- logError("tool", `reassess_roadmap tool failed: ${msg}`, { tool: "gsd_reassess_roadmap", error: String(err) });
1247
- return {
1248
- content: [{ type: "text", text: `Error reassessing roadmap: ${msg}` }],
1249
- details: { operation: "reassess_roadmap", error: msg },
1250
- };
1251
- }
873
+ return executeReassessRoadmap(params, process.cwd());
1252
874
  };
1253
875
  const reassessRoadmapTool = {
1254
876
  name: "gsd_reassess_roadmap",
@@ -1292,53 +914,7 @@ export function registerDbTools(pi) {
1292
914
  registerAlias(pi, reassessRoadmapTool, "gsd_roadmap_reassess", "gsd_reassess_roadmap");
1293
915
  // ─── gsd_save_gate_result ──────────────────────────────────────────────
1294
916
  const saveGateResultExecute = async (_toolCallId, params, _signal, _onUpdate, _ctx) => {
1295
- const dbAvailable = await ensureDbOpen();
1296
- if (!dbAvailable) {
1297
- return {
1298
- content: [{ type: "text", text: "Error: GSD database is not available." }],
1299
- details: { operation: "save_gate_result", error: "db_unavailable" },
1300
- };
1301
- }
1302
- const validGates = ["Q3", "Q4", "Q5", "Q6", "Q7", "Q8"];
1303
- if (!validGates.includes(params.gateId)) {
1304
- return {
1305
- content: [{ type: "text", text: `Error: Invalid gateId "${params.gateId}". Must be one of: ${validGates.join(", ")}` }],
1306
- details: { operation: "save_gate_result", error: "invalid_gate_id" },
1307
- };
1308
- }
1309
- const validVerdicts = ["pass", "flag", "omitted"];
1310
- if (!validVerdicts.includes(params.verdict)) {
1311
- return {
1312
- content: [{ type: "text", text: `Error: Invalid verdict "${params.verdict}". Must be one of: ${validVerdicts.join(", ")}` }],
1313
- details: { operation: "save_gate_result", error: "invalid_verdict" },
1314
- };
1315
- }
1316
- try {
1317
- const { saveGateResult } = await import("../gsd-db.js");
1318
- const { invalidateStateCache } = await import("../state.js");
1319
- saveGateResult({
1320
- milestoneId: params.milestoneId,
1321
- sliceId: params.sliceId,
1322
- gateId: params.gateId,
1323
- taskId: params.taskId ?? "",
1324
- verdict: params.verdict,
1325
- rationale: params.rationale,
1326
- findings: params.findings ?? "",
1327
- });
1328
- invalidateStateCache();
1329
- return {
1330
- content: [{ type: "text", text: `Gate ${params.gateId} result saved: verdict=${params.verdict}` }],
1331
- details: { operation: "save_gate_result", gateId: params.gateId, verdict: params.verdict },
1332
- };
1333
- }
1334
- catch (err) {
1335
- const msg = err instanceof Error ? err.message : String(err);
1336
- logError("tool", `gsd_save_gate_result failed: ${msg}`, { tool: "gsd_save_gate_result", error: String(err) });
1337
- return {
1338
- content: [{ type: "text", text: `Error saving gate result: ${msg}` }],
1339
- details: { operation: "save_gate_result", error: msg },
1340
- };
1341
- }
917
+ return executeSaveGateResult(params, process.cwd());
1342
918
  };
1343
919
  const saveGateResultTool = {
1344
920
  name: "gsd_save_gate_result",
@@ -64,12 +64,9 @@ export function resolveProjectRootDbPath(basePath) {
64
64
  }
65
65
  return join(basePath, ".gsd", "gsd.db");
66
66
  }
67
- export async function ensureDbOpen() {
67
+ export async function ensureDbOpen(basePath = process.cwd()) {
68
68
  try {
69
69
  const db = await import("../gsd-db.js");
70
- if (db.isDbAvailable())
71
- return true;
72
- const basePath = process.cwd();
73
70
  const dbPath = resolveProjectRootDbPath(basePath);
74
71
  const gsdDir = join(basePath, ".gsd");
75
72
  // Derive the project root from the DB path (strip .gsd/gsd.db)