@neriros/ralphy 2.17.1 → 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 +251 -42
- 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;
|
|
@@ -73753,8 +73763,11 @@ init_worktree();
|
|
|
73753
73763
|
// apps/cli/src/debug.ts
|
|
73754
73764
|
init_log();
|
|
73755
73765
|
import { join as join20 } from "path";
|
|
73766
|
+
function fmtTs(d) {
|
|
73767
|
+
return d.toISOString().replace("T", " ").slice(0, 23);
|
|
73768
|
+
}
|
|
73756
73769
|
var LOG_LINE_RE = /^\[(.+?)\] \[(.+?)\] (.+)$/;
|
|
73757
|
-
function
|
|
73770
|
+
function parseTextLog(content) {
|
|
73758
73771
|
return content.split(`
|
|
73759
73772
|
`).filter(Boolean).flatMap((line) => {
|
|
73760
73773
|
const m = LOG_LINE_RE.exec(line);
|
|
@@ -73766,23 +73779,154 @@ function parseLog(content) {
|
|
|
73766
73779
|
return [{ ts, type: m[2], text: m[3] }];
|
|
73767
73780
|
});
|
|
73768
73781
|
}
|
|
73769
|
-
function
|
|
73770
|
-
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 };
|
|
73771
73899
|
}
|
|
73772
73900
|
var SPAWN_RE = /\u25B6 (\S+) \u2192 (\S+)/;
|
|
73773
|
-
async function resolveDebugTarget(opts) {
|
|
73901
|
+
async function resolveDebugTarget(projectRoot, opts) {
|
|
73774
73902
|
const agentLogFile = Bun.file(AGENT_LOG_PATH);
|
|
73775
|
-
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];
|
|
73776
73907
|
if (opts.name && !opts.issue) {
|
|
73777
|
-
for (const line of
|
|
73908
|
+
for (const line of allLines) {
|
|
73778
73909
|
const m = SPAWN_RE.exec(line.text);
|
|
73779
73910
|
if (m && m[2] === opts.name)
|
|
73780
73911
|
return { changeName: opts.name, identifier: m[1] };
|
|
73781
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
|
+
}
|
|
73782
73920
|
return { changeName: opts.name, identifier: undefined };
|
|
73783
73921
|
}
|
|
73784
73922
|
if (opts.issue && !opts.name) {
|
|
73785
|
-
|
|
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) {
|
|
73786
73930
|
const m = SPAWN_RE.exec(line.text);
|
|
73787
73931
|
if (m && m[1] === opts.issue)
|
|
73788
73932
|
return { changeName: m[2], identifier: opts.issue };
|
|
@@ -73852,34 +73996,33 @@ async function fetchGithubPr(changeName) {
|
|
|
73852
73996
|
]) ?? [];
|
|
73853
73997
|
return { ...pr, checks };
|
|
73854
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
|
+
}
|
|
73855
74003
|
async function runDebug(opts) {
|
|
73856
74004
|
const { projectRoot } = opts;
|
|
74005
|
+
const out = (s) => process.stdout.write(s + `
|
|
74006
|
+
`);
|
|
73857
74007
|
const agentLogFile = Bun.file(AGENT_LOG_PATH);
|
|
73858
|
-
const
|
|
73859
|
-
const
|
|
73860
|
-
|
|
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, {
|
|
73861
74013
|
...opts.name !== undefined ? { name: opts.name } : {},
|
|
73862
74014
|
...opts.issue !== undefined ? { issue: opts.issue } : {}
|
|
73863
74015
|
});
|
|
73864
74016
|
if (!changeName) {
|
|
73865
|
-
process.stderr.write(`! Could not resolve
|
|
74017
|
+
process.stderr.write(`! Could not resolve change name for ${opts.issue ?? opts.name}.
|
|
73866
74018
|
`);
|
|
73867
74019
|
process.exit(1);
|
|
73868
74020
|
}
|
|
73869
|
-
const
|
|
73870
|
-
|
|
73871
|
-
|
|
73872
|
-
|
|
73873
|
-
|
|
73874
|
-
issueIdentifier = m[1];
|
|
73875
|
-
break;
|
|
73876
|
-
}
|
|
73877
|
-
}
|
|
73878
|
-
}
|
|
73879
|
-
const workerLogPath = join20(projectRoot, ".ralph", "logs", `${changeName}.log`);
|
|
73880
|
-
const workerLogFile = Bun.file(workerLogPath);
|
|
73881
|
-
const workerLines = await workerLogFile.exists() ? parseLog(await workerLogFile.text()) : [];
|
|
73882
|
-
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);
|
|
73883
74026
|
const seen = new Set;
|
|
73884
74027
|
const timeline = merged.filter((l) => {
|
|
73885
74028
|
const key = `${l.ts.getTime()}:${l.type}:${l.text}`;
|
|
@@ -73888,8 +74031,24 @@ async function runDebug(opts) {
|
|
|
73888
74031
|
seen.add(key);
|
|
73889
74032
|
return true;
|
|
73890
74033
|
});
|
|
73891
|
-
|
|
73892
|
-
|
|
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);
|
|
73893
74052
|
out(`
|
|
73894
74053
|
=== Ralph Debug: ${changeName}${issueIdentifier ? ` (${issueIdentifier})` : ""} ===
|
|
73895
74054
|
`);
|
|
@@ -73897,12 +74056,30 @@ async function runDebug(opts) {
|
|
|
73897
74056
|
if (!timeline.length) {
|
|
73898
74057
|
out(" (no log entries found)");
|
|
73899
74058
|
} else {
|
|
73900
|
-
|
|
73901
|
-
const
|
|
73902
|
-
|
|
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
|
+
}
|
|
73903
74072
|
}
|
|
73904
74073
|
}
|
|
73905
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
|
+
}
|
|
73906
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");
|
|
73907
74084
|
if (!issueIdentifier) {
|
|
73908
74085
|
out(" (unknown identifier \u2014 pass --issue to query Linear directly)");
|
|
@@ -73924,7 +74101,13 @@ async function runDebug(opts) {
|
|
|
73924
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");
|
|
73925
74102
|
const pr = await fetchGithubPr(changeName);
|
|
73926
74103
|
if (!pr) {
|
|
73927
|
-
|
|
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
|
+
}
|
|
73928
74111
|
} else {
|
|
73929
74112
|
const failing = pr.checks.filter((c) => c.conclusion === "FAILURE" || c.conclusion === "failure");
|
|
73930
74113
|
const pending = pr.checks.filter((c) => c.state === "PENDING" || c.state === "IN_PROGRESS");
|
|
@@ -73946,32 +74129,58 @@ async function runDebug(opts) {
|
|
|
73946
74129
|
const lastEvent = timeline.at(-1);
|
|
73947
74130
|
if (lastEvent)
|
|
73948
74131
|
out(` Last event : ${fmtTs(lastEvent.ts)} ${lastEvent.text}`);
|
|
73949
|
-
const exitLine =
|
|
74132
|
+
const exitLine = timeline.find((l) => /exited \(code \d+\)/.test(l.text));
|
|
73950
74133
|
if (exitLine) {
|
|
73951
74134
|
const code = Number(/code (\d+)/.exec(exitLine.text)?.[1]);
|
|
73952
|
-
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";
|
|
73953
74136
|
out(` Exit code : ${code} \u2014 ${meaning}`);
|
|
73954
74137
|
}
|
|
73955
|
-
|
|
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));
|
|
73956
74166
|
if (logHas("setError applied"))
|
|
73957
74167
|
out(" \u26A0 setError applied \u2014 issue is quarantined in Linear");
|
|
73958
74168
|
if (logHas("setDone applied"))
|
|
73959
74169
|
out(" \u2713 setDone applied \u2014 issue marked done in Linear");
|
|
73960
74170
|
if (logHas("clearConflicted applied"))
|
|
73961
|
-
out(" \u2713 clearConflicted applied
|
|
74171
|
+
out(" \u2713 clearConflicted applied");
|
|
73962
74172
|
if (logHas("setConflicted applied"))
|
|
73963
74173
|
out(" \u26A0 setConflicted applied \u2014 merge conflicts detected");
|
|
73964
74174
|
if (logHas("skipping PR phase"))
|
|
73965
74175
|
out(" \u21A9 PR phase skipped \u2014 worker exited non-zero");
|
|
73966
74176
|
if (pr?.mergeable === "CONFLICTING")
|
|
73967
74177
|
out(" \u26A0 PR currently has merge conflicts");
|
|
73968
|
-
if (pr?.checks.some((c) => c.conclusion === "FAILURE"
|
|
74178
|
+
if (pr?.checks.some((c) => c.conclusion === "FAILURE"))
|
|
73969
74179
|
out(" \u26A0 PR has failing CI checks");
|
|
73970
|
-
}
|
|
73971
74180
|
const worktreePath = join20(projectRoot, ".ralph", "worktrees", changeName);
|
|
73972
|
-
|
|
73973
|
-
if (worktreeExists)
|
|
74181
|
+
if (await Bun.file(join20(worktreePath, ".git")).exists()) {
|
|
73974
74182
|
out(` Worktree : ${worktreePath}`);
|
|
74183
|
+
}
|
|
73975
74184
|
if (!timeline.length)
|
|
73976
74185
|
out(" (no log entries \u2014 has this change been started yet?)");
|
|
73977
74186
|
out("");
|
|
@@ -74041,7 +74250,7 @@ try {
|
|
|
74041
74250
|
`);
|
|
74042
74251
|
process.exit(1);
|
|
74043
74252
|
}
|
|
74044
|
-
await runDebug({ name: args.name, projectRoot });
|
|
74253
|
+
await runDebug({ name: args.name, projectRoot: args.projectRoot ?? projectRoot });
|
|
74045
74254
|
await shutdown();
|
|
74046
74255
|
process.exit(0);
|
|
74047
74256
|
}
|