@neriros/ralphy 2.13.7 โ 2.13.8
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 +34 -1
- package/dist/cli/index.js +44 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -82,12 +82,15 @@ ralph status --name fix-auth # Detailed view of one task
|
|
|
82
82
|
```bash
|
|
83
83
|
export LINEAR_API_KEY=lin_api_xxx
|
|
84
84
|
ralph agent --linear-team ENG --linear-assignee me --concurrency 3 --poll-interval 60
|
|
85
|
+
|
|
86
|
+
# Limit the number of tickets processed in this run
|
|
87
|
+
ralph agent --max-tickets 5 --linear-team ENG --linear-assignee me
|
|
85
88
|
```
|
|
86
89
|
|
|
87
90
|
What it does on each tick:
|
|
88
91
|
|
|
89
92
|
1. Polls Linear for open issues matching the filter (team / assignee / status / labels)
|
|
90
|
-
2. Dedupes against
|
|
93
|
+
2. Dedupes against in-flight workers and any already-active issues
|
|
91
94
|
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
95
|
4. Posts a "๐ค started" comment on the Linear issue and applies the `setInProgress` indicator (if configured)
|
|
93
96
|
5. On worker exit, posts a success/failure comment and applies the `setDone` indicator on success or `setError` on failure (if configured)
|
|
@@ -196,11 +199,41 @@ Failed workers (non-zero exit) are not marked processed, so they'll be retried o
|
|
|
196
199
|
| `--linear-assignee <id>` | Filter by assignee (user id, email, or `me`) |
|
|
197
200
|
| `--poll-interval <s>` | Seconds between Linear polls (default: 60) |
|
|
198
201
|
| `--concurrency <n>` | Max concurrent task loops (default: 1) |
|
|
202
|
+
| `--max-tickets <n>` | Stop picking up new issues after N have been started this run (0 = unlimited) |
|
|
199
203
|
| `--worktree` | Run each task in its own git worktree |
|
|
200
204
|
| `--indicator <k>:<t>:<v>` | Override a `linear.indicators` entry; repeatable (e.g. `setDone:status:Done`) |
|
|
201
205
|
| `--create-pr` | Push worker branch + open a GitHub PR on success (needs `--worktree`) |
|
|
202
206
|
| `--fix-ci` | After PR opens, re-run task on CI failures until green (needs `--create-pr`) |
|
|
203
207
|
|
|
208
|
+
#### `--max-tickets`
|
|
209
|
+
|
|
210
|
+
Use `--max-tickets N` to cap how many issues ralph picks up in a single agent run. Once N issues have been started (across fresh, resume, and conflict-fix modes), the coordinator stops enqueuing new work. In-flight workers continue to completion. The limit resets each time you restart `ralph agent`.
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
# Process at most 3 issues this session, then idle
|
|
214
|
+
ralph agent --max-tickets 3 --linear-team ENG
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
When the limit is reached, ralph logs a yellow notice and the dashboard header shows `โ tickets โคN`. Polling continues (to handle conflict re-fixes on already-started issues), but no new issues are queued.
|
|
218
|
+
|
|
219
|
+
#### Dashboard
|
|
220
|
+
|
|
221
|
+
The `ralph agent` terminal dashboard shows a full-terminal layout with three always-visible panels:
|
|
222
|
+
|
|
223
|
+
- **RALPH AGENT** (blue box): engine/model, concurrency, poll interval, active limits (`iter โคN`, `cost โค$N`, `tickets โคN`), feature flags (โ PR โ fixCI โ worktree), and the Linear filter on the second line
|
|
224
|
+
- **POLL STATUS + WORKERS** (side-by-side): last-poll counts and next-poll countdown; active/queued worker totals with colored counts
|
|
225
|
+
- **TASKS tab bar** (when multiple workers run): numbered worker tabs with priority glyph and phase โ Tab/โ โ to switch, 1-9 to jump
|
|
226
|
+
|
|
227
|
+
Each worker card shows:
|
|
228
|
+
|
|
229
|
+
- Priority badge (`โฒ URGENT` / `โ HIGH` / `ยท MED` / `โ LOW`) + issue identifier + title + mode badge (`[NEW]` / `[RESUME]` / `[FIX]`)
|
|
230
|
+
- `โ LINEAR ISSUE-ID` and `โ PR #N` (short labels, not full URLs)
|
|
231
|
+
- `โถ TASK` โ first unchecked task from `tasks.md`, updated every second
|
|
232
|
+
- `PHASE` with color (cyan = working, yellow = git ops, blue = CI, green = done, red = gave-up) + time in phase
|
|
233
|
+
- `โต CMD` when a shell command is in flight (shows the command and how long it's been running)
|
|
234
|
+
- `LOG` โ path to the worker's log file for `tail -f`
|
|
235
|
+
- `โ OUTPUT โ` section with live stdout/stderr (scales to fill remaining terminal height for the focused worker)
|
|
236
|
+
|
|
204
237
|
## OpenSpec Flow
|
|
205
238
|
|
|
206
239
|
There are no phases. One loop, one prompt, one `tasks.md` checklist.
|
package/dist/cli/index.js
CHANGED
|
@@ -56409,7 +56409,7 @@ function log(msg) {
|
|
|
56409
56409
|
// package.json
|
|
56410
56410
|
var package_default = {
|
|
56411
56411
|
name: "@neriros/ralphy",
|
|
56412
|
-
version: "2.13.
|
|
56412
|
+
version: "2.13.8",
|
|
56413
56413
|
description: "An iterative AI task execution framework. Orchestrates multi-phase autonomous work using Claude or Codex engines.",
|
|
56414
56414
|
keywords: [
|
|
56415
56415
|
"agent",
|
|
@@ -56559,6 +56559,7 @@ var HELP_TEXT = [
|
|
|
56559
56559
|
" Types: label, status",
|
|
56560
56560
|
" --create-pr Push the worker branch and open a GitHub PR on success (needs --worktree)",
|
|
56561
56561
|
" --fix-ci After opening the PR, re-run on CI failures until green (needs --create-pr)",
|
|
56562
|
+
" --max-tickets <n> Stop picking up new issues after N have been started (0 = unlimited)",
|
|
56562
56563
|
"",
|
|
56563
56564
|
" --help, -h Show this help message",
|
|
56564
56565
|
"",
|
|
@@ -56643,7 +56644,8 @@ async function parseArgs(argv) {
|
|
|
56643
56644
|
worktree: false,
|
|
56644
56645
|
indicators: {},
|
|
56645
56646
|
createPr: false,
|
|
56646
|
-
fixCi: false
|
|
56647
|
+
fixCi: false,
|
|
56648
|
+
maxTickets: 0
|
|
56647
56649
|
};
|
|
56648
56650
|
let expectModel = false;
|
|
56649
56651
|
let expectModelFlag = false;
|
|
@@ -56659,6 +56661,7 @@ async function parseArgs(argv) {
|
|
|
56659
56661
|
let expectLinearAssignee = false;
|
|
56660
56662
|
let expectPollInterval = false;
|
|
56661
56663
|
let expectConcurrency = false;
|
|
56664
|
+
let expectMaxTickets = false;
|
|
56662
56665
|
let expectIndicator = false;
|
|
56663
56666
|
for (const arg of argv) {
|
|
56664
56667
|
if (expectModel) {
|
|
@@ -56737,6 +56740,11 @@ async function parseArgs(argv) {
|
|
|
56737
56740
|
expectConcurrency = false;
|
|
56738
56741
|
continue;
|
|
56739
56742
|
}
|
|
56743
|
+
if (expectMaxTickets) {
|
|
56744
|
+
result2.maxTickets = parseInt(arg, 10);
|
|
56745
|
+
expectMaxTickets = false;
|
|
56746
|
+
continue;
|
|
56747
|
+
}
|
|
56740
56748
|
if (expectIndicator) {
|
|
56741
56749
|
const { key, marker } = parseIndicatorArg(arg);
|
|
56742
56750
|
mergeIndicator(result2.indicators, key, marker);
|
|
@@ -56807,6 +56815,9 @@ async function parseArgs(argv) {
|
|
|
56807
56815
|
case "--concurrency":
|
|
56808
56816
|
expectConcurrency = true;
|
|
56809
56817
|
break;
|
|
56818
|
+
case "--max-tickets":
|
|
56819
|
+
expectMaxTickets = true;
|
|
56820
|
+
break;
|
|
56810
56821
|
case "--worktree":
|
|
56811
56822
|
result2.worktree = true;
|
|
56812
56823
|
break;
|
|
@@ -70562,6 +70573,7 @@ class AgentCoordinator {
|
|
|
70562
70573
|
queue = [];
|
|
70563
70574
|
stopped = false;
|
|
70564
70575
|
conflictNotified = new Set;
|
|
70576
|
+
ticketsStarted = 0;
|
|
70565
70577
|
constructor(deps, opts) {
|
|
70566
70578
|
this.deps = deps;
|
|
70567
70579
|
this.opts = opts;
|
|
@@ -70575,6 +70587,9 @@ class AgentCoordinator {
|
|
|
70575
70587
|
get activeWorkers() {
|
|
70576
70588
|
return this.workers;
|
|
70577
70589
|
}
|
|
70590
|
+
get ticketsStartedCount() {
|
|
70591
|
+
return this.ticketsStarted;
|
|
70592
|
+
}
|
|
70578
70593
|
async init() {}
|
|
70579
70594
|
async pollOnce() {
|
|
70580
70595
|
if (this.stopped)
|
|
@@ -70596,8 +70611,17 @@ class AgentCoordinator {
|
|
|
70596
70611
|
const queuedIds = new Set(this.queue.map((q) => q.issue.id));
|
|
70597
70612
|
const activeIds = new Set(this.workers.map((w) => w.issueId));
|
|
70598
70613
|
const eligible = (id) => !queuedIds.has(id) && !activeIds.has(id) && !this.pendingIds.has(id);
|
|
70614
|
+
const maxT = this.opts.maxTickets ?? 0;
|
|
70615
|
+
const atTicketLimit = () => {
|
|
70616
|
+
if (maxT === 0)
|
|
70617
|
+
return false;
|
|
70618
|
+
const inFlight = this.ticketsStarted + this.queue.length + this.workers.length + this.pendingIds.size;
|
|
70619
|
+
return inFlight >= maxT;
|
|
70620
|
+
};
|
|
70599
70621
|
let added = 0;
|
|
70600
70622
|
for (const issue of inProgress) {
|
|
70623
|
+
if (atTicketLimit())
|
|
70624
|
+
break;
|
|
70601
70625
|
if (!eligible(issue.id))
|
|
70602
70626
|
continue;
|
|
70603
70627
|
if (!this.dependenciesResolved(issue))
|
|
@@ -70607,6 +70631,8 @@ class AgentCoordinator {
|
|
|
70607
70631
|
added += 1;
|
|
70608
70632
|
}
|
|
70609
70633
|
for (const issue of conflicted) {
|
|
70634
|
+
if (atTicketLimit())
|
|
70635
|
+
break;
|
|
70610
70636
|
if (!eligible(issue.id))
|
|
70611
70637
|
continue;
|
|
70612
70638
|
this.queue.push({ issue, mode: "conflict-fix" });
|
|
@@ -70614,6 +70640,8 @@ class AgentCoordinator {
|
|
|
70614
70640
|
added += 1;
|
|
70615
70641
|
}
|
|
70616
70642
|
for (const issue of todo) {
|
|
70643
|
+
if (atTicketLimit())
|
|
70644
|
+
break;
|
|
70617
70645
|
if (!eligible(issue.id))
|
|
70618
70646
|
continue;
|
|
70619
70647
|
if (!this.dependenciesResolved(issue))
|
|
@@ -70808,6 +70836,11 @@ class AgentCoordinator {
|
|
|
70808
70836
|
};
|
|
70809
70837
|
this.workers.push(worker);
|
|
70810
70838
|
this.pendingIds.delete(issue.id);
|
|
70839
|
+
this.ticketsStarted += 1;
|
|
70840
|
+
const maxT = this.opts.maxTickets ?? 0;
|
|
70841
|
+
if (maxT > 0 && this.ticketsStarted >= maxT) {
|
|
70842
|
+
this.deps.onLog(` ticket limit reached (${maxT}) \u2014 no new issues will be picked up`, "yellow");
|
|
70843
|
+
}
|
|
70811
70844
|
capture("agent_worker_spawned", {
|
|
70812
70845
|
spawn_mode: mode,
|
|
70813
70846
|
issue_identifier: issue.identifier
|
|
@@ -72054,7 +72087,8 @@ PR: ${prUrl}` : ""
|
|
|
72054
72087
|
...indicators.setConflicted !== undefined ? { setConflicted: indicators.setConflicted } : {},
|
|
72055
72088
|
...indicators.clearConflicted !== undefined ? { clearConflicted: indicators.clearConflicted } : {},
|
|
72056
72089
|
postComments: cfg.linear.postComments,
|
|
72057
|
-
commentEveryIterations: cfg.linear.updateEveryIterations
|
|
72090
|
+
commentEveryIterations: cfg.linear.updateEveryIterations,
|
|
72091
|
+
...args.maxTickets > 0 ? { maxTickets: args.maxTickets } : {}
|
|
72058
72092
|
});
|
|
72059
72093
|
const filterDesc = describeIndicators(indicators, team, assignee);
|
|
72060
72094
|
return {
|
|
@@ -72496,6 +72530,13 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
72496
72530
|
cfg.maxCostUsdPerTask
|
|
72497
72531
|
]
|
|
72498
72532
|
}, undefined, true, undefined, this),
|
|
72533
|
+
args.maxTickets > 0 && /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
|
|
72534
|
+
color: "yellow",
|
|
72535
|
+
children: [
|
|
72536
|
+
" \u2502 tickets \u2264",
|
|
72537
|
+
args.maxTickets
|
|
72538
|
+
]
|
|
72539
|
+
}, undefined, true, undefined, this),
|
|
72499
72540
|
cfg.createPrOnSuccess && /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
|
|
72500
72541
|
color: "green",
|
|
72501
72542
|
children: " \u25CF PR"
|