pi-cohort 7.1.1 → 7.1.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [7.1.2] - 2026-09-29
4
+
5
+ ### Fixed
6
+
7
+ - `typebox` moves from `dependencies` to an optional `peerDependencies: "*"` entry, which silences pi's "Host-provided extension packages must be declared in peerDependencies" startup warning and removes the second typebox copy from consumer installs. The detached async runner keeps working without its own copy: `spawnRunner` now passes pi's own `typebox`, `typebox/compile`, and `typebox/value` files to the bare jiti child through `JITI_ALIAS`, the same alias map pi's extension loader applies in-process, so the `Cannot find module 'typebox/compile'` crash from [#9](https://github.com/jjuraszek/pi-cohort/issues/9) stays fixed.
8
+
3
9
  ## [7.1.1] - 2026-09-24
4
10
 
5
11
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-cohort",
3
- "version": "7.1.1",
3
+ "version": "7.1.2",
4
4
  "description": "Delegate Pi work to focused child agents: code review, scouting, implementation, parallel audits, saved chains, and background jobs.",
5
5
  "author": "Jacek Juraszek",
6
6
  "license": "MIT",
@@ -64,7 +64,8 @@
64
64
  "@earendil-works/pi-agent-core": "*",
65
65
  "@earendil-works/pi-ai": "*",
66
66
  "@earendil-works/pi-coding-agent": ">=0.85.0",
67
- "@earendil-works/pi-tui": "*"
67
+ "@earendil-works/pi-tui": "*",
68
+ "typebox": "*"
68
69
  },
69
70
  "peerDependenciesMeta": {
70
71
  "@earendil-works/pi-agent-core": {
@@ -78,16 +79,19 @@
78
79
  },
79
80
  "@earendil-works/pi-tui": {
80
81
  "optional": true
82
+ },
83
+ "typebox": {
84
+ "optional": true
81
85
  }
82
86
  },
83
87
  "dependencies": {
84
- "jiti": "^2.7.0",
85
- "typebox": "^1.3.11"
88
+ "jiti": "^2.7.0"
86
89
  },
87
90
  "devDependencies": {
88
91
  "@earendil-works/pi-agent-core": "^0.85.0",
89
92
  "@earendil-works/pi-ai": "^0.85.0",
90
93
  "@earendil-works/pi-coding-agent": "^0.85.0",
91
- "@earendil-works/pi-tui": "^0.85.0"
94
+ "@earendil-works/pi-tui": "^0.85.0",
95
+ "typebox": "^1.3.11"
92
96
  }
93
97
  }
@@ -14,7 +14,7 @@ import { injectSingleOutputInstruction, normalizeSingleOutputOverride, resolvePa
14
14
  import { buildChainInstructions, isDynamicParallelStep, isParallelStep, resolveStepBehavior, suppressProgressForReadOnlyTask, writeInitialProgressFile, type ChainStep, type ResolvedStepBehavior, type SequentialStep, type StepOverrides } from "../../shared/settings.ts";
15
15
  import type { RunnerStep } from "../shared/parallel-utils.ts";
16
16
  import { resolvePiPackageRoot } from "../shared/pi-spawn.ts";
17
- import { ensureJitiCliPath } from "../shared/jiti-cli.ts";
17
+ import { ensureJitiCliPath, resolveHostModuleAliases } from "../shared/jiti-cli.ts";
18
18
  export { createJitiCliResolver } from "../shared/jiti-cli.ts";
19
19
  import { buildSkillInjection, normalizeSkillInput, resolveSkillsWithFallback } from "../../agents/skills.ts";
20
20
  import { resolveChildCwd } from "../../shared/utils.ts";
@@ -145,7 +145,7 @@ export function isAsyncAvailable(): boolean {
145
145
  * Falls back to ignored stdio if the log file cannot be opened; the parent always
146
146
  * closes its copy of the fd, on every exit path.
147
147
  */
148
- export function spawnDetachedWithLog(command: string, args: string[], cwd: string, asyncDir: string, spawnImpl: typeof spawn = spawn): { pid?: number; error?: string } {
148
+ export function spawnDetachedWithLog(command: string, args: string[], cwd: string, asyncDir: string, spawnImpl: typeof spawn = spawn, env?: NodeJS.ProcessEnv): { pid?: number; error?: string } {
149
149
  let fd: number | undefined;
150
150
  try {
151
151
  try {
@@ -156,6 +156,7 @@ export function spawnDetachedWithLog(command: string, args: string[], cwd: strin
156
156
  }
157
157
  const proc = spawnImpl(command, args, {
158
158
  cwd,
159
+ env,
159
160
  detached: true,
160
161
  stdio: fd === undefined ? "ignore" : ["ignore", fd, fd],
161
162
  windowsHide: true,
@@ -187,7 +188,7 @@ export function spawnRunner(
187
188
  suffix: string,
188
189
  cwd: string,
189
190
  asyncDir: string,
190
- deps: { ensureJiti?: () => string | undefined; spawnImpl?: typeof spawn } = {},
191
+ deps: { ensureJiti?: () => string | undefined; spawnImpl?: typeof spawn; hostAliases?: () => Record<string, string> } = {},
191
192
  ): { pid?: number; error?: string } {
192
193
  const ensureJiti = deps.ensureJiti ?? ensureJitiCliPath;
193
194
  const jitiCliPath = ensureJiti();
@@ -209,7 +210,12 @@ export function spawnRunner(
209
210
  fs.writeFileSync(cfgPath, JSON.stringify(cfg));
210
211
  const runner = path.join(path.dirname(fileURLToPath(import.meta.url)), "subagent-runner.ts");
211
212
 
212
- return spawnDetachedWithLog(process.execPath, [jitiCliPath, runner, cfgPath], cwd, asyncDir, deps.spawnImpl ?? spawn);
213
+ const aliases = (deps.hostAliases ?? resolveHostModuleAliases)();
214
+ const env = Object.keys(aliases).length > 0
215
+ ? { ...process.env, JITI_ALIAS: JSON.stringify(aliases) }
216
+ : process.env;
217
+
218
+ return spawnDetachedWithLog(process.execPath, [jitiCliPath, runner, cfgPath], cwd, asyncDir, deps.spawnImpl ?? spawn, env);
213
219
  }
214
220
 
215
221
  /**
@@ -24,17 +24,23 @@ function resolveJitiCliFromPackageJson(packageJsonPath: string): string | undefi
24
24
  return undefined;
25
25
  }
26
26
 
27
- function resolveJitiCliPath(): string | undefined {
28
- const candidates: Array<() => string | undefined> = [
29
- () => require.resolve("jiti/package.json"),
27
+ function hostResolveCandidates(specifier: string): Array<() => string | undefined> {
28
+ return [
29
+ () => require.resolve(specifier),
30
30
  () => piPackageRoot
31
- ? createRequire(path.join(piPackageRoot, "package.json")).resolve("jiti/package.json")
31
+ ? createRequire(path.join(piPackageRoot, "package.json")).resolve(specifier)
32
32
  : undefined,
33
33
  () => {
34
34
  if (!process.argv[1]) return undefined;
35
35
  const piEntry = fs.realpathSync(process.argv[1]);
36
- return createRequire(piEntry).resolve("jiti/package.json");
36
+ return createRequire(piEntry).resolve(specifier);
37
37
  },
38
+ ];
39
+ }
40
+
41
+ function resolveJitiCliPath(): string | undefined {
42
+ const candidates = [
43
+ ...hostResolveCandidates("jiti/package.json"),
38
44
  () => piPackageRoot ? path.join(piPackageRoot, "node_modules", "jiti", "package.json") : undefined,
39
45
  ];
40
46
  for (const candidate of candidates) {
@@ -50,6 +56,31 @@ function resolveJitiCliPath(): string | undefined {
50
56
  return undefined;
51
57
  }
52
58
 
59
+ function resolveFromHost(specifier: string): string | undefined {
60
+ for (const candidate of hostResolveCandidates(specifier)) {
61
+ try {
62
+ const resolved = candidate();
63
+ if (resolved) return resolved;
64
+ } catch {
65
+ // Candidate not available in this install, continue probing.
66
+ }
67
+ }
68
+ return undefined;
69
+ }
70
+
71
+ // Pi's extension loader aliases these to its own copies; the detached runner boots
72
+ // under the bare jiti CLI, so it gets the same map through JITI_ALIAS.
73
+ const HOST_ALIASED_SPECIFIERS = ["typebox", "typebox/compile", "typebox/value"];
74
+
75
+ export function resolveHostModuleAliases(): Record<string, string> {
76
+ const aliases: Record<string, string> = {};
77
+ for (const specifier of HOST_ALIASED_SPECIFIERS) {
78
+ const resolved = resolveFromHost(specifier);
79
+ if (resolved) aliases[specifier] = resolved;
80
+ }
81
+ return aliases;
82
+ }
83
+
53
84
  export function createJitiCliResolver(deps: { resolve?: () => string | undefined; exists?: (p: string) => boolean } = {}): () => string | undefined {
54
85
  const resolve = deps.resolve ?? resolveJitiCliPath;
55
86
  const exists = deps.exists ?? ((p: string) => fs.existsSync(p));