gsd-pi 2.45.0-dev.6b9da3e → 2.45.0-dev.e0ee972

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 (104) 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 +13 -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/workflow-logger.js +138 -0
  15. package/dist/resources/extensions/gsd/worktree-manager.js +4 -3
  16. package/dist/resources/extensions/gsd/worktree-resolver.js +37 -0
  17. package/dist/resources/extensions/voice/index.js +11 -16
  18. package/dist/resources/extensions/voice/linux-ready.js +67 -0
  19. package/dist/web/standalone/.next/BUILD_ID +1 -1
  20. package/dist/web/standalone/.next/app-path-routes-manifest.json +19 -19
  21. package/dist/web/standalone/.next/build-manifest.json +2 -2
  22. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  23. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  24. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  25. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  32. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  36. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/index.html +1 -1
  40. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app-paths-manifest.json +19 -19
  47. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  48. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  49. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  50. package/package.json +1 -1
  51. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -1
  52. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +2 -0
  53. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
  54. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -1
  55. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  56. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  57. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts +4 -0
  58. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -1
  59. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +10 -5
  60. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -1
  61. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts +2 -0
  62. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts.map +1 -0
  63. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js +185 -0
  64. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js.map +1 -0
  65. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +239 -10
  66. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  67. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +2 -1
  68. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  69. package/packages/pi-coding-agent/dist/core/model-registry.js +20 -2
  70. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  71. package/packages/pi-coding-agent/dist/core/package-commands.test.js +206 -195
  72. package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -1
  73. package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +2 -0
  74. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -1
  75. package/packages/pi-coding-agent/src/core/lifecycle-hooks.test.ts +227 -0
  76. package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +11 -5
  77. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +297 -11
  78. package/packages/pi-coding-agent/src/core/model-registry.ts +30 -3
  79. package/packages/pi-coding-agent/src/core/package-commands.test.ts +227 -205
  80. package/src/resources/extensions/gsd/auto/phases.ts +16 -12
  81. package/src/resources/extensions/gsd/auto/run-unit.ts +6 -3
  82. package/src/resources/extensions/gsd/auto-worktree.ts +8 -5
  83. package/src/resources/extensions/gsd/auto.ts +3 -3
  84. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +13 -12
  85. package/src/resources/extensions/gsd/db-writer.ts +9 -17
  86. package/src/resources/extensions/gsd/doctor-checks.ts +1 -1
  87. package/src/resources/extensions/gsd/doctor.ts +2 -2
  88. package/src/resources/extensions/gsd/gsd-db.ts +5 -1
  89. package/src/resources/extensions/gsd/journal.ts +6 -1
  90. package/src/resources/extensions/gsd/preferences-types.ts +2 -2
  91. package/src/resources/extensions/gsd/preferences.ts +7 -3
  92. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +1 -1
  93. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +42 -3
  94. package/src/resources/extensions/gsd/tests/preferences.test.ts +7 -9
  95. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +275 -0
  96. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +220 -0
  97. package/src/resources/extensions/gsd/workflow-logger.ts +193 -0
  98. package/src/resources/extensions/gsd/worktree-manager.ts +4 -9
  99. package/src/resources/extensions/gsd/worktree-resolver.ts +37 -0
  100. package/src/resources/extensions/voice/index.ts +11 -21
  101. package/src/resources/extensions/voice/linux-ready.ts +87 -0
  102. package/src/resources/extensions/voice/tests/linux-ready.test.ts +124 -0
  103. /package/dist/web/standalone/.next/static/{rzO54ZboyINyEt7cVM_uS → dFMji9G1LZ-Tv36el9pRT}/_buildManifest.js +0 -0
  104. /package/dist/web/standalone/.next/static/{rzO54ZboyINyEt7cVM_uS → dFMji9G1LZ-Tv36el9pRT}/_ssgManifest.js +0 -0
@@ -0,0 +1,227 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync, existsSync } from "node:fs";
3
+ import { homedir, tmpdir } from "node:os";
4
+ import { join, resolve } from "node:path";
5
+ import { describe, it } from "node:test";
6
+ import {
7
+ readManifestRuntimeDeps,
8
+ collectRuntimeDependencies,
9
+ verifyRuntimeDependencies,
10
+ resolveLocalSourcePath,
11
+ } from "./lifecycle-hooks.js";
12
+
13
+ function tmpDir(prefix: string, t: { after: (fn: () => void) => void }): string {
14
+ const dir = mkdtempSync(join(tmpdir(), `pi-lh-${prefix}-`));
15
+ t.after(() => rmSync(dir, { recursive: true, force: true }));
16
+ return dir;
17
+ }
18
+
19
+ // ─── readManifestRuntimeDeps ──────────────────────────────────────────────────
20
+
21
+ describe("readManifestRuntimeDeps", () => {
22
+ it("returns empty array when manifest file is missing", (t) => {
23
+ const dir = tmpDir("no-manifest", t);
24
+ assert.deepEqual(readManifestRuntimeDeps(dir), []);
25
+ });
26
+
27
+ it("returns empty array for malformed JSON", (t) => {
28
+ const dir = tmpDir("bad-json", t);
29
+ writeFileSync(join(dir, "extension-manifest.json"), "not json{{{", "utf-8");
30
+ assert.deepEqual(readManifestRuntimeDeps(dir), []);
31
+ });
32
+
33
+ it("returns runtime deps from valid manifest", (t) => {
34
+ const dir = tmpDir("valid", t);
35
+ writeFileSync(join(dir, "extension-manifest.json"), JSON.stringify({
36
+ dependencies: { runtime: ["claude", "node"] },
37
+ }), "utf-8");
38
+ assert.deepEqual(readManifestRuntimeDeps(dir), ["claude", "node"]);
39
+ });
40
+
41
+ it("returns empty array when dependencies exists but runtime is missing", (t) => {
42
+ const dir = tmpDir("no-runtime", t);
43
+ writeFileSync(join(dir, "extension-manifest.json"), JSON.stringify({
44
+ dependencies: {},
45
+ }), "utf-8");
46
+ assert.deepEqual(readManifestRuntimeDeps(dir), []);
47
+ });
48
+
49
+ it("returns empty array when runtime is empty", (t) => {
50
+ const dir = tmpDir("empty-runtime", t);
51
+ writeFileSync(join(dir, "extension-manifest.json"), JSON.stringify({
52
+ dependencies: { runtime: [] },
53
+ }), "utf-8");
54
+ assert.deepEqual(readManifestRuntimeDeps(dir), []);
55
+ });
56
+
57
+ it("filters out non-string entries in runtime array", (t) => {
58
+ const dir = tmpDir("mixed-types", t);
59
+ writeFileSync(join(dir, "extension-manifest.json"), JSON.stringify({
60
+ dependencies: { runtime: [123, null, "node", false, "python"] },
61
+ }), "utf-8");
62
+ assert.deepEqual(readManifestRuntimeDeps(dir), ["node", "python"]);
63
+ });
64
+
65
+ it("returns empty array when no dependencies field at all", (t) => {
66
+ const dir = tmpDir("no-deps-field", t);
67
+ writeFileSync(join(dir, "extension-manifest.json"), JSON.stringify({
68
+ id: "test",
69
+ name: "Test",
70
+ }), "utf-8");
71
+ assert.deepEqual(readManifestRuntimeDeps(dir), []);
72
+ });
73
+ });
74
+
75
+ // ─── collectRuntimeDependencies ───────────────────────────────────────────────
76
+
77
+ describe("collectRuntimeDependencies", () => {
78
+ it("aggregates deps from installedPath manifest", (t) => {
79
+ const dir = tmpDir("collect-installed", t);
80
+ writeFileSync(join(dir, "extension-manifest.json"), JSON.stringify({
81
+ dependencies: { runtime: ["claude"] },
82
+ }), "utf-8");
83
+ assert.deepEqual(collectRuntimeDependencies(dir, []), ["claude"]);
84
+ });
85
+
86
+ it("aggregates deps from entry path directory manifests", (t) => {
87
+ const root = tmpDir("collect-entry", t);
88
+ const installedDir = join(root, "installed");
89
+ const entryDir = join(root, "entry");
90
+ mkdirSync(installedDir, { recursive: true });
91
+ mkdirSync(entryDir, { recursive: true });
92
+ writeFileSync(join(entryDir, "extension-manifest.json"), JSON.stringify({
93
+ dependencies: { runtime: ["python"] },
94
+ }), "utf-8");
95
+ const deps = collectRuntimeDependencies(installedDir, [join(entryDir, "index.ts")]);
96
+ assert.deepEqual(deps, ["python"]);
97
+ });
98
+
99
+ it("deduplicates across multiple directories", (t) => {
100
+ const root = tmpDir("collect-dedup", t);
101
+ const dir1 = join(root, "dir1");
102
+ const dir2 = join(root, "dir2");
103
+ mkdirSync(dir1, { recursive: true });
104
+ mkdirSync(dir2, { recursive: true });
105
+ writeFileSync(join(dir1, "extension-manifest.json"), JSON.stringify({
106
+ dependencies: { runtime: ["node", "python"] },
107
+ }), "utf-8");
108
+ writeFileSync(join(dir2, "extension-manifest.json"), JSON.stringify({
109
+ dependencies: { runtime: ["python", "claude"] },
110
+ }), "utf-8");
111
+ const deps = collectRuntimeDependencies(dir1, [join(dir2, "index.ts")]);
112
+ assert.equal(deps.length, 3);
113
+ assert.ok(deps.includes("node"));
114
+ assert.ok(deps.includes("python"));
115
+ assert.ok(deps.includes("claude"));
116
+ });
117
+
118
+ it("returns empty when no directories have manifests", (t) => {
119
+ const dir = tmpDir("collect-empty", t);
120
+ assert.deepEqual(collectRuntimeDependencies(dir, []), []);
121
+ });
122
+ });
123
+
124
+ // ─── verifyRuntimeDependencies ────────────────────────────────────────────────
125
+
126
+ describe("verifyRuntimeDependencies", () => {
127
+ it("does not throw for empty deps array", () => {
128
+ assert.doesNotThrow(() => verifyRuntimeDependencies([], "test-source", "pi"));
129
+ });
130
+
131
+ it("does not throw when all deps are present", () => {
132
+ assert.doesNotThrow(() => verifyRuntimeDependencies(["node"], "test-source", "pi"));
133
+ });
134
+
135
+ it("throws for missing dep with 'Missing runtime dependencies' message", () => {
136
+ assert.throws(
137
+ () => verifyRuntimeDependencies(["__nonexistent_dep_for_test__"], "test-source", "pi"),
138
+ (err: Error) => {
139
+ assert.ok(err.message.includes("Missing runtime dependencies"));
140
+ assert.ok(err.message.includes("__nonexistent_dep_for_test__"));
141
+ return true;
142
+ },
143
+ );
144
+ });
145
+
146
+ it("lists all missing deps in error message", () => {
147
+ assert.throws(
148
+ () => verifyRuntimeDependencies(["__missing_1__", "__missing_2__"], "test-source", "pi"),
149
+ (err: Error) => {
150
+ assert.ok(err.message.includes("__missing_1__"));
151
+ assert.ok(err.message.includes("__missing_2__"));
152
+ return true;
153
+ },
154
+ );
155
+ });
156
+
157
+ it("includes appName and source in error for retry hint", () => {
158
+ assert.throws(
159
+ () => verifyRuntimeDependencies(["__missing__"], "github:user/repo", "gsd"),
160
+ (err: Error) => {
161
+ assert.ok(err.message.includes("gsd"));
162
+ assert.ok(err.message.includes("github:user/repo"));
163
+ return true;
164
+ },
165
+ );
166
+ });
167
+ });
168
+
169
+ // ─── resolveLocalSourcePath ───────────────────────────────────────────────────
170
+
171
+ describe("resolveLocalSourcePath", () => {
172
+ it("returns undefined for empty string", () => {
173
+ assert.equal(resolveLocalSourcePath("", "/tmp"), undefined);
174
+ });
175
+
176
+ it("returns undefined for npm: source", () => {
177
+ assert.equal(resolveLocalSourcePath("npm:@foo/bar", "/tmp"), undefined);
178
+ });
179
+
180
+ it("returns undefined for git URL", () => {
181
+ assert.equal(resolveLocalSourcePath("git:github.com/user/repo", "/tmp"), undefined);
182
+ });
183
+
184
+ it("returns undefined for https git URL", () => {
185
+ assert.equal(resolveLocalSourcePath("https://github.com/user/repo", "/tmp"), undefined);
186
+ });
187
+
188
+ it("resolves ~ to homedir", () => {
189
+ const result = resolveLocalSourcePath("~", "/tmp");
190
+ if (existsSync(homedir())) {
191
+ assert.equal(result, homedir());
192
+ } else {
193
+ assert.equal(result, undefined);
194
+ }
195
+ });
196
+
197
+ it("resolves ~/path relative to homedir", () => {
198
+ const result = resolveLocalSourcePath("~/", "/tmp");
199
+ if (existsSync(homedir())) {
200
+ assert.equal(result, homedir());
201
+ } else {
202
+ assert.equal(result, undefined);
203
+ }
204
+ });
205
+
206
+ it("resolves relative path that exists", (t) => {
207
+ const dir = tmpDir("resolve-rel", t);
208
+ const sub = join(dir, "myext");
209
+ mkdirSync(sub, { recursive: true });
210
+ const result = resolveLocalSourcePath("myext", dir);
211
+ assert.equal(result, resolve(dir, "myext"));
212
+ });
213
+
214
+ it("returns undefined for relative path that does not exist", (t) => {
215
+ const dir = tmpDir("resolve-noexist", t);
216
+ assert.equal(resolveLocalSourcePath("nonexistent", dir), undefined);
217
+ });
218
+
219
+ it("resolves absolute path that exists", (t) => {
220
+ const dir = tmpDir("resolve-abs", t);
221
+ assert.equal(resolveLocalSourcePath(dir, "/irrelevant"), dir);
222
+ });
223
+
224
+ it("returns undefined for absolute path that does not exist", () => {
225
+ assert.equal(resolveLocalSourcePath("/tmp/__nonexistent_path_for_test__", "/tmp"), undefined);
226
+ });
227
+ });
@@ -62,7 +62,7 @@ function toScope(local: boolean): LifecycleHookScope {
62
62
  return local ? "project" : "user";
63
63
  }
64
64
 
65
- function readManifestRuntimeDeps(dir: string): string[] {
65
+ export function readManifestRuntimeDeps(dir: string): string[] {
66
66
  const manifestPath = join(dir, "extension-manifest.json");
67
67
  if (!existsSync(manifestPath)) return [];
68
68
  try {
@@ -73,7 +73,7 @@ function readManifestRuntimeDeps(dir: string): string[] {
73
73
  }
74
74
  }
75
75
 
76
- function collectRuntimeDependencies(installedPath: string, entryPaths: string[]): string[] {
76
+ export function collectRuntimeDependencies(installedPath: string, entryPaths: string[]): string[] {
77
77
  const deps = new Set<string>();
78
78
  const candidateDirs = new Set<string>([installedPath, ...entryPaths.map((entryPath) => dirname(entryPath))]);
79
79
  for (const dir of candidateDirs) {
@@ -84,7 +84,7 @@ function collectRuntimeDependencies(installedPath: string, entryPaths: string[])
84
84
  return Array.from(deps);
85
85
  }
86
86
 
87
- function verifyRuntimeDependencies(runtimeDeps: string[], source: string, appName: string): void {
87
+ export function verifyRuntimeDependencies(runtimeDeps: string[], source: string, appName: string): void {
88
88
  const missing: string[] = [];
89
89
  for (const dep of runtimeDeps) {
90
90
  const result = spawnSync(dep, ["--version"], { encoding: "utf-8", timeout: 5000 });
@@ -99,7 +99,7 @@ function verifyRuntimeDependencies(runtimeDeps: string[], source: string, appNam
99
99
  );
100
100
  }
101
101
 
102
- function resolveLocalSourcePath(source: string, cwd: string): string | undefined {
102
+ export function resolveLocalSourcePath(source: string, cwd: string): string | undefined {
103
103
  const trimmed = source.trim();
104
104
  if (!trimmed) return undefined;
105
105
  if (trimmed.startsWith("npm:")) return undefined;
@@ -193,13 +193,19 @@ function getLegacyExportCandidates(phase: LifecycleHookPhase): string[] {
193
193
  return [phase];
194
194
  }
195
195
 
196
+ const _legacyModuleCache = new Map<string, Record<string, unknown>>();
197
+
196
198
  async function runLegacyExportHook(
197
199
  entryPath: string,
198
200
  phase: LifecycleHookPhase,
199
201
  context: LifecycleHookContext,
200
202
  ): Promise<LifecycleHookHandler | null> {
201
203
  try {
202
- const module = await importExtensionModule<Record<string, unknown>>(import.meta.url, pathToFileURL(entryPath).href);
204
+ let module = _legacyModuleCache.get(entryPath);
205
+ if (!module) {
206
+ module = await importExtensionModule<Record<string, unknown>>(import.meta.url, pathToFileURL(entryPath).href);
207
+ _legacyModuleCache.set(entryPath, module);
208
+ }
203
209
  for (const exportName of getLegacyExportCandidates(phase)) {
204
210
  const candidate = module[exportName];
205
211
  if (typeof candidate === "function") {