gsd-pi 2.67.0-dev.1cd1e0f → 2.67.0-dev.2367d7e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +155 -70
- package/dist/resources/extensions/gsd/auto/phases.js +17 -0
- package/dist/resources/extensions/gsd/auto/session.js +10 -0
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +12 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +1 -1
- package/dist/resources/extensions/gsd/auto-start.js +16 -30
- package/dist/resources/extensions/gsd/auto-worktree.js +62 -15
- package/dist/resources/extensions/gsd/auto.js +121 -59
- 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/system-context.js +7 -2
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +88 -8
- package/dist/resources/extensions/gsd/commands/catalog.js +2 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +39 -25
- package/dist/resources/extensions/gsd/commands/index.js +8 -1
- package/dist/resources/extensions/gsd/commands-mcp-status.js +43 -7
- package/dist/resources/extensions/gsd/doctor-git-checks.js +4 -4
- package/dist/resources/extensions/gsd/doctor-proactive.js +3 -3
- package/dist/resources/extensions/gsd/doctor.js +8 -4
- package/dist/resources/extensions/gsd/gsd-db.js +11 -0
- package/dist/resources/extensions/gsd/guided-flow.js +56 -31
- package/dist/resources/extensions/gsd/init-wizard.js +37 -0
- package/dist/resources/extensions/gsd/interrupted-session.js +146 -0
- package/dist/resources/extensions/gsd/mcp-project-config.js +83 -0
- package/dist/resources/extensions/gsd/state.js +7 -2
- 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 +261 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- 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/page_client-reference-manifest.js +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 +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
- 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/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-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/dist/web/standalone/.next/static/chunks/2826.821e01b07d92e948.js +9 -0
- package/dist/web/standalone/.next/static/chunks/app/{page-0c485498795110d6.js → page-f1e30ab6bb269149.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{webpack-b49b09f97429b5d0.js → webpack-6e4d7e9a4f57bed4.js} +1 -1
- package/package.json +4 -2
- package/packages/mcp-server/README.md +38 -0
- package/packages/mcp-server/dist/cli.d.ts +9 -0
- package/packages/mcp-server/dist/cli.d.ts.map +1 -0
- package/packages/mcp-server/dist/cli.js +58 -0
- package/packages/mcp-server/dist/cli.js.map +1 -0
- package/packages/mcp-server/dist/index.d.ts +20 -0
- package/packages/mcp-server/dist/index.d.ts.map +1 -0
- package/packages/mcp-server/dist/index.js +14 -0
- package/packages/mcp-server/dist/index.js.map +1 -0
- package/packages/mcp-server/dist/readers/captures.d.ts +25 -0
- package/packages/mcp-server/dist/readers/captures.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/captures.js +67 -0
- package/packages/mcp-server/dist/readers/captures.js.map +1 -0
- package/packages/mcp-server/dist/readers/doctor-lite.d.ts +20 -0
- package/packages/mcp-server/dist/readers/doctor-lite.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/doctor-lite.js +173 -0
- package/packages/mcp-server/dist/readers/doctor-lite.js.map +1 -0
- package/packages/mcp-server/dist/readers/index.d.ts +14 -0
- package/packages/mcp-server/dist/readers/index.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/index.js +10 -0
- package/packages/mcp-server/dist/readers/index.js.map +1 -0
- package/packages/mcp-server/dist/readers/knowledge.d.ts +18 -0
- package/packages/mcp-server/dist/readers/knowledge.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/knowledge.js +82 -0
- package/packages/mcp-server/dist/readers/knowledge.js.map +1 -0
- package/packages/mcp-server/dist/readers/metrics.d.ts +32 -0
- package/packages/mcp-server/dist/readers/metrics.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/metrics.js +74 -0
- package/packages/mcp-server/dist/readers/metrics.js.map +1 -0
- package/packages/mcp-server/dist/readers/paths.d.ts +42 -0
- package/packages/mcp-server/dist/readers/paths.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/paths.js +199 -0
- package/packages/mcp-server/dist/readers/paths.js.map +1 -0
- package/packages/mcp-server/dist/readers/roadmap.d.ts +26 -0
- package/packages/mcp-server/dist/readers/roadmap.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/roadmap.js +194 -0
- package/packages/mcp-server/dist/readers/roadmap.js.map +1 -0
- package/packages/mcp-server/dist/readers/state.d.ts +43 -0
- package/packages/mcp-server/dist/readers/state.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/state.js +184 -0
- package/packages/mcp-server/dist/readers/state.js.map +1 -0
- package/packages/mcp-server/dist/server.d.ts +28 -0
- package/packages/mcp-server/dist/server.d.ts.map +1 -0
- package/packages/mcp-server/dist/server.js +319 -0
- package/packages/mcp-server/dist/server.js.map +1 -0
- package/packages/mcp-server/dist/session-manager.d.ts +54 -0
- package/packages/mcp-server/dist/session-manager.d.ts.map +1 -0
- package/packages/mcp-server/dist/session-manager.js +284 -0
- package/packages/mcp-server/dist/session-manager.js.map +1 -0
- package/packages/mcp-server/dist/types.d.ts +61 -0
- package/packages/mcp-server/dist/types.d.ts.map +1 -0
- package/packages/mcp-server/dist/types.js +11 -0
- package/packages/mcp-server/dist/types.js.map +1 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts +9 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -0
- package/packages/mcp-server/dist/workflow-tools.js +532 -0
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -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 +997 -0
- package/packages/mcp-server/tsconfig.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +14 -6
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.test.ts +53 -0
- package/packages/pi-agent-core/src/agent-loop.ts +20 -6
- package/packages/pi-coding-agent/dist/core/contextual-tips.d.ts +43 -0
- package/packages/pi-coding-agent/dist/core/contextual-tips.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/contextual-tips.js +208 -0
- package/packages/pi-coding-agent/dist/core/contextual-tips.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/contextual-tips.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/contextual-tips.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/contextual-tips.test.js +227 -0
- package/packages/pi-coding-agent/dist/core/contextual-tips.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/index.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/index.js +1 -0
- package/packages/pi-coding-agent/dist/core/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +28 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +17 -12
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +19 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +14 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +15 -12
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/contextual-tips.test.ts +259 -0
- package/packages/pi-coding-agent/src/core/contextual-tips.ts +232 -0
- package/packages/pi-coding-agent/src/core/index.ts +2 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +54 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +18 -12
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +21 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +19 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +19 -15
- package/packages/rpc-client/dist/index.d.ts +10 -0
- package/packages/rpc-client/dist/index.d.ts.map +1 -0
- package/packages/rpc-client/dist/index.js +9 -0
- package/packages/rpc-client/dist/index.js.map +1 -0
- package/packages/rpc-client/dist/jsonl.d.ts +17 -0
- package/packages/rpc-client/dist/jsonl.d.ts.map +1 -0
- package/packages/rpc-client/dist/jsonl.js +54 -0
- package/packages/rpc-client/dist/jsonl.js.map +1 -0
- package/packages/rpc-client/dist/rpc-client.d.ts +259 -0
- package/packages/rpc-client/dist/rpc-client.d.ts.map +1 -0
- package/packages/rpc-client/dist/rpc-client.js +541 -0
- package/packages/rpc-client/dist/rpc-client.js.map +1 -0
- package/packages/rpc-client/dist/rpc-client.test.d.ts +2 -0
- package/packages/rpc-client/dist/rpc-client.test.d.ts.map +1 -0
- package/packages/rpc-client/dist/rpc-client.test.js +477 -0
- package/packages/rpc-client/dist/rpc-client.test.js.map +1 -0
- package/packages/rpc-client/dist/rpc-types.d.ts +566 -0
- package/packages/rpc-client/dist/rpc-types.d.ts.map +1 -0
- package/packages/rpc-client/dist/rpc-types.js +12 -0
- package/packages/rpc-client/dist/rpc-types.js.map +1 -0
- package/scripts/ensure-workspace-builds.cjs +2 -0
- package/scripts/link-workspace-packages.cjs +21 -14
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +193 -93
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +173 -79
- package/src/resources/extensions/gsd/auto/phases.ts +25 -0
- package/src/resources/extensions/gsd/auto/session.ts +10 -0
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +20 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +1 -1
- package/src/resources/extensions/gsd/auto-start.ts +23 -55
- package/src/resources/extensions/gsd/auto-worktree.ts +59 -15
- package/src/resources/extensions/gsd/auto.ts +133 -64
- 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/system-context.ts +8 -2
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +122 -6
- package/src/resources/extensions/gsd/commands/catalog.ts +2 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +53 -26
- package/src/resources/extensions/gsd/commands/index.ts +7 -1
- package/src/resources/extensions/gsd/commands-mcp-status.ts +53 -7
- package/src/resources/extensions/gsd/doctor-git-checks.ts +4 -4
- package/src/resources/extensions/gsd/doctor-proactive.ts +3 -3
- package/src/resources/extensions/gsd/doctor.ts +9 -5
- package/src/resources/extensions/gsd/gsd-db.ts +12 -0
- package/src/resources/extensions/gsd/guided-flow.ts +66 -36
- package/src/resources/extensions/gsd/init-wizard.ts +40 -0
- package/src/resources/extensions/gsd/interrupted-session.ts +224 -0
- package/src/resources/extensions/gsd/mcp-project-config.ts +128 -0
- package/src/resources/extensions/gsd/state.ts +7 -1
- package/src/resources/extensions/gsd/tests/auto-project-root-env.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +668 -2
- package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +14 -4
- package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/core-overlay-fallback.test.ts +101 -0
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +380 -2
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/integration/doctor-fixlevel.test.ts +52 -1
- package/src/resources/extensions/gsd/tests/integration/doctor-git.test.ts +2 -9
- package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +0 -33
- package/src/resources/extensions/gsd/tests/integration/merge-cwd-restore.test.ts +169 -0
- package/src/resources/extensions/gsd/tests/interrupted-session-auto.test.ts +146 -0
- package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +136 -0
- package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/verification-operational-gate.test.ts +11 -0
- package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +500 -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 +320 -0
- package/dist/web/standalone/.next/static/chunks/6502.b804e48b7919f55e.js +0 -9
- package/packages/pi-coding-agent/dist/modes/interactive/provider-auth-setup.d.ts +0 -13
- package/packages/pi-coding-agent/dist/modes/interactive/provider-auth-setup.d.ts.map +0 -1
- package/packages/pi-coding-agent/dist/modes/interactive/provider-auth-setup.js +0 -27
- package/packages/pi-coding-agent/dist/modes/interactive/provider-auth-setup.js.map +0 -1
- package/packages/pi-coding-agent/src/modes/interactive/provider-auth-setup.ts +0 -40
- /package/dist/web/standalone/.next/static/{PHqEommYRR8CRn3i84CGM → WMDT_0C0XDkBKtsAI_AX4}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{PHqEommYRR8CRn3i84CGM → WMDT_0C0XDkBKtsAI_AX4}/_ssgManifest.js +0 -0
|
@@ -16,7 +16,12 @@ import { buildSkillActivationBlock } from "./auto-prompts.js";
|
|
|
16
16
|
import { deriveState } from "./state.js";
|
|
17
17
|
import { invalidateAllCaches } from "./cache.js";
|
|
18
18
|
import { startAuto } from "./auto.js";
|
|
19
|
-
import {
|
|
19
|
+
import { clearLock } from "./crash-recovery.js";
|
|
20
|
+
import {
|
|
21
|
+
assessInterruptedSession,
|
|
22
|
+
formatInterruptedSessionRunningMessage,
|
|
23
|
+
formatInterruptedSessionSummary,
|
|
24
|
+
} from "./interrupted-session.js";
|
|
20
25
|
import { listUnitRuntimeRecords, clearUnitRuntimeRecord } from "./unit-runtime.js";
|
|
21
26
|
import { resolveExpectedArtifactPath } from "./auto.js";
|
|
22
27
|
import {
|
|
@@ -40,6 +45,10 @@ import { findMilestoneIds, nextMilestoneId, reserveMilestoneId, getReservedMiles
|
|
|
40
45
|
import { parkMilestone, discardMilestone } from "./milestone-actions.js";
|
|
41
46
|
import { selectAndApplyModel } from "./auto-model-selection.js";
|
|
42
47
|
import { DISCUSS_TOOLS_ALLOWLIST } from "./constants.js";
|
|
48
|
+
import {
|
|
49
|
+
getWorkflowTransportSupportError,
|
|
50
|
+
getRequiredWorkflowToolsForGuidedUnit,
|
|
51
|
+
} from "./workflow-mcp.js";
|
|
43
52
|
import {
|
|
44
53
|
runPreparation,
|
|
45
54
|
formatCodebaseBrief,
|
|
@@ -211,17 +220,9 @@ export function checkAutoStartAfterDiscuss(): boolean {
|
|
|
211
220
|
|
|
212
221
|
// Gate 4: Discussion manifest process verification (multi-milestone only)
|
|
213
222
|
// The LLM writes DISCUSSION-MANIFEST.json after each Phase 3 gate decision.
|
|
214
|
-
//
|
|
215
|
-
//
|
|
223
|
+
// When it exists, validate it before auto-starting. Project history alone is
|
|
224
|
+
// not a reliable signal for the current discussion mode.
|
|
216
225
|
const manifestPath = join(gsdRoot(basePath), "DISCUSSION-MANIFEST.json");
|
|
217
|
-
const requiresManifest = projectIds.length > 1 || findMilestoneIds(basePath).length > 1;
|
|
218
|
-
if (requiresManifest && !existsSync(manifestPath)) {
|
|
219
|
-
ctx.ui.notify(
|
|
220
|
-
"Multi-milestone discussion manifest is missing. Auto-start will remain paused until the manifest is written.",
|
|
221
|
-
"warning",
|
|
222
|
-
);
|
|
223
|
-
return false;
|
|
224
|
-
}
|
|
225
226
|
if (existsSync(manifestPath)) {
|
|
226
227
|
try {
|
|
227
228
|
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
@@ -318,6 +319,26 @@ async function dispatchWorkflow(
|
|
|
318
319
|
routing: result.routing,
|
|
319
320
|
});
|
|
320
321
|
}
|
|
322
|
+
|
|
323
|
+
const compatibilityError = getWorkflowTransportSupportError(
|
|
324
|
+
result.appliedModel?.provider ?? ctx.model?.provider,
|
|
325
|
+
getRequiredWorkflowToolsForGuidedUnit(unitType),
|
|
326
|
+
{
|
|
327
|
+
projectRoot: process.cwd(),
|
|
328
|
+
surface: "guided flow",
|
|
329
|
+
unitType,
|
|
330
|
+
authMode: result.appliedModel?.provider
|
|
331
|
+
? ctx.modelRegistry.getProviderAuthMode(result.appliedModel.provider)
|
|
332
|
+
: ctx.model?.provider
|
|
333
|
+
? ctx.modelRegistry.getProviderAuthMode(ctx.model.provider)
|
|
334
|
+
: undefined,
|
|
335
|
+
baseUrl: result.appliedModel?.baseUrl ?? ctx.model?.baseUrl,
|
|
336
|
+
},
|
|
337
|
+
);
|
|
338
|
+
if (compatibilityError) {
|
|
339
|
+
ctx.ui.notify(compatibilityError, "error");
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
321
342
|
}
|
|
322
343
|
|
|
323
344
|
// Scope tools for discuss flows (#2949).
|
|
@@ -1298,36 +1319,45 @@ export async function showSmartEntry(
|
|
|
1298
1319
|
// ── Self-heal stale runtime records from crashed auto-mode sessions ──
|
|
1299
1320
|
selfHealRuntimeRecords(basePath, ctx);
|
|
1300
1321
|
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
clearLock(basePath);
|
|
1322
|
+
const interrupted = await assessInterruptedSession(basePath);
|
|
1323
|
+
if (interrupted.classification === "running") {
|
|
1324
|
+
ctx.ui.notify(formatInterruptedSessionRunningMessage(interrupted), "error");
|
|
1325
|
+
return;
|
|
1326
|
+
}
|
|
1307
1327
|
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
if (!isBootstrapCrash) {
|
|
1316
|
-
const resume = await showNextAction(ctx, {
|
|
1317
|
-
title: "GSD — Interrupted Session Detected",
|
|
1318
|
-
summary: [formatCrashInfo(crashLock)],
|
|
1319
|
-
actions: [
|
|
1320
|
-
{ id: "resume", label: "Resume with /gsd auto", description: "Pick up where it left off", recommended: true },
|
|
1321
|
-
{ id: "continue", label: "Continue manually", description: "Open the wizard as normal" },
|
|
1322
|
-
],
|
|
1323
|
-
});
|
|
1324
|
-
if (resume === "resume") {
|
|
1325
|
-
await startAuto(ctx, pi, basePath, false);
|
|
1326
|
-
return;
|
|
1328
|
+
if (interrupted.classification === "stale") {
|
|
1329
|
+
clearLock(basePath);
|
|
1330
|
+
if (interrupted.pausedSession) {
|
|
1331
|
+
try {
|
|
1332
|
+
unlinkSync(join(gsdRoot(basePath), "runtime", "paused-session.json"));
|
|
1333
|
+
} catch (e) {
|
|
1334
|
+
logWarning("guided", `stale pause file cleanup failed: ${(e as Error).message}`, { file: "guided-flow.ts" });
|
|
1327
1335
|
}
|
|
1328
1336
|
}
|
|
1337
|
+
} else if (interrupted.classification === "recoverable") {
|
|
1338
|
+
if (interrupted.lock) clearLock(basePath);
|
|
1339
|
+
const resumeLabel = interrupted.pausedSession?.stepMode
|
|
1340
|
+
? "Resume with /gsd next"
|
|
1341
|
+
: "Resume with /gsd auto";
|
|
1342
|
+
const resume = await showNextAction(ctx, {
|
|
1343
|
+
title: "GSD — Interrupted Session Detected",
|
|
1344
|
+
summary: formatInterruptedSessionSummary(interrupted),
|
|
1345
|
+
actions: [
|
|
1346
|
+
{ id: "resume", label: resumeLabel, description: "Pick up where it left off", recommended: true },
|
|
1347
|
+
{ id: "continue", label: "Continue manually", description: "Open the wizard as normal" },
|
|
1348
|
+
],
|
|
1349
|
+
});
|
|
1350
|
+
if (resume === "resume") {
|
|
1351
|
+
await startAuto(ctx, pi, basePath, false, {
|
|
1352
|
+
interrupted,
|
|
1353
|
+
step: interrupted.pausedSession?.stepMode ?? false,
|
|
1354
|
+
});
|
|
1355
|
+
return;
|
|
1356
|
+
}
|
|
1329
1357
|
}
|
|
1330
1358
|
|
|
1359
|
+
// Always derive from the project root — the assessment may have derived
|
|
1360
|
+
// state from a worktree path that was cleaned up in the stale branch above.
|
|
1331
1361
|
const state = await deriveState(basePath);
|
|
1332
1362
|
|
|
1333
1363
|
// Rebuild STATE.md from derived state before any dispatch (#3475).
|
|
@@ -235,6 +235,16 @@ export async function showProjectInit(
|
|
|
235
235
|
// ── Step 9: Bootstrap .gsd/ ────────────────────────────────────────────────
|
|
236
236
|
bootstrapGsdDirectory(basePath, prefs, signals);
|
|
237
237
|
|
|
238
|
+
// Initialize SQLite database so GSD starts in full-capability mode (#3880).
|
|
239
|
+
// Without this, isDbAvailable() returns false and GSD enters degraded
|
|
240
|
+
// markdown-only mode until a tool handler happens to call ensureDbOpen().
|
|
241
|
+
try {
|
|
242
|
+
const { ensureDbOpen } = await import("./bootstrap/dynamic-tools.js");
|
|
243
|
+
await ensureDbOpen(basePath);
|
|
244
|
+
} catch {
|
|
245
|
+
// Non-fatal — DB creation failure should not block project init
|
|
246
|
+
}
|
|
247
|
+
|
|
238
248
|
// Ensure .gitignore
|
|
239
249
|
ensureGitignore(basePath);
|
|
240
250
|
untrackRuntimeFiles(basePath);
|
|
@@ -250,6 +260,35 @@ export async function showProjectInit(
|
|
|
250
260
|
// Non-fatal — codebase map generation failure should never block project init
|
|
251
261
|
}
|
|
252
262
|
|
|
263
|
+
// Write initial STATE.md so it exists before the first /gsd invocation.
|
|
264
|
+
// The explicit /gsd init path (ops.ts) returns without entering showSmartEntry(),
|
|
265
|
+
// which would otherwise generate STATE.md at guided-flow.ts:1358.
|
|
266
|
+
try {
|
|
267
|
+
const { deriveState } = await import("./state.js");
|
|
268
|
+
const { buildStateMarkdown } = await import("./doctor.js");
|
|
269
|
+
const { saveFile } = await import("./files.js");
|
|
270
|
+
const { resolveGsdRootFile } = await import("./paths.js");
|
|
271
|
+
const state = await deriveState(basePath);
|
|
272
|
+
await saveFile(resolveGsdRootFile(basePath, "STATE"), buildStateMarkdown(state));
|
|
273
|
+
} catch {
|
|
274
|
+
// Non-fatal — STATE.md will be regenerated on next /gsd invocation
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (ctx.model?.provider === "claude-code") {
|
|
278
|
+
try {
|
|
279
|
+
const { ensureProjectWorkflowMcpConfig } = await import("./mcp-project-config.js");
|
|
280
|
+
const result = ensureProjectWorkflowMcpConfig(basePath);
|
|
281
|
+
if (result.status !== "unchanged") {
|
|
282
|
+
ctx.ui.notify(`Claude Code MCP prepared at ${result.configPath}`, "info");
|
|
283
|
+
}
|
|
284
|
+
} catch (err) {
|
|
285
|
+
ctx.ui.notify(
|
|
286
|
+
`Claude Code MCP prep failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
287
|
+
"warning",
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
253
292
|
ctx.ui.notify("GSD initialized. Starting your first milestone...", "info");
|
|
254
293
|
|
|
255
294
|
return { completed: true, bootstrapped: true };
|
|
@@ -433,6 +472,7 @@ function bootstrapGsdDirectory(
|
|
|
433
472
|
|
|
434
473
|
const gsd = gsdRoot(basePath);
|
|
435
474
|
mkdirSync(join(gsd, "milestones"), { recursive: true });
|
|
475
|
+
mkdirSync(join(gsd, "runtime"), { recursive: true });
|
|
436
476
|
|
|
437
477
|
// Write PREFERENCES.md from wizard answers
|
|
438
478
|
const preferencesContent = buildPreferencesFile(prefs);
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import { verifyExpectedArtifact } from "./auto-recovery.js";
|
|
5
|
+
import {
|
|
6
|
+
formatCrashInfo,
|
|
7
|
+
isLockProcessAlive,
|
|
8
|
+
readCrashLock,
|
|
9
|
+
type LockData,
|
|
10
|
+
} from "./crash-recovery.js";
|
|
11
|
+
import { gsdRoot } from "./paths.js";
|
|
12
|
+
import {
|
|
13
|
+
synthesizeCrashRecovery,
|
|
14
|
+
type RecoveryBriefing,
|
|
15
|
+
} from "./session-forensics.js";
|
|
16
|
+
import { deriveState } from "./state.js";
|
|
17
|
+
import type { GSDState } from "./types.js";
|
|
18
|
+
|
|
19
|
+
export type InterruptedSessionClassification =
|
|
20
|
+
| "none"
|
|
21
|
+
| "running"
|
|
22
|
+
| "recoverable"
|
|
23
|
+
| "stale";
|
|
24
|
+
|
|
25
|
+
export interface PausedSessionMetadata {
|
|
26
|
+
milestoneId?: string;
|
|
27
|
+
worktreePath?: string | null;
|
|
28
|
+
originalBasePath?: string;
|
|
29
|
+
stepMode?: boolean;
|
|
30
|
+
pausedAt?: string;
|
|
31
|
+
sessionFile?: string | null;
|
|
32
|
+
unitType?: string;
|
|
33
|
+
unitId?: string;
|
|
34
|
+
activeEngineId?: string;
|
|
35
|
+
activeRunDir?: string | null;
|
|
36
|
+
autoStartTime?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface InterruptedSessionAssessment {
|
|
40
|
+
classification: InterruptedSessionClassification;
|
|
41
|
+
lock: LockData | null;
|
|
42
|
+
pausedSession: PausedSessionMetadata | null;
|
|
43
|
+
state: GSDState | null;
|
|
44
|
+
recovery: RecoveryBriefing | null;
|
|
45
|
+
recoveryPrompt: string | null;
|
|
46
|
+
recoveryToolCallCount: number;
|
|
47
|
+
artifactSatisfied: boolean;
|
|
48
|
+
hasResumableDiskState: boolean;
|
|
49
|
+
isBootstrapCrash: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function readPausedSessionMetadata(
|
|
53
|
+
basePath: string,
|
|
54
|
+
): PausedSessionMetadata | null {
|
|
55
|
+
const pausedPath = join(gsdRoot(basePath), "runtime", "paused-session.json");
|
|
56
|
+
if (!existsSync(pausedPath)) return null;
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
return JSON.parse(readFileSync(pausedPath, "utf-8")) as PausedSessionMetadata;
|
|
60
|
+
} catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function isBootstrapCrashLock(lock: LockData | null): boolean {
|
|
66
|
+
return !!(
|
|
67
|
+
lock &&
|
|
68
|
+
lock.unitType === "starting" &&
|
|
69
|
+
lock.unitId === "bootstrap"
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function hasResumableDerivedState(state: GSDState | null): boolean {
|
|
74
|
+
return !!(state?.activeMilestone && state.phase !== "complete");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function assessInterruptedSession(
|
|
78
|
+
basePath: string,
|
|
79
|
+
): Promise<InterruptedSessionAssessment> {
|
|
80
|
+
const pausedSession = readPausedSessionMetadata(basePath);
|
|
81
|
+
const worktreeExists = pausedSession?.worktreePath
|
|
82
|
+
? existsSync(pausedSession.worktreePath)
|
|
83
|
+
: false;
|
|
84
|
+
const assessmentBasePath = worktreeExists ? pausedSession!.worktreePath! : basePath;
|
|
85
|
+
const rawLock = readCrashLock(basePath);
|
|
86
|
+
const lock = rawLock && rawLock.pid !== process.pid ? rawLock : null;
|
|
87
|
+
|
|
88
|
+
if (!lock && !pausedSession) {
|
|
89
|
+
return {
|
|
90
|
+
classification: "none",
|
|
91
|
+
lock: null,
|
|
92
|
+
pausedSession: null,
|
|
93
|
+
state: null,
|
|
94
|
+
recovery: null,
|
|
95
|
+
recoveryPrompt: null,
|
|
96
|
+
recoveryToolCallCount: 0,
|
|
97
|
+
artifactSatisfied: false,
|
|
98
|
+
hasResumableDiskState: false,
|
|
99
|
+
isBootstrapCrash: false,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (lock && isLockProcessAlive(lock)) {
|
|
104
|
+
return {
|
|
105
|
+
classification: "running",
|
|
106
|
+
lock,
|
|
107
|
+
pausedSession,
|
|
108
|
+
state: null,
|
|
109
|
+
recovery: null,
|
|
110
|
+
recoveryPrompt: null,
|
|
111
|
+
recoveryToolCallCount: 0,
|
|
112
|
+
artifactSatisfied: false,
|
|
113
|
+
hasResumableDiskState: false,
|
|
114
|
+
isBootstrapCrash: false,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const isBootstrapCrash = isBootstrapCrashLock(lock);
|
|
119
|
+
const state = await deriveState(assessmentBasePath);
|
|
120
|
+
const hasResumableDiskState = hasResumableDerivedState(state);
|
|
121
|
+
const artifactSatisfied = !!(
|
|
122
|
+
lock &&
|
|
123
|
+
!isBootstrapCrash &&
|
|
124
|
+
verifyExpectedArtifact(lock.unitType, lock.unitId, assessmentBasePath)
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
let recovery: RecoveryBriefing | null = null;
|
|
128
|
+
if (lock && !isBootstrapCrash && !artifactSatisfied) {
|
|
129
|
+
recovery = synthesizeCrashRecovery(
|
|
130
|
+
assessmentBasePath,
|
|
131
|
+
lock.unitType,
|
|
132
|
+
lock.unitId,
|
|
133
|
+
lock.sessionFile,
|
|
134
|
+
join(gsdRoot(assessmentBasePath), "activity"),
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const recoveryToolCallCount = recovery?.trace.toolCallCount ?? 0;
|
|
139
|
+
const recoveryPrompt = recoveryToolCallCount > 0 ? recovery!.prompt : null;
|
|
140
|
+
|
|
141
|
+
if (isBootstrapCrash) {
|
|
142
|
+
return {
|
|
143
|
+
classification: pausedSession ? "recoverable" : "stale",
|
|
144
|
+
lock,
|
|
145
|
+
pausedSession,
|
|
146
|
+
state,
|
|
147
|
+
recovery,
|
|
148
|
+
recoveryPrompt,
|
|
149
|
+
recoveryToolCallCount,
|
|
150
|
+
artifactSatisfied,
|
|
151
|
+
hasResumableDiskState,
|
|
152
|
+
isBootstrapCrash: true,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!hasResumableDiskState && pausedSession && !lock && recoveryToolCallCount === 0) {
|
|
157
|
+
return {
|
|
158
|
+
classification: "stale",
|
|
159
|
+
lock,
|
|
160
|
+
pausedSession,
|
|
161
|
+
state,
|
|
162
|
+
recovery,
|
|
163
|
+
recoveryPrompt,
|
|
164
|
+
recoveryToolCallCount,
|
|
165
|
+
artifactSatisfied,
|
|
166
|
+
hasResumableDiskState,
|
|
167
|
+
isBootstrapCrash: false,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (lock && artifactSatisfied && !hasResumableDiskState && recoveryToolCallCount === 0) {
|
|
172
|
+
return {
|
|
173
|
+
classification: "stale",
|
|
174
|
+
lock,
|
|
175
|
+
pausedSession,
|
|
176
|
+
state,
|
|
177
|
+
recovery,
|
|
178
|
+
recoveryPrompt,
|
|
179
|
+
recoveryToolCallCount,
|
|
180
|
+
artifactSatisfied,
|
|
181
|
+
hasResumableDiskState,
|
|
182
|
+
isBootstrapCrash: false,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const hasStrongRecoverySignal =
|
|
187
|
+
hasResumableDiskState || recoveryToolCallCount > 0;
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
classification: hasStrongRecoverySignal ? "recoverable" : "stale",
|
|
191
|
+
lock,
|
|
192
|
+
pausedSession,
|
|
193
|
+
state,
|
|
194
|
+
recovery,
|
|
195
|
+
recoveryPrompt,
|
|
196
|
+
recoveryToolCallCount,
|
|
197
|
+
artifactSatisfied,
|
|
198
|
+
hasResumableDiskState,
|
|
199
|
+
isBootstrapCrash: false,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function formatInterruptedSessionSummary(
|
|
204
|
+
assessment: InterruptedSessionAssessment,
|
|
205
|
+
): string[] {
|
|
206
|
+
if (assessment.lock) return [formatCrashInfo(assessment.lock)];
|
|
207
|
+
|
|
208
|
+
if (assessment.pausedSession?.milestoneId) {
|
|
209
|
+
return [
|
|
210
|
+
`Paused auto-mode session detected for ${assessment.pausedSession.milestoneId}.`,
|
|
211
|
+
];
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return ["Paused auto-mode session detected."];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function formatInterruptedSessionRunningMessage(
|
|
218
|
+
assessment: InterruptedSessionAssessment,
|
|
219
|
+
): string {
|
|
220
|
+
const pid = assessment.lock?.pid;
|
|
221
|
+
return pid
|
|
222
|
+
? `Another auto-mode session (PID ${pid}) appears to be running.\nStop it with \`kill ${pid}\` before starting a new session.`
|
|
223
|
+
: "Another auto-mode session appears to be running.";
|
|
224
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
import { assertSafeDirectory } from "./validate-directory.js";
|
|
6
|
+
import { detectWorkflowMcpLaunchConfig } from "./workflow-mcp.js";
|
|
7
|
+
|
|
8
|
+
export const GSD_WORKFLOW_MCP_SERVER_NAME = "gsd-workflow";
|
|
9
|
+
|
|
10
|
+
export interface ProjectMcpServerConfig {
|
|
11
|
+
command?: string;
|
|
12
|
+
args?: string[];
|
|
13
|
+
cwd?: string;
|
|
14
|
+
env?: Record<string, string>;
|
|
15
|
+
url?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface EnsureProjectWorkflowMcpConfigResult {
|
|
19
|
+
configPath: string;
|
|
20
|
+
serverName: string;
|
|
21
|
+
status: "created" | "updated" | "unchanged";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface McpConfigFile {
|
|
25
|
+
mcpServers?: Record<string, ProjectMcpServerConfig>;
|
|
26
|
+
servers?: Record<string, ProjectMcpServerConfig>;
|
|
27
|
+
[key: string]: unknown;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function resolveBundledGsdCliPath(env: NodeJS.ProcessEnv = process.env): string | null {
|
|
31
|
+
const explicit = env.GSD_CLI_PATH?.trim() || env.GSD_BIN_PATH?.trim();
|
|
32
|
+
if (explicit) return explicit;
|
|
33
|
+
|
|
34
|
+
const candidates = [
|
|
35
|
+
resolve(fileURLToPath(new URL("../../../../scripts/dev-cli.js", import.meta.url))),
|
|
36
|
+
resolve(fileURLToPath(new URL("../../../../dist/loader.js", import.meta.url))),
|
|
37
|
+
resolve(fileURLToPath(new URL("../../../loader.js", import.meta.url))),
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
for (const candidate of candidates) {
|
|
41
|
+
if (existsSync(candidate)) return candidate;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function buildProjectWorkflowMcpServerConfig(
|
|
48
|
+
projectRoot: string,
|
|
49
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
50
|
+
): ProjectMcpServerConfig {
|
|
51
|
+
const resolvedProjectRoot = resolve(projectRoot);
|
|
52
|
+
const gsdCliPath = resolveBundledGsdCliPath(env);
|
|
53
|
+
const launch = detectWorkflowMcpLaunchConfig(resolvedProjectRoot, {
|
|
54
|
+
...env,
|
|
55
|
+
...(gsdCliPath ? { GSD_CLI_PATH: gsdCliPath, GSD_BIN_PATH: gsdCliPath } : {}),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if (!launch) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
"Unable to resolve the GSD workflow MCP server. Build this checkout or install gsd-mcp-server on PATH.",
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
command: launch.command,
|
|
66
|
+
...(launch.args && launch.args.length > 0 ? { args: launch.args } : {}),
|
|
67
|
+
...(launch.cwd ? { cwd: launch.cwd } : {}),
|
|
68
|
+
...(launch.env ? { env: launch.env } : {}),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function readExistingConfig(configPath: string): McpConfigFile {
|
|
73
|
+
if (!existsSync(configPath)) return {};
|
|
74
|
+
|
|
75
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
76
|
+
try {
|
|
77
|
+
const parsed = JSON.parse(raw) as McpConfigFile;
|
|
78
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
79
|
+
} catch (err) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
`Failed to parse ${configPath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function ensureProjectWorkflowMcpConfig(
|
|
87
|
+
projectRoot: string,
|
|
88
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
89
|
+
): EnsureProjectWorkflowMcpConfigResult {
|
|
90
|
+
const resolvedProjectRoot = resolve(projectRoot);
|
|
91
|
+
assertSafeDirectory(resolvedProjectRoot);
|
|
92
|
+
|
|
93
|
+
const configPath = resolve(resolvedProjectRoot, ".mcp.json");
|
|
94
|
+
const existing = readExistingConfig(configPath);
|
|
95
|
+
const desiredServer = buildProjectWorkflowMcpServerConfig(resolvedProjectRoot, env);
|
|
96
|
+
const previousServers = existing.mcpServers ?? {};
|
|
97
|
+
const nextServers = {
|
|
98
|
+
...previousServers,
|
|
99
|
+
[GSD_WORKFLOW_MCP_SERVER_NAME]: desiredServer,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const alreadyPresent = existsSync(configPath);
|
|
103
|
+
const unchanged =
|
|
104
|
+
JSON.stringify(previousServers[GSD_WORKFLOW_MCP_SERVER_NAME] ?? null)
|
|
105
|
+
=== JSON.stringify(desiredServer)
|
|
106
|
+
&& existing.mcpServers !== undefined;
|
|
107
|
+
|
|
108
|
+
if (unchanged) {
|
|
109
|
+
return {
|
|
110
|
+
configPath,
|
|
111
|
+
serverName: GSD_WORKFLOW_MCP_SERVER_NAME,
|
|
112
|
+
status: "unchanged",
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const nextConfig: McpConfigFile = {
|
|
117
|
+
...existing,
|
|
118
|
+
mcpServers: nextServers,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
writeFileSync(configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf-8");
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
configPath,
|
|
125
|
+
serverName: GSD_WORKFLOW_MCP_SERVER_NAME,
|
|
126
|
+
status: alreadyPresent ? "updated" : "created",
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -47,6 +47,7 @@ import { extractVerdict } from './verdict-parser.js';
|
|
|
47
47
|
|
|
48
48
|
import {
|
|
49
49
|
isDbAvailable,
|
|
50
|
+
wasDbOpenAttempted,
|
|
50
51
|
getAllMilestones,
|
|
51
52
|
getMilestone,
|
|
52
53
|
getMilestoneSlices,
|
|
@@ -271,7 +272,12 @@ export async function deriveState(basePath: string): Promise<GSDState> {
|
|
|
271
272
|
_telemetry.markdownDeriveCount++;
|
|
272
273
|
}
|
|
273
274
|
} else {
|
|
274
|
-
|
|
275
|
+
// Only warn when DB initialization was attempted and failed — not when
|
|
276
|
+
// the DB simply hasn't been opened yet (e.g. during before_agent_start
|
|
277
|
+
// context injection which runs before any tool invocation opens the DB).
|
|
278
|
+
if (wasDbOpenAttempted()) {
|
|
279
|
+
logWarning("state", "DB unavailable — using filesystem state derivation (degraded mode)");
|
|
280
|
+
}
|
|
275
281
|
result = await _deriveStateImpl(basePath);
|
|
276
282
|
_telemetry.markdownDeriveCount++;
|
|
277
283
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
const sourcePath = join(import.meta.dirname, "..", "auto.ts");
|
|
7
|
+
const source = readFileSync(sourcePath, "utf-8");
|
|
8
|
+
|
|
9
|
+
test("auto-mode captures GSD_PROJECT_ROOT before entering the dispatch loop", () => {
|
|
10
|
+
const captureDeclIdx = source.indexOf("function captureProjectRootEnv(projectRoot: string): void {");
|
|
11
|
+
assert.ok(captureDeclIdx > -1, "auto.ts should define captureProjectRootEnv()");
|
|
12
|
+
|
|
13
|
+
const resumeCallIdx = source.indexOf("captureProjectRootEnv(s.originalBasePath || s.basePath);");
|
|
14
|
+
assert.ok(resumeCallIdx > -1, "auto.ts should capture GSD_PROJECT_ROOT before resume autoLoop");
|
|
15
|
+
|
|
16
|
+
const firstAutoLoopIdx = source.indexOf("await autoLoop(ctx, pi, s, buildLoopDeps());");
|
|
17
|
+
assert.ok(firstAutoLoopIdx > -1, "auto.ts should invoke autoLoop()");
|
|
18
|
+
assert.ok(
|
|
19
|
+
resumeCallIdx < firstAutoLoopIdx,
|
|
20
|
+
"auto.ts must set GSD_PROJECT_ROOT before the first autoLoop() call",
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("auto-mode restores GSD_PROJECT_ROOT when execution stops or pauses", () => {
|
|
25
|
+
assert.match(source, /function restoreProjectRootEnv\(\): void \{/);
|
|
26
|
+
assert.match(source, /cleanupAfterLoopExit\(ctx: ExtensionContext\): void \{[\s\S]*restoreProjectRootEnv\(\);/);
|
|
27
|
+
assert.match(source, /export async function pauseAuto\([\s\S]*restoreProjectRootEnv\(\);/);
|
|
28
|
+
assert.match(source, /\} finally \{[\s\S]*restoreProjectRootEnv\(\);[\s\S]*s\.reset\(\);/);
|
|
29
|
+
});
|