@open-tomato/rafa 0.40.0 → 0.41.0

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.
@@ -39,6 +39,7 @@ import {
39
39
  NO_NEXT_STEP,
40
40
  NoSessionRefusal,
41
41
  ORIGIN_FIELD,
42
+ PLACEHOLDER_REASON,
42
43
  PREVIOUS_COPY_WARN_ABOVE,
43
44
  PROJECT_SCOPE_FIX,
44
45
  PROVENANCE_FIELD,
@@ -93,6 +94,7 @@ import {
93
94
  branchName,
94
95
  buildReport,
95
96
  buildSettle,
97
+ buildVerifyPrompt,
96
98
  bundledAgentsDirectory,
97
99
  byName,
98
100
  cancelMoveReason,
@@ -134,6 +136,7 @@ import {
134
136
  createPlanDirNames,
135
137
  createPlanRefsVerifier,
136
138
  createStartPreflightClaim,
139
+ criteriaToAsk,
137
140
  crossRepositoryRefusal,
138
141
  defaultCleanupSeams,
139
142
  developmentApplied,
@@ -150,9 +153,10 @@ import {
150
153
  epicTickSentence,
151
154
  estimateEta,
152
155
  etaLine,
153
- eventsFilePath,
156
+ eventsFileOf,
154
157
  expectTwoArguments,
155
158
  failingRows,
159
+ fenceFor,
156
160
  fetchClaimBranches,
157
161
  findReadinessGaps,
158
162
  fingerprintText,
@@ -235,6 +239,7 @@ import {
235
239
  parseRoadmapBody,
236
240
  parseSemanticVersion,
237
241
  parseTreePaths,
242
+ parseVerifyPlan,
238
243
  parseWorkingTree,
239
244
  parseWorktrees,
240
245
  pathDirectories,
@@ -273,6 +278,7 @@ import {
273
278
  readEpicDependents,
274
279
  readEpicProblems,
275
280
  readEpics,
281
+ readEventsFrom,
276
282
  readGuard,
277
283
  readHomeAfterLoop,
278
284
  readIssueFacts,
@@ -303,6 +309,7 @@ import {
303
309
  readTextFlag,
304
310
  readTrees,
305
311
  readTrendInputs,
312
+ readVerifyPrompt,
306
313
  readWaiting,
307
314
  ready_default,
308
315
  receiptProblem,
@@ -320,6 +327,7 @@ import {
320
327
  renderDependentComment,
321
328
  renderDoctorEffortSync,
322
329
  renderDoctorRefs,
330
+ renderEpicBody,
323
331
  renderHorizonComment,
324
332
  renderMoveComment,
325
333
  renderParkedPullRequestComment,
@@ -367,6 +375,7 @@ import {
367
375
  skillItem,
368
376
  skippedFieldWarning,
369
377
  sourceItem,
378
+ splitCriteria,
370
379
  stackPaths,
371
380
  stacksFromEccSkillName,
372
381
  stageOf,
@@ -400,7 +409,7 @@ import {
400
409
  worktreesHolding,
401
410
  writeSession,
402
411
  writeShippedFile
403
- } from "./index-zcbj07aq.js";
412
+ } from "./index-wjsarzf5.js";
404
413
  import {
405
414
  PROBE_TIMEOUT_MS,
406
415
  loadPlanPrerequisites,
@@ -412,7 +421,7 @@ import {
412
421
  runPreflight,
413
422
  splitBlockerComment,
414
423
  trackerPathFor
415
- } from "./index-krpmxpkh.js";
424
+ } from "./index-zg0qsb5b.js";
416
425
  import {
417
426
  ACTION_HEADING,
418
427
  CAUSE_HEADING,
@@ -523,7 +532,7 @@ import {
523
532
  versionLine,
524
533
  writeFrontmatter,
525
534
  writePositionFile
526
- } from "./index-8veaj0t9.js";
535
+ } from "./index-cd0wzc12.js";
527
536
  import {
528
537
  CONFIG_DEFAULTS,
529
538
  CONFIG_FILE,
@@ -542,7 +551,7 @@ import {
542
551
  readItemBytes,
543
552
  readRafaBlocks,
544
553
  resolveTiers
545
- } from "./index-334y39tn.js";
554
+ } from "./index-3jpz9nr3.js";
546
555
  import {
547
556
  promotable
548
557
  } from "./index-d6d0942q.js";
@@ -2168,7 +2177,7 @@ import { join as join4, relative as relative2 } from "path";
2168
2177
  var SEARCH_PROMPT_PREFIX = "# Inventory search instructions";
2169
2178
  var SEARCH_BLOCK_FENCE = "rafa:search";
2170
2179
  var MIN_FENCE_LENGTH = 3;
2171
- function fenceFor(text) {
2180
+ function fenceFor2(text) {
2172
2181
  const longestRun = Math.max(0, ...(text.match(/`+/g) ?? []).map((run) => run.length));
2173
2182
  return "`".repeat(Math.max(MIN_FENCE_LENGTH, longestRun + 1));
2174
2183
  }
@@ -2184,7 +2193,7 @@ var EXAMPLE_BLOCK = [
2184
2193
  ];
2185
2194
  function renderSearchPrompt(input) {
2186
2195
  const plural2 = `${input.kind}s`;
2187
- const fence = fenceFor(input.question);
2196
+ const fence = fenceFor2(input.question);
2188
2197
  const candidates = input.candidates.map((candidate) => `- ${candidate.name}: \`${candidate.file}\``);
2189
2198
  return [
2190
2199
  SEARCH_PROMPT_PREFIX,
@@ -8820,7 +8829,7 @@ function optionalString(record, key) {
8820
8829
  }
8821
8830
 
8822
8831
  // src/commands/stretch/item.ts
8823
- import { closeSync as closeSync2, mkdirSync as mkdirSync6, openSync as openSync2, readdirSync as readdirSync3, readFileSync as readFileSync15, statSync as statSync6 } from "fs";
8832
+ import { closeSync, mkdirSync as mkdirSync6, openSync, readdirSync as readdirSync3, readFileSync as readFileSync15, statSync as statSync6 } from "fs";
8824
8833
  import { dirname as dirname6, join as join20, posix } from "path";
8825
8834
 
8826
8835
  // src/stretch/operators.ts
@@ -8995,83 +9004,6 @@ function lineText(argv) {
8995
9004
  // src/commands/stretch/item-merge.ts
8996
9005
  import { readFileSync as readFileSync14 } from "fs";
8997
9006
 
8998
- // src/loop/events-file.ts
8999
- import { closeSync, fstatSync, openSync, readSync } from "fs";
9000
- var NEWLINE = 10;
9001
- function eventsFileOf(root, record) {
9002
- return eventsFilePath(root, record.sessionId);
9003
- }
9004
- function isPlainObject(value) {
9005
- return typeof value === "object" && value !== null && !Array.isArray(value);
9006
- }
9007
- function parseEventLine(line) {
9008
- let value;
9009
- try {
9010
- value = JSON.parse(line);
9011
- } catch {
9012
- return null;
9013
- }
9014
- if (!isPlainObject(value))
9015
- return null;
9016
- const { name: name2, summary, data, ts } = value;
9017
- if (typeof name2 !== "string" || typeof summary !== "string" || typeof ts !== "string")
9018
- return null;
9019
- if (!isPlainObject(data))
9020
- return null;
9021
- return Object.freeze({ name: name2, summary, data: Object.freeze({ ...data }), ts });
9022
- }
9023
- function bytesFrom(fd, offset) {
9024
- const length = fstatSync(fd).size - offset;
9025
- if (length <= 0)
9026
- return null;
9027
- const buffer = Buffer.alloc(length);
9028
- let filled = 0;
9029
- while (filled < length) {
9030
- const read = readSync(fd, buffer, filled, length - filled, offset + filled);
9031
- if (read === 0)
9032
- break;
9033
- filled += read;
9034
- }
9035
- return buffer.subarray(0, filled);
9036
- }
9037
- function readTail(file, offset) {
9038
- let fd;
9039
- try {
9040
- fd = openSync(file, "r");
9041
- } catch (error) {
9042
- if (errorCode(error) === "ENOENT")
9043
- return "absent";
9044
- throw error;
9045
- }
9046
- try {
9047
- return bytesFrom(fd, offset);
9048
- } finally {
9049
- closeSync(fd);
9050
- }
9051
- }
9052
- function readEventsFrom(file, offset) {
9053
- if (!Number.isSafeInteger(offset) || offset < 0) {
9054
- throw new RangeError(`events file: unusable offset ${String(offset)}`);
9055
- }
9056
- const tail = readTail(file, offset);
9057
- if (tail === "absent")
9058
- return Object.freeze({ kind: "absent" });
9059
- const end = tail?.lastIndexOf(NEWLINE) ?? -1;
9060
- if (tail === null || end < 0) {
9061
- return Object.freeze({ kind: "read", events: Object.freeze([]), malformed: 0, offset });
9062
- }
9063
- const lines = tail.subarray(0, end).toString("utf8").split(`
9064
- `);
9065
- const parsed = lines.map(parseEventLine);
9066
- const events = parsed.filter((event) => event !== null);
9067
- return Object.freeze({
9068
- kind: "read",
9069
- events: Object.freeze(events),
9070
- malformed: parsed.length - events.length,
9071
- offset: offset + end + 1
9072
- });
9073
- }
9074
-
9075
9007
  // src/stretch/items.ts
9076
9008
  import { appendFileSync, mkdirSync as mkdirSync4, readFileSync as readFileSync11 } from "fs";
9077
9009
  import { dirname as dirname3, join as join17 } from "path";
@@ -10194,7 +10126,7 @@ async function spawnOnTerminal(argv, cwd, env) {
10194
10126
  function spawnDetached(argv, cwd, env, logPath) {
10195
10127
  let fd;
10196
10128
  try {
10197
- fd = openSync2(logPath, "a");
10129
+ fd = openSync(logPath, "a");
10198
10130
  } catch {
10199
10131
  return null;
10200
10132
  }
@@ -10205,7 +10137,7 @@ function spawnDetached(argv, cwd, env, logPath) {
10205
10137
  } catch {
10206
10138
  return null;
10207
10139
  } finally {
10208
- closeSync2(fd);
10140
+ closeSync(fd);
10209
10141
  }
10210
10142
  }
10211
10143
  function defaultStretchItemSeams() {
@@ -13159,48 +13091,7 @@ var cancel_default = createEpicCancelCommand();
13159
13091
 
13160
13092
  // src/commands/epic/close.ts
13161
13093
  import { randomUUID as randomUUID3 } from "crypto";
13162
- import { join as join28 } from "path";
13163
-
13164
- // src/board/epic-template.ts
13165
- import { existsSync as existsSync8, readFileSync as readFileSync18 } from "fs";
13166
- import { dirname as dirname8, join as join25 } from "path";
13167
- import { fileURLToPath as fileURLToPath2 } from "url";
13168
- var MODULE_DIR2 = dirname8(fileURLToPath2(import.meta.url));
13169
- var EPIC_TEMPLATE_FILE = "epic.md";
13170
- var CRITERIA_PLACEHOLDERS = Object.freeze([
13171
- "- Placeholder: one line per thing that makes this epic finished," + " written so a check against main can tell whether it holds."
13172
- ]);
13173
- var PLACEHOLDER_REASON = "the line is still the epic template's placeholder, so there is nothing to check";
13174
- function epicTemplateSource(moduleDir = MODULE_DIR2) {
13175
- return join25(moduleDir, TEMPLATES_DIRNAME, EPIC_TEMPLATE_FILE);
13176
- }
13177
- function readEpicTemplate(moduleDir = MODULE_DIR2) {
13178
- const source = epicTemplateSource(moduleDir);
13179
- if (!existsSync8(source)) {
13180
- throw new Error(`epic template: the epic issue template is missing: no file at ${source}`);
13181
- }
13182
- return readFileSync18(source, "utf8");
13183
- }
13184
- function placeholderCriteria(criteria) {
13185
- if (criteria === null)
13186
- return Object.freeze([]);
13187
- const held = criteria.split(/\r\n?|\n/u).map((line) => line.trim()).filter((line) => CRITERIA_PLACEHOLDERS.includes(line));
13188
- return Object.freeze(held.map((text) => Object.freeze({ text, reason: PLACEHOLDER_REASON })));
13189
- }
13190
- function renderEpicBody(template = readEpicTemplate()) {
13191
- const read = readEpicBody(template);
13192
- if (read.criteria === null) {
13193
- throw new Error("epic template: the template carries no acceptance criteria section with a placeholder under it");
13194
- }
13195
- const missing = CRITERIA_PLACEHOLDERS.length - placeholderCriteria(read.criteria).length;
13196
- if (missing > 0) {
13197
- throw new Error(`epic template: the acceptance criteria section has lost ${String(missing)} of its placeholder lines`);
13198
- }
13199
- if (read.lines.length > 0) {
13200
- throw new Error(`epic template: the template carries ${String(read.lines.length)} checklist lines; a new epic has none`);
13201
- }
13202
- return template;
13203
- }
13094
+ import { join as join26 } from "path";
13204
13095
 
13205
13096
  // src/effort/epic-cost.ts
13206
13097
  var MEMBERSHIP_NOTE = "cost follows membership at close time: it counts the sessions of the issues in the epic now, " + "so work on an issue moved out is not counted and work on an issue moved in is";
@@ -13269,211 +13160,9 @@ function renderEpicCost(cost, estimate) {
13269
13160
  ];
13270
13161
  }
13271
13162
 
13272
- // src/epic/verify-plan.ts
13273
- import { existsSync as existsSync9, readFileSync as readFileSync19 } from "fs";
13274
- import { dirname as dirname9, join as join26 } from "path";
13275
- import { fileURLToPath as fileURLToPath3 } from "url";
13276
- var MODULE_DIR3 = dirname9(fileURLToPath3(import.meta.url));
13277
- var VERIFY_PROMPT_FILE = "epic-verify-prompt.md";
13278
- var VERIFY_BLOCK_FENCE = "rafa:verify";
13279
- var VERIFY_PROMPT_SLOTS = ["EPIC_NUMBER", "EPIC_TITLE", "CRITERIA"];
13280
- var SKIPPED_REASON = "the verification plan did not answer it";
13281
- var FENCE2 = /^\s*(?:`{3,}|~{3,})/u;
13282
- var LIST_ITEM = /^( {0,3})(?:[-*+]|\d{1,9}[.)])(?:\s|$)/u;
13283
- var INDENTED = /^\s/u;
13284
- var MIN_FENCE_LENGTH2 = 3;
13285
- var VERIFY_KIND = VERIFY_BLOCK_FENCE.slice("rafa:".length);
13286
- var SLOT_PATTERN = new RegExp(`\\{(${VERIFY_PROMPT_SLOTS.join("|")})\\}`, "gu");
13287
- var ANSWER_KEYS = ["check", "uncheckable"];
13288
- function opensCriterion(text, state) {
13289
- const item = LIST_ITEM.exec(text);
13290
- if (item !== null) {
13291
- const indent = item[1]?.length ?? 0;
13292
- state.topIndent ??= indent;
13293
- return indent <= state.topIndent;
13294
- }
13295
- return state.afterBlank && !INDENTED.test(text);
13296
- }
13297
- function splitCriteria(criteria) {
13298
- if (criteria === null)
13299
- return Object.freeze([]);
13300
- const gathered = [];
13301
- const state = { fenced: false, afterBlank: false, topIndent: null };
13302
- for (const text of criteria.split(/\r\n?|\n/u)) {
13303
- const insideFence = state.fenced;
13304
- if (FENCE2.test(text))
13305
- state.fenced = !state.fenced;
13306
- if (!insideFence && text.trim() === "") {
13307
- state.afterBlank = true;
13308
- gathered.at(-1)?.push(text);
13309
- continue;
13310
- }
13311
- const current = gathered.at(-1);
13312
- const opens = !insideFence && opensCriterion(text, state);
13313
- if (current === undefined || opens) {
13314
- gathered.push([text]);
13315
- } else {
13316
- current.push(text);
13317
- }
13318
- state.afterBlank = false;
13319
- }
13320
- const numbered = gathered.map((lines, index) => {
13321
- const text = lines.join(`
13322
- `).trim();
13323
- return Object.freeze({ number: index + 1, text, placeholder: CRITERIA_PLACEHOLDERS.includes(text) });
13324
- });
13325
- return Object.freeze(numbered);
13326
- }
13327
- function criteriaToAsk(criteria) {
13328
- return Object.freeze(criteria.filter((criterion) => !criterion.placeholder));
13329
- }
13330
- function verifyPromptCandidates(moduleDir = MODULE_DIR3) {
13331
- return [join26(moduleDir, VERIFY_PROMPT_FILE), join26(dirname9(moduleDir), VERIFY_PROMPT_FILE)];
13332
- }
13333
- function readVerifyPrompt(moduleDir = MODULE_DIR3) {
13334
- const candidates = verifyPromptCandidates(moduleDir);
13335
- const found = candidates.find((candidate) => existsSync9(candidate));
13336
- if (found === undefined) {
13337
- throw new Error(`epic verify prompt: the verification prompt is missing: no file at ${candidates.join(" or ")}`);
13338
- }
13339
- return readFileSync19(found, "utf8");
13340
- }
13341
- function fenceFor2(text) {
13342
- const longestRun = Math.max(0, ...(text.match(/`+/gu) ?? []).map((run) => run.length));
13343
- return "`".repeat(Math.max(MIN_FENCE_LENGTH2, longestRun + 1));
13344
- }
13345
- function renderCriterion(criterion) {
13346
- const fence = fenceFor2(criterion.text);
13347
- return [`### Criterion ${String(criterion.number)}`, "", `${fence}text`, criterion.text, fence].join(`
13348
- `);
13349
- }
13350
- function buildVerifyPrompt(template, input) {
13351
- const missing = VERIFY_PROMPT_SLOTS.find((slot) => !template.includes(`{${slot}}`));
13352
- if (missing !== undefined) {
13353
- throw new Error(`epic verify prompt: the template carries no {${missing}} slot`);
13354
- }
13355
- const values = {
13356
- EPIC_NUMBER: String(input.epic),
13357
- EPIC_TITLE: input.title.replace(/\s+/gu, " ").trim(),
13358
- CRITERIA: criteriaToAsk(input.criteria).map(renderCriterion).join(`
13359
-
13360
- `)
13361
- };
13362
- return template.replace(SLOT_PATTERN, (_whole, slot) => values[slot]);
13363
- }
13364
- function isMapping3(value) {
13365
- return typeof value === "object" && value !== null && !Array.isArray(value);
13366
- }
13367
- function fieldOf2(mapping, key) {
13368
- return Object.entries(mapping).find(([own]) => own === key)?.[1];
13369
- }
13370
- function describeValue3(value) {
13371
- if (typeof value === "string")
13372
- return JSON.stringify(value);
13373
- if (value === null || value === undefined)
13374
- return "nothing";
13375
- if (Array.isArray(value))
13376
- return "a list";
13377
- if (typeof value === "object")
13378
- return "a mapping";
13379
- return `the ${typeof value} ${String(value)}`;
13380
- }
13381
- function messageOf4(error) {
13382
- return error instanceof Error ? error.message : String(error);
13383
- }
13384
- function absent2(reason, block, text) {
13385
- return Object.freeze({ present: false, reason, block, text });
13386
- }
13387
- function readEntry(item, field, asked) {
13388
- if (!isMapping3(item))
13389
- return { problem: `${field} is ${describeValue3(item)}, not a mapping`, number: null };
13390
- const number = fieldOf2(item, "criterion");
13391
- if (typeof number !== "number" || !Number.isInteger(number) || number < 1) {
13392
- return { problem: `${field}.criterion is ${describeValue3(number)}, not a criterion number from 1`, number: null };
13393
- }
13394
- if (!asked.has(number)) {
13395
- return { problem: `${field}.criterion ${String(number)} is no criterion the plan was asked about`, number: null };
13396
- }
13397
- const carried = ANSWER_KEYS.filter((key2) => fieldOf2(item, key2) !== undefined);
13398
- const [key] = carried;
13399
- if (carried.length !== 1 || key === undefined) {
13400
- const count = carried.length === 0 ? "neither" : "both";
13401
- return { problem: `${field} carries ${count} check and uncheckable`, number };
13402
- }
13403
- const value = fieldOf2(item, key);
13404
- if (typeof value !== "string" || value.trim() === "") {
13405
- return { problem: `${field}.${key} is ${describeValue3(value)}, not a non-blank string`, number };
13406
- }
13407
- return { answer: { number, key, text: value.trim() } };
13408
- }
13409
- function verdictFor(criterion, answer, problem) {
13410
- if (criterion.placeholder) {
13411
- return { kind: "uncheckable", criterion, reason: PLACEHOLDER_REASON, source: "placeholder" };
13412
- }
13413
- if (answer?.key === "check")
13414
- return { kind: "check", criterion, check: answer.text };
13415
- if (answer?.key === "uncheckable")
13416
- return { kind: "uncheckable", criterion, reason: answer.text, source: "answer" };
13417
- const reason = problem === undefined ? SKIPPED_REASON : `${SKIPPED_REASON}: its entry was dropped, as ${problem}`;
13418
- return { kind: "uncheckable", criterion, reason, source: "skipped" };
13419
- }
13420
- function readVerdicts(items, criteria, block) {
13421
- const asked = new Set(criteriaToAsk(criteria).map((criterion) => criterion.number));
13422
- const answers = new Map;
13423
- const problems = new Map;
13424
- const issues = [];
13425
- for (const [index, item] of items.entries()) {
13426
- const field = `criteria[${String(index)}]`;
13427
- const reading = readEntry(item, field, asked);
13428
- if ("problem" in reading) {
13429
- issues.push(Object.freeze({ field, text: `${reading.problem}; dropped` }));
13430
- if (reading.number !== null && !problems.has(reading.number))
13431
- problems.set(reading.number, reading.problem);
13432
- continue;
13433
- }
13434
- if (answers.has(reading.answer.number)) {
13435
- const text = `${field} answers criterion ${String(reading.answer.number)} a second time; dropped`;
13436
- issues.push(Object.freeze({ field, text }));
13437
- continue;
13438
- }
13439
- answers.set(reading.answer.number, reading.answer);
13440
- }
13441
- const verdicts = criteria.map((criterion) => Object.freeze(verdictFor(criterion, answers.get(criterion.number), problems.get(criterion.number))));
13442
- return Object.freeze({
13443
- present: true,
13444
- verdicts: Object.freeze(verdicts),
13445
- block,
13446
- issues: Object.freeze(issues)
13447
- });
13448
- }
13449
- function parseVerifyPlan(output, criteria) {
13450
- const block = readRafaBlocks(output).filter((each) => each.kind === VERIFY_KIND).at(-1);
13451
- if (block === undefined) {
13452
- return absent2("no-block", null, `the session output holds no ${VERIFY_BLOCK_FENCE} block`);
13453
- }
13454
- const at = `${VERIFY_BLOCK_FENCE} block at line ${String(block.span.first)}`;
13455
- if (!block.closed) {
13456
- return absent2("unclosed-block", block, `${at} is never closed, so it is not read`);
13457
- }
13458
- let document;
13459
- try {
13460
- document = Bun.YAML.parse(block.body);
13461
- } catch (error) {
13462
- return absent2("malformed-block", block, `${at} is not valid YAML (${messageOf4(error)})`);
13463
- }
13464
- if (!isMapping3(document)) {
13465
- return absent2("malformed-block", block, `${at} holds ${describeValue3(document)}, not a mapping`);
13466
- }
13467
- const listed2 = fieldOf2(document, "criteria") ?? [];
13468
- if (!Array.isArray(listed2)) {
13469
- return absent2("malformed-block", block, `${at} has criteria ${describeValue3(listed2)}, not a list`);
13470
- }
13471
- return readVerdicts(listed2, criteria, block);
13472
- }
13473
-
13474
13163
  // src/epic/verify-run.ts
13475
13164
  import { randomUUID as randomUUID2 } from "crypto";
13476
- import { join as join27 } from "path";
13165
+ import { join as join25 } from "path";
13477
13166
  var CHECK_TOOLS = ["Read", "Grep", "Glob", "Bash"];
13478
13167
  var DEFAULT_VERIFY_REMOTE = "origin";
13479
13168
  var CHECK_PROMPT_PREFIX = "# Epic acceptance check instructions";
@@ -13482,7 +13171,7 @@ var CHECK_RESULTS = ["pass", "fail"];
13482
13171
  var WORKTREE_PREFIX = "epic-";
13483
13172
  var VERDICT_KIND = VERDICT_BLOCK_FENCE.slice("rafa:".length);
13484
13173
  function verifyWorktreePath(home, epic) {
13485
- return join27(home, WORKTREES_SUBDIR, `${WORKTREE_PREFIX}${String(epic)}`);
13174
+ return join25(home, WORKTREES_SUBDIR, `${WORKTREE_PREFIX}${String(epic)}`);
13486
13175
  }
13487
13176
  function fetchStep(remote) {
13488
13177
  return { id: "fetch", args: ["fetch", remote] };
@@ -13513,7 +13202,7 @@ function checkFlags(sessionId) {
13513
13202
  return [SESSION_ID_FLAG, sessionId, "--tools", CHECK_TOOLS.join(",")];
13514
13203
  }
13515
13204
  function renderCheckPrompt(input) {
13516
- const fence = fenceFor2(input.check);
13205
+ const fence = fenceFor(input.check);
13517
13206
  return [
13518
13207
  CHECK_PROMPT_PREFIX,
13519
13208
  "",
@@ -13554,13 +13243,13 @@ function renderCheckPrompt(input) {
13554
13243
  ].join(`
13555
13244
  `);
13556
13245
  }
13557
- function isMapping4(value) {
13246
+ function isMapping3(value) {
13558
13247
  return typeof value === "object" && value !== null && !Array.isArray(value);
13559
13248
  }
13560
- function fieldOf3(mapping, key) {
13249
+ function fieldOf2(mapping, key) {
13561
13250
  return Object.entries(mapping).find(([own]) => own === key)?.[1];
13562
13251
  }
13563
- function describeValue4(value) {
13252
+ function describeValue3(value) {
13564
13253
  if (typeof value === "string")
13565
13254
  return JSON.stringify(value);
13566
13255
  if (value === null || value === undefined)
@@ -13574,7 +13263,7 @@ function describeValue4(value) {
13574
13263
  function isResultWord(value) {
13575
13264
  return CHECK_RESULTS.some((word) => word === value);
13576
13265
  }
13577
- function absent3(text, block) {
13266
+ function absent2(text, block) {
13578
13267
  return Object.freeze({ present: false, text, block });
13579
13268
  }
13580
13269
  function readVerdictBody(block, at) {
@@ -13582,29 +13271,29 @@ function readVerdictBody(block, at) {
13582
13271
  try {
13583
13272
  document = Bun.YAML.parse(block.body);
13584
13273
  } catch (error) {
13585
- return absent3(`${at} is not valid YAML (${messageOf(error)})`, block);
13274
+ return absent2(`${at} is not valid YAML (${messageOf(error)})`, block);
13586
13275
  }
13587
- if (!isMapping4(document)) {
13588
- return absent3(`${at} holds ${describeValue4(document)}, not a mapping`, block);
13276
+ if (!isMapping3(document)) {
13277
+ return absent2(`${at} holds ${describeValue3(document)}, not a mapping`, block);
13589
13278
  }
13590
- const result2 = fieldOf3(document, "result");
13279
+ const result2 = fieldOf2(document, "result");
13591
13280
  if (!isResultWord(result2)) {
13592
- return absent3(`${at} has result ${describeValue4(result2)}, not ${CHECK_RESULTS.join(" or ")}`, block);
13281
+ return absent2(`${at} has result ${describeValue3(result2)}, not ${CHECK_RESULTS.join(" or ")}`, block);
13593
13282
  }
13594
- const evidence = fieldOf3(document, "evidence");
13283
+ const evidence = fieldOf2(document, "evidence");
13595
13284
  if (typeof evidence !== "string" || evidence.trim() === "") {
13596
- return absent3(`${at} has evidence ${describeValue4(evidence)}, not a non-blank string`, block);
13285
+ return absent2(`${at} has evidence ${describeValue3(evidence)}, not a non-blank string`, block);
13597
13286
  }
13598
13287
  return Object.freeze({ present: true, result: result2, evidence: evidence.trim(), block });
13599
13288
  }
13600
13289
  function parseCheckVerdict(output) {
13601
13290
  const block = readRafaBlocks(output).filter((each) => each.kind === VERDICT_KIND).at(-1);
13602
13291
  if (block === undefined) {
13603
- return absent3(`the session output holds no ${VERDICT_BLOCK_FENCE} block`, null);
13292
+ return absent2(`the session output holds no ${VERDICT_BLOCK_FENCE} block`, null);
13604
13293
  }
13605
13294
  const at = `${VERDICT_BLOCK_FENCE} block at line ${String(block.span.first)}`;
13606
13295
  if (!block.closed)
13607
- return absent3(`${at} is never closed, so it is not read`, block);
13296
+ return absent2(`${at} is never closed, so it is not read`, block);
13608
13297
  return readVerdictBody(block, at);
13609
13298
  }
13610
13299
  function readCheck(check, sessionId, session) {
@@ -13764,7 +13453,7 @@ async function planVerification(input) {
13764
13453
  return Object.freeze({ status: "planned", sessionId, verdicts: reading.verdicts, issues: reading.issues });
13765
13454
  }
13766
13455
  function closeTriageFile(root, epic) {
13767
- return join28(root, ".rafa", `epic-${String(epic)}-close`);
13456
+ return join26(root, ".rafa", `epic-${String(epic)}-close`);
13768
13457
  }
13769
13458
  function failedCheckArtifact(epic, criterion) {
13770
13459
  return `epic ${ref7(epic)} acceptance criterion: ${oneLine3(criterion.text)}`;
@@ -14367,13 +14056,13 @@ function createEpicPromoteCommand(seams = {}) {
14367
14056
  var promote_default = createEpicPromoteCommand();
14368
14057
 
14369
14058
  // src/commands/init.ts
14370
- import { existsSync as existsSync12, readFileSync as readFileSync23 } from "fs";
14059
+ import { existsSync as existsSync10, readFileSync as readFileSync21 } from "fs";
14371
14060
  import { homedir as homedir3 } from "os";
14372
- import { join as join32, relative as relative6, sep as sep3 } from "path";
14061
+ import { join as join30, relative as relative6, sep as sep3 } from "path";
14373
14062
 
14374
14063
  // src/agents/vendorable.ts
14375
- import { readdirSync as readdirSync4, readFileSync as readFileSync20 } from "fs";
14376
- import { isAbsolute as isAbsolute3, join as join29 } from "path";
14064
+ import { readdirSync as readdirSync4, readFileSync as readFileSync18 } from "fs";
14065
+ import { isAbsolute as isAbsolute3, join as join27 } from "path";
14377
14066
  function planFiles(dir) {
14378
14067
  let entries;
14379
14068
  try {
@@ -14385,7 +14074,7 @@ function planFiles(dir) {
14385
14074
  }
14386
14075
  function readText3(path) {
14387
14076
  try {
14388
- return readFileSync20(path, "utf8");
14077
+ return readFileSync18(path, "utf8");
14389
14078
  } catch {
14390
14079
  return null;
14391
14080
  }
@@ -14395,7 +14084,7 @@ function rafaHolds(roster, name2) {
14395
14084
  return item?.holders.some((holder) => holder.source === "rafa") === true;
14396
14085
  }
14397
14086
  function vendorableAgents(scan) {
14398
- const dir = isAbsolute3(scan.planDir) ? scan.planDir : join29(scan.repoRoot, scan.planDir);
14087
+ const dir = isAbsolute3(scan.planDir) ? scan.planDir : join27(scan.repoRoot, scan.planDir);
14399
14088
  const roots = {
14400
14089
  repoRoot: scan.repoRoot,
14401
14090
  home: scan.home,
@@ -14404,7 +14093,7 @@ function vendorableAgents(scan) {
14404
14093
  const roster = resolveAgentRoster(roots, scan.settings);
14405
14094
  const found = [];
14406
14095
  for (const file of planFiles(dir)) {
14407
- const path = join29(dir, file);
14096
+ const path = join27(dir, file);
14408
14097
  const markdown = readText3(path);
14409
14098
  if (markdown === null)
14410
14099
  continue;
@@ -14430,13 +14119,13 @@ function vendorableAgentWarnings(agents, root) {
14430
14119
 
14431
14120
  // src/project/gitignore.ts
14432
14121
  import { createHash } from "crypto";
14433
- import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync21, writeFileSync as writeFileSync6 } from "fs";
14434
- import { dirname as dirname10, isAbsolute as isAbsolute4, join as join30 } from "path";
14122
+ import { existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync19, writeFileSync as writeFileSync6 } from "fs";
14123
+ import { dirname as dirname8, isAbsolute as isAbsolute4, join as join28 } from "path";
14435
14124
  var GITIGNORE_FILE = ".gitignore";
14436
14125
  var DIGEST_NAME = "tracking.digest";
14437
14126
  var PRIVATE_TRIAGE = "triage/private";
14438
14127
  var CACHE_DIR = "cache";
14439
- var TRACKING_DIGEST_FILE = join30(SCOPE_DIR, DIGEST_NAME);
14128
+ var TRACKING_DIGEST_FILE = join28(SCOPE_DIR, DIGEST_NAME);
14440
14129
  var BLOCK_BEGIN = "# >>> rafa tracking: rafa rewrites the lines up to the end marker";
14441
14130
  var BLOCK_END = "# <<< rafa tracking";
14442
14131
  var SUB_PATHS = [
@@ -14525,13 +14214,13 @@ function attempt2(file, failure3, act) {
14525
14214
  }
14526
14215
  }
14527
14216
  function readIfPresent2(file) {
14528
- if (!existsSync10(file))
14217
+ if (!existsSync8(file))
14529
14218
  return null;
14530
- return attempt2(file, "cannot be read", () => readFileSync21(file, "utf8"));
14219
+ return attempt2(file, "cannot be read", () => readFileSync19(file, "utf8"));
14531
14220
  }
14532
14221
  function writeTrackingGitignore(root, flags) {
14533
14222
  requireAbsolute(root);
14534
- const file = join30(root, GITIGNORE_FILE);
14223
+ const file = join28(root, GITIGNORE_FILE);
14535
14224
  const text = readIfPresent2(file);
14536
14225
  const next = withTrackingBlock(text, flags, file);
14537
14226
  if (next === text)
@@ -14553,7 +14242,7 @@ function trackingDigest(flags) {
14553
14242
  }
14554
14243
  function exposureNotice(root) {
14555
14244
  return [
14556
- `tracking.all is true, so ${join30(root, GITIGNORE_FILE)} no longer ignores ${SCOPE_DIR}/` + " and a commit can carry what it holds, including:",
14245
+ `tracking.all is true, so ${join28(root, GITIGNORE_FILE)} no longer ignores ${SCOPE_DIR}/` + " and a commit can carry what it holds, including:",
14557
14246
  " - unpatched issues described in specs and plans",
14558
14247
  " - effort rows with model names and token counts",
14559
14248
  " - instincts that may quote error output",
@@ -14566,7 +14255,7 @@ function printWarning(message) {
14566
14255
  }
14567
14256
  function noticeTrackingChange(root, flags, print2 = printWarning) {
14568
14257
  requireAbsolute(root);
14569
- const digestFile = join30(root, TRACKING_DIGEST_FILE);
14258
+ const digestFile = join28(root, TRACKING_DIGEST_FILE);
14570
14259
  const recorded = `${trackingDigest(flags)}
14571
14260
  `;
14572
14261
  if (readIfPresent2(digestFile) === recorded) {
@@ -14575,7 +14264,7 @@ function noticeTrackingChange(root, flags, print2 = printWarning) {
14575
14264
  if (flags.trackingAll)
14576
14265
  print2(exposureNotice(root));
14577
14266
  attempt2(digestFile, "cannot be written", () => {
14578
- mkdirSync7(dirname10(digestFile), { recursive: true });
14267
+ mkdirSync7(dirname8(digestFile), { recursive: true });
14579
14268
  writeFileSync6(digestFile, recorded);
14580
14269
  });
14581
14270
  return { digestFile, changed: true, printed: flags.trackingAll };
@@ -14665,14 +14354,14 @@ async function promptForRoot(found, prompter, seams) {
14665
14354
  }
14666
14355
 
14667
14356
  // src/project/scaffold.ts
14668
- import { existsSync as existsSync11, lstatSync, mkdirSync as mkdirSync8, statSync as statSync7, writeFileSync as writeFileSync8 } from "fs";
14669
- import { join as join31 } from "path";
14357
+ import { existsSync as existsSync9, lstatSync, mkdirSync as mkdirSync8, statSync as statSync7, writeFileSync as writeFileSync8 } from "fs";
14358
+ import { join as join29 } from "path";
14670
14359
 
14671
14360
  // src/commands/init-release.ts
14672
14361
  import { writeFileSync as writeFileSync7 } from "fs";
14673
14362
 
14674
14363
  // src/release/setting.ts
14675
- import { readFileSync as readFileSync22 } from "fs";
14364
+ import { readFileSync as readFileSync20 } from "fs";
14676
14365
  var RELEASE_ENABLED_SETTING = "release.enabled";
14677
14366
  function releaseEnabledIn(text, path) {
14678
14367
  return parseConfigText(text, path).values.releaseEnabled ?? null;
@@ -14681,7 +14370,7 @@ function readReleaseSetting(root) {
14681
14370
  const path = configFilePath(root);
14682
14371
  let text;
14683
14372
  try {
14684
- text = readFileSync22(path, "utf8");
14373
+ text = readFileSync20(path, "utf8");
14685
14374
  } catch (error) {
14686
14375
  return { enabled: null, text: "", problem: `${path} could not be read: ${messageOf(error)}` };
14687
14376
  }
@@ -14900,6 +14589,13 @@ var CONFIG_SETTINGS_LINES = Object.freeze([
14900
14589
  "# loop:",
14901
14590
  `# settingSources: ${CONFIG_DEFAULTS.settingSources.join(",")} # a comma-separated subset of user, project, local`,
14902
14591
  `# worktreeDir: ${CONFIG_DEFAULTS.loopWorktreeDir} # where a loop adds its worktrees, read from the project root`,
14592
+ `# retries: ${String(CONFIG_DEFAULTS.loopRetries)} # 1..3 | false, times in a row a run re-enters its loop after a retry-safe stop`,
14593
+ `# retriesOnContinue: ${String(CONFIG_DEFAULTS.loopRetriesOnContinue)} # 1..3 | false, the retries a --continue run opens when --retry is not typed`,
14594
+ "# continue:",
14595
+ `# criteria: ${CONFIG_DEFAULTS.loopContinueCriteria} # the project's own criteria for a --continue decision`,
14596
+ `# criteriaMode: ${CONFIG_DEFAULTS.loopContinueCriteriaMode} # extend | replace, how the project criteria meet the bundled base`,
14597
+ "# forceWrapUp:",
14598
+ `# maxNewFailures: ${String(CONFIG_DEFAULTS.loopForceWrapUpMaxNewFailures)} # 1..50 | false, new suite failures --force-wrap-up tolerates`,
14903
14599
  "# wrapUp:",
14904
14600
  `# retries: ${String(CONFIG_DEFAULTS.loopWrapUpRetries)} # 1..3 | false, wrap-up sessions rerun before rafa opens a missing PR itself`,
14905
14601
  "# pr:",
@@ -14995,7 +14691,7 @@ class ScaffoldError extends Error {
14995
14691
  }
14996
14692
  function layoutOf2(base, tree) {
14997
14693
  const scope = scopeAt(base);
14998
- return { dirs: [scope.dir, ...tree.map((name2) => join31(scope.dir, name2))], configFile: configFilePath(base) };
14694
+ return { dirs: [scope.dir, ...tree.map((name2) => join29(scope.dir, name2))], configFile: configFilePath(base) };
14999
14695
  }
15000
14696
  function occupied(path) {
15001
14697
  return lstatSync(path, { throwIfNoEntry: false }) !== undefined;
@@ -15006,7 +14702,7 @@ function isDirectory2(path) {
15006
14702
  function directoryConflict(dir) {
15007
14703
  if (!occupied(dir) || isDirectory2(dir))
15008
14704
  return null;
15009
- return existsSync11(dir) ? `${dir} is not a directory` : `${dir} is a link to nothing`;
14705
+ return existsSync9(dir) ? `${dir} is not a directory` : `${dir} is a link to nothing`;
15010
14706
  }
15011
14707
  function layoutConflicts(layout) {
15012
14708
  const [scopeDir, ...tree] = layout.dirs;
@@ -15014,7 +14710,7 @@ function layoutConflicts(layout) {
15014
14710
  if (opening !== null)
15015
14711
  return [opening];
15016
14712
  const dirs = tree.flatMap((dir) => directoryConflict(dir) ?? []);
15017
- const file = occupied(layout.configFile) && !existsSync11(layout.configFile) ? [`${layout.configFile} is a link to nothing`] : [];
14713
+ const file = occupied(layout.configFile) && !existsSync9(layout.configFile) ? [`${layout.configFile} is a link to nothing`] : [];
15018
14714
  return [...dirs, ...file];
15019
14715
  }
15020
14716
  function scaffoldConflicts(root, home) {
@@ -15028,7 +14724,7 @@ function attempt3(path, failure3, act) {
15028
14724
  }
15029
14725
  }
15030
14726
  function ensureDirectory(path) {
15031
- if (existsSync11(path))
14727
+ if (existsSync9(path))
15032
14728
  return { path, kind: "directory", change: "unchanged" };
15033
14729
  attempt3(path, "cannot be created", () => {
15034
14730
  mkdirSync8(path, { recursive: true });
@@ -15036,7 +14732,7 @@ function ensureDirectory(path) {
15036
14732
  return { path, kind: "directory", change: "created" };
15037
14733
  }
15038
14734
  function ensureFile(path, text) {
15039
- if (existsSync11(path))
14735
+ if (existsSync9(path))
15040
14736
  return { path, kind: "file", change: "unchanged" };
15041
14737
  attempt3(path, "cannot be written", () => {
15042
14738
  writeFileSync8(path, text, { encoding: "utf8", flag: "wx" });
@@ -15181,9 +14877,9 @@ function resolvedConfig2(root, home, warn3) {
15181
14877
  }
15182
14878
  }
15183
14879
  function checkGitignore(root, flags) {
15184
- const file = join32(root, GITIGNORE_FILE);
14880
+ const file = join30(root, GITIGNORE_FILE);
15185
14881
  try {
15186
- const text = existsSync12(file) ? readFileSync23(file, "utf8") : null;
14882
+ const text = existsSync10(file) ? readFileSync21(file, "utf8") : null;
15187
14883
  withTrackingBlock(text, flags, file);
15188
14884
  } catch (error) {
15189
14885
  const message = error instanceof GitignoreError ? error.message : `rafa init: ${file} cannot be read (${messageOf(error)})`;
@@ -15215,8 +14911,8 @@ function initialise(root, start2, home, context, entry) {
15215
14911
  const resolved = resolvedConfig2(root.path, home, warn3);
15216
14912
  const config = resolved.config;
15217
14913
  checkGitignore(root.path, config);
15218
- const configExisted = existsSync12(configFilePath(root.path));
15219
- const digestExisted = existsSync12(join32(root.path, TRACKING_DIGEST_FILE));
14914
+ const configExisted = existsSync10(configFilePath(root.path));
14915
+ const digestExisted = existsSync10(join30(root.path, TRACKING_DIGEST_FILE));
15220
14916
  const project = writeProjectScope(root.path);
15221
14917
  const tracking = applyTracking(root.path, config, warn3);
15222
14918
  const writes = [...project, ...trackingWrites(tracking, digestExisted), ...writeUserScope(home)];
@@ -16102,8 +15798,8 @@ function createInstinctShowCommand() {
16102
15798
  var show_default3 = createInstinctShowCommand();
16103
15799
 
16104
15800
  // src/commands/issue/check.ts
16105
- import { readdirSync as readdirSync5, readFileSync as readFileSync24, statSync as statSync8 } from "fs";
16106
- import { join as join33, resolve as resolve9 } from "path";
15801
+ import { readdirSync as readdirSync5, readFileSync as readFileSync22, statSync as statSync8 } from "fs";
15802
+ import { join as join31, resolve as resolve9 } from "path";
16107
15803
  var CHECK_USAGE = "rafa issue check <n> [--stamp]";
16108
15804
  var STAMP_FLAG = "stamp";
16109
15805
  var ISSUE_NUMBER8 = /^[1-9]\d*$/u;
@@ -16139,7 +15835,7 @@ function copyNames(dir, issue) {
16139
15835
  }
16140
15836
  const prefix = `${boardId(issue)}-`;
16141
15837
  const notes = notesFileName(issue);
16142
- return names.filter((name2) => name2.startsWith(prefix) && name2.endsWith(SPEC_EXTENSION) && name2 !== notes).filter((name2) => name2.length > prefix.length + SPEC_EXTENSION.length).filter((name2) => statSync8(join33(dir, name2)).isFile()).sort();
15838
+ return names.filter((name2) => name2.startsWith(prefix) && name2.endsWith(SPEC_EXTENSION) && name2 !== notes).filter((name2) => name2.length > prefix.length + SPEC_EXTENSION.length).filter((name2) => statSync8(join31(dir, name2)).isFile()).sort();
16143
15839
  }
16144
15840
  function findSavedCopy(root, specsDir, issue) {
16145
15841
  const names = copyNames(resolve9(root, specsDir), issue);
@@ -16149,11 +15845,11 @@ function findSavedCopy(root, specsDir, issue) {
16149
15845
  if (names.length > 1) {
16150
15846
  throw new CommandExit(1, [
16151
15847
  `\u274C Issue #${String(issue)} has ${String(names.length)} saved copies under ${specsDir}; remove the stale one:`,
16152
- ...names.map((name2) => ` ${join33(specsDir, name2)}`)
15848
+ ...names.map((name2) => ` ${join31(specsDir, name2)}`)
16153
15849
  ].join(`
16154
15850
  `));
16155
15851
  }
16156
- const path = join33(specsDir, only);
15852
+ const path = join31(specsDir, only);
16157
15853
  return { path, absolute: resolve9(root, path) };
16158
15854
  }
16159
15855
  function liveText(live) {
@@ -16202,7 +15898,7 @@ async function readReferences(absolute, issue, verify, stamp) {
16202
15898
  return rows.map((row2) => checkedRef(row2, row2.stamp));
16203
15899
  }
16204
15900
  const once2 = memoiseVerifier(verify);
16205
- const before = await readRefsText({ copy: readFileSync24(absolute, "utf8"), issue, verify: once2 });
15901
+ const before = await readRefsText({ copy: readFileSync22(absolute, "utf8"), issue, verify: once2 });
16206
15902
  const after = await restampCopyRefs({ path: absolute, issue, verify: once2 });
16207
15903
  return before.rows.map((row2, index) => checkedRef(row2, after.rows[index]?.stamp ?? null));
16208
15904
  }
@@ -16555,8 +16251,8 @@ function createIssueCreateCommand(seams = DEFAULT_ISSUE_SEAMS) {
16555
16251
  var create_default = createIssueCreateCommand();
16556
16252
 
16557
16253
  // src/commands/issue/edit-run.ts
16558
- import { existsSync as existsSync13, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
16559
- import { join as join35 } from "path";
16254
+ import { existsSync as existsSync11, mkdirSync as mkdirSync9, writeFileSync as writeFileSync9 } from "fs";
16255
+ import { join as join33 } from "path";
16560
16256
 
16561
16257
  // src/board/issue-edit-body.ts
16562
16258
  var TRAILING_WHITESPACE = /\s+$/u;
@@ -16611,7 +16307,7 @@ function originalPartHolds(before, after, block) {
16611
16307
 
16612
16308
  // src/board/issue-edit-state.ts
16613
16309
  import { readdirSync as readdirSync6, statSync as statSync9 } from "fs";
16614
- import { join as join34, resolve as resolve10 } from "path";
16310
+ import { join as join32, resolve as resolve10 } from "path";
16615
16311
  function isClaimed(labels) {
16616
16312
  return labels.includes(CLAIMED_LABEL) || labels.includes(IN_DEVELOPMENT_LABEL);
16617
16313
  }
@@ -16684,7 +16380,7 @@ function findSavedCopies(root, specsDir, issue) {
16684
16380
  return [];
16685
16381
  throw error;
16686
16382
  }
16687
- return names.filter((name2) => isSavedCopyName(name2, issue)).filter((name2) => statSync9(join34(dir, name2)).isFile()).sort().map((name2) => join34(specsDir, name2));
16383
+ return names.filter((name2) => isSavedCopyName(name2, issue)).filter((name2) => statSync9(join32(dir, name2)).isFile()).sort().map((name2) => join32(specsDir, name2));
16688
16384
  }
16689
16385
 
16690
16386
  // src/board/issue-edit-text.ts
@@ -16708,7 +16404,7 @@ function requireEditText(input) {
16708
16404
  var EDIT_USAGE = "rafa issue edit <n> (--append-file=<file> | --append=<text>) --reason=<text>" + " [--while-in-development] | (--replace-file=<file> [--title=<text>] | --title=<text>) [--dry-run]";
16709
16405
  var EDIT_REFUSAL_EXIT = TRUST_REFUSAL_EXIT;
16710
16406
  var EDIT_CONFLICT_EXIT = 1;
16711
- var EDIT_SCRATCH_DIR = join35(".rafa", "scratch");
16407
+ var EDIT_SCRATCH_DIR = join33(".rafa", "scratch");
16712
16408
  var STDIN_PATH2 = "-";
16713
16409
  var ISSUE_NUMBER9 = /^[1-9]\d*$/u;
16714
16410
  var TRAILING_WHITESPACE2 = /\s+$/u;
@@ -16957,20 +16653,20 @@ function writeHeld(found, planned, reread) {
16957
16653
  }
16958
16654
  function conflictPaths(issue, stamp, attempt4) {
16959
16655
  const suffix = attempt4 === 1 ? "" : `-${String(attempt4)}`;
16960
- const name2 = (side) => join35(EDIT_SCRATCH_DIR, `${boardId(issue)}-edit-${side}-${stamp}${suffix}.md`);
16656
+ const name2 = (side) => join33(EDIT_SCRATCH_DIR, `${boardId(issue)}-edit-${side}-${stamp}${suffix}.md`);
16961
16657
  return { before: name2("before"), after: name2("after") };
16962
16658
  }
16963
16659
  function saveConflictBodies(root, issue, now, before, after) {
16964
- mkdirSync9(join35(root, EDIT_SCRATCH_DIR), { recursive: true });
16660
+ mkdirSync9(join33(root, EDIT_SCRATCH_DIR), { recursive: true });
16965
16661
  const stamp = fileStamp(now);
16966
16662
  let attempt4 = 1;
16967
16663
  let paths = conflictPaths(issue, stamp, attempt4);
16968
- while (existsSync13(join35(root, paths.before)) || existsSync13(join35(root, paths.after))) {
16664
+ while (existsSync11(join33(root, paths.before)) || existsSync11(join33(root, paths.after))) {
16969
16665
  attempt4 += 1;
16970
16666
  paths = conflictPaths(issue, stamp, attempt4);
16971
16667
  }
16972
- writeFileSync9(join35(root, paths.before), before, { flag: "wx" });
16973
- writeFileSync9(join35(root, paths.after), after, { flag: "wx" });
16668
+ writeFileSync9(join33(root, paths.before), before, { flag: "wx" });
16669
+ writeFileSync9(join33(root, paths.after), after, { flag: "wx" });
16974
16670
  return paths;
16975
16671
  }
16976
16672
  function heldOutcome(request, state) {
@@ -17496,7 +17192,7 @@ var wrapped = wrapPhaseZeroCommand({
17496
17192
  subject: "loop",
17497
17193
  action: "start",
17498
17194
  summary: "run a plan task by task, then wrap up, open the PR and wait for CI",
17499
- description: "Walks the tracker of a plan one task at a time, each in a Claude Code session whose work" + " is committed once it exits, and retries blocked tasks first. After the last task a wrap-up" + " session promotes findings, syncs with main, pushes and opens or updates the PR, and the loop" + " then waits on its checks, spending repair sessions on a red or conflicting PR. With no" + " `--plan` it runs `PLAN.md` in `plan.dir`, `.rafa/plans` unless the config names another, or" + " `PLAN.md` at the project root when that one does not exist. Before any session it checks the" + " prerequisites the config and the plan's `PREREQUISITES-<stub>.md` name, halting when a required" + " one fails and naming each failed optional one known-missing in every task prompt, and warns once" + " for each migration the effort store holds that this rafa does not know and that is additive. It" + " refuses to start while `RAFA_EFFORT_DIR` is set, since a loop records to the project's own" + " store. Started on" + " `main` or `master` it offers to create the plan's `feat/<stub>` from the latest" + " `origin/<base>` and run there, `--create-branch` answering that without asking, and refuses" + " the run when the offer is not taken. `--as-worktree` runs the plan in a worktree of its own on" + " that branch instead, leaving the main checkout as it is, and is refused beside" + " `--create-branch`. Each run writes its session record to `.rafa/runs/<session-id>.json`:" + " the plan, the branch, the pid, the start, the state and the running task, and under `--roadmap`" + " the hop away, when one is. It refuses a plan whose" + " record names another branch, and a plan a session is still running. `rafa loop stop`, `pause`," + " `resume`, `status` and `list` reach the run through that record. A run holds its terminal: until" + " phase 6 it refuses `--detached`. With `--runtime` the whole run goes on in that installed rafa," + " never in a `src/` directory.",
17195
+ description: "Walks the tracker of a plan one task at a time, each in a Claude Code session whose work" + " is committed once it exits, and retries blocked tasks first. After the last task a wrap-up" + " session promotes findings, syncs with main, pushes and opens or updates the PR, and the loop" + " then waits on its checks, spending repair sessions on a red or conflicting PR. With no" + " `--plan` it runs `PLAN.md` in `plan.dir`, `.rafa/plans` unless the config names another, or" + " `PLAN.md` at the project root when that one does not exist. Before any session it checks the" + " prerequisites the config and the plan's `PREREQUISITES-<stub>.md` name, halting when a required" + " one fails and naming each failed optional one known-missing in every task prompt, and warns once" + " for each migration the effort store holds that this rafa does not know and that is additive. It" + " refuses to start while `RAFA_EFFORT_DIR` is set, since a loop records to the project's own" + " store. Started on" + " `main` or `master` it offers to create the plan's `feat/<stub>` from the latest" + " `origin/<base>` and run there, `--create-branch` answering that without asking, and refuses" + " the run when the offer is not taken. `--as-worktree` runs the plan in a worktree of its own on" + " that branch instead, leaving the main checkout as it is, and is refused beside" + " `--create-branch`. Each run writes its session record to `.rafa/runs/<session-id>.json`:" + " the plan, the branch, the pid, the start, the state and the running task, and under `--roadmap`" + " the hop away, when one is. It refuses a plan whose" + " record names another branch, and a plan a session is still running. `rafa loop stop`, `pause`," + " `resume`, `status` and `list` reach the run through that record. With `--retry` or" + " `loop.retries` it goes back into its loop instead of stopping, at most that many times in a row," + " after a red suite step, a task session that exited nonzero, or one that left neither a report nor" + " a commit; a later task done, neither the stopped one nor a repair, starts the count over. With `--continue` a stop that would end the run is handed to a decision instead: retry the" + " task with a new approach, stop with exit code 20, jump over it or defer it until a later task is" + " done; a run left with only passed-over tasks ends with exit code 22 and no wrap-up unless" + " `--force-wrap-up`, and under `--output=json` a decision the line does not name ends the run with" + " exit code 21 and the decision prompt. A run holds its terminal: until" + " phase 6 it refuses `--detached`. With `--runtime` the whole run goes on in that installed rafa," + " never in a `src/` directory.",
17500
17196
  args: [],
17501
17197
  flags: [
17502
17198
  {
@@ -17542,6 +17238,36 @@ var wrapped = wrapPhaseZeroCommand({
17542
17238
  type: "number",
17543
17239
  default: DEFAULT_CI_ATTEMPTS
17544
17240
  },
17241
+ {
17242
+ name: "retry",
17243
+ description: "Goes back into the loop instead of stopping, at most this many times in a row, 1 to 3," + " after a red suite step, a task session that exited nonzero but not on its budget, or one that" + " left neither a report nor a commit. The count starts over once a later task is done, neither" + " the stopped one nor a repair. A report that blocks its task, a refused commit, a moved" + " checkout and an interrupt still stop the run. Outranks `loop.retries` in" + " `.rafa/config.yaml`, `false` unless the config names a count; any other value is refused" + " before the run starts.",
17244
+ type: "number"
17245
+ },
17246
+ {
17247
+ name: "continue",
17248
+ description: "Hands a stop that would end the run to a decision instead: a report that holds its task at" + " once, and a retry-safe stop once its retries are spent. One read-only decision session picks" + " `retry` (with a new approach), `stop`, `jump` (pass the task over this run) or `defer` (pass" + " it over until a later task is done), by the bundled criteria and `loop.continue.criteria`." + " The retries are `loop.retriesOnContinue` unless `--retry` names a count.",
17249
+ type: "boolean"
17250
+ },
17251
+ {
17252
+ name: "decide",
17253
+ description: "Names the decision on the line, `retry`, `stop`, `jump` or `defer`, applied once to the first" + " decision point the run meets, so no decision session is spawned for it. Needs `--continue`.",
17254
+ type: "string"
17255
+ },
17256
+ {
17257
+ name: "approach",
17258
+ description: "The new approach a `--decide=retry` hands the task's next session; required by it and" + " read beside nothing else.",
17259
+ type: "string"
17260
+ },
17261
+ {
17262
+ name: "after",
17263
+ description: "The tracker line, counted from 1, of the task a `--decide=defer` waits on; required by it" + " and read beside nothing else.",
17264
+ type: "number"
17265
+ },
17266
+ {
17267
+ name: "force-wrap-up",
17268
+ description: "Wraps up a `--continue` run left with only passed-over tasks anyway, as a draft pull request" + " listing them, once the pre-wrap-up suite step finds no more new failures than" + " `loop.forceWrapUp.maxNewFailures` tolerates. Needs `--continue`.",
17269
+ type: "boolean"
17270
+ },
17545
17271
  {
17546
17272
  name: "create-branch",
17547
17273
  description: "On `main` or `master`, creates `feat/<plan-stub>` from the latest `origin/<base>`" + " and runs the plan there without asking, or switches to that branch when it already" + " exists. Refuses rather than move when a tracked file is modified, the fetch fails or the" + " base has diverged from its remote.",
@@ -17588,7 +17314,7 @@ var wrapped = wrapPhaseZeroCommand({
17588
17314
  outputs: ["text", "json", "events"],
17589
17315
  spends: {
17590
17316
  when: "always",
17591
- what: "one session per task, one for the wrap-up and up to `loop.wrapUp.retries` more when it opens no pull request, and repair sessions while CI is red"
17317
+ what: "one session per task, one for the wrap-up and up to `loop.wrapUp.retries` more when it opens no pull request," + " repair sessions while CI is red, the sessions of up to `--retry` (`loop.retries`) more passes of the loop" + " in a row after the stops it retries, and under `--continue` one read-only decision session per decision"
17592
17318
  }
17593
17319
  }, start);
17594
17320
  var loopStart = Object.freeze({
@@ -18829,7 +18555,7 @@ var wrapped2 = wrapPhaseZeroCommand({
18829
18555
  var create_default2 = endingWith(wrapped2);
18830
18556
 
18831
18557
  // src/commands/plan/risk.ts
18832
- import { readFileSync as readFileSync25 } from "fs";
18558
+ import { readFileSync as readFileSync23 } from "fs";
18833
18559
  import { resolve as resolve11 } from "path";
18834
18560
  var COMMAND6 = "rafa plan risk";
18835
18561
  var USAGE34 = "rafa plan risk [<plan>] [--strict]";
@@ -18857,7 +18583,7 @@ function strictRefusal(report3) {
18857
18583
  }
18858
18584
  async function readRisk(path, project, config, context, seams) {
18859
18585
  const makeRunners = seams.runners ?? spawningRunners;
18860
- return assessPlanRisk({ path, text: readFileSync25(path, "utf8") }, {
18586
+ return assessPlanRisk({ path, text: readFileSync23(path, "utf8") }, {
18861
18587
  repoRoot: project.root,
18862
18588
  home: project.home,
18863
18589
  settingSources: config.settingSources,
@@ -18940,8 +18666,8 @@ function createPlanRiskCommand(seams = {}) {
18940
18666
  var risk_default = createPlanRiskCommand();
18941
18667
 
18942
18668
  // src/commands/plan/show.ts
18943
- import { readFileSync as readFileSync26 } from "fs";
18944
- import { join as join36 } from "path";
18669
+ import { readFileSync as readFileSync24 } from "fs";
18670
+ import { join as join34 } from "path";
18945
18671
  var USAGE35 = "rafa plan show <stub> [--tracker]";
18946
18672
  var COMMAND7 = "rafa plan show";
18947
18673
  function readTrackerFlag(value) {
@@ -18959,10 +18685,10 @@ function showPlan(plans, stub, tracker) {
18959
18685
  throw new CommandExit(1, `\u274C "${stub}" is no plan stub: a stub is letters, digits, ".", "_" and "-"`);
18960
18686
  }
18961
18687
  const name2 = planFileName(stub, tracker);
18962
- const file = join36(plans.path, name2);
18688
+ const file = join34(plans.path, name2);
18963
18689
  if (!isFile(file))
18964
- throw new CommandExit(1, missingText(stub, tracker, join36(plans.label, name2)));
18965
- const model = parsePlan(readFileSync26(file, "utf8"));
18690
+ throw new CommandExit(1, missingText(stub, tracker, join34(plans.label, name2)));
18691
+ const model = parsePlan(readFileSync24(file, "utf8"));
18966
18692
  const { header } = model;
18967
18693
  return {
18968
18694
  stub,
@@ -18985,7 +18711,7 @@ function taskLine(task) {
18985
18711
  return ` ${checkbox(task.status)} ${task.text}`;
18986
18712
  }
18987
18713
  function renderShownPlan(shown, dirLabel) {
18988
- const relative9 = join36(dirLabel, planFileName(shown.stub, shown.tracker));
18714
+ const relative9 = join34(dirLabel, planFileName(shown.stub, shown.tracker));
18989
18715
  const fileLabel = shown.tracker ? "Tracker" : "File";
18990
18716
  const lines = [`Plan: ${shown.stub}`, `${fileLabel}: ${relative9}`];
18991
18717
  if (shown.header.issue !== null)
@@ -19981,7 +19707,7 @@ async function refreshProjectAfterMerge(options) {
19981
19707
 
19982
19708
  // src/commands/pr/merge-loop-worktree.ts
19983
19709
  import { copyFileSync as copyFileSync3, mkdirSync as mkdirSync10, realpathSync as realpathSync3, statSync as statSync10 } from "fs";
19984
- import { dirname as dirname11, isAbsolute as isAbsolute6, join as join37, relative as relative9, resolve as resolve12, sep as sep4 } from "path";
19710
+ import { dirname as dirname9, isAbsolute as isAbsolute6, join as join35, relative as relative9, resolve as resolve12, sep as sep4 } from "path";
19985
19711
  var REFUSAL_PREFIX = "rafa pr merge refuses";
19986
19712
  var INDENT4 = " ";
19987
19713
  var LIVE_STATES = new Set(["running", "paused"]);
@@ -19991,7 +19717,7 @@ function defaultLoopWorktreeSeams(mainCheckout) {
19991
19717
  gitAt: createGitRunner,
19992
19718
  modifiedAt: (path) => statSync10(path, { throwIfNoEntry: false })?.mtime ?? null,
19993
19719
  copyFile: (from, to) => {
19994
- mkdirSync10(dirname11(to), { recursive: true });
19720
+ mkdirSync10(dirname9(to), { recursive: true });
19995
19721
  copyFileSync3(from, to);
19996
19722
  },
19997
19723
  realPath: (path) => {
@@ -20026,7 +19752,7 @@ function freeLoopWorktree(seams, reading, holder) {
20026
19752
  const files = [];
20027
19753
  for (const name2 of planFileNames(holder.stub)) {
20028
19754
  const from = resolve12(holder.path, reading.planDir, name2);
20029
- const to = join37(into, name2);
19755
+ const to = join35(into, name2);
20030
19756
  try {
20031
19757
  files.push({ name: name2, from, to, outcome: copyPlanFile(seams, from, to) });
20032
19758
  } catch (error) {
@@ -20281,7 +20007,7 @@ function workflowNamesBase(workflowText, base) {
20281
20007
  return readWorkflow(document, base) !== false;
20282
20008
  }
20283
20009
  function readWorkflow(document, base) {
20284
- if (!isPlainObject2(document) || !("on" in document))
20010
+ if (!isPlainObject(document) || !("on" in document))
20285
20011
  return UNKNOWN;
20286
20012
  const on = document.on;
20287
20013
  if (typeof on === "string")
@@ -20291,7 +20017,7 @@ function readWorkflow(document, base) {
20291
20017
  return UNKNOWN;
20292
20018
  return on.includes(PULL_REQUEST_EVENT);
20293
20019
  }
20294
- if (!isPlainObject2(on))
20020
+ if (!isPlainObject(on))
20295
20021
  return UNKNOWN;
20296
20022
  if (!(PULL_REQUEST_EVENT in on))
20297
20023
  return false;
@@ -20300,7 +20026,7 @@ function readWorkflow(document, base) {
20300
20026
  function readPullRequestTrigger(trigger, base) {
20301
20027
  if (trigger === null || trigger === undefined)
20302
20028
  return true;
20303
- if (!isPlainObject2(trigger))
20029
+ if (!isPlainObject(trigger))
20304
20030
  return UNKNOWN;
20305
20031
  const branches = trigger.branches;
20306
20032
  const ignored = trigger["branches-ignore"];
@@ -20401,7 +20127,7 @@ function regexSourceOf(pattern) {
20401
20127
  function escapeRegex(char) {
20402
20128
  return char.replace(REGEX_SPECIAL, "\\$&");
20403
20129
  }
20404
- function isPlainObject2(value) {
20130
+ function isPlainObject(value) {
20405
20131
  return typeof value === "object" && value !== null && !Array.isArray(value);
20406
20132
  }
20407
20133
 
@@ -20995,7 +20721,7 @@ var FOLLOW_UP_TASKS = Object.freeze({
20995
20721
  "ci-test": "A test failed. Read the excerpt below, reproduce the named test" + " locally, and fix the implementation rather than the assertion unless the" + " assertion is itself wrong. Run the gates and push.",
20996
20722
  "ci-other": "A step nothing recognised failed. Read the excerpt below, work out" + " from the step name what that job does, and fix it; there is no pinned plan" + " for this class, so nothing has been assumed about the fix."
20997
20723
  });
20998
- var MIN_FENCE_LENGTH3 = 3;
20724
+ var MIN_FENCE_LENGTH2 = 3;
20999
20725
  function isUsableCap(value) {
21000
20726
  return value !== undefined && Number.isInteger(value) && value > 0;
21001
20727
  }
@@ -21010,7 +20736,7 @@ function excerptLines(evidence, maxLines) {
21010
20736
  }
21011
20737
  function fencedBlock(body, language) {
21012
20738
  const runs = Array.from(body.matchAll(/`+/g), (run) => run[0].length);
21013
- const fence = "`".repeat(Math.max(MIN_FENCE_LENGTH3, ...runs.map((length) => length + 1)));
20739
+ const fence = "`".repeat(Math.max(MIN_FENCE_LENGTH2, ...runs.map((length) => length + 1)));
21014
20740
  const text = body.endsWith(`
21015
20741
  `) || body === "" ? body : `${body}
21016
20742
  `;
@@ -21110,7 +20836,7 @@ function findTriageComment(comments) {
21110
20836
  function read(value) {
21111
20837
  return { value, problem: null };
21112
20838
  }
21113
- function absent4() {
20839
+ function absent3() {
21114
20840
  return { value: null, problem: null };
21115
20841
  }
21116
20842
  function unusable(key, value, expected) {
@@ -21119,7 +20845,7 @@ function unusable(key, value, expected) {
21119
20845
  function readText4(fields, key) {
21120
20846
  const value = fields[key];
21121
20847
  if (value === undefined)
21122
- return absent4();
20848
+ return absent3();
21123
20849
  if (typeof value !== "string")
21124
20850
  return unusable(key, value, `text; write it quoted, as ${key}: "..."`);
21125
20851
  const trimmed = value.trim();
@@ -21128,19 +20854,19 @@ function readText4(fields, key) {
21128
20854
  function readBoolean(fields, key) {
21129
20855
  const value = fields[key];
21130
20856
  if (value === undefined)
21131
- return absent4();
20857
+ return absent3();
21132
20858
  return typeof value === "boolean" ? read(value) : unusable(key, value, "true or false");
21133
20859
  }
21134
20860
  function readCount(fields, key) {
21135
20861
  const value = fields[key];
21136
20862
  if (value === undefined)
21137
- return absent4();
20863
+ return absent3();
21138
20864
  return typeof value === "number" && Number.isInteger(value) && value >= 0 ? read(value) : unusable(key, value, "a whole number from 0");
21139
20865
  }
21140
20866
  function readPaths(fields, key) {
21141
20867
  const value = fields[key];
21142
20868
  if (value === undefined)
21143
- return absent4();
20869
+ return absent3();
21144
20870
  if (!Array.isArray(value))
21145
20871
  return unusable(key, value, "a list of quoted paths");
21146
20872
  const paths = value.filter((item) => typeof item === "string" && item.trim() !== "");
@@ -22340,10 +22066,10 @@ function withTaskBudget(plan2, usd) {
22340
22066
  }
22341
22067
 
22342
22068
  // src/pr/plans/load.ts
22343
- import { existsSync as existsSync14, readFileSync as readFileSync27 } from "fs";
22344
- import { dirname as dirname12, join as join38 } from "path";
22345
- import { fileURLToPath as fileURLToPath4 } from "url";
22346
- var MODULE_DIR4 = dirname12(fileURLToPath4(import.meta.url));
22069
+ import { existsSync as existsSync12, readFileSync as readFileSync25 } from "fs";
22070
+ import { dirname as dirname10, join as join36 } from "path";
22071
+ import { fileURLToPath as fileURLToPath2 } from "url";
22072
+ var MODULE_DIR2 = dirname10(fileURLToPath2(import.meta.url));
22347
22073
  var PINNED_PLANS_DIRNAME = "plans";
22348
22074
  var PINNED_PLAN_CLASSES = Object.freeze([
22349
22075
  ...SIMPLE_TRIAGE_CLASSES,
@@ -22358,22 +22084,22 @@ function hasPinnedPlan(triageClass) {
22358
22084
  function pinnedPlanCandidates(moduleDir, triageClass) {
22359
22085
  const name2 = pinnedPlanFileName(triageClass);
22360
22086
  return [
22361
- join38(moduleDir, PINNED_PLANS_DIRNAME, name2),
22362
- join38(moduleDir, name2)
22087
+ join36(moduleDir, PINNED_PLANS_DIRNAME, name2),
22088
+ join36(moduleDir, name2)
22363
22089
  ];
22364
22090
  }
22365
22091
  function readPinnedPlan(triageClass, lookup = {}) {
22366
22092
  if (!hasPinnedPlan(triageClass)) {
22367
22093
  throw new Error(`No pinned resolve plan ships for ${triageClass}; ` + `one ships for ${PINNED_PLAN_CLASSES.join(", ")}`);
22368
22094
  }
22369
- const candidates = pinnedPlanCandidates(lookup.moduleDir ?? MODULE_DIR4, triageClass);
22370
- const found = candidates.find((candidate) => existsSync14(candidate));
22095
+ const candidates = pinnedPlanCandidates(lookup.moduleDir ?? MODULE_DIR2, triageClass);
22096
+ const found = candidates.find((candidate) => existsSync12(candidate));
22371
22097
  if (found === undefined) {
22372
22098
  throw new Error(`The pinned resolve plan for ${triageClass} is missing: ` + `no file at ${candidates.join(" or ")}`);
22373
22099
  }
22374
- return readFileSync27(found, "utf8");
22100
+ return readFileSync25(found, "utf8");
22375
22101
  }
22376
- var SLOT_PATTERN2 = /\{[A-Z][A-Z0-9_]*\}/g;
22102
+ var SLOT_PATTERN = /\{[A-Z][A-Z0-9_]*\}/g;
22377
22103
  var CONFLICT_SENTENCE_SLOT = "CONFLICT_SENTENCE";
22378
22104
  var CONFLICT_FILES_SLOT = "CONFLICT_FILES";
22379
22105
  var NO_CONFLICT_FILES = "the paths `git status` reports as conflicted";
@@ -22383,7 +22109,7 @@ var FAILING_LOG_EXCERPT_LINES = FOLLOW_UP_EXCERPT_LINES;
22383
22109
  var NO_FAILING_LOG = "No failing-job log was carried into this plan. Read it yourself with" + " `gh run view <run-id> --log-failed` before changing anything, and say that" + " the excerpt was missing rather than guessing what failed.";
22384
22110
  function fillPinnedPlan(template, values) {
22385
22111
  const missing = [];
22386
- const filled = template.replaceAll(SLOT_PATTERN2, (token) => {
22112
+ const filled = template.replaceAll(SLOT_PATTERN, (token) => {
22387
22113
  const name2 = token.slice(1, -1);
22388
22114
  const value = values[name2];
22389
22115
  if (value === undefined) {
@@ -22514,19 +22240,19 @@ function readAttemptRepeat(input) {
22514
22240
 
22515
22241
  // src/commands/pr/resolve-loop.ts
22516
22242
  import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync10 } from "fs";
22517
- import { join as join39 } from "path";
22518
- var RESOLVE_SUBDIR = join39(".rafa", "resolve");
22243
+ import { join as join37 } from "path";
22244
+ var RESOLVE_SUBDIR = join37(".rafa", "resolve");
22519
22245
  var RUN_PREFIX = "pr-";
22520
22246
  var ATTEMPT_PREFIX = "attempt-";
22521
22247
  var LOOP_WORDS = Object.freeze(["loop", "start"]);
22522
22248
  var CHILD_OUTPUT = "text";
22523
22249
  function resolveRunDir(home, number, attempt4) {
22524
- return join39(home, RESOLVE_SUBDIR, `${RUN_PREFIX}${number}`, `${ATTEMPT_PREFIX}${attempt4}`);
22250
+ return join37(home, RESOLVE_SUBDIR, `${RUN_PREFIX}${number}`, `${ATTEMPT_PREFIX}${attempt4}`);
22525
22251
  }
22526
22252
  function writeResolvePlan(write) {
22527
22253
  const dir = resolveRunDir(write.home, write.number, write.attempt);
22528
22254
  mkdirSync11(dir, { recursive: true });
22529
- const path = join39(dir, pinnedPlanFileName(write.triageClass));
22255
+ const path = join37(dir, pinnedPlanFileName(write.triageClass));
22530
22256
  writeFileSync10(path, write.plan, "utf8");
22531
22257
  return path;
22532
22258
  }
@@ -22567,8 +22293,8 @@ async function runResolveLoop(run, seams = {}) {
22567
22293
  }
22568
22294
 
22569
22295
  // src/pr/triage/version-convert.ts
22570
- import { mkdirSync as mkdirSync12, readFileSync as readFileSync28, writeFileSync as writeFileSync11 } from "fs";
22571
- import { dirname as dirname13, join as join40, posix as posix2 } from "path";
22296
+ import { mkdirSync as mkdirSync12, readFileSync as readFileSync26, writeFileSync as writeFileSync11 } from "fs";
22297
+ import { dirname as dirname11, join as join38, posix as posix2 } from "path";
22572
22298
  var FALLBACK_PLAN_PREFIX = "pr-";
22573
22299
  var UNUSABLE_PLAN_CHARACTERS = /[^A-Za-z0-9._-]+/g;
22574
22300
  var UNUSABLE_PLAN_START = /^[^A-Za-z0-9]+/;
@@ -22680,7 +22406,7 @@ function planConversion(input, problems) {
22680
22406
  }
22681
22407
  let branchVersionText;
22682
22408
  try {
22683
- branchVersionText = readFileSync28(join40(input.worktree, settings.releaseVersionFile), "utf8");
22409
+ branchVersionText = readFileSync26(join38(input.worktree, settings.releaseVersionFile), "utf8");
22684
22410
  } catch (error) {
22685
22411
  return refused2(`${settings.releaseVersionFile} could not be read in the worktree: ${messageOf(error)}`, problems);
22686
22412
  }
@@ -22706,9 +22432,9 @@ function writeConversion(input, plan2) {
22706
22432
  [input.settings.releaseChangelog, plan2.changelogText]
22707
22433
  ];
22708
22434
  for (const [path, text] of writes) {
22709
- const target = join40(input.worktree, path);
22435
+ const target = join38(input.worktree, path);
22710
22436
  try {
22711
- mkdirSync12(dirname13(target), { recursive: true });
22437
+ mkdirSync12(dirname11(target), { recursive: true });
22712
22438
  writeFileSync11(target, text);
22713
22439
  } catch (error) {
22714
22440
  return `${path} could not be written in the worktree, which may hold part of the conversion: ${messageOf(error)}`;
@@ -23707,9 +23433,9 @@ function tagSettle(worktree, mode, delivered) {
23707
23433
  }
23708
23434
 
23709
23435
  // src/release/settle-worktree.ts
23710
- import { existsSync as existsSync15, mkdtempSync as mkdtempSync2, realpathSync as realpathSync4, rmSync as rmSync3 } from "fs";
23436
+ import { existsSync as existsSync13, mkdtempSync as mkdtempSync2, realpathSync as realpathSync4, rmSync as rmSync3 } from "fs";
23711
23437
  import { tmpdir as tmpdir2 } from "os";
23712
- import { join as join41 } from "path";
23438
+ import { join as join39 } from "path";
23713
23439
  var SETTLE_SCRATCH_PREFIX = "rafa-settle-";
23714
23440
  var WORKTREE_DIRECTORY = "base";
23715
23441
  function removeScratch(git, scratch, path) {
@@ -23726,14 +23452,14 @@ function removeScratch(git, scratch, path) {
23726
23452
  problems.push(`the worktree record of ${path} could not be pruned: ${gitSaid(pruned)}`);
23727
23453
  }
23728
23454
  }
23729
- if (problems.length === 0 && existsSync15(scratch)) {
23455
+ if (problems.length === 0 && existsSync13(scratch)) {
23730
23456
  problems.push(`the scratch directory ${scratch} is still there`);
23731
23457
  }
23732
23458
  return problems.length === 0 ? null : problems.join("; ");
23733
23459
  }
23734
23460
  function makeScratch(root) {
23735
23461
  try {
23736
- return realpathSync4(mkdtempSync2(join41(root, SETTLE_SCRATCH_PREFIX)));
23462
+ return realpathSync4(mkdtempSync2(join39(root, SETTLE_SCRATCH_PREFIX)));
23737
23463
  } catch (error) {
23738
23464
  return { ok: false, problem: `no scratch directory could be made under ${root}: ${messageOf(error)}`, leftover: null };
23739
23465
  }
@@ -23755,7 +23481,7 @@ function addWorktree(options) {
23755
23481
  const scratch = makeScratch(options.scratchRoot ?? tmpdir2());
23756
23482
  if (typeof scratch !== "string")
23757
23483
  return scratch;
23758
- const path = join41(scratch, WORKTREE_DIRECTORY);
23484
+ const path = join39(scratch, WORKTREE_DIRECTORY);
23759
23485
  const added = git(["worktree", "add", "--detach", path, commit]);
23760
23486
  if (!added.ok) {
23761
23487
  return {
@@ -24144,8 +23870,8 @@ function createReleaseSettleCommand(seams = DEFAULT_RELEASE_SETTLE_SEAMS) {
24144
23870
  var settle_default = createReleaseSettleCommand();
24145
23871
 
24146
23872
  // src/commands/release/tag.ts
24147
- import { readFileSync as readFileSync29 } from "fs";
24148
- import { join as join42 } from "path";
23873
+ import { readFileSync as readFileSync27 } from "fs";
23874
+ import { join as join40 } from "path";
24149
23875
 
24150
23876
  // src/release/tag-push.ts
24151
23877
  var LOCAL_REMOTE = ".";
@@ -24221,13 +23947,13 @@ function readBranch2(git) {
24221
23947
  }
24222
23948
  function textOf(path) {
24223
23949
  try {
24224
- return readFileSync29(path, "utf8");
23950
+ return readFileSync27(path, "utf8");
24225
23951
  } catch {
24226
23952
  return null;
24227
23953
  }
24228
23954
  }
24229
23955
  function readVersion(root, configured) {
24230
- const resolved = join42(root, configured);
23956
+ const resolved = join40(root, configured);
24231
23957
  const text = textOf(resolved);
24232
23958
  if (text === null) {
24233
23959
  return {
@@ -24241,7 +23967,7 @@ function readVersion(root, configured) {
24241
23967
  return version2 === null ? { path: configured, text, version: null, problem: `${resolved} declares no version this can read` } : { path: configured, text, version: version2, problem: null };
24242
23968
  }
24243
23969
  function readNewestRelease(root, configured) {
24244
- const resolved = join42(root, configured);
23970
+ const resolved = join40(root, configured);
24245
23971
  const text = textOf(resolved);
24246
23972
  if (text === null) {
24247
23973
  return { path: configured, version: null, problem: `the changelog could not be read at ${resolved}` };
@@ -24525,16 +24251,16 @@ var roadmap_default = createRoadmapCommand();
24525
24251
 
24526
24252
  // src/commands/skill/backfill.ts
24527
24253
  import { statSync as statSync12 } from "fs";
24528
- import { join as join47, resolve as resolve15 } from "path";
24254
+ import { join as join45, resolve as resolve15 } from "path";
24529
24255
 
24530
24256
  // src/backfill/backup.ts
24531
- import { copyFileSync as copyFileSync4, existsSync as existsSync16, mkdirSync as mkdirSync13 } from "fs";
24532
- import { dirname as dirname14, isAbsolute as isAbsolute7, join as join44, relative as relative10, resolve as resolve13, sep as sep5 } from "path";
24257
+ import { copyFileSync as copyFileSync4, existsSync as existsSync14, mkdirSync as mkdirSync13 } from "fs";
24258
+ import { dirname as dirname12, isAbsolute as isAbsolute7, join as join42, relative as relative10, resolve as resolve13, sep as sep5 } from "path";
24533
24259
 
24534
24260
  // src/backfill/proposal-file.ts
24535
- import { readdirSync as readdirSync7, readFileSync as readFileSync30 } from "fs";
24536
- import { join as join43 } from "path";
24537
- var BACKFILL_PATH = join43(".rafa", "backfill");
24261
+ import { readdirSync as readdirSync7, readFileSync as readFileSync28 } from "fs";
24262
+ import { join as join41 } from "path";
24263
+ var BACKFILL_PATH = join41(".rafa", "backfill");
24538
24264
  var PROPOSAL_FILE_PREFIX = "proposals-";
24539
24265
  var PROPOSAL_FILE_EXTENSION = ".yaml";
24540
24266
  var SHA256_PATTERN = /^[0-9a-f]{64}$/;
@@ -24560,7 +24286,7 @@ var PROPOSAL_FILE_HEADER = [
24560
24286
  ].join(`
24561
24287
  `);
24562
24288
  function proposalPath(dir, batch) {
24563
- return join43(dir, `${PROPOSAL_FILE_PREFIX}${String(batch).padStart(2, "0")}${PROPOSAL_FILE_EXTENSION}`);
24289
+ return join41(dir, `${PROPOSAL_FILE_PREFIX}${String(batch).padStart(2, "0")}${PROPOSAL_FILE_EXTENSION}`);
24564
24290
  }
24565
24291
  function isProposalFileName(name2) {
24566
24292
  return name2.startsWith(PROPOSAL_FILE_PREFIX) && name2.endsWith(PROPOSAL_FILE_EXTENSION);
@@ -24696,9 +24422,9 @@ function readProposalFiles(dir) {
24696
24422
  return [];
24697
24423
  }
24698
24424
  return names.filter((name2) => isProposalFileName(name2)).sort((left, right) => left.localeCompare(right)).map((name2) => {
24699
- const path = join43(dir, name2);
24425
+ const path = join41(dir, name2);
24700
24426
  try {
24701
- return { path, result: parseProposalFile(readFileSync30(path, "utf8")) };
24427
+ return { path, result: parseProposalFile(readFileSync28(path, "utf8")) };
24702
24428
  } catch (error) {
24703
24429
  return { path, result: unreadable2(`the file cannot be read (${error instanceof Error ? error.message : String(error)})`) };
24704
24430
  }
@@ -24706,10 +24432,10 @@ function readProposalFiles(dir) {
24706
24432
  }
24707
24433
 
24708
24434
  // src/backfill/backup.ts
24709
- var BACKUP_PATH = join44(BACKFILL_PATH, "backup");
24435
+ var BACKUP_PATH = join42(BACKFILL_PATH, "backup");
24710
24436
  var BACKUP_KINDS = ["copied", "kept", "tracked", "outside", "missing"];
24711
24437
  function backupDirectory(base) {
24712
- return join44(base, BACKUP_PATH);
24438
+ return join42(base, BACKUP_PATH);
24713
24439
  }
24714
24440
  function isUnder(root, path) {
24715
24441
  const step = relative10(resolve13(root), resolve13(path));
@@ -24718,7 +24444,7 @@ function isUnder(root, path) {
24718
24444
  function backupPathFor(path, root, backupDir) {
24719
24445
  if (!isUnder(root, path))
24720
24446
  return null;
24721
- return join44(backupDir, relative10(resolve13(root), resolve13(path)));
24447
+ return join42(backupDir, relative10(resolve13(root), resolve13(path)));
24722
24448
  }
24723
24449
  function backupFile(path, options) {
24724
24450
  if (options.repoRoot !== null && isUnder(options.repoRoot, path)) {
@@ -24727,11 +24453,11 @@ function backupFile(path, options) {
24727
24453
  const backup = backupPathFor(path, options.root, options.backupDir);
24728
24454
  if (backup === null)
24729
24455
  return { path, kind: "outside", backup: null };
24730
- if (!existsSync16(path))
24456
+ if (!existsSync14(path))
24731
24457
  return { path, kind: "missing", backup: null };
24732
- if (existsSync16(backup))
24458
+ if (existsSync14(backup))
24733
24459
  return { path, kind: "kept", backup };
24734
- mkdirSync13(dirname14(backup), { recursive: true });
24460
+ mkdirSync13(dirname12(backup), { recursive: true });
24735
24461
  copyFileSync4(path, backup);
24736
24462
  return { path, kind: "copied", backup };
24737
24463
  }
@@ -24754,7 +24480,7 @@ function countBackups(results) {
24754
24480
  }
24755
24481
 
24756
24482
  // src/backfill/derive.ts
24757
- import { readFileSync as readFileSync31, writeFileSync as writeFileSync12 } from "fs";
24483
+ import { readFileSync as readFileSync29, writeFileSync as writeFileSync12 } from "fs";
24758
24484
  import { basename as basename7 } from "path";
24759
24485
  var DERIVED_FIELDS = ["stack", "paths", "when_to_use"];
24760
24486
  function renderWhenToUse(trigger, prevents) {
@@ -24880,7 +24606,7 @@ function planFile(entry, options) {
24880
24606
  }
24881
24607
  let original;
24882
24608
  try {
24883
- original = readFileSync31(entry.path, "utf8");
24609
+ original = readFileSync29(entry.path, "utf8");
24884
24610
  } catch (error) {
24885
24611
  return inert(entry, "skipped", `the file cannot be read (${error instanceof Error ? error.message : String(error)})`);
24886
24612
  }
@@ -24924,7 +24650,7 @@ function planDerivation(root, options) {
24924
24650
  function applyFile(action, options) {
24925
24651
  if (action.text === null || action.original === null)
24926
24652
  return action;
24927
- const current = readFileSync31(action.path, "utf8");
24653
+ const current = readFileSync29(action.path, "utf8");
24928
24654
  if (current !== action.original) {
24929
24655
  return {
24930
24656
  ...action,
@@ -24955,7 +24681,7 @@ function applyDerivation(plan2, options) {
24955
24681
  }
24956
24682
 
24957
24683
  // src/backfill/proposal-batch.ts
24958
- import { readFileSync as readFileSync32 } from "fs";
24684
+ import { readFileSync as readFileSync30 } from "fs";
24959
24685
  import { relative as relative11 } from "path";
24960
24686
 
24961
24687
  // src/demote/report.ts
@@ -25254,7 +24980,7 @@ function readCandidate(root, entry) {
25254
24980
  return null;
25255
24981
  let text;
25256
24982
  try {
25257
- text = readFileSync32(entry.path, "utf8");
24983
+ text = readFileSync30(entry.path, "utf8");
25258
24984
  } catch {
25259
24985
  return null;
25260
24986
  }
@@ -25360,8 +25086,8 @@ function parseSessionAnswer(stdout) {
25360
25086
  }
25361
25087
 
25362
25088
  // src/backfill/propose.ts
25363
- import { mkdirSync as mkdirSync14, readFileSync as readFileSync33, writeFileSync as writeFileSync13 } from "fs";
25364
- import { join as join45, resolve as resolve14 } from "path";
25089
+ import { mkdirSync as mkdirSync14, readFileSync as readFileSync31, writeFileSync as writeFileSync13 } from "fs";
25090
+ import { join as join43, resolve as resolve14 } from "path";
25365
25091
  var PROPOSAL_ACTION_KINDS = [
25366
25092
  "applied",
25367
25093
  "unchanged",
@@ -25510,7 +25236,7 @@ function writeRow(path, row2, original, options) {
25510
25236
  };
25511
25237
  }
25512
25238
  function applyRow(root, row2, options) {
25513
- const path = join45(root, ...row2.path.split("/"));
25239
+ const path = join43(root, ...row2.path.split("/"));
25514
25240
  if (row2.status === UNANSWERED_ROW) {
25515
25241
  return inert2(path, row2, "unanswered", row2.note ?? "the row carries no answer");
25516
25242
  }
@@ -25519,7 +25245,7 @@ function applyRow(root, row2, options) {
25519
25245
  }
25520
25246
  let original;
25521
25247
  try {
25522
- original = readFileSync33(path, "utf8");
25248
+ original = readFileSync31(path, "utf8");
25523
25249
  } catch {
25524
25250
  return inert2(path, row2, "refused", "the file the row names is gone");
25525
25251
  }
@@ -25547,7 +25273,7 @@ function applyProposals(files, root, options) {
25547
25273
  for (const file of files) {
25548
25274
  const refusal19 = fileRefusal(file, root);
25549
25275
  for (const row2 of file.rows) {
25550
- const action = refusal19 === null ? applyRow(root, row2, options) : inert2(join45(root, ...row2.path.split("/")), row2, "refused", refusal19);
25276
+ const action = refusal19 === null ? applyRow(root, row2, options) : inert2(join43(root, ...row2.path.split("/")), row2, "refused", refusal19);
25551
25277
  actions.push(action);
25552
25278
  if (row2.trigger !== null && action.kind !== "refused" && action.kind !== "unanswered") {
25553
25279
  triggers[row2.name] = row2.trigger;
@@ -25558,13 +25284,13 @@ function applyProposals(files, root, options) {
25558
25284
  }
25559
25285
 
25560
25286
  // src/demote/select.ts
25561
- import { readdirSync as readdirSync8, readFileSync as readFileSync34, realpathSync as realpathSync5, statSync as statSync11 } from "fs";
25562
- import { basename as basename8, dirname as dirname15, join as join46 } from "path";
25287
+ import { readdirSync as readdirSync8, readFileSync as readFileSync32, realpathSync as realpathSync5, statSync as statSync11 } from "fs";
25288
+ import { basename as basename8, dirname as dirname13, join as join44 } from "path";
25563
25289
  var SKILLS_DIRECTORY = "skills";
25564
25290
  var CLAUDE_DIRECTORY = ".claude";
25565
25291
  var AUTO_EXTRACTED_ORIGIN = "auto-extracted";
25566
- var DEMOTED_PATH = join46(".rafa", "demoted");
25567
- var INSTINCTS_PATH = join46(".rafa", "instincts");
25292
+ var DEMOTED_PATH = join44(".rafa", "demoted");
25293
+ var INSTINCTS_PATH = join44(".rafa", "instincts");
25568
25294
  var REPORT_FILE = "report.md";
25569
25295
  function realOrSelf(path) {
25570
25296
  try {
@@ -25583,18 +25309,18 @@ function samePath(left, right) {
25583
25309
  function resolveDemotionScope(dir, home) {
25584
25310
  if (basename8(dir) !== SKILLS_DIRECTORY)
25585
25311
  return null;
25586
- const claude = dirname15(dir);
25312
+ const claude = dirname13(dir);
25587
25313
  if (basename8(claude) !== CLAUDE_DIRECTORY)
25588
25314
  return null;
25589
- const base = dirname15(claude);
25590
- const demotedDir = join46(base, DEMOTED_PATH);
25315
+ const base = dirname13(claude);
25316
+ const demotedDir = join44(base, DEMOTED_PATH);
25591
25317
  return {
25592
25318
  scope: samePath(base, home) ? "user" : "project",
25593
25319
  dir,
25594
25320
  base,
25595
25321
  demotedDir,
25596
- instinctsDir: join46(base, INSTINCTS_PATH),
25597
- reportPath: join46(demotedDir, REPORT_FILE)
25322
+ instinctsDir: join44(base, INSTINCTS_PATH),
25323
+ reportPath: join44(demotedDir, REPORT_FILE)
25598
25324
  };
25599
25325
  }
25600
25326
  function entryKind(path) {
@@ -25620,7 +25346,7 @@ function readSelected(absolute, path, entries) {
25620
25346
  let text;
25621
25347
  let mtime;
25622
25348
  try {
25623
- text = readFileSync34(absolute, "utf8");
25349
+ text = readFileSync32(absolute, "utf8");
25624
25350
  mtime = statSync11(absolute).mtime.toISOString();
25625
25351
  } catch {
25626
25352
  return null;
@@ -25636,14 +25362,14 @@ function selectsLearned(text, scope) {
25636
25362
  return origin === AUTO_EXTRACTED_ORIGIN;
25637
25363
  }
25638
25364
  function selectLearned(scope) {
25639
- const dir = join46(scope.dir, LEARNED_DIRECTORY);
25365
+ const dir = join44(scope.dir, LEARNED_DIRECTORY);
25640
25366
  if (entryKind(dir) !== "directory")
25641
25367
  return [];
25642
25368
  const selected = [];
25643
25369
  for (const name2 of visibleNames(dir)) {
25644
25370
  if (!name2.endsWith(MARKDOWN_EXTENSION) || name2 === MARKDOWN_EXTENSION)
25645
25371
  continue;
25646
- const absolute = join46(dir, name2);
25372
+ const absolute = join44(dir, name2);
25647
25373
  if (entryKind(absolute) !== "file")
25648
25374
  continue;
25649
25375
  const file = readSelected(absolute, `${LEARNED_DIRECTORY}/${name2}`, undefined);
@@ -25657,10 +25383,10 @@ function selectSkillFiles(scope) {
25657
25383
  for (const name2 of visibleNames(scope.dir)) {
25658
25384
  if (name2 === LEARNED_DIRECTORY)
25659
25385
  continue;
25660
- const skillDir = join46(scope.dir, name2);
25386
+ const skillDir = join44(scope.dir, name2);
25661
25387
  if (entryKind(skillDir) !== "directory")
25662
25388
  continue;
25663
- const absolute = join46(skillDir, SKILL_FILE);
25389
+ const absolute = join44(skillDir, SKILL_FILE);
25664
25390
  if (entryKind(absolute) !== "file")
25665
25391
  continue;
25666
25392
  const file = readSelected(absolute, `${name2}/${SKILL_FILE}`, visibleNames(skillDir));
@@ -25845,7 +25571,7 @@ function reviewedProposals(dir) {
25845
25571
  return read2.flatMap((entry) => entry.result.file === null ? [] : [entry.result.file]);
25846
25572
  }
25847
25573
  function rowPaths(files, root) {
25848
- return files.flatMap((file) => file.status !== REVIEWED_FILE ? [] : file.rows.filter((row2) => row2.status === ANSWERED_ROW).map((row2) => join47(root, ...row2.path.split("/"))));
25574
+ return files.flatMap((file) => file.status !== REVIEWED_FILE ? [] : file.rows.filter((row2) => row2.status === ANSWERED_ROW).map((row2) => join45(root, ...row2.path.split("/"))));
25849
25575
  }
25850
25576
  function runApply(context, scope, backfillDir, projectRoot2) {
25851
25577
  const options = checkOptions(context, projectRoot2);
@@ -25891,7 +25617,7 @@ async function runBackfill(context, seams) {
25891
25617
  const dir = readBackfillDirectory(context.args, seams.cwd);
25892
25618
  const projectRoot2 = readConsumerRoot(context.flags["project"], seams.cwd);
25893
25619
  const scope = backfillScopeOf(dir, projectOf11(context).home);
25894
- const backfillDir = join47(scope.base, BACKFILL_PATH);
25620
+ const backfillDir = join45(scope.base, BACKFILL_PATH);
25895
25621
  if (mode === "propose") {
25896
25622
  await runPropose(context, scope, backfillDir, seams);
25897
25623
  return;
@@ -26005,13 +25731,13 @@ function createSkillCheckCommand(seams = DEFAULT_CHECK_SEAMS) {
26005
25731
  var check_default3 = createSkillCheckCommand();
26006
25732
 
26007
25733
  // src/commands/skill/demote.ts
26008
- import { existsSync as existsSync18, mkdirSync as mkdirSync16, readFileSync as readFileSync36, statSync as statSync14, writeFileSync as writeFileSync15 } from "fs";
26009
- import { dirname as dirname17, resolve as resolve16 } from "path";
25734
+ import { existsSync as existsSync16, mkdirSync as mkdirSync16, readFileSync as readFileSync34, statSync as statSync14, writeFileSync as writeFileSync15 } from "fs";
25735
+ import { dirname as dirname15, resolve as resolve16 } from "path";
26010
25736
 
26011
25737
  // src/demote/apply.ts
26012
- import { copyFileSync as copyFileSync5, existsSync as existsSync17, mkdirSync as mkdirSync15, mkdtempSync as mkdtempSync3, readdirSync as readdirSync9, readFileSync as readFileSync35, renameSync as renameSync2, rmSync as rmSync4, statSync as statSync13, unlinkSync, writeFileSync as writeFileSync14 } from "fs";
25738
+ import { copyFileSync as copyFileSync5, existsSync as existsSync15, mkdirSync as mkdirSync15, mkdtempSync as mkdtempSync3, readdirSync as readdirSync9, readFileSync as readFileSync33, renameSync as renameSync2, rmSync as rmSync4, statSync as statSync13, unlinkSync, writeFileSync as writeFileSync14 } from "fs";
26013
25739
  import { tmpdir as tmpdir3 } from "os";
26014
- import { dirname as dirname16, join as join48 } from "path";
25740
+ import { dirname as dirname14, join as join46 } from "path";
26015
25741
  var DEMOTION_ACTION_KINDS = [
26016
25742
  "demoted",
26017
25743
  "kept",
@@ -26032,13 +25758,13 @@ function learnedSkillName(path) {
26032
25758
  return base.slice(0, -MARKDOWN_EXTENSION.length);
26033
25759
  }
26034
25760
  function sourcePath(scope, path) {
26035
- return join48(scope.dir, ...path.split("/"));
25761
+ return join46(scope.dir, ...path.split("/"));
26036
25762
  }
26037
25763
  function demotedPath(scope, path) {
26038
- return join48(scope.demotedDir, ...path.split("/"));
25764
+ return join46(scope.demotedDir, ...path.split("/"));
26039
25765
  }
26040
25766
  function promotedPath(scope, path) {
26041
- return join48(scope.dir, learnedSkillName(path), SKILL_FILE);
25767
+ return join46(scope.dir, learnedSkillName(path), SKILL_FILE);
26042
25768
  }
26043
25769
  function takenInstinctIds(dir) {
26044
25770
  let names;
@@ -26051,7 +25777,7 @@ function takenInstinctIds(dir) {
26051
25777
  }
26052
25778
  function readText5(path) {
26053
25779
  try {
26054
- return statSync13(path).isFile() ? readFileSync35(path, "utf8") : null;
25780
+ return statSync13(path).isFile() ? readFileSync33(path, "utf8") : null;
26055
25781
  } catch {
26056
25782
  return null;
26057
25783
  }
@@ -26079,7 +25805,7 @@ function planKept(scope, row2, file) {
26079
25805
  return inert3(row2.path, "procedure", "kept", "procedure-shaped: left where it is, for the backfill");
26080
25806
  }
26081
25807
  const to = promotedPath(scope, row2.path);
26082
- if (existsSync17(to) || existsSync17(dirname16(to))) {
25808
+ if (existsSync15(to) || existsSync15(dirname14(to))) {
26083
25809
  return inert3(row2.path, "procedure", "refused", `kept as a skill, but ${to} is already there`);
26084
25810
  }
26085
25811
  return {
@@ -26093,7 +25819,7 @@ function planKept(scope, row2, file) {
26093
25819
  };
26094
25820
  }
26095
25821
  function checkCandidate(dir, id2, text, pathDirs) {
26096
- const path = join48(dir, `${id2}${MARKDOWN_EXTENSION}`);
25822
+ const path = join46(dir, `${id2}${MARKDOWN_EXTENSION}`);
26097
25823
  writeFileSync14(path, text, "utf8");
26098
25824
  const report5 = checkFile(path, "instinct", { projectRoot: null, pathDirs });
26099
25825
  return failureLines2(report5.issues);
@@ -26114,11 +25840,11 @@ function planDemoted(scope, row2, file, context) {
26114
25840
  if (failures.length > 0) {
26115
25841
  return inert3(row2.path, "observation", "refused", `the record ${id2} fails the checker \u2014 ${failures.join("; ")}`);
26116
25842
  }
26117
- const destination = join48(scope.instinctsDir, `${id2}${MARKDOWN_EXTENSION}`);
25843
+ const destination = join46(scope.instinctsDir, `${id2}${MARKDOWN_EXTENSION}`);
26118
25844
  const kept = demotedPath(scope, row2.path);
26119
- if (existsSync17(destination))
25845
+ if (existsSync15(destination))
26120
25846
  return inert3(row2.path, "observation", "refused", `${destination} is already there`);
26121
- if (existsSync17(kept))
25847
+ if (existsSync15(kept))
26122
25848
  return inert3(row2.path, "observation", "refused", `${kept} is already there`);
26123
25849
  context.taken.push(id2);
26124
25850
  return {
@@ -26161,7 +25887,7 @@ function countActions(actions) {
26161
25887
  return counts;
26162
25888
  }
26163
25889
  function planDemotion(report5, scope, seams = DEFAULT_APPLY_SEAMS) {
26164
- const temp = mkdtempSync3(join48(tmpdir3(), "rafa-demote-"));
25890
+ const temp = mkdtempSync3(join46(tmpdir3(), "rafa-demote-"));
26165
25891
  const taken = [...takenInstinctIds(scope.instinctsDir)];
26166
25892
  try {
26167
25893
  const actions = report5.rows.map((row2) => planRow(scope, row2, { temp, taken, seams }));
@@ -26171,7 +25897,7 @@ function planDemotion(report5, scope, seams = DEFAULT_APPLY_SEAMS) {
26171
25897
  }
26172
25898
  }
26173
25899
  function moveFile(move3) {
26174
- mkdirSync15(dirname16(move3.to), { recursive: true });
25900
+ mkdirSync15(dirname14(move3.to), { recursive: true });
26175
25901
  try {
26176
25902
  renameSync2(move3.from, move3.to);
26177
25903
  } catch {
@@ -26182,7 +25908,7 @@ function moveFile(move3) {
26182
25908
  function applyDemotion(plan2) {
26183
25909
  for (const action of plan2.actions) {
26184
25910
  if (action.write !== null) {
26185
- mkdirSync15(dirname16(action.write.path), { recursive: true });
25911
+ mkdirSync15(dirname14(action.write.path), { recursive: true });
26186
25912
  writeFileSync14(action.write.path, action.write.text, "utf8");
26187
25913
  }
26188
25914
  if (action.move !== null)
@@ -26280,9 +26006,9 @@ function verdictLine2(report5) {
26280
26006
  return `${String(report5.rows.length)} file(s): ${parts.join(", ")}`;
26281
26007
  }
26282
26008
  function previousReport(path, context) {
26283
- if (!existsSync18(path))
26009
+ if (!existsSync16(path))
26284
26010
  return null;
26285
- const parsed = parseDemotionReport(readFileSync36(path, "utf8"));
26011
+ const parsed = parseDemotionReport(readFileSync34(path, "utf8"));
26286
26012
  if (parsed.report !== null)
26287
26013
  return parsed.report;
26288
26014
  context.output.warn(`the report already at ${path} does not parse, so no override in it is carried forward:` + ` ${parsed.issues.map((issue2) => `line ${String(issue2.line)} ${issue2.code}`).join(", ")}`);
@@ -26291,7 +26017,7 @@ function previousReport(path, context) {
26291
26017
  function runWrite(context, scope) {
26292
26018
  const files = selectSources(scope);
26293
26019
  const report5 = buildDemotionReport(files, previousReport(scope.reportPath, context));
26294
- mkdirSync16(dirname17(scope.reportPath), { recursive: true });
26020
+ mkdirSync16(dirname15(scope.reportPath), { recursive: true });
26295
26021
  writeFileSync15(scope.reportPath, writeDemotionReport(report5), "utf8");
26296
26022
  const result2 = {
26297
26023
  applied: false,
@@ -26315,10 +26041,10 @@ function runWrite(context, scope) {
26315
26041
  context.output.info(line);
26316
26042
  }
26317
26043
  function reviewedReport(scope) {
26318
- if (!existsSync18(scope.reportPath)) {
26044
+ if (!existsSync16(scope.reportPath)) {
26319
26045
  throw new CommandExit(1, `\u274C No report at ${scope.reportPath}; run "rafa skill demote ${scope.dir}" first, then review it`);
26320
26046
  }
26321
- const parsed = parseDemotionReport(readFileSync36(scope.reportPath, "utf8"));
26047
+ const parsed = parseDemotionReport(readFileSync34(scope.reportPath, "utf8"));
26322
26048
  if (parsed.report === null) {
26323
26049
  const lines = parsed.issues.map((issue2) => ` line ${String(issue2.line)} ${issue2.code}: ${issue2.message}`);
26324
26050
  throw new CommandExit(1, [`\u274C ${scope.reportPath} does not parse:`, ...lines].join(`
@@ -26691,7 +26417,7 @@ function statusData(sections) {
26691
26417
  }
26692
26418
 
26693
26419
  // src/status/sections.ts
26694
- import { existsSync as existsSync19 } from "fs";
26420
+ import { existsSync as existsSync17 } from "fs";
26695
26421
 
26696
26422
  // src/status/blocked-count.ts
26697
26423
  async function labelsCount(options) {
@@ -26951,7 +26677,7 @@ function nextOfPlace(current, epic, walk) {
26951
26677
  return epic?.members.some((member) => member.number === line.issue) === true ? line.issue : null;
26952
26678
  }
26953
26679
  function hasBoards(root, listing2) {
26954
- if (existsSync19(positionFilePath(root)))
26680
+ if (existsSync17(positionFilePath(root)))
26955
26681
  return true;
26956
26682
  return listing2.some((issue2) => issue2.state === "OPEN" && issue2.labels.includes(ROADMAP_LABEL));
26957
26683
  }
@@ -27126,8 +26852,8 @@ function createStatusCommand(seams = DEFAULT_STATUS_SEAMS) {
27126
26852
  var status_default4 = createStatusCommand();
27127
26853
 
27128
26854
  // src/commands/stretch/end.ts
27129
- import { existsSync as existsSync20, mkdirSync as mkdirSync17, readFileSync as readFileSync37, writeFileSync as writeFileSync16 } from "fs";
27130
- import { dirname as dirname18, join as join49 } from "path";
26855
+ import { existsSync as existsSync18, mkdirSync as mkdirSync17, readFileSync as readFileSync35, writeFileSync as writeFileSync16 } from "fs";
26856
+ import { dirname as dirname16, join as join47 } from "path";
27131
26857
  var COMMAND_NAME19 = "rafa stretch end";
27132
26858
  var STRETCH_END_USAGE = "rafa stretch end [--dry-run]";
27133
26859
  var REPORT_FILE2 = "report.md";
@@ -27135,10 +26861,10 @@ var BODY_FILE = "pr-body.md";
27135
26861
  var REFUSED_EXIT10 = 1;
27136
26862
  var FAILED_EXIT6 = 2;
27137
26863
  function reportPath(root, n) {
27138
- return join49(stretchFolder(root, n), REPORT_FILE2);
26864
+ return join47(stretchFolder(root, n), REPORT_FILE2);
27139
26865
  }
27140
26866
  function bodyPath(root, n) {
27141
- return join49(stretchFolder(root, n), BODY_FILE);
26867
+ return join47(stretchFolder(root, n), BODY_FILE);
27142
26868
  }
27143
26869
  function endTitle(n) {
27144
26870
  return `Stretch ${String(n)}`;
@@ -27174,11 +26900,11 @@ function requireStretchBase2(prBase) {
27174
26900
  }
27175
26901
  function readReport(root, n) {
27176
26902
  const path = reportPath(root, n);
27177
- if (!existsSync20(path)) {
26903
+ if (!existsSync18(path)) {
27178
26904
  throw refusal19(`there is no ${path}: write the stretch report first, since it is the pull request's body. Nothing was run.`);
27179
26905
  }
27180
26906
  try {
27181
- return readFileSync37(path, "utf8");
26907
+ return readFileSync35(path, "utf8");
27182
26908
  } catch (error) {
27183
26909
  throw refusal19(`${path} could not be read: ${messageOf(error)}. Nothing was run.`);
27184
26910
  }
@@ -27191,11 +26917,11 @@ function recordText(record, name2, path) {
27191
26917
  }
27192
26918
  function readStretchRecord(root, n) {
27193
26919
  const path = stretchRecordPath(root, n);
27194
- if (!existsSync20(path))
26920
+ if (!existsSync18(path))
27195
26921
  return null;
27196
26922
  let value;
27197
26923
  try {
27198
- value = JSON.parse(readFileSync37(path, "utf8"));
26924
+ value = JSON.parse(readFileSync35(path, "utf8"));
27199
26925
  } catch (error) {
27200
26926
  throw refusal19(`${path} could not be read as JSON: ${messageOf(error)}. Nothing was run.`);
27201
26927
  }
@@ -27240,7 +26966,7 @@ function putBack(context, input, merged2) {
27240
26966
  const path = configFilePath(input.root);
27241
26967
  let edit;
27242
26968
  try {
27243
- edit = withConfigSetting(readFileSync37(path, "utf8"), PR_BASE_KEY, value, path);
26969
+ edit = withConfigSetting(readFileSync35(path, "utf8"), PR_BASE_KEY, value, path);
27244
26970
  } catch (error) {
27245
26971
  const why = error instanceof ConfigSetRefusal ? `${PR_BASE_KEY} cannot be set: ${error.message}` : `${path} could not be read: ${messageOf(error)}`;
27246
26972
  throw refusal19(`${why}. Nothing was set.`);
@@ -27277,7 +27003,7 @@ async function openEnd(context, input, body, seams) {
27277
27003
  return;
27278
27004
  }
27279
27005
  try {
27280
- mkdirSync17(dirname18(path), { recursive: true });
27006
+ mkdirSync17(dirname16(path), { recursive: true });
27281
27007
  writeFileSync16(path, body, "utf8");
27282
27008
  } catch (error) {
27283
27009
  throw failure4(`${path} could not be written: ${messageOf(error)}. No pull request was opened.`);
@@ -27362,8 +27088,8 @@ function createUpdateStub(spec) {
27362
27088
  var board_default = createUpdateStub({ action: "board", summary: "apply the board's newest setup and its migrations", issue: 717 });
27363
27089
 
27364
27090
  // src/project/lock.ts
27365
- import { existsSync as existsSync21, lstatSync as lstatSync2, readFileSync as readFileSync38, writeFileSync as writeFileSync17 } from "fs";
27366
- import { join as join50 } from "path";
27091
+ import { existsSync as existsSync19, lstatSync as lstatSync2, readFileSync as readFileSync36, writeFileSync as writeFileSync17 } from "fs";
27092
+ import { join as join48 } from "path";
27367
27093
  var LOCK_FILE = "rafa.lock";
27368
27094
  var LOCKFILE_VERSION = 1;
27369
27095
 
@@ -27371,7 +27097,7 @@ class ProjectLockError extends Error {
27371
27097
  name = "ProjectLockError";
27372
27098
  }
27373
27099
  function lockPath(root) {
27374
- return join50(root, LOCK_FILE);
27100
+ return join48(root, LOCK_FILE);
27375
27101
  }
27376
27102
  function projectLockText(version2) {
27377
27103
  const lock = { lockfileVersion: LOCKFILE_VERSION, rafa: version2 };
@@ -27404,14 +27130,14 @@ function lockOf(value) {
27404
27130
  }
27405
27131
  function readProjectLock(root) {
27406
27132
  const path = lockPath(root);
27407
- if (!existsSync21(path)) {
27133
+ if (!existsSync19(path)) {
27408
27134
  if (isLink(path))
27409
27135
  refuse10("is a link to nothing");
27410
27136
  return null;
27411
27137
  }
27412
27138
  let text;
27413
27139
  try {
27414
- text = readFileSync38(path, "utf8");
27140
+ text = readFileSync36(path, "utf8");
27415
27141
  } catch (error) {
27416
27142
  refuse10(`could not be read: ${messageOf(error)}`);
27417
27143
  }
@@ -27428,11 +27154,11 @@ function writeProjectLock(root, version2) {
27428
27154
  }
27429
27155
 
27430
27156
  // src/project/update-current.ts
27431
- import { existsSync as existsSync22 } from "fs";
27432
- import { join as join51 } from "path";
27157
+ import { existsSync as existsSync20 } from "fs";
27158
+ import { join as join49 } from "path";
27433
27159
  var DEPRECATIONS = Object.freeze([]);
27434
27160
  function missingFolders(root) {
27435
- return PROJECT_TREE.map((name2) => join51(SCOPE_DIR, name2)).filter((relative12) => !existsSync22(join51(root, relative12)));
27161
+ return PROJECT_TREE.map((name2) => join49(SCOPE_DIR, name2)).filter((relative12) => !existsSync20(join49(root, relative12)));
27436
27162
  }
27437
27163
  async function readBoard3(access) {
27438
27164
  if (access.gh === null)
@@ -27485,7 +27211,7 @@ class UpdateStepError extends Error {
27485
27211
  }
27486
27212
  }
27487
27213
  function foldersNowThere(plan2) {
27488
- return plan2.folders.filter((relative12) => existsSync22(join51(plan2.root, relative12)));
27214
+ return plan2.folders.filter((relative12) => existsSync20(join49(plan2.root, relative12)));
27489
27215
  }
27490
27216
  function writeFolders(plan2) {
27491
27217
  if (plan2.folders.length === 0)