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,5 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
1
3
|
const MILESTONE_CONTEXT_RE = /M\d+(?:-[a-z0-9]{6})?-CONTEXT\.md$/;
|
|
2
4
|
const CONTEXT_MILESTONE_RE = /(?:^|[/\\])(M\d+(?:-[a-z0-9]{6})?)-CONTEXT\.md$/i;
|
|
3
5
|
const DEPTH_VERIFICATION_MILESTONE_RE = /depth_verification[_-](M\d+(?:-[a-z0-9]{6})?)/i;
|
|
@@ -57,6 +59,61 @@ const GATE_SAFE_TOOLS = new Set([
|
|
|
57
59
|
"search-the-web", "resolve_library", "get_library_docs", "fetch_page",
|
|
58
60
|
"search_and_read",
|
|
59
61
|
]);
|
|
62
|
+
function shouldPersistWriteGateSnapshot(env = process.env) {
|
|
63
|
+
return env.GSD_PERSIST_WRITE_GATE_STATE === "1";
|
|
64
|
+
}
|
|
65
|
+
function writeGateSnapshotPath(basePath = process.cwd()) {
|
|
66
|
+
return join(basePath, ".gsd", "runtime", "write-gate-state.json");
|
|
67
|
+
}
|
|
68
|
+
function currentWriteGateSnapshot() {
|
|
69
|
+
return {
|
|
70
|
+
verifiedDepthMilestones: [...verifiedDepthMilestones].sort(),
|
|
71
|
+
activeQueuePhase,
|
|
72
|
+
pendingGateId,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function persistWriteGateSnapshot(basePath = process.cwd()) {
|
|
76
|
+
if (!shouldPersistWriteGateSnapshot())
|
|
77
|
+
return;
|
|
78
|
+
const path = writeGateSnapshotPath(basePath);
|
|
79
|
+
mkdirSync(join(basePath, ".gsd", "runtime"), { recursive: true });
|
|
80
|
+
const tempPath = `${path}.tmp`;
|
|
81
|
+
writeFileSync(tempPath, JSON.stringify(currentWriteGateSnapshot(), null, 2), "utf-8");
|
|
82
|
+
renameSync(tempPath, path);
|
|
83
|
+
}
|
|
84
|
+
function clearPersistedWriteGateSnapshot(basePath = process.cwd()) {
|
|
85
|
+
if (!shouldPersistWriteGateSnapshot())
|
|
86
|
+
return;
|
|
87
|
+
const path = writeGateSnapshotPath(basePath);
|
|
88
|
+
try {
|
|
89
|
+
unlinkSync(path);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// swallow
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function normalizeWriteGateSnapshot(value) {
|
|
96
|
+
const record = value && typeof value === "object" ? value : {};
|
|
97
|
+
const verified = Array.isArray(record.verifiedDepthMilestones)
|
|
98
|
+
? record.verifiedDepthMilestones.filter((item) => typeof item === "string")
|
|
99
|
+
: [];
|
|
100
|
+
return {
|
|
101
|
+
verifiedDepthMilestones: [...new Set(verified)].sort(),
|
|
102
|
+
activeQueuePhase: record.activeQueuePhase === true,
|
|
103
|
+
pendingGateId: typeof record.pendingGateId === "string" ? record.pendingGateId : null,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
export function loadWriteGateSnapshot(basePath = process.cwd()) {
|
|
107
|
+
const path = writeGateSnapshotPath(basePath);
|
|
108
|
+
if (!existsSync(path))
|
|
109
|
+
return currentWriteGateSnapshot();
|
|
110
|
+
try {
|
|
111
|
+
return normalizeWriteGateSnapshot(JSON.parse(readFileSync(path, "utf-8")));
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return currentWriteGateSnapshot();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
60
117
|
export function isDepthVerified() {
|
|
61
118
|
return verifiedDepthMilestones.size > 0;
|
|
62
119
|
}
|
|
@@ -68,25 +125,34 @@ export function isMilestoneDepthVerified(milestoneId) {
|
|
|
68
125
|
return false;
|
|
69
126
|
return verifiedDepthMilestones.has(milestoneId);
|
|
70
127
|
}
|
|
128
|
+
export function isMilestoneDepthVerifiedInSnapshot(snapshot, milestoneId) {
|
|
129
|
+
if (!milestoneId)
|
|
130
|
+
return false;
|
|
131
|
+
return snapshot.verifiedDepthMilestones.includes(milestoneId);
|
|
132
|
+
}
|
|
71
133
|
export function isQueuePhaseActive() {
|
|
72
134
|
return activeQueuePhase;
|
|
73
135
|
}
|
|
74
136
|
export function setQueuePhaseActive(active) {
|
|
75
137
|
activeQueuePhase = active;
|
|
138
|
+
persistWriteGateSnapshot();
|
|
76
139
|
}
|
|
77
140
|
export function resetWriteGateState() {
|
|
78
141
|
verifiedDepthMilestones.clear();
|
|
79
142
|
pendingGateId = null;
|
|
143
|
+
persistWriteGateSnapshot();
|
|
80
144
|
}
|
|
81
145
|
export function clearDiscussionFlowState() {
|
|
82
146
|
verifiedDepthMilestones.clear();
|
|
83
147
|
activeQueuePhase = false;
|
|
84
148
|
pendingGateId = null;
|
|
149
|
+
clearPersistedWriteGateSnapshot();
|
|
85
150
|
}
|
|
86
|
-
export function markDepthVerified(milestoneId) {
|
|
151
|
+
export function markDepthVerified(milestoneId, basePath = process.cwd()) {
|
|
87
152
|
if (!milestoneId)
|
|
88
153
|
return;
|
|
89
154
|
verifiedDepthMilestones.add(milestoneId);
|
|
155
|
+
persistWriteGateSnapshot(basePath);
|
|
90
156
|
}
|
|
91
157
|
/**
|
|
92
158
|
* Check whether a question ID matches a recognized gate pattern.
|
|
@@ -114,12 +180,14 @@ function extractContextMilestoneId(inputPath) {
|
|
|
114
180
|
*/
|
|
115
181
|
export function setPendingGate(gateId) {
|
|
116
182
|
pendingGateId = gateId;
|
|
183
|
+
persistWriteGateSnapshot();
|
|
117
184
|
}
|
|
118
185
|
/**
|
|
119
186
|
* Clear the pending gate (called when the user confirms).
|
|
120
187
|
*/
|
|
121
188
|
export function clearPendingGate() {
|
|
122
189
|
pendingGateId = null;
|
|
190
|
+
persistWriteGateSnapshot();
|
|
123
191
|
}
|
|
124
192
|
/**
|
|
125
193
|
* Get the currently pending gate, if any.
|
|
@@ -134,8 +202,11 @@ export function getPendingGate() {
|
|
|
134
202
|
* Returns { block: true, reason } if the tool should be blocked.
|
|
135
203
|
* Read-only tools and ask_user_questions itself are always allowed.
|
|
136
204
|
*/
|
|
137
|
-
export function shouldBlockPendingGate(toolName,
|
|
138
|
-
|
|
205
|
+
export function shouldBlockPendingGate(toolName, milestoneId, queuePhaseActive) {
|
|
206
|
+
return shouldBlockPendingGateInSnapshot(currentWriteGateSnapshot(), toolName, milestoneId, queuePhaseActive);
|
|
207
|
+
}
|
|
208
|
+
export function shouldBlockPendingGateInSnapshot(snapshot, toolName, _milestoneId, _queuePhaseActive) {
|
|
209
|
+
if (!snapshot.pendingGateId)
|
|
139
210
|
return { block: false };
|
|
140
211
|
if (GATE_SAFE_TOOLS.has(toolName))
|
|
141
212
|
return { block: false };
|
|
@@ -145,7 +216,7 @@ export function shouldBlockPendingGate(toolName, _milestoneId, _queuePhaseActive
|
|
|
145
216
|
return {
|
|
146
217
|
block: true,
|
|
147
218
|
reason: [
|
|
148
|
-
`HARD BLOCK: Discussion gate "${pendingGateId}" has not been confirmed by the user.`,
|
|
219
|
+
`HARD BLOCK: Discussion gate "${snapshot.pendingGateId}" has not been confirmed by the user.`,
|
|
149
220
|
`You MUST re-call ask_user_questions with the gate question before making any other tool calls.`,
|
|
150
221
|
`If the previous ask_user_questions call failed, errored, was cancelled, or the user's response`,
|
|
151
222
|
`did not match a provided option, you MUST re-ask — never rationalize past the block.`,
|
|
@@ -157,8 +228,11 @@ export function shouldBlockPendingGate(toolName, _milestoneId, _queuePhaseActive
|
|
|
157
228
|
* Check whether a bash command should be blocked because a discussion gate is pending.
|
|
158
229
|
* Read-only bash commands are allowed; mutating commands are blocked.
|
|
159
230
|
*/
|
|
160
|
-
export function shouldBlockPendingGateBash(command,
|
|
161
|
-
|
|
231
|
+
export function shouldBlockPendingGateBash(command, milestoneId, queuePhaseActive) {
|
|
232
|
+
return shouldBlockPendingGateBashInSnapshot(currentWriteGateSnapshot(), command, milestoneId, queuePhaseActive);
|
|
233
|
+
}
|
|
234
|
+
export function shouldBlockPendingGateBashInSnapshot(snapshot, command, _milestoneId, _queuePhaseActive) {
|
|
235
|
+
if (!snapshot.pendingGateId)
|
|
162
236
|
return { block: false };
|
|
163
237
|
// Allow read-only bash commands
|
|
164
238
|
if (BASH_READ_ONLY_RE.test(command))
|
|
@@ -166,7 +240,7 @@ export function shouldBlockPendingGateBash(command, _milestoneId, _queuePhaseAct
|
|
|
166
240
|
return {
|
|
167
241
|
block: true,
|
|
168
242
|
reason: [
|
|
169
|
-
`HARD BLOCK: Discussion gate "${pendingGateId}" has not been confirmed by the user.`,
|
|
243
|
+
`HARD BLOCK: Discussion gate "${snapshot.pendingGateId}" has not been confirmed by the user.`,
|
|
170
244
|
`You MUST re-call ask_user_questions with the gate question before running mutating commands.`,
|
|
171
245
|
`If the previous ask_user_questions call failed, errored, was cancelled, or the user's response`,
|
|
172
246
|
`did not match a provided option, you MUST re-ask — never rationalize past the block.`,
|
|
@@ -232,6 +306,9 @@ export function shouldBlockContextWrite(toolName, inputPath, milestoneId, _queue
|
|
|
232
306
|
* require the milestone to be depth-verified first.
|
|
233
307
|
*/
|
|
234
308
|
export function shouldBlockContextArtifactSave(artifactType, milestoneId, sliceId) {
|
|
309
|
+
return shouldBlockContextArtifactSaveInSnapshot(currentWriteGateSnapshot(), artifactType, milestoneId, sliceId);
|
|
310
|
+
}
|
|
311
|
+
export function shouldBlockContextArtifactSaveInSnapshot(snapshot, artifactType, milestoneId, sliceId) {
|
|
235
312
|
if (artifactType !== "CONTEXT")
|
|
236
313
|
return { block: false };
|
|
237
314
|
if (sliceId)
|
|
@@ -245,7 +322,7 @@ export function shouldBlockContextArtifactSave(artifactType, milestoneId, sliceI
|
|
|
245
322
|
].join(" "),
|
|
246
323
|
};
|
|
247
324
|
}
|
|
248
|
-
if (
|
|
325
|
+
if (isMilestoneDepthVerifiedInSnapshot(snapshot, milestoneId))
|
|
249
326
|
return { block: false };
|
|
250
327
|
return {
|
|
251
328
|
block: true,
|
|
@@ -271,6 +348,9 @@ export function shouldBlockContextArtifactSave(artifactType, milestoneId, sliceI
|
|
|
271
348
|
* @returns { block, reason } — block=true if the call should be rejected.
|
|
272
349
|
*/
|
|
273
350
|
export function shouldBlockQueueExecution(toolName, input, queuePhaseActive) {
|
|
351
|
+
return shouldBlockQueueExecutionInSnapshot(currentWriteGateSnapshot(), toolName, input, queuePhaseActive);
|
|
352
|
+
}
|
|
353
|
+
export function shouldBlockQueueExecutionInSnapshot(snapshot, toolName, input, queuePhaseActive = snapshot.activeQueuePhase) {
|
|
274
354
|
if (!queuePhaseActive)
|
|
275
355
|
return { block: false };
|
|
276
356
|
// Always-safe tools (read-only, discussion, planning)
|
|
@@ -58,7 +58,7 @@ export const TOP_LEVEL_SUBCOMMANDS = [
|
|
|
58
58
|
{ cmd: "templates", desc: "List available workflow templates" },
|
|
59
59
|
{ cmd: "extensions", desc: "Manage extensions (list, enable, disable, info)" },
|
|
60
60
|
{ cmd: "fast", desc: "Toggle OpenAI service tier (on/off/flex/status)" },
|
|
61
|
-
{ cmd: "mcp", desc: "MCP server status and
|
|
61
|
+
{ cmd: "mcp", desc: "MCP server status, connectivity, and local config bootstrap (status, check, init)" },
|
|
62
62
|
{ cmd: "rethink", desc: "Conversational project reorganization — reorder, park, discard, add milestones" },
|
|
63
63
|
{ cmd: "workflow", desc: "Custom workflow lifecycle (new, run, list, validate, pause, resume)" },
|
|
64
64
|
{ cmd: "codebase", desc: "Generate, refresh, and inspect the codebase map cache (.gsd/CODEBASE.md)" },
|
|
@@ -188,6 +188,7 @@ const NESTED_COMPLETIONS = {
|
|
|
188
188
|
mcp: [
|
|
189
189
|
{ cmd: "status", desc: "Show all MCP server statuses (default)" },
|
|
190
190
|
{ cmd: "check", desc: "Detailed status for a specific server" },
|
|
191
|
+
{ cmd: "init", desc: "Write .mcp.json for the local GSD workflow MCP server" },
|
|
191
192
|
],
|
|
192
193
|
doctor: [
|
|
193
194
|
{ cmd: "fix", desc: "Auto-fix detected issues" },
|
|
@@ -55,7 +55,7 @@ export function showHelp(ctx) {
|
|
|
55
55
|
" /gsd hooks Show post-unit hook configuration",
|
|
56
56
|
" /gsd extensions Manage extensions [list|enable|disable|info]",
|
|
57
57
|
" /gsd fast Toggle OpenAI service tier [on|off|flex|status]",
|
|
58
|
-
" /gsd mcp MCP server status and connectivity [status|check <server
|
|
58
|
+
" /gsd mcp MCP server status and connectivity [status|check <server>|init [dir]]",
|
|
59
59
|
"",
|
|
60
60
|
"MAINTENANCE",
|
|
61
61
|
" /gsd doctor Diagnose and repair .gsd/ state [audit|fix|heal] [scope]",
|
|
@@ -168,6 +168,42 @@ function sortModelsForSelection(models, currentModel) {
|
|
|
168
168
|
return a.id.localeCompare(b.id);
|
|
169
169
|
});
|
|
170
170
|
}
|
|
171
|
+
function buildProviderModelGroups(models, currentModel) {
|
|
172
|
+
const byProvider = new Map();
|
|
173
|
+
for (const model of sortModelsForSelection(models, currentModel)) {
|
|
174
|
+
let group = byProvider.get(model.provider);
|
|
175
|
+
if (!group) {
|
|
176
|
+
group = [];
|
|
177
|
+
byProvider.set(model.provider, group);
|
|
178
|
+
}
|
|
179
|
+
group.push(model);
|
|
180
|
+
}
|
|
181
|
+
return byProvider;
|
|
182
|
+
}
|
|
183
|
+
async function selectModelByProvider(title, models, ctx, currentModel) {
|
|
184
|
+
const byProvider = buildProviderModelGroups(models, currentModel);
|
|
185
|
+
const providerOptions = Array.from(byProvider.entries()).map(([provider, group]) => `${provider} (${group.length} model${group.length === 1 ? "" : "s"})`);
|
|
186
|
+
providerOptions.push("(cancel)");
|
|
187
|
+
const providerChoice = await ctx.ui.select(`${title} — choose provider:`, providerOptions);
|
|
188
|
+
if (!providerChoice || typeof providerChoice !== "string" || providerChoice === "(cancel)")
|
|
189
|
+
return undefined;
|
|
190
|
+
const providerName = providerChoice.replace(/ \(\d+ models?\)$/, "");
|
|
191
|
+
const providerModels = byProvider.get(providerName);
|
|
192
|
+
if (!providerModels || providerModels.length === 0)
|
|
193
|
+
return undefined;
|
|
194
|
+
const optionToModel = new Map();
|
|
195
|
+
const modelOptions = providerModels.map((model) => {
|
|
196
|
+
const isCurrent = currentModel && model.provider === currentModel.provider && model.id === currentModel.id;
|
|
197
|
+
const label = `${isCurrent ? "* " : ""}${model.id}`;
|
|
198
|
+
optionToModel.set(label, model);
|
|
199
|
+
return label;
|
|
200
|
+
});
|
|
201
|
+
modelOptions.push("(cancel)");
|
|
202
|
+
const modelChoice = await ctx.ui.select(`${title} — ${providerName}:`, modelOptions);
|
|
203
|
+
if (!modelChoice || typeof modelChoice !== "string" || modelChoice === "(cancel)")
|
|
204
|
+
return undefined;
|
|
205
|
+
return optionToModel.get(modelChoice);
|
|
206
|
+
}
|
|
171
207
|
async function resolveRequestedModel(query, ctx) {
|
|
172
208
|
const { resolveModelId } = await import("../../auto-model-selection.js");
|
|
173
209
|
const models = ctx.modelRegistry.getAvailable();
|
|
@@ -181,18 +217,7 @@ async function resolveRequestedModel(query, ctx) {
|
|
|
181
217
|
return partialMatches[0];
|
|
182
218
|
if (partialMatches.length === 0 || !ctx.hasUI)
|
|
183
219
|
return undefined;
|
|
184
|
-
|
|
185
|
-
const optionToModel = new Map();
|
|
186
|
-
const options = sorted.map((model) => {
|
|
187
|
-
const label = `${model.provider}/${model.id}`;
|
|
188
|
-
optionToModel.set(label, model);
|
|
189
|
-
return label;
|
|
190
|
-
});
|
|
191
|
-
options.push("(cancel)");
|
|
192
|
-
const choice = await ctx.ui.select(`Multiple models match "${query}" — choose one:`, options);
|
|
193
|
-
if (!choice || typeof choice !== "string" || choice === "(cancel)")
|
|
194
|
-
return undefined;
|
|
195
|
-
return optionToModel.get(choice);
|
|
220
|
+
return selectModelByProvider(`Multiple models match "${query}"`, partialMatches, ctx, ctx.model);
|
|
196
221
|
}
|
|
197
222
|
async function handleModel(trimmedArgs, ctx, pi) {
|
|
198
223
|
const availableModels = ctx.modelRegistry.getAvailable();
|
|
@@ -212,18 +237,7 @@ async function handleModel(trimmedArgs, ctx, pi) {
|
|
|
212
237
|
ctx.ui.notify(`Current model: ${current}\nUsage: /gsd model <provider/model|model-id>`, "info");
|
|
213
238
|
return;
|
|
214
239
|
}
|
|
215
|
-
|
|
216
|
-
const options = sortModelsForSelection(availableModels, ctx.model).map((model) => {
|
|
217
|
-
const isCurrent = ctx.model && model.provider === ctx.model.provider && model.id === ctx.model.id;
|
|
218
|
-
const label = `${isCurrent ? "* " : ""}${model.provider}/${model.id}`;
|
|
219
|
-
optionToModel.set(label, model);
|
|
220
|
-
return label;
|
|
221
|
-
});
|
|
222
|
-
options.push("(cancel)");
|
|
223
|
-
const choice = await ctx.ui.select("Select session model:", options);
|
|
224
|
-
if (!choice || typeof choice !== "string" || choice === "(cancel)")
|
|
225
|
-
return;
|
|
226
|
-
targetModel = optionToModel.get(choice);
|
|
240
|
+
targetModel = await selectModelByProvider("Select session model:", availableModels, ctx, ctx.model);
|
|
227
241
|
}
|
|
228
242
|
else {
|
|
229
243
|
targetModel = await resolveRequestedModel(trimmed, ctx);
|
|
@@ -5,7 +5,14 @@ export function registerGSDCommand(pi) {
|
|
|
5
5
|
getArgumentCompletions: getGsdArgumentCompletions,
|
|
6
6
|
handler: async (args, ctx) => {
|
|
7
7
|
const { handleGSDCommand } = await import("./dispatcher.js");
|
|
8
|
-
await
|
|
8
|
+
const { setStderrLoggingEnabled } = await import("../workflow-logger.js");
|
|
9
|
+
const previousStderrSetting = setStderrLoggingEnabled(false);
|
|
10
|
+
try {
|
|
11
|
+
await handleGSDCommand(args, ctx, pi);
|
|
12
|
+
}
|
|
13
|
+
finally {
|
|
14
|
+
setStderrLoggingEnabled(previousStderrSetting);
|
|
15
|
+
}
|
|
9
16
|
},
|
|
10
17
|
});
|
|
11
18
|
}
|
|
@@ -7,9 +7,26 @@
|
|
|
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
|
import { existsSync, readFileSync } from "node:fs";
|
|
12
|
-
import { join } from "node:path";
|
|
13
|
+
import { join, resolve } from "node:path";
|
|
14
|
+
import { ensureProjectWorkflowMcpConfig } from "./mcp-project-config.js";
|
|
15
|
+
export function formatMcpInitResult(status, configPath, targetPath) {
|
|
16
|
+
const summary = status === "created"
|
|
17
|
+
? "Created project MCP config."
|
|
18
|
+
: status === "updated"
|
|
19
|
+
? "Updated project MCP config."
|
|
20
|
+
: "Project MCP config is already up to date.";
|
|
21
|
+
return [
|
|
22
|
+
summary,
|
|
23
|
+
"",
|
|
24
|
+
`Project: ${targetPath}`,
|
|
25
|
+
`Config: ${configPath}`,
|
|
26
|
+
"",
|
|
27
|
+
"Claude Code can now load the GSD workflow MCP server from this folder.",
|
|
28
|
+
].join("\n");
|
|
29
|
+
}
|
|
13
30
|
function readMcpConfigs() {
|
|
14
31
|
const servers = [];
|
|
15
32
|
const seen = new Set();
|
|
@@ -61,6 +78,7 @@ export function formatMcpStatusReport(servers) {
|
|
|
61
78
|
"No MCP servers configured.",
|
|
62
79
|
"",
|
|
63
80
|
"Add servers to .mcp.json or .gsd/mcp.json to enable MCP integrations.",
|
|
81
|
+
"Tip: run /gsd mcp init . to write the local GSD workflow MCP config.",
|
|
64
82
|
"See: https://modelcontextprotocol.io/quickstart",
|
|
65
83
|
].join("\n");
|
|
66
84
|
}
|
|
@@ -109,11 +127,28 @@ export function formatMcpServerDetail(server) {
|
|
|
109
127
|
* Handle `/gsd mcp [status|check <server>]`.
|
|
110
128
|
*/
|
|
111
129
|
export async function handleMcpStatus(args, ctx) {
|
|
112
|
-
const trimmed = args.trim()
|
|
130
|
+
const trimmed = args.trim();
|
|
131
|
+
const lowered = trimmed.toLowerCase();
|
|
113
132
|
const configs = readMcpConfigs();
|
|
133
|
+
// /gsd mcp init [dir]
|
|
134
|
+
if (!lowered || lowered === "status") {
|
|
135
|
+
// handled below
|
|
136
|
+
}
|
|
137
|
+
else if (lowered === "init" || lowered.startsWith("init ")) {
|
|
138
|
+
const rawPath = trimmed.slice("init".length).trim();
|
|
139
|
+
const targetPath = resolve(rawPath || ".");
|
|
140
|
+
try {
|
|
141
|
+
const result = ensureProjectWorkflowMcpConfig(targetPath);
|
|
142
|
+
ctx.ui.notify(formatMcpInitResult(result.status, result.configPath, targetPath), "info");
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
ctx.ui.notify(`Failed to prepare MCP config for ${targetPath}: ${err instanceof Error ? err.message : String(err)}`, "error");
|
|
146
|
+
}
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
114
149
|
// /gsd mcp check <server>
|
|
115
|
-
if (
|
|
116
|
-
const serverName =
|
|
150
|
+
if (lowered.startsWith("check ")) {
|
|
151
|
+
const serverName = trimmed.slice("check ".length).trim();
|
|
117
152
|
const config = configs.find((c) => c.name === serverName);
|
|
118
153
|
if (!config) {
|
|
119
154
|
const available = configs.map((c) => c.name).join(", ") || "(none)";
|
|
@@ -149,7 +184,7 @@ export async function handleMcpStatus(args, ctx) {
|
|
|
149
184
|
return;
|
|
150
185
|
}
|
|
151
186
|
// /gsd mcp or /gsd mcp status
|
|
152
|
-
if (!
|
|
187
|
+
if (!lowered || lowered === "status") {
|
|
153
188
|
// Build status for each server
|
|
154
189
|
const statuses = [];
|
|
155
190
|
for (const config of configs) {
|
|
@@ -181,7 +216,8 @@ export async function handleMcpStatus(args, ctx) {
|
|
|
181
216
|
return;
|
|
182
217
|
}
|
|
183
218
|
// Unknown subcommand
|
|
184
|
-
ctx.ui.notify("Usage: /gsd mcp [status|check <server
|
|
219
|
+
ctx.ui.notify("Usage: /gsd mcp [status|check <server>|init [dir]]\n\n" +
|
|
185
220
|
" status Show all MCP server statuses (default)\n" +
|
|
186
|
-
" check <server> Detailed status for a specific server"
|
|
221
|
+
" check <server> Detailed status for a specific server\n" +
|
|
222
|
+
" init [dir] Write .mcp.json for the local GSD workflow MCP server", "warning");
|
|
187
223
|
}
|
|
@@ -8,7 +8,7 @@ import { deriveState, isMilestoneComplete } from "./state.js";
|
|
|
8
8
|
import { listWorktrees, resolveGitDir, worktreesDir } from "./worktree-manager.js";
|
|
9
9
|
import { abortAndReset } from "./git-self-heal.js";
|
|
10
10
|
import { RUNTIME_EXCLUSION_PATHS, resolveMilestoneIntegrationBranch, writeIntegrationBranch } from "./git-service.js";
|
|
11
|
-
import { nativeIsRepo, nativeWorktreeList, nativeWorktreeRemove, nativeBranchList, nativeBranchDelete, nativeLsFiles, nativeRmCached, nativeHasChanges, nativeLastCommitEpoch, nativeGetCurrentBranch,
|
|
11
|
+
import { nativeIsRepo, nativeWorktreeList, nativeWorktreeRemove, nativeBranchList, nativeBranchDelete, nativeLsFiles, nativeRmCached, nativeHasChanges, nativeLastCommitEpoch, nativeGetCurrentBranch, nativeAddTracked, nativeCommit } from "./native-git-bridge.js";
|
|
12
12
|
import { getAllWorktreeHealth } from "./worktree-health.js";
|
|
13
13
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
14
14
|
/**
|
|
@@ -380,19 +380,19 @@ export async function checkGitHealth(basePath, issues, fixesApplied, shouldFix,
|
|
|
380
380
|
code: "stale_uncommitted_changes",
|
|
381
381
|
scope: "project",
|
|
382
382
|
unitId: "project",
|
|
383
|
-
message: `Uncommitted changes detected with no commit in ${mins} minute${mins === 1 ? "" : "s"} (threshold: ${thresholdMinutes}m). Snapshotting
|
|
383
|
+
message: `Uncommitted changes detected with no commit in ${mins} minute${mins === 1 ? "" : "s"} (threshold: ${thresholdMinutes}m). Snapshotting tracked files.`,
|
|
384
384
|
fixable: true,
|
|
385
385
|
});
|
|
386
386
|
if (shouldFix("stale_uncommitted_changes")) {
|
|
387
387
|
try {
|
|
388
|
-
|
|
388
|
+
nativeAddTracked(basePath);
|
|
389
389
|
const commitMsg = `gsd snapshot: uncommitted changes after ${mins}m inactivity`;
|
|
390
390
|
const result = nativeCommit(basePath, commitMsg);
|
|
391
391
|
if (result) {
|
|
392
392
|
fixesApplied.push(`created gsd snapshot after ${mins}m of uncommitted changes`);
|
|
393
393
|
}
|
|
394
394
|
else {
|
|
395
|
-
fixesApplied.push("gsd snapshot skipped — nothing to commit after staging
|
|
395
|
+
fixesApplied.push("gsd snapshot skipped — nothing to commit after staging tracked files");
|
|
396
396
|
}
|
|
397
397
|
}
|
|
398
398
|
catch {
|
|
@@ -20,8 +20,8 @@ import { readCrashLock, isLockProcessAlive, clearLock } from "./crash-recovery.j
|
|
|
20
20
|
import { abortAndReset } from "./git-self-heal.js";
|
|
21
21
|
import { rebuildState } from "./doctor.js";
|
|
22
22
|
import { deriveState } from "./state.js";
|
|
23
|
-
import {
|
|
24
|
-
import { nativeIsRepo, nativeHasChanges, nativeLastCommitEpoch, nativeGetCurrentBranch,
|
|
23
|
+
import { resolveMilestoneIntegrationBranch } from "./git-service.js";
|
|
24
|
+
import { nativeIsRepo, nativeHasChanges, nativeLastCommitEpoch, nativeGetCurrentBranch, nativeAddTracked, nativeCommit } from "./native-git-bridge.js";
|
|
25
25
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
26
26
|
import { runEnvironmentChecks } from "./doctor-environment.js";
|
|
27
27
|
/** In-memory health history for the current auto-mode session. */
|
|
@@ -247,7 +247,7 @@ export async function preDispatchHealthGate(basePath) {
|
|
|
247
247
|
if (minutesSinceCommit >= thresholdMinutes) {
|
|
248
248
|
const mins = Math.floor(minutesSinceCommit);
|
|
249
249
|
try {
|
|
250
|
-
|
|
250
|
+
nativeAddTracked(basePath);
|
|
251
251
|
const commitMsg = `gsd snapshot: pre-dispatch, uncommitted changes after ${mins}m inactivity`;
|
|
252
252
|
const result = nativeCommit(basePath, commitMsg);
|
|
253
253
|
if (result) {
|
|
@@ -7,6 +7,7 @@ import { resolveMilestoneFile, resolveMilestonePath, resolveSliceFile, resolveSl
|
|
|
7
7
|
import { deriveState, isMilestoneComplete } from "./state.js";
|
|
8
8
|
import { invalidateAllCaches } from "./cache.js";
|
|
9
9
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
10
|
+
import { isClosedStatus } from "./status-guards.js";
|
|
10
11
|
import { GLOBAL_STATE_CODES } from "./doctor-types.js";
|
|
11
12
|
import { checkGitHealth, checkRuntimeHealth, checkGlobalHealth, checkEngineHealth } from "./doctor-checks.js";
|
|
12
13
|
import { checkEnvironmentHealth } from "./doctor-environment.js";
|
|
@@ -443,8 +444,9 @@ export async function runGSDDoctor(basePath, options) {
|
|
|
443
444
|
slices = dbSlices.map(s => ({
|
|
444
445
|
id: s.id,
|
|
445
446
|
title: s.title,
|
|
446
|
-
done: s.status
|
|
447
|
+
done: isClosedStatus(s.status),
|
|
447
448
|
pending: s.status === "pending",
|
|
449
|
+
skipped: s.status === "skipped",
|
|
448
450
|
risk: (s.risk || "medium"),
|
|
449
451
|
depends: s.depends,
|
|
450
452
|
demo: s.demo,
|
|
@@ -541,8 +543,9 @@ export async function runGSDDoctor(basePath, options) {
|
|
|
541
543
|
const slicePath = resolveSlicePath(basePath, milestoneId, slice.id);
|
|
542
544
|
if (!slicePath) {
|
|
543
545
|
// Pending slices haven't been planned yet — directories are created
|
|
544
|
-
// lazily by ensurePreconditions() at dispatch time.
|
|
545
|
-
|
|
546
|
+
// lazily by ensurePreconditions() at dispatch time. Skipped slices are
|
|
547
|
+
// intentionally allowed to remain summary-less and directory-less.
|
|
548
|
+
if (slice.pending || slice.skipped)
|
|
546
549
|
continue;
|
|
547
550
|
const expectedPath = relSlicePath(basePath, milestoneId, slice.id);
|
|
548
551
|
issues.push({
|
|
@@ -566,7 +569,8 @@ export async function runGSDDoctor(basePath, options) {
|
|
|
566
569
|
const tasksDir = resolveTasksDir(basePath, milestoneId, slice.id);
|
|
567
570
|
if (!tasksDir) {
|
|
568
571
|
// Pending slices haven't been planned yet — tasks/ is created on demand.
|
|
569
|
-
|
|
572
|
+
// Skipped slices may legitimately never create tasks/.
|
|
573
|
+
if (slice.pending || slice.skipped)
|
|
570
574
|
continue;
|
|
571
575
|
issues.push({
|
|
572
576
|
severity: slice.done ? "warning" : "error",
|
|
@@ -701,6 +701,7 @@ let currentDb = null;
|
|
|
701
701
|
let currentPath = null;
|
|
702
702
|
let currentPid = 0;
|
|
703
703
|
let _exitHandlerRegistered = false;
|
|
704
|
+
let _dbOpenAttempted = false;
|
|
704
705
|
export function getDbProvider() {
|
|
705
706
|
loadProvider();
|
|
706
707
|
return providerName;
|
|
@@ -708,7 +709,17 @@ export function getDbProvider() {
|
|
|
708
709
|
export function isDbAvailable() {
|
|
709
710
|
return currentDb !== null;
|
|
710
711
|
}
|
|
712
|
+
/**
|
|
713
|
+
* Returns true if openDatabase() has been called at least once this session.
|
|
714
|
+
* Used to distinguish "DB not yet initialized" from "DB genuinely unavailable"
|
|
715
|
+
* so that early callers (e.g. before_agent_start context injection) don't
|
|
716
|
+
* trigger a false degraded-mode warning.
|
|
717
|
+
*/
|
|
718
|
+
export function wasDbOpenAttempted() {
|
|
719
|
+
return _dbOpenAttempted;
|
|
720
|
+
}
|
|
711
721
|
export function openDatabase(path) {
|
|
722
|
+
_dbOpenAttempted = true;
|
|
712
723
|
if (currentDb && currentPath !== path)
|
|
713
724
|
closeDatabase();
|
|
714
725
|
if (currentDb && currentPath === path)
|
|
@@ -14,7 +14,8 @@ import { buildSkillActivationBlock } from "./auto-prompts.js";
|
|
|
14
14
|
import { deriveState } from "./state.js";
|
|
15
15
|
import { invalidateAllCaches } from "./cache.js";
|
|
16
16
|
import { startAuto } from "./auto.js";
|
|
17
|
-
import {
|
|
17
|
+
import { clearLock } from "./crash-recovery.js";
|
|
18
|
+
import { assessInterruptedSession, formatInterruptedSessionRunningMessage, formatInterruptedSessionSummary, } from "./interrupted-session.js";
|
|
18
19
|
import { listUnitRuntimeRecords, clearUnitRuntimeRecord } from "./unit-runtime.js";
|
|
19
20
|
import { resolveExpectedArtifactPath } from "./auto.js";
|
|
20
21
|
import { gsdRoot, milestonesDir, resolveMilestoneFile, resolveSliceFile, resolveSlicePath, resolveGsdRootFile, relGsdRootFile, relMilestoneFile, relSliceFile, } from "./paths.js";
|
|
@@ -34,6 +35,7 @@ import { findMilestoneIds, nextMilestoneId, reserveMilestoneId, getReservedMiles
|
|
|
34
35
|
import { parkMilestone, discardMilestone } from "./milestone-actions.js";
|
|
35
36
|
import { selectAndApplyModel } from "./auto-model-selection.js";
|
|
36
37
|
import { DISCUSS_TOOLS_ALLOWLIST } from "./constants.js";
|
|
38
|
+
import { getWorkflowTransportSupportError, getRequiredWorkflowToolsForGuidedUnit, } from "./workflow-mcp.js";
|
|
37
39
|
import { runPreparation, formatCodebaseBrief, formatPriorContextBrief, formatEcosystemBrief, } from "./preparation.js";
|
|
38
40
|
// ─── Preparation result storage ─────────────────────────────────────────────
|
|
39
41
|
// Stores the most recent preparation result for injection into discuss prompts.
|
|
@@ -166,14 +168,9 @@ export function checkAutoStartAfterDiscuss() {
|
|
|
166
168
|
}
|
|
167
169
|
// Gate 4: Discussion manifest process verification (multi-milestone only)
|
|
168
170
|
// The LLM writes DISCUSSION-MANIFEST.json after each Phase 3 gate decision.
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
+
// When it exists, validate it before auto-starting. Project history alone is
|
|
172
|
+
// not a reliable signal for the current discussion mode.
|
|
171
173
|
const manifestPath = join(gsdRoot(basePath), "DISCUSSION-MANIFEST.json");
|
|
172
|
-
const requiresManifest = projectIds.length > 1 || findMilestoneIds(basePath).length > 1;
|
|
173
|
-
if (requiresManifest && !existsSync(manifestPath)) {
|
|
174
|
-
ctx.ui.notify("Multi-milestone discussion manifest is missing. Auto-start will remain paused until the manifest is written.", "warning");
|
|
175
|
-
return false;
|
|
176
|
-
}
|
|
177
174
|
if (existsSync(manifestPath)) {
|
|
178
175
|
try {
|
|
179
176
|
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
@@ -259,6 +256,21 @@ async function dispatchWorkflow(pi, note, customType = "gsd-run", ctx, unitType)
|
|
|
259
256
|
routing: result.routing,
|
|
260
257
|
});
|
|
261
258
|
}
|
|
259
|
+
const compatibilityError = getWorkflowTransportSupportError(result.appliedModel?.provider ?? ctx.model?.provider, getRequiredWorkflowToolsForGuidedUnit(unitType), {
|
|
260
|
+
projectRoot: process.cwd(),
|
|
261
|
+
surface: "guided flow",
|
|
262
|
+
unitType,
|
|
263
|
+
authMode: result.appliedModel?.provider
|
|
264
|
+
? ctx.modelRegistry.getProviderAuthMode(result.appliedModel.provider)
|
|
265
|
+
: ctx.model?.provider
|
|
266
|
+
? ctx.modelRegistry.getProviderAuthMode(ctx.model.provider)
|
|
267
|
+
: undefined,
|
|
268
|
+
baseUrl: result.appliedModel?.baseUrl ?? ctx.model?.baseUrl,
|
|
269
|
+
});
|
|
270
|
+
if (compatibilityError) {
|
|
271
|
+
ctx.ui.notify(compatibilityError, "error");
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
262
274
|
}
|
|
263
275
|
// Scope tools for discuss flows (#2949).
|
|
264
276
|
// Providers with grammar-based constrained decoding (xAI/Grok) return
|
|
@@ -1086,33 +1098,46 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1086
1098
|
untrackRuntimeFiles(basePath);
|
|
1087
1099
|
// ── Self-heal stale runtime records from crashed auto-mode sessions ──
|
|
1088
1100
|
selfHealRuntimeRecords(basePath, ctx);
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1101
|
+
const interrupted = await assessInterruptedSession(basePath);
|
|
1102
|
+
if (interrupted.classification === "running") {
|
|
1103
|
+
ctx.ui.notify(formatInterruptedSessionRunningMessage(interrupted), "error");
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
1106
|
+
if (interrupted.classification === "stale") {
|
|
1094
1107
|
clearLock(basePath);
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
if (!isBootstrapCrash) {
|
|
1102
|
-
const resume = await showNextAction(ctx, {
|
|
1103
|
-
title: "GSD — Interrupted Session Detected",
|
|
1104
|
-
summary: [formatCrashInfo(crashLock)],
|
|
1105
|
-
actions: [
|
|
1106
|
-
{ id: "resume", label: "Resume with /gsd auto", description: "Pick up where it left off", recommended: true },
|
|
1107
|
-
{ id: "continue", label: "Continue manually", description: "Open the wizard as normal" },
|
|
1108
|
-
],
|
|
1109
|
-
});
|
|
1110
|
-
if (resume === "resume") {
|
|
1111
|
-
await startAuto(ctx, pi, basePath, false);
|
|
1112
|
-
return;
|
|
1108
|
+
if (interrupted.pausedSession) {
|
|
1109
|
+
try {
|
|
1110
|
+
unlinkSync(join(gsdRoot(basePath), "runtime", "paused-session.json"));
|
|
1111
|
+
}
|
|
1112
|
+
catch (e) {
|
|
1113
|
+
logWarning("guided", `stale pause file cleanup failed: ${e.message}`, { file: "guided-flow.ts" });
|
|
1113
1114
|
}
|
|
1114
1115
|
}
|
|
1115
1116
|
}
|
|
1117
|
+
else if (interrupted.classification === "recoverable") {
|
|
1118
|
+
if (interrupted.lock)
|
|
1119
|
+
clearLock(basePath);
|
|
1120
|
+
const resumeLabel = interrupted.pausedSession?.stepMode
|
|
1121
|
+
? "Resume with /gsd next"
|
|
1122
|
+
: "Resume with /gsd auto";
|
|
1123
|
+
const resume = await showNextAction(ctx, {
|
|
1124
|
+
title: "GSD — Interrupted Session Detected",
|
|
1125
|
+
summary: formatInterruptedSessionSummary(interrupted),
|
|
1126
|
+
actions: [
|
|
1127
|
+
{ id: "resume", label: resumeLabel, description: "Pick up where it left off", recommended: true },
|
|
1128
|
+
{ id: "continue", label: "Continue manually", description: "Open the wizard as normal" },
|
|
1129
|
+
],
|
|
1130
|
+
});
|
|
1131
|
+
if (resume === "resume") {
|
|
1132
|
+
await startAuto(ctx, pi, basePath, false, {
|
|
1133
|
+
interrupted,
|
|
1134
|
+
step: interrupted.pausedSession?.stepMode ?? false,
|
|
1135
|
+
});
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
// Always derive from the project root — the assessment may have derived
|
|
1140
|
+
// state from a worktree path that was cleaned up in the stale branch above.
|
|
1116
1141
|
const state = await deriveState(basePath);
|
|
1117
1142
|
// Rebuild STATE.md from derived state before any dispatch (#3475).
|
|
1118
1143
|
try {
|