gsd-pi 2.45.0-dev.6b9da3e → 2.45.0-dev.fdcf73c
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/help-text.js +1 -1
- package/dist/loader.js +34 -0
- package/dist/resources/extensions/gsd/auto/phases.js +16 -10
- package/dist/resources/extensions/gsd/auto/run-unit.js +6 -3
- package/dist/resources/extensions/gsd/auto-worktree.js +5 -4
- package/dist/resources/extensions/gsd/auto.js +4 -5
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +15 -12
- package/dist/resources/extensions/gsd/db-writer.js +9 -9
- package/dist/resources/extensions/gsd/doctor-checks.js +1 -1
- package/dist/resources/extensions/gsd/doctor.js +2 -2
- package/dist/resources/extensions/gsd/gsd-db.js +5 -1
- package/dist/resources/extensions/gsd/preferences-types.js +2 -2
- package/dist/resources/extensions/gsd/preferences.js +8 -4
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +20 -7
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +4 -0
- package/dist/resources/extensions/gsd/workflow-logger.js +138 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +4 -3
- package/dist/resources/extensions/gsd/worktree-resolver.js +37 -0
- package/dist/resources/extensions/voice/index.js +11 -16
- package/dist/resources/extensions/voice/linux-ready.js +67 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
- 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 +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.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 +17 -17
- 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/package.json +2 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +2 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts +4 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +10 -5
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js +185 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +239 -10
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +2 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +20 -2
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-commands.test.js +206 -195
- package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -1
- package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +2 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -1
- package/packages/pi-coding-agent/src/core/lifecycle-hooks.test.ts +227 -0
- package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +11 -5
- package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +297 -11
- package/packages/pi-coding-agent/src/core/model-registry.ts +30 -3
- package/packages/pi-coding-agent/src/core/package-commands.test.ts +227 -205
- package/src/resources/extensions/gsd/auto/phases.ts +16 -12
- package/src/resources/extensions/gsd/auto/run-unit.ts +6 -3
- package/src/resources/extensions/gsd/auto-worktree.ts +8 -5
- package/src/resources/extensions/gsd/auto.ts +3 -3
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +15 -12
- package/src/resources/extensions/gsd/db-writer.ts +9 -17
- package/src/resources/extensions/gsd/doctor-checks.ts +1 -1
- package/src/resources/extensions/gsd/doctor.ts +2 -2
- package/src/resources/extensions/gsd/gsd-db.ts +5 -1
- package/src/resources/extensions/gsd/journal.ts +6 -1
- package/src/resources/extensions/gsd/preferences-types.ts +2 -2
- package/src/resources/extensions/gsd/preferences.ts +7 -3
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +20 -7
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +96 -0
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +42 -3
- package/src/resources/extensions/gsd/tests/preferences.test.ts +7 -9
- package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +275 -0
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +220 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +6 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +193 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +4 -9
- package/src/resources/extensions/gsd/worktree-resolver.ts +37 -0
- package/src/resources/extensions/voice/index.ts +11 -21
- package/src/resources/extensions/voice/linux-ready.ts +87 -0
- package/src/resources/extensions/voice/tests/linux-ready.test.ts +124 -0
- /package/dist/web/standalone/.next/static/{rzO54ZboyINyEt7cVM_uS → zWYDSwB-terOjfhmWzqk1}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{rzO54ZboyINyEt7cVM_uS → zWYDSwB-terOjfhmWzqk1}/_ssgManifest.js +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { Writable } from "node:stream";
|
|
@@ -25,216 +25,238 @@ function writePackage(root: string, files: Record<string, string>): void {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
function createTestDirs(prefix: string, t: { after: (fn: () => void) => void }) {
|
|
29
|
+
const root = mkdtempSync(join(tmpdir(), `pi-lifecycle-${prefix}-`));
|
|
30
|
+
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
31
|
+
const cwd = join(root, "cwd");
|
|
32
|
+
const agentDir = join(root, "agent");
|
|
33
|
+
const extensionDir = join(root, `ext-${prefix}`);
|
|
34
|
+
mkdirSync(cwd, { recursive: true });
|
|
35
|
+
mkdirSync(agentDir, { recursive: true });
|
|
36
|
+
mkdirSync(extensionDir, { recursive: true });
|
|
37
|
+
return { root, cwd, agentDir, extensionDir };
|
|
38
|
+
}
|
|
39
|
+
|
|
28
40
|
describe("runPackageCommand lifecycle hooks", () => {
|
|
29
|
-
it("executes registered beforeInstall and afterInstall handlers for local packages", async () => {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}),
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
assert.equal(result.handled, true);
|
|
71
|
-
assert.equal(result.exitCode, 0);
|
|
72
|
-
assert.equal(readFileSync(join(extensionDir, "before-install-ran.txt"), "utf-8"), "ok");
|
|
73
|
-
assert.equal(readFileSync(join(extensionDir, "after-install-ran.txt"), "utf-8"), "ok");
|
|
74
|
-
assert.ok(stdout.getOutput().includes(`Installed ${extensionDir}`));
|
|
75
|
-
} finally {
|
|
76
|
-
rmSync(root, { recursive: true, force: true });
|
|
77
|
-
}
|
|
41
|
+
it("executes registered beforeInstall and afterInstall handlers for local packages", async (t) => {
|
|
42
|
+
const { cwd, agentDir, extensionDir } = createTestDirs("install", t);
|
|
43
|
+
|
|
44
|
+
writePackage(extensionDir, {
|
|
45
|
+
"package.json": JSON.stringify({
|
|
46
|
+
name: "ext-registered",
|
|
47
|
+
type: "module",
|
|
48
|
+
pi: { extensions: ["./index.js"] },
|
|
49
|
+
}),
|
|
50
|
+
"index.js": [
|
|
51
|
+
'import { writeFileSync } from "node:fs";',
|
|
52
|
+
'import { join } from "node:path";',
|
|
53
|
+
"export default function (pi) {",
|
|
54
|
+
" pi.registerBeforeInstall((ctx) => {",
|
|
55
|
+
' writeFileSync(join(ctx.installedPath, "before-install-ran.txt"), "ok", "utf-8");',
|
|
56
|
+
" });",
|
|
57
|
+
" pi.registerAfterInstall((ctx) => {",
|
|
58
|
+
' writeFileSync(join(ctx.installedPath, "after-install-ran.txt"), "ok", "utf-8");',
|
|
59
|
+
" });",
|
|
60
|
+
"}",
|
|
61
|
+
].join("\n"),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const stdout = createCaptureStream();
|
|
65
|
+
const stderr = createCaptureStream();
|
|
66
|
+
const result = await runPackageCommand({
|
|
67
|
+
appName: "pi",
|
|
68
|
+
args: ["install", extensionDir],
|
|
69
|
+
cwd,
|
|
70
|
+
agentDir,
|
|
71
|
+
stdout: stdout.stream,
|
|
72
|
+
stderr: stderr.stream,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
assert.equal(result.handled, true);
|
|
76
|
+
assert.equal(result.exitCode, 0);
|
|
77
|
+
assert.equal(readFileSync(join(extensionDir, "before-install-ran.txt"), "utf-8"), "ok");
|
|
78
|
+
assert.equal(readFileSync(join(extensionDir, "after-install-ran.txt"), "utf-8"), "ok");
|
|
79
|
+
assert.ok(stdout.getOutput().includes(`Installed ${extensionDir}`));
|
|
78
80
|
});
|
|
79
81
|
|
|
80
|
-
it("runs legacy named lifecycle hooks when no registered hooks exist", async () => {
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
"
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
"
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
stderr: stderr.stream,
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
assert.equal(removeResult.handled, true);
|
|
141
|
-
assert.equal(removeResult.exitCode, 0);
|
|
142
|
-
assert.equal(readFileSync(join(extensionDir, "legacy-before-remove.txt"), "utf-8"), "ok");
|
|
143
|
-
assert.equal(readFileSync(join(extensionDir, "legacy-after-remove.txt"), "utf-8"), "ok");
|
|
144
|
-
} finally {
|
|
145
|
-
rmSync(root, { recursive: true, force: true });
|
|
146
|
-
}
|
|
82
|
+
it("runs legacy named lifecycle hooks when no registered hooks exist", async (t) => {
|
|
83
|
+
const { cwd, agentDir, extensionDir } = createTestDirs("legacy", t);
|
|
84
|
+
|
|
85
|
+
writePackage(extensionDir, {
|
|
86
|
+
"package.json": JSON.stringify({
|
|
87
|
+
name: "ext-legacy",
|
|
88
|
+
type: "module",
|
|
89
|
+
pi: { extensions: ["./index.js"] },
|
|
90
|
+
}),
|
|
91
|
+
"index.js": [
|
|
92
|
+
'import { writeFileSync } from "node:fs";',
|
|
93
|
+
'import { join } from "node:path";',
|
|
94
|
+
"export default function () {}",
|
|
95
|
+
"export async function beforeInstall(ctx) {",
|
|
96
|
+
' writeFileSync(join(ctx.installedPath, "legacy-before-install.txt"), "ok", "utf-8");',
|
|
97
|
+
"}",
|
|
98
|
+
"export async function afterInstall(ctx) {",
|
|
99
|
+
' writeFileSync(join(ctx.installedPath, "legacy-after-install.txt"), "ok", "utf-8");',
|
|
100
|
+
"}",
|
|
101
|
+
"export async function beforeRemove(ctx) {",
|
|
102
|
+
' writeFileSync(join(ctx.installedPath, "legacy-before-remove.txt"), "ok", "utf-8");',
|
|
103
|
+
"}",
|
|
104
|
+
"export async function afterRemove(ctx) {",
|
|
105
|
+
' writeFileSync(join(ctx.installedPath, "legacy-after-remove.txt"), "ok", "utf-8");',
|
|
106
|
+
"}",
|
|
107
|
+
].join("\n"),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const stdout = createCaptureStream();
|
|
111
|
+
const stderr = createCaptureStream();
|
|
112
|
+
const installResult = await runPackageCommand({
|
|
113
|
+
appName: "pi",
|
|
114
|
+
args: ["install", extensionDir],
|
|
115
|
+
cwd,
|
|
116
|
+
agentDir,
|
|
117
|
+
stdout: stdout.stream,
|
|
118
|
+
stderr: stderr.stream,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
assert.equal(installResult.handled, true);
|
|
122
|
+
assert.equal(installResult.exitCode, 0);
|
|
123
|
+
assert.equal(readFileSync(join(extensionDir, "legacy-before-install.txt"), "utf-8"), "ok");
|
|
124
|
+
assert.equal(readFileSync(join(extensionDir, "legacy-after-install.txt"), "utf-8"), "ok");
|
|
125
|
+
|
|
126
|
+
const removeResult = await runPackageCommand({
|
|
127
|
+
appName: "pi",
|
|
128
|
+
args: ["remove", extensionDir],
|
|
129
|
+
cwd,
|
|
130
|
+
agentDir,
|
|
131
|
+
stdout: stdout.stream,
|
|
132
|
+
stderr: stderr.stream,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
assert.equal(removeResult.handled, true);
|
|
136
|
+
assert.equal(removeResult.exitCode, 0);
|
|
137
|
+
assert.equal(readFileSync(join(extensionDir, "legacy-before-remove.txt"), "utf-8"), "ok");
|
|
138
|
+
assert.equal(readFileSync(join(extensionDir, "legacy-after-remove.txt"), "utf-8"), "ok");
|
|
147
139
|
});
|
|
148
140
|
|
|
149
|
-
it("skips lifecycle phases with no hooks declared", async () => {
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
141
|
+
it("skips lifecycle phases with no hooks declared", async (t) => {
|
|
142
|
+
const { cwd, agentDir, extensionDir } = createTestDirs("skip", t);
|
|
143
|
+
|
|
144
|
+
writePackage(extensionDir, {
|
|
145
|
+
"package.json": JSON.stringify({
|
|
146
|
+
name: "ext-empty",
|
|
147
|
+
type: "module",
|
|
148
|
+
pi: { extensions: ["./index.js"] },
|
|
149
|
+
}),
|
|
150
|
+
"index.js": "export default function () {}",
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const stdout = createCaptureStream();
|
|
154
|
+
const stderr = createCaptureStream();
|
|
155
|
+
const installResult = await runPackageCommand({
|
|
156
|
+
appName: "pi",
|
|
157
|
+
args: ["install", extensionDir],
|
|
158
|
+
cwd,
|
|
159
|
+
agentDir,
|
|
160
|
+
stdout: stdout.stream,
|
|
161
|
+
stderr: stderr.stream,
|
|
162
|
+
});
|
|
163
|
+
assert.equal(installResult.handled, true);
|
|
164
|
+
assert.equal(installResult.exitCode, 0);
|
|
165
|
+
|
|
166
|
+
const removeResult = await runPackageCommand({
|
|
167
|
+
appName: "pi",
|
|
168
|
+
args: ["remove", extensionDir],
|
|
169
|
+
cwd,
|
|
170
|
+
agentDir,
|
|
171
|
+
stdout: stdout.stream,
|
|
172
|
+
stderr: stderr.stream,
|
|
173
|
+
});
|
|
174
|
+
assert.equal(removeResult.handled, true);
|
|
175
|
+
assert.equal(removeResult.exitCode, 0);
|
|
176
|
+
assert.equal(stderr.getOutput().includes("Hook failed"), false);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("fails install when manifest runtime dependency is missing", async (t) => {
|
|
180
|
+
const { cwd, agentDir, extensionDir } = createTestDirs("deps", t);
|
|
181
|
+
|
|
182
|
+
writePackage(extensionDir, {
|
|
183
|
+
"package.json": JSON.stringify({
|
|
184
|
+
name: "ext-runtime-deps",
|
|
185
|
+
type: "module",
|
|
186
|
+
pi: { extensions: ["./index.js"] },
|
|
187
|
+
}),
|
|
188
|
+
"index.js": "export default function () {}",
|
|
189
|
+
"extension-manifest.json": JSON.stringify({
|
|
190
|
+
id: "ext-runtime-deps",
|
|
191
|
+
name: "Runtime Dep Test",
|
|
192
|
+
version: "1.0.0",
|
|
193
|
+
dependencies: { runtime: ["__definitely_missing_command_for_test__"] },
|
|
194
|
+
}),
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const stdout = createCaptureStream();
|
|
198
|
+
const stderr = createCaptureStream();
|
|
199
|
+
const result = await runPackageCommand({
|
|
200
|
+
appName: "pi",
|
|
201
|
+
args: ["install", extensionDir],
|
|
202
|
+
cwd,
|
|
203
|
+
agentDir,
|
|
204
|
+
stdout: stdout.stream,
|
|
205
|
+
stderr: stderr.stream,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
assert.equal(result.handled, true);
|
|
209
|
+
assert.equal(result.exitCode, 1);
|
|
210
|
+
assert.ok(stderr.getOutput().includes("Missing runtime dependencies"));
|
|
195
211
|
});
|
|
196
212
|
|
|
197
|
-
it("
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
"
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
"
|
|
214
|
-
"
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
213
|
+
it("afterRemove hook receives installedPath even when directory is deleted", async (t) => {
|
|
214
|
+
const { cwd, agentDir, extensionDir } = createTestDirs("after-remove", t);
|
|
215
|
+
|
|
216
|
+
writePackage(extensionDir, {
|
|
217
|
+
"package.json": JSON.stringify({
|
|
218
|
+
name: "ext-after-remove",
|
|
219
|
+
type: "module",
|
|
220
|
+
pi: { extensions: ["./index.js"] },
|
|
221
|
+
}),
|
|
222
|
+
"index.js": [
|
|
223
|
+
'import { writeFileSync, existsSync } from "node:fs";',
|
|
224
|
+
'import { join } from "node:path";',
|
|
225
|
+
"export default function () {}",
|
|
226
|
+
"export async function afterRemove(ctx) {",
|
|
227
|
+
' const marker = join(ctx.cwd, "after-remove-marker.json");',
|
|
228
|
+
" writeFileSync(marker, JSON.stringify({",
|
|
229
|
+
" receivedPath: ctx.installedPath,",
|
|
230
|
+
" pathExisted: existsSync(ctx.installedPath),",
|
|
231
|
+
' }), "utf-8");',
|
|
232
|
+
"}",
|
|
233
|
+
].join("\n"),
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
const stdout = createCaptureStream();
|
|
237
|
+
const stderr = createCaptureStream();
|
|
238
|
+
|
|
239
|
+
await runPackageCommand({
|
|
240
|
+
appName: "pi",
|
|
241
|
+
args: ["install", extensionDir],
|
|
242
|
+
cwd,
|
|
243
|
+
agentDir,
|
|
244
|
+
stdout: stdout.stream,
|
|
245
|
+
stderr: stderr.stream,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
await runPackageCommand({
|
|
249
|
+
appName: "pi",
|
|
250
|
+
args: ["remove", extensionDir],
|
|
251
|
+
cwd,
|
|
252
|
+
agentDir,
|
|
253
|
+
stdout: stdout.stream,
|
|
254
|
+
stderr: stderr.stream,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
const markerPath = join(cwd, "after-remove-marker.json");
|
|
258
|
+
assert.ok(existsSync(markerPath), "afterRemove hook must have executed and written marker");
|
|
259
|
+
const marker = JSON.parse(readFileSync(markerPath, "utf-8"));
|
|
260
|
+
assert.equal(typeof marker.receivedPath, "string", "hook must receive installedPath as string");
|
|
239
261
|
});
|
|
240
262
|
});
|
|
@@ -30,6 +30,7 @@ import { PROJECT_FILES } from "../detection.js";
|
|
|
30
30
|
import { MergeConflictError } from "../git-service.js";
|
|
31
31
|
import { join } from "node:path";
|
|
32
32
|
import { existsSync, cpSync } from "node:fs";
|
|
33
|
+
import { logWarning, logError } from "../workflow-logger.js";
|
|
33
34
|
|
|
34
35
|
// ─── generateMilestoneReport ──────────────────────────────────────────────────
|
|
35
36
|
|
|
@@ -164,8 +165,8 @@ export async function runPreDispatch(
|
|
|
164
165
|
debugLog("autoLoop", { phase: "exit", reason: "health-gate-failed" });
|
|
165
166
|
return { action: "break", reason: "health-gate-failed" };
|
|
166
167
|
}
|
|
167
|
-
} catch {
|
|
168
|
-
|
|
168
|
+
} catch (e) {
|
|
169
|
+
logWarning("engine", "Pre-dispatch health gate threw unexpectedly", { error: String(e) });
|
|
169
170
|
}
|
|
170
171
|
|
|
171
172
|
// Sync project root artifacts into worktree
|
|
@@ -247,7 +248,8 @@ export async function runPreDispatch(
|
|
|
247
248
|
await deps.stopAuto(ctx, pi, `Merge conflict on milestone ${s.currentMilestoneId}`);
|
|
248
249
|
return { action: "break", reason: "merge-conflict" };
|
|
249
250
|
}
|
|
250
|
-
// Non-conflict errors — log and continue
|
|
251
|
+
// Non-conflict merge errors — log and continue
|
|
252
|
+
logWarning("engine", "Milestone merge failed with non-conflict error", { milestone: s.currentMilestoneId!, error: String(mergeErr) });
|
|
251
253
|
}
|
|
252
254
|
|
|
253
255
|
// PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
|
|
@@ -290,7 +292,9 @@ export async function runPreDispatch(
|
|
|
290
292
|
cpSync(completedKeysPath, archivePath);
|
|
291
293
|
}
|
|
292
294
|
atomicWriteSync(completedKeysPath, JSON.stringify([], null, 2));
|
|
293
|
-
} catch {
|
|
295
|
+
} catch (e) {
|
|
296
|
+
logWarning("engine", "Failed to archive completed-units on milestone transition", { error: String(e) });
|
|
297
|
+
}
|
|
294
298
|
|
|
295
299
|
// Rebuild STATE.md immediately so it reflects the new active milestone.
|
|
296
300
|
// This bypasses the 30-second throttle in the normal rebuild path —
|
|
@@ -298,8 +302,8 @@ export async function runPreDispatch(
|
|
|
298
302
|
// immediate write.
|
|
299
303
|
try {
|
|
300
304
|
await deps.rebuildState(s.basePath);
|
|
301
|
-
} catch {
|
|
302
|
-
|
|
305
|
+
} catch (e) {
|
|
306
|
+
logWarning("engine", "STATE.md rebuild failed after milestone transition", { error: String(e) });
|
|
303
307
|
}
|
|
304
308
|
}
|
|
305
309
|
|
|
@@ -919,8 +923,8 @@ export async function runUnitPhase(
|
|
|
919
923
|
(decisionsContent?.length ?? 0) +
|
|
920
924
|
(requirementsContent?.length ?? 0) +
|
|
921
925
|
(projectContent?.length ?? 0);
|
|
922
|
-
} catch {
|
|
923
|
-
|
|
926
|
+
} catch (e) {
|
|
927
|
+
logWarning("engine", "Baseline char count measurement failed", { error: String(e) });
|
|
924
928
|
}
|
|
925
929
|
}
|
|
926
930
|
|
|
@@ -930,9 +934,7 @@ export async function runUnitPhase(
|
|
|
930
934
|
} catch (reorderErr) {
|
|
931
935
|
const msg =
|
|
932
936
|
reorderErr instanceof Error ? reorderErr.message : String(reorderErr);
|
|
933
|
-
|
|
934
|
-
`[gsd] prompt reorder failed (non-fatal): ${msg}\n`,
|
|
935
|
-
);
|
|
937
|
+
logWarning("engine", "Prompt reorder failed", { error: msg });
|
|
936
938
|
}
|
|
937
939
|
|
|
938
940
|
// Select and apply model (with tier escalation on retry — normal units only)
|
|
@@ -1135,7 +1137,9 @@ export async function runUnitPhase(
|
|
|
1135
1137
|
const completedKeysPath = join(gsdRoot(s.basePath), "completed-units.json");
|
|
1136
1138
|
const keys = s.completedUnits.map((u) => `${u.type}/${u.id}`);
|
|
1137
1139
|
atomicWriteSync(completedKeysPath, JSON.stringify(keys, null, 2));
|
|
1138
|
-
} catch {
|
|
1140
|
+
} catch (e) {
|
|
1141
|
+
logWarning("engine", "Failed to flush completed-units to disk", { error: String(e) });
|
|
1142
|
+
}
|
|
1139
1143
|
|
|
1140
1144
|
deps.clearUnitRuntimeRecord(s.basePath, unitType, unitId);
|
|
1141
1145
|
s.unitDispatchCount.delete(`${unitType}/${unitId}`);
|
|
@@ -11,6 +11,7 @@ import { NEW_SESSION_TIMEOUT_MS } from "./session.js";
|
|
|
11
11
|
import type { UnitResult } from "./types.js";
|
|
12
12
|
import { _setCurrentResolve, _setSessionSwitchInFlight } from "./resolve.js";
|
|
13
13
|
import { debugLog } from "../debug-logger.js";
|
|
14
|
+
import { logWarning, logError } from "../workflow-logger.js";
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Execute a single unit: create a new session, send the prompt, and await
|
|
@@ -85,7 +86,9 @@ export async function runUnit(
|
|
|
85
86
|
if (process.cwd() !== s.basePath) {
|
|
86
87
|
process.chdir(s.basePath);
|
|
87
88
|
}
|
|
88
|
-
} catch {
|
|
89
|
+
} catch (e) {
|
|
90
|
+
logWarning("engine", "Failed to chdir to basePath before dispatch", { basePath: s.basePath, error: String(e) });
|
|
91
|
+
}
|
|
89
92
|
|
|
90
93
|
// ── Send the prompt ──
|
|
91
94
|
debugLog("runUnit", { phase: "send-message", unitType, unitId });
|
|
@@ -115,8 +118,8 @@ export async function runUnit(
|
|
|
115
118
|
if (typeof cmdCtxAny?.clearQueue === "function") {
|
|
116
119
|
(cmdCtxAny.clearQueue as () => unknown)();
|
|
117
120
|
}
|
|
118
|
-
} catch {
|
|
119
|
-
|
|
121
|
+
} catch (e) {
|
|
122
|
+
logWarning("engine", "clearQueue failed after unit completion", { error: String(e) });
|
|
120
123
|
}
|
|
121
124
|
|
|
122
125
|
return result;
|
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
} from "./worktree.js";
|
|
43
43
|
import { MergeConflictError, readIntegrationBranch, RUNTIME_EXCLUSION_PATHS } from "./git-service.js";
|
|
44
44
|
import { debugLog } from "./debug-logger.js";
|
|
45
|
+
import { logWarning } from "./workflow-logger.js";
|
|
45
46
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
46
47
|
import {
|
|
47
48
|
nativeGetCurrentBranch,
|
|
@@ -700,7 +701,7 @@ export function createAutoWorktree(
|
|
|
700
701
|
const hookError = runWorktreePostCreateHook(basePath, info.path);
|
|
701
702
|
if (hookError) {
|
|
702
703
|
// Non-fatal — log but don't prevent worktree usage
|
|
703
|
-
|
|
704
|
+
logWarning("reconcile", hookError, { worktree: info.name });
|
|
704
705
|
}
|
|
705
706
|
|
|
706
707
|
const previousCwd = process.cwd();
|
|
@@ -793,10 +794,12 @@ export function teardownAutoWorktree(
|
|
|
793
794
|
// backslashes (#1436), leaving ~1 GB+ orphaned directories.
|
|
794
795
|
const wtDir = worktreePath(originalBasePath, milestoneId);
|
|
795
796
|
if (existsSync(wtDir)) {
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
`
|
|
797
|
+
logWarning(
|
|
798
|
+
"reconcile",
|
|
799
|
+
`Worktree directory still exists after teardown: ${wtDir}. ` +
|
|
800
|
+
`This is likely an orphaned directory consuming disk space. ` +
|
|
801
|
+
`Remove it manually with: rm -rf "${wtDir.replaceAll("\\", "/")}"`,
|
|
802
|
+
{ worktree: milestoneId },
|
|
800
803
|
);
|
|
801
804
|
// Attempt a direct filesystem removal as a fallback
|
|
802
805
|
try {
|
|
@@ -250,9 +250,9 @@ const STATE_REBUILD_MIN_INTERVAL_MS = 30_000;
|
|
|
250
250
|
|
|
251
251
|
export function shouldUseWorktreeIsolation(): boolean {
|
|
252
252
|
const prefs = loadEffectiveGSDPreferences()?.preferences?.git;
|
|
253
|
-
if (prefs?.isolation === "
|
|
254
|
-
|
|
255
|
-
return
|
|
253
|
+
if (prefs?.isolation === "worktree") return true;
|
|
254
|
+
// Default is false — worktree isolation requires explicit opt-in
|
|
255
|
+
return false;
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
/** Crash recovery prompt — set by startAuto, consumed by the main loop */
|