@neriros/ralphy 2.10.1 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli/index.js +93 -27
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -50837,8 +50837,8 @@ var require_axios = __commonJS((exports, module) => {
50837
50837
  });
50838
50838
 
50839
50839
  // apps/cli/src/index.ts
50840
- import { resolve, join as join17, dirname as dirname4 } from "path";
50841
- import { exists, mkdir as mkdir3, rm } from "fs/promises";
50840
+ import { resolve, join as join17, dirname as dirname5 } from "path";
50841
+ import { exists as exists2, mkdir as mkdir4, rm } from "fs/promises";
50842
50842
 
50843
50843
  // node_modules/.bun/ink@5.2.1+1f88f629f0141b18/node_modules/ink/build/render.js
50844
50844
  import { Stream } from "stream";
@@ -56407,7 +56407,7 @@ function log(msg) {
56407
56407
  // package.json
56408
56408
  var package_default = {
56409
56409
  name: "@neriros/ralphy",
56410
- version: "2.10.1",
56410
+ version: "2.11.0",
56411
56411
  description: "An iterative AI task execution framework. Orchestrates multi-phase autonomous work using Claude or Codex engines.",
56412
56412
  keywords: [
56413
56413
  "agent",
@@ -64894,7 +64894,9 @@ var {spawn: bunSpawn } = globalThis.Bun;
64894
64894
  var spawn = bunSpawn;
64895
64895
 
64896
64896
  // packages/engine/src/engine.ts
64897
- import { mkdtemp, unlink } from "fs/promises";
64897
+ import { createWriteStream } from "fs";
64898
+ import { mkdtemp, unlink, mkdir } from "fs/promises";
64899
+ import { dirname as dirname2 } from "path";
64898
64900
  import { join as join5 } from "path";
64899
64901
  import { tmpdir } from "os";
64900
64902
 
@@ -65711,6 +65713,21 @@ async function runEngine(opts) {
65711
65713
  stdin.write(new TextEncoder().encode(prompt));
65712
65714
  await stdin.flush();
65713
65715
  stdin.end();
65716
+ let rawWriter = null;
65717
+ if (opts.logFlag && opts.logFile) {
65718
+ await mkdir(dirname2(opts.logFile), { recursive: true });
65719
+ rawWriter = createWriteStream(opts.logFile, { flags: "a" });
65720
+ }
65721
+ const writeRaw = (line) => {
65722
+ if (rawWriter)
65723
+ rawWriter.write(line + `
65724
+ `);
65725
+ };
65726
+ const closeRaw = () => new Promise((resolve) => {
65727
+ if (!rawWriter)
65728
+ return resolve();
65729
+ rawWriter.end(resolve);
65730
+ });
65714
65731
  const emit = opts.onFeedEvent;
65715
65732
  function emitEvent(event) {
65716
65733
  if (emit) {
@@ -65745,6 +65762,7 @@ async function runEngine(opts) {
65745
65762
  usage: null
65746
65763
  };
65747
65764
  for await (const line of streamLines(stdout)) {
65765
+ writeRaw(line);
65748
65766
  if (sessionId === null) {
65749
65767
  try {
65750
65768
  const parsed = JSON.parse(line);
@@ -65772,6 +65790,7 @@ async function runEngine(opts) {
65772
65790
  pendingTools: 0
65773
65791
  };
65774
65792
  for await (const line of streamLines(stdout)) {
65793
+ writeRaw(line);
65775
65794
  for (const event of parseCodexLine(line, codexState)) {
65776
65795
  emitEvent(event);
65777
65796
  }
@@ -65779,12 +65798,14 @@ async function runEngine(opts) {
65779
65798
  if (proc.stderr) {
65780
65799
  const stderr = proc.stderr;
65781
65800
  for await (const line of streamLines(stderr)) {
65801
+ writeRaw(line);
65782
65802
  for (const event of parseCodexLine(line, codexState)) {
65783
65803
  emitEvent(event);
65784
65804
  }
65785
65805
  }
65786
65806
  }
65787
65807
  }
65808
+ await closeRaw();
65788
65809
  const exitCode = await proc.exited;
65789
65810
  const wasIntentionalKill = (exitCode === 143 || exitCode === 137) && (usage !== null || aborted);
65790
65811
  const normalizedExitCode = wasIntentionalKill ? 0 : exitCode;
@@ -65843,7 +65864,7 @@ function commitTaskDir(taskDir, message) {
65843
65864
  }
65844
65865
 
65845
65866
  // node_modules/.bun/posthog-node@4.18.0/node_modules/posthog-node/lib/node/index.mjs
65846
- import { posix, dirname as dirname2, sep } from "path";
65867
+ import { posix, dirname as dirname3, sep } from "path";
65847
65868
  import { createReadStream } from "fs";
65848
65869
  import { createInterface } from "readline";
65849
65870
  var NAME = "posthog-node";
@@ -66423,7 +66444,7 @@ class ErrorTracking {
66423
66444
  return !this.client.isDisabled && this._exceptionAutocaptureEnabled;
66424
66445
  }
66425
66446
  }
66426
- function createGetModuleFromFilename(basePath = process.argv[1] ? dirname2(process.argv[1]) : process.cwd(), isWindows3 = sep === "\\") {
66447
+ function createGetModuleFromFilename(basePath = process.argv[1] ? dirname3(process.argv[1]) : process.cwd(), isWindows3 = sep === "\\") {
66427
66448
  const normalizedBase = isWindows3 ? normalizeWindowsPath(basePath) : basePath;
66428
66449
  return (filename) => {
66429
66450
  if (!filename) {
@@ -69460,11 +69481,16 @@ var STEERING_MAX_LINES = 20;
69460
69481
  function extractFirstUncheckedSection(tasksContent) {
69461
69482
  const sections = tasksContent.split(/(?=^## )/m);
69462
69483
  for (const section of sections) {
69463
- if (/^- \[ \]/m.test(section))
69484
+ if (/^## /m.test(section) && /^- \[ \]/m.test(section))
69464
69485
  return section.trim();
69465
69486
  }
69487
+ if (/^- \[ \]/m.test(tasksContent))
69488
+ return tasksContent.trim();
69466
69489
  return null;
69467
69490
  }
69491
+ function countUncheckedTasks(tasksContent) {
69492
+ return (tasksContent.match(/^- \[ \]/gm) ?? []).length;
69493
+ }
69468
69494
  function allTasksCompleted(tasksContent) {
69469
69495
  return !/^- \[ \]/m.test(tasksContent);
69470
69496
  }
@@ -69504,6 +69530,9 @@ function buildTaskPrompt(state, taskDir) {
69504
69530
  `;
69505
69531
  prompt += `---
69506
69532
 
69533
+ `;
69534
+ 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.
69535
+
69507
69536
  `;
69508
69537
  }
69509
69538
  } else if (state.prompt) {
@@ -69723,6 +69752,10 @@ function useLoop(opts) {
69723
69752
  break;
69724
69753
  }
69725
69754
  const tasksContent = storage.read(join8(tasksDir, "tasks.md"));
69755
+ if (tasksContent !== null) {
69756
+ const remaining = countUncheckedTasks(tasksContent);
69757
+ addInfo(`tasks.md: ${remaining} unchecked item${remaining === 1 ? "" : "s"} remaining`);
69758
+ }
69726
69759
  if (tasksContent !== null && allTasksCompleted(tasksContent)) {
69727
69760
  addInfo("All tasks completed \u2014 archiving change.");
69728
69761
  currentState = {
@@ -69758,6 +69791,7 @@ function useLoop(opts) {
69758
69791
  model: opts.model,
69759
69792
  prompt,
69760
69793
  logFlag: opts.log,
69794
+ logFile: join8(stateDir, "log.json"),
69761
69795
  taskDir: tasksDir,
69762
69796
  interactive: false,
69763
69797
  onFeedEvent: addFeedEvent,
@@ -69780,6 +69814,7 @@ function useLoop(opts) {
69780
69814
  model: opts.model,
69781
69815
  prompt: buildSteeringPrompt(steerMessage),
69782
69816
  logFlag: opts.log,
69817
+ logFile: join8(stateDir, "log.json"),
69783
69818
  taskDir: tasksDir,
69784
69819
  onFeedEvent: addResumeFeedEvent,
69785
69820
  signal: resumeController.signal,
@@ -70262,7 +70297,7 @@ async function writeAgentState(projectRoot, state) {
70262
70297
 
70263
70298
  // apps/cli/src/agent/scaffold.ts
70264
70299
  import { join as join11 } from "path";
70265
- import { mkdir } from "fs/promises";
70300
+ import { mkdir as mkdir2 } from "fs/promises";
70266
70301
  function changeNameForIssue(issue) {
70267
70302
  const slug = issue.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
70268
70303
  return slug ? `${issue.identifier.toLowerCase()}-${slug}` : issue.identifier.toLowerCase();
@@ -70271,9 +70306,9 @@ async function scaffoldChangeForIssue(tasksDir, statesDir, issue, comments = [],
70271
70306
  const name = changeNameForIssue(issue);
70272
70307
  const changeDir = join11(tasksDir, name);
70273
70308
  const stateDir = join11(statesDir, name);
70274
- await mkdir(changeDir, { recursive: true });
70275
- await mkdir(join11(changeDir, "specs"), { recursive: true });
70276
- await mkdir(stateDir, { recursive: true });
70309
+ await mkdir2(changeDir, { recursive: true });
70310
+ await mkdir2(join11(changeDir, "specs"), { recursive: true });
70311
+ await mkdir2(stateDir, { recursive: true });
70277
70312
  const commentsBlock = comments.length > 0 ? [
70278
70313
  "",
70279
70314
  "## Linear comments",
@@ -70308,6 +70343,8 @@ async function scaffoldChangeForIssue(tasksDir, statesDir, issue, comments = [],
70308
70343
  const tasks = [
70309
70344
  `# Tasks for ${issue.identifier}`,
70310
70345
  "",
70346
+ "## Subtasks",
70347
+ "",
70311
70348
  `- [ ] Read the Linear issue at ${issue.url} and break it into concrete subtasks`,
70312
70349
  `- [ ] Implement the changes described in proposal.md`,
70313
70350
  `- [ ] Add or update tests covering the new behavior`,
@@ -70849,6 +70886,30 @@ ${logs}
70849
70886
  // apps/cli/src/components/AgentMode.tsx
70850
70887
  var jsx_dev_runtime9 = __toESM(require_jsx_dev_runtime(), 1);
70851
70888
  import { join as join14 } from "path";
70889
+ import { exists } from "fs/promises";
70890
+ async function seedWorktreeMcpConfig(projectRoot, worktreeCwd) {
70891
+ const dst = join14(worktreeCwd, ".mcp.json");
70892
+ const src = join14(projectRoot, ".mcp.json");
70893
+ const source = await exists(dst) ? dst : await exists(src) ? src : null;
70894
+ if (!source)
70895
+ return;
70896
+ let parsed;
70897
+ try {
70898
+ parsed = await Bun.file(source).json();
70899
+ } catch {
70900
+ return;
70901
+ }
70902
+ const servers = parsed.mcpServers;
70903
+ if (servers && typeof servers === "object") {
70904
+ for (const cfg of Object.values(servers)) {
70905
+ if (Array.isArray(cfg.args)) {
70906
+ cfg.args = cfg.args.map((a) => typeof a === "string" && a.startsWith(".ralph/") ? join14(projectRoot, a) : a);
70907
+ }
70908
+ }
70909
+ }
70910
+ await Bun.write(dst, JSON.stringify(parsed, null, 2) + `
70911
+ `);
70912
+ }
70852
70913
  var bunGitRunner = {
70853
70914
  run: async (args, cwd2) => {
70854
70915
  const proc = Bun.spawn({ cmd: ["git", ...args], cwd: cwd2, stdout: "pipe", stderr: "pipe" });
@@ -71007,6 +71068,11 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
71007
71068
  scaffoldTasksDir = join14(wt.cwd, "openspec", "changes");
71008
71069
  scaffoldStatesDir = join14(wt.cwd, ".ralph", "tasks");
71009
71070
  appendLog(` ${issue.identifier} worktree: ${wt.cwd} (${wt.branch})`, "gray");
71071
+ try {
71072
+ await seedWorktreeMcpConfig(projectRoot, wt.cwd);
71073
+ } catch (err) {
71074
+ appendLog(`! seeding .mcp.json failed for ${issue.identifier}: ${err.message}`, "yellow");
71075
+ }
71010
71076
  } catch (err) {
71011
71077
  appendLog(`! worktree create failed for ${issue.identifier}: ${err.message} \u2014 falling back to project root`, "yellow");
71012
71078
  }
@@ -71450,11 +71516,11 @@ ${check.unpushedCommits}`, "yellow");
71450
71516
  }
71451
71517
 
71452
71518
  // packages/openspec/src/openspec-change-store.ts
71453
- import { join as join15, dirname as dirname3 } from "path";
71454
- import { readdir, mkdir as mkdir2 } from "fs/promises";
71519
+ import { join as join15, dirname as dirname4 } from "path";
71520
+ import { readdir, mkdir as mkdir3 } from "fs/promises";
71455
71521
  function resolveOpenspecBin() {
71456
71522
  const pkgJsonPath = Bun.resolveSync("@fission-ai/openspec/package.json", import.meta.dir);
71457
- return join15(dirname3(pkgJsonPath), "bin", "openspec.js");
71523
+ return join15(dirname4(pkgJsonPath), "bin", "openspec.js");
71458
71524
  }
71459
71525
  function runOpenspec(args, options = {}) {
71460
71526
  const stdio = options.inherit ? ["inherit", "inherit", "inherit"] : ["ignore", "pipe", "pipe"];
@@ -71512,7 +71578,7 @@ class OpenSpecChangeStore {
71512
71578
  }
71513
71579
  async writeTaskList(name, content) {
71514
71580
  const path = join15("openspec", "changes", name, "tasks.md");
71515
- await mkdir2(dirname3(path), { recursive: true });
71581
+ await mkdir3(dirname4(path), { recursive: true });
71516
71582
  await Bun.write(path, content);
71517
71583
  }
71518
71584
  async appendSteering(name, message) {
@@ -71523,7 +71589,7 @@ class OpenSpecChangeStore {
71523
71589
 
71524
71590
  ${existing.trimStart()}` : `${message}
71525
71591
  `;
71526
- await mkdir2(dirname3(path), { recursive: true });
71592
+ await mkdir3(dirname4(path), { recursive: true });
71527
71593
  await Bun.write(path, updated);
71528
71594
  }
71529
71595
  async readSection(name, artifact, heading) {
@@ -71671,7 +71737,7 @@ if (typeof globalThis.Bun === "undefined") {
71671
71737
  async function findProjectRoot() {
71672
71738
  let dir = process.cwd();
71673
71739
  while (dir !== "/") {
71674
- if (await exists(join17(dir, "openspec")))
71740
+ if (await exists2(join17(dir, "openspec")))
71675
71741
  return dir;
71676
71742
  dir = resolve(dir, "..");
71677
71743
  }
@@ -71709,8 +71775,8 @@ try {
71709
71775
  const statesDir = join17(projectRoot, ".ralph", "tasks");
71710
71776
  const tasksDir = join17(projectRoot, "openspec", "changes");
71711
71777
  if (args.mode === "init") {
71712
- await mkdir3(statesDir, { recursive: true });
71713
- const openspecBin = join17(dirname4(Bun.resolveSync("@fission-ai/openspec/package.json", import.meta.dir)), "bin", "openspec.js");
71778
+ await mkdir4(statesDir, { recursive: true });
71779
+ const openspecBin = join17(dirname5(Bun.resolveSync("@fission-ai/openspec/package.json", import.meta.dir)), "bin", "openspec.js");
71714
71780
  Bun.spawnSync({
71715
71781
  cmd: [process.execPath, openspecBin, "init", "--tools", "none", "--force"],
71716
71782
  stdio: ["inherit", "inherit", "inherit"],
@@ -71728,7 +71794,7 @@ try {
71728
71794
  const stateDir = join17(statesDir, args.name);
71729
71795
  const branch = `ralph/${args.name}`;
71730
71796
  const removed = [];
71731
- if (await exists(worktreeDir)) {
71797
+ if (await exists2(worktreeDir)) {
71732
71798
  const proc = Bun.spawn({
71733
71799
  cmd: ["git", "worktree", "remove", "--force", worktreeDir],
71734
71800
  cwd: projectRoot,
@@ -71755,11 +71821,11 @@ try {
71755
71821
  });
71756
71822
  if (await branchProc.exited === 0)
71757
71823
  removed.push(`branch ${branch}`);
71758
- if (await exists(changeDir)) {
71824
+ if (await exists2(changeDir)) {
71759
71825
  await rm(changeDir, { recursive: true, force: true });
71760
71826
  removed.push(`openspec change ${changeDir}`);
71761
71827
  }
71762
- if (await exists(stateDir)) {
71828
+ if (await exists2(stateDir)) {
71763
71829
  await rm(stateDir, { recursive: true, force: true });
71764
71830
  removed.push(`task state ${stateDir}`);
71765
71831
  }
@@ -71786,13 +71852,13 @@ try {
71786
71852
  process.exit(0);
71787
71853
  }
71788
71854
  if (args.mode === "task" && args.name) {
71789
- await mkdir3(join17(statesDir, args.name), { recursive: true });
71790
- await mkdir3(join17(tasksDir, args.name), { recursive: true });
71855
+ await mkdir4(join17(statesDir, args.name), { recursive: true });
71856
+ await mkdir4(join17(tasksDir, args.name), { recursive: true });
71791
71857
  }
71792
71858
  if (args.mode === "agent") {
71793
- await mkdir3(statesDir, { recursive: true });
71794
- await mkdir3(tasksDir, { recursive: true });
71795
- await mkdir3(join17(projectRoot, ".ralph"), { recursive: true });
71859
+ await mkdir4(statesDir, { recursive: true });
71860
+ await mkdir4(tasksDir, { recursive: true });
71861
+ await mkdir4(join17(projectRoot, ".ralph"), { recursive: true });
71796
71862
  }
71797
71863
  await runWithContext(createDefaultContext(), async () => {
71798
71864
  const { waitUntilExit } = render_default(import_react59.createElement(App2, { args, statesDir, tasksDir, projectRoot }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neriros/ralphy",
3
- "version": "2.10.1",
3
+ "version": "2.11.0",
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",