poe-code 3.0.114 → 3.0.116

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
@@ -891,16 +891,16 @@ function getConfigFormat(pathOrFormat) {
891
891
  }
892
892
  return formatRegistry[formatName];
893
893
  }
894
- function detectFormat(path28) {
895
- const ext = getExtension(path28);
894
+ function detectFormat(path29) {
895
+ const ext = getExtension(path29);
896
896
  return extensionMap[ext];
897
897
  }
898
- function getExtension(path28) {
899
- const lastDot = path28.lastIndexOf(".");
898
+ function getExtension(path29) {
899
+ const lastDot = path29.lastIndexOf(".");
900
900
  if (lastDot === -1) {
901
901
  return "";
902
902
  }
903
- return path28.slice(lastDot).toLowerCase();
903
+ return path29.slice(lastDot).toLowerCase();
904
904
  }
905
905
  var formatRegistry, extensionMap;
906
906
  var init_formats = __esm({
@@ -1750,6 +1750,61 @@ var init_store = __esm({
1750
1750
  });
1751
1751
 
1752
1752
  // packages/poe-code-config/src/resolve.ts
1753
+ function resolveScope(schema, fileValues, env = {}) {
1754
+ const resolved = {};
1755
+ for (const key of Object.keys(schema)) {
1756
+ const field = schema[key];
1757
+ const envValue = resolveEnvValue(field, env);
1758
+ const fileValue = resolveFileValue(field, fileValues?.[key]);
1759
+ resolved[key] = envValue ?? fileValue ?? field.default;
1760
+ }
1761
+ return resolved;
1762
+ }
1763
+ function resolveEnvValue(field, env) {
1764
+ if (!field.env) {
1765
+ return void 0;
1766
+ }
1767
+ const raw = env[field.env];
1768
+ if (raw === void 0) {
1769
+ return void 0;
1770
+ }
1771
+ return coerceValue(field, raw);
1772
+ }
1773
+ function resolveFileValue(field, value) {
1774
+ return coerceValue(field, value);
1775
+ }
1776
+ function coerceValue(field, value) {
1777
+ switch (field.type) {
1778
+ case "string":
1779
+ return typeof value === "string" ? value : void 0;
1780
+ case "number":
1781
+ return coerceNumber(value);
1782
+ case "boolean":
1783
+ return coerceBoolean(value);
1784
+ }
1785
+ }
1786
+ function coerceNumber(value) {
1787
+ if (typeof value === "number" && Number.isFinite(value)) {
1788
+ return value;
1789
+ }
1790
+ if (typeof value !== "string" || value.length === 0) {
1791
+ return void 0;
1792
+ }
1793
+ const parsed = Number(value);
1794
+ return Number.isNaN(parsed) ? void 0 : parsed;
1795
+ }
1796
+ function coerceBoolean(value) {
1797
+ if (typeof value === "boolean") {
1798
+ return value;
1799
+ }
1800
+ if (value === "true" || value === "1") {
1801
+ return true;
1802
+ }
1803
+ if (value === "false" || value === "0") {
1804
+ return false;
1805
+ }
1806
+ return void 0;
1807
+ }
1753
1808
  var init_resolve = __esm({
1754
1809
  "packages/poe-code-config/src/resolve.ts"() {
1755
1810
  "use strict";
@@ -1900,38 +1955,38 @@ import { createTwoFilesPatch } from "diff";
1900
1955
  import chalk from "chalk";
1901
1956
  function createDryRunFileSystem(base, recorder) {
1902
1957
  const proxy = {
1903
- async readFile(path28, encoding) {
1958
+ async readFile(path29, encoding) {
1904
1959
  if (encoding) {
1905
- return base.readFile(path28, encoding);
1960
+ return base.readFile(path29, encoding);
1906
1961
  }
1907
- return base.readFile(path28);
1962
+ return base.readFile(path29);
1908
1963
  },
1909
- async writeFile(path28, data, options) {
1910
- const previousContent = await tryReadText(base, path28);
1964
+ async writeFile(path29, data, options) {
1965
+ const previousContent = await tryReadText(base, path29);
1911
1966
  const nextContent = formatData(data, options?.encoding);
1912
1967
  recorder.record({
1913
1968
  type: "writeFile",
1914
- path: path28,
1969
+ path: path29,
1915
1970
  nextContent,
1916
1971
  previousContent
1917
1972
  });
1918
1973
  },
1919
- async mkdir(path28, options) {
1920
- recorder.record({ type: "mkdir", path: path28, options });
1974
+ async mkdir(path29, options) {
1975
+ recorder.record({ type: "mkdir", path: path29, options });
1921
1976
  },
1922
- async stat(path28) {
1923
- return base.stat(path28);
1977
+ async stat(path29) {
1978
+ return base.stat(path29);
1924
1979
  },
1925
- async unlink(path28) {
1926
- recorder.record({ type: "unlink", path: path28 });
1980
+ async unlink(path29) {
1981
+ recorder.record({ type: "unlink", path: path29 });
1927
1982
  },
1928
- async readdir(path28) {
1929
- return base.readdir(path28);
1983
+ async readdir(path29) {
1984
+ return base.readdir(path29);
1930
1985
  }
1931
1986
  };
1932
1987
  if (typeof base.rm === "function") {
1933
- proxy.rm = async (path28, options) => {
1934
- recorder.record({ type: "rm", path: path28, options });
1988
+ proxy.rm = async (path29, options) => {
1989
+ recorder.record({ type: "rm", path: path29, options });
1935
1990
  };
1936
1991
  }
1937
1992
  if (typeof base.copyFile === "function") {
@@ -2021,8 +2076,8 @@ function describeWriteChange(previous, next) {
2021
2076
  }
2022
2077
  return "update";
2023
2078
  }
2024
- function renderWriteCommand(path28, change) {
2025
- const command = `cat > ${path28}`;
2079
+ function renderWriteCommand(path29, change) {
2080
+ const command = `cat > ${path29}`;
2026
2081
  if (change === "create") {
2027
2082
  return renderOperationCommand(command, chalk.green, "# create");
2028
2083
  }
@@ -2184,9 +2239,9 @@ function redactTomlLine(line) {
2184
2239
  }
2185
2240
  return line;
2186
2241
  }
2187
- async function tryReadText(base, path28) {
2242
+ async function tryReadText(base, path29) {
2188
2243
  try {
2189
- return await base.readFile(path28, "utf8");
2244
+ return await base.readFile(path29, "utf8");
2190
2245
  } catch (error2) {
2191
2246
  if (isNotFound(error2)) {
2192
2247
  return null;
@@ -3324,6 +3379,12 @@ var init_text = __esm({
3324
3379
  badge(content) {
3325
3380
  const theme = getTheme();
3326
3381
  return theme.badge(content);
3382
+ },
3383
+ selectLabel(label, detail) {
3384
+ if (!detail) {
3385
+ return label;
3386
+ }
3387
+ return `${label} ${typography.dim("\u2014")} ${typography.dim(detail)}`;
3327
3388
  }
3328
3389
  };
3329
3390
  }
@@ -4028,21 +4089,21 @@ async function* adaptClaude(lines) {
4028
4089
  if (blockType !== "tool_result") continue;
4029
4090
  const kind = toolKindsById.get(item.tool_use_id);
4030
4091
  toolKindsById.delete(item.tool_use_id);
4031
- let path28;
4092
+ let path29;
4032
4093
  if (typeof item.content === "string") {
4033
- path28 = item.content;
4094
+ path29 = item.content;
4034
4095
  } else {
4035
4096
  try {
4036
- path28 = JSON.stringify(item.content);
4097
+ path29 = JSON.stringify(item.content);
4037
4098
  } catch {
4038
- path28 = String(item.content);
4099
+ path29 = String(item.content);
4039
4100
  }
4040
4101
  }
4041
4102
  yield {
4042
4103
  event: "tool_complete",
4043
4104
  id: item.tool_use_id,
4044
4105
  kind,
4045
- path: path28
4106
+ path: path29
4046
4107
  };
4047
4108
  }
4048
4109
  }
@@ -4164,10 +4225,10 @@ async function* adaptCodex(lines) {
4164
4225
  const kindFromStart = toolKindById.get(item.id);
4165
4226
  const kind = kindFromStart ?? (itemType === "command_execution" ? "exec" : itemType === "file_edit" ? "edit" : "other");
4166
4227
  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;
4167
- const path28 = titleFromEvent ?? toolTitleById.get(item.id) ?? "";
4228
+ const path29 = titleFromEvent ?? toolTitleById.get(item.id) ?? "";
4168
4229
  toolTitleById.delete(item.id);
4169
4230
  toolKindById.delete(item.id);
4170
- yield { event: "tool_complete", id: item.id, kind, path: path28 };
4231
+ yield { event: "tool_complete", id: item.id, kind, path: path29 };
4171
4232
  }
4172
4233
  }
4173
4234
  }
@@ -4616,7 +4677,7 @@ function updateSessionFromEvent(ctx, event, toolCallsById) {
4616
4677
  }
4617
4678
  const id = readString(event.id);
4618
4679
  const kind = readString(event.kind);
4619
- const path28 = readString(event.path);
4680
+ const path29 = readString(event.path);
4620
4681
  let toolCall = id ? toolCallsById.get(id) : void 0;
4621
4682
  if (!toolCall) {
4622
4683
  toolCall = {};
@@ -4631,8 +4692,8 @@ function updateSessionFromEvent(ctx, event, toolCallsById) {
4631
4692
  if (kind) {
4632
4693
  toolCall.kind = kind;
4633
4694
  }
4634
- if (path28) {
4635
- toolCall.path = path28;
4695
+ if (path29) {
4696
+ toolCall.path = path29;
4636
4697
  }
4637
4698
  }
4638
4699
  var sessionCapture;
@@ -6630,21 +6691,21 @@ function createSdkContainer(options) {
6630
6691
  });
6631
6692
  loggerFactory.setErrorLogger(errorLogger);
6632
6693
  const asyncFs = {
6633
- readFile: ((path28, encoding) => {
6694
+ readFile: ((path29, encoding) => {
6634
6695
  if (encoding) {
6635
- return fs2.readFile(path28, encoding);
6696
+ return fs2.readFile(path29, encoding);
6636
6697
  }
6637
- return fs2.readFile(path28);
6698
+ return fs2.readFile(path29);
6638
6699
  }),
6639
- writeFile: (path28, data, opts) => fs2.writeFile(path28, data, opts),
6640
- mkdir: (path28, opts) => fs2.mkdir(path28, opts).then(() => {
6700
+ writeFile: (path29, data, opts) => fs2.writeFile(path29, data, opts),
6701
+ mkdir: (path29, opts) => fs2.mkdir(path29, opts).then(() => {
6641
6702
  }),
6642
- stat: (path28) => fs2.stat(path28),
6643
- rm: (path28, opts) => fs2.rm(path28, opts),
6644
- unlink: (path28) => fs2.unlink(path28),
6645
- readdir: (path28) => fs2.readdir(path28),
6703
+ stat: (path29) => fs2.stat(path29),
6704
+ rm: (path29, opts) => fs2.rm(path29, opts),
6705
+ unlink: (path29) => fs2.unlink(path29),
6706
+ readdir: (path29) => fs2.readdir(path29),
6646
6707
  copyFile: (src, dest) => fs2.copyFile(src, dest),
6647
- chmod: (path28, mode) => fs2.chmod(path28, mode)
6708
+ chmod: (path29, mode) => fs2.chmod(path29, mode)
6648
6709
  };
6649
6710
  const contextFactory = createCommandContextFactory({ fs: asyncFs });
6650
6711
  const authFs = {
@@ -7189,16 +7250,46 @@ async function scanPlansDir(fs3, plansDir, displayPrefix) {
7189
7250
  }
7190
7251
  return candidates;
7191
7252
  }
7192
- async function listPlanCandidates(fs3, cwd, homeDir) {
7253
+ async function listPlanCandidates(fs3, cwd, homeDir, planDirectory) {
7254
+ const customDir = planDirectory?.trim();
7255
+ const candidates = customDir ? await scanCustomPlanDir(fs3, customDir, cwd, homeDir) : await scanDefaultPlanDirs(fs3, cwd, homeDir);
7256
+ candidates.sort((left, right) => left.path.localeCompare(right.path));
7257
+ return candidates;
7258
+ }
7259
+ async function scanCustomPlanDir(fs3, planDirectory, cwd, homeDir) {
7260
+ const absoluteDir = resolveAbsoluteDirectory(planDirectory, cwd, homeDir);
7261
+ return scanPlansDir(fs3, absoluteDir, planDirectory);
7262
+ }
7263
+ async function scanDefaultPlanDirs(fs3, cwd, homeDir) {
7193
7264
  const projectDir = path13.join(cwd, ".poe-code", "pipeline", "plans");
7194
7265
  const globalDir = path13.join(homeDir, ".poe-code", "pipeline", "plans");
7195
7266
  const [projectCandidates, globalCandidates] = await Promise.all([
7196
7267
  scanPlansDir(fs3, projectDir, ".poe-code/pipeline/plans"),
7197
7268
  scanPlansDir(fs3, globalDir, "~/.poe-code/pipeline/plans")
7198
7269
  ]);
7199
- const candidates = [...projectCandidates, ...globalCandidates];
7200
- candidates.sort((left, right) => left.path.localeCompare(right.path));
7201
- return candidates;
7270
+ return [...projectCandidates, ...globalCandidates];
7271
+ }
7272
+ function resolveAbsoluteDirectory(dir, cwd, homeDir) {
7273
+ if (dir.startsWith("~/")) {
7274
+ return path13.join(homeDir, dir.slice(2));
7275
+ }
7276
+ return path13.isAbsolute(dir) ? dir : path13.resolve(cwd, dir);
7277
+ }
7278
+ async function resolvePlanDirectory(options) {
7279
+ const customDir = options.planDirectory?.trim();
7280
+ if (customDir) {
7281
+ return resolveAbsoluteDirectory(customDir, options.cwd, options.homeDir);
7282
+ }
7283
+ const fs3 = options.fs ?? createDefaultFs();
7284
+ const projectDir = path13.join(options.cwd, ".poe-code");
7285
+ try {
7286
+ const stat8 = await fs3.stat(projectDir);
7287
+ if (stat8.isDirectory()) {
7288
+ return path13.join(options.cwd, ".poe-code", "pipeline", "plans");
7289
+ }
7290
+ } catch {
7291
+ }
7292
+ return path13.join(options.homeDir, ".poe-code", "pipeline", "plans");
7202
7293
  }
7203
7294
  function resolveAbsolutePlanPath(planPath, cwd, homeDir) {
7204
7295
  if (planPath.startsWith("~/")) {
@@ -7222,7 +7313,7 @@ async function resolvePlanPath(options) {
7222
7313
  await ensurePlanExists(fs3, options.cwd, config2.planPath);
7223
7314
  return config2.planPath;
7224
7315
  }
7225
- const candidates = await listPlanCandidates(fs3, options.cwd, options.homeDir);
7316
+ const candidates = await listPlanCandidates(fs3, options.cwd, options.homeDir, options.planDirectory);
7226
7317
  if (candidates.length >= 1) {
7227
7318
  if (options.assumeYes) {
7228
7319
  return candidates[0].path;
@@ -7507,6 +7598,7 @@ async function runPipeline(options) {
7507
7598
  cwd: options.cwd,
7508
7599
  homeDir: options.homeDir,
7509
7600
  plan: options.plan,
7601
+ planDirectory: options.planDirectory,
7510
7602
  assumeYes: options.assumeYes,
7511
7603
  fs: fs3,
7512
7604
  selectPlan: options.selectPlan,
@@ -7745,7 +7837,7 @@ async function runPipeline2(options) {
7745
7837
  mode: input.mode
7746
7838
  });
7747
7839
  await renderAcpStream(events);
7748
- return result;
7840
+ return await result;
7749
7841
  }
7750
7842
  });
7751
7843
  }
@@ -7755,51 +7847,134 @@ var init_pipeline2 = __esm({
7755
7847
  init_src8();
7756
7848
  init_src6();
7757
7849
  await init_spawn3();
7850
+ init_src8();
7758
7851
  }
7759
7852
  });
7760
7853
 
7761
7854
  // packages/ralph/src/frontmatter/frontmatter.ts
7762
- import { stringify, parse as parse6 } from "yaml";
7855
+ import { parse as parse6, stringify } from "yaml";
7763
7856
  function parseFrontmatter(content) {
7764
- const defaults = { status: "pending", iteration: 0 };
7765
7857
  if (!content.startsWith(`${FENCE}
7766
7858
  `)) {
7767
- return { data: defaults, body: content };
7859
+ return {
7860
+ data: createDefaultFrontmatter(),
7861
+ body: content
7862
+ };
7768
7863
  }
7769
7864
  const closingIndex = content.indexOf(`
7770
7865
  ${FENCE}
7771
7866
  `, FENCE.length);
7772
7867
  if (closingIndex === -1) {
7773
- return { data: defaults, body: content };
7868
+ return {
7869
+ data: createDefaultFrontmatter(),
7870
+ body: content
7871
+ };
7774
7872
  }
7775
7873
  const yamlBlock = content.slice(FENCE.length + 1, closingIndex);
7776
7874
  const body = content.slice(closingIndex + FENCE.length + 2);
7777
7875
  const parsed = parse6(yamlBlock);
7778
7876
  return {
7779
- data: {
7780
- status: isValidStatus(parsed?.status) ? parsed.status : defaults.status,
7781
- iteration: typeof parsed?.iteration === "number" ? parsed.iteration : defaults.iteration
7782
- },
7877
+ data: parseFrontmatterData(parsed),
7783
7878
  body
7784
7879
  };
7785
7880
  }
7786
7881
  function writeFrontmatter(data, body) {
7787
- const yaml = stringify(data).trimEnd();
7882
+ const serialized = {
7883
+ ...data.agent !== void 0 ? { agent: data.agent } : {},
7884
+ ...data.iterations !== void 0 ? { iterations: data.iterations } : {},
7885
+ status: {
7886
+ state: data.status.state,
7887
+ iteration: data.status.iteration
7888
+ }
7889
+ };
7890
+ const yaml = stringify(serialized).trimEnd();
7788
7891
  return `${FENCE}
7789
7892
  ${yaml}
7790
7893
  ${FENCE}
7791
7894
  ${body}`;
7792
7895
  }
7793
- function isValidStatus(value) {
7794
- return typeof value === "string" && ["pending", "in_progress", "completed", "overbake_abort", "cancelled"].includes(
7795
- value
7796
- );
7896
+ function createDefaultFrontmatter() {
7897
+ return {
7898
+ status: {
7899
+ state: DEFAULT_STATUS.state,
7900
+ iteration: DEFAULT_STATUS.iteration
7901
+ }
7902
+ };
7903
+ }
7904
+ function parseFrontmatterData(value) {
7905
+ const defaults = createDefaultFrontmatter();
7906
+ const parsed = isRecord4(value) ? value : void 0;
7907
+ const parsedStatus = isRecord4(parsed?.status) ? parsed.status : void 0;
7908
+ const state = parsePlanStatus(parsedStatus?.state) ?? parseLegacyStatus(parsed?.status) ?? defaults.status.state;
7909
+ const iteration = parseNonNegativeInteger(parsedStatus?.iteration) ?? parseNonNegativeInteger(parsed?.iteration) ?? defaults.status.iteration;
7910
+ const agent2 = parseAgent(parsed?.agent);
7911
+ const iterations = parsePositiveInteger(parsed?.iterations);
7912
+ return {
7913
+ ...agent2 !== void 0 ? { agent: agent2 } : {},
7914
+ ...iterations !== void 0 ? { iterations } : {},
7915
+ status: {
7916
+ state,
7917
+ iteration
7918
+ }
7919
+ };
7920
+ }
7921
+ function parseAgent(value) {
7922
+ if (typeof value === "string") {
7923
+ const trimmed = value.trim();
7924
+ return trimmed.length > 0 ? trimmed : void 0;
7925
+ }
7926
+ if (!Array.isArray(value)) {
7927
+ return void 0;
7928
+ }
7929
+ if (value.length === 0) {
7930
+ return [];
7931
+ }
7932
+ const agents = [];
7933
+ for (const item of value) {
7934
+ if (typeof item !== "string") {
7935
+ return void 0;
7936
+ }
7937
+ const trimmed = item.trim();
7938
+ if (trimmed.length === 0) {
7939
+ return void 0;
7940
+ }
7941
+ agents.push(trimmed);
7942
+ }
7943
+ return agents;
7797
7944
  }
7798
- var FENCE;
7945
+ function parsePlanStatus(value) {
7946
+ if (value === "open" || value === "in_progress" || value === "completed") {
7947
+ return value;
7948
+ }
7949
+ return void 0;
7950
+ }
7951
+ function parseLegacyStatus(value) {
7952
+ if (value === "in_progress" || value === "completed") {
7953
+ return value;
7954
+ }
7955
+ if (value === "open" || value === "pending" || value === "cancelled" || value === "overbake_abort") {
7956
+ return "open";
7957
+ }
7958
+ return void 0;
7959
+ }
7960
+ function parseNonNegativeInteger(value) {
7961
+ return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : void 0;
7962
+ }
7963
+ function parsePositiveInteger(value) {
7964
+ return typeof value === "number" && Number.isInteger(value) && value >= 1 ? value : void 0;
7965
+ }
7966
+ function isRecord4(value) {
7967
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7968
+ }
7969
+ var FENCE, DEFAULT_STATUS;
7799
7970
  var init_frontmatter = __esm({
7800
7971
  "packages/ralph/src/frontmatter/frontmatter.ts"() {
7801
7972
  "use strict";
7802
7973
  FENCE = "---";
7974
+ DEFAULT_STATUS = {
7975
+ state: "open",
7976
+ iteration: 0
7977
+ };
7803
7978
  }
7804
7979
  });
7805
7980
 
@@ -7854,23 +8029,39 @@ async function scanDir(fs3, absoluteDir, displayDir) {
7854
8029
  }
7855
8030
  async function discoverDocs(options) {
7856
8031
  const fs3 = options.fs ?? createDefaultFs4();
8032
+ const customDir = options.planDirectory?.trim();
8033
+ const docs = customDir ? await scanCustomDir(fs3, customDir, options.cwd, options.homeDir) : await scanDefaultDirs(fs3, options.cwd, options.homeDir);
8034
+ return docs.sort((left, right) => {
8035
+ const leftName = path15.basename(left.displayPath).toLowerCase();
8036
+ const rightName = path15.basename(right.displayPath).toLowerCase();
8037
+ return leftName === rightName ? left.displayPath.localeCompare(right.displayPath) : leftName.localeCompare(rightName);
8038
+ });
8039
+ }
8040
+ async function scanCustomDir(fs3, planDirectory, cwd, homeDir) {
8041
+ const absoluteDir = resolveAbsoluteDirectory2(planDirectory, cwd, homeDir);
8042
+ const displayDir = planDirectory;
8043
+ return scanDir(fs3, absoluteDir, displayDir);
8044
+ }
8045
+ async function scanDefaultDirs(fs3, cwd, homeDir) {
7857
8046
  const [localDocs, globalDocs] = await Promise.all([
7858
8047
  scanDir(
7859
8048
  fs3,
7860
- path15.join(options.cwd, ".poe-code", "ralph", "plans"),
8049
+ path15.join(cwd, ".poe-code", "ralph", "plans"),
7861
8050
  ".poe-code/ralph/plans"
7862
8051
  ),
7863
8052
  scanDir(
7864
8053
  fs3,
7865
- path15.join(options.homeDir, ".poe-code", "ralph", "plans"),
8054
+ path15.join(homeDir, ".poe-code", "ralph", "plans"),
7866
8055
  "~/.poe-code/ralph/plans"
7867
8056
  )
7868
8057
  ]);
7869
- return [...localDocs, ...globalDocs].sort((left, right) => {
7870
- const leftName = path15.basename(left.displayPath).toLowerCase();
7871
- const rightName = path15.basename(right.displayPath).toLowerCase();
7872
- return leftName === rightName ? left.displayPath.localeCompare(right.displayPath) : leftName.localeCompare(rightName);
7873
- });
8058
+ return [...localDocs, ...globalDocs];
8059
+ }
8060
+ function resolveAbsoluteDirectory2(dir, cwd, homeDir) {
8061
+ if (dir.startsWith("~/")) {
8062
+ return path15.join(homeDir, dir.slice(2));
8063
+ }
8064
+ return path15.isAbsolute(dir) ? dir : path15.resolve(cwd, dir);
7874
8065
  }
7875
8066
  var init_discovery2 = __esm({
7876
8067
  "packages/ralph/src/discovery/discovery.ts"() {
@@ -7878,37 +8069,6 @@ var init_discovery2 = __esm({
7878
8069
  }
7879
8070
  });
7880
8071
 
7881
- // packages/ralph/src/overbaking/detector.ts
7882
- var OverbakingDetector;
7883
- var init_detector = __esm({
7884
- "packages/ralph/src/overbaking/detector.ts"() {
7885
- "use strict";
7886
- OverbakingDetector = class {
7887
- threshold;
7888
- consecutiveFailures = 0;
7889
- constructor(threshold) {
7890
- this.threshold = threshold;
7891
- }
7892
- record(_success) {
7893
- if (_success) {
7894
- this.consecutiveFailures = 0;
7895
- return {
7896
- consecutiveFailures: 0,
7897
- overbaked: false,
7898
- shouldWarn: false
7899
- };
7900
- }
7901
- this.consecutiveFailures += 1;
7902
- return {
7903
- consecutiveFailures: this.consecutiveFailures,
7904
- overbaked: this.consecutiveFailures >= this.threshold,
7905
- shouldWarn: this.consecutiveFailures === this.threshold
7906
- };
7907
- }
7908
- };
7909
- }
7910
- });
7911
-
7912
8072
  // packages/ralph/src/run/ralph.ts
7913
8073
  import path16 from "node:path";
7914
8074
  import * as fsPromises5 from "node:fs/promises";
@@ -7921,27 +8081,31 @@ async function runRalph(options) {
7921
8081
  if (!Number.isInteger(options.maxIterations) || options.maxIterations < 1) {
7922
8082
  throw new Error("maxIterations must be a positive integer.");
7923
8083
  }
7924
- const threshold = options.maxFailures ?? 3;
7925
- if (!Number.isInteger(threshold) || threshold < 1) {
7926
- throw new Error("maxFailures must be a positive integer.");
7927
- }
8084
+ const agents = normalizeAgents(options.agent);
7928
8085
  const absoluteDocPath = resolveAbsoluteDocPath(
7929
8086
  options.docPath,
7930
8087
  options.cwd,
7931
8088
  options.homeDir
7932
8089
  );
7933
8090
  const rawContent = await fs3.readFile(absoluteDocPath, "utf8");
7934
- const { body: prompt } = parseFrontmatter(rawContent);
7935
- const detector = new OverbakingDetector(threshold);
8091
+ const { data: frontmatter, body: prompt } = parseFrontmatter(rawContent);
7936
8092
  const startTime = Date.now();
7937
8093
  let iterationsCompleted = 0;
7938
- await updateFrontmatter(fs3, absoluteDocPath, prompt, "in_progress", 0);
8094
+ await updateFrontmatter(
8095
+ fs3,
8096
+ absoluteDocPath,
8097
+ prompt,
8098
+ frontmatter,
8099
+ "in_progress",
8100
+ 0
8101
+ );
7939
8102
  async function finalize2(stopReason) {
7940
8103
  const status = stopReasonToStatus(stopReason);
7941
8104
  await updateFrontmatter(
7942
8105
  fs3,
7943
8106
  absoluteDocPath,
7944
8107
  prompt,
8108
+ frontmatter,
7945
8109
  status,
7946
8110
  iterationsCompleted
7947
8111
  );
@@ -7958,12 +8122,13 @@ async function runRalph(options) {
7958
8122
  try {
7959
8123
  for (let iteration = 1; iteration <= options.maxIterations; iteration += 1) {
7960
8124
  assertNotAborted2(options.signal);
7961
- options.onIterationStart?.(iteration, options.maxIterations);
8125
+ const currentAgent = agents[(iteration - 1) % agents.length];
8126
+ options.onIterationStart?.(iteration, options.maxIterations, currentAgent);
7962
8127
  const iterationStart = Date.now();
7963
8128
  let result;
7964
8129
  try {
7965
8130
  result = await runAgent({
7966
- agent: options.agent,
8131
+ agent: currentAgent,
7967
8132
  prompt,
7968
8133
  cwd: options.cwd,
7969
8134
  ...options.model ? { model: options.model } : {},
@@ -7981,6 +8146,7 @@ async function runRalph(options) {
7981
8146
  fs3,
7982
8147
  absoluteDocPath,
7983
8148
  prompt,
8149
+ frontmatter,
7984
8150
  "in_progress",
7985
8151
  iterationsCompleted
7986
8152
  );
@@ -7989,21 +8155,6 @@ async function runRalph(options) {
7989
8155
  Date.now() - iterationStart,
7990
8156
  success2
7991
8157
  );
7992
- const overbake = detector.record(success2);
7993
- if (!overbake.shouldWarn) {
7994
- continue;
7995
- }
7996
- options.onOverbakeWarning?.(
7997
- overbake.consecutiveFailures,
7998
- threshold
7999
- );
8000
- const action = options.promptOverbake ? await options.promptOverbake({
8001
- consecutiveFailures: overbake.consecutiveFailures,
8002
- threshold
8003
- }) : "abort";
8004
- if (action === "abort") {
8005
- return finalize2("overbake_abort");
8006
- }
8007
8158
  }
8008
8159
  } catch (error2) {
8009
8160
  if (isAbortError2(error2)) {
@@ -8031,6 +8182,23 @@ function createDefaultFs5() {
8031
8182
  rename: fsPromises5.rename
8032
8183
  };
8033
8184
  }
8185
+ function normalizeAgents(agent2) {
8186
+ if (typeof agent2 === "string") {
8187
+ const trimmed = agent2.trim();
8188
+ if (trimmed.length === 0) {
8189
+ throw new Error("agent must contain at least one entry.");
8190
+ }
8191
+ return [trimmed];
8192
+ }
8193
+ if (agent2.length === 0) {
8194
+ throw new Error("agent must contain at least one entry.");
8195
+ }
8196
+ const agents = agent2.map((entry) => entry.trim());
8197
+ if (agents.some((entry) => entry.length === 0)) {
8198
+ throw new Error("agent entries must be non-empty strings.");
8199
+ }
8200
+ return agents;
8201
+ }
8034
8202
  function resolveAbsoluteDocPath(docPath, cwd, homeDir) {
8035
8203
  if (docPath.startsWith("~/")) {
8036
8204
  return path16.join(homeDir, docPath.slice(2));
@@ -8051,8 +8219,18 @@ function createAbortError4() {
8051
8219
  function isAbortError2(error2) {
8052
8220
  return error2 instanceof Error && error2.name === "AbortError";
8053
8221
  }
8054
- async function updateFrontmatter(fs3, absoluteDocPath, body, status, iteration) {
8055
- const content = writeFrontmatter({ status, iteration }, body);
8222
+ async function updateFrontmatter(fs3, absoluteDocPath, body, frontmatter, state, iteration) {
8223
+ const content = writeFrontmatter(
8224
+ {
8225
+ ...frontmatter.agent !== void 0 ? { agent: frontmatter.agent } : {},
8226
+ ...frontmatter.iterations !== void 0 ? { iterations: frontmatter.iterations } : {},
8227
+ status: {
8228
+ state,
8229
+ iteration
8230
+ }
8231
+ },
8232
+ body
8233
+ );
8056
8234
  await fs3.writeFile(absoluteDocPath, content);
8057
8235
  }
8058
8236
  async function archivePlan2(fs3, absoluteDocPath) {
@@ -8067,16 +8245,13 @@ function stopReasonToStatus(stopReason) {
8067
8245
  case "completed":
8068
8246
  case "max_iterations":
8069
8247
  return "completed";
8070
- case "overbake_abort":
8071
- return "overbake_abort";
8072
8248
  case "cancelled":
8073
- return "cancelled";
8249
+ return "open";
8074
8250
  }
8075
8251
  }
8076
8252
  var init_ralph = __esm({
8077
8253
  "packages/ralph/src/run/ralph.ts"() {
8078
8254
  "use strict";
8079
- init_detector();
8080
8255
  init_frontmatter();
8081
8256
  }
8082
8257
  });
@@ -8087,7 +8262,6 @@ var init_src9 = __esm({
8087
8262
  "use strict";
8088
8263
  init_frontmatter();
8089
8264
  init_discovery2();
8090
- init_detector();
8091
8265
  init_ralph();
8092
8266
  }
8093
8267
  });
@@ -8182,13 +8356,13 @@ async function readErrorBody(response) {
8182
8356
  }
8183
8357
  }
8184
8358
  function extractTextContent(data) {
8185
- if (!isRecord4(data)) return void 0;
8359
+ if (!isRecord5(data)) return void 0;
8186
8360
  const choices = data.choices;
8187
8361
  if (!Array.isArray(choices) || choices.length === 0) return void 0;
8188
8362
  const first = choices[0];
8189
- if (!isRecord4(first)) return void 0;
8363
+ if (!isRecord5(first)) return void 0;
8190
8364
  const message = first.message;
8191
- if (!isRecord4(message)) return void 0;
8365
+ if (!isRecord5(message)) return void 0;
8192
8366
  return typeof message.content === "string" ? message.content : void 0;
8193
8367
  }
8194
8368
  function extractMediaFromCompletion(data) {
@@ -8196,14 +8370,14 @@ function extractMediaFromCompletion(data) {
8196
8370
  if (!content) return {};
8197
8371
  try {
8198
8372
  const parsed = JSON.parse(content);
8199
- if (isRecord4(parsed) && typeof parsed.url === "string") {
8373
+ if (isRecord5(parsed) && typeof parsed.url === "string") {
8200
8374
  return {
8201
8375
  url: parsed.url,
8202
8376
  mimeType: typeof parsed.mimeType === "string" ? parsed.mimeType : void 0,
8203
8377
  data: typeof parsed.data === "string" ? parsed.data : void 0
8204
8378
  };
8205
8379
  }
8206
- if (isRecord4(parsed) && typeof parsed.data === "string") {
8380
+ if (isRecord5(parsed) && typeof parsed.data === "string") {
8207
8381
  return {
8208
8382
  data: parsed.data,
8209
8383
  mimeType: typeof parsed.mimeType === "string" ? parsed.mimeType : void 0
@@ -8237,7 +8411,7 @@ function isValidUrl(value) {
8237
8411
  return false;
8238
8412
  }
8239
8413
  }
8240
- function isRecord4(value) {
8414
+ function isRecord5(value) {
8241
8415
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
8242
8416
  }
8243
8417
  var init_llm_client = __esm({
@@ -8876,8 +9050,8 @@ function resourceNotFound(resource) {
8876
9050
  `Resource not found: ${resource}`
8877
9051
  );
8878
9052
  }
8879
- function assertAbsolutePath(path28) {
8880
- if (!isAbsolute(path28)) {
9053
+ function assertAbsolutePath(path29) {
9054
+ if (!isAbsolute(path29)) {
8881
9055
  throw invalidParams('"path" must be an absolute path');
8882
9056
  }
8883
9057
  }
@@ -14348,7 +14522,7 @@ function parseLegacyConfigDocument(raw) {
14348
14522
  }
14349
14523
  }
14350
14524
  function normalizeLegacyConfigDocument(value) {
14351
- if (!isRecord5(value)) {
14525
+ if (!isRecord6(value)) {
14352
14526
  return {};
14353
14527
  }
14354
14528
  const document = {};
@@ -14362,12 +14536,12 @@ function normalizeLegacyConfigDocument(value) {
14362
14536
  return document;
14363
14537
  }
14364
14538
  function normalizeConfiguredServices(value) {
14365
- if (!isRecord5(value)) {
14539
+ if (!isRecord6(value)) {
14366
14540
  return {};
14367
14541
  }
14368
14542
  const entries = {};
14369
14543
  for (const [key, entry] of Object.entries(value)) {
14370
- if (!isRecord5(entry)) {
14544
+ if (!isRecord6(entry)) {
14371
14545
  continue;
14372
14546
  }
14373
14547
  entries[key] = normalizeConfiguredServiceMetadata({
@@ -14386,10 +14560,10 @@ function createInvalidBackupPath2(filePath) {
14386
14560
  const baseName = path21.basename(filePath);
14387
14561
  return path21.join(directory, `${baseName}.invalid-${createTimestamp()}.json`);
14388
14562
  }
14389
- function isRecord5(value) {
14563
+ function isRecord6(value) {
14390
14564
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
14391
14565
  }
14392
- var coreConfigScope, knownConfigScopes, CORE_SCOPE, configuredServicesScope, EMPTY_DOCUMENT3;
14566
+ var coreConfigScope, ralphConfigScope, pipelineConfigScope, knownConfigScopes, CORE_SCOPE, configuredServicesScope, EMPTY_DOCUMENT3;
14393
14567
  var init_config3 = __esm({
14394
14568
  "src/services/config.ts"() {
14395
14569
  "use strict";
@@ -14409,7 +14583,23 @@ var init_config3 = __esm({
14409
14583
  doc: "Poe API base URL"
14410
14584
  }
14411
14585
  });
14412
- knownConfigScopes = [coreConfigScope];
14586
+ ralphConfigScope = defineScope("ralph", {
14587
+ plan_directory: {
14588
+ type: "string",
14589
+ default: "",
14590
+ env: "POE_RALPH_PLAN_DIRECTORY",
14591
+ doc: "Custom directory for Ralph plan documents"
14592
+ }
14593
+ });
14594
+ pipelineConfigScope = defineScope("pipeline", {
14595
+ plan_directory: {
14596
+ type: "string",
14597
+ default: "",
14598
+ env: "POE_PIPELINE_PLAN_DIRECTORY",
14599
+ doc: "Custom directory for Pipeline plan files"
14600
+ }
14601
+ });
14602
+ knownConfigScopes = [coreConfigScope, ralphConfigScope, pipelineConfigScope];
14413
14603
  CORE_SCOPE = coreConfigScope.scope;
14414
14604
  configuredServicesScope = "configured_services";
14415
14605
  EMPTY_DOCUMENT3 = `${JSON.stringify({}, null, 2)}
@@ -17663,8 +17853,8 @@ var init_parseUtil = __esm({
17663
17853
  init_errors3();
17664
17854
  init_en();
17665
17855
  makeIssue = (params) => {
17666
- const { data, path: path28, errorMaps, issueData } = params;
17667
- const fullPath = [...path28, ...issueData.path || []];
17856
+ const { data, path: path29, errorMaps, issueData } = params;
17857
+ const fullPath = [...path29, ...issueData.path || []];
17668
17858
  const fullIssue = {
17669
17859
  ...issueData,
17670
17860
  path: fullPath
@@ -17944,11 +18134,11 @@ var init_types4 = __esm({
17944
18134
  init_parseUtil();
17945
18135
  init_util();
17946
18136
  ParseInputLazyPath = class {
17947
- constructor(parent, value, path28, key) {
18137
+ constructor(parent, value, path29, key) {
17948
18138
  this._cachedPath = [];
17949
18139
  this.parent = parent;
17950
18140
  this.data = value;
17951
- this._path = path28;
18141
+ this._path = path29;
17952
18142
  this._key = key;
17953
18143
  }
17954
18144
  get path() {
@@ -21452,10 +21642,10 @@ function mergeDefs(...defs) {
21452
21642
  function cloneDef(schema) {
21453
21643
  return mergeDefs(schema._zod.def);
21454
21644
  }
21455
- function getElementAtPath(obj, path28) {
21456
- if (!path28)
21645
+ function getElementAtPath(obj, path29) {
21646
+ if (!path29)
21457
21647
  return obj;
21458
- return path28.reduce((acc, key) => acc?.[key], obj);
21648
+ return path29.reduce((acc, key) => acc?.[key], obj);
21459
21649
  }
21460
21650
  function promiseAllObject(promisesObj) {
21461
21651
  const keys = Object.keys(promisesObj);
@@ -21767,11 +21957,11 @@ function aborted(x, startIndex = 0) {
21767
21957
  }
21768
21958
  return false;
21769
21959
  }
21770
- function prefixIssues(path28, issues) {
21960
+ function prefixIssues(path29, issues) {
21771
21961
  return issues.map((iss) => {
21772
21962
  var _a2;
21773
21963
  (_a2 = iss).path ?? (_a2.path = []);
21774
- iss.path.unshift(path28);
21964
+ iss.path.unshift(path29);
21775
21965
  return iss;
21776
21966
  });
21777
21967
  }
@@ -33932,8 +34122,8 @@ var require_utils = __commonJS({
33932
34122
  }
33933
34123
  return ind;
33934
34124
  }
33935
- function removeDotSegments(path28) {
33936
- let input = path28;
34125
+ function removeDotSegments(path29) {
34126
+ let input = path29;
33937
34127
  const output = [];
33938
34128
  let nextSlash = -1;
33939
34129
  let len = 0;
@@ -34132,8 +34322,8 @@ var require_schemes = __commonJS({
34132
34322
  wsComponent.secure = void 0;
34133
34323
  }
34134
34324
  if (wsComponent.resourceName) {
34135
- const [path28, query] = wsComponent.resourceName.split("?");
34136
- wsComponent.path = path28 && path28 !== "/" ? path28 : void 0;
34325
+ const [path29, query] = wsComponent.resourceName.split("?");
34326
+ wsComponent.path = path29 && path29 !== "/" ? path29 : void 0;
34137
34327
  wsComponent.query = query;
34138
34328
  wsComponent.resourceName = void 0;
34139
34329
  }
@@ -39427,6 +39617,20 @@ var init_models2 = __esm({
39427
39617
  import path27 from "node:path";
39428
39618
  import { readFile as readFile7, stat as stat7 } from "node:fs/promises";
39429
39619
  import { fileURLToPath as fileURLToPath4 } from "node:url";
39620
+ async function resolvePipelinePlanDirectory(container) {
39621
+ const configDoc = await readMergedDocument(
39622
+ container.fs,
39623
+ container.env.configPath,
39624
+ container.env.projectConfigPath
39625
+ );
39626
+ const pipelineConfig = resolveScope(
39627
+ pipelineConfigScope.schema,
39628
+ configDoc[pipelineConfigScope.scope],
39629
+ container.env.variables
39630
+ );
39631
+ const dir = pipelineConfig.plan_directory?.trim();
39632
+ return dir || void 0;
39633
+ }
39430
39634
  function formatDuration(ms) {
39431
39635
  const totalSeconds = Math.round(ms / 1e3);
39432
39636
  const minutes = Math.floor(totalSeconds / 60);
@@ -39553,10 +39757,12 @@ function registerPipelineCommand(program, container) {
39553
39757
  }
39554
39758
  agent2 = resolvePipelineAgent(selected);
39555
39759
  }
39760
+ const planDirectory = await resolvePipelinePlanDirectory(container);
39556
39761
  const result = await runPipeline2({
39557
39762
  agent: agent2,
39558
39763
  cwd: container.env.cwd,
39559
39764
  homeDir: container.env.homeDir,
39765
+ ...planDirectory ? { planDirectory } : {},
39560
39766
  ...options.model ? { model: options.model } : {},
39561
39767
  ...options.task ? { task: options.task } : {},
39562
39768
  ...options.plan ? { plan: options.plan } : {},
@@ -39682,6 +39888,17 @@ function registerPipelineCommand(program, container) {
39682
39888
  resources.context.finalize();
39683
39889
  }
39684
39890
  });
39891
+ pipeline.command("plan-path").description("Print the directory where pipeline plan files should be placed.").action(async function() {
39892
+ const planDirectory = await resolvePipelinePlanDirectory(container);
39893
+ const resolvedPath = await resolvePlanDirectory({
39894
+ cwd: container.env.cwd,
39895
+ homeDir: container.env.homeDir,
39896
+ planDirectory,
39897
+ fs: container.fs
39898
+ });
39899
+ process.stdout.write(`${resolvedPath}
39900
+ `);
39901
+ });
39685
39902
  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() {
39686
39903
  const flags = resolveCommandFlags(program);
39687
39904
  const resources = createExecutionResources(
@@ -39808,6 +40025,8 @@ var init_pipeline4 = __esm({
39808
40025
  init_src5();
39809
40026
  init_src2();
39810
40027
  init_src15();
40028
+ init_src4();
40029
+ init_config3();
39811
40030
  init_errors();
39812
40031
  init_shared();
39813
40032
  await init_pipeline2();
@@ -39819,19 +40038,20 @@ var init_pipeline4 = __esm({
39819
40038
  });
39820
40039
 
39821
40040
  // src/cli/commands/ralph.ts
40041
+ import path28 from "node:path";
39822
40042
  function formatDuration2(ms) {
39823
40043
  const totalSeconds = Math.round(ms / 1e3);
39824
40044
  const minutes = Math.floor(totalSeconds / 60);
39825
40045
  const seconds = totalSeconds % 60;
39826
40046
  return minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
39827
40047
  }
39828
- function resolveRalphAgent(value) {
40048
+ function resolveRalphAgent(value, sourceLabel = "agent") {
39829
40049
  if (!value || value.trim().length === 0) {
39830
40050
  return DEFAULT_RALPH_AGENT;
39831
40051
  }
39832
40052
  const resolved = resolveAgentId(value.trim());
39833
40053
  if (!resolved) {
39834
- throw new ValidationError(`Unsupported agent: ${value}`);
40054
+ throw new ValidationError(`Unsupported ${sourceLabel}: ${value}`);
39835
40055
  }
39836
40056
  return resolved;
39837
40057
  }
@@ -39847,26 +40067,54 @@ function parsePositiveInt(value, fieldName) {
39847
40067
  }
39848
40068
  return parsed;
39849
40069
  }
39850
- async function resolveAgent(options) {
39851
- if (options.providedAgent) {
39852
- return resolveRalphAgent(options.providedAgent);
40070
+ function normalizeConfiguredIterations(value) {
40071
+ return typeof value === "number" && Number.isInteger(value) && value >= 1 ? value : void 0;
40072
+ }
40073
+ function resolveAbsoluteDocPath2(container, docPath) {
40074
+ if (docPath.startsWith("~/")) {
40075
+ return path28.join(container.env.homeDir, docPath.slice(2));
39853
40076
  }
39854
- const flags = resolveCommandFlags(options.program);
39855
- if (flags.assumeYes) {
39856
- return DEFAULT_RALPH_AGENT;
40077
+ return path28.isAbsolute(docPath) ? docPath : path28.resolve(container.env.cwd, docPath);
40078
+ }
40079
+ async function resolvePlanDirectory2(container) {
40080
+ const configDoc = await readMergedDocument(
40081
+ container.fs,
40082
+ container.env.configPath,
40083
+ container.env.projectConfigPath
40084
+ );
40085
+ const ralphConfig = resolveScope(
40086
+ ralphConfigScope.schema,
40087
+ configDoc[ralphConfigScope.scope],
40088
+ container.env.variables
40089
+ );
40090
+ const configDir = ralphConfig.plan_directory?.trim();
40091
+ return configDir || void 0;
40092
+ }
40093
+ function formatDocHint(frontmatter) {
40094
+ const parts = [];
40095
+ if (frontmatter.agent !== void 0) {
40096
+ const agents = Array.isArray(frontmatter.agent) ? frontmatter.agent : [frontmatter.agent];
40097
+ if (agents.length > 0) {
40098
+ parts.push(agents.join(", "));
40099
+ }
39857
40100
  }
39858
- const selected = await select2({
39859
- message: "Select agent to run Ralph with:",
39860
- options: allSpawnConfigs.map((config2) => ({
39861
- label: config2.agentId,
39862
- value: config2.agentId
39863
- }))
39864
- });
39865
- if (isCancel2(selected)) {
39866
- cancel2("Ralph run cancelled.");
39867
- return null;
40101
+ if (frontmatter.iterations !== void 0) {
40102
+ parts.push(`\xD7${frontmatter.iterations}`);
40103
+ }
40104
+ if (frontmatter.status.state !== "open" || frontmatter.status.iteration > 0) {
40105
+ parts.push(`${frontmatter.status.state} ${frontmatter.status.iteration}`);
40106
+ }
40107
+ return parts.length > 0 ? parts.join(" \xB7 ") : void 0;
40108
+ }
40109
+ async function readDocHint(container, docPath) {
40110
+ const absolutePath = resolveAbsoluteDocPath2(container, docPath);
40111
+ try {
40112
+ const content = await container.fs.readFile(absolutePath, "utf8");
40113
+ const { data } = parseFrontmatter(content);
40114
+ return formatDocHint(data);
40115
+ } catch {
40116
+ return void 0;
39868
40117
  }
39869
- return resolveRalphAgent(typeof selected === "string" ? selected : void 0);
39870
40118
  }
39871
40119
  async function resolveDocPath(options) {
39872
40120
  if (options.providedDoc && options.providedDoc.trim().length > 0) {
@@ -39875,6 +40123,7 @@ async function resolveDocPath(options) {
39875
40123
  const docs = await discoverDocs({
39876
40124
  cwd: options.container.env.cwd,
39877
40125
  homeDir: options.container.env.homeDir,
40126
+ planDirectory: options.planDirectory,
39878
40127
  fs: options.container.fs
39879
40128
  });
39880
40129
  if (docs.length === 0) {
@@ -39886,10 +40135,13 @@ async function resolveDocPath(options) {
39886
40135
  if (flags.assumeYes) {
39887
40136
  return docs[0].path;
39888
40137
  }
40138
+ const hints = await Promise.all(
40139
+ docs.map((doc) => readDocHint(options.container, doc.path))
40140
+ );
39889
40141
  const selected = await select2({
39890
40142
  message: "Select the Ralph markdown doc to run:",
39891
- options: docs.map((doc) => ({
39892
- label: doc.displayPath,
40143
+ options: docs.map((doc, index) => ({
40144
+ label: text.selectLabel(doc.displayPath, hints[index]),
39893
40145
  value: doc.path
39894
40146
  }))
39895
40147
  });
@@ -39899,7 +40151,61 @@ async function resolveDocPath(options) {
39899
40151
  }
39900
40152
  return typeof selected === "string" ? selected : null;
39901
40153
  }
39902
- async function resolveIterations(options) {
40154
+ async function readRalphDoc(container, docPath) {
40155
+ const absolutePath = resolveAbsoluteDocPath2(container, docPath);
40156
+ try {
40157
+ const content = await container.fs.readFile(absolutePath, "utf8");
40158
+ const parsed = parseFrontmatter(content);
40159
+ return {
40160
+ absolutePath,
40161
+ body: parsed.body,
40162
+ data: parsed.data
40163
+ };
40164
+ } catch {
40165
+ throw new ValidationError(`Ralph doc not found: ${docPath}`);
40166
+ }
40167
+ }
40168
+ function resolveConfiguredAgents(value) {
40169
+ if (value === void 0) {
40170
+ return void 0;
40171
+ }
40172
+ if (typeof value === "string") {
40173
+ return resolveRalphAgent(value, "frontmatter agent");
40174
+ }
40175
+ if (value.length === 0) {
40176
+ throw new ValidationError("Frontmatter agent array must not be empty.");
40177
+ }
40178
+ return value.map((entry) => resolveRalphAgent(entry, "frontmatter agent"));
40179
+ }
40180
+ async function promptForAgent(program) {
40181
+ const flags = resolveCommandFlags(program);
40182
+ if (flags.assumeYes) {
40183
+ return DEFAULT_RALPH_AGENT;
40184
+ }
40185
+ const selected = await select2({
40186
+ message: "Select agent to run Ralph with:",
40187
+ options: allSpawnConfigs.map((config2) => ({
40188
+ label: config2.agentId,
40189
+ value: config2.agentId
40190
+ }))
40191
+ });
40192
+ if (isCancel2(selected)) {
40193
+ cancel2("Ralph run cancelled.");
40194
+ return null;
40195
+ }
40196
+ return resolveRalphAgent(typeof selected === "string" ? selected : void 0);
40197
+ }
40198
+ async function resolveRunAgent(options) {
40199
+ if (options.providedAgent) {
40200
+ return resolveRalphAgent(options.providedAgent);
40201
+ }
40202
+ const configured = resolveConfiguredAgents(options.frontmatterAgent);
40203
+ if (configured !== void 0) {
40204
+ return configured;
40205
+ }
40206
+ return promptForAgent(options.program);
40207
+ }
40208
+ async function resolveRunIterations(options) {
39903
40209
  const explicitIterations = parsePositiveInt(
39904
40210
  options.providedIterations,
39905
40211
  "iterations"
@@ -39907,11 +40213,15 @@ async function resolveIterations(options) {
39907
40213
  if (explicitIterations != null) {
39908
40214
  return explicitIterations;
39909
40215
  }
40216
+ const configuredIterations = normalizeConfiguredIterations(
40217
+ options.frontmatterIterations
40218
+ );
40219
+ if (configuredIterations != null) {
40220
+ return configuredIterations;
40221
+ }
39910
40222
  const flags = resolveCommandFlags(options.program);
39911
40223
  if (flags.assumeYes) {
39912
- throw new ValidationError(
39913
- "Iterations are required when using --yes. Provide poe-code ralph run <iterations> [doc]."
39914
- );
40224
+ return DEFAULT_RALPH_ITERATIONS;
39915
40225
  }
39916
40226
  const entered = await text3({
39917
40227
  message: "How many Ralph iterations should run?"
@@ -39925,43 +40235,157 @@ async function resolveIterations(options) {
39925
40235
  "iterations"
39926
40236
  ) ?? null;
39927
40237
  }
40238
+ async function resolveInitAgent(options) {
40239
+ if (options.providedAgent) {
40240
+ return resolveRalphAgent(options.providedAgent);
40241
+ }
40242
+ return promptForAgent(options.program);
40243
+ }
40244
+ async function resolveInitIterations(options) {
40245
+ const explicitIterations = parsePositiveInt(
40246
+ options.providedIterations,
40247
+ "iterations"
40248
+ );
40249
+ if (explicitIterations != null) {
40250
+ return explicitIterations;
40251
+ }
40252
+ const flags = resolveCommandFlags(options.program);
40253
+ if (flags.assumeYes) {
40254
+ return DEFAULT_RALPH_ITERATIONS;
40255
+ }
40256
+ const entered = await text3({
40257
+ message: "How many Ralph iterations should run?"
40258
+ });
40259
+ if (isCancel2(entered)) {
40260
+ cancel2("Ralph init cancelled.");
40261
+ return null;
40262
+ }
40263
+ return parsePositiveInt(
40264
+ typeof entered === "string" ? entered.trim() : void 0,
40265
+ "iterations"
40266
+ ) ?? null;
40267
+ }
40268
+ function formatCurrentConfig(frontmatter) {
40269
+ if (frontmatter.agent === void 0 && frontmatter.iterations === void 0) {
40270
+ return null;
40271
+ }
40272
+ const items = [];
40273
+ if (frontmatter.iterations !== void 0) {
40274
+ items.push(String(frontmatter.iterations));
40275
+ }
40276
+ const agentList = expandAgentList(frontmatter.agent, frontmatter.iterations);
40277
+ if (agentList.length > 0) {
40278
+ items.push(...agentList);
40279
+ }
40280
+ return items.length > 0 ? `Current: ${items.join(", ")}` : null;
40281
+ }
40282
+ function expandAgentList(agent2, iterations) {
40283
+ if (agent2 === void 0) {
40284
+ return [];
40285
+ }
40286
+ if (typeof agent2 === "string") {
40287
+ const count2 = normalizeConfiguredIterations(iterations) ?? 1;
40288
+ return Array.from({ length: count2 }, () => agent2);
40289
+ }
40290
+ if (agent2.length === 0) {
40291
+ return [];
40292
+ }
40293
+ const count = normalizeConfiguredIterations(iterations) ?? agent2.length;
40294
+ return Array.from({ length: count }, (_, index) => agent2[index % agent2.length]);
40295
+ }
39928
40296
  function registerRalphCommand(program, container) {
39929
40297
  const ralph = program.command("ralph").description("Run a simple iterative markdown loop.");
39930
- ralph.command("run").description("Run the selected markdown doc through repeated agent iterations.").argument("[iterations]", "Number of Ralph iterations to run").argument("[doc]", "Markdown doc path").option("--agent <name>", "Agent to run each iteration with").option("--model <model>", "Model override passed to the agent").option(
39931
- "--max-failures <n>",
39932
- "Consecutive failure threshold before overbake protection prompts"
39933
- ).action(async function(iterationsArg, docArg) {
40298
+ ralph.command("init").description("Write Ralph config into an existing markdown doc frontmatter.").argument("[doc]", "Markdown doc path").option("--agent <name>", "Agent to write into frontmatter").option("--iterations <n>", "Number of iterations to write into frontmatter").action(async function(docArg) {
40299
+ const flags = resolveCommandFlags(program);
40300
+ const resources = createExecutionResources(container, flags, "ralph:init");
40301
+ const options = this.opts();
40302
+ resources.logger.intro("ralph init");
40303
+ try {
40304
+ const planDirectory = await resolvePlanDirectory2(container);
40305
+ const docPath = await resolveDocPath({
40306
+ container,
40307
+ program,
40308
+ providedDoc: docArg,
40309
+ planDirectory
40310
+ });
40311
+ if (!docPath) {
40312
+ return;
40313
+ }
40314
+ const doc = await readRalphDoc(container, docPath);
40315
+ const currentConfig = formatCurrentConfig(doc.data);
40316
+ if (!options.agent && !options.iterations && !flags.assumeYes && currentConfig) {
40317
+ resources.logger.info(currentConfig);
40318
+ }
40319
+ const agent2 = await resolveInitAgent({
40320
+ program,
40321
+ providedAgent: options.agent
40322
+ });
40323
+ if (!agent2) {
40324
+ return;
40325
+ }
40326
+ const iterations = await resolveInitIterations({
40327
+ program,
40328
+ providedIterations: options.iterations
40329
+ });
40330
+ if (iterations == null) {
40331
+ return;
40332
+ }
40333
+ const updated = writeFrontmatter(
40334
+ {
40335
+ agent: agent2,
40336
+ iterations,
40337
+ status: {
40338
+ state: doc.data.status.state,
40339
+ iteration: doc.data.status.iteration
40340
+ }
40341
+ },
40342
+ doc.body
40343
+ );
40344
+ await container.fs.writeFile(doc.absolutePath, updated, { encoding: "utf8" });
40345
+ resources.logger.resolved(
40346
+ "Initialized Ralph config",
40347
+ `Doc: ${docPath}
40348
+ Agent: ${agent2}
40349
+ Iterations: ${iterations}`
40350
+ );
40351
+ resources.logger.success("Ralph config saved.");
40352
+ } finally {
40353
+ resources.context.finalize();
40354
+ }
40355
+ });
40356
+ ralph.command("run").description("Run the selected markdown doc through repeated agent iterations.").argument("[doc]", "Markdown doc path").option("--agent <name>", "Override the agent from frontmatter").option("--iterations <n>", "Override iterations from frontmatter").option("--model <model>", "Model override passed to the agent").action(async function(docArg) {
39934
40357
  const flags = resolveCommandFlags(program);
39935
40358
  const resources = createExecutionResources(container, flags, "ralph:run");
39936
40359
  const options = this.opts();
39937
40360
  resources.logger.intro("ralph run");
39938
40361
  try {
40362
+ const planDirectory = await resolvePlanDirectory2(container);
39939
40363
  const docPath = await resolveDocPath({
39940
40364
  container,
39941
40365
  program,
39942
- providedDoc: docArg
40366
+ providedDoc: docArg,
40367
+ planDirectory
39943
40368
  });
39944
40369
  if (!docPath) {
39945
40370
  return;
39946
40371
  }
39947
- const agent2 = await resolveAgent({
40372
+ const doc = await readRalphDoc(container, docPath);
40373
+ const agent2 = await resolveRunAgent({
39948
40374
  program,
39949
- providedAgent: options.agent
40375
+ providedAgent: options.agent,
40376
+ frontmatterAgent: doc.data.agent
39950
40377
  });
39951
40378
  if (!agent2) {
39952
40379
  return;
39953
40380
  }
39954
- const maxIterations = await resolveIterations({
40381
+ const maxIterations = await resolveRunIterations({
39955
40382
  program,
39956
- providedIterations: iterationsArg
40383
+ providedIterations: options.iterations,
40384
+ frontmatterIterations: doc.data.iterations
39957
40385
  });
39958
40386
  if (maxIterations == null) {
39959
40387
  return;
39960
40388
  }
39961
- const maxFailures = parsePositiveInt(
39962
- options.maxFailures,
39963
- "max-failures"
39964
- );
39965
40389
  const result = await runRalph2({
39966
40390
  agent: agent2,
39967
40391
  cwd: container.env.cwd,
@@ -39969,34 +40393,14 @@ function registerRalphCommand(program, container) {
39969
40393
  docPath,
39970
40394
  maxIterations,
39971
40395
  ...options.model ? { model: options.model } : {},
39972
- ...maxFailures != null ? { maxFailures } : {},
39973
- promptOverbake: async (input) => {
39974
- const selected = await select2({
39975
- message: `Ralph hit ${input.consecutiveFailures} consecutive failures (threshold ${input.threshold}). What should happen next?`,
39976
- options: [
39977
- { label: "Continue", value: "continue" },
39978
- { label: "Abort", value: "abort" }
39979
- ]
39980
- });
39981
- if (isCancel2(selected)) {
39982
- cancel2("Ralph run cancelled.");
39983
- return "abort";
39984
- }
39985
- return selected === "continue" ? "continue" : "abort";
39986
- },
39987
- onIterationStart(iteration, total) {
39988
- resources.logger.info(`Iteration ${iteration}/${total}`);
40396
+ onIterationStart(iteration, total, currentAgent) {
40397
+ resources.logger.info(`Iteration ${iteration}/${total} (${currentAgent})`);
39989
40398
  },
39990
40399
  onIterationComplete(iteration, durationMs, success2) {
39991
40400
  const status = success2 ? "done" : "failed";
39992
40401
  resources.logger.info(
39993
40402
  `Iteration ${iteration} ${status} in ${formatDuration2(durationMs)}`
39994
40403
  );
39995
- },
39996
- onOverbakeWarning(consecutiveFailures, threshold) {
39997
- resources.logger.warn(
39998
- `Overbake protection triggered at ${consecutiveFailures} consecutive failures (threshold ${threshold}).`
39999
- );
40000
40404
  }
40001
40405
  });
40002
40406
  const summary = [
@@ -40010,12 +40414,6 @@ function registerRalphCommand(program, container) {
40010
40414
  resources.logger.resolved("Run summary", summary);
40011
40415
  return;
40012
40416
  }
40013
- if (result.stopReason === "overbake_abort") {
40014
- process.exitCode = 1;
40015
- resources.logger.warn("Ralph stopped after repeated failures.");
40016
- resources.logger.resolved("Run summary", summary);
40017
- return;
40018
- }
40019
40417
  resources.logger.resolved("Run summary", summary);
40020
40418
  resources.logger.success("Ralph run finished.");
40021
40419
  } finally {
@@ -40023,7 +40421,7 @@ function registerRalphCommand(program, container) {
40023
40421
  }
40024
40422
  });
40025
40423
  }
40026
- var DEFAULT_RALPH_AGENT;
40424
+ var DEFAULT_RALPH_AGENT, DEFAULT_RALPH_ITERATIONS;
40027
40425
  var init_ralph3 = __esm({
40028
40426
  async "src/cli/commands/ralph.ts"() {
40029
40427
  "use strict";
@@ -40031,10 +40429,13 @@ var init_ralph3 = __esm({
40031
40429
  init_src2();
40032
40430
  init_src6();
40033
40431
  init_src9();
40432
+ init_src4();
40433
+ init_config3();
40034
40434
  init_errors();
40035
40435
  init_shared();
40036
40436
  await init_ralph2();
40037
40437
  DEFAULT_RALPH_AGENT = "claude-code";
40438
+ DEFAULT_RALPH_ITERATIONS = 3;
40038
40439
  }
40039
40440
  });
40040
40441
 
@@ -40044,7 +40445,7 @@ var init_package = __esm({
40044
40445
  "package.json"() {
40045
40446
  package_default = {
40046
40447
  name: "poe-code",
40047
- version: "3.0.114",
40448
+ version: "3.0.116",
40048
40449
  description: "CLI tool to configure Poe API for developer workflows.",
40049
40450
  type: "module",
40050
40451
  main: "./dist/index.js",
@@ -40262,9 +40663,14 @@ function formatHelpText(input) {
40262
40663
  args: "",
40263
40664
  description: "Run a fixed-step task pipeline plan"
40264
40665
  },
40666
+ {
40667
+ name: "ralph init",
40668
+ args: "[doc]",
40669
+ description: "Write Ralph config into a markdown doc frontmatter"
40670
+ },
40265
40671
  {
40266
40672
  name: "ralph run",
40267
- args: "[iterations] [doc]",
40673
+ args: "[doc]",
40268
40674
  description: "Run a markdown doc through repeated agent iterations"
40269
40675
  },
40270
40676
  {