gsd-pi 2.52.0 → 2.53.0-dev.07ffe51
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 +55 -32
- package/dist/headless-query.js +1 -1
- package/dist/resources/extensions/get-secrets-from-user.js +7 -0
- package/dist/resources/extensions/gsd/auto/phases.js +28 -8
- package/dist/resources/extensions/gsd/auto-dispatch.js +5 -1
- package/dist/resources/extensions/gsd/auto-worktree.js +70 -14
- package/dist/resources/extensions/gsd/auto.js +22 -0
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +4 -10
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +3 -3
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +2 -2
- package/dist/resources/extensions/gsd/git-service.js +4 -3
- package/dist/resources/extensions/gsd/guided-flow.js +4 -3
- package/dist/resources/extensions/gsd/markdown-renderer.js +5 -4
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +18 -2
- package/dist/resources/extensions/gsd/preferences-types.js +1 -1
- package/dist/resources/extensions/gsd/state.js +18 -29
- package/dist/resources/extensions/gsd/status-guards.js +12 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +4 -3
- package/dist/resources/extensions/gsd/tools/complete-slice.js +4 -3
- package/dist/resources/extensions/gsd/tools/complete-task.js +4 -3
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +4 -14
- package/dist/resources/extensions/gsd/tools/plan-slice.js +4 -14
- package/dist/resources/extensions/gsd/tools/plan-task.js +4 -14
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +6 -7
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +4 -3
- package/dist/resources/extensions/gsd/tools/reopen-task.js +5 -4
- package/dist/resources/extensions/gsd/tools/replan-slice.js +5 -6
- package/dist/resources/extensions/gsd/validation.js +21 -0
- package/dist/resources/extensions/shared/rtk.js +14 -4
- package/dist/rtk.js +3 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +16 -16
- package/dist/web/standalone/.next/build-manifest.json +4 -4
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- 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 +2 -2
- 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/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 +2 -2
- 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 +2 -2
- 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 +4 -4
- 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 +16 -16
- package/dist/web/standalone/.next/server/chunks/2229.js +1 -1
- package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
- 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/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 +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/4024.87fd909ae0110f50.js +9 -0
- 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-b950e4e384cc62b3.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/.next/static/chunks/{webpack-024d82be84800e52.js → webpack-bca0e732db0dcec3.js} +1 -1
- 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/pi-coding-agent/package.json +1 -1
- package/pkg/package.json +1 -1
- package/scripts/ensure-workspace-builds.cjs +36 -8
- package/src/resources/extensions/get-secrets-from-user.ts +8 -0
- package/src/resources/extensions/gsd/auto/phases.ts +38 -7
- package/src/resources/extensions/gsd/auto-dispatch.ts +6 -1
- package/src/resources/extensions/gsd/auto-worktree.ts +73 -14
- package/src/resources/extensions/gsd/auto.ts +21 -0
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +4 -11
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +3 -3
- package/src/resources/extensions/gsd/docs/preferences-reference.md +2 -2
- package/src/resources/extensions/gsd/git-service.ts +4 -3
- package/src/resources/extensions/gsd/guided-flow.ts +4 -3
- package/src/resources/extensions/gsd/markdown-renderer.ts +5 -4
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +23 -1
- package/src/resources/extensions/gsd/preferences-types.ts +1 -1
- package/src/resources/extensions/gsd/state.ts +18 -29
- package/src/resources/extensions/gsd/status-guards.ts +13 -0
- package/src/resources/extensions/gsd/tests/active-milestone-id-guard.test.ts +91 -0
- package/src/resources/extensions/gsd/tests/auto-stale-lock-self-kill.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-auto-resolve.test.ts +80 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +64 -30
- package/src/resources/extensions/gsd/tests/milestone-report-path.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/parallel-orchestrator-zombie-cleanup.test.ts +277 -0
- package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/rate-limit-model-fallback.test.ts +90 -0
- package/src/resources/extensions/gsd/tests/session-lock-transient-read.test.ts +9 -8
- package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/status-guards.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +12 -2
- package/src/resources/extensions/gsd/tests/validation-gate-patterns.test.ts +124 -0
- package/src/resources/extensions/gsd/tests/validation.test.ts +72 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +4 -3
- package/src/resources/extensions/gsd/tools/complete-slice.ts +4 -3
- package/src/resources/extensions/gsd/tools/complete-task.ts +4 -3
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +4 -16
- package/src/resources/extensions/gsd/tools/plan-slice.ts +4 -16
- package/src/resources/extensions/gsd/tools/plan-task.ts +4 -16
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +6 -7
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +4 -3
- package/src/resources/extensions/gsd/tools/reopen-task.ts +5 -4
- package/src/resources/extensions/gsd/tools/replan-slice.ts +5 -7
- package/src/resources/extensions/gsd/validation.ts +23 -0
- package/src/resources/extensions/shared/rtk.ts +22 -4
- package/dist/web/standalone/.next/static/chunks/4024.21054f459af5cc78.js +0 -9
- package/dist/web/standalone/.next/static/chunks/app/page-fbecd1237e2d6d1f.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/dist/web/standalone/.next/static/{vlgS2rkXjxeKhgXhdp4lh → Q5pfrfJIvgUKR3LJLVB0T}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{vlgS2rkXjxeKhgXhdp4lh → Q5pfrfJIvgUKR3LJLVB0T}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdirSync, mkdtempSync, writeFileSync, existsSync, rmSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
|
|
7
|
+
import { writeLock, readCrashLock, clearLock } from "../crash-recovery.ts";
|
|
8
|
+
import { checkRemoteAutoSession, stopAutoRemote } from "../auto.ts";
|
|
9
|
+
|
|
10
|
+
function makeTmpProject(): string {
|
|
11
|
+
const dir = mkdtempSync(join(tmpdir(), "gsd-stale-lock-test-"));
|
|
12
|
+
mkdirSync(join(dir, ".gsd"), { recursive: true });
|
|
13
|
+
return dir;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// ─── checkRemoteAutoSession: own-PID filtering (#2730) ───────────────────
|
|
17
|
+
|
|
18
|
+
test("#2730: checkRemoteAutoSession returns { running: false } when lock PID matches current process", (t) => {
|
|
19
|
+
const dir = makeTmpProject();
|
|
20
|
+
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
|
21
|
+
|
|
22
|
+
// Write a lock with the current process PID — simulates a stale lock
|
|
23
|
+
// left behind after step-mode exit without full cleanup.
|
|
24
|
+
writeLock(dir, "execute-task", "M001/S01/T01");
|
|
25
|
+
|
|
26
|
+
const lock = readCrashLock(dir);
|
|
27
|
+
assert.ok(lock, "lock file should exist");
|
|
28
|
+
assert.equal(lock!.pid, process.pid, "lock should have our PID");
|
|
29
|
+
|
|
30
|
+
const result = checkRemoteAutoSession(dir);
|
|
31
|
+
assert.equal(result.running, false, "own PID must not be treated as a remote session");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("#2730: checkRemoteAutoSession still detects a genuine remote session (different PID)", (t) => {
|
|
35
|
+
const dir = makeTmpProject();
|
|
36
|
+
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
|
37
|
+
|
|
38
|
+
// Use parent PID — guaranteed alive, guaranteed not our PID.
|
|
39
|
+
const remotePid = process.ppid;
|
|
40
|
+
const lockData = {
|
|
41
|
+
pid: remotePid,
|
|
42
|
+
startedAt: new Date().toISOString(),
|
|
43
|
+
unitType: "execute-task",
|
|
44
|
+
unitId: "M001/S01/T02",
|
|
45
|
+
unitStartedAt: new Date().toISOString(),
|
|
46
|
+
};
|
|
47
|
+
writeFileSync(join(dir, ".gsd", "auto.lock"), JSON.stringify(lockData, null, 2));
|
|
48
|
+
|
|
49
|
+
const result = checkRemoteAutoSession(dir);
|
|
50
|
+
assert.equal(result.running, true, "different live PID should be detected as running");
|
|
51
|
+
assert.equal(result.pid, remotePid);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// ─── stopAutoRemote: self-kill prevention (#2730) ────────────────────────
|
|
55
|
+
|
|
56
|
+
test("#2730: stopAutoRemote does not send SIGTERM when lock PID matches current process", (t) => {
|
|
57
|
+
const dir = makeTmpProject();
|
|
58
|
+
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
|
59
|
+
|
|
60
|
+
// Write a lock with our own PID
|
|
61
|
+
writeLock(dir, "execute-task", "M001/S01/T01");
|
|
62
|
+
|
|
63
|
+
const result = stopAutoRemote(dir);
|
|
64
|
+
assert.equal(result.found, false, "own PID must not be signalled");
|
|
65
|
+
|
|
66
|
+
// The lock should be cleared as part of the self-detection cleanup
|
|
67
|
+
assert.ok(!existsSync(join(dir, ".gsd", "auto.lock")), "stale self-lock should be cleared");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("#2730: stopAutoRemote clears stale lock from dead remote process without error", (t) => {
|
|
71
|
+
const dir = makeTmpProject();
|
|
72
|
+
t.after(() => rmSync(dir, { recursive: true, force: true }));
|
|
73
|
+
|
|
74
|
+
// Simulate a stale lock from a process that no longer exists
|
|
75
|
+
const lockData = {
|
|
76
|
+
pid: 9999999,
|
|
77
|
+
startedAt: "2026-03-01T00:00:00Z",
|
|
78
|
+
unitType: "plan-slice",
|
|
79
|
+
unitId: "M001/S02",
|
|
80
|
+
unitStartedAt: "2026-03-01T00:05:00Z",
|
|
81
|
+
};
|
|
82
|
+
writeFileSync(join(dir, ".gsd", "auto.lock"), JSON.stringify(lockData, null, 2));
|
|
83
|
+
|
|
84
|
+
const result = stopAutoRemote(dir);
|
|
85
|
+
assert.equal(result.found, false, "dead remote PID should not be reported as found");
|
|
86
|
+
assert.ok(!existsSync(join(dir, ".gsd", "auto.lock")), "stale lock should be cleaned up");
|
|
87
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* auto-worktree-auto-resolve.test.ts — Unit tests for isSafeToAutoResolve.
|
|
3
|
+
*
|
|
4
|
+
* Covers: .gsd/ state files, build artifacts (.tsbuildinfo, .pyc, __pycache__,
|
|
5
|
+
* .DS_Store, .map), and rejection of real source files.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, test } from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
isSafeToAutoResolve,
|
|
13
|
+
SAFE_AUTO_RESOLVE_PATTERNS,
|
|
14
|
+
} from "../auto-worktree.ts";
|
|
15
|
+
|
|
16
|
+
describe("isSafeToAutoResolve", () => {
|
|
17
|
+
// ─── .gsd/ state files ───────────────────────────────────────────────────
|
|
18
|
+
test("returns true for .gsd/ prefixed paths", () => {
|
|
19
|
+
assert.ok(isSafeToAutoResolve(".gsd/STATE.md"));
|
|
20
|
+
assert.ok(isSafeToAutoResolve(".gsd/milestones/M001/CONTEXT.md"));
|
|
21
|
+
assert.ok(isSafeToAutoResolve(".gsd/gsd.db"));
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// ─── Build artifact patterns ─────────────────────────────────────────────
|
|
25
|
+
test("returns true for .tsbuildinfo files", () => {
|
|
26
|
+
assert.ok(isSafeToAutoResolve("tsconfig.tsbuildinfo"));
|
|
27
|
+
assert.ok(isSafeToAutoResolve("dist/tsconfig.tsbuildinfo"));
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("returns true for .pyc files", () => {
|
|
31
|
+
assert.ok(isSafeToAutoResolve("module.pyc"));
|
|
32
|
+
assert.ok(isSafeToAutoResolve("src/utils/helpers.pyc"));
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("returns true for __pycache__/ paths", () => {
|
|
36
|
+
assert.ok(isSafeToAutoResolve("src/__pycache__/module.cpython-311.pyc"));
|
|
37
|
+
assert.ok(isSafeToAutoResolve("lib/__pycache__/foo.py"));
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("returns true for .DS_Store files", () => {
|
|
41
|
+
assert.ok(isSafeToAutoResolve(".DS_Store"));
|
|
42
|
+
assert.ok(isSafeToAutoResolve("src/.DS_Store"));
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("returns true for .map source map files", () => {
|
|
46
|
+
assert.ok(isSafeToAutoResolve("dist/index.js.map"));
|
|
47
|
+
assert.ok(isSafeToAutoResolve("out/bundle.css.map"));
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// ─── Real source files (should NOT be auto-resolved) ─────────────────────
|
|
51
|
+
test("returns false for .ts source files", () => {
|
|
52
|
+
assert.ok(!isSafeToAutoResolve("src/index.ts"));
|
|
53
|
+
assert.ok(!isSafeToAutoResolve("lib/utils.ts"));
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("returns false for .js source files", () => {
|
|
57
|
+
assert.ok(!isSafeToAutoResolve("src/index.js"));
|
|
58
|
+
assert.ok(!isSafeToAutoResolve("lib/helpers.js"));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("returns false for .py source files", () => {
|
|
62
|
+
assert.ok(!isSafeToAutoResolve("src/main.py"));
|
|
63
|
+
assert.ok(!isSafeToAutoResolve("scripts/deploy.py"));
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("returns false for config and data files", () => {
|
|
67
|
+
assert.ok(!isSafeToAutoResolve("package.json"));
|
|
68
|
+
assert.ok(!isSafeToAutoResolve("tsconfig.json"));
|
|
69
|
+
assert.ok(!isSafeToAutoResolve("README.md"));
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// ─── SAFE_AUTO_RESOLVE_PATTERNS export ────────────────────────────────────
|
|
73
|
+
test("SAFE_AUTO_RESOLVE_PATTERNS is a non-empty array of RegExp", () => {
|
|
74
|
+
assert.ok(Array.isArray(SAFE_AUTO_RESOLVE_PATTERNS));
|
|
75
|
+
assert.ok(SAFE_AUTO_RESOLVE_PATTERNS.length > 0);
|
|
76
|
+
for (const pattern of SAFE_AUTO_RESOLVE_PATTERNS) {
|
|
77
|
+
assert.ok(pattern instanceof RegExp);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -77,7 +77,7 @@ function addSliceToMilestone(
|
|
|
77
77
|
run(`git branch -d ${sliceBranch}`, wtPath);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
describe("auto-worktree-milestone-merge", () => {
|
|
80
|
+
describe("auto-worktree-milestone-merge", { timeout: 300_000 }, () => {
|
|
81
81
|
const savedCwd = process.cwd();
|
|
82
82
|
const tempDirs: string[] = [];
|
|
83
83
|
|
|
@@ -227,6 +227,45 @@ test("collectSecretsFromManifest: manifest statuses are updated after collection
|
|
|
227
227
|
"KEY_TO_SKIP should have status 'skipped' after user skipped it");
|
|
228
228
|
});
|
|
229
229
|
|
|
230
|
+
test("collectSecretsFromManifest: applied keys hydrate process.env for the running session", async (t) => {
|
|
231
|
+
const { collectSecretsFromManifest } = await loadOrchestrator();
|
|
232
|
+
|
|
233
|
+
const tmp = makeTempDir("manifest-live-env");
|
|
234
|
+
const envKey = "CONTEXT7_API_KEY";
|
|
235
|
+
const saved = process.env[envKey];
|
|
236
|
+
t.after(() => {
|
|
237
|
+
if (saved === undefined) delete process.env[envKey];
|
|
238
|
+
else process.env[envKey] = saved;
|
|
239
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
delete process.env[envKey];
|
|
243
|
+
|
|
244
|
+
const manifest = makeManifest([
|
|
245
|
+
{ key: envKey, status: "pending" },
|
|
246
|
+
]);
|
|
247
|
+
await writeManifestFile(tmp, manifest);
|
|
248
|
+
|
|
249
|
+
let callIndex = 0;
|
|
250
|
+
const mockCtx = {
|
|
251
|
+
cwd: tmp,
|
|
252
|
+
hasUI: true,
|
|
253
|
+
ui: {
|
|
254
|
+
custom: async (_factory: any) => {
|
|
255
|
+
callIndex++;
|
|
256
|
+
if (callIndex <= 1) return null; // summary screen dismiss
|
|
257
|
+
return "c7_live_test_key";
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const result = await collectSecretsFromManifest(tmp, "M001", mockCtx as any);
|
|
263
|
+
|
|
264
|
+
assert.ok(result.applied.includes(envKey), "CONTEXT7_API_KEY should be applied");
|
|
265
|
+
assert.equal(process.env[envKey], "c7_live_test_key",
|
|
266
|
+
"applied keys should be available through process.env without restarting");
|
|
267
|
+
});
|
|
268
|
+
|
|
230
269
|
// ─── showSecretsSummary: render output ────────────────────────────────────────
|
|
231
270
|
|
|
232
271
|
test("showSecretsSummary: produces lines with correct status glyphs for each entry status", async () => {
|
|
@@ -287,9 +287,9 @@ describe('git-service', async () => {
|
|
|
287
287
|
|
|
288
288
|
|
|
289
289
|
const tempDir = mkdtempSync(join(tmpdir(), "gsd-git-service-test-"));
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
290
|
+
runGit(tempDir, ["init", "-b", "main"]);
|
|
291
|
+
runGit(tempDir, ["config", "user.name", "Pi Test"]);
|
|
292
|
+
runGit(tempDir, ["config", "user.email", "pi@example.com"]);
|
|
293
293
|
|
|
294
294
|
// runGit should work on a valid repo
|
|
295
295
|
const branch = runGit(tempDir, ["branch", "--show-current"]);
|
|
@@ -334,13 +334,13 @@ describe('git-service', async () => {
|
|
|
334
334
|
|
|
335
335
|
function initTempRepo(): string {
|
|
336
336
|
const dir = mkdtempSync(join(tmpdir(), "gsd-git-t02-"));
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
337
|
+
runGit(dir, ["init", "-b", "main"]);
|
|
338
|
+
runGit(dir, ["config", "user.name", "Pi Test"]);
|
|
339
|
+
runGit(dir, ["config", "user.email", "pi@example.com"]);
|
|
340
340
|
// Need an initial commit so HEAD exists
|
|
341
341
|
createFile(dir, ".gitkeep", "");
|
|
342
|
-
|
|
343
|
-
|
|
342
|
+
runGit(dir, ["add", "-A"]);
|
|
343
|
+
runGit(dir, ["commit", "-m", "init"]);
|
|
344
344
|
return dir;
|
|
345
345
|
}
|
|
346
346
|
|
|
@@ -577,12 +577,12 @@ describe('git-service', async () => {
|
|
|
577
577
|
|
|
578
578
|
function initBranchTestRepo(): string {
|
|
579
579
|
const dir = mkdtempSync(join(tmpdir(), "gsd-git-t03-"));
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
580
|
+
runGit(dir, ["init", "-b", "main"]);
|
|
581
|
+
runGit(dir, ["config", "user.name", "Pi Test"]);
|
|
582
|
+
runGit(dir, ["config", "user.email", "pi@example.com"]);
|
|
583
583
|
createFile(dir, ".gitkeep", "");
|
|
584
|
-
|
|
585
|
-
|
|
584
|
+
runGit(dir, ["add", "-A"]);
|
|
585
|
+
runGit(dir, ["commit", "-m", "init"]);
|
|
586
586
|
return dir;
|
|
587
587
|
}
|
|
588
588
|
|
|
@@ -618,12 +618,12 @@ describe('git-service', async () => {
|
|
|
618
618
|
{
|
|
619
619
|
// master-only repo
|
|
620
620
|
const repo = mkdtempSync(join(tmpdir(), "gsd-git-t03-master-"));
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
621
|
+
runGit(repo, ["init", "-b", "master"]);
|
|
622
|
+
runGit(repo, ["config", "user.name", "Pi Test"]);
|
|
623
|
+
runGit(repo, ["config", "user.email", "pi@example.com"]);
|
|
624
624
|
createFile(repo, ".gitkeep", "");
|
|
625
|
-
|
|
626
|
-
|
|
625
|
+
runGit(repo, ["add", "-A"]);
|
|
626
|
+
runGit(repo, ["commit", "-m", "init"]);
|
|
627
627
|
|
|
628
628
|
const svc = new GitServiceImpl(repo);
|
|
629
629
|
assert.deepStrictEqual(svc.getMainBranch(), "master", "getMainBranch returns master when only master exists");
|
|
@@ -635,11 +635,11 @@ describe('git-service', async () => {
|
|
|
635
635
|
// S05: Enhanced features — snapshots, pre-merge checks
|
|
636
636
|
// ═══════════════════════════════════════════════════════════════════════
|
|
637
637
|
|
|
638
|
-
// ─── createSnapshot:
|
|
638
|
+
// ─── createSnapshot: default (enabled) ─────────────────────────────────
|
|
639
639
|
|
|
640
|
-
test('createSnapshot: enabled', () => {
|
|
640
|
+
test('createSnapshot: enabled by default when prefs omitted', () => {
|
|
641
641
|
const repo = initBranchTestRepo();
|
|
642
|
-
const svc = new GitServiceImpl(repo
|
|
642
|
+
const svc = new GitServiceImpl(repo);
|
|
643
643
|
|
|
644
644
|
// Create a branch with a commit
|
|
645
645
|
run("git checkout -b gsd/M001/S01", repo);
|
|
@@ -675,6 +675,39 @@ describe('git-service', async () => {
|
|
|
675
675
|
rmSync(repo, { recursive: true, force: true });
|
|
676
676
|
});
|
|
677
677
|
|
|
678
|
+
// ─── runPreMergeCheck: default (auto-detect) ──────────────────────────
|
|
679
|
+
|
|
680
|
+
test('runPreMergeCheck: auto-detects when prefs omitted', () => {
|
|
681
|
+
const repo = initBranchTestRepo();
|
|
682
|
+
createFile(repo, "package.json", JSON.stringify({
|
|
683
|
+
name: "test-default",
|
|
684
|
+
scripts: { test: 'node -e "process.exit(0)"' },
|
|
685
|
+
}));
|
|
686
|
+
run("git add -A", repo);
|
|
687
|
+
run('git commit -m "add package.json"', repo);
|
|
688
|
+
|
|
689
|
+
// No pre_merge_check pref set — should auto-detect and run
|
|
690
|
+
const svc = new GitServiceImpl(repo);
|
|
691
|
+
const result: PreMergeCheckResult = svc.runPreMergeCheck();
|
|
692
|
+
|
|
693
|
+
assert.deepStrictEqual(result.passed, true, "runPreMergeCheck auto-detects and passes when prefs omitted");
|
|
694
|
+
assert.ok(!result.skipped, "runPreMergeCheck is not skipped when prefs omitted and package.json exists");
|
|
695
|
+
|
|
696
|
+
rmSync(repo, { recursive: true, force: true });
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
test('runPreMergeCheck: gracefully skips when prefs omitted and no package.json', () => {
|
|
700
|
+
const repo = initBranchTestRepo();
|
|
701
|
+
// No package.json — auto-detect should skip gracefully
|
|
702
|
+
const svc = new GitServiceImpl(repo);
|
|
703
|
+
const result: PreMergeCheckResult = svc.runPreMergeCheck();
|
|
704
|
+
|
|
705
|
+
assert.deepStrictEqual(result.passed, true, "runPreMergeCheck passes when no package.json (skip)");
|
|
706
|
+
assert.deepStrictEqual(result.skipped, true, "runPreMergeCheck skips when no test runner detected");
|
|
707
|
+
|
|
708
|
+
rmSync(repo, { recursive: true, force: true });
|
|
709
|
+
});
|
|
710
|
+
|
|
678
711
|
// ─── runPreMergeCheck: pass ────────────────────────────────────────────
|
|
679
712
|
|
|
680
713
|
test('runPreMergeCheck: pass', () => {
|
|
@@ -1082,9 +1115,9 @@ describe('git-service', async () => {
|
|
|
1082
1115
|
test('untrackRuntimeFiles', async () => {
|
|
1083
1116
|
const { untrackRuntimeFiles } = await import("../gitignore.ts");
|
|
1084
1117
|
const repo = mkdtempSync(join(tmpdir(), "gsd-untrack-"));
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1118
|
+
runGit(repo, ["init", "-b", "main"]);
|
|
1119
|
+
runGit(repo, ["config", "user.email", "test@test.com"]);
|
|
1120
|
+
runGit(repo, ["config", "user.name", "Test"]);
|
|
1088
1121
|
|
|
1089
1122
|
// Create and track runtime files (simulates pre-.gitignore state)
|
|
1090
1123
|
mkdirSync(join(repo, ".gsd", "activity"), { recursive: true });
|
|
@@ -1095,8 +1128,8 @@ describe('git-service', async () => {
|
|
|
1095
1128
|
writeFileSync(join(repo, ".gsd", "activity", "log.jsonl"), "{}");
|
|
1096
1129
|
writeFileSync(join(repo, ".gsd", "runtime", "data.json"), "{}");
|
|
1097
1130
|
writeFileSync(join(repo, "src.ts"), "code");
|
|
1098
|
-
|
|
1099
|
-
|
|
1131
|
+
runGit(repo, ["add", "-A"]);
|
|
1132
|
+
runGit(repo, ["commit", "-m", "init"]);
|
|
1100
1133
|
|
|
1101
1134
|
// Precondition: runtime files are tracked
|
|
1102
1135
|
const trackedBefore = run("git ls-files .gsd/", repo);
|
|
@@ -1131,11 +1164,12 @@ describe('git-service', async () => {
|
|
|
1131
1164
|
|
|
1132
1165
|
test('smartStage excludes runtime files, allows milestone artifacts', () => {
|
|
1133
1166
|
const repo = mkdtempSync(join(tmpdir(), "gsd-smart-stage-excludes-"));
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1167
|
+
runGit(repo, ["init", "-b", "main"]);
|
|
1168
|
+
runGit(repo, ["config", "user.email", "test@test.com"]);
|
|
1169
|
+
runGit(repo, ["config", "user.name", "Test"]);
|
|
1137
1170
|
writeFileSync(join(repo, "README.md"), "init");
|
|
1138
|
-
|
|
1171
|
+
runGit(repo, ["add", "-A"]);
|
|
1172
|
+
runGit(repo, ["commit", "-m", "init"]);
|
|
1139
1173
|
|
|
1140
1174
|
// Create .gsd/ runtime files + milestone artifacts + a normal source file
|
|
1141
1175
|
mkdirSync(join(repo, ".gsd", "milestones", "M001"), { recursive: true });
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* milestone-report-path.test.ts — Regression test for milestone report path resolution.
|
|
3
|
+
*
|
|
4
|
+
* When running in a worktree, milestone reports must be written to the
|
|
5
|
+
* original project root (originalBasePath), not the worktree path (basePath).
|
|
6
|
+
*
|
|
7
|
+
* Covers: _resolveReportBasePath from auto/phases.ts
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, test } from "node:test";
|
|
11
|
+
import assert from "node:assert/strict";
|
|
12
|
+
|
|
13
|
+
import { _resolveReportBasePath } from "../auto/phases.ts";
|
|
14
|
+
|
|
15
|
+
describe("_resolveReportBasePath", () => {
|
|
16
|
+
test("uses originalBasePath when set (worktree scenario)", () => {
|
|
17
|
+
const session = {
|
|
18
|
+
originalBasePath: "/projects/my-app",
|
|
19
|
+
basePath: "/projects/my-app/.claude/worktrees/agent-abc123",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
assert.equal(_resolveReportBasePath(session), "/projects/my-app");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("falls back to basePath when originalBasePath is empty", () => {
|
|
26
|
+
const session = {
|
|
27
|
+
originalBasePath: "",
|
|
28
|
+
basePath: "/projects/my-app",
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
assert.equal(_resolveReportBasePath(session), "/projects/my-app");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("falls back to basePath when originalBasePath is undefined", () => {
|
|
35
|
+
const session = {
|
|
36
|
+
originalBasePath: undefined as unknown as string,
|
|
37
|
+
basePath: "/projects/my-app",
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
assert.equal(_resolveReportBasePath(session), "/projects/my-app");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("uses originalBasePath even when basePath differs", () => {
|
|
44
|
+
const session = {
|
|
45
|
+
originalBasePath: "/home/user/repo",
|
|
46
|
+
basePath: "/tmp/worktree-xyz",
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
assert.equal(_resolveReportBasePath(session), "/home/user/repo");
|
|
50
|
+
});
|
|
51
|
+
});
|