@neriros/ralphy 2.20.3 → 2.21.0
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 +1 -1
- package/dist/cli/index.js +91 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -245,7 +245,7 @@ Both scripts log failures but never block the loop. **`appendPrompt`** (or `--pr
|
|
|
245
245
|
|
|
246
246
|
### Dashboard and logs
|
|
247
247
|
|
|
248
|
-
The terminal dashboard shows three always-visible panels: **RALPH AGENT** (engine/model, concurrency, poll interval, active limits, feature flags, Linear filter), **POLL STATUS + WORKERS** (last-poll bucket breakdown — `
|
|
248
|
+
The terminal dashboard shows three always-visible panels: **RALPH AGENT** (engine/model, concurrency, poll interval, active limits, feature flags, Linear filter), **POLL STATUS + WORKERS** (last-poll bucket breakdown — `todo · res · conf · rev · @` (each colored when non-zero) plus `↺ Ns` next-poll countdown, active/queued worker totals), and **TASKS tab bar** (numbered worker tabs — `Tab` / `← →` / `1-9` to switch).
|
|
249
249
|
|
|
250
250
|
Each worker card shows: priority badge + identifier + title + mode badge, `↗ LINEAR`, `↗ PR`, `▶ TASK` (first unchecked task from `tasks.md`, refreshed every second), `PHASE` with color + elapsed time, `⏵ CMD` when a shell command is in flight, `LOG` path for `tail -f`, and `─ OUTPUT ─` with live stdout/stderr.
|
|
251
251
|
|
package/dist/cli/index.js
CHANGED
|
@@ -35029,8 +35029,8 @@ import { readFileSync as readFileSync2 } from "fs";
|
|
|
35029
35029
|
import { resolve } from "path";
|
|
35030
35030
|
function getVersion() {
|
|
35031
35031
|
try {
|
|
35032
|
-
if ("2.
|
|
35033
|
-
return "2.
|
|
35032
|
+
if ("2.21.0")
|
|
35033
|
+
return "2.21.0";
|
|
35034
35034
|
} catch {}
|
|
35035
35035
|
const dirsToTry = [];
|
|
35036
35036
|
try {
|
|
@@ -35361,6 +35361,7 @@ var init_cli = __esm(() => {
|
|
|
35361
35361
|
"getInProgress",
|
|
35362
35362
|
"getConflicted",
|
|
35363
35363
|
"getReview",
|
|
35364
|
+
"getAutoMerge",
|
|
35364
35365
|
"setInProgress",
|
|
35365
35366
|
"setDone",
|
|
35366
35367
|
"setError",
|
|
@@ -35372,7 +35373,8 @@ var init_cli = __esm(() => {
|
|
|
35372
35373
|
"getTodo",
|
|
35373
35374
|
"getInProgress",
|
|
35374
35375
|
"getConflicted",
|
|
35375
|
-
"getReview"
|
|
35376
|
+
"getReview",
|
|
35377
|
+
"getAutoMerge"
|
|
35376
35378
|
]);
|
|
35377
35379
|
HELP_TEXT = [
|
|
35378
35380
|
`ralph v${VERSION}`,
|
|
@@ -35415,7 +35417,7 @@ var init_cli = __esm(() => {
|
|
|
35415
35417
|
" --indicator getTodo:status:Todo",
|
|
35416
35418
|
" --indicator setDone:label:shipped",
|
|
35417
35419
|
" --indicator setDone:status:Done (combined with above \u2192 multi-marker)",
|
|
35418
|
-
" Keys: getTodo, getInProgress, getConflicted, getReview,",
|
|
35420
|
+
" Keys: getTodo, getInProgress, getConflicted, getReview, getAutoMerge,",
|
|
35419
35421
|
" setInProgress, setDone, setError, setConflicted,",
|
|
35420
35422
|
" clearConflicted, clearReview",
|
|
35421
35423
|
" Types: label, status",
|
|
@@ -59591,9 +59593,14 @@ var MarkerSchema, GetIndicatorSchema, SetIndicatorSchema, IndicatorsSchema, Ralp
|
|
|
59591
59593
|
// Open a pull request after a task succeeds.
|
|
59592
59594
|
"createPrOnSuccess": false,
|
|
59593
59595
|
|
|
59594
|
-
// Base branch for pull requests.
|
|
59596
|
+
// Base branch for pull requests. Override per-issue by labelling the
|
|
59597
|
+
// Linear issue with "ralph:branch:<branch-name>".
|
|
59595
59598
|
"prBaseBranch": "main",
|
|
59596
59599
|
|
|
59600
|
+
// Merge strategy used when GitHub auto-merge is enabled (see getAutoMerge
|
|
59601
|
+
// indicator below). One of "squash", "merge", "rebase".
|
|
59602
|
+
"autoMergeStrategy": "squash",
|
|
59603
|
+
|
|
59597
59604
|
// Let the agent attempt to fix CI failures after a PR is created.
|
|
59598
59605
|
"fixCiOnFailure": false,
|
|
59599
59606
|
|
|
@@ -59655,6 +59662,11 @@ var MarkerSchema, GetIndicatorSchema, SetIndicatorSchema, IndicatorsSchema, Ralp
|
|
|
59655
59662
|
// and prepend a task that ingests the non-Ralph comments).
|
|
59656
59663
|
// "getReview": { "filter": [{ "type": "label", "value": "ralph:review" }] },
|
|
59657
59664
|
|
|
59665
|
+
// Issues opted in for auto-merge: when an issue matches, Ralph enables
|
|
59666
|
+
// GitHub auto-merge ("gh pr merge --auto --<autoMergeStrategy>") right
|
|
59667
|
+
// after opening the PR so the PR merges as soon as required checks pass.
|
|
59668
|
+
// "getAutoMerge": { "filter": [{ "type": "label", "value": "ralph:auto-merge" }] },
|
|
59669
|
+
|
|
59658
59670
|
// Applied when Ralph picks up an issue.
|
|
59659
59671
|
// "setInProgress": { "type": "label", "value": "ralph:in-progress" },
|
|
59660
59672
|
|
|
@@ -59694,6 +59706,7 @@ var init_config = __esm(() => {
|
|
|
59694
59706
|
getInProgress: GetIndicatorSchema.optional(),
|
|
59695
59707
|
getConflicted: GetIndicatorSchema.optional(),
|
|
59696
59708
|
getReview: GetIndicatorSchema.optional(),
|
|
59709
|
+
getAutoMerge: GetIndicatorSchema.optional(),
|
|
59697
59710
|
setInProgress: SetIndicatorSchema.optional(),
|
|
59698
59711
|
setDone: SetIndicatorSchema.optional(),
|
|
59699
59712
|
setError: SetIndicatorSchema.optional(),
|
|
@@ -59736,6 +59749,7 @@ var init_config = __esm(() => {
|
|
|
59736
59749
|
appendPrompt: exports_external.string().optional(),
|
|
59737
59750
|
createPrOnSuccess: exports_external.boolean().default(false),
|
|
59738
59751
|
prBaseBranch: exports_external.string().default("main"),
|
|
59752
|
+
autoMergeStrategy: exports_external.enum(["squash", "merge", "rebase"]).default("squash"),
|
|
59739
59753
|
fixCiOnFailure: exports_external.boolean().default(false),
|
|
59740
59754
|
maxCiFixAttempts: exports_external.number().int().positive().default(5),
|
|
59741
59755
|
ciPollIntervalSeconds: exports_external.number().int().positive().default(30),
|
|
@@ -60087,6 +60101,23 @@ async function addLabelToIssue(apiKey, issueId, labelId) {
|
|
|
60087
60101
|
labelId
|
|
60088
60102
|
});
|
|
60089
60103
|
}
|
|
60104
|
+
function baseBranchFromLabels(labels) {
|
|
60105
|
+
for (const label of labels) {
|
|
60106
|
+
if (label.toLowerCase().startsWith(BRANCH_LABEL_PREFIX)) {
|
|
60107
|
+
const value = label.slice(BRANCH_LABEL_PREFIX.length).trim();
|
|
60108
|
+
if (value)
|
|
60109
|
+
return value;
|
|
60110
|
+
}
|
|
60111
|
+
}
|
|
60112
|
+
return;
|
|
60113
|
+
}
|
|
60114
|
+
function issueMatchesGetIndicator(issue, indicator) {
|
|
60115
|
+
if (!indicator || indicator.filter.length === 0)
|
|
60116
|
+
return false;
|
|
60117
|
+
const labels = new Set(issue.labels.map((l) => l.toLowerCase()));
|
|
60118
|
+
const stateName = issue.state.name.toLowerCase();
|
|
60119
|
+
return indicator.filter.some((m) => m.type === "label" ? labels.has(m.value.toLowerCase()) : stateName === m.value.toLowerCase());
|
|
60120
|
+
}
|
|
60090
60121
|
async function removeLabelFromIssue(apiKey, issueId, labelId) {
|
|
60091
60122
|
const mutation = `mutation RemoveLabel($id: String!, $labelId: String!) {
|
|
60092
60123
|
issueRemoveLabel(id: $id, labelId: $labelId) { success }
|
|
@@ -60096,7 +60127,7 @@ async function removeLabelFromIssue(apiKey, issueId, labelId) {
|
|
|
60096
60127
|
labelId
|
|
60097
60128
|
});
|
|
60098
60129
|
}
|
|
60099
|
-
var LINEAR_API = "https://api.linear.app/graphql";
|
|
60130
|
+
var LINEAR_API = "https://api.linear.app/graphql", BRANCH_LABEL_PREFIX = "ralph:branch:";
|
|
60100
60131
|
|
|
60101
60132
|
// apps/cli/src/agent/coordinator.ts
|
|
60102
60133
|
class AgentCoordinator {
|
|
@@ -60842,6 +60873,7 @@ ${pe.stderr ?? ""}`;
|
|
|
60842
60873
|
}
|
|
60843
60874
|
}
|
|
60844
60875
|
async function createPrWithRetry(ctx, issue) {
|
|
60876
|
+
const base2 = ctx.base;
|
|
60845
60877
|
const maxAttempts = ctx.cfg.maxCiFixAttempts;
|
|
60846
60878
|
let hookFixAttempt = 0;
|
|
60847
60879
|
let nonFfRebaseAttempted = false;
|
|
@@ -60849,7 +60881,7 @@ async function createPrWithRetry(ctx, issue) {
|
|
|
60849
60881
|
while (true) {
|
|
60850
60882
|
try {
|
|
60851
60883
|
ctx.emit("pr-create", "git push + gh pr create");
|
|
60852
|
-
pr = await createPullRequest({ cwd: ctx.cwd, branch: ctx.branch, issue, base:
|
|
60884
|
+
pr = await createPullRequest({ cwd: ctx.cwd, branch: ctx.branch, issue, base: base2 }, ctx.cmd);
|
|
60853
60885
|
return { pr, gaveUp: false };
|
|
60854
60886
|
} catch (err) {
|
|
60855
60887
|
const e = err;
|
|
@@ -60961,10 +60993,10 @@ async function fixConflictsAndCiLoop(ctx, prUrl, wantFixCi, checkPrConflict) {
|
|
|
60961
60993
|
ctx.emit("conflict-fix-inner", `attempt ${outerAttempt}/${maxOuterAttempts}`);
|
|
60962
60994
|
ctx.log(` merge conflicts on PR (attempt ${outerAttempt}/${maxOuterAttempts}) \u2014 spawning resolution task`, "yellow");
|
|
60963
60995
|
const conflictCode = await runWorkerWithFixTask(ctx, "Resolve PR merge conflicts", [
|
|
60964
|
-
`The PR ${prUrl} has merge conflicts with \`${ctx.
|
|
60996
|
+
`The PR ${prUrl} has merge conflicts with \`${ctx.base}\`.`,
|
|
60965
60997
|
"",
|
|
60966
60998
|
"Steps:",
|
|
60967
|
-
`1. \`git fetch origin ${ctx.
|
|
60999
|
+
`1. \`git fetch origin ${ctx.base}\` then rebase or merge \`${ctx.base}\` into the current branch.`,
|
|
60968
61000
|
"2. Resolve conflicts in the files git lists.",
|
|
60969
61001
|
"3. Stage and commit the resolution."
|
|
60970
61002
|
].join(`
|
|
@@ -61016,16 +61048,32 @@ async function fixConflictsAndCiLoop(ctx, prUrl, wantFixCi, checkPrConflict) {
|
|
|
61016
61048
|
return 0;
|
|
61017
61049
|
}
|
|
61018
61050
|
async function runPrPhase(input, deps) {
|
|
61019
|
-
const {
|
|
61051
|
+
const {
|
|
61052
|
+
changeName,
|
|
61053
|
+
cwd: cwd2,
|
|
61054
|
+
branch,
|
|
61055
|
+
changeDir,
|
|
61056
|
+
stateFilePath,
|
|
61057
|
+
issue,
|
|
61058
|
+
wantFixCi,
|
|
61059
|
+
wantAutoMerge,
|
|
61060
|
+
cfg
|
|
61061
|
+
} = input;
|
|
61020
61062
|
const { cmd, log: log2, emit, respawnWorker, registerPr, checkPrConflict } = deps;
|
|
61021
61063
|
if (!branch || !issue) {
|
|
61022
61064
|
log2(`! createPr requested but no worktree branch is tracked for ${changeName} (use --worktree)`, "yellow");
|
|
61023
61065
|
return PR_FAILED_EXIT;
|
|
61024
61066
|
}
|
|
61067
|
+
const labelBase = baseBranchFromLabels(issue.labels);
|
|
61068
|
+
const base2 = labelBase ?? cfg.prBaseBranch;
|
|
61069
|
+
if (labelBase && labelBase !== cfg.prBaseBranch) {
|
|
61070
|
+
log2(` base branch override from label: ${labelBase}`, "gray");
|
|
61071
|
+
}
|
|
61025
61072
|
const ctx = {
|
|
61026
61073
|
changeName,
|
|
61027
61074
|
cwd: cwd2,
|
|
61028
61075
|
branch,
|
|
61076
|
+
base: base2,
|
|
61029
61077
|
changeDir,
|
|
61030
61078
|
stateFilePath,
|
|
61031
61079
|
cfg,
|
|
@@ -61046,11 +61094,21 @@ async function runPrPhase(input, deps) {
|
|
|
61046
61094
|
if (prGaveUp)
|
|
61047
61095
|
return PR_FAILED_EXIT;
|
|
61048
61096
|
if (!pr) {
|
|
61049
|
-
log2(` no commits ahead of ${
|
|
61097
|
+
log2(` no commits ahead of ${base2} \u2014 skipping PR`, "gray");
|
|
61050
61098
|
return 0;
|
|
61051
61099
|
}
|
|
61052
61100
|
log2(` ${pr.created ? "opened" : "found existing"} PR: ${pr.url}`, "green");
|
|
61053
61101
|
registerPr?.(changeName, pr.url);
|
|
61102
|
+
if (wantAutoMerge) {
|
|
61103
|
+
try {
|
|
61104
|
+
await cmd.run(["gh", "pr", "merge", pr.url, "--auto", `--${cfg.autoMergeStrategy}`], cwd2);
|
|
61105
|
+
log2(` enabled auto-merge (${cfg.autoMergeStrategy}) on ${pr.url}`, "green");
|
|
61106
|
+
emit("auto-merge-enabled", cfg.autoMergeStrategy);
|
|
61107
|
+
} catch (err) {
|
|
61108
|
+
const e = err;
|
|
61109
|
+
log2(`! failed to enable auto-merge on ${pr.url}: ${e.stderr?.trim() || e.message}`, "yellow");
|
|
61110
|
+
}
|
|
61111
|
+
}
|
|
61054
61112
|
return fixConflictsAndCiLoop(ctx, pr.url, wantFixCi, checkPrConflict);
|
|
61055
61113
|
}
|
|
61056
61114
|
async function runWorktreeCleanupPhase(input, deps) {
|
|
@@ -61112,6 +61170,7 @@ async function runPostTask(input, deps) {
|
|
|
61112
61170
|
useWorktree,
|
|
61113
61171
|
wantPr,
|
|
61114
61172
|
wantFixCi,
|
|
61173
|
+
wantAutoMerge,
|
|
61115
61174
|
cfg,
|
|
61116
61175
|
respawnWorker
|
|
61117
61176
|
} = input;
|
|
@@ -61120,7 +61179,17 @@ async function runPostTask(input, deps) {
|
|
|
61120
61179
|
log2(` skipping PR phase for ${changeName} (worker exited with code ${effectiveCode})`, "gray");
|
|
61121
61180
|
}
|
|
61122
61181
|
if (effectiveCode === 0 && wantPr) {
|
|
61123
|
-
effectiveCode = await runPrPhase({
|
|
61182
|
+
effectiveCode = await runPrPhase({
|
|
61183
|
+
changeName,
|
|
61184
|
+
cwd: cwd2,
|
|
61185
|
+
branch,
|
|
61186
|
+
changeDir,
|
|
61187
|
+
stateFilePath,
|
|
61188
|
+
issue,
|
|
61189
|
+
wantFixCi,
|
|
61190
|
+
wantAutoMerge,
|
|
61191
|
+
cfg
|
|
61192
|
+
}, {
|
|
61124
61193
|
cmd,
|
|
61125
61194
|
log: log2,
|
|
61126
61195
|
emit,
|
|
@@ -61646,6 +61715,8 @@ PR: ${prUrl}` : ""
|
|
|
61646
61715
|
const tracedCmd = onWorkerCmd ? traceCmdRunner(cmdRunner, (cmd) => onWorkerCmd(changeName, cmd, "start"), (cmd, ms, ok) => onWorkerCmd(changeName, cmd, "end", ms, ok)) : cmdRunner;
|
|
61647
61716
|
const wantPr = args.createPr || cfg.createPrOnSuccess;
|
|
61648
61717
|
const wantFixCi = args.fixCi || cfg.fixCiOnFailure;
|
|
61718
|
+
const issueForChange = issueByChange.get(changeName);
|
|
61719
|
+
const wantAutoMerge = issueForChange ? issueMatchesGetIndicator(issueForChange, indicators.getAutoMerge) : false;
|
|
61649
61720
|
const wrapped = handle.exited.then(async (code) => {
|
|
61650
61721
|
const workerLayout = projectLayout(cwd2);
|
|
61651
61722
|
const effectiveCode = await runPostTask({
|
|
@@ -61660,9 +61731,11 @@ PR: ${prUrl}` : ""
|
|
|
61660
61731
|
useWorktree,
|
|
61661
61732
|
wantPr,
|
|
61662
61733
|
wantFixCi,
|
|
61734
|
+
wantAutoMerge,
|
|
61663
61735
|
cfg: {
|
|
61664
61736
|
teardownScript: cfg.teardownScript ?? null,
|
|
61665
61737
|
prBaseBranch: cfg.prBaseBranch,
|
|
61738
|
+
autoMergeStrategy: cfg.autoMergeStrategy,
|
|
61666
61739
|
maxCiFixAttempts: cfg.maxCiFixAttempts,
|
|
61667
61740
|
ciPollIntervalSeconds: cfg.ciPollIntervalSeconds,
|
|
61668
61741
|
cleanupWorktreeOnSuccess: cfg.cleanupWorktreeOnSuccess,
|
|
@@ -73223,6 +73296,8 @@ function phaseColor(phase) {
|
|
|
73223
73296
|
case "ci-poll":
|
|
73224
73297
|
case "ci-fix":
|
|
73225
73298
|
return "blue";
|
|
73299
|
+
case "auto-merge-enabled":
|
|
73300
|
+
return "green";
|
|
73226
73301
|
case "teardown":
|
|
73227
73302
|
case "cleanup":
|
|
73228
73303
|
return "gray";
|
|
@@ -73659,18 +73734,6 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
73659
73734
|
}, undefined, false, undefined, this),
|
|
73660
73735
|
pollStatus.lastAt !== null && /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(jsx_dev_runtime9.Fragment, {
|
|
73661
73736
|
children: [
|
|
73662
|
-
/* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
|
|
73663
|
-
dimColor: true,
|
|
73664
|
-
children: "\u2502"
|
|
73665
|
-
}, undefined, false, undefined, this),
|
|
73666
|
-
/* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
|
|
73667
|
-
dimColor: true,
|
|
73668
|
-
children: "found"
|
|
73669
|
-
}, undefined, false, undefined, this),
|
|
73670
|
-
/* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
|
|
73671
|
-
color: "white",
|
|
73672
|
-
children: pollStatus.lastFound
|
|
73673
|
-
}, undefined, false, undefined, this),
|
|
73674
73737
|
pollStatus.lastBuckets && /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(jsx_dev_runtime9.Fragment, {
|
|
73675
73738
|
children: [
|
|
73676
73739
|
/* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
|
|
@@ -73691,7 +73754,7 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
73691
73754
|
}, undefined, false, undefined, this),
|
|
73692
73755
|
/* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
|
|
73693
73756
|
dimColor: true,
|
|
73694
|
-
children: "
|
|
73757
|
+
children: "res"
|
|
73695
73758
|
}, undefined, false, undefined, this),
|
|
73696
73759
|
/* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
|
|
73697
73760
|
color: pollStatus.lastBuckets.inProgress > 0 ? "cyan" : "white",
|
|
@@ -73703,7 +73766,7 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
73703
73766
|
}, undefined, false, undefined, this),
|
|
73704
73767
|
/* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
|
|
73705
73768
|
dimColor: true,
|
|
73706
|
-
children: "
|
|
73769
|
+
children: "conf"
|
|
73707
73770
|
}, undefined, false, undefined, this),
|
|
73708
73771
|
/* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
|
|
73709
73772
|
color: pollStatus.lastBuckets.conflicted > 0 ? "red" : "white",
|
|
@@ -73715,7 +73778,7 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
73715
73778
|
}, undefined, false, undefined, this),
|
|
73716
73779
|
/* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
|
|
73717
73780
|
dimColor: true,
|
|
73718
|
-
children: "
|
|
73781
|
+
children: "rev"
|
|
73719
73782
|
}, undefined, false, undefined, this),
|
|
73720
73783
|
/* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
|
|
73721
73784
|
color: pollStatus.lastBuckets.review > 0 ? "yellow" : "white",
|
|
@@ -73727,7 +73790,7 @@ function AgentMode({ args, projectRoot, statesDir, tasksDir }) {
|
|
|
73727
73790
|
}, undefined, false, undefined, this),
|
|
73728
73791
|
/* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
|
|
73729
73792
|
dimColor: true,
|
|
73730
|
-
children: "
|
|
73793
|
+
children: "@"
|
|
73731
73794
|
}, undefined, false, undefined, this),
|
|
73732
73795
|
/* @__PURE__ */ jsx_dev_runtime9.jsxDEV(Text, {
|
|
73733
73796
|
color: pollStatus.lastBuckets.mentions > 0 ? "magenta" : "white",
|