gsd-pi 2.69.0 → 2.70.0-dev.8f4d92b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/loader.js +4 -0
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +150 -2
- package/dist/resources/extensions/gsd/auto-prompts.js +7 -3
- package/dist/resources/extensions/gsd/auto.js +12 -8
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +6 -2
- package/dist/resources/extensions/gsd/commands-cmux.js +30 -1
- package/dist/resources/extensions/gsd/commands-handlers.js +22 -8
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +12 -0
- package/dist/resources/extensions/gsd/doctor-format.js +2 -0
- package/dist/resources/extensions/gsd/guided-flow.js +19 -9
- package/dist/resources/extensions/gsd/pre-execution-checks.js +2 -2
- package/dist/resources/extensions/gsd/workflow-mcp.js +64 -6
- package/dist/update-check.d.ts +1 -0
- package/dist/update-check.js +30 -27
- package/dist/update-cmd.js +3 -11
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
- 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/required-server-files.json +4 -4
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- 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.js +2 -2
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- 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 +3 -3
- 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/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- 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 +4 -4
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
- package/dist/web/standalone/.next/server/chunks/63.js +3 -3
- package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +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/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/daemon/src/orchestrator.ts +9 -84
- package/packages/mcp-server/README.md +25 -3
- package/packages/mcp-server/dist/cli.d.ts +0 -1
- package/packages/mcp-server/dist/cli.d.ts.map +1 -1
- package/packages/mcp-server/dist/cli.js +4 -2
- package/packages/mcp-server/dist/cli.js.map +1 -1
- package/packages/mcp-server/dist/server.d.ts +32 -1
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +118 -1
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/tool-credentials.d.ts +6 -0
- package/packages/mcp-server/dist/tool-credentials.d.ts.map +1 -0
- package/packages/mcp-server/dist/tool-credentials.js +90 -0
- package/packages/mcp-server/dist/tool-credentials.js.map +1 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts +3 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +308 -4
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/cli.ts +5 -3
- package/packages/mcp-server/src/import-candidates.test.ts +48 -0
- package/packages/mcp-server/src/mcp-server.test.ts +85 -1
- package/packages/mcp-server/src/server.ts +188 -1
- package/packages/mcp-server/src/tool-credentials.test.ts +95 -0
- package/packages/mcp-server/src/tool-credentials.ts +97 -0
- package/packages/mcp-server/src/workflow-tools.test.ts +32 -25
- package/packages/mcp-server/src/workflow-tools.ts +398 -2
- package/packages/pi-agent-core/dist/agent.d.ts +8 -0
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +3 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/src/agent.test.ts +82 -0
- package/packages/pi-agent-core/src/agent.ts +12 -0
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +1 -23
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/index.d.ts +3 -2
- package/packages/pi-ai/dist/utils/oauth/index.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/index.js +3 -5
- package/packages/pi-ai/dist/utils/oauth/index.js.map +1 -1
- package/packages/pi-ai/src/providers/anthropic.ts +1 -31
- package/packages/pi-ai/src/utils/oauth/index.ts +3 -5
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +10 -0
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/sdk.ts +8 -0
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +227 -2
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +172 -0
- package/src/resources/extensions/gsd/auto-prompts.ts +7 -3
- package/src/resources/extensions/gsd/auto.ts +12 -8
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +9 -5
- package/src/resources/extensions/gsd/commands-cmux.ts +32 -1
- package/src/resources/extensions/gsd/commands-handlers.ts +22 -7
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +14 -0
- package/src/resources/extensions/gsd/doctor-format.ts +1 -0
- package/src/resources/extensions/gsd/doctor-types.ts +1 -0
- package/src/resources/extensions/gsd/guided-flow.ts +23 -8
- package/src/resources/extensions/gsd/pre-execution-checks.ts +2 -2
- package/src/resources/extensions/gsd/tests/cmux.test.ts +67 -1
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +43 -0
- package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +6 -2
- package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +48 -1
- package/src/resources/extensions/gsd/tests/resource-loader-import-path.test.ts +8 -7
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +87 -1
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +48 -7
- package/src/resources/extensions/gsd/workflow-mcp.ts +74 -5
- package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
- package/packages/pi-ai/dist/utils/oauth/anthropic.d.ts +0 -17
- package/packages/pi-ai/dist/utils/oauth/anthropic.d.ts.map +0 -1
- package/packages/pi-ai/dist/utils/oauth/anthropic.js +0 -106
- package/packages/pi-ai/dist/utils/oauth/anthropic.js.map +0 -1
- package/packages/pi-ai/src/utils/oauth/anthropic.ts +0 -140
- /package/dist/web/standalone/.next/static/{DrWdzskk28E5Qz-Wjw1mj → j_Ae_qOKzzIlA6oFOxVx4}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{DrWdzskk28E5Qz-Wjw1mj → j_Ae_qOKzzIlA6oFOxVx4}/_ssgManifest.js +0 -0
|
@@ -125,9 +125,9 @@ import {
|
|
|
125
125
|
} from "./metrics.js";
|
|
126
126
|
import { setLogBasePath, logWarning, logError } from "./workflow-logger.js";
|
|
127
127
|
import { homedir } from "node:os";
|
|
128
|
-
import { join
|
|
128
|
+
import { join } from "node:path";
|
|
129
|
+
import { pathToFileURL } from "node:url";
|
|
129
130
|
import { readFileSync, existsSync, mkdirSync, writeFileSync, unlinkSync } from "node:fs";
|
|
130
|
-
import { createRequire } from "node:module";
|
|
131
131
|
import { atomicWriteSync } from "./atomic-write.js";
|
|
132
132
|
import {
|
|
133
133
|
autoCommitCurrentBranch,
|
|
@@ -1334,13 +1334,17 @@ export async function startAuto(
|
|
|
1334
1334
|
restoreHookState(s.basePath);
|
|
1335
1335
|
// Re-sync managed resources on resume so long-lived auto sessions pick up
|
|
1336
1336
|
// bundled extension updates before resume-time verification/state logic runs.
|
|
1337
|
+
// GSD_PKG_ROOT is set by loader.ts and points to the gsd-pi package root.
|
|
1338
|
+
// The relative import ("../../../resource-loader.js") only works from the source
|
|
1339
|
+
// tree; deployed extensions live at ~/.gsd/agent/extensions/gsd/ where the
|
|
1340
|
+
// relative path resolves to ~/.gsd/agent/resource-loader.js which doesn't exist.
|
|
1341
|
+
// Using GSD_PKG_ROOT constructs a correct absolute path in both contexts (#3949).
|
|
1337
1342
|
const agentDir = process.env.GSD_CODING_AGENT_DIR || join(process.env.GSD_HOME || homedir(), ".gsd", "agent");
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
const
|
|
1343
|
-
const { initResources } = await import(join(pkgRoot, "dist", "resource-loader.js"));
|
|
1343
|
+
const pkgRoot = process.env.GSD_PKG_ROOT;
|
|
1344
|
+
const resourceLoaderPath = pkgRoot
|
|
1345
|
+
? pathToFileURL(join(pkgRoot, "dist", "resource-loader.js")).href
|
|
1346
|
+
: new URL("../../../resource-loader.js", import.meta.url).href;
|
|
1347
|
+
const { initResources } = await import(resourceLoaderPath);
|
|
1344
1348
|
initResources(agentDir);
|
|
1345
1349
|
// Open the project DB before rebuild/derive so resume uses DB-backed
|
|
1346
1350
|
// state instead of falling back to stale markdown parsing (#2940).
|
|
@@ -19,6 +19,7 @@ import { deriveState } from "../state.js";
|
|
|
19
19
|
import { formatOverridesSection, formatShortcut, loadActiveOverrides, loadFile, parseContinue, parseSummary } from "../files.js";
|
|
20
20
|
import { toPosixPath } from "../../shared/mod.js";
|
|
21
21
|
import { markCmuxPromptShown, shouldPromptToEnableCmux } from "../../cmux/index.js";
|
|
22
|
+
import { autoEnableCmuxPreferences } from "../commands-cmux.js";
|
|
22
23
|
|
|
23
24
|
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
24
25
|
|
|
@@ -76,13 +77,16 @@ export async function buildBeforeAgentStartResult(
|
|
|
76
77
|
shortcutDashboard: formatShortcut("Ctrl+Alt+G"),
|
|
77
78
|
shortcutShell: formatShortcut("Ctrl+Alt+B"),
|
|
78
79
|
});
|
|
79
|
-
|
|
80
|
+
let loadedPreferences = loadEffectiveGSDPreferences();
|
|
80
81
|
if (shouldPromptToEnableCmux(loadedPreferences?.preferences)) {
|
|
81
82
|
markCmuxPromptShown();
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
if (autoEnableCmuxPreferences()) {
|
|
84
|
+
loadedPreferences = loadEffectiveGSDPreferences();
|
|
85
|
+
ctx.ui.notify(
|
|
86
|
+
"cmux detected — auto-enabled. Run /gsd cmux off to disable.",
|
|
87
|
+
"info",
|
|
88
|
+
);
|
|
89
|
+
}
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
let preferenceBlock = "";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
2
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { clearCmuxSidebar, CmuxClient, detectCmuxEnvironment, resolveCmuxConfig } from "../cmux/index.js";
|
|
4
4
|
import { saveFile } from "./files.js";
|
|
5
5
|
import {
|
|
@@ -9,6 +9,37 @@ import {
|
|
|
9
9
|
} from "./preferences.js";
|
|
10
10
|
import { ensurePreferencesFile, serializePreferencesToFrontmatter } from "./commands-prefs-wizard.js";
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Auto-enable cmux in project preferences when detected but never configured.
|
|
14
|
+
* Called at boot (before agent start) — no ExtensionCommandContext needed.
|
|
15
|
+
* Returns true if preferences were written, false if skipped.
|
|
16
|
+
*/
|
|
17
|
+
export function autoEnableCmuxPreferences(): boolean {
|
|
18
|
+
const path = getProjectGSDPreferencesPath();
|
|
19
|
+
if (!existsSync(path)) return false;
|
|
20
|
+
|
|
21
|
+
const existing = loadProjectGSDPreferences();
|
|
22
|
+
const prefs: Record<string, unknown> = existing?.preferences ? { ...existing.preferences } : { version: 1 };
|
|
23
|
+
prefs.cmux = {
|
|
24
|
+
enabled: true,
|
|
25
|
+
notifications: true,
|
|
26
|
+
sidebar: true,
|
|
27
|
+
splits: false,
|
|
28
|
+
browser: false,
|
|
29
|
+
...((prefs.cmux as Record<string, unknown> | undefined) ?? {}),
|
|
30
|
+
};
|
|
31
|
+
(prefs.cmux as Record<string, unknown>).enabled = true;
|
|
32
|
+
prefs.version = prefs.version || 1;
|
|
33
|
+
|
|
34
|
+
const frontmatter = serializePreferencesToFrontmatter(prefs);
|
|
35
|
+
let body = "\n# GSD Skill Preferences\n\nSee `~/.gsd/agent/extensions/gsd/docs/preferences-reference.md` for full field documentation and examples.\n";
|
|
36
|
+
const preserved = extractBodyAfterFrontmatter(readFileSync(path, "utf-8"));
|
|
37
|
+
if (preserved) body = preserved;
|
|
38
|
+
|
|
39
|
+
writeFileSync(path, `---\n${frontmatter}---${body}`, "utf-8");
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
12
43
|
function extractBodyAfterFrontmatter(content: string): string | null {
|
|
13
44
|
const start = content.startsWith("---\n") ? 4 : content.startsWith("---\r\n") ? 5 : -1;
|
|
14
45
|
if (start === -1) return null;
|
|
@@ -25,6 +25,26 @@ import { getAutoWorktreePath } from "./auto-worktree.js";
|
|
|
25
25
|
import { projectRoot } from "./commands/context.js";
|
|
26
26
|
import { loadPrompt } from "./prompt-loader.js";
|
|
27
27
|
|
|
28
|
+
const UPDATE_REGISTRY_URL = "https://registry.npmjs.org/gsd-pi/latest";
|
|
29
|
+
const UPDATE_FETCH_TIMEOUT_MS = 5000;
|
|
30
|
+
|
|
31
|
+
async function fetchLatestVersionForCommand(): Promise<string | null> {
|
|
32
|
+
const controller = new AbortController();
|
|
33
|
+
const timeout = setTimeout(() => controller.abort(), UPDATE_FETCH_TIMEOUT_MS);
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const res = await fetch(UPDATE_REGISTRY_URL, { signal: controller.signal });
|
|
37
|
+
if (!res.ok) return null;
|
|
38
|
+
const data = (await res.json()) as { version?: string };
|
|
39
|
+
const latest = typeof data.version === "string" ? data.version.trim().replace(/^v/, "") : "";
|
|
40
|
+
return latest.length > 0 ? latest : null;
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
} finally {
|
|
44
|
+
clearTimeout(timeout);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
28
48
|
export function dispatchDoctorHeal(pi: ExtensionAPI, scope: string | undefined, reportText: string, structuredIssues: string): void {
|
|
29
49
|
const workflowPath = process.env.GSD_WORKFLOW_PATH ?? join(process.env.HOME ?? "~", ".gsd", "agent", "GSD-WORKFLOW.md");
|
|
30
50
|
const workflow = readFileSync(workflowPath, "utf-8");
|
|
@@ -394,13 +414,8 @@ export async function handleUpdate(ctx: ExtensionCommandContext): Promise<void>
|
|
|
394
414
|
|
|
395
415
|
ctx.ui.notify(`Current version: v${current}\nChecking npm registry...`, "info");
|
|
396
416
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
latest = execSync(`npm view ${NPM_PACKAGE} version`, {
|
|
400
|
-
encoding: "utf-8",
|
|
401
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
402
|
-
}).trim();
|
|
403
|
-
} catch {
|
|
417
|
+
const latest = await fetchLatestVersionForCommand();
|
|
418
|
+
if (!latest) {
|
|
404
419
|
ctx.ui.notify("Failed to reach npm registry. Check your network connection.", "error");
|
|
405
420
|
return;
|
|
406
421
|
}
|
|
@@ -13,6 +13,20 @@ export async function checkEngineHealth(
|
|
|
13
13
|
issues: DoctorIssue[],
|
|
14
14
|
fixesApplied: string[],
|
|
15
15
|
): Promise<void> {
|
|
16
|
+
const dbPath = join(basePath, ".gsd", "gsd.db");
|
|
17
|
+
|
|
18
|
+
if (!isDbAvailable() && existsSync(dbPath)) {
|
|
19
|
+
issues.push({
|
|
20
|
+
severity: "warning",
|
|
21
|
+
code: "db_unavailable",
|
|
22
|
+
scope: "project",
|
|
23
|
+
unitId: "project",
|
|
24
|
+
message: "Database unavailable — using filesystem state derivation (degraded mode). State queries may be slower and less reliable.",
|
|
25
|
+
file: ".gsd/gsd.db",
|
|
26
|
+
fixable: false,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
16
30
|
// ── DB constraint violation detection (full doctor only, not pre-dispatch per D-10) ──
|
|
17
31
|
try {
|
|
18
32
|
if (isDbAvailable()) {
|
|
@@ -2,6 +2,7 @@ import type { DoctorIssue, DoctorIssueCode, DoctorReport, DoctorSummary } from "
|
|
|
2
2
|
|
|
3
3
|
function matchesScope(unitId: string, scope?: string): boolean {
|
|
4
4
|
if (!scope) return true;
|
|
5
|
+
if (unitId === "project" || unitId === "environment") return true;
|
|
5
6
|
return unitId === scope || unitId.startsWith(`${scope}/`) || unitId.startsWith(`${scope}`);
|
|
6
7
|
}
|
|
7
8
|
|
|
@@ -48,6 +48,7 @@ import { DISCUSS_TOOLS_ALLOWLIST } from "./constants.js";
|
|
|
48
48
|
import {
|
|
49
49
|
getWorkflowTransportSupportError,
|
|
50
50
|
getRequiredWorkflowToolsForGuidedUnit,
|
|
51
|
+
supportsStructuredQuestions,
|
|
51
52
|
} from "./workflow-mcp.js";
|
|
52
53
|
import {
|
|
53
54
|
runPreparation,
|
|
@@ -367,6 +368,20 @@ async function dispatchWorkflow(
|
|
|
367
368
|
}
|
|
368
369
|
}
|
|
369
370
|
|
|
371
|
+
function getStructuredQuestionsAvailability(
|
|
372
|
+
pi: ExtensionAPI,
|
|
373
|
+
ctx: ExtensionContext | undefined,
|
|
374
|
+
): "true" | "false" {
|
|
375
|
+
if (!ctx) return "false";
|
|
376
|
+
|
|
377
|
+
const provider = ctx.model?.provider;
|
|
378
|
+
const authMode = provider ? ctx.modelRegistry.getProviderAuthMode(provider) : undefined;
|
|
379
|
+
return supportsStructuredQuestions(pi.getActiveTools(), {
|
|
380
|
+
authMode,
|
|
381
|
+
baseUrl: ctx.model?.baseUrl,
|
|
382
|
+
}) ? "true" : "false";
|
|
383
|
+
}
|
|
384
|
+
|
|
370
385
|
/**
|
|
371
386
|
* Resolve a model ID string to a model object from available models.
|
|
372
387
|
* Handles "provider/model" and bare ID formats.
|
|
@@ -739,7 +754,7 @@ export async function showDiscuss(
|
|
|
739
754
|
|
|
740
755
|
if (choice === "discuss_draft") {
|
|
741
756
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
742
|
-
const structuredQuestionsAvailable = pi
|
|
757
|
+
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
|
743
758
|
const basePrompt = loadPrompt("guided-discuss-milestone", {
|
|
744
759
|
milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
745
760
|
commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
|
|
@@ -752,7 +767,7 @@ export async function showDiscuss(
|
|
|
752
767
|
await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone");
|
|
753
768
|
} else if (choice === "discuss_fresh") {
|
|
754
769
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
755
|
-
const structuredQuestionsAvailable = pi
|
|
770
|
+
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
|
756
771
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: mid, step: false, createdAt: Date.now() });
|
|
757
772
|
await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
758
773
|
milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
@@ -910,7 +925,7 @@ export async function showDiscuss(
|
|
|
910
925
|
if (confirm !== "rediscuss") continue;
|
|
911
926
|
}
|
|
912
927
|
|
|
913
|
-
const sqAvail = pi
|
|
928
|
+
const sqAvail = getStructuredQuestionsAvailability(pi, ctx);
|
|
914
929
|
const prompt = await buildDiscussSlicePrompt(mid, chosen.id, chosen.title, basePath, { rediscuss: isRediscuss, structuredQuestionsAvailable: sqAvail });
|
|
915
930
|
await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-slice");
|
|
916
931
|
|
|
@@ -1020,7 +1035,7 @@ async function dispatchDiscussForMilestone(
|
|
|
1020
1035
|
].join("\n")
|
|
1021
1036
|
: "";
|
|
1022
1037
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
1023
|
-
const structuredQuestionsAvailable = pi
|
|
1038
|
+
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
|
1024
1039
|
const basePrompt = loadPrompt("guided-discuss-milestone", {
|
|
1025
1040
|
milestoneId: mid,
|
|
1026
1041
|
milestoneTitle,
|
|
@@ -1461,7 +1476,7 @@ export async function showSmartEntry(
|
|
|
1461
1476
|
|
|
1462
1477
|
if (choice === "discuss_draft") {
|
|
1463
1478
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
1464
|
-
const structuredQuestionsAvailable = pi
|
|
1479
|
+
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
|
1465
1480
|
const basePrompt = loadPrompt("guided-discuss-milestone", {
|
|
1466
1481
|
milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
1467
1482
|
commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
|
|
@@ -1474,7 +1489,7 @@ export async function showSmartEntry(
|
|
|
1474
1489
|
await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone");
|
|
1475
1490
|
} else if (choice === "discuss_fresh") {
|
|
1476
1491
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
1477
|
-
const structuredQuestionsAvailable = pi
|
|
1492
|
+
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
|
1478
1493
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId, step: stepMode, createdAt: Date.now() });
|
|
1479
1494
|
await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
1480
1495
|
milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
@@ -1572,7 +1587,7 @@ export async function showSmartEntry(
|
|
|
1572
1587
|
}), "gsd-run", ctx, "plan-milestone");
|
|
1573
1588
|
} else if (choice === "discuss") {
|
|
1574
1589
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
1575
|
-
const structuredQuestionsAvailable = pi
|
|
1590
|
+
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
|
1576
1591
|
await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
|
1577
1592
|
milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
1578
1593
|
commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
|
|
@@ -1712,7 +1727,7 @@ export async function showSmartEntry(
|
|
|
1712
1727
|
}),
|
|
1713
1728
|
}), "gsd-run", ctx, "plan-slice");
|
|
1714
1729
|
} else if (choice === "discuss") {
|
|
1715
|
-
const sqAvail = pi
|
|
1730
|
+
const sqAvail = getStructuredQuestionsAvailability(pi, ctx);
|
|
1716
1731
|
await dispatchWorkflow(pi, await buildDiscussSlicePrompt(milestoneId, sliceId, sliceTitle, basePath, { rediscuss: hasContext, structuredQuestionsAvailable: sqAvail }), "gsd-run", ctx, "discuss-slice");
|
|
1717
1732
|
} else if (choice === "research") {
|
|
1718
1733
|
const researchTemplates = inlineTemplate("research", "Research");
|
|
@@ -263,9 +263,9 @@ function extractPathFromAnnotation(raw: string): string {
|
|
|
263
263
|
const trimmed = raw.trim();
|
|
264
264
|
if (!trimmed) return trimmed;
|
|
265
265
|
|
|
266
|
-
const backtickMatch = trimmed.match(
|
|
266
|
+
const backtickMatch = trimmed.match(/^(`+)([^`]+)\1(?:(?:\s+[—–-]\s+.+)|(?:\s+\([^()]+\)))?$/);
|
|
267
267
|
if (backtickMatch) {
|
|
268
|
-
return backtickMatch[
|
|
268
|
+
return backtickMatch[2].trim();
|
|
269
269
|
}
|
|
270
270
|
|
|
271
271
|
const annotatedMatch = trimmed.match(/^(.+?)\s+[—–-]\s+.+$/);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import test, { describe } from "node:test";
|
|
1
|
+
import test, { describe, beforeEach, afterEach } from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
5
6
|
import { fileURLToPath } from "node:url";
|
|
6
7
|
import {
|
|
7
8
|
buildCmuxProgress,
|
|
@@ -12,6 +13,7 @@ import {
|
|
|
12
13
|
resolveCmuxConfig,
|
|
13
14
|
shouldPromptToEnableCmux,
|
|
14
15
|
} from "../../cmux/index.ts";
|
|
16
|
+
import { autoEnableCmuxPreferences } from "../commands-cmux.ts";
|
|
15
17
|
import type { GSDState } from "../types.ts";
|
|
16
18
|
|
|
17
19
|
test("detectCmuxEnvironment requires workspace, surface, and socket", () => {
|
|
@@ -79,6 +81,70 @@ test("shouldPromptToEnableCmux only prompts once per session", () => {
|
|
|
79
81
|
resetCmuxPromptState();
|
|
80
82
|
});
|
|
81
83
|
|
|
84
|
+
describe("autoEnableCmuxPreferences", () => {
|
|
85
|
+
let tmp: string;
|
|
86
|
+
let originalCwd: string;
|
|
87
|
+
|
|
88
|
+
beforeEach(() => {
|
|
89
|
+
originalCwd = process.cwd();
|
|
90
|
+
tmp = fs.mkdtempSync(path.join(tmpdir(), "cmux-auto-test-"));
|
|
91
|
+
fs.mkdirSync(path.join(tmp, ".gsd"), { recursive: true });
|
|
92
|
+
process.chdir(tmp);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
afterEach(() => {
|
|
96
|
+
process.chdir(originalCwd);
|
|
97
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("writes cmux.enabled true when preferences file exists with no cmux config", () => {
|
|
101
|
+
const prefsPath = path.join(tmp, ".gsd", "preferences.md");
|
|
102
|
+
fs.writeFileSync(prefsPath, [
|
|
103
|
+
"---",
|
|
104
|
+
"version: 1",
|
|
105
|
+
"---",
|
|
106
|
+
"",
|
|
107
|
+
"# GSD Skill Preferences",
|
|
108
|
+
].join("\n"));
|
|
109
|
+
|
|
110
|
+
const result = autoEnableCmuxPreferences();
|
|
111
|
+
assert.equal(result, true);
|
|
112
|
+
|
|
113
|
+
const content = fs.readFileSync(prefsPath, "utf-8");
|
|
114
|
+
assert.ok(content.includes("enabled: true"), "should write enabled: true");
|
|
115
|
+
assert.ok(content.includes("notifications: true"), "should default notifications on");
|
|
116
|
+
assert.ok(content.includes("sidebar: true"), "should default sidebar on");
|
|
117
|
+
assert.ok(content.includes("splits: false"), "should default splits off");
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("returns false when preferences file does not exist", () => {
|
|
121
|
+
const result = autoEnableCmuxPreferences();
|
|
122
|
+
assert.equal(result, false);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("preserves existing cmux sub-preferences when auto-enabling", () => {
|
|
126
|
+
const prefsPath = path.join(tmp, ".gsd", "preferences.md");
|
|
127
|
+
fs.writeFileSync(prefsPath, [
|
|
128
|
+
"---",
|
|
129
|
+
"version: 1",
|
|
130
|
+
"cmux:",
|
|
131
|
+
" splits: true",
|
|
132
|
+
" browser: true",
|
|
133
|
+
"---",
|
|
134
|
+
"",
|
|
135
|
+
"# GSD Skill Preferences",
|
|
136
|
+
].join("\n"));
|
|
137
|
+
|
|
138
|
+
const result = autoEnableCmuxPreferences();
|
|
139
|
+
assert.equal(result, true);
|
|
140
|
+
|
|
141
|
+
const content = fs.readFileSync(prefsPath, "utf-8");
|
|
142
|
+
assert.ok(content.includes("enabled: true"), "should set enabled: true");
|
|
143
|
+
assert.ok(content.includes("splits: true"), "should preserve existing splits: true");
|
|
144
|
+
assert.ok(content.includes("browser: true"), "should preserve existing browser: true");
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
82
148
|
test("buildCmuxStatusLabel and progress prefer deepest active unit", () => {
|
|
83
149
|
const state: GSDState = {
|
|
84
150
|
activeMilestone: { id: "M001", title: "Milestone" },
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { afterEach, test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { closeDatabase } from "../gsd-db.ts";
|
|
4
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
import { filterDoctorIssues } from "../doctor-format.ts";
|
|
8
|
+
import { checkEngineHealth } from "../doctor-engine-checks.ts";
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
closeDatabase();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("filterDoctorIssues keeps project and environment issues in scoped reports", () => {
|
|
15
|
+
const issues = [
|
|
16
|
+
{ severity: "error", code: "env_dependencies", scope: "project", unitId: "environment", message: "node_modules missing", fixable: false },
|
|
17
|
+
{ severity: "warning", code: "db_unavailable", scope: "project", unitId: "project", message: "DB unavailable", fixable: false },
|
|
18
|
+
{ severity: "warning", code: "state_file_missing", scope: "slice", unitId: "M016/S01", message: "slice warning", fixable: false },
|
|
19
|
+
] as const;
|
|
20
|
+
|
|
21
|
+
const filtered = filterDoctorIssues([...issues], { scope: "M016", includeWarnings: true });
|
|
22
|
+
assert.deepEqual(
|
|
23
|
+
filtered.map((issue) => issue.unitId),
|
|
24
|
+
["environment", "project", "M016/S01"],
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("checkEngineHealth reports db_unavailable when gsd.db exists but the DB is closed", async (t) => {
|
|
29
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-doctor-db-unavailable-"));
|
|
30
|
+
t.after(() => rmSync(base, { recursive: true, force: true }));
|
|
31
|
+
|
|
32
|
+
const gsdDir = join(base, ".gsd");
|
|
33
|
+
mkdirSync(gsdDir, { recursive: true });
|
|
34
|
+
writeFileSync(join(gsdDir, "gsd.db"), "");
|
|
35
|
+
|
|
36
|
+
const issues: any[] = [];
|
|
37
|
+
await checkEngineHealth(base, issues, []);
|
|
38
|
+
|
|
39
|
+
const dbIssue = issues.find((issue) => issue.code === "db_unavailable");
|
|
40
|
+
assert.ok(dbIssue, "doctor should surface degraded DB mode when a DB file exists");
|
|
41
|
+
assert.equal(dbIssue.unitId, "project");
|
|
42
|
+
assert.equal(dbIssue.file, ".gsd/gsd.db");
|
|
43
|
+
});
|
|
@@ -26,8 +26,12 @@ test("ensureProjectWorkflowMcpConfig creates .mcp.json with the workflow server"
|
|
|
26
26
|
assert.equal(typeof server?.command, "string");
|
|
27
27
|
assert.equal(Array.isArray(server?.args), true);
|
|
28
28
|
assert.equal(server?.env?.GSD_WORKFLOW_PROJECT_ROOT, projectRoot);
|
|
29
|
-
assert.match(server?.env?.GSD_WORKFLOW_EXECUTORS_MODULE ?? "", /workflow-tool-executors\.js$/);
|
|
30
|
-
assert.match(server?.env?.GSD_WORKFLOW_WRITE_GATE_MODULE ?? "", /write-gate\.js$/);
|
|
29
|
+
assert.match(server?.env?.GSD_WORKFLOW_EXECUTORS_MODULE ?? "", /workflow-tool-executors\.(js|ts)$/);
|
|
30
|
+
assert.match(server?.env?.GSD_WORKFLOW_WRITE_GATE_MODULE ?? "", /write-gate\.(js|ts)$/);
|
|
31
|
+
if ((server?.env?.GSD_WORKFLOW_EXECUTORS_MODULE ?? "").endsWith(".ts")) {
|
|
32
|
+
assert.match(server?.env?.NODE_OPTIONS ?? "", /--experimental-strip-types/);
|
|
33
|
+
assert.match(server?.env?.NODE_OPTIONS ?? "", /resolve-ts\.mjs/);
|
|
34
|
+
}
|
|
31
35
|
} finally {
|
|
32
36
|
rmSync(projectRoot, { recursive: true, force: true });
|
|
33
37
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { describe, it } from 'node:test'
|
|
13
13
|
import assert from 'node:assert/strict'
|
|
14
14
|
import { normalizeFilePath, checkFilePathConsistency } from '../pre-execution-checks.ts'
|
|
15
|
-
import { readFileSync } from 'node:fs'
|
|
15
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
16
16
|
import { resolve } from 'node:path'
|
|
17
17
|
|
|
18
18
|
const src = readFileSync(
|
|
@@ -25,6 +25,11 @@ describe('normalizeFilePath backtick stripping (#3649)', () => {
|
|
|
25
25
|
assert.equal(normalizeFilePath('`src/foo.ts`'), 'src/foo.ts')
|
|
26
26
|
})
|
|
27
27
|
|
|
28
|
+
it('strips doubled backticks and trailing notes from file paths', () => {
|
|
29
|
+
assert.equal(normalizeFilePath('``src/foo.ts`` - current state'), 'src/foo.ts')
|
|
30
|
+
assert.equal(normalizeFilePath('``src/foo.ts`` (current state)'), 'src/foo.ts')
|
|
31
|
+
})
|
|
32
|
+
|
|
28
33
|
it('strips backticks even when mixed with other normalization', () => {
|
|
29
34
|
assert.equal(normalizeFilePath('`./src//bar.ts`'), 'src/bar.ts')
|
|
30
35
|
})
|
|
@@ -66,3 +71,45 @@ describe('checkFilePathConsistency checks task.inputs not task.files (#3626)', (
|
|
|
66
71
|
)
|
|
67
72
|
})
|
|
68
73
|
})
|
|
74
|
+
|
|
75
|
+
describe('checkFilePathConsistency handles doubled-backtick annotations (#3892)', () => {
|
|
76
|
+
it('accepts existing files when task.inputs include doubled-backtick notes', () => {
|
|
77
|
+
const task = {
|
|
78
|
+
milestone_id: 'M001',
|
|
79
|
+
slice_id: 'S01',
|
|
80
|
+
id: 'T01',
|
|
81
|
+
title: 'Test Task',
|
|
82
|
+
status: 'pending',
|
|
83
|
+
one_liner: '',
|
|
84
|
+
narrative: '',
|
|
85
|
+
verification_result: '',
|
|
86
|
+
duration: '',
|
|
87
|
+
completed_at: null,
|
|
88
|
+
blocker_discovered: false,
|
|
89
|
+
deviations: '',
|
|
90
|
+
known_issues: '',
|
|
91
|
+
key_files: [],
|
|
92
|
+
key_decisions: [],
|
|
93
|
+
full_summary_md: '',
|
|
94
|
+
description: '',
|
|
95
|
+
estimate: '',
|
|
96
|
+
files: [],
|
|
97
|
+
verify: '',
|
|
98
|
+
inputs: ['``src/foo.ts`` (current state)'],
|
|
99
|
+
expected_output: [],
|
|
100
|
+
observability_impact: '',
|
|
101
|
+
full_plan_md: '',
|
|
102
|
+
sequence: 0,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const tmp = resolve(process.cwd(), '.tmp-pre-exec-3892')
|
|
106
|
+
try {
|
|
107
|
+
mkdirSync(resolve(tmp, 'src'), { recursive: true })
|
|
108
|
+
writeFileSync(resolve(tmp, 'src', 'foo.ts'), '// ok')
|
|
109
|
+
const results = checkFilePathConsistency([task as any], tmp)
|
|
110
|
+
assert.deepEqual(results, [])
|
|
111
|
+
} finally {
|
|
112
|
+
rmSync(tmp, { recursive: true, force: true })
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
})
|
|
@@ -22,16 +22,17 @@ describe("resource-loader import path", () => {
|
|
|
22
22
|
);
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
test("uses
|
|
26
|
-
// The fix uses
|
|
27
|
-
// dist/resource-loader.js
|
|
25
|
+
test("uses GSD_PKG_ROOT to resolve resource-loader from package root", () => {
|
|
26
|
+
// The fix uses GSD_PKG_ROOT (set by loader.ts) to construct an absolute
|
|
27
|
+
// file URL to dist/resource-loader.js — works in both source and deployed,
|
|
28
|
+
// and on Windows where raw paths fail with ERR_UNSUPPORTED_ESM_URL_SCHEME.
|
|
28
29
|
assert.ok(
|
|
29
|
-
autoSrc.includes('
|
|
30
|
-
"auto.ts should use
|
|
30
|
+
autoSrc.includes('process.env.GSD_PKG_ROOT'),
|
|
31
|
+
"auto.ts should use GSD_PKG_ROOT to resolve resource-loader",
|
|
31
32
|
);
|
|
32
33
|
assert.ok(
|
|
33
|
-
autoSrc.includes('
|
|
34
|
-
"auto.ts should
|
|
34
|
+
autoSrc.includes('pathToFileURL'),
|
|
35
|
+
"auto.ts should convert path to file URL for cross-platform import()",
|
|
35
36
|
);
|
|
36
37
|
});
|
|
37
38
|
});
|