@neriros/ralphy 2.7.5 → 2.7.6

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/README.md CHANGED
@@ -104,11 +104,13 @@ Defaults are written to `ralphy.config.json` on first run; CLI flags override co
104
104
  "doneStatus": "In Review",
105
105
  "doneLabel": "ralphy-done",
106
106
  "postComments": true,
107
+ "updateEveryIterations": 10,
107
108
  },
108
109
  "useWorktree": true,
109
110
  "cleanupWorktreeOnSuccess": false,
110
111
  "setupScript": "bun install",
111
112
  "teardownScript": "git status",
113
+ "appendPrompt": "Always run lint before committing.",
112
114
  }
113
115
  ```
114
116
 
@@ -120,6 +122,10 @@ With `--worktree` (or `useWorktree: true` in config) each task runs in an isolat
120
122
 
121
123
  Use `setupScript` (run inside the worktree right after scaffolding) to install dependencies, copy `.env`, etc. Use `teardownScript` (run after the loop exits, before any worktree cleanup) to gather artifacts or roll back local mutations. Both run via `sh -c`; failures are logged but never block the loop. With `cleanupWorktreeOnSuccess: true` the worktree is removed when the worker exits 0 — failed workers always keep their worktree (and branch) for human inspection.
122
124
 
125
+ **`appendPrompt`** (or `--prompt` in agent mode) is appended to every scaffolded `proposal.md` under an `## Additional instructions` section — use it for cross-cutting guidance every task should see.
126
+
127
+ **`updateEveryIterations`** (default `10`, `0` disables) posts a "🔄 Ralph progress update: iteration N" comment on the Linear issue every N task iterations. Requires `postComments: true`.
128
+
123
129
  Failed workers (non-zero exit) are not marked processed, so they'll be retried on the next poll. SIGINT/SIGTERM cleanly stops polling and kills active workers. All Linear side effects are best-effort — failures log a warning but never block the task loop.
124
130
 
125
131
  ## CLI Options
package/dist/cli/index.js CHANGED
@@ -56131,7 +56131,7 @@ var HELP_TEXT = [
56131
56131
  "",
56132
56132
  "Options:",
56133
56133
  " --name <name> Change name (required for most commands)",
56134
- " --prompt <text> Task description",
56134
+ " --prompt <text> Task description (in agent mode: appended to every scaffolded proposal)",
56135
56135
  " --prompt-file <path> Read prompt from file",
56136
56136
  " --model <model> Set model (haiku|sonnet|opus)",
56137
56137
  " --claude [model] Use Claude engine (haiku|sonnet|opus, default: opus)",
@@ -69772,7 +69772,7 @@ function changeNameForIssue(issue) {
69772
69772
  const slug = issue.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
69773
69773
  return slug ? `${issue.identifier.toLowerCase()}-${slug}` : issue.identifier.toLowerCase();
69774
69774
  }
69775
- async function scaffoldChangeForIssue(tasksDir, statesDir, issue, comments = []) {
69775
+ async function scaffoldChangeForIssue(tasksDir, statesDir, issue, comments = [], appendPrompt = "") {
69776
69776
  const name = changeNameForIssue(issue);
69777
69777
  const changeDir = join11(tasksDir, name);
69778
69778
  const stateDir = join11(statesDir, name);
@@ -69802,6 +69802,7 @@ async function scaffoldChangeForIssue(tasksDir, statesDir, issue, comments = [])
69802
69802
  "",
69803
69803
  issue.description?.trim() || "_No description provided in Linear._",
69804
69804
  ...commentsBlock,
69805
+ ...appendPrompt.trim() ? ["", "## Additional instructions", "", appendPrompt.trim()] : [],
69805
69806
  "",
69806
69807
  "## Steering",
69807
69808
  "",
@@ -69843,6 +69844,7 @@ var RalphyConfigSchema = exports_external.object({
69843
69844
  cleanupWorktreeOnSuccess: exports_external.boolean().default(false),
69844
69845
  setupScript: exports_external.string().optional(),
69845
69846
  teardownScript: exports_external.string().optional(),
69847
+ appendPrompt: exports_external.string().optional(),
69846
69848
  engine: exports_external.enum(["claude", "codex"]).default("claude"),
69847
69849
  model: exports_external.enum(["haiku", "sonnet", "opus"]).default("opus"),
69848
69850
  linear: exports_external.object({
@@ -69853,7 +69855,8 @@ var RalphyConfigSchema = exports_external.object({
69853
69855
  inProgressStatus: exports_external.string().optional(),
69854
69856
  doneStatus: exports_external.string().optional(),
69855
69857
  doneLabel: exports_external.string().optional(),
69856
- postComments: exports_external.boolean().default(true)
69858
+ postComments: exports_external.boolean().default(true),
69859
+ updateEveryIterations: exports_external.number().int().nonnegative().default(10)
69857
69860
  }).default({ statuses: [], labels: [], postComments: true })
69858
69861
  }).default({
69859
69862
  concurrency: 1,
@@ -69939,8 +69942,37 @@ class AgentCoordinator {
69939
69942
  state.lastPollAt = new Date().toISOString();
69940
69943
  await this.deps.saveState(state);
69941
69944
  this.spawnNext();
69945
+ await this.reportProgress();
69942
69946
  return { found: issues.length, added };
69943
69947
  }
69948
+ async reportProgress() {
69949
+ const updater = this.deps.updater;
69950
+ const everyN = this.opts.commentEveryIterations ?? 0;
69951
+ if (everyN <= 0 || !updater || this.opts.postComments === false || !this.deps.getIterationCount) {
69952
+ return;
69953
+ }
69954
+ for (const w of this.workers) {
69955
+ let count;
69956
+ try {
69957
+ count = await this.deps.getIterationCount(w.changeName);
69958
+ } catch (err) {
69959
+ this.deps.onLog(`! iteration count read failed for ${w.issueIdentifier}: ${err.message}`, "yellow");
69960
+ continue;
69961
+ }
69962
+ if (count < everyN)
69963
+ continue;
69964
+ const currMilestone = Math.floor(count / everyN);
69965
+ const lastMilestone = Math.floor(w.lastReportedIteration / everyN);
69966
+ if (currMilestone <= lastMilestone)
69967
+ continue;
69968
+ try {
69969
+ await updater.postComment(w.issue, `\uD83D\uDD04 Ralph progress update: iteration ${count} on \`${w.changeName}\``);
69970
+ w.lastReportedIteration = count;
69971
+ } catch (err) {
69972
+ this.deps.onLog(`! Linear progress comment failed for ${w.issueIdentifier}: ${err.message}`, "red");
69973
+ }
69974
+ }
69975
+ }
69944
69976
  spawnNext() {
69945
69977
  if (this.stopped || !this.state)
69946
69978
  return;
@@ -69970,7 +70002,9 @@ class AgentCoordinator {
69970
70002
  changeName,
69971
70003
  issueId: issue.id,
69972
70004
  issueIdentifier: issue.identifier,
69973
- kill: handle.kill
70005
+ issue,
70006
+ kill: handle.kill,
70007
+ lastReportedIteration: 0
69974
70008
  };
69975
70009
  this.workers.push(worker);
69976
70010
  this.pendingIds.delete(issue.id);
@@ -70157,6 +70191,7 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
70157
70191
  const teamKeyOf = (issue) => issue.identifier.split("-")[0];
70158
70192
  const useWorktree = args.worktree || cfg.useWorktree;
70159
70193
  const cwdByChange = new Map;
70194
+ const statesDirByChange = new Map;
70160
70195
  async function runScript(label, cmd, cwd2) {
70161
70196
  appendLog(` ${label}: ${cmd}`, "gray");
70162
70197
  const proc = Bun.spawn({
@@ -70197,8 +70232,10 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
70197
70232
  appendLog(`! worktree create failed for ${issue.identifier}: ${err.message} \u2014 falling back to project root`, "yellow");
70198
70233
  }
70199
70234
  }
70200
- const changeName = await scaffoldChangeForIssue(scaffoldTasksDir, scaffoldStatesDir, issue, comments);
70235
+ const appendPrompt = args.prompt || cfg.appendPrompt || "";
70236
+ const changeName = await scaffoldChangeForIssue(scaffoldTasksDir, scaffoldStatesDir, issue, comments, appendPrompt);
70201
70237
  cwdByChange.set(changeName, workerCwd);
70238
+ statesDirByChange.set(changeName, scaffoldStatesDir);
70202
70239
  if (cfg.setupScript) {
70203
70240
  await runScript("setup", cfg.setupScript, workerCwd);
70204
70241
  }
@@ -70246,6 +70283,7 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
70246
70283
  }
70247
70284
  }
70248
70285
  cwdByChange.delete(changeName);
70286
+ statesDirByChange.delete(changeName);
70249
70287
  return code;
70250
70288
  });
70251
70289
  return { exited: wrapped, kill: () => proc.kill() };
@@ -70254,6 +70292,14 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
70254
70292
  saveState: (s) => writeAgentState(projectRoot, s),
70255
70293
  onLog: appendLog,
70256
70294
  onWorkersChanged: () => setTick((t) => t + 1),
70295
+ getIterationCount: async (changeName) => {
70296
+ const dir = statesDirByChange.get(changeName) ?? statesDir;
70297
+ const file = Bun.file(join14(dir, changeName, ".ralph-state.json"));
70298
+ if (!await file.exists())
70299
+ return 0;
70300
+ const json = await file.json();
70301
+ return json.iteration ?? 0;
70302
+ },
70257
70303
  updater: {
70258
70304
  postComment: (issue, body) => addIssueComment(apiKey, issue.id, body),
70259
70305
  setState: (issue, stateId) => updateIssueState(apiKey, issue.id, stateId),
@@ -70285,7 +70331,8 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
70285
70331
  inProgressStatus: args.inProgressStatus || cfg.linear.inProgressStatus,
70286
70332
  doneStatus: args.doneStatus || cfg.linear.doneStatus,
70287
70333
  doneLabel: args.doneLabel || cfg.linear.doneLabel,
70288
- postComments: cfg.linear.postComments
70334
+ postComments: cfg.linear.postComments,
70335
+ commentEveryIterations: cfg.linear.updateEveryIterations
70289
70336
  });
70290
70337
  coordRef.current = coord2;
70291
70338
  await coord2.init();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neriros/ralphy",
3
- "version": "2.7.5",
3
+ "version": "2.7.6",
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",