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") {
@@ -3835,16 +3854,16 @@ function getConfigFormat(pathOrFormat) {
3835
3854
  }
3836
3855
  return formatRegistry[formatName];
3837
3856
  }
3838
- function detectFormat2(path32) {
3839
- const ext = getExtension(path32);
3857
+ function detectFormat2(path37) {
3858
+ const ext = getExtension(path37);
3840
3859
  return extensionMap[ext];
3841
3860
  }
3842
- function getExtension(path32) {
3843
- const lastDot = path32.lastIndexOf(".");
3861
+ function getExtension(path37) {
3862
+ const lastDot = path37.lastIndexOf(".");
3844
3863
  if (lastDot === -1) {
3845
3864
  return "";
3846
3865
  }
3847
- return path32.slice(lastDot).toLowerCase();
3866
+ return path37.slice(lastDot).toLowerCase();
3848
3867
  }
3849
3868
 
3850
3869
  // packages/config-mutations/src/execution/path-utils.ts
@@ -3895,9 +3914,9 @@ function resolvePath(rawPath, homeDir, pathMapper) {
3895
3914
  function isNotFound(error2) {
3896
3915
  return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === "ENOENT";
3897
3916
  }
3898
- async function readFileIfExists2(fs3, target) {
3917
+ async function readFileIfExists2(fs4, target) {
3899
3918
  try {
3900
- return await fs3.readFile(target, "utf8");
3919
+ return await fs4.readFile(target, "utf8");
3901
3920
  } catch (error2) {
3902
3921
  if (isNotFound(error2)) {
3903
3922
  return null;
@@ -3905,9 +3924,9 @@ async function readFileIfExists2(fs3, target) {
3905
3924
  throw error2;
3906
3925
  }
3907
3926
  }
3908
- async function pathExists(fs3, target) {
3927
+ async function pathExists(fs4, target) {
3909
3928
  try {
3910
- await fs3.stat(target);
3929
+ await fs4.stat(target);
3911
3930
  return true;
3912
3931
  } catch (error2) {
3913
3932
  if (isNotFound(error2)) {
@@ -3931,9 +3950,9 @@ function createInvalidDocumentBackupPath(targetPath) {
3931
3950
  const ext = targetPath.includes(".") ? targetPath.split(".").pop() : "bak";
3932
3951
  return `${targetPath}.invalid-${createTimestamp()}.${ext}`;
3933
3952
  }
3934
- async function backupInvalidDocument(fs3, targetPath, content) {
3953
+ async function backupInvalidDocument(fs4, targetPath, content) {
3935
3954
  const backupPath = createInvalidDocumentBackupPath(targetPath);
3936
- await fs3.writeFile(backupPath, content, { encoding: "utf8" });
3955
+ await fs4.writeFile(backupPath, content, { encoding: "utf8" });
3937
3956
  }
3938
3957
  function describeMutation(kind, targetPath) {
3939
3958
  const displayPath = targetPath ?? "target";
@@ -4496,12 +4515,12 @@ function isConfigObject5(value) {
4496
4515
  }
4497
4516
 
4498
4517
  // packages/poe-code-config/src/store.ts
4499
- async function readMergedDocument(fs3, globalPath, projectPath) {
4500
- const globalDocument = await readStoredDocument(fs3, globalPath);
4518
+ async function readMergedDocument(fs4, globalPath, projectPath) {
4519
+ const globalDocument = await readStoredDocument(fs4, globalPath);
4501
4520
  if (!projectPath || projectPath === globalPath) {
4502
4521
  return globalDocument.data;
4503
4522
  }
4504
- const projectDocument = await readStoredDocument(fs3, projectPath);
4523
+ const projectDocument = await readStoredDocument(fs4, projectPath);
4505
4524
  const resolved = await resolve(
4506
4525
  [
4507
4526
  {
@@ -4515,16 +4534,16 @@ async function readMergedDocument(fs3, globalPath, projectPath) {
4515
4534
  }
4516
4535
  ],
4517
4536
  {
4518
- fs: createResolvedConfigFs(fs3, globalPath, globalDocument.content),
4537
+ fs: createResolvedConfigFs(fs4, globalPath, globalDocument.content),
4519
4538
  autoExtend: true
4520
4539
  }
4521
4540
  );
4522
4541
  return normalizeDocument(resolved.data);
4523
4542
  }
4524
- async function readStoredDocument(fs3, filePath) {
4543
+ async function readStoredDocument(fs4, filePath) {
4525
4544
  try {
4526
- const raw = await fs3.readFile(filePath, "utf8");
4527
- return await parseStoredDocument(fs3, filePath, raw);
4545
+ const raw = await fs4.readFile(filePath, "utf8");
4546
+ return await parseStoredDocument(fs4, filePath, raw);
4528
4547
  } catch (error2) {
4529
4548
  if (isNotFound(error2)) {
4530
4549
  return {
@@ -4535,7 +4554,7 @@ async function readStoredDocument(fs3, filePath) {
4535
4554
  throw error2;
4536
4555
  }
4537
4556
  }
4538
- async function parseStoredDocument(fs3, filePath, raw) {
4557
+ async function parseStoredDocument(fs4, filePath, raw) {
4539
4558
  try {
4540
4559
  return {
4541
4560
  content: raw,
@@ -4543,7 +4562,7 @@ async function parseStoredDocument(fs3, filePath, raw) {
4543
4562
  };
4544
4563
  } catch (error2) {
4545
4564
  if (error2 instanceof SyntaxError) {
4546
- await recoverInvalidDocument(fs3, filePath, raw);
4565
+ await recoverInvalidDocument(fs4, filePath, raw);
4547
4566
  return {
4548
4567
  content: EMPTY_DOCUMENT,
4549
4568
  data: {}
@@ -4577,21 +4596,21 @@ function normalizeScopeValues(value) {
4577
4596
  }
4578
4597
  return normalized;
4579
4598
  }
4580
- function createResolvedConfigFs(fs3, globalPath, globalContent) {
4599
+ function createResolvedConfigFs(fs4, globalPath, globalContent) {
4581
4600
  return {
4582
4601
  readFile(filePath, _encoding) {
4583
4602
  if (filePath === globalPath) {
4584
4603
  return Promise.resolve(globalContent);
4585
4604
  }
4586
- return fs3.readFile(filePath, "utf8");
4605
+ return fs4.readFile(filePath, "utf8");
4587
4606
  }
4588
4607
  };
4589
4608
  }
4590
- async function recoverInvalidDocument(fs3, filePath, content) {
4591
- await fs3.mkdir(path14.dirname(filePath), { recursive: true });
4609
+ async function recoverInvalidDocument(fs4, filePath, content) {
4610
+ await fs4.mkdir(path14.dirname(filePath), { recursive: true });
4592
4611
  const backupPath = createInvalidBackupPath(filePath);
4593
- await fs3.writeFile(backupPath, content, { encoding: "utf8" });
4594
- await fs3.writeFile(filePath, EMPTY_DOCUMENT, { encoding: "utf8" });
4612
+ await fs4.writeFile(backupPath, content, { encoding: "utf8" });
4613
+ await fs4.writeFile(filePath, EMPTY_DOCUMENT, { encoding: "utf8" });
4595
4614
  }
4596
4615
  function createInvalidBackupPath(filePath) {
4597
4616
  const directory = path14.dirname(filePath);
@@ -4715,7 +4734,7 @@ function mergeScope(scope, baseScope, overrideScope) {
4715
4734
  ...Object.fromEntries(scopeEntries)
4716
4735
  };
4717
4736
  }
4718
- function mergeRuntimeScope(baseScope, overrideScope, path32 = []) {
4737
+ function mergeRuntimeScope(baseScope, overrideScope, path37 = []) {
4719
4738
  const merged = {};
4720
4739
  const keys = /* @__PURE__ */ new Set([...Object.keys(baseScope), ...Object.keys(overrideScope)]);
4721
4740
  for (const key of keys) {
@@ -4727,20 +4746,20 @@ function mergeRuntimeScope(baseScope, overrideScope, path32 = []) {
4727
4746
  }
4728
4747
  continue;
4729
4748
  }
4730
- if (isRuntimeConcatenativeArray([...path32, key]) && Array.isArray(baseValue) && Array.isArray(overrideValue)) {
4749
+ if (isRuntimeConcatenativeArray([...path37, key]) && Array.isArray(baseValue) && Array.isArray(overrideValue)) {
4731
4750
  merged[key] = [...baseValue, ...overrideValue];
4732
4751
  continue;
4733
4752
  }
4734
4753
  if (isRecord4(baseValue) && isRecord4(overrideValue)) {
4735
- merged[key] = mergeRuntimeScope(baseValue, overrideValue, [...path32, key]);
4754
+ merged[key] = mergeRuntimeScope(baseValue, overrideValue, [...path37, key]);
4736
4755
  continue;
4737
4756
  }
4738
4757
  merged[key] = overrideValue;
4739
4758
  }
4740
4759
  return merged;
4741
4760
  }
4742
- function isRuntimeConcatenativeArray(path32) {
4743
- return path32.join(".") === "mounts" || path32.join(".") === "runner.workspace.exclude";
4761
+ function isRuntimeConcatenativeArray(path37) {
4762
+ return path37.join(".") === "mounts" || path37.join(".") === "runner.workspace.exclude";
4744
4763
  }
4745
4764
  function isRecord4(value) {
4746
4765
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
@@ -4928,6 +4947,7 @@ var poeProvider = {
4928
4947
  label: "Poe",
4929
4948
  summary: "Route AI coding agents through Poe's API.",
4930
4949
  baseUrl: "https://api.poe.com",
4950
+ agentBaseUrl: "https://api.poe.com",
4931
4951
  auth: {
4932
4952
  kind: "api-key",
4933
4953
  envVar: "POE_API_KEY",
@@ -4935,6 +4955,12 @@ var poeProvider = {
4935
4955
  prompt: { title: "Poe API key" },
4936
4956
  preferredLogin: "oauth"
4937
4957
  },
4958
+ env: {
4959
+ ANTHROPIC_CUSTOM_HEADERS: {
4960
+ kind: "providerCredential",
4961
+ prefix: "Authorization: Bearer "
4962
+ }
4963
+ },
4938
4964
  apiShapes: [
4939
4965
  {
4940
4966
  id: "openai-chat-completions",
@@ -4963,6 +4989,9 @@ var anthropicProvider = {
4963
4989
  storageKey: "provider:anthropic",
4964
4990
  prompt: { title: "Anthropic API key" }
4965
4991
  },
4992
+ env: {
4993
+ ANTHROPIC_API_KEY: { kind: "providerCredential" }
4994
+ },
4966
4995
  apiShapes: [
4967
4996
  {
4968
4997
  id: "anthropic-messages",
@@ -4985,6 +5014,12 @@ var cloudflareProvider = {
4985
5014
  storageKey: "provider:cloudflare",
4986
5015
  prompt: { title: "Cloudflare AI Gateway token" }
4987
5016
  },
5017
+ env: {
5018
+ ANTHROPIC_CUSTOM_HEADERS: {
5019
+ kind: "providerCredential",
5020
+ prefix: "Authorization: Bearer "
5021
+ }
5022
+ },
4988
5023
  apiShapes: [
4989
5024
  {
4990
5025
  id: "openai-chat-completions",
@@ -5029,7 +5064,7 @@ function isNotFoundError(error2) {
5029
5064
  }
5030
5065
 
5031
5066
  // packages/poe-code-config/src/state/jobs.ts
5032
- function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5067
+ function createJobRegistry(homeDir, fs4 = defaultStateFs) {
5033
5068
  const jobsDir = path17.join(homeDir, ".poe-code", "state", "jobs");
5034
5069
  function jobPath(id) {
5035
5070
  assertSafeJobId(id);
@@ -5037,7 +5072,7 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5037
5072
  }
5038
5073
  async function get(id) {
5039
5074
  try {
5040
- return parseJobEntry(await fs3.readFile(jobPath(id), "utf8"));
5075
+ return parseJobEntry(await fs4.readFile(jobPath(id), "utf8"));
5041
5076
  } catch (error2) {
5042
5077
  if (isNotFoundError(error2)) {
5043
5078
  return null;
@@ -5048,8 +5083,8 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5048
5083
  async function put(entry) {
5049
5084
  assertJobEntry(entry);
5050
5085
  const filePath = jobPath(entry.id);
5051
- await fs3.mkdir(jobsDir, { recursive: true });
5052
- const release = await acquireFileLock(filePath, { fs: fs3 });
5086
+ await fs4.mkdir(jobsDir, { recursive: true });
5087
+ const release = await acquireFileLock(filePath, { fs: fs4 });
5053
5088
  try {
5054
5089
  await writeJobAtomically(filePath, entry);
5055
5090
  } finally {
@@ -5058,8 +5093,8 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5058
5093
  }
5059
5094
  async function update(id, patch) {
5060
5095
  const filePath = jobPath(id);
5061
- await fs3.mkdir(jobsDir, { recursive: true });
5062
- const release = await acquireFileLock(filePath, { fs: fs3 });
5096
+ await fs4.mkdir(jobsDir, { recursive: true });
5097
+ const release = await acquireFileLock(filePath, { fs: fs4 });
5063
5098
  try {
5064
5099
  const current = await get(id);
5065
5100
  if (current === null) {
@@ -5080,7 +5115,7 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5080
5115
  async function list(filter = {}) {
5081
5116
  let entries;
5082
5117
  try {
5083
- entries = await fs3.readdir(jobsDir);
5118
+ entries = await fs4.readdir(jobsDir);
5084
5119
  } catch (error2) {
5085
5120
  if (isNotFoundError(error2)) {
5086
5121
  return [];
@@ -5093,11 +5128,11 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5093
5128
  continue;
5094
5129
  }
5095
5130
  const filePath = path17.join(jobsDir, entry);
5096
- const stat3 = await fs3.stat(filePath);
5131
+ const stat3 = await fs4.stat(filePath);
5097
5132
  if (!stat3.isFile()) {
5098
5133
  continue;
5099
5134
  }
5100
- const job = parseJobEntry(await fs3.readFile(filePath, "utf8"));
5135
+ const job = parseJobEntry(await fs4.readFile(filePath, "utf8"));
5101
5136
  if (matchesFilter(job, filter)) {
5102
5137
  jobs.push(job);
5103
5138
  }
@@ -5107,16 +5142,16 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5107
5142
  async function remove2(id) {
5108
5143
  const filePath = jobPath(id);
5109
5144
  try {
5110
- await fs3.stat(jobsDir);
5145
+ await fs4.stat(jobsDir);
5111
5146
  } catch (error2) {
5112
5147
  if (isNotFoundError(error2)) {
5113
5148
  return;
5114
5149
  }
5115
5150
  throw error2;
5116
5151
  }
5117
- const release = await acquireFileLock(filePath, { fs: fs3 });
5152
+ const release = await acquireFileLock(filePath, { fs: fs4 });
5118
5153
  try {
5119
- await fs3.unlink(filePath);
5154
+ await fs4.unlink(filePath);
5120
5155
  } catch (error2) {
5121
5156
  if (!isNotFoundError(error2)) {
5122
5157
  throw error2;
@@ -5126,14 +5161,14 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5126
5161
  }
5127
5162
  }
5128
5163
  async function writeJobAtomically(filePath, entry) {
5129
- await fs3.mkdir(path17.dirname(filePath), { recursive: true });
5164
+ await fs4.mkdir(path17.dirname(filePath), { recursive: true });
5130
5165
  const tempPath = `${filePath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
5131
5166
  try {
5132
- await fs3.writeFile(tempPath, `${JSON.stringify(entry, null, 2)}
5167
+ await fs4.writeFile(tempPath, `${JSON.stringify(entry, null, 2)}
5133
5168
  `, {
5134
5169
  encoding: "utf8"
5135
5170
  });
5136
- await fs3.rename(tempPath, filePath);
5171
+ await fs4.rename(tempPath, filePath);
5137
5172
  } catch (error2) {
5138
5173
  await removeTempFile(tempPath);
5139
5174
  throw error2;
@@ -5141,7 +5176,7 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5141
5176
  }
5142
5177
  async function removeTempFile(tempPath) {
5143
5178
  try {
5144
- await fs3.unlink(tempPath);
5179
+ await fs4.unlink(tempPath);
5145
5180
  } catch (error2) {
5146
5181
  if (!isNotFoundError(error2)) {
5147
5182
  throw error2;
@@ -5188,11 +5223,11 @@ function isRecord5(value) {
5188
5223
 
5189
5224
  // packages/poe-code-config/src/state/templates.ts
5190
5225
  import path18 from "node:path";
5191
- function createTemplateRegistry(homeDir, fs3 = defaultStateFs) {
5226
+ function createTemplateRegistry(homeDir, fs4 = defaultStateFs) {
5192
5227
  const filePath = path18.join(homeDir, ".poe-code", "state", "templates.json");
5193
5228
  async function readState() {
5194
5229
  try {
5195
- const raw = await fs3.readFile(filePath, "utf8");
5230
+ const raw = await fs4.readFile(filePath, "utf8");
5196
5231
  return normalizeTemplateState(JSON.parse(raw));
5197
5232
  } catch (error2) {
5198
5233
  if (isNotFoundError(error2)) {
@@ -5202,14 +5237,14 @@ function createTemplateRegistry(homeDir, fs3 = defaultStateFs) {
5202
5237
  }
5203
5238
  }
5204
5239
  async function writeState(state) {
5205
- await fs3.writeFile(filePath, `${JSON.stringify(state, null, 2)}
5240
+ await fs4.writeFile(filePath, `${JSON.stringify(state, null, 2)}
5206
5241
  `, {
5207
5242
  encoding: "utf8"
5208
5243
  });
5209
5244
  }
5210
5245
  async function updateState(mutator) {
5211
- await fs3.mkdir(path18.dirname(filePath), { recursive: true });
5212
- const release = await acquireFileLock(filePath, { fs: fs3 });
5246
+ await fs4.mkdir(path18.dirname(filePath), { recursive: true });
5247
+ const release = await acquireFileLock(filePath, { fs: fs4 });
5213
5248
  try {
5214
5249
  const state = await readState();
5215
5250
  mutator(state);
@@ -5279,10 +5314,10 @@ function isRecord6(value) {
5279
5314
  }
5280
5315
 
5281
5316
  // packages/poe-code-config/src/state/index.ts
5282
- function createStateManager(homeDir, fs3) {
5317
+ function createStateManager(homeDir, fs4) {
5283
5318
  return {
5284
- templates: createTemplateRegistry(homeDir, fs3),
5285
- jobs: createJobRegistry(homeDir, fs3)
5319
+ templates: createTemplateRegistry(homeDir, fs4),
5320
+ jobs: createJobRegistry(homeDir, fs4)
5286
5321
  };
5287
5322
  }
5288
5323
 
@@ -5597,7 +5632,7 @@ import { PassThrough as PassThrough2, Writable as Writable2 } from "node:stream"
5597
5632
  import path21 from "node:path";
5598
5633
  var JOB_DIR2 = "/tmp/poe-jobs";
5599
5634
  function createE2bJobHandle(input) {
5600
- const fs3 = createE2bLogStreamFs(input.sandbox);
5635
+ const fs4 = createE2bLogStreamFs(input.sandbox);
5601
5636
  return {
5602
5637
  id: input.jobId,
5603
5638
  envId: input.envId,
@@ -5613,10 +5648,10 @@ function createE2bJobHandle(input) {
5613
5648
  return isRunning ? "running" : "lost";
5614
5649
  },
5615
5650
  stream(opts = {}) {
5616
- return streamLogFile({ fs: fs3 }, input.jobId, opts);
5651
+ return streamLogFile({ fs: fs4 }, input.jobId, opts);
5617
5652
  },
5618
5653
  async wait() {
5619
- const result = await waitForExit({ fs: fs3 }, input.jobId);
5654
+ const result = await waitForExit({ fs: fs4 }, input.jobId);
5620
5655
  const preserveMs = input.preserveAfterExitHours * 60 * 60 * 1e3;
5621
5656
  if (preserveMs > 0) {
5622
5657
  await input.sandbox.setTimeout(preserveMs);
@@ -6113,10 +6148,10 @@ var e2bAuthScope = defineScope("e2b", {
6113
6148
  });
6114
6149
  async function resolveE2bApiKey(input) {
6115
6150
  const homeDir = input.homeDir ?? os4.homedir();
6116
- const fs3 = input.fs ?? nodeFs4;
6151
+ const fs4 = input.fs ?? nodeFs4;
6117
6152
  const env = input.env ?? process.env;
6118
6153
  const document = await readMergedDocument(
6119
- fs3,
6154
+ fs4,
6120
6155
  resolveConfigPath(homeDir),
6121
6156
  resolveProjectConfigPath(input.cwd)
6122
6157
  );
@@ -6623,6 +6658,23 @@ var gooseAcpSpawnConfig = {
6623
6658
  skipAuth: true
6624
6659
  };
6625
6660
 
6661
+ // packages/agent-spawn/src/configs/gemini-cli.ts
6662
+ var geminiCliAcpSpawnConfig = {
6663
+ kind: "acp",
6664
+ agentId: "gemini-cli",
6665
+ acpArgs: ({ model, mcpServers }) => [
6666
+ "--acp",
6667
+ ...model ? ["--model", model] : [],
6668
+ ...mcpServers ? ["--allowed-mcp-server-names", Object.keys(mcpServers).join(",")] : [],
6669
+ ...mcpServers ? ["--skip-trust"] : [],
6670
+ "--yolo"
6671
+ ],
6672
+ env: {
6673
+ GEMINI_SANDBOX: "false"
6674
+ },
6675
+ skipAuth: true
6676
+ };
6677
+
6626
6678
  // packages/agent-spawn/src/configs/index.ts
6627
6679
  var allSpawnConfigs = [
6628
6680
  claudeCodeSpawnConfig,
@@ -6639,6 +6691,7 @@ var acpLookup = /* @__PURE__ */ new Map();
6639
6691
  acpLookup.set(openCodeAcpSpawnConfig.agentId, openCodeAcpSpawnConfig);
6640
6692
  acpLookup.set(kimiAcpSpawnConfig.agentId, kimiAcpSpawnConfig);
6641
6693
  acpLookup.set(gooseAcpSpawnConfig.agentId, gooseAcpSpawnConfig);
6694
+ acpLookup.set(geminiCliAcpSpawnConfig.agentId, geminiCliAcpSpawnConfig);
6642
6695
  function getSpawnConfig(input) {
6643
6696
  const resolvedId = resolveAgentId(input);
6644
6697
  if (!resolvedId) {
@@ -6658,8 +6711,8 @@ function listMcpSupportedAgents() {
6658
6711
  }
6659
6712
 
6660
6713
  // packages/agent-spawn/src/spawn.ts
6661
- import { mkdirSync as mkdirSync3, openSync, writeSync, closeSync } from "node:fs";
6662
- import path29 from "node:path";
6714
+ import { mkdirSync as mkdirSync5, openSync as openSync2, writeSync, closeSync as closeSync2 } from "node:fs";
6715
+ import path34 from "node:path";
6663
6716
 
6664
6717
  // packages/agent-spawn/src/configs/resolve-config.ts
6665
6718
  function resolveConfig(agentId) {
@@ -7060,7 +7113,7 @@ function createEventQueue() {
7060
7113
 
7061
7114
  // packages/agent-spawn/src/skill-bridge.ts
7062
7115
  import crypto from "node:crypto";
7063
- import os6 from "node:os";
7116
+ import os7 from "node:os";
7064
7117
 
7065
7118
  // packages/agent-skill-config/src/configs.ts
7066
7119
  import os5 from "node:os";
@@ -7074,6 +7127,10 @@ var agentSkillConfigs = {
7074
7127
  globalSkillDir: "~/.codex/skills",
7075
7128
  localSkillDir: ".codex/skills"
7076
7129
  },
7130
+ "gemini-cli": {
7131
+ globalSkillDir: "~/.gemini/skills",
7132
+ localSkillDir: ".gemini/skills"
7133
+ },
7077
7134
  opencode: {
7078
7135
  globalSkillDir: "~/.config/opencode/skills",
7079
7136
  localSkillDir: ".opencode/skills"
@@ -7216,7 +7273,7 @@ function resolveSkillReference(ref, cwd, homeDir) {
7216
7273
  import { execFileSync } from "node:child_process";
7217
7274
  import * as fs from "node:fs";
7218
7275
  import path27 from "node:path";
7219
- var markerPrefix = "# poe-code-spawn-skills:";
7276
+ var defaultMarkerPrefix = "poe-code-spawn-skills";
7220
7277
  function defaultGitDirRunner(cwd) {
7221
7278
  try {
7222
7279
  return execFileSync("git", ["rev-parse", "--git-dir"], {
@@ -7236,10 +7293,10 @@ function resolveExcludePath(cwd) {
7236
7293
  }
7237
7294
  return path27.join(path27.isAbsolute(gitDir) ? gitDir : path27.resolve(cwd, gitDir), "info/exclude");
7238
7295
  }
7239
- function markers(runId) {
7296
+ function markers(runId, markerPrefix) {
7240
7297
  return {
7241
- begin: `${markerPrefix}${runId} begin`,
7242
- end: `${markerPrefix}${runId} end`
7298
+ begin: `# ${markerPrefix}:${runId} begin`,
7299
+ end: `# ${markerPrefix}:${runId} end`
7243
7300
  };
7244
7301
  }
7245
7302
  function readExcludeFile(excludePath) {
@@ -7255,8 +7312,8 @@ function readExcludeFile(excludePath) {
7255
7312
  function isNodeError2(error2) {
7256
7313
  return error2 instanceof Error && "code" in error2;
7257
7314
  }
7258
- function removeBlock(content, runId) {
7259
- const { begin, end } = markers(runId);
7315
+ function removeBlock(content, runId, markerPrefix) {
7316
+ const { begin, end } = markers(runId, markerPrefix);
7260
7317
  const lines = content.split("\n");
7261
7318
  const result = [];
7262
7319
  for (let index = 0; index < lines.length; index += 1) {
@@ -7271,23 +7328,27 @@ function removeBlock(content, runId) {
7271
7328
  }
7272
7329
  return result.join("\n");
7273
7330
  }
7274
- function appendBlock(content, runId, entries) {
7275
- const { begin, end } = markers(runId);
7331
+ function appendBlock(content, runId, entries, markerPrefix) {
7332
+ const { begin, end } = markers(runId, markerPrefix);
7276
7333
  const existing = content ?? "";
7277
7334
  const prefix = existing.length === 0 || existing.endsWith("\n") ? existing : `${existing}
7278
7335
  `;
7279
7336
  return `${prefix}${[begin, ...entries, end, ""].join("\n")}`;
7280
7337
  }
7281
- function appendExcludeBlock(cwd, runId, entries) {
7338
+ function appendExcludeBlock(cwd, runId, entries, opts) {
7282
7339
  const excludePath = resolveExcludePath(cwd);
7283
7340
  if (excludePath === void 0) {
7284
7341
  return;
7285
7342
  }
7286
7343
  fs.mkdirSync(path27.dirname(excludePath), { recursive: true });
7287
7344
  const content = readExcludeFile(excludePath);
7288
- fs.writeFileSync(excludePath, appendBlock(content, runId, entries), "utf8");
7345
+ fs.writeFileSync(
7346
+ excludePath,
7347
+ appendBlock(content, runId, entries, opts?.markerPrefix ?? defaultMarkerPrefix),
7348
+ "utf8"
7349
+ );
7289
7350
  }
7290
- function removeExcludeBlock(cwd, runId) {
7351
+ function removeExcludeBlock(cwd, runId, opts) {
7291
7352
  const excludePath = resolveExcludePath(cwd);
7292
7353
  if (excludePath === void 0) {
7293
7354
  return;
@@ -7296,7 +7357,11 @@ function removeExcludeBlock(cwd, runId) {
7296
7357
  if (content === void 0) {
7297
7358
  return;
7298
7359
  }
7299
- fs.writeFileSync(excludePath, removeBlock(content, runId), "utf8");
7360
+ fs.writeFileSync(
7361
+ excludePath,
7362
+ removeBlock(content, runId, opts?.markerPrefix ?? defaultMarkerPrefix),
7363
+ "utf8"
7364
+ );
7300
7365
  }
7301
7366
 
7302
7367
  // packages/agent-skill-config/src/bridge-active-skills.ts
@@ -7502,22 +7567,679 @@ function cleanupBridgedSkills(manifest) {
7502
7567
  removeExcludeBlock(manifest.cwd, manifest.runId);
7503
7568
  }
7504
7569
 
7570
+ // packages/agent-hook-config/src/configs.ts
7571
+ import os6 from "node:os";
7572
+ import path29 from "node:path";
7573
+ var agentHookConfigs = {
7574
+ "claude-code": {
7575
+ globalHookPath: "~/.claude/settings.json",
7576
+ localHookPath: ".claude/settings.json",
7577
+ format: "claude-settings-json",
7578
+ supportedEvents: [
7579
+ "SessionStart",
7580
+ "SessionEnd",
7581
+ "UserPromptSubmit",
7582
+ "PreToolUse",
7583
+ "PostToolUse",
7584
+ "PermissionRequest",
7585
+ "Stop",
7586
+ "StopFailure",
7587
+ "Notification",
7588
+ "PreCompact",
7589
+ "PostCompact",
7590
+ "SubagentStart",
7591
+ "SubagentStop"
7592
+ ],
7593
+ supportedHandlerTypes: ["command", "http", "mcp_tool", "prompt", "agent"],
7594
+ placeholders: {
7595
+ projectDir: "${CLAUDE_PROJECT_DIR}",
7596
+ pluginRoot: "${CLAUDE_PLUGIN_ROOT}",
7597
+ pluginData: "${CLAUDE_PLUGIN_DATA}"
7598
+ }
7599
+ },
7600
+ codex: {
7601
+ globalHookPath: "~/.codex/hooks.json",
7602
+ localHookPath: ".codex/hooks.json",
7603
+ format: "codex-hooks-json",
7604
+ supportedEvents: [
7605
+ "SessionStart",
7606
+ "UserPromptSubmit",
7607
+ "PreToolUse",
7608
+ "PostToolUse",
7609
+ "PermissionRequest",
7610
+ "Stop"
7611
+ ],
7612
+ supportedHandlerTypes: ["command"],
7613
+ placeholders: {
7614
+ projectDir: "$(git rev-parse --show-toplevel)",
7615
+ pluginRoot: "$PLUGIN_ROOT",
7616
+ pluginData: "$PLUGIN_DATA"
7617
+ }
7618
+ }
7619
+ };
7620
+ var supportedHookAgents = Object.keys(agentHookConfigs);
7621
+ function resolveAgentSupport2(input, registry = agentHookConfigs) {
7622
+ const resolvedId = resolveAgentId(input);
7623
+ if (!resolvedId) {
7624
+ return { status: "unknown", input };
7625
+ }
7626
+ const config = registry[resolvedId];
7627
+ if (!config) {
7628
+ return { status: "unsupported", input, id: resolvedId };
7629
+ }
7630
+ return { status: "supported", input, id: resolvedId, config };
7631
+ }
7632
+ function getAgentConfig2(agentId) {
7633
+ const support = resolveAgentSupport2(agentId);
7634
+ return support.status === "supported" ? support.config : void 0;
7635
+ }
7636
+ function expandHome3(targetPath, homeDir = os6.homedir()) {
7637
+ if (!targetPath?.startsWith("~")) {
7638
+ return targetPath;
7639
+ }
7640
+ if (targetPath === "~") {
7641
+ return homeDir;
7642
+ }
7643
+ if (targetPath.startsWith("~./")) {
7644
+ targetPath = `~/.${targetPath.slice(3)}`;
7645
+ }
7646
+ let remainder = targetPath.slice(1);
7647
+ if (remainder.startsWith("/") || remainder.startsWith("\\")) {
7648
+ remainder = remainder.slice(1);
7649
+ } else if (remainder.startsWith(".")) {
7650
+ remainder = remainder.slice(1);
7651
+ if (remainder.startsWith("/") || remainder.startsWith("\\")) {
7652
+ remainder = remainder.slice(1);
7653
+ }
7654
+ }
7655
+ return remainder.length === 0 ? homeDir : path29.join(homeDir, remainder);
7656
+ }
7657
+ function resolveHookPath(config, scope, cwd, homeDir) {
7658
+ if (scope === "global") {
7659
+ return path29.resolve(expandHome3(config.globalHookPath, homeDir));
7660
+ }
7661
+ return config.localHookPath ? path29.resolve(cwd, config.localHookPath) : void 0;
7662
+ }
7663
+
7664
+ // packages/agent-hook-config/src/read-hooks.ts
7665
+ import { readFileSync as readFileSync3 } from "node:fs";
7666
+ import path30 from "node:path";
7667
+ function readSettingsFile(filePath) {
7668
+ let content;
7669
+ try {
7670
+ content = readFileSync3(filePath, "utf8");
7671
+ } catch (error2) {
7672
+ if (error2.code === "ENOENT") {
7673
+ return void 0;
7674
+ }
7675
+ throw error2;
7676
+ }
7677
+ try {
7678
+ return JSON.parse(content);
7679
+ } catch (error2) {
7680
+ throw new Error(`Malformed JSON in ${filePath}`, { cause: error2 });
7681
+ }
7682
+ }
7683
+ function readClaudeHooks(cwd, homeDir, opts) {
7684
+ const projectPath = path30.resolve(cwd, ".claude/settings.json");
7685
+ const userPath = path30.resolve(homeDir, ".claude/settings.json");
7686
+ const scope = opts?.scope ?? "merged";
7687
+ const sourcePaths = scope === "project" ? [projectPath] : scope === "user" ? [userPath] : [userPath, projectPath];
7688
+ const result = { entries: [], readPaths: [] };
7689
+ for (const sourcePath of sourcePaths) {
7690
+ const settings = readSettingsFile(sourcePath);
7691
+ if (settings === void 0) {
7692
+ continue;
7693
+ }
7694
+ result.readPaths.push(sourcePath);
7695
+ for (const [event, groups] of Object.entries(settings.hooks ?? {})) {
7696
+ for (const group of groups) {
7697
+ for (const handler of group.hooks) {
7698
+ result.entries.push({ event, matcher: group.matcher, handler });
7699
+ }
7700
+ }
7701
+ }
7702
+ }
7703
+ return result;
7704
+ }
7705
+
7706
+ // packages/agent-hook-config/src/event-mapping.ts
7707
+ function requireAgentConfig(agentId) {
7708
+ const config = getAgentConfig2(agentId);
7709
+ if (!config) {
7710
+ throw new Error(`Unknown hook agent "${agentId}"`);
7711
+ }
7712
+ return config;
7713
+ }
7714
+ function getEventMappings(sourceAgentId, targetAgentId) {
7715
+ const source = requireAgentConfig(sourceAgentId);
7716
+ const target = requireAgentConfig(targetAgentId);
7717
+ return source.supportedEvents.map((sourceEvent) => {
7718
+ if (target.supportedEvents.includes(sourceEvent)) {
7719
+ return { sourceEvent, targetEvent: sourceEvent };
7720
+ }
7721
+ return {
7722
+ sourceEvent,
7723
+ targetEvent: null,
7724
+ dropReason: `${targetAgentId} has no ${sourceEvent} hook`
7725
+ };
7726
+ });
7727
+ }
7728
+ function getHandlerTypeRules(targetAgentId) {
7729
+ const target = requireAgentConfig(targetAgentId);
7730
+ const registeredTypes = supportedHookAgents.flatMap(
7731
+ (agentId) => requireAgentConfig(agentId).supportedHandlerTypes
7732
+ );
7733
+ const sourceTypes = [...new Set(registeredTypes)];
7734
+ const supportedTypes = target.supportedHandlerTypes.map((handlerType) => `"${handlerType}"`).join(", ");
7735
+ return sourceTypes.map((sourceType) => {
7736
+ if (target.supportedHandlerTypes.includes(sourceType)) {
7737
+ return { sourceType, allowed: true };
7738
+ }
7739
+ return {
7740
+ sourceType,
7741
+ allowed: false,
7742
+ dropReason: `${targetAgentId} only honors handlers of type ${supportedTypes}`
7743
+ };
7744
+ });
7745
+ }
7746
+ function getPlaceholderRewrites(sourceAgentId, targetAgentId) {
7747
+ const source = requireAgentConfig(sourceAgentId);
7748
+ const target = requireAgentConfig(targetAgentId);
7749
+ return Object.keys(source.placeholders).flatMap((key) => {
7750
+ const from = source.placeholders[key];
7751
+ const to = target.placeholders[key];
7752
+ if (!from || !to || from === to) {
7753
+ return [];
7754
+ }
7755
+ return [{ from, to }];
7756
+ });
7757
+ }
7758
+
7759
+ // packages/agent-hook-config/src/transform-hooks.ts
7760
+ function applyPlaceholderRewrites(value, rewrites) {
7761
+ return rewrites.reduce((rewrittenValue, rewrite) => {
7762
+ return rewrittenValue.replaceAll(rewrite.from, rewrite.to);
7763
+ }, value);
7764
+ }
7765
+ function transformHooks(source, sourceAgentId, targetAgentId, opts) {
7766
+ const eventMappings = getEventMappings(sourceAgentId, targetAgentId);
7767
+ const handlerRules = getHandlerTypeRules(targetAgentId);
7768
+ const placeholderRewrites = getPlaceholderRewrites(sourceAgentId, targetAgentId);
7769
+ const result = { entries: [], drops: [] };
7770
+ for (const sourceEntry of source) {
7771
+ const eventMapping = eventMappings.find((mapping) => mapping.sourceEvent === sourceEntry.event);
7772
+ if (!eventMapping || eventMapping.targetEvent === null) {
7773
+ result.drops.push({
7774
+ reason: "unsupported-event",
7775
+ detail: eventMapping?.dropReason ?? `${targetAgentId} has no ${sourceEntry.event} hook`,
7776
+ source: sourceEntry
7777
+ });
7778
+ continue;
7779
+ }
7780
+ const handlerRule = handlerRules.find((rule) => rule.sourceType === sourceEntry.handler.type);
7781
+ if (!handlerRule?.allowed) {
7782
+ result.drops.push({
7783
+ reason: "unsupported-handler-type",
7784
+ detail: `Unsupported handler type "${sourceEntry.handler.type}": ${handlerRule?.dropReason ?? `${targetAgentId} does not honor it`}`,
7785
+ source: sourceEntry
7786
+ });
7787
+ continue;
7788
+ }
7789
+ const handler = {
7790
+ type: "command",
7791
+ command: applyPlaceholderRewrites(sourceEntry.handler.command ?? "", placeholderRewrites),
7792
+ statusMessage: `[generated:${opts.runId}] ${sourceEntry.handler.statusMessage ?? ""}`
7793
+ };
7794
+ if (sourceEntry.handler.args !== void 0) {
7795
+ handler.args = sourceEntry.handler.args.map(
7796
+ (arg) => applyPlaceholderRewrites(arg, placeholderRewrites)
7797
+ );
7798
+ }
7799
+ if (sourceEntry.handler.timeout !== void 0) {
7800
+ handler.timeout = sourceEntry.handler.timeout;
7801
+ }
7802
+ result.entries.push({
7803
+ event: eventMapping.targetEvent,
7804
+ matcher: sourceEntry.matcher,
7805
+ handler,
7806
+ generatedId: `generated-${opts.runId}-${result.entries.length}`
7807
+ });
7808
+ }
7809
+ return result;
7810
+ }
7811
+
7812
+ // packages/agent-hook-config/src/write-hooks.ts
7813
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync4, renameSync, writeFileSync as writeFileSync2 } from "node:fs";
7814
+ import path31 from "node:path";
7815
+ function isGeneratedHandler(handler) {
7816
+ return handler.statusMessage?.startsWith("[generated:") ?? false;
7817
+ }
7818
+ function parseHooksFile(targetPath) {
7819
+ let content;
7820
+ try {
7821
+ content = readFileSync4(targetPath, "utf8");
7822
+ } catch (error2) {
7823
+ if (error2.code === "ENOENT") {
7824
+ return { file: { hooks: {} }, fileCreated: true };
7825
+ }
7826
+ throw error2;
7827
+ }
7828
+ try {
7829
+ return { file: JSON.parse(content), fileCreated: false };
7830
+ } catch (error2) {
7831
+ throw new Error(`Malformed JSON in ${targetPath}`, { cause: error2 });
7832
+ }
7833
+ }
7834
+ function validateEntries(entries) {
7835
+ for (const entry of entries) {
7836
+ if (!isGeneratedHandler(entry.handler)) {
7837
+ throw new Error(
7838
+ `Generated hook entry "${entry.generatedId}" has statusMessage that must start with "[generated:"`
7839
+ );
7840
+ }
7841
+ }
7842
+ }
7843
+ function removeGeneratedHandlers(file) {
7844
+ let removed = 0;
7845
+ const hooks = file.hooks ?? (file.hooks = {});
7846
+ for (const [event, groups] of Object.entries(hooks)) {
7847
+ hooks[event] = groups.filter((group) => {
7848
+ const initialCount = group.hooks.length;
7849
+ const remainingHandlers = group.hooks.filter((handler) => {
7850
+ if (isGeneratedHandler(handler)) {
7851
+ removed += 1;
7852
+ return false;
7853
+ }
7854
+ return true;
7855
+ });
7856
+ group.hooks = remainingHandlers;
7857
+ return group.hooks.length > 0 || group.hooks.length === initialCount;
7858
+ });
7859
+ }
7860
+ return removed;
7861
+ }
7862
+ function appendEntries(file, entries) {
7863
+ const hooks = file.hooks ?? (file.hooks = {});
7864
+ for (const entry of entries) {
7865
+ const groups = hooks[entry.event] ?? (hooks[entry.event] = []);
7866
+ let group = groups.find((candidate) => candidate.matcher === entry.matcher);
7867
+ if (!group) {
7868
+ group = entry.matcher === void 0 ? { hooks: [] } : { matcher: entry.matcher, hooks: [] };
7869
+ groups.push(group);
7870
+ }
7871
+ group.hooks.push(entry.handler);
7872
+ }
7873
+ }
7874
+ function writeCodexHooks(targetPath, entries, _runId) {
7875
+ const { file, fileCreated } = parseHooksFile(targetPath);
7876
+ validateEntries(entries);
7877
+ const previousGeneratedRemoved = removeGeneratedHandlers(file);
7878
+ appendEntries(file, entries);
7879
+ mkdirSync3(path31.dirname(targetPath), { recursive: true });
7880
+ const temporaryPath = `${targetPath}.tmp`;
7881
+ writeFileSync2(temporaryPath, `${JSON.stringify(file, null, 2)}
7882
+ `);
7883
+ renameSync(temporaryPath, targetPath);
7884
+ return {
7885
+ path: targetPath,
7886
+ fileCreated,
7887
+ previousGeneratedRemoved,
7888
+ generatedWritten: entries.length
7889
+ };
7890
+ }
7891
+
7892
+ // packages/agent-hook-config/src/symlink-hooks.ts
7893
+ import {
7894
+ closeSync,
7895
+ lstatSync,
7896
+ mkdirSync as mkdirSync4,
7897
+ openSync,
7898
+ readlinkSync,
7899
+ readSync,
7900
+ symlinkSync,
7901
+ unlinkSync
7902
+ } from "node:fs";
7903
+ import path32 from "node:path";
7904
+ function requireAgentConfig2(agentId) {
7905
+ const config = getAgentConfig2(agentId);
7906
+ if (!config) {
7907
+ throw new Error(`No hook configuration found for agent "${agentId}"`);
7908
+ }
7909
+ return config;
7910
+ }
7911
+ function resolveScopedPath(config, agentId, cwd, homeDir, scope) {
7912
+ const targetPath = resolveHookPath(
7913
+ config,
7914
+ scope === "project" ? "local" : "global",
7915
+ cwd,
7916
+ homeDir
7917
+ );
7918
+ if (!targetPath) {
7919
+ throw new Error(`Agent "${agentId}" has no ${scope} hook path`);
7920
+ }
7921
+ return targetPath;
7922
+ }
7923
+ function readFirstKilobyte(filePath) {
7924
+ const descriptor = openSync(filePath, "r");
7925
+ const buffer = Buffer.alloc(1024);
7926
+ try {
7927
+ const length = readSync(descriptor, buffer, 0, buffer.length, 0);
7928
+ return buffer.toString("utf8", 0, length);
7929
+ } finally {
7930
+ closeSync(descriptor);
7931
+ }
7932
+ }
7933
+ function isRecord7(value) {
7934
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7935
+ }
7936
+ function isFullyGeneratedFile(filePath) {
7937
+ let parsed;
7938
+ try {
7939
+ parsed = JSON.parse(readFirstKilobyte(filePath));
7940
+ } catch {
7941
+ return false;
7942
+ }
7943
+ if (!isRecord7(parsed)) {
7944
+ return false;
7945
+ }
7946
+ if (Object.keys(parsed).some((key) => key !== "hooks")) {
7947
+ return false;
7948
+ }
7949
+ const hooks = parsed.hooks;
7950
+ if (!isRecord7(hooks)) {
7951
+ return false;
7952
+ }
7953
+ let handlerFound = false;
7954
+ for (const groups of Object.values(hooks)) {
7955
+ if (!Array.isArray(groups)) {
7956
+ return false;
7957
+ }
7958
+ for (const group of groups) {
7959
+ if (!isRecord7(group) || !Array.isArray(group.hooks)) {
7960
+ return false;
7961
+ }
7962
+ for (const handler of group.hooks) {
7963
+ if (!isRecord7(handler)) {
7964
+ return false;
7965
+ }
7966
+ handlerFound = true;
7967
+ const statusMessage = handler.statusMessage;
7968
+ if (typeof statusMessage !== "string" || !statusMessage.startsWith("[generated:")) {
7969
+ return false;
7970
+ }
7971
+ }
7972
+ }
7973
+ }
7974
+ return handlerFound;
7975
+ }
7976
+ function symlinkHooks(sourceAgentId, targetAgentId, cwd, homeDir, scope) {
7977
+ const source = requireAgentConfig2(sourceAgentId);
7978
+ const target = requireAgentConfig2(targetAgentId);
7979
+ if (source.format !== target.format) {
7980
+ throw new Error(
7981
+ `Cannot symlink hook formats "${source.format}" and "${target.format}"; use transformation instead`
7982
+ );
7983
+ }
7984
+ const targetPath = resolveScopedPath(source, sourceAgentId, cwd, homeDir, scope);
7985
+ const symlinkPath = resolveScopedPath(target, targetAgentId, cwd, homeDir, scope);
7986
+ let replaced = "none";
7987
+ try {
7988
+ const existing = lstatSync(symlinkPath);
7989
+ if (existing.isSymbolicLink()) {
7990
+ if (readlinkSync(symlinkPath) === targetPath) {
7991
+ return { symlinkPath, targetPath, replaced };
7992
+ }
7993
+ unlinkSync(symlinkPath);
7994
+ replaced = "stale-symlink";
7995
+ } else if (existing.isFile() && isFullyGeneratedFile(symlinkPath)) {
7996
+ unlinkSync(symlinkPath);
7997
+ replaced = "generated-file";
7998
+ } else {
7999
+ throw new Error(`Refuse to replace user-authored hook file at ${symlinkPath}`);
8000
+ }
8001
+ } catch (error2) {
8002
+ if (error2.code !== "ENOENT") {
8003
+ throw error2;
8004
+ }
8005
+ }
8006
+ mkdirSync4(path32.dirname(symlinkPath), { recursive: true });
8007
+ symlinkSync(targetPath, symlinkPath);
8008
+ return { symlinkPath, targetPath, replaced };
8009
+ }
8010
+
8011
+ // packages/agent-hook-config/src/bridge-hooks.ts
8012
+ import * as fs3 from "node:fs";
8013
+ import path33 from "node:path";
8014
+ var hookExcludeMarkerPrefix = "poe-code-spawn-hooks";
8015
+ function isNodeError4(error2) {
8016
+ return error2 instanceof Error && "code" in error2;
8017
+ }
8018
+ function pathExists3(targetPath) {
8019
+ try {
8020
+ fs3.lstatSync(targetPath);
8021
+ return true;
8022
+ } catch (error2) {
8023
+ if (isNodeError4(error2) && error2.code === "ENOENT") {
8024
+ return false;
8025
+ }
8026
+ throw error2;
8027
+ }
8028
+ }
8029
+ function collectMissingParents2(targetPath) {
8030
+ const parents = [];
8031
+ let current = path33.dirname(targetPath);
8032
+ while (!pathExists3(current)) {
8033
+ parents.push(current);
8034
+ const parent = path33.dirname(current);
8035
+ if (parent === current) {
8036
+ break;
8037
+ }
8038
+ current = parent;
8039
+ }
8040
+ return parents.reverse();
8041
+ }
8042
+ function removeDirectoryIfEmpty2(targetPath) {
8043
+ try {
8044
+ fs3.rmdirSync(targetPath);
8045
+ } catch (error2) {
8046
+ if (isNodeError4(error2) && (error2.code === "ENOENT" || error2.code === "ENOTEMPTY" || error2.code === "EEXIST")) {
8047
+ return;
8048
+ }
8049
+ throw error2;
8050
+ }
8051
+ }
8052
+ function requireSupport(input, role) {
8053
+ const support = resolveAgentSupport2(input);
8054
+ if (support.status !== "supported" || !support.id || !support.config) {
8055
+ throw new Error(
8056
+ `Unsupported ${role} hook agent "${input}". Supported hook agents: ${supportedHookAgents.join(", ")}.`
8057
+ );
8058
+ }
8059
+ return { id: support.id, config: support.config };
8060
+ }
8061
+ function requireTargetPath(targetId, config, cwd, homeDir) {
8062
+ const targetPath = resolveHookPath(config, "local", cwd, homeDir);
8063
+ if (!targetPath) {
8064
+ throw new Error(`Agent "${targetId}" has no project hook path`);
8065
+ }
8066
+ return targetPath;
8067
+ }
8068
+ function readCodexFile(targetPath) {
8069
+ let content;
8070
+ try {
8071
+ content = fs3.readFileSync(targetPath, "utf8");
8072
+ } catch (error2) {
8073
+ if (isNodeError4(error2) && error2.code === "ENOENT") {
8074
+ return void 0;
8075
+ }
8076
+ throw error2;
8077
+ }
8078
+ try {
8079
+ return JSON.parse(content);
8080
+ } catch (error2) {
8081
+ throw new Error(`Malformed JSON in ${targetPath}`, { cause: error2 });
8082
+ }
8083
+ }
8084
+ function writeCodexFile(targetPath, file) {
8085
+ fs3.writeFileSync(targetPath, `${JSON.stringify(file, null, 2)}
8086
+ `, "utf8");
8087
+ }
8088
+ function hasOnlyEmptyHooks(file) {
8089
+ return Object.keys(file).every((key) => key === "hooks") && Object.values(file.hooks ?? {}).every((groups) => groups.length === 0);
8090
+ }
8091
+ function relativeToCwd(cwd, targetPath) {
8092
+ return path33.relative(cwd, targetPath);
8093
+ }
8094
+ function matcherKey(event, matcher) {
8095
+ return `${event}\0${matcher === void 0 ? "<undefined>" : matcher}`;
8096
+ }
8097
+ function bridgeHooks(sourceAgentId, targetAgentId, cwd, homeDir, runId, opts) {
8098
+ const source = requireSupport(sourceAgentId, "source");
8099
+ const target = requireSupport(targetAgentId, "target");
8100
+ const strategy = opts?.strategy ?? (source.config.format === target.config.format ? "symlink" : "transform");
8101
+ const manifest = {
8102
+ sourceAgentId,
8103
+ targetAgentId,
8104
+ cwd,
8105
+ runId,
8106
+ strategy,
8107
+ drops: []
8108
+ };
8109
+ if (strategy === "symlink") {
8110
+ const symlinkPath = requireTargetPath(target.id, target.config, cwd, homeDir);
8111
+ manifest.createdParents = collectMissingParents2(symlinkPath);
8112
+ const result = symlinkHooks(source.id, target.id, cwd, homeDir, "project");
8113
+ manifest.symlinkPath = result.symlinkPath;
8114
+ manifest.symlinkTarget = result.targetPath;
8115
+ manifest.symlinkReplaced = result.replaced;
8116
+ appendExcludeBlock(cwd, runId, [relativeToCwd(cwd, result.symlinkPath)], {
8117
+ markerPrefix: hookExcludeMarkerPrefix
8118
+ });
8119
+ return manifest;
8120
+ }
8121
+ if (source.id !== "claude-code") {
8122
+ throw new Error(`Transforming hooks from "${source.id}" is not supported yet`);
8123
+ }
8124
+ if (target.config.format !== "codex-hooks-json") {
8125
+ throw new Error(
8126
+ `Transforming hooks to "${target.id}" is not supported yet; only codex-hook targets can be written`
8127
+ );
8128
+ }
8129
+ const targetPath = requireTargetPath(target.id, target.config, cwd, homeDir);
8130
+ const priorFile = readCodexFile(targetPath);
8131
+ const sourceHooks = readClaudeHooks(cwd, homeDir, { scope: opts?.scope ?? "merged" });
8132
+ const transformed = transformHooks(sourceHooks.entries, source.id, target.id, { runId });
8133
+ const createdParents = collectMissingParents2(targetPath);
8134
+ const writeResult = writeCodexHooks(targetPath, transformed.entries, runId);
8135
+ manifest.writtenPath = targetPath;
8136
+ manifest.generatedEntryIds = transformed.entries.map((entry) => entry.generatedId);
8137
+ manifest.drops = transformed.drops;
8138
+ manifest.createdParents = createdParents;
8139
+ manifest.fileCreated = writeResult.fileCreated;
8140
+ manifest.preExistingEvents = Object.keys(priorFile?.hooks ?? {});
8141
+ manifest.preExistingMatchers = Object.entries(priorFile?.hooks ?? {}).flatMap(
8142
+ ([event, groups]) => groups.map((group) => ({
8143
+ event,
8144
+ ...group.matcher === void 0 ? {} : { matcher: group.matcher }
8145
+ }))
8146
+ );
8147
+ appendExcludeBlock(cwd, runId, [relativeToCwd(cwd, targetPath)], {
8148
+ markerPrefix: hookExcludeMarkerPrefix
8149
+ });
8150
+ return manifest;
8151
+ }
8152
+ function cleanupBridgedHooks(manifest) {
8153
+ if (manifest.strategy === "symlink" && manifest.symlinkPath && manifest.symlinkTarget) {
8154
+ try {
8155
+ if (fs3.lstatSync(manifest.symlinkPath).isSymbolicLink() && fs3.readlinkSync(manifest.symlinkPath) === manifest.symlinkTarget) {
8156
+ fs3.unlinkSync(manifest.symlinkPath);
8157
+ }
8158
+ } catch (error2) {
8159
+ if (!isNodeError4(error2) || error2.code !== "ENOENT") {
8160
+ throw error2;
8161
+ }
8162
+ }
8163
+ for (const parent of [...manifest.createdParents ?? []].reverse()) {
8164
+ removeDirectoryIfEmpty2(parent);
8165
+ }
8166
+ }
8167
+ if (manifest.strategy === "transform" && manifest.writtenPath) {
8168
+ const file = readCodexFile(manifest.writtenPath);
8169
+ if (file) {
8170
+ const generatedPrefix = `[generated:${manifest.runId}]`;
8171
+ const preExistingEvents = new Set(manifest.preExistingEvents ?? []);
8172
+ const preExistingMatchers = new Set(
8173
+ (manifest.preExistingMatchers ?? []).map((group) => matcherKey(group.event, group.matcher))
8174
+ );
8175
+ const hooks = file.hooks ?? {};
8176
+ for (const [event, groups] of Object.entries(hooks)) {
8177
+ hooks[event] = groups.filter((group) => {
8178
+ const priorLength = group.hooks.length;
8179
+ group.hooks = group.hooks.filter(
8180
+ (handler) => !handler.statusMessage?.startsWith(generatedPrefix)
8181
+ );
8182
+ return group.hooks.length > 0 || group.hooks.length === priorLength || preExistingMatchers.has(matcherKey(event, group.matcher));
8183
+ });
8184
+ if (hooks[event].length === 0 && !preExistingEvents.has(event)) {
8185
+ delete hooks[event];
8186
+ }
8187
+ }
8188
+ file.hooks = hooks;
8189
+ if (manifest.fileCreated && hasOnlyEmptyHooks(file)) {
8190
+ fs3.unlinkSync(manifest.writtenPath);
8191
+ } else {
8192
+ writeCodexFile(manifest.writtenPath, file);
8193
+ }
8194
+ }
8195
+ for (const parent of [...manifest.createdParents ?? []].reverse()) {
8196
+ removeDirectoryIfEmpty2(parent);
8197
+ }
8198
+ }
8199
+ removeExcludeBlock(manifest.cwd, manifest.runId, { markerPrefix: hookExcludeMarkerPrefix });
8200
+ }
8201
+
7505
8202
  // packages/agent-spawn/src/skill-bridge.ts
7506
- function bridgeSkillsForRun(agentId, cwd, skills) {
7507
- if (!skills || skills.length === 0) {
8203
+ function bridgeResourcesForRun(agentId, cwd, skills, hooks) {
8204
+ if ((!skills || skills.length === 0) && !hooks) {
7508
8205
  return void 0;
7509
8206
  }
7510
- const manifest = bridgeActiveSkills(agentId, cwd, skills, os6.homedir(), crypto.randomUUID());
7511
- for (const warning2 of manifest.warnings) {
7512
- logger.warn(warning2.message);
8207
+ const runId = crypto.randomUUID();
8208
+ const manifests = {};
8209
+ try {
8210
+ if (skills && skills.length > 0) {
8211
+ manifests.skills = bridgeActiveSkills(agentId, cwd, skills, os7.homedir(), runId);
8212
+ for (const warning2 of manifests.skills.warnings) {
8213
+ logger.warn(warning2.message);
8214
+ }
8215
+ }
8216
+ if (hooks) {
8217
+ manifests.hooks = bridgeHooks(hooks.from, agentId, cwd, os7.homedir(), runId, {
8218
+ strategy: hooks.strategy === "auto" ? void 0 : hooks.strategy,
8219
+ scope: hooks.scope
8220
+ });
8221
+ for (const drop of manifests.hooks.drops) {
8222
+ logger.warn(
8223
+ `Dropped bridged hook event "${drop.source.event}" with handler type "${drop.source.handler.type}": ${drop.detail}`
8224
+ );
8225
+ }
8226
+ }
8227
+ } catch (error2) {
8228
+ cleanupResourcesForRun(manifests);
8229
+ throw error2;
7513
8230
  }
7514
- return manifest;
8231
+ return manifests;
7515
8232
  }
7516
- function cleanupSkillsForRun(manifest) {
8233
+ function cleanupResourcesForRun(manifest) {
7517
8234
  if (!manifest) {
7518
8235
  return;
7519
8236
  }
7520
- cleanupBridgedSkills(manifest);
8237
+ if (manifest.hooks) {
8238
+ cleanupBridgedHooks(manifest.hooks);
8239
+ }
8240
+ if (manifest.skills) {
8241
+ cleanupBridgedSkills(manifest.skills);
8242
+ }
7521
8243
  }
7522
8244
 
7523
8245
  // packages/agent-spawn/src/adapters/utils.ts
@@ -7643,21 +8365,21 @@ async function* adaptClaude(lines) {
7643
8365
  if (blockType !== "tool_result") continue;
7644
8366
  const kind = toolKindsById.get(item.tool_use_id);
7645
8367
  toolKindsById.delete(item.tool_use_id);
7646
- let path32;
8368
+ let path37;
7647
8369
  if (typeof item.content === "string") {
7648
- path32 = item.content;
8370
+ path37 = item.content;
7649
8371
  } else {
7650
8372
  try {
7651
- path32 = JSON.stringify(item.content);
8373
+ path37 = JSON.stringify(item.content);
7652
8374
  } catch {
7653
- path32 = String(item.content);
8375
+ path37 = String(item.content);
7654
8376
  }
7655
8377
  }
7656
8378
  yield {
7657
8379
  event: "tool_complete",
7658
8380
  id: item.tool_use_id,
7659
8381
  kind,
7660
- path: path32
8382
+ path: path37
7661
8383
  };
7662
8384
  }
7663
8385
  }
@@ -7753,10 +8475,10 @@ async function* adaptCodex(lines) {
7753
8475
  const kindFromStart = toolKindById.get(item.id);
7754
8476
  const kind = kindFromStart ?? (itemType === "command_execution" ? "exec" : itemType === "file_edit" ? "edit" : "other");
7755
8477
  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;
7756
- const path32 = titleFromEvent ?? toolTitleById.get(item.id) ?? "";
8478
+ const path37 = titleFromEvent ?? toolTitleById.get(item.id) ?? "";
7757
8479
  toolTitleById.delete(item.id);
7758
8480
  toolKindById.delete(item.id);
7759
- yield { event: "tool_complete", id: item.id, kind, path: path32 };
8481
+ yield { event: "tool_complete", id: item.id, kind, path: path37 };
7760
8482
  }
7761
8483
  }
7762
8484
  }
@@ -8284,7 +9006,7 @@ function spawnStreaming(options) {
8284
9006
  };
8285
9007
  }
8286
9008
  };
8287
- const manifest = bridgeSkillsForRun(options.agentId, cwd, options.skills);
9009
+ const manifest = bridgeResourcesForRun(options.agentId, cwd, options.skills, options.hooks);
8288
9010
  void (async () => {
8289
9011
  try {
8290
9012
  for await (const output of adapter(queue.lines())) {
@@ -8335,7 +9057,7 @@ function spawnStreaming(options) {
8335
9057
  ...ctx.logFile && !result.logFile ? { logFile: ctx.logFile } : {}
8336
9058
  };
8337
9059
  } finally {
8338
- cleanupSkillsForRun(manifest);
9060
+ cleanupResourcesForRun(manifest);
8339
9061
  }
8340
9062
  })();
8341
9063
  return {
@@ -8477,7 +9199,7 @@ async function runSpawn(agentId, options, context) {
8477
9199
  return { stdout: "", stderr: "", exitCode: 0 };
8478
9200
  }
8479
9201
  const cwd = options.cwd ?? process.cwd();
8480
- const manifest = bridgeSkillsForRun(agentId, cwd, options.skills);
9202
+ const manifest = bridgeResourcesForRun(agentId, cwd, options.skills, options.hooks);
8481
9203
  let logFd;
8482
9204
  try {
8483
9205
  const logFilePath = resolveSpawnLogPath(options);
@@ -8545,7 +9267,7 @@ async function runSpawn(agentId, options, context) {
8545
9267
  };
8546
9268
  } finally {
8547
9269
  closeSpawnLog(logFd);
8548
- cleanupSkillsForRun(manifest);
9270
+ cleanupResourcesForRun(manifest);
8549
9271
  }
8550
9272
  }
8551
9273
  spawn4.retry = createSpawnRetry((service, options) => {
@@ -8567,12 +9289,12 @@ function resolveSpawnLogPath(options) {
8567
9289
  if (!options.logDir || !options.logFileName) {
8568
9290
  return void 0;
8569
9291
  }
8570
- return path29.join(options.logDir, options.logFileName);
9292
+ return path34.join(options.logDir, options.logFileName);
8571
9293
  }
8572
9294
  function openSpawnLog(filePath) {
8573
9295
  try {
8574
- mkdirSync3(path29.dirname(filePath), { recursive: true });
8575
- return openSync(filePath, "a");
9296
+ mkdirSync5(path34.dirname(filePath), { recursive: true });
9297
+ return openSync2(filePath, "a");
8576
9298
  } catch {
8577
9299
  return void 0;
8578
9300
  }
@@ -8587,7 +9309,7 @@ function appendSpawnLog(fd, chunk) {
8587
9309
  function closeSpawnLog(fd) {
8588
9310
  if (fd === void 0) return;
8589
9311
  try {
8590
- closeSync(fd);
9312
+ closeSync2(fd);
8591
9313
  } catch {
8592
9314
  }
8593
9315
  }
@@ -8596,7 +9318,7 @@ function closeSpawnLog(fd) {
8596
9318
  var DEFAULT_ACTIVITY_TIMEOUT_MS = 10 * 60 * 1e3;
8597
9319
 
8598
9320
  // packages/agent-spawn/src/acp/replay.ts
8599
- import path30 from "node:path";
9321
+ import path35 from "node:path";
8600
9322
  import { homedir as homedir2 } from "node:os";
8601
9323
  import { open as open2, readdir as readdir2 } from "node:fs/promises";
8602
9324
  import { createInterface } from "node:readline";
@@ -8615,7 +9337,7 @@ import { homedir } from "node:os";
8615
9337
  import { join } from "node:path";
8616
9338
 
8617
9339
  // packages/agent-spawn/src/acp/middlewares/spawn-log.ts
8618
- import path31 from "node:path";
9340
+ import path36 from "node:path";
8619
9341
  import { homedir as homedir3 } from "node:os";
8620
9342
  import { mkdir, open as open3 } from "node:fs/promises";
8621
9343
 
@@ -8629,9 +9351,30 @@ stderr:
8629
9351
  ${stderr}`;
8630
9352
  }
8631
9353
  function createSpawnHealthCheck(agentId, options) {
8632
- const prompt = `Output exactly: ${options.expectedOutput}`;
8633
- const { binaryName, args, env: modeEnv } = buildSpawnArgs(agentId, {
8634
- prompt,
9354
+ const {
9355
+ binaryName,
9356
+ args,
9357
+ env: modeEnv
9358
+ } = options.hooks ? {
9359
+ binaryName: "poe-code",
9360
+ args: [
9361
+ "spawn",
9362
+ "--hooks-from",
9363
+ options.hooks.from,
9364
+ ...options.hooks.strategy ? ["--hooks-strategy", options.hooks.strategy] : [],
9365
+ ...options.model ? ["--model", options.model] : [],
9366
+ "--mode",
9367
+ "yolo",
9368
+ agentId,
9369
+ `Output exactly: ${options.expectedOutput}`
9370
+ ],
9371
+ env: void 0
9372
+ } : options.invocation ? {
9373
+ binaryName: options.invocation.command,
9374
+ args: options.invocation.args,
9375
+ env: options.invocation.env
9376
+ } : buildSpawnArgs(agentId, {
9377
+ prompt: `Output exactly: ${options.expectedOutput}`,
8635
9378
  model: options.model,
8636
9379
  mode: "yolo"
8637
9380
  });
@@ -8646,6 +9389,13 @@ function createSpawnHealthCheck(agentId, options) {
8646
9389
  return;
8647
9390
  }
8648
9391
  const result = modeEnv ? await context.runCommand(binaryName, args, { env: modeEnv }) : await context.runCommand(binaryName, args);
9392
+ if (options.hooks) {
9393
+ for (const line of result.stdout.split("\n")) {
9394
+ if (line.includes("Dropped bridged hook event")) {
9395
+ context.logWarning?.(line);
9396
+ }
9397
+ }
9398
+ }
8649
9399
  if (result.exitCode !== 0) {
8650
9400
  throw new Error(
8651
9401
  `spawn ${agentId} failed with exit code ${result.exitCode}.
@@ -8783,6 +9533,7 @@ function createProvider(opts) {
8783
9533
  configurePrompts: opts.configurePrompts,
8784
9534
  postConfigureMessages: opts.postConfigureMessages,
8785
9535
  extendConfigurePayload: opts.extendConfigurePayload,
9536
+ runtimeEnv: opts.runtimeEnv,
8786
9537
  isolatedEnv: opts.isolatedEnv,
8787
9538
  async configure(context, runOptions) {
8788
9539
  await runMutations(opts.manifest.configure, {
@@ -8870,11 +9621,7 @@ function resolveCodexConfigModel(options) {
8870
9621
  var CODEX_INSTALL_DEFINITION = {
8871
9622
  id: "codex",
8872
9623
  summary: "Codex CLI",
8873
- check: createBinaryExistsCheck(
8874
- "codex",
8875
- "codex-cli-binary",
8876
- "Codex CLI binary must exist"
8877
- ),
9624
+ check: createBinaryExistsCheck("codex", "codex-cli-binary", "Codex CLI binary must exist"),
8878
9625
  steps: [
8879
9626
  {
8880
9627
  id: "install-codex-cli-npm",
@@ -8976,7 +9723,8 @@ var codexService = createProvider({
8976
9723
  return context.runCheck(
8977
9724
  createSpawnHealthCheck("codex", {
8978
9725
  model: context.model ?? DEFAULT_CODEX_MODEL,
8979
- expectedOutput: "CODEX_OK"
9726
+ expectedOutput: "CODEX_OK",
9727
+ hooks: context.hooks
8980
9728
  })
8981
9729
  );
8982
9730
  },