@neriros/ralphy 3.10.6 → 3.10.8
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 +493 -281
- 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.8")
|
|
18932
|
+
return "3.10.8";
|
|
18933
18933
|
} catch {}
|
|
18934
18934
|
const dirsToTry = [];
|
|
18935
18935
|
try {
|
|
@@ -80519,7 +80519,7 @@ function foldLegacyAssignee(v) {
|
|
|
80519
80519
|
}
|
|
80520
80520
|
return rest2;
|
|
80521
80521
|
}
|
|
80522
|
-
var CURRENT_WORKFLOW_VERSION =
|
|
80522
|
+
var CURRENT_WORKFLOW_VERSION = 4, MarkerSchema, SET_INDICATOR_KEYS, GetIndicatorSchema, SetIndicatorSchema, IndicatorsSchema, ProjectSchema, CommandsSchema, DEFAULT_META_ONLY_FILES, BoundariesSchema, WorkflowConfigSchema;
|
|
80523
80523
|
var init_schema = __esm(() => {
|
|
80524
80524
|
init_zod();
|
|
80525
80525
|
MarkerSchema = exports_external.discriminatedUnion("type", [
|
|
@@ -80533,7 +80533,13 @@ var init_schema = __esm(() => {
|
|
|
80533
80533
|
exports_external.object({ type: exports_external.literal("project"), value: exports_external.string().min(1) }).strict(),
|
|
80534
80534
|
exports_external.object({ type: exports_external.literal("comment"), value: exports_external.string().min(1) }).strict()
|
|
80535
80535
|
]);
|
|
80536
|
-
SET_INDICATOR_KEYS = [
|
|
80536
|
+
SET_INDICATOR_KEYS = [
|
|
80537
|
+
"setInProgress",
|
|
80538
|
+
"setDone",
|
|
80539
|
+
"setPrReady",
|
|
80540
|
+
"setError",
|
|
80541
|
+
"clearApproved"
|
|
80542
|
+
];
|
|
80537
80543
|
GetIndicatorSchema = exports_external.object({
|
|
80538
80544
|
filter: exports_external.array(MarkerSchema).default([])
|
|
80539
80545
|
});
|
|
@@ -80547,6 +80553,7 @@ var init_schema = __esm(() => {
|
|
|
80547
80553
|
getAutoApprove: GetIndicatorSchema.optional(),
|
|
80548
80554
|
setInProgress: SetIndicatorSchema.optional(),
|
|
80549
80555
|
setDone: SetIndicatorSchema.optional(),
|
|
80556
|
+
setPrReady: SetIndicatorSchema.optional(),
|
|
80550
80557
|
setError: SetIndicatorSchema.optional(),
|
|
80551
80558
|
setAwaitingConfirmation: SetIndicatorSchema.optional(),
|
|
80552
80559
|
clearApproved: SetIndicatorSchema.optional(),
|
|
@@ -80851,6 +80858,9 @@ linear:
|
|
|
80851
80858
|
# setDone: # status/label to set when the PR is opened
|
|
80852
80859
|
# type: status
|
|
80853
80860
|
# value: In Review
|
|
80861
|
+
# setPrReady: # additive: marker set when the PR is ready for human review
|
|
80862
|
+
# type: status # (fires unless the PR is auto-merged immediately; does not replace setDone)
|
|
80863
|
+
# value: In Review
|
|
80854
80864
|
# setError: # label applied when a task is quarantined
|
|
80855
80865
|
# type: label
|
|
80856
80866
|
# value: "ralph:error"
|
|
@@ -81072,7 +81082,7 @@ function modelOptionValues() {
|
|
|
81072
81082
|
const field = findField("model");
|
|
81073
81083
|
return field && field.spec.kind === "select" ? field.spec.options.map((o) => o.value) : [];
|
|
81074
81084
|
}
|
|
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;
|
|
81085
|
+
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
81086
|
var init_fields = __esm(() => {
|
|
81077
81087
|
PROJECT_NAME = {
|
|
81078
81088
|
id: "project.name",
|
|
@@ -81436,6 +81446,14 @@ var init_fields = __esm(() => {
|
|
|
81436
81446
|
spec: { kind: "number", placeholder: "3" },
|
|
81437
81447
|
when: isOn("linear.confirmationMode.enabled")
|
|
81438
81448
|
},
|
|
81449
|
+
{
|
|
81450
|
+
id: AWAITING_STATUS_FIELD_ID,
|
|
81451
|
+
label: "Park awaiting-approval tickets in a status?",
|
|
81452
|
+
hint: "e.g. Planned \u2014 blank keeps them In Progress",
|
|
81453
|
+
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.",
|
|
81454
|
+
spec: { kind: "text", placeholder: "Planned" },
|
|
81455
|
+
when: isOn("linear.confirmationMode.enabled")
|
|
81456
|
+
},
|
|
81439
81457
|
{
|
|
81440
81458
|
id: "linear.indicators",
|
|
81441
81459
|
label: "Linear lifecycle indicators",
|
|
@@ -82908,6 +82926,20 @@ function buildFromAnswers(mode, answers, build = buildWorkflowMarkdown) {
|
|
|
82908
82926
|
values2["linear.indicators"] = map3;
|
|
82909
82927
|
}
|
|
82910
82928
|
}
|
|
82929
|
+
const parkStatusRaw = values2[AWAITING_STATUS_FIELD_ID];
|
|
82930
|
+
const parkStatus = typeof parkStatusRaw === "string" ? parkStatusRaw.trim() : "";
|
|
82931
|
+
if (values2["linear.confirmationMode.enabled"] === true && parkStatus && values2["linear.indicators"] && typeof values2["linear.indicators"] === "object") {
|
|
82932
|
+
const map3 = { ...values2["linear.indicators"] };
|
|
82933
|
+
map3.setAwaitingConfirmation = { type: "status", value: parkStatus };
|
|
82934
|
+
const existing = map3.getInProgress;
|
|
82935
|
+
const filter2 = existing && !Array.isArray(existing) && "filter" in existing ? [...existing.filter] : [];
|
|
82936
|
+
if (!filter2.some((m) => m.type === "status" && m.value === parkStatus)) {
|
|
82937
|
+
filter2.push({ type: "status", value: parkStatus });
|
|
82938
|
+
}
|
|
82939
|
+
map3.getInProgress = { filter: filter2 };
|
|
82940
|
+
values2["linear.indicators"] = map3;
|
|
82941
|
+
}
|
|
82942
|
+
delete values2[AWAITING_STATUS_FIELD_ID];
|
|
82911
82943
|
const linkRepo = values2[REPO_LINK_FIELD_ID] === true;
|
|
82912
82944
|
delete values2[REPO_LINK_FIELD_ID];
|
|
82913
82945
|
if (!linkRepo) {
|
|
@@ -83931,6 +83963,12 @@ var init_SetupWizard = __esm(async () => {
|
|
|
83931
83963
|
description: "Set when the task finishes and its pull request is opened.",
|
|
83932
83964
|
slots: ["setDone"]
|
|
83933
83965
|
},
|
|
83966
|
+
{
|
|
83967
|
+
key: "prReady",
|
|
83968
|
+
label: "PR ready",
|
|
83969
|
+
description: "Optional, additive: set when the PR is marked ready for human review (non-draft), layered on top of Done. Skipped only on the immediate non-draft auto-merge path.",
|
|
83970
|
+
slots: ["setPrReady"]
|
|
83971
|
+
},
|
|
83934
83972
|
{
|
|
83935
83973
|
key: "error",
|
|
83936
83974
|
label: "Error",
|
|
@@ -84019,6 +84057,11 @@ var init_migrations = __esm(() => {
|
|
|
84019
84057
|
version: 3,
|
|
84020
84058
|
description: "The per-workflow `linear.assignee` setting is replaced by a global " + "`linear.filter` expression (e.g. `assignee = me`) applied to every " + "ticket fetch. Existing `assignee` values are folded in automatically; " + "note that an empty filter now defaults to `assignee = me` (it previously " + "meant unassigned-only).",
|
|
84021
84059
|
fields: ["linear.filter"]
|
|
84060
|
+
},
|
|
84061
|
+
{
|
|
84062
|
+
version: 4,
|
|
84063
|
+
description: "A new additive `setPrReady` Linear indicator marks a ticket the moment its " + "PR is human-mergeable (ready, non-draft), layered on top of `setDone`. " + "Re-run the indicator builder to add it, or keep your current indicators.",
|
|
84064
|
+
fields: ["linear.indicators"]
|
|
84022
84065
|
}
|
|
84023
84066
|
];
|
|
84024
84067
|
LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max2, migration) => Math.max(max2, migration.version), 0);
|
|
@@ -85044,6 +85087,89 @@ var init_schema2 = __esm(() => {
|
|
|
85044
85087
|
ALL_OWNED_SLOTS = new Set(Object.values(OWNERSHIP).flatMap((slots) => [...slots]));
|
|
85045
85088
|
});
|
|
85046
85089
|
|
|
85090
|
+
// packages/core/src/state/sidecar.ts
|
|
85091
|
+
import { dirname as dirname5, join as join9 } from "path";
|
|
85092
|
+
import { mkdir as mkdir3, rename, unlink } from "fs/promises";
|
|
85093
|
+
function slotSidecarPath(changeDir, slot) {
|
|
85094
|
+
return join9(changeDir, `${CORE_STATE_FILE.replace(/\.json$/, "")}.${slot}.json`);
|
|
85095
|
+
}
|
|
85096
|
+
function parseObject(text) {
|
|
85097
|
+
if (text === null)
|
|
85098
|
+
return null;
|
|
85099
|
+
try {
|
|
85100
|
+
const parsed = JSON.parse(text);
|
|
85101
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
85102
|
+
return parsed;
|
|
85103
|
+
}
|
|
85104
|
+
return null;
|
|
85105
|
+
} catch {
|
|
85106
|
+
return null;
|
|
85107
|
+
}
|
|
85108
|
+
}
|
|
85109
|
+
function deepSet(target, path, value) {
|
|
85110
|
+
if (path === "") {
|
|
85111
|
+
for (const k of Object.keys(target))
|
|
85112
|
+
delete target[k];
|
|
85113
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
85114
|
+
Object.assign(target, value);
|
|
85115
|
+
}
|
|
85116
|
+
return;
|
|
85117
|
+
}
|
|
85118
|
+
const segments = path.split(".");
|
|
85119
|
+
let cursor = target;
|
|
85120
|
+
for (let i = 0;i < segments.length - 1; i++) {
|
|
85121
|
+
const key = segments[i];
|
|
85122
|
+
const existing = cursor[key];
|
|
85123
|
+
if (existing === null || typeof existing !== "object" || Array.isArray(existing)) {
|
|
85124
|
+
const next = {};
|
|
85125
|
+
cursor[key] = next;
|
|
85126
|
+
cursor = next;
|
|
85127
|
+
} else {
|
|
85128
|
+
cursor = existing;
|
|
85129
|
+
}
|
|
85130
|
+
}
|
|
85131
|
+
cursor[segments[segments.length - 1]] = value;
|
|
85132
|
+
}
|
|
85133
|
+
async function atomicWrite(path, content) {
|
|
85134
|
+
await mkdir3(dirname5(path), { recursive: true });
|
|
85135
|
+
const tmp = `${path}.tmp-${process.pid}-${writeSeq++}`;
|
|
85136
|
+
try {
|
|
85137
|
+
await Bun.write(tmp, content);
|
|
85138
|
+
await rename(tmp, path);
|
|
85139
|
+
} catch (err) {
|
|
85140
|
+
await unlink(tmp).catch(() => {});
|
|
85141
|
+
throw err;
|
|
85142
|
+
}
|
|
85143
|
+
}
|
|
85144
|
+
async function readSlotSidecar(changeDir, slot) {
|
|
85145
|
+
const file2 = Bun.file(slotSidecarPath(changeDir, slot));
|
|
85146
|
+
if (!await file2.exists())
|
|
85147
|
+
return;
|
|
85148
|
+
const obj = parseObject(await file2.text().catch(() => null));
|
|
85149
|
+
return obj ?? undefined;
|
|
85150
|
+
}
|
|
85151
|
+
async function writeSlotField(changeDir, path, value, seedInline) {
|
|
85152
|
+
const [slot, ...rest2] = path.split(".");
|
|
85153
|
+
const sidecarPath = slotSidecarPath(changeDir, slot);
|
|
85154
|
+
const existing = parseObject(await Bun.file(sidecarPath).text().catch(() => null));
|
|
85155
|
+
const obj = existing ?? (seedInline ? structuredClone(seedInline) : {});
|
|
85156
|
+
deepSet(obj, rest2.join("."), value);
|
|
85157
|
+
await atomicWrite(sidecarPath, JSON.stringify(obj, null, 2) + `
|
|
85158
|
+
`);
|
|
85159
|
+
}
|
|
85160
|
+
function overlaySidecarsSync(changeDir, target, read) {
|
|
85161
|
+
for (const slot of ALL_OWNED_SLOTS) {
|
|
85162
|
+
const obj = parseObject(read(slotSidecarPath(changeDir, slot)));
|
|
85163
|
+
if (obj !== undefined && obj !== null)
|
|
85164
|
+
target[slot] = obj;
|
|
85165
|
+
}
|
|
85166
|
+
return target;
|
|
85167
|
+
}
|
|
85168
|
+
var CORE_STATE_FILE = ".ralph-state.json", writeSeq = 0;
|
|
85169
|
+
var init_sidecar = __esm(() => {
|
|
85170
|
+
init_schema2();
|
|
85171
|
+
});
|
|
85172
|
+
|
|
85047
85173
|
// node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/util.js
|
|
85048
85174
|
var util, objectUtil, ZodParsedType, getParsedType2 = (data) => {
|
|
85049
85175
|
const t = typeof data;
|
|
@@ -89147,16 +89273,24 @@ function formatTaskName(name) {
|
|
|
89147
89273
|
}
|
|
89148
89274
|
|
|
89149
89275
|
// packages/core/src/state.ts
|
|
89150
|
-
import { join as
|
|
89276
|
+
import { join as join10 } from "path";
|
|
89277
|
+
function stripOwnedSlots(state) {
|
|
89278
|
+
const out = { ...state };
|
|
89279
|
+
for (const slot of ALL_OWNED_SLOTS)
|
|
89280
|
+
delete out[slot];
|
|
89281
|
+
return out;
|
|
89282
|
+
}
|
|
89151
89283
|
function readState(changeDir) {
|
|
89152
|
-
const filePath =
|
|
89284
|
+
const filePath = join10(changeDir, STATE_FILE2);
|
|
89153
89285
|
const raw = getStorage().read(filePath);
|
|
89154
89286
|
if (raw === null)
|
|
89155
89287
|
throw new Error(".ralph-state.json not found");
|
|
89156
|
-
|
|
89288
|
+
const base2 = JSON.parse(raw);
|
|
89289
|
+
overlaySidecarsSync(changeDir, base2, (p) => getStorage().read(p));
|
|
89290
|
+
return StateSchema.parse(base2);
|
|
89157
89291
|
}
|
|
89158
89292
|
function tryReadStateRaw(changeDir) {
|
|
89159
|
-
const filePath =
|
|
89293
|
+
const filePath = join10(changeDir, STATE_FILE2);
|
|
89160
89294
|
const text = getStorage().read(filePath);
|
|
89161
89295
|
if (text === null)
|
|
89162
89296
|
return { state: null, raw: null };
|
|
@@ -89167,12 +89301,14 @@ function tryReadStateRaw(changeDir) {
|
|
|
89167
89301
|
return { state: null, raw: null };
|
|
89168
89302
|
}
|
|
89169
89303
|
const raw = parsed && typeof parsed === "object" ? parsed : {};
|
|
89170
|
-
|
|
89304
|
+
overlaySidecarsSync(changeDir, raw, (p) => getStorage().read(p));
|
|
89305
|
+
const result2 = StateSchema.safeParse(raw);
|
|
89171
89306
|
return { state: result2.success ? result2.data : null, raw };
|
|
89172
89307
|
}
|
|
89173
89308
|
function writeState(changeDir, state) {
|
|
89174
|
-
const filePath =
|
|
89175
|
-
|
|
89309
|
+
const filePath = join10(changeDir, STATE_FILE2);
|
|
89310
|
+
const core2 = stripOwnedSlots(state);
|
|
89311
|
+
getStorage().write(filePath, JSON.stringify(core2, null, 2) + `
|
|
89176
89312
|
`);
|
|
89177
89313
|
}
|
|
89178
89314
|
function updateState(changeDir, updater) {
|
|
@@ -89211,7 +89347,7 @@ function buildInitialState(options) {
|
|
|
89211
89347
|
});
|
|
89212
89348
|
}
|
|
89213
89349
|
function ensureState(changeDir) {
|
|
89214
|
-
const filePath =
|
|
89350
|
+
const filePath = join10(changeDir, STATE_FILE2);
|
|
89215
89351
|
const storage = getStorage();
|
|
89216
89352
|
if (storage.read(filePath) !== null) {
|
|
89217
89353
|
return readState(changeDir);
|
|
@@ -89225,11 +89361,12 @@ var STATE_FILE2 = ".ralph-state.json";
|
|
|
89225
89361
|
var init_state = __esm(() => {
|
|
89226
89362
|
init_types2();
|
|
89227
89363
|
init_context();
|
|
89364
|
+
init_schema2();
|
|
89365
|
+
init_sidecar();
|
|
89228
89366
|
});
|
|
89229
89367
|
|
|
89230
89368
|
// packages/core/src/state/store.ts
|
|
89231
|
-
import {
|
|
89232
|
-
import { mkdir as mkdir3 } from "fs/promises";
|
|
89369
|
+
import { join as join11 } from "path";
|
|
89233
89370
|
async function readJson(filePath) {
|
|
89234
89371
|
const file2 = Bun.file(filePath);
|
|
89235
89372
|
if (!await file2.exists())
|
|
@@ -89244,22 +89381,6 @@ async function readJson(filePath) {
|
|
|
89244
89381
|
return {};
|
|
89245
89382
|
}
|
|
89246
89383
|
}
|
|
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
89384
|
async function writeField(changeDir, featureName, path, value) {
|
|
89264
89385
|
const allowed = OWNERSHIP[featureName];
|
|
89265
89386
|
if (!allowed) {
|
|
@@ -89269,16 +89390,15 @@ async function writeField(changeDir, featureName, path, value) {
|
|
|
89269
89390
|
if (!allowed.includes(topSlot)) {
|
|
89270
89391
|
throw new OwnershipError(featureName, path, `feature '${featureName}' may not write '${path}' (owns ${allowed.join(", ")})`);
|
|
89271
89392
|
}
|
|
89272
|
-
const
|
|
89273
|
-
const
|
|
89274
|
-
|
|
89275
|
-
await mkdir3(dirname5(filePath), { recursive: true });
|
|
89276
|
-
await Bun.write(filePath, JSON.stringify(existing, null, 2) + `
|
|
89277
|
-
`);
|
|
89393
|
+
const inline = (await readJson(join11(changeDir, STATE_FILE3)))[topSlot];
|
|
89394
|
+
const seed = inline && typeof inline === "object" && !Array.isArray(inline) ? inline : undefined;
|
|
89395
|
+
await writeSlotField(changeDir, path, value, seed);
|
|
89278
89396
|
}
|
|
89279
89397
|
var STATE_FILE3 = ".ralph-state.json", OwnershipError;
|
|
89280
89398
|
var init_store = __esm(() => {
|
|
89281
89399
|
init_schema2();
|
|
89400
|
+
init_sidecar();
|
|
89401
|
+
init_sidecar();
|
|
89282
89402
|
init_state();
|
|
89283
89403
|
OwnershipError = class OwnershipError extends Error {
|
|
89284
89404
|
featureName;
|
|
@@ -89293,14 +89413,14 @@ var init_store = __esm(() => {
|
|
|
89293
89413
|
});
|
|
89294
89414
|
|
|
89295
89415
|
// apps/loop/src/components/TaskStatus.tsx
|
|
89296
|
-
import { join as
|
|
89416
|
+
import { join as join12 } from "path";
|
|
89297
89417
|
function TaskStatus({ state, stateDir }) {
|
|
89298
89418
|
const storage = getStorage();
|
|
89299
89419
|
const cost = Math.round(state.usage.total_cost_usd * 100) / 100;
|
|
89300
89420
|
const time3 = Math.round(state.usage.total_duration_ms / 1000 * 10) / 10 + "s";
|
|
89301
89421
|
const artifacts = OPENSPEC_ARTIFACTS.map((name) => ({
|
|
89302
89422
|
name,
|
|
89303
|
-
exists: storage.read(
|
|
89423
|
+
exists: storage.read(join12(stateDir, name)) !== null
|
|
89304
89424
|
}));
|
|
89305
89425
|
const recent = state.history.slice(-10);
|
|
89306
89426
|
return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
|
|
@@ -98017,8 +98137,8 @@ var init_rate_limit_detection = __esm(() => {
|
|
|
98017
98137
|
});
|
|
98018
98138
|
|
|
98019
98139
|
// packages/engine/src/agents/claude.ts
|
|
98020
|
-
import { mkdtemp, unlink } from "fs/promises";
|
|
98021
|
-
import { join as
|
|
98140
|
+
import { mkdtemp, unlink as unlink2 } from "fs/promises";
|
|
98141
|
+
import { join as join13 } from "path";
|
|
98022
98142
|
import { tmpdir } from "os";
|
|
98023
98143
|
function buildClaudeArgs(model, resumeSessionId, reviewerContextStrategy, reviewerModel) {
|
|
98024
98144
|
const effectiveModel = reviewerModel ?? model;
|
|
@@ -98039,7 +98159,7 @@ function buildClaudeArgs(model, resumeSessionId, reviewerContextStrategy, review
|
|
|
98039
98159
|
}
|
|
98040
98160
|
async function runInteractive(req) {
|
|
98041
98161
|
const { model, prompt, taskDir } = req;
|
|
98042
|
-
const promptFile = taskDir ?
|
|
98162
|
+
const promptFile = taskDir ? join13(taskDir, "_interactive_prompt.md") : join13(await mkdtemp(join13(tmpdir(), "ralph-")), "prompt.md");
|
|
98043
98163
|
await Bun.write(promptFile, prompt);
|
|
98044
98164
|
try {
|
|
98045
98165
|
const cmd = [
|
|
@@ -98066,14 +98186,14 @@ async function runInteractive(req) {
|
|
|
98066
98186
|
env: scrubClaudeEnv(process.env)
|
|
98067
98187
|
});
|
|
98068
98188
|
const exitCode = await proc.exited;
|
|
98069
|
-
const doneFile = taskDir ?
|
|
98189
|
+
const doneFile = taskDir ? join13(taskDir, "_interactive_done") : null;
|
|
98070
98190
|
if (doneFile && await Bun.file(doneFile).exists()) {
|
|
98071
98191
|
return { exitCode: 0, usage: null, sessionId: null, rateLimited: false };
|
|
98072
98192
|
}
|
|
98073
98193
|
return { exitCode, usage: null, sessionId: null, rateLimited: false };
|
|
98074
98194
|
} finally {
|
|
98075
98195
|
try {
|
|
98076
|
-
await
|
|
98196
|
+
await unlink2(promptFile);
|
|
98077
98197
|
} catch {}
|
|
98078
98198
|
}
|
|
98079
98199
|
}
|
|
@@ -99105,6 +99225,12 @@ class FlowActorStore {
|
|
|
99105
99225
|
return typeof s.value === "string" || typeof s.status === "string";
|
|
99106
99226
|
}
|
|
99107
99227
|
async loadSnapshot(changeDir) {
|
|
99228
|
+
const sidecar = await readSlotSidecar(changeDir, "flow");
|
|
99229
|
+
if (sidecar && typeof sidecar === "object") {
|
|
99230
|
+
const snap = sidecar.actorSnapshot;
|
|
99231
|
+
if (snap !== undefined && snap !== null)
|
|
99232
|
+
return snap;
|
|
99233
|
+
}
|
|
99108
99234
|
const filePath = `${changeDir}/${STATE_FILE4}`;
|
|
99109
99235
|
const file2 = Bun.file(filePath);
|
|
99110
99236
|
if (!await file2.exists())
|
|
@@ -99563,11 +99689,11 @@ var init_meta_prompt = __esm(() => {
|
|
|
99563
99689
|
});
|
|
99564
99690
|
|
|
99565
99691
|
// packages/core/src/loop.ts
|
|
99566
|
-
import { join as
|
|
99692
|
+
import { join as join14 } from "path";
|
|
99567
99693
|
function buildTaskPrompt(state, taskDir, reviewPhase) {
|
|
99568
99694
|
const storage = getStorage();
|
|
99569
99695
|
let prompt = "";
|
|
99570
|
-
const steeringContent = storage.read(
|
|
99696
|
+
const steeringContent = storage.read(join14(taskDir, "steering.md"));
|
|
99571
99697
|
if (steeringContent !== null) {
|
|
99572
99698
|
const steeringLines = steeringContent.split(`
|
|
99573
99699
|
`).filter((line) => !line.startsWith("#")).filter((line) => line.trim()).slice(0, STEERING_MAX_LINES);
|
|
@@ -99586,8 +99712,8 @@ function buildTaskPrompt(state, taskDir, reviewPhase) {
|
|
|
99586
99712
|
`;
|
|
99587
99713
|
}
|
|
99588
99714
|
}
|
|
99589
|
-
const agentTasksPath =
|
|
99590
|
-
const missionTasksPath =
|
|
99715
|
+
const agentTasksPath = join14(taskDir, AGENT_TASKS_FILENAME);
|
|
99716
|
+
const missionTasksPath = join14(taskDir, MISSION_TASKS_FILENAME);
|
|
99591
99717
|
const agentTasksContent = storage.read(agentTasksPath);
|
|
99592
99718
|
const missionTasksContent = storage.read(missionTasksPath);
|
|
99593
99719
|
let activePath = null;
|
|
@@ -99663,7 +99789,7 @@ function buildTaskPrompt(state, taskDir, reviewPhase) {
|
|
|
99663
99789
|
}
|
|
99664
99790
|
}
|
|
99665
99791
|
if (reviewPhase?.enabled) {
|
|
99666
|
-
const reviewFindingsPath =
|
|
99792
|
+
const reviewFindingsPath = join14(taskDir, "review-findings.md");
|
|
99667
99793
|
const reviewFindingsContent = storage.read(reviewFindingsPath);
|
|
99668
99794
|
const hasUncheckedMission = missionTasksContent !== null && /^- \[ \]/m.test(missionTasksContent);
|
|
99669
99795
|
const hasUncheckedAgent = agentTasksContent !== null && /^- \[ \]/m.test(agentTasksContent);
|
|
@@ -99737,7 +99863,7 @@ When all tasks are complete and all files are committed, push your branch and op
|
|
|
99737
99863
|
}
|
|
99738
99864
|
function buildSteeringBlock(taskDir) {
|
|
99739
99865
|
const storage = getStorage();
|
|
99740
|
-
const steeringContent = storage.read(
|
|
99866
|
+
const steeringContent = storage.read(join14(taskDir, "steering.md"));
|
|
99741
99867
|
if (steeringContent === null)
|
|
99742
99868
|
return "";
|
|
99743
99869
|
const steeringLines = steeringContent.split(`
|
|
@@ -99835,7 +99961,7 @@ function buildPlanPrompt(state, taskDir) {
|
|
|
99835
99961
|
return prompt;
|
|
99836
99962
|
}
|
|
99837
99963
|
function buildReviewPrompt(state, taskDir) {
|
|
99838
|
-
const reviewFindingsPath =
|
|
99964
|
+
const reviewFindingsPath = join14(taskDir, "review-findings.md");
|
|
99839
99965
|
let prompt = buildSteeringBlock(taskDir);
|
|
99840
99966
|
prompt += `---
|
|
99841
99967
|
|
|
@@ -99900,7 +100026,7 @@ function buildPhasePrompt(phase, state, taskDir, reviewPhase, metaPromptOptions)
|
|
|
99900
100026
|
}
|
|
99901
100027
|
function checkStopSignal(taskDir, stateDir) {
|
|
99902
100028
|
const storage = getStorage();
|
|
99903
|
-
const stopFile =
|
|
100029
|
+
const stopFile = join14(taskDir, "STOP");
|
|
99904
100030
|
const reason = storage.read(stopFile);
|
|
99905
100031
|
if (reason === null)
|
|
99906
100032
|
return null;
|
|
@@ -99960,7 +100086,7 @@ function updateStateIteration(stateDir, result2, startedAt, engine, model, usage
|
|
|
99960
100086
|
}
|
|
99961
100087
|
function appendSteeringMessage(taskDir, message) {
|
|
99962
100088
|
const storage = getStorage();
|
|
99963
|
-
const steeringPath =
|
|
100089
|
+
const steeringPath = join14(taskDir, "steering.md");
|
|
99964
100090
|
const existing = storage.read(steeringPath);
|
|
99965
100091
|
const updated = existing ? `${message}
|
|
99966
100092
|
|
|
@@ -100010,7 +100136,7 @@ var init_loop2 = __esm(() => {
|
|
|
100010
100136
|
});
|
|
100011
100137
|
|
|
100012
100138
|
// apps/loop/src/hooks/useLoop.ts
|
|
100013
|
-
import { join as
|
|
100139
|
+
import { join as join15 } from "path";
|
|
100014
100140
|
function sleep(seconds) {
|
|
100015
100141
|
return new Promise((resolve3) => setTimeout(resolve3, seconds * 1000));
|
|
100016
100142
|
}
|
|
@@ -100072,7 +100198,7 @@ function useLoop(opts) {
|
|
|
100072
100198
|
}
|
|
100073
100199
|
} else {
|
|
100074
100200
|
if (rawState !== null) {
|
|
100075
|
-
addInfo(`.ralph-state.json was malformed \u2014 reinitialising.
|
|
100201
|
+
addInfo(`.ralph-state.json was malformed \u2014 reinitialising. Feature-owned slots (linearComments, specAttachments, \u2026) live in their own sidecar files and are unaffected.`);
|
|
100076
100202
|
}
|
|
100077
100203
|
currentState = buildInitialState({
|
|
100078
100204
|
name: opts.name,
|
|
@@ -100082,12 +100208,6 @@ function useLoop(opts) {
|
|
|
100082
100208
|
manualTest: opts.manualTest,
|
|
100083
100209
|
createPr: opts.createPr ?? false
|
|
100084
100210
|
});
|
|
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
100211
|
writeState(stateDir, currentState);
|
|
100092
100212
|
}
|
|
100093
100213
|
const isResume2 = currentState.iteration > 0;
|
|
@@ -100132,8 +100252,8 @@ function useLoop(opts) {
|
|
|
100132
100252
|
setState(currentState);
|
|
100133
100253
|
if (!actor.getSnapshot().matches("running"))
|
|
100134
100254
|
break;
|
|
100135
|
-
const tasksContent = storage.read(
|
|
100136
|
-
const agentTasksContent = storage.read(
|
|
100255
|
+
const tasksContent = storage.read(join15(tasksDir, MISSION_TASKS_FILENAME));
|
|
100256
|
+
const agentTasksContent = storage.read(join15(tasksDir, AGENT_TASKS_FILENAME));
|
|
100137
100257
|
if (tasksContent === null && currentState.iteration > 0 && typeof opts.changeStore.listChanges === "function") {
|
|
100138
100258
|
let stillActive = true;
|
|
100139
100259
|
try {
|
|
@@ -100170,7 +100290,7 @@ function useLoop(opts) {
|
|
|
100170
100290
|
const agentDone = agentTasksContent === null || allCompleted(agentTasksContent);
|
|
100171
100291
|
if (missionDone && agentDone && tasksContent !== null) {
|
|
100172
100292
|
if (opts.reviewPhase?.enabled) {
|
|
100173
|
-
const reviewFindingsPath =
|
|
100293
|
+
const reviewFindingsPath = join15(tasksDir, "review-findings.md");
|
|
100174
100294
|
const reviewFindingsFile = Bun.file(reviewFindingsPath);
|
|
100175
100295
|
const findingsExists = await reviewFindingsFile.exists();
|
|
100176
100296
|
const findingsContent = findingsExists ? await reviewFindingsFile.text() : null;
|
|
@@ -100199,7 +100319,7 @@ function useLoop(opts) {
|
|
|
100199
100319
|
model: opts.reviewPhase.reviewerModel ?? opts.model,
|
|
100200
100320
|
prompt: reviewPrompt,
|
|
100201
100321
|
logFlag: opts.log,
|
|
100202
|
-
logFile:
|
|
100322
|
+
logFile: join15(stateDir, `log-review-${roundNum}.json`),
|
|
100203
100323
|
taskDir: tasksDir,
|
|
100204
100324
|
reviewerContextStrategy: opts.reviewPhase.reviewerContextStrategy ?? "fresh",
|
|
100205
100325
|
onFeedEvent: addFeedEvent
|
|
@@ -100273,8 +100393,8 @@ function useLoop(opts) {
|
|
|
100273
100393
|
const time3 = new Date().toLocaleTimeString("en-US", { hour12: false });
|
|
100274
100394
|
addIterationHeader(localIter, time3);
|
|
100275
100395
|
addInfo(`Iteration ${localIter} (total: ${currentState.iteration})`);
|
|
100276
|
-
const proposalContent = storage.read(
|
|
100277
|
-
const designContent = storage.read(
|
|
100396
|
+
const proposalContent = storage.read(join15(tasksDir, "proposal.md"));
|
|
100397
|
+
const designContent = storage.read(join15(tasksDir, "design.md"));
|
|
100278
100398
|
const routedPhase = routeTaskPhase(opts.phase, {
|
|
100279
100399
|
proposal: proposalContent,
|
|
100280
100400
|
design: designContent,
|
|
@@ -100294,7 +100414,7 @@ function useLoop(opts) {
|
|
|
100294
100414
|
model: opts.model,
|
|
100295
100415
|
prompt,
|
|
100296
100416
|
logFlag: opts.log,
|
|
100297
|
-
logFile:
|
|
100417
|
+
logFile: join15(stateDir, "log.json"),
|
|
100298
100418
|
taskDir: tasksDir,
|
|
100299
100419
|
interactive: false,
|
|
100300
100420
|
onFeedEvent: addFeedEvent,
|
|
@@ -100317,7 +100437,7 @@ function useLoop(opts) {
|
|
|
100317
100437
|
model: opts.model,
|
|
100318
100438
|
prompt: buildSteeringPrompt(steerMessage),
|
|
100319
100439
|
logFlag: opts.log,
|
|
100320
|
-
logFile:
|
|
100440
|
+
logFile: join15(stateDir, "log.json"),
|
|
100321
100441
|
taskDir: tasksDir,
|
|
100322
100442
|
onFeedEvent: addResumeFeedEvent,
|
|
100323
100443
|
signal: resumeController.signal,
|
|
@@ -100624,7 +100744,7 @@ var init_TaskLoop = __esm(async () => {
|
|
|
100624
100744
|
});
|
|
100625
100745
|
|
|
100626
100746
|
// apps/loop/src/components/App.tsx
|
|
100627
|
-
import { join as
|
|
100747
|
+
import { join as join16 } from "path";
|
|
100628
100748
|
function ExitAfterRender({ children }) {
|
|
100629
100749
|
const { exit } = use_app_default();
|
|
100630
100750
|
import_react59.useEffect(() => {
|
|
@@ -100677,7 +100797,7 @@ function App2({ args, taskPhase }) {
|
|
|
100677
100797
|
}
|
|
100678
100798
|
const layout = getLayout();
|
|
100679
100799
|
const stateDir = layout.taskStateDir(args.name);
|
|
100680
|
-
if (getStorage().read(
|
|
100800
|
+
if (getStorage().read(join16(stateDir, ".ralph-state.json")) === null) {
|
|
100681
100801
|
return /* @__PURE__ */ jsx_dev_runtime9.jsxDEV(ErrorMessage, {
|
|
100682
100802
|
message: `Error: change '${args.name}' not found`
|
|
100683
100803
|
}, undefined, false, undefined, this);
|
|
@@ -100731,7 +100851,7 @@ var init_App2 = __esm(async () => {
|
|
|
100731
100851
|
|
|
100732
100852
|
// packages/log/src/log.ts
|
|
100733
100853
|
import { appendFile } from "fs/promises";
|
|
100734
|
-
import { join as
|
|
100854
|
+
import { join as join17, dirname as dirname7 } from "path";
|
|
100735
100855
|
import { homedir as homedir4 } from "os";
|
|
100736
100856
|
import { mkdir as mkdir5 } from "fs/promises";
|
|
100737
100857
|
function fmt(type, text) {
|
|
@@ -100780,14 +100900,14 @@ var init_log = __esm(() => {
|
|
|
100780
100900
|
init_version();
|
|
100781
100901
|
jsonLogChains = new Map;
|
|
100782
100902
|
ANSI_RE = /\x1b(?:\[[0-9;]*[A-Za-z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|.)/g;
|
|
100783
|
-
AGENT_LOG_PATH =
|
|
100903
|
+
AGENT_LOG_PATH = join17(homedir4(), ".ralph", "agent-mode.log");
|
|
100784
100904
|
mkdir5(dirname7(AGENT_LOG_PATH), { recursive: true }).catch(() => {
|
|
100785
100905
|
return;
|
|
100786
100906
|
});
|
|
100787
100907
|
});
|
|
100788
100908
|
|
|
100789
100909
|
// apps/loop/src/debug.ts
|
|
100790
|
-
import { join as
|
|
100910
|
+
import { join as join18 } from "path";
|
|
100791
100911
|
function fmtTs(d) {
|
|
100792
100912
|
return d.toISOString().replace("T", " ").slice(0, 23);
|
|
100793
100913
|
}
|
|
@@ -100899,7 +101019,7 @@ function detectDebugStuck(lines) {
|
|
|
100899
101019
|
};
|
|
100900
101020
|
}
|
|
100901
101021
|
async function inspectBinary(projectRoot) {
|
|
100902
|
-
const binPath =
|
|
101022
|
+
const binPath = join18(projectRoot, ".ralph", "bin", "cli.js");
|
|
100903
101023
|
const file2 = Bun.file(binPath);
|
|
100904
101024
|
if (!await file2.exists())
|
|
100905
101025
|
return null;
|
|
@@ -100924,7 +101044,7 @@ async function inspectBinary(projectRoot) {
|
|
|
100924
101044
|
async function resolveDebugTarget(projectRoot, opts) {
|
|
100925
101045
|
const agentLogFile = Bun.file(AGENT_LOG_PATH);
|
|
100926
101046
|
const textLines = await agentLogFile.exists() ? parseTextLog(await agentLogFile.text()) : [];
|
|
100927
|
-
const jsonlLogFile = Bun.file(
|
|
101047
|
+
const jsonlLogFile = Bun.file(join18(projectRoot, ".ralph", "agent.log"));
|
|
100928
101048
|
const jsonlLines = await jsonlLogFile.exists() ? parseJsonlLog(await jsonlLogFile.text()) : [];
|
|
100929
101049
|
const allLines = [...textLines, ...jsonlLines];
|
|
100930
101050
|
if (opts.name && !opts.issue) {
|
|
@@ -101029,7 +101149,7 @@ async function runDebug(opts) {
|
|
|
101029
101149
|
`);
|
|
101030
101150
|
const agentLogFile = Bun.file(AGENT_LOG_PATH);
|
|
101031
101151
|
const textLines = await agentLogFile.exists() ? parseTextLog(await agentLogFile.text()) : [];
|
|
101032
|
-
const jsonlLogPath =
|
|
101152
|
+
const jsonlLogPath = join18(projectRoot, ".ralph", "agent.log");
|
|
101033
101153
|
const jsonlLogFile = Bun.file(jsonlLogPath);
|
|
101034
101154
|
const hasJsonlLog = await jsonlLogFile.exists();
|
|
101035
101155
|
let { changeName, identifier: issueIdentifier } = await resolveDebugTarget(projectRoot, {
|
|
@@ -101043,7 +101163,7 @@ async function runDebug(opts) {
|
|
|
101043
101163
|
}
|
|
101044
101164
|
const jsonlLines = hasJsonlLog ? parseJsonlLog(await jsonlLogFile.text(), changeName) : [];
|
|
101045
101165
|
const relevantText = textLines.filter((l) => l.text.includes(changeName) || issueIdentifier !== undefined && l.text.includes(issueIdentifier));
|
|
101046
|
-
const workerLogFile = Bun.file(
|
|
101166
|
+
const workerLogFile = Bun.file(join18(projectRoot, ".ralph", "logs", `${changeName}.log`));
|
|
101047
101167
|
const workerLines = await workerLogFile.exists() ? parseTextLog(await workerLogFile.text()) : [];
|
|
101048
101168
|
const merged = [...relevantText, ...jsonlLines, ...workerLines].sort((a, b) => +a.ts - +b.ts);
|
|
101049
101169
|
const seen = new Set;
|
|
@@ -101190,6 +101310,8 @@ async function runDebug(opts) {
|
|
|
101190
101310
|
out(" \u26A0 setError applied \u2014 issue is quarantined in Linear");
|
|
101191
101311
|
if (logHas("setDone applied"))
|
|
101192
101312
|
out(" \u2713 setDone applied \u2014 issue marked done in Linear");
|
|
101313
|
+
if (logHas("setPrReady applied"))
|
|
101314
|
+
out(" \u2713 setPrReady applied \u2014 PR is ready for human review");
|
|
101193
101315
|
if (logHas("clearConflicted applied"))
|
|
101194
101316
|
out(" \u2713 clearConflicted applied");
|
|
101195
101317
|
if (logHas("setConflicted applied"))
|
|
@@ -101200,8 +101322,8 @@ async function runDebug(opts) {
|
|
|
101200
101322
|
out(" \u26A0 PR currently has merge conflicts");
|
|
101201
101323
|
if (pr?.checks.some((c) => c.conclusion === "FAILURE"))
|
|
101202
101324
|
out(" \u26A0 PR has failing CI checks");
|
|
101203
|
-
const worktreePath =
|
|
101204
|
-
if (await Bun.file(
|
|
101325
|
+
const worktreePath = join18(projectRoot, ".ralph", "worktrees", changeName);
|
|
101326
|
+
if (await Bun.file(join18(worktreePath, ".git")).exists()) {
|
|
101205
101327
|
out(` Worktree : ${worktreePath}`);
|
|
101206
101328
|
}
|
|
101207
101329
|
if (!timeline.length)
|
|
@@ -101221,12 +101343,12 @@ __export(exports_src2, {
|
|
|
101221
101343
|
taskMain: () => taskMain,
|
|
101222
101344
|
main: () => main2
|
|
101223
101345
|
});
|
|
101224
|
-
import { join as
|
|
101346
|
+
import { join as join19 } from "path";
|
|
101225
101347
|
import { exists as exists2, mkdir as mkdir6, rm as rm2 } from "fs/promises";
|
|
101226
101348
|
async function ensureRalphGitignore(projectRoot) {
|
|
101227
|
-
const ralphDir =
|
|
101349
|
+
const ralphDir = join19(projectRoot, ".ralph");
|
|
101228
101350
|
await mkdir6(ralphDir, { recursive: true });
|
|
101229
|
-
const gitignorePath =
|
|
101351
|
+
const gitignorePath = join19(ralphDir, ".gitignore");
|
|
101230
101352
|
const file2 = Bun.file(gitignorePath);
|
|
101231
101353
|
if (await file2.exists()) {
|
|
101232
101354
|
const existing = await file2.text();
|
|
@@ -101293,9 +101415,9 @@ async function main2(argv) {
|
|
|
101293
101415
|
`);
|
|
101294
101416
|
return 1;
|
|
101295
101417
|
}
|
|
101296
|
-
const worktreeDir =
|
|
101297
|
-
const changeDir =
|
|
101298
|
-
const stateDir =
|
|
101418
|
+
const worktreeDir = join19(worktreesDir(projectRoot), args.name);
|
|
101419
|
+
const changeDir = join19(tasksDir, args.name);
|
|
101420
|
+
const stateDir = join19(statesDir, args.name);
|
|
101299
101421
|
const branch = `ralph/${args.name}`;
|
|
101300
101422
|
const removed = [];
|
|
101301
101423
|
if (await exists2(worktreeDir)) {
|
|
@@ -101346,8 +101468,8 @@ async function main2(argv) {
|
|
|
101346
101468
|
return 0;
|
|
101347
101469
|
}
|
|
101348
101470
|
if (args.mode === "task" && args.name) {
|
|
101349
|
-
await mkdir6(
|
|
101350
|
-
await mkdir6(
|
|
101471
|
+
await mkdir6(join19(statesDir, args.name), { recursive: true });
|
|
101472
|
+
await mkdir6(join19(tasksDir, args.name), { recursive: true });
|
|
101351
101473
|
await ensureRalphGitignore(projectRoot);
|
|
101352
101474
|
}
|
|
101353
101475
|
await runWithContext(createDefaultContext({ layout, args }), async () => {
|
|
@@ -101375,8 +101497,8 @@ async function taskMain(argv) {
|
|
|
101375
101497
|
const layout = projectLayout(projectRoot);
|
|
101376
101498
|
const statesDir = layout.statesDir;
|
|
101377
101499
|
const tasksDir = layout.tasksDir;
|
|
101378
|
-
await mkdir6(
|
|
101379
|
-
await mkdir6(
|
|
101500
|
+
await mkdir6(join19(statesDir, args.name), { recursive: true });
|
|
101501
|
+
await mkdir6(join19(tasksDir, args.name), { recursive: true });
|
|
101380
101502
|
await ensureRalphGitignore(projectRoot);
|
|
101381
101503
|
await runWithContext(createDefaultContext({ layout, args }), async () => {
|
|
101382
101504
|
const { waitUntilExit } = render_default(import_react60.createElement(App2, {
|
|
@@ -101639,6 +101761,7 @@ var init_cli2 = __esm(() => {
|
|
|
101639
101761
|
"getAutoMerge",
|
|
101640
101762
|
"setInProgress",
|
|
101641
101763
|
"setDone",
|
|
101764
|
+
"setPrReady",
|
|
101642
101765
|
"setError"
|
|
101643
101766
|
]);
|
|
101644
101767
|
GET_KEYS = new Set(["getTodo", "getInProgress", "getAutoMerge"]);
|
|
@@ -101677,9 +101800,10 @@ var init_cli2 = __esm(() => {
|
|
|
101677
101800
|
" --worktree Run each task in its own git worktree",
|
|
101678
101801
|
" --indicator <k>:<t>:<v> Override an indicator (repeatable).",
|
|
101679
101802
|
" Keys: getTodo, getInProgress, getAutoMerge,",
|
|
101680
|
-
" setInProgress, setDone, setError",
|
|
101803
|
+
" setInProgress, setDone, setPrReady, setError",
|
|
101681
101804
|
" Types: label, status, attachment, project, comment",
|
|
101682
101805
|
" --indicator setInProgress:attachment:In Progress",
|
|
101806
|
+
" --indicator setPrReady:status:In Review (additive ready marker)",
|
|
101683
101807
|
" (attachment upserts a single 'Ralphy' entry; value = subtitle)",
|
|
101684
101808
|
" --create-pr Push the worker branch and open a GitHub PR on success (needs --worktree)",
|
|
101685
101809
|
" --fix-ci After opening the PR, re-run on CI failures until green (needs --create-pr)",
|
|
@@ -101773,7 +101897,7 @@ function formatError2(err) {
|
|
|
101773
101897
|
}
|
|
101774
101898
|
|
|
101775
101899
|
// apps/agent/src/shared/capabilities/fs-change.ts
|
|
101776
|
-
import { join as
|
|
101900
|
+
import { join as join20, dirname as dirname8 } from "path";
|
|
101777
101901
|
import { mkdir as mkdir7 } from "fs/promises";
|
|
101778
101902
|
var scaffold, prependTask, appendSteering, fsChange;
|
|
101779
101903
|
var init_fs_change = __esm(() => {
|
|
@@ -101786,11 +101910,11 @@ var init_fs_change = __esm(() => {
|
|
|
101786
101910
|
errorFormatter: formatError2,
|
|
101787
101911
|
run: async (args) => {
|
|
101788
101912
|
await mkdir7(args.changeDir, { recursive: true });
|
|
101789
|
-
await mkdir7(
|
|
101913
|
+
await mkdir7(join20(args.changeDir, "specs"), { recursive: true });
|
|
101790
101914
|
await mkdir7(args.stateDir, { recursive: true });
|
|
101791
|
-
await Bun.write(
|
|
101792
|
-
await Bun.write(
|
|
101793
|
-
await Bun.write(
|
|
101915
|
+
await Bun.write(join20(args.changeDir, "proposal.md"), args.proposal);
|
|
101916
|
+
await Bun.write(join20(args.changeDir, "tasks.md"), args.tasks);
|
|
101917
|
+
await Bun.write(join20(args.changeDir, "design.md"), args.design);
|
|
101794
101918
|
}
|
|
101795
101919
|
};
|
|
101796
101920
|
prependTask = {
|
|
@@ -101808,7 +101932,7 @@ var init_fs_change = __esm(() => {
|
|
|
101808
101932
|
retryPolicy: NO_RETRY,
|
|
101809
101933
|
errorFormatter: formatError2,
|
|
101810
101934
|
run: async (args) => {
|
|
101811
|
-
const path =
|
|
101935
|
+
const path = join20(args.changeDir, "steering.md");
|
|
101812
101936
|
const f2 = Bun.file(path);
|
|
101813
101937
|
const existing = await f2.exists() ? await f2.text() : null;
|
|
101814
101938
|
const updated = existing ? `${args.message}
|
|
@@ -101823,11 +101947,11 @@ ${existing.trimStart()}` : `${args.message}
|
|
|
101823
101947
|
});
|
|
101824
101948
|
|
|
101825
101949
|
// apps/agent/src/agent/worktree.ts
|
|
101826
|
-
import { basename as basename2, join as
|
|
101950
|
+
import { basename as basename2, join as join21 } from "path";
|
|
101827
101951
|
import { homedir as homedir5 } from "os";
|
|
101828
101952
|
import { exists as exists3 } from "fs/promises";
|
|
101829
101953
|
function worktreesDir2(projectRoot) {
|
|
101830
|
-
return
|
|
101954
|
+
return join21(homedir5(), ".ralph", basename2(projectRoot), "worktrees");
|
|
101831
101955
|
}
|
|
101832
101956
|
function branchForChange(changeName) {
|
|
101833
101957
|
return `ralph/${changeName}`;
|
|
@@ -101846,7 +101970,7 @@ function createWorktree(projectRoot, changeName, baseBranch, runner) {
|
|
|
101846
101970
|
}
|
|
101847
101971
|
async function provisionWorktree(projectRoot, changeName, baseBranch, runner) {
|
|
101848
101972
|
const dir = worktreesDir2(projectRoot);
|
|
101849
|
-
const cwd2 =
|
|
101973
|
+
const cwd2 = join21(dir, changeName);
|
|
101850
101974
|
const branch = branchForChange(changeName);
|
|
101851
101975
|
const list = await runner.run(["worktree", "list", "--porcelain"], projectRoot);
|
|
101852
101976
|
if (list.stdout.includes(`worktree ${cwd2}
|
|
@@ -101871,7 +101995,7 @@ async function provisionWorktree(projectRoot, changeName, baseBranch, runner) {
|
|
|
101871
101995
|
return { cwd: cwd2, branch };
|
|
101872
101996
|
}
|
|
101873
101997
|
async function installPrePushHook(cwd2, runner) {
|
|
101874
|
-
const hookPath =
|
|
101998
|
+
const hookPath = join21(cwd2, ".ralph-hooks", "pre-push");
|
|
101875
101999
|
await Bun.write(hookPath, PRE_PUSH_HOOK_SCRIPT);
|
|
101876
102000
|
const chmod = Bun.spawn(["chmod", "+x", hookPath]);
|
|
101877
102001
|
await chmod.exited;
|
|
@@ -101917,8 +102041,8 @@ async function isWorktreeSafeToRemove(cwd2, base2, runner) {
|
|
|
101917
102041
|
return { safe: true, dirty, unpushedCommits };
|
|
101918
102042
|
}
|
|
101919
102043
|
async function seedWorktreeMcpConfig(projectRoot, worktreeCwd) {
|
|
101920
|
-
const dst =
|
|
101921
|
-
const src =
|
|
102044
|
+
const dst = join21(worktreeCwd, ".mcp.json");
|
|
102045
|
+
const src = join21(projectRoot, ".mcp.json");
|
|
101922
102046
|
const source = await exists3(dst) ? dst : await exists3(src) ? src : null;
|
|
101923
102047
|
if (!source)
|
|
101924
102048
|
return;
|
|
@@ -101932,7 +102056,7 @@ async function seedWorktreeMcpConfig(projectRoot, worktreeCwd) {
|
|
|
101932
102056
|
if (servers && typeof servers === "object") {
|
|
101933
102057
|
for (const cfg of Object.values(servers)) {
|
|
101934
102058
|
if (Array.isArray(cfg.args)) {
|
|
101935
|
-
cfg.args = cfg.args.map((a) => typeof a === "string" && a.startsWith(".ralph/") ?
|
|
102059
|
+
cfg.args = cfg.args.map((a) => typeof a === "string" && a.startsWith(".ralph/") ? join21(projectRoot, a) : a);
|
|
101936
102060
|
}
|
|
101937
102061
|
}
|
|
101938
102062
|
}
|
|
@@ -103782,7 +103906,7 @@ function emitFeatureSkipped(bus, id, reason) {
|
|
|
103782
103906
|
var init_run_feature = () => {};
|
|
103783
103907
|
|
|
103784
103908
|
// apps/agent/src/agent/post-task.ts
|
|
103785
|
-
import { join as
|
|
103909
|
+
import { join as join22, dirname as dirname9 } from "path";
|
|
103786
103910
|
function summarizeUncommittedStatus(stdout) {
|
|
103787
103911
|
const lines = stdout.split(`
|
|
103788
103912
|
`).filter((line) => line.length > 0);
|
|
@@ -103854,7 +103978,7 @@ async function reactivateState(stateFilePath, log3, changeName) {
|
|
|
103854
103978
|
async function runWorkerWithFixTask(ctx, heading, body) {
|
|
103855
103979
|
try {
|
|
103856
103980
|
await runCapability(fsChange.prependTask, {
|
|
103857
|
-
tasksPath:
|
|
103981
|
+
tasksPath: join22(ctx.changeDir, AGENT_TASKS_FILENAME),
|
|
103858
103982
|
heading,
|
|
103859
103983
|
failureOutput: body
|
|
103860
103984
|
});
|
|
@@ -104149,6 +104273,7 @@ async function runPrPhase(input, deps) {
|
|
|
104149
104273
|
emit: emit3,
|
|
104150
104274
|
respawnWorker,
|
|
104151
104275
|
registerPr,
|
|
104276
|
+
onPrReady,
|
|
104152
104277
|
checkPrConflict,
|
|
104153
104278
|
resolveDependencyBaseBranch
|
|
104154
104279
|
} = deps;
|
|
@@ -104343,6 +104468,9 @@ ${indented}${suffix}`, "yellow");
|
|
|
104343
104468
|
log3(`! manual merge failed for ${prUrl}: ${e.stderr?.trim() || e.message}`, "yellow");
|
|
104344
104469
|
}
|
|
104345
104470
|
}
|
|
104471
|
+
if (!(wantAutoMerge && !prReadyNeeded)) {
|
|
104472
|
+
await onPrReady?.(prUrl);
|
|
104473
|
+
}
|
|
104346
104474
|
return 0;
|
|
104347
104475
|
}
|
|
104348
104476
|
async function runWorktreeCleanupPhase(input, deps) {
|
|
@@ -104401,7 +104529,7 @@ async function runValidateOnlyPhase(input, deps) {
|
|
|
104401
104529
|
emit3("validate-fix", command);
|
|
104402
104530
|
log3(`! validation check failed: ${command}`, "yellow");
|
|
104403
104531
|
try {
|
|
104404
|
-
await prependFixTask(
|
|
104532
|
+
await prependFixTask(join22(changeDir, AGENT_TASKS_FILENAME), `Fix failing validation: ${command}`, output || `Command exited with code ${exitCode}`);
|
|
104405
104533
|
} catch (err) {
|
|
104406
104534
|
log3(`! could not prepend fix task: ${err.message}`, "red");
|
|
104407
104535
|
return 1;
|
|
@@ -104412,7 +104540,7 @@ async function runValidateOnlyPhase(input, deps) {
|
|
|
104412
104540
|
}
|
|
104413
104541
|
}
|
|
104414
104542
|
try {
|
|
104415
|
-
await prependFixTask(
|
|
104543
|
+
await prependFixTask(join22(changeDir, AGENT_TASKS_FILENAME), "Run openspec validation", [
|
|
104416
104544
|
`Run \`bunx openspec validate ${changeName}\` to validate the change artifacts.`,
|
|
104417
104545
|
`Commit any pending changes before running the validation command.`
|
|
104418
104546
|
].join(`
|
|
@@ -104425,7 +104553,7 @@ async function runValidateOnlyPhase(input, deps) {
|
|
|
104425
104553
|
return respawnWorker();
|
|
104426
104554
|
}
|
|
104427
104555
|
async function recordGaveUp(stateFilePath, log3, changeName) {
|
|
104428
|
-
const path =
|
|
104556
|
+
const path = join22(dirname9(stateFilePath), GAVEUP_COUNT_FILE);
|
|
104429
104557
|
try {
|
|
104430
104558
|
const file2 = Bun.file(path);
|
|
104431
104559
|
const current = await file2.exists() ? Number.parseInt(await file2.text(), 10) || 0 : 0;
|
|
@@ -104548,6 +104676,7 @@ async function runPostTask(input, deps) {
|
|
|
104548
104676
|
emit: emit3,
|
|
104549
104677
|
respawnWorker,
|
|
104550
104678
|
...deps.registerPr !== undefined ? { registerPr: deps.registerPr } : {},
|
|
104679
|
+
...deps.onPrReady !== undefined ? { onPrReady: deps.onPrReady } : {},
|
|
104551
104680
|
...deps.checkPrConflict !== undefined ? { checkPrConflict: deps.checkPrConflict } : {},
|
|
104552
104681
|
...deps.resolveDependencyBaseBranch !== undefined ? { resolveDependencyBaseBranch: deps.resolveDependencyBaseBranch } : {}
|
|
104553
104682
|
});
|
|
@@ -105831,15 +105960,15 @@ var init_coordinator2 = __esm(() => {
|
|
|
105831
105960
|
});
|
|
105832
105961
|
|
|
105833
105962
|
// apps/agent/src/agent/scaffold.ts
|
|
105834
|
-
import { join as
|
|
105963
|
+
import { join as join23 } from "path";
|
|
105835
105964
|
function changeNameForIssue(issue2) {
|
|
105836
105965
|
const slug = issue2.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 40).replace(/^-+|-+$/g, "");
|
|
105837
105966
|
return slug ? `${issue2.identifier.toLowerCase()}-${slug}` : issue2.identifier.toLowerCase();
|
|
105838
105967
|
}
|
|
105839
105968
|
async function scaffoldChangeForIssue(tasksDir, statesDir, issue2, comments = [], appendPrompt = "", attachments = []) {
|
|
105840
105969
|
const name = changeNameForIssue(issue2);
|
|
105841
|
-
const changeDir =
|
|
105842
|
-
const stateDir =
|
|
105970
|
+
const changeDir = join23(tasksDir, name);
|
|
105971
|
+
const stateDir = join23(statesDir, name);
|
|
105843
105972
|
const commentsBlock = comments.length > 0 ? [
|
|
105844
105973
|
"",
|
|
105845
105974
|
"## Linear comments",
|
|
@@ -105892,8 +106021,8 @@ async function scaffoldChangeForIssue(tasksDir, statesDir, issue2, comments = []
|
|
|
105892
106021
|
`- [ ] Refine proposal.md with the problem statement, approach, and acceptance criteria derived from the research`,
|
|
105893
106022
|
`- [ ] Fill in \`## Why\` and \`## What Changes\` in proposal.md so \`openspec validate\` passes (these sections are required by the validator)`,
|
|
105894
106023
|
`- [ ] 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.`,
|
|
106024
|
+
`- [ ] 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).`,
|
|
106025
|
+
`- [ ] 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
106026
|
`- [ ] 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
106027
|
""
|
|
105899
106028
|
].join(`
|
|
@@ -105975,19 +106104,22 @@ var init_detections = __esm(() => {
|
|
|
105975
106104
|
});
|
|
105976
106105
|
|
|
105977
106106
|
// apps/agent/src/features/confirmation/state.ts
|
|
105978
|
-
import { dirname as dirname10, join as
|
|
105979
|
-
|
|
105980
|
-
async function readConfirmationState(statePath) {
|
|
106107
|
+
import { dirname as dirname10, join as join24 } from "path";
|
|
106108
|
+
async function readInlineConfirmation(statePath) {
|
|
105981
106109
|
const f2 = Bun.file(statePath);
|
|
105982
|
-
|
|
105983
|
-
|
|
105984
|
-
|
|
105985
|
-
|
|
105986
|
-
|
|
105987
|
-
|
|
105988
|
-
|
|
106110
|
+
if (!await f2.exists())
|
|
106111
|
+
return null;
|
|
106112
|
+
try {
|
|
106113
|
+
const obj = await f2.json();
|
|
106114
|
+
return obj.confirmation ?? null;
|
|
106115
|
+
} catch {
|
|
106116
|
+
return null;
|
|
105989
106117
|
}
|
|
105990
|
-
|
|
106118
|
+
}
|
|
106119
|
+
async function readConfirmationState(statePath) {
|
|
106120
|
+
const changeDir = dirname10(statePath);
|
|
106121
|
+
const sidecar = await readSlotSidecar(changeDir, "confirmation");
|
|
106122
|
+
const existing = sidecar ?? await readInlineConfirmation(statePath) ?? null;
|
|
105991
106123
|
const confirmation = {
|
|
105992
106124
|
askedAt: existing?.askedAt ?? null,
|
|
105993
106125
|
lastReminderAt: existing?.lastReminderAt ?? null,
|
|
@@ -105995,14 +106127,13 @@ async function readConfirmationState(statePath) {
|
|
|
105995
106127
|
rounds: existing?.rounds ?? 0,
|
|
105996
106128
|
stuckPostedAt: existing?.stuckPostedAt ?? null,
|
|
105997
106129
|
lastReviseConsumedAt: existing?.lastReviseConsumedAt ?? null,
|
|
105998
|
-
awaitingMarkerAppliedAt: existing?.awaitingMarkerAppliedAt ?? null
|
|
106130
|
+
awaitingMarkerAppliedAt: existing?.awaitingMarkerAppliedAt ?? null,
|
|
106131
|
+
earlyDraftPrAt: existing?.earlyDraftPrAt ?? null
|
|
105999
106132
|
};
|
|
106000
|
-
return { stateObj, confirmation };
|
|
106133
|
+
return { stateObj: {}, confirmation };
|
|
106001
106134
|
}
|
|
106002
|
-
async function writeConfirmationState(statePath,
|
|
106003
|
-
await
|
|
106004
|
-
await Bun.write(statePath, JSON.stringify({ ...stateObj, confirmation }, null, 2) + `
|
|
106005
|
-
`);
|
|
106135
|
+
async function writeConfirmationState(statePath, _stateObj, confirmation) {
|
|
106136
|
+
await writeSlotField(dirname10(statePath), "confirmation", confirmation);
|
|
106006
106137
|
}
|
|
106007
106138
|
async function restartFromDesign(changeDir, changeName) {
|
|
106008
106139
|
const designStub = [
|
|
@@ -106012,8 +106143,8 @@ async function restartFromDesign(changeDir, changeName) {
|
|
|
106012
106143
|
""
|
|
106013
106144
|
].join(`
|
|
106014
106145
|
`);
|
|
106015
|
-
await Bun.write(
|
|
106016
|
-
const tasksPath =
|
|
106146
|
+
await Bun.write(join24(changeDir, "design.md"), designStub);
|
|
106147
|
+
const tasksPath = join24(changeDir, "tasks.md");
|
|
106017
106148
|
if (await Bun.file(tasksPath).exists()) {
|
|
106018
106149
|
await Bun.write(tasksPath, `# Tasks
|
|
106019
106150
|
|
|
@@ -106025,6 +106156,7 @@ async function appendSteeringNote(changeDir, message) {
|
|
|
106025
106156
|
await runCapability(fsChange.appendSteering, { changeDir, message });
|
|
106026
106157
|
}
|
|
106027
106158
|
var init_state2 = __esm(() => {
|
|
106159
|
+
init_store();
|
|
106028
106160
|
init_fs_change();
|
|
106029
106161
|
});
|
|
106030
106162
|
|
|
@@ -106242,8 +106374,7 @@ var init_inspect = __esm(() => {
|
|
|
106242
106374
|
});
|
|
106243
106375
|
|
|
106244
106376
|
// apps/agent/src/features/confirmation/awaiting.ts
|
|
106245
|
-
import { join as
|
|
106246
|
-
import { mkdir as mkdir9 } from "fs/promises";
|
|
106377
|
+
import { join as join25 } from "path";
|
|
106247
106378
|
async function resolveChangeCwdForIssue(issue2, changeName, deps) {
|
|
106248
106379
|
const tracked = deps.cwdOf(changeName);
|
|
106249
106380
|
if (tracked)
|
|
@@ -106251,12 +106382,12 @@ async function resolveChangeCwdForIssue(issue2, changeName, deps) {
|
|
|
106251
106382
|
if (!deps.useWorktree)
|
|
106252
106383
|
return deps.projectRoot;
|
|
106253
106384
|
const root = worktreesDir2(deps.projectRoot);
|
|
106254
|
-
const canonical =
|
|
106255
|
-
if (await Bun.file(
|
|
106385
|
+
const canonical = join25(root, worktreeDirNameForIssue(issue2));
|
|
106386
|
+
if (await Bun.file(join25(canonical, "openspec", "changes", changeName, "tasks.md")).exists()) {
|
|
106256
106387
|
return canonical;
|
|
106257
106388
|
}
|
|
106258
|
-
const legacy =
|
|
106259
|
-
if (await Bun.file(
|
|
106389
|
+
const legacy = join25(root, changeName);
|
|
106390
|
+
if (await Bun.file(join25(legacy, "openspec", "changes", changeName, "tasks.md")).exists()) {
|
|
106260
106391
|
return legacy;
|
|
106261
106392
|
}
|
|
106262
106393
|
return deps.projectRoot;
|
|
@@ -106276,17 +106407,8 @@ async function postPlanReadyCommentOnce(issue2, statePath, changeName, deps) {
|
|
|
106276
106407
|
return;
|
|
106277
106408
|
if (deps.cfg.linear.postComments === false)
|
|
106278
106409
|
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)
|
|
106410
|
+
const { confirmation } = await readConfirmationState(statePath);
|
|
106411
|
+
if (confirmation.askedAt)
|
|
106290
106412
|
return;
|
|
106291
106413
|
const approvalSentence = describeApprovalMarker(deps.cfg.linear.indicators.getApproved);
|
|
106292
106414
|
const handle = deps.cfg.linear.mentionHandle;
|
|
@@ -106297,16 +106419,8 @@ async function postPlanReadyCommentOnce(issue2, statePath, changeName, deps) {
|
|
|
106297
106419
|
deps.onLog(`! Linear plan-ready comment failed for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
106298
106420
|
return;
|
|
106299
106421
|
}
|
|
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
106422
|
try {
|
|
106307
|
-
await
|
|
106308
|
-
await Bun.write(statePath, JSON.stringify({ ...stateObj, confirmation: nextConfirmation }, null, 2) + `
|
|
106309
|
-
`);
|
|
106423
|
+
await writeConfirmationState(statePath, {}, { ...confirmation, askedAt: new Date().toISOString() });
|
|
106310
106424
|
} catch (err) {
|
|
106311
106425
|
deps.onLog(`! could not persist confirmation.askedAt for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
106312
106426
|
}
|
|
@@ -106330,10 +106444,42 @@ async function applyAwaitingMarkerOnce(issue2, statePath, state, deps) {
|
|
|
106330
106444
|
deps.onLog(`! persist awaitingMarkerAppliedAt for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
106331
106445
|
}
|
|
106332
106446
|
}
|
|
106447
|
+
async function openDraftPrOnce(issue2, statePath, changeName, cwd2, state, deps) {
|
|
106448
|
+
if (deps.cfg.prDraft !== true)
|
|
106449
|
+
return;
|
|
106450
|
+
if (!deps.openDraftPr)
|
|
106451
|
+
return;
|
|
106452
|
+
if (state.confirmation.earlyDraftPrAt)
|
|
106453
|
+
return;
|
|
106454
|
+
let url2 = null;
|
|
106455
|
+
try {
|
|
106456
|
+
url2 = await deps.openDraftPr(issue2, changeName, cwd2);
|
|
106457
|
+
} catch (err) {
|
|
106458
|
+
deps.onLog(`! early draft PR open failed for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
106459
|
+
}
|
|
106460
|
+
state.confirmation.earlyDraftPrAt = new Date().toISOString();
|
|
106461
|
+
try {
|
|
106462
|
+
await writeConfirmationState(statePath, state.stateObj, state.confirmation);
|
|
106463
|
+
} catch (err) {
|
|
106464
|
+
deps.onLog(`! persist earlyDraftPrAt for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
106465
|
+
}
|
|
106466
|
+
if (url2)
|
|
106467
|
+
deps.onLog(` ${issue2.identifier}: opened draft PR for design \u2014 ${url2}`, "gray");
|
|
106468
|
+
}
|
|
106469
|
+
function issueInAwaitingStatus(issue2, indicators) {
|
|
106470
|
+
const set3 = indicators.setAwaitingConfirmation;
|
|
106471
|
+
if (!set3)
|
|
106472
|
+
return false;
|
|
106473
|
+
const current = issue2.state?.name;
|
|
106474
|
+
if (!current)
|
|
106475
|
+
return false;
|
|
106476
|
+
return markersOf(set3).some((m) => m.type === "status" && m.value === current);
|
|
106477
|
+
}
|
|
106333
106478
|
async function releaseAwaitingMarker(issue2, statePath, deps) {
|
|
106334
106479
|
const { stateObj, confirmation } = await readConfirmationState(statePath);
|
|
106335
|
-
if (!confirmation.awaitingMarkerAppliedAt)
|
|
106480
|
+
if (!confirmation.awaitingMarkerAppliedAt && !issueInAwaitingStatus(issue2, deps.indicators)) {
|
|
106336
106481
|
return;
|
|
106482
|
+
}
|
|
106337
106483
|
if (deps.indicators.clearAwaitingConfirmation) {
|
|
106338
106484
|
try {
|
|
106339
106485
|
await deps.applyIndicator(issue2, deps.indicators.clearAwaitingConfirmation);
|
|
@@ -106341,6 +106487,13 @@ async function releaseAwaitingMarker(issue2, statePath, deps) {
|
|
|
106341
106487
|
deps.onLog(`! clearAwaitingConfirmation failed for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
106342
106488
|
}
|
|
106343
106489
|
}
|
|
106490
|
+
if (deps.indicators.setInProgress) {
|
|
106491
|
+
try {
|
|
106492
|
+
await deps.applyIndicator(issue2, deps.indicators.setInProgress);
|
|
106493
|
+
} catch (err) {
|
|
106494
|
+
deps.onLog(`! restore setInProgress after awaiting release failed for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
106495
|
+
}
|
|
106496
|
+
}
|
|
106344
106497
|
confirmation.awaitingMarkerAppliedAt = null;
|
|
106345
106498
|
try {
|
|
106346
106499
|
await writeConfirmationState(statePath, stateObj, confirmation);
|
|
@@ -106369,9 +106522,9 @@ async function processAwaitingForIssue(issue2, deps) {
|
|
|
106369
106522
|
const layout = projectLayout(cwd2);
|
|
106370
106523
|
const changeDir = layout.changeDir(changeName);
|
|
106371
106524
|
const statePath = layout.stateFile(changeName);
|
|
106372
|
-
const tasks2 = await readTextOrNull(
|
|
106373
|
-
const proposal = await readTextOrNull(
|
|
106374
|
-
const design = await readTextOrNull(
|
|
106525
|
+
const tasks2 = await readTextOrNull(join25(changeDir, "tasks.md"));
|
|
106526
|
+
const proposal = await readTextOrNull(join25(changeDir, "proposal.md"));
|
|
106527
|
+
const design = await readTextOrNull(join25(changeDir, "design.md"));
|
|
106375
106528
|
let commentsCache = null;
|
|
106376
106529
|
const getComments = async () => {
|
|
106377
106530
|
if (commentsCache)
|
|
@@ -106455,6 +106608,7 @@ async function processAwaitingForIssue(issue2, deps) {
|
|
|
106455
106608
|
cfg,
|
|
106456
106609
|
onLog: deps.onLog
|
|
106457
106610
|
});
|
|
106611
|
+
await openDraftPrOnce(issue2, statePath, changeName, cwd2, { stateObj, confirmation }, { cfg, openDraftPr: deps.openDraftPr, onLog: deps.onLog });
|
|
106458
106612
|
const { stateObj: state2, confirmation: confirmation2 } = await readConfirmationState(statePath);
|
|
106459
106613
|
const { outcome, next } = await inspectAwaitingTicket(confirmation2, {
|
|
106460
106614
|
mentionHandle: cfg.linear.mentionHandle,
|
|
@@ -106529,6 +106683,7 @@ var init_awaiting = __esm(() => {
|
|
|
106529
106683
|
init_worktree();
|
|
106530
106684
|
init_scaffold();
|
|
106531
106685
|
init_linear();
|
|
106686
|
+
init_types2();
|
|
106532
106687
|
init_workflow();
|
|
106533
106688
|
init_state2();
|
|
106534
106689
|
init_inspect();
|
|
@@ -106603,9 +106758,26 @@ async function resolveDependencyBaseBranchImpl(issue2, runner, runnerCwd, deps)
|
|
|
106603
106758
|
}
|
|
106604
106759
|
return null;
|
|
106605
106760
|
}
|
|
106761
|
+
function createOpenDraftPr(deps) {
|
|
106762
|
+
const create3 = deps.createPr ?? createPullRequest;
|
|
106763
|
+
return async (issue2, changeName, cwd2) => {
|
|
106764
|
+
const branch = deps.branchByChange.get(changeName);
|
|
106765
|
+
if (!branch)
|
|
106766
|
+
return null;
|
|
106767
|
+
const base2 = baseBranchFromLabels(issue2.labels) ?? deps.prBaseBranch;
|
|
106768
|
+
const result2 = await create3({ cwd: cwd2, branch, issue: issue2, base: base2, draft: true }, deps.cmdRunner);
|
|
106769
|
+
const url2 = result2?.url ?? null;
|
|
106770
|
+
if (url2) {
|
|
106771
|
+
deps.prByChange.set(changeName, url2);
|
|
106772
|
+
deps.invalidatePrUrlForIssue(issue2.id);
|
|
106773
|
+
}
|
|
106774
|
+
return url2;
|
|
106775
|
+
};
|
|
106776
|
+
}
|
|
106606
106777
|
var GITHUB_PR_URL_RE, PR_NUMBER_RE, TICKET_IN_TITLE_RE;
|
|
106607
106778
|
var init_pr_helpers = __esm(() => {
|
|
106608
106779
|
init_linear();
|
|
106780
|
+
init_pr();
|
|
106609
106781
|
GITHUB_PR_URL_RE = /^https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+/;
|
|
106610
106782
|
PR_NUMBER_RE = /\/pull\/(\d+)/;
|
|
106611
106783
|
TICKET_IN_TITLE_RE = /^([A-Za-z][A-Za-z0-9]*-\d+)\b/;
|
|
@@ -106895,8 +107067,8 @@ var init_linear_resolvers = __esm(() => {
|
|
|
106895
107067
|
});
|
|
106896
107068
|
|
|
106897
107069
|
// apps/agent/src/agent/wire/prepare.ts
|
|
106898
|
-
import { mkdir as
|
|
106899
|
-
import { join as
|
|
107070
|
+
import { mkdir as mkdir8 } from "fs/promises";
|
|
107071
|
+
import { join as join26 } from "path";
|
|
106900
107072
|
function createPrepareHelpers(input) {
|
|
106901
107073
|
const {
|
|
106902
107074
|
args,
|
|
@@ -106960,7 +107132,7 @@ function createPrepareHelpers(input) {
|
|
|
106960
107132
|
let changeName;
|
|
106961
107133
|
const wtLayoutPre = projectLayout(workerCwd);
|
|
106962
107134
|
const derivedName = changeNameForIssue(issue2);
|
|
106963
|
-
const tasksMdPath =
|
|
107135
|
+
const tasksMdPath = join26(wtLayoutPre.changeDir(derivedName), "tasks.md");
|
|
106964
107136
|
const tasksMdExists = await Bun.file(tasksMdPath).exists();
|
|
106965
107137
|
const isFresh = !tasksMdExists;
|
|
106966
107138
|
if (isFresh) {
|
|
@@ -106999,8 +107171,8 @@ function createPrepareHelpers(input) {
|
|
|
106999
107171
|
changeName = await scaffoldChangeForIssue(scaffoldTasksDir, scaffoldStatesDir, issue2, comments, appendPrompt, attachments);
|
|
107000
107172
|
} else {
|
|
107001
107173
|
changeName = derivedName;
|
|
107002
|
-
await
|
|
107003
|
-
await
|
|
107174
|
+
await mkdir8(wtLayoutPre.changeDir(changeName), { recursive: true });
|
|
107175
|
+
await mkdir8(wtLayoutPre.taskStateDir(changeName), { recursive: true });
|
|
107004
107176
|
}
|
|
107005
107177
|
maps.cwdByChange.set(changeName, workerCwd);
|
|
107006
107178
|
maps.statesDirByChange.set(changeName, scaffoldStatesDir);
|
|
@@ -107038,7 +107210,7 @@ function createPrepareHelpers(input) {
|
|
|
107038
107210
|
if (!workerCwd)
|
|
107039
107211
|
return;
|
|
107040
107212
|
const wtLayout = projectLayout(workerCwd);
|
|
107041
|
-
const tasksFile =
|
|
107213
|
+
const tasksFile = join26(wtLayout.changeDir(changeName), AGENT_TASKS_FILENAME);
|
|
107042
107214
|
if (trigger === "review") {
|
|
107043
107215
|
let body2;
|
|
107044
107216
|
let heading;
|
|
@@ -107331,21 +107503,24 @@ var init_pr_discovery = __esm(() => {
|
|
|
107331
107503
|
});
|
|
107332
107504
|
|
|
107333
107505
|
// apps/agent/src/features/review-followup/scan.ts
|
|
107334
|
-
import { dirname as
|
|
107506
|
+
import { dirname as dirname11, join as join27 } from "path";
|
|
107335
107507
|
async function resolveReviewStateDir(changeName, deps) {
|
|
107336
107508
|
const root = deps.cwdOf(changeName);
|
|
107337
107509
|
if (root)
|
|
107338
|
-
return
|
|
107510
|
+
return dirname11(projectLayout(root).stateFile(changeName));
|
|
107339
107511
|
if (!deps.useWorktree)
|
|
107340
|
-
return
|
|
107341
|
-
const wtPath =
|
|
107512
|
+
return dirname11(projectLayout(deps.projectRoot).stateFile(changeName));
|
|
107513
|
+
const wtPath = join27(worktreesDir2(deps.projectRoot), changeName);
|
|
107342
107514
|
const statePath = projectLayout(wtPath).stateFile(changeName);
|
|
107343
107515
|
if (await Bun.file(statePath).exists())
|
|
107344
|
-
return
|
|
107516
|
+
return dirname11(statePath);
|
|
107345
107517
|
return null;
|
|
107346
107518
|
}
|
|
107347
107519
|
async function readReviewWatermark(stateDir) {
|
|
107348
|
-
const
|
|
107520
|
+
const sidecar = await readSlotSidecar(stateDir, "review");
|
|
107521
|
+
if (sidecar)
|
|
107522
|
+
return sidecar.lastConsumedCommentAt ?? null;
|
|
107523
|
+
const file2 = Bun.file(join27(stateDir, ".ralph-state.json"));
|
|
107349
107524
|
if (!await file2.exists())
|
|
107350
107525
|
return null;
|
|
107351
107526
|
try {
|
|
@@ -107557,7 +107732,7 @@ var init_github = __esm(() => {
|
|
|
107557
107732
|
|
|
107558
107733
|
// apps/agent/src/agent/wire/mention-scan.ts
|
|
107559
107734
|
import { readdir as readdir2 } from "fs/promises";
|
|
107560
|
-
import { join as
|
|
107735
|
+
import { join as join28 } from "path";
|
|
107561
107736
|
function createMentionScanner(input) {
|
|
107562
107737
|
const {
|
|
107563
107738
|
apiKey,
|
|
@@ -107723,7 +107898,7 @@ function createMentionScanner(input) {
|
|
|
107723
107898
|
async function isChangeArchivedForIssue(issue2, cwdByChange, projectRoot) {
|
|
107724
107899
|
const changeName = changeNameForIssue(issue2);
|
|
107725
107900
|
const root = cwdByChange.get(changeName) ?? projectRoot;
|
|
107726
|
-
const archiveDir =
|
|
107901
|
+
const archiveDir = join28(projectLayout(root).tasksDir, "archive");
|
|
107727
107902
|
let entries;
|
|
107728
107903
|
try {
|
|
107729
107904
|
entries = await readdir2(archiveDir);
|
|
@@ -107747,9 +107922,9 @@ var init_mention_scan = __esm(() => {
|
|
|
107747
107922
|
});
|
|
107748
107923
|
|
|
107749
107924
|
// apps/agent/src/agent/wire/spawn/default.ts
|
|
107750
|
-
import { join as
|
|
107925
|
+
import { join as join29 } from "path";
|
|
107751
107926
|
function defaultSpawn(changeName, cmd, cwd2, logsDir, onWorkerOutput, note) {
|
|
107752
|
-
const logFilePath =
|
|
107927
|
+
const logFilePath = join29(logsDir, `${changeName}.log`);
|
|
107753
107928
|
const ANSI_RE2 = /\x1b(?:\[[0-9;]*[A-Za-z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|.)/g;
|
|
107754
107929
|
const BOX_ONLY_RE = /^[\s\u2500\u2502\u256D\u256E\u2570\u256F\u254C\u2504\u2501\u2503]+$/;
|
|
107755
107930
|
const STATUS_BAR_LINE_RE = /^[\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827\u2807\u280F\u2713\u2717]\s+iter\s+\d+/;
|
|
@@ -107810,16 +107985,16 @@ var init_default2 = __esm(() => {
|
|
|
107810
107985
|
});
|
|
107811
107986
|
|
|
107812
107987
|
// apps/agent/src/agent/state/agent-run-state.ts
|
|
107813
|
-
import { basename as basename3, join as
|
|
107988
|
+
import { basename as basename3, join as join30 } from "path";
|
|
107814
107989
|
import { homedir as homedir6 } from "os";
|
|
107815
|
-
import { mkdir as
|
|
107990
|
+
import { mkdir as mkdir9, writeFile } from "fs/promises";
|
|
107816
107991
|
function agentRunStatePath(projectRoot) {
|
|
107817
|
-
return
|
|
107992
|
+
return join30(homedir6(), ".ralph", basename3(projectRoot), "agent-state.json");
|
|
107818
107993
|
}
|
|
107819
107994
|
async function writeAgentRunState(state) {
|
|
107820
107995
|
const path = agentRunStatePath(state.projectRoot);
|
|
107821
107996
|
try {
|
|
107822
|
-
await
|
|
107997
|
+
await mkdir9(join30(homedir6(), ".ralph", basename3(state.projectRoot)), { recursive: true });
|
|
107823
107998
|
await writeFile(path, JSON.stringify(state, null, 2) + `
|
|
107824
107999
|
`, "utf-8");
|
|
107825
108000
|
} catch {}
|
|
@@ -107845,18 +108020,18 @@ var CI_FAILED_EXIT2 = 70, PR_FAILED_EXIT2 = 71, NO_CHANGES_EXIT2 = 72;
|
|
|
107845
108020
|
|
|
107846
108021
|
// packages/retro/src/paths.ts
|
|
107847
108022
|
import { homedir as homedir7 } from "os";
|
|
107848
|
-
import { mkdir as
|
|
107849
|
-
import { join as
|
|
108023
|
+
import { mkdir as mkdir10 } from "fs/promises";
|
|
108024
|
+
import { join as join31 } from "path";
|
|
107850
108025
|
function retroDir() {
|
|
107851
|
-
return
|
|
108026
|
+
return join31(homedir7(), ".ralph", "retro");
|
|
107852
108027
|
}
|
|
107853
108028
|
async function resolveRetroOutputPath(identifier, date5, dir = retroDir()) {
|
|
107854
|
-
await
|
|
107855
|
-
const base2 =
|
|
108029
|
+
await mkdir10(dir, { recursive: true });
|
|
108030
|
+
const base2 = join31(dir, `${identifier}-${date5}.md`);
|
|
107856
108031
|
if (!await Bun.file(base2).exists())
|
|
107857
108032
|
return base2;
|
|
107858
108033
|
for (let n = 2;; n++) {
|
|
107859
|
-
const candidate =
|
|
108034
|
+
const candidate = join31(dir, `${identifier}-${date5}-${n}.md`);
|
|
107860
108035
|
if (!await Bun.file(candidate).exists())
|
|
107861
108036
|
return candidate;
|
|
107862
108037
|
}
|
|
@@ -107970,7 +108145,7 @@ var init_retro = __esm(() => {
|
|
|
107970
108145
|
});
|
|
107971
108146
|
|
|
107972
108147
|
// apps/agent/src/agent/wire/spawn/worker.ts
|
|
107973
|
-
import { join as
|
|
108148
|
+
import { join as join32 } from "path";
|
|
107974
108149
|
function localDateStamp(d) {
|
|
107975
108150
|
const y = d.getFullYear();
|
|
107976
108151
|
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
@@ -108005,6 +108180,8 @@ function createSpawnWorker(input) {
|
|
|
108005
108180
|
indicators,
|
|
108006
108181
|
cmdRunner,
|
|
108007
108182
|
gitRunner,
|
|
108183
|
+
applyIndicator,
|
|
108184
|
+
bus,
|
|
108008
108185
|
onLog,
|
|
108009
108186
|
diag,
|
|
108010
108187
|
runners,
|
|
@@ -108099,7 +108276,7 @@ function createSpawnWorker(input) {
|
|
|
108099
108276
|
paths: {
|
|
108100
108277
|
changeDir: info.changeDir,
|
|
108101
108278
|
stateFilePath: info.stateFilePath,
|
|
108102
|
-
logFile:
|
|
108279
|
+
logFile: join32(logsDir, `${info.changeName}.log`),
|
|
108103
108280
|
jsonLogFile: args.jsonLogFile ?? null,
|
|
108104
108281
|
agentStateFile: agentRunStatePath(projectRoot)
|
|
108105
108282
|
}
|
|
@@ -108116,7 +108293,7 @@ function createSpawnWorker(input) {
|
|
|
108116
108293
|
return function spawnWorker(changeName, _issue, trigger) {
|
|
108117
108294
|
const cwd2 = cwdByChange.get(changeName) ?? projectRoot;
|
|
108118
108295
|
const injected = runners?.spawnWorker;
|
|
108119
|
-
const missionTasksPath =
|
|
108296
|
+
const missionTasksPath = join32(projectLayout(cwd2).changeDir(changeName), MISSION_TASKS_FILENAME);
|
|
108120
108297
|
const prevTasksPromise = (async () => {
|
|
108121
108298
|
const f2 = Bun.file(missionTasksPath);
|
|
108122
108299
|
return await f2.exists() ? await f2.text() : "";
|
|
@@ -108124,7 +108301,7 @@ function createSpawnWorker(input) {
|
|
|
108124
108301
|
let logFilePath;
|
|
108125
108302
|
let handle;
|
|
108126
108303
|
if (injected) {
|
|
108127
|
-
logFilePath =
|
|
108304
|
+
logFilePath = join32(logsDir, `${changeName}.log`);
|
|
108128
108305
|
handle = injected(buildTaskCmdFor(changeName), cwd2);
|
|
108129
108306
|
} else {
|
|
108130
108307
|
const r = defaultSpawn(changeName, buildTaskCmdFor(changeName), cwd2, logsDir, onWorkerOutput, `spawn at ${new Date().toISOString()}`);
|
|
@@ -108146,7 +108323,7 @@ function createSpawnWorker(input) {
|
|
|
108146
108323
|
const wantAutoMerge = issueForChange ? issueMatchesGetIndicator(issueForChange, indicators.getAutoMerge) : false;
|
|
108147
108324
|
const wrapped = handle.exited.then(async (code) => {
|
|
108148
108325
|
const workerLayout = projectLayout(cwd2);
|
|
108149
|
-
const validateSpecPath =
|
|
108326
|
+
const validateSpecPath = join32(workerLayout.changeDir(changeName), "specs", "validate.md");
|
|
108150
108327
|
const hasValidateSpec = await Bun.file(validateSpecPath).exists();
|
|
108151
108328
|
const wantValidateOnly = hasValidateSpec && !wantPrBase;
|
|
108152
108329
|
if (hasValidateSpec) {
|
|
@@ -108233,6 +108410,23 @@ function createSpawnWorker(input) {
|
|
|
108233
108410
|
runScript,
|
|
108234
108411
|
...retroDepEntry(args.agentDebug, runRetrospectiveHook),
|
|
108235
108412
|
registerPr: (cn, url2) => onPrRegistered(cn, url2),
|
|
108413
|
+
...issueForChange && indicators.setPrReady ? {
|
|
108414
|
+
onPrReady: async () => {
|
|
108415
|
+
const issue2 = issueForChange;
|
|
108416
|
+
const marker = indicators.setPrReady;
|
|
108417
|
+
try {
|
|
108418
|
+
await applyIndicator(issue2, marker);
|
|
108419
|
+
onLog(` ${issue2.identifier}: setPrReady applied`, "gray");
|
|
108420
|
+
} catch (err) {
|
|
108421
|
+
onLog(`! Linear setPrReady failed for ${issue2.identifier}: ${err.message}`, "yellow");
|
|
108422
|
+
emitCapture(bus, "agent_indicator_failed", {
|
|
108423
|
+
indicator: "setPrReady",
|
|
108424
|
+
issue_identifier: issue2.identifier,
|
|
108425
|
+
error: err.message
|
|
108426
|
+
});
|
|
108427
|
+
}
|
|
108428
|
+
}
|
|
108429
|
+
} : {},
|
|
108236
108430
|
...onWorkerPhase && {
|
|
108237
108431
|
onPhase: (phase2, detail) => onWorkerPhase(changeName, phase2, detail)
|
|
108238
108432
|
},
|
|
@@ -108270,6 +108464,7 @@ var init_worker = __esm(() => {
|
|
|
108270
108464
|
init_agent_run_state();
|
|
108271
108465
|
init_retro();
|
|
108272
108466
|
init_engine();
|
|
108467
|
+
init_coordinator();
|
|
108273
108468
|
});
|
|
108274
108469
|
|
|
108275
108470
|
// apps/agent/src/agent/baseline/runner.ts
|
|
@@ -108612,44 +108807,33 @@ var init_linear_sync = __esm(() => {
|
|
|
108612
108807
|
});
|
|
108613
108808
|
|
|
108614
108809
|
// apps/agent/src/agent/linear-sync/comment-sync.ts
|
|
108615
|
-
import { dirname as
|
|
108616
|
-
|
|
108617
|
-
async function readStateJson(statePath) {
|
|
108810
|
+
import { dirname as dirname12, join as join33 } from "path";
|
|
108811
|
+
async function readInlineLinearComments(statePath) {
|
|
108618
108812
|
const file2 = Bun.file(statePath);
|
|
108619
108813
|
if (!await file2.exists())
|
|
108620
|
-
return
|
|
108814
|
+
return;
|
|
108621
108815
|
try {
|
|
108622
|
-
|
|
108816
|
+
const obj = await file2.json();
|
|
108817
|
+
return obj.linearComments ?? undefined;
|
|
108623
108818
|
} 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;
|
|
108819
|
+
return;
|
|
108637
108820
|
}
|
|
108638
108821
|
}
|
|
108639
|
-
function readComments(
|
|
108640
|
-
const
|
|
108822
|
+
async function readComments(statePath) {
|
|
108823
|
+
const changeDir = dirname12(statePath);
|
|
108824
|
+
const raw = await readSlotSidecar(changeDir, "linearComments") ?? await readInlineLinearComments(statePath) ?? {};
|
|
108825
|
+
const r = raw;
|
|
108641
108826
|
return {
|
|
108642
|
-
planCommentId:
|
|
108643
|
-
tasksCommentId:
|
|
108644
|
-
planPostedAt:
|
|
108645
|
-
tasksCommentSha256:
|
|
108827
|
+
planCommentId: r.planCommentId ?? null,
|
|
108828
|
+
tasksCommentId: r.tasksCommentId ?? null,
|
|
108829
|
+
planPostedAt: r.planPostedAt ?? null,
|
|
108830
|
+
tasksCommentSha256: r.tasksCommentSha256 ?? null
|
|
108646
108831
|
};
|
|
108647
108832
|
}
|
|
108648
108833
|
async function patchComments(statePath, patch) {
|
|
108649
|
-
const
|
|
108650
|
-
const current = readComments(existing);
|
|
108834
|
+
const current = await readComments(statePath);
|
|
108651
108835
|
const next = { ...current, ...patch };
|
|
108652
|
-
await
|
|
108836
|
+
await writeSlotField(dirname12(statePath), "linearComments", next);
|
|
108653
108837
|
}
|
|
108654
108838
|
function isCommentNotFoundError(err) {
|
|
108655
108839
|
if (!err)
|
|
@@ -108664,7 +108848,7 @@ function isCommentNotFoundError(err) {
|
|
|
108664
108848
|
return text.includes("not found") || text.includes("could not find") || text.includes("entity not found");
|
|
108665
108849
|
}
|
|
108666
108850
|
async function readTasksMd(changeDir, log3) {
|
|
108667
|
-
const file2 = Bun.file(
|
|
108851
|
+
const file2 = Bun.file(join33(changeDir, "tasks.md"));
|
|
108668
108852
|
if (!await file2.exists()) {
|
|
108669
108853
|
log3(` comment-sync: tasks.md missing in ${changeDir}, skipping`, "gray");
|
|
108670
108854
|
return null;
|
|
@@ -108685,8 +108869,7 @@ async function postOrUpdateTasksComment(deps) {
|
|
|
108685
108869
|
return null;
|
|
108686
108870
|
const body = renderTasksCommentBody(tasksMd, deps.changeName, deps.iteration);
|
|
108687
108871
|
const hash2 = sha256Hex(tasksMd);
|
|
108688
|
-
const
|
|
108689
|
-
const comments = readComments(state);
|
|
108872
|
+
const comments = await readComments(deps.statePath);
|
|
108690
108873
|
if (comments.tasksCommentId) {
|
|
108691
108874
|
if (comments.tasksCommentSha256 === hash2) {
|
|
108692
108875
|
deps.log(` comment-sync: tasks.md unchanged for ${deps.changeName}, skipping`, "gray");
|
|
@@ -108762,8 +108945,7 @@ async function readSection(path, heading) {
|
|
|
108762
108945
|
return body.trim() || null;
|
|
108763
108946
|
}
|
|
108764
108947
|
async function postPlanCommentOnce(deps) {
|
|
108765
|
-
const
|
|
108766
|
-
const comments = readComments(state);
|
|
108948
|
+
const comments = await readComments(deps.statePath);
|
|
108767
108949
|
if (comments.planCommentId)
|
|
108768
108950
|
return null;
|
|
108769
108951
|
const tasksMd = await readTasksMd(deps.changeDir, deps.log);
|
|
@@ -108772,14 +108954,14 @@ async function postPlanCommentOnce(deps) {
|
|
|
108772
108954
|
const check2 = parsePlanningSection(tasksMd);
|
|
108773
108955
|
if (!check2.allChecked)
|
|
108774
108956
|
return null;
|
|
108775
|
-
const proposalPath =
|
|
108957
|
+
const proposalPath = join33(deps.changeDir, "proposal.md");
|
|
108776
108958
|
const why = await readSection(proposalPath, "Why");
|
|
108777
108959
|
const whatChanges = await readSection(proposalPath, "What Changes");
|
|
108778
108960
|
if (!why && !whatChanges) {
|
|
108779
108961
|
deps.log(` comment-sync: proposal.md has no Why/What Changes, skipping plan comment`, "gray");
|
|
108780
108962
|
return null;
|
|
108781
108963
|
}
|
|
108782
|
-
const designSummary = await readFirstParagraph(
|
|
108964
|
+
const designSummary = await readFirstParagraph(join33(deps.changeDir, "design.md"));
|
|
108783
108965
|
const parts = [`### ${PLAN_COMMENT_TITLE} \u2014 \`${deps.changeName}\``];
|
|
108784
108966
|
if (why) {
|
|
108785
108967
|
parts.push("", "**Why**", "", why);
|
|
@@ -108817,8 +108999,7 @@ ${deps.message.trim()}`;
|
|
|
108817
108999
|
} catch (err) {
|
|
108818
109000
|
deps.log(`! comment-sync: steering comment create failed: ${err.message}`, "yellow");
|
|
108819
109001
|
}
|
|
108820
|
-
const
|
|
108821
|
-
const comments = readComments(state);
|
|
109002
|
+
const comments = await readComments(deps.statePath);
|
|
108822
109003
|
if (comments.tasksCommentId) {
|
|
108823
109004
|
try {
|
|
108824
109005
|
await deps.mutations.deleteIssueComment(deps.apiKey, comments.tasksCommentId);
|
|
@@ -108841,8 +109022,9 @@ ${deps.message.trim()}`;
|
|
|
108841
109022
|
iteration: deps.iteration
|
|
108842
109023
|
});
|
|
108843
109024
|
}
|
|
108844
|
-
var PLAN_COMMENT_TITLE = "\uD83D\uDCCB Ralph plan", STEERING_COMMENT_TITLE = "\uD83E\uDDED Ralph steering"
|
|
109025
|
+
var PLAN_COMMENT_TITLE = "\uD83D\uDCCB Ralph plan", STEERING_COMMENT_TITLE = "\uD83E\uDDED Ralph steering";
|
|
108845
109026
|
var init_comment_sync = __esm(() => {
|
|
109027
|
+
init_store();
|
|
108846
109028
|
init_linear_sync();
|
|
108847
109029
|
});
|
|
108848
109030
|
|
|
@@ -261055,7 +261237,7 @@ var init_render_pdf = __esm(() => {
|
|
|
261055
261237
|
});
|
|
261056
261238
|
|
|
261057
261239
|
// apps/agent/src/agent/linear-sync/spec-attachments.ts
|
|
261058
|
-
import { dirname as
|
|
261240
|
+
import { dirname as dirname13, join as join34 } from "path";
|
|
261059
261241
|
function describeLinearError(err) {
|
|
261060
261242
|
const e = err;
|
|
261061
261243
|
const parts = [e.message ?? String(err)];
|
|
@@ -261070,25 +261252,29 @@ function describeLinearError(err) {
|
|
|
261070
261252
|
return parts.join(" ");
|
|
261071
261253
|
}
|
|
261072
261254
|
function stateDirOf(statePath) {
|
|
261073
|
-
return
|
|
261255
|
+
return dirname13(statePath);
|
|
261074
261256
|
}
|
|
261075
|
-
async function
|
|
261257
|
+
async function readInlineSpecAttachments(statePath) {
|
|
261076
261258
|
const file2 = Bun.file(statePath);
|
|
261077
261259
|
if (!await file2.exists())
|
|
261078
261260
|
return {};
|
|
261079
261261
|
try {
|
|
261080
261262
|
const parsed = await file2.json();
|
|
261081
261263
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
261082
|
-
|
|
261264
|
+
const sa = parsed.specAttachments;
|
|
261265
|
+
return sa && typeof sa === "object" && !Array.isArray(sa) ? sa : {};
|
|
261083
261266
|
}
|
|
261084
261267
|
return {};
|
|
261085
261268
|
} catch {
|
|
261086
261269
|
return {};
|
|
261087
261270
|
}
|
|
261088
261271
|
}
|
|
261272
|
+
async function readSpecAttachmentsSubtree(statePath) {
|
|
261273
|
+
const sidecar = await readSlotSidecar(dirname13(statePath), "specAttachments");
|
|
261274
|
+
return sidecar ?? await readInlineSpecAttachments(statePath);
|
|
261275
|
+
}
|
|
261089
261276
|
async function readSpecAttachments(statePath) {
|
|
261090
|
-
const
|
|
261091
|
-
const sa = raw.specAttachments ?? {};
|
|
261277
|
+
const sa = await readSpecAttachmentsSubtree(statePath);
|
|
261092
261278
|
return {
|
|
261093
261279
|
proposal: {
|
|
261094
261280
|
attachmentId: sa.proposal?.attachmentId ?? null,
|
|
@@ -261142,12 +261328,25 @@ function hasMeaningfulContent(bytes) {
|
|
|
261142
261328
|
}
|
|
261143
261329
|
return false;
|
|
261144
261330
|
}
|
|
261331
|
+
function extractImplementationSection(tasksMarkdown) {
|
|
261332
|
+
const captured = [];
|
|
261333
|
+
let capturing = false;
|
|
261334
|
+
for (const line of tasksMarkdown.split(/\r?\n/)) {
|
|
261335
|
+
const heading = /^##\s+(.+?)\s*$/.exec(line)?.[1];
|
|
261336
|
+
if (heading !== undefined)
|
|
261337
|
+
capturing = heading.trim().toLowerCase() === "implementation";
|
|
261338
|
+
if (capturing)
|
|
261339
|
+
captured.push(line);
|
|
261340
|
+
}
|
|
261341
|
+
return captured.join(`
|
|
261342
|
+
`).trim();
|
|
261343
|
+
}
|
|
261145
261344
|
async function syncSlot(deps, slot) {
|
|
261146
261345
|
const spec = SLOT_SPECS[slot];
|
|
261147
261346
|
const [primaryName, ...trailingNames] = spec.sourceFiles;
|
|
261148
261347
|
if (!primaryName)
|
|
261149
261348
|
return;
|
|
261150
|
-
const primary = Bun.file(
|
|
261349
|
+
const primary = Bun.file(join34(deps.changeDir, primaryName));
|
|
261151
261350
|
if (!await primary.exists()) {
|
|
261152
261351
|
deps.log(` spec-attachments: ${primaryName} missing, skipping`, "gray");
|
|
261153
261352
|
return;
|
|
@@ -261166,24 +261365,28 @@ async function syncSlot(deps, slot) {
|
|
|
261166
261365
|
const parts = [primaryBytes];
|
|
261167
261366
|
const enc = new TextEncoder;
|
|
261168
261367
|
for (const name of trailingNames) {
|
|
261169
|
-
const f2 = Bun.file(
|
|
261368
|
+
const f2 = Bun.file(join34(deps.changeDir, name));
|
|
261170
261369
|
if (!await f2.exists())
|
|
261171
261370
|
continue;
|
|
261371
|
+
let raw;
|
|
261172
261372
|
try {
|
|
261173
|
-
|
|
261174
|
-
|
|
261175
|
-
|
|
261176
|
-
|
|
261373
|
+
raw = await f2.bytes();
|
|
261374
|
+
} catch (err) {
|
|
261375
|
+
deps.log(`! spec-attachments: read ${name} failed (continuing without it): ${err.message}`, "yellow");
|
|
261376
|
+
continue;
|
|
261377
|
+
}
|
|
261378
|
+
if (raw.length === 0)
|
|
261379
|
+
continue;
|
|
261380
|
+
const decoded = new TextDecoder().decode(raw);
|
|
261381
|
+
const body = name === "tasks.md" ? extractImplementationSection(decoded) : decoded.trim();
|
|
261382
|
+
if (!body)
|
|
261383
|
+
continue;
|
|
261384
|
+
parts.push(enc.encode(`
|
|
261177
261385
|
|
|
261178
261386
|
---
|
|
261179
261387
|
|
|
261180
|
-
|
|
261181
|
-
|
|
261388
|
+
${body}
|
|
261182
261389
|
`));
|
|
261183
|
-
parts.push(bytes);
|
|
261184
|
-
} catch (err) {
|
|
261185
|
-
deps.log(`! spec-attachments: read ${name} failed (continuing without it): ${err.message}`, "yellow");
|
|
261186
|
-
}
|
|
261187
261390
|
}
|
|
261188
261391
|
const totalLen = parts.reduce((n, p) => n + p.length, 0);
|
|
261189
261392
|
const sourceBytes = new Uint8Array(totalLen);
|
|
@@ -261252,8 +261455,7 @@ async function syncSlot(deps, slot) {
|
|
|
261252
261455
|
deps.log(` spec-attachments: created ${spec.uploadFilename} attachment`, "gray");
|
|
261253
261456
|
}
|
|
261254
261457
|
async function purgeLegacyProposalSlots(deps) {
|
|
261255
|
-
const
|
|
261256
|
-
const sa = raw.specAttachments ?? {};
|
|
261458
|
+
const sa = await readSpecAttachmentsSubtree(deps.statePath);
|
|
261257
261459
|
if (sa.legacyProposalPurged === true)
|
|
261258
261460
|
return;
|
|
261259
261461
|
const state = await readSpecAttachments(deps.statePath);
|
|
@@ -261431,9 +261633,9 @@ var init_comment_sync2 = __esm(() => {
|
|
|
261431
261633
|
});
|
|
261432
261634
|
|
|
261433
261635
|
// apps/agent/src/features/pr-tracker/state.ts
|
|
261434
|
-
import { join as
|
|
261636
|
+
import { join as join35 } from "path";
|
|
261435
261637
|
async function readState2(projectRoot) {
|
|
261436
|
-
const path =
|
|
261638
|
+
const path = join35(projectRoot, PR_TRACKER_STATE_RELPATH);
|
|
261437
261639
|
const file2 = Bun.file(path);
|
|
261438
261640
|
if (!await file2.exists())
|
|
261439
261641
|
return {};
|
|
@@ -261449,7 +261651,7 @@ async function readState2(projectRoot) {
|
|
|
261449
261651
|
}
|
|
261450
261652
|
}
|
|
261451
261653
|
async function writeState2(projectRoot, state) {
|
|
261452
|
-
const path =
|
|
261654
|
+
const path = join35(projectRoot, PR_TRACKER_STATE_RELPATH);
|
|
261453
261655
|
await Bun.write(path, JSON.stringify(state, null, 2));
|
|
261454
261656
|
}
|
|
261455
261657
|
var PR_TRACKER_STATE_RELPATH = ".ralph/pr-tracker-state.json";
|
|
@@ -261528,7 +261730,7 @@ var init_pr_tracker = __esm(() => {
|
|
|
261528
261730
|
});
|
|
261529
261731
|
|
|
261530
261732
|
// apps/agent/src/agent/wire.ts
|
|
261531
|
-
import { join as
|
|
261733
|
+
import { join as join36 } from "path";
|
|
261532
261734
|
function buildAgentCoordinator(input) {
|
|
261533
261735
|
const {
|
|
261534
261736
|
args,
|
|
@@ -261547,7 +261749,7 @@ function buildAgentCoordinator(input) {
|
|
|
261547
261749
|
onWorkerCmd,
|
|
261548
261750
|
onAwaitingTicket
|
|
261549
261751
|
} = input;
|
|
261550
|
-
const logsDir =
|
|
261752
|
+
const logsDir = join36(projectRoot, ".ralph", "logs");
|
|
261551
261753
|
const bus = createBus();
|
|
261552
261754
|
subscribeAgentDiag(bus, onLog);
|
|
261553
261755
|
const diag = (area, message, color) => {
|
|
@@ -261657,6 +261859,8 @@ function buildAgentCoordinator(input) {
|
|
|
261657
261859
|
indicators,
|
|
261658
261860
|
cmdRunner,
|
|
261659
261861
|
gitRunner,
|
|
261862
|
+
applyIndicator: resolvers.applyIndicator,
|
|
261863
|
+
bus,
|
|
261660
261864
|
onLog,
|
|
261661
261865
|
diag,
|
|
261662
261866
|
runners: input.runners,
|
|
@@ -261682,6 +261886,13 @@ function buildAgentCoordinator(input) {
|
|
|
261682
261886
|
...onWorkerOutput ? { onWorkerOutput } : {},
|
|
261683
261887
|
...onWorkerCmd ? { onWorkerCmd } : {}
|
|
261684
261888
|
});
|
|
261889
|
+
const openDraftPr = createOpenDraftPr({
|
|
261890
|
+
branchByChange,
|
|
261891
|
+
prByChange,
|
|
261892
|
+
cmdRunner,
|
|
261893
|
+
prBaseBranch: cfg.prBaseBranch,
|
|
261894
|
+
invalidatePrUrlForIssue: (issueId) => prDiscovery.invalidatePrUrlForIssue(issueId)
|
|
261895
|
+
});
|
|
261685
261896
|
const confirmationCaps = {
|
|
261686
261897
|
detect: (issue2) => processAwaitingForIssue(issue2, {
|
|
261687
261898
|
cfg,
|
|
@@ -261694,6 +261905,7 @@ function buildAgentCoordinator(input) {
|
|
|
261694
261905
|
reapForAwaiting: (cn) => coordRef.current?.reapForAwaiting(cn),
|
|
261695
261906
|
applyIndicator: resolvers.applyIndicator,
|
|
261696
261907
|
applyMarker: resolvers.applyMarker,
|
|
261908
|
+
openDraftPr,
|
|
261697
261909
|
...onAwaitingTicket ? { onAwaitingTicket } : {},
|
|
261698
261910
|
onLog
|
|
261699
261911
|
}),
|
|
@@ -261768,7 +261980,7 @@ function buildAgentCoordinator(input) {
|
|
|
261768
261980
|
const changeDir = projectLayout(root).changeDir(changeName);
|
|
261769
261981
|
const parts = [];
|
|
261770
261982
|
for (const name of ["tasks.md", "proposal.md", "design.md"]) {
|
|
261771
|
-
const file2 = Bun.file(
|
|
261983
|
+
const file2 = Bun.file(join36(changeDir, name));
|
|
261772
261984
|
if (!await file2.exists())
|
|
261773
261985
|
continue;
|
|
261774
261986
|
parts.push(`${name}:${file2.lastModified}:${file2.size}`);
|
|
@@ -261813,7 +262025,7 @@ function buildAgentCoordinator(input) {
|
|
|
261813
262025
|
getGaveUpTotal: async () => {
|
|
261814
262026
|
let total = 0;
|
|
261815
262027
|
for (const [changeName, root] of cwdByChange) {
|
|
261816
|
-
const file2 = Bun.file(
|
|
262028
|
+
const file2 = Bun.file(join36(projectLayout(root).taskStateDir(changeName), GAVEUP_COUNT_FILE));
|
|
261817
262029
|
if (!await file2.exists())
|
|
261818
262030
|
continue;
|
|
261819
262031
|
try {
|
|
@@ -261848,14 +262060,14 @@ var init_wire = __esm(() => {
|
|
|
261848
262060
|
});
|
|
261849
262061
|
|
|
261850
262062
|
// apps/agent/src/agent/json-log/json-log-file.ts
|
|
261851
|
-
import { mkdir as
|
|
261852
|
-
import { dirname as
|
|
262063
|
+
import { mkdir as mkdir11, appendFile as appendFile2 } from "fs/promises";
|
|
262064
|
+
import { dirname as dirname14 } from "path";
|
|
261853
262065
|
function createJsonLogFileSink(path) {
|
|
261854
262066
|
if (!path)
|
|
261855
262067
|
return { emit: () => {} };
|
|
261856
262068
|
let chain = (async () => {
|
|
261857
262069
|
try {
|
|
261858
|
-
await
|
|
262070
|
+
await mkdir11(dirname14(path), { recursive: true });
|
|
261859
262071
|
await Bun.write(path, "");
|
|
261860
262072
|
} catch {}
|
|
261861
262073
|
})();
|
|
@@ -262101,7 +262313,7 @@ var init_output_utils = __esm(() => {
|
|
|
262101
262313
|
});
|
|
262102
262314
|
|
|
262103
262315
|
// apps/agent/src/agent/state/worker-state-poll.ts
|
|
262104
|
-
import { join as
|
|
262316
|
+
import { join as join37 } from "path";
|
|
262105
262317
|
function parseSubtasks(tasksMd) {
|
|
262106
262318
|
const out = [];
|
|
262107
262319
|
let skipSection = false;
|
|
@@ -262134,7 +262346,7 @@ function initialWorkerSnapshot() {
|
|
|
262134
262346
|
async function readWorkerSnapshot(input) {
|
|
262135
262347
|
const next = { ...input.prev };
|
|
262136
262348
|
try {
|
|
262137
|
-
const file2 = Bun.file(
|
|
262349
|
+
const file2 = Bun.file(join37(input.statesDir, input.changeName, ".ralph-state.json"));
|
|
262138
262350
|
if (await file2.exists()) {
|
|
262139
262351
|
const json2 = await file2.json();
|
|
262140
262352
|
next.iter = json2.iteration ?? next.iter;
|
|
@@ -262143,10 +262355,10 @@ async function readWorkerSnapshot(input) {
|
|
|
262143
262355
|
} catch {}
|
|
262144
262356
|
if (input.changeDir) {
|
|
262145
262357
|
try {
|
|
262146
|
-
const tasksFile = Bun.file(
|
|
262147
|
-
const proposalFile = Bun.file(
|
|
262148
|
-
const designFile = Bun.file(
|
|
262149
|
-
const reviewFindingsFile = Bun.file(
|
|
262358
|
+
const tasksFile = Bun.file(join37(input.changeDir, "tasks.md"));
|
|
262359
|
+
const proposalFile = Bun.file(join37(input.changeDir, "proposal.md"));
|
|
262360
|
+
const designFile = Bun.file(join37(input.changeDir, "design.md"));
|
|
262361
|
+
const reviewFindingsFile = Bun.file(join37(input.changeDir, "review-findings.md"));
|
|
262150
262362
|
const [tasksText, proposalText, designText, reviewFindingsText] = await Promise.all([
|
|
262151
262363
|
tasksFile.exists().then((ok) => ok ? tasksFile.text() : null),
|
|
262152
262364
|
proposalFile.exists().then((ok) => ok ? proposalFile.text() : null),
|
|
@@ -262209,7 +262421,7 @@ var init_worker_state_poll = __esm(() => {
|
|
|
262209
262421
|
});
|
|
262210
262422
|
|
|
262211
262423
|
// apps/agent/src/components/AgentMode.tsx
|
|
262212
|
-
import { join as
|
|
262424
|
+
import { join as join38 } from "path";
|
|
262213
262425
|
async function appendSteeringImpl(changeDir, message) {
|
|
262214
262426
|
await runWithContext(createDefaultContext(), async () => {
|
|
262215
262427
|
appendSteeringMessage(changeDir, message);
|
|
@@ -263782,7 +263994,7 @@ function AgentMode({
|
|
|
263782
263994
|
},
|
|
263783
263995
|
onSubmit: async (message) => {
|
|
263784
263996
|
try {
|
|
263785
|
-
await appendSteering2(
|
|
263997
|
+
await appendSteering2(join38(tasksDir, w2.changeName), message);
|
|
263786
263998
|
fileEmit({ type: "steering_submitted", changeName: w2.changeName, message });
|
|
263787
263999
|
} catch (err) {
|
|
263788
264000
|
const text = err.message;
|
|
@@ -264086,7 +264298,7 @@ __export(exports_list, {
|
|
|
264086
264298
|
buildBuckets: () => buildBuckets,
|
|
264087
264299
|
backlogRankByIssueId: () => backlogRankByIssueId
|
|
264088
264300
|
});
|
|
264089
|
-
import { join as
|
|
264301
|
+
import { join as join39 } from "path";
|
|
264090
264302
|
function countTaskItems(content) {
|
|
264091
264303
|
const checked = (content.match(/^- \[x\]/gm) ?? []).length;
|
|
264092
264304
|
const unchecked = (content.match(/^- \[ \]/gm) ?? []).length;
|
|
@@ -264102,13 +264314,13 @@ function buildLocalRows() {
|
|
|
264102
264314
|
const sources = [{ dir: statesDir, label: "main" }];
|
|
264103
264315
|
const worktreesRoot = worktreesDir2(projectRoot);
|
|
264104
264316
|
for (const wt of storage.list(worktreesRoot)) {
|
|
264105
|
-
sources.push({ dir:
|
|
264317
|
+
sources.push({ dir: join39(worktreesRoot, wt, ".ralph", "tasks"), label: `wt:${wt}` });
|
|
264106
264318
|
}
|
|
264107
264319
|
for (const { dir, label } of sources) {
|
|
264108
264320
|
for (const entry of storage.list(dir)) {
|
|
264109
264321
|
if (seen.has(entry))
|
|
264110
264322
|
continue;
|
|
264111
|
-
const raw = storage.read(
|
|
264323
|
+
const raw = storage.read(join39(dir, entry, ".ralph-state.json"));
|
|
264112
264324
|
if (raw === null)
|
|
264113
264325
|
continue;
|
|
264114
264326
|
let state;
|
|
@@ -264123,7 +264335,7 @@ function buildLocalRows() {
|
|
|
264123
264335
|
const firstLine = promptRaw.split(`
|
|
264124
264336
|
`).find((l3) => l3.trim() !== "") ?? "";
|
|
264125
264337
|
let progress = "\u2014";
|
|
264126
|
-
const tasksContent = storage.read(
|
|
264338
|
+
const tasksContent = storage.read(join39(dir, entry, "tasks.md"));
|
|
264127
264339
|
if (tasksContent !== null) {
|
|
264128
264340
|
const { checked, unchecked } = countTaskItems(tasksContent);
|
|
264129
264341
|
const total = checked + unchecked;
|
|
@@ -264623,8 +264835,8 @@ var exports_json_runner = {};
|
|
|
264623
264835
|
__export(exports_json_runner, {
|
|
264624
264836
|
runAgentJson: () => runAgentJson
|
|
264625
264837
|
});
|
|
264626
|
-
import { join as
|
|
264627
|
-
import { mkdir as
|
|
264838
|
+
import { join as join40 } from "path";
|
|
264839
|
+
import { mkdir as mkdir12 } from "fs/promises";
|
|
264628
264840
|
import { homedir as homedir8 } from "os";
|
|
264629
264841
|
function makeEmit(fileSink) {
|
|
264630
264842
|
return (event) => {
|
|
@@ -264645,7 +264857,7 @@ async function runAgentJson({
|
|
|
264645
264857
|
tasksDir,
|
|
264646
264858
|
runPreflight: runPreflight2 = runPreflight
|
|
264647
264859
|
}) {
|
|
264648
|
-
await
|
|
264860
|
+
await mkdir12(join40(homedir8(), ".ralph"), { recursive: true }).catch(() => {
|
|
264649
264861
|
return;
|
|
264650
264862
|
});
|
|
264651
264863
|
const fileSink = createJsonLogFileSink(args.jsonLogFile);
|
|
@@ -264861,8 +265073,8 @@ var exports_src3 = {};
|
|
|
264861
265073
|
__export(exports_src3, {
|
|
264862
265074
|
main: () => main3
|
|
264863
265075
|
});
|
|
264864
|
-
import { mkdir as
|
|
264865
|
-
import { join as
|
|
265076
|
+
import { mkdir as mkdir13 } from "fs/promises";
|
|
265077
|
+
import { join as join41 } from "path";
|
|
264866
265078
|
async function main3(argv) {
|
|
264867
265079
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
264868
265080
|
printAgentHelp();
|
|
@@ -264926,9 +265138,9 @@ async function main3(argv) {
|
|
|
264926
265138
|
return 1;
|
|
264927
265139
|
}
|
|
264928
265140
|
}
|
|
264929
|
-
await
|
|
264930
|
-
await
|
|
264931
|
-
await
|
|
265141
|
+
await mkdir13(statesDir, { recursive: true });
|
|
265142
|
+
await mkdir13(tasksDir, { recursive: true });
|
|
265143
|
+
await mkdir13(join41(projectRoot, ".ralph"), { recursive: true });
|
|
264932
265144
|
if (shouldFallbackToJsonOutput(args, process.stdin.isTTY)) {
|
|
264933
265145
|
process.stderr.write(`agent: stdin is not a TTY \u2014 falling back to --json-output mode.
|
|
264934
265146
|
`);
|