poe-code 3.0.224 → 3.0.225

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 (78) hide show
  1. package/dist/bin/poe-gemini.js +23 -0
  2. package/dist/cli/commands/configure-payload.js +50 -3
  3. package/dist/cli/commands/configure-payload.js.map +1 -1
  4. package/dist/cli/commands/pipeline.js +4 -2
  5. package/dist/cli/commands/pipeline.js.map +1 -1
  6. package/dist/cli/commands/ralph.js +1 -0
  7. package/dist/cli/commands/ralph.js.map +1 -1
  8. package/dist/cli/commands/shared.d.ts +4 -0
  9. package/dist/cli/commands/shared.js +33 -2
  10. package/dist/cli/commands/shared.js.map +1 -1
  11. package/dist/cli/commands/spawn.js +20 -0
  12. package/dist/cli/commands/spawn.js.map +1 -1
  13. package/dist/cli/commands/test.d.ts +5 -1
  14. package/dist/cli/commands/test.js +58 -6
  15. package/dist/cli/commands/test.js.map +1 -1
  16. package/dist/cli/constants.d.ts +1 -0
  17. package/dist/cli/constants.js +2 -1
  18. package/dist/cli/constants.js.map +1 -1
  19. package/dist/cli/isolated-env.d.ts +2 -1
  20. package/dist/cli/isolated-env.js +20 -2
  21. package/dist/cli/isolated-env.js.map +1 -1
  22. package/dist/cli/options.d.ts +2 -5
  23. package/dist/cli/poe-code-command-runner.js +7 -2
  24. package/dist/cli/poe-code-command-runner.js.map +1 -1
  25. package/dist/cli/prompts.d.ts +21 -13
  26. package/dist/cli/prompts.js.map +1 -1
  27. package/dist/cli/service-registry.d.ts +12 -3
  28. package/dist/cli/service-registry.js.map +1 -1
  29. package/dist/index.d.ts +3 -1
  30. package/dist/index.js +4694 -2378
  31. package/dist/index.js.map +4 -4
  32. package/dist/providers/claude-code.js +913 -152
  33. package/dist/providers/claude-code.js.map +4 -4
  34. package/dist/providers/codex.js +897 -149
  35. package/dist/providers/codex.js.map +4 -4
  36. package/dist/providers/create-provider.d.ts +1 -0
  37. package/dist/providers/create-provider.js +1 -0
  38. package/dist/providers/create-provider.js.map +1 -1
  39. package/dist/providers/gemini-cli.d.ts +16 -0
  40. package/dist/providers/gemini-cli.js +11349 -0
  41. package/dist/providers/gemini-cli.js.map +7 -0
  42. package/dist/providers/goose.js +870 -161
  43. package/dist/providers/goose.js.map +4 -4
  44. package/dist/providers/kimi.js +894 -143
  45. package/dist/providers/kimi.js.map +4 -4
  46. package/dist/providers/opencode.js +894 -143
  47. package/dist/providers/opencode.js.map +4 -4
  48. package/dist/providers/poe-agent.js +1144 -335
  49. package/dist/providers/poe-agent.js.map +4 -4
  50. package/dist/providers/spawn-options.d.ts +3 -0
  51. package/dist/sdk/pipeline.d.ts +1 -1
  52. package/dist/sdk/pipeline.js +22 -19
  53. package/dist/sdk/pipeline.js.map +1 -1
  54. package/dist/sdk/ralph.js +5 -0
  55. package/dist/sdk/ralph.js.map +1 -1
  56. package/dist/sdk/spawn-core.d.ts +3 -0
  57. package/dist/sdk/spawn-core.js +6 -8
  58. package/dist/sdk/spawn-core.js.map +1 -1
  59. package/dist/sdk/spawn.d.ts +8 -8
  60. package/dist/sdk/spawn.js +26 -3
  61. package/dist/sdk/spawn.js.map +1 -1
  62. package/dist/sdk/types.d.ts +7 -0
  63. package/dist/utils/command-checks.d.ts +8 -0
  64. package/dist/utils/command-checks.js +35 -9
  65. package/dist/utils/command-checks.js.map +1 -1
  66. package/dist/utils/dry-run.js +7 -1
  67. package/dist/utils/dry-run.js.map +1 -1
  68. package/dist/workflow-templates/github-issue-opened.caller.yml +2 -1
  69. package/dist/workflow-templates/github-issue-opened.ejected.yml +2 -1
  70. package/package.json +2 -1
  71. package/packages/agent-skill-config/dist/configs.js +4 -0
  72. package/packages/agent-skill-config/dist/git-exclude.d.ts +6 -2
  73. package/packages/agent-skill-config/dist/git-exclude.js +12 -12
  74. package/packages/memory/dist/index.js +914 -192
  75. package/packages/memory/dist/index.js.map +4 -4
  76. package/packages/superintendent/dist/index.d.ts +2 -1
  77. package/packages/superintendent/dist/runtime/loop.d.ts +1 -0
  78. package/packages/superintendent/dist/runtime/loop.js +14 -2
@@ -804,17 +804,17 @@ function isPidRunning(pid) {
804
804
  }
805
805
  function createDefaultFs() {
806
806
  return {
807
- open: (path32, flags) => fsPromises.open(path32, flags),
808
- readFile: (path32, encoding) => fsPromises.readFile(path32, encoding),
807
+ open: (path37, flags) => fsPromises.open(path37, flags),
808
+ readFile: (path37, encoding) => fsPromises.readFile(path37, encoding),
809
809
  stat: fsPromises.stat,
810
810
  unlink: fsPromises.unlink
811
811
  };
812
812
  }
813
- async function removeLockFile(fs3, lockPath, signal) {
813
+ async function removeLockFile(fs4, lockPath, signal) {
814
814
  for (let attempt = 0; attempt <= 4; attempt += 1) {
815
815
  throwIfAborted(signal);
816
816
  try {
817
- await fs3.unlink(lockPath);
817
+ await fs4.unlink(lockPath);
818
818
  return;
819
819
  } catch (error2) {
820
820
  if (hasErrorCode(error2, "ENOENT")) {
@@ -845,12 +845,12 @@ function parseLockMetadata(content) {
845
845
  }
846
846
  return void 0;
847
847
  }
848
- async function readLockMetadata(fs3, lockPath) {
849
- if (!fs3.readFile) {
848
+ async function readLockMetadata(fs4, lockPath) {
849
+ if (!fs4.readFile) {
850
850
  return void 0;
851
851
  }
852
852
  try {
853
- return parseLockMetadata(await fs3.readFile(lockPath, "utf8"));
853
+ return parseLockMetadata(await fs4.readFile(lockPath, "utf8"));
854
854
  } catch (error2) {
855
855
  if (hasErrorCode(error2, "ENOENT")) {
856
856
  return null;
@@ -884,7 +884,7 @@ async function writeLockMetadata(handle) {
884
884
  }
885
885
  }
886
886
  async function acquireFileLock(filePath, options = {}) {
887
- const fs3 = options.fs ?? createDefaultFs();
887
+ const fs4 = options.fs ?? createDefaultFs();
888
888
  const retries = options.retries ?? 20;
889
889
  const minTimeout = options.minTimeout ?? 25;
890
890
  const maxTimeout = options.maxTimeout ?? 250;
@@ -895,7 +895,7 @@ async function acquireFileLock(filePath, options = {}) {
895
895
  while (attempt <= retries) {
896
896
  throwIfAborted(options.signal);
897
897
  try {
898
- const handle = await fs3.open(lockPath, "wx");
898
+ const handle = await fs4.open(lockPath, "wx");
899
899
  await writeLockMetadata(handle);
900
900
  let released = false;
901
901
  return async () => {
@@ -903,7 +903,7 @@ async function acquireFileLock(filePath, options = {}) {
903
903
  return;
904
904
  }
905
905
  released = true;
906
- await removeLockFile(fs3, lockPath, options.signal);
906
+ await removeLockFile(fs4, lockPath, options.signal);
907
907
  };
908
908
  } catch (error2) {
909
909
  if (!hasErrorCode(error2, "EEXIST")) {
@@ -912,7 +912,7 @@ async function acquireFileLock(filePath, options = {}) {
912
912
  }
913
913
  let stat3;
914
914
  try {
915
- stat3 = await fs3.stat(lockPath);
915
+ stat3 = await fs4.stat(lockPath);
916
916
  } catch (statError) {
917
917
  if (hasErrorCode(statError, "ENOENT")) {
918
918
  continue;
@@ -920,7 +920,7 @@ async function acquireFileLock(filePath, options = {}) {
920
920
  throw statError;
921
921
  }
922
922
  const reclaimLock = await shouldReclaimLock({
923
- fs: fs3,
923
+ fs: fs4,
924
924
  isPidRunning: pidIsRunning,
925
925
  lockPath,
926
926
  staleMs,
@@ -930,7 +930,7 @@ async function acquireFileLock(filePath, options = {}) {
930
930
  continue;
931
931
  }
932
932
  if (reclaimLock) {
933
- await removeLockFile(fs3, lockPath, options.signal);
933
+ await removeLockFile(fs4, lockPath, options.signal);
934
934
  continue;
935
935
  }
936
936
  if (attempt >= retries) {
@@ -1107,6 +1107,24 @@ var codexAgent = {
1107
1107
  }
1108
1108
  };
1109
1109
 
1110
+ // packages/agent-defs/src/agents/gemini-cli.ts
1111
+ var geminiCliAgent = {
1112
+ id: "gemini-cli",
1113
+ name: "gemini-cli",
1114
+ aliases: ["gemini"],
1115
+ label: "Gemini CLI",
1116
+ summary: "Configure Google's Gemini CLI to use a compatible Google generations API.",
1117
+ binaryName: "gemini",
1118
+ configPath: "~/.gemini/settings.json",
1119
+ apiShapes: ["google-generations"],
1120
+ branding: {
1121
+ colors: {
1122
+ dark: "#8AB4F8",
1123
+ light: "#1A73E8"
1124
+ }
1125
+ }
1126
+ };
1127
+
1110
1128
  // packages/agent-defs/src/agents/opencode.ts
1111
1129
  var openCodeAgent = {
1112
1130
  id: "opencode",
@@ -1180,6 +1198,7 @@ var allAgents = [
1180
1198
  claudeCodeAgent,
1181
1199
  claudeDesktopAgent,
1182
1200
  codexAgent,
1201
+ geminiCliAgent,
1183
1202
  openCodeAgent,
1184
1203
  kimiAgent,
1185
1204
  gooseAgent,
@@ -1230,29 +1249,29 @@ function wrapForLogTee(argv, jobId) {
1230
1249
  return ["sh", "-c", script];
1231
1250
  }
1232
1251
  async function* streamLogFile(env, jobId, opts) {
1233
- const fs3 = env.fs ?? nodeFs;
1252
+ const fs4 = env.fs ?? nodeFs;
1234
1253
  const file = jobLogPath(jobId);
1235
1254
  let byteOffset = opts.sinceByte ?? 0;
1236
1255
  while (true) {
1237
- if (opts.since !== void 0 && !await wasModifiedSince(fs3, file, opts.since)) {
1238
- await waitForLogChange(fs3, file);
1256
+ if (opts.since !== void 0 && !await wasModifiedSince(fs4, file, opts.since)) {
1257
+ await waitForLogChange(fs4, file);
1239
1258
  continue;
1240
1259
  }
1241
- const result = await readLogChunk(fs3, file, byteOffset);
1260
+ const result = await readLogChunk(fs4, file, byteOffset);
1242
1261
  if (result !== null) {
1243
1262
  byteOffset = result.nextByteOffset;
1244
1263
  yield result.chunk;
1245
1264
  continue;
1246
1265
  }
1247
- await waitForLogChange(fs3, file);
1266
+ await waitForLogChange(fs4, file);
1248
1267
  }
1249
1268
  }
1250
- async function wasModifiedSince(fs3, file, since) {
1251
- if (fs3.promises.stat === void 0) {
1269
+ async function wasModifiedSince(fs4, file, since) {
1270
+ if (fs4.promises.stat === void 0) {
1252
1271
  return true;
1253
1272
  }
1254
1273
  try {
1255
- const stat3 = await fs3.promises.stat(file);
1274
+ const stat3 = await fs4.promises.stat(file);
1256
1275
  return stat3.mtimeMs >= since.getTime();
1257
1276
  } catch (error2) {
1258
1277
  if (isNodeError(error2) && error2.code === "ENOENT") {
@@ -1262,11 +1281,11 @@ async function wasModifiedSince(fs3, file, since) {
1262
1281
  }
1263
1282
  }
1264
1283
  async function waitForExit(env, jobId, opts = {}) {
1265
- const fs3 = env.fs ?? nodeFs;
1284
+ const fs4 = env.fs ?? nodeFs;
1266
1285
  const file = jobExitPath(jobId);
1267
1286
  while (true) {
1268
1287
  throwIfAborted2(opts.signal);
1269
- const contents = await readTextFileIfExists(fs3, file);
1288
+ const contents = await readTextFileIfExists(fs4, file);
1270
1289
  if (contents !== null) {
1271
1290
  const text5 = contents.trim();
1272
1291
  const exitCode = Number(text5);
@@ -1284,8 +1303,8 @@ function jobLogPath(jobId) {
1284
1303
  function jobExitPath(jobId) {
1285
1304
  return `${JOB_DIR}/${jobId}.exit`;
1286
1305
  }
1287
- async function readLogChunk(fs3, file, byteOffset) {
1288
- const contents = await readFileIfExists(fs3, file);
1306
+ async function readLogChunk(fs4, file, byteOffset) {
1307
+ const contents = await readFileIfExists(fs4, file);
1289
1308
  if (contents === null || byteOffset >= contents.byteLength) {
1290
1309
  return null;
1291
1310
  }
@@ -1297,13 +1316,13 @@ async function readLogChunk(fs3, file, byteOffset) {
1297
1316
  nextByteOffset: contents.byteLength
1298
1317
  };
1299
1318
  }
1300
- async function readTextFileIfExists(fs3, file) {
1301
- const contents = await readFileIfExists(fs3, file);
1319
+ async function readTextFileIfExists(fs4, file) {
1320
+ const contents = await readFileIfExists(fs4, file);
1302
1321
  return contents?.toString("utf8") ?? null;
1303
1322
  }
1304
- async function readFileIfExists(fs3, file) {
1323
+ async function readFileIfExists(fs4, file) {
1305
1324
  try {
1306
- const contents = await fs3.promises.readFile(file);
1325
+ const contents = await fs4.promises.readFile(file);
1307
1326
  return Buffer.isBuffer(contents) ? contents : Buffer.from(contents);
1308
1327
  } catch (error2) {
1309
1328
  if (isNodeError(error2) && error2.code === "ENOENT") {
@@ -1312,8 +1331,8 @@ async function readFileIfExists(fs3, file) {
1312
1331
  throw error2;
1313
1332
  }
1314
1333
  }
1315
- async function waitForLogChange(fs3, file) {
1316
- const watch = fs3.watch;
1334
+ async function waitForLogChange(fs4, file) {
1335
+ const watch = fs4.watch;
1317
1336
  if (typeof watch !== "function") {
1318
1337
  await sleep2(POLL_INTERVAL_MS);
1319
1338
  return;
@@ -2279,7 +2298,7 @@ import path14 from "node:path";
2279
2298
 
2280
2299
  // packages/config-extends/src/discover.ts
2281
2300
  import path10 from "node:path";
2282
- async function findBase(name, bases, fs3) {
2301
+ async function findBase(name, bases, fs4) {
2283
2302
  const checkedPaths = [];
2284
2303
  for (const basePath of bases) {
2285
2304
  for (const extension of [".md", ".yaml", ".yml", ".json"]) {
@@ -2287,7 +2306,7 @@ async function findBase(name, bases, fs3) {
2287
2306
  checkedPaths.push(filePath);
2288
2307
  try {
2289
2308
  return {
2290
- content: await fs3.readFile(filePath, "utf8"),
2309
+ content: await fs4.readFile(filePath, "utf8"),
2291
2310
  filePath
2292
2311
  };
2293
2312
  } catch (error2) {
@@ -2367,11 +2386,11 @@ function stripBom(content) {
2367
2386
  function mergeLayers(layers) {
2368
2387
  return mergeObjectLayers(layers, []);
2369
2388
  }
2370
- function mergeObjectLayers(layers, path32) {
2389
+ function mergeObjectLayers(layers, path37) {
2371
2390
  const data = {};
2372
2391
  const sources = {};
2373
2392
  for (const key of collectKeys(layers)) {
2374
- const resolved = resolveKey(layers, key, path32);
2393
+ const resolved = resolveKey(layers, key, path37);
2375
2394
  if (resolved === void 0) {
2376
2395
  continue;
2377
2396
  }
@@ -2389,7 +2408,7 @@ function collectKeys(layers) {
2389
2408
  }
2390
2409
  return [...keys];
2391
2410
  }
2392
- function resolveKey(layers, key, path32) {
2411
+ function resolveKey(layers, key, path37) {
2393
2412
  let winningSource;
2394
2413
  let winningValue;
2395
2414
  const objectLayers = [];
@@ -2419,9 +2438,9 @@ function resolveKey(layers, key, path32) {
2419
2438
  if (winningSource === void 0) {
2420
2439
  return void 0;
2421
2440
  }
2422
- const fullPath = buildPath(path32, key);
2441
+ const fullPath = buildPath(path37, key);
2423
2442
  if (isPlainObject(winningValue)) {
2424
- const merged = mergeObjectLayers(objectLayers, [...path32, key]);
2443
+ const merged = mergeObjectLayers(objectLayers, [...path37, key]);
2425
2444
  return {
2426
2445
  value: merged.data,
2427
2446
  sources: {
@@ -2446,8 +2465,8 @@ function isWinningCandidate(key, value) {
2446
2465
  }
2447
2466
  return true;
2448
2467
  }
2449
- function buildPath(path32, key) {
2450
- return [...path32, key].join(".");
2468
+ function buildPath(path37, key) {
2469
+ return [...path37, key].join(".");
2451
2470
  }
2452
2471
  function isPlainObject(value) {
2453
2472
  if (value === null || Array.isArray(value) || typeof value !== "object") {
@@ -3801,16 +3820,16 @@ function getConfigFormat(pathOrFormat) {
3801
3820
  }
3802
3821
  return formatRegistry[formatName];
3803
3822
  }
3804
- function detectFormat2(path32) {
3805
- const ext = getExtension(path32);
3823
+ function detectFormat2(path37) {
3824
+ const ext = getExtension(path37);
3806
3825
  return extensionMap[ext];
3807
3826
  }
3808
- function getExtension(path32) {
3809
- const lastDot = path32.lastIndexOf(".");
3827
+ function getExtension(path37) {
3828
+ const lastDot = path37.lastIndexOf(".");
3810
3829
  if (lastDot === -1) {
3811
3830
  return "";
3812
3831
  }
3813
- return path32.slice(lastDot).toLowerCase();
3832
+ return path37.slice(lastDot).toLowerCase();
3814
3833
  }
3815
3834
 
3816
3835
  // packages/config-mutations/src/execution/path-utils.ts
@@ -3861,9 +3880,9 @@ function resolvePath(rawPath, homeDir, pathMapper) {
3861
3880
  function isNotFound(error2) {
3862
3881
  return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === "ENOENT";
3863
3882
  }
3864
- async function readFileIfExists2(fs3, target) {
3883
+ async function readFileIfExists2(fs4, target) {
3865
3884
  try {
3866
- return await fs3.readFile(target, "utf8");
3885
+ return await fs4.readFile(target, "utf8");
3867
3886
  } catch (error2) {
3868
3887
  if (isNotFound(error2)) {
3869
3888
  return null;
@@ -3871,9 +3890,9 @@ async function readFileIfExists2(fs3, target) {
3871
3890
  throw error2;
3872
3891
  }
3873
3892
  }
3874
- async function pathExists(fs3, target) {
3893
+ async function pathExists(fs4, target) {
3875
3894
  try {
3876
- await fs3.stat(target);
3895
+ await fs4.stat(target);
3877
3896
  return true;
3878
3897
  } catch (error2) {
3879
3898
  if (isNotFound(error2)) {
@@ -3897,9 +3916,9 @@ function createInvalidDocumentBackupPath(targetPath) {
3897
3916
  const ext = targetPath.includes(".") ? targetPath.split(".").pop() : "bak";
3898
3917
  return `${targetPath}.invalid-${createTimestamp()}.${ext}`;
3899
3918
  }
3900
- async function backupInvalidDocument(fs3, targetPath, content) {
3919
+ async function backupInvalidDocument(fs4, targetPath, content) {
3901
3920
  const backupPath = createInvalidDocumentBackupPath(targetPath);
3902
- await fs3.writeFile(backupPath, content, { encoding: "utf8" });
3921
+ await fs4.writeFile(backupPath, content, { encoding: "utf8" });
3903
3922
  }
3904
3923
  function describeMutation(kind, targetPath) {
3905
3924
  const displayPath = targetPath ?? "target";
@@ -4457,12 +4476,12 @@ async function executeMutation(mutation, context, options) {
4457
4476
  }
4458
4477
 
4459
4478
  // packages/poe-code-config/src/store.ts
4460
- async function readMergedDocument(fs3, globalPath, projectPath) {
4461
- const globalDocument = await readStoredDocument(fs3, globalPath);
4479
+ async function readMergedDocument(fs4, globalPath, projectPath) {
4480
+ const globalDocument = await readStoredDocument(fs4, globalPath);
4462
4481
  if (!projectPath || projectPath === globalPath) {
4463
4482
  return globalDocument.data;
4464
4483
  }
4465
- const projectDocument = await readStoredDocument(fs3, projectPath);
4484
+ const projectDocument = await readStoredDocument(fs4, projectPath);
4466
4485
  const resolved = await resolve(
4467
4486
  [
4468
4487
  {
@@ -4476,16 +4495,16 @@ async function readMergedDocument(fs3, globalPath, projectPath) {
4476
4495
  }
4477
4496
  ],
4478
4497
  {
4479
- fs: createResolvedConfigFs(fs3, globalPath, globalDocument.content),
4498
+ fs: createResolvedConfigFs(fs4, globalPath, globalDocument.content),
4480
4499
  autoExtend: true
4481
4500
  }
4482
4501
  );
4483
4502
  return normalizeDocument(resolved.data);
4484
4503
  }
4485
- async function readStoredDocument(fs3, filePath) {
4504
+ async function readStoredDocument(fs4, filePath) {
4486
4505
  try {
4487
- const raw = await fs3.readFile(filePath, "utf8");
4488
- return await parseStoredDocument(fs3, filePath, raw);
4506
+ const raw = await fs4.readFile(filePath, "utf8");
4507
+ return await parseStoredDocument(fs4, filePath, raw);
4489
4508
  } catch (error2) {
4490
4509
  if (isNotFound(error2)) {
4491
4510
  return {
@@ -4496,7 +4515,7 @@ async function readStoredDocument(fs3, filePath) {
4496
4515
  throw error2;
4497
4516
  }
4498
4517
  }
4499
- async function parseStoredDocument(fs3, filePath, raw) {
4518
+ async function parseStoredDocument(fs4, filePath, raw) {
4500
4519
  try {
4501
4520
  return {
4502
4521
  content: raw,
@@ -4504,7 +4523,7 @@ async function parseStoredDocument(fs3, filePath, raw) {
4504
4523
  };
4505
4524
  } catch (error2) {
4506
4525
  if (error2 instanceof SyntaxError) {
4507
- await recoverInvalidDocument(fs3, filePath, raw);
4526
+ await recoverInvalidDocument(fs4, filePath, raw);
4508
4527
  return {
4509
4528
  content: EMPTY_DOCUMENT,
4510
4529
  data: {}
@@ -4538,21 +4557,21 @@ function normalizeScopeValues(value) {
4538
4557
  }
4539
4558
  return normalized;
4540
4559
  }
4541
- function createResolvedConfigFs(fs3, globalPath, globalContent) {
4560
+ function createResolvedConfigFs(fs4, globalPath, globalContent) {
4542
4561
  return {
4543
4562
  readFile(filePath, _encoding) {
4544
4563
  if (filePath === globalPath) {
4545
4564
  return Promise.resolve(globalContent);
4546
4565
  }
4547
- return fs3.readFile(filePath, "utf8");
4566
+ return fs4.readFile(filePath, "utf8");
4548
4567
  }
4549
4568
  };
4550
4569
  }
4551
- async function recoverInvalidDocument(fs3, filePath, content) {
4552
- await fs3.mkdir(path14.dirname(filePath), { recursive: true });
4570
+ async function recoverInvalidDocument(fs4, filePath, content) {
4571
+ await fs4.mkdir(path14.dirname(filePath), { recursive: true });
4553
4572
  const backupPath = createInvalidBackupPath(filePath);
4554
- await fs3.writeFile(backupPath, content, { encoding: "utf8" });
4555
- await fs3.writeFile(filePath, EMPTY_DOCUMENT, { encoding: "utf8" });
4573
+ await fs4.writeFile(backupPath, content, { encoding: "utf8" });
4574
+ await fs4.writeFile(filePath, EMPTY_DOCUMENT, { encoding: "utf8" });
4556
4575
  }
4557
4576
  function createInvalidBackupPath(filePath) {
4558
4577
  const directory = path14.dirname(filePath);
@@ -4676,7 +4695,7 @@ function mergeScope(scope, baseScope, overrideScope) {
4676
4695
  ...Object.fromEntries(scopeEntries)
4677
4696
  };
4678
4697
  }
4679
- function mergeRuntimeScope(baseScope, overrideScope, path32 = []) {
4698
+ function mergeRuntimeScope(baseScope, overrideScope, path37 = []) {
4680
4699
  const merged = {};
4681
4700
  const keys = /* @__PURE__ */ new Set([...Object.keys(baseScope), ...Object.keys(overrideScope)]);
4682
4701
  for (const key of keys) {
@@ -4688,20 +4707,20 @@ function mergeRuntimeScope(baseScope, overrideScope, path32 = []) {
4688
4707
  }
4689
4708
  continue;
4690
4709
  }
4691
- if (isRuntimeConcatenativeArray([...path32, key]) && Array.isArray(baseValue) && Array.isArray(overrideValue)) {
4710
+ if (isRuntimeConcatenativeArray([...path37, key]) && Array.isArray(baseValue) && Array.isArray(overrideValue)) {
4692
4711
  merged[key] = [...baseValue, ...overrideValue];
4693
4712
  continue;
4694
4713
  }
4695
4714
  if (isRecord4(baseValue) && isRecord4(overrideValue)) {
4696
- merged[key] = mergeRuntimeScope(baseValue, overrideValue, [...path32, key]);
4715
+ merged[key] = mergeRuntimeScope(baseValue, overrideValue, [...path37, key]);
4697
4716
  continue;
4698
4717
  }
4699
4718
  merged[key] = overrideValue;
4700
4719
  }
4701
4720
  return merged;
4702
4721
  }
4703
- function isRuntimeConcatenativeArray(path32) {
4704
- return path32.join(".") === "mounts" || path32.join(".") === "runner.workspace.exclude";
4722
+ function isRuntimeConcatenativeArray(path37) {
4723
+ return path37.join(".") === "mounts" || path37.join(".") === "runner.workspace.exclude";
4705
4724
  }
4706
4725
  function isRecord4(value) {
4707
4726
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
@@ -4889,6 +4908,7 @@ var poeProvider = {
4889
4908
  label: "Poe",
4890
4909
  summary: "Route AI coding agents through Poe's API.",
4891
4910
  baseUrl: "https://api.poe.com",
4911
+ agentBaseUrl: "https://api.poe.com",
4892
4912
  auth: {
4893
4913
  kind: "api-key",
4894
4914
  envVar: "POE_API_KEY",
@@ -4896,6 +4916,12 @@ var poeProvider = {
4896
4916
  prompt: { title: "Poe API key" },
4897
4917
  preferredLogin: "oauth"
4898
4918
  },
4919
+ env: {
4920
+ ANTHROPIC_CUSTOM_HEADERS: {
4921
+ kind: "providerCredential",
4922
+ prefix: "Authorization: Bearer "
4923
+ }
4924
+ },
4899
4925
  apiShapes: [
4900
4926
  {
4901
4927
  id: "openai-chat-completions",
@@ -4924,6 +4950,9 @@ var anthropicProvider = {
4924
4950
  storageKey: "provider:anthropic",
4925
4951
  prompt: { title: "Anthropic API key" }
4926
4952
  },
4953
+ env: {
4954
+ ANTHROPIC_API_KEY: { kind: "providerCredential" }
4955
+ },
4927
4956
  apiShapes: [
4928
4957
  {
4929
4958
  id: "anthropic-messages",
@@ -4946,6 +4975,12 @@ var cloudflareProvider = {
4946
4975
  storageKey: "provider:cloudflare",
4947
4976
  prompt: { title: "Cloudflare AI Gateway token" }
4948
4977
  },
4978
+ env: {
4979
+ ANTHROPIC_CUSTOM_HEADERS: {
4980
+ kind: "providerCredential",
4981
+ prefix: "Authorization: Bearer "
4982
+ }
4983
+ },
4949
4984
  apiShapes: [
4950
4985
  {
4951
4986
  id: "openai-chat-completions",
@@ -4990,7 +5025,7 @@ function isNotFoundError(error2) {
4990
5025
  }
4991
5026
 
4992
5027
  // packages/poe-code-config/src/state/jobs.ts
4993
- function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5028
+ function createJobRegistry(homeDir, fs4 = defaultStateFs) {
4994
5029
  const jobsDir = path17.join(homeDir, ".poe-code", "state", "jobs");
4995
5030
  function jobPath(id) {
4996
5031
  assertSafeJobId(id);
@@ -4998,7 +5033,7 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
4998
5033
  }
4999
5034
  async function get(id) {
5000
5035
  try {
5001
- return parseJobEntry(await fs3.readFile(jobPath(id), "utf8"));
5036
+ return parseJobEntry(await fs4.readFile(jobPath(id), "utf8"));
5002
5037
  } catch (error2) {
5003
5038
  if (isNotFoundError(error2)) {
5004
5039
  return null;
@@ -5009,8 +5044,8 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5009
5044
  async function put(entry) {
5010
5045
  assertJobEntry(entry);
5011
5046
  const filePath = jobPath(entry.id);
5012
- await fs3.mkdir(jobsDir, { recursive: true });
5013
- const release = await acquireFileLock(filePath, { fs: fs3 });
5047
+ await fs4.mkdir(jobsDir, { recursive: true });
5048
+ const release = await acquireFileLock(filePath, { fs: fs4 });
5014
5049
  try {
5015
5050
  await writeJobAtomically(filePath, entry);
5016
5051
  } finally {
@@ -5019,8 +5054,8 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5019
5054
  }
5020
5055
  async function update(id, patch) {
5021
5056
  const filePath = jobPath(id);
5022
- await fs3.mkdir(jobsDir, { recursive: true });
5023
- const release = await acquireFileLock(filePath, { fs: fs3 });
5057
+ await fs4.mkdir(jobsDir, { recursive: true });
5058
+ const release = await acquireFileLock(filePath, { fs: fs4 });
5024
5059
  try {
5025
5060
  const current = await get(id);
5026
5061
  if (current === null) {
@@ -5041,7 +5076,7 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5041
5076
  async function list(filter = {}) {
5042
5077
  let entries;
5043
5078
  try {
5044
- entries = await fs3.readdir(jobsDir);
5079
+ entries = await fs4.readdir(jobsDir);
5045
5080
  } catch (error2) {
5046
5081
  if (isNotFoundError(error2)) {
5047
5082
  return [];
@@ -5054,11 +5089,11 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5054
5089
  continue;
5055
5090
  }
5056
5091
  const filePath = path17.join(jobsDir, entry);
5057
- const stat3 = await fs3.stat(filePath);
5092
+ const stat3 = await fs4.stat(filePath);
5058
5093
  if (!stat3.isFile()) {
5059
5094
  continue;
5060
5095
  }
5061
- const job = parseJobEntry(await fs3.readFile(filePath, "utf8"));
5096
+ const job = parseJobEntry(await fs4.readFile(filePath, "utf8"));
5062
5097
  if (matchesFilter(job, filter)) {
5063
5098
  jobs.push(job);
5064
5099
  }
@@ -5068,16 +5103,16 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5068
5103
  async function remove2(id) {
5069
5104
  const filePath = jobPath(id);
5070
5105
  try {
5071
- await fs3.stat(jobsDir);
5106
+ await fs4.stat(jobsDir);
5072
5107
  } catch (error2) {
5073
5108
  if (isNotFoundError(error2)) {
5074
5109
  return;
5075
5110
  }
5076
5111
  throw error2;
5077
5112
  }
5078
- const release = await acquireFileLock(filePath, { fs: fs3 });
5113
+ const release = await acquireFileLock(filePath, { fs: fs4 });
5079
5114
  try {
5080
- await fs3.unlink(filePath);
5115
+ await fs4.unlink(filePath);
5081
5116
  } catch (error2) {
5082
5117
  if (!isNotFoundError(error2)) {
5083
5118
  throw error2;
@@ -5087,14 +5122,14 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5087
5122
  }
5088
5123
  }
5089
5124
  async function writeJobAtomically(filePath, entry) {
5090
- await fs3.mkdir(path17.dirname(filePath), { recursive: true });
5125
+ await fs4.mkdir(path17.dirname(filePath), { recursive: true });
5091
5126
  const tempPath = `${filePath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
5092
5127
  try {
5093
- await fs3.writeFile(tempPath, `${JSON.stringify(entry, null, 2)}
5128
+ await fs4.writeFile(tempPath, `${JSON.stringify(entry, null, 2)}
5094
5129
  `, {
5095
5130
  encoding: "utf8"
5096
5131
  });
5097
- await fs3.rename(tempPath, filePath);
5132
+ await fs4.rename(tempPath, filePath);
5098
5133
  } catch (error2) {
5099
5134
  await removeTempFile(tempPath);
5100
5135
  throw error2;
@@ -5102,7 +5137,7 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5102
5137
  }
5103
5138
  async function removeTempFile(tempPath) {
5104
5139
  try {
5105
- await fs3.unlink(tempPath);
5140
+ await fs4.unlink(tempPath);
5106
5141
  } catch (error2) {
5107
5142
  if (!isNotFoundError(error2)) {
5108
5143
  throw error2;
@@ -5149,11 +5184,11 @@ function isRecord5(value) {
5149
5184
 
5150
5185
  // packages/poe-code-config/src/state/templates.ts
5151
5186
  import path18 from "node:path";
5152
- function createTemplateRegistry(homeDir, fs3 = defaultStateFs) {
5187
+ function createTemplateRegistry(homeDir, fs4 = defaultStateFs) {
5153
5188
  const filePath = path18.join(homeDir, ".poe-code", "state", "templates.json");
5154
5189
  async function readState() {
5155
5190
  try {
5156
- const raw = await fs3.readFile(filePath, "utf8");
5191
+ const raw = await fs4.readFile(filePath, "utf8");
5157
5192
  return normalizeTemplateState(JSON.parse(raw));
5158
5193
  } catch (error2) {
5159
5194
  if (isNotFoundError(error2)) {
@@ -5163,14 +5198,14 @@ function createTemplateRegistry(homeDir, fs3 = defaultStateFs) {
5163
5198
  }
5164
5199
  }
5165
5200
  async function writeState(state) {
5166
- await fs3.writeFile(filePath, `${JSON.stringify(state, null, 2)}
5201
+ await fs4.writeFile(filePath, `${JSON.stringify(state, null, 2)}
5167
5202
  `, {
5168
5203
  encoding: "utf8"
5169
5204
  });
5170
5205
  }
5171
5206
  async function updateState(mutator) {
5172
- await fs3.mkdir(path18.dirname(filePath), { recursive: true });
5173
- const release = await acquireFileLock(filePath, { fs: fs3 });
5207
+ await fs4.mkdir(path18.dirname(filePath), { recursive: true });
5208
+ const release = await acquireFileLock(filePath, { fs: fs4 });
5174
5209
  try {
5175
5210
  const state = await readState();
5176
5211
  mutator(state);
@@ -5240,10 +5275,10 @@ function isRecord6(value) {
5240
5275
  }
5241
5276
 
5242
5277
  // packages/poe-code-config/src/state/index.ts
5243
- function createStateManager(homeDir, fs3) {
5278
+ function createStateManager(homeDir, fs4) {
5244
5279
  return {
5245
- templates: createTemplateRegistry(homeDir, fs3),
5246
- jobs: createJobRegistry(homeDir, fs3)
5280
+ templates: createTemplateRegistry(homeDir, fs4),
5281
+ jobs: createJobRegistry(homeDir, fs4)
5247
5282
  };
5248
5283
  }
5249
5284
 
@@ -5558,7 +5593,7 @@ import { PassThrough as PassThrough2, Writable as Writable2 } from "node:stream"
5558
5593
  import path21 from "node:path";
5559
5594
  var JOB_DIR2 = "/tmp/poe-jobs";
5560
5595
  function createE2bJobHandle(input) {
5561
- const fs3 = createE2bLogStreamFs(input.sandbox);
5596
+ const fs4 = createE2bLogStreamFs(input.sandbox);
5562
5597
  return {
5563
5598
  id: input.jobId,
5564
5599
  envId: input.envId,
@@ -5574,10 +5609,10 @@ function createE2bJobHandle(input) {
5574
5609
  return isRunning ? "running" : "lost";
5575
5610
  },
5576
5611
  stream(opts = {}) {
5577
- return streamLogFile({ fs: fs3 }, input.jobId, opts);
5612
+ return streamLogFile({ fs: fs4 }, input.jobId, opts);
5578
5613
  },
5579
5614
  async wait() {
5580
- const result = await waitForExit({ fs: fs3 }, input.jobId);
5615
+ const result = await waitForExit({ fs: fs4 }, input.jobId);
5581
5616
  const preserveMs = input.preserveAfterExitHours * 60 * 60 * 1e3;
5582
5617
  if (preserveMs > 0) {
5583
5618
  await input.sandbox.setTimeout(preserveMs);
@@ -6074,10 +6109,10 @@ var e2bAuthScope = defineScope("e2b", {
6074
6109
  });
6075
6110
  async function resolveE2bApiKey(input) {
6076
6111
  const homeDir = input.homeDir ?? os4.homedir();
6077
- const fs3 = input.fs ?? nodeFs4;
6112
+ const fs4 = input.fs ?? nodeFs4;
6078
6113
  const env = input.env ?? process.env;
6079
6114
  const document = await readMergedDocument(
6080
- fs3,
6115
+ fs4,
6081
6116
  resolveConfigPath(homeDir),
6082
6117
  resolveProjectConfigPath(input.cwd)
6083
6118
  );
@@ -6584,6 +6619,23 @@ var gooseAcpSpawnConfig = {
6584
6619
  skipAuth: true
6585
6620
  };
6586
6621
 
6622
+ // packages/agent-spawn/src/configs/gemini-cli.ts
6623
+ var geminiCliAcpSpawnConfig = {
6624
+ kind: "acp",
6625
+ agentId: "gemini-cli",
6626
+ acpArgs: ({ model, mcpServers }) => [
6627
+ "--acp",
6628
+ ...model ? ["--model", model] : [],
6629
+ ...mcpServers ? ["--allowed-mcp-server-names", Object.keys(mcpServers).join(",")] : [],
6630
+ ...mcpServers ? ["--skip-trust"] : [],
6631
+ "--yolo"
6632
+ ],
6633
+ env: {
6634
+ GEMINI_SANDBOX: "false"
6635
+ },
6636
+ skipAuth: true
6637
+ };
6638
+
6587
6639
  // packages/agent-spawn/src/configs/index.ts
6588
6640
  var allSpawnConfigs = [
6589
6641
  claudeCodeSpawnConfig,
@@ -6600,6 +6652,7 @@ var acpLookup = /* @__PURE__ */ new Map();
6600
6652
  acpLookup.set(openCodeAcpSpawnConfig.agentId, openCodeAcpSpawnConfig);
6601
6653
  acpLookup.set(kimiAcpSpawnConfig.agentId, kimiAcpSpawnConfig);
6602
6654
  acpLookup.set(gooseAcpSpawnConfig.agentId, gooseAcpSpawnConfig);
6655
+ acpLookup.set(geminiCliAcpSpawnConfig.agentId, geminiCliAcpSpawnConfig);
6603
6656
  function getSpawnConfig(input) {
6604
6657
  const resolvedId = resolveAgentId(input);
6605
6658
  if (!resolvedId) {
@@ -6619,8 +6672,8 @@ function listMcpSupportedAgents() {
6619
6672
  }
6620
6673
 
6621
6674
  // packages/agent-spawn/src/spawn.ts
6622
- import { mkdirSync as mkdirSync3, openSync, writeSync, closeSync } from "node:fs";
6623
- import path29 from "node:path";
6675
+ import { mkdirSync as mkdirSync5, openSync as openSync2, writeSync, closeSync as closeSync2 } from "node:fs";
6676
+ import path34 from "node:path";
6624
6677
 
6625
6678
  // packages/agent-spawn/src/configs/resolve-config.ts
6626
6679
  function resolveConfig(agentId) {
@@ -7021,7 +7074,7 @@ function createEventQueue() {
7021
7074
 
7022
7075
  // packages/agent-spawn/src/skill-bridge.ts
7023
7076
  import crypto from "node:crypto";
7024
- import os6 from "node:os";
7077
+ import os7 from "node:os";
7025
7078
 
7026
7079
  // packages/agent-skill-config/src/configs.ts
7027
7080
  import os5 from "node:os";
@@ -7035,6 +7088,10 @@ var agentSkillConfigs = {
7035
7088
  globalSkillDir: "~/.codex/skills",
7036
7089
  localSkillDir: ".codex/skills"
7037
7090
  },
7091
+ "gemini-cli": {
7092
+ globalSkillDir: "~/.gemini/skills",
7093
+ localSkillDir: ".gemini/skills"
7094
+ },
7038
7095
  opencode: {
7039
7096
  globalSkillDir: "~/.config/opencode/skills",
7040
7097
  localSkillDir: ".opencode/skills"
@@ -7177,7 +7234,7 @@ function resolveSkillReference(ref, cwd, homeDir) {
7177
7234
  import { execFileSync } from "node:child_process";
7178
7235
  import * as fs from "node:fs";
7179
7236
  import path27 from "node:path";
7180
- var markerPrefix = "# poe-code-spawn-skills:";
7237
+ var defaultMarkerPrefix = "poe-code-spawn-skills";
7181
7238
  function defaultGitDirRunner(cwd) {
7182
7239
  try {
7183
7240
  return execFileSync("git", ["rev-parse", "--git-dir"], {
@@ -7197,10 +7254,10 @@ function resolveExcludePath(cwd) {
7197
7254
  }
7198
7255
  return path27.join(path27.isAbsolute(gitDir) ? gitDir : path27.resolve(cwd, gitDir), "info/exclude");
7199
7256
  }
7200
- function markers(runId) {
7257
+ function markers(runId, markerPrefix) {
7201
7258
  return {
7202
- begin: `${markerPrefix}${runId} begin`,
7203
- end: `${markerPrefix}${runId} end`
7259
+ begin: `# ${markerPrefix}:${runId} begin`,
7260
+ end: `# ${markerPrefix}:${runId} end`
7204
7261
  };
7205
7262
  }
7206
7263
  function readExcludeFile(excludePath) {
@@ -7216,8 +7273,8 @@ function readExcludeFile(excludePath) {
7216
7273
  function isNodeError2(error2) {
7217
7274
  return error2 instanceof Error && "code" in error2;
7218
7275
  }
7219
- function removeBlock(content, runId) {
7220
- const { begin, end } = markers(runId);
7276
+ function removeBlock(content, runId, markerPrefix) {
7277
+ const { begin, end } = markers(runId, markerPrefix);
7221
7278
  const lines = content.split("\n");
7222
7279
  const result = [];
7223
7280
  for (let index = 0; index < lines.length; index += 1) {
@@ -7232,23 +7289,27 @@ function removeBlock(content, runId) {
7232
7289
  }
7233
7290
  return result.join("\n");
7234
7291
  }
7235
- function appendBlock(content, runId, entries) {
7236
- const { begin, end } = markers(runId);
7292
+ function appendBlock(content, runId, entries, markerPrefix) {
7293
+ const { begin, end } = markers(runId, markerPrefix);
7237
7294
  const existing = content ?? "";
7238
7295
  const prefix = existing.length === 0 || existing.endsWith("\n") ? existing : `${existing}
7239
7296
  `;
7240
7297
  return `${prefix}${[begin, ...entries, end, ""].join("\n")}`;
7241
7298
  }
7242
- function appendExcludeBlock(cwd, runId, entries) {
7299
+ function appendExcludeBlock(cwd, runId, entries, opts) {
7243
7300
  const excludePath = resolveExcludePath(cwd);
7244
7301
  if (excludePath === void 0) {
7245
7302
  return;
7246
7303
  }
7247
7304
  fs.mkdirSync(path27.dirname(excludePath), { recursive: true });
7248
7305
  const content = readExcludeFile(excludePath);
7249
- fs.writeFileSync(excludePath, appendBlock(content, runId, entries), "utf8");
7306
+ fs.writeFileSync(
7307
+ excludePath,
7308
+ appendBlock(content, runId, entries, opts?.markerPrefix ?? defaultMarkerPrefix),
7309
+ "utf8"
7310
+ );
7250
7311
  }
7251
- function removeExcludeBlock(cwd, runId) {
7312
+ function removeExcludeBlock(cwd, runId, opts) {
7252
7313
  const excludePath = resolveExcludePath(cwd);
7253
7314
  if (excludePath === void 0) {
7254
7315
  return;
@@ -7257,7 +7318,11 @@ function removeExcludeBlock(cwd, runId) {
7257
7318
  if (content === void 0) {
7258
7319
  return;
7259
7320
  }
7260
- fs.writeFileSync(excludePath, removeBlock(content, runId), "utf8");
7321
+ fs.writeFileSync(
7322
+ excludePath,
7323
+ removeBlock(content, runId, opts?.markerPrefix ?? defaultMarkerPrefix),
7324
+ "utf8"
7325
+ );
7261
7326
  }
7262
7327
 
7263
7328
  // packages/agent-skill-config/src/bridge-active-skills.ts
@@ -7463,22 +7528,679 @@ function cleanupBridgedSkills(manifest) {
7463
7528
  removeExcludeBlock(manifest.cwd, manifest.runId);
7464
7529
  }
7465
7530
 
7531
+ // packages/agent-hook-config/src/configs.ts
7532
+ import os6 from "node:os";
7533
+ import path29 from "node:path";
7534
+ var agentHookConfigs = {
7535
+ "claude-code": {
7536
+ globalHookPath: "~/.claude/settings.json",
7537
+ localHookPath: ".claude/settings.json",
7538
+ format: "claude-settings-json",
7539
+ supportedEvents: [
7540
+ "SessionStart",
7541
+ "SessionEnd",
7542
+ "UserPromptSubmit",
7543
+ "PreToolUse",
7544
+ "PostToolUse",
7545
+ "PermissionRequest",
7546
+ "Stop",
7547
+ "StopFailure",
7548
+ "Notification",
7549
+ "PreCompact",
7550
+ "PostCompact",
7551
+ "SubagentStart",
7552
+ "SubagentStop"
7553
+ ],
7554
+ supportedHandlerTypes: ["command", "http", "mcp_tool", "prompt", "agent"],
7555
+ placeholders: {
7556
+ projectDir: "${CLAUDE_PROJECT_DIR}",
7557
+ pluginRoot: "${CLAUDE_PLUGIN_ROOT}",
7558
+ pluginData: "${CLAUDE_PLUGIN_DATA}"
7559
+ }
7560
+ },
7561
+ codex: {
7562
+ globalHookPath: "~/.codex/hooks.json",
7563
+ localHookPath: ".codex/hooks.json",
7564
+ format: "codex-hooks-json",
7565
+ supportedEvents: [
7566
+ "SessionStart",
7567
+ "UserPromptSubmit",
7568
+ "PreToolUse",
7569
+ "PostToolUse",
7570
+ "PermissionRequest",
7571
+ "Stop"
7572
+ ],
7573
+ supportedHandlerTypes: ["command"],
7574
+ placeholders: {
7575
+ projectDir: "$(git rev-parse --show-toplevel)",
7576
+ pluginRoot: "$PLUGIN_ROOT",
7577
+ pluginData: "$PLUGIN_DATA"
7578
+ }
7579
+ }
7580
+ };
7581
+ var supportedHookAgents = Object.keys(agentHookConfigs);
7582
+ function resolveAgentSupport2(input, registry = agentHookConfigs) {
7583
+ const resolvedId = resolveAgentId(input);
7584
+ if (!resolvedId) {
7585
+ return { status: "unknown", input };
7586
+ }
7587
+ const config = registry[resolvedId];
7588
+ if (!config) {
7589
+ return { status: "unsupported", input, id: resolvedId };
7590
+ }
7591
+ return { status: "supported", input, id: resolvedId, config };
7592
+ }
7593
+ function getAgentConfig2(agentId) {
7594
+ const support = resolveAgentSupport2(agentId);
7595
+ return support.status === "supported" ? support.config : void 0;
7596
+ }
7597
+ function expandHome3(targetPath, homeDir = os6.homedir()) {
7598
+ if (!targetPath?.startsWith("~")) {
7599
+ return targetPath;
7600
+ }
7601
+ if (targetPath === "~") {
7602
+ return homeDir;
7603
+ }
7604
+ if (targetPath.startsWith("~./")) {
7605
+ targetPath = `~/.${targetPath.slice(3)}`;
7606
+ }
7607
+ let remainder = targetPath.slice(1);
7608
+ if (remainder.startsWith("/") || remainder.startsWith("\\")) {
7609
+ remainder = remainder.slice(1);
7610
+ } else if (remainder.startsWith(".")) {
7611
+ remainder = remainder.slice(1);
7612
+ if (remainder.startsWith("/") || remainder.startsWith("\\")) {
7613
+ remainder = remainder.slice(1);
7614
+ }
7615
+ }
7616
+ return remainder.length === 0 ? homeDir : path29.join(homeDir, remainder);
7617
+ }
7618
+ function resolveHookPath(config, scope, cwd, homeDir) {
7619
+ if (scope === "global") {
7620
+ return path29.resolve(expandHome3(config.globalHookPath, homeDir));
7621
+ }
7622
+ return config.localHookPath ? path29.resolve(cwd, config.localHookPath) : void 0;
7623
+ }
7624
+
7625
+ // packages/agent-hook-config/src/read-hooks.ts
7626
+ import { readFileSync as readFileSync3 } from "node:fs";
7627
+ import path30 from "node:path";
7628
+ function readSettingsFile(filePath) {
7629
+ let content;
7630
+ try {
7631
+ content = readFileSync3(filePath, "utf8");
7632
+ } catch (error2) {
7633
+ if (error2.code === "ENOENT") {
7634
+ return void 0;
7635
+ }
7636
+ throw error2;
7637
+ }
7638
+ try {
7639
+ return JSON.parse(content);
7640
+ } catch (error2) {
7641
+ throw new Error(`Malformed JSON in ${filePath}`, { cause: error2 });
7642
+ }
7643
+ }
7644
+ function readClaudeHooks(cwd, homeDir, opts) {
7645
+ const projectPath = path30.resolve(cwd, ".claude/settings.json");
7646
+ const userPath = path30.resolve(homeDir, ".claude/settings.json");
7647
+ const scope = opts?.scope ?? "merged";
7648
+ const sourcePaths = scope === "project" ? [projectPath] : scope === "user" ? [userPath] : [userPath, projectPath];
7649
+ const result = { entries: [], readPaths: [] };
7650
+ for (const sourcePath of sourcePaths) {
7651
+ const settings = readSettingsFile(sourcePath);
7652
+ if (settings === void 0) {
7653
+ continue;
7654
+ }
7655
+ result.readPaths.push(sourcePath);
7656
+ for (const [event, groups] of Object.entries(settings.hooks ?? {})) {
7657
+ for (const group of groups) {
7658
+ for (const handler of group.hooks) {
7659
+ result.entries.push({ event, matcher: group.matcher, handler });
7660
+ }
7661
+ }
7662
+ }
7663
+ }
7664
+ return result;
7665
+ }
7666
+
7667
+ // packages/agent-hook-config/src/event-mapping.ts
7668
+ function requireAgentConfig(agentId) {
7669
+ const config = getAgentConfig2(agentId);
7670
+ if (!config) {
7671
+ throw new Error(`Unknown hook agent "${agentId}"`);
7672
+ }
7673
+ return config;
7674
+ }
7675
+ function getEventMappings(sourceAgentId, targetAgentId) {
7676
+ const source = requireAgentConfig(sourceAgentId);
7677
+ const target = requireAgentConfig(targetAgentId);
7678
+ return source.supportedEvents.map((sourceEvent) => {
7679
+ if (target.supportedEvents.includes(sourceEvent)) {
7680
+ return { sourceEvent, targetEvent: sourceEvent };
7681
+ }
7682
+ return {
7683
+ sourceEvent,
7684
+ targetEvent: null,
7685
+ dropReason: `${targetAgentId} has no ${sourceEvent} hook`
7686
+ };
7687
+ });
7688
+ }
7689
+ function getHandlerTypeRules(targetAgentId) {
7690
+ const target = requireAgentConfig(targetAgentId);
7691
+ const registeredTypes = supportedHookAgents.flatMap(
7692
+ (agentId) => requireAgentConfig(agentId).supportedHandlerTypes
7693
+ );
7694
+ const sourceTypes = [...new Set(registeredTypes)];
7695
+ const supportedTypes = target.supportedHandlerTypes.map((handlerType) => `"${handlerType}"`).join(", ");
7696
+ return sourceTypes.map((sourceType) => {
7697
+ if (target.supportedHandlerTypes.includes(sourceType)) {
7698
+ return { sourceType, allowed: true };
7699
+ }
7700
+ return {
7701
+ sourceType,
7702
+ allowed: false,
7703
+ dropReason: `${targetAgentId} only honors handlers of type ${supportedTypes}`
7704
+ };
7705
+ });
7706
+ }
7707
+ function getPlaceholderRewrites(sourceAgentId, targetAgentId) {
7708
+ const source = requireAgentConfig(sourceAgentId);
7709
+ const target = requireAgentConfig(targetAgentId);
7710
+ return Object.keys(source.placeholders).flatMap((key) => {
7711
+ const from = source.placeholders[key];
7712
+ const to = target.placeholders[key];
7713
+ if (!from || !to || from === to) {
7714
+ return [];
7715
+ }
7716
+ return [{ from, to }];
7717
+ });
7718
+ }
7719
+
7720
+ // packages/agent-hook-config/src/transform-hooks.ts
7721
+ function applyPlaceholderRewrites(value, rewrites) {
7722
+ return rewrites.reduce((rewrittenValue, rewrite) => {
7723
+ return rewrittenValue.replaceAll(rewrite.from, rewrite.to);
7724
+ }, value);
7725
+ }
7726
+ function transformHooks(source, sourceAgentId, targetAgentId, opts) {
7727
+ const eventMappings = getEventMappings(sourceAgentId, targetAgentId);
7728
+ const handlerRules = getHandlerTypeRules(targetAgentId);
7729
+ const placeholderRewrites = getPlaceholderRewrites(sourceAgentId, targetAgentId);
7730
+ const result = { entries: [], drops: [] };
7731
+ for (const sourceEntry of source) {
7732
+ const eventMapping = eventMappings.find((mapping) => mapping.sourceEvent === sourceEntry.event);
7733
+ if (!eventMapping || eventMapping.targetEvent === null) {
7734
+ result.drops.push({
7735
+ reason: "unsupported-event",
7736
+ detail: eventMapping?.dropReason ?? `${targetAgentId} has no ${sourceEntry.event} hook`,
7737
+ source: sourceEntry
7738
+ });
7739
+ continue;
7740
+ }
7741
+ const handlerRule = handlerRules.find((rule) => rule.sourceType === sourceEntry.handler.type);
7742
+ if (!handlerRule?.allowed) {
7743
+ result.drops.push({
7744
+ reason: "unsupported-handler-type",
7745
+ detail: `Unsupported handler type "${sourceEntry.handler.type}": ${handlerRule?.dropReason ?? `${targetAgentId} does not honor it`}`,
7746
+ source: sourceEntry
7747
+ });
7748
+ continue;
7749
+ }
7750
+ const handler = {
7751
+ type: "command",
7752
+ command: applyPlaceholderRewrites(sourceEntry.handler.command ?? "", placeholderRewrites),
7753
+ statusMessage: `[generated:${opts.runId}] ${sourceEntry.handler.statusMessage ?? ""}`
7754
+ };
7755
+ if (sourceEntry.handler.args !== void 0) {
7756
+ handler.args = sourceEntry.handler.args.map(
7757
+ (arg) => applyPlaceholderRewrites(arg, placeholderRewrites)
7758
+ );
7759
+ }
7760
+ if (sourceEntry.handler.timeout !== void 0) {
7761
+ handler.timeout = sourceEntry.handler.timeout;
7762
+ }
7763
+ result.entries.push({
7764
+ event: eventMapping.targetEvent,
7765
+ matcher: sourceEntry.matcher,
7766
+ handler,
7767
+ generatedId: `generated-${opts.runId}-${result.entries.length}`
7768
+ });
7769
+ }
7770
+ return result;
7771
+ }
7772
+
7773
+ // packages/agent-hook-config/src/write-hooks.ts
7774
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync4, renameSync, writeFileSync as writeFileSync2 } from "node:fs";
7775
+ import path31 from "node:path";
7776
+ function isGeneratedHandler(handler) {
7777
+ return handler.statusMessage?.startsWith("[generated:") ?? false;
7778
+ }
7779
+ function parseHooksFile(targetPath) {
7780
+ let content;
7781
+ try {
7782
+ content = readFileSync4(targetPath, "utf8");
7783
+ } catch (error2) {
7784
+ if (error2.code === "ENOENT") {
7785
+ return { file: { hooks: {} }, fileCreated: true };
7786
+ }
7787
+ throw error2;
7788
+ }
7789
+ try {
7790
+ return { file: JSON.parse(content), fileCreated: false };
7791
+ } catch (error2) {
7792
+ throw new Error(`Malformed JSON in ${targetPath}`, { cause: error2 });
7793
+ }
7794
+ }
7795
+ function validateEntries(entries) {
7796
+ for (const entry of entries) {
7797
+ if (!isGeneratedHandler(entry.handler)) {
7798
+ throw new Error(
7799
+ `Generated hook entry "${entry.generatedId}" has statusMessage that must start with "[generated:"`
7800
+ );
7801
+ }
7802
+ }
7803
+ }
7804
+ function removeGeneratedHandlers(file) {
7805
+ let removed = 0;
7806
+ const hooks = file.hooks ?? (file.hooks = {});
7807
+ for (const [event, groups] of Object.entries(hooks)) {
7808
+ hooks[event] = groups.filter((group) => {
7809
+ const initialCount = group.hooks.length;
7810
+ const remainingHandlers = group.hooks.filter((handler) => {
7811
+ if (isGeneratedHandler(handler)) {
7812
+ removed += 1;
7813
+ return false;
7814
+ }
7815
+ return true;
7816
+ });
7817
+ group.hooks = remainingHandlers;
7818
+ return group.hooks.length > 0 || group.hooks.length === initialCount;
7819
+ });
7820
+ }
7821
+ return removed;
7822
+ }
7823
+ function appendEntries(file, entries) {
7824
+ const hooks = file.hooks ?? (file.hooks = {});
7825
+ for (const entry of entries) {
7826
+ const groups = hooks[entry.event] ?? (hooks[entry.event] = []);
7827
+ let group = groups.find((candidate) => candidate.matcher === entry.matcher);
7828
+ if (!group) {
7829
+ group = entry.matcher === void 0 ? { hooks: [] } : { matcher: entry.matcher, hooks: [] };
7830
+ groups.push(group);
7831
+ }
7832
+ group.hooks.push(entry.handler);
7833
+ }
7834
+ }
7835
+ function writeCodexHooks(targetPath, entries, _runId) {
7836
+ const { file, fileCreated } = parseHooksFile(targetPath);
7837
+ validateEntries(entries);
7838
+ const previousGeneratedRemoved = removeGeneratedHandlers(file);
7839
+ appendEntries(file, entries);
7840
+ mkdirSync3(path31.dirname(targetPath), { recursive: true });
7841
+ const temporaryPath = `${targetPath}.tmp`;
7842
+ writeFileSync2(temporaryPath, `${JSON.stringify(file, null, 2)}
7843
+ `);
7844
+ renameSync(temporaryPath, targetPath);
7845
+ return {
7846
+ path: targetPath,
7847
+ fileCreated,
7848
+ previousGeneratedRemoved,
7849
+ generatedWritten: entries.length
7850
+ };
7851
+ }
7852
+
7853
+ // packages/agent-hook-config/src/symlink-hooks.ts
7854
+ import {
7855
+ closeSync,
7856
+ lstatSync,
7857
+ mkdirSync as mkdirSync4,
7858
+ openSync,
7859
+ readlinkSync,
7860
+ readSync,
7861
+ symlinkSync,
7862
+ unlinkSync
7863
+ } from "node:fs";
7864
+ import path32 from "node:path";
7865
+ function requireAgentConfig2(agentId) {
7866
+ const config = getAgentConfig2(agentId);
7867
+ if (!config) {
7868
+ throw new Error(`No hook configuration found for agent "${agentId}"`);
7869
+ }
7870
+ return config;
7871
+ }
7872
+ function resolveScopedPath(config, agentId, cwd, homeDir, scope) {
7873
+ const targetPath = resolveHookPath(
7874
+ config,
7875
+ scope === "project" ? "local" : "global",
7876
+ cwd,
7877
+ homeDir
7878
+ );
7879
+ if (!targetPath) {
7880
+ throw new Error(`Agent "${agentId}" has no ${scope} hook path`);
7881
+ }
7882
+ return targetPath;
7883
+ }
7884
+ function readFirstKilobyte(filePath) {
7885
+ const descriptor = openSync(filePath, "r");
7886
+ const buffer = Buffer.alloc(1024);
7887
+ try {
7888
+ const length = readSync(descriptor, buffer, 0, buffer.length, 0);
7889
+ return buffer.toString("utf8", 0, length);
7890
+ } finally {
7891
+ closeSync(descriptor);
7892
+ }
7893
+ }
7894
+ function isRecord7(value) {
7895
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7896
+ }
7897
+ function isFullyGeneratedFile(filePath) {
7898
+ let parsed;
7899
+ try {
7900
+ parsed = JSON.parse(readFirstKilobyte(filePath));
7901
+ } catch {
7902
+ return false;
7903
+ }
7904
+ if (!isRecord7(parsed)) {
7905
+ return false;
7906
+ }
7907
+ if (Object.keys(parsed).some((key) => key !== "hooks")) {
7908
+ return false;
7909
+ }
7910
+ const hooks = parsed.hooks;
7911
+ if (!isRecord7(hooks)) {
7912
+ return false;
7913
+ }
7914
+ let handlerFound = false;
7915
+ for (const groups of Object.values(hooks)) {
7916
+ if (!Array.isArray(groups)) {
7917
+ return false;
7918
+ }
7919
+ for (const group of groups) {
7920
+ if (!isRecord7(group) || !Array.isArray(group.hooks)) {
7921
+ return false;
7922
+ }
7923
+ for (const handler of group.hooks) {
7924
+ if (!isRecord7(handler)) {
7925
+ return false;
7926
+ }
7927
+ handlerFound = true;
7928
+ const statusMessage = handler.statusMessage;
7929
+ if (typeof statusMessage !== "string" || !statusMessage.startsWith("[generated:")) {
7930
+ return false;
7931
+ }
7932
+ }
7933
+ }
7934
+ }
7935
+ return handlerFound;
7936
+ }
7937
+ function symlinkHooks(sourceAgentId, targetAgentId, cwd, homeDir, scope) {
7938
+ const source = requireAgentConfig2(sourceAgentId);
7939
+ const target = requireAgentConfig2(targetAgentId);
7940
+ if (source.format !== target.format) {
7941
+ throw new Error(
7942
+ `Cannot symlink hook formats "${source.format}" and "${target.format}"; use transformation instead`
7943
+ );
7944
+ }
7945
+ const targetPath = resolveScopedPath(source, sourceAgentId, cwd, homeDir, scope);
7946
+ const symlinkPath = resolveScopedPath(target, targetAgentId, cwd, homeDir, scope);
7947
+ let replaced = "none";
7948
+ try {
7949
+ const existing = lstatSync(symlinkPath);
7950
+ if (existing.isSymbolicLink()) {
7951
+ if (readlinkSync(symlinkPath) === targetPath) {
7952
+ return { symlinkPath, targetPath, replaced };
7953
+ }
7954
+ unlinkSync(symlinkPath);
7955
+ replaced = "stale-symlink";
7956
+ } else if (existing.isFile() && isFullyGeneratedFile(symlinkPath)) {
7957
+ unlinkSync(symlinkPath);
7958
+ replaced = "generated-file";
7959
+ } else {
7960
+ throw new Error(`Refuse to replace user-authored hook file at ${symlinkPath}`);
7961
+ }
7962
+ } catch (error2) {
7963
+ if (error2.code !== "ENOENT") {
7964
+ throw error2;
7965
+ }
7966
+ }
7967
+ mkdirSync4(path32.dirname(symlinkPath), { recursive: true });
7968
+ symlinkSync(targetPath, symlinkPath);
7969
+ return { symlinkPath, targetPath, replaced };
7970
+ }
7971
+
7972
+ // packages/agent-hook-config/src/bridge-hooks.ts
7973
+ import * as fs3 from "node:fs";
7974
+ import path33 from "node:path";
7975
+ var hookExcludeMarkerPrefix = "poe-code-spawn-hooks";
7976
+ function isNodeError4(error2) {
7977
+ return error2 instanceof Error && "code" in error2;
7978
+ }
7979
+ function pathExists3(targetPath) {
7980
+ try {
7981
+ fs3.lstatSync(targetPath);
7982
+ return true;
7983
+ } catch (error2) {
7984
+ if (isNodeError4(error2) && error2.code === "ENOENT") {
7985
+ return false;
7986
+ }
7987
+ throw error2;
7988
+ }
7989
+ }
7990
+ function collectMissingParents2(targetPath) {
7991
+ const parents = [];
7992
+ let current = path33.dirname(targetPath);
7993
+ while (!pathExists3(current)) {
7994
+ parents.push(current);
7995
+ const parent = path33.dirname(current);
7996
+ if (parent === current) {
7997
+ break;
7998
+ }
7999
+ current = parent;
8000
+ }
8001
+ return parents.reverse();
8002
+ }
8003
+ function removeDirectoryIfEmpty2(targetPath) {
8004
+ try {
8005
+ fs3.rmdirSync(targetPath);
8006
+ } catch (error2) {
8007
+ if (isNodeError4(error2) && (error2.code === "ENOENT" || error2.code === "ENOTEMPTY" || error2.code === "EEXIST")) {
8008
+ return;
8009
+ }
8010
+ throw error2;
8011
+ }
8012
+ }
8013
+ function requireSupport(input, role) {
8014
+ const support = resolveAgentSupport2(input);
8015
+ if (support.status !== "supported" || !support.id || !support.config) {
8016
+ throw new Error(
8017
+ `Unsupported ${role} hook agent "${input}". Supported hook agents: ${supportedHookAgents.join(", ")}.`
8018
+ );
8019
+ }
8020
+ return { id: support.id, config: support.config };
8021
+ }
8022
+ function requireTargetPath(targetId, config, cwd, homeDir) {
8023
+ const targetPath = resolveHookPath(config, "local", cwd, homeDir);
8024
+ if (!targetPath) {
8025
+ throw new Error(`Agent "${targetId}" has no project hook path`);
8026
+ }
8027
+ return targetPath;
8028
+ }
8029
+ function readCodexFile(targetPath) {
8030
+ let content;
8031
+ try {
8032
+ content = fs3.readFileSync(targetPath, "utf8");
8033
+ } catch (error2) {
8034
+ if (isNodeError4(error2) && error2.code === "ENOENT") {
8035
+ return void 0;
8036
+ }
8037
+ throw error2;
8038
+ }
8039
+ try {
8040
+ return JSON.parse(content);
8041
+ } catch (error2) {
8042
+ throw new Error(`Malformed JSON in ${targetPath}`, { cause: error2 });
8043
+ }
8044
+ }
8045
+ function writeCodexFile(targetPath, file) {
8046
+ fs3.writeFileSync(targetPath, `${JSON.stringify(file, null, 2)}
8047
+ `, "utf8");
8048
+ }
8049
+ function hasOnlyEmptyHooks(file) {
8050
+ return Object.keys(file).every((key) => key === "hooks") && Object.values(file.hooks ?? {}).every((groups) => groups.length === 0);
8051
+ }
8052
+ function relativeToCwd(cwd, targetPath) {
8053
+ return path33.relative(cwd, targetPath);
8054
+ }
8055
+ function matcherKey(event, matcher) {
8056
+ return `${event}\0${matcher === void 0 ? "<undefined>" : matcher}`;
8057
+ }
8058
+ function bridgeHooks(sourceAgentId, targetAgentId, cwd, homeDir, runId, opts) {
8059
+ const source = requireSupport(sourceAgentId, "source");
8060
+ const target = requireSupport(targetAgentId, "target");
8061
+ const strategy = opts?.strategy ?? (source.config.format === target.config.format ? "symlink" : "transform");
8062
+ const manifest = {
8063
+ sourceAgentId,
8064
+ targetAgentId,
8065
+ cwd,
8066
+ runId,
8067
+ strategy,
8068
+ drops: []
8069
+ };
8070
+ if (strategy === "symlink") {
8071
+ const symlinkPath = requireTargetPath(target.id, target.config, cwd, homeDir);
8072
+ manifest.createdParents = collectMissingParents2(symlinkPath);
8073
+ const result = symlinkHooks(source.id, target.id, cwd, homeDir, "project");
8074
+ manifest.symlinkPath = result.symlinkPath;
8075
+ manifest.symlinkTarget = result.targetPath;
8076
+ manifest.symlinkReplaced = result.replaced;
8077
+ appendExcludeBlock(cwd, runId, [relativeToCwd(cwd, result.symlinkPath)], {
8078
+ markerPrefix: hookExcludeMarkerPrefix
8079
+ });
8080
+ return manifest;
8081
+ }
8082
+ if (source.id !== "claude-code") {
8083
+ throw new Error(`Transforming hooks from "${source.id}" is not supported yet`);
8084
+ }
8085
+ if (target.config.format !== "codex-hooks-json") {
8086
+ throw new Error(
8087
+ `Transforming hooks to "${target.id}" is not supported yet; only codex-hook targets can be written`
8088
+ );
8089
+ }
8090
+ const targetPath = requireTargetPath(target.id, target.config, cwd, homeDir);
8091
+ const priorFile = readCodexFile(targetPath);
8092
+ const sourceHooks = readClaudeHooks(cwd, homeDir, { scope: opts?.scope ?? "merged" });
8093
+ const transformed = transformHooks(sourceHooks.entries, source.id, target.id, { runId });
8094
+ const createdParents = collectMissingParents2(targetPath);
8095
+ const writeResult = writeCodexHooks(targetPath, transformed.entries, runId);
8096
+ manifest.writtenPath = targetPath;
8097
+ manifest.generatedEntryIds = transformed.entries.map((entry) => entry.generatedId);
8098
+ manifest.drops = transformed.drops;
8099
+ manifest.createdParents = createdParents;
8100
+ manifest.fileCreated = writeResult.fileCreated;
8101
+ manifest.preExistingEvents = Object.keys(priorFile?.hooks ?? {});
8102
+ manifest.preExistingMatchers = Object.entries(priorFile?.hooks ?? {}).flatMap(
8103
+ ([event, groups]) => groups.map((group) => ({
8104
+ event,
8105
+ ...group.matcher === void 0 ? {} : { matcher: group.matcher }
8106
+ }))
8107
+ );
8108
+ appendExcludeBlock(cwd, runId, [relativeToCwd(cwd, targetPath)], {
8109
+ markerPrefix: hookExcludeMarkerPrefix
8110
+ });
8111
+ return manifest;
8112
+ }
8113
+ function cleanupBridgedHooks(manifest) {
8114
+ if (manifest.strategy === "symlink" && manifest.symlinkPath && manifest.symlinkTarget) {
8115
+ try {
8116
+ if (fs3.lstatSync(manifest.symlinkPath).isSymbolicLink() && fs3.readlinkSync(manifest.symlinkPath) === manifest.symlinkTarget) {
8117
+ fs3.unlinkSync(manifest.symlinkPath);
8118
+ }
8119
+ } catch (error2) {
8120
+ if (!isNodeError4(error2) || error2.code !== "ENOENT") {
8121
+ throw error2;
8122
+ }
8123
+ }
8124
+ for (const parent of [...manifest.createdParents ?? []].reverse()) {
8125
+ removeDirectoryIfEmpty2(parent);
8126
+ }
8127
+ }
8128
+ if (manifest.strategy === "transform" && manifest.writtenPath) {
8129
+ const file = readCodexFile(manifest.writtenPath);
8130
+ if (file) {
8131
+ const generatedPrefix = `[generated:${manifest.runId}]`;
8132
+ const preExistingEvents = new Set(manifest.preExistingEvents ?? []);
8133
+ const preExistingMatchers = new Set(
8134
+ (manifest.preExistingMatchers ?? []).map((group) => matcherKey(group.event, group.matcher))
8135
+ );
8136
+ const hooks = file.hooks ?? {};
8137
+ for (const [event, groups] of Object.entries(hooks)) {
8138
+ hooks[event] = groups.filter((group) => {
8139
+ const priorLength = group.hooks.length;
8140
+ group.hooks = group.hooks.filter(
8141
+ (handler) => !handler.statusMessage?.startsWith(generatedPrefix)
8142
+ );
8143
+ return group.hooks.length > 0 || group.hooks.length === priorLength || preExistingMatchers.has(matcherKey(event, group.matcher));
8144
+ });
8145
+ if (hooks[event].length === 0 && !preExistingEvents.has(event)) {
8146
+ delete hooks[event];
8147
+ }
8148
+ }
8149
+ file.hooks = hooks;
8150
+ if (manifest.fileCreated && hasOnlyEmptyHooks(file)) {
8151
+ fs3.unlinkSync(manifest.writtenPath);
8152
+ } else {
8153
+ writeCodexFile(manifest.writtenPath, file);
8154
+ }
8155
+ }
8156
+ for (const parent of [...manifest.createdParents ?? []].reverse()) {
8157
+ removeDirectoryIfEmpty2(parent);
8158
+ }
8159
+ }
8160
+ removeExcludeBlock(manifest.cwd, manifest.runId, { markerPrefix: hookExcludeMarkerPrefix });
8161
+ }
8162
+
7466
8163
  // packages/agent-spawn/src/skill-bridge.ts
7467
- function bridgeSkillsForRun(agentId, cwd, skills) {
7468
- if (!skills || skills.length === 0) {
8164
+ function bridgeResourcesForRun(agentId, cwd, skills, hooks) {
8165
+ if ((!skills || skills.length === 0) && !hooks) {
7469
8166
  return void 0;
7470
8167
  }
7471
- const manifest = bridgeActiveSkills(agentId, cwd, skills, os6.homedir(), crypto.randomUUID());
7472
- for (const warning2 of manifest.warnings) {
7473
- logger.warn(warning2.message);
8168
+ const runId = crypto.randomUUID();
8169
+ const manifests = {};
8170
+ try {
8171
+ if (skills && skills.length > 0) {
8172
+ manifests.skills = bridgeActiveSkills(agentId, cwd, skills, os7.homedir(), runId);
8173
+ for (const warning2 of manifests.skills.warnings) {
8174
+ logger.warn(warning2.message);
8175
+ }
8176
+ }
8177
+ if (hooks) {
8178
+ manifests.hooks = bridgeHooks(hooks.from, agentId, cwd, os7.homedir(), runId, {
8179
+ strategy: hooks.strategy === "auto" ? void 0 : hooks.strategy,
8180
+ scope: hooks.scope
8181
+ });
8182
+ for (const drop of manifests.hooks.drops) {
8183
+ logger.warn(
8184
+ `Dropped bridged hook event "${drop.source.event}" with handler type "${drop.source.handler.type}": ${drop.detail}`
8185
+ );
8186
+ }
8187
+ }
8188
+ } catch (error2) {
8189
+ cleanupResourcesForRun(manifests);
8190
+ throw error2;
7474
8191
  }
7475
- return manifest;
8192
+ return manifests;
7476
8193
  }
7477
- function cleanupSkillsForRun(manifest) {
8194
+ function cleanupResourcesForRun(manifest) {
7478
8195
  if (!manifest) {
7479
8196
  return;
7480
8197
  }
7481
- cleanupBridgedSkills(manifest);
8198
+ if (manifest.hooks) {
8199
+ cleanupBridgedHooks(manifest.hooks);
8200
+ }
8201
+ if (manifest.skills) {
8202
+ cleanupBridgedSkills(manifest.skills);
8203
+ }
7482
8204
  }
7483
8205
 
7484
8206
  // packages/agent-spawn/src/adapters/utils.ts
@@ -7604,21 +8326,21 @@ async function* adaptClaude(lines) {
7604
8326
  if (blockType !== "tool_result") continue;
7605
8327
  const kind = toolKindsById.get(item.tool_use_id);
7606
8328
  toolKindsById.delete(item.tool_use_id);
7607
- let path32;
8329
+ let path37;
7608
8330
  if (typeof item.content === "string") {
7609
- path32 = item.content;
8331
+ path37 = item.content;
7610
8332
  } else {
7611
8333
  try {
7612
- path32 = JSON.stringify(item.content);
8334
+ path37 = JSON.stringify(item.content);
7613
8335
  } catch {
7614
- path32 = String(item.content);
8336
+ path37 = String(item.content);
7615
8337
  }
7616
8338
  }
7617
8339
  yield {
7618
8340
  event: "tool_complete",
7619
8341
  id: item.tool_use_id,
7620
8342
  kind,
7621
- path: path32
8343
+ path: path37
7622
8344
  };
7623
8345
  }
7624
8346
  }
@@ -7714,10 +8436,10 @@ async function* adaptCodex(lines) {
7714
8436
  const kindFromStart = toolKindById.get(item.id);
7715
8437
  const kind = kindFromStart ?? (itemType === "command_execution" ? "exec" : itemType === "file_edit" ? "edit" : "other");
7716
8438
  const titleFromEvent = isNonEmptyString(item.path) ? item.path : itemType === "mcp_tool_call" ? `${isNonEmptyString(item.server) ? item.server : "unknown"}.${isNonEmptyString(item.tool) ? item.tool : "unknown"}` : void 0;
7717
- const path32 = titleFromEvent ?? toolTitleById.get(item.id) ?? "";
8439
+ const path37 = titleFromEvent ?? toolTitleById.get(item.id) ?? "";
7718
8440
  toolTitleById.delete(item.id);
7719
8441
  toolKindById.delete(item.id);
7720
- yield { event: "tool_complete", id: item.id, kind, path: path32 };
8442
+ yield { event: "tool_complete", id: item.id, kind, path: path37 };
7721
8443
  }
7722
8444
  }
7723
8445
  }
@@ -8245,7 +8967,7 @@ function spawnStreaming(options) {
8245
8967
  };
8246
8968
  }
8247
8969
  };
8248
- const manifest = bridgeSkillsForRun(options.agentId, cwd, options.skills);
8970
+ const manifest = bridgeResourcesForRun(options.agentId, cwd, options.skills, options.hooks);
8249
8971
  void (async () => {
8250
8972
  try {
8251
8973
  for await (const output of adapter(queue.lines())) {
@@ -8296,7 +9018,7 @@ function spawnStreaming(options) {
8296
9018
  ...ctx.logFile && !result.logFile ? { logFile: ctx.logFile } : {}
8297
9019
  };
8298
9020
  } finally {
8299
- cleanupSkillsForRun(manifest);
9021
+ cleanupResourcesForRun(manifest);
8300
9022
  }
8301
9023
  })();
8302
9024
  return {
@@ -8438,7 +9160,7 @@ async function runSpawn(agentId, options, context) {
8438
9160
  return { stdout: "", stderr: "", exitCode: 0 };
8439
9161
  }
8440
9162
  const cwd = options.cwd ?? process.cwd();
8441
- const manifest = bridgeSkillsForRun(agentId, cwd, options.skills);
9163
+ const manifest = bridgeResourcesForRun(agentId, cwd, options.skills, options.hooks);
8442
9164
  let logFd;
8443
9165
  try {
8444
9166
  const logFilePath = resolveSpawnLogPath(options);
@@ -8506,7 +9228,7 @@ async function runSpawn(agentId, options, context) {
8506
9228
  };
8507
9229
  } finally {
8508
9230
  closeSpawnLog(logFd);
8509
- cleanupSkillsForRun(manifest);
9231
+ cleanupResourcesForRun(manifest);
8510
9232
  }
8511
9233
  }
8512
9234
  spawn4.retry = createSpawnRetry((service, options) => {
@@ -8528,12 +9250,12 @@ function resolveSpawnLogPath(options) {
8528
9250
  if (!options.logDir || !options.logFileName) {
8529
9251
  return void 0;
8530
9252
  }
8531
- return path29.join(options.logDir, options.logFileName);
9253
+ return path34.join(options.logDir, options.logFileName);
8532
9254
  }
8533
9255
  function openSpawnLog(filePath) {
8534
9256
  try {
8535
- mkdirSync3(path29.dirname(filePath), { recursive: true });
8536
- return openSync(filePath, "a");
9257
+ mkdirSync5(path34.dirname(filePath), { recursive: true });
9258
+ return openSync2(filePath, "a");
8537
9259
  } catch {
8538
9260
  return void 0;
8539
9261
  }
@@ -8548,7 +9270,7 @@ function appendSpawnLog(fd, chunk) {
8548
9270
  function closeSpawnLog(fd) {
8549
9271
  if (fd === void 0) return;
8550
9272
  try {
8551
- closeSync(fd);
9273
+ closeSync2(fd);
8552
9274
  } catch {
8553
9275
  }
8554
9276
  }
@@ -8557,7 +9279,7 @@ function closeSpawnLog(fd) {
8557
9279
  var DEFAULT_ACTIVITY_TIMEOUT_MS = 10 * 60 * 1e3;
8558
9280
 
8559
9281
  // packages/agent-spawn/src/acp/replay.ts
8560
- import path30 from "node:path";
9282
+ import path35 from "node:path";
8561
9283
  import { homedir as homedir2 } from "node:os";
8562
9284
  import { open as open2, readdir as readdir2 } from "node:fs/promises";
8563
9285
  import { createInterface } from "node:readline";
@@ -8576,7 +9298,7 @@ import { homedir } from "node:os";
8576
9298
  import { join } from "node:path";
8577
9299
 
8578
9300
  // packages/agent-spawn/src/acp/middlewares/spawn-log.ts
8579
- import path31 from "node:path";
9301
+ import path36 from "node:path";
8580
9302
  import { homedir as homedir3 } from "node:os";
8581
9303
  import { mkdir, open as open3 } from "node:fs/promises";
8582
9304
 
@@ -8590,9 +9312,30 @@ stderr:
8590
9312
  ${stderr}`;
8591
9313
  }
8592
9314
  function createSpawnHealthCheck(agentId, options) {
8593
- const prompt = `Output exactly: ${options.expectedOutput}`;
8594
- const { binaryName, args, env: modeEnv } = buildSpawnArgs(agentId, {
8595
- prompt,
9315
+ const {
9316
+ binaryName,
9317
+ args,
9318
+ env: modeEnv
9319
+ } = options.hooks ? {
9320
+ binaryName: "poe-code",
9321
+ args: [
9322
+ "spawn",
9323
+ "--hooks-from",
9324
+ options.hooks.from,
9325
+ ...options.hooks.strategy ? ["--hooks-strategy", options.hooks.strategy] : [],
9326
+ ...options.model ? ["--model", options.model] : [],
9327
+ "--mode",
9328
+ "yolo",
9329
+ agentId,
9330
+ `Output exactly: ${options.expectedOutput}`
9331
+ ],
9332
+ env: void 0
9333
+ } : options.invocation ? {
9334
+ binaryName: options.invocation.command,
9335
+ args: options.invocation.args,
9336
+ env: options.invocation.env
9337
+ } : buildSpawnArgs(agentId, {
9338
+ prompt: `Output exactly: ${options.expectedOutput}`,
8596
9339
  model: options.model,
8597
9340
  mode: "yolo"
8598
9341
  });
@@ -8607,6 +9350,13 @@ function createSpawnHealthCheck(agentId, options) {
8607
9350
  return;
8608
9351
  }
8609
9352
  const result = modeEnv ? await context.runCommand(binaryName, args, { env: modeEnv }) : await context.runCommand(binaryName, args);
9353
+ if (options.hooks) {
9354
+ for (const line of result.stdout.split("\n")) {
9355
+ if (line.includes("Dropped bridged hook event")) {
9356
+ context.logWarning?.(line);
9357
+ }
9358
+ }
9359
+ }
8610
9360
  if (result.exitCode !== 0) {
8611
9361
  throw new Error(
8612
9362
  `spawn ${agentId} failed with exit code ${result.exitCode}.
@@ -8763,6 +9513,7 @@ function createProvider(opts) {
8763
9513
  configurePrompts: opts.configurePrompts,
8764
9514
  postConfigureMessages: opts.postConfigureMessages,
8765
9515
  extendConfigurePayload: opts.extendConfigurePayload,
9516
+ runtimeEnv: opts.runtimeEnv,
8766
9517
  isolatedEnv: opts.isolatedEnv,
8767
9518
  async configure(context, runOptions) {
8768
9519
  await runMutations(opts.manifest.configure, {
@@ -8852,18 +9603,17 @@ var claudeCodeService = createProvider({
8852
9603
  postConfigureMessages: [
8853
9604
  "If using VSCode - Open the Disable Login Prompt setting and check the box. vscode://settings/claudeCode.disableLoginPrompt"
8854
9605
  ],
9606
+ runtimeEnv: {
9607
+ ANTHROPIC_BASE_URL: { kind: "agentBaseUrl" }
9608
+ },
8855
9609
  isolatedEnv: {
8856
9610
  agentBinary: claudeCodeAgent.binaryName,
8857
- env: {
8858
- POE_CODE_API_KEY: { kind: "providerCredential" }
8859
- },
9611
+ env: {},
8860
9612
  requiresConfig: false,
8861
9613
  cliSettings: {
8862
- values: {
8863
- apiKeyHelper: "echo $POE_CODE_API_KEY"
8864
- },
9614
+ values: {},
8865
9615
  env: {
8866
- ANTHROPIC_BASE_URL: { kind: "providerBaseUrl" }
9616
+ ANTHROPIC_BASE_URL: { kind: "agentBaseUrl" }
8867
9617
  }
8868
9618
  }
8869
9619
  },
@@ -8878,14 +9628,23 @@ var claudeCodeService = createProvider({
8878
9628
  manifest: {
8879
9629
  configure: [
8880
9630
  fileMutation.ensureDirectory({ path: "~/.claude" }),
9631
+ configMutation.prune({
9632
+ target: "~/.claude/settings.json",
9633
+ shape: {
9634
+ apiKeyHelper: true,
9635
+ env: {
9636
+ ANTHROPIC_API_KEY: true
9637
+ }
9638
+ }
9639
+ }),
8881
9640
  configMutation.merge({
8882
9641
  target: "~/.claude/settings.json",
8883
9642
  value: (ctx) => {
8884
9643
  const options = ctx;
8885
9644
  return {
8886
- apiKeyHelper: `echo ${options.provider?.credential}`,
8887
9645
  env: {
8888
- ANTHROPIC_BASE_URL: options.provider?.baseUrl
9646
+ ...options.provider?.extraEnv,
9647
+ ANTHROPIC_BASE_URL: options.provider?.agentBaseUrl ?? options.provider?.baseUrl
8889
9648
  },
8890
9649
  model: stripModelNamespace2(options.model ?? DEFAULT_CLAUDE_CODE_MODEL).replaceAll(".", "-")
8891
9650
  };
@@ -8898,6 +9657,8 @@ var claudeCodeService = createProvider({
8898
9657
  shape: {
8899
9658
  apiKeyHelper: true,
8900
9659
  env: {
9660
+ ANTHROPIC_API_KEY: true,
9661
+ ANTHROPIC_CUSTOM_HEADERS: true,
8901
9662
  ANTHROPIC_BASE_URL: true,
8902
9663
  ANTHROPIC_DEFAULT_HAIKU_MODEL: true,
8903
9664
  ANTHROPIC_DEFAULT_SONNET_MODEL: true,