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
@@ -827,17 +827,17 @@ function isPidRunning(pid) {
827
827
  }
828
828
  function createDefaultFs() {
829
829
  return {
830
- open: (path32, flags) => fsPromises.open(path32, flags),
831
- readFile: (path32, encoding) => fsPromises.readFile(path32, encoding),
830
+ open: (path37, flags) => fsPromises.open(path37, flags),
831
+ readFile: (path37, encoding) => fsPromises.readFile(path37, encoding),
832
832
  stat: fsPromises.stat,
833
833
  unlink: fsPromises.unlink
834
834
  };
835
835
  }
836
- async function removeLockFile(fs3, lockPath, signal) {
836
+ async function removeLockFile(fs4, lockPath, signal) {
837
837
  for (let attempt = 0; attempt <= 4; attempt += 1) {
838
838
  throwIfAborted(signal);
839
839
  try {
840
- await fs3.unlink(lockPath);
840
+ await fs4.unlink(lockPath);
841
841
  return;
842
842
  } catch (error2) {
843
843
  if (hasErrorCode(error2, "ENOENT")) {
@@ -868,12 +868,12 @@ function parseLockMetadata(content) {
868
868
  }
869
869
  return void 0;
870
870
  }
871
- async function readLockMetadata(fs3, lockPath) {
872
- if (!fs3.readFile) {
871
+ async function readLockMetadata(fs4, lockPath) {
872
+ if (!fs4.readFile) {
873
873
  return void 0;
874
874
  }
875
875
  try {
876
- return parseLockMetadata(await fs3.readFile(lockPath, "utf8"));
876
+ return parseLockMetadata(await fs4.readFile(lockPath, "utf8"));
877
877
  } catch (error2) {
878
878
  if (hasErrorCode(error2, "ENOENT")) {
879
879
  return null;
@@ -907,7 +907,7 @@ async function writeLockMetadata(handle) {
907
907
  }
908
908
  }
909
909
  async function acquireFileLock(filePath, options = {}) {
910
- const fs3 = options.fs ?? createDefaultFs();
910
+ const fs4 = options.fs ?? createDefaultFs();
911
911
  const retries = options.retries ?? 20;
912
912
  const minTimeout = options.minTimeout ?? 25;
913
913
  const maxTimeout = options.maxTimeout ?? 250;
@@ -918,7 +918,7 @@ async function acquireFileLock(filePath, options = {}) {
918
918
  while (attempt <= retries) {
919
919
  throwIfAborted(options.signal);
920
920
  try {
921
- const handle = await fs3.open(lockPath, "wx");
921
+ const handle = await fs4.open(lockPath, "wx");
922
922
  await writeLockMetadata(handle);
923
923
  let released = false;
924
924
  return async () => {
@@ -926,7 +926,7 @@ async function acquireFileLock(filePath, options = {}) {
926
926
  return;
927
927
  }
928
928
  released = true;
929
- await removeLockFile(fs3, lockPath, options.signal);
929
+ await removeLockFile(fs4, lockPath, options.signal);
930
930
  };
931
931
  } catch (error2) {
932
932
  if (!hasErrorCode(error2, "EEXIST")) {
@@ -935,7 +935,7 @@ async function acquireFileLock(filePath, options = {}) {
935
935
  }
936
936
  let stat3;
937
937
  try {
938
- stat3 = await fs3.stat(lockPath);
938
+ stat3 = await fs4.stat(lockPath);
939
939
  } catch (statError) {
940
940
  if (hasErrorCode(statError, "ENOENT")) {
941
941
  continue;
@@ -943,7 +943,7 @@ async function acquireFileLock(filePath, options = {}) {
943
943
  throw statError;
944
944
  }
945
945
  const reclaimLock = await shouldReclaimLock({
946
- fs: fs3,
946
+ fs: fs4,
947
947
  isPidRunning: pidIsRunning,
948
948
  lockPath,
949
949
  staleMs,
@@ -953,7 +953,7 @@ async function acquireFileLock(filePath, options = {}) {
953
953
  continue;
954
954
  }
955
955
  if (reclaimLock) {
956
- await removeLockFile(fs3, lockPath, options.signal);
956
+ await removeLockFile(fs4, lockPath, options.signal);
957
957
  continue;
958
958
  }
959
959
  if (attempt >= retries) {
@@ -1130,6 +1130,24 @@ var codexAgent = {
1130
1130
  }
1131
1131
  };
1132
1132
 
1133
+ // packages/agent-defs/src/agents/gemini-cli.ts
1134
+ var geminiCliAgent = {
1135
+ id: "gemini-cli",
1136
+ name: "gemini-cli",
1137
+ aliases: ["gemini"],
1138
+ label: "Gemini CLI",
1139
+ summary: "Configure Google's Gemini CLI to use a compatible Google generations API.",
1140
+ binaryName: "gemini",
1141
+ configPath: "~/.gemini/settings.json",
1142
+ apiShapes: ["google-generations"],
1143
+ branding: {
1144
+ colors: {
1145
+ dark: "#8AB4F8",
1146
+ light: "#1A73E8"
1147
+ }
1148
+ }
1149
+ };
1150
+
1133
1151
  // packages/agent-defs/src/agents/opencode.ts
1134
1152
  var openCodeAgent = {
1135
1153
  id: "opencode",
@@ -1203,6 +1221,7 @@ var allAgents = [
1203
1221
  claudeCodeAgent,
1204
1222
  claudeDesktopAgent,
1205
1223
  codexAgent,
1224
+ geminiCliAgent,
1206
1225
  openCodeAgent,
1207
1226
  kimiAgent,
1208
1227
  gooseAgent,
@@ -1253,29 +1272,29 @@ function wrapForLogTee(argv, jobId) {
1253
1272
  return ["sh", "-c", script];
1254
1273
  }
1255
1274
  async function* streamLogFile(env, jobId, opts) {
1256
- const fs3 = env.fs ?? nodeFs;
1275
+ const fs4 = env.fs ?? nodeFs;
1257
1276
  const file = jobLogPath(jobId);
1258
1277
  let byteOffset = opts.sinceByte ?? 0;
1259
1278
  while (true) {
1260
- if (opts.since !== void 0 && !await wasModifiedSince(fs3, file, opts.since)) {
1261
- await waitForLogChange(fs3, file);
1279
+ if (opts.since !== void 0 && !await wasModifiedSince(fs4, file, opts.since)) {
1280
+ await waitForLogChange(fs4, file);
1262
1281
  continue;
1263
1282
  }
1264
- const result = await readLogChunk(fs3, file, byteOffset);
1283
+ const result = await readLogChunk(fs4, file, byteOffset);
1265
1284
  if (result !== null) {
1266
1285
  byteOffset = result.nextByteOffset;
1267
1286
  yield result.chunk;
1268
1287
  continue;
1269
1288
  }
1270
- await waitForLogChange(fs3, file);
1289
+ await waitForLogChange(fs4, file);
1271
1290
  }
1272
1291
  }
1273
- async function wasModifiedSince(fs3, file, since) {
1274
- if (fs3.promises.stat === void 0) {
1292
+ async function wasModifiedSince(fs4, file, since) {
1293
+ if (fs4.promises.stat === void 0) {
1275
1294
  return true;
1276
1295
  }
1277
1296
  try {
1278
- const stat3 = await fs3.promises.stat(file);
1297
+ const stat3 = await fs4.promises.stat(file);
1279
1298
  return stat3.mtimeMs >= since.getTime();
1280
1299
  } catch (error2) {
1281
1300
  if (isNodeError(error2) && error2.code === "ENOENT") {
@@ -1285,11 +1304,11 @@ async function wasModifiedSince(fs3, file, since) {
1285
1304
  }
1286
1305
  }
1287
1306
  async function waitForExit(env, jobId, opts = {}) {
1288
- const fs3 = env.fs ?? nodeFs;
1307
+ const fs4 = env.fs ?? nodeFs;
1289
1308
  const file = jobExitPath(jobId);
1290
1309
  while (true) {
1291
1310
  throwIfAborted2(opts.signal);
1292
- const contents = await readTextFileIfExists(fs3, file);
1311
+ const contents = await readTextFileIfExists(fs4, file);
1293
1312
  if (contents !== null) {
1294
1313
  const text5 = contents.trim();
1295
1314
  const exitCode = Number(text5);
@@ -1307,8 +1326,8 @@ function jobLogPath(jobId) {
1307
1326
  function jobExitPath(jobId) {
1308
1327
  return `${JOB_DIR}/${jobId}.exit`;
1309
1328
  }
1310
- async function readLogChunk(fs3, file, byteOffset) {
1311
- const contents = await readFileIfExists(fs3, file);
1329
+ async function readLogChunk(fs4, file, byteOffset) {
1330
+ const contents = await readFileIfExists(fs4, file);
1312
1331
  if (contents === null || byteOffset >= contents.byteLength) {
1313
1332
  return null;
1314
1333
  }
@@ -1320,13 +1339,13 @@ async function readLogChunk(fs3, file, byteOffset) {
1320
1339
  nextByteOffset: contents.byteLength
1321
1340
  };
1322
1341
  }
1323
- async function readTextFileIfExists(fs3, file) {
1324
- const contents = await readFileIfExists(fs3, file);
1342
+ async function readTextFileIfExists(fs4, file) {
1343
+ const contents = await readFileIfExists(fs4, file);
1325
1344
  return contents?.toString("utf8") ?? null;
1326
1345
  }
1327
- async function readFileIfExists(fs3, file) {
1346
+ async function readFileIfExists(fs4, file) {
1328
1347
  try {
1329
- const contents = await fs3.promises.readFile(file);
1348
+ const contents = await fs4.promises.readFile(file);
1330
1349
  return Buffer.isBuffer(contents) ? contents : Buffer.from(contents);
1331
1350
  } catch (error2) {
1332
1351
  if (isNodeError(error2) && error2.code === "ENOENT") {
@@ -1335,8 +1354,8 @@ async function readFileIfExists(fs3, file) {
1335
1354
  throw error2;
1336
1355
  }
1337
1356
  }
1338
- async function waitForLogChange(fs3, file) {
1339
- const watch = fs3.watch;
1357
+ async function waitForLogChange(fs4, file) {
1358
+ const watch = fs4.watch;
1340
1359
  if (typeof watch !== "function") {
1341
1360
  await sleep2(POLL_INTERVAL_MS);
1342
1361
  return;
@@ -2302,7 +2321,7 @@ import path14 from "node:path";
2302
2321
 
2303
2322
  // packages/config-extends/src/discover.ts
2304
2323
  import path10 from "node:path";
2305
- async function findBase(name, bases, fs3) {
2324
+ async function findBase(name, bases, fs4) {
2306
2325
  const checkedPaths = [];
2307
2326
  for (const basePath of bases) {
2308
2327
  for (const extension of [".md", ".yaml", ".yml", ".json"]) {
@@ -2310,7 +2329,7 @@ async function findBase(name, bases, fs3) {
2310
2329
  checkedPaths.push(filePath);
2311
2330
  try {
2312
2331
  return {
2313
- content: await fs3.readFile(filePath, "utf8"),
2332
+ content: await fs4.readFile(filePath, "utf8"),
2314
2333
  filePath
2315
2334
  };
2316
2335
  } catch (error2) {
@@ -2390,11 +2409,11 @@ function stripBom(content) {
2390
2409
  function mergeLayers(layers) {
2391
2410
  return mergeObjectLayers(layers, []);
2392
2411
  }
2393
- function mergeObjectLayers(layers, path32) {
2412
+ function mergeObjectLayers(layers, path37) {
2394
2413
  const data = {};
2395
2414
  const sources = {};
2396
2415
  for (const key of collectKeys(layers)) {
2397
- const resolved = resolveKey(layers, key, path32);
2416
+ const resolved = resolveKey(layers, key, path37);
2398
2417
  if (resolved === void 0) {
2399
2418
  continue;
2400
2419
  }
@@ -2412,7 +2431,7 @@ function collectKeys(layers) {
2412
2431
  }
2413
2432
  return [...keys];
2414
2433
  }
2415
- function resolveKey(layers, key, path32) {
2434
+ function resolveKey(layers, key, path37) {
2416
2435
  let winningSource;
2417
2436
  let winningValue;
2418
2437
  const objectLayers = [];
@@ -2442,9 +2461,9 @@ function resolveKey(layers, key, path32) {
2442
2461
  if (winningSource === void 0) {
2443
2462
  return void 0;
2444
2463
  }
2445
- const fullPath = buildPath(path32, key);
2464
+ const fullPath = buildPath(path37, key);
2446
2465
  if (isPlainObject(winningValue)) {
2447
- const merged = mergeObjectLayers(objectLayers, [...path32, key]);
2466
+ const merged = mergeObjectLayers(objectLayers, [...path37, key]);
2448
2467
  return {
2449
2468
  value: merged.data,
2450
2469
  sources: {
@@ -2469,8 +2488,8 @@ function isWinningCandidate(key, value) {
2469
2488
  }
2470
2489
  return true;
2471
2490
  }
2472
- function buildPath(path32, key) {
2473
- return [...path32, key].join(".");
2491
+ function buildPath(path37, key) {
2492
+ return [...path37, key].join(".");
2474
2493
  }
2475
2494
  function isPlainObject(value) {
2476
2495
  if (value === null || Array.isArray(value) || typeof value !== "object") {
@@ -3824,16 +3843,16 @@ function getConfigFormat(pathOrFormat) {
3824
3843
  }
3825
3844
  return formatRegistry[formatName];
3826
3845
  }
3827
- function detectFormat2(path32) {
3828
- const ext = getExtension(path32);
3846
+ function detectFormat2(path37) {
3847
+ const ext = getExtension(path37);
3829
3848
  return extensionMap[ext];
3830
3849
  }
3831
- function getExtension(path32) {
3832
- const lastDot = path32.lastIndexOf(".");
3850
+ function getExtension(path37) {
3851
+ const lastDot = path37.lastIndexOf(".");
3833
3852
  if (lastDot === -1) {
3834
3853
  return "";
3835
3854
  }
3836
- return path32.slice(lastDot).toLowerCase();
3855
+ return path37.slice(lastDot).toLowerCase();
3837
3856
  }
3838
3857
 
3839
3858
  // packages/config-mutations/src/execution/path-utils.ts
@@ -3884,9 +3903,9 @@ function resolvePath(rawPath, homeDir, pathMapper) {
3884
3903
  function isNotFound(error2) {
3885
3904
  return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === "ENOENT";
3886
3905
  }
3887
- async function readFileIfExists2(fs3, target) {
3906
+ async function readFileIfExists2(fs4, target) {
3888
3907
  try {
3889
- return await fs3.readFile(target, "utf8");
3908
+ return await fs4.readFile(target, "utf8");
3890
3909
  } catch (error2) {
3891
3910
  if (isNotFound(error2)) {
3892
3911
  return null;
@@ -3894,9 +3913,9 @@ async function readFileIfExists2(fs3, target) {
3894
3913
  throw error2;
3895
3914
  }
3896
3915
  }
3897
- async function pathExists(fs3, target) {
3916
+ async function pathExists(fs4, target) {
3898
3917
  try {
3899
- await fs3.stat(target);
3918
+ await fs4.stat(target);
3900
3919
  return true;
3901
3920
  } catch (error2) {
3902
3921
  if (isNotFound(error2)) {
@@ -3920,9 +3939,9 @@ function createInvalidDocumentBackupPath(targetPath) {
3920
3939
  const ext = targetPath.includes(".") ? targetPath.split(".").pop() : "bak";
3921
3940
  return `${targetPath}.invalid-${createTimestamp()}.${ext}`;
3922
3941
  }
3923
- async function backupInvalidDocument(fs3, targetPath, content) {
3942
+ async function backupInvalidDocument(fs4, targetPath, content) {
3924
3943
  const backupPath = createInvalidDocumentBackupPath(targetPath);
3925
- await fs3.writeFile(backupPath, content, { encoding: "utf8" });
3944
+ await fs4.writeFile(backupPath, content, { encoding: "utf8" });
3926
3945
  }
3927
3946
  function describeMutation(kind, targetPath) {
3928
3947
  const displayPath = targetPath ?? "target";
@@ -4480,12 +4499,12 @@ async function executeMutation(mutation, context, options) {
4480
4499
  }
4481
4500
 
4482
4501
  // packages/poe-code-config/src/store.ts
4483
- async function readMergedDocument(fs3, globalPath, projectPath) {
4484
- const globalDocument = await readStoredDocument(fs3, globalPath);
4502
+ async function readMergedDocument(fs4, globalPath, projectPath) {
4503
+ const globalDocument = await readStoredDocument(fs4, globalPath);
4485
4504
  if (!projectPath || projectPath === globalPath) {
4486
4505
  return globalDocument.data;
4487
4506
  }
4488
- const projectDocument = await readStoredDocument(fs3, projectPath);
4507
+ const projectDocument = await readStoredDocument(fs4, projectPath);
4489
4508
  const resolved = await resolve(
4490
4509
  [
4491
4510
  {
@@ -4499,16 +4518,16 @@ async function readMergedDocument(fs3, globalPath, projectPath) {
4499
4518
  }
4500
4519
  ],
4501
4520
  {
4502
- fs: createResolvedConfigFs(fs3, globalPath, globalDocument.content),
4521
+ fs: createResolvedConfigFs(fs4, globalPath, globalDocument.content),
4503
4522
  autoExtend: true
4504
4523
  }
4505
4524
  );
4506
4525
  return normalizeDocument(resolved.data);
4507
4526
  }
4508
- async function readStoredDocument(fs3, filePath) {
4527
+ async function readStoredDocument(fs4, filePath) {
4509
4528
  try {
4510
- const raw = await fs3.readFile(filePath, "utf8");
4511
- return await parseStoredDocument(fs3, filePath, raw);
4529
+ const raw = await fs4.readFile(filePath, "utf8");
4530
+ return await parseStoredDocument(fs4, filePath, raw);
4512
4531
  } catch (error2) {
4513
4532
  if (isNotFound(error2)) {
4514
4533
  return {
@@ -4519,7 +4538,7 @@ async function readStoredDocument(fs3, filePath) {
4519
4538
  throw error2;
4520
4539
  }
4521
4540
  }
4522
- async function parseStoredDocument(fs3, filePath, raw) {
4541
+ async function parseStoredDocument(fs4, filePath, raw) {
4523
4542
  try {
4524
4543
  return {
4525
4544
  content: raw,
@@ -4527,7 +4546,7 @@ async function parseStoredDocument(fs3, filePath, raw) {
4527
4546
  };
4528
4547
  } catch (error2) {
4529
4548
  if (error2 instanceof SyntaxError) {
4530
- await recoverInvalidDocument(fs3, filePath, raw);
4549
+ await recoverInvalidDocument(fs4, filePath, raw);
4531
4550
  return {
4532
4551
  content: EMPTY_DOCUMENT,
4533
4552
  data: {}
@@ -4561,21 +4580,21 @@ function normalizeScopeValues(value) {
4561
4580
  }
4562
4581
  return normalized;
4563
4582
  }
4564
- function createResolvedConfigFs(fs3, globalPath, globalContent) {
4583
+ function createResolvedConfigFs(fs4, globalPath, globalContent) {
4565
4584
  return {
4566
4585
  readFile(filePath, _encoding) {
4567
4586
  if (filePath === globalPath) {
4568
4587
  return Promise.resolve(globalContent);
4569
4588
  }
4570
- return fs3.readFile(filePath, "utf8");
4589
+ return fs4.readFile(filePath, "utf8");
4571
4590
  }
4572
4591
  };
4573
4592
  }
4574
- async function recoverInvalidDocument(fs3, filePath, content) {
4575
- await fs3.mkdir(path14.dirname(filePath), { recursive: true });
4593
+ async function recoverInvalidDocument(fs4, filePath, content) {
4594
+ await fs4.mkdir(path14.dirname(filePath), { recursive: true });
4576
4595
  const backupPath = createInvalidBackupPath(filePath);
4577
- await fs3.writeFile(backupPath, content, { encoding: "utf8" });
4578
- await fs3.writeFile(filePath, EMPTY_DOCUMENT, { encoding: "utf8" });
4596
+ await fs4.writeFile(backupPath, content, { encoding: "utf8" });
4597
+ await fs4.writeFile(filePath, EMPTY_DOCUMENT, { encoding: "utf8" });
4579
4598
  }
4580
4599
  function createInvalidBackupPath(filePath) {
4581
4600
  const directory = path14.dirname(filePath);
@@ -4699,7 +4718,7 @@ function mergeScope(scope, baseScope, overrideScope) {
4699
4718
  ...Object.fromEntries(scopeEntries)
4700
4719
  };
4701
4720
  }
4702
- function mergeRuntimeScope(baseScope, overrideScope, path32 = []) {
4721
+ function mergeRuntimeScope(baseScope, overrideScope, path37 = []) {
4703
4722
  const merged = {};
4704
4723
  const keys = /* @__PURE__ */ new Set([...Object.keys(baseScope), ...Object.keys(overrideScope)]);
4705
4724
  for (const key of keys) {
@@ -4711,20 +4730,20 @@ function mergeRuntimeScope(baseScope, overrideScope, path32 = []) {
4711
4730
  }
4712
4731
  continue;
4713
4732
  }
4714
- if (isRuntimeConcatenativeArray([...path32, key]) && Array.isArray(baseValue) && Array.isArray(overrideValue)) {
4733
+ if (isRuntimeConcatenativeArray([...path37, key]) && Array.isArray(baseValue) && Array.isArray(overrideValue)) {
4715
4734
  merged[key] = [...baseValue, ...overrideValue];
4716
4735
  continue;
4717
4736
  }
4718
4737
  if (isRecord4(baseValue) && isRecord4(overrideValue)) {
4719
- merged[key] = mergeRuntimeScope(baseValue, overrideValue, [...path32, key]);
4738
+ merged[key] = mergeRuntimeScope(baseValue, overrideValue, [...path37, key]);
4720
4739
  continue;
4721
4740
  }
4722
4741
  merged[key] = overrideValue;
4723
4742
  }
4724
4743
  return merged;
4725
4744
  }
4726
- function isRuntimeConcatenativeArray(path32) {
4727
- return path32.join(".") === "mounts" || path32.join(".") === "runner.workspace.exclude";
4745
+ function isRuntimeConcatenativeArray(path37) {
4746
+ return path37.join(".") === "mounts" || path37.join(".") === "runner.workspace.exclude";
4728
4747
  }
4729
4748
  function isRecord4(value) {
4730
4749
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
@@ -4912,6 +4931,7 @@ var poeProvider = {
4912
4931
  label: "Poe",
4913
4932
  summary: "Route AI coding agents through Poe's API.",
4914
4933
  baseUrl: "https://api.poe.com",
4934
+ agentBaseUrl: "https://api.poe.com",
4915
4935
  auth: {
4916
4936
  kind: "api-key",
4917
4937
  envVar: "POE_API_KEY",
@@ -4919,6 +4939,12 @@ var poeProvider = {
4919
4939
  prompt: { title: "Poe API key" },
4920
4940
  preferredLogin: "oauth"
4921
4941
  },
4942
+ env: {
4943
+ ANTHROPIC_CUSTOM_HEADERS: {
4944
+ kind: "providerCredential",
4945
+ prefix: "Authorization: Bearer "
4946
+ }
4947
+ },
4922
4948
  apiShapes: [
4923
4949
  {
4924
4950
  id: "openai-chat-completions",
@@ -4947,6 +4973,9 @@ var anthropicProvider = {
4947
4973
  storageKey: "provider:anthropic",
4948
4974
  prompt: { title: "Anthropic API key" }
4949
4975
  },
4976
+ env: {
4977
+ ANTHROPIC_API_KEY: { kind: "providerCredential" }
4978
+ },
4950
4979
  apiShapes: [
4951
4980
  {
4952
4981
  id: "anthropic-messages",
@@ -4969,6 +4998,12 @@ var cloudflareProvider = {
4969
4998
  storageKey: "provider:cloudflare",
4970
4999
  prompt: { title: "Cloudflare AI Gateway token" }
4971
5000
  },
5001
+ env: {
5002
+ ANTHROPIC_CUSTOM_HEADERS: {
5003
+ kind: "providerCredential",
5004
+ prefix: "Authorization: Bearer "
5005
+ }
5006
+ },
4972
5007
  apiShapes: [
4973
5008
  {
4974
5009
  id: "openai-chat-completions",
@@ -5013,7 +5048,7 @@ function isNotFoundError(error2) {
5013
5048
  }
5014
5049
 
5015
5050
  // packages/poe-code-config/src/state/jobs.ts
5016
- function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5051
+ function createJobRegistry(homeDir, fs4 = defaultStateFs) {
5017
5052
  const jobsDir = path17.join(homeDir, ".poe-code", "state", "jobs");
5018
5053
  function jobPath(id) {
5019
5054
  assertSafeJobId(id);
@@ -5021,7 +5056,7 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5021
5056
  }
5022
5057
  async function get(id) {
5023
5058
  try {
5024
- return parseJobEntry(await fs3.readFile(jobPath(id), "utf8"));
5059
+ return parseJobEntry(await fs4.readFile(jobPath(id), "utf8"));
5025
5060
  } catch (error2) {
5026
5061
  if (isNotFoundError(error2)) {
5027
5062
  return null;
@@ -5032,8 +5067,8 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5032
5067
  async function put(entry) {
5033
5068
  assertJobEntry(entry);
5034
5069
  const filePath = jobPath(entry.id);
5035
- await fs3.mkdir(jobsDir, { recursive: true });
5036
- const release = await acquireFileLock(filePath, { fs: fs3 });
5070
+ await fs4.mkdir(jobsDir, { recursive: true });
5071
+ const release = await acquireFileLock(filePath, { fs: fs4 });
5037
5072
  try {
5038
5073
  await writeJobAtomically(filePath, entry);
5039
5074
  } finally {
@@ -5042,8 +5077,8 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5042
5077
  }
5043
5078
  async function update(id, patch) {
5044
5079
  const filePath = jobPath(id);
5045
- await fs3.mkdir(jobsDir, { recursive: true });
5046
- const release = await acquireFileLock(filePath, { fs: fs3 });
5080
+ await fs4.mkdir(jobsDir, { recursive: true });
5081
+ const release = await acquireFileLock(filePath, { fs: fs4 });
5047
5082
  try {
5048
5083
  const current = await get(id);
5049
5084
  if (current === null) {
@@ -5064,7 +5099,7 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5064
5099
  async function list(filter = {}) {
5065
5100
  let entries;
5066
5101
  try {
5067
- entries = await fs3.readdir(jobsDir);
5102
+ entries = await fs4.readdir(jobsDir);
5068
5103
  } catch (error2) {
5069
5104
  if (isNotFoundError(error2)) {
5070
5105
  return [];
@@ -5077,11 +5112,11 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5077
5112
  continue;
5078
5113
  }
5079
5114
  const filePath = path17.join(jobsDir, entry);
5080
- const stat3 = await fs3.stat(filePath);
5115
+ const stat3 = await fs4.stat(filePath);
5081
5116
  if (!stat3.isFile()) {
5082
5117
  continue;
5083
5118
  }
5084
- const job = parseJobEntry(await fs3.readFile(filePath, "utf8"));
5119
+ const job = parseJobEntry(await fs4.readFile(filePath, "utf8"));
5085
5120
  if (matchesFilter(job, filter)) {
5086
5121
  jobs.push(job);
5087
5122
  }
@@ -5091,16 +5126,16 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5091
5126
  async function remove2(id) {
5092
5127
  const filePath = jobPath(id);
5093
5128
  try {
5094
- await fs3.stat(jobsDir);
5129
+ await fs4.stat(jobsDir);
5095
5130
  } catch (error2) {
5096
5131
  if (isNotFoundError(error2)) {
5097
5132
  return;
5098
5133
  }
5099
5134
  throw error2;
5100
5135
  }
5101
- const release = await acquireFileLock(filePath, { fs: fs3 });
5136
+ const release = await acquireFileLock(filePath, { fs: fs4 });
5102
5137
  try {
5103
- await fs3.unlink(filePath);
5138
+ await fs4.unlink(filePath);
5104
5139
  } catch (error2) {
5105
5140
  if (!isNotFoundError(error2)) {
5106
5141
  throw error2;
@@ -5110,14 +5145,14 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5110
5145
  }
5111
5146
  }
5112
5147
  async function writeJobAtomically(filePath, entry) {
5113
- await fs3.mkdir(path17.dirname(filePath), { recursive: true });
5148
+ await fs4.mkdir(path17.dirname(filePath), { recursive: true });
5114
5149
  const tempPath = `${filePath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
5115
5150
  try {
5116
- await fs3.writeFile(tempPath, `${JSON.stringify(entry, null, 2)}
5151
+ await fs4.writeFile(tempPath, `${JSON.stringify(entry, null, 2)}
5117
5152
  `, {
5118
5153
  encoding: "utf8"
5119
5154
  });
5120
- await fs3.rename(tempPath, filePath);
5155
+ await fs4.rename(tempPath, filePath);
5121
5156
  } catch (error2) {
5122
5157
  await removeTempFile(tempPath);
5123
5158
  throw error2;
@@ -5125,7 +5160,7 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5125
5160
  }
5126
5161
  async function removeTempFile(tempPath) {
5127
5162
  try {
5128
- await fs3.unlink(tempPath);
5163
+ await fs4.unlink(tempPath);
5129
5164
  } catch (error2) {
5130
5165
  if (!isNotFoundError(error2)) {
5131
5166
  throw error2;
@@ -5172,11 +5207,11 @@ function isRecord5(value) {
5172
5207
 
5173
5208
  // packages/poe-code-config/src/state/templates.ts
5174
5209
  import path18 from "node:path";
5175
- function createTemplateRegistry(homeDir, fs3 = defaultStateFs) {
5210
+ function createTemplateRegistry(homeDir, fs4 = defaultStateFs) {
5176
5211
  const filePath = path18.join(homeDir, ".poe-code", "state", "templates.json");
5177
5212
  async function readState() {
5178
5213
  try {
5179
- const raw = await fs3.readFile(filePath, "utf8");
5214
+ const raw = await fs4.readFile(filePath, "utf8");
5180
5215
  return normalizeTemplateState(JSON.parse(raw));
5181
5216
  } catch (error2) {
5182
5217
  if (isNotFoundError(error2)) {
@@ -5186,14 +5221,14 @@ function createTemplateRegistry(homeDir, fs3 = defaultStateFs) {
5186
5221
  }
5187
5222
  }
5188
5223
  async function writeState(state) {
5189
- await fs3.writeFile(filePath, `${JSON.stringify(state, null, 2)}
5224
+ await fs4.writeFile(filePath, `${JSON.stringify(state, null, 2)}
5190
5225
  `, {
5191
5226
  encoding: "utf8"
5192
5227
  });
5193
5228
  }
5194
5229
  async function updateState(mutator) {
5195
- await fs3.mkdir(path18.dirname(filePath), { recursive: true });
5196
- const release = await acquireFileLock(filePath, { fs: fs3 });
5230
+ await fs4.mkdir(path18.dirname(filePath), { recursive: true });
5231
+ const release = await acquireFileLock(filePath, { fs: fs4 });
5197
5232
  try {
5198
5233
  const state = await readState();
5199
5234
  mutator(state);
@@ -5263,10 +5298,10 @@ function isRecord6(value) {
5263
5298
  }
5264
5299
 
5265
5300
  // packages/poe-code-config/src/state/index.ts
5266
- function createStateManager(homeDir, fs3) {
5301
+ function createStateManager(homeDir, fs4) {
5267
5302
  return {
5268
- templates: createTemplateRegistry(homeDir, fs3),
5269
- jobs: createJobRegistry(homeDir, fs3)
5303
+ templates: createTemplateRegistry(homeDir, fs4),
5304
+ jobs: createJobRegistry(homeDir, fs4)
5270
5305
  };
5271
5306
  }
5272
5307
 
@@ -5581,7 +5616,7 @@ import { PassThrough as PassThrough2, Writable as Writable2 } from "node:stream"
5581
5616
  import path21 from "node:path";
5582
5617
  var JOB_DIR2 = "/tmp/poe-jobs";
5583
5618
  function createE2bJobHandle(input) {
5584
- const fs3 = createE2bLogStreamFs(input.sandbox);
5619
+ const fs4 = createE2bLogStreamFs(input.sandbox);
5585
5620
  return {
5586
5621
  id: input.jobId,
5587
5622
  envId: input.envId,
@@ -5597,10 +5632,10 @@ function createE2bJobHandle(input) {
5597
5632
  return isRunning ? "running" : "lost";
5598
5633
  },
5599
5634
  stream(opts = {}) {
5600
- return streamLogFile({ fs: fs3 }, input.jobId, opts);
5635
+ return streamLogFile({ fs: fs4 }, input.jobId, opts);
5601
5636
  },
5602
5637
  async wait() {
5603
- const result = await waitForExit({ fs: fs3 }, input.jobId);
5638
+ const result = await waitForExit({ fs: fs4 }, input.jobId);
5604
5639
  const preserveMs = input.preserveAfterExitHours * 60 * 60 * 1e3;
5605
5640
  if (preserveMs > 0) {
5606
5641
  await input.sandbox.setTimeout(preserveMs);
@@ -6097,10 +6132,10 @@ var e2bAuthScope = defineScope("e2b", {
6097
6132
  });
6098
6133
  async function resolveE2bApiKey(input) {
6099
6134
  const homeDir = input.homeDir ?? os4.homedir();
6100
- const fs3 = input.fs ?? nodeFs4;
6135
+ const fs4 = input.fs ?? nodeFs4;
6101
6136
  const env = input.env ?? process.env;
6102
6137
  const document = await readMergedDocument(
6103
- fs3,
6138
+ fs4,
6104
6139
  resolveConfigPath(homeDir),
6105
6140
  resolveProjectConfigPath(input.cwd)
6106
6141
  );
@@ -6607,6 +6642,23 @@ var gooseAcpSpawnConfig = {
6607
6642
  skipAuth: true
6608
6643
  };
6609
6644
 
6645
+ // packages/agent-spawn/src/configs/gemini-cli.ts
6646
+ var geminiCliAcpSpawnConfig = {
6647
+ kind: "acp",
6648
+ agentId: "gemini-cli",
6649
+ acpArgs: ({ model, mcpServers }) => [
6650
+ "--acp",
6651
+ ...model ? ["--model", model] : [],
6652
+ ...mcpServers ? ["--allowed-mcp-server-names", Object.keys(mcpServers).join(",")] : [],
6653
+ ...mcpServers ? ["--skip-trust"] : [],
6654
+ "--yolo"
6655
+ ],
6656
+ env: {
6657
+ GEMINI_SANDBOX: "false"
6658
+ },
6659
+ skipAuth: true
6660
+ };
6661
+
6610
6662
  // packages/agent-spawn/src/configs/index.ts
6611
6663
  var allSpawnConfigs = [
6612
6664
  claudeCodeSpawnConfig,
@@ -6623,6 +6675,7 @@ var acpLookup = /* @__PURE__ */ new Map();
6623
6675
  acpLookup.set(openCodeAcpSpawnConfig.agentId, openCodeAcpSpawnConfig);
6624
6676
  acpLookup.set(kimiAcpSpawnConfig.agentId, kimiAcpSpawnConfig);
6625
6677
  acpLookup.set(gooseAcpSpawnConfig.agentId, gooseAcpSpawnConfig);
6678
+ acpLookup.set(geminiCliAcpSpawnConfig.agentId, geminiCliAcpSpawnConfig);
6626
6679
  function getSpawnConfig(input) {
6627
6680
  const resolvedId = resolveAgentId(input);
6628
6681
  if (!resolvedId) {
@@ -6642,8 +6695,8 @@ function listMcpSupportedAgents() {
6642
6695
  }
6643
6696
 
6644
6697
  // packages/agent-spawn/src/spawn.ts
6645
- import { mkdirSync as mkdirSync3, openSync, writeSync, closeSync } from "node:fs";
6646
- import path29 from "node:path";
6698
+ import { mkdirSync as mkdirSync5, openSync as openSync2, writeSync, closeSync as closeSync2 } from "node:fs";
6699
+ import path34 from "node:path";
6647
6700
 
6648
6701
  // packages/agent-spawn/src/configs/resolve-config.ts
6649
6702
  function resolveConfig(agentId) {
@@ -7044,7 +7097,7 @@ function createEventQueue() {
7044
7097
 
7045
7098
  // packages/agent-spawn/src/skill-bridge.ts
7046
7099
  import crypto from "node:crypto";
7047
- import os6 from "node:os";
7100
+ import os7 from "node:os";
7048
7101
 
7049
7102
  // packages/agent-skill-config/src/configs.ts
7050
7103
  import os5 from "node:os";
@@ -7058,6 +7111,10 @@ var agentSkillConfigs = {
7058
7111
  globalSkillDir: "~/.codex/skills",
7059
7112
  localSkillDir: ".codex/skills"
7060
7113
  },
7114
+ "gemini-cli": {
7115
+ globalSkillDir: "~/.gemini/skills",
7116
+ localSkillDir: ".gemini/skills"
7117
+ },
7061
7118
  opencode: {
7062
7119
  globalSkillDir: "~/.config/opencode/skills",
7063
7120
  localSkillDir: ".opencode/skills"
@@ -7200,7 +7257,7 @@ function resolveSkillReference(ref, cwd, homeDir) {
7200
7257
  import { execFileSync } from "node:child_process";
7201
7258
  import * as fs from "node:fs";
7202
7259
  import path27 from "node:path";
7203
- var markerPrefix = "# poe-code-spawn-skills:";
7260
+ var defaultMarkerPrefix = "poe-code-spawn-skills";
7204
7261
  function defaultGitDirRunner(cwd) {
7205
7262
  try {
7206
7263
  return execFileSync("git", ["rev-parse", "--git-dir"], {
@@ -7220,10 +7277,10 @@ function resolveExcludePath(cwd) {
7220
7277
  }
7221
7278
  return path27.join(path27.isAbsolute(gitDir) ? gitDir : path27.resolve(cwd, gitDir), "info/exclude");
7222
7279
  }
7223
- function markers(runId) {
7280
+ function markers(runId, markerPrefix) {
7224
7281
  return {
7225
- begin: `${markerPrefix}${runId} begin`,
7226
- end: `${markerPrefix}${runId} end`
7282
+ begin: `# ${markerPrefix}:${runId} begin`,
7283
+ end: `# ${markerPrefix}:${runId} end`
7227
7284
  };
7228
7285
  }
7229
7286
  function readExcludeFile(excludePath) {
@@ -7239,8 +7296,8 @@ function readExcludeFile(excludePath) {
7239
7296
  function isNodeError2(error2) {
7240
7297
  return error2 instanceof Error && "code" in error2;
7241
7298
  }
7242
- function removeBlock(content, runId) {
7243
- const { begin, end } = markers(runId);
7299
+ function removeBlock(content, runId, markerPrefix) {
7300
+ const { begin, end } = markers(runId, markerPrefix);
7244
7301
  const lines = content.split("\n");
7245
7302
  const result = [];
7246
7303
  for (let index = 0; index < lines.length; index += 1) {
@@ -7255,23 +7312,27 @@ function removeBlock(content, runId) {
7255
7312
  }
7256
7313
  return result.join("\n");
7257
7314
  }
7258
- function appendBlock(content, runId, entries) {
7259
- const { begin, end } = markers(runId);
7315
+ function appendBlock(content, runId, entries, markerPrefix) {
7316
+ const { begin, end } = markers(runId, markerPrefix);
7260
7317
  const existing = content ?? "";
7261
7318
  const prefix = existing.length === 0 || existing.endsWith("\n") ? existing : `${existing}
7262
7319
  `;
7263
7320
  return `${prefix}${[begin, ...entries, end, ""].join("\n")}`;
7264
7321
  }
7265
- function appendExcludeBlock(cwd, runId, entries) {
7322
+ function appendExcludeBlock(cwd, runId, entries, opts) {
7266
7323
  const excludePath = resolveExcludePath(cwd);
7267
7324
  if (excludePath === void 0) {
7268
7325
  return;
7269
7326
  }
7270
7327
  fs.mkdirSync(path27.dirname(excludePath), { recursive: true });
7271
7328
  const content = readExcludeFile(excludePath);
7272
- fs.writeFileSync(excludePath, appendBlock(content, runId, entries), "utf8");
7329
+ fs.writeFileSync(
7330
+ excludePath,
7331
+ appendBlock(content, runId, entries, opts?.markerPrefix ?? defaultMarkerPrefix),
7332
+ "utf8"
7333
+ );
7273
7334
  }
7274
- function removeExcludeBlock(cwd, runId) {
7335
+ function removeExcludeBlock(cwd, runId, opts) {
7275
7336
  const excludePath = resolveExcludePath(cwd);
7276
7337
  if (excludePath === void 0) {
7277
7338
  return;
@@ -7280,7 +7341,11 @@ function removeExcludeBlock(cwd, runId) {
7280
7341
  if (content === void 0) {
7281
7342
  return;
7282
7343
  }
7283
- fs.writeFileSync(excludePath, removeBlock(content, runId), "utf8");
7344
+ fs.writeFileSync(
7345
+ excludePath,
7346
+ removeBlock(content, runId, opts?.markerPrefix ?? defaultMarkerPrefix),
7347
+ "utf8"
7348
+ );
7284
7349
  }
7285
7350
 
7286
7351
  // packages/agent-skill-config/src/bridge-active-skills.ts
@@ -7486,22 +7551,679 @@ function cleanupBridgedSkills(manifest) {
7486
7551
  removeExcludeBlock(manifest.cwd, manifest.runId);
7487
7552
  }
7488
7553
 
7554
+ // packages/agent-hook-config/src/configs.ts
7555
+ import os6 from "node:os";
7556
+ import path29 from "node:path";
7557
+ var agentHookConfigs = {
7558
+ "claude-code": {
7559
+ globalHookPath: "~/.claude/settings.json",
7560
+ localHookPath: ".claude/settings.json",
7561
+ format: "claude-settings-json",
7562
+ supportedEvents: [
7563
+ "SessionStart",
7564
+ "SessionEnd",
7565
+ "UserPromptSubmit",
7566
+ "PreToolUse",
7567
+ "PostToolUse",
7568
+ "PermissionRequest",
7569
+ "Stop",
7570
+ "StopFailure",
7571
+ "Notification",
7572
+ "PreCompact",
7573
+ "PostCompact",
7574
+ "SubagentStart",
7575
+ "SubagentStop"
7576
+ ],
7577
+ supportedHandlerTypes: ["command", "http", "mcp_tool", "prompt", "agent"],
7578
+ placeholders: {
7579
+ projectDir: "${CLAUDE_PROJECT_DIR}",
7580
+ pluginRoot: "${CLAUDE_PLUGIN_ROOT}",
7581
+ pluginData: "${CLAUDE_PLUGIN_DATA}"
7582
+ }
7583
+ },
7584
+ codex: {
7585
+ globalHookPath: "~/.codex/hooks.json",
7586
+ localHookPath: ".codex/hooks.json",
7587
+ format: "codex-hooks-json",
7588
+ supportedEvents: [
7589
+ "SessionStart",
7590
+ "UserPromptSubmit",
7591
+ "PreToolUse",
7592
+ "PostToolUse",
7593
+ "PermissionRequest",
7594
+ "Stop"
7595
+ ],
7596
+ supportedHandlerTypes: ["command"],
7597
+ placeholders: {
7598
+ projectDir: "$(git rev-parse --show-toplevel)",
7599
+ pluginRoot: "$PLUGIN_ROOT",
7600
+ pluginData: "$PLUGIN_DATA"
7601
+ }
7602
+ }
7603
+ };
7604
+ var supportedHookAgents = Object.keys(agentHookConfigs);
7605
+ function resolveAgentSupport2(input, registry = agentHookConfigs) {
7606
+ const resolvedId = resolveAgentId(input);
7607
+ if (!resolvedId) {
7608
+ return { status: "unknown", input };
7609
+ }
7610
+ const config = registry[resolvedId];
7611
+ if (!config) {
7612
+ return { status: "unsupported", input, id: resolvedId };
7613
+ }
7614
+ return { status: "supported", input, id: resolvedId, config };
7615
+ }
7616
+ function getAgentConfig2(agentId) {
7617
+ const support = resolveAgentSupport2(agentId);
7618
+ return support.status === "supported" ? support.config : void 0;
7619
+ }
7620
+ function expandHome3(targetPath, homeDir = os6.homedir()) {
7621
+ if (!targetPath?.startsWith("~")) {
7622
+ return targetPath;
7623
+ }
7624
+ if (targetPath === "~") {
7625
+ return homeDir;
7626
+ }
7627
+ if (targetPath.startsWith("~./")) {
7628
+ targetPath = `~/.${targetPath.slice(3)}`;
7629
+ }
7630
+ let remainder = targetPath.slice(1);
7631
+ if (remainder.startsWith("/") || remainder.startsWith("\\")) {
7632
+ remainder = remainder.slice(1);
7633
+ } else if (remainder.startsWith(".")) {
7634
+ remainder = remainder.slice(1);
7635
+ if (remainder.startsWith("/") || remainder.startsWith("\\")) {
7636
+ remainder = remainder.slice(1);
7637
+ }
7638
+ }
7639
+ return remainder.length === 0 ? homeDir : path29.join(homeDir, remainder);
7640
+ }
7641
+ function resolveHookPath(config, scope, cwd, homeDir) {
7642
+ if (scope === "global") {
7643
+ return path29.resolve(expandHome3(config.globalHookPath, homeDir));
7644
+ }
7645
+ return config.localHookPath ? path29.resolve(cwd, config.localHookPath) : void 0;
7646
+ }
7647
+
7648
+ // packages/agent-hook-config/src/read-hooks.ts
7649
+ import { readFileSync as readFileSync3 } from "node:fs";
7650
+ import path30 from "node:path";
7651
+ function readSettingsFile(filePath) {
7652
+ let content;
7653
+ try {
7654
+ content = readFileSync3(filePath, "utf8");
7655
+ } catch (error2) {
7656
+ if (error2.code === "ENOENT") {
7657
+ return void 0;
7658
+ }
7659
+ throw error2;
7660
+ }
7661
+ try {
7662
+ return JSON.parse(content);
7663
+ } catch (error2) {
7664
+ throw new Error(`Malformed JSON in ${filePath}`, { cause: error2 });
7665
+ }
7666
+ }
7667
+ function readClaudeHooks(cwd, homeDir, opts) {
7668
+ const projectPath = path30.resolve(cwd, ".claude/settings.json");
7669
+ const userPath = path30.resolve(homeDir, ".claude/settings.json");
7670
+ const scope = opts?.scope ?? "merged";
7671
+ const sourcePaths = scope === "project" ? [projectPath] : scope === "user" ? [userPath] : [userPath, projectPath];
7672
+ const result = { entries: [], readPaths: [] };
7673
+ for (const sourcePath of sourcePaths) {
7674
+ const settings = readSettingsFile(sourcePath);
7675
+ if (settings === void 0) {
7676
+ continue;
7677
+ }
7678
+ result.readPaths.push(sourcePath);
7679
+ for (const [event, groups] of Object.entries(settings.hooks ?? {})) {
7680
+ for (const group of groups) {
7681
+ for (const handler of group.hooks) {
7682
+ result.entries.push({ event, matcher: group.matcher, handler });
7683
+ }
7684
+ }
7685
+ }
7686
+ }
7687
+ return result;
7688
+ }
7689
+
7690
+ // packages/agent-hook-config/src/event-mapping.ts
7691
+ function requireAgentConfig(agentId) {
7692
+ const config = getAgentConfig2(agentId);
7693
+ if (!config) {
7694
+ throw new Error(`Unknown hook agent "${agentId}"`);
7695
+ }
7696
+ return config;
7697
+ }
7698
+ function getEventMappings(sourceAgentId, targetAgentId) {
7699
+ const source = requireAgentConfig(sourceAgentId);
7700
+ const target = requireAgentConfig(targetAgentId);
7701
+ return source.supportedEvents.map((sourceEvent) => {
7702
+ if (target.supportedEvents.includes(sourceEvent)) {
7703
+ return { sourceEvent, targetEvent: sourceEvent };
7704
+ }
7705
+ return {
7706
+ sourceEvent,
7707
+ targetEvent: null,
7708
+ dropReason: `${targetAgentId} has no ${sourceEvent} hook`
7709
+ };
7710
+ });
7711
+ }
7712
+ function getHandlerTypeRules(targetAgentId) {
7713
+ const target = requireAgentConfig(targetAgentId);
7714
+ const registeredTypes = supportedHookAgents.flatMap(
7715
+ (agentId) => requireAgentConfig(agentId).supportedHandlerTypes
7716
+ );
7717
+ const sourceTypes = [...new Set(registeredTypes)];
7718
+ const supportedTypes = target.supportedHandlerTypes.map((handlerType) => `"${handlerType}"`).join(", ");
7719
+ return sourceTypes.map((sourceType) => {
7720
+ if (target.supportedHandlerTypes.includes(sourceType)) {
7721
+ return { sourceType, allowed: true };
7722
+ }
7723
+ return {
7724
+ sourceType,
7725
+ allowed: false,
7726
+ dropReason: `${targetAgentId} only honors handlers of type ${supportedTypes}`
7727
+ };
7728
+ });
7729
+ }
7730
+ function getPlaceholderRewrites(sourceAgentId, targetAgentId) {
7731
+ const source = requireAgentConfig(sourceAgentId);
7732
+ const target = requireAgentConfig(targetAgentId);
7733
+ return Object.keys(source.placeholders).flatMap((key) => {
7734
+ const from = source.placeholders[key];
7735
+ const to = target.placeholders[key];
7736
+ if (!from || !to || from === to) {
7737
+ return [];
7738
+ }
7739
+ return [{ from, to }];
7740
+ });
7741
+ }
7742
+
7743
+ // packages/agent-hook-config/src/transform-hooks.ts
7744
+ function applyPlaceholderRewrites(value, rewrites) {
7745
+ return rewrites.reduce((rewrittenValue, rewrite) => {
7746
+ return rewrittenValue.replaceAll(rewrite.from, rewrite.to);
7747
+ }, value);
7748
+ }
7749
+ function transformHooks(source, sourceAgentId, targetAgentId, opts) {
7750
+ const eventMappings = getEventMappings(sourceAgentId, targetAgentId);
7751
+ const handlerRules = getHandlerTypeRules(targetAgentId);
7752
+ const placeholderRewrites = getPlaceholderRewrites(sourceAgentId, targetAgentId);
7753
+ const result = { entries: [], drops: [] };
7754
+ for (const sourceEntry of source) {
7755
+ const eventMapping = eventMappings.find((mapping) => mapping.sourceEvent === sourceEntry.event);
7756
+ if (!eventMapping || eventMapping.targetEvent === null) {
7757
+ result.drops.push({
7758
+ reason: "unsupported-event",
7759
+ detail: eventMapping?.dropReason ?? `${targetAgentId} has no ${sourceEntry.event} hook`,
7760
+ source: sourceEntry
7761
+ });
7762
+ continue;
7763
+ }
7764
+ const handlerRule = handlerRules.find((rule) => rule.sourceType === sourceEntry.handler.type);
7765
+ if (!handlerRule?.allowed) {
7766
+ result.drops.push({
7767
+ reason: "unsupported-handler-type",
7768
+ detail: `Unsupported handler type "${sourceEntry.handler.type}": ${handlerRule?.dropReason ?? `${targetAgentId} does not honor it`}`,
7769
+ source: sourceEntry
7770
+ });
7771
+ continue;
7772
+ }
7773
+ const handler = {
7774
+ type: "command",
7775
+ command: applyPlaceholderRewrites(sourceEntry.handler.command ?? "", placeholderRewrites),
7776
+ statusMessage: `[generated:${opts.runId}] ${sourceEntry.handler.statusMessage ?? ""}`
7777
+ };
7778
+ if (sourceEntry.handler.args !== void 0) {
7779
+ handler.args = sourceEntry.handler.args.map(
7780
+ (arg) => applyPlaceholderRewrites(arg, placeholderRewrites)
7781
+ );
7782
+ }
7783
+ if (sourceEntry.handler.timeout !== void 0) {
7784
+ handler.timeout = sourceEntry.handler.timeout;
7785
+ }
7786
+ result.entries.push({
7787
+ event: eventMapping.targetEvent,
7788
+ matcher: sourceEntry.matcher,
7789
+ handler,
7790
+ generatedId: `generated-${opts.runId}-${result.entries.length}`
7791
+ });
7792
+ }
7793
+ return result;
7794
+ }
7795
+
7796
+ // packages/agent-hook-config/src/write-hooks.ts
7797
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync4, renameSync, writeFileSync as writeFileSync2 } from "node:fs";
7798
+ import path31 from "node:path";
7799
+ function isGeneratedHandler(handler) {
7800
+ return handler.statusMessage?.startsWith("[generated:") ?? false;
7801
+ }
7802
+ function parseHooksFile(targetPath) {
7803
+ let content;
7804
+ try {
7805
+ content = readFileSync4(targetPath, "utf8");
7806
+ } catch (error2) {
7807
+ if (error2.code === "ENOENT") {
7808
+ return { file: { hooks: {} }, fileCreated: true };
7809
+ }
7810
+ throw error2;
7811
+ }
7812
+ try {
7813
+ return { file: JSON.parse(content), fileCreated: false };
7814
+ } catch (error2) {
7815
+ throw new Error(`Malformed JSON in ${targetPath}`, { cause: error2 });
7816
+ }
7817
+ }
7818
+ function validateEntries(entries) {
7819
+ for (const entry of entries) {
7820
+ if (!isGeneratedHandler(entry.handler)) {
7821
+ throw new Error(
7822
+ `Generated hook entry "${entry.generatedId}" has statusMessage that must start with "[generated:"`
7823
+ );
7824
+ }
7825
+ }
7826
+ }
7827
+ function removeGeneratedHandlers(file) {
7828
+ let removed = 0;
7829
+ const hooks = file.hooks ?? (file.hooks = {});
7830
+ for (const [event, groups] of Object.entries(hooks)) {
7831
+ hooks[event] = groups.filter((group) => {
7832
+ const initialCount = group.hooks.length;
7833
+ const remainingHandlers = group.hooks.filter((handler) => {
7834
+ if (isGeneratedHandler(handler)) {
7835
+ removed += 1;
7836
+ return false;
7837
+ }
7838
+ return true;
7839
+ });
7840
+ group.hooks = remainingHandlers;
7841
+ return group.hooks.length > 0 || group.hooks.length === initialCount;
7842
+ });
7843
+ }
7844
+ return removed;
7845
+ }
7846
+ function appendEntries(file, entries) {
7847
+ const hooks = file.hooks ?? (file.hooks = {});
7848
+ for (const entry of entries) {
7849
+ const groups = hooks[entry.event] ?? (hooks[entry.event] = []);
7850
+ let group = groups.find((candidate) => candidate.matcher === entry.matcher);
7851
+ if (!group) {
7852
+ group = entry.matcher === void 0 ? { hooks: [] } : { matcher: entry.matcher, hooks: [] };
7853
+ groups.push(group);
7854
+ }
7855
+ group.hooks.push(entry.handler);
7856
+ }
7857
+ }
7858
+ function writeCodexHooks(targetPath, entries, _runId) {
7859
+ const { file, fileCreated } = parseHooksFile(targetPath);
7860
+ validateEntries(entries);
7861
+ const previousGeneratedRemoved = removeGeneratedHandlers(file);
7862
+ appendEntries(file, entries);
7863
+ mkdirSync3(path31.dirname(targetPath), { recursive: true });
7864
+ const temporaryPath = `${targetPath}.tmp`;
7865
+ writeFileSync2(temporaryPath, `${JSON.stringify(file, null, 2)}
7866
+ `);
7867
+ renameSync(temporaryPath, targetPath);
7868
+ return {
7869
+ path: targetPath,
7870
+ fileCreated,
7871
+ previousGeneratedRemoved,
7872
+ generatedWritten: entries.length
7873
+ };
7874
+ }
7875
+
7876
+ // packages/agent-hook-config/src/symlink-hooks.ts
7877
+ import {
7878
+ closeSync,
7879
+ lstatSync,
7880
+ mkdirSync as mkdirSync4,
7881
+ openSync,
7882
+ readlinkSync,
7883
+ readSync,
7884
+ symlinkSync,
7885
+ unlinkSync
7886
+ } from "node:fs";
7887
+ import path32 from "node:path";
7888
+ function requireAgentConfig2(agentId) {
7889
+ const config = getAgentConfig2(agentId);
7890
+ if (!config) {
7891
+ throw new Error(`No hook configuration found for agent "${agentId}"`);
7892
+ }
7893
+ return config;
7894
+ }
7895
+ function resolveScopedPath(config, agentId, cwd, homeDir, scope) {
7896
+ const targetPath = resolveHookPath(
7897
+ config,
7898
+ scope === "project" ? "local" : "global",
7899
+ cwd,
7900
+ homeDir
7901
+ );
7902
+ if (!targetPath) {
7903
+ throw new Error(`Agent "${agentId}" has no ${scope} hook path`);
7904
+ }
7905
+ return targetPath;
7906
+ }
7907
+ function readFirstKilobyte(filePath) {
7908
+ const descriptor = openSync(filePath, "r");
7909
+ const buffer = Buffer.alloc(1024);
7910
+ try {
7911
+ const length = readSync(descriptor, buffer, 0, buffer.length, 0);
7912
+ return buffer.toString("utf8", 0, length);
7913
+ } finally {
7914
+ closeSync(descriptor);
7915
+ }
7916
+ }
7917
+ function isRecord7(value) {
7918
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7919
+ }
7920
+ function isFullyGeneratedFile(filePath) {
7921
+ let parsed;
7922
+ try {
7923
+ parsed = JSON.parse(readFirstKilobyte(filePath));
7924
+ } catch {
7925
+ return false;
7926
+ }
7927
+ if (!isRecord7(parsed)) {
7928
+ return false;
7929
+ }
7930
+ if (Object.keys(parsed).some((key) => key !== "hooks")) {
7931
+ return false;
7932
+ }
7933
+ const hooks = parsed.hooks;
7934
+ if (!isRecord7(hooks)) {
7935
+ return false;
7936
+ }
7937
+ let handlerFound = false;
7938
+ for (const groups of Object.values(hooks)) {
7939
+ if (!Array.isArray(groups)) {
7940
+ return false;
7941
+ }
7942
+ for (const group of groups) {
7943
+ if (!isRecord7(group) || !Array.isArray(group.hooks)) {
7944
+ return false;
7945
+ }
7946
+ for (const handler of group.hooks) {
7947
+ if (!isRecord7(handler)) {
7948
+ return false;
7949
+ }
7950
+ handlerFound = true;
7951
+ const statusMessage = handler.statusMessage;
7952
+ if (typeof statusMessage !== "string" || !statusMessage.startsWith("[generated:")) {
7953
+ return false;
7954
+ }
7955
+ }
7956
+ }
7957
+ }
7958
+ return handlerFound;
7959
+ }
7960
+ function symlinkHooks(sourceAgentId, targetAgentId, cwd, homeDir, scope) {
7961
+ const source = requireAgentConfig2(sourceAgentId);
7962
+ const target = requireAgentConfig2(targetAgentId);
7963
+ if (source.format !== target.format) {
7964
+ throw new Error(
7965
+ `Cannot symlink hook formats "${source.format}" and "${target.format}"; use transformation instead`
7966
+ );
7967
+ }
7968
+ const targetPath = resolveScopedPath(source, sourceAgentId, cwd, homeDir, scope);
7969
+ const symlinkPath = resolveScopedPath(target, targetAgentId, cwd, homeDir, scope);
7970
+ let replaced = "none";
7971
+ try {
7972
+ const existing = lstatSync(symlinkPath);
7973
+ if (existing.isSymbolicLink()) {
7974
+ if (readlinkSync(symlinkPath) === targetPath) {
7975
+ return { symlinkPath, targetPath, replaced };
7976
+ }
7977
+ unlinkSync(symlinkPath);
7978
+ replaced = "stale-symlink";
7979
+ } else if (existing.isFile() && isFullyGeneratedFile(symlinkPath)) {
7980
+ unlinkSync(symlinkPath);
7981
+ replaced = "generated-file";
7982
+ } else {
7983
+ throw new Error(`Refuse to replace user-authored hook file at ${symlinkPath}`);
7984
+ }
7985
+ } catch (error2) {
7986
+ if (error2.code !== "ENOENT") {
7987
+ throw error2;
7988
+ }
7989
+ }
7990
+ mkdirSync4(path32.dirname(symlinkPath), { recursive: true });
7991
+ symlinkSync(targetPath, symlinkPath);
7992
+ return { symlinkPath, targetPath, replaced };
7993
+ }
7994
+
7995
+ // packages/agent-hook-config/src/bridge-hooks.ts
7996
+ import * as fs3 from "node:fs";
7997
+ import path33 from "node:path";
7998
+ var hookExcludeMarkerPrefix = "poe-code-spawn-hooks";
7999
+ function isNodeError4(error2) {
8000
+ return error2 instanceof Error && "code" in error2;
8001
+ }
8002
+ function pathExists3(targetPath) {
8003
+ try {
8004
+ fs3.lstatSync(targetPath);
8005
+ return true;
8006
+ } catch (error2) {
8007
+ if (isNodeError4(error2) && error2.code === "ENOENT") {
8008
+ return false;
8009
+ }
8010
+ throw error2;
8011
+ }
8012
+ }
8013
+ function collectMissingParents2(targetPath) {
8014
+ const parents = [];
8015
+ let current = path33.dirname(targetPath);
8016
+ while (!pathExists3(current)) {
8017
+ parents.push(current);
8018
+ const parent = path33.dirname(current);
8019
+ if (parent === current) {
8020
+ break;
8021
+ }
8022
+ current = parent;
8023
+ }
8024
+ return parents.reverse();
8025
+ }
8026
+ function removeDirectoryIfEmpty2(targetPath) {
8027
+ try {
8028
+ fs3.rmdirSync(targetPath);
8029
+ } catch (error2) {
8030
+ if (isNodeError4(error2) && (error2.code === "ENOENT" || error2.code === "ENOTEMPTY" || error2.code === "EEXIST")) {
8031
+ return;
8032
+ }
8033
+ throw error2;
8034
+ }
8035
+ }
8036
+ function requireSupport(input, role) {
8037
+ const support = resolveAgentSupport2(input);
8038
+ if (support.status !== "supported" || !support.id || !support.config) {
8039
+ throw new Error(
8040
+ `Unsupported ${role} hook agent "${input}". Supported hook agents: ${supportedHookAgents.join(", ")}.`
8041
+ );
8042
+ }
8043
+ return { id: support.id, config: support.config };
8044
+ }
8045
+ function requireTargetPath(targetId, config, cwd, homeDir) {
8046
+ const targetPath = resolveHookPath(config, "local", cwd, homeDir);
8047
+ if (!targetPath) {
8048
+ throw new Error(`Agent "${targetId}" has no project hook path`);
8049
+ }
8050
+ return targetPath;
8051
+ }
8052
+ function readCodexFile(targetPath) {
8053
+ let content;
8054
+ try {
8055
+ content = fs3.readFileSync(targetPath, "utf8");
8056
+ } catch (error2) {
8057
+ if (isNodeError4(error2) && error2.code === "ENOENT") {
8058
+ return void 0;
8059
+ }
8060
+ throw error2;
8061
+ }
8062
+ try {
8063
+ return JSON.parse(content);
8064
+ } catch (error2) {
8065
+ throw new Error(`Malformed JSON in ${targetPath}`, { cause: error2 });
8066
+ }
8067
+ }
8068
+ function writeCodexFile(targetPath, file) {
8069
+ fs3.writeFileSync(targetPath, `${JSON.stringify(file, null, 2)}
8070
+ `, "utf8");
8071
+ }
8072
+ function hasOnlyEmptyHooks(file) {
8073
+ return Object.keys(file).every((key) => key === "hooks") && Object.values(file.hooks ?? {}).every((groups) => groups.length === 0);
8074
+ }
8075
+ function relativeToCwd(cwd, targetPath) {
8076
+ return path33.relative(cwd, targetPath);
8077
+ }
8078
+ function matcherKey(event, matcher) {
8079
+ return `${event}\0${matcher === void 0 ? "<undefined>" : matcher}`;
8080
+ }
8081
+ function bridgeHooks(sourceAgentId, targetAgentId, cwd, homeDir, runId, opts) {
8082
+ const source = requireSupport(sourceAgentId, "source");
8083
+ const target = requireSupport(targetAgentId, "target");
8084
+ const strategy = opts?.strategy ?? (source.config.format === target.config.format ? "symlink" : "transform");
8085
+ const manifest = {
8086
+ sourceAgentId,
8087
+ targetAgentId,
8088
+ cwd,
8089
+ runId,
8090
+ strategy,
8091
+ drops: []
8092
+ };
8093
+ if (strategy === "symlink") {
8094
+ const symlinkPath = requireTargetPath(target.id, target.config, cwd, homeDir);
8095
+ manifest.createdParents = collectMissingParents2(symlinkPath);
8096
+ const result = symlinkHooks(source.id, target.id, cwd, homeDir, "project");
8097
+ manifest.symlinkPath = result.symlinkPath;
8098
+ manifest.symlinkTarget = result.targetPath;
8099
+ manifest.symlinkReplaced = result.replaced;
8100
+ appendExcludeBlock(cwd, runId, [relativeToCwd(cwd, result.symlinkPath)], {
8101
+ markerPrefix: hookExcludeMarkerPrefix
8102
+ });
8103
+ return manifest;
8104
+ }
8105
+ if (source.id !== "claude-code") {
8106
+ throw new Error(`Transforming hooks from "${source.id}" is not supported yet`);
8107
+ }
8108
+ if (target.config.format !== "codex-hooks-json") {
8109
+ throw new Error(
8110
+ `Transforming hooks to "${target.id}" is not supported yet; only codex-hook targets can be written`
8111
+ );
8112
+ }
8113
+ const targetPath = requireTargetPath(target.id, target.config, cwd, homeDir);
8114
+ const priorFile = readCodexFile(targetPath);
8115
+ const sourceHooks = readClaudeHooks(cwd, homeDir, { scope: opts?.scope ?? "merged" });
8116
+ const transformed = transformHooks(sourceHooks.entries, source.id, target.id, { runId });
8117
+ const createdParents = collectMissingParents2(targetPath);
8118
+ const writeResult = writeCodexHooks(targetPath, transformed.entries, runId);
8119
+ manifest.writtenPath = targetPath;
8120
+ manifest.generatedEntryIds = transformed.entries.map((entry) => entry.generatedId);
8121
+ manifest.drops = transformed.drops;
8122
+ manifest.createdParents = createdParents;
8123
+ manifest.fileCreated = writeResult.fileCreated;
8124
+ manifest.preExistingEvents = Object.keys(priorFile?.hooks ?? {});
8125
+ manifest.preExistingMatchers = Object.entries(priorFile?.hooks ?? {}).flatMap(
8126
+ ([event, groups]) => groups.map((group) => ({
8127
+ event,
8128
+ ...group.matcher === void 0 ? {} : { matcher: group.matcher }
8129
+ }))
8130
+ );
8131
+ appendExcludeBlock(cwd, runId, [relativeToCwd(cwd, targetPath)], {
8132
+ markerPrefix: hookExcludeMarkerPrefix
8133
+ });
8134
+ return manifest;
8135
+ }
8136
+ function cleanupBridgedHooks(manifest) {
8137
+ if (manifest.strategy === "symlink" && manifest.symlinkPath && manifest.symlinkTarget) {
8138
+ try {
8139
+ if (fs3.lstatSync(manifest.symlinkPath).isSymbolicLink() && fs3.readlinkSync(manifest.symlinkPath) === manifest.symlinkTarget) {
8140
+ fs3.unlinkSync(manifest.symlinkPath);
8141
+ }
8142
+ } catch (error2) {
8143
+ if (!isNodeError4(error2) || error2.code !== "ENOENT") {
8144
+ throw error2;
8145
+ }
8146
+ }
8147
+ for (const parent of [...manifest.createdParents ?? []].reverse()) {
8148
+ removeDirectoryIfEmpty2(parent);
8149
+ }
8150
+ }
8151
+ if (manifest.strategy === "transform" && manifest.writtenPath) {
8152
+ const file = readCodexFile(manifest.writtenPath);
8153
+ if (file) {
8154
+ const generatedPrefix = `[generated:${manifest.runId}]`;
8155
+ const preExistingEvents = new Set(manifest.preExistingEvents ?? []);
8156
+ const preExistingMatchers = new Set(
8157
+ (manifest.preExistingMatchers ?? []).map((group) => matcherKey(group.event, group.matcher))
8158
+ );
8159
+ const hooks = file.hooks ?? {};
8160
+ for (const [event, groups] of Object.entries(hooks)) {
8161
+ hooks[event] = groups.filter((group) => {
8162
+ const priorLength = group.hooks.length;
8163
+ group.hooks = group.hooks.filter(
8164
+ (handler) => !handler.statusMessage?.startsWith(generatedPrefix)
8165
+ );
8166
+ return group.hooks.length > 0 || group.hooks.length === priorLength || preExistingMatchers.has(matcherKey(event, group.matcher));
8167
+ });
8168
+ if (hooks[event].length === 0 && !preExistingEvents.has(event)) {
8169
+ delete hooks[event];
8170
+ }
8171
+ }
8172
+ file.hooks = hooks;
8173
+ if (manifest.fileCreated && hasOnlyEmptyHooks(file)) {
8174
+ fs3.unlinkSync(manifest.writtenPath);
8175
+ } else {
8176
+ writeCodexFile(manifest.writtenPath, file);
8177
+ }
8178
+ }
8179
+ for (const parent of [...manifest.createdParents ?? []].reverse()) {
8180
+ removeDirectoryIfEmpty2(parent);
8181
+ }
8182
+ }
8183
+ removeExcludeBlock(manifest.cwd, manifest.runId, { markerPrefix: hookExcludeMarkerPrefix });
8184
+ }
8185
+
7489
8186
  // packages/agent-spawn/src/skill-bridge.ts
7490
- function bridgeSkillsForRun(agentId, cwd, skills) {
7491
- if (!skills || skills.length === 0) {
8187
+ function bridgeResourcesForRun(agentId, cwd, skills, hooks) {
8188
+ if ((!skills || skills.length === 0) && !hooks) {
7492
8189
  return void 0;
7493
8190
  }
7494
- const manifest = bridgeActiveSkills(agentId, cwd, skills, os6.homedir(), crypto.randomUUID());
7495
- for (const warning2 of manifest.warnings) {
7496
- logger.warn(warning2.message);
8191
+ const runId = crypto.randomUUID();
8192
+ const manifests = {};
8193
+ try {
8194
+ if (skills && skills.length > 0) {
8195
+ manifests.skills = bridgeActiveSkills(agentId, cwd, skills, os7.homedir(), runId);
8196
+ for (const warning2 of manifests.skills.warnings) {
8197
+ logger.warn(warning2.message);
8198
+ }
8199
+ }
8200
+ if (hooks) {
8201
+ manifests.hooks = bridgeHooks(hooks.from, agentId, cwd, os7.homedir(), runId, {
8202
+ strategy: hooks.strategy === "auto" ? void 0 : hooks.strategy,
8203
+ scope: hooks.scope
8204
+ });
8205
+ for (const drop of manifests.hooks.drops) {
8206
+ logger.warn(
8207
+ `Dropped bridged hook event "${drop.source.event}" with handler type "${drop.source.handler.type}": ${drop.detail}`
8208
+ );
8209
+ }
8210
+ }
8211
+ } catch (error2) {
8212
+ cleanupResourcesForRun(manifests);
8213
+ throw error2;
7497
8214
  }
7498
- return manifest;
8215
+ return manifests;
7499
8216
  }
7500
- function cleanupSkillsForRun(manifest) {
8217
+ function cleanupResourcesForRun(manifest) {
7501
8218
  if (!manifest) {
7502
8219
  return;
7503
8220
  }
7504
- cleanupBridgedSkills(manifest);
8221
+ if (manifest.hooks) {
8222
+ cleanupBridgedHooks(manifest.hooks);
8223
+ }
8224
+ if (manifest.skills) {
8225
+ cleanupBridgedSkills(manifest.skills);
8226
+ }
7505
8227
  }
7506
8228
 
7507
8229
  // packages/agent-spawn/src/adapters/utils.ts
@@ -7627,21 +8349,21 @@ async function* adaptClaude(lines) {
7627
8349
  if (blockType !== "tool_result") continue;
7628
8350
  const kind = toolKindsById.get(item.tool_use_id);
7629
8351
  toolKindsById.delete(item.tool_use_id);
7630
- let path32;
8352
+ let path37;
7631
8353
  if (typeof item.content === "string") {
7632
- path32 = item.content;
8354
+ path37 = item.content;
7633
8355
  } else {
7634
8356
  try {
7635
- path32 = JSON.stringify(item.content);
8357
+ path37 = JSON.stringify(item.content);
7636
8358
  } catch {
7637
- path32 = String(item.content);
8359
+ path37 = String(item.content);
7638
8360
  }
7639
8361
  }
7640
8362
  yield {
7641
8363
  event: "tool_complete",
7642
8364
  id: item.tool_use_id,
7643
8365
  kind,
7644
- path: path32
8366
+ path: path37
7645
8367
  };
7646
8368
  }
7647
8369
  }
@@ -7737,10 +8459,10 @@ async function* adaptCodex(lines) {
7737
8459
  const kindFromStart = toolKindById.get(item.id);
7738
8460
  const kind = kindFromStart ?? (itemType === "command_execution" ? "exec" : itemType === "file_edit" ? "edit" : "other");
7739
8461
  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;
7740
- const path32 = titleFromEvent ?? toolTitleById.get(item.id) ?? "";
8462
+ const path37 = titleFromEvent ?? toolTitleById.get(item.id) ?? "";
7741
8463
  toolTitleById.delete(item.id);
7742
8464
  toolKindById.delete(item.id);
7743
- yield { event: "tool_complete", id: item.id, kind, path: path32 };
8465
+ yield { event: "tool_complete", id: item.id, kind, path: path37 };
7744
8466
  }
7745
8467
  }
7746
8468
  }
@@ -8268,7 +8990,7 @@ function spawnStreaming(options) {
8268
8990
  };
8269
8991
  }
8270
8992
  };
8271
- const manifest = bridgeSkillsForRun(options.agentId, cwd, options.skills);
8993
+ const manifest = bridgeResourcesForRun(options.agentId, cwd, options.skills, options.hooks);
8272
8994
  void (async () => {
8273
8995
  try {
8274
8996
  for await (const output of adapter(queue.lines())) {
@@ -8319,7 +9041,7 @@ function spawnStreaming(options) {
8319
9041
  ...ctx.logFile && !result.logFile ? { logFile: ctx.logFile } : {}
8320
9042
  };
8321
9043
  } finally {
8322
- cleanupSkillsForRun(manifest);
9044
+ cleanupResourcesForRun(manifest);
8323
9045
  }
8324
9046
  })();
8325
9047
  return {
@@ -8461,7 +9183,7 @@ async function runSpawn(agentId, options, context) {
8461
9183
  return { stdout: "", stderr: "", exitCode: 0 };
8462
9184
  }
8463
9185
  const cwd = options.cwd ?? process.cwd();
8464
- const manifest = bridgeSkillsForRun(agentId, cwd, options.skills);
9186
+ const manifest = bridgeResourcesForRun(agentId, cwd, options.skills, options.hooks);
8465
9187
  let logFd;
8466
9188
  try {
8467
9189
  const logFilePath = resolveSpawnLogPath(options);
@@ -8529,7 +9251,7 @@ async function runSpawn(agentId, options, context) {
8529
9251
  };
8530
9252
  } finally {
8531
9253
  closeSpawnLog(logFd);
8532
- cleanupSkillsForRun(manifest);
9254
+ cleanupResourcesForRun(manifest);
8533
9255
  }
8534
9256
  }
8535
9257
  spawn4.retry = createSpawnRetry((service, options) => {
@@ -8551,12 +9273,12 @@ function resolveSpawnLogPath(options) {
8551
9273
  if (!options.logDir || !options.logFileName) {
8552
9274
  return void 0;
8553
9275
  }
8554
- return path29.join(options.logDir, options.logFileName);
9276
+ return path34.join(options.logDir, options.logFileName);
8555
9277
  }
8556
9278
  function openSpawnLog(filePath) {
8557
9279
  try {
8558
- mkdirSync3(path29.dirname(filePath), { recursive: true });
8559
- return openSync(filePath, "a");
9280
+ mkdirSync5(path34.dirname(filePath), { recursive: true });
9281
+ return openSync2(filePath, "a");
8560
9282
  } catch {
8561
9283
  return void 0;
8562
9284
  }
@@ -8571,7 +9293,7 @@ function appendSpawnLog(fd, chunk) {
8571
9293
  function closeSpawnLog(fd) {
8572
9294
  if (fd === void 0) return;
8573
9295
  try {
8574
- closeSync(fd);
9296
+ closeSync2(fd);
8575
9297
  } catch {
8576
9298
  }
8577
9299
  }
@@ -8580,7 +9302,7 @@ function closeSpawnLog(fd) {
8580
9302
  var DEFAULT_ACTIVITY_TIMEOUT_MS = 10 * 60 * 1e3;
8581
9303
 
8582
9304
  // packages/agent-spawn/src/acp/replay.ts
8583
- import path30 from "node:path";
9305
+ import path35 from "node:path";
8584
9306
  import { homedir as homedir2 } from "node:os";
8585
9307
  import { open as open2, readdir as readdir2 } from "node:fs/promises";
8586
9308
  import { createInterface } from "node:readline";
@@ -8599,7 +9321,7 @@ import { homedir } from "node:os";
8599
9321
  import { join } from "node:path";
8600
9322
 
8601
9323
  // packages/agent-spawn/src/acp/middlewares/spawn-log.ts
8602
- import path31 from "node:path";
9324
+ import path36 from "node:path";
8603
9325
  import { homedir as homedir3 } from "node:os";
8604
9326
  import { mkdir, open as open3 } from "node:fs/promises";
8605
9327
 
@@ -8613,9 +9335,30 @@ stderr:
8613
9335
  ${stderr}`;
8614
9336
  }
8615
9337
  function createSpawnHealthCheck(agentId, options) {
8616
- const prompt = `Output exactly: ${options.expectedOutput}`;
8617
- const { binaryName, args, env: modeEnv } = buildSpawnArgs(agentId, {
8618
- prompt,
9338
+ const {
9339
+ binaryName,
9340
+ args,
9341
+ env: modeEnv
9342
+ } = options.hooks ? {
9343
+ binaryName: "poe-code",
9344
+ args: [
9345
+ "spawn",
9346
+ "--hooks-from",
9347
+ options.hooks.from,
9348
+ ...options.hooks.strategy ? ["--hooks-strategy", options.hooks.strategy] : [],
9349
+ ...options.model ? ["--model", options.model] : [],
9350
+ "--mode",
9351
+ "yolo",
9352
+ agentId,
9353
+ `Output exactly: ${options.expectedOutput}`
9354
+ ],
9355
+ env: void 0
9356
+ } : options.invocation ? {
9357
+ binaryName: options.invocation.command,
9358
+ args: options.invocation.args,
9359
+ env: options.invocation.env
9360
+ } : buildSpawnArgs(agentId, {
9361
+ prompt: `Output exactly: ${options.expectedOutput}`,
8619
9362
  model: options.model,
8620
9363
  mode: "yolo"
8621
9364
  });
@@ -8630,6 +9373,13 @@ function createSpawnHealthCheck(agentId, options) {
8630
9373
  return;
8631
9374
  }
8632
9375
  const result = modeEnv ? await context.runCommand(binaryName, args, { env: modeEnv }) : await context.runCommand(binaryName, args);
9376
+ if (options.hooks) {
9377
+ for (const line of result.stdout.split("\n")) {
9378
+ if (line.includes("Dropped bridged hook event")) {
9379
+ context.logWarning?.(line);
9380
+ }
9381
+ }
9382
+ }
8633
9383
  if (result.exitCode !== 0) {
8634
9384
  throw new Error(
8635
9385
  `spawn ${agentId} failed with exit code ${result.exitCode}.
@@ -8767,6 +9517,7 @@ function createProvider(opts) {
8767
9517
  configurePrompts: opts.configurePrompts,
8768
9518
  postConfigureMessages: opts.postConfigureMessages,
8769
9519
  extendConfigurePayload: opts.extendConfigurePayload,
9520
+ runtimeEnv: opts.runtimeEnv,
8770
9521
  isolatedEnv: opts.isolatedEnv,
8771
9522
  async configure(context, runOptions) {
8772
9523
  await runMutations(opts.manifest.configure, {