@neriros/ralphy 3.10.6 → 3.10.7
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/mcp/index.js +75 -39
- package/dist/shell/index.js +438 -278
- 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.7")
|
|
18932
|
+
return "3.10.7";
|
|
18933
18933
|
} catch {}
|
|
18934
18934
|
const dirsToTry = [];
|
|
18935
18935
|
try {
|
|
@@ -81072,7 +81072,7 @@ function modelOptionValues() {
|
|
|
81072
81072
|
const field = findField("model");
|
|
81073
81073
|
return field && field.spec.kind === "select" ? field.spec.options.map((o) => o.value) : [];
|
|
81074
81074
|
}
|
|
81075
|
-
var PROMPT_BODY_FIELD_ID = "promptBody", REPO_LINK_FIELD_ID = "repo.link", yes = () => ({ kind: "confirm", defaultChoice: "confirm" }), no = () => ({ kind: "confirm", defaultChoice: "cancel" }), PROJECT_NAME, LINEAR_TEAM, REPO_LINK, LINEAR_FILTER, QUICK_FIELDS, isOn = (id) => (answers) => answers[id] === true, CUSTOMIZED_FIELDS, COMMON_CLI_OPTIONS, FIELD_DESCRIPTIONS;
|
|
81075
|
+
var PROMPT_BODY_FIELD_ID = "promptBody", REPO_LINK_FIELD_ID = "repo.link", AWAITING_STATUS_FIELD_ID = "linear.confirmationMode.awaitingStatus", yes = () => ({ kind: "confirm", defaultChoice: "confirm" }), no = () => ({ kind: "confirm", defaultChoice: "cancel" }), PROJECT_NAME, LINEAR_TEAM, REPO_LINK, LINEAR_FILTER, QUICK_FIELDS, isOn = (id) => (answers) => answers[id] === true, CUSTOMIZED_FIELDS, COMMON_CLI_OPTIONS, FIELD_DESCRIPTIONS;
|
|
81076
81076
|
var init_fields = __esm(() => {
|
|
81077
81077
|
PROJECT_NAME = {
|
|
81078
81078
|
id: "project.name",
|
|
@@ -81436,6 +81436,14 @@ var init_fields = __esm(() => {
|
|
|
81436
81436
|
spec: { kind: "number", placeholder: "3" },
|
|
81437
81437
|
when: isOn("linear.confirmationMode.enabled")
|
|
81438
81438
|
},
|
|
81439
|
+
{
|
|
81440
|
+
id: AWAITING_STATUS_FIELD_ID,
|
|
81441
|
+
label: "Park awaiting-approval tickets in a status?",
|
|
81442
|
+
hint: "e.g. Planned \u2014 blank keeps them In Progress",
|
|
81443
|
+
description: "When the confirmation gate opens, move the ticket to this Linear status so the board shows it waiting on a human (it must be a real status in your team). Ralphy also adds it to the in-progress pickup filter so the parked ticket keeps being polled, and re-asserts In Progress on approval. Leave blank to keep parked tickets in In Progress. Pairs with status-based indicators.",
|
|
81444
|
+
spec: { kind: "text", placeholder: "Planned" },
|
|
81445
|
+
when: isOn("linear.confirmationMode.enabled")
|
|
81446
|
+
},
|
|
81439
81447
|
{
|
|
81440
81448
|
id: "linear.indicators",
|
|
81441
81449
|
label: "Linear lifecycle indicators",
|
|
@@ -82908,6 +82916,20 @@ function buildFromAnswers(mode, answers, build = buildWorkflowMarkdown) {
|
|
|
82908
82916
|
values2["linear.indicators"] = map3;
|
|
82909
82917
|
}
|
|
82910
82918
|
}
|
|
82919
|
+
const parkStatusRaw = values2[AWAITING_STATUS_FIELD_ID];
|
|
82920
|
+
const parkStatus = typeof parkStatusRaw === "string" ? parkStatusRaw.trim() : "";
|
|
82921
|
+
if (values2["linear.confirmationMode.enabled"] === true && parkStatus && values2["linear.indicators"] && typeof values2["linear.indicators"] === "object") {
|
|
82922
|
+
const map3 = { ...values2["linear.indicators"] };
|
|
82923
|
+
map3.setAwaitingConfirmation = { type: "status", value: parkStatus };
|
|
82924
|
+
const existing = map3.getInProgress;
|
|
82925
|
+
const filter2 = existing && !Array.isArray(existing) && "filter" in existing ? [...existing.filter] : [];
|
|
82926
|
+
if (!filter2.some((m) => m.type === "status" && m.value === parkStatus)) {
|
|
82927
|
+
filter2.push({ type: "status", value: parkStatus });
|
|
82928
|
+
}
|
|
82929
|
+
map3.getInProgress = { filter: filter2 };
|
|
82930
|
+
values2["linear.indicators"] = map3;
|
|
82931
|
+
}
|
|
82932
|
+
delete values2[AWAITING_STATUS_FIELD_ID];
|
|
82911
82933
|
const linkRepo = values2[REPO_LINK_FIELD_ID] === true;
|
|
82912
82934
|
delete values2[REPO_LINK_FIELD_ID];
|
|
82913
82935
|
if (!linkRepo) {
|
|
@@ -85044,6 +85066,89 @@ var init_schema2 = __esm(() => {
|
|
|
85044
85066
|
ALL_OWNED_SLOTS = new Set(Object.values(OWNERSHIP).flatMap((slots) => [...slots]));
|
|
85045
85067
|
});
|
|
85046
85068
|
|
|
85069
|
+
// packages/core/src/state/sidecar.ts
|
|
85070
|
+
import { dirname as dirname5, join as join9 } from "path";
|
|
85071
|
+
import { mkdir as mkdir3, rename, unlink } from "fs/promises";
|
|
85072
|
+
function slotSidecarPath(changeDir, slot) {
|
|
85073
|
+
return join9(changeDir, `${CORE_STATE_FILE.replace(/\.json$/, "")}.${slot}.json`);
|
|
85074
|
+
}
|
|
85075
|
+
function parseObject(text) {
|
|
85076
|
+
if (text === null)
|
|
85077
|
+
return null;
|
|
85078
|
+
try {
|
|
85079
|
+
const parsed = JSON.parse(text);
|
|
85080
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
85081
|
+
return parsed;
|
|
85082
|
+
}
|
|
85083
|
+
return null;
|
|
85084
|
+
} catch {
|
|
85085
|
+
return null;
|
|
85086
|
+
}
|
|
85087
|
+
}
|
|
85088
|
+
function deepSet(target, path, value) {
|
|
85089
|
+
if (path === "") {
|
|
85090
|
+
for (const k of Object.keys(target))
|
|
85091
|
+
delete target[k];
|
|
85092
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
85093
|
+
Object.assign(target, value);
|
|
85094
|
+
}
|
|
85095
|
+
return;
|
|
85096
|
+
}
|
|
85097
|
+
const segments = path.split(".");
|
|
85098
|
+
let cursor = target;
|
|
85099
|
+
for (let i = 0;i < segments.length - 1; i++) {
|
|
85100
|
+
const key = segments[i];
|
|
85101
|
+
const existing = cursor[key];
|
|
85102
|
+
if (existing === null || typeof existing !== "object" || Array.isArray(existing)) {
|
|
85103
|
+
const next = {};
|
|
85104
|
+
cursor[key] = next;
|
|
85105
|
+
cursor = next;
|
|
85106
|
+
} else {
|
|
85107
|
+
cursor = existing;
|
|
85108
|
+
}
|
|
85109
|
+
}
|
|
85110
|
+
cursor[segments[segments.length - 1]] = value;
|
|
85111
|
+
}
|
|
85112
|
+
async function atomicWrite(path, content) {
|
|
85113
|
+
await mkdir3(dirname5(path), { recursive: true });
|
|
85114
|
+
const tmp = `${path}.tmp-${process.pid}-${writeSeq++}`;
|
|
85115
|
+
try {
|
|
85116
|
+
await Bun.write(tmp, content);
|
|
85117
|
+
await rename(tmp, path);
|
|
85118
|
+
} catch (err) {
|
|
85119
|
+
await unlink(tmp).catch(() => {});
|
|
85120
|
+
throw err;
|
|
85121
|
+
}
|
|
85122
|
+
}
|
|
85123
|
+
async function readSlotSidecar(changeDir, slot) {
|
|
85124
|
+
const file2 = Bun.file(slotSidecarPath(changeDir, slot));
|
|
85125
|
+
if (!await file2.exists())
|
|
85126
|
+
return;
|
|
85127
|
+
const obj = parseObject(await file2.text().catch(() => null));
|
|
85128
|
+
return obj ?? undefined;
|
|
85129
|
+
}
|
|
85130
|
+
async function writeSlotField(changeDir, path, value, seedInline) {
|
|
85131
|
+
const [slot, ...rest2] = path.split(".");
|
|
85132
|
+
const sidecarPath = slotSidecarPath(changeDir, slot);
|
|
85133
|
+
const existing = parseObject(await Bun.file(sidecarPath).text().catch(() => null));
|
|
85134
|
+
const obj = existing ?? (seedInline ? structuredClone(seedInline) : {});
|
|
85135
|
+
deepSet(obj, rest2.join("."), value);
|
|
85136
|
+
await atomicWrite(sidecarPath, JSON.stringify(obj, null, 2) + `
|
|
85137
|
+
`);
|
|
85138
|
+
}
|
|
85139
|
+
function overlaySidecarsSync(changeDir, target, read) {
|
|
85140
|
+
for (const slot of ALL_OWNED_SLOTS) {
|
|
85141
|
+
const obj = parseObject(read(slotSidecarPath(changeDir, slot)));
|
|
85142
|
+
if (obj !== undefined && obj !== null)
|
|
85143
|
+
target[slot] = obj;
|
|
85144
|
+
}
|
|
85145
|
+
return target;
|
|
85146
|
+
}
|
|
85147
|
+
var CORE_STATE_FILE = ".ralph-state.json", writeSeq = 0;
|
|
85148
|
+
var init_sidecar = __esm(() => {
|
|
85149
|
+
init_schema2();
|
|
85150
|
+
});
|
|
85151
|
+
|
|
85047
85152
|
// node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/util.js
|
|
85048
85153
|
var util, objectUtil, ZodParsedType, getParsedType2 = (data) => {
|
|
85049
85154
|
const t = typeof data;
|
|
@@ -89147,16 +89252,24 @@ function formatTaskName(name) {
|
|
|
89147
89252
|
}
|
|
89148
89253
|
|
|
89149
89254
|
// packages/core/src/state.ts
|
|
89150
|
-
import { join as
|
|
89255
|
+
import { join as join10 } from "path";
|
|
89256
|
+
function stripOwnedSlots(state) {
|
|
89257
|
+
const out = { ...state };
|
|
89258
|
+
for (const slot of ALL_OWNED_SLOTS)
|
|
89259
|
+
delete out[slot];
|
|
89260
|
+
return out;
|
|
89261
|
+
}
|
|
89151
89262
|
function readState(changeDir) {
|
|
89152
|
-
const filePath =
|
|
89263
|
+
const filePath = join10(changeDir, STATE_FILE2);
|
|
89153
89264
|
const raw = getStorage().read(filePath);
|
|
89154
89265
|
if (raw === null)
|
|
89155
89266
|
throw new Error(".ralph-state.json not found");
|
|
89156
|
-
|
|
89267
|
+
const base2 = JSON.parse(raw);
|
|
89268
|
+
overlaySidecarsSync(changeDir, base2, (p) => getStorage().read(p));
|
|
89269
|
+
return StateSchema.parse(base2);
|
|
89157
89270
|
}
|
|
89158
89271
|
function tryReadStateRaw(changeDir) {
|
|
89159
|
-
const filePath =
|
|
89272
|
+
const filePath = join10(changeDir, STATE_FILE2);
|
|
89160
89273
|
const text = getStorage().read(filePath);
|
|
89161
89274
|
if (text === null)
|
|
89162
89275
|
return { state: null, raw: null };
|
|
@@ -89167,12 +89280,14 @@ function tryReadStateRaw(changeDir) {
|
|
|
89167
89280
|
return { state: null, raw: null };
|
|
89168
89281
|
}
|
|
89169
89282
|
const raw = parsed && typeof parsed === "object" ? parsed : {};
|
|
89170
|
-
|
|
89283
|
+
overlaySidecarsSync(changeDir, raw, (p) => getStorage().read(p));
|
|
89284
|
+
const result2 = StateSchema.safeParse(raw);
|
|
89171
89285
|
return { state: result2.success ? result2.data : null, raw };
|
|
89172
89286
|
}
|
|
89173
89287
|
function writeState(changeDir, state) {
|
|
89174
|
-
const filePath =
|
|
89175
|
-
|
|
89288
|
+
const filePath = join10(changeDir, STATE_FILE2);
|
|
89289
|
+
const core2 = stripOwnedSlots(state);
|
|
89290
|
+
getStorage().write(filePath, JSON.stringify(core2, null, 2) + `
|
|
89176
89291
|
`);
|
|
89177
89292
|
}
|
|
89178
89293
|
function updateState(changeDir, updater) {
|
|
@@ -89211,7 +89326,7 @@ function buildInitialState(options) {
|
|
|
89211
89326
|
});
|
|
89212
89327
|
}
|
|
89213
89328
|
function ensureState(changeDir) {
|
|
89214
|
-
const filePath =
|
|
89329
|
+
const filePath = join10(changeDir, STATE_FILE2);
|
|
89215
89330
|
const storage = getStorage();
|
|
89216
89331
|
if (storage.read(filePath) !== null) {
|
|
89217
89332
|
return readState(changeDir);
|
|
@@ -89225,11 +89340,12 @@ var STATE_FILE2 = ".ralph-state.json";
|
|
|
89225
89340
|
var init_state = __esm(() => {
|
|
89226
89341
|
init_types2();
|
|
89227
89342
|
init_context();
|
|
89343
|
+
init_schema2();
|
|
89344
|
+
init_sidecar();
|
|
89228
89345
|
});
|
|
89229
89346
|
|
|
89230
89347
|
// packages/core/src/state/store.ts
|
|
89231
|
-
import {
|
|
89232
|
-
import { mkdir as mkdir3 } from "fs/promises";
|
|
89348
|
+
import { join as join11 } from "path";
|
|
89233
89349
|
async function readJson(filePath) {
|
|
89234
89350
|
const file2 = Bun.file(filePath);
|
|
89235
89351
|
if (!await file2.exists())
|
|
@@ -89244,22 +89360,6 @@ async function readJson(filePath) {
|
|
|
89244
89360
|
return {};
|
|
89245
89361
|
}
|
|
89246
89362
|
}
|
|
89247
|
-
function deepSet(target, path, value) {
|
|
89248
|
-
const segments = path.split(".");
|
|
89249
|
-
let cursor = target;
|
|
89250
|
-
for (let i = 0;i < segments.length - 1; i++) {
|
|
89251
|
-
const key = segments[i];
|
|
89252
|
-
const existing = cursor[key];
|
|
89253
|
-
if (existing === undefined || existing === null || typeof existing !== "object" || Array.isArray(existing)) {
|
|
89254
|
-
const next = {};
|
|
89255
|
-
cursor[key] = next;
|
|
89256
|
-
cursor = next;
|
|
89257
|
-
} else {
|
|
89258
|
-
cursor = existing;
|
|
89259
|
-
}
|
|
89260
|
-
}
|
|
89261
|
-
cursor[segments[segments.length - 1]] = value;
|
|
89262
|
-
}
|
|
89263
89363
|
async function writeField(changeDir, featureName, path, value) {
|
|
89264
89364
|
const allowed = OWNERSHIP[featureName];
|
|
89265
89365
|
if (!allowed) {
|
|
@@ -89269,16 +89369,15 @@ async function writeField(changeDir, featureName, path, value) {
|
|
|
89269
89369
|
if (!allowed.includes(topSlot)) {
|
|
89270
89370
|
throw new OwnershipError(featureName, path, `feature '${featureName}' may not write '${path}' (owns ${allowed.join(", ")})`);
|
|
89271
89371
|
}
|
|
89272
|
-
const
|
|
89273
|
-
const
|
|
89274
|
-
|
|
89275
|
-
await mkdir3(dirname5(filePath), { recursive: true });
|
|
89276
|
-
await Bun.write(filePath, JSON.stringify(existing, null, 2) + `
|
|
89277
|
-
`);
|
|
89372
|
+
const inline = (await readJson(join11(changeDir, STATE_FILE3)))[topSlot];
|
|
89373
|
+
const seed = inline && typeof inline === "object" && !Array.isArray(inline) ? inline : undefined;
|
|
89374
|
+
await writeSlotField(changeDir, path, value, seed);
|
|
89278
89375
|
}
|
|
89279
89376
|
var STATE_FILE3 = ".ralph-state.json", OwnershipError;
|
|
89280
89377
|
var init_store = __esm(() => {
|
|
89281
89378
|
init_schema2();
|
|
89379
|
+
init_sidecar();
|
|
89380
|
+
init_sidecar();
|
|
89282
89381
|
init_state();
|
|
89283
89382
|
OwnershipError = class OwnershipError extends Error {
|
|
89284
89383
|
featureName;
|
|
@@ -89293,14 +89392,14 @@ var init_store = __esm(() => {
|
|
|
89293
89392
|
});
|
|
89294
89393
|
|
|
89295
89394
|
// apps/loop/src/components/TaskStatus.tsx
|
|
89296
|
-
import { join as
|
|
89395
|
+
import { join as join12 } from "path";
|
|
89297
89396
|
function TaskStatus({ state, stateDir }) {
|
|
89298
89397
|
const storage = getStorage();
|
|
89299
89398
|
const cost = Math.round(state.usage.total_cost_usd * 100) / 100;
|
|
89300
89399
|
const time3 = Math.round(state.usage.total_duration_ms / 1000 * 10) / 10 + "s";
|
|
89301
89400
|
const artifacts = OPENSPEC_ARTIFACTS.map((name) => ({
|
|
89302
89401
|
name,
|
|
89303
|
-
exists: storage.read(
|
|
89402
|
+
exists: storage.read(join12(stateDir, name)) !== null
|
|
89304
89403
|
}));
|
|
89305
89404
|
const recent = state.history.slice(-10);
|
|
89306
89405
|
return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
|
|
@@ -98017,8 +98116,8 @@ var init_rate_limit_detection = __esm(() => {
|
|
|
98017
98116
|
});
|
|
98018
98117
|
|
|
98019
98118
|
// packages/engine/src/agents/claude.ts
|
|
98020
|
-
import { mkdtemp, unlink } from "fs/promises";
|
|
98021
|
-
import { join as
|
|
98119
|
+
import { mkdtemp, unlink as unlink2 } from "fs/promises";
|
|
98120
|
+
import { join as join13 } from "path";
|
|
98022
98121
|
import { tmpdir } from "os";
|
|
98023
98122
|
function buildClaudeArgs(model, resumeSessionId, reviewerContextStrategy, reviewerModel) {
|
|
98024
98123
|
const effectiveModel = reviewerModel ?? model;
|
|
@@ -98039,7 +98138,7 @@ function buildClaudeArgs(model, resumeSessionId, reviewerContextStrategy, review
|
|
|
98039
98138
|
}
|
|
98040
98139
|
async function runInteractive(req) {
|
|
98041
98140
|
const { model, prompt, taskDir } = req;
|
|
98042
|
-
const promptFile = taskDir ?
|
|
98141
|
+
const promptFile = taskDir ? join13(taskDir, "_interactive_prompt.md") : join13(await mkdtemp(join13(tmpdir(), "ralph-")), "prompt.md");
|
|
98043
98142
|
await Bun.write(promptFile, prompt);
|
|
98044
98143
|
try {
|
|
98045
98144
|
const cmd = [
|
|
@@ -98066,14 +98165,14 @@ async function runInteractive(req) {
|
|
|
98066
98165
|
env: scrubClaudeEnv(process.env)
|
|
98067
98166
|
});
|
|
98068
98167
|
const exitCode = await proc.exited;
|
|
98069
|
-
const doneFile = taskDir ?
|
|
98168
|
+
const doneFile = taskDir ? join13(taskDir, "_interactive_done") : null;
|
|
98070
98169
|
if (doneFile && await Bun.file(doneFile).exists()) {
|
|
98071
98170
|
return { exitCode: 0, usage: null, sessionId: null, rateLimited: false };
|
|
98072
98171
|
}
|
|
98073
98172
|
return { exitCode, usage: null, sessionId: null, rateLimited: false };
|
|
98074
98173
|
} finally {
|
|
98075
98174
|
try {
|
|
98076
|
-
await
|
|
98175
|
+
await unlink2(promptFile);
|
|
98077
98176
|
} catch {}
|
|
98078
98177
|
}
|
|
98079
98178
|
}
|
|
@@ -99105,6 +99204,12 @@ class FlowActorStore {
|
|
|
99105
99204
|
return typeof s.value === "string" || typeof s.status === "string";
|
|
99106
99205
|
}
|
|
99107
99206
|
async loadSnapshot(changeDir) {
|
|
99207
|
+
const sidecar = await readSlotSidecar(changeDir, "flow");
|
|
99208
|
+
if (sidecar && typeof sidecar === "object") {
|
|
99209
|
+
const snap = sidecar.actorSnapshot;
|
|
99210
|
+
if (snap !== undefined && snap !== null)
|
|
99211
|
+
return snap;
|
|
99212
|
+
}
|
|
99108
99213
|
const filePath = `${changeDir}/${STATE_FILE4}`;
|
|
99109
99214
|
const file2 = Bun.file(filePath);
|
|
99110
99215
|
if (!await file2.exists())
|
|
@@ -99563,11 +99668,11 @@ var init_meta_prompt = __esm(() => {
|
|
|
99563
99668
|
});
|
|
99564
99669
|
|
|
99565
99670
|
// packages/core/src/loop.ts
|
|
99566
|
-
import { join as
|
|
99671
|
+
import { join as join14 } from "path";
|
|
99567
99672
|
function buildTaskPrompt(state, taskDir, reviewPhase) {
|
|
99568
99673
|
const storage = getStorage();
|
|
99569
99674
|
let prompt = "";
|
|
99570
|
-
const steeringContent = storage.read(
|
|
99675
|
+
const steeringContent = storage.read(join14(taskDir, "steering.md"));
|
|
99571
99676
|
if (steeringContent !== null) {
|
|
99572
99677
|
const steeringLines = steeringContent.split(`
|
|
99573
99678
|
`).filter((line) => !line.startsWith("#")).filter((line) => line.trim()).slice(0, STEERING_MAX_LINES);
|
|
@@ -99586,8 +99691,8 @@ function buildTaskPrompt(state, taskDir, reviewPhase) {
|
|
|
99586
99691
|
`;
|
|
99587
99692
|
}
|
|
99588
99693
|
}
|
|
99589
|
-
const agentTasksPath =
|
|
99590
|
-
const missionTasksPath =
|
|
99694
|
+
const agentTasksPath = join14(taskDir, AGENT_TASKS_FILENAME);
|
|
99695
|
+
const missionTasksPath = join14(taskDir, MISSION_TASKS_FILENAME);
|
|
99591
99696
|
const agentTasksContent = storage.read(agentTasksPath);
|
|
99592
99697
|
const missionTasksContent = storage.read(missionTasksPath);
|
|
99593
99698
|
let activePath = null;
|
|
@@ -99663,7 +99768,7 @@ function buildTaskPrompt(state, taskDir, reviewPhase) {
|
|
|
99663
99768
|
}
|
|
99664
99769
|
}
|
|
99665
99770
|
if (reviewPhase?.enabled) {
|
|
99666
|
-
const reviewFindingsPath =
|
|
99771
|
+
const reviewFindingsPath = join14(taskDir, "review-findings.md");
|
|
99667
99772
|
const reviewFindingsContent = storage.read(reviewFindingsPath);
|
|
99668
99773
|
const hasUncheckedMission = missionTasksContent !== null && /^- \[ \]/m.test(missionTasksContent);
|
|
99669
99774
|
const hasUncheckedAgent = agentTasksContent !== null && /^- \[ \]/m.test(agentTasksContent);
|
|
@@ -99737,7 +99842,7 @@ When all tasks are complete and all files are committed, push your branch and op
|
|
|
99737
99842
|
}
|
|
99738
99843
|
function buildSteeringBlock(taskDir) {
|
|
99739
99844
|
const storage = getStorage();
|
|
99740
|
-
const steeringContent = storage.read(
|
|
99845
|
+
const steeringContent = storage.read(join14(taskDir, "steering.md"));
|
|
99741
99846
|
if (steeringContent === null)
|
|
99742
99847
|
return "";
|
|
99743
99848
|
const steeringLines = steeringContent.split(`
|
|
@@ -99835,7 +99940,7 @@ function buildPlanPrompt(state, taskDir) {
|
|
|
99835
99940
|
return prompt;
|
|
99836
99941
|
}
|
|
99837
99942
|
function buildReviewPrompt(state, taskDir) {
|
|
99838
|
-
const reviewFindingsPath =
|
|
99943
|
+
const reviewFindingsPath = join14(taskDir, "review-findings.md");
|
|
99839
99944
|
let prompt = buildSteeringBlock(taskDir);
|
|
99840
99945
|
prompt += `---
|
|
99841
99946
|
|
|
@@ -99900,7 +100005,7 @@ function buildPhasePrompt(phase, state, taskDir, reviewPhase, metaPromptOptions)
|
|
|
99900
100005
|
}
|
|
99901
100006
|
function checkStopSignal(taskDir, stateDir) {
|
|
99902
100007
|
const storage = getStorage();
|
|
99903
|
-
const stopFile =
|
|
100008
|
+
const stopFile = join14(taskDir, "STOP");
|
|
99904
100009
|
const reason = storage.read(stopFile);
|
|
99905
100010
|
if (reason === null)
|
|
99906
100011
|
return null;
|
|
@@ -99960,7 +100065,7 @@ function updateStateIteration(stateDir, result2, startedAt, engine, model, usage
|
|
|
99960
100065
|
}
|
|
99961
100066
|
function appendSteeringMessage(taskDir, message) {
|
|
99962
100067
|
const storage = getStorage();
|
|
99963
|
-
const steeringPath =
|
|
100068
|
+
const steeringPath = join14(taskDir, "steering.md");
|
|
99964
100069
|
const existing = storage.read(steeringPath);
|
|
99965
100070
|
const updated = existing ? `${message}
|
|
99966
100071
|
|
|
@@ -100010,7 +100115,7 @@ var init_loop2 = __esm(() => {
|
|
|
100010
100115
|
});
|
|
100011
100116
|
|
|
100012
100117
|
// apps/loop/src/hooks/useLoop.ts
|
|
100013
|
-
import { join as
|
|
100118
|
+
import { join as join15 } from "path";
|
|
100014
100119
|
function sleep(seconds) {
|
|
100015
100120
|
return new Promise((resolve3) => setTimeout(resolve3, seconds * 1000));
|
|
100016
100121
|
}
|
|
@@ -100072,7 +100177,7 @@ function useLoop(opts) {
|
|
|
100072
100177
|
}
|
|
100073
100178
|
} else {
|
|
100074
100179
|
if (rawState !== null) {
|
|
100075
|
-
addInfo(`.ralph-state.json was malformed \u2014 reinitialising.
|
|
100180
|
+
addInfo(`.ralph-state.json was malformed \u2014 reinitialising. Feature-owned slots (linearComments, specAttachments, \u2026) live in their own sidecar files and are unaffected.`);
|
|
100076
100181
|
}
|
|
100077
100182
|
currentState = buildInitialState({
|
|
100078
100183
|
name: opts.name,
|
|
@@ -100082,12 +100187,6 @@ function useLoop(opts) {
|
|
|
100082
100187
|
manualTest: opts.manualTest,
|
|
100083
100188
|
createPr: opts.createPr ?? false
|
|
100084
100189
|
});
|
|
100085
|
-
if (rawState !== null && rawState.linearComments) {
|
|
100086
|
-
currentState.linearComments = rawState.linearComments;
|
|
100087
|
-
}
|
|
100088
|
-
if (rawState !== null && rawState.specAttachments) {
|
|
100089
|
-
currentState.specAttachments = rawState.specAttachments;
|
|
100090
|
-
}
|
|
100091
100190
|
writeState(stateDir, currentState);
|
|
100092
100191
|
}
|
|
100093
100192
|
const isResume2 = currentState.iteration > 0;
|
|
@@ -100132,8 +100231,8 @@ function useLoop(opts) {
|
|
|
100132
100231
|
setState(currentState);
|
|
100133
100232
|
if (!actor.getSnapshot().matches("running"))
|
|
100134
100233
|
break;
|
|
100135
|
-
const tasksContent = storage.read(
|
|
100136
|
-
const agentTasksContent = storage.read(
|
|
100234
|
+
const tasksContent = storage.read(join15(tasksDir, MISSION_TASKS_FILENAME));
|
|
100235
|
+
const agentTasksContent = storage.read(join15(tasksDir, AGENT_TASKS_FILENAME));
|
|
100137
100236
|
if (tasksContent === null && currentState.iteration > 0 && typeof opts.changeStore.listChanges === "function") {
|
|
100138
100237
|
let stillActive = true;
|
|
100139
100238
|
try {
|
|
@@ -100170,7 +100269,7 @@ function useLoop(opts) {
|
|
|
100170
100269
|
const agentDone = agentTasksContent === null || allCompleted(agentTasksContent);
|
|
100171
100270
|
if (missionDone && agentDone && tasksContent !== null) {
|
|
100172
100271
|
if (opts.reviewPhase?.enabled) {
|
|
100173
|
-
const reviewFindingsPath =
|
|
100272
|
+
const reviewFindingsPath = join15(tasksDir, "review-findings.md");
|
|
100174
100273
|
const reviewFindingsFile = Bun.file(reviewFindingsPath);
|
|
100175
100274
|
const findingsExists = await reviewFindingsFile.exists();
|
|
100176
100275
|
const findingsContent = findingsExists ? await reviewFindingsFile.text() : null;
|
|
@@ -100199,7 +100298,7 @@ function useLoop(opts) {
|
|
|
100199
100298
|
model: opts.reviewPhase.reviewerModel ?? opts.model,
|
|
100200
100299
|
prompt: reviewPrompt,
|
|
100201
100300
|
logFlag: opts.log,
|
|
100202
|
-
logFile:
|
|
100301
|
+
logFile: join15(stateDir, `log-review-${roundNum}.json`),
|
|
100203
100302
|
taskDir: tasksDir,
|
|
100204
100303
|
reviewerContextStrategy: opts.reviewPhase.reviewerContextStrategy ?? "fresh",
|
|
100205
100304
|
onFeedEvent: addFeedEvent
|
|
@@ -100273,8 +100372,8 @@ function useLoop(opts) {
|
|
|
100273
100372
|
const time3 = new Date().toLocaleTimeString("en-US", { hour12: false });
|
|
100274
100373
|
addIterationHeader(localIter, time3);
|
|
100275
100374
|
addInfo(`Iteration ${localIter} (total: ${currentState.iteration})`);
|
|
100276
|
-
const proposalContent = storage.read(
|
|
100277
|
-
const designContent = storage.read(
|
|
100375
|
+
const proposalContent = storage.read(join15(tasksDir, "proposal.md"));
|
|
100376
|
+
const designContent = storage.read(join15(tasksDir, "design.md"));
|
|
100278
100377
|
const routedPhase = routeTaskPhase(opts.phase, {
|
|
100279
100378
|
proposal: proposalContent,
|
|
100280
100379
|
design: designContent,
|
|
@@ -100294,7 +100393,7 @@ function useLoop(opts) {
|
|
|
100294
100393
|
model: opts.model,
|
|
100295
100394
|
prompt,
|
|
100296
100395
|
logFlag: opts.log,
|
|
100297
|
-
logFile:
|
|
100396
|
+
logFile: join15(stateDir, "log.json"),
|
|
100298
100397
|
taskDir: tasksDir,
|
|
100299
100398
|
interactive: false,
|
|
100300
100399
|
onFeedEvent: addFeedEvent,
|
|
@@ -100317,7 +100416,7 @@ function useLoop(opts) {
|
|
|
100317
100416
|
model: opts.model,
|
|
100318
100417
|
prompt: buildSteeringPrompt(steerMessage),
|
|
100319
100418
|
logFlag: opts.log,
|
|
100320
|
-
logFile:
|
|
100419
|
+
logFile: join15(stateDir, "log.json"),
|
|
100321
100420
|
taskDir: tasksDir,
|
|
100322
100421
|
onFeedEvent: addResumeFeedEvent,
|
|
100323
100422
|
signal: resumeController.signal,
|
|
@@ -100624,7 +100723,7 @@ var init_TaskLoop = __esm(async () => {
|
|
|
100624
100723
|
});
|
|
100625
100724
|
|
|
100626
100725
|
// apps/loop/src/components/App.tsx
|
|
100627
|
-
import { join as
|
|
100726
|
+
import { join as join16 } from "path";
|
|
100628
100727
|
function ExitAfterRender({ children }) {
|
|
100629
100728
|
const { exit } = use_app_default();
|
|
100630
100729
|
import_react59.useEffect(() => {
|
|
@@ -100677,7 +100776,7 @@ function App2({ args, taskPhase }) {
|
|
|
100677
100776
|
}
|
|
100678
100777
|
const layout = getLayout();
|
|
100679
100778
|
const stateDir = layout.taskStateDir(args.name);
|
|
100680
|
-
if (getStorage().read(
|
|
100779
|
+
if (getStorage().read(join16(stateDir, ".ralph-state.json")) === null) {
|
|
100681
100780
|
return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(ErrorMessage, {
|
|
100682
100781
|
message: `Error: change '${args.name}' not found`
|
|
100683
100782
|
}, undefined, false, undefined, this);
|
|
@@ -100731,7 +100830,7 @@ var init_App2 = __esm(async () => {
|
|
|
100731
100830
|
|
|
100732
100831
|
// packages/log/src/log.ts
|
|
100733
100832
|
import { appendFile } from "fs/promises";
|
|
100734
|
-
import { join as
|
|
100833
|
+
import { join as join17, dirname as dirname7 } from "path";
|
|
100735
100834
|
import { homedir as homedir4 } from "os";
|
|
100736
100835
|
import { mkdir as mkdir5 } from "fs/promises";
|
|
100737
100836
|
function fmt(type, text) {
|
|
@@ -100780,14 +100879,14 @@ var init_log = __esm(() => {
|
|
|
100780
100879
|
init_version();
|
|
100781
100880
|
jsonLogChains = new Map;
|
|
100782
100881
|
ANSI_RE = /\x1b(?:\[[0-9;]*[A-Za-z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|.)/g;
|
|
100783
|
-
AGENT_LOG_PATH =
|
|
100882
|
+
AGENT_LOG_PATH = join17(homedir4(), ".ralph", "agent-mode.log");
|
|
100784
100883
|
mkdir5(dirname7(AGENT_LOG_PATH), { recursive: true }).catch(() => {
|
|
100785
100884
|
return;
|
|
100786
100885
|
});
|
|
100787
100886
|
});
|
|
100788
100887
|
|
|
100789
100888
|
// apps/loop/src/debug.ts
|
|
100790
|
-
import { join as
|
|
100889
|
+
import { join as join18 } from "path";
|
|
100791
100890
|
function fmtTs(d) {
|
|
100792
100891
|
return d.toISOString().replace("T", " ").slice(0, 23);
|
|
100793
100892
|
}
|
|
@@ -100899,7 +100998,7 @@ function detectDebugStuck(lines) {
|
|
|
100899
100998
|
};
|
|
100900
100999
|
}
|
|
100901
101000
|
async function inspectBinary(projectRoot) {
|
|
100902
|
-
const binPath =
|
|
101001
|
+
const binPath = join18(projectRoot, ".ralph", "bin", "cli.js");
|
|
100903
101002
|
const file2 = Bun.file(binPath);
|
|
100904
101003
|
if (!await file2.exists())
|
|
100905
101004
|
return null;
|
|
@@ -100924,7 +101023,7 @@ async function inspectBinary(projectRoot) {
|
|
|
100924
101023
|
async function resolveDebugTarget(projectRoot, opts) {
|
|
100925
101024
|
const agentLogFile = Bun.file(AGENT_LOG_PATH);
|
|
100926
101025
|
const textLines = await agentLogFile.exists() ? parseTextLog(await agentLogFile.text()) : [];
|
|
100927
|
-
const jsonlLogFile = Bun.file(
|
|
101026
|
+
const jsonlLogFile = Bun.file(join18(projectRoot, ".ralph", "agent.log"));
|
|
100928
101027
|
const jsonlLines = await jsonlLogFile.exists() ? parseJsonlLog(await jsonlLogFile.text()) : [];
|
|
100929
101028
|
const allLines = [...textLines, ...jsonlLines];
|
|
100930
101029
|
if (opts.name && !opts.issue) {
|
|
@@ -101029,7 +101128,7 @@ async function runDebug(opts) {
|
|
|
101029
101128
|
`);
|
|
101030
101129
|
const agentLogFile = Bun.file(AGENT_LOG_PATH);
|
|
101031
101130
|
const textLines = await agentLogFile.exists() ? parseTextLog(await agentLogFile.text()) : [];
|
|
101032
|
-
const jsonlLogPath =
|
|
101131
|
+
const jsonlLogPath = join18(projectRoot, ".ralph", "agent.log");
|
|
101033
101132
|
const jsonlLogFile = Bun.file(jsonlLogPath);
|
|
101034
101133
|
const hasJsonlLog = await jsonlLogFile.exists();
|
|
101035
101134
|
let { changeName, identifier: issueIdentifier } = await resolveDebugTarget(projectRoot, {
|
|
@@ -101043,7 +101142,7 @@ async function runDebug(opts) {
|
|
|
101043
101142
|
}
|
|
101044
101143
|
const jsonlLines = hasJsonlLog ? parseJsonlLog(await jsonlLogFile.text(), changeName) : [];
|
|
101045
101144
|
const relevantText = textLines.filter((l) => l.text.includes(changeName) || issueIdentifier !== undefined && l.text.includes(issueIdentifier));
|
|
101046
|
-
const workerLogFile = Bun.file(
|
|
101145
|
+
const workerLogFile = Bun.file(join18(projectRoot, ".ralph", "logs", `${changeName}.log`));
|
|
101047
101146
|
const workerLines = await workerLogFile.exists() ? parseTextLog(await workerLogFile.text()) : [];
|
|
101048
101147
|
const merged = [...relevantText, ...jsonlLines, ...workerLines].sort((a, b) => +a.ts - +b.ts);
|
|
101049
101148
|
const seen = new Set;
|
|
@@ -101200,8 +101299,8 @@ async function runDebug(opts) {
|
|
|
101200
101299
|
out(" \u26A0 PR currently has merge conflicts");
|
|
101201
101300
|
if (pr?.checks.some((c) => c.conclusion === "FAILURE"))
|
|
101202
101301
|
out(" \u26A0 PR has failing CI checks");
|
|
101203
|
-
const worktreePath =
|
|
101204
|
-
if (await Bun.file(
|
|
101302
|
+
const worktreePath = join18(projectRoot, ".ralph", "worktrees", changeName);
|
|
101303
|
+
if (await Bun.file(join18(worktreePath, ".git")).exists()) {
|
|
101205
101304
|
out(` Worktree : ${worktreePath}`);
|
|
101206
101305
|
}
|
|
101207
101306
|
if (!timeline.length)
|
|
@@ -101221,12 +101320,12 @@ __export(exports_src2, {
|
|
|
101221
101320
|
taskMain: () => taskMain,
|
|
101222
101321
|
main: () => main2
|
|
101223
101322
|
});
|
|
101224
|
-
import { join as
|
|
101323
|
+
import { join as join19 } from "path";
|
|
101225
101324
|
import { exists as exists2, mkdir as mkdir6, rm as rm2 } from "fs/promises";
|
|
101226
101325
|
async function ensureRalphGitignore(projectRoot) {
|
|
101227
|
-
const ralphDir =
|
|
101326
|
+
const ralphDir = join19(projectRoot, ".ralph");
|
|
101228
101327
|
await mkdir6(ralphDir, { recursive: true });
|
|
101229
|
-
const gitignorePath =
|
|
101328
|
+
const gitignorePath = join19(ralphDir, ".gitignore");
|
|
101230
101329
|
const file2 = Bun.file(gitignorePath);
|
|
101231
101330
|
if (await file2.exists()) {
|
|
101232
101331
|
const existing = await file2.text();
|
|
@@ -101293,9 +101392,9 @@ async function main2(argv) {
|
|
|
101293
101392
|
`);
|
|
101294
101393
|
return 1;
|
|
101295
101394
|
}
|
|
101296
|
-
const worktreeDir =
|
|
101297
|
-
const changeDir =
|
|
101298
|
-
const stateDir =
|
|
101395
|
+
const worktreeDir = join19(worktreesDir(projectRoot), args.name);
|
|
101396
|
+
const changeDir = join19(tasksDir, args.name);
|
|
101397
|
+
const stateDir = join19(statesDir, args.name);
|
|
101299
101398
|
const branch = `ralph/${args.name}`;
|
|
101300
101399
|
const removed = [];
|
|
101301
101400
|
if (await exists2(worktreeDir)) {
|
|
@@ -101346,8 +101445,8 @@ async function main2(argv) {
|
|
|
101346
101445
|
return 0;
|
|
101347
101446
|
}
|
|
101348
101447
|
if (args.mode === "task" && args.name) {
|
|
101349
|
-
await mkdir6(
|
|
101350
|
-
await mkdir6(
|
|
101448
|
+
await mkdir6(join19(statesDir, args.name), { recursive: true });
|
|
101449
|
+
await mkdir6(join19(tasksDir, args.name), { recursive: true });
|
|
101351
101450
|
await ensureRalphGitignore(projectRoot);
|
|
101352
101451
|
}
|
|
101353
101452
|
await runWithContext(createDefaultContext({ layout, args }), async () => {
|
|
@@ -101375,8 +101474,8 @@ async function taskMain(argv) {
|
|
|
101375
101474
|
const layout = projectLayout(projectRoot);
|
|
101376
101475
|
const statesDir = layout.statesDir;
|
|
101377
101476
|
const tasksDir = layout.tasksDir;
|
|
101378
|
-
await mkdir6(
|
|
101379
|
-
await mkdir6(
|
|
101477
|
+
await mkdir6(join19(statesDir, args.name), { recursive: true });
|
|
101478
|
+
await mkdir6(join19(tasksDir, args.name), { recursive: true });
|
|
101380
101479
|
await ensureRalphGitignore(projectRoot);
|
|
101381
101480
|
await runWithContext(createDefaultContext({ layout, args }), async () => {
|
|
101382
101481
|
const { waitUntilExit } = render_default(import_react60.createElement(App2, {
|
|
@@ -101773,7 +101872,7 @@ function formatError2(err) {
|
|
|
101773
101872
|
}
|
|
101774
101873
|
|
|
101775
101874
|
// apps/agent/src/shared/capabilities/fs-change.ts
|
|
101776
|
-
import { join as
|
|
101875
|
+
import { join as join20, dirname as dirname8 } from "path";
|
|
101777
101876
|
import { mkdir as mkdir7 } from "fs/promises";
|
|
101778
101877
|
var scaffold, prependTask, appendSteering, fsChange;
|
|
101779
101878
|
var init_fs_change = __esm(() => {
|
|
@@ -101786,11 +101885,11 @@ var init_fs_change = __esm(() => {
|
|
|
101786
101885
|
errorFormatter: formatError2,
|
|
101787
101886
|
run: async (args) => {
|
|
101788
101887
|
await mkdir7(args.changeDir, { recursive: true });
|
|
101789
|
-
await mkdir7(
|
|
101888
|
+
await mkdir7(join20(args.changeDir, "specs"), { recursive: true });
|
|
101790
101889
|
await mkdir7(args.stateDir, { recursive: true });
|
|
101791
|
-
await Bun.write(
|
|
101792
|
-
await Bun.write(
|
|
101793
|
-
await Bun.write(
|
|
101890
|
+
await Bun.write(join20(args.changeDir, "proposal.md"), args.proposal);
|
|
101891
|
+
await Bun.write(join20(args.changeDir, "tasks.md"), args.tasks);
|
|
101892
|
+
await Bun.write(join20(args.changeDir, "design.md"), args.design);
|
|
101794
101893
|
}
|
|
101795
101894
|
};
|
|
101796
101895
|
prependTask = {
|
|
@@ -101808,7 +101907,7 @@ var init_fs_change = __esm(() => {
|
|
|
101808
101907
|
retryPolicy: NO_RETRY,
|
|
101809
101908
|
errorFormatter: formatError2,
|
|
101810
101909
|
run: async (args) => {
|
|
101811
|
-
const path =
|
|
101910
|
+
const path = join20(args.changeDir, "steering.md");
|
|
101812
101911
|
const f2 = Bun.file(path);
|
|
101813
101912
|
const existing = await f2.exists() ? await f2.text() : null;
|
|
101814
101913
|
const updated = existing ? `${args.message}
|
|
@@ -101823,11 +101922,11 @@ ${existing.trimStart()}` : `${args.message}
|
|
|
101823
101922
|
});
|
|
101824
101923
|
|
|
101825
101924
|
// apps/agent/src/agent/worktree.ts
|
|
101826
|
-
import { basename as basename2, join as
|
|
101925
|
+
import { basename as basename2, join as join21 } from "path";
|
|
101827
101926
|
import { homedir as homedir5 } from "os";
|
|
101828
101927
|
import { exists as exists3 } from "fs/promises";
|
|
101829
101928
|
function worktreesDir2(projectRoot) {
|
|
101830
|
-
return
|
|
101929
|
+
return join21(homedir5(), ".ralph", basename2(projectRoot), "worktrees");
|
|
101831
101930
|
}
|
|
101832
101931
|
function branchForChange(changeName) {
|
|
101833
101932
|
return `ralph/${changeName}`;
|
|
@@ -101846,7 +101945,7 @@ function createWorktree(projectRoot, changeName, baseBranch, runner) {
|
|
|
101846
101945
|
}
|
|
101847
101946
|
async function provisionWorktree(projectRoot, changeName, baseBranch, runner) {
|
|
101848
101947
|
const dir = worktreesDir2(projectRoot);
|
|
101849
|
-
const cwd2 =
|
|
101948
|
+
const cwd2 = join21(dir, changeName);
|
|
101850
101949
|
const branch = branchForChange(changeName);
|
|
101851
101950
|
const list = await runner.run(["worktree", "list", "--porcelain"], projectRoot);
|
|
101852
101951
|
if (list.stdout.includes(`worktree ${cwd2}
|
|
@@ -101871,7 +101970,7 @@ async function provisionWorktree(projectRoot, changeName, baseBranch, runner) {
|
|
|
101871
101970
|
return { cwd: cwd2, branch };
|
|
101872
101971
|
}
|
|
101873
101972
|
async function installPrePushHook(cwd2, runner) {
|
|
101874
|
-
const hookPath =
|
|
101973
|
+
const hookPath = join21(cwd2, ".ralph-hooks", "pre-push");
|
|
101875
101974
|
await Bun.write(hookPath, PRE_PUSH_HOOK_SCRIPT);
|
|
101876
101975
|
const chmod = Bun.spawn(["chmod", "+x", hookPath]);
|
|
101877
101976
|
await chmod.exited;
|
|
@@ -101917,8 +102016,8 @@ async function isWorktreeSafeToRemove(cwd2, base2, runner) {
|
|
|
101917
102016
|
return { safe: true, dirty, unpushedCommits };
|
|
101918
102017
|
}
|
|
101919
102018
|
async function seedWorktreeMcpConfig(projectRoot, worktreeCwd) {
|
|
101920
|
-
const dst =
|
|
101921
|
-
const src =
|
|
102019
|
+
const dst = join21(worktreeCwd, ".mcp.json");
|
|
102020
|
+
const src = join21(projectRoot, ".mcp.json");
|
|
101922
102021
|
const source = await exists3(dst) ? dst : await exists3(src) ? src : null;
|
|
101923
102022
|
if (!source)
|
|
101924
102023
|
return;
|
|
@@ -101932,7 +102031,7 @@ async function seedWorktreeMcpConfig(projectRoot, worktreeCwd) {
|
|
|
101932
102031
|
if (servers && typeof servers === "object") {
|
|
101933
102032
|
for (const cfg of Object.values(servers)) {
|
|
101934
102033
|
if (Array.isArray(cfg.args)) {
|
|
101935
|
-
cfg.args = cfg.args.map((a) => typeof a === "string" && a.startsWith(".ralph/") ?
|
|
102034
|
+
cfg.args = cfg.args.map((a) => typeof a === "string" && a.startsWith(".ralph/") ? join21(projectRoot, a) : a);
|
|
101936
102035
|
}
|
|
101937
102036
|
}
|
|
101938
102037
|
}
|
|
@@ -103782,7 +103881,7 @@ function emitFeatureSkipped(bus, id, reason) {
|
|
|
103782
103881
|
var init_run_feature = () => {};
|
|
103783
103882
|
|
|
103784
103883
|
// apps/agent/src/agent/post-task.ts
|
|
103785
|
-
import { join as
|
|
103884
|
+
import { join as join22, dirname as dirname9 } from "path";
|
|
103786
103885
|
function summarizeUncommittedStatus(stdout) {
|
|
103787
103886
|
const lines = stdout.split(`
|
|
103788
103887
|
`).filter((line) => line.length > 0);
|
|
@@ -103854,7 +103953,7 @@ async function reactivateState(stateFilePath, log3, changeName) {
|
|
|
103854
103953
|
async function runWorkerWithFixTask(ctx, heading, body) {
|
|
103855
103954
|
try {
|
|
103856
103955
|
await runCapability(fsChange.prependTask, {
|
|
103857
|
-
tasksPath:
|
|
103956
|
+
tasksPath: join22(ctx.changeDir, AGENT_TASKS_FILENAME),
|
|
103858
103957
|
heading,
|
|
103859
103958
|
failureOutput: body
|
|
103860
103959
|
});
|
|
@@ -104401,7 +104500,7 @@ async function runValidateOnlyPhase(input, deps) {
|
|
|
104401
104500
|
emit3("validate-fix", command);
|
|
104402
104501
|
log3(`! validation check failed: ${command}`, "yellow");
|
|
104403
104502
|
try {
|
|
104404
|
-
await prependFixTask(
|
|
104503
|
+
await prependFixTask(join22(changeDir, AGENT_TASKS_FILENAME), `Fix failing validation: ${command}`, output || `Command exited with code ${exitCode}`);
|
|
104405
104504
|
} catch (err) {
|
|
104406
104505
|
log3(`! could not prepend fix task: ${err.message}`, "red");
|
|
104407
104506
|
return 1;
|
|
@@ -104412,7 +104511,7 @@ async function runValidateOnlyPhase(input, deps) {
|
|
|
104412
104511
|
}
|
|
104413
104512
|
}
|
|
104414
104513
|
try {
|
|
104415
|
-
await prependFixTask(
|
|
104514
|
+
await prependFixTask(join22(changeDir, AGENT_TASKS_FILENAME), "Run openspec validation", [
|
|
104416
104515
|
`Run \`bunx openspec validate ${changeName}\` to validate the change artifacts.`,
|
|
104417
104516
|
`Commit any pending changes before running the validation command.`
|
|
104418
104517
|
].join(`
|
|
@@ -104425,7 +104524,7 @@ async function runValidateOnlyPhase(input, deps) {
|
|
|
104425
104524
|
return respawnWorker();
|
|
104426
104525
|
}
|
|
104427
104526
|
async function recordGaveUp(stateFilePath, log3, changeName) {
|
|
104428
|
-
const path =
|
|
104527
|
+
const path = join22(dirname9(stateFilePath), GAVEUP_COUNT_FILE);
|
|
104429
104528
|
try {
|
|
104430
104529
|
const file2 = Bun.file(path);
|
|
104431
104530
|
const current = await file2.exists() ? Number.parseInt(await file2.text(), 10) || 0 : 0;
|
|
@@ -105831,15 +105930,15 @@ var init_coordinator2 = __esm(() => {
|
|
|
105831
105930
|
});
|
|
105832
105931
|
|
|
105833
105932
|
// apps/agent/src/agent/scaffold.ts
|
|
105834
|
-
import { join as
|
|
105933
|
+
import { join as join23 } from "path";
|
|
105835
105934
|
function changeNameForIssue(issue2) {
|
|
105836
105935
|
const slug = issue2.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 40).replace(/^-+|-+$/g, "");
|
|
105837
105936
|
return slug ? `${issue2.identifier.toLowerCase()}-${slug}` : issue2.identifier.toLowerCase();
|
|
105838
105937
|
}
|
|
105839
105938
|
async function scaffoldChangeForIssue(tasksDir, statesDir, issue2, comments = [], appendPrompt = "", attachments = []) {
|
|
105840
105939
|
const name = changeNameForIssue(issue2);
|
|
105841
|
-
const changeDir =
|
|
105842
|
-
const stateDir =
|
|
105940
|
+
const changeDir = join23(tasksDir, name);
|
|
105941
|
+
const stateDir = join23(statesDir, name);
|
|
105843
105942
|
const commentsBlock = comments.length > 0 ? [
|
|
105844
105943
|
"",
|
|
105845
105944
|
"## Linear comments",
|
|
@@ -105892,8 +105991,8 @@ async function scaffoldChangeForIssue(tasksDir, statesDir, issue2, comments = []
|
|
|
105892
105991
|
`- [ ] Refine proposal.md with the problem statement, approach, and acceptance criteria derived from the research`,
|
|
105893
105992
|
`- [ ] Fill in \`## Why\` and \`## What Changes\` in proposal.md so \`openspec validate\` passes (these sections are required by the validator)`,
|
|
105894
105993
|
`- [ ] Add at least one spec delta under \`specs/<capability>/spec.md\` describing the behavior added/modified/removed by this change`,
|
|
105895
|
-
`- [ ] Fill in design.md with the technical design (files to touch, data flow, edge cases)
|
|
105896
|
-
`- [ ] Append an \`## Implementation\` section below with concrete mission-specific tasks derived from the plan, including tests and \`bun run lint\` / \`bun run test\`. Every item in the new section MUST start as \`- [ ]\` (unchecked) \u2014 do not pre-check items even if you already did the work during planning. The loop ticks them off in later iterations after each one is verified.`,
|
|
105994
|
+
`- [ ] Fill in design.md with the technical design (files to touch, data flow, edge cases). design.md holds prose and tables ONLY \u2014 never a task checklist; the implementation tasks belong in this tasks.md file (next item).`,
|
|
105995
|
+
`- [ ] Append an \`## Implementation\` section to **this tasks.md file** (below the \`## Planning\` section above \u2014 NOT in design.md) with concrete mission-specific tasks derived from the plan, including tests and \`bun run lint\` / \`bun run test\`. Every item in the new section MUST start as \`- [ ]\` (unchecked) \u2014 do not pre-check items even if you already did the work during planning. The loop ticks them off in later iterations after each one is verified.`,
|
|
105897
105996
|
`- [ ] Is there anything else to add? Review the complete change context and document any additional edge cases, constraints, or open questions not captured above.`,
|
|
105898
105997
|
""
|
|
105899
105998
|
].join(`
|
|
@@ -105975,19 +106074,22 @@ var init_detections = __esm(() => {
|
|
|
105975
106074
|
});
|
|
105976
106075
|
|
|
105977
106076
|
// apps/agent/src/features/confirmation/state.ts
|
|
105978
|
-
import { dirname as dirname10, join as
|
|
105979
|
-
|
|
105980
|
-
async function readConfirmationState(statePath) {
|
|
106077
|
+
import { dirname as dirname10, join as join24 } from "path";
|
|
106078
|
+
async function readInlineConfirmation(statePath) {
|
|
105981
106079
|
const f2 = Bun.file(statePath);
|
|
105982
|
-
|
|
105983
|
-
|
|
105984
|
-
|
|
105985
|
-
|
|
105986
|
-
|
|
105987
|
-
|
|
105988
|
-
|
|
106080
|
+
if (!await f2.exists())
|
|
106081
|
+
return null;
|
|
106082
|
+
try {
|
|
106083
|
+
const obj = await f2.json();
|
|
106084
|
+
return obj.confirmation ?? null;
|
|
106085
|
+
} catch {
|
|
106086
|
+
return null;
|
|
105989
106087
|
}
|
|
105990
|
-
|
|
106088
|
+
}
|
|
106089
|
+
async function readConfirmationState(statePath) {
|
|
106090
|
+
const changeDir = dirname10(statePath);
|
|
106091
|
+
const sidecar = await readSlotSidecar(changeDir, "confirmation");
|
|
106092
|
+
const existing = sidecar ?? await readInlineConfirmation(statePath) ?? null;
|
|
105991
106093
|
const confirmation = {
|
|
105992
106094
|
askedAt: existing?.askedAt ?? null,
|
|
105993
106095
|
lastReminderAt: existing?.lastReminderAt ?? null,
|
|
@@ -105995,14 +106097,13 @@ async function readConfirmationState(statePath) {
|
|
|
105995
106097
|
rounds: existing?.rounds ?? 0,
|
|
105996
106098
|
stuckPostedAt: existing?.stuckPostedAt ?? null,
|
|
105997
106099
|
lastReviseConsumedAt: existing?.lastReviseConsumedAt ?? null,
|
|
105998
|
-
awaitingMarkerAppliedAt: existing?.awaitingMarkerAppliedAt ?? null
|
|
106100
|
+
awaitingMarkerAppliedAt: existing?.awaitingMarkerAppliedAt ?? null,
|
|
106101
|
+
earlyDraftPrAt: existing?.earlyDraftPrAt ?? null
|
|
105999
106102
|
};
|
|
106000
|
-
return { stateObj, confirmation };
|
|
106103
|
+
return { stateObj: {}, confirmation };
|
|
106001
106104
|
}
|
|
106002
|
-
async function writeConfirmationState(statePath,
|
|
106003
|
-
await
|
|
106004
|
-
await Bun.write(statePath, JSON.stringify({ ...stateObj, confirmation }, null, 2) + `
|
|
106005
|
-
`);
|
|
106105
|
+
async function writeConfirmationState(statePath, _stateObj, confirmation) {
|
|
106106
|
+
await writeSlotField(dirname10(statePath), "confirmation", confirmation);
|
|
106006
106107
|
}
|
|
106007
106108
|
async function restartFromDesign(changeDir, changeName) {
|
|
106008
106109
|
const designStub = [
|
|
@@ -106012,8 +106113,8 @@ async function restartFromDesign(changeDir, changeName) {
|
|
|
106012
106113
|
""
|
|
106013
106114
|
].join(`
|
|
106014
106115
|
`);
|
|
106015
|
-
await Bun.write(
|
|
106016
|
-
const tasksPath =
|
|
106116
|
+
await Bun.write(join24(changeDir, "design.md"), designStub);
|
|
106117
|
+
const tasksPath = join24(changeDir, "tasks.md");
|
|
106017
106118
|
if (await Bun.file(tasksPath).exists()) {
|
|
106018
106119
|
await Bun.write(tasksPath, `# Tasks
|
|
106019
106120
|
|
|
@@ -106025,6 +106126,7 @@ async function appendSteeringNote(changeDir, message) {
|
|
|
106025
106126
|
await runCapability(fsChange.appendSteering, { changeDir, message });
|
|
106026
106127
|
}
|
|
106027
106128
|
var init_state2 = __esm(() => {
|
|
106129
|
+
init_store();
|
|
106028
106130
|
init_fs_change();
|
|
106029
106131
|
});
|
|
106030
106132
|
|
|
@@ -106242,8 +106344,7 @@ var init_inspect = __esm(() => {
|
|
|
106242
106344
|
});
|
|
106243
106345
|
|
|
106244
106346
|
// apps/agent/src/features/confirmation/awaiting.ts
|
|
106245
|
-
import { join as
|
|
106246
|
-
import { mkdir as mkdir9 } from "fs/promises";
|
|
106347
|
+
import { join as join25 } from "path";
|
|
106247
106348
|
async function resolveChangeCwdForIssue(issue2, changeName, deps) {
|
|
106248
106349
|
const tracked = deps.cwdOf(changeName);
|
|
106249
106350
|
if (tracked)
|
|
@@ -106251,12 +106352,12 @@ async function resolveChangeCwdForIssue(issue2, changeName, deps) {
|
|
|
106251
106352
|
if (!deps.useWorktree)
|
|
106252
106353
|
return deps.projectRoot;
|
|
106253
106354
|
const root = worktreesDir2(deps.projectRoot);
|
|
106254
|
-
const canonical =
|
|
106255
|
-
if (await Bun.file(
|
|
106355
|
+
const canonical = join25(root, worktreeDirNameForIssue(issue2));
|
|
106356
|
+
if (await Bun.file(join25(canonical, "openspec", "changes", changeName, "tasks.md")).exists()) {
|
|
106256
106357
|
return canonical;
|
|
106257
106358
|
}
|
|
106258
|
-
const legacy =
|
|
106259
|
-
if (await Bun.file(
|
|
106359
|
+
const legacy = join25(root, changeName);
|
|
106360
|
+
if (await Bun.file(join25(legacy, "openspec", "changes", changeName, "tasks.md")).exists()) {
|
|
106260
106361
|
return legacy;
|
|
106261
106362
|
}
|
|
106262
106363
|
return deps.projectRoot;
|
|
@@ -106276,17 +106377,8 @@ async function postPlanReadyCommentOnce(issue2, statePath, changeName, deps) {
|
|
|
106276
106377
|
return;
|
|
106277
106378
|
if (deps.cfg.linear.postComments === false)
|
|
106278
106379
|
return;
|
|
106279
|
-
|
|
106280
|
-
|
|
106281
|
-
if (await f2.exists()) {
|
|
106282
|
-
try {
|
|
106283
|
-
stateObj = await f2.json();
|
|
106284
|
-
} catch {
|
|
106285
|
-
stateObj = {};
|
|
106286
|
-
}
|
|
106287
|
-
}
|
|
106288
|
-
const confirmation = stateObj.confirmation ?? null;
|
|
106289
|
-
if (confirmation?.askedAt)
|
|
106380
|
+
const { confirmation } = await readConfirmationState(statePath);
|
|
106381
|
+
if (confirmation.askedAt)
|
|
106290
106382
|
return;
|
|
106291
106383
|
const approvalSentence = describeApprovalMarker(deps.cfg.linear.indicators.getApproved);
|
|
106292
106384
|
const handle = deps.cfg.linear.mentionHandle;
|
|
@@ -106297,16 +106389,8 @@ async function postPlanReadyCommentOnce(issue2, statePath, changeName, deps) {
|
|
|
106297
106389
|
deps.onLog(`! Linear plan-ready comment failed for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
106298
106390
|
return;
|
|
106299
106391
|
}
|
|
106300
|
-
const nextConfirmation = {
|
|
106301
|
-
askedAt: new Date().toISOString(),
|
|
106302
|
-
lastReminderAt: confirmation?.lastReminderAt ?? null,
|
|
106303
|
-
confirmedAt: confirmation?.confirmedAt ?? null,
|
|
106304
|
-
rounds: confirmation?.rounds ?? 0
|
|
106305
|
-
};
|
|
106306
106392
|
try {
|
|
106307
|
-
await
|
|
106308
|
-
await Bun.write(statePath, JSON.stringify({ ...stateObj, confirmation: nextConfirmation }, null, 2) + `
|
|
106309
|
-
`);
|
|
106393
|
+
await writeConfirmationState(statePath, {}, { ...confirmation, askedAt: new Date().toISOString() });
|
|
106310
106394
|
} catch (err) {
|
|
106311
106395
|
deps.onLog(`! could not persist confirmation.askedAt for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
106312
106396
|
}
|
|
@@ -106330,10 +106414,42 @@ async function applyAwaitingMarkerOnce(issue2, statePath, state, deps) {
|
|
|
106330
106414
|
deps.onLog(`! persist awaitingMarkerAppliedAt for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
106331
106415
|
}
|
|
106332
106416
|
}
|
|
106417
|
+
async function openDraftPrOnce(issue2, statePath, changeName, cwd2, state, deps) {
|
|
106418
|
+
if (deps.cfg.prDraft !== true)
|
|
106419
|
+
return;
|
|
106420
|
+
if (!deps.openDraftPr)
|
|
106421
|
+
return;
|
|
106422
|
+
if (state.confirmation.earlyDraftPrAt)
|
|
106423
|
+
return;
|
|
106424
|
+
let url2 = null;
|
|
106425
|
+
try {
|
|
106426
|
+
url2 = await deps.openDraftPr(issue2, changeName, cwd2);
|
|
106427
|
+
} catch (err) {
|
|
106428
|
+
deps.onLog(`! early draft PR open failed for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
106429
|
+
}
|
|
106430
|
+
state.confirmation.earlyDraftPrAt = new Date().toISOString();
|
|
106431
|
+
try {
|
|
106432
|
+
await writeConfirmationState(statePath, state.stateObj, state.confirmation);
|
|
106433
|
+
} catch (err) {
|
|
106434
|
+
deps.onLog(`! persist earlyDraftPrAt for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
106435
|
+
}
|
|
106436
|
+
if (url2)
|
|
106437
|
+
deps.onLog(` ${issue2.identifier}: opened draft PR for design \u2014 ${url2}`, "gray");
|
|
106438
|
+
}
|
|
106439
|
+
function issueInAwaitingStatus(issue2, indicators) {
|
|
106440
|
+
const set3 = indicators.setAwaitingConfirmation;
|
|
106441
|
+
if (!set3)
|
|
106442
|
+
return false;
|
|
106443
|
+
const current = issue2.state?.name;
|
|
106444
|
+
if (!current)
|
|
106445
|
+
return false;
|
|
106446
|
+
return markersOf(set3).some((m) => m.type === "status" && m.value === current);
|
|
106447
|
+
}
|
|
106333
106448
|
async function releaseAwaitingMarker(issue2, statePath, deps) {
|
|
106334
106449
|
const { stateObj, confirmation } = await readConfirmationState(statePath);
|
|
106335
|
-
if (!confirmation.awaitingMarkerAppliedAt)
|
|
106450
|
+
if (!confirmation.awaitingMarkerAppliedAt && !issueInAwaitingStatus(issue2, deps.indicators)) {
|
|
106336
106451
|
return;
|
|
106452
|
+
}
|
|
106337
106453
|
if (deps.indicators.clearAwaitingConfirmation) {
|
|
106338
106454
|
try {
|
|
106339
106455
|
await deps.applyIndicator(issue2, deps.indicators.clearAwaitingConfirmation);
|
|
@@ -106341,6 +106457,13 @@ async function releaseAwaitingMarker(issue2, statePath, deps) {
|
|
|
106341
106457
|
deps.onLog(`! clearAwaitingConfirmation failed for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
106342
106458
|
}
|
|
106343
106459
|
}
|
|
106460
|
+
if (deps.indicators.setInProgress) {
|
|
106461
|
+
try {
|
|
106462
|
+
await deps.applyIndicator(issue2, deps.indicators.setInProgress);
|
|
106463
|
+
} catch (err) {
|
|
106464
|
+
deps.onLog(`! restore setInProgress after awaiting release failed for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
106465
|
+
}
|
|
106466
|
+
}
|
|
106344
106467
|
confirmation.awaitingMarkerAppliedAt = null;
|
|
106345
106468
|
try {
|
|
106346
106469
|
await writeConfirmationState(statePath, stateObj, confirmation);
|
|
@@ -106369,9 +106492,9 @@ async function processAwaitingForIssue(issue2, deps) {
|
|
|
106369
106492
|
const layout = projectLayout(cwd2);
|
|
106370
106493
|
const changeDir = layout.changeDir(changeName);
|
|
106371
106494
|
const statePath = layout.stateFile(changeName);
|
|
106372
|
-
const tasks2 = await readTextOrNull(
|
|
106373
|
-
const proposal = await readTextOrNull(
|
|
106374
|
-
const design = await readTextOrNull(
|
|
106495
|
+
const tasks2 = await readTextOrNull(join25(changeDir, "tasks.md"));
|
|
106496
|
+
const proposal = await readTextOrNull(join25(changeDir, "proposal.md"));
|
|
106497
|
+
const design = await readTextOrNull(join25(changeDir, "design.md"));
|
|
106375
106498
|
let commentsCache = null;
|
|
106376
106499
|
const getComments = async () => {
|
|
106377
106500
|
if (commentsCache)
|
|
@@ -106455,6 +106578,7 @@ async function processAwaitingForIssue(issue2, deps) {
|
|
|
106455
106578
|
cfg,
|
|
106456
106579
|
onLog: deps.onLog
|
|
106457
106580
|
});
|
|
106581
|
+
await openDraftPrOnce(issue2, statePath, changeName, cwd2, { stateObj, confirmation }, { cfg, openDraftPr: deps.openDraftPr, onLog: deps.onLog });
|
|
106458
106582
|
const { stateObj: state2, confirmation: confirmation2 } = await readConfirmationState(statePath);
|
|
106459
106583
|
const { outcome, next } = await inspectAwaitingTicket(confirmation2, {
|
|
106460
106584
|
mentionHandle: cfg.linear.mentionHandle,
|
|
@@ -106529,6 +106653,7 @@ var init_awaiting = __esm(() => {
|
|
|
106529
106653
|
init_worktree();
|
|
106530
106654
|
init_scaffold();
|
|
106531
106655
|
init_linear();
|
|
106656
|
+
init_types2();
|
|
106532
106657
|
init_workflow();
|
|
106533
106658
|
init_state2();
|
|
106534
106659
|
init_inspect();
|
|
@@ -106603,9 +106728,26 @@ async function resolveDependencyBaseBranchImpl(issue2, runner, runnerCwd, deps)
|
|
|
106603
106728
|
}
|
|
106604
106729
|
return null;
|
|
106605
106730
|
}
|
|
106731
|
+
function createOpenDraftPr(deps) {
|
|
106732
|
+
const create3 = deps.createPr ?? createPullRequest;
|
|
106733
|
+
return async (issue2, changeName, cwd2) => {
|
|
106734
|
+
const branch = deps.branchByChange.get(changeName);
|
|
106735
|
+
if (!branch)
|
|
106736
|
+
return null;
|
|
106737
|
+
const base2 = baseBranchFromLabels(issue2.labels) ?? deps.prBaseBranch;
|
|
106738
|
+
const result2 = await create3({ cwd: cwd2, branch, issue: issue2, base: base2, draft: true }, deps.cmdRunner);
|
|
106739
|
+
const url2 = result2?.url ?? null;
|
|
106740
|
+
if (url2) {
|
|
106741
|
+
deps.prByChange.set(changeName, url2);
|
|
106742
|
+
deps.invalidatePrUrlForIssue(issue2.id);
|
|
106743
|
+
}
|
|
106744
|
+
return url2;
|
|
106745
|
+
};
|
|
106746
|
+
}
|
|
106606
106747
|
var GITHUB_PR_URL_RE, PR_NUMBER_RE, TICKET_IN_TITLE_RE;
|
|
106607
106748
|
var init_pr_helpers = __esm(() => {
|
|
106608
106749
|
init_linear();
|
|
106750
|
+
init_pr();
|
|
106609
106751
|
GITHUB_PR_URL_RE = /^https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+/;
|
|
106610
106752
|
PR_NUMBER_RE = /\/pull\/(\d+)/;
|
|
106611
106753
|
TICKET_IN_TITLE_RE = /^([A-Za-z][A-Za-z0-9]*-\d+)\b/;
|
|
@@ -106895,8 +107037,8 @@ var init_linear_resolvers = __esm(() => {
|
|
|
106895
107037
|
});
|
|
106896
107038
|
|
|
106897
107039
|
// apps/agent/src/agent/wire/prepare.ts
|
|
106898
|
-
import { mkdir as
|
|
106899
|
-
import { join as
|
|
107040
|
+
import { mkdir as mkdir8 } from "fs/promises";
|
|
107041
|
+
import { join as join26 } from "path";
|
|
106900
107042
|
function createPrepareHelpers(input) {
|
|
106901
107043
|
const {
|
|
106902
107044
|
args,
|
|
@@ -106960,7 +107102,7 @@ function createPrepareHelpers(input) {
|
|
|
106960
107102
|
let changeName;
|
|
106961
107103
|
const wtLayoutPre = projectLayout(workerCwd);
|
|
106962
107104
|
const derivedName = changeNameForIssue(issue2);
|
|
106963
|
-
const tasksMdPath =
|
|
107105
|
+
const tasksMdPath = join26(wtLayoutPre.changeDir(derivedName), "tasks.md");
|
|
106964
107106
|
const tasksMdExists = await Bun.file(tasksMdPath).exists();
|
|
106965
107107
|
const isFresh = !tasksMdExists;
|
|
106966
107108
|
if (isFresh) {
|
|
@@ -106999,8 +107141,8 @@ function createPrepareHelpers(input) {
|
|
|
106999
107141
|
changeName = await scaffoldChangeForIssue(scaffoldTasksDir, scaffoldStatesDir, issue2, comments, appendPrompt, attachments);
|
|
107000
107142
|
} else {
|
|
107001
107143
|
changeName = derivedName;
|
|
107002
|
-
await
|
|
107003
|
-
await
|
|
107144
|
+
await mkdir8(wtLayoutPre.changeDir(changeName), { recursive: true });
|
|
107145
|
+
await mkdir8(wtLayoutPre.taskStateDir(changeName), { recursive: true });
|
|
107004
107146
|
}
|
|
107005
107147
|
maps.cwdByChange.set(changeName, workerCwd);
|
|
107006
107148
|
maps.statesDirByChange.set(changeName, scaffoldStatesDir);
|
|
@@ -107038,7 +107180,7 @@ function createPrepareHelpers(input) {
|
|
|
107038
107180
|
if (!workerCwd)
|
|
107039
107181
|
return;
|
|
107040
107182
|
const wtLayout = projectLayout(workerCwd);
|
|
107041
|
-
const tasksFile =
|
|
107183
|
+
const tasksFile = join26(wtLayout.changeDir(changeName), AGENT_TASKS_FILENAME);
|
|
107042
107184
|
if (trigger === "review") {
|
|
107043
107185
|
let body2;
|
|
107044
107186
|
let heading;
|
|
@@ -107331,21 +107473,24 @@ var init_pr_discovery = __esm(() => {
|
|
|
107331
107473
|
});
|
|
107332
107474
|
|
|
107333
107475
|
// apps/agent/src/features/review-followup/scan.ts
|
|
107334
|
-
import { dirname as
|
|
107476
|
+
import { dirname as dirname11, join as join27 } from "path";
|
|
107335
107477
|
async function resolveReviewStateDir(changeName, deps) {
|
|
107336
107478
|
const root = deps.cwdOf(changeName);
|
|
107337
107479
|
if (root)
|
|
107338
|
-
return
|
|
107480
|
+
return dirname11(projectLayout(root).stateFile(changeName));
|
|
107339
107481
|
if (!deps.useWorktree)
|
|
107340
|
-
return
|
|
107341
|
-
const wtPath =
|
|
107482
|
+
return dirname11(projectLayout(deps.projectRoot).stateFile(changeName));
|
|
107483
|
+
const wtPath = join27(worktreesDir2(deps.projectRoot), changeName);
|
|
107342
107484
|
const statePath = projectLayout(wtPath).stateFile(changeName);
|
|
107343
107485
|
if (await Bun.file(statePath).exists())
|
|
107344
|
-
return
|
|
107486
|
+
return dirname11(statePath);
|
|
107345
107487
|
return null;
|
|
107346
107488
|
}
|
|
107347
107489
|
async function readReviewWatermark(stateDir) {
|
|
107348
|
-
const
|
|
107490
|
+
const sidecar = await readSlotSidecar(stateDir, "review");
|
|
107491
|
+
if (sidecar)
|
|
107492
|
+
return sidecar.lastConsumedCommentAt ?? null;
|
|
107493
|
+
const file2 = Bun.file(join27(stateDir, ".ralph-state.json"));
|
|
107349
107494
|
if (!await file2.exists())
|
|
107350
107495
|
return null;
|
|
107351
107496
|
try {
|
|
@@ -107557,7 +107702,7 @@ var init_github = __esm(() => {
|
|
|
107557
107702
|
|
|
107558
107703
|
// apps/agent/src/agent/wire/mention-scan.ts
|
|
107559
107704
|
import { readdir as readdir2 } from "fs/promises";
|
|
107560
|
-
import { join as
|
|
107705
|
+
import { join as join28 } from "path";
|
|
107561
107706
|
function createMentionScanner(input) {
|
|
107562
107707
|
const {
|
|
107563
107708
|
apiKey,
|
|
@@ -107723,7 +107868,7 @@ function createMentionScanner(input) {
|
|
|
107723
107868
|
async function isChangeArchivedForIssue(issue2, cwdByChange, projectRoot) {
|
|
107724
107869
|
const changeName = changeNameForIssue(issue2);
|
|
107725
107870
|
const root = cwdByChange.get(changeName) ?? projectRoot;
|
|
107726
|
-
const archiveDir =
|
|
107871
|
+
const archiveDir = join28(projectLayout(root).tasksDir, "archive");
|
|
107727
107872
|
let entries;
|
|
107728
107873
|
try {
|
|
107729
107874
|
entries = await readdir2(archiveDir);
|
|
@@ -107747,9 +107892,9 @@ var init_mention_scan = __esm(() => {
|
|
|
107747
107892
|
});
|
|
107748
107893
|
|
|
107749
107894
|
// apps/agent/src/agent/wire/spawn/default.ts
|
|
107750
|
-
import { join as
|
|
107895
|
+
import { join as join29 } from "path";
|
|
107751
107896
|
function defaultSpawn(changeName, cmd, cwd2, logsDir, onWorkerOutput, note) {
|
|
107752
|
-
const logFilePath =
|
|
107897
|
+
const logFilePath = join29(logsDir, `${changeName}.log`);
|
|
107753
107898
|
const ANSI_RE2 = /\x1b(?:\[[0-9;]*[A-Za-z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|.)/g;
|
|
107754
107899
|
const BOX_ONLY_RE = /^[\s\u2500\u2502\u256D\u256E\u2570\u256F\u254C\u2504\u2501\u2503]+$/;
|
|
107755
107900
|
const STATUS_BAR_LINE_RE = /^[\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827\u2807\u280F\u2713\u2717]\s+iter\s+\d+/;
|
|
@@ -107810,16 +107955,16 @@ var init_default2 = __esm(() => {
|
|
|
107810
107955
|
});
|
|
107811
107956
|
|
|
107812
107957
|
// apps/agent/src/agent/state/agent-run-state.ts
|
|
107813
|
-
import { basename as basename3, join as
|
|
107958
|
+
import { basename as basename3, join as join30 } from "path";
|
|
107814
107959
|
import { homedir as homedir6 } from "os";
|
|
107815
|
-
import { mkdir as
|
|
107960
|
+
import { mkdir as mkdir9, writeFile } from "fs/promises";
|
|
107816
107961
|
function agentRunStatePath(projectRoot) {
|
|
107817
|
-
return
|
|
107962
|
+
return join30(homedir6(), ".ralph", basename3(projectRoot), "agent-state.json");
|
|
107818
107963
|
}
|
|
107819
107964
|
async function writeAgentRunState(state) {
|
|
107820
107965
|
const path = agentRunStatePath(state.projectRoot);
|
|
107821
107966
|
try {
|
|
107822
|
-
await
|
|
107967
|
+
await mkdir9(join30(homedir6(), ".ralph", basename3(state.projectRoot)), { recursive: true });
|
|
107823
107968
|
await writeFile(path, JSON.stringify(state, null, 2) + `
|
|
107824
107969
|
`, "utf-8");
|
|
107825
107970
|
} catch {}
|
|
@@ -107845,18 +107990,18 @@ var CI_FAILED_EXIT2 = 70, PR_FAILED_EXIT2 = 71, NO_CHANGES_EXIT2 = 72;
|
|
|
107845
107990
|
|
|
107846
107991
|
// packages/retro/src/paths.ts
|
|
107847
107992
|
import { homedir as homedir7 } from "os";
|
|
107848
|
-
import { mkdir as
|
|
107849
|
-
import { join as
|
|
107993
|
+
import { mkdir as mkdir10 } from "fs/promises";
|
|
107994
|
+
import { join as join31 } from "path";
|
|
107850
107995
|
function retroDir() {
|
|
107851
|
-
return
|
|
107996
|
+
return join31(homedir7(), ".ralph", "retro");
|
|
107852
107997
|
}
|
|
107853
107998
|
async function resolveRetroOutputPath(identifier, date5, dir = retroDir()) {
|
|
107854
|
-
await
|
|
107855
|
-
const base2 =
|
|
107999
|
+
await mkdir10(dir, { recursive: true });
|
|
108000
|
+
const base2 = join31(dir, `${identifier}-${date5}.md`);
|
|
107856
108001
|
if (!await Bun.file(base2).exists())
|
|
107857
108002
|
return base2;
|
|
107858
108003
|
for (let n = 2;; n++) {
|
|
107859
|
-
const candidate =
|
|
108004
|
+
const candidate = join31(dir, `${identifier}-${date5}-${n}.md`);
|
|
107860
108005
|
if (!await Bun.file(candidate).exists())
|
|
107861
108006
|
return candidate;
|
|
107862
108007
|
}
|
|
@@ -107970,7 +108115,7 @@ var init_retro = __esm(() => {
|
|
|
107970
108115
|
});
|
|
107971
108116
|
|
|
107972
108117
|
// apps/agent/src/agent/wire/spawn/worker.ts
|
|
107973
|
-
import { join as
|
|
108118
|
+
import { join as join32 } from "path";
|
|
107974
108119
|
function localDateStamp(d) {
|
|
107975
108120
|
const y = d.getFullYear();
|
|
107976
108121
|
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
@@ -108099,7 +108244,7 @@ function createSpawnWorker(input) {
|
|
|
108099
108244
|
paths: {
|
|
108100
108245
|
changeDir: info.changeDir,
|
|
108101
108246
|
stateFilePath: info.stateFilePath,
|
|
108102
|
-
logFile:
|
|
108247
|
+
logFile: join32(logsDir, `${info.changeName}.log`),
|
|
108103
108248
|
jsonLogFile: args.jsonLogFile ?? null,
|
|
108104
108249
|
agentStateFile: agentRunStatePath(projectRoot)
|
|
108105
108250
|
}
|
|
@@ -108116,7 +108261,7 @@ function createSpawnWorker(input) {
|
|
|
108116
108261
|
return function spawnWorker(changeName, _issue, trigger) {
|
|
108117
108262
|
const cwd2 = cwdByChange.get(changeName) ?? projectRoot;
|
|
108118
108263
|
const injected = runners?.spawnWorker;
|
|
108119
|
-
const missionTasksPath =
|
|
108264
|
+
const missionTasksPath = join32(projectLayout(cwd2).changeDir(changeName), MISSION_TASKS_FILENAME);
|
|
108120
108265
|
const prevTasksPromise = (async () => {
|
|
108121
108266
|
const f2 = Bun.file(missionTasksPath);
|
|
108122
108267
|
return await f2.exists() ? await f2.text() : "";
|
|
@@ -108124,7 +108269,7 @@ function createSpawnWorker(input) {
|
|
|
108124
108269
|
let logFilePath;
|
|
108125
108270
|
let handle;
|
|
108126
108271
|
if (injected) {
|
|
108127
|
-
logFilePath =
|
|
108272
|
+
logFilePath = join32(logsDir, `${changeName}.log`);
|
|
108128
108273
|
handle = injected(buildTaskCmdFor(changeName), cwd2);
|
|
108129
108274
|
} else {
|
|
108130
108275
|
const r = defaultSpawn(changeName, buildTaskCmdFor(changeName), cwd2, logsDir, onWorkerOutput, `spawn at ${new Date().toISOString()}`);
|
|
@@ -108146,7 +108291,7 @@ function createSpawnWorker(input) {
|
|
|
108146
108291
|
const wantAutoMerge = issueForChange ? issueMatchesGetIndicator(issueForChange, indicators.getAutoMerge) : false;
|
|
108147
108292
|
const wrapped = handle.exited.then(async (code) => {
|
|
108148
108293
|
const workerLayout = projectLayout(cwd2);
|
|
108149
|
-
const validateSpecPath =
|
|
108294
|
+
const validateSpecPath = join32(workerLayout.changeDir(changeName), "specs", "validate.md");
|
|
108150
108295
|
const hasValidateSpec = await Bun.file(validateSpecPath).exists();
|
|
108151
108296
|
const wantValidateOnly = hasValidateSpec && !wantPrBase;
|
|
108152
108297
|
if (hasValidateSpec) {
|
|
@@ -108612,44 +108757,33 @@ var init_linear_sync = __esm(() => {
|
|
|
108612
108757
|
});
|
|
108613
108758
|
|
|
108614
108759
|
// apps/agent/src/agent/linear-sync/comment-sync.ts
|
|
108615
|
-
import { dirname as
|
|
108616
|
-
|
|
108617
|
-
async function readStateJson(statePath) {
|
|
108760
|
+
import { dirname as dirname12, join as join33 } from "path";
|
|
108761
|
+
async function readInlineLinearComments(statePath) {
|
|
108618
108762
|
const file2 = Bun.file(statePath);
|
|
108619
108763
|
if (!await file2.exists())
|
|
108620
|
-
return
|
|
108764
|
+
return;
|
|
108621
108765
|
try {
|
|
108622
|
-
|
|
108766
|
+
const obj = await file2.json();
|
|
108767
|
+
return obj.linearComments ?? undefined;
|
|
108623
108768
|
} catch {
|
|
108624
|
-
return
|
|
108625
|
-
}
|
|
108626
|
-
}
|
|
108627
|
-
async function writeStateJson(statePath, state) {
|
|
108628
|
-
await mkdir13(dirname13(statePath), { recursive: true });
|
|
108629
|
-
const tmp = `${statePath}.tmp-${process.pid}-${writeStateSeq++}`;
|
|
108630
|
-
try {
|
|
108631
|
-
await Bun.write(tmp, JSON.stringify(state, null, 2) + `
|
|
108632
|
-
`);
|
|
108633
|
-
await rename(tmp, statePath);
|
|
108634
|
-
} catch (err) {
|
|
108635
|
-
await unlink2(tmp).catch(() => {});
|
|
108636
|
-
throw err;
|
|
108769
|
+
return;
|
|
108637
108770
|
}
|
|
108638
108771
|
}
|
|
108639
|
-
function readComments(
|
|
108640
|
-
const
|
|
108772
|
+
async function readComments(statePath) {
|
|
108773
|
+
const changeDir = dirname12(statePath);
|
|
108774
|
+
const raw = await readSlotSidecar(changeDir, "linearComments") ?? await readInlineLinearComments(statePath) ?? {};
|
|
108775
|
+
const r = raw;
|
|
108641
108776
|
return {
|
|
108642
|
-
planCommentId:
|
|
108643
|
-
tasksCommentId:
|
|
108644
|
-
planPostedAt:
|
|
108645
|
-
tasksCommentSha256:
|
|
108777
|
+
planCommentId: r.planCommentId ?? null,
|
|
108778
|
+
tasksCommentId: r.tasksCommentId ?? null,
|
|
108779
|
+
planPostedAt: r.planPostedAt ?? null,
|
|
108780
|
+
tasksCommentSha256: r.tasksCommentSha256 ?? null
|
|
108646
108781
|
};
|
|
108647
108782
|
}
|
|
108648
108783
|
async function patchComments(statePath, patch) {
|
|
108649
|
-
const
|
|
108650
|
-
const current = readComments(existing);
|
|
108784
|
+
const current = await readComments(statePath);
|
|
108651
108785
|
const next = { ...current, ...patch };
|
|
108652
|
-
await
|
|
108786
|
+
await writeSlotField(dirname12(statePath), "linearComments", next);
|
|
108653
108787
|
}
|
|
108654
108788
|
function isCommentNotFoundError(err) {
|
|
108655
108789
|
if (!err)
|
|
@@ -108664,7 +108798,7 @@ function isCommentNotFoundError(err) {
|
|
|
108664
108798
|
return text.includes("not found") || text.includes("could not find") || text.includes("entity not found");
|
|
108665
108799
|
}
|
|
108666
108800
|
async function readTasksMd(changeDir, log3) {
|
|
108667
|
-
const file2 = Bun.file(
|
|
108801
|
+
const file2 = Bun.file(join33(changeDir, "tasks.md"));
|
|
108668
108802
|
if (!await file2.exists()) {
|
|
108669
108803
|
log3(` comment-sync: tasks.md missing in ${changeDir}, skipping`, "gray");
|
|
108670
108804
|
return null;
|
|
@@ -108685,8 +108819,7 @@ async function postOrUpdateTasksComment(deps) {
|
|
|
108685
108819
|
return null;
|
|
108686
108820
|
const body = renderTasksCommentBody(tasksMd, deps.changeName, deps.iteration);
|
|
108687
108821
|
const hash2 = sha256Hex(tasksMd);
|
|
108688
|
-
const
|
|
108689
|
-
const comments = readComments(state);
|
|
108822
|
+
const comments = await readComments(deps.statePath);
|
|
108690
108823
|
if (comments.tasksCommentId) {
|
|
108691
108824
|
if (comments.tasksCommentSha256 === hash2) {
|
|
108692
108825
|
deps.log(` comment-sync: tasks.md unchanged for ${deps.changeName}, skipping`, "gray");
|
|
@@ -108762,8 +108895,7 @@ async function readSection(path, heading) {
|
|
|
108762
108895
|
return body.trim() || null;
|
|
108763
108896
|
}
|
|
108764
108897
|
async function postPlanCommentOnce(deps) {
|
|
108765
|
-
const
|
|
108766
|
-
const comments = readComments(state);
|
|
108898
|
+
const comments = await readComments(deps.statePath);
|
|
108767
108899
|
if (comments.planCommentId)
|
|
108768
108900
|
return null;
|
|
108769
108901
|
const tasksMd = await readTasksMd(deps.changeDir, deps.log);
|
|
@@ -108772,14 +108904,14 @@ async function postPlanCommentOnce(deps) {
|
|
|
108772
108904
|
const check2 = parsePlanningSection(tasksMd);
|
|
108773
108905
|
if (!check2.allChecked)
|
|
108774
108906
|
return null;
|
|
108775
|
-
const proposalPath =
|
|
108907
|
+
const proposalPath = join33(deps.changeDir, "proposal.md");
|
|
108776
108908
|
const why = await readSection(proposalPath, "Why");
|
|
108777
108909
|
const whatChanges = await readSection(proposalPath, "What Changes");
|
|
108778
108910
|
if (!why && !whatChanges) {
|
|
108779
108911
|
deps.log(` comment-sync: proposal.md has no Why/What Changes, skipping plan comment`, "gray");
|
|
108780
108912
|
return null;
|
|
108781
108913
|
}
|
|
108782
|
-
const designSummary = await readFirstParagraph(
|
|
108914
|
+
const designSummary = await readFirstParagraph(join33(deps.changeDir, "design.md"));
|
|
108783
108915
|
const parts = [`### ${PLAN_COMMENT_TITLE} \u2014 \`${deps.changeName}\``];
|
|
108784
108916
|
if (why) {
|
|
108785
108917
|
parts.push("", "**Why**", "", why);
|
|
@@ -108817,8 +108949,7 @@ ${deps.message.trim()}`;
|
|
|
108817
108949
|
} catch (err) {
|
|
108818
108950
|
deps.log(`! comment-sync: steering comment create failed: ${err.message}`, "yellow");
|
|
108819
108951
|
}
|
|
108820
|
-
const
|
|
108821
|
-
const comments = readComments(state);
|
|
108952
|
+
const comments = await readComments(deps.statePath);
|
|
108822
108953
|
if (comments.tasksCommentId) {
|
|
108823
108954
|
try {
|
|
108824
108955
|
await deps.mutations.deleteIssueComment(deps.apiKey, comments.tasksCommentId);
|
|
@@ -108841,8 +108972,9 @@ ${deps.message.trim()}`;
|
|
|
108841
108972
|
iteration: deps.iteration
|
|
108842
108973
|
});
|
|
108843
108974
|
}
|
|
108844
|
-
var PLAN_COMMENT_TITLE = "\uD83D\uDCCB Ralph plan", STEERING_COMMENT_TITLE = "\uD83E\uDDED Ralph steering"
|
|
108975
|
+
var PLAN_COMMENT_TITLE = "\uD83D\uDCCB Ralph plan", STEERING_COMMENT_TITLE = "\uD83E\uDDED Ralph steering";
|
|
108845
108976
|
var init_comment_sync = __esm(() => {
|
|
108977
|
+
init_store();
|
|
108846
108978
|
init_linear_sync();
|
|
108847
108979
|
});
|
|
108848
108980
|
|
|
@@ -261055,7 +261187,7 @@ var init_render_pdf = __esm(() => {
|
|
|
261055
261187
|
});
|
|
261056
261188
|
|
|
261057
261189
|
// apps/agent/src/agent/linear-sync/spec-attachments.ts
|
|
261058
|
-
import { dirname as
|
|
261190
|
+
import { dirname as dirname13, join as join34 } from "path";
|
|
261059
261191
|
function describeLinearError(err) {
|
|
261060
261192
|
const e = err;
|
|
261061
261193
|
const parts = [e.message ?? String(err)];
|
|
@@ -261070,25 +261202,29 @@ function describeLinearError(err) {
|
|
|
261070
261202
|
return parts.join(" ");
|
|
261071
261203
|
}
|
|
261072
261204
|
function stateDirOf(statePath) {
|
|
261073
|
-
return
|
|
261205
|
+
return dirname13(statePath);
|
|
261074
261206
|
}
|
|
261075
|
-
async function
|
|
261207
|
+
async function readInlineSpecAttachments(statePath) {
|
|
261076
261208
|
const file2 = Bun.file(statePath);
|
|
261077
261209
|
if (!await file2.exists())
|
|
261078
261210
|
return {};
|
|
261079
261211
|
try {
|
|
261080
261212
|
const parsed = await file2.json();
|
|
261081
261213
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
261082
|
-
|
|
261214
|
+
const sa = parsed.specAttachments;
|
|
261215
|
+
return sa && typeof sa === "object" && !Array.isArray(sa) ? sa : {};
|
|
261083
261216
|
}
|
|
261084
261217
|
return {};
|
|
261085
261218
|
} catch {
|
|
261086
261219
|
return {};
|
|
261087
261220
|
}
|
|
261088
261221
|
}
|
|
261222
|
+
async function readSpecAttachmentsSubtree(statePath) {
|
|
261223
|
+
const sidecar = await readSlotSidecar(dirname13(statePath), "specAttachments");
|
|
261224
|
+
return sidecar ?? await readInlineSpecAttachments(statePath);
|
|
261225
|
+
}
|
|
261089
261226
|
async function readSpecAttachments(statePath) {
|
|
261090
|
-
const
|
|
261091
|
-
const sa = raw.specAttachments ?? {};
|
|
261227
|
+
const sa = await readSpecAttachmentsSubtree(statePath);
|
|
261092
261228
|
return {
|
|
261093
261229
|
proposal: {
|
|
261094
261230
|
attachmentId: sa.proposal?.attachmentId ?? null,
|
|
@@ -261142,12 +261278,25 @@ function hasMeaningfulContent(bytes) {
|
|
|
261142
261278
|
}
|
|
261143
261279
|
return false;
|
|
261144
261280
|
}
|
|
261281
|
+
function extractImplementationSection(tasksMarkdown) {
|
|
261282
|
+
const captured = [];
|
|
261283
|
+
let capturing = false;
|
|
261284
|
+
for (const line of tasksMarkdown.split(/\r?\n/)) {
|
|
261285
|
+
const heading = /^##\s+(.+?)\s*$/.exec(line)?.[1];
|
|
261286
|
+
if (heading !== undefined)
|
|
261287
|
+
capturing = heading.trim().toLowerCase() === "implementation";
|
|
261288
|
+
if (capturing)
|
|
261289
|
+
captured.push(line);
|
|
261290
|
+
}
|
|
261291
|
+
return captured.join(`
|
|
261292
|
+
`).trim();
|
|
261293
|
+
}
|
|
261145
261294
|
async function syncSlot(deps, slot) {
|
|
261146
261295
|
const spec = SLOT_SPECS[slot];
|
|
261147
261296
|
const [primaryName, ...trailingNames] = spec.sourceFiles;
|
|
261148
261297
|
if (!primaryName)
|
|
261149
261298
|
return;
|
|
261150
|
-
const primary = Bun.file(
|
|
261299
|
+
const primary = Bun.file(join34(deps.changeDir, primaryName));
|
|
261151
261300
|
if (!await primary.exists()) {
|
|
261152
261301
|
deps.log(` spec-attachments: ${primaryName} missing, skipping`, "gray");
|
|
261153
261302
|
return;
|
|
@@ -261166,24 +261315,28 @@ async function syncSlot(deps, slot) {
|
|
|
261166
261315
|
const parts = [primaryBytes];
|
|
261167
261316
|
const enc = new TextEncoder;
|
|
261168
261317
|
for (const name of trailingNames) {
|
|
261169
|
-
const f2 = Bun.file(
|
|
261318
|
+
const f2 = Bun.file(join34(deps.changeDir, name));
|
|
261170
261319
|
if (!await f2.exists())
|
|
261171
261320
|
continue;
|
|
261321
|
+
let raw;
|
|
261172
261322
|
try {
|
|
261173
|
-
|
|
261174
|
-
|
|
261175
|
-
|
|
261176
|
-
|
|
261323
|
+
raw = await f2.bytes();
|
|
261324
|
+
} catch (err) {
|
|
261325
|
+
deps.log(`! spec-attachments: read ${name} failed (continuing without it): ${err.message}`, "yellow");
|
|
261326
|
+
continue;
|
|
261327
|
+
}
|
|
261328
|
+
if (raw.length === 0)
|
|
261329
|
+
continue;
|
|
261330
|
+
const decoded = new TextDecoder().decode(raw);
|
|
261331
|
+
const body = name === "tasks.md" ? extractImplementationSection(decoded) : decoded.trim();
|
|
261332
|
+
if (!body)
|
|
261333
|
+
continue;
|
|
261334
|
+
parts.push(enc.encode(`
|
|
261177
261335
|
|
|
261178
261336
|
---
|
|
261179
261337
|
|
|
261180
|
-
|
|
261181
|
-
|
|
261338
|
+
${body}
|
|
261182
261339
|
`));
|
|
261183
|
-
parts.push(bytes);
|
|
261184
|
-
} catch (err) {
|
|
261185
|
-
deps.log(`! spec-attachments: read ${name} failed (continuing without it): ${err.message}`, "yellow");
|
|
261186
|
-
}
|
|
261187
261340
|
}
|
|
261188
261341
|
const totalLen = parts.reduce((n, p) => n + p.length, 0);
|
|
261189
261342
|
const sourceBytes = new Uint8Array(totalLen);
|
|
@@ -261252,8 +261405,7 @@ async function syncSlot(deps, slot) {
|
|
|
261252
261405
|
deps.log(` spec-attachments: created ${spec.uploadFilename} attachment`, "gray");
|
|
261253
261406
|
}
|
|
261254
261407
|
async function purgeLegacyProposalSlots(deps) {
|
|
261255
|
-
const
|
|
261256
|
-
const sa = raw.specAttachments ?? {};
|
|
261408
|
+
const sa = await readSpecAttachmentsSubtree(deps.statePath);
|
|
261257
261409
|
if (sa.legacyProposalPurged === true)
|
|
261258
261410
|
return;
|
|
261259
261411
|
const state = await readSpecAttachments(deps.statePath);
|
|
@@ -261431,9 +261583,9 @@ var init_comment_sync2 = __esm(() => {
|
|
|
261431
261583
|
});
|
|
261432
261584
|
|
|
261433
261585
|
// apps/agent/src/features/pr-tracker/state.ts
|
|
261434
|
-
import { join as
|
|
261586
|
+
import { join as join35 } from "path";
|
|
261435
261587
|
async function readState2(projectRoot) {
|
|
261436
|
-
const path =
|
|
261588
|
+
const path = join35(projectRoot, PR_TRACKER_STATE_RELPATH);
|
|
261437
261589
|
const file2 = Bun.file(path);
|
|
261438
261590
|
if (!await file2.exists())
|
|
261439
261591
|
return {};
|
|
@@ -261449,7 +261601,7 @@ async function readState2(projectRoot) {
|
|
|
261449
261601
|
}
|
|
261450
261602
|
}
|
|
261451
261603
|
async function writeState2(projectRoot, state) {
|
|
261452
|
-
const path =
|
|
261604
|
+
const path = join35(projectRoot, PR_TRACKER_STATE_RELPATH);
|
|
261453
261605
|
await Bun.write(path, JSON.stringify(state, null, 2));
|
|
261454
261606
|
}
|
|
261455
261607
|
var PR_TRACKER_STATE_RELPATH = ".ralph/pr-tracker-state.json";
|
|
@@ -261528,7 +261680,7 @@ var init_pr_tracker = __esm(() => {
|
|
|
261528
261680
|
});
|
|
261529
261681
|
|
|
261530
261682
|
// apps/agent/src/agent/wire.ts
|
|
261531
|
-
import { join as
|
|
261683
|
+
import { join as join36 } from "path";
|
|
261532
261684
|
function buildAgentCoordinator(input) {
|
|
261533
261685
|
const {
|
|
261534
261686
|
args,
|
|
@@ -261547,7 +261699,7 @@ function buildAgentCoordinator(input) {
|
|
|
261547
261699
|
onWorkerCmd,
|
|
261548
261700
|
onAwaitingTicket
|
|
261549
261701
|
} = input;
|
|
261550
|
-
const logsDir =
|
|
261702
|
+
const logsDir = join36(projectRoot, ".ralph", "logs");
|
|
261551
261703
|
const bus = createBus();
|
|
261552
261704
|
subscribeAgentDiag(bus, onLog);
|
|
261553
261705
|
const diag = (area, message, color) => {
|
|
@@ -261682,6 +261834,13 @@ function buildAgentCoordinator(input) {
|
|
|
261682
261834
|
...onWorkerOutput ? { onWorkerOutput } : {},
|
|
261683
261835
|
...onWorkerCmd ? { onWorkerCmd } : {}
|
|
261684
261836
|
});
|
|
261837
|
+
const openDraftPr = createOpenDraftPr({
|
|
261838
|
+
branchByChange,
|
|
261839
|
+
prByChange,
|
|
261840
|
+
cmdRunner,
|
|
261841
|
+
prBaseBranch: cfg.prBaseBranch,
|
|
261842
|
+
invalidatePrUrlForIssue: (issueId) => prDiscovery.invalidatePrUrlForIssue(issueId)
|
|
261843
|
+
});
|
|
261685
261844
|
const confirmationCaps = {
|
|
261686
261845
|
detect: (issue2) => processAwaitingForIssue(issue2, {
|
|
261687
261846
|
cfg,
|
|
@@ -261694,6 +261853,7 @@ function buildAgentCoordinator(input) {
|
|
|
261694
261853
|
reapForAwaiting: (cn) => coordRef.current?.reapForAwaiting(cn),
|
|
261695
261854
|
applyIndicator: resolvers.applyIndicator,
|
|
261696
261855
|
applyMarker: resolvers.applyMarker,
|
|
261856
|
+
openDraftPr,
|
|
261697
261857
|
...onAwaitingTicket ? { onAwaitingTicket } : {},
|
|
261698
261858
|
onLog
|
|
261699
261859
|
}),
|
|
@@ -261768,7 +261928,7 @@ function buildAgentCoordinator(input) {
|
|
|
261768
261928
|
const changeDir = projectLayout(root).changeDir(changeName);
|
|
261769
261929
|
const parts = [];
|
|
261770
261930
|
for (const name of ["tasks.md", "proposal.md", "design.md"]) {
|
|
261771
|
-
const file2 = Bun.file(
|
|
261931
|
+
const file2 = Bun.file(join36(changeDir, name));
|
|
261772
261932
|
if (!await file2.exists())
|
|
261773
261933
|
continue;
|
|
261774
261934
|
parts.push(`${name}:${file2.lastModified}:${file2.size}`);
|
|
@@ -261813,7 +261973,7 @@ function buildAgentCoordinator(input) {
|
|
|
261813
261973
|
getGaveUpTotal: async () => {
|
|
261814
261974
|
let total = 0;
|
|
261815
261975
|
for (const [changeName, root] of cwdByChange) {
|
|
261816
|
-
const file2 = Bun.file(
|
|
261976
|
+
const file2 = Bun.file(join36(projectLayout(root).taskStateDir(changeName), GAVEUP_COUNT_FILE));
|
|
261817
261977
|
if (!await file2.exists())
|
|
261818
261978
|
continue;
|
|
261819
261979
|
try {
|
|
@@ -261848,14 +262008,14 @@ var init_wire = __esm(() => {
|
|
|
261848
262008
|
});
|
|
261849
262009
|
|
|
261850
262010
|
// apps/agent/src/agent/json-log/json-log-file.ts
|
|
261851
|
-
import { mkdir as
|
|
261852
|
-
import { dirname as
|
|
262011
|
+
import { mkdir as mkdir11, appendFile as appendFile2 } from "fs/promises";
|
|
262012
|
+
import { dirname as dirname14 } from "path";
|
|
261853
262013
|
function createJsonLogFileSink(path) {
|
|
261854
262014
|
if (!path)
|
|
261855
262015
|
return { emit: () => {} };
|
|
261856
262016
|
let chain = (async () => {
|
|
261857
262017
|
try {
|
|
261858
|
-
await
|
|
262018
|
+
await mkdir11(dirname14(path), { recursive: true });
|
|
261859
262019
|
await Bun.write(path, "");
|
|
261860
262020
|
} catch {}
|
|
261861
262021
|
})();
|
|
@@ -262101,7 +262261,7 @@ var init_output_utils = __esm(() => {
|
|
|
262101
262261
|
});
|
|
262102
262262
|
|
|
262103
262263
|
// apps/agent/src/agent/state/worker-state-poll.ts
|
|
262104
|
-
import { join as
|
|
262264
|
+
import { join as join37 } from "path";
|
|
262105
262265
|
function parseSubtasks(tasksMd) {
|
|
262106
262266
|
const out = [];
|
|
262107
262267
|
let skipSection = false;
|
|
@@ -262134,7 +262294,7 @@ function initialWorkerSnapshot() {
|
|
|
262134
262294
|
async function readWorkerSnapshot(input) {
|
|
262135
262295
|
const next = { ...input.prev };
|
|
262136
262296
|
try {
|
|
262137
|
-
const file2 = Bun.file(
|
|
262297
|
+
const file2 = Bun.file(join37(input.statesDir, input.changeName, ".ralph-state.json"));
|
|
262138
262298
|
if (await file2.exists()) {
|
|
262139
262299
|
const json2 = await file2.json();
|
|
262140
262300
|
next.iter = json2.iteration ?? next.iter;
|
|
@@ -262143,10 +262303,10 @@ async function readWorkerSnapshot(input) {
|
|
|
262143
262303
|
} catch {}
|
|
262144
262304
|
if (input.changeDir) {
|
|
262145
262305
|
try {
|
|
262146
|
-
const tasksFile = Bun.file(
|
|
262147
|
-
const proposalFile = Bun.file(
|
|
262148
|
-
const designFile = Bun.file(
|
|
262149
|
-
const reviewFindingsFile = Bun.file(
|
|
262306
|
+
const tasksFile = Bun.file(join37(input.changeDir, "tasks.md"));
|
|
262307
|
+
const proposalFile = Bun.file(join37(input.changeDir, "proposal.md"));
|
|
262308
|
+
const designFile = Bun.file(join37(input.changeDir, "design.md"));
|
|
262309
|
+
const reviewFindingsFile = Bun.file(join37(input.changeDir, "review-findings.md"));
|
|
262150
262310
|
const [tasksText, proposalText, designText, reviewFindingsText] = await Promise.all([
|
|
262151
262311
|
tasksFile.exists().then((ok) => ok ? tasksFile.text() : null),
|
|
262152
262312
|
proposalFile.exists().then((ok) => ok ? proposalFile.text() : null),
|
|
@@ -262209,7 +262369,7 @@ var init_worker_state_poll = __esm(() => {
|
|
|
262209
262369
|
});
|
|
262210
262370
|
|
|
262211
262371
|
// apps/agent/src/components/AgentMode.tsx
|
|
262212
|
-
import { join as
|
|
262372
|
+
import { join as join38 } from "path";
|
|
262213
262373
|
async function appendSteeringImpl(changeDir, message) {
|
|
262214
262374
|
await runWithContext(createDefaultContext(), async () => {
|
|
262215
262375
|
appendSteeringMessage(changeDir, message);
|
|
@@ -263782,7 +263942,7 @@ function AgentMode({
|
|
|
263782
263942
|
},
|
|
263783
263943
|
onSubmit: async (message) => {
|
|
263784
263944
|
try {
|
|
263785
|
-
await appendSteering2(
|
|
263945
|
+
await appendSteering2(join38(tasksDir, w2.changeName), message);
|
|
263786
263946
|
fileEmit({ type: "steering_submitted", changeName: w2.changeName, message });
|
|
263787
263947
|
} catch (err) {
|
|
263788
263948
|
const text = err.message;
|
|
@@ -264086,7 +264246,7 @@ __export(exports_list, {
|
|
|
264086
264246
|
buildBuckets: () => buildBuckets,
|
|
264087
264247
|
backlogRankByIssueId: () => backlogRankByIssueId
|
|
264088
264248
|
});
|
|
264089
|
-
import { join as
|
|
264249
|
+
import { join as join39 } from "path";
|
|
264090
264250
|
function countTaskItems(content) {
|
|
264091
264251
|
const checked = (content.match(/^- \[x\]/gm) ?? []).length;
|
|
264092
264252
|
const unchecked = (content.match(/^- \[ \]/gm) ?? []).length;
|
|
@@ -264102,13 +264262,13 @@ function buildLocalRows() {
|
|
|
264102
264262
|
const sources = [{ dir: statesDir, label: "main" }];
|
|
264103
264263
|
const worktreesRoot = worktreesDir2(projectRoot);
|
|
264104
264264
|
for (const wt of storage.list(worktreesRoot)) {
|
|
264105
|
-
sources.push({ dir:
|
|
264265
|
+
sources.push({ dir: join39(worktreesRoot, wt, ".ralph", "tasks"), label: `wt:${wt}` });
|
|
264106
264266
|
}
|
|
264107
264267
|
for (const { dir, label } of sources) {
|
|
264108
264268
|
for (const entry of storage.list(dir)) {
|
|
264109
264269
|
if (seen.has(entry))
|
|
264110
264270
|
continue;
|
|
264111
|
-
const raw = storage.read(
|
|
264271
|
+
const raw = storage.read(join39(dir, entry, ".ralph-state.json"));
|
|
264112
264272
|
if (raw === null)
|
|
264113
264273
|
continue;
|
|
264114
264274
|
let state;
|
|
@@ -264123,7 +264283,7 @@ function buildLocalRows() {
|
|
|
264123
264283
|
const firstLine = promptRaw.split(`
|
|
264124
264284
|
`).find((l3) => l3.trim() !== "") ?? "";
|
|
264125
264285
|
let progress = "\u2014";
|
|
264126
|
-
const tasksContent = storage.read(
|
|
264286
|
+
const tasksContent = storage.read(join39(dir, entry, "tasks.md"));
|
|
264127
264287
|
if (tasksContent !== null) {
|
|
264128
264288
|
const { checked, unchecked } = countTaskItems(tasksContent);
|
|
264129
264289
|
const total = checked + unchecked;
|
|
@@ -264623,8 +264783,8 @@ var exports_json_runner = {};
|
|
|
264623
264783
|
__export(exports_json_runner, {
|
|
264624
264784
|
runAgentJson: () => runAgentJson
|
|
264625
264785
|
});
|
|
264626
|
-
import { join as
|
|
264627
|
-
import { mkdir as
|
|
264786
|
+
import { join as join40 } from "path";
|
|
264787
|
+
import { mkdir as mkdir12 } from "fs/promises";
|
|
264628
264788
|
import { homedir as homedir8 } from "os";
|
|
264629
264789
|
function makeEmit(fileSink) {
|
|
264630
264790
|
return (event) => {
|
|
@@ -264645,7 +264805,7 @@ async function runAgentJson({
|
|
|
264645
264805
|
tasksDir,
|
|
264646
264806
|
runPreflight: runPreflight2 = runPreflight
|
|
264647
264807
|
}) {
|
|
264648
|
-
await
|
|
264808
|
+
await mkdir12(join40(homedir8(), ".ralph"), { recursive: true }).catch(() => {
|
|
264649
264809
|
return;
|
|
264650
264810
|
});
|
|
264651
264811
|
const fileSink = createJsonLogFileSink(args.jsonLogFile);
|
|
@@ -264861,8 +265021,8 @@ var exports_src3 = {};
|
|
|
264861
265021
|
__export(exports_src3, {
|
|
264862
265022
|
main: () => main3
|
|
264863
265023
|
});
|
|
264864
|
-
import { mkdir as
|
|
264865
|
-
import { join as
|
|
265024
|
+
import { mkdir as mkdir13 } from "fs/promises";
|
|
265025
|
+
import { join as join41 } from "path";
|
|
264866
265026
|
async function main3(argv) {
|
|
264867
265027
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
264868
265028
|
printAgentHelp();
|
|
@@ -264926,9 +265086,9 @@ async function main3(argv) {
|
|
|
264926
265086
|
return 1;
|
|
264927
265087
|
}
|
|
264928
265088
|
}
|
|
264929
|
-
await
|
|
264930
|
-
await
|
|
264931
|
-
await
|
|
265089
|
+
await mkdir13(statesDir, { recursive: true });
|
|
265090
|
+
await mkdir13(tasksDir, { recursive: true });
|
|
265091
|
+
await mkdir13(join41(projectRoot, ".ralph"), { recursive: true });
|
|
264932
265092
|
if (shouldFallbackToJsonOutput(args, process.stdin.isTTY)) {
|
|
264933
265093
|
process.stderr.write(`agent: stdin is not a TTY \u2014 falling back to --json-output mode.
|
|
264934
265094
|
`);
|