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.
Files changed (109) hide show
  1. package/dist/help-text.js +1 -1
  2. package/dist/loader.js +34 -0
  3. package/dist/resources/extensions/gsd/auto/phases.js +16 -10
  4. package/dist/resources/extensions/gsd/auto/run-unit.js +6 -3
  5. package/dist/resources/extensions/gsd/auto-worktree.js +5 -4
  6. package/dist/resources/extensions/gsd/auto.js +4 -5
  7. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +15 -12
  8. package/dist/resources/extensions/gsd/db-writer.js +9 -9
  9. package/dist/resources/extensions/gsd/doctor-checks.js +1 -1
  10. package/dist/resources/extensions/gsd/doctor.js +2 -2
  11. package/dist/resources/extensions/gsd/gsd-db.js +5 -1
  12. package/dist/resources/extensions/gsd/preferences-types.js +2 -2
  13. package/dist/resources/extensions/gsd/preferences.js +8 -4
  14. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +20 -7
  15. package/dist/resources/extensions/gsd/tools/complete-milestone.js +4 -0
  16. package/dist/resources/extensions/gsd/workflow-logger.js +138 -0
  17. package/dist/resources/extensions/gsd/worktree-manager.js +4 -3
  18. package/dist/resources/extensions/gsd/worktree-resolver.js +37 -0
  19. package/dist/resources/extensions/voice/index.js +11 -16
  20. package/dist/resources/extensions/voice/linux-ready.js +67 -0
  21. package/dist/web/standalone/.next/BUILD_ID +1 -1
  22. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  23. package/dist/web/standalone/.next/build-manifest.json +2 -2
  24. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  25. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  26. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  34. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  36. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/index.html +1 -1
  42. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  49. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  50. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  51. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  52. package/package.json +2 -1
  53. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -1
  54. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +2 -0
  55. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
  56. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -1
  57. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  58. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  59. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts +4 -0
  60. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -1
  61. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +10 -5
  62. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -1
  63. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts +2 -0
  64. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts.map +1 -0
  65. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js +185 -0
  66. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js.map +1 -0
  67. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +239 -10
  68. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  69. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +2 -1
  70. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  71. package/packages/pi-coding-agent/dist/core/model-registry.js +20 -2
  72. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  73. package/packages/pi-coding-agent/dist/core/package-commands.test.js +206 -195
  74. package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -1
  75. package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +2 -0
  76. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -1
  77. package/packages/pi-coding-agent/src/core/lifecycle-hooks.test.ts +227 -0
  78. package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +11 -5
  79. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +297 -11
  80. package/packages/pi-coding-agent/src/core/model-registry.ts +30 -3
  81. package/packages/pi-coding-agent/src/core/package-commands.test.ts +227 -205
  82. package/src/resources/extensions/gsd/auto/phases.ts +16 -12
  83. package/src/resources/extensions/gsd/auto/run-unit.ts +6 -3
  84. package/src/resources/extensions/gsd/auto-worktree.ts +8 -5
  85. package/src/resources/extensions/gsd/auto.ts +3 -3
  86. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +15 -12
  87. package/src/resources/extensions/gsd/db-writer.ts +9 -17
  88. package/src/resources/extensions/gsd/doctor-checks.ts +1 -1
  89. package/src/resources/extensions/gsd/doctor.ts +2 -2
  90. package/src/resources/extensions/gsd/gsd-db.ts +5 -1
  91. package/src/resources/extensions/gsd/journal.ts +6 -1
  92. package/src/resources/extensions/gsd/preferences-types.ts +2 -2
  93. package/src/resources/extensions/gsd/preferences.ts +7 -3
  94. package/src/resources/extensions/gsd/prompts/complete-milestone.md +20 -7
  95. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +96 -0
  96. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +1 -1
  97. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +42 -3
  98. package/src/resources/extensions/gsd/tests/preferences.test.ts +7 -9
  99. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +275 -0
  100. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +220 -0
  101. package/src/resources/extensions/gsd/tools/complete-milestone.ts +6 -0
  102. package/src/resources/extensions/gsd/workflow-logger.ts +193 -0
  103. package/src/resources/extensions/gsd/worktree-manager.ts +4 -9
  104. package/src/resources/extensions/gsd/worktree-resolver.ts +37 -0
  105. package/src/resources/extensions/voice/index.ts +11 -21
  106. package/src/resources/extensions/voice/linux-ready.ts +87 -0
  107. package/src/resources/extensions/voice/tests/linux-ready.test.ts +124 -0
  108. /package/dist/web/standalone/.next/static/{rzO54ZboyINyEt7cVM_uS → zWYDSwB-terOjfhmWzqk1}/_buildManifest.js +0 -0
  109. /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";
@@ -22,204 +22,215 @@ function writePackage(root, files) {
22
22
  writeFileSync(abs, content, "utf-8");
23
23
  }
24
24
  }
25
+ function createTestDirs(prefix, t) {
26
+ const root = mkdtempSync(join(tmpdir(), `pi-lifecycle-${prefix}-`));
27
+ t.after(() => rmSync(root, { recursive: true, force: true }));
28
+ const cwd = join(root, "cwd");
29
+ const agentDir = join(root, "agent");
30
+ const extensionDir = join(root, `ext-${prefix}`);
31
+ mkdirSync(cwd, { recursive: true });
32
+ mkdirSync(agentDir, { recursive: true });
33
+ mkdirSync(extensionDir, { recursive: true });
34
+ return { root, cwd, agentDir, extensionDir };
35
+ }
25
36
  describe("runPackageCommand lifecycle hooks", () => {
26
- it("executes registered beforeInstall and afterInstall handlers for local packages", async () => {
27
- const root = mkdtempSync(join(tmpdir(), "pi-lifecycle-install-"));
28
- const cwd = join(root, "cwd");
29
- const agentDir = join(root, "agent");
30
- const extensionDir = join(root, "ext-registered");
31
- mkdirSync(cwd, { recursive: true });
32
- mkdirSync(agentDir, { recursive: true });
33
- mkdirSync(extensionDir, { recursive: true });
34
- try {
35
- writePackage(extensionDir, {
36
- "package.json": JSON.stringify({
37
- name: "ext-registered",
38
- type: "module",
39
- pi: { extensions: ["./index.js"] },
40
- }),
41
- "index.js": `
42
- import { writeFileSync } from "node:fs";
43
- import { join } from "node:path";
44
- export default function (pi) {
45
- pi.registerBeforeInstall((ctx) => {
46
- writeFileSync(join(ctx.installedPath, "before-install-ran.txt"), "ok", "utf-8");
47
- });
48
- pi.registerAfterInstall((ctx) => {
49
- writeFileSync(join(ctx.installedPath, "after-install-ran.txt"), "ok", "utf-8");
50
- });
51
- }
52
- `,
53
- });
54
- const stdout = createCaptureStream();
55
- const stderr = createCaptureStream();
56
- const result = await runPackageCommand({
57
- appName: "pi",
58
- args: ["install", extensionDir],
59
- cwd,
60
- agentDir,
61
- stdout: stdout.stream,
62
- stderr: stderr.stream,
63
- });
64
- assert.equal(result.handled, true);
65
- assert.equal(result.exitCode, 0);
66
- assert.equal(readFileSync(join(extensionDir, "before-install-ran.txt"), "utf-8"), "ok");
67
- assert.equal(readFileSync(join(extensionDir, "after-install-ran.txt"), "utf-8"), "ok");
68
- assert.ok(stdout.getOutput().includes(`Installed ${extensionDir}`));
69
- }
70
- finally {
71
- rmSync(root, { recursive: true, force: true });
72
- }
37
+ it("executes registered beforeInstall and afterInstall handlers for local packages", async (t) => {
38
+ const { cwd, agentDir, extensionDir } = createTestDirs("install", t);
39
+ writePackage(extensionDir, {
40
+ "package.json": JSON.stringify({
41
+ name: "ext-registered",
42
+ type: "module",
43
+ pi: { extensions: ["./index.js"] },
44
+ }),
45
+ "index.js": [
46
+ 'import { writeFileSync } from "node:fs";',
47
+ 'import { join } from "node:path";',
48
+ "export default function (pi) {",
49
+ " pi.registerBeforeInstall((ctx) => {",
50
+ ' writeFileSync(join(ctx.installedPath, "before-install-ran.txt"), "ok", "utf-8");',
51
+ " });",
52
+ " pi.registerAfterInstall((ctx) => {",
53
+ ' writeFileSync(join(ctx.installedPath, "after-install-ran.txt"), "ok", "utf-8");',
54
+ " });",
55
+ "}",
56
+ ].join("\n"),
57
+ });
58
+ const stdout = createCaptureStream();
59
+ const stderr = createCaptureStream();
60
+ const result = await runPackageCommand({
61
+ appName: "pi",
62
+ args: ["install", extensionDir],
63
+ cwd,
64
+ agentDir,
65
+ stdout: stdout.stream,
66
+ stderr: stderr.stream,
67
+ });
68
+ assert.equal(result.handled, true);
69
+ assert.equal(result.exitCode, 0);
70
+ assert.equal(readFileSync(join(extensionDir, "before-install-ran.txt"), "utf-8"), "ok");
71
+ assert.equal(readFileSync(join(extensionDir, "after-install-ran.txt"), "utf-8"), "ok");
72
+ assert.ok(stdout.getOutput().includes(`Installed ${extensionDir}`));
73
+ });
74
+ it("runs legacy named lifecycle hooks when no registered hooks exist", async (t) => {
75
+ const { cwd, agentDir, extensionDir } = createTestDirs("legacy", t);
76
+ writePackage(extensionDir, {
77
+ "package.json": JSON.stringify({
78
+ name: "ext-legacy",
79
+ type: "module",
80
+ pi: { extensions: ["./index.js"] },
81
+ }),
82
+ "index.js": [
83
+ 'import { writeFileSync } from "node:fs";',
84
+ 'import { join } from "node:path";',
85
+ "export default function () {}",
86
+ "export async function beforeInstall(ctx) {",
87
+ ' writeFileSync(join(ctx.installedPath, "legacy-before-install.txt"), "ok", "utf-8");',
88
+ "}",
89
+ "export async function afterInstall(ctx) {",
90
+ ' writeFileSync(join(ctx.installedPath, "legacy-after-install.txt"), "ok", "utf-8");',
91
+ "}",
92
+ "export async function beforeRemove(ctx) {",
93
+ ' writeFileSync(join(ctx.installedPath, "legacy-before-remove.txt"), "ok", "utf-8");',
94
+ "}",
95
+ "export async function afterRemove(ctx) {",
96
+ ' writeFileSync(join(ctx.installedPath, "legacy-after-remove.txt"), "ok", "utf-8");',
97
+ "}",
98
+ ].join("\n"),
99
+ });
100
+ const stdout = createCaptureStream();
101
+ const stderr = createCaptureStream();
102
+ const installResult = await runPackageCommand({
103
+ appName: "pi",
104
+ args: ["install", extensionDir],
105
+ cwd,
106
+ agentDir,
107
+ stdout: stdout.stream,
108
+ stderr: stderr.stream,
109
+ });
110
+ assert.equal(installResult.handled, true);
111
+ assert.equal(installResult.exitCode, 0);
112
+ assert.equal(readFileSync(join(extensionDir, "legacy-before-install.txt"), "utf-8"), "ok");
113
+ assert.equal(readFileSync(join(extensionDir, "legacy-after-install.txt"), "utf-8"), "ok");
114
+ const removeResult = await runPackageCommand({
115
+ appName: "pi",
116
+ args: ["remove", extensionDir],
117
+ cwd,
118
+ agentDir,
119
+ stdout: stdout.stream,
120
+ stderr: stderr.stream,
121
+ });
122
+ assert.equal(removeResult.handled, true);
123
+ assert.equal(removeResult.exitCode, 0);
124
+ assert.equal(readFileSync(join(extensionDir, "legacy-before-remove.txt"), "utf-8"), "ok");
125
+ assert.equal(readFileSync(join(extensionDir, "legacy-after-remove.txt"), "utf-8"), "ok");
73
126
  });
74
- it("runs legacy named lifecycle hooks when no registered hooks exist", async () => {
75
- const root = mkdtempSync(join(tmpdir(), "pi-lifecycle-legacy-"));
76
- const cwd = join(root, "cwd");
77
- const agentDir = join(root, "agent");
78
- const extensionDir = join(root, "ext-legacy");
79
- mkdirSync(cwd, { recursive: true });
80
- mkdirSync(agentDir, { recursive: true });
81
- mkdirSync(extensionDir, { recursive: true });
82
- try {
83
- writePackage(extensionDir, {
84
- "package.json": JSON.stringify({
85
- name: "ext-legacy",
86
- type: "module",
87
- pi: { extensions: ["./index.js"] },
88
- }),
89
- "index.js": `
90
- import { writeFileSync } from "node:fs";
91
- import { join } from "node:path";
92
- export default function () {}
93
- export async function beforeInstall(ctx) {
94
- writeFileSync(join(ctx.installedPath, "legacy-before-install.txt"), "ok", "utf-8");
95
- }
96
- export async function afterInstall(ctx) {
97
- writeFileSync(join(ctx.installedPath, "legacy-after-install.txt"), "ok", "utf-8");
98
- }
99
- export async function beforeRemove(ctx) {
100
- writeFileSync(join(ctx.installedPath, "legacy-before-remove.txt"), "ok", "utf-8");
101
- }
102
- export async function afterRemove(ctx) {
103
- writeFileSync(join(ctx.installedPath, "legacy-after-remove.txt"), "ok", "utf-8");
104
- }
105
- `,
106
- });
107
- const stdout = createCaptureStream();
108
- const stderr = createCaptureStream();
109
- const installResult = await runPackageCommand({
110
- appName: "pi",
111
- args: ["install", extensionDir],
112
- cwd,
113
- agentDir,
114
- stdout: stdout.stream,
115
- stderr: stderr.stream,
116
- });
117
- assert.equal(installResult.handled, true);
118
- assert.equal(installResult.exitCode, 0);
119
- assert.equal(readFileSync(join(extensionDir, "legacy-before-install.txt"), "utf-8"), "ok");
120
- assert.equal(readFileSync(join(extensionDir, "legacy-after-install.txt"), "utf-8"), "ok");
121
- const removeResult = await runPackageCommand({
122
- appName: "pi",
123
- args: ["remove", extensionDir],
124
- cwd,
125
- agentDir,
126
- stdout: stdout.stream,
127
- stderr: stderr.stream,
128
- });
129
- assert.equal(removeResult.handled, true);
130
- assert.equal(removeResult.exitCode, 0);
131
- assert.equal(readFileSync(join(extensionDir, "legacy-before-remove.txt"), "utf-8"), "ok");
132
- assert.equal(readFileSync(join(extensionDir, "legacy-after-remove.txt"), "utf-8"), "ok");
133
- }
134
- finally {
135
- rmSync(root, { recursive: true, force: true });
136
- }
127
+ it("skips lifecycle phases with no hooks declared", async (t) => {
128
+ const { cwd, agentDir, extensionDir } = createTestDirs("skip", t);
129
+ writePackage(extensionDir, {
130
+ "package.json": JSON.stringify({
131
+ name: "ext-empty",
132
+ type: "module",
133
+ pi: { extensions: ["./index.js"] },
134
+ }),
135
+ "index.js": "export default function () {}",
136
+ });
137
+ const stdout = createCaptureStream();
138
+ const stderr = createCaptureStream();
139
+ const installResult = await runPackageCommand({
140
+ appName: "pi",
141
+ args: ["install", extensionDir],
142
+ cwd,
143
+ agentDir,
144
+ stdout: stdout.stream,
145
+ stderr: stderr.stream,
146
+ });
147
+ assert.equal(installResult.handled, true);
148
+ assert.equal(installResult.exitCode, 0);
149
+ const removeResult = await runPackageCommand({
150
+ appName: "pi",
151
+ args: ["remove", extensionDir],
152
+ cwd,
153
+ agentDir,
154
+ stdout: stdout.stream,
155
+ stderr: stderr.stream,
156
+ });
157
+ assert.equal(removeResult.handled, true);
158
+ assert.equal(removeResult.exitCode, 0);
159
+ assert.equal(stderr.getOutput().includes("Hook failed"), false);
137
160
  });
138
- it("skips lifecycle phases with no hooks declared", async () => {
139
- const root = mkdtempSync(join(tmpdir(), "pi-lifecycle-skip-"));
140
- const cwd = join(root, "cwd");
141
- const agentDir = join(root, "agent");
142
- const extensionDir = join(root, "ext-empty");
143
- mkdirSync(cwd, { recursive: true });
144
- mkdirSync(agentDir, { recursive: true });
145
- mkdirSync(extensionDir, { recursive: true });
146
- try {
147
- writePackage(extensionDir, {
148
- "package.json": JSON.stringify({
149
- name: "ext-empty",
150
- type: "module",
151
- pi: { extensions: ["./index.js"] },
152
- }),
153
- "index.js": `export default function () {}`,
154
- });
155
- const stdout = createCaptureStream();
156
- const stderr = createCaptureStream();
157
- const installResult = await runPackageCommand({
158
- appName: "pi",
159
- args: ["install", extensionDir],
160
- cwd,
161
- agentDir,
162
- stdout: stdout.stream,
163
- stderr: stderr.stream,
164
- });
165
- assert.equal(installResult.handled, true);
166
- assert.equal(installResult.exitCode, 0);
167
- const removeResult = await runPackageCommand({
168
- appName: "pi",
169
- args: ["remove", extensionDir],
170
- cwd,
171
- agentDir,
172
- stdout: stdout.stream,
173
- stderr: stderr.stream,
174
- });
175
- assert.equal(removeResult.handled, true);
176
- assert.equal(removeResult.exitCode, 0);
177
- assert.equal(stderr.getOutput().includes("Hook failed"), false);
178
- }
179
- finally {
180
- rmSync(root, { recursive: true, force: true });
181
- }
161
+ it("fails install when manifest runtime dependency is missing", async (t) => {
162
+ const { cwd, agentDir, extensionDir } = createTestDirs("deps", t);
163
+ writePackage(extensionDir, {
164
+ "package.json": JSON.stringify({
165
+ name: "ext-runtime-deps",
166
+ type: "module",
167
+ pi: { extensions: ["./index.js"] },
168
+ }),
169
+ "index.js": "export default function () {}",
170
+ "extension-manifest.json": JSON.stringify({
171
+ id: "ext-runtime-deps",
172
+ name: "Runtime Dep Test",
173
+ version: "1.0.0",
174
+ dependencies: { runtime: ["__definitely_missing_command_for_test__"] },
175
+ }),
176
+ });
177
+ const stdout = createCaptureStream();
178
+ const stderr = createCaptureStream();
179
+ const result = await runPackageCommand({
180
+ appName: "pi",
181
+ args: ["install", extensionDir],
182
+ cwd,
183
+ agentDir,
184
+ stdout: stdout.stream,
185
+ stderr: stderr.stream,
186
+ });
187
+ assert.equal(result.handled, true);
188
+ assert.equal(result.exitCode, 1);
189
+ assert.ok(stderr.getOutput().includes("Missing runtime dependencies"));
182
190
  });
183
- it("fails install when manifest runtime dependency is missing", async () => {
184
- const root = mkdtempSync(join(tmpdir(), "pi-lifecycle-deps-"));
185
- const cwd = join(root, "cwd");
186
- const agentDir = join(root, "agent");
187
- const extensionDir = join(root, "ext-runtime-deps");
188
- mkdirSync(cwd, { recursive: true });
189
- mkdirSync(agentDir, { recursive: true });
190
- mkdirSync(extensionDir, { recursive: true });
191
- try {
192
- writePackage(extensionDir, {
193
- "package.json": JSON.stringify({
194
- name: "ext-runtime-deps",
195
- type: "module",
196
- pi: { extensions: ["./index.js"] },
197
- }),
198
- "index.js": `export default function () {}`,
199
- "extension-manifest.json": JSON.stringify({
200
- id: "ext-runtime-deps",
201
- name: "Runtime Dep Test",
202
- version: "1.0.0",
203
- dependencies: { runtime: ["__definitely_missing_command_for_test__"] },
204
- }),
205
- });
206
- const stdout = createCaptureStream();
207
- const stderr = createCaptureStream();
208
- const result = await runPackageCommand({
209
- appName: "pi",
210
- args: ["install", extensionDir],
211
- cwd,
212
- agentDir,
213
- stdout: stdout.stream,
214
- stderr: stderr.stream,
215
- });
216
- assert.equal(result.handled, true);
217
- assert.equal(result.exitCode, 1);
218
- assert.ok(stderr.getOutput().includes("Missing runtime dependencies"));
219
- }
220
- finally {
221
- rmSync(root, { recursive: true, force: true });
222
- }
191
+ it("afterRemove hook receives installedPath even when directory is deleted", async (t) => {
192
+ const { cwd, agentDir, extensionDir } = createTestDirs("after-remove", t);
193
+ writePackage(extensionDir, {
194
+ "package.json": JSON.stringify({
195
+ name: "ext-after-remove",
196
+ type: "module",
197
+ pi: { extensions: ["./index.js"] },
198
+ }),
199
+ "index.js": [
200
+ 'import { writeFileSync, existsSync } from "node:fs";',
201
+ 'import { join } from "node:path";',
202
+ "export default function () {}",
203
+ "export async function afterRemove(ctx) {",
204
+ ' const marker = join(ctx.cwd, "after-remove-marker.json");',
205
+ " writeFileSync(marker, JSON.stringify({",
206
+ " receivedPath: ctx.installedPath,",
207
+ " pathExisted: existsSync(ctx.installedPath),",
208
+ ' }), "utf-8");',
209
+ "}",
210
+ ].join("\n"),
211
+ });
212
+ const stdout = createCaptureStream();
213
+ const stderr = createCaptureStream();
214
+ await runPackageCommand({
215
+ appName: "pi",
216
+ args: ["install", extensionDir],
217
+ cwd,
218
+ agentDir,
219
+ stdout: stdout.stream,
220
+ stderr: stderr.stream,
221
+ });
222
+ await runPackageCommand({
223
+ appName: "pi",
224
+ args: ["remove", extensionDir],
225
+ cwd,
226
+ agentDir,
227
+ stdout: stdout.stream,
228
+ stderr: stderr.stream,
229
+ });
230
+ const markerPath = join(cwd, "after-remove-marker.json");
231
+ assert.ok(existsSync(markerPath), "afterRemove hook must have executed and written marker");
232
+ const marker = JSON.parse(readFileSync(markerPath, "utf-8"));
233
+ assert.equal(typeof marker.receivedPath, "string", "hook must receive installedPath as string");
223
234
  });
224
235
  });
225
236
  //# sourceMappingURL=package-commands.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"package-commands.test.js","sourceRoot":"","sources":["../../src/core/package-commands.test.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from \"node:assert/strict\";\nimport { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { Writable } from \"node:stream\";\nimport { describe, it } from \"node:test\";\nimport { runPackageCommand } from \"./package-commands.js\";\n\nfunction createCaptureStream() {\n\tlet output = \"\";\n\tconst stream = new Writable({\n\t\twrite(chunk, _encoding, callback) {\n\t\t\toutput += chunk.toString();\n\t\t\tcallback();\n\t\t},\n\t}) as unknown as NodeJS.WriteStream;\n\treturn { stream, getOutput: () => output };\n}\n\nfunction writePackage(root: string, files: Record<string, string>): void {\n\tfor (const [relPath, content] of Object.entries(files)) {\n\t\tconst abs = join(root, relPath);\n\t\tmkdirSync(join(abs, \"..\"), { recursive: true });\n\t\twriteFileSync(abs, content, \"utf-8\");\n\t}\n}\n\ndescribe(\"runPackageCommand lifecycle hooks\", () => {\n\tit(\"executes registered beforeInstall and afterInstall handlers for local packages\", async () => {\n\t\tconst root = mkdtempSync(join(tmpdir(), \"pi-lifecycle-install-\"));\n\t\tconst cwd = join(root, \"cwd\");\n\t\tconst agentDir = join(root, \"agent\");\n\t\tconst extensionDir = join(root, \"ext-registered\");\n\t\tmkdirSync(cwd, { recursive: true });\n\t\tmkdirSync(agentDir, { recursive: true });\n\t\tmkdirSync(extensionDir, { recursive: true });\n\n\t\ttry {\n\t\t\twritePackage(extensionDir, {\n\t\t\t\t\"package.json\": JSON.stringify({\n\t\t\t\t\tname: \"ext-registered\",\n\t\t\t\t\ttype: \"module\",\n\t\t\t\t\tpi: { extensions: [\"./index.js\"] },\n\t\t\t\t}),\n\t\t\t\t\"index.js\": `\n\t\t\t\t\timport { writeFileSync } from \"node:fs\";\n\t\t\t\t\timport { join } from \"node:path\";\n\t\t\t\t\texport default function (pi) {\n\t\t\t\t\t\tpi.registerBeforeInstall((ctx) => {\n\t\t\t\t\t\t\twriteFileSync(join(ctx.installedPath, \"before-install-ran.txt\"), \"ok\", \"utf-8\");\n\t\t\t\t\t\t});\n\t\t\t\t\t\tpi.registerAfterInstall((ctx) => {\n\t\t\t\t\t\t\twriteFileSync(join(ctx.installedPath, \"after-install-ran.txt\"), \"ok\", \"utf-8\");\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t`,\n\t\t\t});\n\n\t\t\tconst stdout = createCaptureStream();\n\t\t\tconst stderr = createCaptureStream();\n\t\t\tconst result = await runPackageCommand({\n\t\t\t\tappName: \"pi\",\n\t\t\t\targs: [\"install\", extensionDir],\n\t\t\t\tcwd,\n\t\t\t\tagentDir,\n\t\t\t\tstdout: stdout.stream,\n\t\t\t\tstderr: stderr.stream,\n\t\t\t});\n\n\t\t\tassert.equal(result.handled, true);\n\t\t\tassert.equal(result.exitCode, 0);\n\t\t\tassert.equal(readFileSync(join(extensionDir, \"before-install-ran.txt\"), \"utf-8\"), \"ok\");\n\t\t\tassert.equal(readFileSync(join(extensionDir, \"after-install-ran.txt\"), \"utf-8\"), \"ok\");\n\t\t\tassert.ok(stdout.getOutput().includes(`Installed ${extensionDir}`));\n\t\t} finally {\n\t\t\trmSync(root, { recursive: true, force: true });\n\t\t}\n\t});\n\n\tit(\"runs legacy named lifecycle hooks when no registered hooks exist\", async () => {\n\t\tconst root = mkdtempSync(join(tmpdir(), \"pi-lifecycle-legacy-\"));\n\t\tconst cwd = join(root, \"cwd\");\n\t\tconst agentDir = join(root, \"agent\");\n\t\tconst extensionDir = join(root, \"ext-legacy\");\n\t\tmkdirSync(cwd, { recursive: true });\n\t\tmkdirSync(agentDir, { recursive: true });\n\t\tmkdirSync(extensionDir, { recursive: true });\n\n\t\ttry {\n\t\t\twritePackage(extensionDir, {\n\t\t\t\t\"package.json\": JSON.stringify({\n\t\t\t\t\tname: \"ext-legacy\",\n\t\t\t\t\ttype: \"module\",\n\t\t\t\t\tpi: { extensions: [\"./index.js\"] },\n\t\t\t\t}),\n\t\t\t\t\"index.js\": `\n\t\t\t\t\timport { writeFileSync } from \"node:fs\";\n\t\t\t\t\timport { join } from \"node:path\";\n\t\t\t\t\texport default function () {}\n\t\t\t\t\texport async function beforeInstall(ctx) {\n\t\t\t\t\t\twriteFileSync(join(ctx.installedPath, \"legacy-before-install.txt\"), \"ok\", \"utf-8\");\n\t\t\t\t\t}\n\t\t\t\t\texport async function afterInstall(ctx) {\n\t\t\t\t\t\twriteFileSync(join(ctx.installedPath, \"legacy-after-install.txt\"), \"ok\", \"utf-8\");\n\t\t\t\t\t}\n\t\t\t\t\texport async function beforeRemove(ctx) {\n\t\t\t\t\t\twriteFileSync(join(ctx.installedPath, \"legacy-before-remove.txt\"), \"ok\", \"utf-8\");\n\t\t\t\t\t}\n\t\t\t\t\texport async function afterRemove(ctx) {\n\t\t\t\t\t\twriteFileSync(join(ctx.installedPath, \"legacy-after-remove.txt\"), \"ok\", \"utf-8\");\n\t\t\t\t\t}\n\t\t\t\t`,\n\t\t\t});\n\n\t\t\tconst stdout = createCaptureStream();\n\t\t\tconst stderr = createCaptureStream();\n\t\t\tconst installResult = await runPackageCommand({\n\t\t\t\tappName: \"pi\",\n\t\t\t\targs: [\"install\", extensionDir],\n\t\t\t\tcwd,\n\t\t\t\tagentDir,\n\t\t\t\tstdout: stdout.stream,\n\t\t\t\tstderr: stderr.stream,\n\t\t\t});\n\n\t\t\tassert.equal(installResult.handled, true);\n\t\t\tassert.equal(installResult.exitCode, 0);\n\t\t\tassert.equal(readFileSync(join(extensionDir, \"legacy-before-install.txt\"), \"utf-8\"), \"ok\");\n\t\t\tassert.equal(readFileSync(join(extensionDir, \"legacy-after-install.txt\"), \"utf-8\"), \"ok\");\n\n\t\t\tconst removeResult = await runPackageCommand({\n\t\t\t\tappName: \"pi\",\n\t\t\t\targs: [\"remove\", extensionDir],\n\t\t\t\tcwd,\n\t\t\t\tagentDir,\n\t\t\t\tstdout: stdout.stream,\n\t\t\t\tstderr: stderr.stream,\n\t\t\t});\n\n\t\t\tassert.equal(removeResult.handled, true);\n\t\t\tassert.equal(removeResult.exitCode, 0);\n\t\t\tassert.equal(readFileSync(join(extensionDir, \"legacy-before-remove.txt\"), \"utf-8\"), \"ok\");\n\t\t\tassert.equal(readFileSync(join(extensionDir, \"legacy-after-remove.txt\"), \"utf-8\"), \"ok\");\n\t\t} finally {\n\t\t\trmSync(root, { recursive: true, force: true });\n\t\t}\n\t});\n\n\tit(\"skips lifecycle phases with no hooks declared\", async () => {\n\t\tconst root = mkdtempSync(join(tmpdir(), \"pi-lifecycle-skip-\"));\n\t\tconst cwd = join(root, \"cwd\");\n\t\tconst agentDir = join(root, \"agent\");\n\t\tconst extensionDir = join(root, \"ext-empty\");\n\t\tmkdirSync(cwd, { recursive: true });\n\t\tmkdirSync(agentDir, { recursive: true });\n\t\tmkdirSync(extensionDir, { recursive: true });\n\n\t\ttry {\n\t\t\twritePackage(extensionDir, {\n\t\t\t\t\"package.json\": JSON.stringify({\n\t\t\t\t\tname: \"ext-empty\",\n\t\t\t\t\ttype: \"module\",\n\t\t\t\t\tpi: { extensions: [\"./index.js\"] },\n\t\t\t\t}),\n\t\t\t\t\"index.js\": `export default function () {}`,\n\t\t\t});\n\n\t\t\tconst stdout = createCaptureStream();\n\t\t\tconst stderr = createCaptureStream();\n\t\t\tconst installResult = await runPackageCommand({\n\t\t\t\tappName: \"pi\",\n\t\t\t\targs: [\"install\", extensionDir],\n\t\t\t\tcwd,\n\t\t\t\tagentDir,\n\t\t\t\tstdout: stdout.stream,\n\t\t\t\tstderr: stderr.stream,\n\t\t\t});\n\t\t\tassert.equal(installResult.handled, true);\n\t\t\tassert.equal(installResult.exitCode, 0);\n\n\t\t\tconst removeResult = await runPackageCommand({\n\t\t\t\tappName: \"pi\",\n\t\t\t\targs: [\"remove\", extensionDir],\n\t\t\t\tcwd,\n\t\t\t\tagentDir,\n\t\t\t\tstdout: stdout.stream,\n\t\t\t\tstderr: stderr.stream,\n\t\t\t});\n\t\t\tassert.equal(removeResult.handled, true);\n\t\t\tassert.equal(removeResult.exitCode, 0);\n\t\t\tassert.equal(stderr.getOutput().includes(\"Hook failed\"), false);\n\t\t} finally {\n\t\t\trmSync(root, { recursive: true, force: true });\n\t\t}\n\t});\n\n\tit(\"fails install when manifest runtime dependency is missing\", async () => {\n\t\tconst root = mkdtempSync(join(tmpdir(), \"pi-lifecycle-deps-\"));\n\t\tconst cwd = join(root, \"cwd\");\n\t\tconst agentDir = join(root, \"agent\");\n\t\tconst extensionDir = join(root, \"ext-runtime-deps\");\n\t\tmkdirSync(cwd, { recursive: true });\n\t\tmkdirSync(agentDir, { recursive: true });\n\t\tmkdirSync(extensionDir, { recursive: true });\n\n\t\ttry {\n\t\t\twritePackage(extensionDir, {\n\t\t\t\t\"package.json\": JSON.stringify({\n\t\t\t\t\tname: \"ext-runtime-deps\",\n\t\t\t\t\ttype: \"module\",\n\t\t\t\t\tpi: { extensions: [\"./index.js\"] },\n\t\t\t\t}),\n\t\t\t\t\"index.js\": `export default function () {}`,\n\t\t\t\t\"extension-manifest.json\": JSON.stringify({\n\t\t\t\t\tid: \"ext-runtime-deps\",\n\t\t\t\t\tname: \"Runtime Dep Test\",\n\t\t\t\t\tversion: \"1.0.0\",\n\t\t\t\t\tdependencies: { runtime: [\"__definitely_missing_command_for_test__\"] },\n\t\t\t\t}),\n\t\t\t});\n\n\t\t\tconst stdout = createCaptureStream();\n\t\t\tconst stderr = createCaptureStream();\n\t\t\tconst result = await runPackageCommand({\n\t\t\t\tappName: \"pi\",\n\t\t\t\targs: [\"install\", extensionDir],\n\t\t\t\tcwd,\n\t\t\t\tagentDir,\n\t\t\t\tstdout: stdout.stream,\n\t\t\t\tstderr: stderr.stream,\n\t\t\t});\n\n\t\t\tassert.equal(result.handled, true);\n\t\t\tassert.equal(result.exitCode, 1);\n\t\t\tassert.ok(stderr.getOutput().includes(\"Missing runtime dependencies\"));\n\t\t} finally {\n\t\t\trmSync(root, { recursive: true, force: true });\n\t\t}\n\t});\n});\n"]}
1
+ {"version":3,"file":"package-commands.test.js","sourceRoot":"","sources":["../../src/core/package-commands.test.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from \"node:assert/strict\";\nimport { existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { Writable } from \"node:stream\";\nimport { describe, it } from \"node:test\";\nimport { runPackageCommand } from \"./package-commands.js\";\n\nfunction createCaptureStream() {\n\tlet output = \"\";\n\tconst stream = new Writable({\n\t\twrite(chunk, _encoding, callback) {\n\t\t\toutput += chunk.toString();\n\t\t\tcallback();\n\t\t},\n\t}) as unknown as NodeJS.WriteStream;\n\treturn { stream, getOutput: () => output };\n}\n\nfunction writePackage(root: string, files: Record<string, string>): void {\n\tfor (const [relPath, content] of Object.entries(files)) {\n\t\tconst abs = join(root, relPath);\n\t\tmkdirSync(join(abs, \"..\"), { recursive: true });\n\t\twriteFileSync(abs, content, \"utf-8\");\n\t}\n}\n\nfunction createTestDirs(prefix: string, t: { after: (fn: () => void) => void }) {\n\tconst root = mkdtempSync(join(tmpdir(), `pi-lifecycle-${prefix}-`));\n\tt.after(() => rmSync(root, { recursive: true, force: true }));\n\tconst cwd = join(root, \"cwd\");\n\tconst agentDir = join(root, \"agent\");\n\tconst extensionDir = join(root, `ext-${prefix}`);\n\tmkdirSync(cwd, { recursive: true });\n\tmkdirSync(agentDir, { recursive: true });\n\tmkdirSync(extensionDir, { recursive: true });\n\treturn { root, cwd, agentDir, extensionDir };\n}\n\ndescribe(\"runPackageCommand lifecycle hooks\", () => {\n\tit(\"executes registered beforeInstall and afterInstall handlers for local packages\", async (t) => {\n\t\tconst { cwd, agentDir, extensionDir } = createTestDirs(\"install\", t);\n\n\t\twritePackage(extensionDir, {\n\t\t\t\"package.json\": JSON.stringify({\n\t\t\t\tname: \"ext-registered\",\n\t\t\t\ttype: \"module\",\n\t\t\t\tpi: { extensions: [\"./index.js\"] },\n\t\t\t}),\n\t\t\t\"index.js\": [\n\t\t\t\t'import { writeFileSync } from \"node:fs\";',\n\t\t\t\t'import { join } from \"node:path\";',\n\t\t\t\t\"export default function (pi) {\",\n\t\t\t\t\" pi.registerBeforeInstall((ctx) => {\",\n\t\t\t\t' writeFileSync(join(ctx.installedPath, \"before-install-ran.txt\"), \"ok\", \"utf-8\");',\n\t\t\t\t\" });\",\n\t\t\t\t\" pi.registerAfterInstall((ctx) => {\",\n\t\t\t\t' writeFileSync(join(ctx.installedPath, \"after-install-ran.txt\"), \"ok\", \"utf-8\");',\n\t\t\t\t\" });\",\n\t\t\t\t\"}\",\n\t\t\t].join(\"\\n\"),\n\t\t});\n\n\t\tconst stdout = createCaptureStream();\n\t\tconst stderr = createCaptureStream();\n\t\tconst result = await runPackageCommand({\n\t\t\tappName: \"pi\",\n\t\t\targs: [\"install\", extensionDir],\n\t\t\tcwd,\n\t\t\tagentDir,\n\t\t\tstdout: stdout.stream,\n\t\t\tstderr: stderr.stream,\n\t\t});\n\n\t\tassert.equal(result.handled, true);\n\t\tassert.equal(result.exitCode, 0);\n\t\tassert.equal(readFileSync(join(extensionDir, \"before-install-ran.txt\"), \"utf-8\"), \"ok\");\n\t\tassert.equal(readFileSync(join(extensionDir, \"after-install-ran.txt\"), \"utf-8\"), \"ok\");\n\t\tassert.ok(stdout.getOutput().includes(`Installed ${extensionDir}`));\n\t});\n\n\tit(\"runs legacy named lifecycle hooks when no registered hooks exist\", async (t) => {\n\t\tconst { cwd, agentDir, extensionDir } = createTestDirs(\"legacy\", t);\n\n\t\twritePackage(extensionDir, {\n\t\t\t\"package.json\": JSON.stringify({\n\t\t\t\tname: \"ext-legacy\",\n\t\t\t\ttype: \"module\",\n\t\t\t\tpi: { extensions: [\"./index.js\"] },\n\t\t\t}),\n\t\t\t\"index.js\": [\n\t\t\t\t'import { writeFileSync } from \"node:fs\";',\n\t\t\t\t'import { join } from \"node:path\";',\n\t\t\t\t\"export default function () {}\",\n\t\t\t\t\"export async function beforeInstall(ctx) {\",\n\t\t\t\t' writeFileSync(join(ctx.installedPath, \"legacy-before-install.txt\"), \"ok\", \"utf-8\");',\n\t\t\t\t\"}\",\n\t\t\t\t\"export async function afterInstall(ctx) {\",\n\t\t\t\t' writeFileSync(join(ctx.installedPath, \"legacy-after-install.txt\"), \"ok\", \"utf-8\");',\n\t\t\t\t\"}\",\n\t\t\t\t\"export async function beforeRemove(ctx) {\",\n\t\t\t\t' writeFileSync(join(ctx.installedPath, \"legacy-before-remove.txt\"), \"ok\", \"utf-8\");',\n\t\t\t\t\"}\",\n\t\t\t\t\"export async function afterRemove(ctx) {\",\n\t\t\t\t' writeFileSync(join(ctx.installedPath, \"legacy-after-remove.txt\"), \"ok\", \"utf-8\");',\n\t\t\t\t\"}\",\n\t\t\t].join(\"\\n\"),\n\t\t});\n\n\t\tconst stdout = createCaptureStream();\n\t\tconst stderr = createCaptureStream();\n\t\tconst installResult = await runPackageCommand({\n\t\t\tappName: \"pi\",\n\t\t\targs: [\"install\", extensionDir],\n\t\t\tcwd,\n\t\t\tagentDir,\n\t\t\tstdout: stdout.stream,\n\t\t\tstderr: stderr.stream,\n\t\t});\n\n\t\tassert.equal(installResult.handled, true);\n\t\tassert.equal(installResult.exitCode, 0);\n\t\tassert.equal(readFileSync(join(extensionDir, \"legacy-before-install.txt\"), \"utf-8\"), \"ok\");\n\t\tassert.equal(readFileSync(join(extensionDir, \"legacy-after-install.txt\"), \"utf-8\"), \"ok\");\n\n\t\tconst removeResult = await runPackageCommand({\n\t\t\tappName: \"pi\",\n\t\t\targs: [\"remove\", extensionDir],\n\t\t\tcwd,\n\t\t\tagentDir,\n\t\t\tstdout: stdout.stream,\n\t\t\tstderr: stderr.stream,\n\t\t});\n\n\t\tassert.equal(removeResult.handled, true);\n\t\tassert.equal(removeResult.exitCode, 0);\n\t\tassert.equal(readFileSync(join(extensionDir, \"legacy-before-remove.txt\"), \"utf-8\"), \"ok\");\n\t\tassert.equal(readFileSync(join(extensionDir, \"legacy-after-remove.txt\"), \"utf-8\"), \"ok\");\n\t});\n\n\tit(\"skips lifecycle phases with no hooks declared\", async (t) => {\n\t\tconst { cwd, agentDir, extensionDir } = createTestDirs(\"skip\", t);\n\n\t\twritePackage(extensionDir, {\n\t\t\t\"package.json\": JSON.stringify({\n\t\t\t\tname: \"ext-empty\",\n\t\t\t\ttype: \"module\",\n\t\t\t\tpi: { extensions: [\"./index.js\"] },\n\t\t\t}),\n\t\t\t\"index.js\": \"export default function () {}\",\n\t\t});\n\n\t\tconst stdout = createCaptureStream();\n\t\tconst stderr = createCaptureStream();\n\t\tconst installResult = await runPackageCommand({\n\t\t\tappName: \"pi\",\n\t\t\targs: [\"install\", extensionDir],\n\t\t\tcwd,\n\t\t\tagentDir,\n\t\t\tstdout: stdout.stream,\n\t\t\tstderr: stderr.stream,\n\t\t});\n\t\tassert.equal(installResult.handled, true);\n\t\tassert.equal(installResult.exitCode, 0);\n\n\t\tconst removeResult = await runPackageCommand({\n\t\t\tappName: \"pi\",\n\t\t\targs: [\"remove\", extensionDir],\n\t\t\tcwd,\n\t\t\tagentDir,\n\t\t\tstdout: stdout.stream,\n\t\t\tstderr: stderr.stream,\n\t\t});\n\t\tassert.equal(removeResult.handled, true);\n\t\tassert.equal(removeResult.exitCode, 0);\n\t\tassert.equal(stderr.getOutput().includes(\"Hook failed\"), false);\n\t});\n\n\tit(\"fails install when manifest runtime dependency is missing\", async (t) => {\n\t\tconst { cwd, agentDir, extensionDir } = createTestDirs(\"deps\", t);\n\n\t\twritePackage(extensionDir, {\n\t\t\t\"package.json\": JSON.stringify({\n\t\t\t\tname: \"ext-runtime-deps\",\n\t\t\t\ttype: \"module\",\n\t\t\t\tpi: { extensions: [\"./index.js\"] },\n\t\t\t}),\n\t\t\t\"index.js\": \"export default function () {}\",\n\t\t\t\"extension-manifest.json\": JSON.stringify({\n\t\t\t\tid: \"ext-runtime-deps\",\n\t\t\t\tname: \"Runtime Dep Test\",\n\t\t\t\tversion: \"1.0.0\",\n\t\t\t\tdependencies: { runtime: [\"__definitely_missing_command_for_test__\"] },\n\t\t\t}),\n\t\t});\n\n\t\tconst stdout = createCaptureStream();\n\t\tconst stderr = createCaptureStream();\n\t\tconst result = await runPackageCommand({\n\t\t\tappName: \"pi\",\n\t\t\targs: [\"install\", extensionDir],\n\t\t\tcwd,\n\t\t\tagentDir,\n\t\t\tstdout: stdout.stream,\n\t\t\tstderr: stderr.stream,\n\t\t});\n\n\t\tassert.equal(result.handled, true);\n\t\tassert.equal(result.exitCode, 1);\n\t\tassert.ok(stderr.getOutput().includes(\"Missing runtime dependencies\"));\n\t});\n\n\tit(\"afterRemove hook receives installedPath even when directory is deleted\", async (t) => {\n\t\tconst { cwd, agentDir, extensionDir } = createTestDirs(\"after-remove\", t);\n\n\t\twritePackage(extensionDir, {\n\t\t\t\"package.json\": JSON.stringify({\n\t\t\t\tname: \"ext-after-remove\",\n\t\t\t\ttype: \"module\",\n\t\t\t\tpi: { extensions: [\"./index.js\"] },\n\t\t\t}),\n\t\t\t\"index.js\": [\n\t\t\t\t'import { writeFileSync, existsSync } from \"node:fs\";',\n\t\t\t\t'import { join } from \"node:path\";',\n\t\t\t\t\"export default function () {}\",\n\t\t\t\t\"export async function afterRemove(ctx) {\",\n\t\t\t\t' const marker = join(ctx.cwd, \"after-remove-marker.json\");',\n\t\t\t\t\" writeFileSync(marker, JSON.stringify({\",\n\t\t\t\t\" receivedPath: ctx.installedPath,\",\n\t\t\t\t\" pathExisted: existsSync(ctx.installedPath),\",\n\t\t\t\t' }), \"utf-8\");',\n\t\t\t\t\"}\",\n\t\t\t].join(\"\\n\"),\n\t\t});\n\n\t\tconst stdout = createCaptureStream();\n\t\tconst stderr = createCaptureStream();\n\n\t\tawait runPackageCommand({\n\t\t\tappName: \"pi\",\n\t\t\targs: [\"install\", extensionDir],\n\t\t\tcwd,\n\t\t\tagentDir,\n\t\t\tstdout: stdout.stream,\n\t\t\tstderr: stderr.stream,\n\t\t});\n\n\t\tawait runPackageCommand({\n\t\t\tappName: \"pi\",\n\t\t\targs: [\"remove\", extensionDir],\n\t\t\tcwd,\n\t\t\tagentDir,\n\t\t\tstdout: stdout.stream,\n\t\t\tstderr: stderr.stream,\n\t\t});\n\n\t\tconst markerPath = join(cwd, \"after-remove-marker.json\");\n\t\tassert.ok(existsSync(markerPath), \"afterRemove hook must have executed and written marker\");\n\t\tconst marker = JSON.parse(readFileSync(markerPath, \"utf-8\"));\n\t\tassert.equal(typeof marker.receivedPath, \"string\", \"hook must receive installedPath as string\");\n\t});\n});\n"]}
@@ -97,6 +97,7 @@ export class CompactionOrchestrator {
97
97
  if (!this._deps.modelRegistry.isProviderRequestReady(model.provider)) {
98
98
  throw new Error(`No API key for ${model.provider}`);
99
99
  }
100
+ // undefined for externalCli/none providers — stripped at the streamSimple boundary (model-registry.ts)
100
101
  const apiKey = await this._deps.modelRegistry.getApiKey(model, this._deps.getSessionId());
101
102
 
102
103
  const pathEntries = this._deps.sessionManager.getBranch();
@@ -303,6 +304,7 @@ export class CompactionOrchestrator {
303
304
  this._deps.emit({ type: "auto_compaction_end", result: undefined, aborted: false, willRetry: false });
304
305
  return;
305
306
  }
307
+ // undefined for externalCli/none providers — stripped at the streamSimple boundary (model-registry.ts)
306
308
  const apiKey = await this._deps.modelRegistry.getApiKey(model, this._deps.getSessionId());
307
309
 
308
310
  const pathEntries = this._deps.sessionManager.getBranch();
@@ -1242,7 +1242,8 @@ export interface ExtensionAPI {
1242
1242
  export interface ProviderConfig {
1243
1243
  /** Auth behavior for provider availability and request key handling. Defaults to "apiKey". */
1244
1244
  authMode?: "apiKey" | "oauth" | "externalCli" | "none";
1245
- /** Optional readiness check. Return false if the provider cannot accept requests (e.g., CLI not authenticated, API key invalid). Called before default auth checks. */
1245
+ /** Optional readiness check. Return false if the provider cannot accept requests (e.g., CLI not authenticated, API key invalid).
1246
+ * Called before default auth checks. Trusted at the same level as extension code — extensions already have arbitrary code execution. */
1246
1247
  isReady?: () => boolean;
1247
1248
  /** Base URL for the API endpoint. Required when defining models. */
1248
1249
  baseUrl?: string;