@neriros/ralphy 2.17.0 → 2.17.2
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/dist/cli/index.js +264 -47
- package/package.json +1 -1
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.17.
|
|
35033
|
-
return "2.17.
|
|
35032
|
+
if ("2.17.2")
|
|
35033
|
+
return "2.17.2";
|
|
35034
35034
|
} catch {}
|
|
35035
35035
|
const dirsToTry = [];
|
|
35036
35036
|
try {
|
|
@@ -35131,6 +35131,7 @@ async function parseArgs(argv) {
|
|
|
35131
35131
|
createPr: false,
|
|
35132
35132
|
fixCi: false,
|
|
35133
35133
|
maxTickets: 0,
|
|
35134
|
+
projectRoot: undefined,
|
|
35134
35135
|
jsonOutput: false
|
|
35135
35136
|
};
|
|
35136
35137
|
let expectModel = false;
|
|
@@ -35149,6 +35150,7 @@ async function parseArgs(argv) {
|
|
|
35149
35150
|
let expectConcurrency = false;
|
|
35150
35151
|
let expectMaxTickets = false;
|
|
35151
35152
|
let expectIndicator = false;
|
|
35153
|
+
let expectProjectRoot = false;
|
|
35152
35154
|
for (const arg of argv) {
|
|
35153
35155
|
if (expectModel) {
|
|
35154
35156
|
if (VALID_MODELS.has(arg)) {
|
|
@@ -35237,6 +35239,11 @@ async function parseArgs(argv) {
|
|
|
35237
35239
|
expectIndicator = false;
|
|
35238
35240
|
continue;
|
|
35239
35241
|
}
|
|
35242
|
+
if (expectProjectRoot) {
|
|
35243
|
+
result2.projectRoot = arg;
|
|
35244
|
+
expectProjectRoot = false;
|
|
35245
|
+
continue;
|
|
35246
|
+
}
|
|
35240
35247
|
switch (arg) {
|
|
35241
35248
|
case "--claude":
|
|
35242
35249
|
if (result2.engineSet && result2.engine !== "claude") {
|
|
@@ -35325,6 +35332,9 @@ async function parseArgs(argv) {
|
|
|
35325
35332
|
case "--from-agent":
|
|
35326
35333
|
result2.fromAgent = true;
|
|
35327
35334
|
break;
|
|
35335
|
+
case "--project-root":
|
|
35336
|
+
expectProjectRoot = true;
|
|
35337
|
+
break;
|
|
35328
35338
|
default:
|
|
35329
35339
|
if (VALID_MODES.has(arg)) {
|
|
35330
35340
|
result2.mode = arg;
|
|
@@ -60796,6 +60806,8 @@ async function fixConflictsAndCiLoop(ctx, prUrl, wantFixCi, checkPrConflict) {
|
|
|
60796
60806
|
} catch (err) {
|
|
60797
60807
|
ctx.log(`! conflict check failed: ${err.message}`, "yellow");
|
|
60798
60808
|
}
|
|
60809
|
+
if (!conflicting && ciConfirmedGreen)
|
|
60810
|
+
return 0;
|
|
60799
60811
|
if (conflicting) {
|
|
60800
60812
|
outerAttempt++;
|
|
60801
60813
|
ciConfirmedGreen = false;
|
|
@@ -61442,12 +61454,18 @@ PR: ${prUrl}` : ""
|
|
|
61442
61454
|
onPhase: (phase, detail) => onWorkerPhase(changeName, phase, detail)
|
|
61443
61455
|
},
|
|
61444
61456
|
checkPrConflict: async (prUrl) => {
|
|
61445
|
-
|
|
61446
|
-
|
|
61447
|
-
|
|
61448
|
-
|
|
61449
|
-
|
|
61457
|
+
for (let attempt2 = 0;attempt2 < 5; attempt2++) {
|
|
61458
|
+
try {
|
|
61459
|
+
const res = await tracedCmd.run(["gh", "pr", "view", prUrl, "--json", "mergeable", "--jq", ".mergeable"], cwd2);
|
|
61460
|
+
const mergeable = res.stdout.trim();
|
|
61461
|
+
if (mergeable !== "UNKNOWN")
|
|
61462
|
+
return mergeable === "CONFLICTING";
|
|
61463
|
+
} catch {
|
|
61464
|
+
return false;
|
|
61465
|
+
}
|
|
61466
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
61450
61467
|
}
|
|
61468
|
+
return false;
|
|
61451
61469
|
}
|
|
61452
61470
|
});
|
|
61453
61471
|
cwdByChange.delete(changeName);
|
|
@@ -73745,8 +73763,11 @@ init_worktree();
|
|
|
73745
73763
|
// apps/cli/src/debug.ts
|
|
73746
73764
|
init_log();
|
|
73747
73765
|
import { join as join20 } from "path";
|
|
73766
|
+
function fmtTs(d) {
|
|
73767
|
+
return d.toISOString().replace("T", " ").slice(0, 23);
|
|
73768
|
+
}
|
|
73748
73769
|
var LOG_LINE_RE = /^\[(.+?)\] \[(.+?)\] (.+)$/;
|
|
73749
|
-
function
|
|
73770
|
+
function parseTextLog(content) {
|
|
73750
73771
|
return content.split(`
|
|
73751
73772
|
`).filter(Boolean).flatMap((line) => {
|
|
73752
73773
|
const m = LOG_LINE_RE.exec(line);
|
|
@@ -73758,23 +73779,154 @@ function parseLog(content) {
|
|
|
73758
73779
|
return [{ ts, type: m[2], text: m[3] }];
|
|
73759
73780
|
});
|
|
73760
73781
|
}
|
|
73761
|
-
function
|
|
73762
|
-
return
|
|
73782
|
+
function parseJsonlLog(content, filterChangeName) {
|
|
73783
|
+
return content.split(`
|
|
73784
|
+
`).filter(Boolean).flatMap((line) => {
|
|
73785
|
+
let entry;
|
|
73786
|
+
try {
|
|
73787
|
+
entry = JSON.parse(line);
|
|
73788
|
+
} catch {
|
|
73789
|
+
return [];
|
|
73790
|
+
}
|
|
73791
|
+
const ts = new Date(entry.ts);
|
|
73792
|
+
if (isNaN(ts.getTime()))
|
|
73793
|
+
return [];
|
|
73794
|
+
if (filterChangeName && entry.changeName && entry.changeName !== filterChangeName) {
|
|
73795
|
+
return [];
|
|
73796
|
+
}
|
|
73797
|
+
const cn = entry.changeName ?? "";
|
|
73798
|
+
switch (entry.type) {
|
|
73799
|
+
case "started":
|
|
73800
|
+
return [{ ts, type: "agent", text: `agent started v${entry.version ?? "?"}` }];
|
|
73801
|
+
case "stopped":
|
|
73802
|
+
return [{ ts, type: "agent", text: "agent stopped" }];
|
|
73803
|
+
case "worker_started":
|
|
73804
|
+
return [{ ts, type: "spawn", text: `${cn}: worker spawned` }];
|
|
73805
|
+
case "worker_phase": {
|
|
73806
|
+
const detail = entry.detail ? ` (${entry.detail})` : "";
|
|
73807
|
+
return [{ ts, type: "phase", text: `${cn}: ${entry.phase}${detail}` }];
|
|
73808
|
+
}
|
|
73809
|
+
case "worker_cmd_start":
|
|
73810
|
+
return [
|
|
73811
|
+
{
|
|
73812
|
+
ts,
|
|
73813
|
+
type: "cmd",
|
|
73814
|
+
text: `${cn}: \u2192 ${(entry.cmd ?? []).slice(0, 4).join(" ")}`
|
|
73815
|
+
}
|
|
73816
|
+
];
|
|
73817
|
+
case "worker_cmd_end":
|
|
73818
|
+
return [
|
|
73819
|
+
{
|
|
73820
|
+
ts,
|
|
73821
|
+
type: "cmd",
|
|
73822
|
+
text: `${cn}: \u2190 ${(entry.cmd ?? []).slice(0, 2).join(" ")} (${entry.durationMs}ms, ${entry.ok ? "ok" : "err"})`
|
|
73823
|
+
}
|
|
73824
|
+
];
|
|
73825
|
+
case "worker_pr":
|
|
73826
|
+
return [{ ts, type: "pr", text: `${cn}: PR \u2192 ${entry.prUrl}` }];
|
|
73827
|
+
case "worker_exited":
|
|
73828
|
+
return [
|
|
73829
|
+
{
|
|
73830
|
+
ts,
|
|
73831
|
+
type: "exit",
|
|
73832
|
+
text: `${cn}: exited (code ${entry.exitCode ?? "?"})`
|
|
73833
|
+
}
|
|
73834
|
+
];
|
|
73835
|
+
case "log":
|
|
73836
|
+
return [{ ts, type: "coord", text: entry.text ?? "" }];
|
|
73837
|
+
case "poll_done":
|
|
73838
|
+
return [
|
|
73839
|
+
{
|
|
73840
|
+
ts,
|
|
73841
|
+
type: "poll",
|
|
73842
|
+
text: `poll: found=${entry.found} added=${entry.added}`
|
|
73843
|
+
}
|
|
73844
|
+
];
|
|
73845
|
+
default:
|
|
73846
|
+
return [];
|
|
73847
|
+
}
|
|
73848
|
+
});
|
|
73849
|
+
}
|
|
73850
|
+
function detectStuck(lines) {
|
|
73851
|
+
if (lines.length < 10)
|
|
73852
|
+
return null;
|
|
73853
|
+
const recent = lines.slice(-20).filter((l) => l.type === "phase");
|
|
73854
|
+
if (recent.length < 5)
|
|
73855
|
+
return null;
|
|
73856
|
+
const phaseNames = recent.map((l) => l.text.split(": ").slice(1).join(": "));
|
|
73857
|
+
const unique = new Set(phaseNames);
|
|
73858
|
+
if (unique.size !== 1)
|
|
73859
|
+
return null;
|
|
73860
|
+
const phase = phaseNames[0];
|
|
73861
|
+
const all = lines.filter((l) => l.type === "phase" && l.text.includes(phase));
|
|
73862
|
+
const first = all[0];
|
|
73863
|
+
const last2 = all[all.length - 1];
|
|
73864
|
+
const minutesStuck = (last2.ts.getTime() - first.ts.getTime()) / 60000;
|
|
73865
|
+
const prEntry = lines.find((l) => l.type === "pr");
|
|
73866
|
+
const cmdEntry = lines.find((l) => l.type === "cmd" && l.text.includes("gh") && l.text.includes("mergeable"));
|
|
73867
|
+
const watchingPrUrl = prEntry?.text.split("PR \u2192 ")[1] ?? cmdEntry?.text.match(/https:\/\/github\.com\/[^\s)]+/)?.[0];
|
|
73868
|
+
return {
|
|
73869
|
+
phase,
|
|
73870
|
+
count: all.length,
|
|
73871
|
+
firstSeen: first.ts,
|
|
73872
|
+
lastSeen: last2.ts,
|
|
73873
|
+
minutesStuck,
|
|
73874
|
+
watchingPrUrl
|
|
73875
|
+
};
|
|
73876
|
+
}
|
|
73877
|
+
async function inspectBinary(projectRoot) {
|
|
73878
|
+
const binPath = join20(projectRoot, ".ralph", "bin", "cli.js");
|
|
73879
|
+
const file = Bun.file(binPath);
|
|
73880
|
+
if (!await file.exists())
|
|
73881
|
+
return null;
|
|
73882
|
+
let embeddedVersion;
|
|
73883
|
+
try {
|
|
73884
|
+
const slice2 = await file.slice(0, 50000).text();
|
|
73885
|
+
const m = /"(\d+\.\d+\.\d+)"/.exec(slice2);
|
|
73886
|
+
if (m)
|
|
73887
|
+
embeddedVersion = m[1];
|
|
73888
|
+
} catch {}
|
|
73889
|
+
let builtAt;
|
|
73890
|
+
try {
|
|
73891
|
+
const r = Bun.spawnSync(["stat", "-f", "%Sm", "-t", "%Y-%m-%dT%H:%M:%S", binPath], {
|
|
73892
|
+
stderr: "ignore"
|
|
73893
|
+
});
|
|
73894
|
+
const s = r.stdout.toString().trim();
|
|
73895
|
+
if (s)
|
|
73896
|
+
builtAt = new Date(s);
|
|
73897
|
+
} catch {}
|
|
73898
|
+
return { path: binPath, embeddedVersion, builtAt };
|
|
73763
73899
|
}
|
|
73764
73900
|
var SPAWN_RE = /\u25B6 (\S+) \u2192 (\S+)/;
|
|
73765
|
-
async function resolveDebugTarget(opts) {
|
|
73901
|
+
async function resolveDebugTarget(projectRoot, opts) {
|
|
73766
73902
|
const agentLogFile = Bun.file(AGENT_LOG_PATH);
|
|
73767
|
-
const
|
|
73903
|
+
const textLines = await agentLogFile.exists() ? parseTextLog(await agentLogFile.text()) : [];
|
|
73904
|
+
const jsonlLogFile = Bun.file(join20(projectRoot, ".ralph", "agent.log"));
|
|
73905
|
+
const jsonlLines = await jsonlLogFile.exists() ? parseJsonlLog(await jsonlLogFile.text()) : [];
|
|
73906
|
+
const allLines = [...textLines, ...jsonlLines];
|
|
73768
73907
|
if (opts.name && !opts.issue) {
|
|
73769
|
-
for (const line of
|
|
73908
|
+
for (const line of allLines) {
|
|
73770
73909
|
const m = SPAWN_RE.exec(line.text);
|
|
73771
73910
|
if (m && m[2] === opts.name)
|
|
73772
73911
|
return { changeName: opts.name, identifier: m[1] };
|
|
73773
73912
|
}
|
|
73913
|
+
for (const line of allLines) {
|
|
73914
|
+
if (line.text.includes(opts.name) && line.text.includes("COD-")) {
|
|
73915
|
+
const id = /COD-\d+/.exec(line.text)?.[0];
|
|
73916
|
+
if (id)
|
|
73917
|
+
return { changeName: opts.name, identifier: id };
|
|
73918
|
+
}
|
|
73919
|
+
}
|
|
73774
73920
|
return { changeName: opts.name, identifier: undefined };
|
|
73775
73921
|
}
|
|
73776
73922
|
if (opts.issue && !opts.name) {
|
|
73777
|
-
|
|
73923
|
+
const pattern = new RegExp(`(cod-${opts.issue.toLowerCase().replace("cod-", "")}[\\w-]+)`);
|
|
73924
|
+
for (const line of allLines) {
|
|
73925
|
+
const m = pattern.exec(line.text);
|
|
73926
|
+
if (m)
|
|
73927
|
+
return { changeName: m[1], identifier: opts.issue };
|
|
73928
|
+
}
|
|
73929
|
+
for (const line of allLines) {
|
|
73778
73930
|
const m = SPAWN_RE.exec(line.text);
|
|
73779
73931
|
if (m && m[1] === opts.issue)
|
|
73780
73932
|
return { changeName: m[2], identifier: opts.issue };
|
|
@@ -73844,34 +73996,33 @@ async function fetchGithubPr(changeName) {
|
|
|
73844
73996
|
]) ?? [];
|
|
73845
73997
|
return { ...pr, checks };
|
|
73846
73998
|
}
|
|
73999
|
+
function fetchMergeableNow(prUrl) {
|
|
74000
|
+
const result2 = Bun.spawnSync(["gh", "pr", "view", prUrl, "--json", "mergeable", "--jq", ".mergeable"], { stderr: "ignore" });
|
|
74001
|
+
return result2.exitCode === 0 ? result2.stdout.toString().trim() : null;
|
|
74002
|
+
}
|
|
73847
74003
|
async function runDebug(opts) {
|
|
73848
74004
|
const { projectRoot } = opts;
|
|
74005
|
+
const out = (s) => process.stdout.write(s + `
|
|
74006
|
+
`);
|
|
73849
74007
|
const agentLogFile = Bun.file(AGENT_LOG_PATH);
|
|
73850
|
-
const
|
|
73851
|
-
const
|
|
73852
|
-
|
|
74008
|
+
const textLines = await agentLogFile.exists() ? parseTextLog(await agentLogFile.text()) : [];
|
|
74009
|
+
const jsonlLogPath = join20(projectRoot, ".ralph", "agent.log");
|
|
74010
|
+
const jsonlLogFile = Bun.file(jsonlLogPath);
|
|
74011
|
+
const hasJsonlLog = await jsonlLogFile.exists();
|
|
74012
|
+
let { changeName, identifier: issueIdentifier } = await resolveDebugTarget(projectRoot, {
|
|
73853
74013
|
...opts.name !== undefined ? { name: opts.name } : {},
|
|
73854
74014
|
...opts.issue !== undefined ? { issue: opts.issue } : {}
|
|
73855
74015
|
});
|
|
73856
74016
|
if (!changeName) {
|
|
73857
|
-
process.stderr.write(`! Could not resolve
|
|
74017
|
+
process.stderr.write(`! Could not resolve change name for ${opts.issue ?? opts.name}.
|
|
73858
74018
|
`);
|
|
73859
74019
|
process.exit(1);
|
|
73860
74020
|
}
|
|
73861
|
-
const
|
|
73862
|
-
|
|
73863
|
-
|
|
73864
|
-
|
|
73865
|
-
|
|
73866
|
-
issueIdentifier = m[1];
|
|
73867
|
-
break;
|
|
73868
|
-
}
|
|
73869
|
-
}
|
|
73870
|
-
}
|
|
73871
|
-
const workerLogPath = join20(projectRoot, ".ralph", "logs", `${changeName}.log`);
|
|
73872
|
-
const workerLogFile = Bun.file(workerLogPath);
|
|
73873
|
-
const workerLines = await workerLogFile.exists() ? parseLog(await workerLogFile.text()) : [];
|
|
73874
|
-
const merged = [...relevant, ...workerLines].sort((a, b) => +a.ts - +b.ts);
|
|
74021
|
+
const jsonlLines = hasJsonlLog ? parseJsonlLog(await jsonlLogFile.text(), changeName) : [];
|
|
74022
|
+
const relevantText = textLines.filter((l) => l.text.includes(changeName) || issueIdentifier !== undefined && l.text.includes(issueIdentifier));
|
|
74023
|
+
const workerLogFile = Bun.file(join20(projectRoot, ".ralph", "logs", `${changeName}.log`));
|
|
74024
|
+
const workerLines = await workerLogFile.exists() ? parseTextLog(await workerLogFile.text()) : [];
|
|
74025
|
+
const merged = [...relevantText, ...jsonlLines, ...workerLines].sort((a, b) => +a.ts - +b.ts);
|
|
73875
74026
|
const seen = new Set;
|
|
73876
74027
|
const timeline = merged.filter((l) => {
|
|
73877
74028
|
const key = `${l.ts.getTime()}:${l.type}:${l.text}`;
|
|
@@ -73880,8 +74031,24 @@ async function runDebug(opts) {
|
|
|
73880
74031
|
seen.add(key);
|
|
73881
74032
|
return true;
|
|
73882
74033
|
});
|
|
73883
|
-
|
|
73884
|
-
|
|
74034
|
+
if (!issueIdentifier) {
|
|
74035
|
+
for (const line of timeline) {
|
|
74036
|
+
const m = SPAWN_RE.exec(line.text);
|
|
74037
|
+
if (m && m[2] === changeName) {
|
|
74038
|
+
issueIdentifier = m[1];
|
|
74039
|
+
break;
|
|
74040
|
+
}
|
|
74041
|
+
if (line.text.includes(changeName)) {
|
|
74042
|
+
const id = /(COD|ENG|DEV)-\d+/.exec(line.text)?.[0];
|
|
74043
|
+
if (id) {
|
|
74044
|
+
issueIdentifier = id;
|
|
74045
|
+
break;
|
|
74046
|
+
}
|
|
74047
|
+
}
|
|
74048
|
+
}
|
|
74049
|
+
}
|
|
74050
|
+
const stuck = detectStuck(timeline);
|
|
74051
|
+
const binary = await inspectBinary(projectRoot);
|
|
73885
74052
|
out(`
|
|
73886
74053
|
=== Ralph Debug: ${changeName}${issueIdentifier ? ` (${issueIdentifier})` : ""} ===
|
|
73887
74054
|
`);
|
|
@@ -73889,12 +74056,30 @@ async function runDebug(opts) {
|
|
|
73889
74056
|
if (!timeline.length) {
|
|
73890
74057
|
out(" (no log entries found)");
|
|
73891
74058
|
} else {
|
|
73892
|
-
|
|
73893
|
-
const
|
|
73894
|
-
|
|
74059
|
+
if (stuck && timeline.length > 20) {
|
|
74060
|
+
const phaseLines = timeline.filter((l) => l.type === "phase" && l.text.includes(stuck.phase));
|
|
74061
|
+
const nonPhase = timeline.filter((l) => !(l.type === "phase" && l.text.includes(stuck.phase)));
|
|
74062
|
+
for (const line of nonPhase) {
|
|
74063
|
+
const prefix = line.type === "output" ? " \u2502" : " \xB7";
|
|
74064
|
+
out(`${prefix} ${fmtTs(line.ts)} [${line.type.padEnd(7)}] ${line.text}`);
|
|
74065
|
+
}
|
|
74066
|
+
out(` \u21BA ... ${phaseLines.length}\xD7 ${stuck.phase} (${stuck.minutesStuck.toFixed(1)} min) ...`);
|
|
74067
|
+
} else {
|
|
74068
|
+
for (const line of timeline) {
|
|
74069
|
+
const prefix = line.type === "output" ? " \u2502" : " \xB7";
|
|
74070
|
+
out(`${prefix} ${fmtTs(line.ts)} [${line.type.padEnd(7)}] ${line.text}`);
|
|
74071
|
+
}
|
|
73895
74072
|
}
|
|
73896
74073
|
}
|
|
73897
74074
|
out("");
|
|
74075
|
+
if (binary) {
|
|
74076
|
+
out("\u2500\u2500 Installed binary \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
74077
|
+
out(` Path : ${binary.path}`);
|
|
74078
|
+
out(` Embedded version : ${binary.embeddedVersion ?? "(unknown)"}`);
|
|
74079
|
+
if (binary.builtAt)
|
|
74080
|
+
out(` Built at : ${fmtTs(binary.builtAt)}`);
|
|
74081
|
+
out("");
|
|
74082
|
+
}
|
|
73898
74083
|
out("\u2500\u2500 Current Linear state \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
73899
74084
|
if (!issueIdentifier) {
|
|
73900
74085
|
out(" (unknown identifier \u2014 pass --issue to query Linear directly)");
|
|
@@ -73916,7 +74101,13 @@ async function runDebug(opts) {
|
|
|
73916
74101
|
out("\u2500\u2500 Current GitHub PR \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
73917
74102
|
const pr = await fetchGithubPr(changeName);
|
|
73918
74103
|
if (!pr) {
|
|
73919
|
-
|
|
74104
|
+
if (stuck?.watchingPrUrl) {
|
|
74105
|
+
const m = fetchMergeableNow(stuck.watchingPrUrl);
|
|
74106
|
+
out(` Watching : ${stuck.watchingPrUrl}`);
|
|
74107
|
+
out(` Mergeable: ${m ?? "(error fetching)"}`);
|
|
74108
|
+
} else {
|
|
74109
|
+
out(` (no PR found for branch ralph/${changeName})`);
|
|
74110
|
+
}
|
|
73920
74111
|
} else {
|
|
73921
74112
|
const failing = pr.checks.filter((c) => c.conclusion === "FAILURE" || c.conclusion === "failure");
|
|
73922
74113
|
const pending = pr.checks.filter((c) => c.state === "PENDING" || c.state === "IN_PROGRESS");
|
|
@@ -73938,32 +74129,58 @@ async function runDebug(opts) {
|
|
|
73938
74129
|
const lastEvent = timeline.at(-1);
|
|
73939
74130
|
if (lastEvent)
|
|
73940
74131
|
out(` Last event : ${fmtTs(lastEvent.ts)} ${lastEvent.text}`);
|
|
73941
|
-
const exitLine =
|
|
74132
|
+
const exitLine = timeline.find((l) => /exited \(code \d+\)/.test(l.text));
|
|
73942
74133
|
if (exitLine) {
|
|
73943
74134
|
const code = Number(/code (\d+)/.exec(exitLine.text)?.[1]);
|
|
73944
|
-
const meaning = code === 0 ? "success" : code === 70 ? "CI fix loop exhausted its attempt budget" : code === 71 ? "push or PR creation failed
|
|
74135
|
+
const meaning = code === 0 ? "success" : code === 70 ? "CI fix loop exhausted its attempt budget" : code === 71 ? "push or PR creation failed" : "worker subprocess failed";
|
|
73945
74136
|
out(` Exit code : ${code} \u2014 ${meaning}`);
|
|
73946
74137
|
}
|
|
73947
|
-
|
|
74138
|
+
if (stuck) {
|
|
74139
|
+
out(` \u26A0 STUCK in ${stuck.phase} \u2014 ${stuck.count} iterations over ${stuck.minutesStuck.toFixed(1)} min`);
|
|
74140
|
+
if (stuck.watchingPrUrl) {
|
|
74141
|
+
const mergeable = fetchMergeableNow(stuck.watchingPrUrl);
|
|
74142
|
+
out(` Watching : ${stuck.watchingPrUrl}`);
|
|
74143
|
+
out(` Mergeable : ${mergeable ?? "(error)"} (live fetch)`);
|
|
74144
|
+
if (mergeable === "MERGEABLE") {
|
|
74145
|
+
out(` \u2192 PR is MERGEABLE \u2014 loop should have exited. Likely cause:`);
|
|
74146
|
+
if (binary?.embeddedVersion && binary.embeddedVersion < "2.17.1") {
|
|
74147
|
+
out(` Local binary is v${binary.embeddedVersion} (fix shipped in v2.17.1). Update with:`);
|
|
74148
|
+
out(` cd ${projectRoot} && bunx @neriros/ralphy@latest make-install`);
|
|
74149
|
+
} else {
|
|
74150
|
+
out(` This is the conflict-check infinite loop bug (fixed in v2.17.1).`);
|
|
74151
|
+
out(` Restart the agent after updating to v2.17.1.`);
|
|
74152
|
+
}
|
|
74153
|
+
}
|
|
74154
|
+
}
|
|
74155
|
+
}
|
|
74156
|
+
if (binary) {
|
|
74157
|
+
const embV = binary.embeddedVersion ?? "?";
|
|
74158
|
+
const logV = timeline.find((l) => l.text.includes("agent started"))?.text.match(/v([\d.]+)/)?.[1];
|
|
74159
|
+
if (logV && embV !== logV) {
|
|
74160
|
+
out(` \u26A0 Version mismatch: binary says v${embV}, agent reported v${logV}`);
|
|
74161
|
+
out(` The binary reads version from a package.json at runtime \u2014 the actual`);
|
|
74162
|
+
out(` running code is v${embV}, not v${logV}. Update the local install.`);
|
|
74163
|
+
}
|
|
74164
|
+
}
|
|
74165
|
+
const logHas = (s) => timeline.some((l) => l.text.includes(s));
|
|
73948
74166
|
if (logHas("setError applied"))
|
|
73949
74167
|
out(" \u26A0 setError applied \u2014 issue is quarantined in Linear");
|
|
73950
74168
|
if (logHas("setDone applied"))
|
|
73951
74169
|
out(" \u2713 setDone applied \u2014 issue marked done in Linear");
|
|
73952
74170
|
if (logHas("clearConflicted applied"))
|
|
73953
|
-
out(" \u2713 clearConflicted applied
|
|
74171
|
+
out(" \u2713 clearConflicted applied");
|
|
73954
74172
|
if (logHas("setConflicted applied"))
|
|
73955
74173
|
out(" \u26A0 setConflicted applied \u2014 merge conflicts detected");
|
|
73956
74174
|
if (logHas("skipping PR phase"))
|
|
73957
74175
|
out(" \u21A9 PR phase skipped \u2014 worker exited non-zero");
|
|
73958
74176
|
if (pr?.mergeable === "CONFLICTING")
|
|
73959
74177
|
out(" \u26A0 PR currently has merge conflicts");
|
|
73960
|
-
if (pr?.checks.some((c) => c.conclusion === "FAILURE"
|
|
74178
|
+
if (pr?.checks.some((c) => c.conclusion === "FAILURE"))
|
|
73961
74179
|
out(" \u26A0 PR has failing CI checks");
|
|
73962
|
-
}
|
|
73963
74180
|
const worktreePath = join20(projectRoot, ".ralph", "worktrees", changeName);
|
|
73964
|
-
|
|
73965
|
-
if (worktreeExists)
|
|
74181
|
+
if (await Bun.file(join20(worktreePath, ".git")).exists()) {
|
|
73966
74182
|
out(` Worktree : ${worktreePath}`);
|
|
74183
|
+
}
|
|
73967
74184
|
if (!timeline.length)
|
|
73968
74185
|
out(" (no log entries \u2014 has this change been started yet?)");
|
|
73969
74186
|
out("");
|
|
@@ -74033,7 +74250,7 @@ try {
|
|
|
74033
74250
|
`);
|
|
74034
74251
|
process.exit(1);
|
|
74035
74252
|
}
|
|
74036
|
-
await runDebug({ name: args.name, projectRoot });
|
|
74253
|
+
await runDebug({ name: args.name, projectRoot: args.projectRoot ?? projectRoot });
|
|
74037
74254
|
await shutdown();
|
|
74038
74255
|
process.exit(0);
|
|
74039
74256
|
}
|