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
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
1
4
|
const MILESTONE_CONTEXT_RE = /M\d+(?:-[a-z0-9]{6})?-CONTEXT\.md$/;
|
|
2
5
|
const CONTEXT_MILESTONE_RE = /(?:^|[/\\])(M\d+(?:-[a-z0-9]{6})?)-CONTEXT\.md$/i;
|
|
3
6
|
const DEPTH_VERIFICATION_MILESTONE_RE = /depth_verification[_-](M\d+(?:-[a-z0-9]{6})?)/i;
|
|
@@ -65,6 +68,69 @@ const GATE_SAFE_TOOLS = new Set([
|
|
|
65
68
|
"search_and_read",
|
|
66
69
|
]);
|
|
67
70
|
|
|
71
|
+
export interface WriteGateSnapshot {
|
|
72
|
+
verifiedDepthMilestones: string[];
|
|
73
|
+
activeQueuePhase: boolean;
|
|
74
|
+
pendingGateId: string | null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function shouldPersistWriteGateSnapshot(env: NodeJS.ProcessEnv = process.env): boolean {
|
|
78
|
+
return env.GSD_PERSIST_WRITE_GATE_STATE === "1";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function writeGateSnapshotPath(basePath: string = process.cwd()): string {
|
|
82
|
+
return join(basePath, ".gsd", "runtime", "write-gate-state.json");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function currentWriteGateSnapshot(): WriteGateSnapshot {
|
|
86
|
+
return {
|
|
87
|
+
verifiedDepthMilestones: [...verifiedDepthMilestones].sort(),
|
|
88
|
+
activeQueuePhase,
|
|
89
|
+
pendingGateId,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function persistWriteGateSnapshot(basePath: string = process.cwd()): void {
|
|
94
|
+
if (!shouldPersistWriteGateSnapshot()) return;
|
|
95
|
+
const path = writeGateSnapshotPath(basePath);
|
|
96
|
+
mkdirSync(join(basePath, ".gsd", "runtime"), { recursive: true });
|
|
97
|
+
const tempPath = `${path}.tmp`;
|
|
98
|
+
writeFileSync(tempPath, JSON.stringify(currentWriteGateSnapshot(), null, 2), "utf-8");
|
|
99
|
+
renameSync(tempPath, path);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function clearPersistedWriteGateSnapshot(basePath: string = process.cwd()): void {
|
|
103
|
+
if (!shouldPersistWriteGateSnapshot()) return;
|
|
104
|
+
const path = writeGateSnapshotPath(basePath);
|
|
105
|
+
try {
|
|
106
|
+
unlinkSync(path);
|
|
107
|
+
} catch {
|
|
108
|
+
// swallow
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function normalizeWriteGateSnapshot(value: unknown): WriteGateSnapshot {
|
|
113
|
+
const record = value && typeof value === "object" ? value as Record<string, unknown> : {};
|
|
114
|
+
const verified = Array.isArray(record.verifiedDepthMilestones)
|
|
115
|
+
? record.verifiedDepthMilestones.filter((item): item is string => typeof item === "string")
|
|
116
|
+
: [];
|
|
117
|
+
return {
|
|
118
|
+
verifiedDepthMilestones: [...new Set(verified)].sort(),
|
|
119
|
+
activeQueuePhase: record.activeQueuePhase === true,
|
|
120
|
+
pendingGateId: typeof record.pendingGateId === "string" ? record.pendingGateId : null,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function loadWriteGateSnapshot(basePath: string = process.cwd()): WriteGateSnapshot {
|
|
125
|
+
const path = writeGateSnapshotPath(basePath);
|
|
126
|
+
if (!existsSync(path)) return currentWriteGateSnapshot();
|
|
127
|
+
try {
|
|
128
|
+
return normalizeWriteGateSnapshot(JSON.parse(readFileSync(path, "utf-8")));
|
|
129
|
+
} catch {
|
|
130
|
+
return currentWriteGateSnapshot();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
68
134
|
export function isDepthVerified(): boolean {
|
|
69
135
|
return verifiedDepthMilestones.size > 0;
|
|
70
136
|
}
|
|
@@ -77,28 +143,40 @@ export function isMilestoneDepthVerified(milestoneId: string | null | undefined)
|
|
|
77
143
|
return verifiedDepthMilestones.has(milestoneId);
|
|
78
144
|
}
|
|
79
145
|
|
|
146
|
+
export function isMilestoneDepthVerifiedInSnapshot(
|
|
147
|
+
snapshot: WriteGateSnapshot,
|
|
148
|
+
milestoneId: string | null | undefined,
|
|
149
|
+
): boolean {
|
|
150
|
+
if (!milestoneId) return false;
|
|
151
|
+
return snapshot.verifiedDepthMilestones.includes(milestoneId);
|
|
152
|
+
}
|
|
153
|
+
|
|
80
154
|
export function isQueuePhaseActive(): boolean {
|
|
81
155
|
return activeQueuePhase;
|
|
82
156
|
}
|
|
83
157
|
|
|
84
158
|
export function setQueuePhaseActive(active: boolean): void {
|
|
85
159
|
activeQueuePhase = active;
|
|
160
|
+
persistWriteGateSnapshot();
|
|
86
161
|
}
|
|
87
162
|
|
|
88
163
|
export function resetWriteGateState(): void {
|
|
89
164
|
verifiedDepthMilestones.clear();
|
|
90
165
|
pendingGateId = null;
|
|
166
|
+
persistWriteGateSnapshot();
|
|
91
167
|
}
|
|
92
168
|
|
|
93
169
|
export function clearDiscussionFlowState(): void {
|
|
94
170
|
verifiedDepthMilestones.clear();
|
|
95
171
|
activeQueuePhase = false;
|
|
96
172
|
pendingGateId = null;
|
|
173
|
+
clearPersistedWriteGateSnapshot();
|
|
97
174
|
}
|
|
98
175
|
|
|
99
|
-
export function markDepthVerified(milestoneId?: string | null): void {
|
|
176
|
+
export function markDepthVerified(milestoneId?: string | null, basePath: string = process.cwd()): void {
|
|
100
177
|
if (!milestoneId) return;
|
|
101
178
|
verifiedDepthMilestones.add(milestoneId);
|
|
179
|
+
persistWriteGateSnapshot(basePath);
|
|
102
180
|
}
|
|
103
181
|
|
|
104
182
|
/**
|
|
@@ -130,6 +208,7 @@ function extractContextMilestoneId(inputPath: string): string | null {
|
|
|
130
208
|
*/
|
|
131
209
|
export function setPendingGate(gateId: string): void {
|
|
132
210
|
pendingGateId = gateId;
|
|
211
|
+
persistWriteGateSnapshot();
|
|
133
212
|
}
|
|
134
213
|
|
|
135
214
|
/**
|
|
@@ -137,6 +216,7 @@ export function setPendingGate(gateId: string): void {
|
|
|
137
216
|
*/
|
|
138
217
|
export function clearPendingGate(): void {
|
|
139
218
|
pendingGateId = null;
|
|
219
|
+
persistWriteGateSnapshot();
|
|
140
220
|
}
|
|
141
221
|
|
|
142
222
|
/**
|
|
@@ -154,11 +234,20 @@ export function getPendingGate(): string | null {
|
|
|
154
234
|
* Read-only tools and ask_user_questions itself are always allowed.
|
|
155
235
|
*/
|
|
156
236
|
export function shouldBlockPendingGate(
|
|
237
|
+
toolName: string,
|
|
238
|
+
milestoneId: string | null,
|
|
239
|
+
queuePhaseActive?: boolean,
|
|
240
|
+
): { block: boolean; reason?: string } {
|
|
241
|
+
return shouldBlockPendingGateInSnapshot(currentWriteGateSnapshot(), toolName, milestoneId, queuePhaseActive);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function shouldBlockPendingGateInSnapshot(
|
|
245
|
+
snapshot: WriteGateSnapshot,
|
|
157
246
|
toolName: string,
|
|
158
247
|
_milestoneId: string | null,
|
|
159
248
|
_queuePhaseActive?: boolean,
|
|
160
249
|
): { block: boolean; reason?: string } {
|
|
161
|
-
if (!pendingGateId) return { block: false };
|
|
250
|
+
if (!snapshot.pendingGateId) return { block: false };
|
|
162
251
|
|
|
163
252
|
if (GATE_SAFE_TOOLS.has(toolName)) return { block: false };
|
|
164
253
|
|
|
@@ -168,7 +257,7 @@ export function shouldBlockPendingGate(
|
|
|
168
257
|
return {
|
|
169
258
|
block: true,
|
|
170
259
|
reason: [
|
|
171
|
-
`HARD BLOCK: Discussion gate "${pendingGateId}" has not been confirmed by the user.`,
|
|
260
|
+
`HARD BLOCK: Discussion gate "${snapshot.pendingGateId}" has not been confirmed by the user.`,
|
|
172
261
|
`You MUST re-call ask_user_questions with the gate question before making any other tool calls.`,
|
|
173
262
|
`If the previous ask_user_questions call failed, errored, was cancelled, or the user's response`,
|
|
174
263
|
`did not match a provided option, you MUST re-ask — never rationalize past the block.`,
|
|
@@ -182,11 +271,20 @@ export function shouldBlockPendingGate(
|
|
|
182
271
|
* Read-only bash commands are allowed; mutating commands are blocked.
|
|
183
272
|
*/
|
|
184
273
|
export function shouldBlockPendingGateBash(
|
|
274
|
+
command: string,
|
|
275
|
+
milestoneId: string | null,
|
|
276
|
+
queuePhaseActive?: boolean,
|
|
277
|
+
): { block: boolean; reason?: string } {
|
|
278
|
+
return shouldBlockPendingGateBashInSnapshot(currentWriteGateSnapshot(), command, milestoneId, queuePhaseActive);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export function shouldBlockPendingGateBashInSnapshot(
|
|
282
|
+
snapshot: WriteGateSnapshot,
|
|
185
283
|
command: string,
|
|
186
284
|
_milestoneId: string | null,
|
|
187
285
|
_queuePhaseActive?: boolean,
|
|
188
286
|
): { block: boolean; reason?: string } {
|
|
189
|
-
if (!pendingGateId) return { block: false };
|
|
287
|
+
if (!snapshot.pendingGateId) return { block: false };
|
|
190
288
|
|
|
191
289
|
// Allow read-only bash commands
|
|
192
290
|
if (BASH_READ_ONLY_RE.test(command)) return { block: false };
|
|
@@ -194,7 +292,7 @@ export function shouldBlockPendingGateBash(
|
|
|
194
292
|
return {
|
|
195
293
|
block: true,
|
|
196
294
|
reason: [
|
|
197
|
-
`HARD BLOCK: Discussion gate "${pendingGateId}" has not been confirmed by the user.`,
|
|
295
|
+
`HARD BLOCK: Discussion gate "${snapshot.pendingGateId}" has not been confirmed by the user.`,
|
|
198
296
|
`You MUST re-call ask_user_questions with the gate question before running mutating commands.`,
|
|
199
297
|
`If the previous ask_user_questions call failed, errored, was cancelled, or the user's response`,
|
|
200
298
|
`did not match a provided option, you MUST re-ask — never rationalize past the block.`,
|
|
@@ -275,6 +373,15 @@ export function shouldBlockContextArtifactSave(
|
|
|
275
373
|
artifactType: string,
|
|
276
374
|
milestoneId: string | null,
|
|
277
375
|
sliceId?: string | null,
|
|
376
|
+
): { block: boolean; reason?: string } {
|
|
377
|
+
return shouldBlockContextArtifactSaveInSnapshot(currentWriteGateSnapshot(), artifactType, milestoneId, sliceId);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export function shouldBlockContextArtifactSaveInSnapshot(
|
|
381
|
+
snapshot: WriteGateSnapshot,
|
|
382
|
+
artifactType: string,
|
|
383
|
+
milestoneId: string | null,
|
|
384
|
+
sliceId?: string | null,
|
|
278
385
|
): { block: boolean; reason?: string } {
|
|
279
386
|
if (artifactType !== "CONTEXT") return { block: false };
|
|
280
387
|
if (sliceId) return { block: false };
|
|
@@ -287,7 +394,7 @@ export function shouldBlockContextArtifactSave(
|
|
|
287
394
|
].join(" "),
|
|
288
395
|
};
|
|
289
396
|
}
|
|
290
|
-
if (
|
|
397
|
+
if (isMilestoneDepthVerifiedInSnapshot(snapshot, milestoneId)) return { block: false };
|
|
291
398
|
|
|
292
399
|
return {
|
|
293
400
|
block: true,
|
|
@@ -317,6 +424,15 @@ export function shouldBlockQueueExecution(
|
|
|
317
424
|
toolName: string,
|
|
318
425
|
input: string,
|
|
319
426
|
queuePhaseActive: boolean,
|
|
427
|
+
): { block: boolean; reason?: string } {
|
|
428
|
+
return shouldBlockQueueExecutionInSnapshot(currentWriteGateSnapshot(), toolName, input, queuePhaseActive);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export function shouldBlockQueueExecutionInSnapshot(
|
|
432
|
+
snapshot: WriteGateSnapshot,
|
|
433
|
+
toolName: string,
|
|
434
|
+
input: string,
|
|
435
|
+
queuePhaseActive: boolean = snapshot.activeQueuePhase,
|
|
320
436
|
): { block: boolean; reason?: string } {
|
|
321
437
|
if (!queuePhaseActive) return { block: false };
|
|
322
438
|
|
|
@@ -70,7 +70,7 @@ export const TOP_LEVEL_SUBCOMMANDS: readonly GsdCommandDefinition[] = [
|
|
|
70
70
|
{ cmd: "templates", desc: "List available workflow templates" },
|
|
71
71
|
{ cmd: "extensions", desc: "Manage extensions (list, enable, disable, info)" },
|
|
72
72
|
{ cmd: "fast", desc: "Toggle OpenAI service tier (on/off/flex/status)" },
|
|
73
|
-
{ cmd: "mcp", desc: "MCP server status and
|
|
73
|
+
{ cmd: "mcp", desc: "MCP server status, connectivity, and local config bootstrap (status, check, init)" },
|
|
74
74
|
{ cmd: "rethink", desc: "Conversational project reorganization — reorder, park, discard, add milestones" },
|
|
75
75
|
{ cmd: "workflow", desc: "Custom workflow lifecycle (new, run, list, validate, pause, resume)" },
|
|
76
76
|
{ cmd: "codebase", desc: "Generate, refresh, and inspect the codebase map cache (.gsd/CODEBASE.md)" },
|
|
@@ -201,6 +201,7 @@ const NESTED_COMPLETIONS: CompletionMap = {
|
|
|
201
201
|
mcp: [
|
|
202
202
|
{ cmd: "status", desc: "Show all MCP server statuses (default)" },
|
|
203
203
|
{ cmd: "check", desc: "Detailed status for a specific server" },
|
|
204
|
+
{ cmd: "init", desc: "Write .mcp.json for the local GSD workflow MCP server" },
|
|
204
205
|
],
|
|
205
206
|
doctor: [
|
|
206
207
|
{ cmd: "fix", desc: "Auto-fix detected issues" },
|
|
@@ -60,7 +60,7 @@ export function showHelp(ctx: ExtensionCommandContext): void {
|
|
|
60
60
|
" /gsd hooks Show post-unit hook configuration",
|
|
61
61
|
" /gsd extensions Manage extensions [list|enable|disable|info]",
|
|
62
62
|
" /gsd fast Toggle OpenAI service tier [on|off|flex|status]",
|
|
63
|
-
" /gsd mcp MCP server status and connectivity [status|check <server
|
|
63
|
+
" /gsd mcp MCP server status and connectivity [status|check <server>|init [dir]]",
|
|
64
64
|
"",
|
|
65
65
|
"MAINTENANCE",
|
|
66
66
|
" /gsd doctor Diagnose and repair .gsd/ state [audit|fix|heal] [scope]",
|
|
@@ -194,6 +194,56 @@ function sortModelsForSelection(models: Model<any>[], currentModel: Model<any> |
|
|
|
194
194
|
});
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
+
function buildProviderModelGroups(
|
|
198
|
+
models: Model<any>[],
|
|
199
|
+
currentModel: Model<any> | undefined,
|
|
200
|
+
): Map<string, Model<any>[]> {
|
|
201
|
+
const byProvider = new Map<string, Model<any>[]>();
|
|
202
|
+
|
|
203
|
+
for (const model of sortModelsForSelection(models, currentModel)) {
|
|
204
|
+
let group = byProvider.get(model.provider);
|
|
205
|
+
if (!group) {
|
|
206
|
+
group = [];
|
|
207
|
+
byProvider.set(model.provider, group);
|
|
208
|
+
}
|
|
209
|
+
group.push(model);
|
|
210
|
+
}
|
|
211
|
+
return byProvider;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async function selectModelByProvider(
|
|
215
|
+
title: string,
|
|
216
|
+
models: Model<any>[],
|
|
217
|
+
ctx: ExtensionCommandContext,
|
|
218
|
+
currentModel: Model<any> | undefined,
|
|
219
|
+
): Promise<Model<any> | undefined> {
|
|
220
|
+
const byProvider = buildProviderModelGroups(models, currentModel);
|
|
221
|
+
const providerOptions = Array.from(byProvider.entries()).map(([provider, group]) =>
|
|
222
|
+
`${provider} (${group.length} model${group.length === 1 ? "" : "s"})`,
|
|
223
|
+
);
|
|
224
|
+
providerOptions.push("(cancel)");
|
|
225
|
+
|
|
226
|
+
const providerChoice = await ctx.ui.select(`${title} — choose provider:`, providerOptions);
|
|
227
|
+
if (!providerChoice || typeof providerChoice !== "string" || providerChoice === "(cancel)") return undefined;
|
|
228
|
+
|
|
229
|
+
const providerName = providerChoice.replace(/ \(\d+ models?\)$/, "");
|
|
230
|
+
const providerModels = byProvider.get(providerName);
|
|
231
|
+
if (!providerModels || providerModels.length === 0) return undefined;
|
|
232
|
+
|
|
233
|
+
const optionToModel = new Map<string, Model<any>>();
|
|
234
|
+
const modelOptions = providerModels.map((model) => {
|
|
235
|
+
const isCurrent = currentModel && model.provider === currentModel.provider && model.id === currentModel.id;
|
|
236
|
+
const label = `${isCurrent ? "* " : ""}${model.id}`;
|
|
237
|
+
optionToModel.set(label, model);
|
|
238
|
+
return label;
|
|
239
|
+
});
|
|
240
|
+
modelOptions.push("(cancel)");
|
|
241
|
+
|
|
242
|
+
const modelChoice = await ctx.ui.select(`${title} — ${providerName}:`, modelOptions);
|
|
243
|
+
if (!modelChoice || typeof modelChoice !== "string" || modelChoice === "(cancel)") return undefined;
|
|
244
|
+
return optionToModel.get(modelChoice);
|
|
245
|
+
}
|
|
246
|
+
|
|
197
247
|
async function resolveRequestedModel(
|
|
198
248
|
query: string,
|
|
199
249
|
ctx: ExtensionCommandContext,
|
|
@@ -211,19 +261,7 @@ async function resolveRequestedModel(
|
|
|
211
261
|
|
|
212
262
|
if (partialMatches.length === 1) return partialMatches[0];
|
|
213
263
|
if (partialMatches.length === 0 || !ctx.hasUI) return undefined;
|
|
214
|
-
|
|
215
|
-
const sorted = sortModelsForSelection(partialMatches, ctx.model);
|
|
216
|
-
const optionToModel = new Map<string, Model<any>>();
|
|
217
|
-
const options = sorted.map((model) => {
|
|
218
|
-
const label = `${model.provider}/${model.id}`;
|
|
219
|
-
optionToModel.set(label, model);
|
|
220
|
-
return label;
|
|
221
|
-
});
|
|
222
|
-
options.push("(cancel)");
|
|
223
|
-
|
|
224
|
-
const choice = await ctx.ui.select(`Multiple models match "${query}" — choose one:`, options);
|
|
225
|
-
if (!choice || typeof choice !== "string" || choice === "(cancel)") return undefined;
|
|
226
|
-
return optionToModel.get(choice);
|
|
264
|
+
return selectModelByProvider(`Multiple models match "${query}"`, partialMatches, ctx, ctx.model);
|
|
227
265
|
}
|
|
228
266
|
|
|
229
267
|
async function handleModel(trimmedArgs: string, ctx: ExtensionCommandContext, pi: ExtensionAPI | undefined): Promise<void> {
|
|
@@ -247,18 +285,7 @@ async function handleModel(trimmedArgs: string, ctx: ExtensionCommandContext, pi
|
|
|
247
285
|
return;
|
|
248
286
|
}
|
|
249
287
|
|
|
250
|
-
|
|
251
|
-
const options = sortModelsForSelection(availableModels, ctx.model).map((model) => {
|
|
252
|
-
const isCurrent = ctx.model && model.provider === ctx.model.provider && model.id === ctx.model.id;
|
|
253
|
-
const label = `${isCurrent ? "* " : ""}${model.provider}/${model.id}`;
|
|
254
|
-
optionToModel.set(label, model);
|
|
255
|
-
return label;
|
|
256
|
-
});
|
|
257
|
-
options.push("(cancel)");
|
|
258
|
-
|
|
259
|
-
const choice = await ctx.ui.select("Select session model:", options);
|
|
260
|
-
if (!choice || typeof choice !== "string" || choice === "(cancel)") return;
|
|
261
|
-
targetModel = optionToModel.get(choice);
|
|
288
|
+
targetModel = await selectModelByProvider("Select session model:", availableModels, ctx, ctx.model);
|
|
262
289
|
} else {
|
|
263
290
|
targetModel = await resolveRequestedModel(trimmed, ctx);
|
|
264
291
|
}
|
|
@@ -8,7 +8,13 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
|
|
|
8
8
|
getArgumentCompletions: getGsdArgumentCompletions,
|
|
9
9
|
handler: async (args: string, ctx: ExtensionCommandContext) => {
|
|
10
10
|
const { handleGSDCommand } = await import("./dispatcher.js");
|
|
11
|
-
await
|
|
11
|
+
const { setStderrLoggingEnabled } = await import("../workflow-logger.js");
|
|
12
|
+
const previousStderrSetting = setStderrLoggingEnabled(false);
|
|
13
|
+
try {
|
|
14
|
+
await handleGSDCommand(args, ctx, pi);
|
|
15
|
+
} finally {
|
|
16
|
+
setStderrLoggingEnabled(previousStderrSetting);
|
|
17
|
+
}
|
|
12
18
|
},
|
|
13
19
|
});
|
|
14
20
|
}
|
|
@@ -7,12 +7,15 @@
|
|
|
7
7
|
* /gsd mcp — Overview of all servers (alias: /gsd mcp status)
|
|
8
8
|
* /gsd mcp status — Same as bare /gsd mcp
|
|
9
9
|
* /gsd mcp check <srv> — Detailed status for a specific server
|
|
10
|
+
* /gsd mcp init [dir] — Write project-local GSD workflow MCP config
|
|
10
11
|
*/
|
|
11
12
|
|
|
12
13
|
import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
13
14
|
|
|
14
15
|
import { existsSync, readFileSync } from "node:fs";
|
|
15
|
-
import { join } from "node:path";
|
|
16
|
+
import { join, resolve } from "node:path";
|
|
17
|
+
|
|
18
|
+
import { ensureProjectWorkflowMcpConfig } from "./mcp-project-config.js";
|
|
16
19
|
|
|
17
20
|
// ─── Types ──────────────────────────────────────────────────────────────────
|
|
18
21
|
|
|
@@ -28,6 +31,28 @@ export interface McpServerDetail extends McpServerStatus {
|
|
|
28
31
|
tools: string[];
|
|
29
32
|
}
|
|
30
33
|
|
|
34
|
+
export function formatMcpInitResult(
|
|
35
|
+
status: "created" | "updated" | "unchanged",
|
|
36
|
+
configPath: string,
|
|
37
|
+
targetPath: string,
|
|
38
|
+
): string {
|
|
39
|
+
const summary =
|
|
40
|
+
status === "created"
|
|
41
|
+
? "Created project MCP config."
|
|
42
|
+
: status === "updated"
|
|
43
|
+
? "Updated project MCP config."
|
|
44
|
+
: "Project MCP config is already up to date.";
|
|
45
|
+
|
|
46
|
+
return [
|
|
47
|
+
summary,
|
|
48
|
+
"",
|
|
49
|
+
`Project: ${targetPath}`,
|
|
50
|
+
`Config: ${configPath}`,
|
|
51
|
+
"",
|
|
52
|
+
"Claude Code can now load the GSD workflow MCP server from this folder.",
|
|
53
|
+
].join("\n");
|
|
54
|
+
}
|
|
55
|
+
|
|
31
56
|
// ─── Config reader (standalone — does not import mcp-client internals) ──────
|
|
32
57
|
|
|
33
58
|
interface McpServerRawConfig {
|
|
@@ -94,6 +119,7 @@ export function formatMcpStatusReport(servers: McpServerStatus[]): string {
|
|
|
94
119
|
"No MCP servers configured.",
|
|
95
120
|
"",
|
|
96
121
|
"Add servers to .mcp.json or .gsd/mcp.json to enable MCP integrations.",
|
|
122
|
+
"Tip: run /gsd mcp init . to write the local GSD workflow MCP config.",
|
|
97
123
|
"See: https://modelcontextprotocol.io/quickstart",
|
|
98
124
|
].join("\n");
|
|
99
125
|
}
|
|
@@ -153,12 +179,31 @@ export async function handleMcpStatus(
|
|
|
153
179
|
args: string,
|
|
154
180
|
ctx: ExtensionCommandContext,
|
|
155
181
|
): Promise<void> {
|
|
156
|
-
const trimmed = args.trim()
|
|
182
|
+
const trimmed = args.trim();
|
|
183
|
+
const lowered = trimmed.toLowerCase();
|
|
157
184
|
const configs = readMcpConfigs();
|
|
158
185
|
|
|
186
|
+
// /gsd mcp init [dir]
|
|
187
|
+
if (!lowered || lowered === "status") {
|
|
188
|
+
// handled below
|
|
189
|
+
} else if (lowered === "init" || lowered.startsWith("init ")) {
|
|
190
|
+
const rawPath = trimmed.slice("init".length).trim();
|
|
191
|
+
const targetPath = resolve(rawPath || ".");
|
|
192
|
+
try {
|
|
193
|
+
const result = ensureProjectWorkflowMcpConfig(targetPath);
|
|
194
|
+
ctx.ui.notify(formatMcpInitResult(result.status, result.configPath, targetPath), "info");
|
|
195
|
+
} catch (err) {
|
|
196
|
+
ctx.ui.notify(
|
|
197
|
+
`Failed to prepare MCP config for ${targetPath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
198
|
+
"error",
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
159
204
|
// /gsd mcp check <server>
|
|
160
|
-
if (
|
|
161
|
-
const serverName =
|
|
205
|
+
if (lowered.startsWith("check ")) {
|
|
206
|
+
const serverName = trimmed.slice("check ".length).trim();
|
|
162
207
|
const config = configs.find((c) => c.name === serverName);
|
|
163
208
|
if (!config) {
|
|
164
209
|
const available = configs.map((c) => c.name).join(", ") || "(none)";
|
|
@@ -202,7 +247,7 @@ export async function handleMcpStatus(
|
|
|
202
247
|
}
|
|
203
248
|
|
|
204
249
|
// /gsd mcp or /gsd mcp status
|
|
205
|
-
if (!
|
|
250
|
+
if (!lowered || lowered === "status") {
|
|
206
251
|
// Build status for each server
|
|
207
252
|
const statuses: McpServerStatus[] = [];
|
|
208
253
|
|
|
@@ -239,9 +284,10 @@ export async function handleMcpStatus(
|
|
|
239
284
|
|
|
240
285
|
// Unknown subcommand
|
|
241
286
|
ctx.ui.notify(
|
|
242
|
-
"Usage: /gsd mcp [status|check <server
|
|
287
|
+
"Usage: /gsd mcp [status|check <server>|init [dir]]\n\n" +
|
|
243
288
|
" status Show all MCP server statuses (default)\n" +
|
|
244
|
-
" check <server> Detailed status for a specific server"
|
|
289
|
+
" check <server> Detailed status for a specific server\n" +
|
|
290
|
+
" init [dir] Write .mcp.json for the local GSD workflow MCP server",
|
|
245
291
|
"warning",
|
|
246
292
|
);
|
|
247
293
|
}
|
|
@@ -10,7 +10,7 @@ import { deriveState, isMilestoneComplete } from "./state.js";
|
|
|
10
10
|
import { listWorktrees, resolveGitDir, worktreesDir } from "./worktree-manager.js";
|
|
11
11
|
import { abortAndReset } from "./git-self-heal.js";
|
|
12
12
|
import { RUNTIME_EXCLUSION_PATHS, resolveMilestoneIntegrationBranch, writeIntegrationBranch } from "./git-service.js";
|
|
13
|
-
import { nativeIsRepo, nativeWorktreeList, nativeWorktreeRemove, nativeBranchList, nativeBranchDelete, nativeLsFiles, nativeRmCached, nativeHasChanges, nativeLastCommitEpoch, nativeGetCurrentBranch,
|
|
13
|
+
import { nativeIsRepo, nativeWorktreeList, nativeWorktreeRemove, nativeBranchList, nativeBranchDelete, nativeLsFiles, nativeRmCached, nativeHasChanges, nativeLastCommitEpoch, nativeGetCurrentBranch, nativeAddTracked, nativeCommit } from "./native-git-bridge.js";
|
|
14
14
|
import { getAllWorktreeHealth } from "./worktree-health.js";
|
|
15
15
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
16
16
|
|
|
@@ -386,19 +386,19 @@ export async function checkGitHealth(
|
|
|
386
386
|
code: "stale_uncommitted_changes",
|
|
387
387
|
scope: "project",
|
|
388
388
|
unitId: "project",
|
|
389
|
-
message: `Uncommitted changes detected with no commit in ${mins} minute${mins === 1 ? "" : "s"} (threshold: ${thresholdMinutes}m). Snapshotting
|
|
389
|
+
message: `Uncommitted changes detected with no commit in ${mins} minute${mins === 1 ? "" : "s"} (threshold: ${thresholdMinutes}m). Snapshotting tracked files.`,
|
|
390
390
|
fixable: true,
|
|
391
391
|
});
|
|
392
392
|
|
|
393
393
|
if (shouldFix("stale_uncommitted_changes")) {
|
|
394
394
|
try {
|
|
395
|
-
|
|
395
|
+
nativeAddTracked(basePath);
|
|
396
396
|
const commitMsg = `gsd snapshot: uncommitted changes after ${mins}m inactivity`;
|
|
397
397
|
const result = nativeCommit(basePath, commitMsg);
|
|
398
398
|
if (result) {
|
|
399
399
|
fixesApplied.push(`created gsd snapshot after ${mins}m of uncommitted changes`);
|
|
400
400
|
} else {
|
|
401
|
-
fixesApplied.push("gsd snapshot skipped — nothing to commit after staging
|
|
401
|
+
fixesApplied.push("gsd snapshot skipped — nothing to commit after staging tracked files");
|
|
402
402
|
}
|
|
403
403
|
} catch {
|
|
404
404
|
fixesApplied.push("failed to create gsd snapshot commit");
|
|
@@ -21,8 +21,8 @@ import { readCrashLock, isLockProcessAlive, clearLock } from "./crash-recovery.j
|
|
|
21
21
|
import { abortAndReset } from "./git-self-heal.js";
|
|
22
22
|
import { rebuildState } from "./doctor.js";
|
|
23
23
|
import { deriveState } from "./state.js";
|
|
24
|
-
import {
|
|
25
|
-
import { nativeIsRepo, nativeHasChanges, nativeLastCommitEpoch, nativeGetCurrentBranch,
|
|
24
|
+
import { resolveMilestoneIntegrationBranch } from "./git-service.js";
|
|
25
|
+
import { nativeIsRepo, nativeHasChanges, nativeLastCommitEpoch, nativeGetCurrentBranch, nativeAddTracked, nativeCommit } from "./native-git-bridge.js";
|
|
26
26
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
27
27
|
import { runEnvironmentChecks } from "./doctor-environment.js";
|
|
28
28
|
|
|
@@ -312,7 +312,7 @@ export async function preDispatchHealthGate(basePath: string): Promise<PreDispat
|
|
|
312
312
|
if (minutesSinceCommit >= thresholdMinutes) {
|
|
313
313
|
const mins = Math.floor(minutesSinceCommit);
|
|
314
314
|
try {
|
|
315
|
-
|
|
315
|
+
nativeAddTracked(basePath);
|
|
316
316
|
const commitMsg = `gsd snapshot: pre-dispatch, uncommitted changes after ${mins}m inactivity`;
|
|
317
317
|
const result = nativeCommit(basePath, commitMsg);
|
|
318
318
|
if (result) {
|
|
@@ -8,6 +8,7 @@ import { resolveMilestoneFile, resolveMilestonePath, resolveSliceFile, resolveSl
|
|
|
8
8
|
import { deriveState, isMilestoneComplete } from "./state.js";
|
|
9
9
|
import { invalidateAllCaches } from "./cache.js";
|
|
10
10
|
import { loadEffectiveGSDPreferences, type GSDPreferences } from "./preferences.js";
|
|
11
|
+
import { isClosedStatus } from "./status-guards.js";
|
|
11
12
|
|
|
12
13
|
import type { DoctorIssue, DoctorIssueCode, DoctorReport } from "./doctor-types.js";
|
|
13
14
|
import { GLOBAL_STATE_CODES } from "./doctor-types.js";
|
|
@@ -474,15 +475,16 @@ export async function runGSDDoctor(basePath: string, options?: { fix?: boolean;
|
|
|
474
475
|
if (!roadmapContent) continue;
|
|
475
476
|
|
|
476
477
|
// Normalize slices: prefer DB, fall back to parser
|
|
477
|
-
type NormSlice = RoadmapSliceEntry & { pending?: boolean };
|
|
478
|
+
type NormSlice = RoadmapSliceEntry & { pending?: boolean; skipped?: boolean };
|
|
478
479
|
let slices: NormSlice[];
|
|
479
480
|
if (isDbAvailable()) {
|
|
480
481
|
const dbSlices = getMilestoneSlices(milestoneId);
|
|
481
482
|
slices = dbSlices.map(s => ({
|
|
482
483
|
id: s.id,
|
|
483
484
|
title: s.title,
|
|
484
|
-
done: s.status
|
|
485
|
+
done: isClosedStatus(s.status),
|
|
485
486
|
pending: s.status === "pending",
|
|
487
|
+
skipped: s.status === "skipped",
|
|
486
488
|
risk: (s.risk || "medium") as RoadmapSliceEntry["risk"],
|
|
487
489
|
depends: s.depends,
|
|
488
490
|
demo: s.demo,
|
|
@@ -578,8 +580,9 @@ export async function runGSDDoctor(basePath: string, options?: { fix?: boolean;
|
|
|
578
580
|
const slicePath = resolveSlicePath(basePath, milestoneId, slice.id);
|
|
579
581
|
if (!slicePath) {
|
|
580
582
|
// Pending slices haven't been planned yet — directories are created
|
|
581
|
-
// lazily by ensurePreconditions() at dispatch time.
|
|
582
|
-
|
|
583
|
+
// lazily by ensurePreconditions() at dispatch time. Skipped slices are
|
|
584
|
+
// intentionally allowed to remain summary-less and directory-less.
|
|
585
|
+
if (slice.pending || slice.skipped) continue;
|
|
583
586
|
const expectedPath = relSlicePath(basePath, milestoneId, slice.id);
|
|
584
587
|
issues.push({
|
|
585
588
|
severity: slice.done ? "warning" : "error",
|
|
@@ -603,7 +606,8 @@ export async function runGSDDoctor(basePath: string, options?: { fix?: boolean;
|
|
|
603
606
|
const tasksDir = resolveTasksDir(basePath, milestoneId, slice.id);
|
|
604
607
|
if (!tasksDir) {
|
|
605
608
|
// Pending slices haven't been planned yet — tasks/ is created on demand.
|
|
606
|
-
|
|
609
|
+
// Skipped slices may legitimately never create tasks/.
|
|
610
|
+
if (slice.pending || slice.skipped) continue;
|
|
607
611
|
issues.push({
|
|
608
612
|
severity: slice.done ? "warning" : "error",
|
|
609
613
|
code: "missing_tasks_dir",
|
|
@@ -778,6 +778,7 @@ let currentDb: DbAdapter | null = null;
|
|
|
778
778
|
let currentPath: string | null = null;
|
|
779
779
|
let currentPid: number = 0;
|
|
780
780
|
let _exitHandlerRegistered = false;
|
|
781
|
+
let _dbOpenAttempted = false;
|
|
781
782
|
|
|
782
783
|
export function getDbProvider(): ProviderName | null {
|
|
783
784
|
loadProvider();
|
|
@@ -788,7 +789,18 @@ export function isDbAvailable(): boolean {
|
|
|
788
789
|
return currentDb !== null;
|
|
789
790
|
}
|
|
790
791
|
|
|
792
|
+
/**
|
|
793
|
+
* Returns true if openDatabase() has been called at least once this session.
|
|
794
|
+
* Used to distinguish "DB not yet initialized" from "DB genuinely unavailable"
|
|
795
|
+
* so that early callers (e.g. before_agent_start context injection) don't
|
|
796
|
+
* trigger a false degraded-mode warning.
|
|
797
|
+
*/
|
|
798
|
+
export function wasDbOpenAttempted(): boolean {
|
|
799
|
+
return _dbOpenAttempted;
|
|
800
|
+
}
|
|
801
|
+
|
|
791
802
|
export function openDatabase(path: string): boolean {
|
|
803
|
+
_dbOpenAttempted = true;
|
|
792
804
|
if (currentDb && currentPath !== path) closeDatabase();
|
|
793
805
|
if (currentDb && currentPath === path) return true;
|
|
794
806
|
|