@neriros/ralphy 2.17.2 → 2.17.3

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/cli/index.js CHANGED
@@ -35029,8 +35029,8 @@ import { readFileSync as readFileSync2 } from "fs";
35029
35029
  import { resolve } from "path";
35030
35030
  function getVersion() {
35031
35031
  try {
35032
- if ("2.17.2")
35033
- return "2.17.2";
35032
+ if ("2.17.3")
35033
+ return "2.17.3";
35034
35034
  } catch {}
35035
35035
  const dirsToTry = [];
35036
35036
  try {
@@ -39468,6 +39468,99 @@ var init_types2 = __esm(() => {
39468
39468
  });
39469
39469
  });
39470
39470
 
39471
+ // apps/cli/src/agent/worktree.ts
39472
+ import { basename, join as join3 } from "path";
39473
+ import { homedir } from "os";
39474
+ import { exists } from "fs/promises";
39475
+ function worktreesDir(projectRoot) {
39476
+ return join3(homedir(), ".ralph", basename(projectRoot), "worktrees");
39477
+ }
39478
+ function branchForChange(changeName) {
39479
+ return `ralph/${changeName}`;
39480
+ }
39481
+ async function createWorktree(projectRoot, changeName, runner) {
39482
+ const dir = worktreesDir(projectRoot);
39483
+ const cwd2 = join3(dir, changeName);
39484
+ const branch = branchForChange(changeName);
39485
+ const list = await runner.run(["worktree", "list", "--porcelain"], projectRoot);
39486
+ if (list.stdout.includes(`worktree ${cwd2}
39487
+ `)) {
39488
+ return { cwd: cwd2, branch };
39489
+ }
39490
+ let branchExists = true;
39491
+ try {
39492
+ await runner.run(["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`], projectRoot);
39493
+ } catch {
39494
+ branchExists = false;
39495
+ }
39496
+ const cmd = branchExists ? ["worktree", "add", cwd2, branch] : ["worktree", "add", "-b", branch, cwd2];
39497
+ await runner.run(cmd, projectRoot);
39498
+ return { cwd: cwd2, branch };
39499
+ }
39500
+ async function removeWorktree(projectRoot, cwd2, runner) {
39501
+ await runner.run(["worktree", "remove", "--force", cwd2], projectRoot);
39502
+ }
39503
+ async function isWorktreeSafeToRemove(cwd2, base2, runner) {
39504
+ const status = await runner.run(["status", "--porcelain"], cwd2);
39505
+ const dirty = status.stdout.trim();
39506
+ let unpushedCommits = "";
39507
+ try {
39508
+ const log2 = await runner.run(["log", "--oneline", `${base2}..HEAD`, "--no-merges"], cwd2);
39509
+ unpushedCommits = log2.stdout.trim();
39510
+ } catch {
39511
+ unpushedCommits = "<unknown: failed to compare against base>";
39512
+ }
39513
+ if (dirty && unpushedCommits) {
39514
+ return {
39515
+ safe: false,
39516
+ reason: "uncommitted changes AND unpushed commits present",
39517
+ dirty,
39518
+ unpushedCommits
39519
+ };
39520
+ }
39521
+ if (dirty) {
39522
+ return {
39523
+ safe: false,
39524
+ reason: "uncommitted or untracked files present",
39525
+ dirty,
39526
+ unpushedCommits
39527
+ };
39528
+ }
39529
+ if (unpushedCommits) {
39530
+ return {
39531
+ safe: false,
39532
+ reason: `commits ahead of ${base2} were not pushed/PR'd`,
39533
+ dirty,
39534
+ unpushedCommits
39535
+ };
39536
+ }
39537
+ return { safe: true, dirty, unpushedCommits };
39538
+ }
39539
+ async function seedWorktreeMcpConfig(projectRoot, worktreeCwd) {
39540
+ const dst = join3(worktreeCwd, ".mcp.json");
39541
+ const src = join3(projectRoot, ".mcp.json");
39542
+ const source = await exists(dst) ? dst : await exists(src) ? src : null;
39543
+ if (!source)
39544
+ return;
39545
+ let parsed;
39546
+ try {
39547
+ parsed = await Bun.file(source).json();
39548
+ } catch {
39549
+ return;
39550
+ }
39551
+ const servers = parsed.mcpServers;
39552
+ if (servers && typeof servers === "object") {
39553
+ for (const cfg of Object.values(servers)) {
39554
+ if (Array.isArray(cfg.args)) {
39555
+ cfg.args = cfg.args.map((a) => typeof a === "string" && a.startsWith(".ralph/") ? join3(projectRoot, a) : a);
39556
+ }
39557
+ }
39558
+ }
39559
+ await Bun.write(dst, JSON.stringify(parsed, null, 2) + `
39560
+ `);
39561
+ }
39562
+ var init_worktree = () => {};
39563
+
39471
39564
  // node_modules/.bun/react@18.3.1/node_modules/react/cjs/react-jsx-dev-runtime.development.js
39472
39565
  var require_react_jsx_dev_runtime_development = __commonJS((exports) => {
39473
39566
  var React10 = __toESM(require_react());
@@ -59333,8 +59426,8 @@ var init_node = __esm(() => {
59333
59426
  });
59334
59427
 
59335
59428
  // packages/telemetry/src/index.ts
59336
- import { homedir } from "os";
59337
- import { join as join6 } from "path";
59429
+ import { homedir as homedir2 } from "os";
59430
+ import { join as join7 } from "path";
59338
59431
  import { randomUUID } from "crypto";
59339
59432
  function setDefaultProperties(props) {
59340
59433
  defaultProps = { ...defaultProps, ...props };
@@ -59342,7 +59435,7 @@ function setDefaultProperties(props) {
59342
59435
  async function init() {
59343
59436
  if (!enabled)
59344
59437
  return;
59345
- const idPath = join6(homedir(), ".ralph", ".telemetry-id");
59438
+ const idPath = join7(homedir2(), ".ralph", ".telemetry-id");
59346
59439
  const idFile = Bun.file(idPath);
59347
59440
  if (await idFile.exists()) {
59348
59441
  distinctId = (await idFile.text()).trim();
@@ -59417,12 +59510,12 @@ ${fence}`;
59417
59510
  }
59418
59511
 
59419
59512
  // apps/cli/src/agent/config.ts
59420
- import { join as join10 } from "path";
59513
+ import { join as join11 } from "path";
59421
59514
  function stripJsonComments(text) {
59422
59515
  return text.replace(/\/\/[^\n]*/g, "");
59423
59516
  }
59424
59517
  async function loadRalphyConfig(projectRoot) {
59425
- const path = join10(projectRoot, "ralphy.config.json");
59518
+ const path = join11(projectRoot, "ralphy.config.json");
59426
59519
  const file = Bun.file(path);
59427
59520
  if (!await file.exists()) {
59428
59521
  return RalphyConfigSchema.parse({});
@@ -59432,7 +59525,7 @@ async function loadRalphyConfig(projectRoot) {
59432
59525
  return RalphyConfigSchema.parse(raw);
59433
59526
  }
59434
59527
  async function ensureRalphyConfig(projectRoot) {
59435
- const path = join10(projectRoot, "ralphy.config.json");
59528
+ const path = join11(projectRoot, "ralphy.config.json");
59436
59529
  const file = Bun.file(path);
59437
59530
  if (await file.exists())
59438
59531
  return path;
@@ -59634,8 +59727,8 @@ var init_config = __esm(() => {
59634
59727
 
59635
59728
  // packages/log/src/log.ts
59636
59729
  import { appendFile } from "fs/promises";
59637
- import { join as join11, dirname as dirname4 } from "path";
59638
- import { homedir as homedir2 } from "os";
59730
+ import { join as join12, dirname as dirname4 } from "path";
59731
+ import { homedir as homedir3 } from "os";
59639
59732
  import { mkdir as mkdir2 } from "fs/promises";
59640
59733
  function fmt(type, text) {
59641
59734
  return `[${new Date().toISOString()}] [${type}] ${text}
@@ -59680,25 +59773,25 @@ async function initWorkerLog(logFile) {
59680
59773
  var ANSI_RE, AGENT_LOG_PATH;
59681
59774
  var init_log = __esm(() => {
59682
59775
  ANSI_RE = /\x1b(?:\[[0-9;]*[A-Za-z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|.)/g;
59683
- AGENT_LOG_PATH = join11(homedir2(), ".ralph", "agent-mode.log");
59776
+ AGENT_LOG_PATH = join12(homedir3(), ".ralph", "agent-mode.log");
59684
59777
  mkdir2(dirname4(AGENT_LOG_PATH), { recursive: true }).catch(() => {
59685
59778
  return;
59686
59779
  });
59687
59780
  });
59688
59781
 
59689
59782
  // packages/core/src/layout.ts
59690
- import { join as join12 } from "path";
59783
+ import { join as join13 } from "path";
59691
59784
  function projectLayout(root) {
59692
- const statesDir = join12(root, ".ralph", "tasks");
59693
- const tasksDir = join12(root, "openspec", "changes");
59785
+ const statesDir = join13(root, ".ralph", "tasks");
59786
+ const tasksDir = join13(root, "openspec", "changes");
59694
59787
  return {
59695
59788
  root,
59696
59789
  statesDir,
59697
59790
  tasksDir,
59698
- agentStateFile: join12(root, ".ralph", "agent-state.json"),
59699
- changeDir: (name) => join12(tasksDir, name),
59700
- taskStateDir: (name) => join12(statesDir, name),
59701
- stateFile: (name) => join12(statesDir, name, STATE_FILE2)
59791
+ agentStateFile: join13(root, ".ralph", "agent-state.json"),
59792
+ changeDir: (name) => join13(tasksDir, name),
59793
+ taskStateDir: (name) => join13(statesDir, name),
59794
+ stateFile: (name) => join13(statesDir, name, STATE_FILE2)
59702
59795
  };
59703
59796
  }
59704
59797
  var STATE_FILE2 = ".ralph-state.json";
@@ -59753,7 +59846,15 @@ function buildIssueFilter(spec) {
59753
59846
  where.and = [{ state: current }, noStatus];
59754
59847
  }
59755
59848
  if (labels.length > 0) {
59756
- where.labels = { ...where.labels, every: { name: { nin: labels } } };
59849
+ const includeLabels = where.labels;
59850
+ const excludeLabels = { every: { name: { nin: labels } } };
59851
+ if (includeLabels === undefined) {
59852
+ where.labels = excludeLabels;
59853
+ } else {
59854
+ const existingAnd = where.and ?? [];
59855
+ where.and = [...existingAnd, { labels: includeLabels }, { labels: excludeLabels }];
59856
+ delete where.labels;
59857
+ }
59757
59858
  }
59758
59859
  }
59759
59860
  return where;
@@ -60308,7 +60409,7 @@ var init_coordinator = __esm(() => {
60308
60409
  });
60309
60410
 
60310
60411
  // apps/cli/src/agent/scaffold.ts
60311
- import { join as join13 } from "path";
60412
+ import { join as join14 } from "path";
60312
60413
  import { mkdir as mkdir3 } from "fs/promises";
60313
60414
  function changeNameForIssue(issue) {
60314
60415
  const slug = issue.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
@@ -60316,10 +60417,10 @@ function changeNameForIssue(issue) {
60316
60417
  }
60317
60418
  async function scaffoldChangeForIssue(tasksDir, statesDir, issue, comments = [], appendPrompt = "") {
60318
60419
  const name = changeNameForIssue(issue);
60319
- const changeDir = join13(tasksDir, name);
60320
- const stateDir = join13(statesDir, name);
60420
+ const changeDir = join14(tasksDir, name);
60421
+ const stateDir = join14(statesDir, name);
60321
60422
  await mkdir3(changeDir, { recursive: true });
60322
- await mkdir3(join13(changeDir, "specs"), { recursive: true });
60423
+ await mkdir3(join14(changeDir, "specs"), { recursive: true });
60323
60424
  await mkdir3(stateDir, { recursive: true });
60324
60425
  const commentsBlock = comments.length > 0 ? [
60325
60426
  "",
@@ -60371,106 +60472,13 @@ async function scaffoldChangeForIssue(tasksDir, statesDir, issue, comments = [],
60371
60472
  ""
60372
60473
  ].join(`
60373
60474
  `);
60374
- await Bun.write(join13(changeDir, "proposal.md"), proposal);
60375
- await Bun.write(join13(changeDir, "tasks.md"), tasks);
60376
- await Bun.write(join13(changeDir, "design.md"), design);
60475
+ await Bun.write(join14(changeDir, "proposal.md"), proposal);
60476
+ await Bun.write(join14(changeDir, "tasks.md"), tasks);
60477
+ await Bun.write(join14(changeDir, "design.md"), design);
60377
60478
  return name;
60378
60479
  }
60379
60480
  var init_scaffold = () => {};
60380
60481
 
60381
- // apps/cli/src/agent/worktree.ts
60382
- import { basename, join as join14 } from "path";
60383
- import { homedir as homedir3 } from "os";
60384
- import { exists } from "fs/promises";
60385
- function worktreesDir(projectRoot) {
60386
- return join14(homedir3(), ".ralph", basename(projectRoot), "worktrees");
60387
- }
60388
- function branchForChange(changeName) {
60389
- return `ralph/${changeName}`;
60390
- }
60391
- async function createWorktree(projectRoot, changeName, runner) {
60392
- const dir = worktreesDir(projectRoot);
60393
- const cwd2 = join14(dir, changeName);
60394
- const branch = branchForChange(changeName);
60395
- const list = await runner.run(["worktree", "list", "--porcelain"], projectRoot);
60396
- if (list.stdout.includes(`worktree ${cwd2}
60397
- `)) {
60398
- return { cwd: cwd2, branch };
60399
- }
60400
- let branchExists = true;
60401
- try {
60402
- await runner.run(["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`], projectRoot);
60403
- } catch {
60404
- branchExists = false;
60405
- }
60406
- const cmd = branchExists ? ["worktree", "add", cwd2, branch] : ["worktree", "add", "-b", branch, cwd2];
60407
- await runner.run(cmd, projectRoot);
60408
- return { cwd: cwd2, branch };
60409
- }
60410
- async function removeWorktree(projectRoot, cwd2, runner) {
60411
- await runner.run(["worktree", "remove", "--force", cwd2], projectRoot);
60412
- }
60413
- async function isWorktreeSafeToRemove(cwd2, base2, runner) {
60414
- const status = await runner.run(["status", "--porcelain"], cwd2);
60415
- const dirty = status.stdout.trim();
60416
- let unpushedCommits = "";
60417
- try {
60418
- const log2 = await runner.run(["log", "--oneline", `${base2}..HEAD`, "--no-merges"], cwd2);
60419
- unpushedCommits = log2.stdout.trim();
60420
- } catch {
60421
- unpushedCommits = "<unknown: failed to compare against base>";
60422
- }
60423
- if (dirty && unpushedCommits) {
60424
- return {
60425
- safe: false,
60426
- reason: "uncommitted changes AND unpushed commits present",
60427
- dirty,
60428
- unpushedCommits
60429
- };
60430
- }
60431
- if (dirty) {
60432
- return {
60433
- safe: false,
60434
- reason: "uncommitted or untracked files present",
60435
- dirty,
60436
- unpushedCommits
60437
- };
60438
- }
60439
- if (unpushedCommits) {
60440
- return {
60441
- safe: false,
60442
- reason: `commits ahead of ${base2} were not pushed/PR'd`,
60443
- dirty,
60444
- unpushedCommits
60445
- };
60446
- }
60447
- return { safe: true, dirty, unpushedCommits };
60448
- }
60449
- async function seedWorktreeMcpConfig(projectRoot, worktreeCwd) {
60450
- const dst = join14(worktreeCwd, ".mcp.json");
60451
- const src = join14(projectRoot, ".mcp.json");
60452
- const source = await exists(dst) ? dst : await exists(src) ? src : null;
60453
- if (!source)
60454
- return;
60455
- let parsed;
60456
- try {
60457
- parsed = await Bun.file(source).json();
60458
- } catch {
60459
- return;
60460
- }
60461
- const servers = parsed.mcpServers;
60462
- if (servers && typeof servers === "object") {
60463
- for (const cfg of Object.values(servers)) {
60464
- if (Array.isArray(cfg.args)) {
60465
- cfg.args = cfg.args.map((a) => typeof a === "string" && a.startsWith(".ralph/") ? join14(projectRoot, a) : a);
60466
- }
60467
- }
60468
- }
60469
- await Bun.write(dst, JSON.stringify(parsed, null, 2) + `
60470
- `);
60471
- }
60472
- var init_worktree = () => {};
60473
-
60474
60482
  // apps/cli/src/agent/pr.ts
60475
60483
  function defaultTitle(issue) {
60476
60484
  return `${issue.identifier}: ${issue.title}`;
@@ -66963,7 +66971,8 @@ function ensureState(changeDir) {
66963
66971
 
66964
66972
  // apps/cli/src/components/TaskList.tsx
66965
66973
  var import_react22 = __toESM(require_react(), 1);
66966
- import { join as join3 } from "path";
66974
+ import { join as join4 } from "path";
66975
+ init_worktree();
66967
66976
  var jsx_dev_runtime = __toESM(require_jsx_dev_runtime(), 1);
66968
66977
  function countTaskItems(content) {
66969
66978
  const checked = (content.match(/^- \[x\]/gm) ?? []).length;
@@ -66976,10 +66985,10 @@ function buildRows(statesDir, projectRoot) {
66976
66985
  const seenNames = new Set;
66977
66986
  const sources = [{ dir: statesDir, label: "main" }];
66978
66987
  if (projectRoot) {
66979
- const worktreesRoot = join3(projectRoot, ".ralph", "worktrees");
66988
+ const worktreesRoot = worktreesDir(projectRoot);
66980
66989
  for (const wt of storage.list(worktreesRoot)) {
66981
66990
  sources.push({
66982
- dir: join3(worktreesRoot, wt, ".ralph", "tasks"),
66991
+ dir: join4(worktreesRoot, wt, ".ralph", "tasks"),
66983
66992
  label: `wt:${wt}`
66984
66993
  });
66985
66994
  }
@@ -66988,7 +66997,7 @@ function buildRows(statesDir, projectRoot) {
66988
66997
  for (const entry of storage.list(dir)) {
66989
66998
  if (seenNames.has(entry))
66990
66999
  continue;
66991
- const raw = storage.read(join3(dir, entry, ".ralph-state.json"));
67000
+ const raw = storage.read(join4(dir, entry, ".ralph-state.json"));
66992
67001
  if (raw === null)
66993
67002
  continue;
66994
67003
  let state;
@@ -67004,7 +67013,7 @@ function buildRows(statesDir, projectRoot) {
67004
67013
  `).find((l) => l.trim() !== "") ?? "";
67005
67014
  let progress = "\u2014";
67006
67015
  let progressStyled = true;
67007
- const tasksContent = storage.read(join3(dir, entry, "tasks.md"));
67016
+ const tasksContent = storage.read(join4(dir, entry, "tasks.md"));
67008
67017
  if (tasksContent !== null) {
67009
67018
  const { checked, unchecked } = countTaskItems(tasksContent);
67010
67019
  const total = checked + unchecked;
@@ -67145,7 +67154,7 @@ function TaskList({ statesDir, projectRoot }) {
67145
67154
  }
67146
67155
 
67147
67156
  // apps/cli/src/components/TaskStatus.tsx
67148
- import { join as join4 } from "path";
67157
+ import { join as join5 } from "path";
67149
67158
  var jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
67150
67159
  var HEAVY_RULE = "============================================";
67151
67160
  var LIGHT_RULE = "--------------------------------------------";
@@ -67156,7 +67165,7 @@ function TaskStatus({ state, stateDir }) {
67156
67165
  const time = Math.round(state.usage.total_duration_ms / 1000 * 10) / 10 + "s";
67157
67166
  const artifacts = OPENSPEC_ARTIFACTS.map((name) => ({
67158
67167
  name,
67159
- exists: storage.read(join4(stateDir, name)) !== null
67168
+ exists: storage.read(join5(stateDir, name)) !== null
67160
67169
  }));
67161
67170
  const recent = state.history.slice(-10);
67162
67171
  return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
@@ -67299,7 +67308,7 @@ function TaskStatus({ state, stateDir }) {
67299
67308
 
67300
67309
  // apps/cli/src/components/TaskLoop.tsx
67301
67310
  var import_react56 = __toESM(require_react(), 1);
67302
- import { join as join9 } from "path";
67311
+ import { join as join10 } from "path";
67303
67312
 
67304
67313
  // node_modules/.bun/@inkjs+ui@2.0.0+5b84dde3d6cd3930/node_modules/@inkjs/ui/build/components/badge/badge.js
67305
67314
  var import_react24 = __toESM(require_react(), 1);
@@ -70863,7 +70872,7 @@ function StopMessage({
70863
70872
 
70864
70873
  // apps/cli/src/hooks/useLoop.ts
70865
70874
  var import_react55 = __toESM(require_react(), 1);
70866
- import { join as join8 } from "path";
70875
+ import { join as join9 } from "path";
70867
70876
 
70868
70877
  // packages/engine/src/spawn.ts
70869
70878
  var {spawn: bunSpawn } = globalThis.Bun;
@@ -70873,7 +70882,7 @@ var spawn = bunSpawn;
70873
70882
  import { createWriteStream } from "fs";
70874
70883
  import { mkdtemp, unlink, mkdir } from "fs/promises";
70875
70884
  import { dirname as dirname2 } from "path";
70876
- import { join as join5 } from "path";
70885
+ import { join as join6 } from "path";
70877
70886
  import { tmpdir } from "os";
70878
70887
 
70879
70888
  // packages/engine/src/feed-events.ts
@@ -71606,7 +71615,7 @@ function buildCodexArgs() {
71606
71615
  return ["exec", "--json", "--color", "never", "--dangerously-bypass-approvals-and-sandbox", "-"];
71607
71616
  }
71608
71617
  async function runInteractive(model, prompt, taskDir) {
71609
- const promptFile = taskDir ? join5(taskDir, "_interactive_prompt.md") : join5(await mkdtemp(join5(tmpdir(), "ralph-")), "prompt.md");
71618
+ const promptFile = taskDir ? join6(taskDir, "_interactive_prompt.md") : join6(await mkdtemp(join6(tmpdir(), "ralph-")), "prompt.md");
71610
71619
  await Bun.write(promptFile, prompt);
71611
71620
  try {
71612
71621
  const cmd = [
@@ -71632,7 +71641,7 @@ async function runInteractive(model, prompt, taskDir) {
71632
71641
  stderr: "inherit"
71633
71642
  });
71634
71643
  const exitCode = await proc.exited;
71635
- const doneFile = taskDir ? join5(taskDir, "_interactive_done") : null;
71644
+ const doneFile = taskDir ? join6(taskDir, "_interactive_done") : null;
71636
71645
  if (doneFile && await Bun.file(doneFile).exists()) {
71637
71646
  return { exitCode: 0, usage: null, sessionId: null, rateLimited: false };
71638
71647
  }
@@ -71849,12 +71858,12 @@ function commitTaskDir(taskDir, message) {
71849
71858
  init_src();
71850
71859
 
71851
71860
  // packages/core/src/loop.ts
71852
- import { join as join7 } from "path";
71861
+ import { join as join8 } from "path";
71853
71862
  var STEERING_MAX_LINES = 20;
71854
71863
  function buildTaskPrompt(state, taskDir) {
71855
71864
  const storage = getStorage();
71856
71865
  let prompt = "";
71857
- const steeringContent = storage.read(join7(taskDir, "steering.md"));
71866
+ const steeringContent = storage.read(join8(taskDir, "steering.md"));
71858
71867
  if (steeringContent !== null) {
71859
71868
  const steeringLines = steeringContent.split(`
71860
71869
  `).filter((line) => !line.startsWith("#")).filter((line) => line.trim()).slice(0, STEERING_MAX_LINES);
@@ -71873,7 +71882,7 @@ function buildTaskPrompt(state, taskDir) {
71873
71882
  `;
71874
71883
  }
71875
71884
  }
71876
- const tasksContent = storage.read(join7(taskDir, "tasks.md"));
71885
+ const tasksContent = storage.read(join8(taskDir, "tasks.md"));
71877
71886
  if (tasksContent !== null) {
71878
71887
  const section = firstUnchecked(tasksContent);
71879
71888
  if (section) {
@@ -71888,7 +71897,7 @@ function buildTaskPrompt(state, taskDir) {
71888
71897
  prompt += `---
71889
71898
 
71890
71899
  `;
71891
- prompt += `**Tracking progress**: as you finish each item above, edit ` + `\`${join7(taskDir, "tasks.md")}\` and change its \`- [ ]\` to ` + `\`- [x]\` in the same commit. The loop reads this file between ` + `iterations and stops when no \`- [ ]\` items remain \u2014 if you do ` + `not tick the box, the next iteration will repeat this task.
71900
+ prompt += `**Tracking progress**: as you finish each item above, edit ` + `\`${join8(taskDir, "tasks.md")}\` and change its \`- [ ]\` to ` + `\`- [x]\` in the same commit. The loop reads this file between ` + `iterations and stops when no \`- [ ]\` items remain \u2014 if you do ` + `not tick the box, the next iteration will repeat this task.
71892
71901
 
71893
71902
  `;
71894
71903
  }
@@ -71909,7 +71918,7 @@ function buildTaskPrompt(state, taskDir) {
71909
71918
  `;
71910
71919
  }
71911
71920
  if (state.manualTest) {
71912
- const tasksContent2 = storage.read(join7(taskDir, "tasks.md"));
71921
+ const tasksContent2 = storage.read(join8(taskDir, "tasks.md"));
71913
71922
  const hasUncheckedTasks = tasksContent2 !== null && /^- \[ \]/m.test(tasksContent2);
71914
71923
  if (!hasUncheckedTasks) {
71915
71924
  const hasManualTestSection = tasksContent2 !== null && /^## Manual Testing/m.test(tasksContent2);
@@ -71958,7 +71967,7 @@ When all tasks are complete and all files are committed, push your branch and op
71958
71967
  }
71959
71968
  function checkStopSignal(taskDir, stateDir) {
71960
71969
  const storage = getStorage();
71961
- const stopFile = join7(taskDir, "STOP");
71970
+ const stopFile = join8(taskDir, "STOP");
71962
71971
  const reason = storage.read(stopFile);
71963
71972
  if (reason === null)
71964
71973
  return null;
@@ -72033,7 +72042,7 @@ function updateStateIteration(stateDir, result2, startedAt, engine, model, usage
72033
72042
  }
72034
72043
  function appendSteeringMessage(taskDir, message) {
72035
72044
  const storage = getStorage();
72036
- const steeringPath = join7(taskDir, "steering.md");
72045
+ const steeringPath = join8(taskDir, "steering.md");
72037
72046
  const existing = storage.read(steeringPath);
72038
72047
  const updated = existing ? `${message}
72039
72048
 
@@ -72100,11 +72109,11 @@ function useLoop(opts) {
72100
72109
  setLogLines((prev) => [...prev, { id: nextId(), kind: "feed", event }]);
72101
72110
  };
72102
72111
  runWithContext(createDefaultContext(), async () => {
72103
- const stateDir = join8(opts.statesDir, opts.name);
72104
- const tasksDir = join8(opts.tasksDir, opts.name);
72112
+ const stateDir = join9(opts.statesDir, opts.name);
72113
+ const tasksDir = join9(opts.tasksDir, opts.name);
72105
72114
  const storage = getStorage();
72106
72115
  let currentState;
72107
- const existingStateRaw = storage.read(join8(stateDir, ".ralph-state.json"));
72116
+ const existingStateRaw = storage.read(join9(stateDir, ".ralph-state.json"));
72108
72117
  if (existingStateRaw !== null) {
72109
72118
  currentState = readState(stateDir);
72110
72119
  if (currentState.engine !== opts.engine || currentState.model !== opts.model) {
@@ -72151,7 +72160,7 @@ function useLoop(opts) {
72151
72160
  setStopReason(stop);
72152
72161
  break;
72153
72162
  }
72154
- const tasksContent = storage.read(join8(tasksDir, "tasks.md"));
72163
+ const tasksContent = storage.read(join9(tasksDir, "tasks.md"));
72155
72164
  if (tasksContent !== null) {
72156
72165
  const remaining = countUnchecked(tasksContent);
72157
72166
  addInfo(`tasks.md: ${remaining} unchecked item${remaining === 1 ? "" : "s"} remaining`);
@@ -72191,7 +72200,7 @@ function useLoop(opts) {
72191
72200
  model: opts.model,
72192
72201
  prompt,
72193
72202
  logFlag: opts.log,
72194
- logFile: join8(stateDir, "log.json"),
72203
+ logFile: join9(stateDir, "log.json"),
72195
72204
  taskDir: tasksDir,
72196
72205
  interactive: false,
72197
72206
  onFeedEvent: addFeedEvent,
@@ -72214,7 +72223,7 @@ function useLoop(opts) {
72214
72223
  model: opts.model,
72215
72224
  prompt: buildSteeringPrompt(steerMessage),
72216
72225
  logFlag: opts.log,
72217
- logFile: join8(stateDir, "log.json"),
72226
+ logFile: join9(stateDir, "log.json"),
72218
72227
  taskDir: tasksDir,
72219
72228
  onFeedEvent: addResumeFeedEvent,
72220
72229
  signal: resumeController.signal,
@@ -72409,7 +72418,7 @@ function TaskLoop({ opts }) {
72409
72418
  }, [loop.isRunning, exit]);
72410
72419
  if (!loop.state)
72411
72420
  return null;
72412
- const stateDir = join9(opts.statesDir, opts.name);
72421
+ const stateDir = join10(opts.statesDir, opts.name);
72413
72422
  return /* @__PURE__ */ jsx_dev_runtime8.jsxDEV(Box_default, {
72414
72423
  flexDirection: "column",
72415
72424
  children: [
package/dist/mcp/index.js CHANGED
@@ -3047,10 +3047,13 @@ var require_data = __commonJS((exports, module) => {
3047
3047
  };
3048
3048
  });
3049
3049
 
3050
- // node_modules/.bun/fast-uri@3.1.0/node_modules/fast-uri/lib/utils.js
3050
+ // node_modules/.bun/fast-uri@3.1.2/node_modules/fast-uri/lib/utils.js
3051
3051
  var require_utils = __commonJS((exports, module) => {
3052
3052
  var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
3053
3053
  var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
3054
+ var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
3055
+ var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
3056
+ var isPathCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/]$/iu);
3054
3057
  function stringArrayToHexStripped(input) {
3055
3058
  let acc = "";
3056
3059
  let code = 0;
@@ -3244,27 +3247,77 @@ var require_utils = __commonJS((exports, module) => {
3244
3247
  }
3245
3248
  return output.join("");
3246
3249
  }
3247
- function normalizeComponentEncoding(component, esc2) {
3248
- const func = esc2 !== true ? escape : unescape;
3249
- if (component.scheme !== undefined) {
3250
- component.scheme = func(component.scheme);
3251
- }
3252
- if (component.userinfo !== undefined) {
3253
- component.userinfo = func(component.userinfo);
3254
- }
3255
- if (component.host !== undefined) {
3256
- component.host = func(component.host);
3250
+ var HOST_DELIMS = { "@": "%40", "/": "%2F", "?": "%3F", "#": "%23", ":": "%3A" };
3251
+ var HOST_DELIM_RE = /[@/?#:]/g;
3252
+ var HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
3253
+ function reescapeHostDelimiters(host, isIP) {
3254
+ const re = isIP ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE;
3255
+ re.lastIndex = 0;
3256
+ return host.replace(re, (ch) => HOST_DELIMS[ch]);
3257
+ }
3258
+ function normalizePercentEncoding(input, decodeUnreserved = false) {
3259
+ if (input.indexOf("%") === -1) {
3260
+ return input;
3257
3261
  }
3258
- if (component.path !== undefined) {
3259
- component.path = func(component.path);
3262
+ let output = "";
3263
+ for (let i = 0;i < input.length; i++) {
3264
+ if (input[i] === "%" && i + 2 < input.length) {
3265
+ const hex = input.slice(i + 1, i + 3);
3266
+ if (isHexPair(hex)) {
3267
+ const normalizedHex = hex.toUpperCase();
3268
+ const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
3269
+ if (decodeUnreserved && isUnreserved(decoded)) {
3270
+ output += decoded;
3271
+ } else {
3272
+ output += "%" + normalizedHex;
3273
+ }
3274
+ i += 2;
3275
+ continue;
3276
+ }
3277
+ }
3278
+ output += input[i];
3260
3279
  }
3261
- if (component.query !== undefined) {
3262
- component.query = func(component.query);
3280
+ return output;
3281
+ }
3282
+ function normalizePathEncoding(input) {
3283
+ let output = "";
3284
+ for (let i = 0;i < input.length; i++) {
3285
+ if (input[i] === "%" && i + 2 < input.length) {
3286
+ const hex = input.slice(i + 1, i + 3);
3287
+ if (isHexPair(hex)) {
3288
+ const normalizedHex = hex.toUpperCase();
3289
+ const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
3290
+ if (decoded !== "." && isUnreserved(decoded)) {
3291
+ output += decoded;
3292
+ } else {
3293
+ output += "%" + normalizedHex;
3294
+ }
3295
+ i += 2;
3296
+ continue;
3297
+ }
3298
+ }
3299
+ if (isPathCharacter(input[i])) {
3300
+ output += input[i];
3301
+ } else {
3302
+ output += escape(input[i]);
3303
+ }
3263
3304
  }
3264
- if (component.fragment !== undefined) {
3265
- component.fragment = func(component.fragment);
3305
+ return output;
3306
+ }
3307
+ function escapePreservingEscapes(input) {
3308
+ let output = "";
3309
+ for (let i = 0;i < input.length; i++) {
3310
+ if (input[i] === "%" && i + 2 < input.length) {
3311
+ const hex = input.slice(i + 1, i + 3);
3312
+ if (isHexPair(hex)) {
3313
+ output += "%" + hex.toUpperCase();
3314
+ i += 2;
3315
+ continue;
3316
+ }
3317
+ }
3318
+ output += escape(input[i]);
3266
3319
  }
3267
- return component;
3320
+ return output;
3268
3321
  }
3269
3322
  function recomposeAuthority(component) {
3270
3323
  const uriTokens = [];
@@ -3279,7 +3332,7 @@ var require_utils = __commonJS((exports, module) => {
3279
3332
  if (ipV6res.isIPV6 === true) {
3280
3333
  host = `[${ipV6res.escapedHost}]`;
3281
3334
  } else {
3282
- host = component.host;
3335
+ host = reescapeHostDelimiters(host, false);
3283
3336
  }
3284
3337
  }
3285
3338
  uriTokens.push(host);
@@ -3293,7 +3346,10 @@ var require_utils = __commonJS((exports, module) => {
3293
3346
  module.exports = {
3294
3347
  nonSimpleDomain,
3295
3348
  recomposeAuthority,
3296
- normalizeComponentEncoding,
3349
+ reescapeHostDelimiters,
3350
+ normalizePercentEncoding,
3351
+ normalizePathEncoding,
3352
+ escapePreservingEscapes,
3297
3353
  removeDotSegments,
3298
3354
  isIPv4,
3299
3355
  isUUID,
@@ -3302,7 +3358,7 @@ var require_utils = __commonJS((exports, module) => {
3302
3358
  };
3303
3359
  });
3304
3360
 
3305
- // node_modules/.bun/fast-uri@3.1.0/node_modules/fast-uri/lib/schemes.js
3361
+ // node_modules/.bun/fast-uri@3.1.2/node_modules/fast-uri/lib/schemes.js
3306
3362
  var require_schemes = __commonJS((exports, module) => {
3307
3363
  var { isUUID } = require_utils();
3308
3364
  var URN_REG = /([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-.:;=@]|%[\da-f]{2})+)/iu;
@@ -3476,13 +3532,13 @@ var require_schemes = __commonJS((exports, module) => {
3476
3532
  };
3477
3533
  });
3478
3534
 
3479
- // node_modules/.bun/fast-uri@3.1.0/node_modules/fast-uri/index.js
3535
+ // node_modules/.bun/fast-uri@3.1.2/node_modules/fast-uri/index.js
3480
3536
  var require_fast_uri = __commonJS((exports, module) => {
3481
- var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils();
3537
+ var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
3482
3538
  var { SCHEMES, getSchemeHandler } = require_schemes();
3483
3539
  function normalize(uri, options) {
3484
3540
  if (typeof uri === "string") {
3485
- uri = serialize(parse6(uri, options), options);
3541
+ uri = normalizeString(uri, options);
3486
3542
  } else if (typeof uri === "object") {
3487
3543
  uri = parse6(serialize(uri, options), options);
3488
3544
  }
@@ -3548,19 +3604,9 @@ var require_fast_uri = __commonJS((exports, module) => {
3548
3604
  return target;
3549
3605
  }
3550
3606
  function equal(uriA, uriB, options) {
3551
- if (typeof uriA === "string") {
3552
- uriA = unescape(uriA);
3553
- uriA = serialize(normalizeComponentEncoding(parse6(uriA, options), true), { ...options, skipEscape: true });
3554
- } else if (typeof uriA === "object") {
3555
- uriA = serialize(normalizeComponentEncoding(uriA, true), { ...options, skipEscape: true });
3556
- }
3557
- if (typeof uriB === "string") {
3558
- uriB = unescape(uriB);
3559
- uriB = serialize(normalizeComponentEncoding(parse6(uriB, options), true), { ...options, skipEscape: true });
3560
- } else if (typeof uriB === "object") {
3561
- uriB = serialize(normalizeComponentEncoding(uriB, true), { ...options, skipEscape: true });
3562
- }
3563
- return uriA.toLowerCase() === uriB.toLowerCase();
3607
+ const normalizedA = normalizeComparableURI(uriA, options);
3608
+ const normalizedB = normalizeComparableURI(uriB, options);
3609
+ return normalizedA !== undefined && normalizedB !== undefined && normalizedA.toLowerCase() === normalizedB.toLowerCase();
3564
3610
  }
3565
3611
  function serialize(cmpts, opts) {
3566
3612
  const component = {
@@ -3586,12 +3632,12 @@ var require_fast_uri = __commonJS((exports, module) => {
3586
3632
  schemeHandler.serialize(component, options);
3587
3633
  if (component.path !== undefined) {
3588
3634
  if (!options.skipEscape) {
3589
- component.path = escape(component.path);
3635
+ component.path = escapePreservingEscapes(component.path);
3590
3636
  if (component.scheme !== undefined) {
3591
3637
  component.path = component.path.split("%3A").join(":");
3592
3638
  }
3593
3639
  } else {
3594
- component.path = unescape(component.path);
3640
+ component.path = normalizePercentEncoding(component.path);
3595
3641
  }
3596
3642
  }
3597
3643
  if (options.reference !== "suffix" && component.scheme) {
@@ -3626,7 +3672,16 @@ var require_fast_uri = __commonJS((exports, module) => {
3626
3672
  return uriTokens.join("");
3627
3673
  }
3628
3674
  var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
3629
- function parse6(uri, opts) {
3675
+ function getParseError(parsed, matches) {
3676
+ if (matches[2] !== undefined && parsed.path && parsed.path[0] !== "/") {
3677
+ return 'URI path must start with "/" when authority is present.';
3678
+ }
3679
+ if (typeof parsed.port === "number" && (parsed.port < 0 || parsed.port > 65535)) {
3680
+ return "URI port is malformed.";
3681
+ }
3682
+ return;
3683
+ }
3684
+ function parseWithStatus(uri, opts) {
3630
3685
  const options = Object.assign({}, opts);
3631
3686
  const parsed = {
3632
3687
  scheme: undefined,
@@ -3637,6 +3692,7 @@ var require_fast_uri = __commonJS((exports, module) => {
3637
3692
  query: undefined,
3638
3693
  fragment: undefined
3639
3694
  };
3695
+ let malformedAuthorityOrPort = false;
3640
3696
  let isIP = false;
3641
3697
  if (options.reference === "suffix") {
3642
3698
  if (options.scheme) {
@@ -3657,6 +3713,11 @@ var require_fast_uri = __commonJS((exports, module) => {
3657
3713
  if (isNaN(parsed.port)) {
3658
3714
  parsed.port = matches[5];
3659
3715
  }
3716
+ const parseError = getParseError(parsed, matches);
3717
+ if (parseError !== undefined) {
3718
+ parsed.error = parsed.error || parseError;
3719
+ malformedAuthorityOrPort = true;
3720
+ }
3660
3721
  if (parsed.host) {
3661
3722
  const ipv4result = isIPv4(parsed.host);
3662
3723
  if (ipv4result === false) {
@@ -3695,14 +3756,18 @@ var require_fast_uri = __commonJS((exports, module) => {
3695
3756
  parsed.scheme = unescape(parsed.scheme);
3696
3757
  }
3697
3758
  if (parsed.host !== undefined) {
3698
- parsed.host = unescape(parsed.host);
3759
+ parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP);
3699
3760
  }
3700
3761
  }
3701
3762
  if (parsed.path) {
3702
- parsed.path = escape(unescape(parsed.path));
3763
+ parsed.path = normalizePathEncoding(parsed.path);
3703
3764
  }
3704
3765
  if (parsed.fragment) {
3705
- parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
3766
+ try {
3767
+ parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
3768
+ } catch {
3769
+ parsed.error = parsed.error || "URI malformed";
3770
+ }
3706
3771
  }
3707
3772
  }
3708
3773
  if (schemeHandler && schemeHandler.parse) {
@@ -3711,7 +3776,29 @@ var require_fast_uri = __commonJS((exports, module) => {
3711
3776
  } else {
3712
3777
  parsed.error = parsed.error || "URI can not be parsed.";
3713
3778
  }
3714
- return parsed;
3779
+ return { parsed, malformedAuthorityOrPort };
3780
+ }
3781
+ function parse6(uri, opts) {
3782
+ return parseWithStatus(uri, opts).parsed;
3783
+ }
3784
+ function normalizeString(uri, opts) {
3785
+ return normalizeStringWithStatus(uri, opts).normalized;
3786
+ }
3787
+ function normalizeStringWithStatus(uri, opts) {
3788
+ const { parsed, malformedAuthorityOrPort } = parseWithStatus(uri, opts);
3789
+ return {
3790
+ normalized: malformedAuthorityOrPort ? uri : serialize(parsed, opts),
3791
+ malformedAuthorityOrPort
3792
+ };
3793
+ }
3794
+ function normalizeComparableURI(uri, opts) {
3795
+ if (typeof uri === "string") {
3796
+ const { normalized, malformedAuthorityOrPort } = normalizeStringWithStatus(uri, opts);
3797
+ return malformedAuthorityOrPort ? undefined : normalized;
3798
+ }
3799
+ if (typeof uri === "object") {
3800
+ return serialize(uri, opts);
3801
+ }
3715
3802
  }
3716
3803
  var fastUri = {
3717
3804
  SCHEMES,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neriros/ralphy",
3
- "version": "2.17.2",
3
+ "version": "2.17.3",
4
4
  "description": "An iterative AI task execution framework. Orchestrates multi-phase autonomous work using Claude or Codex engines.",
5
5
  "keywords": [
6
6
  "agent",
@@ -57,33 +57,35 @@
57
57
  "prepublishOnly": "bun run build:publish"
58
58
  },
59
59
  "devDependencies": {
60
- "@commitlint/cli": "^20.4.3",
61
- "@commitlint/config-conventional": "^20.4.3",
60
+ "@commitlint/cli": "^20.5.3",
61
+ "@commitlint/config-conventional": "^20.5.3",
62
62
  "@fission-ai/openspec": "latest",
63
- "@modelcontextprotocol/sdk": "^1.12.0",
63
+ "@modelcontextprotocol/sdk": "^1.29.0",
64
64
  "@nx/devkit": "^22.7.1",
65
65
  "@nx/js": "^22.7.1",
66
- "@secretlint/secretlint-rule-preset-recommend": "^11.3.1",
66
+ "@secretlint/secretlint-rule-preset-recommend": "^11.7.1",
67
67
  "@swc-node/register": "^1.11.1",
68
- "@swc/core": "^1.15.18",
68
+ "@swc/core": "^1.15.33",
69
69
  "@total-typescript/ts-reset": "^0.6.1",
70
- "@types/node": "^22.0.0",
71
- "bun-types": "^1.3.0",
72
- "chalk": "^5.4.0",
73
- "dependency-cruiser": "^17.3.8",
70
+ "@types/node": "^22.19.18",
71
+ "bun-types": "^1.3.13",
72
+ "chalk": "^5.6.2",
73
+ "dependency-cruiser": "^17.4.0",
74
74
  "husky": "^9.1.7",
75
- "knip": "^5.85.0",
76
- "lint-staged": "^16.3.2",
75
+ "knip": "^5.88.1",
76
+ "lint-staged": "^16.4.0",
77
77
  "nx": "22.5.3",
78
78
  "oxc-parser": "^0.126.0",
79
79
  "oxfmt": "^0.36.0",
80
- "oxlint": "^1.51.0",
81
- "secretlint": "^11.3.1",
82
- "typescript": "^5.8.0",
83
- "zod": "^3.24.0"
80
+ "oxlint": "^1.63.0",
81
+ "secretlint": "^11.7.1",
82
+ "typescript": "^5.9.3",
83
+ "zod": "^3.25.76"
84
84
  },
85
85
  "overrides": {
86
+ "@babel/plugin-transform-modules-systemjs": "^7.29.4",
86
87
  "axios": "^1.15.1",
88
+ "fast-uri": "^3.1.2",
87
89
  "minimatch": "^10.2.3"
88
90
  },
89
91
  "engines": {