@neriros/ralphy 3.10.6 → 3.10.7

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