gsd-pi 2.67.0-dev.509bd95 → 2.67.0-dev.5399650
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/resources/extensions/gsd/auto/session.js +6 -0
- package/dist/resources/extensions/gsd/auto.js +27 -0
- package/dist/resources/extensions/gsd/init-wizard.js +34 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +38 -7
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- 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.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 +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- 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-paths-manifest.json +11 -11
- package/dist/web/standalone/.next/server/middleware-build-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/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 +526 -0
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -0
- package/packages/mcp-server/tsconfig.json +1 -1
- 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/gsd/auto/session.ts +6 -0
- package/src/resources/extensions/gsd/auto.ts +29 -1
- package/src/resources/extensions/gsd/init-wizard.ts +34 -0
- package/src/resources/extensions/gsd/tests/auto-project-root-env.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/init-bootstrap-completeness.test.ts +121 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +39 -1
- package/src/resources/extensions/gsd/workflow-mcp.ts +41 -7
- /package/dist/web/standalone/.next/static/{mHJZ3Z8yGRzZ32BmQs-I7 → 6_QPFhgX0DQnDhhquheRc}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{mHJZ3Z8yGRzZ32BmQs-I7 → 6_QPFhgX0DQnDhhquheRc}/_ssgManifest.js +0 -0
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* link-workspace-packages.cjs
|
|
4
4
|
*
|
|
5
|
-
* Creates node_modules/@gsd/* symlinks pointing
|
|
5
|
+
* Creates node_modules/@gsd/* and node_modules/@gsd-build/* symlinks pointing
|
|
6
|
+
* to shipped packages/* directories.
|
|
6
7
|
*
|
|
7
8
|
* During development, npm workspaces creates these automatically. But in the
|
|
8
9
|
* published tarball, workspace packages are shipped under packages/ (via the
|
|
@@ -20,27 +21,33 @@ const { resolve, join } = require('path')
|
|
|
20
21
|
|
|
21
22
|
const root = resolve(__dirname, '..')
|
|
22
23
|
const packagesDir = join(root, 'packages')
|
|
23
|
-
const
|
|
24
|
+
const scopeDirs = {
|
|
25
|
+
'@gsd': join(root, 'node_modules', '@gsd'),
|
|
26
|
+
'@gsd-build': join(root, 'node_modules', '@gsd-build'),
|
|
27
|
+
}
|
|
24
28
|
|
|
25
|
-
// Map directory names to package names
|
|
29
|
+
// Map directory names to scoped package names
|
|
26
30
|
const packageMap = {
|
|
27
|
-
'native': 'native',
|
|
28
|
-
'pi-agent-core': 'pi-agent-core',
|
|
29
|
-
'pi-ai': 'pi-ai',
|
|
30
|
-
'pi-coding-agent': 'pi-coding-agent',
|
|
31
|
-
'pi-tui': 'pi-tui',
|
|
31
|
+
'native': { scope: '@gsd', name: 'native' },
|
|
32
|
+
'pi-agent-core': { scope: '@gsd', name: 'pi-agent-core' },
|
|
33
|
+
'pi-ai': { scope: '@gsd', name: 'pi-ai' },
|
|
34
|
+
'pi-coding-agent': { scope: '@gsd', name: 'pi-coding-agent' },
|
|
35
|
+
'pi-tui': { scope: '@gsd', name: 'pi-tui' },
|
|
36
|
+
'rpc-client': { scope: '@gsd-build', name: 'rpc-client' },
|
|
32
37
|
}
|
|
33
38
|
|
|
34
|
-
|
|
35
|
-
if (!existsSync(
|
|
36
|
-
|
|
39
|
+
for (const scopeDir of Object.values(scopeDirs)) {
|
|
40
|
+
if (!existsSync(scopeDir)) {
|
|
41
|
+
mkdirSync(scopeDir, { recursive: true })
|
|
42
|
+
}
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
let linked = 0
|
|
40
46
|
let copied = 0
|
|
41
|
-
for (const [dir,
|
|
47
|
+
for (const [dir, pkg] of Object.entries(packageMap)) {
|
|
42
48
|
const source = join(packagesDir, dir)
|
|
43
|
-
const
|
|
49
|
+
const scopeDir = scopeDirs[pkg.scope]
|
|
50
|
+
const target = join(scopeDir, pkg.name)
|
|
44
51
|
|
|
45
52
|
if (!existsSync(source)) continue
|
|
46
53
|
|
|
@@ -50,7 +57,7 @@ for (const [dir, name] of Object.entries(packageMap)) {
|
|
|
50
57
|
const stat = lstatSync(target)
|
|
51
58
|
if (stat.isSymbolicLink()) {
|
|
52
59
|
const linkTarget = readlinkSync(target)
|
|
53
|
-
if (resolve(join(
|
|
60
|
+
if (resolve(join(scopeDir, linkTarget)) === source || linkTarget === source) {
|
|
54
61
|
continue // Already correct
|
|
55
62
|
}
|
|
56
63
|
unlinkSync(target) // Wrong target, relink
|
|
@@ -84,6 +84,9 @@ export class AutoSession {
|
|
|
84
84
|
// ── Paths ────────────────────────────────────────────────────────────────
|
|
85
85
|
basePath = "";
|
|
86
86
|
originalBasePath = "";
|
|
87
|
+
previousProjectRootEnv: string | null = null;
|
|
88
|
+
hadProjectRootEnv = false;
|
|
89
|
+
projectRootEnvCaptured = false;
|
|
87
90
|
gitService: GitServiceImpl | null = null;
|
|
88
91
|
|
|
89
92
|
// ── Dispatch counters ────────────────────────────────────────────────────
|
|
@@ -192,6 +195,9 @@ export class AutoSession {
|
|
|
192
195
|
// Paths
|
|
193
196
|
this.basePath = "";
|
|
194
197
|
this.originalBasePath = "";
|
|
198
|
+
this.previousProjectRootEnv = null;
|
|
199
|
+
this.hadProjectRootEnv = false;
|
|
200
|
+
this.projectRootEnvCaptured = false;
|
|
195
201
|
this.gitService = null;
|
|
196
202
|
|
|
197
203
|
// Dispatch
|
|
@@ -241,6 +241,29 @@ const s = new AutoSession();
|
|
|
241
241
|
/** Throttle STATE.md rebuilds — at most once per 30 seconds */
|
|
242
242
|
const STATE_REBUILD_MIN_INTERVAL_MS = 30_000;
|
|
243
243
|
|
|
244
|
+
function captureProjectRootEnv(projectRoot: string): void {
|
|
245
|
+
if (!s.projectRootEnvCaptured) {
|
|
246
|
+
s.hadProjectRootEnv = Object.prototype.hasOwnProperty.call(process.env, "GSD_PROJECT_ROOT");
|
|
247
|
+
s.previousProjectRootEnv = process.env.GSD_PROJECT_ROOT ?? null;
|
|
248
|
+
s.projectRootEnvCaptured = true;
|
|
249
|
+
}
|
|
250
|
+
process.env.GSD_PROJECT_ROOT = projectRoot;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function restoreProjectRootEnv(): void {
|
|
254
|
+
if (!s.projectRootEnvCaptured) return;
|
|
255
|
+
|
|
256
|
+
if (s.hadProjectRootEnv && s.previousProjectRootEnv !== null) {
|
|
257
|
+
process.env.GSD_PROJECT_ROOT = s.previousProjectRootEnv;
|
|
258
|
+
} else {
|
|
259
|
+
delete process.env.GSD_PROJECT_ROOT;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
s.previousProjectRootEnv = null;
|
|
263
|
+
s.hadProjectRootEnv = false;
|
|
264
|
+
s.projectRootEnvCaptured = false;
|
|
265
|
+
}
|
|
266
|
+
|
|
244
267
|
export function shouldUseWorktreeIsolation(): boolean {
|
|
245
268
|
const prefs = loadEffectiveGSDPreferences()?.preferences?.git;
|
|
246
269
|
if (prefs?.isolation === "worktree") return true;
|
|
@@ -542,6 +565,7 @@ function handleLostSessionLock(
|
|
|
542
565
|
s.active = false;
|
|
543
566
|
s.paused = false;
|
|
544
567
|
clearUnitTimeout();
|
|
568
|
+
restoreProjectRootEnv();
|
|
545
569
|
deregisterSigtermHandler();
|
|
546
570
|
clearCmuxSidebar(loadEffectiveGSDPreferences()?.preferences);
|
|
547
571
|
const base = lockBase();
|
|
@@ -577,6 +601,7 @@ function cleanupAfterLoopExit(ctx: ExtensionContext): void {
|
|
|
577
601
|
s.currentUnit = null;
|
|
578
602
|
s.active = false;
|
|
579
603
|
clearUnitTimeout();
|
|
604
|
+
restoreProjectRootEnv();
|
|
580
605
|
|
|
581
606
|
// Clear crash lock and release session lock so the next `/gsd next` does
|
|
582
607
|
// not see a stale lock with the current PID and treat it as a "remote"
|
|
@@ -846,6 +871,7 @@ export async function stopAuto(
|
|
|
846
871
|
ctx?.ui.setStatus("gsd-auto", undefined);
|
|
847
872
|
ctx?.ui.setWidget("gsd-progress", undefined);
|
|
848
873
|
ctx?.ui.setFooter(undefined);
|
|
874
|
+
restoreProjectRootEnv();
|
|
849
875
|
|
|
850
876
|
// Reset all session state in one call
|
|
851
877
|
s.reset();
|
|
@@ -934,6 +960,7 @@ export async function pauseAuto(
|
|
|
934
960
|
|
|
935
961
|
s.active = false;
|
|
936
962
|
s.paused = true;
|
|
963
|
+
restoreProjectRootEnv();
|
|
937
964
|
s.pendingVerificationRetry = null;
|
|
938
965
|
s.verificationRetryCount.clear();
|
|
939
966
|
ctx?.ui.setStatus("gsd-auto", "paused");
|
|
@@ -1305,6 +1332,7 @@ export async function startAuto(
|
|
|
1305
1332
|
);
|
|
1306
1333
|
logCmuxEvent(loadEffectiveGSDPreferences()?.preferences, s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "progress");
|
|
1307
1334
|
|
|
1335
|
+
captureProjectRootEnv(s.originalBasePath || s.basePath);
|
|
1308
1336
|
await autoLoop(ctx, pi, s, buildLoopDeps());
|
|
1309
1337
|
cleanupAfterLoopExit(ctx);
|
|
1310
1338
|
return;
|
|
@@ -1329,6 +1357,7 @@ export async function startAuto(
|
|
|
1329
1357
|
);
|
|
1330
1358
|
if (!ready) return;
|
|
1331
1359
|
|
|
1360
|
+
captureProjectRootEnv(s.originalBasePath || s.basePath);
|
|
1332
1361
|
try {
|
|
1333
1362
|
syncCmuxSidebar(loadEffectiveGSDPreferences()?.preferences, await deriveState(s.basePath));
|
|
1334
1363
|
} catch (err) {
|
|
@@ -1569,4 +1598,3 @@ export {
|
|
|
1569
1598
|
buildLoopRemediationSteps,
|
|
1570
1599
|
} from "./auto-recovery.js";
|
|
1571
1600
|
export { resolveExpectedArtifactPath } from "./auto-artifact-paths.js";
|
|
1572
|
-
|
|
@@ -235,6 +235,20 @@ export async function showProjectInit(
|
|
|
235
235
|
// ── Step 9: Bootstrap .gsd/ ────────────────────────────────────────────────
|
|
236
236
|
bootstrapGsdDirectory(basePath, prefs, signals);
|
|
237
237
|
|
|
238
|
+
// Initialize SQLite database so GSD starts in full-capability mode (#3880).
|
|
239
|
+
// Without this, isDbAvailable() returns false and GSD enters degraded
|
|
240
|
+
// markdown-only mode until a tool handler happens to call ensureDbOpen().
|
|
241
|
+
let dbReady = false;
|
|
242
|
+
try {
|
|
243
|
+
const { ensureDbOpen } = await import("./bootstrap/dynamic-tools.js");
|
|
244
|
+
dbReady = await ensureDbOpen(basePath);
|
|
245
|
+
} catch {
|
|
246
|
+
// Swallowed — warning surfaced below
|
|
247
|
+
}
|
|
248
|
+
if (!dbReady) {
|
|
249
|
+
ctx.ui.notify("Warning: database initialization failed — GSD will run in degraded mode until the next /gsd invocation.", "warning");
|
|
250
|
+
}
|
|
251
|
+
|
|
238
252
|
// Ensure .gitignore
|
|
239
253
|
ensureGitignore(basePath);
|
|
240
254
|
untrackRuntimeFiles(basePath);
|
|
@@ -250,6 +264,25 @@ export async function showProjectInit(
|
|
|
250
264
|
// Non-fatal — codebase map generation failure should never block project init
|
|
251
265
|
}
|
|
252
266
|
|
|
267
|
+
// Write initial STATE.md so it exists before the first /gsd invocation.
|
|
268
|
+
// The explicit /gsd init path (ops.ts) returns without entering showSmartEntry(),
|
|
269
|
+
// which would otherwise generate STATE.md at guided-flow.ts:1358.
|
|
270
|
+
let stateReady = false;
|
|
271
|
+
try {
|
|
272
|
+
const { deriveState } = await import("./state.js");
|
|
273
|
+
const { buildStateMarkdown } = await import("./doctor.js");
|
|
274
|
+
const { saveFile } = await import("./files.js");
|
|
275
|
+
const { resolveGsdRootFile } = await import("./paths.js");
|
|
276
|
+
const state = await deriveState(basePath);
|
|
277
|
+
await saveFile(resolveGsdRootFile(basePath, "STATE"), buildStateMarkdown(state));
|
|
278
|
+
stateReady = true;
|
|
279
|
+
} catch {
|
|
280
|
+
// Swallowed — warning surfaced below
|
|
281
|
+
}
|
|
282
|
+
if (!stateReady) {
|
|
283
|
+
ctx.ui.notify("Warning: initial STATE.md generation failed — it will be created on the next /gsd invocation.", "warning");
|
|
284
|
+
}
|
|
285
|
+
|
|
253
286
|
ctx.ui.notify("GSD initialized. Starting your first milestone...", "info");
|
|
254
287
|
|
|
255
288
|
return { completed: true, bootstrapped: true };
|
|
@@ -433,6 +466,7 @@ function bootstrapGsdDirectory(
|
|
|
433
466
|
|
|
434
467
|
const gsd = gsdRoot(basePath);
|
|
435
468
|
mkdirSync(join(gsd, "milestones"), { recursive: true });
|
|
469
|
+
mkdirSync(join(gsd, "runtime"), { recursive: true });
|
|
436
470
|
|
|
437
471
|
// Write PREFERENCES.md from wizard answers
|
|
438
472
|
const preferencesContent = buildPreferencesFile(prefs);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
const sourcePath = join(import.meta.dirname, "..", "auto.ts");
|
|
7
|
+
const source = readFileSync(sourcePath, "utf-8");
|
|
8
|
+
|
|
9
|
+
test("auto-mode captures GSD_PROJECT_ROOT before entering the dispatch loop", () => {
|
|
10
|
+
const captureDeclIdx = source.indexOf("function captureProjectRootEnv(projectRoot: string): void {");
|
|
11
|
+
assert.ok(captureDeclIdx > -1, "auto.ts should define captureProjectRootEnv()");
|
|
12
|
+
|
|
13
|
+
const resumeCallIdx = source.indexOf("captureProjectRootEnv(s.originalBasePath || s.basePath);");
|
|
14
|
+
assert.ok(resumeCallIdx > -1, "auto.ts should capture GSD_PROJECT_ROOT before resume autoLoop");
|
|
15
|
+
|
|
16
|
+
const firstAutoLoopIdx = source.indexOf("await autoLoop(ctx, pi, s, buildLoopDeps());");
|
|
17
|
+
assert.ok(firstAutoLoopIdx > -1, "auto.ts should invoke autoLoop()");
|
|
18
|
+
assert.ok(
|
|
19
|
+
resumeCallIdx < firstAutoLoopIdx,
|
|
20
|
+
"auto.ts must set GSD_PROJECT_ROOT before the first autoLoop() call",
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("auto-mode restores GSD_PROJECT_ROOT when execution stops or pauses", () => {
|
|
25
|
+
assert.match(source, /function restoreProjectRootEnv\(\): void \{/);
|
|
26
|
+
assert.match(source, /cleanupAfterLoopExit\(ctx: ExtensionContext\): void \{[\s\S]*restoreProjectRootEnv\(\);/);
|
|
27
|
+
assert.match(source, /export async function pauseAuto\([\s\S]*restoreProjectRootEnv\(\);/);
|
|
28
|
+
assert.match(source, /\} finally \{[\s\S]*restoreProjectRootEnv\(\);[\s\S]*s\.reset\(\);/);
|
|
29
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GSD Init Wizard — Bootstrap completeness regression tests
|
|
3
|
+
*
|
|
4
|
+
* Regression test for #3880 — fresh install never creates gsd.db.
|
|
5
|
+
*
|
|
6
|
+
* The init wizard must create all artifacts needed for full-capability
|
|
7
|
+
* mode: gsd.db (via ensureDbOpen), runtime/ directory, and STATE.md
|
|
8
|
+
* (via deriveState + buildStateMarkdown). Without these, GSD enters
|
|
9
|
+
* degraded markdown-only mode on every fresh install.
|
|
10
|
+
*
|
|
11
|
+
* These are structural tests that verify the init-wizard.ts source
|
|
12
|
+
* contains the required calls in the correct order.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { describe, test } from "node:test";
|
|
16
|
+
import assert from "node:assert/strict";
|
|
17
|
+
import { readFileSync } from "node:fs";
|
|
18
|
+
import { fileURLToPath } from "node:url";
|
|
19
|
+
import { dirname, join } from "node:path";
|
|
20
|
+
|
|
21
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
22
|
+
const __dirname = dirname(__filename);
|
|
23
|
+
|
|
24
|
+
const wizardSrc = readFileSync(
|
|
25
|
+
join(__dirname, "..", "init-wizard.ts"),
|
|
26
|
+
"utf-8",
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
describe("init-wizard bootstrap completeness (#3880)", () => {
|
|
30
|
+
// ── Gap 1: gsd.db must be created during init ─────────────────────────
|
|
31
|
+
|
|
32
|
+
test("bootstrapGsdDirectory is followed by ensureDbOpen", () => {
|
|
33
|
+
const bootstrapIdx = wizardSrc.indexOf("bootstrapGsdDirectory(basePath");
|
|
34
|
+
const ensureDbIdx = wizardSrc.indexOf("ensureDbOpen(basePath)");
|
|
35
|
+
assert.ok(bootstrapIdx > -1, "bootstrapGsdDirectory call should exist");
|
|
36
|
+
assert.ok(ensureDbIdx > -1, "ensureDbOpen(basePath) call should exist");
|
|
37
|
+
assert.ok(
|
|
38
|
+
ensureDbIdx > bootstrapIdx,
|
|
39
|
+
"ensureDbOpen must appear after bootstrapGsdDirectory so .gsd/ exists first",
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("ensureDbOpen is imported from dynamic-tools", () => {
|
|
44
|
+
assert.match(
|
|
45
|
+
wizardSrc,
|
|
46
|
+
/import.*dynamic-tools/,
|
|
47
|
+
"init-wizard should import from dynamic-tools for ensureDbOpen",
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// ── Gap 2: runtime/ directory must be created during init ──────────────
|
|
52
|
+
|
|
53
|
+
test("bootstrapGsdDirectory creates runtime/ directory", () => {
|
|
54
|
+
// Find the bootstrapGsdDirectory function body
|
|
55
|
+
const fnStart = wizardSrc.indexOf("function bootstrapGsdDirectory(");
|
|
56
|
+
assert.ok(fnStart > -1, "bootstrapGsdDirectory function should exist");
|
|
57
|
+
|
|
58
|
+
// Find the next function definition to bound the search
|
|
59
|
+
const fnBody = wizardSrc.slice(fnStart, wizardSrc.indexOf("\nfunction ", fnStart + 1));
|
|
60
|
+
|
|
61
|
+
assert.match(
|
|
62
|
+
fnBody,
|
|
63
|
+
/mkdirSync\(.*"runtime"/,
|
|
64
|
+
'bootstrapGsdDirectory should create "runtime" directory',
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// ── Gap 3: STATE.md must be written during init ────────────────────────
|
|
69
|
+
|
|
70
|
+
test("showProjectInit generates STATE.md after bootstrap", () => {
|
|
71
|
+
const bootstrapIdx = wizardSrc.indexOf("bootstrapGsdDirectory(basePath");
|
|
72
|
+
const deriveIdx = wizardSrc.indexOf("deriveState(basePath)");
|
|
73
|
+
const stateIdx = wizardSrc.indexOf("buildStateMarkdown(state)");
|
|
74
|
+
const saveIdx = wizardSrc.indexOf('resolveGsdRootFile(basePath, "STATE")');
|
|
75
|
+
|
|
76
|
+
assert.ok(deriveIdx > -1, "deriveState call should exist in init-wizard");
|
|
77
|
+
assert.ok(stateIdx > -1, "buildStateMarkdown call should exist in init-wizard");
|
|
78
|
+
assert.ok(saveIdx > -1, "resolveGsdRootFile STATE call should exist in init-wizard");
|
|
79
|
+
assert.ok(
|
|
80
|
+
deriveIdx > bootstrapIdx,
|
|
81
|
+
"deriveState must appear after bootstrapGsdDirectory",
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// ── Ordering: DB must be open before deriveState ───────────────────────
|
|
86
|
+
|
|
87
|
+
test("ensureDbOpen appears before deriveState", () => {
|
|
88
|
+
const ensureDbIdx = wizardSrc.indexOf("ensureDbOpen(basePath)");
|
|
89
|
+
const deriveIdx = wizardSrc.indexOf("deriveState(basePath)");
|
|
90
|
+
assert.ok(ensureDbIdx > -1, "ensureDbOpen should exist");
|
|
91
|
+
assert.ok(deriveIdx > -1, "deriveState should exist");
|
|
92
|
+
assert.ok(
|
|
93
|
+
ensureDbIdx < deriveIdx,
|
|
94
|
+
"ensureDbOpen must appear before deriveState so DB is ready for state derivation",
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// ── Failure visibility: user must be warned on partial bootstrap ───────
|
|
99
|
+
|
|
100
|
+
test("ensureDbOpen failure surfaces a warning to the user", () => {
|
|
101
|
+
assert.match(
|
|
102
|
+
wizardSrc,
|
|
103
|
+
/if\s*\(\s*!dbReady\s*\)/,
|
|
104
|
+
"init-wizard should check dbReady and warn the user on failure",
|
|
105
|
+
);
|
|
106
|
+
// The warning must reference degraded mode so the user knows what happened
|
|
107
|
+
assert.match(
|
|
108
|
+
wizardSrc,
|
|
109
|
+
/degraded mode/,
|
|
110
|
+
"DB failure warning should mention degraded mode",
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("STATE.md failure surfaces a warning to the user", () => {
|
|
115
|
+
assert.match(
|
|
116
|
+
wizardSrc,
|
|
117
|
+
/if\s*\(\s*!stateReady\s*\)/,
|
|
118
|
+
"init-wizard should check stateReady and warn the user on failure",
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import test from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import { readFileSync } from "node:fs";
|
|
3
|
+
import { mkdtempSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
5
6
|
import { fileURLToPath } from "node:url";
|
|
6
7
|
|
|
7
8
|
import {
|
|
@@ -70,6 +71,43 @@ test("buildWorkflowMcpServers mirrors explicit launch config", () => {
|
|
|
70
71
|
});
|
|
71
72
|
});
|
|
72
73
|
|
|
74
|
+
test("detectWorkflowMcpLaunchConfig resolves the bundled server from GSD_PROJECT_ROOT", () => {
|
|
75
|
+
const repoRoot = mkdtempSync(join(tmpdir(), "gsd-workflow-root-"));
|
|
76
|
+
const worktreeRoot = mkdtempSync(join(tmpdir(), "gsd-workflow-worktree-"));
|
|
77
|
+
const cliPath = join(repoRoot, "packages", "mcp-server", "dist", "cli.js");
|
|
78
|
+
|
|
79
|
+
mkdirSync(join(repoRoot, "packages", "mcp-server", "dist"), { recursive: true });
|
|
80
|
+
writeFileSync(cliPath, "#!/usr/bin/env node\n", "utf-8");
|
|
81
|
+
|
|
82
|
+
const launch = detectWorkflowMcpLaunchConfig(worktreeRoot, {
|
|
83
|
+
GSD_PROJECT_ROOT: repoRoot,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
assert.deepEqual(launch, {
|
|
87
|
+
name: "gsd-workflow",
|
|
88
|
+
command: process.execPath,
|
|
89
|
+
args: [cliPath],
|
|
90
|
+
cwd: repoRoot,
|
|
91
|
+
env: {
|
|
92
|
+
GSD_PERSIST_WRITE_GATE_STATE: "1",
|
|
93
|
+
GSD_WORKFLOW_PROJECT_ROOT: repoRoot,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("detectWorkflowMcpLaunchConfig resolves the bundled server relative to the installed GSD package", () => {
|
|
99
|
+
const launch = detectWorkflowMcpLaunchConfig("/tmp/project", {
|
|
100
|
+
GSD_BIN_PATH: "/tmp/gsd-loader.js",
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
assert.equal(launch?.command, process.execPath);
|
|
104
|
+
assert.equal(launch?.cwd, "/tmp/project");
|
|
105
|
+
assert.equal(launch?.env?.GSD_CLI_PATH, "/tmp/gsd-loader.js");
|
|
106
|
+
assert.equal(launch?.env?.GSD_WORKFLOW_PROJECT_ROOT, "/tmp/project");
|
|
107
|
+
assert.equal(typeof launch?.args?.[0], "string");
|
|
108
|
+
assert.match(launch?.args?.[0] ?? "", /packages[\/\\]mcp-server[\/\\]dist[\/\\]cli\.js$/);
|
|
109
|
+
});
|
|
110
|
+
|
|
73
111
|
test("usesWorkflowMcpTransport matches local externalCli providers", () => {
|
|
74
112
|
assert.equal(usesWorkflowMcpTransport("externalCli", "local://claude-code"), true);
|
|
75
113
|
assert.equal(usesWorkflowMcpTransport("externalCli", "https://api.example.com"), false);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { execSync } from "node:child_process";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
4
5
|
|
|
5
6
|
export interface WorkflowMcpLaunchConfig {
|
|
6
7
|
name: string;
|
|
@@ -66,6 +67,21 @@ function lookupCommand(command: string, platform: NodeJS.Platform = process.plat
|
|
|
66
67
|
}
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
function getBundledWorkflowMcpCliPath(env: NodeJS.ProcessEnv): string | null {
|
|
71
|
+
if (!env.GSD_BIN_PATH?.trim() && !env.GSD_CLI_PATH?.trim()) return null;
|
|
72
|
+
|
|
73
|
+
const candidates = [
|
|
74
|
+
resolve(fileURLToPath(new URL("../../../../packages/mcp-server/dist/cli.js", import.meta.url))),
|
|
75
|
+
resolve(fileURLToPath(new URL("../../../../../packages/mcp-server/dist/cli.js", import.meta.url))),
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
for (const bundledCli of candidates) {
|
|
79
|
+
if (existsSync(bundledCli)) return bundledCli;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
69
85
|
export function detectWorkflowMcpLaunchConfig(
|
|
70
86
|
projectRoot = process.cwd(),
|
|
71
87
|
env: NodeJS.ProcessEnv = process.env,
|
|
@@ -75,16 +91,19 @@ export function detectWorkflowMcpLaunchConfig(
|
|
|
75
91
|
const explicitArgs = parseJsonEnv<unknown>(env, "GSD_WORKFLOW_MCP_ARGS");
|
|
76
92
|
const explicitEnv = parseJsonEnv<Record<string, string>>(env, "GSD_WORKFLOW_MCP_ENV");
|
|
77
93
|
const explicitCwd = env.GSD_WORKFLOW_MCP_CWD?.trim();
|
|
94
|
+
const gsdCliPath = env.GSD_CLI_PATH?.trim() || env.GSD_BIN_PATH?.trim();
|
|
78
95
|
const workflowProjectRoot =
|
|
79
96
|
explicitEnv?.GSD_WORKFLOW_PROJECT_ROOT?.trim() ||
|
|
80
97
|
env.GSD_WORKFLOW_PROJECT_ROOT?.trim() ||
|
|
98
|
+
env.GSD_PROJECT_ROOT?.trim() ||
|
|
81
99
|
explicitCwd ||
|
|
82
100
|
projectRoot;
|
|
101
|
+
const resolvedWorkflowProjectRoot = resolve(workflowProjectRoot);
|
|
83
102
|
|
|
84
103
|
if (explicitCommand) {
|
|
85
104
|
const launchEnv = {
|
|
86
105
|
...(explicitEnv ?? {}),
|
|
87
|
-
...(
|
|
106
|
+
...(gsdCliPath ? { GSD_CLI_PATH: gsdCliPath } : {}),
|
|
88
107
|
GSD_PERSIST_WRITE_GATE_STATE: "1",
|
|
89
108
|
GSD_WORKFLOW_PROJECT_ROOT: resolve(workflowProjectRoot),
|
|
90
109
|
};
|
|
@@ -97,17 +116,32 @@ export function detectWorkflowMcpLaunchConfig(
|
|
|
97
116
|
};
|
|
98
117
|
}
|
|
99
118
|
|
|
100
|
-
const distCli = resolve(
|
|
119
|
+
const distCli = resolve(resolvedWorkflowProjectRoot, "packages", "mcp-server", "dist", "cli.js");
|
|
101
120
|
if (existsSync(distCli)) {
|
|
102
121
|
return {
|
|
103
122
|
name,
|
|
104
123
|
command: process.execPath,
|
|
105
124
|
args: [distCli],
|
|
106
|
-
cwd:
|
|
125
|
+
cwd: resolvedWorkflowProjectRoot,
|
|
126
|
+
env: {
|
|
127
|
+
...(gsdCliPath ? { GSD_CLI_PATH: gsdCliPath } : {}),
|
|
128
|
+
GSD_PERSIST_WRITE_GATE_STATE: "1",
|
|
129
|
+
GSD_WORKFLOW_PROJECT_ROOT: resolvedWorkflowProjectRoot,
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const bundledCli = getBundledWorkflowMcpCliPath(env);
|
|
135
|
+
if (bundledCli) {
|
|
136
|
+
return {
|
|
137
|
+
name,
|
|
138
|
+
command: process.execPath,
|
|
139
|
+
args: [bundledCli],
|
|
140
|
+
cwd: resolvedWorkflowProjectRoot,
|
|
107
141
|
env: {
|
|
108
|
-
...(
|
|
142
|
+
...(gsdCliPath ? { GSD_CLI_PATH: gsdCliPath } : {}),
|
|
109
143
|
GSD_PERSIST_WRITE_GATE_STATE: "1",
|
|
110
|
-
GSD_WORKFLOW_PROJECT_ROOT:
|
|
144
|
+
GSD_WORKFLOW_PROJECT_ROOT: resolvedWorkflowProjectRoot,
|
|
111
145
|
},
|
|
112
146
|
};
|
|
113
147
|
}
|
|
@@ -118,9 +152,9 @@ export function detectWorkflowMcpLaunchConfig(
|
|
|
118
152
|
name,
|
|
119
153
|
command: binPath,
|
|
120
154
|
env: {
|
|
121
|
-
...(
|
|
155
|
+
...(gsdCliPath ? { GSD_CLI_PATH: gsdCliPath } : {}),
|
|
122
156
|
GSD_PERSIST_WRITE_GATE_STATE: "1",
|
|
123
|
-
GSD_WORKFLOW_PROJECT_ROOT:
|
|
157
|
+
GSD_WORKFLOW_PROJECT_ROOT: resolvedWorkflowProjectRoot,
|
|
124
158
|
},
|
|
125
159
|
};
|
|
126
160
|
}
|
|
File without changes
|
|
File without changes
|