poe-code 3.0.183 → 3.0.184

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -777,11 +777,11 @@ var init_parse = __esm({
777
777
  function mergeLayers(layers) {
778
778
  return mergeObjectLayers(layers, []);
779
779
  }
780
- function mergeObjectLayers(layers, path82) {
780
+ function mergeObjectLayers(layers, path84) {
781
781
  const data = {};
782
782
  const sources = {};
783
783
  for (const key of collectKeys(layers)) {
784
- const resolved = resolveKey(layers, key, path82);
784
+ const resolved = resolveKey(layers, key, path84);
785
785
  if (resolved === void 0) {
786
786
  continue;
787
787
  }
@@ -799,7 +799,7 @@ function collectKeys(layers) {
799
799
  }
800
800
  return [...keys];
801
801
  }
802
- function resolveKey(layers, key, path82) {
802
+ function resolveKey(layers, key, path84) {
803
803
  let winningSource;
804
804
  let winningValue;
805
805
  const objectLayers = [];
@@ -829,9 +829,9 @@ function resolveKey(layers, key, path82) {
829
829
  if (winningSource === void 0) {
830
830
  return void 0;
831
831
  }
832
- const fullPath = buildPath(path82, key);
832
+ const fullPath = buildPath(path84, key);
833
833
  if (isPlainObject(winningValue)) {
834
- const merged = mergeObjectLayers(objectLayers, [...path82, key]);
834
+ const merged = mergeObjectLayers(objectLayers, [...path84, key]);
835
835
  return {
836
836
  value: merged.data,
837
837
  sources: {
@@ -856,8 +856,8 @@ function isWinningCandidate(key, value) {
856
856
  }
857
857
  return true;
858
858
  }
859
- function buildPath(path82, key) {
860
- return [...path82, key].join(".");
859
+ function buildPath(path84, key) {
860
+ return [...path84, key].join(".");
861
861
  }
862
862
  function isPlainObject(value) {
863
863
  if (value === null || Array.isArray(value) || typeof value !== "object") {
@@ -1535,16 +1535,16 @@ function getConfigFormat(pathOrFormat) {
1535
1535
  }
1536
1536
  return formatRegistry[formatName];
1537
1537
  }
1538
- function detectFormat2(path82) {
1539
- const ext = getExtension(path82);
1538
+ function detectFormat2(path84) {
1539
+ const ext = getExtension(path84);
1540
1540
  return extensionMap[ext];
1541
1541
  }
1542
- function getExtension(path82) {
1543
- const lastDot = path82.lastIndexOf(".");
1542
+ function getExtension(path84) {
1543
+ const lastDot = path84.lastIndexOf(".");
1544
1544
  if (lastDot === -1) {
1545
1545
  return "";
1546
1546
  }
1547
- return path82.slice(lastDot).toLowerCase();
1547
+ return path84.slice(lastDot).toLowerCase();
1548
1548
  }
1549
1549
  var formatRegistry, extensionMap;
1550
1550
  var init_formats = __esm({
@@ -1880,8 +1880,8 @@ async function applyChmod(mutation, context, options) {
1880
1880
  };
1881
1881
  }
1882
1882
  try {
1883
- const stat23 = await context.fs.stat(targetPath);
1884
- const currentMode = typeof stat23.mode === "number" ? stat23.mode & 511 : null;
1883
+ const stat24 = await context.fs.stat(targetPath);
1884
+ const currentMode = typeof stat24.mode === "number" ? stat24.mode & 511 : null;
1885
1885
  if (currentMode === mutation.mode) {
1886
1886
  return {
1887
1887
  outcome: { changed: false, effect: "none", detail: "noop" },
@@ -2713,50 +2713,50 @@ import { createTwoFilesPatch } from "diff";
2713
2713
  import chalk from "chalk";
2714
2714
  function createDryRunFileSystem(base, recorder) {
2715
2715
  const proxy = {
2716
- async readFile(path82, encoding) {
2716
+ async readFile(path84, encoding) {
2717
2717
  if (encoding) {
2718
- return base.readFile(path82, encoding);
2718
+ return base.readFile(path84, encoding);
2719
2719
  }
2720
- return base.readFile(path82);
2720
+ return base.readFile(path84);
2721
2721
  },
2722
- async writeFile(path82, data, options) {
2723
- const previousContent = await tryReadText(base, path82);
2722
+ async writeFile(path84, data, options) {
2723
+ const previousContent = await tryReadText(base, path84);
2724
2724
  const nextContent = formatData(data, options?.encoding);
2725
2725
  recorder.record({
2726
2726
  type: "writeFile",
2727
- path: path82,
2727
+ path: path84,
2728
2728
  nextContent,
2729
2729
  previousContent
2730
2730
  });
2731
2731
  },
2732
- async symlink(target, path82) {
2733
- recorder.record({ type: "symlink", target, path: path82 });
2732
+ async symlink(target, path84) {
2733
+ recorder.record({ type: "symlink", target, path: path84 });
2734
2734
  },
2735
- async readlink(path82) {
2736
- return base.readlink(path82);
2735
+ async readlink(path84) {
2736
+ return base.readlink(path84);
2737
2737
  },
2738
- async mkdir(path82, options) {
2739
- recorder.record({ type: "mkdir", path: path82, options });
2738
+ async mkdir(path84, options) {
2739
+ recorder.record({ type: "mkdir", path: path84, options });
2740
2740
  },
2741
- async stat(path82) {
2742
- return base.stat(path82);
2741
+ async stat(path84) {
2742
+ return base.stat(path84);
2743
2743
  },
2744
- async lstat(path82) {
2745
- return base.lstat(path82);
2744
+ async lstat(path84) {
2745
+ return base.lstat(path84);
2746
2746
  },
2747
2747
  async rename(from, to) {
2748
2748
  recorder.record({ type: "rename", from, to });
2749
2749
  },
2750
- async unlink(path82) {
2751
- recorder.record({ type: "unlink", path: path82 });
2750
+ async unlink(path84) {
2751
+ recorder.record({ type: "unlink", path: path84 });
2752
2752
  },
2753
- async readdir(path82) {
2754
- return base.readdir(path82);
2753
+ async readdir(path84) {
2754
+ return base.readdir(path84);
2755
2755
  }
2756
2756
  };
2757
2757
  if (typeof base.rm === "function") {
2758
- proxy.rm = async (path82, options) => {
2759
- recorder.record({ type: "rm", path: path82, options });
2758
+ proxy.rm = async (path84, options) => {
2759
+ recorder.record({ type: "rm", path: path84, options });
2760
2760
  };
2761
2761
  }
2762
2762
  if (typeof base.copyFile === "function") {
@@ -2858,8 +2858,8 @@ function describeWriteChange(previous, next) {
2858
2858
  }
2859
2859
  return "update";
2860
2860
  }
2861
- function renderWriteCommand(path82, change) {
2862
- const command = `cat > ${path82}`;
2861
+ function renderWriteCommand(path84, change) {
2862
+ const command = `cat > ${path84}`;
2863
2863
  if (change === "create") {
2864
2864
  return renderOperationCommand(command, chalk.green, "# create");
2865
2865
  }
@@ -3021,9 +3021,9 @@ function redactTomlLine(line) {
3021
3021
  }
3022
3022
  return line;
3023
3023
  }
3024
- async function tryReadText(base, path82) {
3024
+ async function tryReadText(base, path84) {
3025
3025
  try {
3026
- return await base.readFile(path82, "utf8");
3026
+ return await base.readFile(path84, "utf8");
3027
3027
  } catch (error2) {
3028
3028
  if (isNotFound(error2)) {
3029
3029
  return null;
@@ -9823,9 +9823,10 @@ function statsToLines(stats, width) {
9823
9823
  }
9824
9824
  const mutedStyle = getToneStyle("muted");
9825
9825
  const totalTokens = stats.tokensIn + stats.tokensOut;
9826
+ const iterationsLabel = stats.iterationsLabel ?? "Iteration";
9826
9827
  const lines = [
9827
9828
  createKeyValueLine("Status", formatStatus(stats.status), width, getStatusStyle(stats.status)),
9828
- createKeyValueLine("Iteration", formatNumber(stats.iterations), width),
9829
+ createKeyValueLine(iterationsLabel, formatNumber(stats.iterations), width),
9829
9830
  createKeyValueLine("Elapsed", formatElapsed(stats.elapsedMs), width),
9830
9831
  createBlankLine(),
9831
9832
  createKeyValueLine("Tokens In", formatNumber(stats.tokensIn), width),
@@ -11936,8 +11937,8 @@ function resourceNotFound(resource) {
11936
11937
  `Resource not found: ${resource}`
11937
11938
  );
11938
11939
  }
11939
- function assertAbsolutePath(path82) {
11940
- if (!isAbsolute(path82)) {
11940
+ function assertAbsolutePath(path84) {
11941
+ if (!isAbsolute(path84)) {
11941
11942
  throw invalidParams('"path" must be an absolute path');
11942
11943
  }
11943
11944
  }
@@ -12812,21 +12813,21 @@ async function* adaptClaude(lines) {
12812
12813
  if (blockType !== "tool_result") continue;
12813
12814
  const kind = toolKindsById.get(item.tool_use_id);
12814
12815
  toolKindsById.delete(item.tool_use_id);
12815
- let path82;
12816
+ let path84;
12816
12817
  if (typeof item.content === "string") {
12817
- path82 = item.content;
12818
+ path84 = item.content;
12818
12819
  } else {
12819
12820
  try {
12820
- path82 = JSON.stringify(item.content);
12821
+ path84 = JSON.stringify(item.content);
12821
12822
  } catch {
12822
- path82 = String(item.content);
12823
+ path84 = String(item.content);
12823
12824
  }
12824
12825
  }
12825
12826
  yield {
12826
12827
  event: "tool_complete",
12827
12828
  id: item.tool_use_id,
12828
12829
  kind,
12829
- path: path82
12830
+ path: path84
12830
12831
  };
12831
12832
  }
12832
12833
  }
@@ -12949,10 +12950,10 @@ async function* adaptCodex(lines) {
12949
12950
  const kindFromStart = toolKindById.get(item.id);
12950
12951
  const kind = kindFromStart ?? (itemType === "command_execution" ? "exec" : itemType === "file_edit" ? "edit" : "other");
12951
12952
  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;
12952
- const path82 = titleFromEvent ?? toolTitleById.get(item.id) ?? "";
12953
+ const path84 = titleFromEvent ?? toolTitleById.get(item.id) ?? "";
12953
12954
  toolTitleById.delete(item.id);
12954
12955
  toolKindById.delete(item.id);
12955
- yield { event: "tool_complete", id: item.id, kind, path: path82 };
12956
+ yield { event: "tool_complete", id: item.id, kind, path: path84 };
12956
12957
  }
12957
12958
  }
12958
12959
  }
@@ -13636,7 +13637,7 @@ function updateSessionFromEvent(ctx, event, toolCallsById) {
13636
13637
  }
13637
13638
  const id = readString(event.id);
13638
13639
  const kind = readString(event.kind);
13639
- const path82 = readString(event.path);
13640
+ const path84 = readString(event.path);
13640
13641
  let toolCall = id ? toolCallsById.get(id) : void 0;
13641
13642
  if (!toolCall) {
13642
13643
  toolCall = {};
@@ -13651,8 +13652,8 @@ function updateSessionFromEvent(ctx, event, toolCallsById) {
13651
13652
  if (kind) {
13652
13653
  toolCall.kind = kind;
13653
13654
  }
13654
- if (path82) {
13655
- toolCall.path = path82;
13655
+ if (path84) {
13656
+ toolCall.path = path84;
13656
13657
  }
13657
13658
  }
13658
13659
  var sessionCapture;
@@ -22016,8 +22017,35 @@ var init_stage = __esm({
22016
22017
 
22017
22018
  // packages/agent-kit/src/lock.ts
22018
22019
  import * as fsPromises4 from "node:fs/promises";
22019
- function sleep(ms) {
22020
- return new Promise((resolve2) => setTimeout(resolve2, ms));
22020
+ function createAbortError4() {
22021
+ const error2 = new Error("The operation was aborted.");
22022
+ error2.name = "AbortError";
22023
+ return error2;
22024
+ }
22025
+ function throwIfAborted(signal) {
22026
+ if (signal?.aborted) {
22027
+ throw createAbortError4();
22028
+ }
22029
+ }
22030
+ function sleep(ms, signal) {
22031
+ if (!signal) {
22032
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
22033
+ }
22034
+ if (signal.aborted) {
22035
+ return Promise.reject(createAbortError4());
22036
+ }
22037
+ return new Promise((resolve2, reject) => {
22038
+ const timeoutId = setTimeout(() => {
22039
+ signal.removeEventListener("abort", onAbort);
22040
+ resolve2();
22041
+ }, ms);
22042
+ const onAbort = () => {
22043
+ clearTimeout(timeoutId);
22044
+ signal.removeEventListener("abort", onAbort);
22045
+ reject(createAbortError4());
22046
+ };
22047
+ signal.addEventListener("abort", onAbort, { once: true });
22048
+ });
22021
22049
  }
22022
22050
  function backoff(attempt, minTimeout, maxTimeout) {
22023
22051
  const delay2 = Math.min(maxTimeout, minTimeout * Math.pow(2, attempt));
@@ -22028,16 +22056,16 @@ function hasErrorCode(error2, code) {
22028
22056
  }
22029
22057
  function createDefaultFs() {
22030
22058
  return {
22031
- mkdir: async (path82, options) => {
22032
- await fsPromises4.mkdir(path82, options);
22059
+ mkdir: async (path84, options) => {
22060
+ await fsPromises4.mkdir(path84, options);
22033
22061
  },
22034
22062
  rmdir: fsPromises4.rmdir,
22035
- stat: async (path82) => {
22036
- const stat23 = await fsPromises4.stat(path82);
22063
+ stat: async (path84) => {
22064
+ const stat24 = await fsPromises4.stat(path84);
22037
22065
  return {
22038
- isFile: () => stat23.isFile(),
22039
- isDirectory: () => stat23.isDirectory(),
22040
- mtimeMs: stat23.mtimeMs
22066
+ isFile: () => stat24.isFile(),
22067
+ isDirectory: () => stat24.isDirectory(),
22068
+ mtimeMs: stat24.mtimeMs
22041
22069
  };
22042
22070
  }
22043
22071
  };
@@ -22059,6 +22087,7 @@ async function lockWorkflow(docPath, options = {}) {
22059
22087
  const staleMs = options.staleMs ?? 3e4;
22060
22088
  const lockPath = `${docPath}.lock`;
22061
22089
  for (let attempt = 0; attempt <= retries; attempt += 1) {
22090
+ throwIfAborted(options.signal);
22062
22091
  try {
22063
22092
  await fs17.mkdir(lockPath);
22064
22093
  let released = false;
@@ -22073,21 +22102,21 @@ async function lockWorkflow(docPath, options = {}) {
22073
22102
  if (!hasErrorCode(error2, "EEXIST")) {
22074
22103
  throw error2;
22075
22104
  }
22076
- let stat23;
22105
+ let stat24;
22077
22106
  try {
22078
- stat23 = await fs17.stat(lockPath);
22107
+ stat24 = await fs17.stat(lockPath);
22079
22108
  } catch (statError) {
22080
22109
  if (hasErrorCode(statError, "ENOENT")) {
22081
22110
  continue;
22082
22111
  }
22083
22112
  throw statError;
22084
22113
  }
22085
- if (Date.now() - stat23.mtimeMs > staleMs) {
22114
+ if (Date.now() - stat24.mtimeMs > staleMs) {
22086
22115
  await removeLockDirectory(fs17, lockPath);
22087
22116
  continue;
22088
22117
  }
22089
22118
  if (attempt < retries) {
22090
- await sleep(backoff(attempt, minTimeout, maxTimeout));
22119
+ await sleep(backoff(attempt, minTimeout, maxTimeout), options.signal);
22091
22120
  }
22092
22121
  }
22093
22122
  }
@@ -22115,7 +22144,7 @@ function toAbortError2(reason) {
22115
22144
  }
22116
22145
  return new Error(reason === void 0 ? "This operation was aborted." : String(reason));
22117
22146
  }
22118
- function throwIfAborted(signal) {
22147
+ function throwIfAborted2(signal) {
22119
22148
  if (!signal?.aborted) {
22120
22149
  return;
22121
22150
  }
@@ -22248,7 +22277,7 @@ async function runDocumentWorkflow(options) {
22248
22277
  let pendingError;
22249
22278
  let currentWorkflow = initialWorkflow;
22250
22279
  try {
22251
- throwIfAborted(options.signal);
22280
+ throwIfAborted2(options.signal);
22252
22281
  if (initialWorkflow.setup) {
22253
22282
  await runWorkflowHook(initialWorkflow.setup, {
22254
22283
  cwd: options.cwd,
@@ -22264,12 +22293,12 @@ async function runDocumentWorkflow(options) {
22264
22293
  }
22265
22294
  let shouldStop = false;
22266
22295
  for (let iteration = 0; iteration < initialWorkflow.maxIterations; iteration += 1) {
22267
- throwIfAborted(options.signal);
22296
+ throwIfAborted2(options.signal);
22268
22297
  currentWorkflow = iteration === 0 ? initialWorkflow : await readWorkflow();
22269
22298
  await options.onIterationStart?.(iteration);
22270
22299
  let iterationResult = "completed";
22271
22300
  for (const stage of currentWorkflow.stages) {
22272
- throwIfAborted(options.signal);
22301
+ throwIfAborted2(options.signal);
22273
22302
  try {
22274
22303
  const stageResult = await runWorkflowStage(stage, {
22275
22304
  cwd: options.cwd,
@@ -23536,11 +23565,11 @@ function createDefaultFs2() {
23536
23565
  writeFile: fsPromises5.writeFile,
23537
23566
  readdir: fsPromises5.readdir,
23538
23567
  stat: async (filePath) => {
23539
- const stat23 = await fsPromises5.stat(filePath);
23568
+ const stat24 = await fsPromises5.stat(filePath);
23540
23569
  return {
23541
- isFile: () => stat23.isFile(),
23542
- isDirectory: () => stat23.isDirectory(),
23543
- mtimeMs: stat23.mtimeMs
23570
+ isFile: () => stat24.isFile(),
23571
+ isDirectory: () => stat24.isDirectory(),
23572
+ mtimeMs: stat24.mtimeMs
23544
23573
  };
23545
23574
  },
23546
23575
  mkdir: async (filePath, options) => {
@@ -25971,11 +26000,11 @@ function createDefaultFs3() {
25971
26000
  writeFile: fsPromises6.writeFile,
25972
26001
  readdir: fsPromises6.readdir,
25973
26002
  stat: async (filePath) => {
25974
- const stat23 = await fsPromises6.stat(filePath);
26003
+ const stat24 = await fsPromises6.stat(filePath);
25975
26004
  return {
25976
- isFile: () => stat23.isFile(),
25977
- isDirectory: () => stat23.isDirectory(),
25978
- mtimeMs: stat23.mtimeMs
26005
+ isFile: () => stat24.isFile(),
26006
+ isDirectory: () => stat24.isDirectory(),
26007
+ mtimeMs: stat24.mtimeMs
25979
26008
  };
25980
26009
  },
25981
26010
  mkdir: async (filePath, mkdirOptions) => {
@@ -26099,8 +26128,8 @@ var init_run = __esm({
26099
26128
  },
26100
26129
  unlink: (filePath) => fsPromises6.unlink(filePath),
26101
26130
  stat: async (filePath) => {
26102
- const stat23 = await fsPromises6.stat(filePath);
26103
- return { mode: stat23.mode };
26131
+ const stat24 = await fsPromises6.stat(filePath);
26132
+ return { mode: stat24.mode };
26104
26133
  },
26105
26134
  readdir: (filePath) => fsPromises6.readdir(filePath)
26106
26135
  };
@@ -26964,14 +26993,14 @@ function parseDockerLocator(input) {
26964
26993
  throw new Error(`Invalid docker workspace locator "${input}".`);
26965
26994
  }
26966
26995
  const container = input.slice(0, slashIndex);
26967
- const path82 = input.slice(slashIndex);
26968
- if (container.length === 0 || path82.length === 0) {
26996
+ const path84 = input.slice(slashIndex);
26997
+ if (container.length === 0 || path84.length === 0) {
26969
26998
  throw new Error(`Invalid docker workspace locator "${input}".`);
26970
26999
  }
26971
27000
  return {
26972
27001
  scheme: "docker",
26973
27002
  container,
26974
- path: path82
27003
+ path: path84
26975
27004
  };
26976
27005
  }
26977
27006
  function splitOnce(input, separator) {
@@ -28795,25 +28824,25 @@ function createSdkContainer(options) {
28795
28824
  });
28796
28825
  loggerFactory.setErrorLogger(errorLogger);
28797
28826
  const asyncFs = {
28798
- readFile: ((path82, encoding) => {
28827
+ readFile: ((path84, encoding) => {
28799
28828
  if (encoding) {
28800
- return fs3.readFile(path82, encoding);
28829
+ return fs3.readFile(path84, encoding);
28801
28830
  }
28802
- return fs3.readFile(path82);
28831
+ return fs3.readFile(path84);
28803
28832
  }),
28804
- symlink: (target, path82) => fs3.symlink(target, path82),
28805
- readlink: (path82) => fs3.readlink(path82, { encoding: "utf8" }),
28806
- writeFile: (path82, data, opts) => fs3.writeFile(path82, data, opts),
28807
- mkdir: (path82, opts) => fs3.mkdir(path82, opts).then(() => {
28833
+ symlink: (target, path84) => fs3.symlink(target, path84),
28834
+ readlink: (path84) => fs3.readlink(path84, { encoding: "utf8" }),
28835
+ writeFile: (path84, data, opts) => fs3.writeFile(path84, data, opts),
28836
+ mkdir: (path84, opts) => fs3.mkdir(path84, opts).then(() => {
28808
28837
  }),
28809
- stat: (path82) => fs3.stat(path82),
28810
- lstat: (path82) => fs3.lstat(path82),
28838
+ stat: (path84) => fs3.stat(path84),
28839
+ lstat: (path84) => fs3.lstat(path84),
28811
28840
  rename: (oldPath, newPath) => fs3.rename(oldPath, newPath),
28812
- rm: (path82, opts) => fs3.rm(path82, opts),
28813
- unlink: (path82) => fs3.unlink(path82),
28814
- readdir: (path82) => fs3.readdir(path82),
28841
+ rm: (path84, opts) => fs3.rm(path84, opts),
28842
+ unlink: (path84) => fs3.unlink(path84),
28843
+ readdir: (path84) => fs3.readdir(path84),
28815
28844
  copyFile: (src, dest) => fs3.copyFile(src, dest),
28816
- chmod: (path82, mode) => fs3.chmod(path82, mode)
28845
+ chmod: (path84, mode) => fs3.chmod(path84, mode)
28817
28846
  };
28818
28847
  const contextFactory = createCommandContextFactory({ fs: asyncFs });
28819
28848
  const authFs = {
@@ -29180,9 +29209,9 @@ function assertNotAborted4(signal) {
29180
29209
  if (!signal?.aborted) {
29181
29210
  return;
29182
29211
  }
29183
- throw createAbortError4();
29212
+ throw createAbortError5();
29184
29213
  }
29185
- function createAbortError4() {
29214
+ function createAbortError5() {
29186
29215
  const error2 = new Error("Pipeline run cancelled");
29187
29216
  error2.name = "AbortError";
29188
29217
  return error2;
@@ -29195,7 +29224,7 @@ var init_utils2 = __esm({
29195
29224
 
29196
29225
  // packages/pipeline/src/config/loader.ts
29197
29226
  import path34 from "node:path";
29198
- import { parse as parse6, stringify } from "yaml";
29227
+ import { parse as parse6 } from "yaml";
29199
29228
  function asStepMode(value) {
29200
29229
  if (value === void 0 || value === null) {
29201
29230
  return "yolo";
@@ -29213,13 +29242,6 @@ function parseYamlDocument(filePath, content) {
29213
29242
  throw new Error(`Invalid pipeline step config YAML in "${filePath}": ${message2}`);
29214
29243
  }
29215
29244
  }
29216
- function parseStepConfigSource(filePath, content) {
29217
- const document = parseYamlDocument(filePath, content);
29218
- return {
29219
- config: parseStepConfigData(filePath, document),
29220
- extendsBase: isRecord11(document) && document.extends === true
29221
- };
29222
- }
29223
29245
  function parseStepConfigData(filePath, document) {
29224
29246
  if (document === null || document === void 0) {
29225
29247
  return { steps: {} };
@@ -29261,102 +29283,72 @@ function parseStepConfigData(filePath, document) {
29261
29283
  }
29262
29284
  return result;
29263
29285
  }
29264
- function encodeShallowStepsConfig(config) {
29265
- const data = {};
29266
- if (Object.keys(config.steps).length > 0) {
29267
- data.steps = Object.fromEntries(
29268
- Object.entries(config.steps).map(([stepName, definition]) => [
29269
- stepName,
29270
- JSON.stringify(definition)
29271
- ])
29272
- );
29273
- }
29274
- if (config.setup !== void 0) {
29275
- data.setup = JSON.stringify(config.setup);
29276
- }
29277
- if (config.teardown !== void 0) {
29278
- data.teardown = JSON.stringify(config.teardown);
29286
+ function mergeStepDefinition(base, override, context) {
29287
+ const prompt = override.prompt ?? base?.prompt;
29288
+ if (typeof prompt !== "string" || prompt.length === 0) {
29289
+ throw new Error(`Missing prompt for ${context}.`);
29279
29290
  }
29280
- return data;
29291
+ const agent2 = override.agent ?? base?.agent;
29292
+ const model = override.model ?? base?.model;
29293
+ return {
29294
+ mode: override.mode ?? base?.mode ?? "yolo",
29295
+ prompt,
29296
+ ...agent2 ? { agent: agent2 } : {},
29297
+ ...model ? { model } : {}
29298
+ };
29281
29299
  }
29282
- function decodeShallowStepsConfig(filePath, document) {
29283
- const decoded = {};
29284
- if (document.steps !== void 0) {
29285
- if (!isRecord11(document.steps)) {
29286
- throw new Error(`Invalid pipeline step config in "${filePath}": "steps" must be an object.`);
29287
- }
29288
- decoded.steps = Object.fromEntries(
29289
- Object.entries(document.steps).map(([stepName, definition]) => [
29290
- stepName,
29291
- decodeEncodedDefinition(filePath, definition, `step "${stepName}"`)
29292
- ])
29293
- );
29300
+ function applyStepOverrides(config, stepOverrides) {
29301
+ if (!stepOverrides || Object.keys(stepOverrides).length === 0) {
29302
+ return config;
29294
29303
  }
29295
- if (document.setup !== void 0) {
29296
- decoded.setup = decodeEncodedDefinition(filePath, document.setup, "setup");
29304
+ const steps = { ...config.steps };
29305
+ for (const [stepName, override] of Object.entries(stepOverrides)) {
29306
+ steps[stepName] = mergeStepDefinition(steps[stepName], override, `plan step "${stepName}"`);
29297
29307
  }
29298
- if (document.teardown !== void 0) {
29299
- decoded.teardown = decodeEncodedDefinition(filePath, document.teardown, "teardown");
29300
- }
29301
- return decoded;
29308
+ return {
29309
+ ...config,
29310
+ steps
29311
+ };
29302
29312
  }
29303
- function decodeEncodedDefinition(filePath, value, context) {
29304
- if (typeof value !== "string") {
29305
- throw new Error(`Invalid ${context} in "${filePath}": expected an object.`);
29306
- }
29307
- let decoded;
29313
+ async function directoryExists(fs17, targetPath) {
29308
29314
  try {
29309
- decoded = JSON.parse(value);
29315
+ return (await fs17.stat(targetPath)).isDirectory();
29310
29316
  } catch (error2) {
29311
- const message2 = error2 instanceof Error ? error2.message : String(error2);
29312
- throw new Error(`Invalid ${context} in "${filePath}": ${message2}`);
29313
- }
29314
- if (!isRecord11(decoded)) {
29315
- throw new Error(`Invalid ${context} in "${filePath}": expected an object.`);
29317
+ if (isNotFound2(error2)) {
29318
+ return false;
29319
+ }
29320
+ throw error2;
29316
29321
  }
29317
- return decoded;
29318
29322
  }
29319
- async function loadResolvedSteps(options) {
29320
- const globalDir = path34.join(options.homeDir, ".poe-code", "pipeline");
29321
- const globalPath = path34.join(globalDir, "steps.yaml");
29322
- const projectPath = path34.join(options.cwd, ".poe-code", "pipeline", "steps.yaml");
29323
- const [globalContent, projectContent] = await Promise.all([
29324
- readOptionalFile2(options.fs, globalPath),
29325
- readOptionalFile2(options.fs, projectPath)
29326
- ]);
29327
- const globalSource = globalContent == null ? void 0 : parseStepConfigSource(globalPath, globalContent);
29328
- if (projectContent == null) {
29329
- return globalSource?.config ?? { steps: {} };
29323
+ async function resolveStepsDirectory(options) {
29324
+ const projectDir = path34.join(options.cwd, ".poe-code", "pipeline", "steps");
29325
+ if (await directoryExists(options.fs, projectDir)) {
29326
+ return projectDir;
29330
29327
  }
29331
- const projectSource = parseStepConfigSource(projectPath, projectContent);
29332
- if (projectSource.extendsBase) {
29333
- const resolved2 = await resolve(
29334
- [
29335
- { source: "document", filePath: projectPath, content: projectContent },
29336
- { source: "base", path: globalDir }
29337
- ],
29338
- { fs: options.fs }
29339
- );
29340
- return parseStepConfigData(projectPath, resolved2.data);
29328
+ const globalDir = path34.join(options.homeDir, ".poe-code", "pipeline", "steps");
29329
+ if (await directoryExists(options.fs, globalDir)) {
29330
+ return globalDir;
29341
29331
  }
29342
- if (globalSource == null) {
29343
- return projectSource.config;
29344
- }
29345
- const resolved = await resolve(
29346
- [
29347
- {
29348
- source: "document",
29349
- filePath: projectPath,
29350
- content: stringify(encodeShallowStepsConfig(projectSource.config))
29351
- },
29352
- {
29353
- source: "global",
29354
- data: encodeShallowStepsConfig(globalSource.config)
29355
- }
29356
- ],
29357
- { fs: options.fs }
29332
+ return null;
29333
+ }
29334
+ async function loadResolvedSteps(options) {
29335
+ const name = options.name?.trim() || "default";
29336
+ const stepsDir = await resolveStepsDirectory(options);
29337
+ if (!stepsDir) {
29338
+ if (name !== "default") {
29339
+ throw new Error(`Unknown pipeline step config "${name}": no pipeline steps directory found.`);
29340
+ }
29341
+ return applyStepOverrides({ steps: {} }, options.stepOverrides);
29342
+ }
29343
+ const filePath = path34.join(stepsDir, `${name}.yaml`);
29344
+ const content = await readOptionalFile2(options.fs, filePath);
29345
+ if (content == null) {
29346
+ throw new Error(`Unknown pipeline step config "${name}" in "${stepsDir}".`);
29347
+ }
29348
+ return applyStepOverrides(
29349
+ parseStepConfigData(filePath, parseYamlDocument(filePath, content)),
29350
+ options.stepOverrides
29358
29351
  );
29359
- return parseStepConfigData(projectPath, decodeShallowStepsConfig(projectPath, resolved.data));
29360
29352
  }
29361
29353
  var init_loader = __esm({
29362
29354
  "packages/pipeline/src/config/loader.ts"() {
@@ -29420,23 +29412,60 @@ function parseTaskStatus(value, availableSteps, taskId) {
29420
29412
  }
29421
29413
  return statusMap;
29422
29414
  }
29423
- function asStepMode2(value) {
29424
- if (value === "edit" || value === "read") return value;
29425
- return "yolo";
29415
+ function parseStepMode(value, label) {
29416
+ if (value === void 0 || value === null) {
29417
+ return void 0;
29418
+ }
29419
+ if (value === "yolo" || value === "edit" || value === "read") {
29420
+ return value;
29421
+ }
29422
+ throw new Error(`Invalid plan YAML: "${label}.mode" must be "yolo", "edit", or "read".`);
29426
29423
  }
29427
- function parseStepDef(value, label) {
29424
+ function parseOptionalAgentFields(value, label) {
29425
+ const result = {};
29426
+ if (value.agent !== void 0) {
29427
+ if (typeof value.agent !== "string" || value.agent.length === 0) {
29428
+ throw new Error(`Invalid plan YAML: "${label}.agent" must be a non-empty string.`);
29429
+ }
29430
+ result.agent = value.agent;
29431
+ }
29432
+ if (value.model !== void 0) {
29433
+ if (typeof value.model !== "string" || value.model.length === 0) {
29434
+ throw new Error(`Invalid plan YAML: "${label}.model" must be a non-empty string.`);
29435
+ }
29436
+ result.model = value.model;
29437
+ }
29438
+ return result;
29439
+ }
29440
+ function parseStepOverride(value, label) {
29428
29441
  if (!isRecord11(value)) {
29429
29442
  throw new Error(`Invalid plan YAML: "${label}" must be an object.`);
29430
29443
  }
29431
- const prompt = value.prompt;
29432
- if (typeof prompt !== "string" || prompt.length === 0) {
29444
+ const result = {
29445
+ ...parseOptionalAgentFields(value, label)
29446
+ };
29447
+ const mode = parseStepMode(value.mode, label);
29448
+ if (mode !== void 0) {
29449
+ result.mode = mode;
29450
+ }
29451
+ if (value.prompt !== void 0) {
29452
+ if (typeof value.prompt !== "string" || value.prompt.length === 0) {
29453
+ throw new Error(`Invalid plan YAML: "${label}.prompt" must be a non-empty string.`);
29454
+ }
29455
+ result.prompt = value.prompt;
29456
+ }
29457
+ return result;
29458
+ }
29459
+ function parseStepDef(value, label) {
29460
+ const override = parseStepOverride(value, label);
29461
+ if (override.prompt === void 0) {
29433
29462
  throw new Error(`Invalid plan YAML: "${label}" is missing a prompt.`);
29434
29463
  }
29435
29464
  return {
29436
- mode: asStepMode2(value.mode),
29437
- prompt,
29438
- ...typeof value.agent === "string" && value.agent.length > 0 ? { agent: value.agent } : {},
29439
- ...typeof value.model === "string" && value.model.length > 0 ? { model: value.model } : {}
29465
+ mode: override.mode ?? "yolo",
29466
+ prompt: override.prompt,
29467
+ ...override.agent ? { agent: override.agent } : {},
29468
+ ...override.model ? { model: override.model } : {}
29440
29469
  };
29441
29470
  }
29442
29471
  function parseMcpConfig2(value) {
@@ -29480,6 +29509,23 @@ function parsePlan(planContent, options = {}) {
29480
29509
  if (!isRecord11(document)) {
29481
29510
  throw new Error("Invalid plan YAML: expected a top-level object.");
29482
29511
  }
29512
+ let extendsName = "default";
29513
+ if (document.extends !== void 0) {
29514
+ if (typeof document.extends !== "string" || document.extends.trim().length === 0) {
29515
+ throw new Error('Invalid plan YAML: "extends" must be a non-empty string.');
29516
+ }
29517
+ extendsName = document.extends.trim();
29518
+ }
29519
+ let stepOverrides;
29520
+ if (document.steps !== void 0) {
29521
+ if (!isRecord11(document.steps)) {
29522
+ throw new Error('Invalid plan YAML: "steps" must be an object.');
29523
+ }
29524
+ stepOverrides = {};
29525
+ for (const [stepName, value] of Object.entries(document.steps)) {
29526
+ stepOverrides[stepName] = parseStepOverride(value, `steps.${stepName}`);
29527
+ }
29528
+ }
29483
29529
  const tasksValue = document.tasks;
29484
29530
  if (!Array.isArray(tasksValue)) {
29485
29531
  throw new Error('Invalid plan YAML: expected "tasks" to be an array.');
@@ -29519,6 +29565,8 @@ function parsePlan(planContent, options = {}) {
29519
29565
  }
29520
29566
  }
29521
29567
  return {
29568
+ extends: extendsName,
29569
+ ...stepOverrides !== void 0 ? { stepOverrides } : {},
29522
29570
  tasks,
29523
29571
  ...vars !== void 0 ? { vars } : {},
29524
29572
  ...setup !== void 0 ? { setup } : {},
@@ -29543,11 +29591,11 @@ function createDefaultFs4() {
29543
29591
  readFile: fsPromises7.readFile,
29544
29592
  readdir: fsPromises7.readdir,
29545
29593
  stat: async (filePath) => {
29546
- const stat23 = await fsPromises7.stat(filePath);
29594
+ const stat24 = await fsPromises7.stat(filePath);
29547
29595
  return {
29548
- isFile: () => stat23.isFile(),
29549
- isDirectory: () => stat23.isDirectory(),
29550
- mtimeMs: stat23.mtimeMs
29596
+ isFile: () => stat24.isFile(),
29597
+ isDirectory: () => stat24.isDirectory(),
29598
+ mtimeMs: stat24.mtimeMs
29551
29599
  };
29552
29600
  }
29553
29601
  };
@@ -29574,8 +29622,8 @@ function countCompletedTasks(planPath, content) {
29574
29622
  async function ensurePlanExists(fs17, cwd, planPath) {
29575
29623
  const absolutePath = path35.isAbsolute(planPath) ? planPath : path35.resolve(cwd, planPath);
29576
29624
  try {
29577
- const stat23 = await fs17.stat(absolutePath);
29578
- if (!stat23.isFile()) {
29625
+ const stat24 = await fs17.stat(absolutePath);
29626
+ if (!stat24.isFile()) {
29579
29627
  throw new Error(`Plan not found at "${planPath}".`);
29580
29628
  }
29581
29629
  } catch (error2) {
@@ -29601,8 +29649,8 @@ async function scanPlansDir(fs17, plansDir, displayPrefix) {
29601
29649
  continue;
29602
29650
  }
29603
29651
  const absolutePath = path35.join(plansDir, entry);
29604
- const stat23 = await fs17.stat(absolutePath);
29605
- if (!stat23.isFile()) {
29652
+ const stat24 = await fs17.stat(absolutePath);
29653
+ if (!stat24.isFile()) {
29606
29654
  continue;
29607
29655
  }
29608
29656
  const displayPath2 = path35.join(displayPrefix, entry);
@@ -29875,6 +29923,23 @@ var init_writer2 = __esm({
29875
29923
  }
29876
29924
  });
29877
29925
 
29926
+ // packages/pipeline/src/vars/interpolate.ts
29927
+ function interpolatePipelineVars(template, values, context = "template") {
29928
+ return template.replace(PLACEHOLDER_PATTERN, (_match, key) => {
29929
+ if (!Object.prototype.hasOwnProperty.call(values, key)) {
29930
+ throw new Error(`Missing pipeline variable "${key}" in ${context}.`);
29931
+ }
29932
+ return values[key];
29933
+ });
29934
+ }
29935
+ var PLACEHOLDER_PATTERN;
29936
+ var init_interpolate = __esm({
29937
+ "packages/pipeline/src/vars/interpolate.ts"() {
29938
+ "use strict";
29939
+ PLACEHOLDER_PATTERN = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;
29940
+ }
29941
+ });
29942
+
29878
29943
  // packages/pipeline/src/run/runner.ts
29879
29944
  import path36 from "node:path";
29880
29945
  function selectFromTask(task) {
@@ -29897,12 +29962,8 @@ function selectNextExecution(plan, taskId) {
29897
29962
  }
29898
29963
  return { kind: "completed" };
29899
29964
  }
29900
- function interpolate(template, values) {
29901
- let output = template;
29902
- for (const [key, value] of Object.entries(values)) {
29903
- output = output.split(`{{${key}}}`).join(value);
29904
- }
29905
- return output;
29965
+ function describeExecutionContext(selection) {
29966
+ return selection.stepName ? `task "${selection.task.id}" step "${selection.stepName}"` : `task "${selection.task.id}"`;
29906
29967
  }
29907
29968
  async function resolveFileIncludes(template, cwd, readFile34) {
29908
29969
  const matches2 = [...template.matchAll(FILE_INCLUDE_PATTERN)];
@@ -29918,31 +29979,128 @@ async function resolveFileIncludes(template, cwd, readFile34) {
29918
29979
  return result;
29919
29980
  }
29920
29981
  function buildExecutionPrompt(input) {
29982
+ const context = describeExecutionContext(input.selection);
29983
+ const resolvedTaskPrompt = interpolatePipelineVars(
29984
+ input.selection.task.prompt,
29985
+ input.vars ?? {},
29986
+ context
29987
+ );
29921
29988
  if (!input.selection.stepName) {
29922
- return input.vars && Object.keys(input.vars).length > 0 ? interpolate(input.selection.task.prompt, input.vars) : input.selection.task.prompt;
29989
+ return resolvedTaskPrompt;
29923
29990
  }
29924
29991
  const step = input.steps[input.selection.stepName];
29925
29992
  if (!step) {
29926
29993
  throw new Error(`Missing step definition for "${input.selection.stepName}".`);
29927
29994
  }
29928
- return interpolate(step.prompt, {
29929
- ...input.vars ?? {},
29930
- id: input.selection.task.id,
29931
- title: input.selection.task.title,
29932
- prompt: input.selection.task.prompt,
29933
- plan_path: input.planPath
29934
- });
29995
+ return interpolatePipelineVars(
29996
+ step.prompt,
29997
+ {
29998
+ ...input.vars ?? {},
29999
+ id: input.selection.task.id,
30000
+ title: input.selection.task.title,
30001
+ prompt: resolvedTaskPrompt,
30002
+ plan_path: input.planPath
30003
+ },
30004
+ context
30005
+ );
29935
30006
  }
29936
30007
  var FILE_INCLUDE_PATTERN;
29937
30008
  var init_runner2 = __esm({
29938
30009
  "packages/pipeline/src/run/runner.ts"() {
29939
30010
  "use strict";
30011
+ init_interpolate();
29940
30012
  FILE_INCLUDE_PATTERN = /\{\{file\s+['"]([^'"]+)['"]\s*\}\}/g;
29941
30013
  }
29942
30014
  });
29943
30015
 
29944
- // packages/pipeline/src/run/pipeline.ts
30016
+ // packages/pipeline/src/vars/resolve.ts
29945
30017
  import path37 from "node:path";
30018
+ function looksLikeDocPath(value) {
30019
+ const trimmed = value.trim();
30020
+ if (trimmed.length === 0) return false;
30021
+ if (trimmed.includes("\n")) return false;
30022
+ if (trimmed.includes("{{")) return false;
30023
+ if (trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
30024
+ return true;
30025
+ }
30026
+ if (trimmed.includes("/")) return true;
30027
+ if (trimmed.endsWith(".md") || trimmed.endsWith(".markdown") || trimmed.endsWith(".txt")) {
30028
+ return true;
30029
+ }
30030
+ return false;
30031
+ }
30032
+ async function resolveDocVarFromPath(options) {
30033
+ const trimmed = options.value.trim();
30034
+ const absolutePath = path37.isAbsolute(trimmed) ? trimmed : path37.join(options.cwd, trimmed);
30035
+ try {
30036
+ return await options.readFile(absolutePath, "utf8");
30037
+ } catch (error2) {
30038
+ throw new Error(
30039
+ `Failed to read doc var "${options.key}" from "${trimmed}" (resolved to ${absolutePath}).`,
30040
+ { cause: error2 }
30041
+ );
30042
+ }
30043
+ }
30044
+ async function resolvePipelineVars(vars, cwd, readFile34) {
30045
+ const resolved = {};
30046
+ for (const [key, value] of Object.entries(vars)) {
30047
+ if (key.endsWith("_doc") && looksLikeDocPath(value)) {
30048
+ const docContent = await resolveDocVarFromPath({ key, value, cwd, readFile: readFile34 });
30049
+ resolved[key] = await resolveFileIncludes(docContent, cwd, readFile34);
30050
+ continue;
30051
+ }
30052
+ resolved[key] = await resolveFileIncludes(value, cwd, readFile34);
30053
+ }
30054
+ return resolved;
30055
+ }
30056
+ var init_resolve4 = __esm({
30057
+ "packages/pipeline/src/vars/resolve.ts"() {
30058
+ "use strict";
30059
+ init_runner2();
30060
+ }
30061
+ });
30062
+
30063
+ // packages/pipeline/src/vars/validate.ts
30064
+ function validatePhasePrompt(phase, definition, vars) {
30065
+ if (!definition) {
30066
+ return;
30067
+ }
30068
+ interpolatePipelineVars(definition.prompt, vars, phase);
30069
+ }
30070
+ function validateResolvedPromptVars(input) {
30071
+ const vars = input.vars ?? {};
30072
+ validatePhasePrompt("setup", input.setup, vars);
30073
+ for (const task of input.plan.tasks) {
30074
+ if (typeof task.status === "string") {
30075
+ buildExecutionPrompt({
30076
+ selection: { kind: "run", task },
30077
+ steps: input.steps,
30078
+ planPath: input.planPath,
30079
+ vars
30080
+ });
30081
+ continue;
30082
+ }
30083
+ for (const stepName of Object.keys(task.status)) {
30084
+ buildExecutionPrompt({
30085
+ selection: { kind: "run", task, stepName },
30086
+ steps: input.steps,
30087
+ planPath: input.planPath,
30088
+ vars
30089
+ });
30090
+ }
30091
+ }
30092
+ validatePhasePrompt("teardown", input.teardown, vars);
30093
+ }
30094
+ var init_validate = __esm({
30095
+ "packages/pipeline/src/vars/validate.ts"() {
30096
+ "use strict";
30097
+ init_runner2();
30098
+ init_interpolate();
30099
+ }
30100
+ });
30101
+
30102
+ // packages/pipeline/src/run/pipeline.ts
30103
+ import path38 from "node:path";
29946
30104
  import * as fsPromises8 from "node:fs/promises";
29947
30105
  function createDefaultFs5() {
29948
30106
  return {
@@ -29950,11 +30108,11 @@ function createDefaultFs5() {
29950
30108
  writeFile: fsPromises8.writeFile,
29951
30109
  readdir: fsPromises8.readdir,
29952
30110
  stat: async (filePath) => {
29953
- const stat23 = await fsPromises8.stat(filePath);
30111
+ const stat24 = await fsPromises8.stat(filePath);
29954
30112
  return {
29955
- isFile: () => stat23.isFile(),
29956
- isDirectory: () => stat23.isDirectory(),
29957
- mtimeMs: stat23.mtimeMs
30113
+ isFile: () => stat24.isFile(),
30114
+ isDirectory: () => stat24.isDirectory(),
30115
+ mtimeMs: stat24.mtimeMs
29958
30116
  };
29959
30117
  },
29960
30118
  mkdir: async (filePath, options) => {
@@ -29964,15 +30122,54 @@ function createDefaultFs5() {
29964
30122
  rename: fsPromises8.rename
29965
30123
  };
29966
30124
  }
30125
+ function isTaskDone(status) {
30126
+ if (typeof status === "string") {
30127
+ return status === "done";
30128
+ }
30129
+ return Object.values(status).every((stepStatus) => stepStatus === "done");
30130
+ }
30131
+ function completesTaskOnSuccess(task, stepName) {
30132
+ if (!stepName || typeof task.status === "string") {
30133
+ return true;
30134
+ }
30135
+ return Object.entries(task.status).every(
30136
+ ([currentStepName, currentStatus]) => currentStepName === stepName || currentStatus === "done"
30137
+ );
30138
+ }
29967
30139
  function isAbortError(error2) {
29968
30140
  return error2 instanceof Error && error2.name === "AbortError";
29969
30141
  }
29970
- async function resolveVars(vars, cwd, readFile34) {
29971
- const resolved = {};
29972
- for (const [key, value] of Object.entries(vars)) {
29973
- resolved[key] = await resolveFileIncludes(value, cwd, readFile34);
30142
+ function formatLockWaitMessage(planPath) {
30143
+ return `Another pipeline run is holding the lock for ${planPath}. Waiting...`;
30144
+ }
30145
+ function formatLockAcquiredMessage(planPath) {
30146
+ return `Lock acquired for ${planPath}. Continuing.`;
30147
+ }
30148
+ async function acquirePipelineLock(options) {
30149
+ let waitingReported = false;
30150
+ const timerId = global.setTimeout(() => {
30151
+ waitingReported = true;
30152
+ options.onLockStatusChange?.({
30153
+ state: "waiting",
30154
+ message: formatLockWaitMessage(options.planPath)
30155
+ });
30156
+ }, 2e3);
30157
+ try {
30158
+ const release = await lockWorkflow(options.absolutePlanPath, {
30159
+ fs: options.fs,
30160
+ retries: Number.POSITIVE_INFINITY,
30161
+ ...options.signal ? { signal: options.signal } : {}
30162
+ });
30163
+ if (waitingReported) {
30164
+ options.onLockStatusChange?.({
30165
+ state: "acquired",
30166
+ message: formatLockAcquiredMessage(options.planPath)
30167
+ });
30168
+ }
30169
+ return release;
30170
+ } finally {
30171
+ global.clearTimeout(timerId);
29974
30172
  }
29975
- return resolved;
29976
30173
  }
29977
30174
  function resolveMode2(stepName, steps) {
29978
30175
  if (!stepName) {
@@ -29985,9 +30182,9 @@ function resolveMode2(stepName, steps) {
29985
30182
  return step.mode;
29986
30183
  }
29987
30184
  async function archivePlan(fs17, absolutePlanPath) {
29988
- const dir = path37.dirname(absolutePlanPath);
29989
- const archiveDir = path37.join(dir, "archive");
29990
- const archivePath = path37.join(archiveDir, path37.basename(absolutePlanPath));
30185
+ const dir = path38.dirname(absolutePlanPath);
30186
+ const archiveDir = path38.join(dir, "archive");
30187
+ const archivePath = path38.join(archiveDir, path38.basename(absolutePlanPath));
29991
30188
  await fs17.mkdir(archiveDir, { recursive: true });
29992
30189
  await fs17.rename(absolutePlanPath, archivePath);
29993
30190
  }
@@ -30024,11 +30221,7 @@ async function runPipeline(options) {
30024
30221
  metrics
30025
30222
  };
30026
30223
  }
30027
- const absolutePlanPath = resolveAbsolutePlanPath(
30028
- planPath,
30029
- options.cwd,
30030
- options.homeDir
30031
- );
30224
+ const absolutePlanPath = resolveAbsolutePlanPath(planPath, options.cwd, options.homeDir);
30032
30225
  const runLogDir = options.logDir ?? resolveRunLogDir({
30033
30226
  planPath: absolutePlanPath,
30034
30227
  runner: "pipeline",
@@ -30038,10 +30231,7 @@ async function runPipeline(options) {
30038
30231
  const content = await fs17.readFile(absolutePlanPath, "utf8");
30039
30232
  const plan = parsePlan(content);
30040
30233
  const total = plan.tasks.length;
30041
- const done = plan.tasks.filter((t) => {
30042
- if (typeof t.status === "string") return t.status === "done";
30043
- return Object.values(t.status).every((s) => s === "done");
30044
- }).length;
30234
+ const done = plan.tasks.filter((t) => isTaskDone(t.status)).length;
30045
30235
  const failed = plan.tasks.filter((t) => {
30046
30236
  if (typeof t.status === "string") return t.status === "failed";
30047
30237
  return Object.values(t.status).some((s) => s === "failed");
@@ -30061,6 +30251,20 @@ async function runPipeline(options) {
30061
30251
  let lastGoodPlan;
30062
30252
  let lastGoodStepsConfig;
30063
30253
  const pipelineStartTime = Date.now();
30254
+ async function readResolvedPlanFromContent(content) {
30255
+ const draftPlan = parsePlan(content);
30256
+ const stepsConfig = await loadResolvedSteps({
30257
+ cwd: options.cwd,
30258
+ homeDir: options.homeDir,
30259
+ fs: fs17,
30260
+ name: draftPlan.extends,
30261
+ stepOverrides: draftPlan.stepOverrides
30262
+ });
30263
+ return {
30264
+ plan: parsePlan(content, { availableSteps: stepsConfig.steps }),
30265
+ stepsConfig
30266
+ };
30267
+ }
30064
30268
  async function runPhase(phaseDef, phase, totalTasks, vars, mcp) {
30065
30269
  const phaseProgress = {
30066
30270
  taskId: phase,
@@ -30073,7 +30277,7 @@ async function runPipeline(options) {
30073
30277
  const startTime = Date.now();
30074
30278
  let result;
30075
30279
  try {
30076
- const rawPrompt = Object.keys(vars).length > 0 ? interpolate(phaseDef.prompt, vars) : phaseDef.prompt;
30280
+ const rawPrompt = interpolatePipelineVars(phaseDef.prompt, vars, phase);
30077
30281
  const phasePrompt = await resolveFileIncludes(rawPrompt, options.cwd, fs17.readFile.bind(fs17));
30078
30282
  result = await runAgent({
30079
30283
  agent: phaseDef.agent ?? options.agent,
@@ -30108,19 +30312,35 @@ async function runPipeline(options) {
30108
30312
  });
30109
30313
  return { success: success2, cancelled: false };
30110
30314
  }
30111
- const initialStepsConfig = await loadResolvedSteps({
30112
- cwd: options.cwd,
30113
- homeDir: options.homeDir,
30114
- fs: fs17
30115
- });
30116
30315
  const initialContent = await fs17.readFile(absolutePlanPath, "utf8");
30117
- const initialPlan = parsePlan(initialContent, { availableSteps: initialStepsConfig.steps });
30316
+ const {
30317
+ plan: initialPlan,
30318
+ stepsConfig: initialStepsConfig
30319
+ } = await readResolvedPlanFromContent(initialContent);
30118
30320
  const resolvedSetup = initialPlan.setup === null ? void 0 : initialPlan.setup ?? initialStepsConfig.setup;
30119
- const resolvedTeardown = initialPlan.teardown === null ? void 0 : initialPlan.teardown ?? initialStepsConfig.teardown;
30321
+ const initialResolvedTeardown = initialPlan.teardown === null ? void 0 : initialPlan.teardown ?? initialStepsConfig.teardown;
30120
30322
  const initialTotalTasks = initialPlan.tasks.length;
30121
- const initialVars = await resolveVars(initialPlan.vars ?? {}, options.cwd, fs17.readFile.bind(fs17));
30323
+ const initialVars = await resolvePipelineVars(
30324
+ initialPlan.vars ?? {},
30325
+ options.cwd,
30326
+ fs17.readFile.bind(fs17)
30327
+ );
30328
+ validateResolvedPromptVars({
30329
+ plan: initialPlan,
30330
+ steps: initialStepsConfig.steps,
30331
+ planPath,
30332
+ vars: initialVars,
30333
+ ...resolvedSetup ? { setup: resolvedSetup } : {},
30334
+ ...initialResolvedTeardown ? { teardown: initialResolvedTeardown } : {}
30335
+ });
30122
30336
  if (resolvedSetup) {
30123
- const { success: success2, cancelled } = await runPhase(resolvedSetup, "setup", initialTotalTasks, initialVars, initialPlan.mcp);
30337
+ const { success: success2, cancelled } = await runPhase(
30338
+ resolvedSetup,
30339
+ "setup",
30340
+ initialTotalTasks,
30341
+ initialVars,
30342
+ initialPlan.mcp
30343
+ );
30124
30344
  if (cancelled || !success2) {
30125
30345
  return {
30126
30346
  stopReason: cancelled ? "cancelled" : "failed",
@@ -30133,18 +30353,19 @@ async function runPipeline(options) {
30133
30353
  }
30134
30354
  while (runsCompleted < maxRuns) {
30135
30355
  assertNotAborted4(options.signal);
30136
- const release = await lockWorkflow(absolutePlanPath, { fs: fs17 });
30356
+ const release = await acquirePipelineLock({
30357
+ absolutePlanPath,
30358
+ planPath,
30359
+ fs: fs17,
30360
+ ...options.signal ? { signal: options.signal } : {},
30361
+ onLockStatusChange: options.onLockStatusChange
30362
+ });
30137
30363
  try {
30138
30364
  let stepsConfig;
30139
30365
  let plan;
30140
30366
  try {
30141
- stepsConfig = await loadResolvedSteps({
30142
- cwd: options.cwd,
30143
- homeDir: options.homeDir,
30144
- fs: fs17
30145
- });
30146
30367
  const content = await fs17.readFile(absolutePlanPath, "utf8");
30147
- plan = parsePlan(content, { availableSteps: stepsConfig.steps });
30368
+ ({ plan, stepsConfig } = await readResolvedPlanFromContent(content));
30148
30369
  lastGoodPlan = plan;
30149
30370
  lastGoodStepsConfig = stepsConfig;
30150
30371
  } catch (reloadError) {
@@ -30158,13 +30379,31 @@ async function runPipeline(options) {
30158
30379
  stepsConfig = lastGoodStepsConfig;
30159
30380
  }
30160
30381
  const totalTasks = plan.tasks.length;
30161
- const planVars = await resolveVars(plan.vars ?? {}, options.cwd, fs17.readFile.bind(fs17));
30382
+ const planVars = await resolvePipelineVars(
30383
+ plan.vars ?? {},
30384
+ options.cwd,
30385
+ fs17.readFile.bind(fs17)
30386
+ );
30387
+ const resolvedTeardown = plan.teardown === null ? void 0 : plan.teardown ?? stepsConfig.teardown;
30388
+ validateResolvedPromptVars({
30389
+ plan,
30390
+ steps: stepsConfig.steps,
30391
+ planPath,
30392
+ vars: planVars,
30393
+ ...resolvedTeardown ? { teardown: resolvedTeardown } : {}
30394
+ });
30162
30395
  const selection = selectNextExecution(plan, options.task);
30163
30396
  if (selection.kind === "completed") {
30164
30397
  if (runsCompleted > 0) {
30165
30398
  await archivePlan(fs17, absolutePlanPath);
30166
30399
  if (resolvedTeardown) {
30167
- const { success: success3, cancelled } = await runPhase(resolvedTeardown, "teardown", initialTotalTasks, planVars, plan.mcp);
30400
+ const { success: success3, cancelled } = await runPhase(
30401
+ resolvedTeardown,
30402
+ "teardown",
30403
+ initialTotalTasks,
30404
+ planVars,
30405
+ plan.mcp
30406
+ );
30168
30407
  if (cancelled || !success3) {
30169
30408
  return {
30170
30409
  stopReason: cancelled ? "cancelled" : "failed",
@@ -30250,8 +30489,11 @@ async function runPipeline(options) {
30250
30489
  metrics.totalCachedTokens += result.usage.cachedTokens ?? 0;
30251
30490
  }
30252
30491
  metrics.stepsCompleted += 1;
30492
+ const taskCompleted = success2 && completesTaskOnSuccess(selection.task, selection.stepName);
30253
30493
  if (success2) {
30254
- metrics.tasksCompleted += 1;
30494
+ if (taskCompleted) {
30495
+ metrics.tasksCompleted += 1;
30496
+ }
30255
30497
  } else {
30256
30498
  metrics.tasksFailed += 1;
30257
30499
  }
@@ -30278,6 +30520,7 @@ async function runPipeline(options) {
30278
30520
  ...taskProgress,
30279
30521
  durationMs: taskDurationMs,
30280
30522
  success: success2,
30523
+ taskCompleted,
30281
30524
  ...result.usage ? { usage: result.usage } : {}
30282
30525
  });
30283
30526
  if (!success2) {
@@ -30312,6 +30555,9 @@ var init_pipeline = __esm({
30312
30555
  init_parser2();
30313
30556
  init_writer2();
30314
30557
  init_runner2();
30558
+ init_interpolate();
30559
+ init_resolve4();
30560
+ init_validate();
30315
30561
  init_utils2();
30316
30562
  }
30317
30563
  });
@@ -30326,6 +30572,9 @@ var init_src18 = __esm({
30326
30572
  init_writer2();
30327
30573
  init_runner2();
30328
30574
  init_pipeline();
30575
+ init_interpolate();
30576
+ init_resolve4();
30577
+ init_validate();
30329
30578
  }
30330
30579
  });
30331
30580
 
@@ -30339,7 +30588,7 @@ var init_types4 = __esm({
30339
30588
  // packages/experiment-loop/src/frontmatter/frontmatter.ts
30340
30589
  import matter2 from "gray-matter";
30341
30590
  import { dirname } from "node:path";
30342
- import { stringify as stringify2 } from "yaml";
30591
+ import { stringify } from "yaml";
30343
30592
  function parseExperimentFrontmatter(content) {
30344
30593
  const parsed = matter2(content);
30345
30594
  return {
@@ -30638,7 +30887,7 @@ var init_git = __esm({
30638
30887
  });
30639
30888
 
30640
30889
  // packages/experiment-loop/src/config/loader.ts
30641
- import path38 from "node:path";
30890
+ import path39 from "node:path";
30642
30891
  import { readFile as readFile9 } from "node:fs/promises";
30643
30892
  import { fileURLToPath as fileURLToPath6 } from "node:url";
30644
30893
  import { parse as parse8 } from "yaml";
@@ -30709,7 +30958,7 @@ async function loadInstructions() {
30709
30958
  return readBundledFile("default-instructions.md");
30710
30959
  }
30711
30960
  async function loadRunConfig(options) {
30712
- const projectPath = path38.join(options.cwd, ".poe-code", "experiments", "run.yaml");
30961
+ const projectPath = path39.join(options.cwd, ".poe-code", "experiments", "run.yaml");
30713
30962
  const projectContent = await readOptionalFile3(options.fs, projectPath);
30714
30963
  if (projectContent == null) {
30715
30964
  return readDefaultRunConfig();
@@ -30718,8 +30967,8 @@ async function loadRunConfig(options) {
30718
30967
  if (projectDocument === null || projectDocument === void 0) {
30719
30968
  return readDefaultRunConfig();
30720
30969
  }
30721
- const bundledConfigDir = path38.resolve(fileURLToPath6(new URL(".", import.meta.url)));
30722
- const globalConfigDir = path38.resolve(path38.join(options.homeDir, ".poe-code", "experiments"));
30970
+ const bundledConfigDir = path39.resolve(fileURLToPath6(new URL(".", import.meta.url)));
30971
+ const globalConfigDir = path39.resolve(path39.join(options.homeDir, ".poe-code", "experiments"));
30723
30972
  const resolved = await resolve(
30724
30973
  [
30725
30974
  { source: "document", filePath: projectPath, content: projectContent },
@@ -30742,15 +30991,15 @@ var init_loader2 = __esm({
30742
30991
  });
30743
30992
 
30744
30993
  // packages/experiment-loop/src/discovery/discovery.ts
30745
- import path39 from "node:path";
30994
+ import path40 from "node:path";
30746
30995
  import * as fsPromises9 from "node:fs/promises";
30747
30996
  function createDefaultFs6() {
30748
30997
  return {
30749
30998
  readdir: fsPromises9.readdir,
30750
30999
  stat: async (filePath) => {
30751
- const stat23 = await fsPromises9.stat(filePath);
31000
+ const stat24 = await fsPromises9.stat(filePath);
30752
31001
  return {
30753
- isFile: () => stat23.isFile()
31002
+ isFile: () => stat24.isFile()
30754
31003
  };
30755
31004
  }
30756
31005
  };
@@ -30776,12 +31025,12 @@ async function scanDir(fs17, absoluteDir, displayDir) {
30776
31025
  if (!isMarkdownFile(entry)) {
30777
31026
  continue;
30778
31027
  }
30779
- const absolutePath = path39.join(absoluteDir, entry);
30780
- const stat23 = await fs17.stat(absolutePath);
30781
- if (!stat23.isFile()) {
31028
+ const absolutePath = path40.join(absoluteDir, entry);
31029
+ const stat24 = await fs17.stat(absolutePath);
31030
+ if (!stat24.isFile()) {
30782
31031
  continue;
30783
31032
  }
30784
- const displayPath2 = path39.join(displayDir, entry);
31033
+ const displayPath2 = path40.join(displayDir, entry);
30785
31034
  docs.push({
30786
31035
  path: displayPath2,
30787
31036
  displayPath: displayPath2
@@ -30790,18 +31039,18 @@ async function scanDir(fs17, absoluteDir, displayDir) {
30790
31039
  return docs;
30791
31040
  }
30792
31041
  function toDisplayPath(options) {
30793
- const localDir = path39.join(options.cwd, ".poe-code", "experiments");
30794
- const globalDir = path39.join(options.homeDir, ".poe-code", "experiments");
30795
- if (options.absolutePath.startsWith(`${localDir}${path39.sep}`)) {
30796
- return path39.join(
31042
+ const localDir = path40.join(options.cwd, ".poe-code", "experiments");
31043
+ const globalDir = path40.join(options.homeDir, ".poe-code", "experiments");
31044
+ if (options.absolutePath.startsWith(`${localDir}${path40.sep}`)) {
31045
+ return path40.join(
30797
31046
  ".poe-code/experiments",
30798
- path39.relative(localDir, options.absolutePath)
31047
+ path40.relative(localDir, options.absolutePath)
30799
31048
  );
30800
31049
  }
30801
- if (options.absolutePath.startsWith(`${globalDir}${path39.sep}`)) {
30802
- return path39.join(
31050
+ if (options.absolutePath.startsWith(`${globalDir}${path40.sep}`)) {
31051
+ return path40.join(
30803
31052
  "~/.poe-code/experiments",
30804
- path39.relative(globalDir, options.absolutePath)
31053
+ path40.relative(globalDir, options.absolutePath)
30805
31054
  );
30806
31055
  }
30807
31056
  return options.absolutePath;
@@ -30834,8 +31083,8 @@ async function discoverExperimentDocs(options) {
30834
31083
  const customDir = options.planDirectory?.trim();
30835
31084
  const docs = customDir ? await scanCustomDir(fs17, customDir, options.cwd, options.homeDir) : await scanDefaultDirs(fs17, options.cwd, options.homeDir);
30836
31085
  return docs.sort((left, right) => {
30837
- const leftName = path39.basename(left.displayPath).toLowerCase();
30838
- const rightName = path39.basename(right.displayPath).toLowerCase();
31086
+ const leftName = path40.basename(left.displayPath).toLowerCase();
31087
+ const rightName = path40.basename(right.displayPath).toLowerCase();
30839
31088
  return leftName === rightName ? left.displayPath.localeCompare(right.displayPath) : leftName.localeCompare(rightName);
30840
31089
  });
30841
31090
  }
@@ -30849,18 +31098,18 @@ var init_discovery2 = __esm({
30849
31098
  // packages/experiment-loop/src/run/loop.ts
30850
31099
  import { exec as execCallback } from "node:child_process";
30851
31100
  import * as fsPromises10 from "node:fs/promises";
30852
- import path40 from "node:path";
31101
+ import path41 from "node:path";
30853
31102
  function createDefaultFs7() {
30854
31103
  return {
30855
31104
  readFile: fsPromises10.readFile,
30856
31105
  writeFile: (filePath, content) => fsPromises10.writeFile(filePath, content, "utf8"),
30857
31106
  readdir: fsPromises10.readdir,
30858
31107
  stat: async (filePath) => {
30859
- const stat23 = await fsPromises10.stat(filePath);
31108
+ const stat24 = await fsPromises10.stat(filePath);
30860
31109
  return {
30861
- isFile: () => stat23.isFile(),
30862
- isDirectory: () => stat23.isDirectory(),
30863
- mtimeMs: stat23.mtimeMs
31110
+ isFile: () => stat24.isFile(),
31111
+ isDirectory: () => stat24.isDirectory(),
31112
+ mtimeMs: stat24.mtimeMs
30864
31113
  };
30865
31114
  },
30866
31115
  mkdir: async (filePath, options) => {
@@ -30896,9 +31145,9 @@ function createDefaultExec() {
30896
31145
  });
30897
31146
  }
30898
31147
  function resolveJournalPath(docPath) {
30899
- return path40.join(
30900
- path40.dirname(docPath),
30901
- `${path40.basename(docPath, path40.extname(docPath))}.journal.jsonl`
31148
+ return path41.join(
31149
+ path41.dirname(docPath),
31150
+ `${path41.basename(docPath, path41.extname(docPath))}.journal.jsonl`
30902
31151
  );
30903
31152
  }
30904
31153
  function normalizeMetrics(metric) {
@@ -30939,9 +31188,9 @@ function assertNotAborted5(signal) {
30939
31188
  if (!signal?.aborted) {
30940
31189
  return;
30941
31190
  }
30942
- throw createAbortError5();
31191
+ throw createAbortError6();
30943
31192
  }
30944
- function createAbortError5() {
31193
+ function createAbortError6() {
30945
31194
  const error2 = new Error("Experiment loop cancelled");
30946
31195
  error2.name = "AbortError";
30947
31196
  return error2;
@@ -30949,7 +31198,7 @@ function createAbortError5() {
30949
31198
  function isAbortError2(error2) {
30950
31199
  return error2 instanceof Error && error2.name === "AbortError";
30951
31200
  }
30952
- function interpolate2(template, values) {
31201
+ function interpolate(template, values) {
30953
31202
  let output = template;
30954
31203
  for (const [key, value] of Object.entries(values)) {
30955
31204
  output = output.split(`{{${key}}}`).join(value);
@@ -30980,8 +31229,8 @@ function buildPrompt(options) {
30980
31229
  doc_path: options.docPath,
30981
31230
  doc_name: options.docName
30982
31231
  };
30983
- const context = interpolate2(options.runConfig.prompt, vars).trim();
30984
- const instructions = interpolate2(options.instructions, vars).trim();
31232
+ const context = interpolate(options.runConfig.prompt, vars).trim();
31233
+ const instructions = interpolate(options.instructions, vars).trim();
30985
31234
  return `${context}
30986
31235
 
30987
31236
  ${instructions}`;
@@ -31032,8 +31281,8 @@ async function runExperimentLoop(options) {
31032
31281
  [
31033
31282
  { source: "cli", data: { agent: options.agent } },
31034
31283
  { source: "document", filePath: absoluteDocPath, content: rawContent },
31035
- { source: "base", path: path40.join(options.cwd, ".poe-code/experiments/bases") },
31036
- { source: "base", path: path40.join(options.homeDir, ".poe-code/experiments/bases") },
31284
+ { source: "base", path: path41.join(options.cwd, ".poe-code/experiments/bases") },
31285
+ { source: "base", path: path41.join(options.homeDir, ".poe-code/experiments/bases") },
31037
31286
  { source: "defaults", data: { agent: "claude-code" } }
31038
31287
  ],
31039
31288
  { fs: fs17 }
@@ -31113,7 +31362,7 @@ async function runExperimentLoop(options) {
31113
31362
  experimentIndex,
31114
31363
  baseline,
31115
31364
  docPath: options.docPath,
31116
- docName: path40.basename(absoluteDocPath, path40.extname(absoluteDocPath))
31365
+ docName: path41.basename(absoluteDocPath, path41.extname(absoluteDocPath))
31117
31366
  });
31118
31367
  const currentSpecifier = agents[(experimentIndex - 1) % agents.length];
31119
31368
  const model = currentSpecifier.model;
@@ -31209,7 +31458,7 @@ var init_src19 = __esm({
31209
31458
  });
31210
31459
 
31211
31460
  // packages/ralph/src/frontmatter/frontmatter.ts
31212
- import { parse as parse9, stringify as stringify3 } from "yaml";
31461
+ import { parse as parse9, stringify as stringify2 } from "yaml";
31213
31462
  function parseFrontmatter2(content) {
31214
31463
  if (!content.startsWith(`${FENCE}
31215
31464
  `)) {
@@ -31248,7 +31497,7 @@ function writeFrontmatter(data, body) {
31248
31497
  iteration: data.status.iteration
31249
31498
  }
31250
31499
  };
31251
- const yaml = stringify3(serialized).trimEnd();
31500
+ const yaml = stringify2(serialized).trimEnd();
31252
31501
  return `${FENCE}
31253
31502
  ${yaml}
31254
31503
  ${FENCE}
@@ -31346,17 +31595,17 @@ var init_frontmatter3 = __esm({
31346
31595
  });
31347
31596
 
31348
31597
  // packages/ralph/src/discovery/discovery.ts
31349
- import path41 from "node:path";
31598
+ import path42 from "node:path";
31350
31599
  import * as fsPromises11 from "node:fs/promises";
31351
31600
  function createDefaultFs8() {
31352
31601
  return {
31353
31602
  readdir: fsPromises11.readdir,
31354
31603
  stat: async (filePath) => {
31355
- const stat23 = await fsPromises11.stat(filePath);
31604
+ const stat24 = await fsPromises11.stat(filePath);
31356
31605
  return {
31357
- isFile: () => stat23.isFile(),
31358
- isDirectory: () => stat23.isDirectory(),
31359
- mtimeMs: stat23.mtimeMs
31606
+ isFile: () => stat24.isFile(),
31607
+ isDirectory: () => stat24.isDirectory(),
31608
+ mtimeMs: stat24.mtimeMs
31360
31609
  };
31361
31610
  }
31362
31611
  };
@@ -31382,12 +31631,12 @@ async function scanDir2(fs17, absoluteDir, displayDir) {
31382
31631
  if (!isMarkdownFile2(entry)) {
31383
31632
  continue;
31384
31633
  }
31385
- const absolutePath = path41.join(absoluteDir, entry);
31386
- const stat23 = await fs17.stat(absolutePath);
31387
- if (!stat23.isFile()) {
31634
+ const absolutePath = path42.join(absoluteDir, entry);
31635
+ const stat24 = await fs17.stat(absolutePath);
31636
+ if (!stat24.isFile()) {
31388
31637
  continue;
31389
31638
  }
31390
- const displayPath2 = path41.join(displayDir, entry);
31639
+ const displayPath2 = path42.join(displayDir, entry);
31391
31640
  docs.push({
31392
31641
  path: displayPath2,
31393
31642
  displayPath: displayPath2
@@ -31396,18 +31645,18 @@ async function scanDir2(fs17, absoluteDir, displayDir) {
31396
31645
  return docs;
31397
31646
  }
31398
31647
  function toDisplayPath2(options) {
31399
- const localDir = path41.join(options.cwd, ".poe-code", "ralph", "plans");
31400
- const globalDir = path41.join(options.homeDir, ".poe-code", "ralph", "plans");
31401
- if (options.absolutePath.startsWith(`${localDir}${path41.sep}`)) {
31402
- return path41.join(
31648
+ const localDir = path42.join(options.cwd, ".poe-code", "ralph", "plans");
31649
+ const globalDir = path42.join(options.homeDir, ".poe-code", "ralph", "plans");
31650
+ if (options.absolutePath.startsWith(`${localDir}${path42.sep}`)) {
31651
+ return path42.join(
31403
31652
  ".poe-code/ralph/plans",
31404
- path41.relative(localDir, options.absolutePath)
31653
+ path42.relative(localDir, options.absolutePath)
31405
31654
  );
31406
31655
  }
31407
- if (options.absolutePath.startsWith(`${globalDir}${path41.sep}`)) {
31408
- return path41.join(
31656
+ if (options.absolutePath.startsWith(`${globalDir}${path42.sep}`)) {
31657
+ return path42.join(
31409
31658
  "~/.poe-code/ralph/plans",
31410
- path41.relative(globalDir, options.absolutePath)
31659
+ path42.relative(globalDir, options.absolutePath)
31411
31660
  );
31412
31661
  }
31413
31662
  return options.absolutePath;
@@ -31440,8 +31689,8 @@ async function discoverDocs(options) {
31440
31689
  const customDir = options.planDirectory?.trim();
31441
31690
  const docs = customDir ? await scanCustomDir2(fs17, customDir, options.cwd, options.homeDir) : await scanDefaultDirs2(fs17, options.cwd, options.homeDir);
31442
31691
  return docs.sort((left, right) => {
31443
- const leftName = path41.basename(left.displayPath).toLowerCase();
31444
- const rightName = path41.basename(right.displayPath).toLowerCase();
31692
+ const leftName = path42.basename(left.displayPath).toLowerCase();
31693
+ const rightName = path42.basename(right.displayPath).toLowerCase();
31445
31694
  return leftName === rightName ? left.displayPath.localeCompare(right.displayPath) : leftName.localeCompare(rightName);
31446
31695
  });
31447
31696
  }
@@ -31465,7 +31714,7 @@ var init_variables = __esm({
31465
31714
  });
31466
31715
 
31467
31716
  // packages/ralph/src/run/ralph.ts
31468
- import path42 from "node:path";
31717
+ import path43 from "node:path";
31469
31718
  import * as fsPromises12 from "node:fs/promises";
31470
31719
  async function runRalph(options) {
31471
31720
  const fs17 = options.fs ?? createDefaultFs9();
@@ -31669,11 +31918,11 @@ async function resolveDocumentConfigFromContent(options, fs17, absoluteDocPath,
31669
31918
  },
31670
31919
  {
31671
31920
  source: "base",
31672
- path: path42.join(options.cwd, ".poe-code/ralph/bases")
31921
+ path: path43.join(options.cwd, ".poe-code/ralph/bases")
31673
31922
  },
31674
31923
  {
31675
31924
  source: "base",
31676
- path: path42.join(options.homeDir, ".poe-code/ralph/bases")
31925
+ path: path43.join(options.homeDir, ".poe-code/ralph/bases")
31677
31926
  },
31678
31927
  {
31679
31928
  source: "defaults",
@@ -31724,11 +31973,11 @@ function createDefaultFs9() {
31724
31973
  writeFile: (filePath, content) => fsPromises12.writeFile(filePath, content, "utf8"),
31725
31974
  readdir: fsPromises12.readdir,
31726
31975
  stat: async (filePath) => {
31727
- const stat23 = await fsPromises12.stat(filePath);
31976
+ const stat24 = await fsPromises12.stat(filePath);
31728
31977
  return {
31729
- isFile: () => stat23.isFile(),
31730
- isDirectory: () => stat23.isDirectory(),
31731
- mtimeMs: stat23.mtimeMs
31978
+ isFile: () => stat24.isFile(),
31979
+ isDirectory: () => stat24.isDirectory(),
31980
+ mtimeMs: stat24.mtimeMs
31732
31981
  };
31733
31982
  },
31734
31983
  mkdir: async (filePath, options) => {
@@ -31806,9 +32055,9 @@ async function updateFrontmatter2(fs17, absoluteDocPath, state, iteration) {
31806
32055
  await fs17.writeFile(absoluteDocPath, content);
31807
32056
  }
31808
32057
  async function archivePlan2(fs17, absoluteDocPath) {
31809
- const dir = path42.dirname(absoluteDocPath);
31810
- const archiveDir = path42.join(dir, "archive");
31811
- const archivePath = path42.join(archiveDir, path42.basename(absoluteDocPath));
32058
+ const dir = path43.dirname(absoluteDocPath);
32059
+ const archiveDir = path43.join(dir, "archive");
32060
+ const archivePath = path43.join(archiveDir, path43.basename(absoluteDocPath));
31812
32061
  await fs17.mkdir(archiveDir, { recursive: true });
31813
32062
  await fs17.rename(absoluteDocPath, archivePath);
31814
32063
  }
@@ -31843,7 +32092,7 @@ var init_src20 = __esm({
31843
32092
  });
31844
32093
 
31845
32094
  // packages/plan-browser/src/format.ts
31846
- import path43 from "node:path";
32095
+ import path44 from "node:path";
31847
32096
  import { parseDocument as parseDocument5 } from "yaml";
31848
32097
  function isPipelineTaskDone(task) {
31849
32098
  if (typeof task.status === "string") {
@@ -32014,9 +32263,9 @@ function formatPipelinePlanMarkdown(options) {
32014
32263
  return lines.join("\n").trimEnd();
32015
32264
  }
32016
32265
  function resolveExperimentJournalPath(absolutePath) {
32017
- return path43.join(
32018
- path43.dirname(absolutePath),
32019
- `${path43.basename(absolutePath, path43.extname(absolutePath))}.journal.jsonl`
32266
+ return path44.join(
32267
+ path44.dirname(absolutePath),
32268
+ `${path44.basename(absolutePath, path44.extname(absolutePath))}.journal.jsonl`
32020
32269
  );
32021
32270
  }
32022
32271
  async function readExperimentState(fs17, absolutePath) {
@@ -32046,7 +32295,7 @@ function resolveFormatFromPath(filePath) {
32046
32295
  }
32047
32296
  async function readPlanMetadata(options) {
32048
32297
  const content = await options.fs.readFile(options.absolutePath, "utf8");
32049
- const fallbackName = path43.basename(options.path);
32298
+ const fallbackName = path44.basename(options.path);
32050
32299
  if (options.kind === "pipeline") {
32051
32300
  return {
32052
32301
  title: fallbackName,
@@ -32107,7 +32356,7 @@ var init_format = __esm({
32107
32356
  });
32108
32357
 
32109
32358
  // packages/plan-browser/src/discovery.ts
32110
- import path44 from "node:path";
32359
+ import path45 from "node:path";
32111
32360
  import * as fsPromises13 from "node:fs/promises";
32112
32361
  function createDefaultFs10() {
32113
32362
  return {
@@ -32115,11 +32364,11 @@ function createDefaultFs10() {
32115
32364
  writeFile: fsPromises13.writeFile,
32116
32365
  readdir: fsPromises13.readdir,
32117
32366
  stat: async (filePath) => {
32118
- const stat23 = await fsPromises13.stat(filePath);
32367
+ const stat24 = await fsPromises13.stat(filePath);
32119
32368
  return {
32120
- isFile: () => stat23.isFile(),
32121
- isDirectory: () => stat23.isDirectory(),
32122
- mtimeMs: stat23.mtimeMs
32369
+ isFile: () => stat24.isFile(),
32370
+ isDirectory: () => stat24.isDirectory(),
32371
+ mtimeMs: stat24.mtimeMs
32123
32372
  };
32124
32373
  },
32125
32374
  mkdir: async (directoryPath, mkdirOptions) => {
@@ -32134,9 +32383,9 @@ function isNotFound5(error2) {
32134
32383
  }
32135
32384
  function resolveAbsoluteDirectory3(dir, cwd, homeDir) {
32136
32385
  if (dir.startsWith("~/")) {
32137
- return path44.join(homeDir, dir.slice(2));
32386
+ return path45.join(homeDir, dir.slice(2));
32138
32387
  }
32139
- return path44.isAbsolute(dir) ? dir : path44.resolve(cwd, dir);
32388
+ return path45.isAbsolute(dir) ? dir : path45.resolve(cwd, dir);
32140
32389
  }
32141
32390
  function isMarkdownFile3(name) {
32142
32391
  return name.toLowerCase().endsWith(".md");
@@ -32213,12 +32462,12 @@ async function discoverSharedPlans(options) {
32213
32462
  if (!isMarkdownFile3(name)) {
32214
32463
  continue;
32215
32464
  }
32216
- const absolutePath = path44.join(absoluteDir, name);
32217
- const stat23 = await options.fs.stat(absolutePath);
32218
- if (!stat23.isFile()) {
32465
+ const absolutePath = path45.join(absoluteDir, name);
32466
+ const stat24 = await options.fs.stat(absolutePath);
32467
+ if (!stat24.isFile()) {
32219
32468
  continue;
32220
32469
  }
32221
- const displayPath2 = path44.join(displayDir, name);
32470
+ const displayPath2 = path45.join(displayDir, name);
32222
32471
  const content = await options.fs.readFile(absolutePath, "utf8");
32223
32472
  const kind = classifyPlanKind(content, displayPath2);
32224
32473
  if (options.kind && kind !== options.kind) {
@@ -32239,7 +32488,7 @@ async function discoverSharedPlans(options) {
32239
32488
  format: metadata.format,
32240
32489
  title: metadata.title,
32241
32490
  detail: metadata.detail,
32242
- updatedAt: stat23.mtimeMs
32491
+ updatedAt: stat24.mtimeMs
32243
32492
  });
32244
32493
  }
32245
32494
  return plans;
@@ -32263,7 +32512,7 @@ var init_discovery4 = __esm({
32263
32512
  });
32264
32513
 
32265
32514
  // packages/plan-browser/src/actions.ts
32266
- import path45 from "node:path";
32515
+ import path46 from "node:path";
32267
32516
  import { spawnSync as nodeSpawnSync2 } from "node:child_process";
32268
32517
  function resolveEditor2(env = process.env) {
32269
32518
  const editor = env.EDITOR?.trim() || env.VISUAL?.trim() || "vi";
@@ -32275,8 +32524,8 @@ function editPlan2(absolutePath, options = {}) {
32275
32524
  spawnSync3(editor, [absolutePath], { stdio: "inherit" });
32276
32525
  }
32277
32526
  async function archivePlan3(entry, fs17) {
32278
- const archiveDir = path45.join(path45.dirname(entry.absolutePath), "archive");
32279
- const archivedPath = path45.join(archiveDir, path45.basename(entry.absolutePath));
32527
+ const archiveDir = path46.join(path46.dirname(entry.absolutePath), "archive");
32528
+ const archivedPath = path46.join(archiveDir, path46.basename(entry.absolutePath));
32280
32529
  await fs17.mkdir(archiveDir, { recursive: true });
32281
32530
  await fs17.rename(entry.absolutePath, archivedPath);
32282
32531
  return archivedPath;
@@ -32291,7 +32540,7 @@ var init_actions = __esm({
32291
32540
  });
32292
32541
 
32293
32542
  // packages/plan-browser/src/browser.ts
32294
- import path46 from "node:path";
32543
+ import path47 from "node:path";
32295
32544
  async function runPlanBrowser(options) {
32296
32545
  const renderPlanPreview = async (entry) => {
32297
32546
  const markdown = await loadPlanPreviewMarkdown(entry, options.fs);
@@ -32320,7 +32569,7 @@ async function runPlanBrowser(options) {
32320
32569
  const selectedPath = await select2({
32321
32570
  message: "Select a plan",
32322
32571
  options: plans.map((plan) => ({
32323
- label: text.selectLabel(path46.basename(plan.path), plan.detail),
32572
+ label: text.selectLabel(path47.basename(plan.path), plan.detail),
32324
32573
  hint: plan.typeLabel,
32325
32574
  value: plan.absolutePath
32326
32575
  }))
@@ -32356,7 +32605,7 @@ async function runPlanBrowser(options) {
32356
32605
  if (action === "archive") {
32357
32606
  try {
32358
32607
  const confirmed = options.assumeYes || await confirmOrCancel({
32359
- message: `Archive ${path46.basename(selectedPlan.path)}?`,
32608
+ message: `Archive ${path47.basename(selectedPlan.path)}?`,
32360
32609
  initialValue: true
32361
32610
  });
32362
32611
  if (confirmed) {
@@ -32371,7 +32620,7 @@ async function runPlanBrowser(options) {
32371
32620
  }
32372
32621
  try {
32373
32622
  const confirmed = options.assumeYes || await confirmOrCancel({
32374
- message: `Permanently delete ${path46.basename(selectedPlan.path)}?`,
32623
+ message: `Permanently delete ${path47.basename(selectedPlan.path)}?`,
32375
32624
  initialValue: true
32376
32625
  });
32377
32626
  if (confirmed) {
@@ -32516,7 +32765,7 @@ var init_scan = __esm({
32516
32765
 
32517
32766
  // packages/markdown-reader/src/core/document.ts
32518
32767
  import nodeFs2 from "node:fs/promises";
32519
- import path47 from "node:path";
32768
+ import path48 from "node:path";
32520
32769
  import { parseDocument as parseDocument6 } from "yaml";
32521
32770
  async function loadMarkdownDocument(file, dependencies = {}) {
32522
32771
  const resolvedFile = resolveMarkdownPath(file, dependencies.cwd);
@@ -32531,7 +32780,7 @@ async function loadMarkdownDocument(file, dependencies = {}) {
32531
32780
  };
32532
32781
  }
32533
32782
  function resolveMarkdownPath(file, cwd = process.cwd()) {
32534
- return path47.isAbsolute(file) ? file : path47.resolve(cwd, file);
32783
+ return path48.isAbsolute(file) ? file : path48.resolve(cwd, file);
32535
32784
  }
32536
32785
  function sliceMarkdownBytes(source, start, end) {
32537
32786
  return Buffer.from(source, "utf8").subarray(start, end).toString("utf8");
@@ -32678,7 +32927,7 @@ function resolveSection(sections, id) {
32678
32927
  `no section matching "${id}" (try 'read-markdown' to see the table of contents)`
32679
32928
  );
32680
32929
  }
32681
- var init_resolve4 = __esm({
32930
+ var init_resolve5 = __esm({
32682
32931
  "packages/markdown-reader/src/core/resolve.ts"() {
32683
32932
  "use strict";
32684
32933
  init_src13();
@@ -32707,7 +32956,7 @@ var init_read_section = __esm({
32707
32956
  "packages/markdown-reader/src/core/read-section.ts"() {
32708
32957
  "use strict";
32709
32958
  init_document();
32710
- init_resolve4();
32959
+ init_resolve5();
32711
32960
  readSection = createReadSection();
32712
32961
  }
32713
32962
  });
@@ -33624,13 +33873,13 @@ function isPlainObject3(value) {
33624
33873
  }
33625
33874
  function createFs() {
33626
33875
  return {
33627
- readFile: async (path82, encoding = "utf8") => readFile13(path82, { encoding }),
33628
- writeFile: async (path82, contents) => {
33629
- await writeFile9(path82, contents);
33876
+ readFile: async (path84, encoding = "utf8") => readFile13(path84, { encoding }),
33877
+ writeFile: async (path84, contents) => {
33878
+ await writeFile9(path84, contents);
33630
33879
  },
33631
- exists: async (path82) => {
33880
+ exists: async (path84) => {
33632
33881
  try {
33633
- await access(path82);
33882
+ await access(path84);
33634
33883
  return true;
33635
33884
  } catch {
33636
33885
  return false;
@@ -33675,11 +33924,11 @@ function applySchemaCasing(schema, casing) {
33675
33924
  ...required === void 0 ? {} : { required }
33676
33925
  };
33677
33926
  }
33678
- function collectParamSummaries(schema, casing, path82 = [], inheritedOptional = false) {
33927
+ function collectParamSummaries(schema, casing, path84 = [], inheritedOptional = false) {
33679
33928
  const summaries = [];
33680
33929
  for (const [key, rawChildSchema] of Object.entries(schema.shape)) {
33681
33930
  const childSchema = unwrapOptional2(rawChildSchema);
33682
- const nextPath = [...path82, formatSegment(key, casing)];
33931
+ const nextPath = [...path84, formatSegment(key, casing)];
33683
33932
  const optional = inheritedOptional || isOptional(rawChildSchema);
33684
33933
  if (childSchema.kind === "object") {
33685
33934
  summaries.push(...collectParamSummaries(childSchema, casing, nextPath, optional));
@@ -33708,17 +33957,17 @@ function matchesAllowlist(toolName, allowlist) {
33708
33957
  const candidates = segments.map((_segment, index) => segments.slice(0, index + 1).join("__"));
33709
33958
  return candidates.some((candidate) => allowlist.includes(candidate));
33710
33959
  }
33711
- function formatToolName(path82) {
33712
- return path82.map((segment) => formatSegment(segment, "snake")).join("__");
33960
+ function formatToolName(path84) {
33961
+ return path84.map((segment) => formatSegment(segment, "snake")).join("__");
33713
33962
  }
33714
33963
  function enumerateTools(root, casing, allowlist) {
33715
33964
  const tools = [];
33716
- function visit(node, path82) {
33965
+ function visit(node, path84) {
33717
33966
  if (node.kind === "command") {
33718
33967
  if (!node.scope.includes("mcp")) {
33719
33968
  return;
33720
33969
  }
33721
- const name = formatToolName([...path82, node.name]);
33970
+ const name = formatToolName([...path84, node.name]);
33722
33971
  const params = filterSchemaForScope(node.params, "mcp");
33723
33972
  if (!matchesAllowlist(name, allowlist)) {
33724
33973
  return;
@@ -33734,7 +33983,7 @@ function enumerateTools(root, casing, allowlist) {
33734
33983
  });
33735
33984
  return;
33736
33985
  }
33737
- const nextPath = [...path82, node.name];
33986
+ const nextPath = [...path84, node.name];
33738
33987
  for (const child of node.children) {
33739
33988
  visit(child, nextPath);
33740
33989
  }
@@ -33993,7 +34242,7 @@ var init_SKILL_plan = __esm({
33993
34242
  });
33994
34243
 
33995
34244
  // src/cli/commands/plan.ts
33996
- import path48 from "node:path";
34245
+ import path49 from "node:path";
33997
34246
  import { stringify as stringifyYaml2 } from "yaml";
33998
34247
  function buildPlanPrompt(options) {
33999
34248
  const trimmedQuestion = options.question.trim();
@@ -34145,7 +34394,7 @@ async function discoverPlans(container, kind) {
34145
34394
  async function resolveSelectedPlan(options) {
34146
34395
  const providedPath = options.providedPath?.trim();
34147
34396
  if (providedPath) {
34148
- const resolvedAbsolute = providedPath.startsWith("~/") ? path48.join(options.container.env.homeDir, providedPath.slice(2)) : path48.isAbsolute(providedPath) ? providedPath : path48.resolve(options.container.env.cwd, providedPath);
34397
+ const resolvedAbsolute = providedPath.startsWith("~/") ? path49.join(options.container.env.homeDir, providedPath.slice(2)) : path49.isAbsolute(providedPath) ? providedPath : path49.resolve(options.container.env.cwd, providedPath);
34149
34398
  const matched2 = options.plans.find(
34150
34399
  (plan) => plan.path === providedPath || plan.absolutePath === providedPath || plan.absolutePath === resolvedAbsolute
34151
34400
  );
@@ -34163,7 +34412,7 @@ async function resolveSelectedPlan(options) {
34163
34412
  const selected = await select2({
34164
34413
  message: options.promptMessage,
34165
34414
  options: options.plans.map((plan) => ({
34166
- label: text.selectLabel(path48.basename(plan.path), plan.detail),
34415
+ label: text.selectLabel(path49.basename(plan.path), plan.detail),
34167
34416
  hint: plan.typeLabel,
34168
34417
  value: plan.absolutePath
34169
34418
  }))
@@ -34201,7 +34450,7 @@ async function renderPlanList(container, options) {
34201
34450
  kind: plan.kind,
34202
34451
  type: plan.typeLabel,
34203
34452
  runner: plan.runner,
34204
- name: path48.basename(plan.path),
34453
+ name: path49.basename(plan.path),
34205
34454
  path: plan.path,
34206
34455
  detail: plan.detail,
34207
34456
  updated: formatDate(plan.updatedAt)
@@ -34226,7 +34475,7 @@ async function renderPlanList(container, options) {
34226
34475
  rows: plans.map((plan) => ({
34227
34476
  kind: plan.kind,
34228
34477
  type: plan.typeLabel,
34229
- name: path48.basename(plan.path),
34478
+ name: path49.basename(plan.path),
34230
34479
  detail: plan.detail,
34231
34480
  updated: formatDate(plan.updatedAt)
34232
34481
  }))
@@ -34260,7 +34509,7 @@ async function executePlanAction(options) {
34260
34509
  }
34261
34510
  if (!flags.assumeYes) {
34262
34511
  const confirmed = await confirmOrCancel({
34263
- message: options.action === "archive" ? `Archive ${path48.basename(plan.path)}?` : `Permanently delete ${path48.basename(plan.path)}?`,
34512
+ message: options.action === "archive" ? `Archive ${path49.basename(plan.path)}?` : `Permanently delete ${path49.basename(plan.path)}?`,
34264
34513
  initialValue: true
34265
34514
  });
34266
34515
  if (!confirmed) {
@@ -34610,7 +34859,7 @@ var init_plan = __esm({
34610
34859
  });
34611
34860
 
34612
34861
  // src/cli/commands/pipeline-init.ts
34613
- import path49 from "node:path";
34862
+ import path50 from "node:path";
34614
34863
  function buildPipelineInitPrompt(options) {
34615
34864
  const trimmedQuestion = options.question?.trim() ?? "";
34616
34865
  const sourceTitle = extractTitle2(options.sourceDocPath, options.sourceDocContent);
@@ -34623,7 +34872,9 @@ function buildPipelineInitPrompt(options) {
34623
34872
  "",
34624
34873
  "---",
34625
34874
  "",
34626
- `Plan directory: ${options.planDirectory}`,
34875
+ "Edit the source document in place by prepending valid YAML frontmatter.",
34876
+ "Do not create a new file.",
34877
+ `The final result must remain at: ${options.sourceDocPath}`,
34627
34878
  "",
34628
34879
  "User request:",
34629
34880
  userRequest,
@@ -34650,7 +34901,7 @@ async function discoverPipelineInitSources(options) {
34650
34901
  const sourceFiles = await discoverMarkdownFiles(options.container.fs, sourceAbsoluteDirectory);
34651
34902
  const sources = [];
34652
34903
  for (const file of sourceFiles) {
34653
- const sourceName = path49.basename(file.relativePath, path49.extname(file.relativePath));
34904
+ const sourceName = path50.basename(file.relativePath, path50.extname(file.relativePath));
34654
34905
  if (existingPlanNames.has(`plan-${sourceName}`)) {
34655
34906
  continue;
34656
34907
  }
@@ -34700,12 +34951,12 @@ async function discoverExistingPipelinePlanNames(fs17, absoluteDirectory) {
34700
34951
  if (!lower.endsWith(".md") && !lower.endsWith(".yaml")) {
34701
34952
  continue;
34702
34953
  }
34703
- const absolutePath = path49.join(absoluteDirectory, entry);
34704
- const stat23 = await fs17.stat(absolutePath);
34705
- if (!stat23.isFile()) {
34954
+ const absolutePath = path50.join(absoluteDirectory, entry);
34955
+ const stat24 = await fs17.stat(absolutePath);
34956
+ if (!stat24.isFile()) {
34706
34957
  continue;
34707
34958
  }
34708
- planNames.add(path49.basename(entry, path49.extname(entry)));
34959
+ planNames.add(path50.basename(entry, path50.extname(entry)));
34709
34960
  }
34710
34961
  return planNames;
34711
34962
  }
@@ -34721,17 +34972,17 @@ async function discoverMarkdownFiles(fs17, absoluteDirectory, parentRelativePath
34721
34972
  }
34722
34973
  const markdownFiles = [];
34723
34974
  for (const entry of [...entries].sort((left, right) => left.localeCompare(right))) {
34724
- const absolutePath = path49.join(absoluteDirectory, entry);
34725
- const relativePath = parentRelativePath.length > 0 ? path49.join(parentRelativePath, entry) : entry;
34726
- const stat23 = await fs17.stat(absolutePath);
34727
- if (stat23.isDirectory()) {
34975
+ const absolutePath = path50.join(absoluteDirectory, entry);
34976
+ const relativePath = parentRelativePath.length > 0 ? path50.join(parentRelativePath, entry) : entry;
34977
+ const stat24 = await fs17.stat(absolutePath);
34978
+ if (stat24.isDirectory()) {
34728
34979
  if (entry === "archive") {
34729
34980
  continue;
34730
34981
  }
34731
34982
  markdownFiles.push(...await discoverMarkdownFiles(fs17, absolutePath, relativePath));
34732
34983
  continue;
34733
34984
  }
34734
- if (!stat23.isFile() || !entry.toLowerCase().endsWith(".md")) {
34985
+ if (!stat24.isFile() || !entry.toLowerCase().endsWith(".md")) {
34735
34986
  continue;
34736
34987
  }
34737
34988
  markdownFiles.push({
@@ -34752,7 +35003,7 @@ function extractTitle2(filePath, content) {
34752
35003
  return title;
34753
35004
  }
34754
35005
  }
34755
- return path49.basename(filePath, path49.extname(filePath));
35006
+ return path50.basename(filePath, path50.extname(filePath));
34756
35007
  }
34757
35008
  function createMarkdownFence(content) {
34758
35009
  return "`".repeat(Math.max(3, longestBacktickRun(content) + 1));
@@ -34794,33 +35045,150 @@ var init_pipeline_init = __esm({
34794
35045
  var SKILL_plan_default2;
34795
35046
  var init_SKILL_plan2 = __esm({
34796
35047
  "src/templates/pipeline/SKILL_plan.md"() {
34797
- SKILL_plan_default2 = '---\nname: poe-code-pipeline-plan\ndescription: \'Generate a Pipeline plan markdown file with YAML frontmatter from a user request. Triggers on: create a pipeline plan, write plan for, plan this feature, pipeline plan.\'\n---\n\n## If The Request Is Empty\n\nAsk the user for a one-sentence description of what they want to build.\n\n## Goal\n\nWrite a markdown pipeline plan with YAML frontmatter. Before writing, determine where to place it:\n\n1. Write the plan to `docs/plans/plan-<name>.md` by default.\n2. Find the `steps.yaml` file. Check these locations in order and use the first one found:\n a. `<project-root>/.poe-code/pipeline/steps.yaml` (project-level)\n b. `~/.poe-code/pipeline/steps.yaml` (user-global)\n If found, read it to determine available steps and note any `setup`/`teardown` hooks defined there. If not found in either location, use stepless tasks.\n3. Decide what belongs in the markdown body. If the user already has a Markdown design/context doc (for example in `docs/plans/`), use that content as the plan body when it makes sense. If they want to keep the source doc separate, link it via `vars` so it is available in every prompt without repeating the path.\n\n## Rules\n\n- Each task must be self-contained. Put all context needed to execute the task inside that task\'s `prompt`.\n- Do not create tasks that depend on hidden state from previous tasks.\n- Use short kebab-case ids.\n- Keep titles concise and descriptive.\n- The available steps come from the `steps.yaml` file you found (project or global). Use the current step names instead of inventing hardcoded ones.\n- If no step configuration is present, use stepless tasks with scalar `status: open`.\n- If step configuration is present, start every configured step status at `open`.\n- `setup` and `teardown` defined in `steps.yaml` are inherited automatically.\n- To disable an inherited hook for a specific plan, set `setup: false` or `teardown: false`.\n- To override an inherited hook, define the full block with an `prompt` field.\n- If the user keeps an existing Markdown plan document as a separate file, add a `vars` block with a `plan_doc` key pointing to the file path. The pipeline runtime reads the file and makes its contents available as a named placeholder in every prompt.\n- `vars` values are plain strings. Use the `file` include syntax inside a value to load a file at runtime (path relative to project root): write `var_name: "` followed by the file include tag and a closing `"`.\n- Each var name becomes a double-curly-brace placeholder usable in any task, step, setup, or teardown prompt.\n- The markdown body is for context, notes, acceptance criteria, or the design doc. Keep executable pipeline config in the YAML frontmatter.\n- Do not rely on the body alone for runtime context. Each task prompt must still include everything it needs directly or via `vars` placeholders.\n- Start the frontmatter with canonical metadata: `$schema: https://poe-platform.github.io/poe-code/schemas/plans/pipeline.schema.json`, `kind: pipeline`, and `version: 1`.\n\n## Output Format\n\n```markdown\n---\n$schema: https://poe-platform.github.io/poe-code/schemas/plans/pipeline.schema.json\nkind: pipeline\nversion: 1\n\n# vars: optional. Define named values available as double-curly-brace placeholders in every prompt.\n# Each value is a plain string. To load a file at runtime, write the value using the\n# file include syntax: double-curly-brace file followed by a quoted path.\n# Omit this block if there are no shared values to inject.\n#\n# vars:\n# plan_doc: "(file include for docs/plans/my-feature.md)" # loaded at runtime\n# env: production # literal string\n\n# setup/teardown are inherited from steps.yaml automatically.\n# Include them only to disable or override:\n#\n# setup: false # disable the inherited setup hook\n# teardown: false # disable the inherited teardown hook\n# setup: # override with a different prompt\n# prompt: Custom setup\n# mode: yolo\n\ntasks:\n - id: auth-hardening\n title: Harden auth flow\n prompt: |\n Improve auth validation and session handling.\n # If vars defines plan_doc, reference it here using the double-curly-brace placeholder syntax.\n # scalar when no steps.yaml steps are defined:\n status: open\n # stepped when steps.yaml defines steps:\n # status:\n # implement: open\n # test: open\n # commit: open\n---\n\n# Context\n\nPaste the design doc, notes, acceptance criteria, or implementation details here.\nThis body is for human-readable context and future tooling.\nAny context needed at runtime must still appear in each task prompt or via vars placeholders.\n```\n\n## After Writing\n\nRun `poe-code pipeline validate <path>` to check the plan is valid before running it.\n\n## Notes\n\n- Match the uncommented step names and order from whichever `steps.yaml` file you find.\n- If `poe-code` is not available as a global command, use `npx poe-code` instead.\n';
35048
+ SKILL_plan_default2 = '---\nname: poe-code-pipeline-plan\ndescription: \'Generate a Pipeline plan markdown file with YAML frontmatter from a user request. Triggers on: create a pipeline plan, write plan for, plan this feature, pipeline plan.\'\n---\n\n## If The Request Is Empty\n\nAsk the user for a one-sentence description of what they want to build.\n\n## Goal\n\nWrite a markdown pipeline plan with YAML frontmatter. Before writing, determine where to place it:\n\n1. Write the plan to `docs/plans/plan-<name>.md` by default.\n2. Find named step configs in `.poe-code/pipeline/steps/`.\n a. Check `<project-root>/.poe-code/pipeline/steps/` first.\n b. If that directory does not exist, check `~/.poe-code/pipeline/steps/`.\n c. Read the available `*.yaml` files there (for example `default.yaml`, `fast.yaml`). Use `default` when the user does not ask for a different config. If no directory exists, use stepless tasks.\n3. Decide what belongs in the markdown body. If the user already has a Markdown design/context doc (for example in `docs/plans/`), use that content as the plan body when it makes sense. If they want to keep the source doc separate, link it via `vars` so it is available in every prompt without repeating the path.\n\n## Rules\n\n- Each task must be self-contained. Put all context needed to execute the task inside that task\'s `prompt`.\n- Do not create tasks that depend on hidden state from previous tasks.\n- Use short kebab-case ids.\n- Keep titles concise and descriptive.\n- The available steps come from the named step config you found in `.poe-code/pipeline/steps/`. Use the current step names instead of inventing hardcoded ones.\n- Always write `extends: <name>` in the frontmatter. Use `default` unless you discovered or intentionally chose another named config.\n- If no step configuration directory is present, use stepless tasks with scalar `status: open`.\n- If step configuration is present, start every configured step status at `open`.\n- `setup` and `teardown` from the extended step config are inherited automatically.\n- To disable an inherited hook for a specific plan, set `setup: false` or `teardown: false`.\n- To override an inherited hook, define the full block with a `prompt` field.\n- Use the plan-level `steps:` block only for per-plan step overrides. Override only the fields you need; unset fields inherit from the extended config.\n- If the user keeps an existing Markdown plan document as a separate file, add a `vars` block with a `plan_doc` key pointing to the file path. The pipeline runtime reads the file and makes its contents available as a named placeholder in every prompt.\n- `vars` values are plain strings. Use the `file` include syntax inside a value to load a file at runtime (path relative to project root): write `var_name: "` followed by the file include tag and a closing `"`.\n- Each var name becomes a double-curly-brace placeholder usable in any task, step, setup, or teardown prompt.\n- The markdown body is for context, notes, acceptance criteria, or the design doc. Keep executable pipeline config in the YAML frontmatter.\n- Do not rely on the body alone for runtime context. Each task prompt must still include everything it needs directly or via `vars` placeholders.\n- Start the frontmatter with canonical metadata: `$schema: https://poe-platform.github.io/poe-code/schemas/plans/pipeline.schema.json`, `kind: pipeline`, and `version: 1`.\n\n## Output Format\n\n```markdown\n---\n$schema: https://poe-platform.github.io/poe-code/schemas/plans/pipeline.schema.json\nkind: pipeline\nversion: 1\nextends: default\n\n# vars: optional. Define named values available as double-curly-brace placeholders in every prompt.\n# Each value is a plain string. To load a file at runtime, write the value using the\n# file include syntax: double-curly-brace file followed by a quoted path.\n# Omit this block if there are no shared values to inject.\n#\n# vars:\n# plan_doc: "(file include for docs/plans/my-feature.md)" # loaded at runtime\n# env: production # literal string\n\n# Optional per-plan step overrides. Unset fields inherit from the extended config.\n# Omit this block when the base config already fits.\n#\n# steps:\n# implement:\n# prompt: |\n# Focus on the risky auth paths first.\n# test:\n# mode: read\n\n# setup/teardown are inherited from the extended step config automatically.\n# Include them only to disable or override:\n#\n# setup: false # disable the inherited setup hook\n# teardown: false # disable the inherited teardown hook\n# setup: # override with a different prompt\n# prompt: Custom setup\n# mode: yolo\n\ntasks:\n - id: auth-hardening\n title: Harden auth flow\n prompt: |\n Improve auth validation and session handling.\n # If vars defines plan_doc, reference it here using the double-curly-brace placeholder syntax.\n # scalar when no step config is available:\n status: open\n # stepped when the extended config defines steps:\n # status:\n # implement: open\n # test: open\n # commit: open\n---\n\n# Context\n\nPaste the design doc, notes, acceptance criteria, or implementation details here.\nThis body is for human-readable context and future tooling.\nAny context needed at runtime must still appear in each task prompt or via vars placeholders.\n```\n\n## After Writing\n\nRun `poe-code pipeline validate <path>` to check the plan is valid before running it.\n\n## Notes\n\n- Match the uncommented step names and order from the named step config you choose.\n- If `poe-code` is not available as a global command, use `npx poe-code` instead.\n';
34798
35049
  }
34799
35050
  });
34800
35051
 
34801
35052
  // src/sdk/pipeline.ts
34802
35053
  import * as fsPromises14 from "node:fs/promises";
35054
+ import path51 from "node:path";
34803
35055
  function isAbortError4(error2) {
34804
35056
  return error2 instanceof Error && error2.name === "AbortError";
34805
35057
  }
35058
+ function createDefaultFs11() {
35059
+ return {
35060
+ readFile: fsPromises14.readFile,
35061
+ writeFile: fsPromises14.writeFile,
35062
+ readdir: fsPromises14.readdir,
35063
+ stat: async (filePath) => {
35064
+ const stat24 = await fsPromises14.stat(filePath);
35065
+ return {
35066
+ isFile: () => stat24.isFile(),
35067
+ isDirectory: () => stat24.isDirectory(),
35068
+ mtimeMs: stat24.mtimeMs
35069
+ };
35070
+ },
35071
+ mkdir: async (filePath, options) => {
35072
+ await fsPromises14.mkdir(filePath, options);
35073
+ },
35074
+ rmdir: fsPromises14.rmdir,
35075
+ rename: fsPromises14.rename
35076
+ };
35077
+ }
35078
+ function createEmptyMetrics() {
35079
+ return {
35080
+ totalInputTokens: 0,
35081
+ totalOutputTokens: 0,
35082
+ totalCachedTokens: 0,
35083
+ tasksCompleted: 0,
35084
+ tasksFailed: 0,
35085
+ stepsCompleted: 0
35086
+ };
35087
+ }
35088
+ function needsPipelineInit(planContent) {
35089
+ try {
35090
+ return parsePlan(planContent).tasks.length === 0;
35091
+ } catch (error2) {
35092
+ const message2 = error2 instanceof Error ? error2.message : String(error2);
35093
+ if (message2 === "Invalid plan YAML: expected a top-level object." || message2 === 'Invalid plan YAML: expected "tasks" to be an array.') {
35094
+ return true;
35095
+ }
35096
+ throw error2;
35097
+ }
35098
+ }
35099
+ function withPipelineTimeoutRetries(runAgent) {
35100
+ return async (input) => {
35101
+ for (let attempt = 1; attempt <= PIPELINE_ACTIVITY_TIMEOUT_RETRY_COUNT; attempt += 1) {
35102
+ try {
35103
+ return await runAgent(input);
35104
+ } catch (error2) {
35105
+ if (!isActivityTimeoutError(error2) || attempt === PIPELINE_ACTIVITY_TIMEOUT_RETRY_COUNT) {
35106
+ throw error2;
35107
+ }
35108
+ }
35109
+ }
35110
+ throw new Error("Unreachable");
35111
+ };
35112
+ }
34806
35113
  async function runPipeline2(options) {
34807
- const runAgent = options.runAgent ?? (async (input) => {
35114
+ const fs17 = options.fs ?? createDefaultFs11();
35115
+ const runAgent = withPipelineTimeoutRetries(options.runAgent ?? (async (input) => {
34808
35116
  return await spawn7.autonomous(input.agent, {
34809
35117
  prompt: input.prompt,
34810
35118
  cwd: input.cwd,
34811
35119
  logDir: input.logDir,
34812
35120
  model: input.model,
34813
35121
  mode: input.mode,
35122
+ maxTimeoutRetries: 1,
34814
35123
  ...input.mcpServers ? { mcpServers: input.mcpServers } : {},
34815
35124
  ...input.signal ? { signal: input.signal } : {}
34816
35125
  });
35126
+ }));
35127
+ const planPath = await resolvePlanPath({
35128
+ cwd: options.cwd,
35129
+ homeDir: options.homeDir,
35130
+ plan: options.plan,
35131
+ planDirectory: options.planDirectory,
35132
+ assumeYes: options.assumeYes,
35133
+ fs: fs17,
35134
+ selectPlan: options.selectPlan,
35135
+ promptForPath: options.promptForPath
34817
35136
  });
35137
+ if (!planPath) {
35138
+ return {
35139
+ stopReason: "cancelled",
35140
+ planPath: "",
35141
+ runsCompleted: 0,
35142
+ totalDurationMs: 0,
35143
+ metrics: createEmptyMetrics()
35144
+ };
35145
+ }
35146
+ const absolutePlanPath = resolveAbsolutePlanPath(planPath, options.cwd, options.homeDir);
35147
+ const planContent = await fs17.readFile(absolutePlanPath, "utf8");
35148
+ if (needsPipelineInit(planContent)) {
35149
+ const initResult = await runPipelineInit({
35150
+ agent: options.agent,
35151
+ ...options.model ? { model: options.model } : {},
35152
+ cwd: options.cwd,
35153
+ homeDir: options.homeDir,
35154
+ fs: fs17,
35155
+ sources: [
35156
+ {
35157
+ absolutePath: absolutePlanPath,
35158
+ relativePath: planPath,
35159
+ title: path51.basename(planPath, path51.extname(planPath))
35160
+ }
35161
+ ],
35162
+ assumeYes: options.assumeYes ?? false,
35163
+ runAgent,
35164
+ ...options.signal ? { signal: options.signal } : {}
35165
+ });
35166
+ if (initResult.stopReason === "cancelled") {
35167
+ return {
35168
+ stopReason: "cancelled",
35169
+ planPath,
35170
+ runsCompleted: 0,
35171
+ totalDurationMs: 0,
35172
+ metrics: createEmptyMetrics()
35173
+ };
35174
+ }
35175
+ if (initResult.stopReason === "failed") {
35176
+ throw new Error(`Pipeline init failed for "${planPath}".`);
35177
+ }
35178
+ const initializedPlanContent = await fs17.readFile(absolutePlanPath, "utf8");
35179
+ if (needsPipelineInit(initializedPlanContent)) {
35180
+ throw new Error(`Pipeline init did not produce runnable tasks for "${planPath}".`);
35181
+ }
35182
+ }
34818
35183
  return runPipeline({
34819
35184
  ...options,
35185
+ fs: fs17,
35186
+ plan: planPath,
34820
35187
  runAgent
34821
35188
  });
34822
35189
  }
34823
35190
  async function runPipelineInit(options) {
35191
+ const fs17 = options.fs ?? createDefaultFs11();
34824
35192
  const runAgent = options.runAgent ?? (async (input) => {
34825
35193
  return await spawn7.autonomous(input.agent, {
34826
35194
  prompt: input.prompt,
@@ -34836,11 +35204,6 @@ async function runPipelineInit(options) {
34836
35204
  sourcesProcessed: 0
34837
35205
  };
34838
35206
  }
34839
- const planDirectory = resolvePlanDirectory2({
34840
- cwd: options.cwd,
34841
- homeDir: options.homeDir,
34842
- planDirectory: options.planDirectory ?? ".poe-code/pipeline/plans"
34843
- });
34844
35207
  let sourcesProcessed = 0;
34845
35208
  const totalSources = options.sources.length;
34846
35209
  for (const [sourceIndex, source] of options.sources.entries()) {
@@ -34853,7 +35216,7 @@ async function runPipelineInit(options) {
34853
35216
  const displayIndex = sourceIndex + 1;
34854
35217
  try {
34855
35218
  options.onSourceStart?.(source, displayIndex, totalSources);
34856
- const sourceDocContent = await fsPromises14.readFile(source.absolutePath, "utf8");
35219
+ const sourceDocContent = await fs17.readFile(source.absolutePath, "utf8");
34857
35220
  if (options.signal?.aborted) {
34858
35221
  return {
34859
35222
  stopReason: "cancelled",
@@ -34862,7 +35225,6 @@ async function runPipelineInit(options) {
34862
35225
  }
34863
35226
  const prompt = buildPipelineInitPrompt({
34864
35227
  question: options.question,
34865
- planDirectory,
34866
35228
  sourceDocPath: source.relativePath,
34867
35229
  sourceDocContent,
34868
35230
  skillContent: SKILL_plan_default2
@@ -34903,19 +35265,22 @@ async function runPipelineInit(options) {
34903
35265
  sourcesProcessed
34904
35266
  };
34905
35267
  }
35268
+ var PIPELINE_ACTIVITY_TIMEOUT_RETRY_COUNT;
34906
35269
  var init_pipeline2 = __esm({
34907
35270
  async "src/sdk/pipeline.ts"() {
34908
35271
  "use strict";
35272
+ init_src8();
34909
35273
  init_src18();
34910
35274
  await init_pipeline_init();
34911
35275
  init_SKILL_plan2();
34912
35276
  await init_spawn3();
34913
35277
  init_src18();
35278
+ PIPELINE_ACTIVITY_TIMEOUT_RETRY_COUNT = 3;
34914
35279
  }
34915
35280
  });
34916
35281
 
34917
35282
  // packages/process-launcher/src/state/state-store.ts
34918
- import path50 from "node:path";
35283
+ import path52 from "node:path";
34919
35284
  import * as nodeFs3 from "node:fs/promises";
34920
35285
  function isNotFoundError2(error2) {
34921
35286
  return error2 instanceof Error && "code" in error2 && error2.code === "ENOENT";
@@ -34931,9 +35296,9 @@ async function removeDirectory2(fs17, directoryPath) {
34931
35296
  throw error2;
34932
35297
  }
34933
35298
  for (const entry of entries) {
34934
- const entryPath = path50.join(directoryPath, entry);
34935
- const stat23 = await fs17.stat(entryPath);
34936
- if (stat23.isFile()) {
35299
+ const entryPath = path52.join(directoryPath, entry);
35300
+ const stat24 = await fs17.stat(entryPath);
35301
+ if (stat24.isFile()) {
34937
35302
  await fs17.rm(entryPath, { force: true });
34938
35303
  continue;
34939
35304
  }
@@ -34948,7 +35313,7 @@ async function removeDirectory2(fs17, directoryPath) {
34948
35313
  }
34949
35314
  function createStateStore(stateDir, fs17 = nodeFs3) {
34950
35315
  async function read(id) {
34951
- const statePath = path50.join(stateDir, id, "state.json");
35316
+ const statePath = path52.join(stateDir, id, "state.json");
34952
35317
  try {
34953
35318
  const content = await fs17.readFile(statePath, "utf8");
34954
35319
  return JSON.parse(content);
@@ -34960,9 +35325,9 @@ function createStateStore(stateDir, fs17 = nodeFs3) {
34960
35325
  }
34961
35326
  }
34962
35327
  async function write2(id, state) {
34963
- const processDir = path50.join(stateDir, id);
35328
+ const processDir = path52.join(stateDir, id);
34964
35329
  await fs17.mkdir(processDir, { recursive: true });
34965
- await fs17.writeFile(path50.join(processDir, "state.json"), `${JSON.stringify(state, null, 2)}
35330
+ await fs17.writeFile(path52.join(processDir, "state.json"), `${JSON.stringify(state, null, 2)}
34966
35331
  `);
34967
35332
  }
34968
35333
  async function list() {
@@ -34977,10 +35342,10 @@ function createStateStore(stateDir, fs17 = nodeFs3) {
34977
35342
  }
34978
35343
  const states = [];
34979
35344
  for (const entry of [...entries].sort()) {
34980
- const entryPath = path50.join(stateDir, entry);
35345
+ const entryPath = path52.join(stateDir, entry);
34981
35346
  try {
34982
- const stat23 = await fs17.stat(entryPath);
34983
- if (stat23.isFile()) {
35347
+ const stat24 = await fs17.stat(entryPath);
35348
+ if (stat24.isFile()) {
34984
35349
  continue;
34985
35350
  }
34986
35351
  } catch (error2) {
@@ -34997,7 +35362,7 @@ function createStateStore(stateDir, fs17 = nodeFs3) {
34997
35362
  return states;
34998
35363
  }
34999
35364
  async function remove2(id) {
35000
- await removeDirectory2(fs17, path50.join(stateDir, id));
35365
+ await removeDirectory2(fs17, path52.join(stateDir, id));
35001
35366
  }
35002
35367
  return { read, write: write2, list, remove: remove2 };
35003
35368
  }
@@ -35008,16 +35373,16 @@ var init_state_store = __esm({
35008
35373
  });
35009
35374
 
35010
35375
  // packages/process-launcher/src/logs/log-writer.ts
35011
- import path51 from "node:path";
35376
+ import path53 from "node:path";
35012
35377
  import * as nodeFs4 from "node:fs/promises";
35013
35378
  function isNotFoundError3(error2) {
35014
35379
  return error2 instanceof Error && "code" in error2 && error2.code === "ENOENT";
35015
35380
  }
35016
35381
  function getCurrentLogPath(logDir, stream) {
35017
- return path51.join(logDir, `${stream}.log`);
35382
+ return path53.join(logDir, `${stream}.log`);
35018
35383
  }
35019
35384
  function getRotatedLogPath(logDir, stream, index) {
35020
- return path51.join(logDir, `${stream}.${index}.log`);
35385
+ return path53.join(logDir, `${stream}.${index}.log`);
35021
35386
  }
35022
35387
  async function isFile(fs17, filePath) {
35023
35388
  try {
@@ -35064,7 +35429,7 @@ async function removeAllStreamLogs(fs17, logDir, stream) {
35064
35429
  if (getRotatedLogIndex(fileName, stream) === null) {
35065
35430
  continue;
35066
35431
  }
35067
- await fs17.rm(path51.join(logDir, fileName), { force: true });
35432
+ await fs17.rm(path53.join(logDir, fileName), { force: true });
35068
35433
  }
35069
35434
  } catch (error2) {
35070
35435
  if (isNotFoundError3(error2)) {
@@ -35324,7 +35689,7 @@ var init_engine = __esm({
35324
35689
  });
35325
35690
 
35326
35691
  // packages/process-runner/src/docker/args.ts
35327
- import path52 from "node:path";
35692
+ import path54 from "node:path";
35328
35693
  function buildDockerRunArgs(input) {
35329
35694
  const args = [input.engine];
35330
35695
  if (input.engine === "docker" && input.context) {
@@ -35351,7 +35716,7 @@ function buildDockerRunArgs(input) {
35351
35716
  args.push("-e", `${key}=${value}`);
35352
35717
  }
35353
35718
  for (const mount of input.mounts) {
35354
- const volume = `${path52.resolve(mount.source)}:${mount.target}${mount.readonly ? ":ro" : ""}`;
35719
+ const volume = `${path54.resolve(mount.source)}:${mount.target}${mount.readonly ? ":ro" : ""}`;
35355
35720
  args.push("-v", volume);
35356
35721
  }
35357
35722
  for (const port of input.ports) {
@@ -35608,13 +35973,13 @@ var init_src24 = __esm({
35608
35973
  import { spawnSync } from "node:child_process";
35609
35974
  import * as nodeFs5 from "node:fs/promises";
35610
35975
  import os5 from "node:os";
35611
- import path53 from "node:path";
35976
+ import path55 from "node:path";
35612
35977
  function createSupervisor(options) {
35613
35978
  const { spec: spec10 } = options;
35614
35979
  const runner = resolveRunner(options);
35615
35980
  const stateStore = createStateStore(options.stateDir, options.fs);
35616
35981
  const logWriter = createLogWriter(
35617
- path53.join(options.stateDir, spec10.id, "logs"),
35982
+ path55.join(options.stateDir, spec10.id, "logs"),
35618
35983
  spec10.logRetainCount ?? 5,
35619
35984
  options.fs
35620
35985
  );
@@ -35982,8 +36347,8 @@ async function resolveProcessWorkspace(cwd, stateDir) {
35982
36347
  };
35983
36348
  }
35984
36349
  function resolveWorkspaceHomeDir(stateDir) {
35985
- if (path53.basename(stateDir) === "launch" && path53.basename(path53.dirname(stateDir)) === ".poe-code") {
35986
- return path53.dirname(path53.dirname(stateDir));
36350
+ if (path55.basename(stateDir) === "launch" && path55.basename(path55.dirname(stateDir)) === ".poe-code") {
36351
+ return path55.dirname(path55.dirname(stateDir));
35987
36352
  }
35988
36353
  return os5.homedir();
35989
36354
  }
@@ -36035,7 +36400,7 @@ var init_supervisor = __esm({
36035
36400
  });
36036
36401
 
36037
36402
  // packages/process-launcher/src/launcher.ts
36038
- import path54 from "node:path";
36403
+ import path56 from "node:path";
36039
36404
  import * as nodeFs6 from "node:fs/promises";
36040
36405
  async function startManagedProcess(options) {
36041
36406
  const fs17 = options.fs ?? defaultFs2();
@@ -36384,10 +36749,10 @@ async function listIds(fs17, baseDir) {
36384
36749
  const entries = await fs17.readdir(baseDir);
36385
36750
  const ids = [];
36386
36751
  for (const entry of entries) {
36387
- const entryPath = path54.join(baseDir, entry);
36752
+ const entryPath = path56.join(baseDir, entry);
36388
36753
  try {
36389
- const stat23 = await fs17.stat(entryPath);
36390
- if (!stat23.isFile()) {
36754
+ const stat24 = await fs17.stat(entryPath);
36755
+ if (!stat24.isFile()) {
36391
36756
  ids.push(entry);
36392
36757
  }
36393
36758
  } catch (error2) {
@@ -36434,24 +36799,24 @@ async function readJsonFile(fs17, filePath) {
36434
36799
  }
36435
36800
  }
36436
36801
  async function writeJsonFile(fs17, filePath, value) {
36437
- await fs17.mkdir(path54.dirname(filePath), { recursive: true });
36802
+ await fs17.mkdir(path56.dirname(filePath), { recursive: true });
36438
36803
  await fs17.writeFile(filePath, `${JSON.stringify(value, null, 2)}
36439
36804
  `);
36440
36805
  }
36441
36806
  function resolveProcessDir(baseDir, id) {
36442
- return path54.join(baseDir, id);
36807
+ return path56.join(baseDir, id);
36443
36808
  }
36444
36809
  function resolveSpecPath(baseDir, id) {
36445
- return path54.join(resolveProcessDir(baseDir, id), "spec.json");
36810
+ return path56.join(resolveProcessDir(baseDir, id), "spec.json");
36446
36811
  }
36447
36812
  function resolveStatePath(baseDir, id) {
36448
- return path54.join(resolveProcessDir(baseDir, id), "state.json");
36813
+ return path56.join(resolveProcessDir(baseDir, id), "state.json");
36449
36814
  }
36450
36815
  function resolveMetaPath(baseDir, id) {
36451
- return path54.join(resolveProcessDir(baseDir, id), "meta.json");
36816
+ return path56.join(resolveProcessDir(baseDir, id), "meta.json");
36452
36817
  }
36453
36818
  function resolveLogDir2(baseDir, id) {
36454
- return path54.join(resolveProcessDir(baseDir, id), "logs");
36819
+ return path56.join(resolveProcessDir(baseDir, id), "logs");
36455
36820
  }
36456
36821
  function isNotFoundError4(error2) {
36457
36822
  return error2 instanceof Error && "code" in error2 && error2.code === "ENOENT";
@@ -36532,8 +36897,8 @@ var init_ralph2 = __esm({
36532
36897
 
36533
36898
  // src/sdk/experiment.ts
36534
36899
  import * as fsPromises15 from "node:fs/promises";
36535
- import path55 from "node:path";
36536
- function createDefaultFs11() {
36900
+ import path57 from "node:path";
36901
+ function createDefaultFs12() {
36537
36902
  return {
36538
36903
  readFile: fsPromises15.readFile,
36539
36904
  writeFile: async (filePath, content) => {
@@ -36541,11 +36906,11 @@ function createDefaultFs11() {
36541
36906
  },
36542
36907
  readdir: fsPromises15.readdir,
36543
36908
  stat: async (filePath) => {
36544
- const stat23 = await fsPromises15.stat(filePath);
36909
+ const stat24 = await fsPromises15.stat(filePath);
36545
36910
  return {
36546
- isFile: () => stat23.isFile(),
36547
- isDirectory: () => stat23.isDirectory(),
36548
- mtimeMs: stat23.mtimeMs
36911
+ isFile: () => stat24.isFile(),
36912
+ isDirectory: () => stat24.isDirectory(),
36913
+ mtimeMs: stat24.mtimeMs
36549
36914
  };
36550
36915
  },
36551
36916
  mkdir: async (filePath, options) => {
@@ -36560,9 +36925,9 @@ function createDefaultFs11() {
36560
36925
  };
36561
36926
  }
36562
36927
  function resolveJournalPath2(docPath) {
36563
- return path55.join(
36564
- path55.dirname(docPath),
36565
- `${path55.basename(docPath, path55.extname(docPath))}.journal.jsonl`
36928
+ return path57.join(
36929
+ path57.dirname(docPath),
36930
+ `${path57.basename(docPath, path57.extname(docPath))}.journal.jsonl`
36566
36931
  );
36567
36932
  }
36568
36933
  async function runExperiment(options) {
@@ -36580,13 +36945,13 @@ async function runExperiment(options) {
36580
36945
  });
36581
36946
  }
36582
36947
  async function readExperimentJournal(options) {
36583
- const fs17 = options.fs ?? createDefaultFs11();
36948
+ const fs17 = options.fs ?? createDefaultFs12();
36584
36949
  const absoluteDocPath = resolveWorkflowPath(options.docPath, options.cwd, options.homeDir);
36585
36950
  const journal = new ExperimentJournal(resolveJournalPath2(absoluteDocPath), fs17);
36586
36951
  return await journal.readAll();
36587
36952
  }
36588
36953
  async function appendExperimentJournalEntry(options) {
36589
- const fs17 = options.fs ?? createDefaultFs11();
36954
+ const fs17 = options.fs ?? createDefaultFs12();
36590
36955
  const absoluteDocPath = resolveWorkflowPath(options.docPath, options.cwd, options.homeDir);
36591
36956
  const journal = new ExperimentJournal(resolveJournalPath2(absoluteDocPath), fs17);
36592
36957
  await journal.init();
@@ -36769,7 +37134,7 @@ var init_frontmatter4 = __esm({
36769
37134
  });
36770
37135
 
36771
37136
  // packages/github-workflows/src/discover.ts
36772
- import { readdir as readdir15, readFile as readFile16 } from "node:fs/promises";
37137
+ import { readdir as readdir16, readFile as readFile16 } from "node:fs/promises";
36773
37138
  import { join as join2 } from "node:path";
36774
37139
  async function discoverAutomations(builtInDir, ...projectDirs) {
36775
37140
  const directories = [builtInDir, ...projectDirs];
@@ -36802,7 +37167,7 @@ async function loadAutomation(name, dirs) {
36802
37167
  }
36803
37168
  async function listMarkdownFiles(dir) {
36804
37169
  try {
36805
- const entries = await readdir15(dir);
37170
+ const entries = await readdir16(dir);
36806
37171
  return entries.filter((entry) => entry.endsWith(".md")).sort((left, right) => left.localeCompare(right));
36807
37172
  } catch (error2) {
36808
37173
  if (isMissingPathError(error2)) {
@@ -37122,9 +37487,9 @@ var init_setup_agent = __esm({
37122
37487
  });
37123
37488
 
37124
37489
  // packages/github-workflows/src/variables.ts
37125
- import path56 from "node:path";
37490
+ import path58 from "node:path";
37126
37491
  import { readFile as readFile17 } from "node:fs/promises";
37127
- import { isMap as isMap3, parseDocument as parseDocument7, stringify as stringify4 } from "yaml";
37492
+ import { isMap as isMap3, parseDocument as parseDocument7, stringify as stringify3 } from "yaml";
37128
37493
  function isRecord18(value) {
37129
37494
  return typeof value === "object" && value !== null && !Array.isArray(value);
37130
37495
  }
@@ -37236,15 +37601,15 @@ function formatVariableBlock(name, value) {
37236
37601
  return formatYamlBlock(name, value);
37237
37602
  }
37238
37603
  function formatYamlBlock(name, value) {
37239
- return stringify4({ [name]: value }).trimEnd();
37604
+ return stringify3({ [name]: value }).trimEnd();
37240
37605
  }
37241
37606
  function formatCommentedBlock(name, value) {
37242
37607
  return formatVariableBlock(name, value).split("\n").map((line) => `# ${line}`).join("\n");
37243
37608
  }
37244
37609
  async function loadVariableSources(builtInDir, projectDir) {
37245
- const builtInPath = path56.join(builtInDir, VARIABLES_FILE_NAME);
37610
+ const builtInPath = path58.join(builtInDir, VARIABLES_FILE_NAME);
37246
37611
  const builtInVariables = parseVariables(builtInPath, await readFile17(builtInPath, "utf8"));
37247
- const projectVariablesPath = projectDir === void 0 ? void 0 : path56.join(projectDir, VARIABLES_FILE_NAME);
37612
+ const projectVariablesPath = projectDir === void 0 ? void 0 : path58.join(projectDir, VARIABLES_FILE_NAME);
37248
37613
  const projectVariablesContent = projectVariablesPath === void 0 ? void 0 : await readOptionalVariablesContent(projectVariablesPath);
37249
37614
  if (projectVariablesPath === void 0 || projectVariablesContent === void 0) {
37250
37615
  return { builtInVariables, extendsBuiltIns: true, projectVariables: {} };
@@ -37256,13 +37621,13 @@ async function loadVariableSources(builtInDir, projectDir) {
37256
37621
  return { builtInVariables, extendsBuiltIns, projectVariables };
37257
37622
  }
37258
37623
  async function loadVariables(builtInDir, projectDir) {
37259
- const builtInPath = path56.join(builtInDir, VARIABLES_FILE_NAME);
37624
+ const builtInPath = path58.join(builtInDir, VARIABLES_FILE_NAME);
37260
37625
  const builtInContent = await readFile17(builtInPath, "utf8");
37261
37626
  const builtInVariables = parseVariables(builtInPath, builtInContent);
37262
37627
  if (projectDir === void 0) {
37263
37628
  return filterDisabledVariables(builtInVariables);
37264
37629
  }
37265
- const projectVariablesPath = path56.join(projectDir, VARIABLES_FILE_NAME);
37630
+ const projectVariablesPath = path58.join(projectDir, VARIABLES_FILE_NAME);
37266
37631
  const projectVariablesContent = await readOptionalVariablesContent(projectVariablesPath);
37267
37632
  if (projectVariablesContent === void 0) {
37268
37633
  return filterDisabledVariables(builtInVariables);
@@ -37295,7 +37660,7 @@ async function loadVariableStatuses(builtInDir, projectDir) {
37295
37660
  (key) => !extendsBuiltIns || !Object.prototype.hasOwnProperty.call(builtInVariables, key)
37296
37661
  )
37297
37662
  ];
37298
- const projectVariablesPath = projectDir === void 0 ? void 0 : path56.join(projectDir, VARIABLES_FILE_NAME);
37663
+ const projectVariablesPath = projectDir === void 0 ? void 0 : path58.join(projectDir, VARIABLES_FILE_NAME);
37299
37664
  return orderedNames.map((name) => {
37300
37665
  if (!Object.prototype.hasOwnProperty.call(projectVariables, name)) {
37301
37666
  return {
@@ -37356,8 +37721,8 @@ var init_variables2 = __esm({
37356
37721
  });
37357
37722
 
37358
37723
  // packages/github-workflows/src/commands.ts
37359
- import { access as access2, mkdir as mkdir15, readFile as readFile18, unlink as unlink5, writeFile as writeFile11 } from "node:fs/promises";
37360
- import path57 from "node:path";
37724
+ import { access as access2, mkdir as mkdir16, readFile as readFile18, unlink as unlink5, writeFile as writeFile12 } from "node:fs/promises";
37725
+ import path59 from "node:path";
37361
37726
  import { fileURLToPath as fileURLToPath7 } from "node:url";
37362
37727
  import Mustache3 from "mustache";
37363
37728
  function formatLabel(name) {
@@ -37380,7 +37745,7 @@ async function loadNamedAutomation(name, cwd) {
37380
37745
  }
37381
37746
  async function readBuiltInPromptFile(name) {
37382
37747
  try {
37383
- return await readFile18(path57.join(await resolveBuiltInPromptsDir(), `${name}.md`), "utf8");
37748
+ return await readFile18(path59.join(await resolveBuiltInPromptsDir(), `${name}.md`), "utf8");
37384
37749
  } catch (error2) {
37385
37750
  if (isMissingPathError2(error2)) {
37386
37751
  throw new UserError(`Automation "${name}" was not found.`);
@@ -37389,11 +37754,11 @@ async function readBuiltInPromptFile(name) {
37389
37754
  }
37390
37755
  }
37391
37756
  async function readBuiltInWorkflowTemplate(name, variant, automationName = name, promptPath) {
37392
- const templatePath = path57.join(await resolveBuiltInWorkflowTemplatesDir(), `${name}.${variant}.yml`);
37757
+ const templatePath = path59.join(await resolveBuiltInWorkflowTemplatesDir(), `${name}.${variant}.yml`);
37393
37758
  try {
37394
37759
  const content = await readFile18(templatePath, "utf8");
37395
37760
  const header = promptPath !== void 0 ? `# Auto-generated by: poe-code github-workflows install ${name}
37396
- # Edit ${path57.relative(process.cwd(), promptPath)} to customize the prompt.
37761
+ # Edit ${path59.relative(process.cwd(), promptPath)} to customize the prompt.
37397
37762
  ` : `# Auto-generated by: poe-code github-workflows install ${name}
37398
37763
  `;
37399
37764
  const processedContent = content.replaceAll(` ${name}`, ` ${automationName}`).replaceAll("__UPSTREAM_REPO__", UPSTREAM_REPO);
@@ -37422,7 +37787,7 @@ function projectPromptDirs(cwd) {
37422
37787
  return [projectWorkflowDir(cwd)];
37423
37788
  }
37424
37789
  function projectWorkflowDir(cwd) {
37425
- return path57.join(cwd, ".github", "workflows");
37790
+ return path59.join(cwd, ".github", "workflows");
37426
37791
  }
37427
37792
  function projectGitHubWorkflowsDir(cwd) {
37428
37793
  return projectWorkflowDir(cwd);
@@ -37441,7 +37806,7 @@ async function resolveBuiltInPromptsDir() {
37441
37806
  return builtInPromptsDirCandidates[0];
37442
37807
  }
37443
37808
  async function resolveBuiltInAssetsDir() {
37444
- return path57.dirname(await resolveBuiltInPromptsDir());
37809
+ return path59.dirname(await resolveBuiltInPromptsDir());
37445
37810
  }
37446
37811
  function resolveCwd(cwd) {
37447
37812
  return cwd ?? process.cwd();
@@ -37595,17 +37960,17 @@ function addPromptHeader(content, name) {
37595
37960
  async function installAutomation(name, cwd, isEject) {
37596
37961
  const variant = isEject ? "ejected" : "caller";
37597
37962
  const localAutomationName = isEject ? `poe-code-${name}` : name;
37598
- const promptPath = isEject ? path57.join(projectWorkflowDir(cwd), `${localAutomationName}.md`) : void 0;
37963
+ const promptPath = isEject ? path59.join(projectWorkflowDir(cwd), `${localAutomationName}.md`) : void 0;
37599
37964
  const [workflowTemplate, rawPrompt] = await Promise.all([
37600
37965
  readBuiltInWorkflowTemplate(name, variant, localAutomationName, promptPath),
37601
37966
  readBuiltInPromptFile(name)
37602
37967
  ]);
37603
- const workflowPath = path57.join(cwd, ".github", "workflows", `poe-code-${name}.yml`);
37604
- await mkdir15(path57.dirname(workflowPath), { recursive: true });
37605
- await writeFile11(workflowPath, workflowTemplate, "utf8");
37968
+ const workflowPath = path59.join(cwd, ".github", "workflows", `poe-code-${name}.yml`);
37969
+ await mkdir16(path59.dirname(workflowPath), { recursive: true });
37970
+ await writeFile12(workflowPath, workflowTemplate, "utf8");
37606
37971
  if (promptPath !== void 0) {
37607
- await mkdir15(path57.dirname(promptPath), { recursive: true });
37608
- await writeFile11(promptPath, addPromptHeader(rawPrompt, name), "utf8");
37972
+ await mkdir16(path59.dirname(promptPath), { recursive: true });
37973
+ await writeFile12(promptPath, addPromptHeader(rawPrompt, name), "utf8");
37609
37974
  }
37610
37975
  return {
37611
37976
  name,
@@ -37617,15 +37982,15 @@ async function installAutomation(name, cwd, isEject) {
37617
37982
  }
37618
37983
  async function ensureProjectSupportFiles(cwd, builtInVariables) {
37619
37984
  const projectDir = projectGitHubWorkflowsDir(cwd);
37620
- const variablesPath = path57.join(projectDir, "variables.yaml");
37621
- const readmePath = path57.join(projectDir, "README.md");
37622
- await mkdir15(projectDir, { recursive: true });
37623
- await writeFile11(
37985
+ const variablesPath = path59.join(projectDir, "variables.yaml");
37986
+ const readmePath = path59.join(projectDir, "README.md");
37987
+ await mkdir16(projectDir, { recursive: true });
37988
+ await writeFile12(
37624
37989
  variablesPath,
37625
37990
  generateProjectVariablesFile(builtInVariables, await readOptionalFile4(variablesPath)),
37626
37991
  "utf8"
37627
37992
  );
37628
- await writeFile11(readmePath, renderProjectReadme(), "utf8");
37993
+ await writeFile12(readmePath, renderProjectReadme(), "utf8");
37629
37994
  return { readmePath, variablesPath };
37630
37995
  }
37631
37996
  async function readOptionalFile4(filePath) {
@@ -37900,7 +38265,7 @@ var init_commands2 = __esm({
37900
38265
  scope: ["cli"],
37901
38266
  handler: async ({ params }) => {
37902
38267
  const name = params.name;
37903
- const workflowPath = path57.join(resolveCwd(), ".github", "workflows", `poe-code-${name}.yml`);
38268
+ const workflowPath = path59.join(resolveCwd(), ".github", "workflows", `poe-code-${name}.yml`);
37904
38269
  try {
37905
38270
  await unlink5(workflowPath);
37906
38271
  } catch (error2) {
@@ -38003,7 +38368,7 @@ var init_commands2 = __esm({
38003
38368
  return (await loadVariableStatuses(await resolveBuiltInAssetsDir(), projectGitHubWorkflowsDir(cwd))).map(
38004
38369
  (status) => ({
38005
38370
  ...status,
38006
- source: status.source === "built-in" ? status.source : path57.relative(cwd, status.source)
38371
+ source: status.source === "built-in" ? status.source : path59.relative(cwd, status.source)
38007
38372
  })
38008
38373
  );
38009
38374
  },
@@ -38181,7 +38546,7 @@ var init_execution_context = __esm({
38181
38546
  });
38182
38547
 
38183
38548
  // src/sdk/launch.ts
38184
- import path58 from "node:path";
38549
+ import path60 from "node:path";
38185
38550
  import { spawnSync as spawnSync2 } from "node:child_process";
38186
38551
  async function startLaunch(options) {
38187
38552
  const homeDir = resolveHomeDir(options.homeDir);
@@ -38287,7 +38652,7 @@ async function runLaunchDaemon(options) {
38287
38652
  });
38288
38653
  }
38289
38654
  function resolveLaunchBaseDir(homeDir) {
38290
- return path58.join(homeDir, ".poe-code", "launch");
38655
+ return path60.join(homeDir, ".poe-code", "launch");
38291
38656
  }
38292
38657
  function resolveHomeDir(homeDir) {
38293
38658
  if (homeDir) {
@@ -38303,7 +38668,7 @@ function normalizeLaunchSpec(spec10, baseDir) {
38303
38668
  if (locator.scheme !== "local") {
38304
38669
  return spec10;
38305
38670
  }
38306
- const cwd = path58.isAbsolute(locator.path) ? locator.path : path58.resolve(baseDir, locator.path);
38671
+ const cwd = path60.isAbsolute(locator.path) ? locator.path : path60.resolve(baseDir, locator.path);
38307
38672
  return {
38308
38673
  ...spec10,
38309
38674
  cwd
@@ -39280,15 +39645,15 @@ var init_renderer3 = __esm({
39280
39645
  });
39281
39646
 
39282
39647
  // packages/cmdkit/src/cli.ts
39283
- import { access as access3, readFile as readFile19, writeFile as writeFile12 } from "node:fs/promises";
39284
- import path60 from "node:path";
39648
+ import { access as access3, readFile as readFile19, writeFile as writeFile13 } from "node:fs/promises";
39649
+ import path62 from "node:path";
39285
39650
  import { Command as CommanderCommand, CommanderError, InvalidArgumentError, Option } from "commander";
39286
39651
  function inferProgramName(argv) {
39287
39652
  const entrypoint = argv[1];
39288
39653
  if (typeof entrypoint !== "string" || entrypoint.length === 0) {
39289
39654
  return "cmdkit";
39290
39655
  }
39291
- const parsed = path60.parse(entrypoint);
39656
+ const parsed = path62.parse(entrypoint);
39292
39657
  return parsed.name.length > 0 ? parsed.name : "cmdkit";
39293
39658
  }
39294
39659
  function normalizeRoots2(roots, argv) {
@@ -39345,11 +39710,11 @@ function formatSegment2(segment, casing) {
39345
39710
  const separator = casing === "snake" ? "_" : "-";
39346
39711
  return splitWords2(segment).join(separator);
39347
39712
  }
39348
- function toOptionFlag(path82, casing) {
39349
- return `--${path82.map((segment) => formatSegment2(segment, casing)).join(".")}`;
39713
+ function toOptionFlag(path84, casing) {
39714
+ return `--${path84.map((segment) => formatSegment2(segment, casing)).join(".")}`;
39350
39715
  }
39351
- function toOptionAttribute(path82, casing) {
39352
- return path82.map((segment) => {
39716
+ function toOptionAttribute(path84, casing) {
39717
+ return path84.map((segment) => {
39353
39718
  const formatted = formatSegment2(segment, casing);
39354
39719
  if (casing === "snake") {
39355
39720
  return formatted;
@@ -39360,13 +39725,13 @@ function toOptionAttribute(path82, casing) {
39360
39725
  ).join("");
39361
39726
  }).join(".");
39362
39727
  }
39363
- function toDisplayPath3(path82) {
39364
- return path82.join(".");
39728
+ function toDisplayPath3(path84) {
39729
+ return path84.join(".");
39365
39730
  }
39366
- function collectFields(schema, casing, path82 = [], inheritedOptional = false) {
39731
+ function collectFields(schema, casing, path84 = [], inheritedOptional = false) {
39367
39732
  const fields = [];
39368
39733
  for (const [key, rawChildSchema] of Object.entries(schema.shape)) {
39369
- const nextPath = [...path82, key];
39734
+ const nextPath = [...path84, key];
39370
39735
  const optional = inheritedOptional || rawChildSchema.kind === "optional";
39371
39736
  const childSchema = unwrapOptional3(rawChildSchema);
39372
39737
  if (childSchema.kind === "object") {
@@ -39389,9 +39754,9 @@ function collectFields(schema, casing, path82 = [], inheritedOptional = false) {
39389
39754
  }
39390
39755
  return fields;
39391
39756
  }
39392
- function toCommanderOptionAttribute(path82, casing) {
39393
- const optionAttribute = toOptionAttribute(path82, casing);
39394
- const optionFlag = toOptionFlag(path82, casing);
39757
+ function toCommanderOptionAttribute(path84, casing) {
39758
+ const optionAttribute = toOptionAttribute(path84, casing);
39759
+ const optionFlag = toOptionFlag(path84, casing);
39395
39760
  if (!GLOBAL_LONG_OPTION_FLAGS.has(optionFlag)) {
39396
39761
  return optionAttribute;
39397
39762
  }
@@ -39850,10 +40215,10 @@ function addGlobalOptions(command) {
39850
40215
  throw new InvalidArgumentError('Invalid value for "--output". Expected one of: rich, md, markdown, json.');
39851
40216
  }).option("--verbose", "Print stack traces for unexpected errors.");
39852
40217
  }
39853
- function setNestedValue(target, path82, value) {
40218
+ function setNestedValue(target, path84, value) {
39854
40219
  let cursor = target;
39855
- for (let index = 0; index < path82.length - 1; index += 1) {
39856
- const segment = path82[index] ?? "";
40220
+ for (let index = 0; index < path84.length - 1; index += 1) {
40221
+ const segment = path84[index] ?? "";
39857
40222
  const existing = cursor[segment];
39858
40223
  if (typeof existing === "object" && existing !== null) {
39859
40224
  cursor = existing;
@@ -39863,7 +40228,7 @@ function setNestedValue(target, path82, value) {
39863
40228
  cursor[segment] = next;
39864
40229
  cursor = next;
39865
40230
  }
39866
- const leaf = path82[path82.length - 1];
40231
+ const leaf = path84[path84.length - 1];
39867
40232
  if (leaf !== void 0) {
39868
40233
  cursor[leaf] = value;
39869
40234
  }
@@ -39954,13 +40319,13 @@ async function withOutputFormat2(output, fn) {
39954
40319
  }
39955
40320
  function createFs2() {
39956
40321
  return {
39957
- readFile: async (path82, encoding = "utf8") => readFile19(path82, { encoding }),
39958
- writeFile: async (path82, contents) => {
39959
- await writeFile12(path82, contents);
40322
+ readFile: async (path84, encoding = "utf8") => readFile19(path84, { encoding }),
40323
+ writeFile: async (path84, contents) => {
40324
+ await writeFile13(path84, contents);
39960
40325
  },
39961
- exists: async (path82) => {
40326
+ exists: async (path84) => {
39962
40327
  try {
39963
- await access3(path82);
40328
+ await access3(path84);
39964
40329
  return true;
39965
40330
  } catch {
39966
40331
  return false;
@@ -39981,9 +40346,9 @@ function isPlainObject4(value) {
39981
40346
  function hasFieldValue(value) {
39982
40347
  return value !== void 0;
39983
40348
  }
39984
- function hasNestedField(fields, path82) {
40349
+ function hasNestedField(fields, path84) {
39985
40350
  return fields.some(
39986
- (field) => path82.length < field.path.length && path82.every((segment, index) => field.path[index] === segment)
40351
+ (field) => path84.length < field.path.length && path84.every((segment, index) => field.path[index] === segment)
39987
40352
  );
39988
40353
  }
39989
40354
  function describeExpectedPresetValue(schema) {
@@ -40070,9 +40435,9 @@ async function loadPresetValues(fields, presetPath) {
40070
40435
  }
40071
40436
  const fieldByPath = new Map(fields.map((field) => [field.displayPath, field]));
40072
40437
  const presetValues = {};
40073
- function visitObject(current, path82) {
40438
+ function visitObject(current, path84) {
40074
40439
  for (const [key, value] of Object.entries(current)) {
40075
- const nextPath = [...path82, key];
40440
+ const nextPath = [...path84, key];
40076
40441
  const displayPath2 = toDisplayPath3(nextPath);
40077
40442
  const field = fieldByPath.get(displayPath2);
40078
40443
  if (field !== void 0) {
@@ -40270,8 +40635,8 @@ function createFixtureService(definition) {
40270
40635
  );
40271
40636
  }
40272
40637
  function resolveFixturePath(commandPath) {
40273
- const parsed = path60.parse(commandPath);
40274
- return path60.join(parsed.dir, `${parsed.name}.fixture.json`);
40638
+ const parsed = path62.parse(commandPath);
40639
+ return path62.join(parsed.dir, `${parsed.name}.fixture.json`);
40275
40640
  }
40276
40641
  function selectFixtureScenario(scenarios, selector) {
40277
40642
  if (isNumericFixtureSelector(selector)) {
@@ -40990,7 +41355,7 @@ var init_agent2 = __esm({
40990
41355
  });
40991
41356
 
40992
41357
  // src/cli/commands/spawn.ts
40993
- import path61 from "node:path";
41358
+ import path63 from "node:path";
40994
41359
  import { Option as Option2 } from "commander";
40995
41360
  function registerSpawnCommand(program, container, options = {}) {
40996
41361
  const spawnServices = container.registry.list().filter((service) => typeof service.spawn === "function" || getSpawnConfig(service.name));
@@ -41271,7 +41636,7 @@ async function resolvePromptInput(input, fs17, baseDir) {
41271
41636
  if (rawPath.length === 0) {
41272
41637
  throw new ValidationError("prompt @<path> requires a file path after '@'");
41273
41638
  }
41274
- const filePath = path61.isAbsolute(rawPath) ? rawPath : path61.join(baseDir, rawPath);
41639
+ const filePath = path63.isAbsolute(rawPath) ? rawPath : path63.join(baseDir, rawPath);
41275
41640
  try {
41276
41641
  const contents = await fs17.readFile(filePath, "utf8");
41277
41642
  return contents.trim();
@@ -41292,7 +41657,7 @@ async function resolveMcpSpawnInput(input, fs17, baseDir) {
41292
41657
  if (rawPath.length === 0) {
41293
41658
  throw new ValidationError("--mcp-servers @<path> requires a file path after '@'");
41294
41659
  }
41295
- const filePath = path61.isAbsolute(rawPath) ? rawPath : path61.join(baseDir, rawPath);
41660
+ const filePath = path63.isAbsolute(rawPath) ? rawPath : path63.join(baseDir, rawPath);
41296
41661
  try {
41297
41662
  return await fs17.readFile(filePath, "utf8");
41298
41663
  } catch (error2) {
@@ -42132,9 +42497,9 @@ var init_command_not_found = __esm({
42132
42497
 
42133
42498
  // src/cli/commands/utils-symlink-ops.ts
42134
42499
  import { dirname as dirname4 } from "node:path";
42135
- async function tryLstat(fs17, path82) {
42500
+ async function tryLstat(fs17, path84) {
42136
42501
  try {
42137
- return await fs17.lstat(path82);
42502
+ return await fs17.lstat(path84);
42138
42503
  } catch (error2) {
42139
42504
  if (isNotFound(error2)) {
42140
42505
  return null;
@@ -42185,13 +42550,13 @@ async function applySymlinkOps(fs17, ops, opts) {
42185
42550
  }
42186
42551
  return { conflicts };
42187
42552
  }
42188
- async function isSymlinkPointingTo(fs17, path82, expectedTarget) {
42553
+ async function isSymlinkPointingTo(fs17, path84, expectedTarget) {
42189
42554
  try {
42190
- const stats = await fs17.lstat(path82);
42555
+ const stats = await fs17.lstat(path84);
42191
42556
  if (!stats.isSymbolicLink()) {
42192
42557
  return false;
42193
42558
  }
42194
- return await fs17.readlink(path82) === expectedTarget;
42559
+ return await fs17.readlink(path84) === expectedTarget;
42195
42560
  } catch (error2) {
42196
42561
  if (isNotFound(error2)) {
42197
42562
  return false;
@@ -42668,7 +43033,7 @@ var init_media_download = __esm({
42668
43033
  });
42669
43034
 
42670
43035
  // src/cli/commands/generate.ts
42671
- import path62 from "node:path";
43036
+ import path64 from "node:path";
42672
43037
  function registerGenerateCommand(program, container) {
42673
43038
  const generate2 = program.command("generate").alias("g").description("Generate content via Poe API.").option("--model <model>", `Model identifier (default: ${DEFAULT_TEXT_MODEL})`).option(
42674
43039
  "--param <key=value>",
@@ -42941,11 +43306,11 @@ function getDefaultMimeType(type) {
42941
43306
  return defaults[type];
42942
43307
  }
42943
43308
  function resolveOutputPath(filename, cwd) {
42944
- if (path62.isAbsolute(filename)) {
43309
+ if (path64.isAbsolute(filename)) {
42945
43310
  return { path: filename, label: filename };
42946
43311
  }
42947
43312
  return {
42948
- path: path62.join(cwd, filename),
43313
+ path: path64.join(cwd, filename),
42949
43314
  label: `./${filename}`
42950
43315
  };
42951
43316
  }
@@ -43546,10 +43911,10 @@ var init_shapes = __esm({
43546
43911
  });
43547
43912
 
43548
43913
  // packages/agent-mcp-config/src/apply.ts
43549
- import path63 from "node:path";
43914
+ import path65 from "node:path";
43550
43915
  import { parse as parseYaml3, stringify as stringifyYaml3 } from "yaml";
43551
43916
  function getConfigDirectory(configPath) {
43552
- return path63.dirname(configPath);
43917
+ return path65.dirname(configPath);
43553
43918
  }
43554
43919
  function isConfigObject6(value) {
43555
43920
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
@@ -43569,9 +43934,9 @@ function expandHomePath(configPath, homeDir) {
43569
43934
  return homeDir;
43570
43935
  }
43571
43936
  if (configPath.startsWith("~/")) {
43572
- return path63.join(homeDir, configPath.slice(2));
43937
+ return path65.join(homeDir, configPath.slice(2));
43573
43938
  }
43574
- return path63.join(homeDir, configPath.slice(1));
43939
+ return path65.join(homeDir, configPath.slice(1));
43575
43940
  }
43576
43941
  function parseYamlDocument2(content) {
43577
43942
  if (content.trim() === "") {
@@ -43604,7 +43969,7 @@ async function writeYamlConfig(configPath, document, options) {
43604
43969
  return;
43605
43970
  }
43606
43971
  const absolutePath = expandHomePath(configPath, options.homeDir);
43607
- const configDir = path63.dirname(absolutePath);
43972
+ const configDir = path65.dirname(absolutePath);
43608
43973
  await options.fs.mkdir(configDir, { recursive: true });
43609
43974
  await options.fs.writeFile(absolutePath, serializeYamlDocument(document), {
43610
43975
  encoding: "utf8"
@@ -44875,8 +45240,8 @@ var init_dashboard_loop_shared = __esm({
44875
45240
  });
44876
45241
 
44877
45242
  // src/cli/commands/pipeline.ts
44878
- import path64 from "node:path";
44879
- import { readFile as readFile20, stat as stat17 } from "node:fs/promises";
45243
+ import path66 from "node:path";
45244
+ import { readFile as readFile20, stat as stat18 } from "node:fs/promises";
44880
45245
  import { fileURLToPath as fileURLToPath9 } from "node:url";
44881
45246
  async function resolvePipelineCommandConfig(container) {
44882
45247
  const configDoc = await readMergedDocument(
@@ -44924,18 +45289,19 @@ function resolveMaxRuns(value) {
44924
45289
  return parsed;
44925
45290
  }
44926
45291
  function resolvePipelineInitSourcePath(container, sourcePath) {
44927
- const absolutePath = sourcePath.startsWith("~/") ? path64.join(container.env.homeDir, sourcePath.slice(2)) : path64.isAbsolute(sourcePath) ? sourcePath : path64.resolve(container.env.cwd, sourcePath);
45292
+ const absolutePath = sourcePath.startsWith("~/") ? path66.join(container.env.homeDir, sourcePath.slice(2)) : path66.isAbsolute(sourcePath) ? sourcePath : path66.resolve(container.env.cwd, sourcePath);
44928
45293
  return {
44929
45294
  absolutePath,
44930
45295
  relativePath: sourcePath,
44931
- title: path64.basename(sourcePath, path64.extname(sourcePath))
45296
+ title: path66.basename(sourcePath, path66.extname(sourcePath))
44932
45297
  };
44933
45298
  }
44934
45299
  function formatRunSummary(result) {
44935
45300
  const metrics = result.metrics;
44936
45301
  return [
44937
45302
  `Runs: ${result.runsCompleted}`,
44938
- `tasksCompleted: ${metrics.tasksCompleted}, tasksFailed: ${metrics.tasksFailed}, stepsCompleted: ${metrics.stepsCompleted}`,
45303
+ `Tasks: ${metrics.tasksCompleted} completed, ${metrics.tasksFailed} failed`,
45304
+ `Steps: ${metrics.stepsCompleted} completed`,
44939
45305
  `Total tokens: ${metrics.totalInputTokens} input, ${metrics.totalOutputTokens} output, ${metrics.totalCachedTokens} cached`,
44940
45306
  `Duration: ${formatDashboardDuration(result.totalDurationMs)}`
44941
45307
  ].join("\n ");
@@ -44976,6 +45342,14 @@ function formatTaskCompleteMessage(progress) {
44976
45342
  if (progress.phase) {
44977
45343
  return `${progress.taskTitle} ${status} in ${duration}${usage}`;
44978
45344
  }
45345
+ if (progress.stepName) {
45346
+ if (progress.success && !progress.taskCompleted) {
45347
+ return `Task ${progress.taskId} (${progress.stepName}) step done in ${duration}${usage}`;
45348
+ }
45349
+ if (!progress.success) {
45350
+ return `Task ${progress.taskId} (${progress.stepName}) failed in ${duration}${usage}`;
45351
+ }
45352
+ }
44979
45353
  return `Task ${progress.taskId} ${status} in ${duration}${usage}`;
44980
45354
  }
44981
45355
  function formatDashboardCurrentAction(progress) {
@@ -45059,68 +45433,63 @@ async function streamAcpEventsToDashboard(options) {
45059
45433
  }
45060
45434
  function createPipelineDashboardRunAgent(options) {
45061
45435
  return async (input) => {
45062
- for (let attempt = 1; attempt <= 3; attempt += 1) {
45063
- const toolBuffer = createDashboardLineBuffer((line) => {
45064
- options.appendOutput("tool", `[${options.activeStage()}] ${line}`);
45065
- });
45066
- const errorBuffer = createDashboardLineBuffer((line) => {
45067
- options.appendOutput("error", `[${options.activeStage()}] ${line}`);
45068
- });
45069
- let sawStdout = false;
45070
- let sawStderr = false;
45071
- try {
45072
- const { events, result } = spawn7(input.agent, {
45073
- prompt: input.prompt,
45074
- cwd: input.cwd,
45075
- logDir: input.logDir,
45076
- model: input.model,
45077
- mode: input.mode,
45078
- ...input.mcpServers ? { mcpServers: input.mcpServers } : {},
45079
- ...input.signal ? { signal: input.signal } : {},
45080
- tee: {
45081
- stdout: {
45082
- write(chunk) {
45083
- sawStdout = true;
45084
- toolBuffer.push(chunk);
45085
- }
45086
- },
45087
- stderr: {
45088
- write(chunk) {
45089
- sawStderr = true;
45090
- errorBuffer.push(chunk);
45091
- }
45436
+ const toolBuffer = createDashboardLineBuffer((line) => {
45437
+ options.appendOutput("tool", `[${options.activeStage()}] ${line}`);
45438
+ });
45439
+ const errorBuffer = createDashboardLineBuffer((line) => {
45440
+ options.appendOutput("error", `[${options.activeStage()}] ${line}`);
45441
+ });
45442
+ let sawStdout = false;
45443
+ let sawStderr = false;
45444
+ try {
45445
+ const { events, result } = spawn7(input.agent, {
45446
+ prompt: input.prompt,
45447
+ cwd: input.cwd,
45448
+ logDir: input.logDir,
45449
+ model: input.model,
45450
+ mode: input.mode,
45451
+ ...input.mcpServers ? { mcpServers: input.mcpServers } : {},
45452
+ ...input.signal ? { signal: input.signal } : {},
45453
+ tee: {
45454
+ stdout: {
45455
+ write(chunk) {
45456
+ sawStdout = true;
45457
+ toolBuffer.push(chunk);
45092
45458
  }
45093
45459
  },
45094
- activityTimeoutMs: 10 * 60 * 1e3
45095
- });
45096
- const eventStream = streamAcpEventsToDashboard({
45097
- events,
45098
- onToolOutput(chunk) {
45099
- toolBuffer.push(chunk);
45100
- },
45101
- onErrorOutput(chunk) {
45102
- errorBuffer.push(chunk);
45460
+ stderr: {
45461
+ write(chunk) {
45462
+ sawStderr = true;
45463
+ errorBuffer.push(chunk);
45464
+ }
45103
45465
  }
45104
- });
45105
- const [spawnResult, sawEvents] = await Promise.all([result, eventStream]);
45106
- if (!sawEvents && !sawStdout && spawnResult.stdout.length > 0) {
45107
- toolBuffer.push(spawnResult.stdout);
45108
- }
45109
- if (!sawStderr && spawnResult.stderr.length > 0) {
45110
- errorBuffer.push(spawnResult.stderr);
45111
- }
45112
- toolBuffer.flush();
45113
- errorBuffer.flush();
45114
- return spawnResult;
45115
- } catch (error2) {
45116
- toolBuffer.flush();
45117
- errorBuffer.flush();
45118
- if (!isActivityTimeoutError(error2) || attempt === 3) {
45119
- throw error2;
45466
+ },
45467
+ activityTimeoutMs: 10 * 60 * 1e3
45468
+ });
45469
+ const eventStream = streamAcpEventsToDashboard({
45470
+ events,
45471
+ onToolOutput(chunk) {
45472
+ toolBuffer.push(chunk);
45473
+ },
45474
+ onErrorOutput(chunk) {
45475
+ errorBuffer.push(chunk);
45120
45476
  }
45477
+ });
45478
+ const [spawnResult, sawEvents] = await Promise.all([result, eventStream]);
45479
+ if (!sawEvents && !sawStdout && spawnResult.stdout.length > 0) {
45480
+ toolBuffer.push(spawnResult.stdout);
45481
+ }
45482
+ if (!sawStderr && spawnResult.stderr.length > 0) {
45483
+ errorBuffer.push(spawnResult.stderr);
45121
45484
  }
45485
+ toolBuffer.flush();
45486
+ errorBuffer.flush();
45487
+ return spawnResult;
45488
+ } catch (error2) {
45489
+ toolBuffer.flush();
45490
+ errorBuffer.flush();
45491
+ throw error2;
45122
45492
  }
45123
- throw new Error("Unreachable");
45124
45493
  };
45125
45494
  }
45126
45495
  function dashboardStatusForResult(result) {
@@ -45145,15 +45514,18 @@ async function runPipelineWithDashboard(options) {
45145
45514
  let currentAction;
45146
45515
  let currentStage = "pipeline";
45147
45516
  let status = "running";
45517
+ let waitingForLock = false;
45148
45518
  const syncStats = () => {
45149
- dashboard.updateStats({
45519
+ const stats = {
45150
45520
  status,
45151
45521
  iterations,
45522
+ iterationsLabel: "Tasks",
45152
45523
  tokensIn,
45153
45524
  tokensOut,
45154
45525
  elapsedMs: Math.max(0, Date.now() - startedAt),
45155
45526
  ...currentAction ? { currentAction } : {}
45156
- });
45527
+ };
45528
+ dashboard.updateStats(stats);
45157
45529
  };
45158
45530
  const appendOutput = (kind, message2) => {
45159
45531
  dashboard.appendOutput({
@@ -45196,6 +45568,17 @@ async function runPipelineWithDashboard(options) {
45196
45568
  onPlanReloadError(error2) {
45197
45569
  appendOutput("error", `Plan reload failed, using last good state: ${error2.message}`);
45198
45570
  },
45571
+ onLockStatusChange(lockStatus) {
45572
+ appendOutput("status", lockStatus.message);
45573
+ if (lockStatus.state === "waiting") {
45574
+ waitingForLock = true;
45575
+ currentAction = lockStatus.message;
45576
+ } else if (waitingForLock) {
45577
+ waitingForLock = false;
45578
+ currentAction = void 0;
45579
+ }
45580
+ syncStats();
45581
+ },
45199
45582
  onPlanResolved(summary) {
45200
45583
  appendOutput(
45201
45584
  "info",
@@ -45217,7 +45600,9 @@ async function runPipelineWithDashboard(options) {
45217
45600
  syncStats();
45218
45601
  },
45219
45602
  onTaskComplete(progress) {
45220
- iterations += 1;
45603
+ if (progress.taskCompleted) {
45604
+ iterations += 1;
45605
+ }
45221
45606
  if (progress.usage) {
45222
45607
  tokensIn += progress.usage.inputTokens;
45223
45608
  tokensOut += progress.usage.outputTokens;
@@ -45243,13 +45628,18 @@ async function runPipelineWithDashboard(options) {
45243
45628
  }
45244
45629
  }
45245
45630
  function resolvePipelinePaths(scope, cwd, homeDir) {
45246
- const rootPath = scope === "global" ? path64.join(homeDir, ".poe-code", "pipeline") : path64.join(cwd, ".poe-code", "pipeline");
45631
+ const rootPath = scope === "global" ? path66.join(homeDir, ".poe-code", "pipeline") : path66.join(cwd, ".poe-code", "pipeline");
45247
45632
  const displayRoot = scope === "global" ? "~/.poe-code/pipeline" : ".poe-code/pipeline";
45633
+ const stepsDirectoryPath = path66.join(rootPath, "steps");
45248
45634
  return {
45249
- plansPath: path64.join(rootPath, "plans"),
45250
- stepsPath: path64.join(rootPath, "steps.yaml"),
45635
+ plansPath: path66.join(rootPath, "plans"),
45636
+ stepsDirectoryPath,
45637
+ defaultStepsPath: path66.join(stepsDirectoryPath, "default.yaml"),
45638
+ legacyStepsPath: path66.join(rootPath, "steps.yaml"),
45251
45639
  displayPlansPath: `${displayRoot}/plans`,
45252
- displayStepsPath: `${displayRoot}/steps.yaml`
45640
+ displayStepsDirectoryPath: `${displayRoot}/steps`,
45641
+ displayDefaultStepsPath: `${displayRoot}/steps/default.yaml`,
45642
+ displayLegacyStepsPath: `${displayRoot}/steps.yaml`
45253
45643
  };
45254
45644
  }
45255
45645
  async function loadPipelineTemplates() {
@@ -45258,16 +45648,16 @@ async function loadPipelineTemplates() {
45258
45648
  }
45259
45649
  const packageRoot = await findPackageRoot3(fileURLToPath9(import.meta.url));
45260
45650
  const templateRoots = [
45261
- path64.join(packageRoot, "src", "templates", "pipeline"),
45262
- path64.join(packageRoot, "dist", "templates", "pipeline")
45651
+ path66.join(packageRoot, "src", "templates", "pipeline"),
45652
+ path66.join(packageRoot, "dist", "templates", "pipeline")
45263
45653
  ];
45264
45654
  for (const templateRoot of templateRoots) {
45265
45655
  if (!await pathExistsOnDisk(templateRoot)) {
45266
45656
  continue;
45267
45657
  }
45268
45658
  const [skillPlan, steps] = await Promise.all([
45269
- readFile20(path64.join(templateRoot, "SKILL_plan.md"), "utf8"),
45270
- readFile20(path64.join(templateRoot, "steps.yaml.mustache"), "utf8")
45659
+ readFile20(path66.join(templateRoot, "SKILL_plan.md"), "utf8"),
45660
+ readFile20(path66.join(templateRoot, "steps.yaml.mustache"), "utf8")
45271
45661
  ]);
45272
45662
  pipelineTemplatesCache = { skillPlan, steps };
45273
45663
  return pipelineTemplatesCache;
@@ -45276,7 +45666,7 @@ async function loadPipelineTemplates() {
45276
45666
  }
45277
45667
  async function pathExistsOnDisk(targetPath) {
45278
45668
  try {
45279
- await stat17(targetPath);
45669
+ await stat18(targetPath);
45280
45670
  return true;
45281
45671
  } catch (error2) {
45282
45672
  if (error2 && typeof error2 === "object" && "code" in error2 && error2.code === "ENOENT") {
@@ -45286,12 +45676,12 @@ async function pathExistsOnDisk(targetPath) {
45286
45676
  }
45287
45677
  }
45288
45678
  async function findPackageRoot3(entryFilePath) {
45289
- let currentPath = path64.dirname(entryFilePath);
45679
+ let currentPath = path66.dirname(entryFilePath);
45290
45680
  while (true) {
45291
- if (await pathExistsOnDisk(path64.join(currentPath, "package.json"))) {
45681
+ if (await pathExistsOnDisk(path66.join(currentPath, "package.json"))) {
45292
45682
  return currentPath;
45293
45683
  }
45294
- const parentPath = path64.dirname(currentPath);
45684
+ const parentPath = path66.dirname(currentPath);
45295
45685
  if (parentPath === currentPath) {
45296
45686
  throw new Error("Unable to locate package root for Pipeline templates.");
45297
45687
  }
@@ -45407,6 +45797,9 @@ function registerPipelineCommand(program, container) {
45407
45797
  `Plan reload failed, using last good state: ${error2.message}`
45408
45798
  );
45409
45799
  },
45800
+ onLockStatusChange(lockStatus) {
45801
+ resources.logger.info(lockStatus.message);
45802
+ },
45410
45803
  onPlanResolved(summary2) {
45411
45804
  resources.logger.resolved(
45412
45805
  "Config",
@@ -45463,11 +45856,7 @@ function registerPipelineCommand(program, container) {
45463
45856
  });
45464
45857
  pipeline.command("init").description("Initialize pipeline plans from source Markdown docs.").argument("[question]", "Optional user question to forward to the plan generator").option("--agent <name>", "Agent to generate the plan with").option("--model <model>", "Model override passed to the agent").option("--source <path>", "Single source Markdown doc to convert").option("--sources <paths...>", "Multiple source Markdown docs to convert").action(async function(question) {
45465
45858
  const flags = resolveCommandFlags(program);
45466
- const resources = createExecutionResources(
45467
- container,
45468
- flags,
45469
- "pipeline:init"
45470
- );
45859
+ const resources = createExecutionResources(container, flags, "pipeline:init");
45471
45860
  const options = this.opts();
45472
45861
  resources.logger.intro("pipeline init");
45473
45862
  try {
@@ -45499,7 +45888,6 @@ function registerPipelineCommand(program, container) {
45499
45888
  }
45500
45889
  agent2 = resolvePipelineAgent(selected);
45501
45890
  }
45502
- const commandConfig = await resolvePipelineCommandConfig(container);
45503
45891
  if (options.source && sourcePaths && sourcePaths.length > 0) {
45504
45892
  throw new ValidationError("Use either --source or --sources, not both.");
45505
45893
  }
@@ -45532,7 +45920,9 @@ function registerPipelineCommand(program, container) {
45532
45920
  return;
45533
45921
  }
45534
45922
  const selectedSourcePaths = Array.isArray(selected) ? new Set(selected) : /* @__PURE__ */ new Set();
45535
- sources = discoveredSources.filter((source) => selectedSourcePaths.has(source.relativePath));
45923
+ sources = discoveredSources.filter(
45924
+ (source) => selectedSourcePaths.has(source.relativePath)
45925
+ );
45536
45926
  if (sources.length === 0) {
45537
45927
  return;
45538
45928
  }
@@ -45541,7 +45931,6 @@ function registerPipelineCommand(program, container) {
45541
45931
  agent: agent2,
45542
45932
  cwd: container.env.cwd,
45543
45933
  homeDir: container.env.homeDir,
45544
- ...commandConfig.planDirectory ? { planDirectory: commandConfig.planDirectory } : {},
45545
45934
  ...options.model ? { model: options.model } : {},
45546
45935
  ...resolvedQuestion ? { question: resolvedQuestion } : {},
45547
45936
  sources,
@@ -45581,35 +45970,46 @@ function registerPipelineCommand(program, container) {
45581
45970
  container.env.homeDir
45582
45971
  );
45583
45972
  const content = await container.fs.readFile(absolutePath, "utf8");
45973
+ const draftPlan = parsePlan(content);
45584
45974
  const steps = await loadResolvedSteps({
45585
45975
  cwd: container.env.cwd,
45586
45976
  homeDir: container.env.homeDir,
45587
- fs: container.fs
45977
+ fs: container.fs,
45978
+ name: draftPlan.extends,
45979
+ stepOverrides: draftPlan.stepOverrides
45588
45980
  });
45589
- const hasSteps = Object.keys(steps.steps).length > 0;
45590
- const plan = parsePlan(content, hasSteps ? { availableSteps: steps.steps } : {});
45981
+ const plan = parsePlan(content, { availableSteps: steps.steps });
45591
45982
  const total = plan.tasks.length;
45592
45983
  const done = plan.tasks.filter((t) => {
45593
45984
  if (typeof t.status === "string") return t.status === "done";
45594
45985
  return Object.values(t.status).every((s) => s === "done");
45595
45986
  }).length;
45987
+ const readFile34 = container.fs.readFile.bind(container.fs);
45988
+ const resolvedVars = await resolvePipelineVars(
45989
+ plan.vars ?? {},
45990
+ container.env.cwd,
45991
+ readFile34
45992
+ );
45993
+ const resolvedSetup = plan.setup === null ? void 0 : plan.setup ?? steps.setup;
45994
+ const resolvedTeardown = plan.teardown === null ? void 0 : plan.teardown ?? steps.teardown;
45995
+ validateResolvedPromptVars({
45996
+ plan,
45997
+ steps: steps.steps,
45998
+ planPath: file,
45999
+ vars: resolvedVars,
46000
+ setup: resolvedSetup,
46001
+ teardown: resolvedTeardown
46002
+ });
45596
46003
  resources.logger.resolved("Plan", file);
45597
46004
  resources.logger.resolved("Tasks", `${total} tasks (${done} done)`);
45598
- if (hasSteps) {
46005
+ if (Object.keys(steps.steps).length > 0) {
45599
46006
  resources.logger.resolved("Steps", Object.keys(steps.steps).join(", "));
45600
46007
  }
45601
46008
  resources.logger.success("Plan is valid.");
45602
46009
  const opts = this.opts();
45603
46010
  if (opts.preview) {
45604
- const readFile34 = container.fs.readFile.bind(container.fs);
45605
- const resolvedVars = {};
45606
- for (const [key, value] of Object.entries(plan.vars ?? {})) {
45607
- resolvedVars[key] = await resolveFileIncludes(value, container.env.cwd, readFile34);
45608
- }
45609
- const resolvedSetup = plan.setup === null ? void 0 : plan.setup ?? steps.setup;
45610
- const resolvedTeardown = plan.teardown === null ? void 0 : plan.teardown ?? steps.teardown;
45611
46011
  if (resolvedSetup) {
45612
- const raw = Object.keys(resolvedVars).length > 0 ? interpolate(resolvedSetup.prompt, resolvedVars) : resolvedSetup.prompt;
46012
+ const raw = interpolatePipelineVars(resolvedSetup.prompt, resolvedVars, "setup");
45613
46013
  const expanded = await resolveFileIncludes(raw, container.env.cwd, readFile34);
45614
46014
  resources.logger.resolved("setup", expanded);
45615
46015
  }
@@ -45643,7 +46043,7 @@ function registerPipelineCommand(program, container) {
45643
46043
  }
45644
46044
  }
45645
46045
  if (resolvedTeardown) {
45646
- const raw = Object.keys(resolvedVars).length > 0 ? interpolate(resolvedTeardown.prompt, resolvedVars) : resolvedTeardown.prompt;
46046
+ const raw = interpolatePipelineVars(resolvedTeardown.prompt, resolvedVars, "teardown");
45647
46047
  const expanded = await resolveFileIncludes(raw, container.env.cwd, readFile34);
45648
46048
  resources.logger.resolved("teardown", expanded);
45649
46049
  }
@@ -45662,7 +46062,7 @@ function registerPipelineCommand(program, container) {
45662
46062
  process.stdout.write(`${resolvedPath}
45663
46063
  `);
45664
46064
  });
45665
- pipeline.command("install").description("Install the Pipeline /plan skill and scaffold pipeline files.").option("--agent <name>", "Agent to install the Pipeline skill for").option("--local", "Install project-local skill and pipeline files").option("--global", "Install user-global skill and pipeline files").option("--force", "Overwrite an existing steps.yaml scaffold").action(async function() {
46065
+ pipeline.command("install").description("Install the Pipeline /plan skill and scaffold pipeline files.").option("--agent <name>", "Agent to install the Pipeline skill for").option("--local", "Install project-local skill and pipeline files").option("--global", "Install user-global skill and pipeline files").option("--force", "Overwrite an existing default step config scaffold").action(async function() {
45666
46066
  const flags = resolveCommandFlags(program);
45667
46067
  const resources = createExecutionResources(container, flags, "pipeline:install");
45668
46068
  const options = this.opts();
@@ -45747,22 +46147,39 @@ function registerPipelineCommand(program, container) {
45747
46147
  resources.logger.info(`Create: ${pipelinePaths.displayPlansPath}`);
45748
46148
  }
45749
46149
  }
45750
- const stepsExists = await pathExists5(container.fs, pipelinePaths.stepsPath);
46150
+ const legacyStepsExists = await pathExists5(container.fs, pipelinePaths.legacyStepsPath);
46151
+ let stepsExists = await pathExists5(container.fs, pipelinePaths.defaultStepsPath);
46152
+ if (legacyStepsExists && !stepsExists) {
46153
+ if (flags.dryRun) {
46154
+ resources.logger.dryRun(
46155
+ `Would rename: ${pipelinePaths.displayLegacyStepsPath} -> ${pipelinePaths.displayDefaultStepsPath}`
46156
+ );
46157
+ } else {
46158
+ await container.fs.mkdir(pipelinePaths.stepsDirectoryPath, {
46159
+ recursive: true
46160
+ });
46161
+ await container.fs.rename(pipelinePaths.legacyStepsPath, pipelinePaths.defaultStepsPath);
46162
+ resources.logger.info(
46163
+ `Rename: ${pipelinePaths.displayLegacyStepsPath} -> ${pipelinePaths.displayDefaultStepsPath}`
46164
+ );
46165
+ }
46166
+ stepsExists = true;
46167
+ }
45751
46168
  if (stepsExists && !options.force) {
45752
- resources.logger.info(`Skip: ${pipelinePaths.displayStepsPath} (already exists)`);
46169
+ resources.logger.info(`Skip: ${pipelinePaths.displayDefaultStepsPath} (already exists)`);
45753
46170
  } else if (flags.dryRun) {
45754
46171
  resources.logger.dryRun(
45755
- `Would ${stepsExists ? "overwrite" : "create"}: ${pipelinePaths.displayStepsPath}`
46172
+ `Would ${stepsExists ? "overwrite" : "create"}: ${pipelinePaths.displayDefaultStepsPath}`
45756
46173
  );
45757
46174
  } else {
45758
- await container.fs.mkdir(path64.dirname(pipelinePaths.stepsPath), {
46175
+ await container.fs.mkdir(path66.dirname(pipelinePaths.defaultStepsPath), {
45759
46176
  recursive: true
45760
46177
  });
45761
- await container.fs.writeFile(pipelinePaths.stepsPath, templates.steps, {
46178
+ await container.fs.writeFile(pipelinePaths.defaultStepsPath, templates.steps, {
45762
46179
  encoding: "utf8"
45763
46180
  });
45764
46181
  resources.logger.info(
45765
- `${stepsExists ? "Overwrite" : "Create"}: ${pipelinePaths.displayStepsPath}`
46182
+ `${stepsExists ? "Overwrite" : "Create"}: ${pipelinePaths.displayDefaultStepsPath}`
45766
46183
  );
45767
46184
  }
45768
46185
  resources.context.complete({
@@ -45798,7 +46215,7 @@ var init_pipeline3 = __esm({
45798
46215
  });
45799
46216
 
45800
46217
  // src/cli/commands/ralph.ts
45801
- import path65 from "node:path";
46218
+ import path67 from "node:path";
45802
46219
  function formatRalphAgentSummary(agent2) {
45803
46220
  return Array.isArray(agent2) ? agent2.join(", ") : agent2;
45804
46221
  }
@@ -45979,9 +46396,9 @@ function normalizeConfiguredIterations(value) {
45979
46396
  }
45980
46397
  function resolveAbsoluteDocPath(container, docPath) {
45981
46398
  if (docPath.startsWith("~/")) {
45982
- return path65.join(container.env.homeDir, docPath.slice(2));
46399
+ return path67.join(container.env.homeDir, docPath.slice(2));
45983
46400
  }
45984
- return path65.isAbsolute(docPath) ? docPath : path65.resolve(container.env.cwd, docPath);
46401
+ return path67.isAbsolute(docPath) ? docPath : path67.resolve(container.env.cwd, docPath);
45985
46402
  }
45986
46403
  async function resolveRalphCommandConfig(container) {
45987
46404
  const configDoc = await readMergedDocument(
@@ -46360,11 +46777,11 @@ var init_ralph3 = __esm({
46360
46777
  });
46361
46778
 
46362
46779
  // src/cli/commands/experiment.ts
46363
- import path66 from "node:path";
46364
- import { readFile as readFile21, stat as stat18 } from "node:fs/promises";
46780
+ import path68 from "node:path";
46781
+ import { readFile as readFile21, stat as stat19 } from "node:fs/promises";
46365
46782
  import { fileURLToPath as fileURLToPath10 } from "node:url";
46366
46783
  function resolveExperimentPaths(scope, cwd, homeDir) {
46367
- const rootPath = scope === "global" ? path66.join(homeDir, ".poe-code", "experiments") : path66.join(cwd, ".poe-code", "experiments");
46784
+ const rootPath = scope === "global" ? path68.join(homeDir, ".poe-code", "experiments") : path68.join(cwd, ".poe-code", "experiments");
46368
46785
  const displayRoot = scope === "global" ? "~/.poe-code/experiments" : ".poe-code/experiments";
46369
46786
  return {
46370
46787
  experimentsPath: rootPath,
@@ -46373,7 +46790,7 @@ function resolveExperimentPaths(scope, cwd, homeDir) {
46373
46790
  }
46374
46791
  async function pathExistsOnDisk2(targetPath) {
46375
46792
  try {
46376
- await stat18(targetPath);
46793
+ await stat19(targetPath);
46377
46794
  return true;
46378
46795
  } catch (error2) {
46379
46796
  if (error2 && typeof error2 === "object" && "code" in error2 && error2.code === "ENOENT") {
@@ -46383,12 +46800,12 @@ async function pathExistsOnDisk2(targetPath) {
46383
46800
  }
46384
46801
  }
46385
46802
  async function findPackageRoot4(entryFilePath) {
46386
- let currentPath = path66.dirname(entryFilePath);
46803
+ let currentPath = path68.dirname(entryFilePath);
46387
46804
  while (true) {
46388
- if (await pathExistsOnDisk2(path66.join(currentPath, "package.json"))) {
46805
+ if (await pathExistsOnDisk2(path68.join(currentPath, "package.json"))) {
46389
46806
  return currentPath;
46390
46807
  }
46391
- const parentPath = path66.dirname(currentPath);
46808
+ const parentPath = path68.dirname(currentPath);
46392
46809
  if (parentPath === currentPath) {
46393
46810
  throw new Error("Unable to locate package root for Experiment templates.");
46394
46811
  }
@@ -46401,16 +46818,16 @@ async function loadExperimentTemplates() {
46401
46818
  }
46402
46819
  const packageRoot = await findPackageRoot4(fileURLToPath10(import.meta.url));
46403
46820
  const templateRoots = [
46404
- path66.join(packageRoot, "src", "templates", "experiment"),
46405
- path66.join(packageRoot, "dist", "templates", "experiment")
46821
+ path68.join(packageRoot, "src", "templates", "experiment"),
46822
+ path68.join(packageRoot, "dist", "templates", "experiment")
46406
46823
  ];
46407
46824
  for (const templateRoot of templateRoots) {
46408
46825
  if (!await pathExistsOnDisk2(templateRoot)) {
46409
46826
  continue;
46410
46827
  }
46411
46828
  const [skillPlan, runYaml] = await Promise.all([
46412
- readFile21(path66.join(templateRoot, "SKILL_experiment.md"), "utf8"),
46413
- readFile21(path66.join(templateRoot, "run.yaml.mustache"), "utf8")
46829
+ readFile21(path68.join(templateRoot, "SKILL_experiment.md"), "utf8"),
46830
+ readFile21(path68.join(templateRoot, "run.yaml.mustache"), "utf8")
46414
46831
  ]);
46415
46832
  experimentTemplatesCache = { skillPlan, runYaml };
46416
46833
  return experimentTemplatesCache;
@@ -47095,8 +47512,8 @@ function registerExperimentCommand(program, container) {
47095
47512
  resources.logger.info(`Create: ${experimentPaths.displayExperimentsPath}`);
47096
47513
  }
47097
47514
  }
47098
- const runYamlPath = path66.join(experimentPaths.experimentsPath, "run.yaml");
47099
- const runYamlDisplayPath = path66.join(experimentPaths.displayExperimentsPath, "run.yaml");
47515
+ const runYamlPath = path68.join(experimentPaths.experimentsPath, "run.yaml");
47516
+ const runYamlDisplayPath = path68.join(experimentPaths.displayExperimentsPath, "run.yaml");
47100
47517
  if (!await pathExists6(container.fs, runYamlPath)) {
47101
47518
  if (flags.dryRun) {
47102
47519
  resources.logger.dryRun(`Would create: ${runYamlDisplayPath}`);
@@ -47559,9 +47976,9 @@ var init_launch2 = __esm({
47559
47976
  });
47560
47977
 
47561
47978
  // packages/memory/src/paths.ts
47562
- import path67 from "node:path";
47979
+ import path69 from "node:path";
47563
47980
  function resolveMemoryRoot(cwd) {
47564
- return path67.resolve(cwd, ".poe-code", "memory");
47981
+ return path69.resolve(cwd, ".poe-code", "memory");
47565
47982
  }
47566
47983
  function assertSafeRelPath(input) {
47567
47984
  const trimmed = input.trim();
@@ -47569,10 +47986,10 @@ function assertSafeRelPath(input) {
47569
47986
  throw new MemoryPathError("Expected a non-empty relative path.");
47570
47987
  }
47571
47988
  const slashNormalized = trimmed.replaceAll("\\", "/");
47572
- if (path67.posix.isAbsolute(slashNormalized) || path67.win32.isAbsolute(slashNormalized)) {
47989
+ if (path69.posix.isAbsolute(slashNormalized) || path69.win32.isAbsolute(slashNormalized)) {
47573
47990
  throw new MemoryPathError(`Expected a relative path, received absolute path "${input}".`);
47574
47991
  }
47575
- const normalized = path67.posix.normalize(slashNormalized);
47992
+ const normalized = path69.posix.normalize(slashNormalized);
47576
47993
  if (normalized === "." || normalized.length === 0) {
47577
47994
  throw new MemoryPathError("Expected a relative path to a file or directory.");
47578
47995
  }
@@ -47602,11 +48019,11 @@ var init_paths2 = __esm({
47602
48019
 
47603
48020
  // packages/memory/src/init.ts
47604
48021
  import * as fs4 from "node:fs/promises";
47605
- import path68 from "node:path";
48022
+ import path70 from "node:path";
47606
48023
  async function initMemory(root) {
47607
- await fs4.mkdir(path68.join(root, MEMORY_PAGES_DIR_RELPATH), { recursive: true });
47608
- await writeFileIfMissing(path68.join(root, MEMORY_INDEX_RELPATH), "# Memory index\n");
47609
- await writeFileIfMissing(path68.join(root, MEMORY_LOG_RELPATH), "");
48024
+ await fs4.mkdir(path70.join(root, MEMORY_PAGES_DIR_RELPATH), { recursive: true });
48025
+ await writeFileIfMissing(path70.join(root, MEMORY_INDEX_RELPATH), "# Memory index\n");
48026
+ await writeFileIfMissing(path70.join(root, MEMORY_LOG_RELPATH), "");
47610
48027
  }
47611
48028
  async function writeFileIfMissing(filePath, content) {
47612
48029
  try {
@@ -47628,7 +48045,7 @@ var init_init = __esm({
47628
48045
  });
47629
48046
 
47630
48047
  // packages/memory/src/frontmatter.ts
47631
- import { parse as parse11, stringify as stringify5 } from "yaml";
48048
+ import { parse as parse11, stringify as stringify4 } from "yaml";
47632
48049
  function parseFrontmatter4(markdown) {
47633
48050
  const content = markdown.startsWith("\uFEFF") ? markdown.slice(1) : markdown;
47634
48051
  const openingLineBreak = readOpeningLineBreak5(content);
@@ -47790,10 +48207,10 @@ function parseSources(value) {
47790
48207
  if (!isRecord20(item)) {
47791
48208
  throw new Error('Invalid "sources" frontmatter. Expected each source to be a string or object.');
47792
48209
  }
47793
- const path82 = readRequiredString(item.path, "sources[].path");
48210
+ const path84 = readRequiredString(item.path, "sources[].path");
47794
48211
  const startLine = readOptionalPositiveInteger(item.startLine, "sources[].startLine");
47795
48212
  const endLine = readOptionalPositiveInteger(item.endLine, "sources[].endLine");
47796
- return parseSourceRef(serializeSourceRef({ path: path82, ...startLine === void 0 ? {} : { startLine }, ...endLine === void 0 ? {} : { endLine } }));
48213
+ return parseSourceRef(serializeSourceRef({ path: path84, ...startLine === void 0 ? {} : { startLine }, ...endLine === void 0 ? {} : { endLine } }));
47797
48214
  });
47798
48215
  }
47799
48216
  function readOptionalString3(value, field) {
@@ -47837,7 +48254,7 @@ var init_frontmatter5 = __esm({
47837
48254
 
47838
48255
  // packages/memory/src/pages.ts
47839
48256
  import * as fs5 from "node:fs/promises";
47840
- import path69 from "node:path";
48257
+ import path71 from "node:path";
47841
48258
  async function listPages(root) {
47842
48259
  const relPaths = await collectMarkdownRelPaths(root, MEMORY_PAGES_DIR_RELPATH);
47843
48260
  const pages = await Promise.all(relPaths.map(async (relPath) => readPage(root, relPath)));
@@ -47845,8 +48262,8 @@ async function listPages(root) {
47845
48262
  }
47846
48263
  async function readPage(root, relPath) {
47847
48264
  const normalizedRelPath = assertMarkdownRelPath(relPath);
47848
- const absPath = path69.join(root, normalizedRelPath);
47849
- const [content, stat23] = await Promise.all([fs5.readFile(absPath, "utf8"), fs5.stat(absPath)]);
48265
+ const absPath = path71.join(root, normalizedRelPath);
48266
+ const [content, stat24] = await Promise.all([fs5.readFile(absPath, "utf8"), fs5.stat(absPath)]);
47850
48267
  try {
47851
48268
  const parsed = parseFrontmatter4(content);
47852
48269
  return {
@@ -47854,7 +48271,7 @@ async function readPage(root, relPath) {
47854
48271
  frontmatter: parsed.frontmatter,
47855
48272
  body: parsed.body,
47856
48273
  bytes: Buffer.byteLength(content),
47857
- mtimeMs: stat23.mtimeMs
48274
+ mtimeMs: stat24.mtimeMs
47858
48275
  };
47859
48276
  } catch (error2) {
47860
48277
  const message2 = error2 instanceof Error ? error2.message : String(error2);
@@ -47864,7 +48281,7 @@ async function readPage(root, relPath) {
47864
48281
  frontmatter: {},
47865
48282
  body: content,
47866
48283
  bytes: Buffer.byteLength(content),
47867
- mtimeMs: stat23.mtimeMs
48284
+ mtimeMs: stat24.mtimeMs
47868
48285
  };
47869
48286
  }
47870
48287
  }
@@ -47874,7 +48291,7 @@ async function collectMarkdownRelPaths(root, startRelPath = "") {
47874
48291
  return relPaths.sort((left, right) => left.localeCompare(right));
47875
48292
  }
47876
48293
  async function collectMarkdownRelPathsInto(root, currentRelPath, relPaths) {
47877
- const absPath = path69.join(root, currentRelPath);
48294
+ const absPath = path71.join(root, currentRelPath);
47878
48295
  let entryNames;
47879
48296
  try {
47880
48297
  entryNames = await fs5.readdir(absPath);
@@ -47885,8 +48302,8 @@ async function collectMarkdownRelPathsInto(root, currentRelPath, relPaths) {
47885
48302
  throw error2;
47886
48303
  }
47887
48304
  for (const entryName of entryNames.sort((left, right) => left.localeCompare(right))) {
47888
- const entryRelPath = currentRelPath.length === 0 ? entryName : path69.posix.join(currentRelPath, entryName);
47889
- const entryAbsPath = path69.join(root, entryRelPath);
48305
+ const entryRelPath = currentRelPath.length === 0 ? entryName : path71.posix.join(currentRelPath, entryName);
48306
+ const entryAbsPath = path71.join(root, entryRelPath);
47890
48307
  const entryStat = await fs5.stat(entryAbsPath);
47891
48308
  if (entryStat.isDirectory()) {
47892
48309
  if (entryName === MEMORY_CACHE_DIR_RELPATH) {
@@ -47916,7 +48333,7 @@ function assertMarkdownRelPath(relPath) {
47916
48333
  return normalizedRelPath;
47917
48334
  }
47918
48335
  function isMarkdownPath(relPath) {
47919
- return path69.posix.extname(relPath).toLowerCase() === ".md";
48336
+ return path71.posix.extname(relPath).toLowerCase() === ".md";
47920
48337
  }
47921
48338
  function isMissing(error2) {
47922
48339
  return typeof error2 === "object" && error2 !== null && "code" in error2 && error2.code === "ENOENT";
@@ -47931,7 +48348,7 @@ var init_pages = __esm({
47931
48348
 
47932
48349
  // packages/memory/src/search.ts
47933
48350
  import * as fs6 from "node:fs/promises";
47934
- import path70 from "node:path";
48351
+ import path72 from "node:path";
47935
48352
  async function searchMemory(root, query) {
47936
48353
  const normalizedQuery = query.trim();
47937
48354
  if (normalizedQuery.length === 0) {
@@ -47940,7 +48357,7 @@ async function searchMemory(root, query) {
47940
48357
  const relPaths = await collectMarkdownRelPaths(root);
47941
48358
  const hits = [];
47942
48359
  for (const relPath of relPaths) {
47943
- const content = await fs6.readFile(path70.join(root, relPath), "utf8");
48360
+ const content = await fs6.readFile(path72.join(root, relPath), "utf8");
47944
48361
  if (content.length === 0) {
47945
48362
  continue;
47946
48363
  }
@@ -47967,7 +48384,7 @@ var init_search = __esm({
47967
48384
 
47968
48385
  // packages/memory/src/status.ts
47969
48386
  import * as fs7 from "node:fs/promises";
47970
- import path71 from "node:path";
48387
+ import path73 from "node:path";
47971
48388
  async function statusOf(root) {
47972
48389
  if (!await pathExists7(root)) {
47973
48390
  return {
@@ -47984,9 +48401,9 @@ async function statusOf(root) {
47984
48401
  let totalBytes = 0;
47985
48402
  let lastWriteAtMs = Number.NEGATIVE_INFINITY;
47986
48403
  for (const relPath of markdownRelPaths) {
47987
- const stat23 = await fs7.stat(path71.join(root, relPath));
47988
- totalBytes += stat23.size;
47989
- lastWriteAtMs = Math.max(lastWriteAtMs, stat23.mtimeMs);
48404
+ const stat24 = await fs7.stat(path73.join(root, relPath));
48405
+ totalBytes += stat24.size;
48406
+ lastWriteAtMs = Math.max(lastWriteAtMs, stat24.mtimeMs);
47990
48407
  }
47991
48408
  return {
47992
48409
  pageCount: pageRelPaths.length,
@@ -48016,8 +48433,8 @@ var init_status = __esm({
48016
48433
 
48017
48434
  // packages/memory/src/lock.ts
48018
48435
  import * as fsPromises18 from "node:fs/promises";
48019
- import path72 from "node:path";
48020
- function createDefaultFs12() {
48436
+ import path74 from "node:path";
48437
+ function createDefaultFs13() {
48021
48438
  return {
48022
48439
  readFile: (filePath, encoding) => fsPromises18.readFile(filePath, encoding),
48023
48440
  unlink: fsPromises18.unlink,
@@ -48074,8 +48491,8 @@ async function readLockPid(fs17, lockPath) {
48074
48491
  }
48075
48492
  }
48076
48493
  async function withLock(root, run, options = {}) {
48077
- const fs17 = options.fs ?? createDefaultFs12();
48078
- const lockPath = path72.join(root, MEMORY_LOCK_RELPATH);
48494
+ const fs17 = options.fs ?? createDefaultFs13();
48495
+ const lockPath = path74.join(root, MEMORY_LOCK_RELPATH);
48079
48496
  const pid = options.pid ?? process.pid;
48080
48497
  const retries = options.retries ?? 20;
48081
48498
  const minTimeoutMs = options.minTimeoutMs ?? 25;
@@ -48127,7 +48544,7 @@ var init_confidence = __esm({
48127
48544
  // packages/memory/src/reconcile.ts
48128
48545
  import { createHash } from "node:crypto";
48129
48546
  import * as fs8 from "node:fs/promises";
48130
- import path73 from "node:path";
48547
+ import path75 from "node:path";
48131
48548
  var init_reconcile = __esm({
48132
48549
  "packages/memory/src/reconcile.ts"() {
48133
48550
  "use strict";
@@ -48141,7 +48558,7 @@ var init_reconcile = __esm({
48141
48558
 
48142
48559
  // packages/memory/src/write.ts
48143
48560
  import * as fs9 from "node:fs/promises";
48144
- import path74 from "node:path";
48561
+ import path76 from "node:path";
48145
48562
  async function clearMemory(root) {
48146
48563
  await withLock(root, async () => {
48147
48564
  await removeChildren(root);
@@ -48153,13 +48570,13 @@ async function removeChildren(directoryPath) {
48153
48570
  if (entryName === MEMORY_LOCK_RELPATH) {
48154
48571
  continue;
48155
48572
  }
48156
- const entryPath = path74.join(directoryPath, entryName);
48157
- const stat23 = await fs9.stat(entryPath);
48158
- if (stat23.isDirectory()) {
48573
+ const entryPath = path76.join(directoryPath, entryName);
48574
+ const stat24 = await fs9.stat(entryPath);
48575
+ if (stat24.isDirectory()) {
48159
48576
  await removeDirectory3(entryPath);
48160
48577
  continue;
48161
48578
  }
48162
- if (stat23.isFile()) {
48579
+ if (stat24.isFile()) {
48163
48580
  await fs9.unlink(entryPath);
48164
48581
  }
48165
48582
  }
@@ -48181,7 +48598,7 @@ var init_write2 = __esm({
48181
48598
 
48182
48599
  // packages/memory/src/edit.ts
48183
48600
  import * as fs10 from "node:fs/promises";
48184
- import path75 from "node:path";
48601
+ import path77 from "node:path";
48185
48602
  var init_edit = __esm({
48186
48603
  "packages/memory/src/edit.ts"() {
48187
48604
  "use strict";
@@ -48192,7 +48609,7 @@ var init_edit = __esm({
48192
48609
 
48193
48610
  // packages/memory/src/audit.ts
48194
48611
  import * as fs11 from "node:fs/promises";
48195
- import path76 from "node:path";
48612
+ import path78 from "node:path";
48196
48613
  var init_audit = __esm({
48197
48614
  "packages/memory/src/audit.ts"() {
48198
48615
  "use strict";
@@ -48205,7 +48622,7 @@ var init_audit = __esm({
48205
48622
  // packages/memory/src/cache.ts
48206
48623
  import { createHash as createHash2 } from "node:crypto";
48207
48624
  import * as fs12 from "node:fs/promises";
48208
- import path77 from "node:path";
48625
+ import path79 from "node:path";
48209
48626
  var init_cache = __esm({
48210
48627
  "packages/memory/src/cache.ts"() {
48211
48628
  "use strict";
@@ -48312,7 +48729,7 @@ var init_src28 = __esm({
48312
48729
 
48313
48730
  // packages/memory/src/tokens.ts
48314
48731
  import * as fs13 from "node:fs/promises";
48315
- import path78 from "node:path";
48732
+ import path80 from "node:path";
48316
48733
  async function computeTokenStats(root) {
48317
48734
  if (!await pathExists8(root)) {
48318
48735
  return {
@@ -48334,11 +48751,11 @@ async function computeTokenStats(root) {
48334
48751
  }
48335
48752
  }
48336
48753
  }
48337
- const repoRoot = path78.resolve(root, "..", "..");
48754
+ const repoRoot = path80.resolve(root, "..", "..");
48338
48755
  let sourceTokens = 0;
48339
48756
  const missingSources = [];
48340
48757
  for (const sourcePath of sourcePaths) {
48341
- const absPath = path78.isAbsolute(sourcePath) ? sourcePath : path78.resolve(repoRoot, sourcePath);
48758
+ const absPath = path80.isAbsolute(sourcePath) ? sourcePath : path80.resolve(repoRoot, sourcePath);
48342
48759
  try {
48343
48760
  const content = await fs13.readFile(absPath, "utf8");
48344
48761
  sourceTokens += countTokens(content);
@@ -48383,7 +48800,7 @@ var init_tokens2 = __esm({
48383
48800
 
48384
48801
  // packages/memory/src/ingest.ts
48385
48802
  import * as fs14 from "node:fs/promises";
48386
- import path79 from "node:path";
48803
+ import path81 from "node:path";
48387
48804
  var init_ingest = __esm({
48388
48805
  "packages/memory/src/ingest.ts"() {
48389
48806
  "use strict";
@@ -48418,7 +48835,7 @@ var init_install3 = __esm({
48418
48835
 
48419
48836
  // packages/memory/src/query.ts
48420
48837
  import * as fs15 from "node:fs/promises";
48421
- import path80 from "node:path";
48838
+ import path82 from "node:path";
48422
48839
  var init_query = __esm({
48423
48840
  "packages/memory/src/query.ts"() {
48424
48841
  "use strict";
@@ -48474,7 +48891,7 @@ var init_src29 = __esm({
48474
48891
  });
48475
48892
 
48476
48893
  // src/cli/commands/memory.ts
48477
- import path81 from "node:path";
48894
+ import path83 from "node:path";
48478
48895
  import * as fs16 from "node:fs/promises";
48479
48896
  function resolvePageRelPath(input) {
48480
48897
  const trimmed = input.trim();
@@ -48482,7 +48899,7 @@ function resolvePageRelPath(input) {
48482
48899
  throw new ValidationError("Missing page path.");
48483
48900
  }
48484
48901
  const normalized = trimmed.replaceAll("\\", "/");
48485
- const withExt = path81.posix.extname(normalized).length === 0 ? `${normalized}.md` : normalized;
48902
+ const withExt = path83.posix.extname(normalized).length === 0 ? `${normalized}.md` : normalized;
48486
48903
  return withExt.startsWith("pages/") ? withExt : `pages/${withExt}`;
48487
48904
  }
48488
48905
  function displayPageRelPath(relPath) {
@@ -48520,8 +48937,8 @@ function registerMemoryCommand(program, container) {
48520
48937
  }
48521
48938
  await initMemory(root);
48522
48939
  resources.context.complete({
48523
- success: `Initialized memory at ${path81.relative(container.env.cwd, root)}`,
48524
- dry: `Would initialize memory at ${path81.relative(container.env.cwd, root)}`
48940
+ success: `Initialized memory at ${path83.relative(container.env.cwd, root)}`,
48941
+ dry: `Would initialize memory at ${path83.relative(container.env.cwd, root)}`
48525
48942
  });
48526
48943
  resources.context.finalize();
48527
48944
  });
@@ -48551,7 +48968,7 @@ function registerMemoryCommand(program, container) {
48551
48968
  resources.logger.intro("memory show");
48552
48969
  await assertInitialized(root);
48553
48970
  const relPath = resolvePageRelPath(pagePath);
48554
- const absPath = path81.join(root, relPath);
48971
+ const absPath = path83.join(root, relPath);
48555
48972
  try {
48556
48973
  const content = await fs16.readFile(absPath, "utf8");
48557
48974
  process.stdout.write(content.endsWith("\n") ? content : `${content}
@@ -48649,7 +49066,7 @@ var init_package2 = __esm({
48649
49066
  "package.json"() {
48650
49067
  package_default2 = {
48651
49068
  name: "poe-code",
48652
- version: "3.0.183",
49069
+ version: "3.0.184",
48653
49070
  description: "CLI tool to configure Poe API for developer workflows.",
48654
49071
  type: "module",
48655
49072
  main: "./dist/index.js",
@@ -48864,22 +49281,22 @@ function formatCommandHeader(cmd) {
48864
49281
  }
48865
49282
  return `Poe - ${parts.reverse().join(" ")}`;
48866
49283
  }
48867
- function findCommandByPath(root, path82) {
49284
+ function findCommandByPath(root, path84) {
48868
49285
  let current = root;
48869
- for (const segment of path82) {
49286
+ for (const segment of path84) {
48870
49287
  const next = current.commands.find(
48871
49288
  (command) => Reflect.get(command, "_hidden") !== true && command.name() === segment
48872
49289
  );
48873
49290
  if (!next) {
48874
- throw new Error(`Root help command is missing: ${path82.join(" ")}`);
49291
+ throw new Error(`Root help command is missing: ${path84.join(" ")}`);
48875
49292
  }
48876
49293
  current = next;
48877
49294
  }
48878
49295
  return current;
48879
49296
  }
48880
- function formatRootHelpCommandName(path82, command) {
49297
+ function formatRootHelpCommandName(path84, command) {
48881
49298
  const leaf = [command.name(), ...command.aliases()].join(", ");
48882
- return path82.length > 1 ? [...path82.slice(0, -1), leaf].join(" ") : leaf;
49299
+ return path84.length > 1 ? [...path84.slice(0, -1), leaf].join(" ") : leaf;
48883
49300
  }
48884
49301
  function splitUsageParts(usage) {
48885
49302
  return usage.split(" ").map((part) => part.trim()).filter((part) => part.length > 0);
@@ -49546,7 +49963,7 @@ init_config3();
49546
49963
  init_constants();
49547
49964
  init_errors2();
49548
49965
  import os7 from "node:os";
49549
- import path59 from "node:path";
49966
+ import path61 from "node:path";
49550
49967
  import fsPromises17 from "node:fs/promises";
49551
49968
  import { Command } from "commander";
49552
49969
  function parseMcpSpawnConfig(input) {
@@ -49618,10 +50035,10 @@ function resolveWorkingDirectory(baseDir, candidate) {
49618
50035
  if (!candidate || candidate.trim().length === 0) {
49619
50036
  return void 0;
49620
50037
  }
49621
- if (path59.isAbsolute(candidate)) {
50038
+ if (path61.isAbsolute(candidate)) {
49622
50039
  return candidate;
49623
50040
  }
49624
- return path59.resolve(baseDir, candidate);
50041
+ return path61.resolve(baseDir, candidate);
49625
50042
  }
49626
50043
  function configurePoeAgentRunOptions(command) {
49627
50044
  return command.option("-y, --yes", "Accept configured defaults without prompting").option("--model <model>", "Model identifier override").option("--prompt <text>", "Prompt text to send").option("-C, --cwd <path>", "Working directory for the agent").option("--stdin", "Read the prompt from stdin").option(