@neriros/ralphy 2.9.2 → 2.9.4

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 +162 -66
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -56407,7 +56407,7 @@ function log(msg) {
56407
56407
  // package.json
56408
56408
  var package_default = {
56409
56409
  name: "@neriros/ralphy",
56410
- version: "2.9.2",
56410
+ version: "2.9.4",
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",
@@ -70806,6 +70806,29 @@ function nextId() {
70806
70806
  return `${Date.now()}-${lineCounter}`;
70807
70807
  }
70808
70808
  var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
70809
+ async function injectFixSteering(changeDir, heading, steering) {
70810
+ const steeringFile = Bun.file(join14(changeDir, "steering.md"));
70811
+ const existing = await steeringFile.exists() ? await steeringFile.text() : "";
70812
+ const stamped = `## ${heading} (${new Date().toISOString()})
70813
+
70814
+ ${steering}
70815
+ `;
70816
+ const nextSteering = existing ? `${stamped}
70817
+ ${existing.trimStart()}` : `${stamped}
70818
+ `;
70819
+ await Bun.write(join14(changeDir, "steering.md"), nextSteering);
70820
+ const tasksFile = Bun.file(join14(changeDir, "tasks.md"));
70821
+ const tasks = await tasksFile.exists() ? await tasksFile.text() : "";
70822
+ const taskSection = `
70823
+ ## ${heading} (${new Date().toISOString()})
70824
+
70825
+ ` + `- [ ] ${heading}. The error output is recorded in steering.md \u2014 read it first, ` + `then fix the underlying problem (do not just retry the failing command).
70826
+ `;
70827
+ const nextTasks = tasks.endsWith(`
70828
+ `) ? tasks + taskSection : tasks + `
70829
+ ` + taskSection;
70830
+ await Bun.write(join14(changeDir, "tasks.md"), nextTasks);
70831
+ }
70809
70832
  function fmtElapsed(ms) {
70810
70833
  const s = Math.floor(ms / 1000);
70811
70834
  if (s < 60)
@@ -70988,76 +71011,149 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
70988
71011
  appendLog(`! createPr requested but no worktree branch is tracked for ${changeName} (use --worktree)`, "yellow");
70989
71012
  effectiveCode = PR_FAILED_EXIT;
70990
71013
  } else {
70991
- try {
70992
- const pr = await createPullRequest({ cwd: cwd2, branch, issue: prIssue, base: cfg.prBaseBranch }, bunCmdRunner);
70993
- if (!pr) {
70994
- appendLog(` no commits ahead of ${cfg.prBaseBranch} \u2014 skipping PR`, "gray");
70995
- } else {
70996
- appendLog(` ${pr.created ? "opened" : "found existing"} PR: ${pr.url}`, "green");
70997
- const wantFixCi = args.fixCi || cfg.fixCiOnFailure;
70998
- if (wantFixCi) {
70999
- appendLog(` watching CI for ${pr.url} (max ${cfg.maxCiFixAttempts} fix attempts)`, "gray");
71000
- const proposalPath = join14(statesDirByChange.get(changeName) ?? statesDir, "..", "..", "openspec", "changes", changeName, "proposal.md");
71001
- const result2 = await fixCiUntilGreen({
71002
- getStatus: () => getPrChecksStatus(pr.url, bunCmdRunner, cwd2),
71003
- getFailedLogs: (ids) => fetchFailedRunLogs(ids, bunCmdRunner, cwd2),
71004
- runTaskWithSteering: async (steering) => {
71005
- try {
71006
- const file = Bun.file(proposalPath);
71007
- const prev = await file.exists() ? await file.text() : "";
71008
- const stamped = `
71009
-
71010
- ### CI feedback (${new Date().toISOString()})
71014
+ const changeDir = join14(statesDirByChange.get(changeName) ?? statesDir, "..", "..", "openspec", "changes", changeName);
71015
+ const maxHookFixAttempts = cfg.maxCiFixAttempts;
71016
+ const runWorkerWithFixSteering = async (heading, steering) => {
71017
+ try {
71018
+ await injectFixSteering(changeDir, heading, steering);
71019
+ } catch (steerErr) {
71020
+ appendLog(`! could not inject steering: ${steerErr.message}`, "red");
71021
+ return 1;
71022
+ }
71023
+ const rp = Bun.spawn({
71024
+ cmd: buildTaskCmd(),
71025
+ cwd: cwd2,
71026
+ stdout: "ignore",
71027
+ stderr: "ignore",
71028
+ stdin: "ignore"
71029
+ });
71030
+ return rp.exited;
71031
+ };
71032
+ let commitFixAttempt = 0;
71033
+ let commitGaveUp = false;
71034
+ while (true) {
71035
+ let dirty = "";
71036
+ try {
71037
+ const status = await bunCmdRunner.run(["git", "status", "--porcelain"], cwd2);
71038
+ dirty = status.stdout.trim();
71039
+ } catch (err) {
71040
+ appendLog(`! git status failed for ${changeName}: ${err.message}`, "yellow");
71041
+ break;
71042
+ }
71043
+ if (!dirty)
71044
+ break;
71045
+ try {
71046
+ await bunCmdRunner.run(["git", "add", "-A"], cwd2);
71047
+ await bunCmdRunner.run(["git", "commit", "-m", `ralph: residual changes for ${changeName}`], cwd2);
71048
+ appendLog(` committed residual changes for ${changeName}`, "gray");
71049
+ break;
71050
+ } catch (err) {
71051
+ const e = err;
71052
+ const detail = e.stderr?.trim() || e.message;
71053
+ const combined = `${e.stdout ?? ""}
71054
+ ${e.stderr ?? ""}`;
71055
+ if (/nothing to commit/i.test(combined))
71056
+ break;
71057
+ if (commitFixAttempt >= maxHookFixAttempts) {
71058
+ appendLog(`! commit rejected for ${changeName} after ${commitFixAttempt} fix attempts (host pre-commit hook still failing) \u2014 worktree preserved at ${cwd2}`, "red");
71059
+ appendLog(` detail: ${detail}`, "red");
71060
+ effectiveCode = PR_FAILED_EXIT;
71061
+ commitGaveUp = true;
71062
+ break;
71063
+ }
71064
+ commitFixAttempt += 1;
71065
+ appendLog(`! commit rejected for ${changeName} \u2014 feeding error back to worker (attempt ${commitFixAttempt}/${maxHookFixAttempts})`, "yellow");
71066
+ appendLog(` detail: ${detail}`, "yellow");
71067
+ const retryCode = await runWorkerWithFixSteering("Fix host pre-commit hook rejection", `Committing residual changes was rejected by the host repo's pre-commit hook. ` + `Fix the underlying problem reported below, then the commit will be retried.
71011
71068
 
71012
- ${steering}
71013
- `;
71014
- const next = prev.includes("## Steering") ? prev.replace(/## Steering\n+([\s\S]*?)$/, (_m, rest2) => `## Steering
71015
- ${stamped}
71016
- ${rest2}`) : prev + `
71017
- ## Steering
71018
- ${stamped}
71019
- `;
71020
- await Bun.write(proposalPath, next);
71021
- } catch (err) {
71022
- appendLog(`! could not append steering: ${err.message}`, "red");
71023
- }
71024
- const p = Bun.spawn({
71025
- cmd: buildTaskCmd(),
71026
- cwd: cwd2,
71027
- stdout: "ignore",
71028
- stderr: "ignore",
71029
- stdin: "ignore"
71030
- });
71031
- return p.exited;
71032
- },
71033
- pushBranch: async () => {
71034
- await bunCmdRunner.run(["git", "push", "origin", branch], cwd2);
71035
- },
71036
- log: appendLog,
71037
- sleep: (ms) => new Promise((r) => setTimeout(r, ms))
71038
- }, {
71039
- maxAttempts: cfg.maxCiFixAttempts,
71040
- pollIntervalSeconds: cfg.ciPollIntervalSeconds
71041
- });
71042
- if (!result2.success) {
71043
- appendLog(`! CI fix loop gave up after ${result2.attempts} attempts (${result2.reason ?? "unknown"}) \u2014 withholding done-status until CI passes`, "red");
71044
- effectiveCode = CI_FAILED_EXIT;
71045
- }
71069
+ ` + "```\n" + combined.trim() + "\n```");
71070
+ if (retryCode !== 0) {
71071
+ appendLog(`! worker re-run after commit rejection exited code ${retryCode} \u2014 giving up`, "red");
71072
+ effectiveCode = PR_FAILED_EXIT;
71073
+ commitGaveUp = true;
71074
+ break;
71046
71075
  }
71047
71076
  }
71048
- } catch (err) {
71049
- const e = err;
71050
- const detail = e.stderr?.trim() || e.message;
71051
- const combined = `${e.stdout ?? ""}
71077
+ }
71078
+ let pushFixAttempt = 0;
71079
+ let pr = null;
71080
+ let prGaveUp = commitGaveUp;
71081
+ while (!prGaveUp) {
71082
+ try {
71083
+ pr = await createPullRequest({ cwd: cwd2, branch, issue: prIssue, base: cfg.prBaseBranch }, bunCmdRunner);
71084
+ break;
71085
+ } catch (err) {
71086
+ const e = err;
71087
+ const detail = e.stderr?.trim() || e.message;
71088
+ const combined = `${e.stdout ?? ""}
71052
71089
  ${e.stderr ?? ""}`;
71053
- const pushRejected = /failed to push some refs|pre-push hook|hook declined/i.test(combined);
71054
- if (pushRejected) {
71055
- appendLog(`! push rejected for ${changeName} (host repo pre-push hook failed) \u2014 worktree preserved at ${cwd2}`, "red");
71056
- appendLog(` detail: ${detail}`, "red");
71057
- } else {
71058
- appendLog(`! PR create failed for ${changeName}: ${detail}`, "red");
71090
+ const pushRejected = /failed to push some refs|pre-push hook|hook declined/i.test(combined);
71091
+ if (!pushRejected || pushFixAttempt >= maxHookFixAttempts) {
71092
+ if (pushRejected) {
71093
+ appendLog(`! push rejected for ${changeName} after ${pushFixAttempt} fix attempts (host pre-push hook still failing) \u2014 worktree preserved at ${cwd2}`, "red");
71094
+ appendLog(` detail: ${detail}`, "red");
71095
+ } else {
71096
+ appendLog(`! PR create failed for ${changeName}: ${detail}`, "red");
71097
+ }
71098
+ effectiveCode = PR_FAILED_EXIT;
71099
+ prGaveUp = true;
71100
+ break;
71101
+ }
71102
+ pushFixAttempt += 1;
71103
+ appendLog(`! push rejected for ${changeName} \u2014 feeding error back to worker (attempt ${pushFixAttempt}/${maxHookFixAttempts})`, "yellow");
71104
+ appendLog(` detail: ${detail}`, "yellow");
71105
+ const retryCode = await runWorkerWithFixSteering("Fix host pre-push hook rejection", `Push to origin/${branch} was rejected by the host repo's pre-push hook. ` + `Fix the underlying problem reported below, then the push will be retried.
71106
+
71107
+ ` + "```\n" + combined.trim() + "\n```");
71108
+ if (retryCode !== 0) {
71109
+ appendLog(`! worker re-run after push rejection exited code ${retryCode} \u2014 giving up`, "red");
71110
+ effectiveCode = PR_FAILED_EXIT;
71111
+ prGaveUp = true;
71112
+ break;
71113
+ }
71114
+ }
71115
+ }
71116
+ if (prGaveUp) {} else if (!pr) {
71117
+ appendLog(` no commits ahead of ${cfg.prBaseBranch} \u2014 skipping PR`, "gray");
71118
+ } else {
71119
+ appendLog(` ${pr.created ? "opened" : "found existing"} PR: ${pr.url}`, "green");
71120
+ const wantFixCi = args.fixCi || cfg.fixCiOnFailure;
71121
+ if (wantFixCi) {
71122
+ appendLog(` watching CI for ${pr.url} (max ${cfg.maxCiFixAttempts} fix attempts)`, "gray");
71123
+ const result2 = await fixCiUntilGreen({
71124
+ getStatus: () => getPrChecksStatus(pr.url, bunCmdRunner, cwd2),
71125
+ getFailedLogs: (ids) => fetchFailedRunLogs(ids, bunCmdRunner, cwd2),
71126
+ runTaskWithSteering: async (steering) => {
71127
+ try {
71128
+ await injectFixSteering(changeDir, "Fix failing CI checks", `CI feedback:
71129
+
71130
+ ${steering}`);
71131
+ } catch (err) {
71132
+ appendLog(`! could not inject steering: ${err.message}`, "red");
71133
+ }
71134
+ const p = Bun.spawn({
71135
+ cmd: buildTaskCmd(),
71136
+ cwd: cwd2,
71137
+ stdout: "ignore",
71138
+ stderr: "ignore",
71139
+ stdin: "ignore"
71140
+ });
71141
+ return p.exited;
71142
+ },
71143
+ pushBranch: async () => {
71144
+ await bunCmdRunner.run(["git", "push", "origin", branch], cwd2);
71145
+ },
71146
+ log: appendLog,
71147
+ sleep: (ms) => new Promise((r) => setTimeout(r, ms))
71148
+ }, {
71149
+ maxAttempts: cfg.maxCiFixAttempts,
71150
+ pollIntervalSeconds: cfg.ciPollIntervalSeconds
71151
+ });
71152
+ if (!result2.success) {
71153
+ appendLog(`! CI fix loop gave up after ${result2.attempts} attempts (${result2.reason ?? "unknown"}) \u2014 withholding done-status until CI passes`, "red");
71154
+ effectiveCode = CI_FAILED_EXIT;
71155
+ }
71059
71156
  }
71060
- effectiveCode = PR_FAILED_EXIT;
71061
71157
  }
71062
71158
  }
71063
71159
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neriros/ralphy",
3
- "version": "2.9.2",
3
+ "version": "2.9.4",
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",