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.
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +3 -0
- package/dist/resources/extensions/gsd/auto/phases.js +17 -0
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +12 -0
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +11 -435
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +1 -4
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +7 -64
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +88 -8
- package/dist/resources/extensions/gsd/commands/handlers/core.js +38 -24
- package/dist/resources/extensions/gsd/commands/index.js +8 -1
- package/dist/resources/extensions/gsd/guided-flow.js +16 -0
- package/dist/resources/extensions/gsd/init-wizard.js +34 -0
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +508 -0
- package/dist/resources/extensions/gsd/workflow-logger.js +18 -3
- package/dist/resources/extensions/gsd/workflow-mcp.js +190 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/mcp-server/README.md +38 -0
- package/packages/mcp-server/src/server.ts +6 -2
- package/packages/mcp-server/src/workflow-tools.test.ts +976 -0
- package/packages/mcp-server/src/workflow-tools.ts +986 -0
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +3 -0
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +121 -0
- package/src/resources/extensions/gsd/auto/phases.ts +25 -0
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +20 -0
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +22 -435
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +1 -5
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +7 -72
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +122 -6
- package/src/resources/extensions/gsd/commands/handlers/core.ts +52 -25
- package/src/resources/extensions/gsd/commands/index.ts +7 -1
- package/src/resources/extensions/gsd/guided-flow.ts +24 -0
- package/src/resources/extensions/gsd/init-wizard.ts +34 -0
- package/src/resources/extensions/gsd/tests/core-overlay-fallback.test.ts +101 -0
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/init-bootstrap-completeness.test.ts +121 -0
- package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +301 -0
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +625 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +629 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +19 -3
- package/src/resources/extensions/gsd/workflow-mcp.ts +233 -0
- /package/dist/web/standalone/.next/static/{PHqEommYRR8CRn3i84CGM → xR6qurkuYSvyjBjRyJLxG}/_buildManifest.js +0 -0
- /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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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)
|