@neriros/ralphy 2.4.1 → 2.5.1

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
@@ -35498,9 +35498,8 @@ var require_cjs = __commonJS((exports, module) => {
35498
35498
  });
35499
35499
 
35500
35500
  // apps/cli/src/index.ts
35501
- import { resolve, join as join11 } from "path";
35502
- import { existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
35503
- import { spawnSync as spawnSync2 } from "child_process";
35501
+ import { resolve, join as join11, dirname as dirname3 } from "path";
35502
+ import { exists, mkdir as mkdir2 } from "fs/promises";
35504
35503
 
35505
35504
  // node_modules/.bun/ink@5.2.1+1f88f629f0141b18/node_modules/ink/build/render.js
35506
35505
  import { Stream } from "stream";
@@ -41044,9 +41043,6 @@ var import_react21 = __toESM(require_react(), 1);
41044
41043
  // apps/cli/src/index.ts
41045
41044
  var import_react58 = __toESM(require_react(), 1);
41046
41045
 
41047
- // apps/cli/src/cli.ts
41048
- import { readFileSync as readFileSync2 } from "fs";
41049
-
41050
41046
  // packages/output/src/output.ts
41051
41047
  var formatters = {
41052
41048
  bold: (text) => source_default.bold(text),
@@ -41111,7 +41107,7 @@ var HELP_TEXT = [
41111
41107
  function printHelp() {
41112
41108
  log(HELP_TEXT);
41113
41109
  }
41114
- function parseArgs(argv) {
41110
+ async function parseArgs(argv) {
41115
41111
  const result2 = {
41116
41112
  mode: "task",
41117
41113
  name: "",
@@ -41150,7 +41146,7 @@ function parseArgs(argv) {
41150
41146
  }
41151
41147
  if (expectModelFlag) {
41152
41148
  if (!VALID_MODELS.has(arg)) {
41153
- throw new Error(`Invalid model '${arg}'. Valid models: ${[...VALID_MODELS].join(", ")}`);
41149
+ throw new Error("Invalid model");
41154
41150
  }
41155
41151
  result2.model = arg;
41156
41152
  expectModelFlag = false;
@@ -41167,7 +41163,7 @@ function parseArgs(argv) {
41167
41163
  continue;
41168
41164
  }
41169
41165
  if (expectPromptFile) {
41170
- result2.prompt = readFileSync2(arg, "utf-8");
41166
+ result2.prompt = await Bun.file(arg).text();
41171
41167
  expectPromptFile = false;
41172
41168
  continue;
41173
41169
  }
@@ -41270,9 +41266,7 @@ function parseArgs(argv) {
41270
41266
  if (VALID_MODES.has(arg)) {
41271
41267
  result2.mode = arg;
41272
41268
  } else {
41273
- throw new Error(`Unknown argument '${arg}'
41274
-
41275
- Run 'ralph --help' for usage information.`);
41269
+ throw new Error("Unknown argument. Run 'ralph --help' for usage information.");
41276
41270
  }
41277
41271
  break;
41278
41272
  }
@@ -41283,7 +41277,7 @@ Run 'ralph --help' for usage information.`);
41283
41277
  // packages/context/src/context.ts
41284
41278
  import { AsyncLocalStorage } from "async_hooks";
41285
41279
  import {
41286
- readFileSync as readFileSync3,
41280
+ readFileSync as readFileSync2,
41287
41281
  writeFileSync,
41288
41282
  existsSync as existsSync2,
41289
41283
  unlinkSync,
@@ -41296,7 +41290,7 @@ class FileSystemProvider {
41296
41290
  read(path) {
41297
41291
  if (!existsSync2(path))
41298
41292
  return null;
41299
- return readFileSync3(path, "utf-8");
41293
+ return readFileSync2(path, "utf-8");
41300
41294
  }
41301
41295
  write(path, content) {
41302
41296
  mkdirSync(dirname(path), { recursive: true });
@@ -41336,11 +41330,9 @@ function createDefaultContext() {
41336
41330
  // apps/cli/src/components/App.tsx
41337
41331
  var import_react57 = __toESM(require_react(), 1);
41338
41332
  import { join as join10 } from "path";
41339
- import { existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
41340
41333
 
41341
41334
  // packages/core/src/state.ts
41342
41335
  import { join as join2 } from "path";
41343
- import { execSync } from "child_process";
41344
41336
 
41345
41337
  // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/external.js
41346
41338
  var exports_external = {};
@@ -45394,7 +45386,7 @@ function readState(changeDir) {
45394
45386
  const filePath = join2(changeDir, STATE_FILE);
45395
45387
  const raw = getStorage().read(filePath);
45396
45388
  if (raw === null)
45397
- throw new Error(`.ralph-state.json not found in ${changeDir}`);
45389
+ throw new Error(".ralph-state.json not found");
45398
45390
  return StateSchema.parse(JSON.parse(raw));
45399
45391
  }
45400
45392
  function writeState(changeDir, state) {
@@ -45412,7 +45404,16 @@ function buildInitialState(options) {
45412
45404
  const now2 = new Date().toISOString();
45413
45405
  let branch = "main";
45414
45406
  try {
45415
- branch = execSync("git branch --show-current", { encoding: "utf-8" }).trim();
45407
+ const proc = Bun.spawnSync({
45408
+ cmd: ["git", "branch", "--show-current"],
45409
+ stdout: "pipe",
45410
+ stderr: "pipe"
45411
+ });
45412
+ if (proc.exitCode === 0) {
45413
+ const out = new TextDecoder().decode(proc.stdout).trim();
45414
+ if (out)
45415
+ branch = out;
45416
+ }
45416
45417
  } catch {}
45417
45418
  return StateSchema.parse({
45418
45419
  version: "2",
@@ -49316,7 +49317,7 @@ var {spawn: bunSpawn } = globalThis.Bun;
49316
49317
  var spawn = bunSpawn;
49317
49318
 
49318
49319
  // packages/engine/src/engine.ts
49319
- import { writeFileSync as writeFileSync2, unlinkSync as unlinkSync2, existsSync as existsSync3, mkdtempSync } from "fs";
49320
+ import { mkdtemp, unlink } from "fs/promises";
49320
49321
  import { join as join5 } from "path";
49321
49322
  import { tmpdir } from "os";
49322
49323
 
@@ -50049,8 +50050,8 @@ function buildCodexArgs() {
50049
50050
  return ["exec", "--json", "--color", "never", "--dangerously-bypass-approvals-and-sandbox", "-"];
50050
50051
  }
50051
50052
  async function runInteractive(model, prompt, taskDir) {
50052
- const promptFile = taskDir ? join5(taskDir, "_interactive_prompt.md") : join5(mkdtempSync(join5(tmpdir(), "ralph-")), "prompt.md");
50053
- writeFileSync2(promptFile, prompt);
50053
+ const promptFile = taskDir ? join5(taskDir, "_interactive_prompt.md") : join5(await mkdtemp(join5(tmpdir(), "ralph-")), "prompt.md");
50054
+ await Bun.write(promptFile, prompt);
50054
50055
  try {
50055
50056
  const cmd = [
50056
50057
  "claude",
@@ -50076,13 +50077,13 @@ async function runInteractive(model, prompt, taskDir) {
50076
50077
  });
50077
50078
  const exitCode = await proc.exited;
50078
50079
  const doneFile = taskDir ? join5(taskDir, "_interactive_done") : null;
50079
- if (doneFile && existsSync3(doneFile)) {
50080
+ if (doneFile && await Bun.file(doneFile).exists()) {
50080
50081
  return { exitCode: 0, usage: null, sessionId: null, rateLimited: false };
50081
50082
  }
50082
50083
  return { exitCode, usage: null, sessionId: null, rateLimited: false };
50083
50084
  } finally {
50084
50085
  try {
50085
- unlinkSync2(promptFile);
50086
+ await unlink(promptFile);
50086
50087
  } catch {}
50087
50088
  }
50088
50089
  }
@@ -50218,37 +50219,44 @@ function isRateLimitText(text) {
50218
50219
  }
50219
50220
 
50220
50221
  // packages/core/src/git.ts
50221
- import { execSync as execSync2 } from "child_process";
50222
+ function runGit(args) {
50223
+ const proc = Bun.spawnSync({
50224
+ cmd: ["git", ...args],
50225
+ stdout: "pipe",
50226
+ stderr: "pipe"
50227
+ });
50228
+ const decoder = new TextDecoder;
50229
+ return {
50230
+ exitCode: proc.exitCode,
50231
+ stdout: proc.stdout ? decoder.decode(proc.stdout) : "",
50232
+ stderr: proc.stderr ? decoder.decode(proc.stderr) : ""
50233
+ };
50234
+ }
50222
50235
  function getCurrentBranch() {
50223
- try {
50224
- return execSync2("git branch --show-current", { encoding: "utf-8" }).trim();
50225
- } catch {
50236
+ const result2 = runGit(["branch", "--show-current"]);
50237
+ if (result2.exitCode !== 0)
50226
50238
  return "main";
50227
- }
50239
+ return result2.stdout.trim() || "main";
50228
50240
  }
50229
50241
  function gitAdd(files) {
50230
- execSync2(`git add ${files.map((f) => `"${f}"`).join(" ")}`, {
50231
- stdio: "pipe"
50232
- });
50242
+ const result2 = runGit(["add", ...files]);
50243
+ if (result2.exitCode !== 0) {
50244
+ throw new Error("git add failed", { cause: { stderr: result2.stderr.trim() } });
50245
+ }
50233
50246
  }
50234
50247
  function gitCommit(message) {
50235
- execSync2(`git commit -m "${message.replace(/"/g, "\\\"")}"`, {
50236
- stdio: "pipe"
50237
- });
50248
+ const result2 = runGit(["commit", "-m", message]);
50249
+ if (result2.exitCode !== 0) {
50250
+ throw new Error("git commit failed", { cause: { stderr: result2.stderr.trim() } });
50251
+ }
50238
50252
  }
50239
50253
  function gitPush() {
50240
50254
  const branch = getCurrentBranch();
50241
- try {
50242
- execSync2("git push", { stdio: "pipe" });
50243
- } catch {
50244
- try {
50245
- execSync2(`git push -u origin ${branch}`, { stdio: "pipe" });
50246
- } catch {
50247
- try {
50248
- execSync2(`git push --set-upstream origin ${branch}`, { stdio: "pipe" });
50249
- } catch {}
50250
- }
50251
- }
50255
+ if (runGit(["push"]).exitCode === 0)
50256
+ return;
50257
+ if (runGit(["push", "-u", "origin", branch]).exitCode === 0)
50258
+ return;
50259
+ runGit(["push", "--set-upstream", "origin", branch]);
50252
50260
  }
50253
50261
  function commitTaskDir(taskDir, message) {
50254
50262
  try {
@@ -50815,123 +50823,121 @@ function TaskLoop({ opts }) {
50815
50823
  }
50816
50824
 
50817
50825
  // packages/openspec/src/openspec-change-store.ts
50818
- import { join as join9 } from "path";
50819
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, readdirSync as readdirSync2, existsSync as existsSync4 } from "fs";
50820
- import { spawnSync } from "child_process";
50821
- var runnerCache = null;
50822
- function resolveRunner() {
50823
- if (runnerCache)
50824
- return runnerCache;
50825
- for (const candidate of ["bunx", "npx"]) {
50826
- const probe = spawnSync(candidate, ["--version"], { encoding: "utf-8" });
50827
- if (!probe.error) {
50828
- runnerCache = candidate;
50829
- return candidate;
50830
- }
50831
- }
50832
- throw new Error("ralph requires `bunx` or `npx` on PATH to run `openspec`. Install bun (https://bun.sh/) or Node.js and re-run.");
50826
+ import { join as join9, dirname as dirname2 } from "path";
50827
+ import { readdir, mkdir } from "fs/promises";
50828
+ function resolveOpenspecBin() {
50829
+ const pkgJsonPath = Bun.resolveSync("@fission-ai/openspec/package.json", import.meta.dir);
50830
+ return join9(dirname2(pkgJsonPath), "bin", "openspec.js");
50833
50831
  }
50834
- function runBunx(args, options = {}) {
50835
- const runner = resolveRunner();
50836
- return spawnSync(runner, args, { ...options, encoding: "utf-8" });
50832
+ function runOpenspec(args, options = {}) {
50833
+ const stdio = options.inherit ? ["inherit", "inherit", "inherit"] : ["ignore", "pipe", "pipe"];
50834
+ const proc = Bun.spawnSync({
50835
+ cmd: [process.execPath, resolveOpenspecBin(), ...args],
50836
+ stdio
50837
+ });
50838
+ const decoder = new TextDecoder;
50839
+ return {
50840
+ status: proc.exitCode,
50841
+ stdout: proc.stdout ? decoder.decode(proc.stdout) : "",
50842
+ stderr: proc.stderr ? decoder.decode(proc.stderr) : ""
50843
+ };
50837
50844
  }
50838
50845
 
50839
50846
  class OpenSpecChangeStore {
50840
- createChange(name, description) {
50841
- const result2 = runBunx(["openspec", "new", "change", name, "--description", description], {
50842
- stdio: "inherit"
50847
+ async createChange(name, description) {
50848
+ const result2 = runOpenspec(["new", "change", name, "--description", description], {
50849
+ inherit: true
50843
50850
  });
50844
50851
  if (result2.status !== 0) {
50845
- throw new Error(`openspec new change failed with exit code ${result2.status ?? "unknown"}`);
50852
+ throw new Error("openspec new change failed");
50846
50853
  }
50847
- return Promise.resolve();
50848
50854
  }
50849
50855
  getChangeDirectory(name) {
50850
50856
  return join9("openspec", "changes", name);
50851
50857
  }
50852
- listChanges() {
50853
- const result2 = runBunx(["openspec", "list", "--json"]);
50858
+ async listChanges() {
50859
+ const result2 = runOpenspec(["list", "--json"]);
50854
50860
  if (result2.stdout) {
50855
50861
  try {
50856
50862
  const parsed = JSON.parse(result2.stdout);
50857
- if (Array.isArray(parsed)) {
50858
- return Promise.resolve(parsed.map((item) => String(item)));
50859
- }
50863
+ if (Array.isArray(parsed))
50864
+ return parsed.map((item) => String(item));
50860
50865
  if (parsed && typeof parsed === "object" && "changes" in parsed && parsed.changes) {
50861
- return Promise.resolve(parsed.changes.map((change) => change.name));
50866
+ return parsed.changes.map((change) => change.name);
50862
50867
  }
50863
50868
  } catch {}
50864
50869
  }
50865
50870
  const changesDir = join9("openspec", "changes");
50866
- if (!existsSync4(changesDir))
50867
- return Promise.resolve([]);
50868
- const names = readdirSync2(changesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
50869
- return Promise.resolve(names);
50871
+ if (!await Bun.file(changesDir).exists())
50872
+ return [];
50873
+ try {
50874
+ const entries = await readdir(changesDir, { withFileTypes: true });
50875
+ return entries.filter((entry) => entry.isDirectory() && entry.name !== "archive").map((entry) => entry.name);
50876
+ } catch {
50877
+ return [];
50878
+ }
50870
50879
  }
50871
- readTaskList(name) {
50872
- const path = join9("openspec", "changes", name, "tasks.md");
50873
- if (!existsSync4(path))
50874
- return Promise.resolve("");
50875
- return Promise.resolve(readFileSync4(path, "utf-8"));
50880
+ async readTaskList(name) {
50881
+ const file = Bun.file(join9("openspec", "changes", name, "tasks.md"));
50882
+ if (!await file.exists())
50883
+ return "";
50884
+ return await file.text();
50876
50885
  }
50877
- writeTaskList(name, content) {
50886
+ async writeTaskList(name, content) {
50878
50887
  const path = join9("openspec", "changes", name, "tasks.md");
50879
- writeFileSync3(path, content, "utf-8");
50880
- return Promise.resolve();
50888
+ await mkdir(dirname2(path), { recursive: true });
50889
+ await Bun.write(path, content);
50881
50890
  }
50882
- appendSteering(name, message) {
50891
+ async appendSteering(name, message) {
50883
50892
  const path = join9("openspec", "changes", name, "steering.md");
50884
- const existing = existsSync4(path) ? readFileSync4(path, "utf-8") : null;
50893
+ const file = Bun.file(path);
50894
+ const existing = await file.exists() ? await file.text() : null;
50885
50895
  const updated = existing ? `${message}
50886
50896
 
50887
50897
  ${existing.trimStart()}` : `${message}
50888
50898
  `;
50889
- writeFileSync3(path, updated, "utf-8");
50890
- return Promise.resolve();
50891
- }
50892
- readSection(name, artifact, heading) {
50893
- const path = join9("openspec", "changes", name, artifact);
50894
- if (!existsSync4(path))
50895
- return Promise.resolve("");
50896
- const content = readFileSync4(path, "utf-8");
50899
+ await mkdir(dirname2(path), { recursive: true });
50900
+ await Bun.write(path, updated);
50901
+ }
50902
+ async readSection(name, artifact, heading) {
50903
+ const file = Bun.file(join9("openspec", "changes", name, artifact));
50904
+ if (!await file.exists())
50905
+ return "";
50906
+ const content = await file.text();
50897
50907
  const headingIndex = content.indexOf(heading);
50898
50908
  if (headingIndex === -1)
50899
- return Promise.resolve("");
50909
+ return "";
50900
50910
  const afterHeading = content.slice(headingIndex + heading.length);
50901
50911
  const levelMatch = heading.match(/^(#+)/);
50902
50912
  const level = levelMatch ? levelMatch[1].length : 2;
50903
50913
  const nextHeadingPattern = new RegExp(`\\n#{1,${level}} `);
50904
50914
  const nextMatch = afterHeading.match(nextHeadingPattern);
50905
50915
  const sectionContent = nextMatch ? afterHeading.slice(0, nextMatch.index) : afterHeading;
50906
- return Promise.resolve(sectionContent.trim());
50916
+ return sectionContent.trim();
50907
50917
  }
50908
- validateChange(name) {
50909
- const result2 = runBunx(["openspec", "validate", name, "--json", "--no-interactive"]);
50918
+ async validateChange(name) {
50919
+ const result2 = runOpenspec(["validate", name, "--json", "--no-interactive"]);
50910
50920
  if (result2.stdout) {
50911
50921
  try {
50912
50922
  const parsed = JSON.parse(result2.stdout);
50913
- return Promise.resolve({
50923
+ return {
50914
50924
  valid: parsed.valid ?? result2.status === 0,
50915
50925
  warnings: parsed.warnings ?? [],
50916
50926
  errors: parsed.errors ?? []
50917
- });
50927
+ };
50918
50928
  } catch {}
50919
50929
  }
50920
- return Promise.resolve({
50930
+ return {
50921
50931
  valid: result2.status === 0,
50922
50932
  warnings: [],
50923
50933
  errors: result2.stderr ? [result2.stderr] : []
50924
- });
50934
+ };
50925
50935
  }
50926
- archiveChange(name) {
50927
- const result2 = spawnSync("bunx", ["openspec", "archive", name, "-y", "--skip-specs"], {
50928
- stdio: "inherit",
50929
- encoding: "utf-8"
50930
- });
50936
+ async archiveChange(name) {
50937
+ const result2 = runOpenspec(["archive", name, "-y", "--skip-specs"], { inherit: true });
50931
50938
  if (result2.status !== 0) {
50932
- throw new Error(`openspec archive failed with exit code ${result2.status ?? "unknown"}`);
50939
+ throw new Error("openspec archive failed");
50933
50940
  }
50934
- return Promise.resolve();
50935
50941
  }
50936
50942
  }
50937
50943
  // apps/cli/src/components/App.tsx
@@ -50969,7 +50975,7 @@ function App2({ args, statesDir, tasksDir }) {
50969
50975
  }, undefined, false, undefined, this);
50970
50976
  }
50971
50977
  const stateDir = join10(statesDir, args.name);
50972
- if (!existsSync5(join10(stateDir, ".ralph-state.json"))) {
50978
+ if (getStorage().read(join10(stateDir, ".ralph-state.json")) === null) {
50973
50979
  return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(ErrorMessage, {
50974
50980
  message: `Error: change '${args.name}' not found`
50975
50981
  }, undefined, false, undefined, this);
@@ -50995,8 +51001,6 @@ function App2({ args, statesDir, tasksDir }) {
50995
51001
  message: "Error: --name is required for task mode"
50996
51002
  }, undefined, false, undefined, this);
50997
51003
  }
50998
- mkdirSync2(join10(statesDir, args.name), { recursive: true });
50999
- mkdirSync2(join10(tasksDir, args.name), { recursive: true });
51000
51004
  return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(TaskLoop, {
51001
51005
  opts: {
51002
51006
  name: args.name,
@@ -51020,27 +51024,47 @@ function App2({ args, statesDir, tasksDir }) {
51020
51024
  }
51021
51025
 
51022
51026
  // apps/cli/src/index.ts
51023
- function findProjectRoot() {
51027
+ if (typeof globalThis.Bun === "undefined") {
51028
+ process.stderr.write(`ralph requires the Bun runtime (https://bun.sh/). It is not compatible with plain Node.js.
51029
+ ` + "Install Bun and re-run with `bun` or `bunx ralphy`.\n");
51030
+ process.exit(1);
51031
+ }
51032
+ async function findProjectRoot() {
51024
51033
  let dir = process.cwd();
51025
51034
  while (dir !== "/") {
51026
- if (existsSync6(join11(dir, "openspec")))
51035
+ if (await exists(join11(dir, "openspec")))
51027
51036
  return dir;
51028
51037
  dir = resolve(dir, "..");
51029
51038
  }
51030
51039
  return process.cwd();
51031
51040
  }
51041
+ var args;
51042
+ try {
51043
+ args = await parseArgs(process.argv.slice(2));
51044
+ } catch (err) {
51045
+ process.stderr.write((err instanceof Error ? err.message : String(err)) + `
51046
+
51047
+ `);
51048
+ printHelp();
51049
+ process.exit(1);
51050
+ }
51032
51051
  try {
51033
- const args = parseArgs(process.argv.slice(2));
51034
- const projectRoot = findProjectRoot();
51052
+ const projectRoot = await findProjectRoot();
51035
51053
  const statesDir = join11(projectRoot, ".ralph", "tasks");
51036
51054
  const tasksDir = join11(projectRoot, "openspec", "changes");
51037
51055
  if (args.mode === "init") {
51038
- mkdirSync3(statesDir, { recursive: true });
51039
- spawnSync2("bunx", ["openspec", "init", "--tools", "none", "--force"], {
51040
- stdio: "inherit",
51056
+ await mkdir2(statesDir, { recursive: true });
51057
+ const openspecBin = join11(dirname3(Bun.resolveSync("@fission-ai/openspec/package.json", import.meta.dir)), "bin", "openspec.js");
51058
+ Bun.spawnSync({
51059
+ cmd: [process.execPath, openspecBin, "init", "--tools", "none", "--force"],
51060
+ stdio: ["inherit", "inherit", "inherit"],
51041
51061
  cwd: process.cwd()
51042
51062
  });
51043
51063
  }
51064
+ if (args.mode === "task" && args.name) {
51065
+ await mkdir2(join11(statesDir, args.name), { recursive: true });
51066
+ await mkdir2(join11(tasksDir, args.name), { recursive: true });
51067
+ }
51044
51068
  runWithContext(createDefaultContext(), () => {
51045
51069
  render_default(import_react58.createElement(App2, { args, statesDir, tasksDir }));
51046
51070
  });
package/dist/mcp/index.js CHANGED
@@ -6519,7 +6519,7 @@ var require_dist = __commonJS((exports, module) => {
6519
6519
 
6520
6520
  // apps/mcp/src/index.ts
6521
6521
  import { resolve, join as join4 } from "path";
6522
- import { existsSync as existsSync3 } from "fs";
6522
+ import { exists } from "fs/promises";
6523
6523
 
6524
6524
  // packages/context/src/context.ts
6525
6525
  import { AsyncLocalStorage } from "async_hooks";
@@ -19946,7 +19946,6 @@ class StdioServerTransport {
19946
19946
 
19947
19947
  // apps/mcp/src/tools.ts
19948
19948
  import { join as join2 } from "path";
19949
- import { spawn } from "child_process";
19950
19949
 
19951
19950
  // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/external.js
19952
19951
  var exports_external = {};
@@ -23923,7 +23922,6 @@ var coerce = {
23923
23922
  var NEVER2 = INVALID2;
23924
23923
  // packages/core/src/state.ts
23925
23924
  import { join } from "path";
23926
- import { execSync } from "child_process";
23927
23925
 
23928
23926
  // packages/types/src/types.ts
23929
23927
  var IterationUsageSchema = exports_external.object({
@@ -24004,7 +24002,7 @@ function readState(changeDir) {
24004
24002
  const filePath = join(changeDir, STATE_FILE);
24005
24003
  const raw = getStorage().read(filePath);
24006
24004
  if (raw === null)
24007
- throw new Error(`.ralph-state.json not found in ${changeDir}`);
24005
+ throw new Error(".ralph-state.json not found");
24008
24006
  return StateSchema.parse(JSON.parse(raw));
24009
24007
  }
24010
24008
  function writeState(changeDir, state) {
@@ -24016,7 +24014,16 @@ function buildInitialState(options) {
24016
24014
  const now = new Date().toISOString();
24017
24015
  let branch = "main";
24018
24016
  try {
24019
- branch = execSync("git branch --show-current", { encoding: "utf-8" }).trim();
24017
+ const proc = Bun.spawnSync({
24018
+ cmd: ["git", "branch", "--show-current"],
24019
+ stdout: "pipe",
24020
+ stderr: "pipe"
24021
+ });
24022
+ if (proc.exitCode === 0) {
24023
+ const out = new TextDecoder().decode(proc.stdout).trim();
24024
+ if (out)
24025
+ branch = out;
24026
+ }
24020
24027
  } catch {}
24021
24028
  return StateSchema.parse({
24022
24029
  version: "2",
@@ -24177,9 +24184,11 @@ function registerTools(server, changesDir, changeStore, taskFilesDir = changesDi
24177
24184
  cliArgs.push("--" + engine);
24178
24185
  if (model)
24179
24186
  cliArgs.push("--model", model);
24180
- const child = spawn("bun", cliArgs, {
24181
- detached: true,
24182
- stdio: "ignore"
24187
+ const child = Bun.spawn({
24188
+ cmd: ["bun", ...cliArgs],
24189
+ stdin: "ignore",
24190
+ stdout: "ignore",
24191
+ stderr: "ignore"
24183
24192
  });
24184
24193
  child.unref();
24185
24194
  return {
@@ -24800,130 +24809,128 @@ function error2(msg) {
24800
24809
  }
24801
24810
 
24802
24811
  // packages/openspec/src/openspec-change-store.ts
24803
- import { join as join3 } from "path";
24804
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, readdirSync as readdirSync2, existsSync as existsSync2 } from "fs";
24805
- import { spawnSync } from "child_process";
24806
- var runnerCache = null;
24807
- function resolveRunner() {
24808
- if (runnerCache)
24809
- return runnerCache;
24810
- for (const candidate of ["bunx", "npx"]) {
24811
- const probe = spawnSync(candidate, ["--version"], { encoding: "utf-8" });
24812
- if (!probe.error) {
24813
- runnerCache = candidate;
24814
- return candidate;
24815
- }
24816
- }
24817
- throw new Error("ralph requires `bunx` or `npx` on PATH to run `openspec`. Install bun (https://bun.sh/) or Node.js and re-run.");
24818
- }
24819
- function runBunx(args, options = {}) {
24820
- const runner = resolveRunner();
24821
- return spawnSync(runner, args, { ...options, encoding: "utf-8" });
24812
+ import { join as join3, dirname as dirname2 } from "path";
24813
+ import { readdir, mkdir } from "fs/promises";
24814
+ function resolveOpenspecBin() {
24815
+ const pkgJsonPath = Bun.resolveSync("@fission-ai/openspec/package.json", import.meta.dir);
24816
+ return join3(dirname2(pkgJsonPath), "bin", "openspec.js");
24817
+ }
24818
+ function runOpenspec(args, options = {}) {
24819
+ const stdio = options.inherit ? ["inherit", "inherit", "inherit"] : ["ignore", "pipe", "pipe"];
24820
+ const proc = Bun.spawnSync({
24821
+ cmd: [process.execPath, resolveOpenspecBin(), ...args],
24822
+ stdio
24823
+ });
24824
+ const decoder = new TextDecoder;
24825
+ return {
24826
+ status: proc.exitCode,
24827
+ stdout: proc.stdout ? decoder.decode(proc.stdout) : "",
24828
+ stderr: proc.stderr ? decoder.decode(proc.stderr) : ""
24829
+ };
24822
24830
  }
24823
24831
 
24824
24832
  class OpenSpecChangeStore {
24825
- createChange(name, description) {
24826
- const result = runBunx(["openspec", "new", "change", name, "--description", description], {
24827
- stdio: "inherit"
24833
+ async createChange(name, description) {
24834
+ const result = runOpenspec(["new", "change", name, "--description", description], {
24835
+ inherit: true
24828
24836
  });
24829
24837
  if (result.status !== 0) {
24830
- throw new Error(`openspec new change failed with exit code ${result.status ?? "unknown"}`);
24838
+ throw new Error("openspec new change failed");
24831
24839
  }
24832
- return Promise.resolve();
24833
24840
  }
24834
24841
  getChangeDirectory(name) {
24835
24842
  return join3("openspec", "changes", name);
24836
24843
  }
24837
- listChanges() {
24838
- const result = runBunx(["openspec", "list", "--json"]);
24844
+ async listChanges() {
24845
+ const result = runOpenspec(["list", "--json"]);
24839
24846
  if (result.stdout) {
24840
24847
  try {
24841
24848
  const parsed = JSON.parse(result.stdout);
24842
- if (Array.isArray(parsed)) {
24843
- return Promise.resolve(parsed.map((item) => String(item)));
24844
- }
24849
+ if (Array.isArray(parsed))
24850
+ return parsed.map((item) => String(item));
24845
24851
  if (parsed && typeof parsed === "object" && "changes" in parsed && parsed.changes) {
24846
- return Promise.resolve(parsed.changes.map((change) => change.name));
24852
+ return parsed.changes.map((change) => change.name);
24847
24853
  }
24848
24854
  } catch {}
24849
24855
  }
24850
24856
  const changesDir = join3("openspec", "changes");
24851
- if (!existsSync2(changesDir))
24852
- return Promise.resolve([]);
24853
- const names = readdirSync2(changesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
24854
- return Promise.resolve(names);
24857
+ if (!await Bun.file(changesDir).exists())
24858
+ return [];
24859
+ try {
24860
+ const entries = await readdir(changesDir, { withFileTypes: true });
24861
+ return entries.filter((entry) => entry.isDirectory() && entry.name !== "archive").map((entry) => entry.name);
24862
+ } catch {
24863
+ return [];
24864
+ }
24855
24865
  }
24856
- readTaskList(name) {
24857
- const path = join3("openspec", "changes", name, "tasks.md");
24858
- if (!existsSync2(path))
24859
- return Promise.resolve("");
24860
- return Promise.resolve(readFileSync2(path, "utf-8"));
24866
+ async readTaskList(name) {
24867
+ const file = Bun.file(join3("openspec", "changes", name, "tasks.md"));
24868
+ if (!await file.exists())
24869
+ return "";
24870
+ return await file.text();
24861
24871
  }
24862
- writeTaskList(name, content) {
24872
+ async writeTaskList(name, content) {
24863
24873
  const path = join3("openspec", "changes", name, "tasks.md");
24864
- writeFileSync2(path, content, "utf-8");
24865
- return Promise.resolve();
24874
+ await mkdir(dirname2(path), { recursive: true });
24875
+ await Bun.write(path, content);
24866
24876
  }
24867
- appendSteering(name, message) {
24877
+ async appendSteering(name, message) {
24868
24878
  const path = join3("openspec", "changes", name, "steering.md");
24869
- const existing = existsSync2(path) ? readFileSync2(path, "utf-8") : null;
24879
+ const file = Bun.file(path);
24880
+ const existing = await file.exists() ? await file.text() : null;
24870
24881
  const updated = existing ? `${message}
24871
24882
 
24872
24883
  ${existing.trimStart()}` : `${message}
24873
24884
  `;
24874
- writeFileSync2(path, updated, "utf-8");
24875
- return Promise.resolve();
24876
- }
24877
- readSection(name, artifact, heading) {
24878
- const path = join3("openspec", "changes", name, artifact);
24879
- if (!existsSync2(path))
24880
- return Promise.resolve("");
24881
- const content = readFileSync2(path, "utf-8");
24885
+ await mkdir(dirname2(path), { recursive: true });
24886
+ await Bun.write(path, updated);
24887
+ }
24888
+ async readSection(name, artifact, heading) {
24889
+ const file = Bun.file(join3("openspec", "changes", name, artifact));
24890
+ if (!await file.exists())
24891
+ return "";
24892
+ const content = await file.text();
24882
24893
  const headingIndex = content.indexOf(heading);
24883
24894
  if (headingIndex === -1)
24884
- return Promise.resolve("");
24895
+ return "";
24885
24896
  const afterHeading = content.slice(headingIndex + heading.length);
24886
24897
  const levelMatch = heading.match(/^(#+)/);
24887
24898
  const level = levelMatch ? levelMatch[1].length : 2;
24888
24899
  const nextHeadingPattern = new RegExp(`\\n#{1,${level}} `);
24889
24900
  const nextMatch = afterHeading.match(nextHeadingPattern);
24890
24901
  const sectionContent = nextMatch ? afterHeading.slice(0, nextMatch.index) : afterHeading;
24891
- return Promise.resolve(sectionContent.trim());
24902
+ return sectionContent.trim();
24892
24903
  }
24893
- validateChange(name) {
24894
- const result = runBunx(["openspec", "validate", name, "--json", "--no-interactive"]);
24904
+ async validateChange(name) {
24905
+ const result = runOpenspec(["validate", name, "--json", "--no-interactive"]);
24895
24906
  if (result.stdout) {
24896
24907
  try {
24897
24908
  const parsed = JSON.parse(result.stdout);
24898
- return Promise.resolve({
24909
+ return {
24899
24910
  valid: parsed.valid ?? result.status === 0,
24900
24911
  warnings: parsed.warnings ?? [],
24901
24912
  errors: parsed.errors ?? []
24902
- });
24913
+ };
24903
24914
  } catch {}
24904
24915
  }
24905
- return Promise.resolve({
24916
+ return {
24906
24917
  valid: result.status === 0,
24907
24918
  warnings: [],
24908
24919
  errors: result.stderr ? [result.stderr] : []
24909
- });
24920
+ };
24910
24921
  }
24911
- archiveChange(name) {
24912
- const result = spawnSync("bunx", ["openspec", "archive", name, "-y", "--skip-specs"], {
24913
- stdio: "inherit",
24914
- encoding: "utf-8"
24915
- });
24922
+ async archiveChange(name) {
24923
+ const result = runOpenspec(["archive", name, "-y", "--skip-specs"], { inherit: true });
24916
24924
  if (result.status !== 0) {
24917
- throw new Error(`openspec archive failed with exit code ${result.status ?? "unknown"}`);
24925
+ throw new Error("openspec archive failed");
24918
24926
  }
24919
- return Promise.resolve();
24920
24927
  }
24921
24928
  }
24922
24929
  // apps/mcp/src/index.ts
24923
- function findProjectRoot(startDir) {
24930
+ async function findProjectRoot(startDir) {
24924
24931
  let dir = startDir;
24925
24932
  while (dir !== "/") {
24926
- if (existsSync3(join4(dir, "openspec")))
24933
+ if (await exists(join4(dir, "openspec")))
24927
24934
  return dir;
24928
24935
  dir = resolve(dir, "..");
24929
24936
  }
@@ -24936,7 +24943,7 @@ async function main() {
24936
24943
  if (dirIdx !== -1 && args[dirIdx + 1]) {
24937
24944
  startDir = resolve(args[dirIdx + 1]);
24938
24945
  }
24939
- const projectRoot = findProjectRoot(startDir);
24946
+ const projectRoot = await findProjectRoot(startDir);
24940
24947
  const changesDir = join4(projectRoot, ".ralph", "tasks");
24941
24948
  const taskFilesDir = join4(projectRoot, "openspec", "changes");
24942
24949
  const changeStore = new OpenSpecChangeStore;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neriros/ralphy",
3
- "version": "2.4.1",
3
+ "version": "2.5.1",
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",
@@ -41,12 +41,16 @@
41
41
  "typecheck": "nx affected -t typecheck",
42
42
  "check:deps": "depcruise packages/*/src apps/*/src --config .dependency-cruiser.cjs",
43
43
  "check:unused": "knip",
44
- "lint:ci": "nx affected -t lint",
45
- "fmt:ci": "nx affected -t fmt:check",
46
- "typecheck:ci": "nx affected -t typecheck --parallel=1",
47
- "test:ci": "nx affected -t test",
48
- "test:coverage:ci": "nx affected -t test:coverage",
44
+ "lint:ci": "nx affected -t lint --exclude=ui",
45
+ "fmt:ci": "nx affected -t fmt:check --exclude=ui",
46
+ "typecheck:ci": "nx affected -t typecheck --parallel=1 --exclude=ui",
47
+ "test:ci": "nx affected -t test --exclude=ui",
48
+ "test:affected-files:coverage:ci": "bun scripts/bun-test-affected-files.ts --coverage",
49
+ "test:coverage:ci": "nx affected -t test:coverage --exclude=ui",
50
+ "build:ci": "nx affected -t build --exclude=ui",
49
51
  "check:circular:ci": "depcruise packages/*/src apps/*/src --config .dependency-cruiser.cjs",
52
+ "check:unused:ci": "knip",
53
+ "check:outdated:ci": "bun scripts/check-outdated.ts",
50
54
  "prepare": "bunx husky",
51
55
  "build:publish": "bunx nx run-many --target=build --projects=cli,mcp --output-style stream && bun run copy-assets",
52
56
  "copy-assets": "bun scripts/copy-assets.ts",
@@ -57,7 +61,7 @@
57
61
  "@commitlint/config-conventional": "^20.4.3",
58
62
  "@fission-ai/openspec": "latest",
59
63
  "@modelcontextprotocol/sdk": "^1.12.0",
60
- "@nx/devkit": "^22.5.3",
64
+ "@nx/devkit": "^22.6.5",
61
65
  "@nx/js": "^22.5.3",
62
66
  "@secretlint/secretlint-rule-preset-recommend": "^11.3.1",
63
67
  "@swc-node/register": "^1.11.1",
@@ -67,17 +71,20 @@
67
71
  "bun-types": "^1.3.0",
68
72
  "chalk": "^5.4.0",
69
73
  "dependency-cruiser": "^17.3.8",
70
- "front-matter": "^4.0.2",
71
74
  "husky": "^9.1.7",
72
75
  "knip": "^5.85.0",
73
76
  "lint-staged": "^16.3.2",
74
77
  "nx": "22.5.3",
78
+ "oxc-parser": "^0.126.0",
75
79
  "oxfmt": "^0.36.0",
76
80
  "oxlint": "^1.51.0",
77
81
  "secretlint": "^11.3.1",
78
82
  "typescript": "^5.8.0",
79
83
  "zod": "^3.24.0"
80
84
  },
85
+ "overrides": {
86
+ "minimatch": "^10.2.3"
87
+ },
81
88
  "engines": {
82
89
  "bun": ">=1.0.0"
83
90
  }