poe-code 3.0.223 → 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 (88) hide show
  1. package/dist/bin/poe-gemini.js +23 -0
  2. package/dist/cli/commands/configure-payload.js +100 -13
  3. package/dist/cli/commands/configure-payload.js.map +1 -1
  4. package/dist/cli/commands/configure.js +2 -2
  5. package/dist/cli/commands/configure.js.map +1 -1
  6. package/dist/cli/commands/pipeline.js +4 -2
  7. package/dist/cli/commands/pipeline.js.map +1 -1
  8. package/dist/cli/commands/provider.d.ts +1 -0
  9. package/dist/cli/commands/provider.js +94 -3
  10. package/dist/cli/commands/provider.js.map +1 -1
  11. package/dist/cli/commands/ralph.js +1 -0
  12. package/dist/cli/commands/ralph.js.map +1 -1
  13. package/dist/cli/commands/shared.d.ts +8 -1
  14. package/dist/cli/commands/shared.js +88 -7
  15. package/dist/cli/commands/shared.js.map +1 -1
  16. package/dist/cli/commands/spawn.js +20 -0
  17. package/dist/cli/commands/spawn.js.map +1 -1
  18. package/dist/cli/commands/test.d.ts +5 -1
  19. package/dist/cli/commands/test.js +58 -6
  20. package/dist/cli/commands/test.js.map +1 -1
  21. package/dist/cli/constants.d.ts +2 -2
  22. package/dist/cli/constants.js +3 -14
  23. package/dist/cli/constants.js.map +1 -1
  24. package/dist/cli/isolated-env.d.ts +2 -1
  25. package/dist/cli/isolated-env.js +20 -2
  26. package/dist/cli/isolated-env.js.map +1 -1
  27. package/dist/cli/options.d.ts +2 -5
  28. package/dist/cli/options.js +10 -1
  29. package/dist/cli/options.js.map +1 -1
  30. package/dist/cli/poe-code-command-runner.js +7 -2
  31. package/dist/cli/poe-code-command-runner.js.map +1 -1
  32. package/dist/cli/prompt-runner.js +4 -2
  33. package/dist/cli/prompt-runner.js.map +1 -1
  34. package/dist/cli/prompts.d.ts +23 -13
  35. package/dist/cli/prompts.js +13 -0
  36. package/dist/cli/prompts.js.map +1 -1
  37. package/dist/cli/service-registry.d.ts +12 -3
  38. package/dist/cli/service-registry.js.map +1 -1
  39. package/dist/index.d.ts +3 -1
  40. package/dist/index.js +4936 -2415
  41. package/dist/index.js.map +4 -4
  42. package/dist/providers/claude-code.js +924 -174
  43. package/dist/providers/claude-code.js.map +4 -4
  44. package/dist/providers/codex.js +916 -178
  45. package/dist/providers/codex.js.map +4 -4
  46. package/dist/providers/create-provider.d.ts +1 -0
  47. package/dist/providers/create-provider.js +1 -0
  48. package/dist/providers/create-provider.js.map +1 -1
  49. package/dist/providers/gemini-cli.d.ts +16 -0
  50. package/dist/providers/gemini-cli.js +11349 -0
  51. package/dist/providers/gemini-cli.js.map +7 -0
  52. package/dist/providers/goose.js +881 -183
  53. package/dist/providers/goose.js.map +4 -4
  54. package/dist/providers/kimi.js +905 -165
  55. package/dist/providers/kimi.js.map +4 -4
  56. package/dist/providers/opencode.js +905 -165
  57. package/dist/providers/opencode.js.map +4 -4
  58. package/dist/providers/poe-agent.js +1155 -357
  59. package/dist/providers/poe-agent.js.map +4 -4
  60. package/dist/providers/spawn-options.d.ts +3 -0
  61. package/dist/sdk/pipeline.d.ts +1 -1
  62. package/dist/sdk/pipeline.js +22 -19
  63. package/dist/sdk/pipeline.js.map +1 -1
  64. package/dist/sdk/ralph.js +5 -0
  65. package/dist/sdk/ralph.js.map +1 -1
  66. package/dist/sdk/spawn-core.d.ts +3 -0
  67. package/dist/sdk/spawn-core.js +6 -8
  68. package/dist/sdk/spawn-core.js.map +1 -1
  69. package/dist/sdk/spawn.d.ts +8 -8
  70. package/dist/sdk/spawn.js +26 -3
  71. package/dist/sdk/spawn.js.map +1 -1
  72. package/dist/sdk/types.d.ts +7 -0
  73. package/dist/utils/command-checks.d.ts +8 -0
  74. package/dist/utils/command-checks.js +35 -9
  75. package/dist/utils/command-checks.js.map +1 -1
  76. package/dist/utils/dry-run.js +7 -1
  77. package/dist/utils/dry-run.js.map +1 -1
  78. package/dist/workflow-templates/github-issue-opened.caller.yml +2 -1
  79. package/dist/workflow-templates/github-issue-opened.ejected.yml +2 -1
  80. package/package.json +2 -1
  81. package/packages/agent-skill-config/dist/configs.js +4 -0
  82. package/packages/agent-skill-config/dist/git-exclude.d.ts +6 -2
  83. package/packages/agent-skill-config/dist/git-exclude.js +12 -12
  84. package/packages/memory/dist/index.js +924 -200
  85. package/packages/memory/dist/index.js.map +4 -4
  86. package/packages/superintendent/dist/index.d.ts +2 -1
  87. package/packages/superintendent/dist/runtime/loop.d.ts +1 -0
  88. package/packages/superintendent/dist/runtime/loop.js +14 -2
@@ -48,7 +48,7 @@ var require_requirements_txt = __commonJS({
48
48
  // src/templates/codex/config.toml.mustache
49
49
  var require_config_toml = __commonJS({
50
50
  "src/templates/codex/config.toml.mustache"(exports, module) {
51
- module.exports = 'model_provider = "{{providerId}}"\n\n[profiles."{{{profileName}}}"]\nmodel = "{{{model}}}"\nmodel_provider = "{{providerId}}"\nmodel_reasoning_effort = "{{reasoningEffort}}"\nmodel_verbosity = "medium"\n\n[model_providers.{{providerId}}]\nname = "{{providerId}}"\nbase_url = "{{{baseUrl}}}"\nwire_api = "responses"\nexperimental_bearer_token = "{{apiKey}}"\nrequires_openai_auth = false\nsupports_websockets = false\n';
51
+ module.exports = 'model_provider = "{{providerId}}"\nmodel = "{{{model}}}"\nmodel_reasoning_effort = "{{reasoningEffort}}"\nmodel_verbosity = "medium"\n\n[profiles."{{{profileName}}}"]\nmodel = "{{{model}}}"\nmodel_provider = "{{providerId}}"\nmodel_reasoning_effort = "{{reasoningEffort}}"\nmodel_verbosity = "medium"\n\n[model_providers.{{providerId}}]\nname = "{{providerId}}"\nbase_url = "{{{baseUrl}}}"\nwire_api = "responses"\nexperimental_bearer_token = "{{apiKey}}"\nrequires_openai_auth = false\nsupports_websockets = false\n';
52
52
  }
53
53
  });
54
54
 
@@ -67,19 +67,6 @@ var CLAUDE_CODE_VARIANTS = {
67
67
  opus: "anthropic/claude-opus-4.7"
68
68
  };
69
69
  var DEFAULT_CLAUDE_CODE_MODEL = CLAUDE_CODE_VARIANTS.sonnet;
70
- var CODEX_MODELS = [
71
- "openai/gpt-5.5",
72
- "openai/gpt-5.4",
73
- "openai/gpt-5.3-codex",
74
- "openai/gpt-5.2-codex",
75
- "openai/gpt-5.2",
76
- "openai/gpt-5.2-chat",
77
- "openai/gpt-5.2-pro",
78
- "openai/gpt-5.1",
79
- "openai/gpt-5.1-codex-mini",
80
- "anthropic/claude-opus-4.7"
81
- ];
82
- var DEFAULT_CODEX_MODEL = CODEX_MODELS[0];
83
70
  var KIMI_MODELS = [
84
71
  "novitaai/kimi-k2.5",
85
72
  "novitaai/kimi-k2-thinking",
@@ -840,17 +827,17 @@ function isPidRunning(pid) {
840
827
  }
841
828
  function createDefaultFs() {
842
829
  return {
843
- open: (path32, flags) => fsPromises.open(path32, flags),
844
- readFile: (path32, encoding) => fsPromises.readFile(path32, encoding),
830
+ open: (path37, flags) => fsPromises.open(path37, flags),
831
+ readFile: (path37, encoding) => fsPromises.readFile(path37, encoding),
845
832
  stat: fsPromises.stat,
846
833
  unlink: fsPromises.unlink
847
834
  };
848
835
  }
849
- async function removeLockFile(fs3, lockPath, signal) {
836
+ async function removeLockFile(fs4, lockPath, signal) {
850
837
  for (let attempt = 0; attempt <= 4; attempt += 1) {
851
838
  throwIfAborted(signal);
852
839
  try {
853
- await fs3.unlink(lockPath);
840
+ await fs4.unlink(lockPath);
854
841
  return;
855
842
  } catch (error2) {
856
843
  if (hasErrorCode(error2, "ENOENT")) {
@@ -881,12 +868,12 @@ function parseLockMetadata(content) {
881
868
  }
882
869
  return void 0;
883
870
  }
884
- async function readLockMetadata(fs3, lockPath) {
885
- if (!fs3.readFile) {
871
+ async function readLockMetadata(fs4, lockPath) {
872
+ if (!fs4.readFile) {
886
873
  return void 0;
887
874
  }
888
875
  try {
889
- return parseLockMetadata(await fs3.readFile(lockPath, "utf8"));
876
+ return parseLockMetadata(await fs4.readFile(lockPath, "utf8"));
890
877
  } catch (error2) {
891
878
  if (hasErrorCode(error2, "ENOENT")) {
892
879
  return null;
@@ -920,7 +907,7 @@ async function writeLockMetadata(handle) {
920
907
  }
921
908
  }
922
909
  async function acquireFileLock(filePath, options = {}) {
923
- const fs3 = options.fs ?? createDefaultFs();
910
+ const fs4 = options.fs ?? createDefaultFs();
924
911
  const retries = options.retries ?? 20;
925
912
  const minTimeout = options.minTimeout ?? 25;
926
913
  const maxTimeout = options.maxTimeout ?? 250;
@@ -931,7 +918,7 @@ async function acquireFileLock(filePath, options = {}) {
931
918
  while (attempt <= retries) {
932
919
  throwIfAborted(options.signal);
933
920
  try {
934
- const handle = await fs3.open(lockPath, "wx");
921
+ const handle = await fs4.open(lockPath, "wx");
935
922
  await writeLockMetadata(handle);
936
923
  let released = false;
937
924
  return async () => {
@@ -939,7 +926,7 @@ async function acquireFileLock(filePath, options = {}) {
939
926
  return;
940
927
  }
941
928
  released = true;
942
- await removeLockFile(fs3, lockPath, options.signal);
929
+ await removeLockFile(fs4, lockPath, options.signal);
943
930
  };
944
931
  } catch (error2) {
945
932
  if (!hasErrorCode(error2, "EEXIST")) {
@@ -948,7 +935,7 @@ async function acquireFileLock(filePath, options = {}) {
948
935
  }
949
936
  let stat3;
950
937
  try {
951
- stat3 = await fs3.stat(lockPath);
938
+ stat3 = await fs4.stat(lockPath);
952
939
  } catch (statError) {
953
940
  if (hasErrorCode(statError, "ENOENT")) {
954
941
  continue;
@@ -956,7 +943,7 @@ async function acquireFileLock(filePath, options = {}) {
956
943
  throw statError;
957
944
  }
958
945
  const reclaimLock = await shouldReclaimLock({
959
- fs: fs3,
946
+ fs: fs4,
960
947
  isPidRunning: pidIsRunning,
961
948
  lockPath,
962
949
  staleMs,
@@ -966,7 +953,7 @@ async function acquireFileLock(filePath, options = {}) {
966
953
  continue;
967
954
  }
968
955
  if (reclaimLock) {
969
- await removeLockFile(fs3, lockPath, options.signal);
956
+ await removeLockFile(fs4, lockPath, options.signal);
970
957
  continue;
971
958
  }
972
959
  if (attempt >= retries) {
@@ -1143,6 +1130,24 @@ var codexAgent = {
1143
1130
  }
1144
1131
  };
1145
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
+
1146
1151
  // packages/agent-defs/src/agents/opencode.ts
1147
1152
  var openCodeAgent = {
1148
1153
  id: "opencode",
@@ -1216,6 +1221,7 @@ var allAgents = [
1216
1221
  claudeCodeAgent,
1217
1222
  claudeDesktopAgent,
1218
1223
  codexAgent,
1224
+ geminiCliAgent,
1219
1225
  openCodeAgent,
1220
1226
  kimiAgent,
1221
1227
  gooseAgent,
@@ -1266,29 +1272,29 @@ function wrapForLogTee(argv, jobId) {
1266
1272
  return ["sh", "-c", script];
1267
1273
  }
1268
1274
  async function* streamLogFile(env, jobId, opts) {
1269
- const fs3 = env.fs ?? nodeFs;
1275
+ const fs4 = env.fs ?? nodeFs;
1270
1276
  const file = jobLogPath(jobId);
1271
1277
  let byteOffset = opts.sinceByte ?? 0;
1272
1278
  while (true) {
1273
- if (opts.since !== void 0 && !await wasModifiedSince(fs3, file, opts.since)) {
1274
- await waitForLogChange(fs3, file);
1279
+ if (opts.since !== void 0 && !await wasModifiedSince(fs4, file, opts.since)) {
1280
+ await waitForLogChange(fs4, file);
1275
1281
  continue;
1276
1282
  }
1277
- const result = await readLogChunk(fs3, file, byteOffset);
1283
+ const result = await readLogChunk(fs4, file, byteOffset);
1278
1284
  if (result !== null) {
1279
1285
  byteOffset = result.nextByteOffset;
1280
1286
  yield result.chunk;
1281
1287
  continue;
1282
1288
  }
1283
- await waitForLogChange(fs3, file);
1289
+ await waitForLogChange(fs4, file);
1284
1290
  }
1285
1291
  }
1286
- async function wasModifiedSince(fs3, file, since) {
1287
- if (fs3.promises.stat === void 0) {
1292
+ async function wasModifiedSince(fs4, file, since) {
1293
+ if (fs4.promises.stat === void 0) {
1288
1294
  return true;
1289
1295
  }
1290
1296
  try {
1291
- const stat3 = await fs3.promises.stat(file);
1297
+ const stat3 = await fs4.promises.stat(file);
1292
1298
  return stat3.mtimeMs >= since.getTime();
1293
1299
  } catch (error2) {
1294
1300
  if (isNodeError(error2) && error2.code === "ENOENT") {
@@ -1298,11 +1304,11 @@ async function wasModifiedSince(fs3, file, since) {
1298
1304
  }
1299
1305
  }
1300
1306
  async function waitForExit(env, jobId, opts = {}) {
1301
- const fs3 = env.fs ?? nodeFs;
1307
+ const fs4 = env.fs ?? nodeFs;
1302
1308
  const file = jobExitPath(jobId);
1303
1309
  while (true) {
1304
1310
  throwIfAborted2(opts.signal);
1305
- const contents = await readTextFileIfExists(fs3, file);
1311
+ const contents = await readTextFileIfExists(fs4, file);
1306
1312
  if (contents !== null) {
1307
1313
  const text5 = contents.trim();
1308
1314
  const exitCode = Number(text5);
@@ -1320,8 +1326,8 @@ function jobLogPath(jobId) {
1320
1326
  function jobExitPath(jobId) {
1321
1327
  return `${JOB_DIR}/${jobId}.exit`;
1322
1328
  }
1323
- async function readLogChunk(fs3, file, byteOffset) {
1324
- const contents = await readFileIfExists(fs3, file);
1329
+ async function readLogChunk(fs4, file, byteOffset) {
1330
+ const contents = await readFileIfExists(fs4, file);
1325
1331
  if (contents === null || byteOffset >= contents.byteLength) {
1326
1332
  return null;
1327
1333
  }
@@ -1333,13 +1339,13 @@ async function readLogChunk(fs3, file, byteOffset) {
1333
1339
  nextByteOffset: contents.byteLength
1334
1340
  };
1335
1341
  }
1336
- async function readTextFileIfExists(fs3, file) {
1337
- const contents = await readFileIfExists(fs3, file);
1342
+ async function readTextFileIfExists(fs4, file) {
1343
+ const contents = await readFileIfExists(fs4, file);
1338
1344
  return contents?.toString("utf8") ?? null;
1339
1345
  }
1340
- async function readFileIfExists(fs3, file) {
1346
+ async function readFileIfExists(fs4, file) {
1341
1347
  try {
1342
- const contents = await fs3.promises.readFile(file);
1348
+ const contents = await fs4.promises.readFile(file);
1343
1349
  return Buffer.isBuffer(contents) ? contents : Buffer.from(contents);
1344
1350
  } catch (error2) {
1345
1351
  if (isNodeError(error2) && error2.code === "ENOENT") {
@@ -1348,8 +1354,8 @@ async function readFileIfExists(fs3, file) {
1348
1354
  throw error2;
1349
1355
  }
1350
1356
  }
1351
- async function waitForLogChange(fs3, file) {
1352
- const watch = fs3.watch;
1357
+ async function waitForLogChange(fs4, file) {
1358
+ const watch = fs4.watch;
1353
1359
  if (typeof watch !== "function") {
1354
1360
  await sleep2(POLL_INTERVAL_MS);
1355
1361
  return;
@@ -2315,7 +2321,7 @@ import path14 from "node:path";
2315
2321
 
2316
2322
  // packages/config-extends/src/discover.ts
2317
2323
  import path10 from "node:path";
2318
- async function findBase(name, bases, fs3) {
2324
+ async function findBase(name, bases, fs4) {
2319
2325
  const checkedPaths = [];
2320
2326
  for (const basePath of bases) {
2321
2327
  for (const extension of [".md", ".yaml", ".yml", ".json"]) {
@@ -2323,7 +2329,7 @@ async function findBase(name, bases, fs3) {
2323
2329
  checkedPaths.push(filePath);
2324
2330
  try {
2325
2331
  return {
2326
- content: await fs3.readFile(filePath, "utf8"),
2332
+ content: await fs4.readFile(filePath, "utf8"),
2327
2333
  filePath
2328
2334
  };
2329
2335
  } catch (error2) {
@@ -2403,11 +2409,11 @@ function stripBom(content) {
2403
2409
  function mergeLayers(layers) {
2404
2410
  return mergeObjectLayers(layers, []);
2405
2411
  }
2406
- function mergeObjectLayers(layers, path32) {
2412
+ function mergeObjectLayers(layers, path37) {
2407
2413
  const data = {};
2408
2414
  const sources = {};
2409
2415
  for (const key of collectKeys(layers)) {
2410
- const resolved = resolveKey(layers, key, path32);
2416
+ const resolved = resolveKey(layers, key, path37);
2411
2417
  if (resolved === void 0) {
2412
2418
  continue;
2413
2419
  }
@@ -2425,7 +2431,7 @@ function collectKeys(layers) {
2425
2431
  }
2426
2432
  return [...keys];
2427
2433
  }
2428
- function resolveKey(layers, key, path32) {
2434
+ function resolveKey(layers, key, path37) {
2429
2435
  let winningSource;
2430
2436
  let winningValue;
2431
2437
  const objectLayers = [];
@@ -2455,9 +2461,9 @@ function resolveKey(layers, key, path32) {
2455
2461
  if (winningSource === void 0) {
2456
2462
  return void 0;
2457
2463
  }
2458
- const fullPath = buildPath(path32, key);
2464
+ const fullPath = buildPath(path37, key);
2459
2465
  if (isPlainObject(winningValue)) {
2460
- const merged = mergeObjectLayers(objectLayers, [...path32, key]);
2466
+ const merged = mergeObjectLayers(objectLayers, [...path37, key]);
2461
2467
  return {
2462
2468
  value: merged.data,
2463
2469
  sources: {
@@ -2482,8 +2488,8 @@ function isWinningCandidate(key, value) {
2482
2488
  }
2483
2489
  return true;
2484
2490
  }
2485
- function buildPath(path32, key) {
2486
- return [...path32, key].join(".");
2491
+ function buildPath(path37, key) {
2492
+ return [...path37, key].join(".");
2487
2493
  }
2488
2494
  function isPlainObject(value) {
2489
2495
  if (value === null || Array.isArray(value) || typeof value !== "object") {
@@ -3837,16 +3843,16 @@ function getConfigFormat(pathOrFormat) {
3837
3843
  }
3838
3844
  return formatRegistry[formatName];
3839
3845
  }
3840
- function detectFormat2(path32) {
3841
- const ext = getExtension(path32);
3846
+ function detectFormat2(path37) {
3847
+ const ext = getExtension(path37);
3842
3848
  return extensionMap[ext];
3843
3849
  }
3844
- function getExtension(path32) {
3845
- const lastDot = path32.lastIndexOf(".");
3850
+ function getExtension(path37) {
3851
+ const lastDot = path37.lastIndexOf(".");
3846
3852
  if (lastDot === -1) {
3847
3853
  return "";
3848
3854
  }
3849
- return path32.slice(lastDot).toLowerCase();
3855
+ return path37.slice(lastDot).toLowerCase();
3850
3856
  }
3851
3857
 
3852
3858
  // packages/config-mutations/src/execution/path-utils.ts
@@ -3897,9 +3903,9 @@ function resolvePath(rawPath, homeDir, pathMapper) {
3897
3903
  function isNotFound(error2) {
3898
3904
  return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === "ENOENT";
3899
3905
  }
3900
- async function readFileIfExists2(fs3, target) {
3906
+ async function readFileIfExists2(fs4, target) {
3901
3907
  try {
3902
- return await fs3.readFile(target, "utf8");
3908
+ return await fs4.readFile(target, "utf8");
3903
3909
  } catch (error2) {
3904
3910
  if (isNotFound(error2)) {
3905
3911
  return null;
@@ -3907,9 +3913,9 @@ async function readFileIfExists2(fs3, target) {
3907
3913
  throw error2;
3908
3914
  }
3909
3915
  }
3910
- async function pathExists(fs3, target) {
3916
+ async function pathExists(fs4, target) {
3911
3917
  try {
3912
- await fs3.stat(target);
3918
+ await fs4.stat(target);
3913
3919
  return true;
3914
3920
  } catch (error2) {
3915
3921
  if (isNotFound(error2)) {
@@ -3933,9 +3939,9 @@ function createInvalidDocumentBackupPath(targetPath) {
3933
3939
  const ext = targetPath.includes(".") ? targetPath.split(".").pop() : "bak";
3934
3940
  return `${targetPath}.invalid-${createTimestamp()}.${ext}`;
3935
3941
  }
3936
- async function backupInvalidDocument(fs3, targetPath, content) {
3942
+ async function backupInvalidDocument(fs4, targetPath, content) {
3937
3943
  const backupPath = createInvalidDocumentBackupPath(targetPath);
3938
- await fs3.writeFile(backupPath, content, { encoding: "utf8" });
3944
+ await fs4.writeFile(backupPath, content, { encoding: "utf8" });
3939
3945
  }
3940
3946
  function describeMutation(kind, targetPath) {
3941
3947
  const displayPath = targetPath ?? "target";
@@ -4493,12 +4499,12 @@ async function executeMutation(mutation, context, options) {
4493
4499
  }
4494
4500
 
4495
4501
  // packages/poe-code-config/src/store.ts
4496
- async function readMergedDocument(fs3, globalPath, projectPath) {
4497
- const globalDocument = await readStoredDocument(fs3, globalPath);
4502
+ async function readMergedDocument(fs4, globalPath, projectPath) {
4503
+ const globalDocument = await readStoredDocument(fs4, globalPath);
4498
4504
  if (!projectPath || projectPath === globalPath) {
4499
4505
  return globalDocument.data;
4500
4506
  }
4501
- const projectDocument = await readStoredDocument(fs3, projectPath);
4507
+ const projectDocument = await readStoredDocument(fs4, projectPath);
4502
4508
  const resolved = await resolve(
4503
4509
  [
4504
4510
  {
@@ -4512,16 +4518,16 @@ async function readMergedDocument(fs3, globalPath, projectPath) {
4512
4518
  }
4513
4519
  ],
4514
4520
  {
4515
- fs: createResolvedConfigFs(fs3, globalPath, globalDocument.content),
4521
+ fs: createResolvedConfigFs(fs4, globalPath, globalDocument.content),
4516
4522
  autoExtend: true
4517
4523
  }
4518
4524
  );
4519
4525
  return normalizeDocument(resolved.data);
4520
4526
  }
4521
- async function readStoredDocument(fs3, filePath) {
4527
+ async function readStoredDocument(fs4, filePath) {
4522
4528
  try {
4523
- const raw = await fs3.readFile(filePath, "utf8");
4524
- return await parseStoredDocument(fs3, filePath, raw);
4529
+ const raw = await fs4.readFile(filePath, "utf8");
4530
+ return await parseStoredDocument(fs4, filePath, raw);
4525
4531
  } catch (error2) {
4526
4532
  if (isNotFound(error2)) {
4527
4533
  return {
@@ -4532,7 +4538,7 @@ async function readStoredDocument(fs3, filePath) {
4532
4538
  throw error2;
4533
4539
  }
4534
4540
  }
4535
- async function parseStoredDocument(fs3, filePath, raw) {
4541
+ async function parseStoredDocument(fs4, filePath, raw) {
4536
4542
  try {
4537
4543
  return {
4538
4544
  content: raw,
@@ -4540,7 +4546,7 @@ async function parseStoredDocument(fs3, filePath, raw) {
4540
4546
  };
4541
4547
  } catch (error2) {
4542
4548
  if (error2 instanceof SyntaxError) {
4543
- await recoverInvalidDocument(fs3, filePath, raw);
4549
+ await recoverInvalidDocument(fs4, filePath, raw);
4544
4550
  return {
4545
4551
  content: EMPTY_DOCUMENT,
4546
4552
  data: {}
@@ -4574,21 +4580,21 @@ function normalizeScopeValues(value) {
4574
4580
  }
4575
4581
  return normalized;
4576
4582
  }
4577
- function createResolvedConfigFs(fs3, globalPath, globalContent) {
4583
+ function createResolvedConfigFs(fs4, globalPath, globalContent) {
4578
4584
  return {
4579
4585
  readFile(filePath, _encoding) {
4580
4586
  if (filePath === globalPath) {
4581
4587
  return Promise.resolve(globalContent);
4582
4588
  }
4583
- return fs3.readFile(filePath, "utf8");
4589
+ return fs4.readFile(filePath, "utf8");
4584
4590
  }
4585
4591
  };
4586
4592
  }
4587
- async function recoverInvalidDocument(fs3, filePath, content) {
4588
- await fs3.mkdir(path14.dirname(filePath), { recursive: true });
4593
+ async function recoverInvalidDocument(fs4, filePath, content) {
4594
+ await fs4.mkdir(path14.dirname(filePath), { recursive: true });
4589
4595
  const backupPath = createInvalidBackupPath(filePath);
4590
- await fs3.writeFile(backupPath, content, { encoding: "utf8" });
4591
- 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" });
4592
4598
  }
4593
4599
  function createInvalidBackupPath(filePath) {
4594
4600
  const directory = path14.dirname(filePath);
@@ -4712,7 +4718,7 @@ function mergeScope(scope, baseScope, overrideScope) {
4712
4718
  ...Object.fromEntries(scopeEntries)
4713
4719
  };
4714
4720
  }
4715
- function mergeRuntimeScope(baseScope, overrideScope, path32 = []) {
4721
+ function mergeRuntimeScope(baseScope, overrideScope, path37 = []) {
4716
4722
  const merged = {};
4717
4723
  const keys = /* @__PURE__ */ new Set([...Object.keys(baseScope), ...Object.keys(overrideScope)]);
4718
4724
  for (const key of keys) {
@@ -4724,20 +4730,20 @@ function mergeRuntimeScope(baseScope, overrideScope, path32 = []) {
4724
4730
  }
4725
4731
  continue;
4726
4732
  }
4727
- if (isRuntimeConcatenativeArray([...path32, key]) && Array.isArray(baseValue) && Array.isArray(overrideValue)) {
4733
+ if (isRuntimeConcatenativeArray([...path37, key]) && Array.isArray(baseValue) && Array.isArray(overrideValue)) {
4728
4734
  merged[key] = [...baseValue, ...overrideValue];
4729
4735
  continue;
4730
4736
  }
4731
4737
  if (isRecord4(baseValue) && isRecord4(overrideValue)) {
4732
- merged[key] = mergeRuntimeScope(baseValue, overrideValue, [...path32, key]);
4738
+ merged[key] = mergeRuntimeScope(baseValue, overrideValue, [...path37, key]);
4733
4739
  continue;
4734
4740
  }
4735
4741
  merged[key] = overrideValue;
4736
4742
  }
4737
4743
  return merged;
4738
4744
  }
4739
- function isRuntimeConcatenativeArray(path32) {
4740
- return path32.join(".") === "mounts" || path32.join(".") === "runner.workspace.exclude";
4745
+ function isRuntimeConcatenativeArray(path37) {
4746
+ return path37.join(".") === "mounts" || path37.join(".") === "runner.workspace.exclude";
4741
4747
  }
4742
4748
  function isRecord4(value) {
4743
4749
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
@@ -4925,6 +4931,7 @@ var poeProvider = {
4925
4931
  label: "Poe",
4926
4932
  summary: "Route AI coding agents through Poe's API.",
4927
4933
  baseUrl: "https://api.poe.com",
4934
+ agentBaseUrl: "https://api.poe.com",
4928
4935
  auth: {
4929
4936
  kind: "api-key",
4930
4937
  envVar: "POE_API_KEY",
@@ -4932,6 +4939,12 @@ var poeProvider = {
4932
4939
  prompt: { title: "Poe API key" },
4933
4940
  preferredLogin: "oauth"
4934
4941
  },
4942
+ env: {
4943
+ ANTHROPIC_CUSTOM_HEADERS: {
4944
+ kind: "providerCredential",
4945
+ prefix: "Authorization: Bearer "
4946
+ }
4947
+ },
4935
4948
  apiShapes: [
4936
4949
  {
4937
4950
  id: "openai-chat-completions",
@@ -4960,6 +4973,9 @@ var anthropicProvider = {
4960
4973
  storageKey: "provider:anthropic",
4961
4974
  prompt: { title: "Anthropic API key" }
4962
4975
  },
4976
+ env: {
4977
+ ANTHROPIC_API_KEY: { kind: "providerCredential" }
4978
+ },
4963
4979
  apiShapes: [
4964
4980
  {
4965
4981
  id: "anthropic-messages",
@@ -4972,30 +4988,38 @@ var anthropicProvider = {
4972
4988
  var cloudflareProvider = {
4973
4989
  id: "cloudflare",
4974
4990
  label: "Cloudflare AI Gateway",
4975
- summary: "Route through the Poe Cloudflare gateway with BYOK keys.",
4976
- baseUrl: "https://poe-ai-gateway.poe-dev.workers.dev",
4991
+ summary: "Route coding agents through Cloudflare AI Gateway.",
4992
+ baseUrlEnvVar: "CF_AIG_BASE_URL",
4993
+ requiresBaseUrl: true,
4994
+ modelInput: { kind: "freeform" },
4977
4995
  auth: {
4978
4996
  kind: "api-key",
4979
- envVar: "CLOUDFLARE_API_KEY",
4997
+ envVar: "CF_AIG_TOKEN",
4980
4998
  storageKey: "provider:cloudflare",
4981
- prompt: { title: "Cloudflare API key" }
4999
+ prompt: { title: "Cloudflare AI Gateway token" }
5000
+ },
5001
+ env: {
5002
+ ANTHROPIC_CUSTOM_HEADERS: {
5003
+ kind: "providerCredential",
5004
+ prefix: "Authorization: Bearer "
5005
+ }
4982
5006
  },
4983
5007
  apiShapes: [
4984
5008
  {
4985
5009
  id: "openai-chat-completions",
4986
- defaultBaseUrl: "https://poe-ai-gateway.poe-dev.workers.dev/openai/v1"
5010
+ baseUrlPath: "compat"
4987
5011
  },
4988
5012
  {
4989
5013
  id: "openai-responses",
4990
- defaultBaseUrl: "https://poe-ai-gateway.poe-dev.workers.dev/openai/v1"
5014
+ baseUrlPath: "openai"
4991
5015
  },
4992
5016
  {
4993
5017
  id: "anthropic-messages",
4994
- defaultBaseUrl: "https://poe-ai-gateway.poe-dev.workers.dev/anthropic"
5018
+ baseUrlPath: "anthropic"
4995
5019
  },
4996
5020
  {
4997
5021
  id: "google-generations",
4998
- defaultBaseUrl: "https://poe-ai-gateway.poe-dev.workers.dev/google-ai-studio"
5022
+ baseUrlPath: "google-ai-studio"
4999
5023
  }
5000
5024
  ]
5001
5025
  };
@@ -5024,7 +5048,7 @@ function isNotFoundError(error2) {
5024
5048
  }
5025
5049
 
5026
5050
  // packages/poe-code-config/src/state/jobs.ts
5027
- function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5051
+ function createJobRegistry(homeDir, fs4 = defaultStateFs) {
5028
5052
  const jobsDir = path17.join(homeDir, ".poe-code", "state", "jobs");
5029
5053
  function jobPath(id) {
5030
5054
  assertSafeJobId(id);
@@ -5032,7 +5056,7 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5032
5056
  }
5033
5057
  async function get(id) {
5034
5058
  try {
5035
- return parseJobEntry(await fs3.readFile(jobPath(id), "utf8"));
5059
+ return parseJobEntry(await fs4.readFile(jobPath(id), "utf8"));
5036
5060
  } catch (error2) {
5037
5061
  if (isNotFoundError(error2)) {
5038
5062
  return null;
@@ -5043,8 +5067,8 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5043
5067
  async function put(entry) {
5044
5068
  assertJobEntry(entry);
5045
5069
  const filePath = jobPath(entry.id);
5046
- await fs3.mkdir(jobsDir, { recursive: true });
5047
- const release = await acquireFileLock(filePath, { fs: fs3 });
5070
+ await fs4.mkdir(jobsDir, { recursive: true });
5071
+ const release = await acquireFileLock(filePath, { fs: fs4 });
5048
5072
  try {
5049
5073
  await writeJobAtomically(filePath, entry);
5050
5074
  } finally {
@@ -5053,8 +5077,8 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5053
5077
  }
5054
5078
  async function update(id, patch) {
5055
5079
  const filePath = jobPath(id);
5056
- await fs3.mkdir(jobsDir, { recursive: true });
5057
- const release = await acquireFileLock(filePath, { fs: fs3 });
5080
+ await fs4.mkdir(jobsDir, { recursive: true });
5081
+ const release = await acquireFileLock(filePath, { fs: fs4 });
5058
5082
  try {
5059
5083
  const current = await get(id);
5060
5084
  if (current === null) {
@@ -5075,7 +5099,7 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5075
5099
  async function list(filter = {}) {
5076
5100
  let entries;
5077
5101
  try {
5078
- entries = await fs3.readdir(jobsDir);
5102
+ entries = await fs4.readdir(jobsDir);
5079
5103
  } catch (error2) {
5080
5104
  if (isNotFoundError(error2)) {
5081
5105
  return [];
@@ -5088,11 +5112,11 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5088
5112
  continue;
5089
5113
  }
5090
5114
  const filePath = path17.join(jobsDir, entry);
5091
- const stat3 = await fs3.stat(filePath);
5115
+ const stat3 = await fs4.stat(filePath);
5092
5116
  if (!stat3.isFile()) {
5093
5117
  continue;
5094
5118
  }
5095
- const job = parseJobEntry(await fs3.readFile(filePath, "utf8"));
5119
+ const job = parseJobEntry(await fs4.readFile(filePath, "utf8"));
5096
5120
  if (matchesFilter(job, filter)) {
5097
5121
  jobs.push(job);
5098
5122
  }
@@ -5102,16 +5126,16 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5102
5126
  async function remove2(id) {
5103
5127
  const filePath = jobPath(id);
5104
5128
  try {
5105
- await fs3.stat(jobsDir);
5129
+ await fs4.stat(jobsDir);
5106
5130
  } catch (error2) {
5107
5131
  if (isNotFoundError(error2)) {
5108
5132
  return;
5109
5133
  }
5110
5134
  throw error2;
5111
5135
  }
5112
- const release = await acquireFileLock(filePath, { fs: fs3 });
5136
+ const release = await acquireFileLock(filePath, { fs: fs4 });
5113
5137
  try {
5114
- await fs3.unlink(filePath);
5138
+ await fs4.unlink(filePath);
5115
5139
  } catch (error2) {
5116
5140
  if (!isNotFoundError(error2)) {
5117
5141
  throw error2;
@@ -5121,14 +5145,14 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5121
5145
  }
5122
5146
  }
5123
5147
  async function writeJobAtomically(filePath, entry) {
5124
- await fs3.mkdir(path17.dirname(filePath), { recursive: true });
5148
+ await fs4.mkdir(path17.dirname(filePath), { recursive: true });
5125
5149
  const tempPath = `${filePath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`;
5126
5150
  try {
5127
- await fs3.writeFile(tempPath, `${JSON.stringify(entry, null, 2)}
5151
+ await fs4.writeFile(tempPath, `${JSON.stringify(entry, null, 2)}
5128
5152
  `, {
5129
5153
  encoding: "utf8"
5130
5154
  });
5131
- await fs3.rename(tempPath, filePath);
5155
+ await fs4.rename(tempPath, filePath);
5132
5156
  } catch (error2) {
5133
5157
  await removeTempFile(tempPath);
5134
5158
  throw error2;
@@ -5136,7 +5160,7 @@ function createJobRegistry(homeDir, fs3 = defaultStateFs) {
5136
5160
  }
5137
5161
  async function removeTempFile(tempPath) {
5138
5162
  try {
5139
- await fs3.unlink(tempPath);
5163
+ await fs4.unlink(tempPath);
5140
5164
  } catch (error2) {
5141
5165
  if (!isNotFoundError(error2)) {
5142
5166
  throw error2;
@@ -5183,11 +5207,11 @@ function isRecord5(value) {
5183
5207
 
5184
5208
  // packages/poe-code-config/src/state/templates.ts
5185
5209
  import path18 from "node:path";
5186
- function createTemplateRegistry(homeDir, fs3 = defaultStateFs) {
5210
+ function createTemplateRegistry(homeDir, fs4 = defaultStateFs) {
5187
5211
  const filePath = path18.join(homeDir, ".poe-code", "state", "templates.json");
5188
5212
  async function readState() {
5189
5213
  try {
5190
- const raw = await fs3.readFile(filePath, "utf8");
5214
+ const raw = await fs4.readFile(filePath, "utf8");
5191
5215
  return normalizeTemplateState(JSON.parse(raw));
5192
5216
  } catch (error2) {
5193
5217
  if (isNotFoundError(error2)) {
@@ -5197,14 +5221,14 @@ function createTemplateRegistry(homeDir, fs3 = defaultStateFs) {
5197
5221
  }
5198
5222
  }
5199
5223
  async function writeState(state) {
5200
- await fs3.writeFile(filePath, `${JSON.stringify(state, null, 2)}
5224
+ await fs4.writeFile(filePath, `${JSON.stringify(state, null, 2)}
5201
5225
  `, {
5202
5226
  encoding: "utf8"
5203
5227
  });
5204
5228
  }
5205
5229
  async function updateState(mutator) {
5206
- await fs3.mkdir(path18.dirname(filePath), { recursive: true });
5207
- const release = await acquireFileLock(filePath, { fs: fs3 });
5230
+ await fs4.mkdir(path18.dirname(filePath), { recursive: true });
5231
+ const release = await acquireFileLock(filePath, { fs: fs4 });
5208
5232
  try {
5209
5233
  const state = await readState();
5210
5234
  mutator(state);
@@ -5274,10 +5298,10 @@ function isRecord6(value) {
5274
5298
  }
5275
5299
 
5276
5300
  // packages/poe-code-config/src/state/index.ts
5277
- function createStateManager(homeDir, fs3) {
5301
+ function createStateManager(homeDir, fs4) {
5278
5302
  return {
5279
- templates: createTemplateRegistry(homeDir, fs3),
5280
- jobs: createJobRegistry(homeDir, fs3)
5303
+ templates: createTemplateRegistry(homeDir, fs4),
5304
+ jobs: createJobRegistry(homeDir, fs4)
5281
5305
  };
5282
5306
  }
5283
5307
 
@@ -5592,7 +5616,7 @@ import { PassThrough as PassThrough2, Writable as Writable2 } from "node:stream"
5592
5616
  import path21 from "node:path";
5593
5617
  var JOB_DIR2 = "/tmp/poe-jobs";
5594
5618
  function createE2bJobHandle(input) {
5595
- const fs3 = createE2bLogStreamFs(input.sandbox);
5619
+ const fs4 = createE2bLogStreamFs(input.sandbox);
5596
5620
  return {
5597
5621
  id: input.jobId,
5598
5622
  envId: input.envId,
@@ -5608,10 +5632,10 @@ function createE2bJobHandle(input) {
5608
5632
  return isRunning ? "running" : "lost";
5609
5633
  },
5610
5634
  stream(opts = {}) {
5611
- return streamLogFile({ fs: fs3 }, input.jobId, opts);
5635
+ return streamLogFile({ fs: fs4 }, input.jobId, opts);
5612
5636
  },
5613
5637
  async wait() {
5614
- const result = await waitForExit({ fs: fs3 }, input.jobId);
5638
+ const result = await waitForExit({ fs: fs4 }, input.jobId);
5615
5639
  const preserveMs = input.preserveAfterExitHours * 60 * 60 * 1e3;
5616
5640
  if (preserveMs > 0) {
5617
5641
  await input.sandbox.setTimeout(preserveMs);
@@ -6108,10 +6132,10 @@ var e2bAuthScope = defineScope("e2b", {
6108
6132
  });
6109
6133
  async function resolveE2bApiKey(input) {
6110
6134
  const homeDir = input.homeDir ?? os4.homedir();
6111
- const fs3 = input.fs ?? nodeFs4;
6135
+ const fs4 = input.fs ?? nodeFs4;
6112
6136
  const env = input.env ?? process.env;
6113
6137
  const document = await readMergedDocument(
6114
- fs3,
6138
+ fs4,
6115
6139
  resolveConfigPath(homeDir),
6116
6140
  resolveProjectConfigPath(input.cwd)
6117
6141
  );
@@ -6618,6 +6642,23 @@ var gooseAcpSpawnConfig = {
6618
6642
  skipAuth: true
6619
6643
  };
6620
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
+
6621
6662
  // packages/agent-spawn/src/configs/index.ts
6622
6663
  var allSpawnConfigs = [
6623
6664
  claudeCodeSpawnConfig,
@@ -6634,6 +6675,7 @@ var acpLookup = /* @__PURE__ */ new Map();
6634
6675
  acpLookup.set(openCodeAcpSpawnConfig.agentId, openCodeAcpSpawnConfig);
6635
6676
  acpLookup.set(kimiAcpSpawnConfig.agentId, kimiAcpSpawnConfig);
6636
6677
  acpLookup.set(gooseAcpSpawnConfig.agentId, gooseAcpSpawnConfig);
6678
+ acpLookup.set(geminiCliAcpSpawnConfig.agentId, geminiCliAcpSpawnConfig);
6637
6679
  function getSpawnConfig(input) {
6638
6680
  const resolvedId = resolveAgentId(input);
6639
6681
  if (!resolvedId) {
@@ -6653,8 +6695,8 @@ function listMcpSupportedAgents() {
6653
6695
  }
6654
6696
 
6655
6697
  // packages/agent-spawn/src/spawn.ts
6656
- import { mkdirSync as mkdirSync3, openSync, writeSync, closeSync } from "node:fs";
6657
- 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";
6658
6700
 
6659
6701
  // packages/agent-spawn/src/configs/resolve-config.ts
6660
6702
  function resolveConfig(agentId) {
@@ -7055,7 +7097,7 @@ function createEventQueue() {
7055
7097
 
7056
7098
  // packages/agent-spawn/src/skill-bridge.ts
7057
7099
  import crypto from "node:crypto";
7058
- import os6 from "node:os";
7100
+ import os7 from "node:os";
7059
7101
 
7060
7102
  // packages/agent-skill-config/src/configs.ts
7061
7103
  import os5 from "node:os";
@@ -7069,6 +7111,10 @@ var agentSkillConfigs = {
7069
7111
  globalSkillDir: "~/.codex/skills",
7070
7112
  localSkillDir: ".codex/skills"
7071
7113
  },
7114
+ "gemini-cli": {
7115
+ globalSkillDir: "~/.gemini/skills",
7116
+ localSkillDir: ".gemini/skills"
7117
+ },
7072
7118
  opencode: {
7073
7119
  globalSkillDir: "~/.config/opencode/skills",
7074
7120
  localSkillDir: ".opencode/skills"
@@ -7211,7 +7257,7 @@ function resolveSkillReference(ref, cwd, homeDir) {
7211
7257
  import { execFileSync } from "node:child_process";
7212
7258
  import * as fs from "node:fs";
7213
7259
  import path27 from "node:path";
7214
- var markerPrefix = "# poe-code-spawn-skills:";
7260
+ var defaultMarkerPrefix = "poe-code-spawn-skills";
7215
7261
  function defaultGitDirRunner(cwd) {
7216
7262
  try {
7217
7263
  return execFileSync("git", ["rev-parse", "--git-dir"], {
@@ -7231,10 +7277,10 @@ function resolveExcludePath(cwd) {
7231
7277
  }
7232
7278
  return path27.join(path27.isAbsolute(gitDir) ? gitDir : path27.resolve(cwd, gitDir), "info/exclude");
7233
7279
  }
7234
- function markers(runId) {
7280
+ function markers(runId, markerPrefix) {
7235
7281
  return {
7236
- begin: `${markerPrefix}${runId} begin`,
7237
- end: `${markerPrefix}${runId} end`
7282
+ begin: `# ${markerPrefix}:${runId} begin`,
7283
+ end: `# ${markerPrefix}:${runId} end`
7238
7284
  };
7239
7285
  }
7240
7286
  function readExcludeFile(excludePath) {
@@ -7250,8 +7296,8 @@ function readExcludeFile(excludePath) {
7250
7296
  function isNodeError2(error2) {
7251
7297
  return error2 instanceof Error && "code" in error2;
7252
7298
  }
7253
- function removeBlock(content, runId) {
7254
- const { begin, end } = markers(runId);
7299
+ function removeBlock(content, runId, markerPrefix) {
7300
+ const { begin, end } = markers(runId, markerPrefix);
7255
7301
  const lines = content.split("\n");
7256
7302
  const result = [];
7257
7303
  for (let index = 0; index < lines.length; index += 1) {
@@ -7266,23 +7312,27 @@ function removeBlock(content, runId) {
7266
7312
  }
7267
7313
  return result.join("\n");
7268
7314
  }
7269
- function appendBlock(content, runId, entries) {
7270
- const { begin, end } = markers(runId);
7315
+ function appendBlock(content, runId, entries, markerPrefix) {
7316
+ const { begin, end } = markers(runId, markerPrefix);
7271
7317
  const existing = content ?? "";
7272
7318
  const prefix = existing.length === 0 || existing.endsWith("\n") ? existing : `${existing}
7273
7319
  `;
7274
7320
  return `${prefix}${[begin, ...entries, end, ""].join("\n")}`;
7275
7321
  }
7276
- function appendExcludeBlock(cwd, runId, entries) {
7322
+ function appendExcludeBlock(cwd, runId, entries, opts) {
7277
7323
  const excludePath = resolveExcludePath(cwd);
7278
7324
  if (excludePath === void 0) {
7279
7325
  return;
7280
7326
  }
7281
7327
  fs.mkdirSync(path27.dirname(excludePath), { recursive: true });
7282
7328
  const content = readExcludeFile(excludePath);
7283
- fs.writeFileSync(excludePath, appendBlock(content, runId, entries), "utf8");
7329
+ fs.writeFileSync(
7330
+ excludePath,
7331
+ appendBlock(content, runId, entries, opts?.markerPrefix ?? defaultMarkerPrefix),
7332
+ "utf8"
7333
+ );
7284
7334
  }
7285
- function removeExcludeBlock(cwd, runId) {
7335
+ function removeExcludeBlock(cwd, runId, opts) {
7286
7336
  const excludePath = resolveExcludePath(cwd);
7287
7337
  if (excludePath === void 0) {
7288
7338
  return;
@@ -7291,7 +7341,11 @@ function removeExcludeBlock(cwd, runId) {
7291
7341
  if (content === void 0) {
7292
7342
  return;
7293
7343
  }
7294
- fs.writeFileSync(excludePath, removeBlock(content, runId), "utf8");
7344
+ fs.writeFileSync(
7345
+ excludePath,
7346
+ removeBlock(content, runId, opts?.markerPrefix ?? defaultMarkerPrefix),
7347
+ "utf8"
7348
+ );
7295
7349
  }
7296
7350
 
7297
7351
  // packages/agent-skill-config/src/bridge-active-skills.ts
@@ -7497,22 +7551,679 @@ function cleanupBridgedSkills(manifest) {
7497
7551
  removeExcludeBlock(manifest.cwd, manifest.runId);
7498
7552
  }
7499
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
+
7500
8186
  // packages/agent-spawn/src/skill-bridge.ts
7501
- function bridgeSkillsForRun(agentId, cwd, skills) {
7502
- if (!skills || skills.length === 0) {
8187
+ function bridgeResourcesForRun(agentId, cwd, skills, hooks) {
8188
+ if ((!skills || skills.length === 0) && !hooks) {
7503
8189
  return void 0;
7504
8190
  }
7505
- const manifest = bridgeActiveSkills(agentId, cwd, skills, os6.homedir(), crypto.randomUUID());
7506
- for (const warning2 of manifest.warnings) {
7507
- 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;
7508
8214
  }
7509
- return manifest;
8215
+ return manifests;
7510
8216
  }
7511
- function cleanupSkillsForRun(manifest) {
8217
+ function cleanupResourcesForRun(manifest) {
7512
8218
  if (!manifest) {
7513
8219
  return;
7514
8220
  }
7515
- cleanupBridgedSkills(manifest);
8221
+ if (manifest.hooks) {
8222
+ cleanupBridgedHooks(manifest.hooks);
8223
+ }
8224
+ if (manifest.skills) {
8225
+ cleanupBridgedSkills(manifest.skills);
8226
+ }
7516
8227
  }
7517
8228
 
7518
8229
  // packages/agent-spawn/src/adapters/utils.ts
@@ -7638,21 +8349,21 @@ async function* adaptClaude(lines) {
7638
8349
  if (blockType !== "tool_result") continue;
7639
8350
  const kind = toolKindsById.get(item.tool_use_id);
7640
8351
  toolKindsById.delete(item.tool_use_id);
7641
- let path32;
8352
+ let path37;
7642
8353
  if (typeof item.content === "string") {
7643
- path32 = item.content;
8354
+ path37 = item.content;
7644
8355
  } else {
7645
8356
  try {
7646
- path32 = JSON.stringify(item.content);
8357
+ path37 = JSON.stringify(item.content);
7647
8358
  } catch {
7648
- path32 = String(item.content);
8359
+ path37 = String(item.content);
7649
8360
  }
7650
8361
  }
7651
8362
  yield {
7652
8363
  event: "tool_complete",
7653
8364
  id: item.tool_use_id,
7654
8365
  kind,
7655
- path: path32
8366
+ path: path37
7656
8367
  };
7657
8368
  }
7658
8369
  }
@@ -7748,10 +8459,10 @@ async function* adaptCodex(lines) {
7748
8459
  const kindFromStart = toolKindById.get(item.id);
7749
8460
  const kind = kindFromStart ?? (itemType === "command_execution" ? "exec" : itemType === "file_edit" ? "edit" : "other");
7750
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;
7751
- const path32 = titleFromEvent ?? toolTitleById.get(item.id) ?? "";
8462
+ const path37 = titleFromEvent ?? toolTitleById.get(item.id) ?? "";
7752
8463
  toolTitleById.delete(item.id);
7753
8464
  toolKindById.delete(item.id);
7754
- yield { event: "tool_complete", id: item.id, kind, path: path32 };
8465
+ yield { event: "tool_complete", id: item.id, kind, path: path37 };
7755
8466
  }
7756
8467
  }
7757
8468
  }
@@ -8279,7 +8990,7 @@ function spawnStreaming(options) {
8279
8990
  };
8280
8991
  }
8281
8992
  };
8282
- const manifest = bridgeSkillsForRun(options.agentId, cwd, options.skills);
8993
+ const manifest = bridgeResourcesForRun(options.agentId, cwd, options.skills, options.hooks);
8283
8994
  void (async () => {
8284
8995
  try {
8285
8996
  for await (const output of adapter(queue.lines())) {
@@ -8330,7 +9041,7 @@ function spawnStreaming(options) {
8330
9041
  ...ctx.logFile && !result.logFile ? { logFile: ctx.logFile } : {}
8331
9042
  };
8332
9043
  } finally {
8333
- cleanupSkillsForRun(manifest);
9044
+ cleanupResourcesForRun(manifest);
8334
9045
  }
8335
9046
  })();
8336
9047
  return {
@@ -8472,7 +9183,7 @@ async function runSpawn(agentId, options, context) {
8472
9183
  return { stdout: "", stderr: "", exitCode: 0 };
8473
9184
  }
8474
9185
  const cwd = options.cwd ?? process.cwd();
8475
- const manifest = bridgeSkillsForRun(agentId, cwd, options.skills);
9186
+ const manifest = bridgeResourcesForRun(agentId, cwd, options.skills, options.hooks);
8476
9187
  let logFd;
8477
9188
  try {
8478
9189
  const logFilePath = resolveSpawnLogPath(options);
@@ -8540,7 +9251,7 @@ async function runSpawn(agentId, options, context) {
8540
9251
  };
8541
9252
  } finally {
8542
9253
  closeSpawnLog(logFd);
8543
- cleanupSkillsForRun(manifest);
9254
+ cleanupResourcesForRun(manifest);
8544
9255
  }
8545
9256
  }
8546
9257
  spawn4.retry = createSpawnRetry((service, options) => {
@@ -8562,12 +9273,12 @@ function resolveSpawnLogPath(options) {
8562
9273
  if (!options.logDir || !options.logFileName) {
8563
9274
  return void 0;
8564
9275
  }
8565
- return path29.join(options.logDir, options.logFileName);
9276
+ return path34.join(options.logDir, options.logFileName);
8566
9277
  }
8567
9278
  function openSpawnLog(filePath) {
8568
9279
  try {
8569
- mkdirSync3(path29.dirname(filePath), { recursive: true });
8570
- return openSync(filePath, "a");
9280
+ mkdirSync5(path34.dirname(filePath), { recursive: true });
9281
+ return openSync2(filePath, "a");
8571
9282
  } catch {
8572
9283
  return void 0;
8573
9284
  }
@@ -8582,7 +9293,7 @@ function appendSpawnLog(fd, chunk) {
8582
9293
  function closeSpawnLog(fd) {
8583
9294
  if (fd === void 0) return;
8584
9295
  try {
8585
- closeSync(fd);
9296
+ closeSync2(fd);
8586
9297
  } catch {
8587
9298
  }
8588
9299
  }
@@ -8591,7 +9302,7 @@ function closeSpawnLog(fd) {
8591
9302
  var DEFAULT_ACTIVITY_TIMEOUT_MS = 10 * 60 * 1e3;
8592
9303
 
8593
9304
  // packages/agent-spawn/src/acp/replay.ts
8594
- import path30 from "node:path";
9305
+ import path35 from "node:path";
8595
9306
  import { homedir as homedir2 } from "node:os";
8596
9307
  import { open as open2, readdir as readdir2 } from "node:fs/promises";
8597
9308
  import { createInterface } from "node:readline";
@@ -8610,7 +9321,7 @@ import { homedir } from "node:os";
8610
9321
  import { join } from "node:path";
8611
9322
 
8612
9323
  // packages/agent-spawn/src/acp/middlewares/spawn-log.ts
8613
- import path31 from "node:path";
9324
+ import path36 from "node:path";
8614
9325
  import { homedir as homedir3 } from "node:os";
8615
9326
  import { mkdir, open as open3 } from "node:fs/promises";
8616
9327
 
@@ -8624,9 +9335,30 @@ stderr:
8624
9335
  ${stderr}`;
8625
9336
  }
8626
9337
  function createSpawnHealthCheck(agentId, options) {
8627
- const prompt = `Output exactly: ${options.expectedOutput}`;
8628
- const { binaryName, args, env: modeEnv } = buildSpawnArgs(agentId, {
8629
- 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}`,
8630
9362
  model: options.model,
8631
9363
  mode: "yolo"
8632
9364
  });
@@ -8641,6 +9373,13 @@ function createSpawnHealthCheck(agentId, options) {
8641
9373
  return;
8642
9374
  }
8643
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
+ }
8644
9383
  if (result.exitCode !== 0) {
8645
9384
  throw new Error(
8646
9385
  `spawn ${agentId} failed with exit code ${result.exitCode}.
@@ -8778,6 +9517,7 @@ function createProvider(opts) {
8778
9517
  configurePrompts: opts.configurePrompts,
8779
9518
  postConfigureMessages: opts.postConfigureMessages,
8780
9519
  extendConfigurePayload: opts.extendConfigurePayload,
9520
+ runtimeEnv: opts.runtimeEnv,
8781
9521
  isolatedEnv: opts.isolatedEnv,
8782
9522
  async configure(context, runOptions) {
8783
9523
  await runMutations(opts.manifest.configure, {