gsd-pi 2.67.0-dev.2142d3e → 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 +152 -70
- package/dist/resources/extensions/gsd/auto/session.js +10 -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/system-context.js +7 -2
- package/dist/resources/extensions/gsd/commands/catalog.js +2 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -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 +40 -31
- package/dist/resources/extensions/gsd/init-wizard.js +15 -12
- 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/workflow-mcp.js +90 -19
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
- 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 +14 -14
- 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/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/workflow-tools.ts +13 -2
- 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 +190 -93
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +89 -116
- package/src/resources/extensions/gsd/auto/session.ts +10 -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/system-context.ts +8 -2
- package/src/resources/extensions/gsd/commands/catalog.ts +2 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -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 +42 -36
- package/src/resources/extensions/gsd/init-wizard.ts +17 -11
- 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/crash-recovery.test.ts +380 -2
- 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-mcp.test.ts +212 -13
- package/src/resources/extensions/gsd/workflow-mcp.ts +106 -19
- 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/src/resources/extensions/gsd/tests/init-bootstrap-completeness.test.ts +0 -121
- /package/dist/web/standalone/.next/static/{xR6qurkuYSvyjBjRyJLxG → WMDT_0C0XDkBKtsAI_AX4}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{xR6qurkuYSvyjBjRyJLxG → WMDT_0C0XDkBKtsAI_AX4}/_ssgManifest.js +0 -0
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { deriveState } from "./state.js";
|
|
13
13
|
import { parseUnitId } from "./unit-id.js";
|
|
14
|
+
import { assessInterruptedSession, readPausedSessionMetadata, } from "./interrupted-session.js";
|
|
14
15
|
import { getManifestStatus } from "./files.js";
|
|
15
16
|
export { inlinePriorMilestoneSummary } from "./files.js";
|
|
16
17
|
import { collectSecretsFromManifest } from "../get-secrets-from-user.js";
|
|
@@ -18,7 +19,7 @@ import { gsdRoot, resolveMilestoneFile, resolveMilestonePath, resolveDir, milest
|
|
|
18
19
|
import { invalidateAllCaches } from "./cache.js";
|
|
19
20
|
import { clearActivityLogState } from "./activity-log.js";
|
|
20
21
|
import { synthesizeCrashRecovery, getDeepDiagnostic, readActiveMilestoneId, } from "./session-forensics.js";
|
|
21
|
-
import { writeLock, clearLock, readCrashLock, isLockProcessAlive, } from "./crash-recovery.js";
|
|
22
|
+
import { writeLock, clearLock, readCrashLock, isLockProcessAlive, formatCrashInfo, } from "./crash-recovery.js";
|
|
22
23
|
import { acquireSessionLock, getSessionLockStatus, releaseSessionLock, updateSessionLock, } from "./session-lock.js";
|
|
23
24
|
import { resolveAutoSupervisorConfig, loadEffectiveGSDPreferences, getIsolationMode, } from "./preferences.js";
|
|
24
25
|
import { sendDesktopNotification } from "./notifications.js";
|
|
@@ -34,7 +35,8 @@ import { clearSkillSnapshot } from "./skill-discovery.js";
|
|
|
34
35
|
import { captureAvailableSkills, resetSkillTelemetry, } from "./skill-telemetry.js";
|
|
35
36
|
import { getRtkSessionSavings } from "../shared/rtk-session-stats.js";
|
|
36
37
|
import { initMetrics, resetMetrics, getLedger, getProjectTotals, formatCost, formatTokenCount, } from "./metrics.js";
|
|
37
|
-
import {
|
|
38
|
+
import { logWarning } from "./workflow-logger.js";
|
|
39
|
+
import { homedir } from "node:os";
|
|
38
40
|
import { join } from "node:path";
|
|
39
41
|
import { readFileSync, existsSync, mkdirSync, writeFileSync, unlinkSync } from "node:fs";
|
|
40
42
|
import { atomicWriteSync } from "./atomic-write.js";
|
|
@@ -79,6 +81,27 @@ export { MAX_UNIT_DISPATCHES, STUB_RECOVERY_THRESHOLD, MAX_LIFETIME_DISPATCHES,
|
|
|
79
81
|
const s = new AutoSession();
|
|
80
82
|
/** Throttle STATE.md rebuilds — at most once per 30 seconds */
|
|
81
83
|
const STATE_REBUILD_MIN_INTERVAL_MS = 30_000;
|
|
84
|
+
function captureProjectRootEnv(projectRoot) {
|
|
85
|
+
if (!s.projectRootEnvCaptured) {
|
|
86
|
+
s.hadProjectRootEnv = Object.prototype.hasOwnProperty.call(process.env, "GSD_PROJECT_ROOT");
|
|
87
|
+
s.previousProjectRootEnv = process.env.GSD_PROJECT_ROOT ?? null;
|
|
88
|
+
s.projectRootEnvCaptured = true;
|
|
89
|
+
}
|
|
90
|
+
process.env.GSD_PROJECT_ROOT = projectRoot;
|
|
91
|
+
}
|
|
92
|
+
function restoreProjectRootEnv() {
|
|
93
|
+
if (!s.projectRootEnvCaptured)
|
|
94
|
+
return;
|
|
95
|
+
if (s.hadProjectRootEnv && s.previousProjectRootEnv !== null) {
|
|
96
|
+
process.env.GSD_PROJECT_ROOT = s.previousProjectRootEnv;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
delete process.env.GSD_PROJECT_ROOT;
|
|
100
|
+
}
|
|
101
|
+
s.previousProjectRootEnv = null;
|
|
102
|
+
s.hadProjectRootEnv = false;
|
|
103
|
+
s.projectRootEnvCaptured = false;
|
|
104
|
+
}
|
|
82
105
|
export function shouldUseWorktreeIsolation() {
|
|
83
106
|
const prefs = loadEffectiveGSDPreferences()?.preferences?.git;
|
|
84
107
|
if (prefs?.isolation === "worktree")
|
|
@@ -315,6 +338,7 @@ function handleLostSessionLock(ctx, lockStatus) {
|
|
|
315
338
|
s.active = false;
|
|
316
339
|
s.paused = false;
|
|
317
340
|
clearUnitTimeout();
|
|
341
|
+
restoreProjectRootEnv();
|
|
318
342
|
deregisterSigtermHandler();
|
|
319
343
|
clearCmuxSidebar(loadEffectiveGSDPreferences()?.preferences);
|
|
320
344
|
const base = lockBase();
|
|
@@ -345,6 +369,7 @@ function cleanupAfterLoopExit(ctx) {
|
|
|
345
369
|
s.currentUnit = null;
|
|
346
370
|
s.active = false;
|
|
347
371
|
clearUnitTimeout();
|
|
372
|
+
restoreProjectRootEnv();
|
|
348
373
|
// Clear crash lock and release session lock so the next `/gsd next` does
|
|
349
374
|
// not see a stale lock with the current PID and treat it as a "remote"
|
|
350
375
|
// session (which would cause it to SIGTERM itself). (#2730)
|
|
@@ -601,6 +626,7 @@ export async function stopAuto(ctx, pi, reason) {
|
|
|
601
626
|
ctx?.ui.setStatus("gsd-auto", undefined);
|
|
602
627
|
ctx?.ui.setWidget("gsd-progress", undefined);
|
|
603
628
|
ctx?.ui.setFooter(undefined);
|
|
629
|
+
restoreProjectRootEnv();
|
|
604
630
|
// Reset all session state in one call
|
|
605
631
|
s.reset();
|
|
606
632
|
}
|
|
@@ -641,6 +667,8 @@ export async function pauseAuto(ctx, _pi, _errorContext) {
|
|
|
641
667
|
stepMode: s.stepMode,
|
|
642
668
|
pausedAt: new Date().toISOString(),
|
|
643
669
|
sessionFile: s.pausedSessionFile,
|
|
670
|
+
unitType: s.currentUnit?.type ?? undefined,
|
|
671
|
+
unitId: s.currentUnit?.id ?? undefined,
|
|
644
672
|
activeEngineId: s.activeEngineId,
|
|
645
673
|
activeRunDir: s.activeRunDir,
|
|
646
674
|
autoStartTime: s.autoStartTime,
|
|
@@ -674,6 +702,7 @@ export async function pauseAuto(ctx, _pi, _errorContext) {
|
|
|
674
702
|
_resetPendingResolve();
|
|
675
703
|
s.active = false;
|
|
676
704
|
s.paused = true;
|
|
705
|
+
restoreProjectRootEnv();
|
|
677
706
|
s.pendingVerificationRetry = null;
|
|
678
707
|
s.verificationRetryCount.clear();
|
|
679
708
|
ctx?.ui.setStatus("gsd-auto", "paused");
|
|
@@ -828,38 +857,54 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
828
857
|
return;
|
|
829
858
|
}
|
|
830
859
|
const requestedStepMode = options?.step ?? false;
|
|
860
|
+
const interruptedAssessment = options?.interrupted ?? null;
|
|
831
861
|
// Escape stale worktree cwd from a previous milestone (#608).
|
|
832
862
|
base = escapeStaleWorktree(base);
|
|
863
|
+
const freshStartAssessment = interruptedAssessment
|
|
864
|
+
?? await assessInterruptedSession(base);
|
|
865
|
+
if (freshStartAssessment.classification === "running") {
|
|
866
|
+
const pid = freshStartAssessment.lock?.pid;
|
|
867
|
+
ctx.ui.notify(pid
|
|
868
|
+
? `Another auto-mode session (PID ${pid}) appears to be running.\nStop it with \`kill ${pid}\` before starting a new session.`
|
|
869
|
+
: "Another auto-mode session appears to be running.", "error");
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
833
872
|
// If resuming from paused state, just re-activate and dispatch next unit.
|
|
834
873
|
// Check persisted paused-session first (#1383) — survives /exit.
|
|
835
874
|
if (!s.paused) {
|
|
836
875
|
try {
|
|
876
|
+
const meta = freshStartAssessment.pausedSession ?? readPausedSessionMetadata(base);
|
|
837
877
|
const pausedPath = join(gsdRoot(base), "runtime", "paused-session.json");
|
|
838
|
-
if (
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
// Don't delete pause file yet — defer until lock is acquired.
|
|
849
|
-
// If lock fails, the file must survive for retry.
|
|
850
|
-
s.pausedSessionFile = pausedPath;
|
|
851
|
-
ctx.ui.notify(`Resuming paused custom workflow${meta.activeRunDir ? ` (${meta.activeRunDir})` : ""}.`, "info");
|
|
878
|
+
if (meta?.activeEngineId && meta.activeEngineId !== "dev") {
|
|
879
|
+
// Custom workflow resume — restore engine state
|
|
880
|
+
s.activeEngineId = meta.activeEngineId;
|
|
881
|
+
s.activeRunDir = meta.activeRunDir ?? null;
|
|
882
|
+
s.originalBasePath = meta.originalBasePath || base;
|
|
883
|
+
s.stepMode = meta.stepMode ?? requestedStepMode;
|
|
884
|
+
s.autoStartTime = meta.autoStartTime || Date.now();
|
|
885
|
+
s.paused = true;
|
|
886
|
+
try {
|
|
887
|
+
unlinkSync(pausedPath);
|
|
852
888
|
}
|
|
853
|
-
|
|
889
|
+
catch (e) {
|
|
890
|
+
logWarning("session", `pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
|
|
891
|
+
}
|
|
892
|
+
ctx.ui.notify(`Resuming paused custom workflow${meta.activeRunDir ? ` (${meta.activeRunDir})` : ""}.`, "info");
|
|
893
|
+
}
|
|
894
|
+
else if (meta?.milestoneId) {
|
|
895
|
+
const shouldResumePausedSession = freshStartAssessment.classification === "recoverable"
|
|
896
|
+
&& (freshStartAssessment.hasResumableDiskState
|
|
897
|
+
|| !!freshStartAssessment.recoveryPrompt
|
|
898
|
+
|| !!freshStartAssessment.lock);
|
|
899
|
+
if (shouldResumePausedSession) {
|
|
854
900
|
// Validate the milestone still exists and isn't already complete (#1664).
|
|
855
901
|
const mDir = resolveMilestonePath(base, meta.milestoneId);
|
|
856
902
|
const summaryFile = resolveMilestoneFile(base, meta.milestoneId, "SUMMARY");
|
|
857
903
|
if (!mDir || summaryFile) {
|
|
858
|
-
// Stale milestone — clean up and fall through to fresh bootstrap
|
|
859
904
|
try {
|
|
860
905
|
unlinkSync(pausedPath);
|
|
861
906
|
}
|
|
862
|
-
catch (err) {
|
|
907
|
+
catch (err) {
|
|
863
908
|
logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
|
|
864
909
|
}
|
|
865
910
|
ctx.ui.notify(`Paused milestone ${meta.milestoneId} is ${!mDir ? "missing" : "already complete"}. Starting fresh.`, "info");
|
|
@@ -868,12 +913,26 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
868
913
|
s.currentMilestoneId = meta.milestoneId;
|
|
869
914
|
s.originalBasePath = meta.originalBasePath || base;
|
|
870
915
|
s.stepMode = meta.stepMode ?? requestedStepMode;
|
|
916
|
+
s.pausedSessionFile = meta.sessionFile ?? null;
|
|
917
|
+
s.pausedUnitType = meta.unitType ?? null;
|
|
918
|
+
s.pausedUnitId = meta.unitId ?? null;
|
|
871
919
|
s.autoStartTime = meta.autoStartTime || Date.now();
|
|
872
920
|
s.paused = true;
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
921
|
+
try {
|
|
922
|
+
unlinkSync(pausedPath);
|
|
923
|
+
}
|
|
924
|
+
catch (e) {
|
|
925
|
+
logWarning("session", `pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
|
|
926
|
+
}
|
|
927
|
+
ctx.ui.notify(`Resuming paused session for ${meta.milestoneId}${meta.worktreePath && existsSync(meta.worktreePath) ? ` (worktree)` : ""}.`, "info");
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
else if (existsSync(pausedPath)) {
|
|
931
|
+
try {
|
|
932
|
+
unlinkSync(pausedPath);
|
|
933
|
+
}
|
|
934
|
+
catch (e) {
|
|
935
|
+
logWarning("session", `stale pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
|
|
877
936
|
}
|
|
878
937
|
}
|
|
879
938
|
}
|
|
@@ -882,6 +941,30 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
882
941
|
// Malformed or missing — proceed with fresh bootstrap
|
|
883
942
|
logWarning("session", `paused-session restore failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
|
|
884
943
|
}
|
|
944
|
+
// Guard against zero/missing autoStartTime after resume (#3585)
|
|
945
|
+
if (!s.autoStartTime || s.autoStartTime <= 0)
|
|
946
|
+
s.autoStartTime = Date.now();
|
|
947
|
+
}
|
|
948
|
+
if (!s.paused) {
|
|
949
|
+
s.stepMode = requestedStepMode;
|
|
950
|
+
}
|
|
951
|
+
if (freshStartAssessment.lock) {
|
|
952
|
+
clearLock(base);
|
|
953
|
+
}
|
|
954
|
+
if (!s.paused) {
|
|
955
|
+
s.pendingCrashRecovery =
|
|
956
|
+
freshStartAssessment.classification === "recoverable"
|
|
957
|
+
? freshStartAssessment.recoveryPrompt
|
|
958
|
+
: null;
|
|
959
|
+
if (freshStartAssessment.classification === "recoverable" && freshStartAssessment.lock) {
|
|
960
|
+
const info = formatCrashInfo(freshStartAssessment.lock);
|
|
961
|
+
if (freshStartAssessment.recoveryToolCallCount > 0) {
|
|
962
|
+
ctx.ui.notify(`${info}\nRecovered ${freshStartAssessment.recoveryToolCallCount} tool calls from crashed session. Resuming with full context.`, "warning");
|
|
963
|
+
}
|
|
964
|
+
else if (freshStartAssessment.hasResumableDiskState) {
|
|
965
|
+
ctx.ui.notify(`${info}\nResuming from disk state.`, "warning");
|
|
966
|
+
}
|
|
967
|
+
}
|
|
885
968
|
}
|
|
886
969
|
if (s.paused) {
|
|
887
970
|
const resumeLock = acquireSessionLock(base);
|
|
@@ -906,29 +989,19 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
906
989
|
s.active = true;
|
|
907
990
|
s.verbose = verboseMode;
|
|
908
991
|
s.stepMode = requestedStepMode;
|
|
909
|
-
|
|
910
|
-
// when resuming from a provider-error pause. The resume callback receives
|
|
911
|
-
// an ExtensionContext (from the agent_end hook) which lacks newSession —
|
|
912
|
-
// using it would crash runUnit with "newSession is not a function".
|
|
913
|
-
// Only override if the new ctx actually has newSession (user-initiated resume).
|
|
914
|
-
if ("newSession" in ctx && typeof ctx.newSession === "function") {
|
|
915
|
-
s.cmdCtx = ctx;
|
|
916
|
-
}
|
|
917
|
-
else if (!s.cmdCtx) {
|
|
918
|
-
// No saved cmdCtx — this shouldn't happen, but handle gracefully
|
|
919
|
-
s.cmdCtx = ctx;
|
|
920
|
-
}
|
|
921
|
-
// else: keep existing s.cmdCtx which has the real newSession
|
|
992
|
+
s.cmdCtx = ctx;
|
|
922
993
|
s.basePath = base;
|
|
923
|
-
setLogBasePath(base);
|
|
924
|
-
if (!s.autoStartTime || s.autoStartTime <= 0)
|
|
925
|
-
s.autoStartTime = Date.now();
|
|
926
994
|
s.unitDispatchCount.clear();
|
|
927
995
|
s.unitLifetimeDispatches.clear();
|
|
928
996
|
if (!getLedger())
|
|
929
997
|
initMetrics(base);
|
|
930
998
|
if (s.currentMilestoneId)
|
|
931
999
|
setActiveMilestoneId(base, s.currentMilestoneId);
|
|
1000
|
+
// Re-register health level notification callback lost across process restart
|
|
1001
|
+
setLevelChangeCallback((_from, to, summary) => {
|
|
1002
|
+
const level = to === "red" ? "error" : to === "yellow" ? "warning" : "info";
|
|
1003
|
+
ctx.ui.notify(summary, level);
|
|
1004
|
+
});
|
|
932
1005
|
// ── Auto-worktree: re-enter worktree on resume ──
|
|
933
1006
|
if (s.currentMilestoneId &&
|
|
934
1007
|
shouldUseWorktreeIsolation() &&
|
|
@@ -945,6 +1018,11 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
945
1018
|
ctx.ui.setFooter(hideFooter);
|
|
946
1019
|
ctx.ui.notify(s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "info");
|
|
947
1020
|
restoreHookState(s.basePath);
|
|
1021
|
+
// Re-sync managed resources on resume so long-lived auto sessions pick up
|
|
1022
|
+
// bundled extension updates before resume-time verification/state logic runs.
|
|
1023
|
+
const agentDir = process.env.GSD_CODING_AGENT_DIR || join(process.env.GSD_HOME || homedir(), ".gsd", "agent");
|
|
1024
|
+
const { initResources } = await import("../../../" + "resource-loader.js");
|
|
1025
|
+
initResources(agentDir);
|
|
948
1026
|
// Open the project DB before rebuild/derive so resume uses DB-backed
|
|
949
1027
|
// state instead of falling back to stale markdown parsing (#2940).
|
|
950
1028
|
await openProjectDbIfPresent(s.basePath);
|
|
@@ -971,7 +1049,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
971
1049
|
invalidateAllCaches();
|
|
972
1050
|
if (s.pausedSessionFile) {
|
|
973
1051
|
const activityDir = join(gsdRoot(s.basePath), "activity");
|
|
974
|
-
const recovery = synthesizeCrashRecovery(s.basePath, s.currentUnit?.type ?? "unknown", s.currentUnit?.id ?? "unknown", s.pausedSessionFile ?? undefined, activityDir);
|
|
1052
|
+
const recovery = synthesizeCrashRecovery(s.basePath, s.currentUnit?.type ?? s.pausedUnitType ?? "unknown", s.currentUnit?.id ?? s.pausedUnitId ?? "unknown", s.pausedSessionFile ?? undefined, activityDir);
|
|
975
1053
|
if (recovery && recovery.trace.toolCallCount > 0) {
|
|
976
1054
|
s.pendingCrashRecovery = recovery.prompt;
|
|
977
1055
|
ctx.ui.notify(`Recovered ${recovery.trace.toolCallCount} tool calls from paused session. Resuming with context.`, "info");
|
|
@@ -981,6 +1059,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
981
1059
|
updateSessionLock(lockBase(), "resuming", s.currentMilestoneId ?? "unknown");
|
|
982
1060
|
writeLock(lockBase(), "resuming", s.currentMilestoneId ?? "unknown");
|
|
983
1061
|
logCmuxEvent(loadEffectiveGSDPreferences()?.preferences, s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "progress");
|
|
1062
|
+
captureProjectRootEnv(s.originalBasePath || s.basePath);
|
|
984
1063
|
await autoLoop(ctx, pi, s, buildLoopDeps());
|
|
985
1064
|
cleanupAfterLoopExit(ctx);
|
|
986
1065
|
return;
|
|
@@ -992,9 +1071,10 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
992
1071
|
lockBase,
|
|
993
1072
|
buildResolver,
|
|
994
1073
|
};
|
|
995
|
-
const ready = await bootstrapAutoSession(s, ctx, pi, base, verboseMode, requestedStepMode, bootstrapDeps);
|
|
1074
|
+
const ready = await bootstrapAutoSession(s, ctx, pi, base, verboseMode, requestedStepMode, bootstrapDeps, freshStartAssessment);
|
|
996
1075
|
if (!ready)
|
|
997
1076
|
return;
|
|
1077
|
+
captureProjectRootEnv(s.originalBasePath || s.basePath);
|
|
998
1078
|
try {
|
|
999
1079
|
syncCmuxSidebar(loadEffectiveGSDPreferences()?.preferences, await deriveState(s.basePath));
|
|
1000
1080
|
}
|
|
@@ -1074,24 +1154,6 @@ function ensurePreconditions(unitType, unitId, base, state) {
|
|
|
1074
1154
|
}
|
|
1075
1155
|
}
|
|
1076
1156
|
}
|
|
1077
|
-
// ─── Diagnostics ──────────────────────────────────────────────────────────────
|
|
1078
|
-
/** Build recovery context from module state for recoverTimedOutUnit */
|
|
1079
|
-
function buildRecoveryContext() {
|
|
1080
|
-
return {
|
|
1081
|
-
basePath: s.basePath,
|
|
1082
|
-
verbose: s.verbose,
|
|
1083
|
-
currentUnitStartedAt: s.currentUnit?.startedAt ?? Date.now(),
|
|
1084
|
-
unitRecoveryCount: s.unitRecoveryCount,
|
|
1085
|
-
};
|
|
1086
|
-
}
|
|
1087
|
-
/**
|
|
1088
|
-
* Test-only: expose skip-loop state for unit tests.
|
|
1089
|
-
* Not part of the public API.
|
|
1090
|
-
*/
|
|
1091
|
-
/**
|
|
1092
|
-
* Dispatch a hook unit directly, bypassing normal pre-dispatch hooks.
|
|
1093
|
-
* Used for manual hook triggers via /gsd run-hook.
|
|
1094
|
-
*/
|
|
1095
1157
|
export async function dispatchHookUnit(ctx, pi, hookName, triggerUnitType, triggerUnitId, hookPrompt, hookModel, targetBasePath) {
|
|
1096
1158
|
if (!s.active) {
|
|
1097
1159
|
s.active = true;
|
|
@@ -141,7 +141,7 @@ export async function buildBeforeAgentStartResult(event, ctx) {
|
|
|
141
141
|
warnDeprecatedAgentInstructions();
|
|
142
142
|
const injection = await buildGuidedExecuteContextInjection(event.prompt, process.cwd());
|
|
143
143
|
// Re-inject forensics context on follow-up turns (#2941)
|
|
144
|
-
const forensicsInjection = !injection ? buildForensicsContextInjection(process.cwd()) : null;
|
|
144
|
+
const forensicsInjection = !injection ? buildForensicsContextInjection(process.cwd(), event.prompt) : null;
|
|
145
145
|
const worktreeBlock = buildWorktreeContextBlock();
|
|
146
146
|
const fullSystem = `${event.systemPrompt}\n\n[SYSTEM CONTEXT — GSD]\n\n${systemContent}${preferenceBlock}${knowledgeBlock}${codebaseBlock}${memoryBlock}${newSkillsBlock}${worktreeBlock}`;
|
|
147
147
|
stopContextTimer({
|
|
@@ -425,7 +425,7 @@ function oneLine(text) {
|
|
|
425
425
|
* Check for an active forensics session and return the prompt content
|
|
426
426
|
* so it can be re-injected on follow-up turns.
|
|
427
427
|
*/
|
|
428
|
-
function buildForensicsContextInjection(basePath) {
|
|
428
|
+
export function buildForensicsContextInjection(basePath, prompt) {
|
|
429
429
|
const marker = readForensicsMarker(basePath);
|
|
430
430
|
if (!marker)
|
|
431
431
|
return null;
|
|
@@ -435,6 +435,11 @@ function buildForensicsContextInjection(basePath) {
|
|
|
435
435
|
clearForensicsMarker(basePath);
|
|
436
436
|
return null;
|
|
437
437
|
}
|
|
438
|
+
const trimmed = prompt.trim().toLowerCase().replace(/[.!?,]+$/g, "");
|
|
439
|
+
if (trimmed && !RESUME_INTENT_PATTERNS.test(trimmed)) {
|
|
440
|
+
clearForensicsMarker(basePath);
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
438
443
|
return marker.promptContent;
|
|
439
444
|
}
|
|
440
445
|
/**
|
|
@@ -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]",
|
|
@@ -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)
|