@neriros/ralphy 2.13.1 → 2.13.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/README.md +36 -24
- package/dist/cli/index.js +128 -49
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -89,10 +89,10 @@ What it does on each tick:
|
|
|
89
89
|
1. Polls Linear for open issues matching the filter (team / assignee / status / labels)
|
|
90
90
|
2. Dedupes against `.ralph/agent-state.json` (already processed) plus any in-flight workers
|
|
91
91
|
3. For each new issue: fetches existing comments, scaffolds `openspec/changes/<id-slug>/{proposal.md,tasks.md,design.md}` (with the comments embedded so the worker sees prior discussion), then spawns `ralph task --name <id-slug>` up to the concurrency cap
|
|
92
|
-
4. Posts a "🤖 started" comment on the Linear issue and
|
|
93
|
-
5. On worker exit, posts a success/failure comment and
|
|
92
|
+
4. Posts a "🤖 started" comment on the Linear issue and applies the `setInProgress` indicator (if configured)
|
|
93
|
+
5. On worker exit, posts a success/failure comment and applies the `setDone` indicator on success or `setError` on failure (if configured)
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
A default `ralphy.config.json` is written on first run with every defaulted setting filled in; CLI flags override config values per invocation.
|
|
96
96
|
|
|
97
97
|
```jsonc
|
|
98
98
|
{
|
|
@@ -105,13 +105,23 @@ Defaults are written to `ralphy.config.json` on first run; CLI flags override co
|
|
|
105
105
|
"linear": {
|
|
106
106
|
"team": "ENG",
|
|
107
107
|
"assignee": "me",
|
|
108
|
-
"statuses": ["Todo", "In Progress"],
|
|
109
|
-
"labels": ["ralph", "automation"],
|
|
110
|
-
"inProgressStatus": "In Progress",
|
|
111
|
-
"doneStatus": "In Review",
|
|
112
|
-
"doneLabel": "ralphy-done",
|
|
113
108
|
"postComments": true,
|
|
114
109
|
"updateEveryIterations": 10,
|
|
110
|
+
"indicators": {
|
|
111
|
+
"getTodo": { "filter": [{ "type": "status", "value": "Todo" }] },
|
|
112
|
+
"getInProgress": { "filter": [{ "type": "status", "value": "In Progress" }] },
|
|
113
|
+
"getConflicted": { "filter": [{ "type": "label", "value": "ralph:conflicted" }] },
|
|
114
|
+
"setInProgress": { "type": "status", "value": "In Progress" },
|
|
115
|
+
"setDone": {
|
|
116
|
+
"apply": [
|
|
117
|
+
{ "type": "status", "value": "In Review" },
|
|
118
|
+
{ "type": "label", "value": "ralphy-done" },
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
"setError": { "type": "label", "value": "ralph:error" },
|
|
122
|
+
"setConflicted": { "type": "label", "value": "ralph:conflicted" },
|
|
123
|
+
"clearConflicted": { "type": "label", "value": "ralph:conflicted" },
|
|
124
|
+
},
|
|
115
125
|
},
|
|
116
126
|
"useWorktree": true,
|
|
117
127
|
"cleanupWorktreeOnSuccess": false,
|
|
@@ -131,7 +141,13 @@ Defaults are written to `ralphy.config.json` on first run; CLI flags override co
|
|
|
131
141
|
}
|
|
132
142
|
```
|
|
133
143
|
|
|
134
|
-
|
|
144
|
+
Linear is the source of truth for which issues Ralph has touched. Each `linear.indicators` key names a lifecycle event:
|
|
145
|
+
|
|
146
|
+
- `getTodo` / `getInProgress` / `getConflicted` — `{ filter: [...] }` selectors used to find issues to pick up, resume, or repair.
|
|
147
|
+
- `setInProgress` / `setDone` / `setError` / `setConflicted` — single marker `{ type, value }` or `{ apply: [...] }` for multi-marker.
|
|
148
|
+
- `clearConflicted` — labels to remove once a conflicted PR is fixed (status removal is not supported).
|
|
149
|
+
|
|
150
|
+
Marker types are `"label"` or `"status"`. Combine markers under `apply` when one event needs to set multiple — e.g. `setDone` flipping a status _and_ adding a "shipped" label.
|
|
135
151
|
|
|
136
152
|
#### Per-task git worktrees
|
|
137
153
|
|
|
@@ -147,7 +163,7 @@ Use `setupScript` (run inside the worktree right after scaffolding) to install d
|
|
|
147
163
|
|
|
148
164
|
**`fixCiOnFailure`** (or `--fix-ci`) watches the PR's checks via `gh pr checks` and, on failure, fetches the failed-run logs (`gh run view --log-failed`), appends them to `proposal.md` under `## Steering`, re-spawns the task loop in the worktree, and pushes the new commits — repeating until checks go green or `maxCiFixAttempts` is hit (default 5, polling interval `ciPollIntervalSeconds` defaults to 30s). Requires `--create-pr`.
|
|
149
165
|
|
|
150
|
-
When `fixCiOnFailure` is enabled, the
|
|
166
|
+
When `fixCiOnFailure` is enabled, the `setDone` indicator is **not** applied (and the issue is not marked processed in `.ralph/agent-state.json`) until CI actually goes green. If the fix loop exhausts its attempts the worker is treated as failed for completion-marking purposes and the issue will be re-picked-up on the next poll (the `getInProgress` filter ensures that).
|
|
151
167
|
|
|
152
168
|
Every CLI flag is also configurable in `ralphy.config.json`; CLI values override config when both are set. The agent forwards `maxRuntimeMinutesPerTask` / `maxConsecutiveFailuresPerTask` / `iterationDelaySeconds` / `logRawStream` / `taskVerbose` to each spawned `ralph task` worker.
|
|
153
169
|
|
|
@@ -174,20 +190,16 @@ Failed workers (non-zero exit) are not marked processed, so they'll be retried o
|
|
|
174
190
|
|
|
175
191
|
### Agent mode flags
|
|
176
192
|
|
|
177
|
-
| Option
|
|
178
|
-
|
|
|
179
|
-
| `--linear-team <key>`
|
|
180
|
-
| `--linear-assignee <id>`
|
|
181
|
-
| `--
|
|
182
|
-
| `--
|
|
183
|
-
| `--
|
|
184
|
-
| `--
|
|
185
|
-
| `--
|
|
186
|
-
| `--
|
|
187
|
-
| `--done-status <name>` | Linear status to set on successful completion |
|
|
188
|
-
| `--done-label <name>` | Linear label to add on successful completion |
|
|
189
|
-
| `--create-pr` | Push worker branch + open a GitHub PR on success (needs `--worktree`) |
|
|
190
|
-
| `--fix-ci` | After PR opens, re-run task on CI failures until green (needs `--create-pr`) |
|
|
193
|
+
| Option | Description |
|
|
194
|
+
| ------------------------- | ----------------------------------------------------------------------------- |
|
|
195
|
+
| `--linear-team <key>` | Linear team key (e.g. `ENG`) |
|
|
196
|
+
| `--linear-assignee <id>` | Filter by assignee (user id, email, or `me`) |
|
|
197
|
+
| `--poll-interval <s>` | Seconds between Linear polls (default: 60) |
|
|
198
|
+
| `--concurrency <n>` | Max concurrent task loops (default: 1) |
|
|
199
|
+
| `--worktree` | Run each task in its own git worktree |
|
|
200
|
+
| `--indicator <k>:<t>:<v>` | Override a `linear.indicators` entry; repeatable (e.g. `setDone:status:Done`) |
|
|
201
|
+
| `--create-pr` | Push worker branch + open a GitHub PR on success (needs `--worktree`) |
|
|
202
|
+
| `--fix-ci` | After PR opens, re-run task on CI failures until green (needs `--create-pr`) |
|
|
191
203
|
|
|
192
204
|
## OpenSpec Flow
|
|
193
205
|
|
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.13.
|
|
56410
|
+
version: "2.13.3",
|
|
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",
|
|
@@ -56653,8 +56653,6 @@ async function parseArgs(argv) {
|
|
|
56653
56653
|
let expectMaxRuntime = false;
|
|
56654
56654
|
let expectMaxFailures = false;
|
|
56655
56655
|
let expectMaxIterations = false;
|
|
56656
|
-
let expectTimeout = false;
|
|
56657
|
-
let expectPushInterval = false;
|
|
56658
56656
|
let expectLinearTeam = false;
|
|
56659
56657
|
let expectLinearAssignee = false;
|
|
56660
56658
|
let expectPollInterval = false;
|
|
@@ -56717,14 +56715,6 @@ async function parseArgs(argv) {
|
|
|
56717
56715
|
expectMaxIterations = false;
|
|
56718
56716
|
continue;
|
|
56719
56717
|
}
|
|
56720
|
-
if (expectTimeout) {
|
|
56721
|
-
expectTimeout = false;
|
|
56722
|
-
continue;
|
|
56723
|
-
}
|
|
56724
|
-
if (expectPushInterval) {
|
|
56725
|
-
expectPushInterval = false;
|
|
56726
|
-
continue;
|
|
56727
|
-
}
|
|
56728
56718
|
if (expectLinearTeam) {
|
|
56729
56719
|
result2.linearTeam = arg;
|
|
56730
56720
|
expectLinearTeam = false;
|
|
@@ -56794,12 +56784,6 @@ async function parseArgs(argv) {
|
|
|
56794
56784
|
case "--max-iterations":
|
|
56795
56785
|
expectMaxIterations = true;
|
|
56796
56786
|
break;
|
|
56797
|
-
case "--timeout":
|
|
56798
|
-
expectTimeout = true;
|
|
56799
|
-
break;
|
|
56800
|
-
case "--push-interval":
|
|
56801
|
-
expectPushInterval = true;
|
|
56802
|
-
break;
|
|
56803
56787
|
case "--unlimited":
|
|
56804
56788
|
result2.maxIterations = 0;
|
|
56805
56789
|
break;
|
|
@@ -70196,23 +70180,7 @@ var RalphyConfigSchema = exports_external.object({
|
|
|
70196
70180
|
postComments: exports_external.boolean().default(true),
|
|
70197
70181
|
updateEveryIterations: exports_external.number().int().nonnegative().default(10),
|
|
70198
70182
|
indicators: IndicatorsSchema.default({})
|
|
70199
|
-
}).
|
|
70200
|
-
const LEGACY_KEYS = [
|
|
70201
|
-
"statuses",
|
|
70202
|
-
"labels",
|
|
70203
|
-
"inProgressStatus",
|
|
70204
|
-
"doneStatus",
|
|
70205
|
-
"doneLabel"
|
|
70206
|
-
];
|
|
70207
|
-
const found = LEGACY_KEYS.filter((k) => (k in value));
|
|
70208
|
-
if (found.length === 0)
|
|
70209
|
-
return;
|
|
70210
|
-
ctx.addIssue({
|
|
70211
|
-
code: exports_external.ZodIssueCode.custom,
|
|
70212
|
-
path: ["linear"],
|
|
70213
|
-
message: `legacy linear keys [${found.join(", ")}] cannot be used together with the new ` + `\`linear.indicators\` map \u2014 they describe the same lifecycle and combining them is ` + `not possible. Migrate by moving each legacy key into linear.indicators (e.g. ` + `doneStatus: "Done" \u2192 indicators.setDone: {type: "status", value: "Done"}; ` + `statuses/labels \u2192 indicators.getTodo.filter; inProgressStatus \u2192 indicators.setInProgress; ` + `doneLabel \u2192 indicators.setDone {type: "label", ...}).`
|
|
70214
|
-
});
|
|
70215
|
-
}).default({ postComments: true, updateEveryIterations: 10, indicators: {} })
|
|
70183
|
+
}).strict().default({ postComments: true, updateEveryIterations: 10, indicators: {} })
|
|
70216
70184
|
}).default({
|
|
70217
70185
|
concurrency: 1,
|
|
70218
70186
|
pollIntervalSeconds: 60,
|
|
@@ -70222,23 +70190,145 @@ var RalphyConfigSchema = exports_external.object({
|
|
|
70222
70190
|
model: "opus",
|
|
70223
70191
|
linear: { postComments: true, updateEveryIterations: 10, indicators: {} }
|
|
70224
70192
|
});
|
|
70193
|
+
function stripJsonComments(text) {
|
|
70194
|
+
return text.replace(/\/\/[^\n]*/g, "");
|
|
70195
|
+
}
|
|
70225
70196
|
async function loadRalphyConfig(projectRoot) {
|
|
70226
70197
|
const path = join10(projectRoot, "ralphy.config.json");
|
|
70227
70198
|
const file = Bun.file(path);
|
|
70228
70199
|
if (!await file.exists()) {
|
|
70229
70200
|
return RalphyConfigSchema.parse({});
|
|
70230
70201
|
}
|
|
70231
|
-
const
|
|
70202
|
+
const text = await file.text();
|
|
70203
|
+
const raw = JSON.parse(stripJsonComments(text));
|
|
70232
70204
|
return RalphyConfigSchema.parse(raw);
|
|
70233
70205
|
}
|
|
70206
|
+
var DEFAULT_CONFIG_TEMPLATE = `{
|
|
70207
|
+
// How many tasks to run in parallel.
|
|
70208
|
+
"concurrency": 1,
|
|
70209
|
+
|
|
70210
|
+
// Seconds between polls for new Linear issues (agent mode).
|
|
70211
|
+
"pollIntervalSeconds": 60,
|
|
70212
|
+
|
|
70213
|
+
// Maximum iterations per task. 0 = unlimited.
|
|
70214
|
+
"maxIterationsPerTask": 0,
|
|
70215
|
+
|
|
70216
|
+
// Maximum cost in USD per task. 0 = unlimited.
|
|
70217
|
+
"maxCostUsdPerTask": 0,
|
|
70218
|
+
|
|
70219
|
+
// Maximum wall-clock minutes per task. 0 = unlimited.
|
|
70220
|
+
"maxRuntimeMinutesPerTask": 0,
|
|
70221
|
+
|
|
70222
|
+
// Stop a task after this many consecutive identical failures.
|
|
70223
|
+
"maxConsecutiveFailuresPerTask": 5,
|
|
70224
|
+
|
|
70225
|
+
// Seconds to wait between loop iterations (throttle).
|
|
70226
|
+
"iterationDelaySeconds": 0,
|
|
70227
|
+
|
|
70228
|
+
// Log the raw engine stream to stdout.
|
|
70229
|
+
"logRawStream": false,
|
|
70230
|
+
|
|
70231
|
+
// Pass --verbose to the ralph task sub-process.
|
|
70232
|
+
"taskVerbose": false,
|
|
70233
|
+
|
|
70234
|
+
// Run each task in an isolated git worktree.
|
|
70235
|
+
"useWorktree": false,
|
|
70236
|
+
|
|
70237
|
+
// Delete the worktree after a successful task.
|
|
70238
|
+
"cleanupWorktreeOnSuccess": false,
|
|
70239
|
+
|
|
70240
|
+
// Shell script to run inside the worktree before the task starts.
|
|
70241
|
+
// "setupScript": "bun install",
|
|
70242
|
+
|
|
70243
|
+
// Shell script to run inside the worktree after the task finishes.
|
|
70244
|
+
// "teardownScript": "bun run lint",
|
|
70245
|
+
|
|
70246
|
+
// Extra text appended to every task prompt.
|
|
70247
|
+
// "appendPrompt": "Always run tests before finishing.",
|
|
70248
|
+
|
|
70249
|
+
// Open a pull request after a task succeeds.
|
|
70250
|
+
"createPrOnSuccess": false,
|
|
70251
|
+
|
|
70252
|
+
// Base branch for pull requests.
|
|
70253
|
+
"prBaseBranch": "main",
|
|
70254
|
+
|
|
70255
|
+
// Let the agent attempt to fix CI failures after a PR is created.
|
|
70256
|
+
"fixCiOnFailure": false,
|
|
70257
|
+
|
|
70258
|
+
// Maximum number of CI-fix attempts per task.
|
|
70259
|
+
"maxCiFixAttempts": 5,
|
|
70260
|
+
|
|
70261
|
+
// Seconds between CI status polls.
|
|
70262
|
+
"ciPollIntervalSeconds": 30,
|
|
70263
|
+
|
|
70264
|
+
// Underlying engine: "claude" or "codex".
|
|
70265
|
+
"engine": "claude",
|
|
70266
|
+
|
|
70267
|
+
// Model tier: "haiku", "sonnet", or "opus".
|
|
70268
|
+
"model": "opus",
|
|
70269
|
+
|
|
70270
|
+
"linear": {
|
|
70271
|
+
// Linear team key to filter issues (e.g. "ENG"). Omit to match all teams.
|
|
70272
|
+
// "team": "ENG",
|
|
70273
|
+
|
|
70274
|
+
// Linear user to filter issues. Can be an email address or user ID.
|
|
70275
|
+
// Omit to match issues regardless of assignee.
|
|
70276
|
+
// "assignee": "dev@example.com",
|
|
70277
|
+
|
|
70278
|
+
// Post progress comments on the Linear issue while a task is running.
|
|
70279
|
+
"postComments": true,
|
|
70280
|
+
|
|
70281
|
+
// Post a progress update every N iterations. 0 disables. Requires postComments.
|
|
70282
|
+
"updateEveryIterations": 10,
|
|
70283
|
+
|
|
70284
|
+
// ---------------------------------------------------------------------------
|
|
70285
|
+
// Linear indicators \u2014 COMMENTED OUT BY DEFAULT
|
|
70286
|
+
//
|
|
70287
|
+
// Indicators map Ralph lifecycle events to Linear labels/statuses.
|
|
70288
|
+
// WARNING: Activating indicators will query AND mutate your Linear workspace.
|
|
70289
|
+
// Labels or statuses that do not already exist may be created automatically.
|
|
70290
|
+
// Review every value against your actual Linear workspace before enabling,
|
|
70291
|
+
// then replace the empty object below with the full indicators block.
|
|
70292
|
+
//
|
|
70293
|
+
// To activate, replace "indicators": {} with:
|
|
70294
|
+
//
|
|
70295
|
+
// "indicators": {
|
|
70296
|
+
// // Issues to pick up (any-of filter \u2014 Ralph will start working on these).
|
|
70297
|
+
// "getTodo": { "filter": [{ "type": "status", "value": "Todo" }] },
|
|
70298
|
+
//
|
|
70299
|
+
// // Issues already in flight (resume after restart).
|
|
70300
|
+
// "getInProgress": { "filter": [{ "type": "label", "value": "ralph:in-progress" }] },
|
|
70301
|
+
//
|
|
70302
|
+
// // Issues whose PR has a merge conflict (Ralph will attempt a re-fix run).
|
|
70303
|
+
// "getConflicted": { "filter": [{ "type": "label", "value": "ralph:conflict" }] },
|
|
70304
|
+
//
|
|
70305
|
+
// // Applied when Ralph picks up an issue.
|
|
70306
|
+
// "setInProgress": { "type": "label", "value": "ralph:in-progress" },
|
|
70307
|
+
//
|
|
70308
|
+
// // Applied on clean success.
|
|
70309
|
+
// "setDone": { "type": "status", "value": "In Review" },
|
|
70310
|
+
//
|
|
70311
|
+
// // Applied when the task exits with an error (quarantine signal).
|
|
70312
|
+
// "setError": { "type": "label", "value": "ralph:error" },
|
|
70313
|
+
//
|
|
70314
|
+
// // Applied when a PR merge conflict is detected.
|
|
70315
|
+
// "setConflicted": { "type": "label", "value": "ralph:conflict" },
|
|
70316
|
+
//
|
|
70317
|
+
// // Label-only marker(s) removed once the conflict is fixed.
|
|
70318
|
+
// // Note: only label-typed markers are valid here \u2014 status removal is not supported.
|
|
70319
|
+
// "clearConflicted": { "type": "label", "value": "ralph:conflict" }
|
|
70320
|
+
// }
|
|
70321
|
+
// ---------------------------------------------------------------------------
|
|
70322
|
+
"indicators": {}
|
|
70323
|
+
}
|
|
70324
|
+
}
|
|
70325
|
+
`;
|
|
70234
70326
|
async function ensureRalphyConfig(projectRoot) {
|
|
70235
70327
|
const path = join10(projectRoot, "ralphy.config.json");
|
|
70236
70328
|
const file = Bun.file(path);
|
|
70237
70329
|
if (await file.exists())
|
|
70238
70330
|
return path;
|
|
70239
|
-
|
|
70240
|
-
await Bun.write(path, JSON.stringify(defaults2, null, 2) + `
|
|
70241
|
-
`);
|
|
70331
|
+
await Bun.write(path, DEFAULT_CONFIG_TEMPLATE);
|
|
70242
70332
|
return path;
|
|
70243
70333
|
}
|
|
70244
70334
|
|
|
@@ -71908,17 +71998,6 @@ PR: ${prUrl}` : ""
|
|
|
71908
71998
|
postComments: cfg.linear.postComments,
|
|
71909
71999
|
commentEveryIterations: cfg.linear.updateEveryIterations
|
|
71910
72000
|
});
|
|
71911
|
-
(async () => {
|
|
71912
|
-
const legacy = Bun.file(projectLayout(projectRoot).agentStateFile);
|
|
71913
|
-
if (await legacy.exists()) {
|
|
71914
|
-
onLog(" legacy .ralph/agent-state.json detected \u2014 Linear is now the source of truth; deleting", "gray");
|
|
71915
|
-
try {
|
|
71916
|
-
await legacy.delete();
|
|
71917
|
-
} catch (err) {
|
|
71918
|
-
onLog(`! failed to delete legacy agent-state.json: ${err.message}`, "yellow");
|
|
71919
|
-
}
|
|
71920
|
-
}
|
|
71921
|
-
})();
|
|
71922
72001
|
const filterDesc = describeIndicators(indicators, team, assignee);
|
|
71923
72002
|
return {
|
|
71924
72003
|
coord,
|