@neriros/ralphy 3.10.5 → 3.10.6
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/shell/index.js +328 -63
- package/package.json +1 -1
package/dist/shell/index.js
CHANGED
|
@@ -18928,8 +18928,8 @@ import { readFileSync } from "fs";
|
|
|
18928
18928
|
import { resolve } from "path";
|
|
18929
18929
|
function getVersion() {
|
|
18930
18930
|
try {
|
|
18931
|
-
if ("3.10.
|
|
18932
|
-
return "3.10.
|
|
18931
|
+
if ("3.10.6")
|
|
18932
|
+
return "3.10.6";
|
|
18933
18933
|
} catch {}
|
|
18934
18934
|
const dirsToTry = [];
|
|
18935
18935
|
try {
|
|
@@ -101471,6 +101471,7 @@ async function parseAgentArgs(argv) {
|
|
|
101471
101471
|
noTmux: false,
|
|
101472
101472
|
checks: false,
|
|
101473
101473
|
review: false,
|
|
101474
|
+
agentDebug: false,
|
|
101474
101475
|
ticketTokens: []
|
|
101475
101476
|
};
|
|
101476
101477
|
const state = emptyParseState();
|
|
@@ -101593,6 +101594,9 @@ async function parseAgentArgs(argv) {
|
|
|
101593
101594
|
case "--debug":
|
|
101594
101595
|
result2.debug = true;
|
|
101595
101596
|
break;
|
|
101597
|
+
case "--agent-debug":
|
|
101598
|
+
result2.agentDebug = true;
|
|
101599
|
+
break;
|
|
101596
101600
|
case "--pre-existing-error-check":
|
|
101597
101601
|
result2.preExistingErrorCheck = true;
|
|
101598
101602
|
break;
|
|
@@ -101692,6 +101696,7 @@ var init_cli2 = __esm(() => {
|
|
|
101692
101696
|
" --checks List mode: show failing CI check names per PR",
|
|
101693
101697
|
" --review List mode: show unresolved review comment count per PR",
|
|
101694
101698
|
" --debug List mode: explain why a Linear ticket was not picked up (use with --name)",
|
|
101699
|
+
" --agent-debug After each ticket finishes, run a one-shot self-review and write a report to ~/.ralph/retro/",
|
|
101695
101700
|
" --help, -h Show this help message",
|
|
101696
101701
|
"",
|
|
101697
101702
|
"Examples:",
|
|
@@ -104551,6 +104556,15 @@ async function runPostTask(input, deps) {
|
|
|
104551
104556
|
emit3(succeeded ? "done" : "gave-up", succeeded ? undefined : `exit ${effectiveCode}`);
|
|
104552
104557
|
if (!succeeded)
|
|
104553
104558
|
await recordGaveUp(stateFilePath, log3, changeName);
|
|
104559
|
+
await deps.runRetrospective?.({
|
|
104560
|
+
changeName,
|
|
104561
|
+
cwd: cwd2,
|
|
104562
|
+
changeDir,
|
|
104563
|
+
stateFilePath,
|
|
104564
|
+
branch,
|
|
104565
|
+
issue: issue2,
|
|
104566
|
+
effectiveCode
|
|
104567
|
+
});
|
|
104554
104568
|
await runWorktreeCleanupPhase({ changeName, cwd: cwd2, projectRoot, useWorktree, effectiveCode, cfg }, { git: git2, log: log3, emit: emit3 });
|
|
104555
104569
|
await runTeardownPhase({ cwd: cwd2, teardownScript: cfg.teardownScript }, { runScript, log: log3, emit: emit3 });
|
|
104556
104570
|
return effectiveCode;
|
|
@@ -105218,6 +105232,32 @@ class AgentCoordinator {
|
|
|
105218
105232
|
if (!this.deps.syncTasks || !this.deps.getIterationCount)
|
|
105219
105233
|
return;
|
|
105220
105234
|
for (const w of this.workers) {
|
|
105235
|
+
if (this.deps.getTasksFingerprint) {
|
|
105236
|
+
let fingerprint;
|
|
105237
|
+
try {
|
|
105238
|
+
fingerprint = await this.deps.getTasksFingerprint(w.changeName);
|
|
105239
|
+
} catch (err) {
|
|
105240
|
+
this.deps.onLog(`! tasks fingerprint read failed for ${w.issueIdentifier}: ${err.message}`, "yellow");
|
|
105241
|
+
continue;
|
|
105242
|
+
}
|
|
105243
|
+
if (fingerprint === null || fingerprint === w.lastSyncedTasksFingerprint) {
|
|
105244
|
+
continue;
|
|
105245
|
+
}
|
|
105246
|
+
let iteration;
|
|
105247
|
+
try {
|
|
105248
|
+
iteration = await this.deps.getIterationCount(w.changeName);
|
|
105249
|
+
} catch (err) {
|
|
105250
|
+
this.deps.onLog(`! iteration count read failed for ${w.issueIdentifier}: ${err.message}`, "yellow");
|
|
105251
|
+
continue;
|
|
105252
|
+
}
|
|
105253
|
+
try {
|
|
105254
|
+
await this.deps.syncTasks(w, iteration);
|
|
105255
|
+
w.lastSyncedTasksFingerprint = fingerprint;
|
|
105256
|
+
} catch (err) {
|
|
105257
|
+
this.deps.onLog(`! sync-tasks (poll) failed for ${w.issueIdentifier}: ${err.message}`, "yellow");
|
|
105258
|
+
}
|
|
105259
|
+
continue;
|
|
105260
|
+
}
|
|
105221
105261
|
let count;
|
|
105222
105262
|
try {
|
|
105223
105263
|
count = await this.deps.getIterationCount(w.changeName);
|
|
@@ -105497,6 +105537,7 @@ class AgentCoordinator {
|
|
|
105497
105537
|
kill: handle.kill,
|
|
105498
105538
|
lastReportedIteration: 0,
|
|
105499
105539
|
lastSyncedIteration: 0,
|
|
105540
|
+
lastSyncedTasksFingerprint: null,
|
|
105500
105541
|
restarting: false,
|
|
105501
105542
|
reapedForAwaiting: false
|
|
105502
105543
|
};
|
|
@@ -105660,6 +105701,7 @@ class AgentCoordinator {
|
|
|
105660
105701
|
kill: () => {},
|
|
105661
105702
|
lastReportedIteration: 0,
|
|
105662
105703
|
lastSyncedIteration: 0,
|
|
105704
|
+
lastSyncedTasksFingerprint: null,
|
|
105663
105705
|
restarting: false,
|
|
105664
105706
|
reapedForAwaiting: false
|
|
105665
105707
|
};
|
|
@@ -107767,8 +107809,190 @@ var init_default2 = __esm(() => {
|
|
|
107767
107809
|
init_log();
|
|
107768
107810
|
});
|
|
107769
107811
|
|
|
107812
|
+
// apps/agent/src/agent/state/agent-run-state.ts
|
|
107813
|
+
import { basename as basename3, join as join29 } from "path";
|
|
107814
|
+
import { homedir as homedir6 } from "os";
|
|
107815
|
+
import { mkdir as mkdir11, writeFile } from "fs/promises";
|
|
107816
|
+
function agentRunStatePath(projectRoot) {
|
|
107817
|
+
return join29(homedir6(), ".ralph", basename3(projectRoot), "agent-state.json");
|
|
107818
|
+
}
|
|
107819
|
+
async function writeAgentRunState(state) {
|
|
107820
|
+
const path = agentRunStatePath(state.projectRoot);
|
|
107821
|
+
try {
|
|
107822
|
+
await mkdir11(join29(homedir6(), ".ralph", basename3(state.projectRoot)), { recursive: true });
|
|
107823
|
+
await writeFile(path, JSON.stringify(state, null, 2) + `
|
|
107824
|
+
`, "utf-8");
|
|
107825
|
+
} catch {}
|
|
107826
|
+
}
|
|
107827
|
+
var init_agent_run_state = () => {};
|
|
107828
|
+
|
|
107829
|
+
// packages/retro/src/disposition.ts
|
|
107830
|
+
function dispositionFromExitCode(code) {
|
|
107831
|
+
switch (code) {
|
|
107832
|
+
case 0:
|
|
107833
|
+
return "done";
|
|
107834
|
+
case NO_CHANGES_EXIT2:
|
|
107835
|
+
return "no-changes";
|
|
107836
|
+
case CI_FAILED_EXIT2:
|
|
107837
|
+
return "ci-failed";
|
|
107838
|
+
case PR_FAILED_EXIT2:
|
|
107839
|
+
return "pr-failed";
|
|
107840
|
+
default:
|
|
107841
|
+
return "error";
|
|
107842
|
+
}
|
|
107843
|
+
}
|
|
107844
|
+
var CI_FAILED_EXIT2 = 70, PR_FAILED_EXIT2 = 71, NO_CHANGES_EXIT2 = 72;
|
|
107845
|
+
|
|
107846
|
+
// packages/retro/src/paths.ts
|
|
107847
|
+
import { homedir as homedir7 } from "os";
|
|
107848
|
+
import { mkdir as mkdir12 } from "fs/promises";
|
|
107849
|
+
import { join as join30 } from "path";
|
|
107850
|
+
function retroDir() {
|
|
107851
|
+
return join30(homedir7(), ".ralph", "retro");
|
|
107852
|
+
}
|
|
107853
|
+
async function resolveRetroOutputPath(identifier, date5, dir = retroDir()) {
|
|
107854
|
+
await mkdir12(dir, { recursive: true });
|
|
107855
|
+
const base2 = join30(dir, `${identifier}-${date5}.md`);
|
|
107856
|
+
if (!await Bun.file(base2).exists())
|
|
107857
|
+
return base2;
|
|
107858
|
+
for (let n = 2;; n++) {
|
|
107859
|
+
const candidate = join30(dir, `${identifier}-${date5}-${n}.md`);
|
|
107860
|
+
if (!await Bun.file(candidate).exists())
|
|
107861
|
+
return candidate;
|
|
107862
|
+
}
|
|
107863
|
+
}
|
|
107864
|
+
var init_paths2 = () => {};
|
|
107865
|
+
|
|
107866
|
+
// packages/retro/src/prompt.ts
|
|
107867
|
+
function buildRetroPrompt(ctx, outputPath) {
|
|
107868
|
+
const disposition = dispositionFromExitCode(ctx.exitCode);
|
|
107869
|
+
const { paths } = ctx;
|
|
107870
|
+
const line = (label, value) => value ? `- ${label}: ${value}` : `- ${label}: (unavailable \u2014 note this in the report)`;
|
|
107871
|
+
return [
|
|
107872
|
+
`You are a retrospective analysis agent reviewing a finished automated ticket run.`,
|
|
107873
|
+
`Your job is to read the run's artifacts and write a thorough, honest self-review`,
|
|
107874
|
+
`to a markdown file. You are NOT fixing anything \u2014 this is analysis only.`,
|
|
107875
|
+
``,
|
|
107876
|
+
`## Ticket`,
|
|
107877
|
+
``,
|
|
107878
|
+
`- Identifier: ${ctx.identifier}`,
|
|
107879
|
+
`- Change name: ${ctx.changeName}`,
|
|
107880
|
+
`- Terminal disposition: ${disposition} (worker exit code ${ctx.exitCode})`,
|
|
107881
|
+
ctx.prUrl ? `- Pull request: ${ctx.prUrl}` : `- Pull request: none was opened`,
|
|
107882
|
+
`- Date: ${ctx.date}`,
|
|
107883
|
+
``,
|
|
107884
|
+
`### Ticket details`,
|
|
107885
|
+
``,
|
|
107886
|
+
ctx.ticketDigest,
|
|
107887
|
+
``,
|
|
107888
|
+
`## Data sources`,
|
|
107889
|
+
``,
|
|
107890
|
+
`Read whatever of the following exist. If a path is missing or empty, say so`,
|
|
107891
|
+
`explicitly in the report rather than guessing.`,
|
|
107892
|
+
``,
|
|
107893
|
+
line("Change directory (proposal/design/tasks/specs)", paths.changeDir),
|
|
107894
|
+
line("Loop state file", paths.stateFilePath),
|
|
107895
|
+
line("Worker log", paths.logFile),
|
|
107896
|
+
line("JSON event log", paths.jsonLogFile),
|
|
107897
|
+
line("Agent run state", paths.agentStateFile),
|
|
107898
|
+
ctx.prUrl ? `- You may inspect the PR read-only with \`gh pr view ${ctx.prUrl}\` and \`gh pr diff ${ctx.prUrl}\`.` : `- No PR exists; skip the PR section and note "no PR".`,
|
|
107899
|
+
``,
|
|
107900
|
+
`## Required report structure`,
|
|
107901
|
+
``,
|
|
107902
|
+
`Write GitHub-flavored markdown with these sections:`,
|
|
107903
|
+
`1. **Summary** \u2014 what the ticket asked for and how the run ended.`,
|
|
107904
|
+
`2. **What went well** \u2014 concrete things the run did right.`,
|
|
107905
|
+
`3. **What went wrong / friction** \u2014 failures, retries, wasted iterations,`,
|
|
107906
|
+
` wrong turns, anything that cost time or quality.`,
|
|
107907
|
+
`4. **Root-cause analysis** \u2014 for each problem, why it happened.`,
|
|
107908
|
+
`5. **Recommendations** \u2014 specific, actionable improvements (to the prompt,`,
|
|
107909
|
+
` the tasks, the codebase, or the workflow).`,
|
|
107910
|
+
`6. **Data gaps** \u2014 which data sources were unavailable or unread.`,
|
|
107911
|
+
``,
|
|
107912
|
+
`## Output`,
|
|
107913
|
+
``,
|
|
107914
|
+
`Write the complete report to this exact path using your file-write tool:`,
|
|
107915
|
+
``,
|
|
107916
|
+
` ${outputPath}`,
|
|
107917
|
+
``,
|
|
107918
|
+
`## Hard rules`,
|
|
107919
|
+
``,
|
|
107920
|
+
`- Do NOT run any git mutation: no commit, add, push, rebase, reset, checkout,`,
|
|
107921
|
+
` branch, merge, tag, or stash.`,
|
|
107922
|
+
`- Do NOT create, edit, comment on, close, or merge any pull request or issue.`,
|
|
107923
|
+
`- Do NOT modify any source file. The ONLY file you may write is the report at`,
|
|
107924
|
+
` the path above.`,
|
|
107925
|
+
`- Read-only inspection commands (\`git log\`, \`git diff\`, \`gh pr view\`,`,
|
|
107926
|
+
` \`gh pr diff\`, reading files) are allowed.`
|
|
107927
|
+
].join(`
|
|
107928
|
+
`);
|
|
107929
|
+
}
|
|
107930
|
+
var init_prompt = () => {};
|
|
107931
|
+
|
|
107932
|
+
// packages/retro/src/retro.ts
|
|
107933
|
+
async function runRetrospective(ctx, deps) {
|
|
107934
|
+
const { log: log3, runEngine: runEngine2, seen } = deps;
|
|
107935
|
+
const disposition = dispositionFromExitCode(ctx.exitCode);
|
|
107936
|
+
const key = `${ctx.identifier}:${disposition}:${ctx.date}`;
|
|
107937
|
+
if (seen.has(key)) {
|
|
107938
|
+
log3(` retrospective skipped for ${ctx.identifier} (already generated this run)`, "gray");
|
|
107939
|
+
return { written: false, skipped: "duplicate", disposition };
|
|
107940
|
+
}
|
|
107941
|
+
seen.add(key);
|
|
107942
|
+
try {
|
|
107943
|
+
const outputPath = await resolveRetroOutputPath(ctx.identifier, ctx.date);
|
|
107944
|
+
const prompt = buildRetroPrompt(ctx, outputPath);
|
|
107945
|
+
log3(` running retrospective for ${ctx.identifier} (${disposition}) \u2192 ${outputPath}`, "cyan");
|
|
107946
|
+
await runEngine2({
|
|
107947
|
+
engine: ctx.engine,
|
|
107948
|
+
model: ctx.model,
|
|
107949
|
+
prompt,
|
|
107950
|
+
cwd: ctx.cwd,
|
|
107951
|
+
onOutput: (l) => log3(l, "gray")
|
|
107952
|
+
});
|
|
107953
|
+
const written = await Bun.file(outputPath).exists();
|
|
107954
|
+
if (written) {
|
|
107955
|
+
log3(` retrospective written: ${outputPath}`, "green");
|
|
107956
|
+
} else {
|
|
107957
|
+
log3(`! retrospective engine finished but no report was written at ${outputPath}`, "yellow");
|
|
107958
|
+
}
|
|
107959
|
+
return { written, outputPath, disposition };
|
|
107960
|
+
} catch (err) {
|
|
107961
|
+
log3(`! retrospective failed for ${ctx.identifier}: ${err.message}`, "yellow");
|
|
107962
|
+
return { written: false, disposition };
|
|
107963
|
+
}
|
|
107964
|
+
}
|
|
107965
|
+
var init_retro = __esm(() => {
|
|
107966
|
+
init_paths2();
|
|
107967
|
+
init_prompt();
|
|
107968
|
+
init_paths2();
|
|
107969
|
+
init_prompt();
|
|
107970
|
+
});
|
|
107971
|
+
|
|
107770
107972
|
// apps/agent/src/agent/wire/spawn/worker.ts
|
|
107771
|
-
import { join as
|
|
107973
|
+
import { join as join31 } from "path";
|
|
107974
|
+
function localDateStamp(d) {
|
|
107975
|
+
const y = d.getFullYear();
|
|
107976
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
107977
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
107978
|
+
return `${y}-${m}-${day}`;
|
|
107979
|
+
}
|
|
107980
|
+
function buildTicketDigest(issue2, comments) {
|
|
107981
|
+
if (!issue2)
|
|
107982
|
+
return "(ticket details unavailable)";
|
|
107983
|
+
const lines = [`Title: ${issue2.title}`, "", issue2.description?.trim() || "(no description)"];
|
|
107984
|
+
if (comments.length > 0) {
|
|
107985
|
+
lines.push("", "Comments:");
|
|
107986
|
+
for (const c of comments) {
|
|
107987
|
+
lines.push(`- ${c.user?.name ?? "unknown"}: ${c.body}`);
|
|
107988
|
+
}
|
|
107989
|
+
}
|
|
107990
|
+
return lines.join(`
|
|
107991
|
+
`);
|
|
107992
|
+
}
|
|
107993
|
+
function retroDepEntry(agentDebug, hook) {
|
|
107994
|
+
return agentDebug ? { runRetrospective: hook } : {};
|
|
107995
|
+
}
|
|
107772
107996
|
function createSpawnWorker(input) {
|
|
107773
107997
|
const {
|
|
107774
107998
|
args,
|
|
@@ -107847,10 +108071,52 @@ function createSpawnWorker(input) {
|
|
|
107847
108071
|
c.push("--from-agent");
|
|
107848
108072
|
return c;
|
|
107849
108073
|
}
|
|
108074
|
+
const retroSeen = new Set;
|
|
108075
|
+
const runRetrospectiveHook = async (info) => {
|
|
108076
|
+
try {
|
|
108077
|
+
const identifier = info.issue?.identifier ?? info.changeName;
|
|
108078
|
+
const prUrl = prByChange?.get(info.changeName) ?? null;
|
|
108079
|
+
let digest = "(ticket details unavailable)";
|
|
108080
|
+
if (info.issue) {
|
|
108081
|
+
let comments = [];
|
|
108082
|
+
try {
|
|
108083
|
+
comments = await fetchIssueComments(apiKey, info.issue.id);
|
|
108084
|
+
} catch {}
|
|
108085
|
+
digest = buildTicketDigest(info.issue, comments);
|
|
108086
|
+
}
|
|
108087
|
+
const engine = args.engineSet ? args.engine : cfg.engine;
|
|
108088
|
+
const model = args.engineSet ? args.model : cfg.model;
|
|
108089
|
+
const ctx = {
|
|
108090
|
+
identifier,
|
|
108091
|
+
changeName: info.changeName,
|
|
108092
|
+
cwd: info.cwd,
|
|
108093
|
+
engine,
|
|
108094
|
+
model,
|
|
108095
|
+
exitCode: info.effectiveCode,
|
|
108096
|
+
prUrl,
|
|
108097
|
+
date: localDateStamp(new Date),
|
|
108098
|
+
ticketDigest: digest,
|
|
108099
|
+
paths: {
|
|
108100
|
+
changeDir: info.changeDir,
|
|
108101
|
+
stateFilePath: info.stateFilePath,
|
|
108102
|
+
logFile: join31(logsDir, `${info.changeName}.log`),
|
|
108103
|
+
jsonLogFile: args.jsonLogFile ?? null,
|
|
108104
|
+
agentStateFile: agentRunStatePath(projectRoot)
|
|
108105
|
+
}
|
|
108106
|
+
};
|
|
108107
|
+
await runRetrospective(ctx, {
|
|
108108
|
+
runEngine: (opts) => runEngine(opts),
|
|
108109
|
+
log: onLog,
|
|
108110
|
+
seen: retroSeen
|
|
108111
|
+
});
|
|
108112
|
+
} catch (err) {
|
|
108113
|
+
onLog(`! retrospective failed: ${err.message}`, "yellow");
|
|
108114
|
+
}
|
|
108115
|
+
};
|
|
107850
108116
|
return function spawnWorker(changeName, _issue, trigger) {
|
|
107851
108117
|
const cwd2 = cwdByChange.get(changeName) ?? projectRoot;
|
|
107852
108118
|
const injected = runners?.spawnWorker;
|
|
107853
|
-
const missionTasksPath =
|
|
108119
|
+
const missionTasksPath = join31(projectLayout(cwd2).changeDir(changeName), MISSION_TASKS_FILENAME);
|
|
107854
108120
|
const prevTasksPromise = (async () => {
|
|
107855
108121
|
const f2 = Bun.file(missionTasksPath);
|
|
107856
108122
|
return await f2.exists() ? await f2.text() : "";
|
|
@@ -107858,7 +108124,7 @@ function createSpawnWorker(input) {
|
|
|
107858
108124
|
let logFilePath;
|
|
107859
108125
|
let handle;
|
|
107860
108126
|
if (injected) {
|
|
107861
|
-
logFilePath =
|
|
108127
|
+
logFilePath = join31(logsDir, `${changeName}.log`);
|
|
107862
108128
|
handle = injected(buildTaskCmdFor(changeName), cwd2);
|
|
107863
108129
|
} else {
|
|
107864
108130
|
const r = defaultSpawn(changeName, buildTaskCmdFor(changeName), cwd2, logsDir, onWorkerOutput, `spawn at ${new Date().toISOString()}`);
|
|
@@ -107880,7 +108146,7 @@ function createSpawnWorker(input) {
|
|
|
107880
108146
|
const wantAutoMerge = issueForChange ? issueMatchesGetIndicator(issueForChange, indicators.getAutoMerge) : false;
|
|
107881
108147
|
const wrapped = handle.exited.then(async (code) => {
|
|
107882
108148
|
const workerLayout = projectLayout(cwd2);
|
|
107883
|
-
const validateSpecPath =
|
|
108149
|
+
const validateSpecPath = join31(workerLayout.changeDir(changeName), "specs", "validate.md");
|
|
107884
108150
|
const hasValidateSpec = await Bun.file(validateSpecPath).exists();
|
|
107885
108151
|
const wantValidateOnly = hasValidateSpec && !wantPrBase;
|
|
107886
108152
|
if (hasValidateSpec) {
|
|
@@ -107965,6 +108231,7 @@ function createSpawnWorker(input) {
|
|
|
107965
108231
|
git: gitRunner,
|
|
107966
108232
|
log: onLog,
|
|
107967
108233
|
runScript,
|
|
108234
|
+
...retroDepEntry(args.agentDebug, runRetrospectiveHook),
|
|
107968
108235
|
registerPr: (cn, url2) => onPrRegistered(cn, url2),
|
|
107969
108236
|
...onWorkerPhase && {
|
|
107970
108237
|
onPhase: (phase2, detail) => onWorkerPhase(changeName, phase2, detail)
|
|
@@ -108000,6 +108267,9 @@ var init_worker = __esm(() => {
|
|
|
108000
108267
|
init_runners();
|
|
108001
108268
|
init_pr_helpers();
|
|
108002
108269
|
init_wait_for_mergeability();
|
|
108270
|
+
init_agent_run_state();
|
|
108271
|
+
init_retro();
|
|
108272
|
+
init_engine();
|
|
108003
108273
|
});
|
|
108004
108274
|
|
|
108005
108275
|
// apps/agent/src/agent/baseline/runner.ts
|
|
@@ -108342,8 +108612,8 @@ var init_linear_sync = __esm(() => {
|
|
|
108342
108612
|
});
|
|
108343
108613
|
|
|
108344
108614
|
// apps/agent/src/agent/linear-sync/comment-sync.ts
|
|
108345
|
-
import { dirname as dirname13, join as
|
|
108346
|
-
import { mkdir as
|
|
108615
|
+
import { dirname as dirname13, join as join32 } from "path";
|
|
108616
|
+
import { mkdir as mkdir13, rename, unlink as unlink2 } from "fs/promises";
|
|
108347
108617
|
async function readStateJson(statePath) {
|
|
108348
108618
|
const file2 = Bun.file(statePath);
|
|
108349
108619
|
if (!await file2.exists())
|
|
@@ -108355,7 +108625,7 @@ async function readStateJson(statePath) {
|
|
|
108355
108625
|
}
|
|
108356
108626
|
}
|
|
108357
108627
|
async function writeStateJson(statePath, state) {
|
|
108358
|
-
await
|
|
108628
|
+
await mkdir13(dirname13(statePath), { recursive: true });
|
|
108359
108629
|
const tmp = `${statePath}.tmp-${process.pid}-${writeStateSeq++}`;
|
|
108360
108630
|
try {
|
|
108361
108631
|
await Bun.write(tmp, JSON.stringify(state, null, 2) + `
|
|
@@ -108394,7 +108664,7 @@ function isCommentNotFoundError(err) {
|
|
|
108394
108664
|
return text.includes("not found") || text.includes("could not find") || text.includes("entity not found");
|
|
108395
108665
|
}
|
|
108396
108666
|
async function readTasksMd(changeDir, log3) {
|
|
108397
|
-
const file2 = Bun.file(
|
|
108667
|
+
const file2 = Bun.file(join32(changeDir, "tasks.md"));
|
|
108398
108668
|
if (!await file2.exists()) {
|
|
108399
108669
|
log3(` comment-sync: tasks.md missing in ${changeDir}, skipping`, "gray");
|
|
108400
108670
|
return null;
|
|
@@ -108502,14 +108772,14 @@ async function postPlanCommentOnce(deps) {
|
|
|
108502
108772
|
const check2 = parsePlanningSection(tasksMd);
|
|
108503
108773
|
if (!check2.allChecked)
|
|
108504
108774
|
return null;
|
|
108505
|
-
const proposalPath =
|
|
108775
|
+
const proposalPath = join32(deps.changeDir, "proposal.md");
|
|
108506
108776
|
const why = await readSection(proposalPath, "Why");
|
|
108507
108777
|
const whatChanges = await readSection(proposalPath, "What Changes");
|
|
108508
108778
|
if (!why && !whatChanges) {
|
|
108509
108779
|
deps.log(` comment-sync: proposal.md has no Why/What Changes, skipping plan comment`, "gray");
|
|
108510
108780
|
return null;
|
|
108511
108781
|
}
|
|
108512
|
-
const designSummary = await readFirstParagraph(
|
|
108782
|
+
const designSummary = await readFirstParagraph(join32(deps.changeDir, "design.md"));
|
|
108513
108783
|
const parts = [`### ${PLAN_COMMENT_TITLE} \u2014 \`${deps.changeName}\``];
|
|
108514
108784
|
if (why) {
|
|
108515
108785
|
parts.push("", "**Why**", "", why);
|
|
@@ -260785,7 +261055,7 @@ var init_render_pdf = __esm(() => {
|
|
|
260785
261055
|
});
|
|
260786
261056
|
|
|
260787
261057
|
// apps/agent/src/agent/linear-sync/spec-attachments.ts
|
|
260788
|
-
import { dirname as dirname14, join as
|
|
261058
|
+
import { dirname as dirname14, join as join33 } from "path";
|
|
260789
261059
|
function describeLinearError(err) {
|
|
260790
261060
|
const e = err;
|
|
260791
261061
|
const parts = [e.message ?? String(err)];
|
|
@@ -260877,7 +261147,7 @@ async function syncSlot(deps, slot) {
|
|
|
260877
261147
|
const [primaryName, ...trailingNames] = spec.sourceFiles;
|
|
260878
261148
|
if (!primaryName)
|
|
260879
261149
|
return;
|
|
260880
|
-
const primary = Bun.file(
|
|
261150
|
+
const primary = Bun.file(join33(deps.changeDir, primaryName));
|
|
260881
261151
|
if (!await primary.exists()) {
|
|
260882
261152
|
deps.log(` spec-attachments: ${primaryName} missing, skipping`, "gray");
|
|
260883
261153
|
return;
|
|
@@ -260896,7 +261166,7 @@ async function syncSlot(deps, slot) {
|
|
|
260896
261166
|
const parts = [primaryBytes];
|
|
260897
261167
|
const enc = new TextEncoder;
|
|
260898
261168
|
for (const name of trailingNames) {
|
|
260899
|
-
const f2 = Bun.file(
|
|
261169
|
+
const f2 = Bun.file(join33(deps.changeDir, name));
|
|
260900
261170
|
if (!await f2.exists())
|
|
260901
261171
|
continue;
|
|
260902
261172
|
try {
|
|
@@ -261161,9 +261431,9 @@ var init_comment_sync2 = __esm(() => {
|
|
|
261161
261431
|
});
|
|
261162
261432
|
|
|
261163
261433
|
// apps/agent/src/features/pr-tracker/state.ts
|
|
261164
|
-
import { join as
|
|
261434
|
+
import { join as join34 } from "path";
|
|
261165
261435
|
async function readState2(projectRoot) {
|
|
261166
|
-
const path =
|
|
261436
|
+
const path = join34(projectRoot, PR_TRACKER_STATE_RELPATH);
|
|
261167
261437
|
const file2 = Bun.file(path);
|
|
261168
261438
|
if (!await file2.exists())
|
|
261169
261439
|
return {};
|
|
@@ -261179,7 +261449,7 @@ async function readState2(projectRoot) {
|
|
|
261179
261449
|
}
|
|
261180
261450
|
}
|
|
261181
261451
|
async function writeState2(projectRoot, state) {
|
|
261182
|
-
const path =
|
|
261452
|
+
const path = join34(projectRoot, PR_TRACKER_STATE_RELPATH);
|
|
261183
261453
|
await Bun.write(path, JSON.stringify(state, null, 2));
|
|
261184
261454
|
}
|
|
261185
261455
|
var PR_TRACKER_STATE_RELPATH = ".ralph/pr-tracker-state.json";
|
|
@@ -261258,7 +261528,7 @@ var init_pr_tracker = __esm(() => {
|
|
|
261258
261528
|
});
|
|
261259
261529
|
|
|
261260
261530
|
// apps/agent/src/agent/wire.ts
|
|
261261
|
-
import { join as
|
|
261531
|
+
import { join as join35 } from "path";
|
|
261262
261532
|
function buildAgentCoordinator(input) {
|
|
261263
261533
|
const {
|
|
261264
261534
|
args,
|
|
@@ -261277,7 +261547,7 @@ function buildAgentCoordinator(input) {
|
|
|
261277
261547
|
onWorkerCmd,
|
|
261278
261548
|
onAwaitingTicket
|
|
261279
261549
|
} = input;
|
|
261280
|
-
const logsDir =
|
|
261550
|
+
const logsDir = join35(projectRoot, ".ralph", "logs");
|
|
261281
261551
|
const bus = createBus();
|
|
261282
261552
|
subscribeAgentDiag(bus, onLog);
|
|
261283
261553
|
const diag = (area, message, color) => {
|
|
@@ -261493,6 +261763,18 @@ function buildAgentCoordinator(input) {
|
|
|
261493
261763
|
const json2 = await file2.json();
|
|
261494
261764
|
return json2.iteration ?? 0;
|
|
261495
261765
|
},
|
|
261766
|
+
getTasksFingerprint: async (changeName) => {
|
|
261767
|
+
const root = cwdByChange.get(changeName) ?? projectRoot;
|
|
261768
|
+
const changeDir = projectLayout(root).changeDir(changeName);
|
|
261769
|
+
const parts = [];
|
|
261770
|
+
for (const name of ["tasks.md", "proposal.md", "design.md"]) {
|
|
261771
|
+
const file2 = Bun.file(join35(changeDir, name));
|
|
261772
|
+
if (!await file2.exists())
|
|
261773
|
+
continue;
|
|
261774
|
+
parts.push(`${name}:${file2.lastModified}:${file2.size}`);
|
|
261775
|
+
}
|
|
261776
|
+
return parts.length > 0 ? parts.join("|") : null;
|
|
261777
|
+
},
|
|
261496
261778
|
...commentSync.enabled && commentSync.syncTasks ? { syncTasks: commentSync.syncTasks } : {},
|
|
261497
261779
|
...commentSync.enabled && commentSync.onSteeringAppended ? { onSteeringAppended: commentSync.onSteeringAppended } : {}
|
|
261498
261780
|
}, {
|
|
@@ -261531,7 +261813,7 @@ function buildAgentCoordinator(input) {
|
|
|
261531
261813
|
getGaveUpTotal: async () => {
|
|
261532
261814
|
let total = 0;
|
|
261533
261815
|
for (const [changeName, root] of cwdByChange) {
|
|
261534
|
-
const file2 = Bun.file(
|
|
261816
|
+
const file2 = Bun.file(join35(projectLayout(root).taskStateDir(changeName), GAVEUP_COUNT_FILE));
|
|
261535
261817
|
if (!await file2.exists())
|
|
261536
261818
|
continue;
|
|
261537
261819
|
try {
|
|
@@ -261566,14 +261848,14 @@ var init_wire = __esm(() => {
|
|
|
261566
261848
|
});
|
|
261567
261849
|
|
|
261568
261850
|
// apps/agent/src/agent/json-log/json-log-file.ts
|
|
261569
|
-
import { mkdir as
|
|
261851
|
+
import { mkdir as mkdir14, appendFile as appendFile2 } from "fs/promises";
|
|
261570
261852
|
import { dirname as dirname15 } from "path";
|
|
261571
261853
|
function createJsonLogFileSink(path) {
|
|
261572
261854
|
if (!path)
|
|
261573
261855
|
return { emit: () => {} };
|
|
261574
261856
|
let chain = (async () => {
|
|
261575
261857
|
try {
|
|
261576
|
-
await
|
|
261858
|
+
await mkdir14(dirname15(path), { recursive: true });
|
|
261577
261859
|
await Bun.write(path, "");
|
|
261578
261860
|
} catch {}
|
|
261579
261861
|
})();
|
|
@@ -261819,7 +262101,7 @@ var init_output_utils = __esm(() => {
|
|
|
261819
262101
|
});
|
|
261820
262102
|
|
|
261821
262103
|
// apps/agent/src/agent/state/worker-state-poll.ts
|
|
261822
|
-
import { join as
|
|
262104
|
+
import { join as join36 } from "path";
|
|
261823
262105
|
function parseSubtasks(tasksMd) {
|
|
261824
262106
|
const out = [];
|
|
261825
262107
|
let skipSection = false;
|
|
@@ -261852,7 +262134,7 @@ function initialWorkerSnapshot() {
|
|
|
261852
262134
|
async function readWorkerSnapshot(input) {
|
|
261853
262135
|
const next = { ...input.prev };
|
|
261854
262136
|
try {
|
|
261855
|
-
const file2 = Bun.file(
|
|
262137
|
+
const file2 = Bun.file(join36(input.statesDir, input.changeName, ".ralph-state.json"));
|
|
261856
262138
|
if (await file2.exists()) {
|
|
261857
262139
|
const json2 = await file2.json();
|
|
261858
262140
|
next.iter = json2.iteration ?? next.iter;
|
|
@@ -261861,10 +262143,10 @@ async function readWorkerSnapshot(input) {
|
|
|
261861
262143
|
} catch {}
|
|
261862
262144
|
if (input.changeDir) {
|
|
261863
262145
|
try {
|
|
261864
|
-
const tasksFile = Bun.file(
|
|
261865
|
-
const proposalFile = Bun.file(
|
|
261866
|
-
const designFile = Bun.file(
|
|
261867
|
-
const reviewFindingsFile = Bun.file(
|
|
262146
|
+
const tasksFile = Bun.file(join36(input.changeDir, "tasks.md"));
|
|
262147
|
+
const proposalFile = Bun.file(join36(input.changeDir, "proposal.md"));
|
|
262148
|
+
const designFile = Bun.file(join36(input.changeDir, "design.md"));
|
|
262149
|
+
const reviewFindingsFile = Bun.file(join36(input.changeDir, "review-findings.md"));
|
|
261868
262150
|
const [tasksText, proposalText, designText, reviewFindingsText] = await Promise.all([
|
|
261869
262151
|
tasksFile.exists().then((ok) => ok ? tasksFile.text() : null),
|
|
261870
262152
|
proposalFile.exists().then((ok) => ok ? proposalFile.text() : null),
|
|
@@ -261927,7 +262209,7 @@ var init_worker_state_poll = __esm(() => {
|
|
|
261927
262209
|
});
|
|
261928
262210
|
|
|
261929
262211
|
// apps/agent/src/components/AgentMode.tsx
|
|
261930
|
-
import { join as
|
|
262212
|
+
import { join as join37 } from "path";
|
|
261931
262213
|
async function appendSteeringImpl(changeDir, message) {
|
|
261932
262214
|
await runWithContext(createDefaultContext(), async () => {
|
|
261933
262215
|
appendSteeringMessage(changeDir, message);
|
|
@@ -263500,7 +263782,7 @@ function AgentMode({
|
|
|
263500
263782
|
},
|
|
263501
263783
|
onSubmit: async (message) => {
|
|
263502
263784
|
try {
|
|
263503
|
-
await appendSteering2(
|
|
263785
|
+
await appendSteering2(join37(tasksDir, w2.changeName), message);
|
|
263504
263786
|
fileEmit({ type: "steering_submitted", changeName: w2.changeName, message });
|
|
263505
263787
|
} catch (err) {
|
|
263506
263788
|
const text = err.message;
|
|
@@ -263614,7 +263896,7 @@ function shouldFallbackToJsonOutput(args, stdinIsTty) {
|
|
|
263614
263896
|
}
|
|
263615
263897
|
|
|
263616
263898
|
// apps/agent/src/runtime/tmux.ts
|
|
263617
|
-
import { basename as
|
|
263899
|
+
import { basename as basename4 } from "path";
|
|
263618
263900
|
function tmuxAvailable() {
|
|
263619
263901
|
const result2 = Bun.spawnSync({ cmd: ["tmux", "-V"], stderr: "pipe" });
|
|
263620
263902
|
return result2.exitCode === 0;
|
|
@@ -263623,7 +263905,7 @@ function sessionName(projectRoot) {
|
|
|
263623
263905
|
const override = process.env["RALPH_SESSION_NAME"];
|
|
263624
263906
|
if (override)
|
|
263625
263907
|
return override;
|
|
263626
|
-
return `ralphy-agent-${
|
|
263908
|
+
return `ralphy-agent-${basename4(projectRoot)}`;
|
|
263627
263909
|
}
|
|
263628
263910
|
function sessionExists(name) {
|
|
263629
263911
|
const result2 = Bun.spawnSync({ cmd: ["tmux", "has-session", "-t", name], stderr: "pipe" });
|
|
@@ -263804,7 +264086,7 @@ __export(exports_list, {
|
|
|
263804
264086
|
buildBuckets: () => buildBuckets,
|
|
263805
264087
|
backlogRankByIssueId: () => backlogRankByIssueId
|
|
263806
264088
|
});
|
|
263807
|
-
import { join as
|
|
264089
|
+
import { join as join38 } from "path";
|
|
263808
264090
|
function countTaskItems(content) {
|
|
263809
264091
|
const checked = (content.match(/^- \[x\]/gm) ?? []).length;
|
|
263810
264092
|
const unchecked = (content.match(/^- \[ \]/gm) ?? []).length;
|
|
@@ -263820,13 +264102,13 @@ function buildLocalRows() {
|
|
|
263820
264102
|
const sources = [{ dir: statesDir, label: "main" }];
|
|
263821
264103
|
const worktreesRoot = worktreesDir2(projectRoot);
|
|
263822
264104
|
for (const wt of storage.list(worktreesRoot)) {
|
|
263823
|
-
sources.push({ dir:
|
|
264105
|
+
sources.push({ dir: join38(worktreesRoot, wt, ".ralph", "tasks"), label: `wt:${wt}` });
|
|
263824
264106
|
}
|
|
263825
264107
|
for (const { dir, label } of sources) {
|
|
263826
264108
|
for (const entry of storage.list(dir)) {
|
|
263827
264109
|
if (seen.has(entry))
|
|
263828
264110
|
continue;
|
|
263829
|
-
const raw = storage.read(
|
|
264111
|
+
const raw = storage.read(join38(dir, entry, ".ralph-state.json"));
|
|
263830
264112
|
if (raw === null)
|
|
263831
264113
|
continue;
|
|
263832
264114
|
let state;
|
|
@@ -263841,7 +264123,7 @@ function buildLocalRows() {
|
|
|
263841
264123
|
const firstLine = promptRaw.split(`
|
|
263842
264124
|
`).find((l3) => l3.trim() !== "") ?? "";
|
|
263843
264125
|
let progress = "\u2014";
|
|
263844
|
-
const tasksContent = storage.read(
|
|
264126
|
+
const tasksContent = storage.read(join38(dir, entry, "tasks.md"));
|
|
263845
264127
|
if (tasksContent !== null) {
|
|
263846
264128
|
const { checked, unchecked } = countTaskItems(tasksContent);
|
|
263847
264129
|
const total = checked + unchecked;
|
|
@@ -264336,31 +264618,14 @@ var init_list = __esm(() => {
|
|
|
264336
264618
|
};
|
|
264337
264619
|
});
|
|
264338
264620
|
|
|
264339
|
-
// apps/agent/src/agent/state/agent-run-state.ts
|
|
264340
|
-
import { basename as basename4, join as join37 } from "path";
|
|
264341
|
-
import { homedir as homedir6 } from "os";
|
|
264342
|
-
import { mkdir as mkdir13, writeFile } from "fs/promises";
|
|
264343
|
-
function agentRunStatePath(projectRoot) {
|
|
264344
|
-
return join37(homedir6(), ".ralph", basename4(projectRoot), "agent-state.json");
|
|
264345
|
-
}
|
|
264346
|
-
async function writeAgentRunState(state) {
|
|
264347
|
-
const path = agentRunStatePath(state.projectRoot);
|
|
264348
|
-
try {
|
|
264349
|
-
await mkdir13(join37(homedir6(), ".ralph", basename4(state.projectRoot)), { recursive: true });
|
|
264350
|
-
await writeFile(path, JSON.stringify(state, null, 2) + `
|
|
264351
|
-
`, "utf-8");
|
|
264352
|
-
} catch {}
|
|
264353
|
-
}
|
|
264354
|
-
var init_agent_run_state = () => {};
|
|
264355
|
-
|
|
264356
264621
|
// apps/agent/src/agent/json-runner.ts
|
|
264357
264622
|
var exports_json_runner = {};
|
|
264358
264623
|
__export(exports_json_runner, {
|
|
264359
264624
|
runAgentJson: () => runAgentJson
|
|
264360
264625
|
});
|
|
264361
|
-
import { join as
|
|
264362
|
-
import { mkdir as
|
|
264363
|
-
import { homedir as
|
|
264626
|
+
import { join as join39 } from "path";
|
|
264627
|
+
import { mkdir as mkdir15 } from "fs/promises";
|
|
264628
|
+
import { homedir as homedir8 } from "os";
|
|
264364
264629
|
function makeEmit(fileSink) {
|
|
264365
264630
|
return (event) => {
|
|
264366
264631
|
const payload = { ts: Date.now(), ...event };
|
|
@@ -264380,7 +264645,7 @@ async function runAgentJson({
|
|
|
264380
264645
|
tasksDir,
|
|
264381
264646
|
runPreflight: runPreflight2 = runPreflight
|
|
264382
264647
|
}) {
|
|
264383
|
-
await
|
|
264648
|
+
await mkdir15(join39(homedir8(), ".ralph"), { recursive: true }).catch(() => {
|
|
264384
264649
|
return;
|
|
264385
264650
|
});
|
|
264386
264651
|
const fileSink = createJsonLogFileSink(args.jsonLogFile);
|
|
@@ -264596,8 +264861,8 @@ var exports_src3 = {};
|
|
|
264596
264861
|
__export(exports_src3, {
|
|
264597
264862
|
main: () => main3
|
|
264598
264863
|
});
|
|
264599
|
-
import { mkdir as
|
|
264600
|
-
import { join as
|
|
264864
|
+
import { mkdir as mkdir16 } from "fs/promises";
|
|
264865
|
+
import { join as join40 } from "path";
|
|
264601
264866
|
async function main3(argv) {
|
|
264602
264867
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
264603
264868
|
printAgentHelp();
|
|
@@ -264661,9 +264926,9 @@ async function main3(argv) {
|
|
|
264661
264926
|
return 1;
|
|
264662
264927
|
}
|
|
264663
264928
|
}
|
|
264664
|
-
await
|
|
264665
|
-
await
|
|
264666
|
-
await
|
|
264929
|
+
await mkdir16(statesDir, { recursive: true });
|
|
264930
|
+
await mkdir16(tasksDir, { recursive: true });
|
|
264931
|
+
await mkdir16(join40(projectRoot, ".ralph"), { recursive: true });
|
|
264667
264932
|
if (shouldFallbackToJsonOutput(args, process.stdin.isTTY)) {
|
|
264668
264933
|
process.stderr.write(`agent: stdin is not a TTY \u2014 falling back to --json-output mode.
|
|
264669
264934
|
`);
|