@open-tomato/rafa 0.9.0 → 0.9.1

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.
Files changed (4) hide show
  1. package/README.md +25 -8
  2. package/dist/cli.js +791 -501
  3. package/dist/index.js +393 -143
  4. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1914,7 +1914,7 @@ async function report(args, repoRoot) {
1914
1914
  }
1915
1915
  }
1916
1916
  // package.json
1917
- var version = "0.9.0";
1917
+ var version = "0.9.1";
1918
1918
 
1919
1919
  // src/modules/manifest.ts
1920
1920
  var MANIFEST_VERSION = 1;
@@ -3503,8 +3503,8 @@ function createGhIssueBoard(options) {
3503
3503
  }
3504
3504
 
3505
3505
  // src/board/issue.ts
3506
- import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync6, statSync as statSync4, writeFileSync as writeFileSync2 } from "fs";
3507
- import { dirname as dirname2, resolve as resolve5 } from "path";
3506
+ import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync6, statSync as statSync5, writeFileSync as writeFileSync2 } from "fs";
3507
+ import { dirname as dirname2, resolve as resolve6 } from "path";
3508
3508
 
3509
3509
  // src/board/naming.ts
3510
3510
  import path2 from "path";
@@ -3584,8 +3584,72 @@ function notesPath(specsDir, issue) {
3584
3584
  return path2.join(specsDir, notesFileName(issue));
3585
3585
  }
3586
3586
 
3587
+ // src/board/previous-copy.ts
3588
+ import { linkSync, mkdirSync as mkdirSync2, readdirSync as readdirSync3, statSync as statSync4, unlinkSync } from "fs";
3589
+ import { basename as basename3, extname, join as join5, resolve as resolve5 } from "path";
3590
+ var PREFIX2 = "board previous copy";
3591
+ var PREVIOUS_DIR_NAME = "previous";
3592
+ var COPY_EXTENSION = ".md";
3593
+ var FIRST_SUFFIX = 2;
3594
+ function previousDir(specsDir) {
3595
+ return join5(specsDir, PREVIOUS_DIR_NAME);
3596
+ }
3597
+ function two(value) {
3598
+ return String(value).padStart(2, "0");
3599
+ }
3600
+ function previousTimestamp(mtime) {
3601
+ const date = `${String(mtime.getUTCFullYear()).padStart(4, "0")}${two(mtime.getUTCMonth() + 1)}${two(mtime.getUTCDate())}`;
3602
+ const time = `${two(mtime.getUTCHours())}${two(mtime.getUTCMinutes())}${two(mtime.getUTCSeconds())}`;
3603
+ return `${date}T${time}Z`;
3604
+ }
3605
+ function previousCopyName(savedName, timestamp, attempt) {
3606
+ const stem = basename3(savedName, extname(savedName));
3607
+ const suffix = attempt < FIRST_SUFFIX ? "" : `-${String(attempt)}`;
3608
+ return `${stem}.${timestamp}${suffix}${COPY_EXTENSION}`;
3609
+ }
3610
+ function isTaken(error) {
3611
+ return error instanceof Error && "code" in error && error.code === "EEXIST";
3612
+ }
3613
+ function linkUnderFreeName(source, directory, savedName, timestamp) {
3614
+ for (let attempt = 1;; attempt += 1) {
3615
+ const name = previousCopyName(savedName, timestamp, attempt);
3616
+ try {
3617
+ linkSync(source, join5(directory, name));
3618
+ return name;
3619
+ } catch (error) {
3620
+ if (!isTaken(error))
3621
+ throw error;
3622
+ }
3623
+ }
3624
+ }
3625
+ function movePreviousCopy(options) {
3626
+ const { repoRoot, specsDir, path: path3 } = options;
3627
+ const source = resolve5(repoRoot, path3);
3628
+ const directory = previousDir(specsDir);
3629
+ const absoluteDirectory = resolve5(repoRoot, directory);
3630
+ const savedName = basename3(path3);
3631
+ let name;
3632
+ try {
3633
+ const timestamp = previousTimestamp(statSync4(source).mtime);
3634
+ mkdirSync2(absoluteDirectory, { recursive: true });
3635
+ name = linkUnderFreeName(source, absoluteDirectory, savedName, timestamp);
3636
+ } catch (error) {
3637
+ throw new Error(`${PREFIX2}: ${path3} could not be moved to ${directory}: ${messageOf(error)}`, { cause: error });
3638
+ }
3639
+ const moved = {
3640
+ path: join5(directory, name),
3641
+ absolute: join5(absoluteDirectory, name)
3642
+ };
3643
+ try {
3644
+ unlinkSync(source);
3645
+ } catch (error) {
3646
+ throw new Error(`${PREFIX2}: ${path3} was copied to ${moved.path} but could not be removed: ${messageOf(error)}`, { cause: error });
3647
+ }
3648
+ return moved;
3649
+ }
3650
+
3587
3651
  // src/board/issue.ts
3588
- var PREFIX2 = "board issue";
3652
+ var PREFIX3 = "board issue";
3589
3653
  var ISSUE_VIEW_FIELDS = "number,title,body,state,labels,author";
3590
3654
  var SPEC_LABEL = "type:spec";
3591
3655
  var ISSUE_REFUSAL_EXIT = 2;
@@ -3596,7 +3660,7 @@ function detailOf3(result, command) {
3596
3660
  }
3597
3661
  function issueNumber2(value) {
3598
3662
  if (!Number.isSafeInteger(value) || value < 1) {
3599
- throw new TypeError(`${PREFIX2}: the read refused issue number ${describeValue(value)}, expected a positive whole number`);
3663
+ throw new TypeError(`${PREFIX3}: the read refused issue number ${describeValue(value)}, expected a positive whole number`);
3600
3664
  }
3601
3665
  return String(value);
3602
3666
  }
@@ -3631,11 +3695,11 @@ function readViewedIssue(stdout, number, command) {
3631
3695
  try {
3632
3696
  payload = JSON.parse(stdout);
3633
3697
  } catch (error) {
3634
- throw new Error(`${PREFIX2}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
3698
+ throw new Error(`${PREFIX3}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
3635
3699
  }
3636
3700
  const problem = viewProblem(payload, number);
3637
3701
  if (problem !== null)
3638
- throw new Error(`${PREFIX2}: ${command} answered ${problem}`);
3702
+ throw new Error(`${PREFIX3}: ${command} answered ${problem}`);
3639
3703
  const checked = payload;
3640
3704
  return {
3641
3705
  number,
@@ -3653,7 +3717,7 @@ function createGhSpecIssueReader(options) {
3653
3717
  const command = `gh issue view ${number} --json ${ISSUE_VIEW_FIELDS}`;
3654
3718
  const result = await gh(["issue", "view", number, "--json", ISSUE_VIEW_FIELDS]);
3655
3719
  if (!result.ok)
3656
- throw new Error(`${PREFIX2}: ${detailOf3(result, command)}`);
3720
+ throw new Error(`${PREFIX3}: ${detailOf3(result, command)}`);
3657
3721
  return readViewedIssue(result.stdout, issue, command);
3658
3722
  };
3659
3723
  }
@@ -3691,10 +3755,91 @@ ${local}`
3691
3755
  `)}
3692
3756
  `;
3693
3757
  }
3758
+ var NOTES_SEPARATOR = `
3759
+
3760
+ ${LOCAL_NOTES_HEADING}
3761
+
3762
+ `;
3763
+ var HEADING = /^#{1,6}(?:[ \t]|$)/u;
3764
+ var FENCE = /^ {0,3}(?:`{3,}|~{3,})/u;
3765
+ function commonLines(before, after) {
3766
+ let previous = new Uint32Array(after.length + 1);
3767
+ for (const line of before) {
3768
+ const row = new Uint32Array(after.length + 1);
3769
+ after.forEach((other, index) => {
3770
+ row[index + 1] = line === other ? (previous[index] ?? 0) + 1 : Math.max(previous[index + 1] ?? 0, row[index] ?? 0);
3771
+ });
3772
+ previous = row;
3773
+ }
3774
+ return previous[after.length] ?? 0;
3775
+ }
3776
+ function headingName(line) {
3777
+ const text = line.replace(/^#+/u, "").replace(/[ \t]+#+[ \t]*$/u, "").trim();
3778
+ return text === "" ? line.trim() : text;
3779
+ }
3780
+ function headingSections(lines) {
3781
+ const sections = new Map;
3782
+ let fenced = false;
3783
+ let current = null;
3784
+ const close = () => {
3785
+ if (current === null)
3786
+ return;
3787
+ sections.set(current.key, [...sections.get(current.key) ?? [], current.lines.join(`
3788
+ `)]);
3789
+ };
3790
+ for (const line of lines) {
3791
+ if (FENCE.test(line))
3792
+ fenced = !fenced;
3793
+ if (fenced || !HEADING.test(line)) {
3794
+ current?.lines.push(line);
3795
+ continue;
3796
+ }
3797
+ close();
3798
+ current = { key: line.trim(), lines: [] };
3799
+ }
3800
+ close();
3801
+ return sections;
3802
+ }
3803
+ function changedHeadings(before, after) {
3804
+ const old = headingSections(before);
3805
+ const now = headingSections(after);
3806
+ const keys = [...now.keys(), ...[...old.keys()].filter((key) => !now.has(key))];
3807
+ const changed = keys.filter((key) => JSON.stringify(old.get(key) ?? []) !== JSON.stringify(now.get(key) ?? []));
3808
+ return [...new Set(changed.map(headingName))];
3809
+ }
3810
+ function bodyChangeLine(before, after) {
3811
+ const old = before.split(`
3812
+ `);
3813
+ const now = after.split(`
3814
+ `);
3815
+ const common = commonLines(old, now);
3816
+ const headings = changedHeadings(old, now);
3817
+ const clause = headings.length === 0 ? "no heading changed" : `headings changed: ${headings.join(", ")}`;
3818
+ return `issue body: +${String(now.length - common)} -${String(old.length - common)} lines; ${clause}`;
3819
+ }
3820
+ function notesChangeLine(path3) {
3821
+ return `local notes: ${path3} changed since the saved copy`;
3822
+ }
3823
+ function readSnapshotChange(options) {
3824
+ const { saved, body, notes, notesPath: path3 } = options;
3825
+ if (saved === snapshotText(body, notes))
3826
+ return { kind: "unchanged", bodyLine: null, notesLine: null };
3827
+ const now = normalise(body);
3828
+ if (saved === `${now}
3829
+ ` || saved.startsWith(`${now}${NOTES_SEPARATOR}`)) {
3830
+ return { kind: "notes", bodyLine: null, notesLine: notesChangeLine(path3) };
3831
+ }
3832
+ const split = saved.lastIndexOf(NOTES_SEPARATOR);
3833
+ const oldBody = split === -1 ? saved : saved.slice(0, split);
3834
+ const oldNotes = split === -1 ? "" : normalise(saved.slice(split + NOTES_SEPARATOR.length)).trim();
3835
+ const newNotes = notes === null ? "" : normalise(notes).trim();
3836
+ const bodyLine = bodyChangeLine(normalise(oldBody), now);
3837
+ return oldNotes === newNotes ? { kind: "body", bodyLine, notesLine: null } : { kind: "both", bodyLine, notesLine: notesChangeLine(path3) };
3838
+ }
3694
3839
  function readIfFile(file, what) {
3695
3840
  if (!existsSync7(file))
3696
3841
  return null;
3697
- if (!statSync4(file).isFile()) {
3842
+ if (!statSync5(file).isFile()) {
3698
3843
  throw new CommandExit(ISSUE_REFUSAL_EXIT, `${what} ${file} is not a file, and a spec is one`);
3699
3844
  }
3700
3845
  try {
@@ -3704,7 +3849,7 @@ function readIfFile(file, what) {
3704
3849
  }
3705
3850
  }
3706
3851
  function readLocalNotes(repoRoot, specsDir, issue) {
3707
- return readIfFile(resolve5(repoRoot, notesPath(specsDir, issue)), "the local notes");
3852
+ return readIfFile(resolve6(repoRoot, notesPath(specsDir, issue)), "the local notes");
3708
3853
  }
3709
3854
  function snapshotDiffersMessage(path3, issue) {
3710
3855
  return `${path3} is a snapshot of issue #${String(issue)} that no longer matches it:` + " the issue body, or the local notes appended to it, changed since the snapshot was written;" + ` pass ${REFRESH_FLAG} to plan from the issue as it reads now, or --spec=${path3} to plan from` + " the snapshot as it stands";
@@ -3721,27 +3866,38 @@ function writeSpecSnapshot(options) {
3721
3866
  }
3722
3867
  const local = readLocalNotes(repoRoot, specsDir, issue.number);
3723
3868
  const text = snapshotText(issue.body, local);
3724
- const absolute = resolve5(repoRoot, path3);
3869
+ const absolute = resolve6(repoRoot, path3);
3725
3870
  const existing = readIfFile(absolute, "the spec snapshot");
3726
3871
  const written = {
3727
3872
  path: path3,
3728
3873
  absolute,
3729
3874
  action: "created",
3730
3875
  notes: local === null ? null : notes,
3876
+ previous: null,
3731
3877
  text
3732
3878
  };
3733
3879
  if (existing === text)
3734
3880
  return { ...written, action: "unchanged" };
3735
- if (existing !== null && !refresh) {
3881
+ const action = rewriteAction(existing, issue.body, local, notes, refresh);
3882
+ if (action === null) {
3736
3883
  throw new CommandExit(ISSUE_REFUSAL_EXIT, snapshotDiffersMessage(path3, issue.number));
3737
3884
  }
3885
+ const previous = action === "created" ? null : movePreviousCopy({ repoRoot, specsDir, path: path3 }).path;
3738
3886
  try {
3739
- mkdirSync2(dirname2(absolute), { recursive: true });
3887
+ mkdirSync3(dirname2(absolute), { recursive: true });
3740
3888
  writeFileSync2(absolute, text);
3741
3889
  } catch (error) {
3742
- throw new Error(`${PREFIX2}: the snapshot ${path3} could not be written: ${messageOf(error)}`, { cause: error });
3890
+ throw new Error(`${PREFIX3}: the snapshot ${path3} could not be written: ${messageOf(error)}`, { cause: error });
3743
3891
  }
3744
- return existing === null ? written : { ...written, action: "refreshed" };
3892
+ return { ...written, action, previous };
3893
+ }
3894
+ function rewriteAction(existing, body, notes, notesFile, refresh) {
3895
+ if (existing === null)
3896
+ return "created";
3897
+ const { kind } = readSnapshotChange({ saved: existing, body, notes, notesPath: notesFile });
3898
+ if (kind === "notes")
3899
+ return "notes-rebuilt";
3900
+ return refresh ? "refreshed" : null;
3745
3901
  }
3746
3902
 
3747
3903
  // src/board/leak.ts
@@ -3843,7 +3999,7 @@ function requireNoLeak(source, body) {
3843
3999
  }
3844
4000
 
3845
4001
  // src/board/roadmap.ts
3846
- var PREFIX3 = "board roadmap";
4002
+ var PREFIX4 = "board roadmap";
3847
4003
  var ROADMAP_TITLE = "Roadmap";
3848
4004
  var ROADMAP_SETTING = "roadmap.issue";
3849
4005
  var ROADMAP_REFUSAL_EXIT = 2;
@@ -3852,7 +4008,7 @@ var OPEN_PULL_REQUEST_LIMIT = 100;
3852
4008
  var PR_LIST_FIELDS = "number,headRefName,body";
3853
4009
  var ISSUE_LIST_FIELDS = "number,title";
3854
4010
  var TASK_LINE = /^\s*[-*+]\s+\[([ xX])\]\s+#(\d+)\s*(.*)$/u;
3855
- var FENCE = /^\s*(?:`{3,}|~{3,})/u;
4011
+ var FENCE2 = /^\s*(?:`{3,}|~{3,})/u;
3856
4012
  var WHY_SEPARATOR = /^[-:,.\u2013\u2014]\s*/u;
3857
4013
  var CLOSING = /\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\b\s*:?\s*#(\d+)\b/giu;
3858
4014
  function whyOf(rest) {
@@ -3862,7 +4018,7 @@ function parseRoadmapBody(body) {
3862
4018
  let lines = [];
3863
4019
  let fenced = false;
3864
4020
  body.split(/\r\n?|\n/u).forEach((text, index) => {
3865
- if (FENCE.test(text)) {
4021
+ if (FENCE2.test(text)) {
3866
4022
  fenced = !fenced;
3867
4023
  return;
3868
4024
  }
@@ -3893,24 +4049,24 @@ function parseRows(stdout, command) {
3893
4049
  try {
3894
4050
  payload = JSON.parse(stdout);
3895
4051
  } catch (error) {
3896
- throw new Error(`${PREFIX3}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
4052
+ throw new Error(`${PREFIX4}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
3897
4053
  }
3898
4054
  if (!Array.isArray(payload)) {
3899
- throw new Error(`${PREFIX3}: ${command} answered ${describeValue(payload)}, expected a list`);
4055
+ throw new Error(`${PREFIX4}: ${command} answered ${describeValue(payload)}, expected a list`);
3900
4056
  }
3901
4057
  return payload;
3902
4058
  }
3903
4059
  function rowNumber(row, key, command, where) {
3904
4060
  const value = isMapping(row) ? row[key] : null;
3905
4061
  if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1) {
3906
- throw new Error(`${PREFIX3}: ${command} answered ${where}.${key} as ${describeValue(value)}, expected a positive whole number`);
4062
+ throw new Error(`${PREFIX4}: ${command} answered ${where}.${key} as ${describeValue(value)}, expected a positive whole number`);
3907
4063
  }
3908
4064
  return value;
3909
4065
  }
3910
4066
  function rowText(row, key, command, where) {
3911
4067
  const value = isMapping(row) ? row[key] : null;
3912
4068
  if (typeof value !== "string") {
3913
- throw new Error(`${PREFIX3}: ${command} answered ${where}.${key} as ${describeValue(value)}, expected a string`);
4069
+ throw new Error(`${PREFIX4}: ${command} answered ${where}.${key} as ${describeValue(value)}, expected a string`);
3914
4070
  }
3915
4071
  return value;
3916
4072
  }
@@ -3922,7 +4078,7 @@ function createGhRoadmapSearch(options) {
3922
4078
  return async () => {
3923
4079
  const result = await gh(args);
3924
4080
  if (!result.ok)
3925
- throw new Error(`${PREFIX3}: ${detailOf4(result, command)}`);
4081
+ throw new Error(`${PREFIX4}: ${detailOf4(result, command)}`);
3926
4082
  return Object.freeze(parseRows(result.stdout, command).map((row, index) => ({
3927
4083
  number: rowNumber(row, "number", command, `issue ${String(index)}`),
3928
4084
  title: rowText(row, "title", command, `issue ${String(index)}`)
@@ -3957,7 +4113,7 @@ function createGhOpenPullRequests(options) {
3957
4113
  return async () => {
3958
4114
  const result = await gh(args);
3959
4115
  if (!result.ok)
3960
- throw new Error(`${PREFIX3}: ${detailOf4(result, command)}`);
4116
+ throw new Error(`${PREFIX4}: ${detailOf4(result, command)}`);
3961
4117
  return Object.freeze(parseRows(result.stdout, command).map((row, index) => ({
3962
4118
  number: rowNumber(row, "number", command, `pull request ${String(index)}`),
3963
4119
  headRefName: rowText(row, "headRefName", command, `pull request ${String(index)}`),
@@ -4216,8 +4372,74 @@ function declinedMessage(alternative) {
4216
4372
  return `#${String(alternative)} was not planned, and nothing was written`;
4217
4373
  }
4218
4374
 
4375
+ // src/board/snapshot-settle.ts
4376
+ import { existsSync as existsSync8, readFileSync as readFileSync7, statSync as statSync6 } from "fs";
4377
+ import { resolve as resolve7 } from "path";
4378
+ var PREFIX5 = "board snapshot settle";
4379
+ var DATE_LENGTH = 10;
4380
+ function savedCopyDate(savedAt) {
4381
+ return savedAt.toISOString().slice(0, DATE_LENGTH);
4382
+ }
4383
+ function refreshQuestion(issue, savedAt) {
4384
+ return `Issue #${String(issue)} changed since the saved copy of ${savedCopyDate(savedAt)}.` + " Plan from it as it reads now? [y/N] ";
4385
+ }
4386
+ function notesRebuiltLine(path3, issue, previous) {
4387
+ return `local notes changed; rebuilt ${path3} from issue #${String(issue)} and the notes,` + ` the old copy is at ${previous}`;
4388
+ }
4389
+ function readSavedCopy(absolute, path3) {
4390
+ if (!existsSync8(absolute))
4391
+ return null;
4392
+ try {
4393
+ const stat = statSync6(absolute);
4394
+ if (!stat.isFile())
4395
+ return null;
4396
+ return { text: readFileSync7(absolute, "utf8"), savedAt: stat.mtime };
4397
+ } catch (error) {
4398
+ throw new Error(`${PREFIX5}: the saved copy ${path3} could not be read: ${messageOf(error)}`, { cause: error });
4399
+ }
4400
+ }
4401
+ function printChange(change, output) {
4402
+ for (const line of [change.bodyLine, change.notesLine]) {
4403
+ if (line !== null)
4404
+ output.info(line);
4405
+ }
4406
+ }
4407
+ async function settleSpecSnapshot(options) {
4408
+ const { repoRoot, specsDir, issue, refresh, output } = options;
4409
+ const write = (rewrite) => writeSpecSnapshot({ repoRoot, specsDir, issue, refresh: rewrite });
4410
+ const path3 = specPath(specsDir, issue.number, issue.title);
4411
+ const notes = notesPath(specsDir, issue.number);
4412
+ if (path3 === notes)
4413
+ return write(refresh);
4414
+ const saved = readSavedCopy(resolve7(repoRoot, path3), path3);
4415
+ if (saved === null)
4416
+ return write(refresh);
4417
+ const change = readSnapshotChange({
4418
+ saved: saved.text,
4419
+ body: issue.body,
4420
+ notes: readLocalNotes(repoRoot, specsDir, issue.number),
4421
+ notesPath: notes
4422
+ });
4423
+ if (change.kind === "unchanged")
4424
+ return write(refresh);
4425
+ printChange(change, output);
4426
+ if (change.kind === "notes") {
4427
+ const rebuilt = write(refresh);
4428
+ if (rebuilt.previous !== null)
4429
+ output.info(notesRebuiltLine(rebuilt.path, issue.number, rebuilt.previous));
4430
+ return rebuilt;
4431
+ }
4432
+ if (refresh)
4433
+ return write(true);
4434
+ const offer = options.offerRefresh ?? null;
4435
+ const accepted = offer !== null && await offer({ issue: issue.number, path: path3, savedAt: saved.savedAt });
4436
+ if (!accepted)
4437
+ throw new CommandExit(ISSUE_REFUSAL_EXIT, snapshotDiffersMessage(path3, issue.number));
4438
+ return write(true);
4439
+ }
4440
+
4219
4441
  // src/board/spec-source.ts
4220
- var PREFIX4 = "board spec source";
4442
+ var PREFIX6 = "board spec source";
4221
4443
  var SOURCE_FLAGS = [SPEC_FLAG, ISSUE_FLAG, NEXT_FLAG];
4222
4444
  var SOURCE_REFUSAL_EXIT = 1;
4223
4445
  function severalSourcesMessage(given) {
@@ -4336,7 +4558,7 @@ async function settleBlockedPick(options) {
4336
4558
  async function pickRoadmapIssue(request, options, issues, output) {
4337
4559
  const seams = options.roadmap;
4338
4560
  if (seams === undefined) {
4339
- throw new TypeError(`${PREFIX4}: ${NEXT_FLAG} was resolved with no roadmap seams`);
4561
+ throw new TypeError(`${PREFIX6}: ${NEXT_FLAG} was resolved with no roadmap seams`);
4340
4562
  }
4341
4563
  const roadmap = await resolveRoadmapIssue({
4342
4564
  configured: request.roadmap ?? seams.configured,
@@ -4409,7 +4631,14 @@ async function resolveSpecSource(options) {
4409
4631
  output.info(dryRunLine(describeIssue(read)));
4410
4632
  return stopped("dry-run");
4411
4633
  }
4412
- const snapshot = writeSpecSnapshot({ repoRoot, specsDir, issue: read, refresh });
4634
+ const snapshot = await settleSpecSnapshot({
4635
+ repoRoot,
4636
+ specsDir,
4637
+ issue: read,
4638
+ refresh,
4639
+ offerRefresh: options.offerRefresh ?? null,
4640
+ output
4641
+ });
4413
4642
  return Object.freeze({
4414
4643
  outcome: "spec",
4415
4644
  spec: Object.freeze({
@@ -4482,6 +4711,7 @@ async function resolvePlanSpec(options) {
4482
4711
  const offer = offerReady === null || options.dryRun ? undefined : (issue) => offerReady({ issue, gh, trust: trust(), output });
4483
4712
  const offerAlternative = options.offerAlternative ?? null;
4484
4713
  const alternative = offerAlternative === null || options.dryRun ? undefined : offerAlternative;
4714
+ const offerRefresh = options.dryRun ? null : options.offerRefresh ?? null;
4485
4715
  const resolution = await resolveSpecSource({
4486
4716
  request: options.request,
4487
4717
  refresh: options.refresh,
@@ -4491,6 +4721,7 @@ async function resolvePlanSpec(options) {
4491
4721
  findSpec: options.findSpec,
4492
4722
  issues: createGhSpecIssueReader({ gh }),
4493
4723
  inspect: (issue) => inspectSpecIssue(issue, trust(), offer),
4724
+ offerRefresh,
4494
4725
  roadmap: {
4495
4726
  configured: options.roadmapIssue,
4496
4727
  search: createGhRoadmapSearch({ gh }),
@@ -4516,15 +4747,15 @@ async function resolvePlanSpec(options) {
4516
4747
  import { createInterface as createInterface2 } from "readline";
4517
4748
 
4518
4749
  // src/project/roots.ts
4519
- import { accessSync, constants, readFileSync as readFileSync7, statSync as statSync5 } from "fs";
4750
+ import { accessSync, constants, readFileSync as readFileSync8, statSync as statSync7 } from "fs";
4520
4751
 
4521
4752
  // src/project/scope.ts
4522
- import { existsSync as existsSync8, realpathSync as realpathSync2 } from "fs";
4523
- import { dirname as dirname3, isAbsolute as isAbsolute2, join as join5 } from "path";
4753
+ import { existsSync as existsSync9, realpathSync as realpathSync2 } from "fs";
4754
+ import { dirname as dirname3, isAbsolute as isAbsolute2, join as join6 } from "path";
4524
4755
  var SCOPE_DIR = dirname3(CONFIG_FILE);
4525
4756
  var INIT_COMMAND = "rafa init";
4526
4757
  var DISK_FILE_SYSTEM = Object.freeze({
4527
- exists: (path3) => existsSync8(path3),
4758
+ exists: (path3) => existsSync9(path3),
4528
4759
  realpath: (path3) => realpathSync2(path3)
4529
4760
  });
4530
4761
 
@@ -4535,7 +4766,7 @@ class ScopeError extends Error {
4535
4766
  }
4536
4767
  }
4537
4768
  function scopeAt(base) {
4538
- return { dir: join5(base, SCOPE_DIR), configFile: configFilePath(base) };
4769
+ return { dir: join6(base, SCOPE_DIR), configFile: configFilePath(base) };
4539
4770
  }
4540
4771
  function initHint(start, passedUserConfig = null) {
4541
4772
  const passed2 = passedUserConfig === null ? [] : [`${passedUserConfig} is the user scope's config and marks no project`];
@@ -4612,8 +4843,8 @@ var MONOREPO_MARKER_FILES = Object.freeze([
4612
4843
  ]);
4613
4844
  var DISK_ROOTS_FILE_SYSTEM = Object.freeze({
4614
4845
  ...DISK_FILE_SYSTEM,
4615
- readFile: (path3) => readFileSync7(path3, "utf8"),
4616
- isDirectory: (path3) => statSync5(path3).isDirectory(),
4846
+ readFile: (path3) => readFileSync8(path3, "utf8"),
4847
+ isDirectory: (path3) => statSync7(path3).isDirectory(),
4617
4848
  assertWritable: (path3) => accessSync(path3, constants.W_OK)
4618
4849
  });
4619
4850
 
@@ -4731,8 +4962,8 @@ function formatRows(rows) {
4731
4962
  `);
4732
4963
  }
4733
4964
  function defaultSleep(ms) {
4734
- return new Promise((resolve6) => {
4735
- setTimeout(resolve6, ms);
4965
+ return new Promise((resolve8) => {
4966
+ setTimeout(resolve8, ms);
4736
4967
  });
4737
4968
  }
4738
4969
  async function waitForChecks(options) {
@@ -4877,7 +5108,7 @@ function compareUrl(remote, branch) {
4877
5108
  return `https://${segments[0] ?? host}/${path3}/compare/${encodeBranch(branch)}${COMPARE_QUERY}`;
4878
5109
  }
4879
5110
  // src/pr/gh.ts
4880
- var PREFIX5 = "gh pull requests";
5111
+ var PREFIX7 = "gh pull requests";
4881
5112
  var REPO_PATH3 = "{owner}/{repo}";
4882
5113
  var SUMMARY_FIELDS = "author,baseRefName,headRefName,isCrossRepository,number,state,title,updatedAt,url";
4883
5114
  var DETAIL_FIELDS = `${SUMMARY_FIELDS},body,headRefOid,labels,mergeStateStatus,mergeable`;
@@ -4906,16 +5137,16 @@ function detailOf5(result, command) {
4906
5137
  return result.ok ? `${command} exited 0 and wrote nothing` : `${command} exited non-zero and wrote nothing`;
4907
5138
  }
4908
5139
  function failure2(command, result) {
4909
- return new Error(`${PREFIX5}: ${command} failed: ${detailOf5(result, command)}`);
5140
+ return new Error(`${PREFIX7}: ${command} failed: ${detailOf5(result, command)}`);
4910
5141
  }
4911
5142
  function refuse3(command, problem) {
4912
- throw new Error(`${PREFIX5}: ${command} answered ${problem}`);
5143
+ throw new Error(`${PREFIX7}: ${command} answered ${problem}`);
4913
5144
  }
4914
5145
  function parseJson2(stdout, command) {
4915
5146
  try {
4916
5147
  return JSON.parse(stdout);
4917
5148
  } catch (error) {
4918
- throw new Error(`${PREFIX5}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
5149
+ throw new Error(`${PREFIX7}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
4919
5150
  }
4920
5151
  }
4921
5152
  function readString2(value, command, where) {
@@ -5018,7 +5249,7 @@ function readWorkflowCount(stdout) {
5018
5249
  }
5019
5250
  function pullNumber(value, member) {
5020
5251
  if (!Number.isSafeInteger(value) || value < 1) {
5021
- throw new TypeError(`${PREFIX5}: ${member} refused pull request number ${describeValue(value)},` + " expected a positive whole number");
5252
+ throw new TypeError(`${PREFIX7}: ${member} refused pull request number ${describeValue(value)},` + " expected a positive whole number");
5022
5253
  }
5023
5254
  return String(value);
5024
5255
  }
@@ -5026,13 +5257,13 @@ function textArgument(value, member, what, whole) {
5026
5257
  const bad = typeof value !== "string" || value === "" || whole && !(WHOLE_NUMBER.test(value) && Number.isSafeInteger(Number(value)));
5027
5258
  if (bad) {
5028
5259
  const expected2 = whole ? "a positive whole number with no leading zero" : "a non-empty string";
5029
- throw new TypeError(`${PREFIX5}: ${member} refused ${what} ${describeValue(value)}, expected ${expected2}`);
5260
+ throw new TypeError(`${PREFIX7}: ${member} refused ${what} ${describeValue(value)}, expected ${expected2}`);
5030
5261
  }
5031
5262
  return value;
5032
5263
  }
5033
5264
  function bodyText(value, member) {
5034
5265
  if (typeof value !== "string") {
5035
- throw new TypeError(`${PREFIX5}: ${member} refused a body ${describeValue(value)}, expected a string`);
5266
+ throw new TypeError(`${PREFIX7}: ${member} refused a body ${describeValue(value)}, expected a string`);
5036
5267
  }
5037
5268
  return value;
5038
5269
  }
@@ -5102,7 +5333,7 @@ function createGhPullRequests(options) {
5102
5333
  merge: async (number, method) => {
5103
5334
  const target = pullNumber(number, "merge");
5104
5335
  if (!isMergeMethod(method)) {
5105
- throw new TypeError(`${PREFIX5}: merge refused method ${describeValue(method)},` + ` expected one of: ${MERGE_METHODS.join(", ")}`);
5336
+ throw new TypeError(`${PREFIX7}: merge refused method ${describeValue(method)},` + ` expected one of: ${MERGE_METHODS.join(", ")}`);
5106
5337
  }
5107
5338
  const command = `gh pr merge ${target} --${method}`;
5108
5339
  const result = await gh(["pr", "merge", target, `--${method}`]);
@@ -5418,19 +5649,19 @@ function parseArgs(argv, spec = {}) {
5418
5649
 
5419
5650
  // src/commands/loop/loop-sessions.ts
5420
5651
  import { execFileSync as execFileSync2 } from "child_process";
5421
- import { join as join7, relative } from "path";
5652
+ import { join as join8, relative } from "path";
5422
5653
 
5423
5654
  // src/loop/sessions.ts
5424
5655
  import {
5425
- linkSync,
5426
- mkdirSync as mkdirSync3,
5427
- readdirSync as readdirSync3,
5428
- readFileSync as readFileSync8,
5656
+ linkSync as linkSync2,
5657
+ mkdirSync as mkdirSync4,
5658
+ readdirSync as readdirSync4,
5659
+ readFileSync as readFileSync9,
5429
5660
  renameSync as renameSync2,
5430
5661
  rmSync,
5431
5662
  writeFileSync as writeFileSync3
5432
5663
  } from "fs";
5433
- import { basename as basename3, dirname as dirname4, join as join6 } from "path";
5664
+ import { basename as basename4, dirname as dirname4, join as join7 } from "path";
5434
5665
  var SESSION_STATES = Object.freeze(["running", "paused", "stopped", "done"]);
5435
5666
  var RUNS_DIR = "runs";
5436
5667
  var RECORD_EXTENSION = ".json";
@@ -5470,13 +5701,13 @@ function isSessionId(value) {
5470
5701
  return SESSION_ID_PATTERN.test(value);
5471
5702
  }
5472
5703
  function runsDir(root) {
5473
- return join6(scopeAt(root).dir, RUNS_DIR);
5704
+ return join7(scopeAt(root).dir, RUNS_DIR);
5474
5705
  }
5475
5706
  function sessionFilePath(root, sessionId) {
5476
5707
  if (!isSessionId(sessionId)) {
5477
5708
  throw new Error(`session record: unusable session id ${JSON.stringify(sessionId)}`);
5478
5709
  }
5479
- return join6(runsDir(root), `${sessionId}${RECORD_EXTENSION}`);
5710
+ return join7(runsDir(root), `${sessionId}${RECORD_EXTENSION}`);
5480
5711
  }
5481
5712
  function describeValue3(value) {
5482
5713
  return value === undefined ? "missing" : JSON.stringify(value) ?? String(value);
@@ -5507,7 +5738,7 @@ function sessionIdProblem(sessionId, file) {
5507
5738
  if (typeof sessionId !== "string" || !isSessionId(sessionId)) {
5508
5739
  return `sessionId is ${describeValue3(sessionId)}, expected a plain file name`;
5509
5740
  }
5510
- return basename3(file) === `${sessionId}${RECORD_EXTENSION}` ? null : `sessionId ${JSON.stringify(sessionId)} is not the file's name`;
5741
+ return basename4(file) === `${sessionId}${RECORD_EXTENSION}` ? null : `sessionId ${JSON.stringify(sessionId)} is not the file's name`;
5511
5742
  }
5512
5743
  function planStubProblem(planStub2) {
5513
5744
  return planStub2 === null || typeof planStub2 === "string" && isStampableStub(planStub2) ? null : `planStub is ${describeValue3(planStub2)}, expected null or a plan stub`;
@@ -5578,7 +5809,7 @@ function readState2(record, isAlive = isPidAlive) {
5578
5809
  function readRecordFile(file) {
5579
5810
  let text;
5580
5811
  try {
5581
- text = readFileSync8(file, "utf8");
5812
+ text = readFileSync9(file, "utf8");
5582
5813
  } catch (error) {
5583
5814
  throw new SessionRecordError(file, `cannot be read: ${messageOf(error)}`);
5584
5815
  }
@@ -5586,7 +5817,7 @@ function readRecordFile(file) {
5586
5817
  }
5587
5818
  function recordNames(dir) {
5588
5819
  try {
5589
- return readdirSync3(dir);
5820
+ return readdirSync4(dir);
5590
5821
  } catch (error) {
5591
5822
  if (errorCode(error) === "ENOENT")
5592
5823
  return [];
@@ -5599,7 +5830,7 @@ function byStart(a, b) {
5599
5830
  function readSessions(root, seams = {}) {
5600
5831
  const dir = runsDir(root);
5601
5832
  const isAlive = seams.isAlive ?? isPidAlive;
5602
- const records = recordNames(dir).filter((name) => name.endsWith(RECORD_EXTENSION)).map((name) => readRecordFile(join6(dir, name))).map((record) => Object.freeze({ ...record, state: readState2(record, isAlive) }));
5833
+ const records = recordNames(dir).filter((name) => name.endsWith(RECORD_EXTENSION)).map((name) => readRecordFile(join7(dir, name))).map((record) => Object.freeze({ ...record, state: readState2(record, isAlive) }));
5603
5834
  return Object.freeze(records.sort(byStart));
5604
5835
  }
5605
5836
  function readSession(root, sessionId, seams = {}) {
@@ -5625,13 +5856,13 @@ function checkRecord(record, file) {
5625
5856
  }
5626
5857
  function writeRecordFile(file, record, isNew) {
5627
5858
  checkRecord(record, file);
5628
- mkdirSync3(dirname4(file), { recursive: true });
5859
+ mkdirSync4(dirname4(file), { recursive: true });
5629
5860
  const temporary = `${file}.${process.pid}.tmp`;
5630
5861
  writeFileSync3(temporary, `${JSON.stringify(record, null, 2)}
5631
5862
  `);
5632
5863
  try {
5633
5864
  if (isNew)
5634
- linkSync(temporary, file);
5865
+ linkSync2(temporary, file);
5635
5866
  else
5636
5867
  renameSync2(temporary, file);
5637
5868
  } finally {
@@ -5708,7 +5939,7 @@ function readRecords(root, seams) {
5708
5939
  }
5709
5940
 
5710
5941
  // src/next/sources.ts
5711
- var PREFIX6 = "rafa next";
5942
+ var PREFIX8 = "rafa next";
5712
5943
  var DEFAULT_BASE_BRANCH = "main";
5713
5944
  var DEFAULT_NEXT_SOURCE_SEAMS = Object.freeze({});
5714
5945
  function memoiseIssues2(issues) {
@@ -5746,7 +5977,7 @@ function ghNextBoard(options) {
5746
5977
  }
5747
5978
  function nextProject(context) {
5748
5979
  if (context.project === null)
5749
- throw new Error(`${PREFIX6} runs inside a project, and was handed none`);
5980
+ throw new Error(`${PREFIX8} runs inside a project, and was handed none`);
5750
5981
  return context.project;
5751
5982
  }
5752
5983
  function nextConfig(project, warn) {
@@ -5756,7 +5987,7 @@ function nextConfig(project, warn) {
5756
5987
  if (!(error instanceof ConfigError))
5757
5988
  throw error;
5758
5989
  throw new CommandExit(1, [
5759
- `\u274C ${PREFIX6}: the config cannot be used:`,
5990
+ `\u274C ${PREFIX8}: the config cannot be used:`,
5760
5991
  ...error.problems.map((problem) => ` ${problem}`)
5761
5992
  ].join(`
5762
5993
  `));
@@ -5786,22 +6017,22 @@ function openNextSources(context, seams = DEFAULT_NEXT_SOURCE_SEAMS) {
5786
6017
  }
5787
6018
 
5788
6019
  // src/commands/plan/list.ts
5789
- import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync9 } from "fs";
5790
- import { join as join8 } from "path";
6020
+ import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync10 } from "fs";
6021
+ import { join as join9 } from "path";
5791
6022
  var USAGE2 = "rafa plan list";
5792
6023
  function listing(dir, stub) {
5793
- const plan = join8(dir, planFileName(stub, false));
5794
- const trackerPath = join8(dir, planFileName(stub, true));
6024
+ const plan = join9(dir, planFileName(stub, false));
6025
+ const trackerPath = join9(dir, planFileName(stub, true));
5795
6026
  const tracker = isFile(trackerPath) ? trackerPath : null;
5796
- const planModel = parsePlan(readFileSync9(plan, "utf8"));
5797
- const counted2 = tracker === null ? planModel : parsePlan(readFileSync9(tracker, "utf8"));
6027
+ const planModel = parsePlan(readFileSync10(plan, "utf8"));
6028
+ const counted2 = tracker === null ? planModel : parsePlan(readFileSync10(tracker, "utf8"));
5798
6029
  return { stub, plan, tracker, tasks: countTasks(counted2.tasks), issues: planModel.issues.length };
5799
6030
  }
5800
6031
  function listPlans(plans) {
5801
6032
  const dir = plans.path;
5802
- if (!existsSync9(dir))
6033
+ if (!existsSync10(dir))
5803
6034
  return { dir, plans: [] };
5804
- const stubs = readdirSync4(dir).filter((name) => isFile(join8(dir, name))).map((name) => stubOfPlanFile(name)).filter((stub) => stub !== null).sort((a, b) => a.localeCompare(b));
6035
+ const stubs = readdirSync5(dir).filter((name) => isFile(join9(dir, name))).map((name) => stubOfPlanFile(name)).filter((stub) => stub !== null).sort((a, b) => a.localeCompare(b));
5805
6036
  return { dir, plans: stubs.map((stub) => listing(dir, stub)) };
5806
6037
  }
5807
6038
  function rowNotes(plan) {
@@ -5996,7 +6227,7 @@ function branchLabel(world) {
5996
6227
  }
5997
6228
 
5998
6229
  // src/next/state.ts
5999
- var PREFIX7 = "rafa next";
6230
+ var PREFIX9 = "rafa next";
6000
6231
  var MAX_NAMED_FILES = 5;
6001
6232
  var NEXT_PRECONDITIONS = Object.freeze([
6002
6233
  "tree-modified",
@@ -6274,7 +6505,7 @@ async function readNextState(sources) {
6274
6505
  if (found !== null)
6275
6506
  return answer(found, world.problems());
6276
6507
  }
6277
- throw new Error(`${PREFIX7}: no row of the table answered, and the last row answers for every reading`);
6508
+ throw new Error(`${PREFIX9}: no row of the table answered, and the last row answers for every reading`);
6278
6509
  }
6279
6510
 
6280
6511
  // src/next/hint.ts
@@ -6300,8 +6531,8 @@ function wantsHint(flags) {
6300
6531
  }
6301
6532
  var DEFAULT_HINT_SEAMS = Object.freeze({});
6302
6533
  function afterDelay(ms) {
6303
- return new Promise((resolve6) => {
6304
- setTimeout(resolve6, ms).unref();
6534
+ return new Promise((resolve8) => {
6535
+ setTimeout(resolve8, ms).unref();
6305
6536
  });
6306
6537
  }
6307
6538
  function silenced() {
@@ -6355,10 +6586,10 @@ async function nextStepHint(context, seams = DEFAULT_HINT_SEAMS) {
6355
6586
  }
6356
6587
 
6357
6588
  // src/next/actions.ts
6358
- var PREFIX8 = "rafa next";
6589
+ var PREFIX10 = "rafa next";
6359
6590
  function needed(state, value, what) {
6360
6591
  if (value === null) {
6361
- throw new Error(`${PREFIX8}: state "${state.id}" proposes "${state.action}" and names no ${what}`);
6592
+ throw new Error(`${PREFIX10}: state "${state.id}" proposes "${state.action}" and names no ${what}`);
6362
6593
  }
6363
6594
  return String(value);
6364
6595
  }
@@ -6408,12 +6639,12 @@ function actionContext(caller, command, argv) {
6408
6639
  async function runAction(caller, state) {
6409
6640
  const invocation = actionInvocation(state);
6410
6641
  if (invocation === null) {
6411
- throw new Error(`${PREFIX8}: the action "${state.action}" of state "${state.id}" runs no registered command`);
6642
+ throw new Error(`${PREFIX10}: the action "${state.action}" of state "${state.id}" runs no registered command`);
6412
6643
  }
6413
6644
  const spec = ACTION_COMMANDS[invocation.action];
6414
6645
  const command = caller.registry.find(spec.subject, spec.action);
6415
6646
  if (command === undefined) {
6416
- throw new CommandExit(1, `\u274C ${PREFIX8}: the "${invocation.action}" step runs "rafa ${invocation.command}", which is registered by nothing`);
6647
+ throw new CommandExit(1, `\u274C ${PREFIX10}: the "${invocation.action}" step runs "rafa ${invocation.command}", which is registered by nothing`);
6417
6648
  }
6418
6649
  await command.run(actionContext(caller, command, invocation.argv));
6419
6650
  }
@@ -6452,7 +6683,7 @@ async function endWithNextStep(context, seams = DEFAULT_ENDING_SEAMS) {
6452
6683
  }
6453
6684
 
6454
6685
  // src/adapters/tracker/resolve.ts
6455
- var PREFIX9 = "tracker chain";
6686
+ var PREFIX11 = "tracker chain";
6456
6687
  var FAILURE_SEPARATOR = "; ";
6457
6688
  function warnThroughActiveOutput(message) {
6458
6689
  activeOutput().warn(message);
@@ -6493,9 +6724,9 @@ async function resolveTracker(options) {
6493
6724
  }
6494
6725
  attempts = [...attempts, Object.freeze({ kind, ok: false, reason: outcome.reason })];
6495
6726
  failures = [...failures, `${kind}: ${outcome.reason}`];
6496
- log(`${PREFIX9}: ${kind} unavailable: ${outcome.reason}`);
6727
+ log(`${PREFIX11}: ${kind} unavailable: ${outcome.reason}`);
6497
6728
  }
6498
- throw new Error(`${PREFIX9}: no tracker available; tried ${failures.join(FAILURE_SEPARATOR)}`);
6729
+ throw new Error(`${PREFIX11}: no tracker available; tried ${failures.join(FAILURE_SEPARATOR)}`);
6499
6730
  }
6500
6731
 
6501
6732
  // src/commands/issue/issue-tracker.ts
@@ -6738,6 +6969,23 @@ function createPlanReadyOffer(seams = DEFAULT_OFFER_SEAMS) {
6738
6969
  };
6739
6970
  }
6740
6971
 
6972
+ // src/commands/plan/refresh-offer.ts
6973
+ var DEFAULT_REFRESH_OFFER_SEAMS = Object.freeze({});
6974
+ function createPlanRefreshOffer(seams = DEFAULT_REFRESH_OFFER_SEAMS) {
6975
+ const isTerminal = seams.isTerminal ?? (() => process.stdin.isTTY === true);
6976
+ if (!isTerminal())
6977
+ return null;
6978
+ const openPrompter = seams.openPrompter ?? (() => createLinePrompter(process.stdin, process.stderr));
6979
+ return async (request) => {
6980
+ const prompter = lazyPrompter(openPrompter);
6981
+ try {
6982
+ return await prompter.ask(refreshQuestion(request.issue, request.savedAt));
6983
+ } finally {
6984
+ prompter.close();
6985
+ }
6986
+ };
6987
+ }
6988
+
6741
6989
  // src/commands/plan/spec-route.ts
6742
6990
  function specCandidates(specArg, specsDir) {
6743
6991
  if (path3.isAbsolute(specArg))
@@ -6767,10 +7015,11 @@ async function resolveCreateSpec(options, seams = DEFAULT_ROUTE_SEAMS) {
6767
7015
  const source = readSpecSourceFlags(options.args);
6768
7016
  if (source.request === null)
6769
7017
  throw new CommandExit(SOURCE_REFUSAL_EXIT, usageRefusal(specsDir));
6770
- const resolve6 = seams.resolve ?? resolvePlanSpec;
7018
+ const resolve8 = seams.resolve ?? resolvePlanSpec;
6771
7019
  const makeReadyOffer = seams.makeReadyOffer ?? createPlanReadyOffer;
6772
7020
  const makeAlternativeOffer = seams.makeAlternativeOffer ?? createPlanBlockedOffer;
6773
- return await resolve6({
7021
+ const makeRefreshOffer = seams.makeRefreshOffer ?? createPlanRefreshOffer;
7022
+ return await resolve8({
6774
7023
  request: source.request,
6775
7024
  refresh: source.refresh,
6776
7025
  dryRun: source.dryRun,
@@ -6780,7 +7029,8 @@ async function resolveCreateSpec(options, seams = DEFAULT_ROUTE_SEAMS) {
6780
7029
  trustedAuthors: options.trustedAuthors,
6781
7030
  findSpec: (spec) => findSpec(repoRoot, spec, specsDir),
6782
7031
  offerReady: makeReadyOffer(),
6783
- offerAlternative: makeAlternativeOffer()
7032
+ offerAlternative: makeAlternativeOffer(),
7033
+ offerRefresh: makeRefreshOffer()
6784
7034
  });
6785
7035
  }
6786
7036
 
@@ -6791,13 +7041,13 @@ import { homedir as homedir3 } from "os";
6791
7041
  var RAFA_VERSION = version;
6792
7042
 
6793
7043
  // src/notices/notices.ts
6794
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync4 } from "fs";
6795
- import { dirname as dirname5, join as join9 } from "path";
7044
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "fs";
7045
+ import { dirname as dirname5, join as join10 } from "path";
6796
7046
  var NOTICE_IDS = Object.freeze(["alpha", "danger"]);
6797
7047
  var FEEDBACK_URL = "https://github.com/open-tomato/rafa/issues/new/choose";
6798
7048
  var ROADMAP_URL = "https://github.com/open-tomato/rafa/issues/31";
6799
7049
  function noticesPath(home) {
6800
- return join9(home, ".rafa", "notices.json");
7050
+ return join10(home, ".rafa", "notices.json");
6801
7051
  }
6802
7052
  function isNoticeId(value) {
6803
7053
  return typeof value === "string" && NOTICE_IDS.includes(value);
@@ -6805,7 +7055,7 @@ function isNoticeId(value) {
6805
7055
  function readDismissed(home) {
6806
7056
  let raw;
6807
7057
  try {
6808
- raw = readFileSync10(noticesPath(home), "utf8");
7058
+ raw = readFileSync11(noticesPath(home), "utf8");
6809
7059
  } catch {
6810
7060
  return [];
6811
7061
  }
@@ -6824,7 +7074,7 @@ function readDismissed(home) {
6824
7074
  function writeDismissed(home, ids) {
6825
7075
  const all = NOTICE_IDS.filter((id) => ids.includes(id) || readDismissed(home).includes(id));
6826
7076
  const path4 = noticesPath(home);
6827
- mkdirSync4(dirname5(path4), { recursive: true });
7077
+ mkdirSync5(dirname5(path4), { recursive: true });
6828
7078
  writeFileSync4(path4, `${JSON.stringify({ dismissed: all }, null, 2)}
6829
7079
  `);
6830
7080
  }
@@ -7514,7 +7764,7 @@ import { randomUUID as randomUUID7 } from "crypto";
7514
7764
 
7515
7765
  // src/effort/store/absences.ts
7516
7766
  import { randomUUID as randomUUID3 } from "crypto";
7517
- import { existsSync as existsSync10 } from "fs";
7767
+ import { existsSync as existsSync11 } from "fs";
7518
7768
  var ABSENCE_REASONS = {
7519
7769
  "no-block": true,
7520
7770
  "unclosed-block": true,
@@ -7573,13 +7823,13 @@ function writeReportAbsence(repoRoot, write, seams = {}) {
7573
7823
  write.outcome,
7574
7824
  collectedAt
7575
7825
  ];
7576
- const appended = withSqliteStore(path5, !existsSync10(path5), (db) => db.query(INSERT_ABSENCE).run(...values).changes);
7826
+ const appended = withSqliteStore(path5, !existsSync11(path5), (db) => db.query(INSERT_ABSENCE).run(...values).changes);
7577
7827
  return { path: path5, appended, skipped: 1 - appended };
7578
7828
  }
7579
7829
 
7580
7830
  // src/effort/store/changes.ts
7581
7831
  import { randomUUID as randomUUID4 } from "crypto";
7582
- import { existsSync as existsSync11 } from "fs";
7832
+ import { existsSync as existsSync12 } from "fs";
7583
7833
  var INSERT_CHANGE = `
7584
7834
  INSERT INTO changes (
7585
7835
  id, session_id, plan_stub, task_line,
@@ -7658,7 +7908,7 @@ var SELECT_PLAN_CHANGES = `
7658
7908
  `;
7659
7909
  function readPlanChanges(repoRoot, planStub2) {
7660
7910
  const path5 = sqliteStorePath(repoRoot);
7661
- if (!existsSync11(path5))
7911
+ if (!existsSync12(path5))
7662
7912
  return [];
7663
7913
  const rows = withSqliteStore(path5, false, (db) => db.query(SELECT_PLAN_CHANGES).all(planStub2));
7664
7914
  return rows.map((row) => ({
@@ -7891,8 +8141,8 @@ function describeTaskReportRecord(record) {
7891
8141
  }
7892
8142
 
7893
8143
  // src/utils/progress.ts
7894
- import { existsSync as existsSync12, writeFileSync as writeFileSync5 } from "fs";
7895
- import { join as join10 } from "path";
8144
+ import { existsSync as existsSync13, writeFileSync as writeFileSync5 } from "fs";
8145
+ import { join as join11 } from "path";
7896
8146
  var PROGRESS_FILE_NAME = "progress.txt";
7897
8147
  var PROGRESS_CAP_BYTES = 16000;
7898
8148
  var LISTED_FIELDS = [
@@ -7934,7 +8184,7 @@ var ESCAPED_CODE_POINTS = new Set([
7934
8184
  8233
7935
8185
  ]);
7936
8186
  function progressFilePath(repoRoot) {
7937
- return join10(repoRoot, PROGRESS_FILE_NAME);
8187
+ return join11(repoRoot, PROGRESS_FILE_NAME);
7938
8188
  }
7939
8189
  function escapeValue(value) {
7940
8190
  let escaped = "";
@@ -7995,7 +8245,7 @@ function renderProgressText(findings, capBytes = PROGRESS_CAP_BYTES) {
7995
8245
  }
7996
8246
  function readProgressFindings(repoRoot, planStub2) {
7997
8247
  const path5 = sqliteStorePath(repoRoot);
7998
- if (!existsSync12(path5))
8248
+ if (!existsSync13(path5))
7999
8249
  return [];
8000
8250
  const rows = withSqliteStore(path5, false, (db) => {
8001
8251
  const query = db.query(SELECT_FINDINGS);
@@ -8022,16 +8272,16 @@ function writeProgress(repoRoot, planStub2) {
8022
8272
 
8023
8273
  // src/start/preflight.ts
8024
8274
  import { randomUUID as randomUUID6 } from "crypto";
8025
- import { existsSync as existsSync13, readFileSync as readFileSync12 } from "fs";
8275
+ import { existsSync as existsSync14, readFileSync as readFileSync13 } from "fs";
8026
8276
  import { homedir as homedir5 } from "os";
8027
- import { basename as basename4 } from "path";
8277
+ import { basename as basename5 } from "path";
8028
8278
 
8029
8279
  // src/preflight/first-dispatch.ts
8030
- import { readFileSync as readFileSync11 } from "fs";
8280
+ import { readFileSync as readFileSync12 } from "fs";
8031
8281
  var TICKED_TASK = /^- \[[xX]\] (.+)/;
8032
8282
  function readIfReadable(path5) {
8033
8283
  try {
8034
- return readFileSync11(path5, "utf8");
8284
+ return readFileSync12(path5, "utf8");
8035
8285
  } catch {
8036
8286
  return null;
8037
8287
  }
@@ -8064,11 +8314,11 @@ function knownMissingNotice(lines) {
8064
8314
  }
8065
8315
  function agentSourcePath(planPath) {
8066
8316
  const tracker = trackerPathFor(planPath);
8067
- return existsSync13(tracker) ? tracker : planPath;
8317
+ return existsSync14(tracker) ? tracker : planPath;
8068
8318
  }
8069
8319
  function readIfReadable2(path5) {
8070
8320
  try {
8071
- return readFileSync12(path5, "utf8");
8321
+ return readFileSync13(path5, "utf8");
8072
8322
  } catch {
8073
8323
  return null;
8074
8324
  }
@@ -8084,7 +8334,7 @@ function refuseUnresolvableAgents(options) {
8084
8334
  if (missing.length === 0)
8085
8335
  return;
8086
8336
  throw new CommandExit(1, [
8087
- `\u274C Refusing to start: ${basename4(path5)} names ${missing.length} agent(s) no loaded scope` + ` defines (loop.settingSources: ${settingSources.join(", ")}).`,
8337
+ `\u274C Refusing to start: ${basename5(path5)} names ${missing.length} agent(s) no loaded scope` + ` defines (loop.settingSources: ${settingSources.join(", ")}).`,
8088
8338
  ...missing.map((agent) => ` ${missingAgentLine(agent)}`),
8089
8339
  " Nothing was checked and nothing was dispatched."
8090
8340
  ].join(`
@@ -8117,7 +8367,7 @@ function skippedStartLine(item, tracker) {
8117
8367
  return `\u23ED start-only item ${item.kind} ${JSON.stringify(item.name)} was not checked:` + " it is probed on a first dispatch alone," + ` and ${tracker} already holds a ticked task`;
8118
8368
  }
8119
8369
  function announceSkippedStart(planPath, skipped) {
8120
- const tracker = basename4(trackerPathFor(planPath));
8370
+ const tracker = basename5(trackerPathFor(planPath));
8121
8371
  const output = activeOutput();
8122
8372
  for (const [index, item] of skipped.entries()) {
8123
8373
  const lead = index === 0 ? `
@@ -8136,7 +8386,7 @@ function startTier(planPath, items) {
8136
8386
  function announceReminders(planPath, reminders) {
8137
8387
  if (reminders.length === 0)
8138
8388
  return;
8139
- const file = basename4(prerequisitesPathForPlan(planPath) ?? planPath);
8389
+ const file = basename5(prerequisitesPathForPlan(planPath) ?? planPath);
8140
8390
  const output = activeOutput();
8141
8391
  output.info(`
8142
8392
  \uD83D\uDCCC ${file} names ${reminders.length} step(s) the preflight does not check:`);
@@ -8372,14 +8622,14 @@ async function holdWhilePaused(options) {
8372
8622
  }
8373
8623
 
8374
8624
  // src/start/plan-path.ts
8375
- import { existsSync as existsSync14 } from "fs";
8376
- import { resolve as resolve6 } from "path";
8625
+ import { existsSync as existsSync15 } from "fs";
8626
+ import { resolve as resolve8 } from "path";
8377
8627
  var DEFAULT_PLAN_FILE = "PLAN.md";
8378
8628
  function resolvePlanPath(repoRoot, planDir, planArg) {
8379
8629
  if (planArg)
8380
- return resolve6(repoRoot, planArg);
8381
- const inPlanDir = resolve6(repoRoot, planDir, DEFAULT_PLAN_FILE);
8382
- return existsSync14(inPlanDir) ? inPlanDir : resolve6(repoRoot, DEFAULT_PLAN_FILE);
8630
+ return resolve8(repoRoot, planArg);
8631
+ const inPlanDir = resolve8(repoRoot, planDir, DEFAULT_PLAN_FILE);
8632
+ return existsSync15(inPlanDir) ? inPlanDir : resolve8(repoRoot, DEFAULT_PLAN_FILE);
8383
8633
  }
8384
8634
 
8385
8635
  // src/utils/git.ts
@@ -8562,7 +8812,7 @@ async function handleRedChecks(io, branch, prNumber, rows) {
8562
8812
  }
8563
8813
 
8564
8814
  // src/release/prepare.ts
8565
- import { readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "fs";
8815
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync7 } from "fs";
8566
8816
 
8567
8817
  // src/release/changelog.ts
8568
8818
  var RELEASE_HEADING_FIELDS = ["version", "date", "title"];
@@ -8747,17 +8997,17 @@ function changelogDate(now) {
8747
8997
  }
8748
8998
 
8749
8999
  // src/release/enabled.ts
8750
- import { statSync as statSync6 } from "fs";
8751
- import { resolve as resolve7 } from "path";
9000
+ import { statSync as statSync8 } from "fs";
9001
+ import { resolve as resolve9 } from "path";
8752
9002
  function isRegularFile(full) {
8753
9003
  try {
8754
- return statSync6(full).isFile();
9004
+ return statSync8(full).isFile();
8755
9005
  } catch {
8756
9006
  return false;
8757
9007
  }
8758
9008
  }
8759
9009
  function readFile(repoRoot, path5) {
8760
- const resolved = resolve7(repoRoot, path5);
9010
+ const resolved = resolve9(repoRoot, path5);
8761
9011
  return { path: path5, resolved, present: isRegularFile(resolved) };
8762
9012
  }
8763
9013
  function resolveReleaseEnabled(settings, repoRoot) {
@@ -8794,7 +9044,7 @@ function resolveReleaseLevel(declared, notes) {
8794
9044
  }
8795
9045
 
8796
9046
  // src/release/version.ts
8797
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync6 } from "fs";
9047
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync6 } from "fs";
8798
9048
  var RELEASE_REMOTE = "origin";
8799
9049
  var RELEASE_BASE_BRANCH = "main";
8800
9050
  var VERSION_SHAPE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?(?:\+([0-9A-Za-z.-]+))?$/;
@@ -8886,7 +9136,7 @@ function writeManifestVersion(path5, next) {
8886
9136
  const unwritten = { path: path5, written: false, previous: null, version: next };
8887
9137
  let text;
8888
9138
  try {
8889
- text = readFileSync13(path5, "utf8");
9139
+ text = readFileSync14(path5, "utf8");
8890
9140
  } catch (error) {
8891
9141
  return { ...unwritten, problem: `${path5} could not be read: ${messageOf(error)}` };
8892
9142
  }
@@ -8989,7 +9239,7 @@ function planVersion(input, versionFile, level) {
8989
9239
  }
8990
9240
  function textOf(path5) {
8991
9241
  try {
8992
- return readFileSync14(path5, "utf8");
9242
+ return readFileSync15(path5, "utf8");
8993
9243
  } catch {
8994
9244
  return null;
8995
9245
  }
@@ -9102,7 +9352,7 @@ function changelogFailure(failed3, versionEdit, restored) {
9102
9352
  }
9103
9353
 
9104
9354
  // src/release/verify.ts
9105
- import { readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "fs";
9355
+ import { readFileSync as readFileSync16, writeFileSync as writeFileSync8 } from "fs";
9106
9356
  function changelogInsertionSpan(before, after) {
9107
9357
  const beforeLines = before.split(`
9108
9358
  `);
@@ -9119,7 +9369,7 @@ function changelogInsertionSpan(before, after) {
9119
9369
  }
9120
9370
  function textOf2(path5) {
9121
9371
  try {
9122
- return readFileSync15(path5, "utf8");
9372
+ return readFileSync16(path5, "utf8");
9123
9373
  } catch {
9124
9374
  return null;
9125
9375
  }
@@ -9601,11 +9851,11 @@ function announcePlanIssues(planContent) {
9601
9851
  }
9602
9852
 
9603
9853
  // src/start/runtime.ts
9604
- import { existsSync as existsSync15, readdirSync as readdirSync5, realpathSync as realpathSync3, statSync as statSync7 } from "fs";
9854
+ import { existsSync as existsSync16, readdirSync as readdirSync6, realpathSync as realpathSync3, statSync as statSync9 } from "fs";
9605
9855
  import { homedir as homedir6 } from "os";
9606
- import { isAbsolute as isAbsolute3, join as join11, relative as relative3, resolve as resolve8, sep } from "path";
9856
+ import { isAbsolute as isAbsolute3, join as join12, relative as relative3, resolve as resolve10, sep } from "path";
9607
9857
  var RUNTIME_FLAG = "--runtime";
9608
- var RUNTIME_SUBDIR = join11(".rafa", "runtime");
9858
+ var RUNTIME_SUBDIR = join12(".rafa", "runtime");
9609
9859
  var RUNTIME_ENTRY = "cli.js";
9610
9860
  var SOURCE_DIR = "src";
9611
9861
  var RUNTIME_COMMAND = ["start"];
@@ -9622,7 +9872,7 @@ function resolveRuntime(value, places) {
9622
9872
  throw refusal3([`\u274C Refusing to start: \`--runtime\` names no runtime. Spell it ${SPELLING}.`]);
9623
9873
  }
9624
9874
  const isVersion = VERSION_SHAPE2.test(value);
9625
- const named = isVersion ? join11(places.home, RUNTIME_SUBDIR, value, RUNTIME_ENTRY) : resolve8(places.cwd, value);
9875
+ const named = isVersion ? join12(places.home, RUNTIME_SUBDIR, value, RUNTIME_ENTRY) : resolve10(places.cwd, value);
9626
9876
  refuseInsideSource(value, named, places);
9627
9877
  const entry = entryOf(value, named, isVersion, places.home);
9628
9878
  const real = realpathSync3(entry);
@@ -9633,7 +9883,7 @@ function entryOf(value, named, isVersion, home) {
9633
9883
  if (isVersion) {
9634
9884
  if (isFile2(named))
9635
9885
  return named;
9636
- const root = join11(home, RUNTIME_SUBDIR);
9886
+ const root = join12(home, RUNTIME_SUBDIR);
9637
9887
  const installed = installedVersions(root);
9638
9888
  throw refusal3([
9639
9889
  `\u274C Refusing to start: \`--runtime=${value}\` names no installed runtime: ${named} is no file.`,
@@ -9642,7 +9892,7 @@ function entryOf(value, named, isVersion, home) {
9642
9892
  }
9643
9893
  if (isFile2(named))
9644
9894
  return named;
9645
- const inside = join11(named, RUNTIME_ENTRY);
9895
+ const inside = join12(named, RUNTIME_ENTRY);
9646
9896
  if (isDirectory(named) && isFile2(inside))
9647
9897
  return inside;
9648
9898
  throw refusal3([
@@ -9660,8 +9910,8 @@ function refuseInsideSource(value, path5, places) {
9660
9910
  ]);
9661
9911
  }
9662
9912
  function sourceDirs({ cwd, root }) {
9663
- const dirs = [join11(cwd, SOURCE_DIR), join11(root, SOURCE_DIR)];
9664
- const real = dirs.filter((dir) => existsSync15(dir)).map((dir) => realpathSync3(dir));
9913
+ const dirs = [join12(cwd, SOURCE_DIR), join12(root, SOURCE_DIR)];
9914
+ const real = dirs.filter((dir) => existsSync16(dir)).map((dir) => realpathSync3(dir));
9665
9915
  return [...new Set([...dirs, ...real])];
9666
9916
  }
9667
9917
  function isInside(path5, dir) {
@@ -9670,21 +9920,21 @@ function isInside(path5, dir) {
9670
9920
  }
9671
9921
  function installedVersions(root) {
9672
9922
  try {
9673
- return readdirSync5(root).filter((name) => isFile2(join11(root, name, RUNTIME_ENTRY))).sort((a, b) => a.localeCompare(b));
9923
+ return readdirSync6(root).filter((name) => isFile2(join12(root, name, RUNTIME_ENTRY))).sort((a, b) => a.localeCompare(b));
9674
9924
  } catch {
9675
9925
  return [];
9676
9926
  }
9677
9927
  }
9678
9928
  function isFile2(path5) {
9679
9929
  try {
9680
- return statSync7(path5).isFile();
9930
+ return statSync9(path5).isFile();
9681
9931
  } catch {
9682
9932
  return false;
9683
9933
  }
9684
9934
  }
9685
9935
  function isDirectory(path5) {
9686
9936
  try {
9687
- return statSync7(path5).isDirectory();
9937
+ return statSync9(path5).isDirectory();
9688
9938
  } catch {
9689
9939
  return false;
9690
9940
  }
@@ -9715,7 +9965,7 @@ function realOrResolved(path5) {
9715
9965
  try {
9716
9966
  return realpathSync3(path5);
9717
9967
  } catch {
9718
- return resolve8(path5);
9968
+ return resolve10(path5);
9719
9969
  }
9720
9970
  }
9721
9971
  async function runRuntime(entry, words, run) {
@@ -9801,11 +10051,11 @@ function eventOf(line) {
9801
10051
  }
9802
10052
 
9803
10053
  // src/triage/triage.ts
9804
- import { basename as basename5, join as join12 } from "path";
10054
+ import { basename as basename6, join as join13 } from "path";
9805
10055
 
9806
10056
  // src/effort/store/tracker-refs.ts
9807
10057
  import { randomUUID as randomUUID9 } from "crypto";
9808
- import { existsSync as existsSync16 } from "fs";
10058
+ import { existsSync as existsSync17 } from "fs";
9809
10059
  var SELECT_HELD = `
9810
10060
  SELECT seq, tracker_ref FROM findings
9811
10061
  WHERE session_id = ? AND artifact = ?
@@ -9928,7 +10178,7 @@ function writeTrackerRef(repoRoot, write, seams = {}) {
9928
10178
  wanted.json,
9929
10179
  collectedAt
9930
10180
  ];
9931
- const [action, stored] = withSqliteStore(path5, !existsSync16(path5), (db) => {
10181
+ const [action, stored] = withSqliteStore(path5, !existsSync17(path5), (db) => {
9932
10182
  const place = db.transaction(() => placeRef(db, path5, write, wanted, insertValues));
9933
10183
  return place.immediate();
9934
10184
  });
@@ -9937,14 +10187,14 @@ function writeTrackerRef(repoRoot, write, seams = {}) {
9937
10187
  function readTrackerRef(repoRoot, artifact) {
9938
10188
  checkArtifact("read", artifact);
9939
10189
  const path5 = sqliteStorePath(repoRoot);
9940
- if (!existsSync16(path5))
10190
+ if (!existsSync17(path5))
9941
10191
  return null;
9942
10192
  const oldest = withSqliteStore(path5, false, (db) => db.query(SELECT_OLDEST).get(artifact));
9943
10193
  return oldest === null ? null : heldRefOf(path5, oldest.seq, oldest.tracker_ref).ref;
9944
10194
  }
9945
10195
 
9946
10196
  // src/triage/triage.ts
9947
- var PRIVATE_TRIAGE_DIR = join12(".rafa", "triage", "private");
10197
+ var PRIVATE_TRIAGE_DIR = join13(".rafa", "triage", "private");
9948
10198
  var SECRET_ENV_NAMES = ["GITHUB_TOKEN", "ANTHROPIC_API_KEY", "LINEAR_API_KEY"];
9949
10199
  var TRIAGE_MODULE = "unassigned";
9950
10200
  var BLOCKER_SEPARATOR = "; ";
@@ -9955,7 +10205,7 @@ var MIN_FENCE_LENGTH = 3;
9955
10205
  var ISSUE_OPENING = "An out-of-scope bug a rafa task session reported. The loop filed it and" + " dispatches no task for it: a plan that wants it fixed declares a task.";
9956
10206
  var COMMENT_OPENING = "Reported again by a rafa task session.";
9957
10207
  function privateTriageDir(repoRoot) {
9958
- return join12(repoRoot, PRIVATE_TRIAGE_DIR);
10208
+ return join13(repoRoot, PRIVATE_TRIAGE_DIR);
9959
10209
  }
9960
10210
  function createPrivateTriageTracker(repoRoot, options = {}) {
9961
10211
  return createLocalTracker({
@@ -10018,7 +10268,7 @@ function oneLine3(text) {
10018
10268
  return text.replace(/\s+/g, " ").trim();
10019
10269
  }
10020
10270
  function bugKeyOf(trackerPath, artifact) {
10021
- return `${basename5(trackerPath)}${KEY_SEPARATOR}${oneLine3(artifact)}`;
10271
+ return `${basename6(trackerPath)}${KEY_SEPARATOR}${oneLine3(artifact)}`;
10022
10272
  }
10023
10273
  function fenced(value) {
10024
10274
  const runs = Array.from(value.matchAll(/`+/g), (run) => run[0].length);
@@ -10252,9 +10502,9 @@ function describeStartTriage(result) {
10252
10502
  };
10253
10503
  }
10254
10504
  async function resolvePublicTracker(options, redact) {
10255
- const resolve9 = options.resolve ?? resolveTracker;
10505
+ const resolve11 = options.resolve ?? resolveTracker;
10256
10506
  try {
10257
- const { tracker, attempts } = await resolve9({
10507
+ const { tracker, attempts } = await resolve11({
10258
10508
  config: options.config,
10259
10509
  context: { repoRoot: options.repoRoot }
10260
10510
  });
@@ -10425,8 +10675,8 @@ function msUntil(startAt, now = new Date) {
10425
10675
  return target.getTime() - now.getTime();
10426
10676
  }
10427
10677
  function sleepFor(ms) {
10428
- return new Promise((resolve9) => {
10429
- setTimeout(resolve9, ms);
10678
+ return new Promise((resolve11) => {
10679
+ setTimeout(resolve11, ms);
10430
10680
  });
10431
10681
  }
10432
10682
  async function deferUntil(startAt, now = new Date, sleep = sleepFor) {