@open-tomato/rafa 0.32.0 → 0.33.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.
@@ -60,9 +60,11 @@ import {
60
60
  TRIAGE_MODULE,
61
61
  VENDOR_COMMAND,
62
62
  WHEN_TO_USE_HEADING,
63
+ WORKTREES_SUBDIR,
63
64
  actionInvocation,
64
65
  actionOutput,
65
66
  addBlockerToBody,
67
+ addResolveWorktree,
66
68
  alignRows,
67
69
  allocateFragmentName,
68
70
  announceRiskTotal,
@@ -92,6 +94,8 @@ import {
92
94
  claimBranchIssue,
93
95
  classifySkill,
94
96
  cleanUpAfterMerge,
97
+ cleanupCounts,
98
+ cleanupSteps,
95
99
  closedIssuesIn,
96
100
  collect,
97
101
  collectSessionRow,
@@ -104,7 +108,6 @@ import {
104
108
  createGhBoardListing,
105
109
  createGhIssueBoard,
106
110
  createGhOpenPullRequests,
107
- createGhPermissions,
108
111
  createGhPullRequests,
109
112
  createGhRoadmapBody,
110
113
  createGhRoadmapSearch,
@@ -116,9 +119,12 @@ import {
116
119
  createPlanDirNames,
117
120
  createPlanRefsVerifier,
118
121
  createStartPreflightClaim,
122
+ crossRepositoryRefusal,
123
+ defaultCleanupSeams,
119
124
  developmentApplied,
120
125
  directoryPrefix,
121
126
  driftLines,
127
+ dryRunLines,
122
128
  editChecklist,
123
129
  effortSyncRefusal,
124
130
  endWithNextStep,
@@ -205,16 +211,19 @@ import {
205
211
  parseWorkingTree,
206
212
  parseWorktrees,
207
213
  pathDirectories,
214
+ phaseNote,
215
+ phasedCounts,
208
216
  pickSession,
209
217
  plan,
210
218
  planLabel,
211
219
  previousDir,
212
220
  projectRoot,
213
221
  provenanceBlock,
222
+ pullBeforeRead,
214
223
  pushOwnershipCommit,
224
+ pushResolved,
215
225
  rankCandidates,
216
226
  readAgentDirectory,
217
- readAuthorTrust,
218
227
  readBinPath,
219
228
  readBlockedBy,
220
229
  readBlockedIssues,
@@ -226,6 +235,7 @@ import {
226
235
  readChoiceFlag,
227
236
  readClaimBranch,
228
237
  readClaimState,
238
+ readCleanup,
229
239
  readConfiguredRelations,
230
240
  readCopyRefs,
231
241
  readDoctorEffortSync,
@@ -268,6 +278,7 @@ import {
268
278
  refusal,
269
279
  refuseMisplacedRuntime,
270
280
  releaseCommitSubject,
281
+ removeResolveWorktree,
271
282
  renderBlockedIssues,
272
283
  renderCancelComment,
273
284
  renderCloseComment,
@@ -283,7 +294,6 @@ import {
283
294
  report,
284
295
  reportFollowUps,
285
296
  requireGhProvider,
286
- requireTrustedAuthor,
287
297
  resolveAgentRoster,
288
298
  resolveCreateSpec,
289
299
  resolveDefaultBoard,
@@ -294,10 +304,12 @@ import {
294
304
  resolvePrProvider,
295
305
  resolveReleaseEnabled,
296
306
  resolveTracker,
307
+ resolveWorktreePath,
297
308
  restampCopyRefs,
298
309
  rootCandidates,
299
310
  rootRefusal,
300
311
  runAction,
312
+ runCleanupSteps,
301
313
  runIssueList,
302
314
  samePlace,
303
315
  scanClaimBranches,
@@ -324,7 +336,6 @@ import {
324
336
  toolOf,
325
337
  trendReportOf,
326
338
  triageReport,
327
- trustRefusalClause,
328
339
  unaskedReasonMessage,
329
340
  unblockAfterMerge,
330
341
  unblock_default,
@@ -342,9 +353,10 @@ import {
342
353
  waitForChecks,
343
354
  waitingSettingsOf,
344
355
  watchDryEpic,
356
+ worktreeAt,
345
357
  writeSession,
346
358
  writeShippedFile
347
- } from "./index-qgayjcym.js";
359
+ } from "./index-w3vyb08t.js";
348
360
  import {
349
361
  PROBE_TIMEOUT_MS,
350
362
  loadPlanPrerequisites,
@@ -356,7 +368,7 @@ import {
356
368
  runPreflight,
357
369
  splitBlockerComment,
358
370
  trackerPathFor
359
- } from "./index-pbeyv281.js";
371
+ } from "./index-zn08ywsv.js";
360
372
  import {
361
373
  ACTION_HEADING,
362
374
  CAUSE_HEADING,
@@ -389,6 +401,7 @@ import {
389
401
  copyEffortStore,
390
402
  countTasks,
391
403
  createFileSync,
404
+ createGhPermissions,
392
405
  createGhRunner,
393
406
  createGitRunner,
394
407
  effortStoreDir,
@@ -419,6 +432,7 @@ import {
419
432
  plansDirAt,
420
433
  plural,
421
434
  positionFilePath,
435
+ readAuthorTrust,
422
436
  readFrontmatter,
423
437
  readFrontmatterDocument,
424
438
  readHopRecord,
@@ -430,22 +444,24 @@ import {
430
444
  rehome,
431
445
  renderFrontmatter,
432
446
  requireProject,
447
+ requireTrustedAuthor,
433
448
  resolvePlansDir,
434
449
  resolveProjectConfig,
435
450
  runClaudeCaptured,
436
- runsDir,
437
451
  scanLayout,
438
452
  scopeAt,
439
453
  selectEffortStore,
454
+ sessionPhase,
440
455
  sessionSpawnEnv,
441
456
  spawnClaudeCaptured,
442
457
  staleAgainst,
443
458
  toHeldRecords,
459
+ trustRefusalClause,
444
460
  version,
445
461
  versionLine,
446
462
  writeFrontmatter,
447
463
  writePositionFile
448
- } from "./index-wvm2s4pr.js";
464
+ } from "./index-kz7e4gtt.js";
449
465
  import {
450
466
  CONFIG_DEFAULTS,
451
467
  CONFIG_FILE,
@@ -460,7 +476,7 @@ import {
460
476
  readItemBytes,
461
477
  readRafaBlocks,
462
478
  resolveTiers
463
- } from "./index-9aph6ft9.js";
479
+ } from "./index-dv0ycmp8.js";
464
480
  import {
465
481
  promotable
466
482
  } from "./index-d6d0942q.js";
@@ -468,10 +484,10 @@ import {
468
484
  createCommandRegistry,
469
485
  describeRegistry,
470
486
  mountKey
471
- } from "./index-5tftj69y.js";
487
+ } from "./index-k6sd4ym5.js";
472
488
  import {
473
489
  CommandExit
474
- } from "./index-mt6hpsry.js";
490
+ } from "./index-3mzw3kzh.js";
475
491
  import {
476
492
  CLAIMS_STALE_DISABLED,
477
493
  MERGE_METHODS,
@@ -484,7 +500,7 @@ import {
484
500
  isSkillTier,
485
501
  messageOf,
486
502
  parseBudgetUsd
487
- } from "./index-rs79dq9e.js";
503
+ } from "./index-fzhd3d4y.js";
488
504
  import {
489
505
  triggerKey
490
506
  } from "./index-wp8c28fd.js";
@@ -3797,710 +3813,6 @@ function createClaimTakeCommand(makeSeams = DEFAULT_SEAMS4) {
3797
3813
  }
3798
3814
  var take_default = createClaimTakeCommand();
3799
3815
 
3800
- // src/cleanup/branches.ts
3801
- var BRANCH_FORMAT = [
3802
- "%(refname:lstrip=2)",
3803
- "%(upstream:short)",
3804
- "%(upstream:track,nobracket)",
3805
- "%(committerdate:unix)",
3806
- "%(HEAD)"
3807
- ].join("%09");
3808
- var REMOTE2 = "origin";
3809
- var FIELD_COUNT = 5;
3810
- var MS_PER_SECOND = 1000;
3811
- var GONE_TRACK = "gone";
3812
- function resolveBaseBranch(git, configured) {
3813
- if (configured !== null) {
3814
- return configured;
3815
- }
3816
- const result = git(["symbolic-ref", "--quiet", "--short", `refs/remotes/${REMOTE2}/HEAD`]);
3817
- const target = result.stdout.trim();
3818
- const prefix = `${REMOTE2}/`;
3819
- if (!result.ok || !target.startsWith(prefix) || target.length === prefix.length) {
3820
- return DEFAULT_BASE_BRANCH;
3821
- }
3822
- return target.slice(prefix.length);
3823
- }
3824
- function isKept(name, keep) {
3825
- return keep.some((pattern) => new Bun.Glob(pattern).match(name));
3826
- }
3827
- function readBranches(git, settings) {
3828
- const base = resolveBaseBranch(git, settings.base);
3829
- const result = git(["for-each-ref", `--format=${BRANCH_FORMAT}`, "refs/heads"]);
3830
- if (!result.ok) {
3831
- const said = gitSaid(result);
3832
- const detail = said === "" ? "git for-each-ref failed" : `git for-each-ref failed: ${said}`;
3833
- return { ok: false, detail };
3834
- }
3835
- const branches = [];
3836
- for (const line of result.stdout.split(`
3837
- `)) {
3838
- if (line === "") {
3839
- continue;
3840
- }
3841
- const parsed = parseBranchLine(line);
3842
- if (typeof parsed === "string") {
3843
- return { ok: false, detail: parsed };
3844
- }
3845
- if (!parsed.current && parsed.branch.name !== base && !isKept(parsed.branch.name, settings.keep)) {
3846
- branches.push(parsed.branch);
3847
- }
3848
- }
3849
- return { ok: true, base, branches };
3850
- }
3851
- function parseBranchLine(line) {
3852
- const fields = line.split("\t");
3853
- const [name = "", upstreamField = "", track = "", unix = "", head = ""] = fields;
3854
- if (fields.length !== FIELD_COUNT || name === "") {
3855
- return `git for-each-ref wrote a line with ${String(fields.length)} fields, expected ${String(FIELD_COUNT)}: ${JSON.stringify(line)}`;
3856
- }
3857
- const seconds = Number(unix);
3858
- if (unix === "" || !Number.isInteger(seconds)) {
3859
- return `git for-each-ref wrote no commit date for ${name}: ${JSON.stringify(unix)}`;
3860
- }
3861
- const lastCommit = new Date(seconds * MS_PER_SECOND);
3862
- const current = head === "*";
3863
- if (upstreamField === "") {
3864
- return { current, branch: { name, upstream: null, gone: false, ahead: null, lastCommit } };
3865
- }
3866
- if (track === GONE_TRACK) {
3867
- return { current, branch: { name, upstream: upstreamField, gone: true, ahead: null, lastCommit } };
3868
- }
3869
- return { current, branch: { name, upstream: upstreamField, gone: false, ahead: aheadOf(track), lastCommit } };
3870
- }
3871
- function aheadOf(track) {
3872
- const count = /\bahead (\d+)/.exec(track)?.[1];
3873
- if (count === undefined) {
3874
- return 0;
3875
- }
3876
- return Number(count);
3877
- }
3878
-
3879
- // src/cleanup/groups.ts
3880
- var MERGED_STATE_UNKNOWN = "merged state unknown";
3881
- var MS_PER_DAY = 24 * 60 * 60 * 1000;
3882
- function unreachableNote(detail) {
3883
- return `pull request provider unreachable (${detail}); merged pull requests not read, grouped from git alone`;
3884
- }
3885
- function baseUnreadNote(base, detail) {
3886
- return `could not read which branches are merged into ${base} (${detail}); none is listed as merged into it`;
3887
- }
3888
- async function readProviderMerges(pulls) {
3889
- if (pulls === null) {
3890
- return { state: "none" };
3891
- }
3892
- try {
3893
- return { state: "read", pullRequests: await pulls.listMerged() };
3894
- } catch (error) {
3895
- return { state: "unreachable", detail: firstLine(messageOf(error)) };
3896
- }
3897
- }
3898
- function classifyBranches(git, read, provider, settings) {
3899
- const notes = provider.state === "unreachable" ? [unreachableNote(provider.detail)] : [];
3900
- const baseRef = `refs/heads/${read.base}`;
3901
- const mergedRead = git(["branch", "--merged", baseRef, "--format=%(refname:lstrip=2)"]);
3902
- const reachable = new Set(mergedRead.ok ? lines(mergedRead.stdout) : []);
3903
- if (!mergedRead.ok) {
3904
- notes.push(baseUnreadNote(read.base, failure("git branch --merged", mergedRead)));
3905
- }
3906
- const named = provider.state === "read" ? namedPullRequests(provider.pullRequests, read.branches) : new Map;
3907
- const tips = readTips(git, named);
3908
- if (typeof tips === "string") {
3909
- return { ok: false, detail: tips };
3910
- }
3911
- const merged = [];
3912
- const stale = [];
3913
- const notPushed = [];
3914
- const context = {
3915
- unknown: provider.state === "none",
3916
- exclude: mergedRead.ok ? [baseRef] : []
3917
- };
3918
- for (const branch of read.branches) {
3919
- const pulls = named.get(branch.name) ?? [];
3920
- const atTip = pulls.find((pull) => pull.headRefOid === tips.get(branch.name)) ?? null;
3921
- const mergedBy = mergedReadings(branch, reachable.has(branch.name), atTip);
3922
- if (mergedBy.length > 0) {
3923
- merged.push(mergedRow(branch, mergedBy, atTip, read.base));
3924
- continue;
3925
- }
3926
- const suffix = rowSuffix(context, pulls[0] ?? null);
3927
- if (branch.upstream === null || (branch.ahead ?? 0) > 0) {
3928
- const commits = countLost(git, branch.name, context.exclude);
3929
- if (typeof commits === "string") {
3930
- return { ok: false, detail: commits };
3931
- }
3932
- notPushed.push(notPushedRow(branch, commits, suffix));
3933
- continue;
3934
- }
3935
- const age = settings.now.getTime() - branch.lastCommit.getTime();
3936
- if (age > settings.staleDays * MS_PER_DAY) {
3937
- const idleDays = Math.floor(age / MS_PER_DAY);
3938
- stale.push({ group: "stale", branch, ticked: false, idleDays, reason: `no commit in ${plural2(idleDays, "day")}${suffix}` });
3939
- }
3940
- }
3941
- return { ok: true, base: read.base, merged, stale, notPushed, notes };
3942
- }
3943
- function namedPullRequests(pullRequests, branches) {
3944
- const listed = new Set(branches.map((branch) => branch.name));
3945
- const named = new Map;
3946
- for (const pull of pullRequests) {
3947
- if (listed.has(pull.headRefName)) {
3948
- named.set(pull.headRefName, [...named.get(pull.headRefName) ?? [], pull]);
3949
- }
3950
- }
3951
- return named;
3952
- }
3953
- function readTips(git, named) {
3954
- const tips = new Map;
3955
- if (named.size === 0) {
3956
- return tips;
3957
- }
3958
- const result = git(["for-each-ref", "--format=%(refname:lstrip=2)%09%(objectname)", "refs/heads"]);
3959
- if (!result.ok) {
3960
- return failure("git for-each-ref", result);
3961
- }
3962
- for (const line of lines(result.stdout)) {
3963
- const [name = "", oid = ""] = line.split("\t");
3964
- tips.set(name, oid);
3965
- }
3966
- return tips;
3967
- }
3968
- function mergedReadings(branch, reachable, atTip) {
3969
- const readings = [];
3970
- if (reachable) {
3971
- readings.push("base");
3972
- }
3973
- if (atTip !== null) {
3974
- readings.push("pull-request");
3975
- }
3976
- if (branch.gone) {
3977
- readings.push("gone");
3978
- }
3979
- return readings;
3980
- }
3981
- function mergedRow(branch, mergedBy, pullRequest, base) {
3982
- const reasons = mergedBy.map((by) => {
3983
- if (by === "base") {
3984
- return `merged into ${base}`;
3985
- }
3986
- if (by === "pull-request") {
3987
- return `pull request #${String(pullRequest?.number)} merged`;
3988
- }
3989
- return `upstream ${String(branch.upstream)} is gone`;
3990
- });
3991
- return { group: "merged", branch, ticked: true, mergedBy, pullRequest, reason: reasons.join("; ") };
3992
- }
3993
- function notPushedRow(branch, commits, suffix) {
3994
- const why = branch.upstream === null ? "no upstream" : `${String(branch.ahead)} ahead of ${branch.upstream}`;
3995
- const reason = `${why}; ${plural2(commits, "commit")} not on any remote${suffix}`;
3996
- return { group: "not-pushed", branch, ticked: false, commits, reason };
3997
- }
3998
- function rowSuffix(context, elsewhere) {
3999
- if (context.unknown) {
4000
- return `; ${MERGED_STATE_UNKNOWN}`;
4001
- }
4002
- if (elsewhere !== null) {
4003
- return `; pull request #${String(elsewhere.number)} merged at another commit`;
4004
- }
4005
- return "";
4006
- }
4007
- function countLost(git, name, exclude) {
4008
- const result = git(["rev-list", "--count", `refs/heads/${name}`, "--not", "--remotes", ...exclude, "--"]);
4009
- const count = Number(result.stdout.trim());
4010
- if (!result.ok || result.stdout.trim() === "" || !Number.isInteger(count)) {
4011
- return failure(`git rev-list --count ${name}`, result);
4012
- }
4013
- return count;
4014
- }
4015
- function failure(what, result) {
4016
- const said = firstLine(gitSaid(result));
4017
- return said === "" ? `${what} failed` : `${what} failed: ${said}`;
4018
- }
4019
- function plural2(count, noun) {
4020
- return count === 1 ? `1 ${noun}` : `${String(count)} ${noun}s`;
4021
- }
4022
- function lines(text) {
4023
- return text.split(`
4024
- `).filter((line) => line !== "");
4025
- }
4026
- function firstLine(text) {
4027
- return text.split(`
4028
- `).map((line) => line.trim()).find((line) => line !== "") ?? "";
4029
- }
4030
-
4031
- // src/cleanup/worktrees.ts
4032
- import { realpathSync as realpathSync3, statSync as statSync3 } from "fs";
4033
- import { join as join7, sep } from "path";
4034
-
4035
- // src/pr/worktree.ts
4036
- import { existsSync as existsSync3, realpathSync as realpathSync2 } from "fs";
4037
- import { join as join6 } from "path";
4038
- var WORKTREES_SUBDIR = join6(".rafa", "worktrees");
4039
- var WORKTREE_PREFIX = "pr-";
4040
- var DEFAULT_REMOTE = "origin";
4041
- var REFUSAL_PREFIX = "rafa pr triage --resolve refuses";
4042
- var FORCING_FLAG = /^(?:-f|--force(?:-with-lease|-if-includes)?(?:=.*)?|--mirror)$/;
4043
- var FORCING_REFSPEC_MARK = "+";
4044
- function resolveWorktreePath(home, number) {
4045
- return join6(home, WORKTREES_SUBDIR, `${WORKTREE_PREFIX}${number}`);
4046
- }
4047
- function crossRepositoryRefusal(pr) {
4048
- if (!pr.isCrossRepository)
4049
- return null;
4050
- return `${REFUSAL_PREFIX} #${pr.number}: its head branch \`${pr.headRefName}\` is on a fork.` + " There is no branch in this repository to check a worktree out of, and the resolution" + " would have to be pushed to a repository this checkout does not own." + ` Assess it with \`rafa pr triage ${pr.number}\`, which needs no worktree, and resolve it` + " where the fork is.";
4051
- }
4052
- function frozenStep(step) {
4053
- return Object.freeze({ ...step, argv: Object.freeze(step.argv) });
4054
- }
4055
- function addWorktreeStep(path, branch) {
4056
- return frozenStep({
4057
- id: "add",
4058
- label: `add a worktree for ${branch} at ${path}`,
4059
- argv: ["git", "worktree", "add", path, branch]
4060
- });
4061
- }
4062
- function removeWorktreeStep(path) {
4063
- return frozenStep({
4064
- id: "remove",
4065
- label: `remove the worktree at ${path}`,
4066
- argv: ["git", "worktree", "remove", path]
4067
- });
4068
- }
4069
- function pushResolvedStep(branch, remote = DEFAULT_REMOTE) {
4070
- return frozenStep({
4071
- id: "push",
4072
- label: `push ${branch} to ${remote}`,
4073
- argv: ["git", "push", remote, branch]
4074
- });
4075
- }
4076
- function worktreeCommandLine(step) {
4077
- return step.argv.map((word) => shellQuote(word)).join(" ");
4078
- }
4079
- function forcingWords(argv) {
4080
- return argv.filter((word) => FORCING_FLAG.test(word) || word.startsWith(FORCING_REFSPEC_MARK));
4081
- }
4082
- function forcedPushRefusal(argv) {
4083
- const forcing = forcingWords(argv);
4084
- if (forcing.length === 0)
4085
- return null;
4086
- const words = forcing.map((word) => `\`${word}\``).join(", ");
4087
- return `${REFUSAL_PREFIX} a push that git would force: ${words}.` + " A resolve run never overwrites what is on the remote, so nothing was pushed.";
4088
- }
4089
- function runStep(git, step) {
4090
- const result = git(step.argv.slice(1));
4091
- return {
4092
- id: step.id,
4093
- ok: result.ok,
4094
- command: worktreeCommandLine(step),
4095
- said: gitSaid(result)
4096
- };
4097
- }
4098
- function addResolveWorktree(git, path, branch) {
4099
- return runStep(git, addWorktreeStep(path, branch));
4100
- }
4101
- function removeResolveWorktree(git, path) {
4102
- return runStep(git, removeWorktreeStep(path));
4103
- }
4104
- function pushResolved(git, branch, remote = DEFAULT_REMOTE) {
4105
- const step = pushResolvedStep(branch, remote);
4106
- const refusal3 = forcedPushRefusal(step.argv);
4107
- if (refusal3 === null)
4108
- return runStep(git, step);
4109
- return { id: step.id, ok: false, command: worktreeCommandLine(step), said: refusal3 };
4110
- }
4111
- function pathForms(path) {
4112
- if (!existsSync3(path))
4113
- return [path];
4114
- return [path, realpathSync2(path)];
4115
- }
4116
- function worktreeAt(git, path) {
4117
- const listed = git(["worktree", "list", "--porcelain"]);
4118
- if (!listed.ok)
4119
- return null;
4120
- const forms = pathForms(path);
4121
- return parseWorktrees(listed.stdout).find((entry) => forms.includes(entry.path)) ?? null;
4122
- }
4123
-
4124
- // src/cleanup/worktrees.ts
4125
- var CLAUDE_WORKTREES_SUBDIR = join7(".claude", "worktrees");
4126
- var WORKTREE_LIST = Object.freeze(["worktree", "list", "--porcelain"]);
4127
- var GIT_DIR = Object.freeze(["rev-parse", "--absolute-git-dir"]);
4128
- var WORKTREE_STATUS = Object.freeze([
4129
- "--no-optional-locks",
4130
- "status",
4131
- "--porcelain=v1",
4132
- "--untracked-files=all"
4133
- ]);
4134
- var ADMIN_FILES = Object.freeze(["HEAD", join7("logs", "HEAD"), "index"]);
4135
- var MS_PER_DAY2 = 24 * 60 * 60 * 1000;
4136
- var UNTRACKED_MARK = "?? ";
4137
- var LOCKED_LINE = "locked";
4138
- var LIVE_STATES = new Set(["running", "paused"]);
4139
- function defaultWorktreeSeams(cwd) {
4140
- return {
4141
- git: createGitRunner(cwd),
4142
- gitAt: createGitRunner,
4143
- sessions: (root) => readSessions(root),
4144
- modifiedAt: (path) => {
4145
- try {
4146
- return statSync3(path).mtime;
4147
- } catch {
4148
- return null;
4149
- }
4150
- },
4151
- realPath: (path) => {
4152
- try {
4153
- return realpathSync3(path);
4154
- } catch {
4155
- return path;
4156
- }
4157
- }
4158
- };
4159
- }
4160
- function readWorktrees(seams, read) {
4161
- const listed = seams.git(WORKTREE_LIST);
4162
- if (!listed.ok) {
4163
- return { ok: false, detail: failure2("git worktree list", listed) };
4164
- }
4165
- const entries = parseWorktrees(listed.stdout);
4166
- const main = entries[0]?.path;
4167
- if (main === undefined) {
4168
- return { ok: true, worktrees: [] };
4169
- }
4170
- const roots = [
4171
- ...pathForms2(seams, join7(main, CLAUDE_WORKTREES_SUBDIR)),
4172
- ...pathForms2(seams, join7(read.home, WORKTREES_SUBDIR))
4173
- ];
4174
- const locks = parseLocks(listed.stdout);
4175
- const context = {
4176
- seams,
4177
- read,
4178
- cwd: pathForms2(seams, read.cwd),
4179
- projectSessions: readLive(seams, read.projectRoot),
4180
- merged: new Set(read.mergedBranches)
4181
- };
4182
- const worktrees = entries.slice(1).filter((entry) => roots.some((root) => isInside(entry.path, root))).map((entry) => worktreeRow(context, entry.path, entry.branch, locks.get(entry.path) ?? null));
4183
- return { ok: true, worktrees };
4184
- }
4185
- function parseLocks(stdout) {
4186
- const locks = new Map;
4187
- let path = null;
4188
- for (const raw of stdout.split(`
4189
- `)) {
4190
- const line = raw.replace(/\r$/, "");
4191
- if (line.startsWith("worktree ")) {
4192
- path = line.slice("worktree ".length).trim();
4193
- continue;
4194
- }
4195
- if (path !== null && (line === LOCKED_LINE || line.startsWith(`${LOCKED_LINE} `))) {
4196
- locks.set(path, line.slice(LOCKED_LINE.length).trim());
4197
- }
4198
- }
4199
- return locks;
4200
- }
4201
- function countChanges(stdout) {
4202
- const entries = stdout.split(`
4203
- `).filter((line) => line.trim() !== "");
4204
- const untracked = entries.filter((line) => line.startsWith(UNTRACKED_MARK)).length;
4205
- return { changed: entries.length - untracked, untracked };
4206
- }
4207
- function worktreeRow(context, path, branch, lock) {
4208
- const { seams, read } = context;
4209
- const git = seams.gitAt(path);
4210
- const modified = lastModified(seams, git, path);
4211
- const blockers = [];
4212
- blockers.push(...dirtyBlockers(git));
4213
- if (lock !== null) {
4214
- const reason = lock === "" ? LOCKED_LINE : `${LOCKED_LINE}: ${lock}`;
4215
- blockers.push({ kind: "locked", reason });
4216
- }
4217
- if (context.cwd.some((dir) => dir === path || isInside(dir, path))) {
4218
- blockers.push({ kind: "current", reason: "rafa cleanup runs from it" });
4219
- }
4220
- blockers.push(...sessionBlockers(context, path, branch));
4221
- blockers.push(...recentBlockers(modified, read));
4222
- const ordered = orderBlockers(blockers);
4223
- const branchMerged = branch !== null && context.merged.has(branch);
4224
- const tickable = ordered.length === 0;
4225
- return {
4226
- path,
4227
- branch,
4228
- lastModified: typeof modified === "string" ? null : modified,
4229
- branchMerged,
4230
- blockers: ordered,
4231
- tickable,
4232
- ticked: tickable && branchMerged,
4233
- reason: rowReason(ordered, branch, branchMerged)
4234
- };
4235
- }
4236
- function lastModified(seams, git, path) {
4237
- const gitDir = git(GIT_DIR);
4238
- const adminDir = gitDir.stdout.trim();
4239
- if (!gitDir.ok || adminDir === "") {
4240
- return failure2("git rev-parse --absolute-git-dir", gitDir);
4241
- }
4242
- const times = [path, ...ADMIN_FILES.map((file) => join7(adminDir, file))].map((file) => seams.modifiedAt(file)).filter((time) => time !== null).map((time) => time.getTime());
4243
- if (times.length === 0) {
4244
- return `no modification time could be read under ${path} or ${adminDir}`;
4245
- }
4246
- return new Date(Math.max(...times));
4247
- }
4248
- function dirtyBlockers(git) {
4249
- const status = git(WORKTREE_STATUS);
4250
- if (!status.ok) {
4251
- return [{ kind: "unreadable", reason: `its changes could not be read: ${failure2("git status", status)}` }];
4252
- }
4253
- const { changed, untracked } = countChanges(status.stdout);
4254
- const parts = [
4255
- ...changed > 0 ? [plural3(changed, "uncommitted change")] : [],
4256
- ...untracked > 0 ? [plural3(untracked, "untracked file")] : []
4257
- ];
4258
- return parts.length === 0 ? [] : [{ kind: "dirty", reason: parts.join(", ") }];
4259
- }
4260
- function sessionBlockers(context, path, branch) {
4261
- const own = readLive(context.seams, path);
4262
- const readings = [own];
4263
- if (typeof context.projectSessions === "string") {
4264
- readings.push(context.projectSessions);
4265
- } else if (branch !== null) {
4266
- readings.push(context.projectSessions.filter((record) => record.branch === branch));
4267
- }
4268
- const blockers = [];
4269
- for (const reading of readings) {
4270
- if (typeof reading === "string") {
4271
- blockers.push({ kind: "unreadable", reason: reading });
4272
- continue;
4273
- }
4274
- for (const record of reading) {
4275
- blockers.push({ kind: "session", reason: `loop session ${record.sessionId} is ${record.state} in it` });
4276
- }
4277
- }
4278
- return blockers;
4279
- }
4280
- function recentBlockers(modified, read) {
4281
- if (typeof modified === "string") {
4282
- return [{ kind: "unreadable", reason: `when it was last modified could not be read: ${modified}` }];
4283
- }
4284
- const age = read.now.getTime() - modified.getTime();
4285
- if (age >= read.idleDays * MS_PER_DAY2) {
4286
- return [];
4287
- }
4288
- const days = Math.max(0, Math.floor(age / MS_PER_DAY2));
4289
- const when = days === 0 ? "today" : `${plural3(days, "day")} ago`;
4290
- return [{ kind: "recent", reason: `modified ${when}, within cleanup.worktreeIdleDays (${String(read.idleDays)})` }];
4291
- }
4292
- function readLive(seams, root) {
4293
- try {
4294
- return seams.sessions(root).filter((record) => LIVE_STATES.has(record.state));
4295
- } catch (error) {
4296
- return `the session records under ${runsDir(root)} could not be read: ${messageOf(error)}`;
4297
- }
4298
- }
4299
- var BLOCK_ORDER = ["dirty", "locked", "current", "session", "recent", "unreadable"];
4300
- function orderBlockers(blockers) {
4301
- return BLOCK_ORDER.flatMap((kind) => blockers.filter((blocker) => blocker.kind === kind));
4302
- }
4303
- function rowReason(blockers, branch, merged) {
4304
- if (blockers.length > 0) {
4305
- return blockers.map((blocker) => blocker.reason).join("; ");
4306
- }
4307
- if (branch === null) {
4308
- return "clean; detached HEAD";
4309
- }
4310
- return merged ? `clean; branch ${branch} is merged` : `clean; branch ${branch} is not merged`;
4311
- }
4312
- function pathForms2(seams, path) {
4313
- const resolved = seams.realPath(path);
4314
- return resolved === path ? [path] : [path, resolved];
4315
- }
4316
- function isInside(path, dir) {
4317
- const prefix = dir.endsWith(sep) ? dir : `${dir}${sep}`;
4318
- return path.startsWith(prefix);
4319
- }
4320
- function failure2(what, result) {
4321
- const said = gitSaid(result).split(`
4322
- `).map((line) => line.trim()).find((line) => line !== "") ?? "";
4323
- return said === "" ? `${what} failed` : `${what} failed: ${said}`;
4324
- }
4325
- function plural3(count, noun) {
4326
- return count === 1 ? `1 ${noun}` : `${String(count)} ${noun}s`;
4327
- }
4328
- // src/cleanup/steps.ts
4329
- var FORCING_FLAG2 = /^(?:--force(?:=.*)?|-[^-]*f[^-]*)$/;
4330
- function frozenStep2(step) {
4331
- return Object.freeze({ ...step, argv: Object.freeze([...step.argv]) });
4332
- }
4333
- function removeWorktreeStep2(path) {
4334
- return frozenStep2({
4335
- kind: "remove-worktree",
4336
- subject: path,
4337
- argv: ["git", "worktree", "remove", path],
4338
- after: null
4339
- });
4340
- }
4341
- function deleteBranchStep(branch, forced, after = null) {
4342
- const flag = forced ? "-D" : "-d";
4343
- return frozenStep2({
4344
- kind: "delete-branch",
4345
- subject: branch,
4346
- argv: ["git", "branch", flag, branch],
4347
- after
4348
- });
4349
- }
4350
- function needsForcedDelete(row) {
4351
- return !row.mergedBy.includes("base") && row.mergedBy.includes("pull-request") && row.pullRequest !== null;
4352
- }
4353
- function branchPicks(selection) {
4354
- return [
4355
- ...selection.merged.map((row) => ({ name: row.branch.name, merged: true, forced: needsForcedDelete(row) })),
4356
- ...selection.stale.map((row) => ({ name: row.branch.name, merged: false, forced: true })),
4357
- ...selection.notPushed.map((row) => ({ name: row.branch.name, merged: false, forced: true }))
4358
- ];
4359
- }
4360
- function heldReason(path) {
4361
- return `held by the worktree at ${path}; only a Merged branch is deleted with its worktree`;
4362
- }
4363
- function blockedHolderReason(path) {
4364
- return `held by the worktree at ${path}, which is not removed`;
4365
- }
4366
- function branchStep(pick, removed, blocked) {
4367
- const blockedPath = blocked.get(pick.name);
4368
- if (blockedPath !== undefined) {
4369
- return { kind: "delete-branch", subject: pick.name, reason: blockedHolderReason(blockedPath) };
4370
- }
4371
- const path = removed.get(pick.name) ?? null;
4372
- if (path !== null && !pick.merged) {
4373
- return { kind: "delete-branch", subject: pick.name, reason: heldReason(path) };
4374
- }
4375
- return deleteBranchStep(pick.name, pick.forced, path);
4376
- }
4377
- function isStep(entry) {
4378
- return "argv" in entry;
4379
- }
4380
- function holders(rows) {
4381
- return new Map(rows.flatMap((row) => row.branch === null ? [] : [[row.branch, row.path]]));
4382
- }
4383
- function cleanupSteps(selection) {
4384
- const removable = selection.worktrees.filter((row) => row.tickable);
4385
- const untickable = selection.worktrees.filter((row) => !row.tickable);
4386
- const branchEntries = branchPicks(selection).map((pick) => branchStep(pick, holders(removable), holders(untickable)));
4387
- return Object.freeze({
4388
- steps: Object.freeze([
4389
- ...removable.map((row) => removeWorktreeStep2(row.path)),
4390
- ...branchEntries.filter(isStep)
4391
- ]),
4392
- withheld: Object.freeze([
4393
- ...untickable.map((row) => ({ kind: "remove-worktree", subject: row.path, reason: row.reason })),
4394
- ...branchEntries.filter((entry) => !isStep(entry))
4395
- ])
4396
- });
4397
- }
4398
- function cleanupCommandLine(step) {
4399
- return step.argv.map((word) => shellQuote(word)).join(" ");
4400
- }
4401
- function dryRunLines(plan2) {
4402
- return plan2.steps.map((step) => cleanupCommandLine(step));
4403
- }
4404
- function forcingWords2(argv) {
4405
- return argv.filter((word) => FORCING_FLAG2.test(word));
4406
- }
4407
- function forcedFlagRefusal(argv) {
4408
- const forcing = forcingWords2(argv);
4409
- if (forcing.length === 0)
4410
- return null;
4411
- const words = forcing.map((word) => `\`${word}\``).join(", ");
4412
- return `rafa cleanup refuses a forced removal: ${words}. Nothing was run.`;
4413
- }
4414
- function afterFailedReason(path) {
4415
- return `not run: the worktree at ${path} was not removed`;
4416
- }
4417
- function notRun(step, said) {
4418
- return { step, ran: false, ok: false, command: cleanupCommandLine(step), said };
4419
- }
4420
- function runStep2(git, step, removed) {
4421
- const refusal3 = forcedFlagRefusal(step.argv);
4422
- if (refusal3 !== null)
4423
- return notRun(step, refusal3);
4424
- if (step.after !== null && !removed.has(step.after))
4425
- return notRun(step, afterFailedReason(step.after));
4426
- const result = git(step.argv.slice(1));
4427
- return { step, ran: true, ok: result.ok, command: cleanupCommandLine(step), said: gitSaid(result) };
4428
- }
4429
- function runCleanupSteps(git, plan2) {
4430
- const removed = new Set;
4431
- return plan2.steps.map((step) => {
4432
- const outcome = runStep2(git, step, removed);
4433
- if (outcome.ok && step.kind === "remove-worktree")
4434
- removed.add(step.subject);
4435
- return outcome;
4436
- });
4437
- }
4438
-
4439
- // src/cleanup/index.ts
4440
- var FETCH_PRUNE = Object.freeze(["fetch", "--prune"]);
4441
- function defaultCleanupSeams(cwd, pulls) {
4442
- return { ...defaultWorktreeSeams(cwd), pulls };
4443
- }
4444
- function fetchFailedNote(detail) {
4445
- return `git fetch --prune failed (${detail}); upstreams read as gone as of the last fetch`;
4446
- }
4447
- async function readCleanup(seams, settings) {
4448
- const notes = [];
4449
- let fetched = false;
4450
- if (settings.fetch) {
4451
- const result = seams.git(FETCH_PRUNE);
4452
- fetched = result.ok;
4453
- if (!result.ok) {
4454
- notes.push(fetchFailedNote(firstLine2(gitSaid(result)) || "git said nothing"));
4455
- }
4456
- }
4457
- const branches = readBranches(seams.git, { base: settings.base, keep: settings.keep });
4458
- if (!branches.ok) {
4459
- return branches;
4460
- }
4461
- const provider = await readProviderMerges(seams.pulls);
4462
- const groups = classifyBranches(seams.git, branches, provider, {
4463
- staleDays: settings.staleDays,
4464
- now: settings.now
4465
- });
4466
- if (!groups.ok) {
4467
- return groups;
4468
- }
4469
- const worktrees = readWorktrees(seams, {
4470
- home: settings.home,
4471
- cwd: settings.cwd,
4472
- projectRoot: settings.projectRoot,
4473
- idleDays: settings.worktreeIdleDays,
4474
- now: settings.now,
4475
- mergedBranches: groups.merged.map((row) => row.branch.name)
4476
- });
4477
- if (!worktrees.ok) {
4478
- return worktrees;
4479
- }
4480
- return {
4481
- ok: true,
4482
- base: groups.base,
4483
- fetched,
4484
- merged: groups.merged,
4485
- stale: groups.stale,
4486
- notPushed: groups.notPushed,
4487
- worktrees: worktrees.worktrees,
4488
- notes: [...notes, ...groups.notes]
4489
- };
4490
- }
4491
- function cleanupCounts(read) {
4492
- return {
4493
- merged: read.merged.length,
4494
- stale: read.stale.length,
4495
- notPushed: read.notPushed.length,
4496
- worktrees: read.worktrees.length
4497
- };
4498
- }
4499
- function firstLine2(text) {
4500
- return text.split(`
4501
- `).map((line) => line.trim()).find((line) => line !== "") ?? "";
4502
- }
4503
-
4504
3816
  // src/cli/prompt/multi-select.ts
4505
3817
  var NONE_CHECKED_TEXT = "(none)";
4506
3818
  var CHECKED_MARK = "\u25C9";
@@ -4531,8 +3843,8 @@ function scrollTo2(top, cursor, pageSize) {
4531
3843
  return cursor - pageSize + 1;
4532
3844
  return top;
4533
3845
  }
4534
- function eraseFrame3(lines2) {
4535
- return lines2 > 1 ? `\x1B[${lines2 - 1}A${CLEAR_DOWN3}` : CLEAR_DOWN3;
3846
+ function eraseFrame3(lines) {
3847
+ return lines > 1 ? `\x1B[${lines - 1}A${CLEAR_DOWN3}` : CLEAR_DOWN3;
4536
3848
  }
4537
3849
  function move2(state, step, pageSize) {
4538
3850
  const count = state.matches.length;
@@ -4602,19 +3914,19 @@ function layoutOf(groups) {
4602
3914
  const choices = [];
4603
3915
  const groupOf = [];
4604
3916
  const lineOf = [];
4605
- const lines2 = [];
3917
+ const lines = [];
4606
3918
  groups.forEach((group, groupIndex) => {
4607
3919
  if (group.choices.length === 0)
4608
3920
  return;
4609
- lines2.push({ heading: group.title });
3921
+ lines.push({ heading: group.title });
4610
3922
  for (const choice of group.choices) {
4611
- lineOf.push(lines2.length);
4612
- lines2.push({ index: choices.length });
3923
+ lineOf.push(lines.length);
3924
+ lines.push({ index: choices.length });
4613
3925
  groupOf.push(groupIndex);
4614
3926
  choices.push(choice);
4615
3927
  }
4616
3928
  });
4617
- return { choices, groupOf, lineOf, lines: lines2 };
3929
+ return { choices, groupOf, lineOf, lines };
4618
3930
  }
4619
3931
  function tickable(choice) {
4620
3932
  return choice !== undefined && choice.disabled === undefined;
@@ -4749,7 +4061,7 @@ var CLEANUP_GROUP_TITLES = Object.freeze({
4749
4061
  });
4750
4062
  var NO_ROWS_TEXT = "(none)";
4751
4063
  var UNKNOWN_DATE = "unknown";
4752
- var DEFAULT_REMOTE2 = "origin";
4064
+ var DEFAULT_REMOTE = "origin";
4753
4065
  var ROW_INDENT = " ";
4754
4066
  var COLUMN_GAP = " ";
4755
4067
  function cleanupDate(date) {
@@ -4758,7 +4070,7 @@ function cleanupDate(date) {
4758
4070
  function remoteDeleteLine(branch, upstream) {
4759
4071
  const slash = upstream === null ? -1 : upstream.indexOf("/");
4760
4072
  if (upstream === null || slash <= 0 || slash === upstream.length - 1) {
4761
- return `git push ${DEFAULT_REMOTE2} --delete ${shellQuote(branch)}`;
4073
+ return `git push ${DEFAULT_REMOTE} --delete ${shellQuote(branch)}`;
4762
4074
  }
4763
4075
  const remote = upstream.slice(0, slash);
4764
4076
  const name = upstream.slice(slash + 1);
@@ -5097,8 +4409,8 @@ var describe_default = Object.freeze(describeCommand);
5097
4409
  import { basename as basename5, resolve as resolve3 } from "path";
5098
4410
 
5099
4411
  // src/board/status.ts
5100
- import { statSync as statSync4 } from "fs";
5101
- import { join as join8 } from "path";
4412
+ import { statSync as statSync3 } from "fs";
4413
+ import { join as join6 } from "path";
5102
4414
  var ROADMAP_ROW_NAME = `${ROADMAP_TITLE} issue`;
5103
4415
  function rowOf(kind, name, outcome, detail) {
5104
4416
  return { kind, name, outcome, detail };
@@ -5118,11 +4430,11 @@ async function readLabelRows(gh) {
5118
4430
  return Object.freeze(BOARD_LABELS.map((label) => missing.includes(label) ? rowOf("label", label.name, "missing", "the repository carries no label of that name") : rowOf("label", label.name, "present", "the repository carries it")));
5119
4431
  }
5120
4432
  function readTemplateRow(root) {
5121
- const path = join8(root, SPEC_TEMPLATE_PATH);
4433
+ const path = join6(root, SPEC_TEMPLATE_PATH);
5122
4434
  const name = SPEC_TEMPLATE_PATH;
5123
4435
  let found;
5124
4436
  try {
5125
- found = statSync4(path);
4437
+ found = statSync3(path);
5126
4438
  } catch {
5127
4439
  return rowOf("template", name, "missing", "the repository carries no spec issue template");
5128
4440
  }
@@ -5320,21 +4632,21 @@ function renderDoctorEpics(report2) {
5320
4632
  }
5321
4633
 
5322
4634
  // src/commands/init-board.ts
5323
- import { join as join10 } from "path";
4635
+ import { join as join8 } from "path";
5324
4636
 
5325
4637
  // src/board/epic-guard.ts
5326
- import { existsSync as existsSync4, readFileSync as readFileSync5 } from "fs";
5327
- import { dirname, join as join9 } from "path";
4638
+ import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
4639
+ import { dirname, join as join7 } from "path";
5328
4640
  import { fileURLToPath } from "url";
5329
4641
  var MODULE_DIR = dirname(fileURLToPath(import.meta.url));
5330
- var EPIC_GUARD_PATH = join9(".github", "workflows", "epic-guard.yml");
4642
+ var EPIC_GUARD_PATH = join7(".github", "workflows", "epic-guard.yml");
5331
4643
  var EPIC_GUARD_FILE = "epic-guard.yml";
5332
4644
  function epicGuardSource(moduleDir = MODULE_DIR) {
5333
- return join9(moduleDir, TEMPLATES_DIRNAME, EPIC_GUARD_FILE);
4645
+ return join7(moduleDir, TEMPLATES_DIRNAME, EPIC_GUARD_FILE);
5334
4646
  }
5335
4647
  function readEpicGuard(moduleDir = MODULE_DIR) {
5336
4648
  const source = epicGuardSource(moduleDir);
5337
- if (!existsSync4(source)) {
4649
+ if (!existsSync3(source)) {
5338
4650
  throw new Error(`board setup: the epic guard workflow is missing: no file at ${source}`);
5339
4651
  }
5340
4652
  return readFileSync5(source, "utf8");
@@ -5434,14 +4746,14 @@ function toNativeMembers(readings, epic) {
5434
4746
  }
5435
4747
  function checklistWrite(epic, members) {
5436
4748
  const named = new Set(parseRoadmapBody(epic.body).map((line) => line.issue));
5437
- const lines2 = members.filter((member) => !named.has(member.number)).map((member) => Object.freeze({ issue: member.number, why: member.title, ticked: member.state === "CLOSED" }));
5438
- if (lines2.length === 0)
4749
+ const lines = members.filter((member) => !named.has(member.number)).map((member) => Object.freeze({ issue: member.number, why: member.title, ticked: member.state === "CLOSED" }));
4750
+ if (lines.length === 0)
5439
4751
  return [];
5440
4752
  return [Object.freeze({
5441
4753
  kind: "checklist",
5442
4754
  epic: epic.number,
5443
- lines: Object.freeze(lines2),
5444
- what: `put ${lines2.map((line) => ref(line.issue)).join(", ")} on epic ${ref(epic.number)}'s checklist`
4755
+ lines: Object.freeze(lines),
4756
+ what: `put ${lines.map((line) => ref(line.issue)).join(", ")} on epic ${ref(epic.number)}'s checklist`
5445
4757
  })];
5446
4758
  }
5447
4759
  function toLabelsMembers(readings, epic) {
@@ -5567,9 +4879,9 @@ function withoutBlockedLine(issue, body) {
5567
4879
  const { line } = readBlockedBy(issue, body);
5568
4880
  if (line === null)
5569
4881
  return body;
5570
- const lines2 = body.split(`
4882
+ const lines = body.split(`
5571
4883
  `);
5572
- return [...lines2.slice(0, line - 1), ...lines2.slice(line)].join(`
4884
+ return [...lines.slice(0, line - 1), ...lines.slice(line)].join(`
5573
4885
  `);
5574
4886
  }
5575
4887
  async function sendWrite(gh, bodies, write) {
@@ -5806,7 +5118,7 @@ async function runEpicGuardStep(options) {
5806
5118
  part: writeEpicGuard(root, moduleDir),
5807
5119
  warnings: []
5808
5120
  });
5809
- if (anythingAt(join10(root, EPIC_GUARD_PATH)))
5121
+ if (anythingAt(join8(root, EPIC_GUARD_PATH)))
5810
5122
  return write(false);
5811
5123
  if (wanted === true)
5812
5124
  return write(false);
@@ -5838,9 +5150,9 @@ function moveQuestion(plan2) {
5838
5150
  function moveUnreadWarning(to, problem) {
5839
5151
  return `board.relationships is ${to}: the board could not be read for relationships to move, so nothing` + ` was moved: ${problem}; run ${MOVE_FIX} again.`;
5840
5152
  }
5841
- function moveFailureWarning(result2, failure3) {
5153
+ function moveFailureWarning(result2, failure) {
5842
5154
  const left = result2.left.length;
5843
- return `the board refused "${failure3.what}": ${failure3.problem}. ${String(result2.done.length)} write(s) went` + ` through and ${String(left)} did not, and the old marks were kept; every write is idempotent, so run` + ` ${MOVE_FIX} again to finish the move.`;
5155
+ return `the board refused "${failure.what}": ${failure.problem}. ${String(result2.done.length)} write(s) went` + ` through and ${String(left)} did not, and the old marks were kept; every write is idempotent, so run` + ` ${MOVE_FIX} again to finish the move.`;
5844
5156
  }
5845
5157
  function planLines(plan2) {
5846
5158
  return [
@@ -6071,9 +5383,9 @@ function renderDoctorRelations(report2) {
6071
5383
  }
6072
5384
 
6073
5385
  // src/commands/doctor-render.ts
6074
- import { basename as basename3, relative as relative3, sep as sep2 } from "path";
5386
+ import { basename as basename3, relative as relative3, sep } from "path";
6075
5387
  function shownPath(path, root) {
6076
- return path.startsWith(`${root}${sep2}`) ? relative3(root, path) : path;
5388
+ return path.startsWith(`${root}${sep}`) ? relative3(root, path) : path;
6077
5389
  }
6078
5390
  function checkedPhrase(count, automatic, whose) {
6079
5391
  if (count === 0)
@@ -6368,11 +5680,11 @@ function readSessionEnv(seams) {
6368
5680
  }
6369
5681
 
6370
5682
  // src/plan/needs.ts
6371
- import { statSync as statSync5 } from "fs";
6372
- import { join as join12 } from "path";
5683
+ import { statSync as statSync4 } from "fs";
5684
+ import { join as join10 } from "path";
6373
5685
 
6374
5686
  // src/inventory/mcp.ts
6375
- import { join as join11 } from "path";
5687
+ import { join as join9 } from "path";
6376
5688
  var MCP_JSON_FILE = ".mcp.json";
6377
5689
  var CLAUDE_JSON_FILE = ".claude.json";
6378
5690
  var MCP_SERVERS_KEY = "mcpServers";
@@ -6436,12 +5748,12 @@ function readRejections(seams, warnings) {
6436
5748
  }
6437
5749
  function readMcpServers(seams) {
6438
5750
  const warnings = [];
6439
- const claudeJsonPath = join11(seams.home, CLAUDE_JSON_FILE);
5751
+ const claudeJsonPath = join9(seams.home, CLAUDE_JSON_FILE);
6440
5752
  const claudeJson = readFile(claudeJsonPath, warnings);
6441
5753
  const projects = claudeJson === null ? null : readMapping(claudeJson, PROJECTS_KEY, claudeJsonPath, warnings);
6442
5754
  const root = seams.projectRoot;
6443
5755
  const projectEntry = projects === null || root === null || !Object.hasOwn(projects, root) ? null : readMapping(projects, root, claudeJsonPath, warnings);
6444
- const mcpJsonPath = root === null ? null : join11(root, MCP_JSON_FILE);
5756
+ const mcpJsonPath = root === null ? null : join9(root, MCP_JSON_FILE);
6445
5757
  const projectNames = mcpJsonPath === null ? [] : serverNames(readFile(mcpJsonPath, warnings), mcpJsonPath, warnings);
6446
5758
  const held = {
6447
5759
  local: { path: claudeJsonPath, names: serverNames(projectEntry, claudeJsonPath, warnings) },
@@ -6537,7 +5849,7 @@ function fencePrograms(text) {
6537
5849
  function programDirectory(name, pathDirs) {
6538
5850
  return pathDirs.find((dir) => {
6539
5851
  try {
6540
- const stats = statSync5(join12(dir, name));
5852
+ const stats = statSync4(join10(dir, name));
6541
5853
  return stats.isFile() && (stats.mode & 73) !== 0;
6542
5854
  } catch {
6543
5855
  return false;
@@ -6545,13 +5857,13 @@ function programDirectory(name, pathDirs) {
6545
5857
  }) ?? null;
6546
5858
  }
6547
5859
  function holdersOf2(records) {
6548
- const holders2 = new Map;
5860
+ const holders = new Map;
6549
5861
  for (const record of records) {
6550
5862
  const key = needKey(record.kind, record.name);
6551
- if (!holders2.has(key))
6552
- holders2.set(key, record);
5863
+ if (!holders.has(key))
5864
+ holders.set(key, record);
6553
5865
  }
6554
- return holders2;
5866
+ return holders;
6555
5867
  }
6556
5868
  function needKey(kind, name) {
6557
5869
  return `${kind}\x00${name}`;
@@ -6677,9 +5989,9 @@ function programSearchPath(program, seams) {
6677
5989
  function stackMarker(tool, projectRoot2) {
6678
5990
  if (projectRoot2 === null)
6679
5991
  return null;
6680
- const found = tool.markers.map((marker) => join12(projectRoot2, marker)).find((path) => {
5992
+ const found = tool.markers.map((marker) => join10(projectRoot2, marker)).find((path) => {
6681
5993
  try {
6682
- return statSync5(path).isFile();
5994
+ return statSync4(path).isFile();
6683
5995
  } catch {
6684
5996
  return false;
6685
5997
  }
@@ -6766,17 +6078,17 @@ function wholeWord(name) {
6766
6078
  const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6767
6079
  return new RegExp(`(?<![\\w-])${escaped}(?![\\w-])`);
6768
6080
  }
6769
- function firstMention(lines2, name) {
6081
+ function firstMention(lines, name) {
6770
6082
  const pattern = wholeWord(name);
6771
- const index = lines2.findIndex((line) => pattern.test(line));
6083
+ const index = lines.findIndex((line) => pattern.test(line));
6772
6084
  return index === -1 ? null : index + 1;
6773
6085
  }
6774
6086
  async function readSpecNeeds(specPath, seams) {
6775
- const lines2 = (await readRequired(specPath)).split(`
6087
+ const lines = (await readRequired(specPath)).split(`
6776
6088
  `);
6777
6089
  return readAgainstMachine(seams, (machine) => {
6778
6090
  const named = [...machine.holders.values()].flatMap((holder) => {
6779
- const line = firstMention(lines2, holder.name);
6091
+ const line = firstMention(lines, holder.name);
6780
6092
  return line === null ? [] : [{ kind: holder.kind, name: holder.name, origin: { by: "mentioned", line } }];
6781
6093
  });
6782
6094
  return Promise.resolve({ named, stacks: [], warnings: [] });
@@ -7176,11 +6488,11 @@ var UNREAD_NONE = Object.freeze({ provider: "none", source: "config", remote: nu
7176
6488
  function trackerChain(config) {
7177
6489
  return [...new Set([config.trackerDefault, ...config.trackerFallback])];
7178
6490
  }
7179
- function firstLine3(result2) {
7180
- const lines2 = `${result2.stderr}
6491
+ function firstLine(result2) {
6492
+ const lines = `${result2.stderr}
7181
6493
  ${result2.stdout}`.split(`
7182
6494
  `);
7183
- return lines2.map((line) => line.trim()).find((line) => line !== "") ?? "";
6495
+ return lines.map((line) => line.trim()).find((line) => line !== "") ?? "";
7184
6496
  }
7185
6497
  function activeLoginOf(stdout, host) {
7186
6498
  let payload;
@@ -7197,7 +6509,7 @@ function activeLoginOf(stdout, host) {
7197
6509
  }
7198
6510
  async function readLoginAndPermission(gh, host) {
7199
6511
  const status = await gh(["auth", "status", "--active", "--hostname", host, "--json", "hosts"]);
7200
- const login = status.ok ? activeLoginOf(status.stdout, host) : { login: null, detail: `gh auth status --json hosts failed: ${firstLine3(status) || "it wrote nothing"}` };
6512
+ const login = status.ok ? activeLoginOf(status.stdout, host) : { login: null, detail: `gh auth status --json hosts failed: ${firstLine(status) || "it wrote nothing"}` };
7201
6513
  if (login.login === null)
7202
6514
  return { login, permission: null };
7203
6515
  const permission = await readAuthorTrust({
@@ -7215,7 +6527,7 @@ async function probeGh(seams, host) {
7215
6527
  const openGh = seams.openGh ?? createGhRunner;
7216
6528
  const gh = openGh({ cwd, command: found, env });
7217
6529
  const status = await gh(["auth", "status", "--hostname", host]);
7218
- const auth = { ok: status.ok, detail: firstLine3(status) };
6530
+ const auth = { ok: status.ok, detail: firstLine(status) };
7219
6531
  if (!auth.ok)
7220
6532
  return { host, found, auth, login: null, permission: null };
7221
6533
  return { host, found, auth, ...await readLoginAndPermission(gh, host) };
@@ -7299,7 +6611,7 @@ function renderDeepSection(section) {
7299
6611
  }
7300
6612
 
7301
6613
  // src/commands/doctor-deep-settings.ts
7302
- import { join as join13 } from "path";
6614
+ import { join as join11 } from "path";
7303
6615
  var SETTINGS_SECTION_TITLE = "Settings";
7304
6616
  var SETTING_SOURCES_KEY = "loop.settingSources";
7305
6617
  function addSourceFix(source) {
@@ -7364,7 +6676,7 @@ function declarationReason(declaration, answering, seams) {
7364
6676
  if (!sources.includes(scope))
7365
6677
  return { why: sourceOffWhy(scope, sources), fix: addSourceFix(scope) };
7366
6678
  if (declaration.disabled) {
7367
- const file = join13(seams.home, CLAUDE_JSON_FILE);
6679
+ const file = join11(seams.home, CLAUDE_JSON_FILE);
7368
6680
  const entry = `${PROJECTS_KEY}["${seams.projectRoot ?? ""}"]`;
7369
6681
  return {
7370
6682
  why: `${DISABLE_KEY} names it in ${file}`,
@@ -7592,7 +6904,7 @@ function renderDeep(reading) {
7592
6904
  var DOCTOR_DESCRIPTION = "Checks the prerequisites `rafa loop start` checks before its first session and prints each" + " check, starting no run and storing nothing: the required and optional items of" + " `.rafa/config.yaml`, with the `PREREQUISITES-<stub>.md` beside the plan merged in, and, when the" + " repository resolves to `pr.provider: gh`, the two required items that provider adds ahead of them:" + " `gh` on PATH and `gh auth status` for the remote's host. A plan's `[start]` items are checked" + " after those and ahead of the configured tiers, on a first dispatch alone: a" + " `PLAN_TRACKER-<stub>.md` beside the plan already holding a ticked task makes the next run a" + " resume, which checks none of them and says in one line how many it passed over." + " The plan is the" + " one `--plan=<file>` names, relative to the project root, or the default plan `rafa loop start`" + ` runs. Each probe runs in the project root with stdin closed and a ${String(PROBE_TIMEOUT_MS / 1000)}-second` + " timeout. It exits 1 when a required item fails, naming the item, its probe and its exit code or" + " first line of stderr, where `rafa loop start` would halt. It reads the effort store as" + " `rafa effort schema --check` does, changing nothing, and prints its `effort store schema` row:" + " it exits 1 where that check fails, naming the next safe step, and warns on a migration this rafa" + " does not know that is additive and on one the project's own store logs as applied by a development" + " build. It prints its `effort sync` row, the strategy `effort.sync` names (`local` when unset) and" + " whether core or a loaded module serves it, and exits 1 when no adapter serves it, naming for" + " `git`, `service` and `p2p` the `modules:` and `allowList:` lines that load a module providing it." + " It exits 0 otherwise. It warns when" + " `.ralph/effort/` holds an effort store and `.rafa/effort/` holds none, and when `~/.rafa/bin` is" + " not on PATH ahead of `~/.bun/bin`, and when `previous/` under `specs.dir` holds more than fifty" + " previous copies of issue specs, which are safe to delete; a warning never changes the exit code. On a repository whose" + " provider is `gh` it also reads the GitHub board `rafa init --board` sets up and prints one row per" + " part \u2014 the seven labels, the spec issue template, the Roadmap issue and `roadmap.issue` \u2014 as present," + " missing, or unknown for a reading that failed, naming `rafa init --board` as the fix; it writes" + " nothing to the board and a row never changes the exit code. It then names, under `Blocked" + " issues:`, every open issue labelled `spec:blocked` whose `Blocked by:` line is missing, names no" + " issue, names itself, or names an id the board has no issue for, with what an author does about" + " it; that reading writes nothing and never changes the exit code either. It then names, under" + " `Epic labels:`, every issue carrying two `epic:` labels and every `epic:` label no `type:epic` issue" + " carries, read off one board listing, writing nothing and never changing the exit code. It then names, under" + " `Boards:`, every type:roadmap board whose Owner: handle resolves to nobody GitHub shows, every open issue titled" + ` "Roadmap" without type:roadmap while labelled boards exist, and every slot of this checkout's position on a` + " board or epic that is closed, unlabelled or gone, read off that same listing and printing nothing when there is" + " none; it writes nothing and never changes the exit code. It then counts, without" + " fetching, the branches and worktrees `rafa cleanup` would list, and prints them in one row naming" + " `rafa cleanup` when any group holds one. It then counts the suspect, dangling and unknown references" + " of every saved copy under `specs.dir`, writing nothing, and names `rafa issue check <n>` for each" + " copy holding a suspect or dangling one. Where the release is on, it then prints one `Release:` row naming" + " the version `origin/<pr.base>` declares as last fetched, the latest release tag, the version the changelog's" + " top heading names and the change fragments waiting on the base, as a warning naming `rafa release settle`" + " while any wait; it fetches nothing and never changes the exit code." + " It then prints, under `Skill tiers`, a warning per" + " skill or agent name two tiers hold with different contents, per unreviewed third-party rafa or" + " add-on item, and for an installed Claude Code other than the version skill serving was probed" + " against, and a note per byte-identical copy that can be deleted and per user-tier item with no" + " `provenance`; none changes the exit code. With `--output=json` the" + " checks, both readings, those rows, those issues, those labels and those boards are the data of the terminal result event," + " unless a required item failed. A plan `--plan` names also gets the one-line risk total" + " `rafa loop start` prints before its notices, which never changes the exit code. With `--deep` it" + " also prints the machine as a loop session sees it, starting no session: the session's" + " Environment and working directory against the shell's, the Settings each agent, skill and MCP" + " server is read from and whether a session sees it, the Providers `gh` answers under the session's" + " environment, and the Stack tools the project's stack needs, with Plan needs for a plan `--plan`" + " names; each row is ok, warn or note with its fix, printed after the blocked issues and before a" + " refusal, and none changes the exit code. With `--output=json` they are the `deep` of the data.";
7593
6905
 
7594
6906
  // src/commands/doctor-effort-schema.ts
7595
- import { join as join14 } from "path";
6907
+ import { join as join12 } from "path";
7596
6908
  var EFFORT_SCHEMA_ROW = "effort store schema";
7597
6909
  var SCHEMA_COMMAND = "rafa effort schema";
7598
6910
  function developmentAppliedWarning(path, id2, appliedBy) {
@@ -7617,7 +6929,7 @@ function readDoctorEffortSchema(root, env, options = {}) {
7617
6929
  const live = override === undefined || override === "";
7618
6930
  let path;
7619
6931
  try {
7620
- path = join14(effortStoreDir(root, env), SQLITE_STORE_FILE_NAME);
6932
+ path = join12(effortStoreDir(root, env), SQLITE_STORE_FILE_NAME);
7621
6933
  } catch (error) {
7622
6934
  return unreadable(null, live, messageOf(error));
7623
6935
  }
@@ -7655,9 +6967,9 @@ function effortSchemaRefusal(reading) {
7655
6967
  }
7656
6968
 
7657
6969
  // src/effort/store/legacy.ts
7658
- import { statSync as statSync6 } from "fs";
7659
- import { join as join15 } from "path";
7660
- var LEGACY_EFFORT_STORE_DIR = join15(".ralph", "effort");
6970
+ import { statSync as statSync5 } from "fs";
6971
+ import { join as join13 } from "path";
6972
+ var LEGACY_EFFORT_STORE_DIR = join13(".ralph", "effort");
7661
6973
  var NDJSON_KINDS = { sessions: true, commits: true };
7662
6974
  var STORE_FILE_NAMES = Object.freeze([
7663
6975
  SQLITE_STORE_FILE_NAME,
@@ -7670,7 +6982,7 @@ function isAbsence(error) {
7670
6982
  }
7671
6983
  function isStoreFile(path) {
7672
6984
  try {
7673
- return statSync6(path).isFile();
6985
+ return statSync5(path).isFile();
7674
6986
  } catch (error) {
7675
6987
  if (isAbsence(error))
7676
6988
  return false;
@@ -7678,7 +6990,7 @@ function isStoreFile(path) {
7678
6990
  }
7679
6991
  }
7680
6992
  function storeFilesIn(dir) {
7681
- return Object.freeze(STORE_FILE_NAMES.filter((name) => isStoreFile(join15(dir, name))));
6993
+ return Object.freeze(STORE_FILE_NAMES.filter((name) => isStoreFile(join13(dir, name))));
7682
6994
  }
7683
6995
  function shellQuoted(path) {
7684
6996
  return `'${path.replaceAll("'", "'\\''")}'`;
@@ -7688,8 +7000,8 @@ function legacyWarning(legacyDir, storeDir, legacyFiles) {
7688
7000
  return `${legacyDir} holds an effort store (${legacyFiles.join(", ")}) and ${storeDir} holds none;` + ` rafa reads ${storeDir} alone, so its reports start from an empty store. Once no loop is running,` + ` move it: ${move3}`;
7689
7001
  }
7690
7002
  function readLegacyStore(root) {
7691
- const legacyDir = join15(root, LEGACY_EFFORT_STORE_DIR);
7692
- const storeDir = join15(root, EFFORT_STORE_DIR);
7003
+ const legacyDir = join13(root, LEGACY_EFFORT_STORE_DIR);
7004
+ const storeDir = join13(root, EFFORT_STORE_DIR);
7693
7005
  const legacyFiles = storeFilesIn(legacyDir);
7694
7006
  const storeFiles = storeFilesIn(storeDir);
7695
7007
  const warning = legacyFiles.length > 0 && storeFiles.length === 0 ? legacyWarning(legacyDir, storeDir, legacyFiles) : null;
@@ -7697,7 +7009,7 @@ function readLegacyStore(root) {
7697
7009
  }
7698
7010
 
7699
7011
  // src/project/pre-init-dirs.ts
7700
- import { normalize, sep as sep3 } from "path";
7012
+ import { normalize, sep as sep2 } from "path";
7701
7013
  var PRE_INIT_DIRS = Object.freeze([
7702
7014
  Object.freeze({
7703
7015
  setting: "planDir",
@@ -7714,7 +7026,7 @@ var PRE_INIT_DIRS = Object.freeze([
7714
7026
  ]);
7715
7027
  function compared(value) {
7716
7028
  const normalized = normalize(value);
7717
- return normalized.endsWith(sep3) && normalized.length > sep3.length ? normalized.slice(0, -sep3.length) : normalized;
7029
+ return normalized.endsWith(sep2) && normalized.length > sep2.length ? normalized.slice(0, -sep2.length) : normalized;
7718
7030
  }
7719
7031
  function preInitWarning(found) {
7720
7032
  if (found.length === 0)
@@ -7792,11 +7104,11 @@ function writeInstall(context, install) {
7792
7104
 
7793
7105
  // src/commands/doctor-release.ts
7794
7106
  import { readFileSync as readFileSync6 } from "fs";
7795
- import { join as join16 } from "path";
7107
+ import { join as join14 } from "path";
7796
7108
  var SETTLE_COMMAND = "rafa release settle";
7797
7109
  var UNREAD = "?";
7798
7110
  function readTopHeading(root, configured) {
7799
- const resolved = join16(root, configured);
7111
+ const resolved = join14(root, configured);
7800
7112
  try {
7801
7113
  const text = readFileSync6(resolved, "utf8");
7802
7114
  return { path: configured, version: changelogVersions(text)[0] ?? null, problem: null };
@@ -7880,7 +7192,7 @@ function writeDoctorRelease(context, reading) {
7880
7192
  }
7881
7193
 
7882
7194
  // src/commands/doctor-tiers.ts
7883
- import { readFileSync as readFileSync7, realpathSync as realpathSync4 } from "fs";
7195
+ import { readFileSync as readFileSync7, realpathSync as realpathSync2 } from "fs";
7884
7196
  import { basename as basename4, dirname as dirname2 } from "path";
7885
7197
  var TIERS_SECTION_TITLE = "Skill tiers";
7886
7198
  var CLAUDE_VERSION_TIMEOUT_MS = 1e4;
@@ -7894,7 +7206,7 @@ function readTextFile(path) {
7894
7206
  }
7895
7207
  function realPathOf(path) {
7896
7208
  try {
7897
- return realpathSync4(path);
7209
+ return realpathSync2(path);
7898
7210
  } catch {
7899
7211
  return path;
7900
7212
  }
@@ -8023,8 +7335,8 @@ function renderDoctorTiers(reading) {
8023
7335
  // src/commands/doctor.ts
8024
7336
  var DEFAULT_DOCTOR_SEAMS = Object.freeze({ checks: Object.freeze({}) });
8025
7337
  var NOTHING_CHECKED = "Nothing was checked.";
8026
- function refusal3(lines2) {
8027
- return new CommandExit(1, [...lines2, NOTHING_CHECKED].join(`
7338
+ function refusal3(lines) {
7339
+ return new CommandExit(1, [...lines, NOTHING_CHECKED].join(`
8028
7340
  `));
8029
7341
  }
8030
7342
  function expectNoArgument2(args) {
@@ -8156,8 +7468,8 @@ function doctorRefusal(halt, rows) {
8156
7468
  `rafa doctor: ${String(halt)}`,
8157
7469
  "rafa loop start would halt here, before any session. No run was started and nothing was stored."
8158
7470
  ];
8159
- const lines2 = halt === null ? [] : halted;
8160
- const all = [...lines2, ...failed2];
7471
+ const lines = halt === null ? [] : halted;
7472
+ const all = [...lines, ...failed2];
8161
7473
  return all.length === 0 ? null : new CommandExit(1, all.join(`
8162
7474
  `));
8163
7475
  }
@@ -8200,10 +7512,10 @@ function projectOf7(context) {
8200
7512
  throw new Error("rafa doctor runs inside a project, and was handed none");
8201
7513
  return context.project;
8202
7514
  }
8203
- function writeText(context, lines2) {
7515
+ function writeText(context, lines) {
8204
7516
  if (context.outputMode === "json")
8205
7517
  return;
8206
- for (const line of lines2)
7518
+ for (const line of lines)
8207
7519
  context.output.info(line);
8208
7520
  }
8209
7521
  async function announceRisk(context, preflight, seams) {
@@ -8389,13 +7701,13 @@ var collect_default = wrapPhaseZeroCommand({
8389
7701
  }, collect);
8390
7702
 
8391
7703
  // src/commands/effort/copy.ts
8392
- import { join as join17, resolve as resolve4 } from "path";
7704
+ import { join as join15, resolve as resolve4 } from "path";
8393
7705
  var COMMAND_NAME5 = "rafa effort copy";
8394
7706
  var COPY_USAGE = "rafa effort copy [--to=<dir>]";
8395
7707
  var TO_FLAG2 = "to";
8396
7708
  var REFUSED_EXIT = 1;
8397
7709
  var FAILED_EXIT = 2;
8398
- var SCRATCH_DIR = join17(".rafa", "scratch");
7710
+ var SCRATCH_DIR = join15(".rafa", "scratch");
8399
7711
  function effortDirLine(directory) {
8400
7712
  return `${EFFORT_DIR_VARIABLE}=${directory}`;
8401
7713
  }
@@ -8427,10 +7739,10 @@ function runCopy(context, seams) {
8427
7739
  const root = requireProject(context, COMMAND_NAME5).root;
8428
7740
  const named = readTarget(context, root);
8429
7741
  refuseEffortDirOverride(context);
8430
- const target = named ?? join17(root, SCRATCH_DIR, `effort-${fileStamp((seams.now ?? (() => new Date))())}`);
7742
+ const target = named ?? join15(root, SCRATCH_DIR, `effort-${fileStamp((seams.now ?? (() => new Date))())}`);
8431
7743
  let result2;
8432
7744
  try {
8433
- result2 = copyEffortStore({ source: join17(root, EFFORT_STORE_DIR), target });
7745
+ result2 = copyEffortStore({ source: join15(root, EFFORT_STORE_DIR), target });
8434
7746
  } catch (error) {
8435
7747
  if (error instanceof EffortCopyRefusal)
8436
7748
  throw new CommandExit(REFUSED_EXIT, `\u274C ${COMMAND_NAME5}: ${error.message}`);
@@ -8574,7 +7886,7 @@ function formatDashboard(dashboard) {
8574
7886
  }
8575
7887
 
8576
7888
  // src/effort/dashboard-status.ts
8577
- var MS_PER_SECOND2 = 1000;
7889
+ var MS_PER_SECOND = 1000;
8578
7890
  var SECONDS_PER_MINUTE = 60;
8579
7891
  function tasksLeft(counts) {
8580
7892
  return counts.open + counts.blocked;
@@ -8592,7 +7904,7 @@ function byTaskEstimate(row2, counts) {
8592
7904
  function byProgressEstimate(planStartedAt, counts, now) {
8593
7905
  if (counts.done === 0)
8594
7906
  return null;
8595
- const elapsedSeconds = Math.max(0, (now.getTime() - Date.parse(planStartedAt)) / MS_PER_SECOND2);
7907
+ const elapsedSeconds = Math.max(0, (now.getTime() - Date.parse(planStartedAt)) / MS_PER_SECOND);
8596
7908
  const estimate = estimateFrom(elapsedSeconds / counts.done, tasksLeft(counts));
8597
7909
  return estimate === null ? null : { ...estimate, planStartedAt, elapsedSeconds: Math.round(elapsedSeconds) };
8598
7910
  }
@@ -8802,7 +8114,7 @@ var dashboard_default = createEffortDashboardCommand();
8802
8114
  import { resolve as resolve6 } from "path";
8803
8115
 
8804
8116
  // src/commands/effort/merge.ts
8805
- import { join as join18, resolve as resolve5 } from "path";
8117
+ import { join as join16, resolve as resolve5 } from "path";
8806
8118
  var COMMAND_NAME7 = "rafa effort merge";
8807
8119
  var MERGE_USAGE = "rafa effort merge <file> [--dry-run]";
8808
8120
  var DRY_RUN_FLAG2 = "dry-run";
@@ -8841,7 +8153,7 @@ function renderMerge(result2, command = COMMAND_NAME7) {
8841
8153
  }
8842
8154
  function mergeTargetPath(context, root, command = COMMAND_NAME7) {
8843
8155
  try {
8844
- return join18(effortStoreDir(root, context.env), SQLITE_STORE_FILE_NAME);
8156
+ return join16(effortStoreDir(root, context.env), SQLITE_STORE_FILE_NAME);
8845
8157
  } catch (error) {
8846
8158
  throw new CommandExit(REFUSED_EXIT2, `\u274C ${command}: ${messageOf(error)}`);
8847
8159
  }
@@ -9012,7 +8324,7 @@ function createImportCommand(seams = {}) {
9012
8324
  var import_default = createImportCommand();
9013
8325
 
9014
8326
  // src/commands/effort/migrate.ts
9015
- import { join as join19 } from "path";
8327
+ import { join as join17 } from "path";
9016
8328
  var COMMAND_NAME9 = "rafa effort migrate";
9017
8329
  var MIGRATE_USAGE = "rafa effort migrate [--dry-run]";
9018
8330
  var DRY_RUN_FLAG4 = "dry-run";
@@ -9045,7 +8357,7 @@ function renderMigrate(result2) {
9045
8357
  }
9046
8358
  function storePath(context, root) {
9047
8359
  try {
9048
- return join19(effortStoreDir(root, context.env), SQLITE_STORE_FILE_NAME);
8360
+ return join17(effortStoreDir(root, context.env), SQLITE_STORE_FILE_NAME);
9049
8361
  } catch (error) {
9050
8362
  throw new CommandExit(REFUSED_EXIT3, `\u274C ${COMMAND_NAME9}: ${messageOf(error)}`);
9051
8363
  }
@@ -9141,17 +8453,17 @@ function namesStore(text) {
9141
8453
  return isMapping(document) && Object.hasOwn(document, "store");
9142
8454
  }
9143
8455
  function withSqliteStoreSetting(text) {
9144
- const lines2 = text.split(`
8456
+ const lines = text.split(`
9145
8457
  `);
9146
- const at = lines2.findIndex((line) => STORE_KEY.test(line));
9147
- const commented = lines2.findIndex((line) => COMMENTED_STORE_KEY.test(line));
8458
+ const at = lines.findIndex((line) => STORE_KEY.test(line));
8459
+ const commented = lines.findIndex((line) => COMMENTED_STORE_KEY.test(line));
9148
8460
  const target = at >= 0 ? at : commented;
9149
8461
  if (target >= 0)
9150
- return [...lines2.slice(0, target), STORE_LINE, ...lines2.slice(target + 1)].join(`
8462
+ return [...lines.slice(0, target), STORE_LINE, ...lines.slice(target + 1)].join(`
9151
8463
  `);
9152
8464
  if (namesStore(text))
9153
8465
  return null;
9154
- const body = lines2.at(-1) === "" ? lines2.slice(0, -1) : lines2;
8466
+ const body = lines.at(-1) === "" ? lines.slice(0, -1) : lines;
9155
8467
  return [...body, STORE_LINE, ""].join(`
9156
8468
  `);
9157
8469
  }
@@ -9401,7 +8713,7 @@ var report_default = wrapPhaseZeroCommand({
9401
8713
  }, report);
9402
8714
 
9403
8715
  // src/commands/effort/schema.ts
9404
- import { join as join20 } from "path";
8716
+ import { join as join18 } from "path";
9405
8717
  var COMMAND_NAME11 = "rafa effort schema";
9406
8718
  var SCHEMA_USAGE = "rafa effort schema [--check]";
9407
8719
  var CHECK_FLAG = "check";
@@ -9447,7 +8759,7 @@ function renderSchema(report2) {
9447
8759
  }
9448
8760
  function storePath2(context, root) {
9449
8761
  try {
9450
- return join20(effortStoreDir(root, context.env), SQLITE_STORE_FILE_NAME);
8762
+ return join18(effortStoreDir(root, context.env), SQLITE_STORE_FILE_NAME);
9451
8763
  } catch (error) {
9452
8764
  throw new CommandExit(REFUSED_EXIT5, `\u274C ${COMMAND_NAME11}: ${messageOf(error)}`);
9453
8765
  }
@@ -9811,9 +9123,9 @@ function workLines(result2) {
9811
9123
  return [info(`No terminal to ask on, so its open work is kept: ${workPhrase(work)}.`)];
9812
9124
  }
9813
9125
  case "closed": {
9814
- const lines2 = result2.closed.map((pull2) => pull2.status === "closed" ? info(`Closed pull request ${ref4(pull2.number)} with a comment.`) : { text: `Could not close pull request ${ref4(pull2.number)}: ${pull2.problem}`, warn: true });
9126
+ const lines = result2.closed.map((pull2) => pull2.status === "closed" ? info(`Closed pull request ${ref4(pull2.number)} with a comment.`) : { text: `Could not close pull request ${ref4(pull2.number)}: ${pull2.problem}`, warn: true });
9815
9127
  const kept = work.branches.length === 0 ? "No branch was deleted." : `No branch was deleted: ${work.branches.join(", ")}.`;
9816
- return [...lines2, info(kept)];
9128
+ return [...lines, info(kept)];
9817
9129
  }
9818
9130
  }
9819
9131
  }
@@ -9830,10 +9142,10 @@ function closeFailure(result2) {
9830
9142
  }
9831
9143
  async function runEpicHorizon(context, action, seams) {
9832
9144
  const result2 = await changeEpicHorizon(context, action, seams);
9833
- const failure3 = closeFailure(result2);
9145
+ const failure = closeFailure(result2);
9834
9146
  if (context.outputMode === "json") {
9835
- if (failure3 !== null)
9836
- throw failure3;
9147
+ if (failure !== null)
9148
+ throw failure;
9837
9149
  context.output.result(result2);
9838
9150
  return;
9839
9151
  }
@@ -9843,8 +9155,8 @@ async function runEpicHorizon(context, action, seams) {
9843
9155
  else
9844
9156
  context.output.info(line.text);
9845
9157
  }
9846
- if (failure3 !== null)
9847
- throw failure3;
9158
+ if (failure !== null)
9159
+ throw failure;
9848
9160
  }
9849
9161
 
9850
9162
  // src/commands/epic/move.ts
@@ -10130,8 +9442,8 @@ function movedLines2(result2, outcome) {
10130
9442
  };
10131
9443
  const work = result2.work === null || result2.work.branches.length + result2.work.pullRequests.length === 0 ? [] : [{ text: `Its open work stays as it is: ${workPhrase(result2.work)}.`, warn: false }];
10132
9444
  const comment = outcome.commentProblem === "" ? [] : [{ text: `Could not comment on ${ref5(result2.issue)}: ${outcome.commentProblem}`, warn: true }];
10133
- const lines2 = outcome.added === undefined || outcome.removed === undefined ? [{ text: nativeParentLine(result2.to), warn: false }] : [addedLine(result2, outcome.added), removedLine(result2, outcome.removed)];
10134
- return [head, ...lines2, ...work, ...comment];
9445
+ const lines = outcome.added === undefined || outcome.removed === undefined ? [{ text: nativeParentLine(result2.to), warn: false }] : [addedLine(result2, outcome.added), removedLine(result2, outcome.removed)];
9446
+ return [head, ...lines, ...work, ...comment];
10135
9447
  }
10136
9448
  function renderEpicMove(result2) {
10137
9449
  const problems = result2.problems.map((problem) => ({ text: `The open work named may be short: ${problem}`, warn: true }));
@@ -10158,10 +9470,10 @@ function moveFailure(result2) {
10158
9470
  }
10159
9471
  async function runEpicMove(context, seams) {
10160
9472
  const result2 = await moveEpicIssue(context, seams);
10161
- const failure3 = moveFailure(result2);
9473
+ const failure = moveFailure(result2);
10162
9474
  if (context.outputMode === "json") {
10163
- if (failure3 !== null)
10164
- throw failure3;
9475
+ if (failure !== null)
9476
+ throw failure;
10165
9477
  context.output.result(result2);
10166
9478
  return;
10167
9479
  }
@@ -10171,8 +9483,8 @@ async function runEpicMove(context, seams) {
10171
9483
  else
10172
9484
  context.output.info(out.text);
10173
9485
  }
10174
- if (failure3 !== null)
10175
- throw failure3;
9486
+ if (failure !== null)
9487
+ throw failure;
10176
9488
  }
10177
9489
  function createEpicMoveCommand(seams = {}) {
10178
9490
  const command = {
@@ -10370,21 +9682,21 @@ async function applyMove(input, dependent, reading) {
10370
9682
  try {
10371
9683
  const writes = { board: input.writes.board, relations: input.writes.moves, issues: input.issues };
10372
9684
  const outcome = await applyEpicMove(writes, reading, cancelMoveReason(input.epic));
10373
- const lines2 = [info(`Moved ${ref6(change.issue)} from epic ${ref6(change.from)} to ${ref6(change.to)}.`)];
9685
+ const lines = [info(`Moved ${ref6(change.issue)} from epic ${ref6(change.from)} to ${ref6(change.to)}.`)];
10374
9686
  const left = [];
10375
9687
  if (outcome.added?.status === "failed") {
10376
- lines2.push(warn(`Could not add its line to epic ${ref6(change.to)}'s checklist: ${outcome.added.problem}`));
9688
+ lines.push(warn(`Could not add its line to epic ${ref6(change.to)}'s checklist: ${outcome.added.problem}`));
10377
9689
  left.push(`add ${ref6(change.issue)}'s line to epic ${ref6(change.to)}'s checklist`);
10378
9690
  }
10379
9691
  if (outcome.removed?.status === "failed") {
10380
- lines2.push(warn(`Could not take its line off epic ${ref6(change.from)}'s checklist: ${outcome.removed.problem}`));
9692
+ lines.push(warn(`Could not take its line off epic ${ref6(change.from)}'s checklist: ${outcome.removed.problem}`));
10381
9693
  left.push(`take ${ref6(change.issue)}'s line off epic ${ref6(change.from)}'s checklist`);
10382
9694
  }
10383
9695
  if (outcome.commentProblem !== "") {
10384
- lines2.push(warn(`Could not comment on ${ref6(change.issue)}: ${outcome.commentProblem}`));
9696
+ lines.push(warn(`Could not comment on ${ref6(change.issue)}: ${outcome.commentProblem}`));
10385
9697
  left.push(`comment the move on ${ref6(change.issue)}`);
10386
9698
  }
10387
- return Object.freeze({ ...base, landed: true, lines: lines2, left });
9699
+ return Object.freeze({ ...base, landed: true, lines, left });
10388
9700
  } catch (error) {
10389
9701
  return Object.freeze({
10390
9702
  ...base,
@@ -10416,26 +9728,26 @@ async function applyUnblock(input, dependent) {
10416
9728
  const still = readUnblockStill(input.relations, input.issues, issue, waitsOn);
10417
9729
  const note = renderUnblockNote(input.day, input.epic, waitsOn, still.named);
10418
9730
  const edit = await editChecklist({ issue: issue.number, edit: (body) => appendNote(body, note), board: input.writes.bodies });
10419
- const lines2 = [];
9731
+ const lines = [];
10420
9732
  const left = [];
10421
9733
  if (edit.status === "failed") {
10422
- lines2.push(warn(`Could not add the note to ${ref6(issue.number)}'s body: ${edit.problem}`));
9734
+ lines.push(warn(`Could not add the note to ${ref6(issue.number)}'s body: ${edit.problem}`));
10423
9735
  left.push(`add the note to ${ref6(issue.number)}'s body: ${note}`);
10424
9736
  } else {
10425
9737
  const verb = waitsOn.length === 1 ? "blocks" : "block";
10426
- lines2.push(info(edit.status === "edited" ? `Unblocked ${ref6(issue.number)}: noted below its body that ${refs(waitsOn)} no longer ${verb} it.` : `Unblocked ${ref6(issue.number)}: its body carries the note already.`));
9738
+ lines.push(info(edit.status === "edited" ? `Unblocked ${ref6(issue.number)}: noted below its body that ${refs(waitsOn)} no longer ${verb} it.` : `Unblocked ${ref6(issue.number)}: its body carries the note already.`));
10427
9739
  }
10428
9740
  const label = input.relations.mode === "native" ? { line: info(keptLinksLine(issue.number, waitsOn)), left: null } : await unblockLabel(input, issue, still.maybeOpen);
10429
9741
  if (label.line !== null)
10430
- lines2.push(label.line);
9742
+ lines.push(label.line);
10431
9743
  if (label.left !== null)
10432
9744
  left.push(label.left);
10433
9745
  const problem = await commentOn(input.writes, issue.number, renderDependentComment("unblocked", input.epic, waitsOn));
10434
9746
  if (problem !== "") {
10435
- lines2.push(warn(`Could not comment on ${ref6(issue.number)}: ${problem}`));
9747
+ lines.push(warn(`Could not comment on ${ref6(issue.number)}: ${problem}`));
10436
9748
  left.push(`comment the unblock on ${ref6(issue.number)}`);
10437
9749
  }
10438
- return Object.freeze({ issue: issue.number, waitsOn, answer: { kind: "unblocked" }, landed: edit.status !== "failed", lines: lines2, left });
9750
+ return Object.freeze({ issue: issue.number, waitsOn, answer: { kind: "unblocked" }, landed: edit.status !== "failed", lines, left });
10439
9751
  }
10440
9752
  async function applyCancel(input, dependent) {
10441
9753
  const { issue, waitsOn } = dependent;
@@ -10576,9 +9888,9 @@ function cancelFailure(result2) {
10576
9888
  }
10577
9889
  async function runEpicCancel(context, seams) {
10578
9890
  const result2 = await cancelEpic(context, seams);
10579
- const failure3 = cancelFailure(result2);
10580
- if (failure3 !== null)
10581
- throw failure3;
9891
+ const failure = cancelFailure(result2);
9892
+ if (failure !== null)
9893
+ throw failure;
10582
9894
  if (context.outputMode === "json")
10583
9895
  context.output.result(result2);
10584
9896
  }
@@ -10619,11 +9931,11 @@ var cancel_default = createEpicCancelCommand();
10619
9931
 
10620
9932
  // src/commands/epic/close.ts
10621
9933
  import { randomUUID as randomUUID3 } from "crypto";
10622
- import { join as join24 } from "path";
9934
+ import { join as join22 } from "path";
10623
9935
 
10624
9936
  // src/board/epic-template.ts
10625
- import { existsSync as existsSync5, readFileSync as readFileSync9 } from "fs";
10626
- import { dirname as dirname3, join as join21 } from "path";
9937
+ import { existsSync as existsSync4, readFileSync as readFileSync9 } from "fs";
9938
+ import { dirname as dirname3, join as join19 } from "path";
10627
9939
  import { fileURLToPath as fileURLToPath2 } from "url";
10628
9940
  var MODULE_DIR2 = dirname3(fileURLToPath2(import.meta.url));
10629
9941
  var EPIC_TEMPLATE_FILE = "epic.md";
@@ -10632,11 +9944,11 @@ var CRITERIA_PLACEHOLDERS = Object.freeze([
10632
9944
  ]);
10633
9945
  var PLACEHOLDER_REASON = "the line is still the epic template's placeholder, so there is nothing to check";
10634
9946
  function epicTemplateSource(moduleDir = MODULE_DIR2) {
10635
- return join21(moduleDir, TEMPLATES_DIRNAME, EPIC_TEMPLATE_FILE);
9947
+ return join19(moduleDir, TEMPLATES_DIRNAME, EPIC_TEMPLATE_FILE);
10636
9948
  }
10637
9949
  function readEpicTemplate(moduleDir = MODULE_DIR2) {
10638
9950
  const source = epicTemplateSource(moduleDir);
10639
- if (!existsSync5(source)) {
9951
+ if (!existsSync4(source)) {
10640
9952
  throw new Error(`epic template: the epic issue template is missing: no file at ${source}`);
10641
9953
  }
10642
9954
  return readFileSync9(source, "utf8");
@@ -10730,8 +10042,8 @@ function renderEpicCost(cost, estimate) {
10730
10042
  }
10731
10043
 
10732
10044
  // src/epic/verify-plan.ts
10733
- import { existsSync as existsSync6, readFileSync as readFileSync10 } from "fs";
10734
- import { dirname as dirname4, join as join22 } from "path";
10045
+ import { existsSync as existsSync5, readFileSync as readFileSync10 } from "fs";
10046
+ import { dirname as dirname4, join as join20 } from "path";
10735
10047
  import { fileURLToPath as fileURLToPath3 } from "url";
10736
10048
  var MODULE_DIR3 = dirname4(fileURLToPath3(import.meta.url));
10737
10049
  var VERIFY_PROMPT_FILE = "epic-verify-prompt.md";
@@ -10777,8 +10089,8 @@ function splitCriteria(criteria) {
10777
10089
  }
10778
10090
  state.afterBlank = false;
10779
10091
  }
10780
- const numbered = gathered.map((lines2, index) => {
10781
- const text = lines2.join(`
10092
+ const numbered = gathered.map((lines, index) => {
10093
+ const text = lines.join(`
10782
10094
  `).trim();
10783
10095
  return Object.freeze({ number: index + 1, text, placeholder: CRITERIA_PLACEHOLDERS.includes(text) });
10784
10096
  });
@@ -10788,11 +10100,11 @@ function criteriaToAsk(criteria) {
10788
10100
  return Object.freeze(criteria.filter((criterion) => !criterion.placeholder));
10789
10101
  }
10790
10102
  function verifyPromptCandidates(moduleDir = MODULE_DIR3) {
10791
- return [join22(moduleDir, VERIFY_PROMPT_FILE), join22(dirname4(moduleDir), VERIFY_PROMPT_FILE)];
10103
+ return [join20(moduleDir, VERIFY_PROMPT_FILE), join20(dirname4(moduleDir), VERIFY_PROMPT_FILE)];
10792
10104
  }
10793
10105
  function readVerifyPrompt(moduleDir = MODULE_DIR3) {
10794
10106
  const candidates = verifyPromptCandidates(moduleDir);
10795
- const found = candidates.find((candidate) => existsSync6(candidate));
10107
+ const found = candidates.find((candidate) => existsSync5(candidate));
10796
10108
  if (found === undefined) {
10797
10109
  throw new Error(`epic verify prompt: the verification prompt is missing: no file at ${candidates.join(" or ")}`);
10798
10110
  }
@@ -10933,16 +10245,16 @@ function parseVerifyPlan(output, criteria) {
10933
10245
 
10934
10246
  // src/epic/verify-run.ts
10935
10247
  import { randomUUID as randomUUID2 } from "crypto";
10936
- import { join as join23 } from "path";
10248
+ import { join as join21 } from "path";
10937
10249
  var CHECK_TOOLS = ["Read", "Grep", "Glob", "Bash"];
10938
10250
  var DEFAULT_VERIFY_REMOTE = "origin";
10939
10251
  var CHECK_PROMPT_PREFIX = "# Epic acceptance check instructions";
10940
10252
  var VERDICT_BLOCK_FENCE = "rafa:verdict";
10941
10253
  var CHECK_RESULTS = ["pass", "fail"];
10942
- var WORKTREE_PREFIX2 = "epic-";
10254
+ var WORKTREE_PREFIX = "epic-";
10943
10255
  var VERDICT_KIND = VERDICT_BLOCK_FENCE.slice("rafa:".length);
10944
10256
  function verifyWorktreePath(home, epic) {
10945
- return join23(home, WORKTREES_SUBDIR, `${WORKTREE_PREFIX2}${String(epic)}`);
10257
+ return join21(home, WORKTREES_SUBDIR, `${WORKTREE_PREFIX}${String(epic)}`);
10946
10258
  }
10947
10259
  function fetchStep(remote) {
10948
10260
  return { id: "fetch", args: ["fetch", remote] };
@@ -10956,7 +10268,7 @@ function addStep(path, commit) {
10956
10268
  function removeStep(path) {
10957
10269
  return { id: "remove", args: ["worktree", "remove", "--force", path] };
10958
10270
  }
10959
- function runStep3(git, step) {
10271
+ function runStep(git, step) {
10960
10272
  const result2 = git(step.args);
10961
10273
  return {
10962
10274
  id: step.id,
@@ -11089,7 +10401,7 @@ async function runChecks(options, worktree, prompt) {
11089
10401
  }
11090
10402
  return Object.freeze(results);
11091
10403
  }
11092
- function notRun2(step, worktree) {
10404
+ function notRun(step, worktree) {
11093
10405
  return Object.freeze({ status: "not-run", step: reported(step), worktree });
11094
10406
  }
11095
10407
  async function runVerification(options) {
@@ -11098,22 +10410,22 @@ async function runVerification(options) {
11098
10410
  const remote = options.remote ?? DEFAULT_VERIFY_REMOTE;
11099
10411
  const base = options.base ?? DEFAULT_BASE_BRANCH;
11100
10412
  const worktree = verifyWorktreePath(options.home, options.epic);
11101
- const fetched = runStep3(options.git, fetchStep(remote));
10413
+ const fetched = runStep(options.git, fetchStep(remote));
11102
10414
  if (!fetched.ok)
11103
- return notRun2(fetched, worktree);
11104
- const resolved = runStep3(options.git, resolveStep(remote, base));
10415
+ return notRun(fetched, worktree);
10416
+ const resolved = runStep(options.git, resolveStep(remote, base));
11105
10417
  const commit = resolved.stdout.trim();
11106
10418
  if (!resolved.ok || commit === "")
11107
- return notRun2(resolved, worktree);
11108
- const added = runStep3(options.git, addStep(worktree, commit));
10419
+ return notRun(resolved, worktree);
10420
+ const added = runStep(options.git, addStep(worktree, commit));
11109
10421
  if (!added.ok)
11110
- return notRun2(added, worktree);
10422
+ return notRun(added, worktree);
11111
10423
  let results;
11112
10424
  let removal;
11113
10425
  try {
11114
10426
  results = await runChecks(options, worktree, { commit, ref: `${remote}/${base}` });
11115
10427
  } finally {
11116
- removal = reported(runStep3(options.git, removeStep(worktree)));
10428
+ removal = reported(runStep(options.git, removeStep(worktree)));
11117
10429
  }
11118
10430
  return Object.freeze({ status: "ran", commit, worktree, results, removal });
11119
10431
  }
@@ -11224,13 +10536,13 @@ async function planVerification(input) {
11224
10536
  return Object.freeze({ status: "planned", sessionId, verdicts: reading.verdicts, issues: reading.issues });
11225
10537
  }
11226
10538
  function closeTriageFile(root, epic) {
11227
- return join24(root, ".rafa", `epic-${String(epic)}-close`);
10539
+ return join22(root, ".rafa", `epic-${String(epic)}-close`);
11228
10540
  }
11229
10541
  function failedCheckArtifact(epic, criterion) {
11230
10542
  return `epic ${ref7(epic)} acceptance criterion: ${oneLine(criterion.text)}`;
11231
10543
  }
11232
- function failedCheckReport(epic, failure3, commit) {
11233
- const { criterion } = failure3.check;
10544
+ function failedCheckReport(epic, failure, commit) {
10545
+ const { criterion } = failure.check;
11234
10546
  const feedback = [
11235
10547
  `Epic ${ref7(epic)} was being closed through the closing gate, and this acceptance criterion's check failed.`,
11236
10548
  "",
@@ -11238,10 +10550,10 @@ function failedCheckReport(epic, failure3, commit) {
11238
10550
  criterion.text,
11239
10551
  "",
11240
10552
  "Check:",
11241
- failure3.check.check,
10553
+ failure.check.check,
11242
10554
  "",
11243
10555
  `Evidence, against commit ${commit}:`,
11244
- failure3.evidence
10556
+ failure.evidence
11245
10557
  ].join(`
11246
10558
  `);
11247
10559
  return Object.freeze({
@@ -11296,16 +10608,16 @@ async function fileFailures(run, epic, failures, commit) {
11296
10608
  warn2(run, `No tracker resolved, so no failed check is filed: ${messageOf(error)}`);
11297
10609
  tracker = unresolvedTracker(run.config.trackerDefault, messageOf(error));
11298
10610
  }
11299
- for (const failure3 of failures) {
11300
- const { criterion } = failure3.check;
10611
+ for (const failure of failures) {
10612
+ const { criterion } = failure.check;
11301
10613
  try {
11302
10614
  const result2 = await triageReport({
11303
10615
  repoRoot: run.root,
11304
10616
  trackerPath: closeTriageFile(run.root, epic),
11305
10617
  lineNum: 0,
11306
- dispatch: { sessionId: failure3.sessionId, planStub: null, taskLine: `rafa epic close ${String(epic)}` },
10618
+ dispatch: { sessionId: failure.sessionId, planStub: null, taskLine: `rafa epic close ${String(epic)}` },
11307
10619
  outcome: "failed",
11308
- report: failedCheckReport(epic, failure3, commit),
10620
+ report: failedCheckReport(epic, failure, commit),
11309
10621
  tracker,
11310
10622
  privateTracker: run.seams.privateTracker,
11311
10623
  secrets,
@@ -11821,13 +11133,13 @@ function createEpicPromoteCommand(seams = {}) {
11821
11133
  var promote_default = createEpicPromoteCommand();
11822
11134
 
11823
11135
  // src/commands/init.ts
11824
- import { existsSync as existsSync9, readFileSync as readFileSync14 } from "fs";
11136
+ import { existsSync as existsSync8, readFileSync as readFileSync14 } from "fs";
11825
11137
  import { homedir as homedir2 } from "os";
11826
- import { join as join28, relative as relative5, sep as sep4 } from "path";
11138
+ import { join as join26, relative as relative5, sep as sep3 } from "path";
11827
11139
 
11828
11140
  // src/agents/vendorable.ts
11829
11141
  import { readdirSync, readFileSync as readFileSync11 } from "fs";
11830
- import { isAbsolute as isAbsolute3, join as join25 } from "path";
11142
+ import { isAbsolute as isAbsolute3, join as join23 } from "path";
11831
11143
  function planFiles(dir) {
11832
11144
  let entries;
11833
11145
  try {
@@ -11849,7 +11161,7 @@ function rafaHolds(roster, name) {
11849
11161
  return item?.holders.some((holder) => holder.source === "rafa") === true;
11850
11162
  }
11851
11163
  function vendorableAgents(scan) {
11852
- const dir = isAbsolute3(scan.planDir) ? scan.planDir : join25(scan.repoRoot, scan.planDir);
11164
+ const dir = isAbsolute3(scan.planDir) ? scan.planDir : join23(scan.repoRoot, scan.planDir);
11853
11165
  const roots = {
11854
11166
  repoRoot: scan.repoRoot,
11855
11167
  home: scan.home,
@@ -11858,7 +11170,7 @@ function vendorableAgents(scan) {
11858
11170
  const roster = resolveAgentRoster(roots, scan.settings);
11859
11171
  const found = [];
11860
11172
  for (const file of planFiles(dir)) {
11861
- const path = join25(dir, file);
11173
+ const path = join23(dir, file);
11862
11174
  const markdown = readText2(path);
11863
11175
  if (markdown === null)
11864
11176
  continue;
@@ -11884,13 +11196,13 @@ function vendorableAgentWarnings(agents, root) {
11884
11196
 
11885
11197
  // src/project/gitignore.ts
11886
11198
  import { createHash } from "crypto";
11887
- import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync12, writeFileSync as writeFileSync3 } from "fs";
11888
- import { dirname as dirname5, isAbsolute as isAbsolute4, join as join26 } from "path";
11199
+ import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync12, writeFileSync as writeFileSync3 } from "fs";
11200
+ import { dirname as dirname5, isAbsolute as isAbsolute4, join as join24 } from "path";
11889
11201
  var GITIGNORE_FILE = ".gitignore";
11890
11202
  var DIGEST_NAME = "tracking.digest";
11891
11203
  var PRIVATE_TRIAGE = "triage/private";
11892
11204
  var CACHE_DIR = "cache";
11893
- var TRACKING_DIGEST_FILE = join26(SCOPE_DIR, DIGEST_NAME);
11205
+ var TRACKING_DIGEST_FILE = join24(SCOPE_DIR, DIGEST_NAME);
11894
11206
  var BLOCK_BEGIN = "# >>> rafa tracking: rafa rewrites the lines up to the end marker";
11895
11207
  var BLOCK_END = "# <<< rafa tracking";
11896
11208
  var SUB_PATHS = [
@@ -11924,8 +11236,8 @@ function lineBreakOf(text) {
11924
11236
  return /\r?\n/.exec(text)?.[0] ?? `
11925
11237
  `;
11926
11238
  }
11927
- function indicesOf(lines2, marker) {
11928
- return lines2.flatMap((line, index) => line.replace(/\r$/, "") === marker ? [index] : []);
11239
+ function indicesOf(lines, marker) {
11240
+ return lines.flatMap((line, index) => line.replace(/\r$/, "") === marker ? [index] : []);
11929
11241
  }
11930
11242
  function whereLines(indices) {
11931
11243
  if (indices.length === 0)
@@ -11933,9 +11245,9 @@ function whereLines(indices) {
11933
11245
  const numbers = indices.map((index) => String(index + 1));
11934
11246
  return indices.length === 1 ? `line ${numbers.join("")}` : `lines ${numbers.join(", ")}`;
11935
11247
  }
11936
- function blockSpan(lines2, file) {
11937
- const begins = indicesOf(lines2, BLOCK_BEGIN);
11938
- const ends = indicesOf(lines2, BLOCK_END);
11248
+ function blockSpan(lines, file) {
11249
+ const begins = indicesOf(lines, BLOCK_BEGIN);
11250
+ const ends = indicesOf(lines, BLOCK_END);
11939
11251
  if (begins.length === 0 && ends.length === 0)
11940
11252
  return null;
11941
11253
  const [begin] = begins;
@@ -11952,17 +11264,17 @@ function withTrackingBlock(text, flags, file = GITIGNORE_FILE) {
11952
11264
  const block = [BLOCK_BEGIN, ...trackingEntry(flags), BLOCK_END].map((line) => `${line}${eol}`).join("");
11953
11265
  if (source === "")
11954
11266
  return block;
11955
- const lines2 = source.split(`
11267
+ const lines = source.split(`
11956
11268
  `);
11957
- const span = blockSpan(lines2, file);
11269
+ const span = blockSpan(lines, file);
11958
11270
  if (span === null) {
11959
11271
  const separator = source.endsWith(`
11960
11272
  `) ? eol : `${eol}${eol}`;
11961
11273
  return `${source}${separator}${block}`;
11962
11274
  }
11963
- const before = lines2.slice(0, span.begin).map((line) => `${line}
11275
+ const before = lines.slice(0, span.begin).map((line) => `${line}
11964
11276
  `).join("");
11965
- const after = lines2.slice(span.end + 1).join(`
11277
+ const after = lines.slice(span.end + 1).join(`
11966
11278
  `);
11967
11279
  return `${before}${block}${after}`;
11968
11280
  }
@@ -11971,21 +11283,21 @@ function requireAbsolute(root) {
11971
11283
  throw new GitignoreError(`project root is ${describeValue(root)}, expected an absolute path`);
11972
11284
  }
11973
11285
  }
11974
- function attempt(file, failure3, act) {
11286
+ function attempt(file, failure, act) {
11975
11287
  try {
11976
11288
  return act();
11977
11289
  } catch (error) {
11978
- throw new GitignoreError(`${file} ${failure3} (${messageOf(error)})`, { cause: error });
11290
+ throw new GitignoreError(`${file} ${failure} (${messageOf(error)})`, { cause: error });
11979
11291
  }
11980
11292
  }
11981
11293
  function readIfPresent2(file) {
11982
- if (!existsSync7(file))
11294
+ if (!existsSync6(file))
11983
11295
  return null;
11984
11296
  return attempt(file, "cannot be read", () => readFileSync12(file, "utf8"));
11985
11297
  }
11986
11298
  function writeTrackingGitignore(root, flags) {
11987
11299
  requireAbsolute(root);
11988
- const file = join26(root, GITIGNORE_FILE);
11300
+ const file = join24(root, GITIGNORE_FILE);
11989
11301
  const text = readIfPresent2(file);
11990
11302
  const next = withTrackingBlock(text, flags, file);
11991
11303
  if (next === text)
@@ -12007,7 +11319,7 @@ function trackingDigest(flags) {
12007
11319
  }
12008
11320
  function exposureNotice(root) {
12009
11321
  return [
12010
- `tracking.all is true, so ${join26(root, GITIGNORE_FILE)} no longer ignores ${SCOPE_DIR}/` + " and a commit can carry what it holds, including:",
11322
+ `tracking.all is true, so ${join24(root, GITIGNORE_FILE)} no longer ignores ${SCOPE_DIR}/` + " and a commit can carry what it holds, including:",
12011
11323
  " - unpatched issues described in specs and plans",
12012
11324
  " - effort rows with model names and token counts",
12013
11325
  " - instincts that may quote error output",
@@ -12020,7 +11332,7 @@ function printWarning(message) {
12020
11332
  }
12021
11333
  function noticeTrackingChange(root, flags, print2 = printWarning) {
12022
11334
  requireAbsolute(root);
12023
- const digestFile = join26(root, TRACKING_DIGEST_FILE);
11335
+ const digestFile = join24(root, TRACKING_DIGEST_FILE);
12024
11336
  const recorded = `${trackingDigest(flags)}
12025
11337
  `;
12026
11338
  if (readIfPresent2(digestFile) === recorded) {
@@ -12119,8 +11431,8 @@ async function promptForRoot(found, prompter, seams) {
12119
11431
  }
12120
11432
 
12121
11433
  // src/project/scaffold.ts
12122
- import { existsSync as existsSync8, lstatSync, mkdirSync as mkdirSync4, statSync as statSync7, writeFileSync as writeFileSync5 } from "fs";
12123
- import { join as join27 } from "path";
11434
+ import { existsSync as existsSync7, lstatSync, mkdirSync as mkdirSync4, statSync as statSync6, writeFileSync as writeFileSync5 } from "fs";
11435
+ import { join as join25 } from "path";
12124
11436
 
12125
11437
  // src/commands/init-release.ts
12126
11438
  import { writeFileSync as writeFileSync4 } from "fs";
@@ -12150,9 +11462,9 @@ var COMMENTED_RELEASE_KEY = /^#\s*release:\s*$/u;
12150
11462
  var COMMENTED_ENABLED_KEY = /^#\s+enabled:/u;
12151
11463
  var ENABLED_KEY = /^\s+enabled\s*:/u;
12152
11464
  var SKIPPABLE = /^(\s*#|\s*$)/u;
12153
- function enabledLineIn(lines2, open) {
12154
- for (let index = open + 1;index < lines2.length; index += 1) {
12155
- const line = lines2[index] ?? "";
11465
+ function enabledLineIn(lines, open) {
11466
+ for (let index = open + 1;index < lines.length; index += 1) {
11467
+ const line = lines[index] ?? "";
12156
11468
  if (SKIPPABLE.test(line))
12157
11469
  continue;
12158
11470
  if (ENABLED_KEY.test(line))
@@ -12162,8 +11474,8 @@ function enabledLineIn(lines2, open) {
12162
11474
  }
12163
11475
  return null;
12164
11476
  }
12165
- function appended(lines2, block) {
12166
- const body = lines2.at(-1) === "" ? lines2.slice(0, -1) : [...lines2];
11477
+ function appended(lines, block) {
11478
+ const body = lines.at(-1) === "" ? lines.slice(0, -1) : [...lines];
12167
11479
  return [...body, ...block, ""];
12168
11480
  }
12169
11481
  function namesRelease(text) {
@@ -12177,28 +11489,28 @@ function namesRelease(text) {
12177
11489
  }
12178
11490
  function withReleaseEnabled(text, enabled) {
12179
11491
  const setting = ` enabled: ${String(enabled)}`;
12180
- const lines2 = text.split(`
11492
+ const lines = text.split(`
12181
11493
  `);
12182
- const open = lines2.findIndex((line) => RELEASE_KEY.test(line));
11494
+ const open = lines.findIndex((line) => RELEASE_KEY.test(line));
12183
11495
  if (open >= 0) {
12184
- const at = enabledLineIn(lines2, open);
11496
+ const at = enabledLineIn(lines, open);
12185
11497
  if (at === null) {
12186
- return [...lines2.slice(0, open + 1), setting, ...lines2.slice(open + 1)].join(`
11498
+ return [...lines.slice(0, open + 1), setting, ...lines.slice(open + 1)].join(`
12187
11499
  `);
12188
11500
  }
12189
- return [...lines2.slice(0, at), setting, ...lines2.slice(at + 1)].join(`
11501
+ return [...lines.slice(0, at), setting, ...lines.slice(at + 1)].join(`
12190
11502
  `);
12191
11503
  }
12192
- const commented = lines2.findIndex((line) => COMMENTED_RELEASE_KEY.test(line));
11504
+ const commented = lines.findIndex((line) => COMMENTED_RELEASE_KEY.test(line));
12193
11505
  if (commented >= 0) {
12194
- const next = lines2[commented + 1] ?? "";
11506
+ const next = lines[commented + 1] ?? "";
12195
11507
  const after = COMMENTED_ENABLED_KEY.test(next) ? commented + 2 : commented + 1;
12196
- return [...lines2.slice(0, commented), "release:", setting, ...lines2.slice(after)].join(`
11508
+ return [...lines.slice(0, commented), "release:", setting, ...lines.slice(after)].join(`
12197
11509
  `);
12198
11510
  }
12199
11511
  if (namesRelease(text))
12200
11512
  return null;
12201
- return appended(lines2, ["release:", setting]).join(`
11513
+ return appended(lines, ["release:", setting]).join(`
12202
11514
  `);
12203
11515
  }
12204
11516
 
@@ -12313,11 +11625,18 @@ function renderReleaseStep(result2) {
12313
11625
  // src/project/scaffold.ts
12314
11626
  var PROJECT_TREE = Object.freeze(["specs", "plans", "runs", "effort", "instincts"]);
12315
11627
  var USER_TREE = Object.freeze(["instincts"]);
11628
+ function globsLine(patterns) {
11629
+ return `[${patterns.map((pattern) => JSON.stringify(pattern)).join(", ")}]`;
11630
+ }
12316
11631
  var CONFIG_SETTINGS_LINES = Object.freeze([
12317
11632
  `# store: ${CONFIG_DEFAULTS.store} # sqlite | ndjson`,
12318
11633
  "# effort:",
12319
11634
  `# busyTimeoutMs: ${String(CONFIG_DEFAULTS.effortBusyTimeoutMs)} # 1..60000 ms; how long a store open waits for another process's lock`,
12320
11635
  `# sync: ${CONFIG_DEFAULTS.effortSync} # local | file | git | service | p2p; how the store travels between devices`,
11636
+ "# hub:",
11637
+ "# url: # the hub effort.sync: service reaches, required with it",
11638
+ "# tokenSecret: # the secret store name the hub token is kept under, never the token",
11639
+ `# timeout: ${CONFIG_DEFAULTS.hubTimeout} # 1s..30s; how long one request to the hub may take`,
12321
11640
  "# plan:",
12322
11641
  `# inject: ${CONFIG_DEFAULTS.inject} # full | stage | task`,
12323
11642
  `# dir: ${CONFIG_DEFAULTS.planDir}`,
@@ -12347,6 +11666,8 @@ var CONFIG_SETTINGS_LINES = Object.freeze([
12347
11666
  "# loop:",
12348
11667
  `# settingSources: ${CONFIG_DEFAULTS.settingSources.join(",")} # a comma-separated subset of user, project, local`,
12349
11668
  `# worktreeDir: ${CONFIG_DEFAULTS.loopWorktreeDir} # where a loop adds its worktrees, read from the project root`,
11669
+ "# wrapUp:",
11670
+ `# retries: ${String(CONFIG_DEFAULTS.loopWrapUpRetries)} # 1..3 | false, wrap-up sessions rerun before rafa opens a missing PR itself`,
12350
11671
  "# pr:",
12351
11672
  "# provider: # gh | none; unset reads it off the origin remote",
12352
11673
  `# mergeMethod: ${CONFIG_DEFAULTS.prMergeMethod} # squash | merge | rebase`,
@@ -12386,7 +11707,10 @@ var CONFIG_SETTINGS_LINES = Object.freeze([
12386
11707
  ...DEFAULT_ROUTES.map(([shape, agent]) => `# ${shape}: ${agent}`),
12387
11708
  "# task:",
12388
11709
  `# skills: ${CONFIG_DEFAULTS.taskSkills} # planner | tag | none, the resolver that picks a task's skills`,
12389
- `# lessons: ${CONFIG_DEFAULTS.taskLessons} # on | off, whether blessed lessons join a task's prompt`
11710
+ `# lessons: ${CONFIG_DEFAULTS.taskLessons} # on | off, whether blessed lessons join a task's prompt`,
11711
+ "# tests:",
11712
+ `# fullSuiteTriggers: ${globsLine(CONFIG_DEFAULTS.testsFullSuiteTriggers)} # a task whose diff matches one runs the full suite`,
11713
+ `# integration: ${globsLine(CONFIG_DEFAULTS.testsIntegration)} # test files every stage step runs`
12390
11714
  ]);
12391
11715
  var VERSION_LINE = `version: ${String(CONFIG_DEFAULTS.version)}`;
12392
11716
  var PROJECT_HEADER = Object.freeze([
@@ -12424,18 +11748,18 @@ class ScaffoldError extends Error {
12424
11748
  }
12425
11749
  function layoutOf2(base, tree) {
12426
11750
  const scope = scopeAt(base);
12427
- return { dirs: [scope.dir, ...tree.map((name) => join27(scope.dir, name))], configFile: configFilePath(base) };
11751
+ return { dirs: [scope.dir, ...tree.map((name) => join25(scope.dir, name))], configFile: configFilePath(base) };
12428
11752
  }
12429
11753
  function occupied(path) {
12430
11754
  return lstatSync(path, { throwIfNoEntry: false }) !== undefined;
12431
11755
  }
12432
11756
  function isDirectory2(path) {
12433
- return statSync7(path, { throwIfNoEntry: false })?.isDirectory() === true;
11757
+ return statSync6(path, { throwIfNoEntry: false })?.isDirectory() === true;
12434
11758
  }
12435
11759
  function directoryConflict(dir) {
12436
11760
  if (!occupied(dir) || isDirectory2(dir))
12437
11761
  return null;
12438
- return existsSync8(dir) ? `${dir} is not a directory` : `${dir} is a link to nothing`;
11762
+ return existsSync7(dir) ? `${dir} is not a directory` : `${dir} is a link to nothing`;
12439
11763
  }
12440
11764
  function layoutConflicts(layout) {
12441
11765
  const [scopeDir, ...tree] = layout.dirs;
@@ -12443,21 +11767,21 @@ function layoutConflicts(layout) {
12443
11767
  if (opening !== null)
12444
11768
  return [opening];
12445
11769
  const dirs = tree.flatMap((dir) => directoryConflict(dir) ?? []);
12446
- const file = occupied(layout.configFile) && !existsSync8(layout.configFile) ? [`${layout.configFile} is a link to nothing`] : [];
11770
+ const file = occupied(layout.configFile) && !existsSync7(layout.configFile) ? [`${layout.configFile} is a link to nothing`] : [];
12447
11771
  return [...dirs, ...file];
12448
11772
  }
12449
11773
  function scaffoldConflicts(root, home) {
12450
11774
  return [...layoutConflicts(layoutOf2(root, PROJECT_TREE)), ...layoutConflicts(layoutOf2(home, USER_TREE))];
12451
11775
  }
12452
- function attempt2(path, failure3, act) {
11776
+ function attempt2(path, failure, act) {
12453
11777
  try {
12454
11778
  act();
12455
11779
  } catch (error) {
12456
- throw new ScaffoldError(`${path} ${failure3} (${messageOf(error)})`, { cause: error });
11780
+ throw new ScaffoldError(`${path} ${failure} (${messageOf(error)})`, { cause: error });
12457
11781
  }
12458
11782
  }
12459
11783
  function ensureDirectory(path) {
12460
- if (existsSync8(path))
11784
+ if (existsSync7(path))
12461
11785
  return { path, kind: "directory", change: "unchanged" };
12462
11786
  attempt2(path, "cannot be created", () => {
12463
11787
  mkdirSync4(path, { recursive: true });
@@ -12465,7 +11789,7 @@ function ensureDirectory(path) {
12465
11789
  return { path, kind: "directory", change: "created" };
12466
11790
  }
12467
11791
  function ensureFile(path, text) {
12468
- if (existsSync8(path))
11792
+ if (existsSync7(path))
12469
11793
  return { path, kind: "file", change: "unchanged" };
12470
11794
  attempt2(path, "cannot be written", () => {
12471
11795
  writeFileSync5(path, text, { encoding: "utf8", flag: "wx" });
@@ -12504,8 +11828,8 @@ var SOURCE_PHRASES = {
12504
11828
  "root-flag": "named by --root",
12505
11829
  typed: "typed at the prompt"
12506
11830
  };
12507
- function refusal9(lines2) {
12508
- return new CommandExit(1, [...lines2, NOTHING_WRITTEN2].join(`
11831
+ function refusal9(lines) {
11832
+ return new CommandExit(1, [...lines, NOTHING_WRITTEN2].join(`
12509
11833
  `));
12510
11834
  }
12511
11835
  function expectNoArgument3(args) {
@@ -12601,9 +11925,9 @@ function resolvedConfig2(root, home, warn3) {
12601
11925
  }
12602
11926
  }
12603
11927
  function checkGitignore(root, flags) {
12604
- const file = join28(root, GITIGNORE_FILE);
11928
+ const file = join26(root, GITIGNORE_FILE);
12605
11929
  try {
12606
- const text = existsSync9(file) ? readFileSync14(file, "utf8") : null;
11930
+ const text = existsSync8(file) ? readFileSync14(file, "utf8") : null;
12607
11931
  withTrackingBlock(text, flags, file);
12608
11932
  } catch (error) {
12609
11933
  const message = error instanceof GitignoreError ? error.message : `rafa init: ${file} cannot be read (${messageOf(error)})`;
@@ -12635,8 +11959,8 @@ function initialise(root, start2, home, context, entry) {
12635
11959
  const resolved = resolvedConfig2(root.path, home, warn3);
12636
11960
  const config = resolved.config;
12637
11961
  checkGitignore(root.path, config);
12638
- const configExisted = existsSync9(configFilePath(root.path));
12639
- const digestExisted = existsSync9(join28(root.path, TRACKING_DIGEST_FILE));
11962
+ const configExisted = existsSync8(configFilePath(root.path));
11963
+ const digestExisted = existsSync8(join26(root.path, TRACKING_DIGEST_FILE));
12640
11964
  const project = writeProjectScope(root.path);
12641
11965
  const tracking = applyTracking(root.path, config, warn3);
12642
11966
  const writes = [...project, ...trackingWrites(tracking, digestExisted), ...writeUserScope(home)];
@@ -12689,7 +12013,7 @@ async function releaseStep(scopes, wanted, yes, seams) {
12689
12013
  });
12690
12014
  }
12691
12015
  function shownPath2(write, root) {
12692
- const shown = write.path.startsWith(`${root}${sep4}`) ? relative5(root, write.path) : write.path;
12016
+ const shown = write.path.startsWith(`${root}${sep3}`) ? relative5(root, write.path) : write.path;
12693
12017
  return write.kind === "directory" ? `${shown}/` : shown;
12694
12018
  }
12695
12019
  function renderInit(result2) {
@@ -12921,15 +12245,15 @@ async function runCheckCommand(context, spec, seams) {
12921
12245
  fix
12922
12246
  }, spec.usage);
12923
12247
  const result2 = checkCommandResult(report2, projectRoot2);
12924
- const lines2 = renderCheck(result2);
12248
+ const lines = renderCheck(result2);
12925
12249
  if (report2.exitCode > 0)
12926
- throw new CommandExit(report2.exitCode, lines2.join(`
12250
+ throw new CommandExit(report2.exitCode, lines.join(`
12927
12251
  `));
12928
12252
  if (context.outputMode === "json") {
12929
12253
  context.output.result(result2);
12930
12254
  return;
12931
12255
  }
12932
- for (const line of lines2)
12256
+ for (const line of lines)
12933
12257
  context.output.info(line);
12934
12258
  }
12935
12259
  function checkCommandRun(spec, seams) {
@@ -13290,12 +12614,12 @@ function everyRecord(context) {
13290
12614
  clean: records.filter((entry) => entry.instinct !== null).length
13291
12615
  };
13292
12616
  }
13293
- function answer(context, result2, lines2) {
12617
+ function answer(context, result2, lines) {
13294
12618
  if (context.outputMode === "json") {
13295
12619
  context.output.result(result2);
13296
12620
  return;
13297
12621
  }
13298
- for (const line of lines2())
12622
+ for (const line of lines())
13299
12623
  context.output.info(line);
13300
12624
  }
13301
12625
  async function runList4(context, seams) {
@@ -13497,8 +12821,8 @@ function createInstinctShowCommand() {
13497
12821
  var show_default3 = createInstinctShowCommand();
13498
12822
 
13499
12823
  // src/commands/issue/check.ts
13500
- import { readdirSync as readdirSync2, readFileSync as readFileSync15, statSync as statSync8 } from "fs";
13501
- import { join as join29, resolve as resolve9 } from "path";
12824
+ import { readdirSync as readdirSync2, readFileSync as readFileSync15, statSync as statSync7 } from "fs";
12825
+ import { join as join27, resolve as resolve9 } from "path";
13502
12826
  var CHECK_USAGE = "rafa issue check <n> [--stamp]";
13503
12827
  var STAMP_FLAG = "stamp";
13504
12828
  var ISSUE_NUMBER7 = /^[1-9]\d*$/u;
@@ -13530,7 +12854,7 @@ function copyNames(dir, issue) {
13530
12854
  }
13531
12855
  const prefix = `${boardId(issue)}-`;
13532
12856
  const notes = notesFileName(issue);
13533
- return names.filter((name) => name.startsWith(prefix) && name.endsWith(SPEC_EXTENSION) && name !== notes).filter((name) => name.length > prefix.length + SPEC_EXTENSION.length).filter((name) => statSync8(join29(dir, name)).isFile()).sort();
12857
+ return names.filter((name) => name.startsWith(prefix) && name.endsWith(SPEC_EXTENSION) && name !== notes).filter((name) => name.length > prefix.length + SPEC_EXTENSION.length).filter((name) => statSync7(join27(dir, name)).isFile()).sort();
13534
12858
  }
13535
12859
  function findSavedCopy(root, specsDir, issue) {
13536
12860
  const names = copyNames(resolve9(root, specsDir), issue);
@@ -13540,11 +12864,11 @@ function findSavedCopy(root, specsDir, issue) {
13540
12864
  if (names.length > 1) {
13541
12865
  throw new CommandExit(1, [
13542
12866
  `\u274C Issue #${String(issue)} has ${String(names.length)} saved copies under ${specsDir}; remove the stale one:`,
13543
- ...names.map((name) => ` ${join29(specsDir, name)}`)
12867
+ ...names.map((name) => ` ${join27(specsDir, name)}`)
13544
12868
  ].join(`
13545
12869
  `));
13546
12870
  }
13547
- const path = join29(specsDir, only);
12871
+ const path = join27(specsDir, only);
13548
12872
  return { path, absolute: resolve9(root, path) };
13549
12873
  }
13550
12874
  function liveText(live) {
@@ -13562,7 +12886,7 @@ function checkedRef(row2, stamp) {
13562
12886
  unblock: row2.unblock
13563
12887
  });
13564
12888
  }
13565
- function rowSuffix2(ref8) {
12889
+ function rowSuffix(ref8) {
13566
12890
  const noun = ref8.changedHeadings.length === 1 ? "heading" : "headings";
13567
12891
  const headings = ref8.changedHeadings.length === 0 ? "" : `: ${noun} ${ref8.changedHeadings.map((heading) => `"${heading}"`).join(", ")} changed`;
13568
12892
  const after = ref8.unblock === null ? "" : ` \u2014 ${ref8.unblock}`;
@@ -13570,7 +12894,7 @@ function rowSuffix2(ref8) {
13570
12894
  return `${headings} (line ${String(ref8.line)})${after}${unknown}`;
13571
12895
  }
13572
12896
  function checkedRefLine(ref8) {
13573
- return `${ref8.state} ${ref8.kind} ${ref8.text}${rowSuffix2(ref8)}`;
12897
+ return `${ref8.state} ${ref8.kind} ${ref8.text}${rowSuffix(ref8)}`;
13574
12898
  }
13575
12899
  function countLine(references) {
13576
12900
  const noun = references.length === 1 ? "reference" : "references";
@@ -13961,7 +13285,7 @@ function renderSessionList(root, list) {
13961
13285
  return [
13962
13286
  "Running sessions:",
13963
13287
  ...list.sessions.map(({ session, tasks }) => {
13964
- const counts = tasks === null ? "no plan or tracker to count" : formatCounts(tasks);
13288
+ const counts = tasks === null ? `no plan or tracker to count ${phaseNote(session)}` : phasedCounts(tasks, session);
13965
13289
  return ` ${sessionLine(session)}; ${counts}; ${checkoutColumn(root, session)}`;
13966
13290
  })
13967
13291
  ];
@@ -13973,6 +13297,7 @@ async function runList5(context, seams) {
13973
13297
  const checklist = readSessionChecklist(root, session);
13974
13298
  return {
13975
13299
  session,
13300
+ phase: sessionPhase(session),
13976
13301
  tasks: checklist === null ? null : countTasks(checklist.tasks)
13977
13302
  };
13978
13303
  });
@@ -13991,7 +13316,7 @@ function createLoopListCommand(seams = {}) {
13991
13316
  subject: "loop",
13992
13317
  action: "list",
13993
13318
  summary: "list the running sessions, with the tasks done in each plan",
13994
- description: "Lists every session record under `.rafa/runs/` that reads `running` or `paused`, oldest" + " first: its id, plan, branch, state, pid and start, the tasks of its plan done, blocked and" + " open, and the worktree it runs in, or the main checkout. A record whose pid is gone reads `stopped` and is not listed. Until phase 6 a plan runs in" + " one session at a time, so each row is a plan running. With `--output=json` the sessions are the" + " data of the terminal result event.",
13319
+ description: "Lists every session record under `.rafa/runs/` that reads `running` or `paused`, oldest" + " first: its id, plan, branch, state, pid and start, the tasks of its plan done, blocked and" + " open with the phase its run is in beside them, and the worktree it runs in, or the main checkout. A record whose pid is gone reads `stopped` and is not listed. Until phase 6 a plan runs in" + " one session at a time, so each row is a plan running. With `--output=json` the sessions are the" + " data of the terminal result event.",
13995
13320
  args: [],
13996
13321
  flags: [],
13997
13322
  examples: [
@@ -14205,10 +13530,10 @@ var wrapped = wrapPhaseZeroCommand({
14205
13530
  }
14206
13531
  ],
14207
13532
  aliases: ["start"],
14208
- outputs: ["text", "json"],
13533
+ outputs: ["text", "json", "events"],
14209
13534
  spends: {
14210
13535
  when: "always",
14211
- what: "one session per task, one for the wrap-up, and repair sessions while CI is red"
13536
+ 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"
14212
13537
  }
14213
13538
  }, start);
14214
13539
  var loopStart = Object.freeze({
@@ -14250,7 +13575,7 @@ function renderStatus(status) {
14250
13575
  const pending = session.state === "paused" && task !== null ? " (the run holds once it ends)" : "";
14251
13576
  return [
14252
13577
  `Session ${sessionLine(session)}`,
14253
- tasks === null ? ` Tasks: neither \`${session.plan}\` nor its tracker is there to count` : ` Tasks: ${formatCounts(tasks)}`,
13578
+ tasks === null ? ` Tasks: neither \`${session.plan}\` nor its tracker is there to count ${phaseNote(session)}` : ` Tasks: ${phasedCounts(tasks, session)}`,
14254
13579
  ...blocked.flatMap(blockedLines),
14255
13580
  ...task === null ? [] : [` Task: line ${task.line}, ${task.text}${pending}`],
14256
13581
  ...eta === null ? [] : [` ${etaLine(eta)}`]
@@ -14271,6 +13596,7 @@ async function runStatus(context, seams) {
14271
13596
  }
14272
13597
  const status = {
14273
13598
  session: record,
13599
+ phase: sessionPhase(record),
14274
13600
  checklist: checklist?.file ?? null,
14275
13601
  tasks,
14276
13602
  blocked: blockedTasks(checklist),
@@ -14290,13 +13616,13 @@ function createLoopStatusCommand(seams = {}) {
14290
13616
  subject: "loop",
14291
13617
  action: "status",
14292
13618
  summary: "show a session: its state, tasks done over total and a rough ETA",
14293
- description: "Reads a session record and its plan's tracker: the state, the pid, the start, the task" + " it names, and the tasks done, blocked and open over the whole plan. A running or paused session" + " also gets a rough ETA from the effort store: the time from its start to the last task it" + " finished, per task finished, times the open and blocked tasks left, and none before its first" + " task finishes. Every blocked task of the checklist is shown with the blocker comment its line" + " trails, which is what the run wrote when that task ended blocked. Without `--session-id` it shows the session running on the branch checked out at" + " the project root, or the newest one that ran there. With `--output=json` the record, the counts" + " and the ETA are the data of the terminal result event.",
13619
+ description: "Reads a session record and its plan's tracker: the state, the pid, the start, the task" + " it names, the tasks done, blocked and open over the whole plan, and beside them the phase the" + " run is in: task, wrap-up, pull-request, ci or repair. A running or paused session" + " also gets a rough ETA from the effort store: the time from its start to the last task it" + " finished, per task finished, times the open and blocked tasks left, and none before its first" + " task finishes. Every blocked task of the checklist is shown with the blocker comment its line" + " trails, which is what the run wrote when that task ended blocked. Without `--session-id` it shows the session running on the branch checked out at" + " the project root, or the newest one that ran there. With `--output=json` the record, the counts" + " and the ETA are the data of the terminal result event.",
14294
13620
  args: [],
14295
13621
  flags: [sessionIdFlag("to show")],
14296
13622
  examples: [
14297
13623
  {
14298
13624
  cmd: "rafa loop status",
14299
- note: "Shows the session on the branch checked out here: its state, tasks done over total and ETA."
13625
+ note: "Shows the session on the branch checked out here: its state, tasks done over total, phase and ETA."
14300
13626
  },
14301
13627
  {
14302
13628
  cmd: "rafa loop status --session-id=9185b41c-65f7-4dd6-a0c1-6494c4028f0f --output=json",
@@ -14686,8 +14012,8 @@ function dryRunOf(flag, ceiling, hasTerminal) {
14686
14012
 
14687
14013
  // src/next/sync.ts
14688
14014
  var INDENT2 = " ";
14689
- function refuse6(lines2) {
14690
- return Object.freeze({ kind: "refused", message: lines2.join(`
14015
+ function refuse6(lines) {
14016
+ return Object.freeze({ kind: "refused", message: lines.join(`
14691
14017
  `) });
14692
14018
  }
14693
14019
  function quotedLines(said) {
@@ -14868,6 +14194,7 @@ async function runNext(context, seams) {
14868
14194
  ...roadmap ? { roadmap } : {},
14869
14195
  ...relations === undefined ? {} : { relations }
14870
14196
  });
14197
+ await (seams.pull ?? pullBeforeRead)({ roots: sources.roots, warn: (line) => context.output.warn(line) });
14871
14198
  const openPrompter = seams.openPrompter ?? (() => createLinePrompter(process.stdin, process.stderr));
14872
14199
  const prompter = lazyPrompter(openPrompter);
14873
14200
  const dry = watchDryEpic();
@@ -15187,7 +14514,7 @@ var risk_default = createPlanRiskCommand();
15187
14514
 
15188
14515
  // src/commands/plan/show.ts
15189
14516
  import { readFileSync as readFileSync17 } from "fs";
15190
- import { join as join30 } from "path";
14517
+ import { join as join28 } from "path";
15191
14518
  var USAGE33 = "rafa plan show <stub> [--tracker]";
15192
14519
  var COMMAND7 = "rafa plan show";
15193
14520
  function readTrackerFlag(value) {
@@ -15205,9 +14532,9 @@ function showPlan(plans, stub, tracker) {
15205
14532
  throw new CommandExit(1, `\u274C "${stub}" is no plan stub: a stub is letters, digits, ".", "_" and "-"`);
15206
14533
  }
15207
14534
  const name = planFileName(stub, tracker);
15208
- const file = join30(plans.path, name);
14535
+ const file = join28(plans.path, name);
15209
14536
  if (!isFile(file))
15210
- throw new CommandExit(1, missingText(stub, tracker, join30(plans.label, name)));
14537
+ throw new CommandExit(1, missingText(stub, tracker, join28(plans.label, name)));
15211
14538
  const model = parsePlan(readFileSync17(file, "utf8"));
15212
14539
  const { header } = model;
15213
14540
  return {
@@ -15231,24 +14558,24 @@ function taskLine(task) {
15231
14558
  return ` ${checkbox(task.status)} ${task.text}`;
15232
14559
  }
15233
14560
  function renderShownPlan(shown, dirLabel) {
15234
- const relative7 = join30(dirLabel, planFileName(shown.stub, shown.tracker));
14561
+ const relative7 = join28(dirLabel, planFileName(shown.stub, shown.tracker));
15235
14562
  const fileLabel = shown.tracker ? "Tracker" : "File";
15236
- const lines2 = [`Plan: ${shown.stub}`, `${fileLabel}: ${relative7}`];
14563
+ const lines = [`Plan: ${shown.stub}`, `${fileLabel}: ${relative7}`];
15237
14564
  if (shown.header.issue !== null)
15238
- lines2.push(`Issue: ${shown.header.issue}`);
14565
+ lines.push(`Issue: ${shown.header.issue}`);
15239
14566
  if (shown.header.spec !== null)
15240
- lines2.push(`Spec: ${shown.header.spec}`);
15241
- lines2.push(`Tasks: ${formatCounts(shown.counts)}`);
14567
+ lines.push(`Spec: ${shown.header.spec}`);
14568
+ lines.push(`Tasks: ${formatCounts(shown.counts)}`);
15242
14569
  const unstaged = shown.tasks.filter((task) => task.stage === null);
15243
14570
  if (unstaged.length > 0)
15244
- lines2.push("", ...unstaged.map(taskLine));
14571
+ lines.push("", ...unstaged.map(taskLine));
15245
14572
  for (const [index, stage] of shown.stages.entries()) {
15246
- lines2.push("", `Stage: ${stage.name}`, ...shown.tasks.filter((task) => task.stage === index).map(taskLine));
14573
+ lines.push("", `Stage: ${stage.name}`, ...shown.tasks.filter((task) => task.stage === index).map(taskLine));
15247
14574
  }
15248
14575
  if (shown.issues.length > 0) {
15249
- lines2.push("", "Issues:", ...shown.issues.map((issue) => ` ${issueLine(relative7, issue)}`));
14576
+ lines.push("", "Issues:", ...shown.issues.map((issue) => ` ${issueLine(relative7, issue)}`));
15250
14577
  }
15251
- return lines2;
14578
+ return lines;
15252
14579
  }
15253
14580
  var planShowCommand = {
15254
14581
  name: "plan show",
@@ -15820,19 +15147,19 @@ var YES_ANSWERS2 = ["y", "yes"];
15820
15147
  function answerOf(reading) {
15821
15148
  return reading.ok ? reading.verdict.answer : "unread";
15822
15149
  }
15823
- function guardRefusal(head, lines2) {
15824
- return new CommandExit(1, [`\u274C ${head}`, ...lines2.map((line) => ` ${line}`)].join(`
15150
+ function guardRefusal(head, lines) {
15151
+ return new CommandExit(1, [`\u274C ${head}`, ...lines.map((line) => ` ${line}`)].join(`
15825
15152
  `));
15826
15153
  }
15827
15154
  function refusalHead(number, answer2) {
15828
15155
  return answer2 === "collision" ? `rafa pr merge refuses #${number}: its release guard reads collision, which only dangerous.acceptVersionCollision lets through.` : `rafa pr merge refuses #${number}: its release guard reads ${answer2}, and pr.versionCollision is refuse.`;
15829
15156
  }
15830
- async function askGuard(step, answer2, lines2) {
15157
+ async function askGuard(step, answer2, lines) {
15831
15158
  const { number } = step.detail;
15832
15159
  if (!step.isTerminal()) {
15833
- throw guardRefusal(`rafa pr merge asks before merging #${number} with its release guard reading ${answer2} (pr.versionCollision is ask),` + " and standard input is no terminal; --yes does not answer it.", lines2);
15160
+ throw guardRefusal(`rafa pr merge asks before merging #${number} with its release guard reading ${answer2} (pr.versionCollision is ask),` + " and standard input is no terminal; --yes does not answer it.", lines);
15834
15161
  }
15835
- for (const line of lines2)
15162
+ for (const line of lines)
15836
15163
  step.warn(line);
15837
15164
  const prompter = step.openPrompter();
15838
15165
  try {
@@ -15842,11 +15169,11 @@ async function askGuard(step, answer2, lines2) {
15842
15169
  prompter.close();
15843
15170
  }
15844
15171
  }
15845
- function printLines(step, reaction, lines2) {
15172
+ function printLines(step, reaction, lines) {
15846
15173
  if (reaction === "silent")
15847
15174
  return;
15848
15175
  const write = reaction === "print" ? step.info : step.warn;
15849
- for (const line of lines2)
15176
+ for (const line of lines)
15850
15177
  write(line);
15851
15178
  if (reaction === "accept") {
15852
15179
  step.warn(`#${step.detail.number} merges anyway: dangerous.acceptVersionCollision is true.`);
@@ -15868,13 +15195,13 @@ async function guardBeforeMerge(step) {
15868
15195
  });
15869
15196
  const answer2 = answerOf(reading);
15870
15197
  const reaction = guardReaction(reading, settings);
15871
- const lines2 = guardLines(reading, settings.releaseVersionFile);
15872
- const report3 = { answer: answer2, reaction, lines: lines2 };
15198
+ const lines = guardLines(reading, settings.releaseVersionFile);
15199
+ const report3 = { answer: answer2, reaction, lines };
15873
15200
  if (reaction === "refuse")
15874
- throw guardRefusal(refusalHead(step.detail.number, answer2), lines2);
15201
+ throw guardRefusal(refusalHead(step.detail.number, answer2), lines);
15875
15202
  if (reaction === "ask")
15876
- return { report: report3, go: await askGuard(step, answer2, lines2) };
15877
- printLines(step, reaction, lines2);
15203
+ return { report: report3, go: await askGuard(step, answer2, lines) };
15204
+ printLines(step, reaction, lines);
15878
15205
  return { report: report3, go: true };
15879
15206
  }
15880
15207
 
@@ -15984,8 +15311,8 @@ async function postUncheckedComment(pulls, number, unchecked, warn3) {
15984
15311
  var USAGE36 = PR_USAGE.merge;
15985
15312
  var YES_ANSWERS4 = ["y", "yes"];
15986
15313
  var DEFAULT_MERGE_SEAMS = Object.freeze({});
15987
- function refusal11(lines2) {
15988
- return new CommandExit(1, lines2.join(`
15314
+ function refusal11(lines) {
15315
+ return new CommandExit(1, lines.join(`
15989
15316
  `));
15990
15317
  }
15991
15318
  function readMethodFlag(flags, usage2) {
@@ -16359,9 +15686,9 @@ function parseFailedLog(text) {
16359
15686
  end -= 1;
16360
15687
  return raw.slice(0, end).map((line) => parseFailedLogLine(line));
16361
15688
  }
16362
- function readFailedStep(lines2) {
15689
+ function readFailedStep(lines) {
16363
15690
  let group;
16364
- for (const line of lines2) {
15691
+ for (const line of lines) {
16365
15692
  const head = groupStep(line.text);
16366
15693
  if (head !== undefined)
16367
15694
  group = head;
@@ -16372,14 +15699,14 @@ function readFailedStep(lines2) {
16372
15699
  if (group !== undefined)
16373
15700
  return { name: group, source: "group-marker" };
16374
15701
  }
16375
- const column = lines2.find((line) => line.step !== undefined)?.step;
15702
+ const column = lines.find((line) => line.step !== undefined)?.step;
16376
15703
  if (column !== undefined)
16377
15704
  return { name: column, source: "step-column" };
16378
15705
  return group === undefined ? undefined : { name: group, source: "group-marker" };
16379
15706
  }
16380
- function readJobs(lines2) {
15707
+ function readJobs(lines) {
16381
15708
  const jobs = [];
16382
- for (const line of lines2) {
15709
+ for (const line of lines) {
16383
15710
  if (line.job !== undefined && !jobs.includes(line.job))
16384
15711
  jobs.push(line.job);
16385
15712
  }
@@ -16395,14 +15722,14 @@ function readCap(value) {
16395
15722
  }
16396
15723
  function readFailedLog(text, options = {}) {
16397
15724
  const cap = readCap(options.maxLines);
16398
- const lines2 = parseFailedLog(text);
16399
- const kept = lines2.slice(Math.max(0, lines2.length - cap));
15725
+ const lines = parseFailedLog(text);
15726
+ const kept = lines.slice(Math.max(0, lines.length - cap));
16400
15727
  return {
16401
- jobs: readJobs(lines2),
16402
- step: readFailedStep(lines2),
15728
+ jobs: readJobs(lines),
15729
+ step: readFailedStep(lines),
16403
15730
  lines: kept.map((line) => line.text),
16404
- omitted: lines2.length - kept.length,
16405
- total: lines2.length
15731
+ omitted: lines.length - kept.length,
15732
+ total: lines.length
16406
15733
  };
16407
15734
  }
16408
15735
 
@@ -17277,7 +16604,7 @@ var TRIAGE_FRESH_WINDOW_MS = TRIAGE_FRESH_HOURS * HOUR_MS2;
17277
16604
  var MAX_TRIAGE_CANDIDATES = 3;
17278
16605
  var REFUSE_EXIT_CODE = 2;
17279
16606
  var DAYS_FROM_HOURS = 48;
17280
- var REFUSAL_PREFIX2 = "rafa pr triage --resolve refuses";
16607
+ var REFUSAL_PREFIX = "rafa pr triage --resolve refuses";
17281
16608
  var INDENT6 = " ";
17282
16609
  function ageMs(updatedAt, now) {
17283
16610
  const then = Date.parse(updatedAt);
@@ -17290,7 +16617,7 @@ function isFreshCandidate(updatedAt, now) {
17290
16617
  const age = ageMs(updatedAt, now);
17291
16618
  return age !== null && age <= TRIAGE_FRESH_WINDOW_MS;
17292
16619
  }
17293
- function plural4(count, singular, many) {
16620
+ function plural2(count, singular, many) {
17294
16621
  return count === 1 ? `1 ${singular}` : `${count} ${many}`;
17295
16622
  }
17296
16623
  function describeAge(updatedAt, now) {
@@ -17303,8 +16630,8 @@ function describeAge(updatedAt, now) {
17303
16630
  if (hours < 1)
17304
16631
  return "less than an hour ago";
17305
16632
  if (hours < DAYS_FROM_HOURS)
17306
- return `${plural4(hours, "hour", "hours")} ago`;
17307
- return `${plural4(Math.floor(age / DAY_MS2), "day", "days")} ago`;
16633
+ return `${plural2(hours, "hour", "hours")} ago`;
16634
+ return `${plural2(Math.floor(age / DAY_MS2), "day", "days")} ago`;
17308
16635
  }
17309
16636
  function triageCommand(number, resolve11 = false) {
17310
16637
  return resolve11 ? `rafa pr triage ${number} --resolve` : `rafa pr triage ${number}`;
@@ -17343,24 +16670,24 @@ function plain(decision, assess, headline) {
17343
16670
  });
17344
16671
  }
17345
16672
  function listing(decision, assess, rest, headline, exitCode = 0) {
17346
- const lines2 = Object.freeze(rest.map((one) => skippedLine(one)));
16673
+ const lines = Object.freeze(rest.map((one) => skippedLine(one)));
17347
16674
  return Object.freeze({
17348
16675
  decision,
17349
16676
  assess,
17350
16677
  skipped: Object.freeze([...rest]),
17351
16678
  exitCode,
17352
16679
  headline,
17353
- lines: lines2,
17354
- message: [headline, ...lines2].join(`
16680
+ lines,
16681
+ message: [headline, ...lines].join(`
17355
16682
  `)
17356
16683
  });
17357
16684
  }
17358
16685
  function refusal12(rest, count) {
17359
- const headline = `${REFUSAL_PREFIX2}: ${plural4(count, "red pull request", "red pull requests")}, ` + "and --resolve acts on one. Run one of:";
16686
+ const headline = `${REFUSAL_PREFIX}: ${plural2(count, "red pull request", "red pull requests")}, ` + "and --resolve acts on one. Run one of:";
17360
16687
  return listing("refuse", Object.freeze([]), rest, headline, REFUSE_EXIT_CODE);
17361
16688
  }
17362
16689
  function someReading(fresh, stale, count) {
17363
- const older = stale.length === 0 ? "" : `; ${plural4(stale.length, "older one is", "older ones are")} skipped`;
16690
+ const older = stale.length === 0 ? "" : `; ${plural2(stale.length, "older one is", "older ones are")} skipped`;
17364
16691
  const headline = `${count} red: assessing ${joinNumbers(fresh)}${older}`;
17365
16692
  return listing("some", fresh, stale, headline);
17366
16693
  }
@@ -17376,9 +16703,9 @@ function selectTriagePullRequests(input) {
17376
16703
  }
17377
16704
  const candidates = byNumber2(input.candidates);
17378
16705
  const count = candidates.length;
17379
- const lines2 = candidates.map((pull2) => skipped(pull2, now, resolve11));
16706
+ const lines = candidates.map((pull2) => skipped(pull2, now, resolve11));
17380
16707
  if (resolve11 && count > 1)
17381
- return refusal12(lines2, count);
16708
+ return refusal12(lines, count);
17382
16709
  if (count === 0) {
17383
16710
  return plain("none", Object.freeze([]), "no red pull request to triage, and no pull request on the current branch");
17384
16711
  }
@@ -17387,9 +16714,9 @@ function selectTriagePullRequests(input) {
17387
16714
  return plain("single", Object.freeze([only]), `1 red: assessing #${only.number}, last moved ${describeAge(only.updatedAt, now)}`);
17388
16715
  }
17389
16716
  if (count > MAX_TRIAGE_CANDIDATES)
17390
- return listReading(lines2, count);
16717
+ return listReading(lines, count);
17391
16718
  const fresh = candidates.filter((pull2) => isFreshCandidate(pull2.updatedAt, now));
17392
- const stale = lines2.filter((one) => !one.fresh);
16719
+ const stale = lines.filter((one) => !one.fresh);
17393
16720
  if (fresh.length === 0)
17394
16721
  return listReading(stale, count);
17395
16722
  return someReading(Object.freeze(fresh), stale, count);
@@ -17462,7 +16789,7 @@ function readConflict(git, base, head) {
17462
16789
  // src/commands/pr/triage-read.ts
17463
16790
  var RUN_URL = /\/actions\/runs\/(\d+)(?:[/?#]|$)/;
17464
16791
  var CONFLICT_UNREAD = "unread";
17465
- var REMOTE3 = "origin";
16792
+ var REMOTE2 = "origin";
17466
16793
  function runIdOf(link) {
17467
16794
  return RUN_URL.exec(link)?.[1] ?? null;
17468
16795
  }
@@ -17510,10 +16837,10 @@ async function readWorkflowCount(pulls, rows) {
17510
16837
  }
17511
16838
  }
17512
16839
  function baseCandidates(refs2) {
17513
- return [`${REMOTE3}/${refs2.baseRefName}`, refs2.baseRefName];
16840
+ return [`${REMOTE2}/${refs2.baseRefName}`, refs2.baseRefName];
17514
16841
  }
17515
16842
  function headCandidates(refs2) {
17516
- return refs2.isCrossRepository ? [refs2.headRefOid] : [refs2.headRefOid, `${REMOTE3}/${refs2.headRefName}`];
16843
+ return refs2.isCrossRepository ? [refs2.headRefOid] : [refs2.headRefOid, `${REMOTE2}/${refs2.headRefName}`];
17517
16844
  }
17518
16845
  function resolvesRef(git, ref8) {
17519
16846
  return git(["rev-parse", "--verify", "--quiet", `${ref8}^{commit}`]).ok;
@@ -17737,8 +17064,8 @@ function withTaskBudget(plan2, usd) {
17737
17064
  }
17738
17065
 
17739
17066
  // src/pr/plans/load.ts
17740
- import { existsSync as existsSync10, readFileSync as readFileSync18 } from "fs";
17741
- import { dirname as dirname6, join as join31 } from "path";
17067
+ import { existsSync as existsSync9, readFileSync as readFileSync18 } from "fs";
17068
+ import { dirname as dirname6, join as join29 } from "path";
17742
17069
  import { fileURLToPath as fileURLToPath4 } from "url";
17743
17070
  var MODULE_DIR4 = dirname6(fileURLToPath4(import.meta.url));
17744
17071
  var PINNED_PLANS_DIRNAME = "plans";
@@ -17755,8 +17082,8 @@ function hasPinnedPlan(triageClass) {
17755
17082
  function pinnedPlanCandidates(moduleDir, triageClass) {
17756
17083
  const name = pinnedPlanFileName(triageClass);
17757
17084
  return [
17758
- join31(moduleDir, PINNED_PLANS_DIRNAME, name),
17759
- join31(moduleDir, name)
17085
+ join29(moduleDir, PINNED_PLANS_DIRNAME, name),
17086
+ join29(moduleDir, name)
17760
17087
  ];
17761
17088
  }
17762
17089
  function readPinnedPlan(triageClass, lookup = {}) {
@@ -17764,7 +17091,7 @@ function readPinnedPlan(triageClass, lookup = {}) {
17764
17091
  throw new Error(`No pinned resolve plan ships for ${triageClass}; ` + `one ships for ${PINNED_PLAN_CLASSES.join(", ")}`);
17765
17092
  }
17766
17093
  const candidates = pinnedPlanCandidates(lookup.moduleDir ?? MODULE_DIR4, triageClass);
17767
- const found = candidates.find((candidate) => existsSync10(candidate));
17094
+ const found = candidates.find((candidate) => existsSync9(candidate));
17768
17095
  if (found === undefined) {
17769
17096
  throw new Error(`The pinned resolve plan for ${triageClass} is missing: ` + `no file at ${candidates.join(" or ")}`);
17770
17097
  }
@@ -17909,19 +17236,19 @@ function readAttemptRepeat(input) {
17909
17236
 
17910
17237
  // src/commands/pr/resolve-loop.ts
17911
17238
  import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync6 } from "fs";
17912
- import { join as join32 } from "path";
17913
- var RESOLVE_SUBDIR = join32(".rafa", "resolve");
17239
+ import { join as join30 } from "path";
17240
+ var RESOLVE_SUBDIR = join30(".rafa", "resolve");
17914
17241
  var RUN_PREFIX = "pr-";
17915
17242
  var ATTEMPT_PREFIX = "attempt-";
17916
17243
  var LOOP_WORDS = Object.freeze(["loop", "start"]);
17917
17244
  var CHILD_OUTPUT = "text";
17918
17245
  function resolveRunDir(home, number, attempt3) {
17919
- return join32(home, RESOLVE_SUBDIR, `${RUN_PREFIX}${number}`, `${ATTEMPT_PREFIX}${attempt3}`);
17246
+ return join30(home, RESOLVE_SUBDIR, `${RUN_PREFIX}${number}`, `${ATTEMPT_PREFIX}${attempt3}`);
17920
17247
  }
17921
17248
  function writeResolvePlan(write) {
17922
17249
  const dir = resolveRunDir(write.home, write.number, write.attempt);
17923
17250
  mkdirSync5(dir, { recursive: true });
17924
- const path = join32(dir, pinnedPlanFileName(write.triageClass));
17251
+ const path = join30(dir, pinnedPlanFileName(write.triageClass));
17925
17252
  writeFileSync6(path, write.plan, "utf8");
17926
17253
  return path;
17927
17254
  }
@@ -17933,10 +17260,10 @@ async function forwardLines(stream, onLine) {
17933
17260
  let pending = "";
17934
17261
  for await (const chunk of stream) {
17935
17262
  pending += decoder.decode(chunk, { stream: true });
17936
- const lines2 = pending.split(`
17263
+ const lines = pending.split(`
17937
17264
  `);
17938
- pending = lines2.pop() ?? "";
17939
- for (const line of lines2)
17265
+ pending = lines.pop() ?? "";
17266
+ for (const line of lines)
17940
17267
  onLine(line);
17941
17268
  }
17942
17269
  if (pending !== "")
@@ -17963,7 +17290,7 @@ async function runResolveLoop(run, seams = {}) {
17963
17290
 
17964
17291
  // src/pr/triage/version-convert.ts
17965
17292
  import { mkdirSync as mkdirSync6, readFileSync as readFileSync19, writeFileSync as writeFileSync7 } from "fs";
17966
- import { dirname as dirname7, join as join33, posix } from "path";
17293
+ import { dirname as dirname7, join as join31, posix } from "path";
17967
17294
  var FALLBACK_PLAN_PREFIX = "pr-";
17968
17295
  var UNUSABLE_PLAN_CHARACTERS = /[^A-Za-z0-9._-]+/g;
17969
17296
  var UNUSABLE_PLAN_START = /^[^A-Za-z0-9]+/;
@@ -18075,7 +17402,7 @@ function planConversion(input, problems) {
18075
17402
  }
18076
17403
  let branchVersionText;
18077
17404
  try {
18078
- branchVersionText = readFileSync19(join33(input.worktree, settings.releaseVersionFile), "utf8");
17405
+ branchVersionText = readFileSync19(join31(input.worktree, settings.releaseVersionFile), "utf8");
18079
17406
  } catch (error) {
18080
17407
  return refused(`${settings.releaseVersionFile} could not be read in the worktree: ${messageOf(error)}`, problems);
18081
17408
  }
@@ -18101,7 +17428,7 @@ function writeConversion(input, plan2) {
18101
17428
  [input.settings.releaseChangelog, plan2.changelogText]
18102
17429
  ];
18103
17430
  for (const [path, text] of writes) {
18104
- const target = join33(input.worktree, path);
17431
+ const target = join31(input.worktree, path);
18105
17432
  try {
18106
17433
  mkdirSync6(dirname7(target), { recursive: true });
18107
17434
  writeFileSync7(target, text);
@@ -18913,7 +18240,7 @@ var USAGE40 = PR_USAGE.wait;
18913
18240
  var TIMEOUT_FLAG = "timeout";
18914
18241
  var DEFAULT_WAIT_TIMEOUT_MIN = DEFAULT_CI_TIMEOUT_MIN;
18915
18242
  var MS_PER_MIN = 60000;
18916
- var MS_PER_SECOND3 = 1000;
18243
+ var MS_PER_SECOND2 = 1000;
18917
18244
  var WAIT_EXIT_CODES = Object.freeze({
18918
18245
  green: 0,
18919
18246
  red: 1,
@@ -18942,7 +18269,7 @@ function pollWord(polls) {
18942
18269
  return polls === 1 ? "1 poll" : `${polls} polls`;
18943
18270
  }
18944
18271
  function secondsWord(elapsedMs) {
18945
- return `${Math.round(elapsedMs / MS_PER_SECOND3)}s`;
18272
+ return `${Math.round(elapsedMs / MS_PER_SECOND2)}s`;
18946
18273
  }
18947
18274
  function verdictWord(verdict) {
18948
18275
  return verdict === "timeout" || verdict === "pending" ? "still running" : verdict;
@@ -19333,9 +18660,9 @@ function tagSettle(worktree, mode, delivered) {
19333
18660
  }
19334
18661
 
19335
18662
  // src/release/settle-worktree.ts
19336
- import { existsSync as existsSync11, mkdtempSync as mkdtempSync2, realpathSync as realpathSync5, rmSync as rmSync2 } from "fs";
18663
+ import { existsSync as existsSync10, mkdtempSync as mkdtempSync2, realpathSync as realpathSync3, rmSync as rmSync2 } from "fs";
19337
18664
  import { tmpdir as tmpdir2 } from "os";
19338
- import { join as join34 } from "path";
18665
+ import { join as join32 } from "path";
19339
18666
  var SETTLE_SCRATCH_PREFIX = "rafa-settle-";
19340
18667
  var WORKTREE_DIRECTORY = "base";
19341
18668
  function removeScratch(git, scratch, path) {
@@ -19352,14 +18679,14 @@ function removeScratch(git, scratch, path) {
19352
18679
  problems.push(`the worktree record of ${path} could not be pruned: ${gitSaid(pruned)}`);
19353
18680
  }
19354
18681
  }
19355
- if (problems.length === 0 && existsSync11(scratch)) {
18682
+ if (problems.length === 0 && existsSync10(scratch)) {
19356
18683
  problems.push(`the scratch directory ${scratch} is still there`);
19357
18684
  }
19358
18685
  return problems.length === 0 ? null : problems.join("; ");
19359
18686
  }
19360
18687
  function makeScratch(root) {
19361
18688
  try {
19362
- return realpathSync5(mkdtempSync2(join34(root, SETTLE_SCRATCH_PREFIX)));
18689
+ return realpathSync3(mkdtempSync2(join32(root, SETTLE_SCRATCH_PREFIX)));
19363
18690
  } catch (error) {
19364
18691
  return { ok: false, problem: `no scratch directory could be made under ${root}: ${messageOf(error)}`, leftover: null };
19365
18692
  }
@@ -19381,7 +18708,7 @@ function addWorktree(options) {
19381
18708
  const scratch = makeScratch(options.scratchRoot ?? tmpdir2());
19382
18709
  if (typeof scratch !== "string")
19383
18710
  return scratch;
19384
- const path = join34(scratch, WORKTREE_DIRECTORY);
18711
+ const path = join32(scratch, WORKTREE_DIRECTORY);
19385
18712
  const added = git(["worktree", "add", "--detach", path, commit]);
19386
18713
  if (!added.ok) {
19387
18714
  return {
@@ -19435,26 +18762,26 @@ function short(hash) {
19435
18762
  return hash.slice(0, SHORT_HASH4);
19436
18763
  }
19437
18764
  function readingLines(reading, ref8) {
19438
- const lines2 = [`Strategy: ${reading.strategy}`];
18765
+ const lines = [`Strategy: ${reading.strategy}`];
19439
18766
  if (reading.outcome === "unread")
19440
- return lines2;
18767
+ return lines;
19441
18768
  if (reading.outcome === "malformed") {
19442
- return [...lines2, `Base: ${ref8} at ${short(reading.commit)}`];
18769
+ return [...lines, `Base: ${ref8} at ${short(reading.commit)}`];
19443
18770
  }
19444
- lines2.push(`Base: ${ref8} at ${short(reading.commit)}, version ${reading.baseVersion}`);
18771
+ lines.push(`Base: ${ref8} at ${short(reading.commit)}, version ${reading.baseVersion}`);
19445
18772
  if (reading.fragments.length === 0) {
19446
- lines2.push("Fragments: none waiting");
18773
+ lines.push("Fragments: none waiting");
19447
18774
  } else {
19448
- lines2.push("Fragments, in the order the base received them:");
18775
+ lines.push("Fragments, in the order the base received them:");
19449
18776
  reading.fragments.forEach((each, index) => {
19450
18777
  const { level, title } = each.fragment;
19451
- lines2.push(`${INDENT8}${String(index + 1)}. ${each.path} \u2014 ${level}, "${title}", added ${each.addedOn} in ${short(each.commit)}`);
18778
+ lines.push(`${INDENT8}${String(index + 1)}. ${each.path} \u2014 ${level}, "${title}", added ${each.addedOn} in ${short(each.commit)}`);
19452
18779
  });
19453
18780
  }
19454
18781
  if (reading.outcome === "folded" || reading.outcome === "built" || reading.outcome === "unbuilt") {
19455
- lines2.push(`Version: ${reading.baseVersion} \u2192 ${reading.version}`);
18782
+ lines.push(`Version: ${reading.baseVersion} \u2192 ${reading.version}`);
19456
18783
  }
19457
- return lines2;
18784
+ return lines;
19458
18785
  }
19459
18786
  function unwrittenEnding(reading) {
19460
18787
  switch (reading.outcome) {
@@ -19468,19 +18795,19 @@ function unwrittenEnding(reading) {
19468
18795
  return { line: "Nothing to settle: no fragment waits that ships a release.", refuses: false };
19469
18796
  }
19470
18797
  }
19471
- function resultOf5(base, lines2, problem2) {
18798
+ function resultOf5(base, lines, problem2) {
19472
18799
  const exitCode = problem2 === null ? 0 : 1;
19473
- return { ...base, lines: lines2, problem: problem2, exitCode };
18800
+ return { ...base, lines, problem: problem2, exitCode };
19474
18801
  }
19475
18802
  function dryRunResult(reading, ref8, branch) {
19476
18803
  const base = { dryRun: true, delivery: null, reading, delivered: null, tag: null };
19477
- const lines2 = [...readingLines(reading, ref8)];
18804
+ const lines = [...readingLines(reading, ref8)];
19478
18805
  if (reading.outcome === "folded") {
19479
- lines2.push(`Dry run: settle would commit "${releaseCommitSubject(reading.version)}" on ${branch}; nothing was written.`);
19480
- return resultOf5(base, lines2, null);
18806
+ lines.push(`Dry run: settle would commit "${releaseCommitSubject(reading.version)}" on ${branch}; nothing was written.`);
18807
+ return resultOf5(base, lines, null);
19481
18808
  }
19482
18809
  const ending = unwrittenEnding(reading);
19483
- return ending.refuses ? resultOf5(base, lines2, ending.line) : resultOf5(base, [...lines2, ending.line], null);
18810
+ return ending.refuses ? resultOf5(base, lines, ending.line) : resultOf5(base, [...lines, ending.line], null);
19484
18811
  }
19485
18812
  function deliveryEnding(delivered, branch) {
19486
18813
  const { outcome } = delivered;
@@ -19509,18 +18836,18 @@ function deliveryEnding(delivered, branch) {
19509
18836
  function settledResult(delivered, tag, ref8, branch) {
19510
18837
  const reading = delivered.outcome.build;
19511
18838
  const base = { dryRun: false, delivery: delivered.delivery, reading, delivered, tag };
19512
- const lines2 = [...readingLines(reading, ref8)];
18839
+ const lines = [...readingLines(reading, ref8)];
19513
18840
  const ending = deliveryEnding(delivered, branch);
19514
18841
  if (ending.refuses)
19515
- return resultOf5(base, lines2, ending.line);
19516
- lines2.push(ending.line);
18842
+ return resultOf5(base, lines, ending.line);
18843
+ lines.push(ending.line);
19517
18844
  if (tag.outcome === "failed")
19518
- return resultOf5(base, lines2, `\u274C ${tag.sentence}`);
18845
+ return resultOf5(base, lines, `\u274C ${tag.sentence}`);
19519
18846
  if (tag.outcome === "tagged")
19520
- lines2.push(`\u2705 ${tag.sentence}`);
18847
+ lines.push(`\u2705 ${tag.sentence}`);
19521
18848
  if (tag.outcome === "skipped" && tag.sentence !== null)
19522
- lines2.push(`Tag: ${tag.sentence}`);
19523
- return resultOf5(base, lines2, null);
18849
+ lines.push(`Tag: ${tag.sentence}`);
18850
+ return resultOf5(base, lines, null);
19524
18851
  }
19525
18852
  function providerFor(project, config, seams) {
19526
18853
  requireGhProvider(resolvePrProvider({ configured: config.prProvider, dir: project.root, readRemote: seams.readRemote }));
@@ -19604,7 +18931,7 @@ var settle_default = createReleaseSettleCommand();
19604
18931
 
19605
18932
  // src/commands/release/tag.ts
19606
18933
  import { readFileSync as readFileSync20 } from "fs";
19607
- import { join as join35 } from "path";
18934
+ import { join as join33 } from "path";
19608
18935
 
19609
18936
  // src/commands/release/release-commit.ts
19610
18937
  function unread(reason, message) {
@@ -19668,7 +18995,7 @@ function textOf(path) {
19668
18995
  }
19669
18996
  }
19670
18997
  function readVersion(root, configured) {
19671
- const resolved = join35(root, configured);
18998
+ const resolved = join33(root, configured);
19672
18999
  const text = textOf(resolved);
19673
19000
  if (text === null) {
19674
19001
  return {
@@ -19682,7 +19009,7 @@ function readVersion(root, configured) {
19682
19009
  return version2 === null ? { path: configured, text, version: null, problem: `${resolved} declares no version this can read` } : { path: configured, text, version: version2, problem: null };
19683
19010
  }
19684
19011
  function readNewestRelease(root, configured) {
19685
- const resolved = join35(root, configured);
19012
+ const resolved = join33(root, configured);
19686
19013
  const text = textOf(resolved);
19687
19014
  if (text === null) {
19688
19015
  return { path: configured, version: null, problem: `the changelog could not be read at ${resolved}` };
@@ -19893,8 +19220,8 @@ function createReleaseTagCommand(seams = DEFAULT_RELEASE_SEAMS) {
19893
19220
  context.output.result(result2);
19894
19221
  return;
19895
19222
  }
19896
- const lines2 = renderTagged(result2.written, result2.inputs.releaseBranch, result2.followUps);
19897
- for (const line of lines2)
19223
+ const lines = renderTagged(result2.written, result2.inputs.releaseBranch, result2.followUps);
19224
+ for (const line of lines)
19898
19225
  context.output.info(line);
19899
19226
  }
19900
19227
  };
@@ -19952,17 +19279,17 @@ function createRoadmapCommand(seams = DEFAULT_ISSUE_SEAMS) {
19952
19279
  var roadmap_default = createRoadmapCommand();
19953
19280
 
19954
19281
  // src/commands/skill/backfill.ts
19955
- import { statSync as statSync10 } from "fs";
19956
- import { join as join40, resolve as resolve13 } from "path";
19282
+ import { statSync as statSync9 } from "fs";
19283
+ import { join as join38, resolve as resolve13 } from "path";
19957
19284
 
19958
19285
  // src/backfill/backup.ts
19959
- import { copyFileSync as copyFileSync3, existsSync as existsSync12, mkdirSync as mkdirSync7 } from "fs";
19960
- import { dirname as dirname8, isAbsolute as isAbsolute6, join as join37, relative as relative7, resolve as resolve11, sep as sep5 } from "path";
19286
+ import { copyFileSync as copyFileSync3, existsSync as existsSync11, mkdirSync as mkdirSync7 } from "fs";
19287
+ import { dirname as dirname8, isAbsolute as isAbsolute6, join as join35, relative as relative7, resolve as resolve11, sep as sep4 } from "path";
19961
19288
 
19962
19289
  // src/backfill/proposal-file.ts
19963
19290
  import { readdirSync as readdirSync3, readFileSync as readFileSync21 } from "fs";
19964
- import { join as join36 } from "path";
19965
- var BACKFILL_PATH = join36(".rafa", "backfill");
19291
+ import { join as join34 } from "path";
19292
+ var BACKFILL_PATH = join34(".rafa", "backfill");
19966
19293
  var PROPOSAL_FILE_PREFIX = "proposals-";
19967
19294
  var PROPOSAL_FILE_EXTENSION = ".yaml";
19968
19295
  var SHA256_PATTERN = /^[0-9a-f]{64}$/;
@@ -19988,7 +19315,7 @@ var PROPOSAL_FILE_HEADER = [
19988
19315
  ].join(`
19989
19316
  `);
19990
19317
  function proposalPath(dir, batch) {
19991
- return join36(dir, `${PROPOSAL_FILE_PREFIX}${String(batch).padStart(2, "0")}${PROPOSAL_FILE_EXTENSION}`);
19318
+ return join34(dir, `${PROPOSAL_FILE_PREFIX}${String(batch).padStart(2, "0")}${PROPOSAL_FILE_EXTENSION}`);
19992
19319
  }
19993
19320
  function isProposalFileName(name) {
19994
19321
  return name.startsWith(PROPOSAL_FILE_PREFIX) && name.endsWith(PROPOSAL_FILE_EXTENSION);
@@ -20124,7 +19451,7 @@ function readProposalFiles(dir) {
20124
19451
  return [];
20125
19452
  }
20126
19453
  return names.filter((name) => isProposalFileName(name)).sort((left, right) => left.localeCompare(right)).map((name) => {
20127
- const path = join36(dir, name);
19454
+ const path = join34(dir, name);
20128
19455
  try {
20129
19456
  return { path, result: parseProposalFile(readFileSync21(path, "utf8")) };
20130
19457
  } catch (error) {
@@ -20134,19 +19461,19 @@ function readProposalFiles(dir) {
20134
19461
  }
20135
19462
 
20136
19463
  // src/backfill/backup.ts
20137
- var BACKUP_PATH = join37(BACKFILL_PATH, "backup");
19464
+ var BACKUP_PATH = join35(BACKFILL_PATH, "backup");
20138
19465
  var BACKUP_KINDS = ["copied", "kept", "tracked", "outside", "missing"];
20139
19466
  function backupDirectory(base) {
20140
- return join37(base, BACKUP_PATH);
19467
+ return join35(base, BACKUP_PATH);
20141
19468
  }
20142
19469
  function isUnder(root, path) {
20143
19470
  const step = relative7(resolve11(root), resolve11(path));
20144
- return step === "" || !step.startsWith(`..${sep5}`) && step !== ".." && !isAbsolute6(step);
19471
+ return step === "" || !step.startsWith(`..${sep4}`) && step !== ".." && !isAbsolute6(step);
20145
19472
  }
20146
19473
  function backupPathFor(path, root, backupDir) {
20147
19474
  if (!isUnder(root, path))
20148
19475
  return null;
20149
- return join37(backupDir, relative7(resolve11(root), resolve11(path)));
19476
+ return join35(backupDir, relative7(resolve11(root), resolve11(path)));
20150
19477
  }
20151
19478
  function backupFile(path, options) {
20152
19479
  if (options.repoRoot !== null && isUnder(options.repoRoot, path)) {
@@ -20155,9 +19482,9 @@ function backupFile(path, options) {
20155
19482
  const backup = backupPathFor(path, options.root, options.backupDir);
20156
19483
  if (backup === null)
20157
19484
  return { path, kind: "outside", backup: null };
20158
- if (!existsSync12(path))
19485
+ if (!existsSync11(path))
20159
19486
  return { path, kind: "missing", backup: null };
20160
- if (existsSync12(backup))
19487
+ if (existsSync11(backup))
20161
19488
  return { path, kind: "kept", backup };
20162
19489
  mkdirSync7(dirname8(backup), { recursive: true });
20163
19490
  copyFileSync3(path, backup);
@@ -20516,12 +19843,12 @@ function readVerdict(cell) {
20516
19843
  function isEmptyCell(cell) {
20517
19844
  return cell === "" || cell === EMPTY_CELL;
20518
19845
  }
20519
- function findTable(lines2) {
20520
- for (let index = 0;index + 1 < lines2.length; index += 1) {
20521
- const header = splitTableRow(lines2[index] ?? "");
19846
+ function findTable(lines) {
19847
+ for (let index = 0;index + 1 < lines.length; index += 1) {
19848
+ const header = splitTableRow(lines[index] ?? "");
20522
19849
  if (header === null)
20523
19850
  continue;
20524
- const delimiter = splitTableRow(lines2[index + 1] ?? "");
19851
+ const delimiter = splitTableRow(lines[index + 1] ?? "");
20525
19852
  if (delimiter !== null && isDelimiterRow(delimiter)) {
20526
19853
  return { header, start: index };
20527
19854
  }
@@ -20582,11 +19909,11 @@ function parseDemotionReport(text) {
20582
19909
  const status = readStatus(document.data);
20583
19910
  if (typeof status !== "string")
20584
19911
  issues.push(status);
20585
- const lines2 = text.split(`
19912
+ const lines = text.split(`
20586
19913
  `).map((line) => line.replace(/\r$/, ""));
20587
- const bodyStart = lines2.length - document.body.split(`
19914
+ const bodyStart = lines.length - document.body.split(`
20588
19915
  `).length;
20589
- const table = findTable(lines2.slice(bodyStart));
19916
+ const table = findTable(lines.slice(bodyStart));
20590
19917
  if (table === null) {
20591
19918
  issues.push(issue("missing-table", "file", "the report holds no row table", 1));
20592
19919
  return { issues, report: null };
@@ -20599,8 +19926,8 @@ function parseDemotionReport(text) {
20599
19926
  }
20600
19927
  const rows = [];
20601
19928
  const seen = new Set;
20602
- for (let index = bodyStart + table.start + 2;index < lines2.length; index += 1) {
20603
- const cells = splitTableRow(lines2[index] ?? "");
19929
+ for (let index = bodyStart + table.start + 2;index < lines.length; index += 1) {
19930
+ const cells = splitTableRow(lines[index] ?? "");
20604
19931
  if (cells === null)
20605
19932
  break;
20606
19933
  if (cells.length !== DEMOTION_REPORT_COLUMNS.length) {
@@ -20789,7 +20116,7 @@ function parseSessionAnswer(stdout) {
20789
20116
 
20790
20117
  // src/backfill/propose.ts
20791
20118
  import { mkdirSync as mkdirSync8, readFileSync as readFileSync24, writeFileSync as writeFileSync9 } from "fs";
20792
- import { join as join38, resolve as resolve12 } from "path";
20119
+ import { join as join36, resolve as resolve12 } from "path";
20793
20120
  var PROPOSAL_ACTION_KINDS = [
20794
20121
  "applied",
20795
20122
  "unchanged",
@@ -20938,7 +20265,7 @@ function writeRow(path, row2, original, options) {
20938
20265
  };
20939
20266
  }
20940
20267
  function applyRow(root, row2, options) {
20941
- const path = join38(root, ...row2.path.split("/"));
20268
+ const path = join36(root, ...row2.path.split("/"));
20942
20269
  if (row2.status === UNANSWERED_ROW) {
20943
20270
  return inert2(path, row2, "unanswered", row2.note ?? "the row carries no answer");
20944
20271
  }
@@ -20975,7 +20302,7 @@ function applyProposals(files, root, options) {
20975
20302
  for (const file of files) {
20976
20303
  const refusal13 = fileRefusal(file, root);
20977
20304
  for (const row2 of file.rows) {
20978
- const action = refusal13 === null ? applyRow(root, row2, options) : inert2(join38(root, ...row2.path.split("/")), row2, "refused", refusal13);
20305
+ const action = refusal13 === null ? applyRow(root, row2, options) : inert2(join36(root, ...row2.path.split("/")), row2, "refused", refusal13);
20979
20306
  actions.push(action);
20980
20307
  if (row2.trigger !== null && action.kind !== "refused" && action.kind !== "unanswered") {
20981
20308
  triggers[row2.name] = row2.trigger;
@@ -20986,17 +20313,17 @@ function applyProposals(files, root, options) {
20986
20313
  }
20987
20314
 
20988
20315
  // src/demote/select.ts
20989
- import { readdirSync as readdirSync4, readFileSync as readFileSync25, realpathSync as realpathSync6, statSync as statSync9 } from "fs";
20990
- import { basename as basename7, dirname as dirname9, join as join39 } from "path";
20316
+ import { readdirSync as readdirSync4, readFileSync as readFileSync25, realpathSync as realpathSync4, statSync as statSync8 } from "fs";
20317
+ import { basename as basename7, dirname as dirname9, join as join37 } from "path";
20991
20318
  var SKILLS_DIRECTORY = "skills";
20992
20319
  var CLAUDE_DIRECTORY = ".claude";
20993
20320
  var AUTO_EXTRACTED_ORIGIN = "auto-extracted";
20994
- var DEMOTED_PATH = join39(".rafa", "demoted");
20995
- var INSTINCTS_PATH = join39(".rafa", "instincts");
20321
+ var DEMOTED_PATH = join37(".rafa", "demoted");
20322
+ var INSTINCTS_PATH = join37(".rafa", "instincts");
20996
20323
  var REPORT_FILE = "report.md";
20997
20324
  function realOrSelf(path) {
20998
20325
  try {
20999
- return realpathSync6(path);
20326
+ return realpathSync4(path);
21000
20327
  } catch {
21001
20328
  return path;
21002
20329
  }
@@ -21015,19 +20342,19 @@ function resolveDemotionScope(dir, home) {
21015
20342
  if (basename7(claude) !== CLAUDE_DIRECTORY)
21016
20343
  return null;
21017
20344
  const base = dirname9(claude);
21018
- const demotedDir = join39(base, DEMOTED_PATH);
20345
+ const demotedDir = join37(base, DEMOTED_PATH);
21019
20346
  return {
21020
20347
  scope: samePath(base, home) ? "user" : "project",
21021
20348
  dir,
21022
20349
  base,
21023
20350
  demotedDir,
21024
- instinctsDir: join39(base, INSTINCTS_PATH),
21025
- reportPath: join39(demotedDir, REPORT_FILE)
20351
+ instinctsDir: join37(base, INSTINCTS_PATH),
20352
+ reportPath: join37(demotedDir, REPORT_FILE)
21026
20353
  };
21027
20354
  }
21028
20355
  function entryKind(path) {
21029
20356
  try {
21030
- const stats = statSync9(path);
20357
+ const stats = statSync8(path);
21031
20358
  if (stats.isFile())
21032
20359
  return "file";
21033
20360
  if (stats.isDirectory())
@@ -21049,7 +20376,7 @@ function readSelected(absolute, path, entries) {
21049
20376
  let mtime;
21050
20377
  try {
21051
20378
  text = readFileSync25(absolute, "utf8");
21052
- mtime = statSync9(absolute).mtime.toISOString();
20379
+ mtime = statSync8(absolute).mtime.toISOString();
21053
20380
  } catch {
21054
20381
  return null;
21055
20382
  }
@@ -21064,14 +20391,14 @@ function selectsLearned(text, scope) {
21064
20391
  return origin === AUTO_EXTRACTED_ORIGIN;
21065
20392
  }
21066
20393
  function selectLearned(scope) {
21067
- const dir = join39(scope.dir, LEARNED_DIRECTORY);
20394
+ const dir = join37(scope.dir, LEARNED_DIRECTORY);
21068
20395
  if (entryKind(dir) !== "directory")
21069
20396
  return [];
21070
20397
  const selected = [];
21071
20398
  for (const name of visibleNames(dir)) {
21072
20399
  if (!name.endsWith(MARKDOWN_EXTENSION) || name === MARKDOWN_EXTENSION)
21073
20400
  continue;
21074
- const absolute = join39(dir, name);
20401
+ const absolute = join37(dir, name);
21075
20402
  if (entryKind(absolute) !== "file")
21076
20403
  continue;
21077
20404
  const file = readSelected(absolute, `${LEARNED_DIRECTORY}/${name}`, undefined);
@@ -21085,10 +20412,10 @@ function selectSkillFiles(scope) {
21085
20412
  for (const name of visibleNames(scope.dir)) {
21086
20413
  if (name === LEARNED_DIRECTORY)
21087
20414
  continue;
21088
- const skillDir = join39(scope.dir, name);
20415
+ const skillDir = join37(scope.dir, name);
21089
20416
  if (entryKind(skillDir) !== "directory")
21090
20417
  continue;
21091
- const absolute = join39(skillDir, SKILL_FILE);
20418
+ const absolute = join37(skillDir, SKILL_FILE);
21092
20419
  if (entryKind(absolute) !== "file")
21093
20420
  continue;
21094
20421
  const file = readSelected(absolute, `${name}/${SKILL_FILE}`, visibleNames(skillDir));
@@ -21149,7 +20476,7 @@ Usage: ${BACKFILL_USAGE}`);
21149
20476
  }
21150
20477
  function isDirectory3(path) {
21151
20478
  try {
21152
- return statSync10(path).isDirectory();
20479
+ return statSync9(path).isDirectory();
21153
20480
  } catch {
21154
20481
  return false;
21155
20482
  }
@@ -21198,12 +20525,12 @@ function actionLines(actions) {
21198
20525
  function checkOptions(context, projectRoot2) {
21199
20526
  return { projectRoot: projectRoot2, pathDirs: pathDirectories(context.env["PATH"]) };
21200
20527
  }
21201
- function report3(context, result2, lines2) {
20528
+ function report3(context, result2, lines) {
21202
20529
  if (context.outputMode === "json") {
21203
20530
  context.output.result(result2);
21204
20531
  return;
21205
20532
  }
21206
- for (const line of lines2)
20533
+ for (const line of lines)
21207
20534
  context.output.info(line);
21208
20535
  }
21209
20536
  function runPlan(context, scope, backfillDir, projectRoot2) {
@@ -21273,7 +20600,7 @@ function reviewedProposals(dir) {
21273
20600
  return read2.flatMap((entry) => entry.result.file === null ? [] : [entry.result.file]);
21274
20601
  }
21275
20602
  function rowPaths(files, root) {
21276
- return files.flatMap((file) => file.status !== REVIEWED_FILE ? [] : file.rows.filter((row2) => row2.status === ANSWERED_ROW).map((row2) => join40(root, ...row2.path.split("/"))));
20603
+ return files.flatMap((file) => file.status !== REVIEWED_FILE ? [] : file.rows.filter((row2) => row2.status === ANSWERED_ROW).map((row2) => join38(root, ...row2.path.split("/"))));
21277
20604
  }
21278
20605
  function runApply(context, scope, backfillDir, projectRoot2) {
21279
20606
  const options = checkOptions(context, projectRoot2);
@@ -21300,7 +20627,7 @@ function runApply(context, scope, backfillDir, projectRoot2) {
21300
20627
  derivationCounts: countDerivations(derived.actions),
21301
20628
  backups: copied
21302
20629
  };
21303
- const lines2 = [
20630
+ const lines = [
21304
20631
  `${scope.scope} scope: ${scope.dir}`,
21305
20632
  ...actionLines(proposalActions),
21306
20633
  proposalLine2(proposals.actions.length, proposals.counts),
@@ -21310,16 +20637,16 @@ function runApply(context, scope, backfillDir, projectRoot2) {
21310
20637
  ];
21311
20638
  const refused2 = proposals.counts.refused + derived.counts.refused;
21312
20639
  if (refused2 > 0)
21313
- throw new CommandExit(Math.min(refused2, REFUSED_CAP), lines2.join(`
20640
+ throw new CommandExit(Math.min(refused2, REFUSED_CAP), lines.join(`
21314
20641
  `));
21315
- report3(context, result2, lines2);
20642
+ report3(context, result2, lines);
21316
20643
  }
21317
20644
  async function runBackfill(context, seams) {
21318
20645
  const mode = readMode(context.flags);
21319
20646
  const dir = readBackfillDirectory(context.args, seams.cwd);
21320
20647
  const projectRoot2 = readConsumerRoot(context.flags["project"], seams.cwd);
21321
20648
  const scope = backfillScopeOf(dir, projectOf11(context).home);
21322
- const backfillDir = join40(scope.base, BACKFILL_PATH);
20649
+ const backfillDir = join38(scope.base, BACKFILL_PATH);
21323
20650
  if (mode === "propose") {
21324
20651
  await runPropose(context, scope, backfillDir, seams);
21325
20652
  return;
@@ -21433,13 +20760,13 @@ function createSkillCheckCommand(seams = DEFAULT_CHECK_SEAMS) {
21433
20760
  var check_default3 = createSkillCheckCommand();
21434
20761
 
21435
20762
  // src/commands/skill/demote.ts
21436
- import { existsSync as existsSync14, mkdirSync as mkdirSync10, readFileSync as readFileSync27, statSync as statSync12, writeFileSync as writeFileSync11 } from "fs";
20763
+ import { existsSync as existsSync13, mkdirSync as mkdirSync10, readFileSync as readFileSync27, statSync as statSync11, writeFileSync as writeFileSync11 } from "fs";
21437
20764
  import { dirname as dirname11, resolve as resolve14 } from "path";
21438
20765
 
21439
20766
  // src/demote/apply.ts
21440
- import { copyFileSync as copyFileSync4, existsSync as existsSync13, mkdirSync as mkdirSync9, mkdtempSync as mkdtempSync3, readdirSync as readdirSync5, readFileSync as readFileSync26, renameSync, rmSync as rmSync3, statSync as statSync11, unlinkSync, writeFileSync as writeFileSync10 } from "fs";
20767
+ import { copyFileSync as copyFileSync4, existsSync as existsSync12, mkdirSync as mkdirSync9, mkdtempSync as mkdtempSync3, readdirSync as readdirSync5, readFileSync as readFileSync26, renameSync, rmSync as rmSync3, statSync as statSync10, unlinkSync, writeFileSync as writeFileSync10 } from "fs";
21441
20768
  import { tmpdir as tmpdir3 } from "os";
21442
- import { dirname as dirname10, join as join41 } from "path";
20769
+ import { dirname as dirname10, join as join39 } from "path";
21443
20770
  var DEMOTION_ACTION_KINDS = [
21444
20771
  "demoted",
21445
20772
  "kept",
@@ -21460,13 +20787,13 @@ function learnedSkillName(path) {
21460
20787
  return base.slice(0, -MARKDOWN_EXTENSION.length);
21461
20788
  }
21462
20789
  function sourcePath(scope, path) {
21463
- return join41(scope.dir, ...path.split("/"));
20790
+ return join39(scope.dir, ...path.split("/"));
21464
20791
  }
21465
20792
  function demotedPath(scope, path) {
21466
- return join41(scope.demotedDir, ...path.split("/"));
20793
+ return join39(scope.demotedDir, ...path.split("/"));
21467
20794
  }
21468
20795
  function promotedPath(scope, path) {
21469
- return join41(scope.dir, learnedSkillName(path), SKILL_FILE);
20796
+ return join39(scope.dir, learnedSkillName(path), SKILL_FILE);
21470
20797
  }
21471
20798
  function takenInstinctIds(dir) {
21472
20799
  let names;
@@ -21479,7 +20806,7 @@ function takenInstinctIds(dir) {
21479
20806
  }
21480
20807
  function readText4(path) {
21481
20808
  try {
21482
- return statSync11(path).isFile() ? readFileSync26(path, "utf8") : null;
20809
+ return statSync10(path).isFile() ? readFileSync26(path, "utf8") : null;
21483
20810
  } catch {
21484
20811
  return null;
21485
20812
  }
@@ -21507,7 +20834,7 @@ function planKept(scope, row2, file) {
21507
20834
  return inert3(row2.path, "procedure", "kept", "procedure-shaped: left where it is, for the backfill");
21508
20835
  }
21509
20836
  const to = promotedPath(scope, row2.path);
21510
- if (existsSync13(to) || existsSync13(dirname10(to))) {
20837
+ if (existsSync12(to) || existsSync12(dirname10(to))) {
21511
20838
  return inert3(row2.path, "procedure", "refused", `kept as a skill, but ${to} is already there`);
21512
20839
  }
21513
20840
  return {
@@ -21521,7 +20848,7 @@ function planKept(scope, row2, file) {
21521
20848
  };
21522
20849
  }
21523
20850
  function checkCandidate(dir, id2, text, pathDirs) {
21524
- const path = join41(dir, `${id2}${MARKDOWN_EXTENSION}`);
20851
+ const path = join39(dir, `${id2}${MARKDOWN_EXTENSION}`);
21525
20852
  writeFileSync10(path, text, "utf8");
21526
20853
  const report4 = checkFile(path, "instinct", { projectRoot: null, pathDirs });
21527
20854
  return failureLines2(report4.issues);
@@ -21542,11 +20869,11 @@ function planDemoted(scope, row2, file, context) {
21542
20869
  if (failures.length > 0) {
21543
20870
  return inert3(row2.path, "observation", "refused", `the record ${id2} fails the checker \u2014 ${failures.join("; ")}`);
21544
20871
  }
21545
- const destination = join41(scope.instinctsDir, `${id2}${MARKDOWN_EXTENSION}`);
20872
+ const destination = join39(scope.instinctsDir, `${id2}${MARKDOWN_EXTENSION}`);
21546
20873
  const kept = demotedPath(scope, row2.path);
21547
- if (existsSync13(destination))
20874
+ if (existsSync12(destination))
21548
20875
  return inert3(row2.path, "observation", "refused", `${destination} is already there`);
21549
- if (existsSync13(kept))
20876
+ if (existsSync12(kept))
21550
20877
  return inert3(row2.path, "observation", "refused", `${kept} is already there`);
21551
20878
  context.taken.push(id2);
21552
20879
  return {
@@ -21578,7 +20905,7 @@ function planRow(scope, row2, context) {
21578
20905
  entries: undefined,
21579
20906
  absolute,
21580
20907
  hash,
21581
- mtime: statSync11(absolute).mtime.toISOString()
20908
+ mtime: statSync10(absolute).mtime.toISOString()
21582
20909
  };
21583
20910
  return verdict === "procedure" ? planKept(scope, row2, file) : planDemoted(scope, row2, file, context);
21584
20911
  }
@@ -21589,7 +20916,7 @@ function countActions(actions) {
21589
20916
  return counts;
21590
20917
  }
21591
20918
  function planDemotion(report4, scope, seams = DEFAULT_APPLY_SEAMS) {
21592
- const temp = mkdtempSync3(join41(tmpdir3(), "rafa-demote-"));
20919
+ const temp = mkdtempSync3(join39(tmpdir3(), "rafa-demote-"));
21593
20920
  const taken = [...takenInstinctIds(scope.instinctsDir)];
21594
20921
  try {
21595
20922
  const actions = report4.rows.map((row2) => planRow(scope, row2, { temp, taken, seams }));
@@ -21680,7 +21007,7 @@ Usage: ${DEMOTE_USAGE}`);
21680
21007
  }
21681
21008
  function isDirectory4(path) {
21682
21009
  try {
21683
- return statSync12(path).isDirectory();
21010
+ return statSync11(path).isDirectory();
21684
21011
  } catch {
21685
21012
  return false;
21686
21013
  }
@@ -21708,7 +21035,7 @@ function verdictLine2(report4) {
21708
21035
  return `${String(report4.rows.length)} file(s): ${parts.join(", ")}`;
21709
21036
  }
21710
21037
  function previousReport(path, context) {
21711
- if (!existsSync14(path))
21038
+ if (!existsSync13(path))
21712
21039
  return null;
21713
21040
  const parsed = parseDemotionReport(readFileSync27(path, "utf8"));
21714
21041
  if (parsed.report !== null)
@@ -21743,13 +21070,13 @@ function runWrite(context, scope) {
21743
21070
  context.output.info(line);
21744
21071
  }
21745
21072
  function reviewedReport(scope) {
21746
- if (!existsSync14(scope.reportPath)) {
21073
+ if (!existsSync13(scope.reportPath)) {
21747
21074
  throw new CommandExit(1, `\u274C No report at ${scope.reportPath}; run "rafa skill demote ${scope.dir}" first, then review it`);
21748
21075
  }
21749
21076
  const parsed = parseDemotionReport(readFileSync27(scope.reportPath, "utf8"));
21750
21077
  if (parsed.report === null) {
21751
- const lines2 = parsed.issues.map((issue2) => ` line ${String(issue2.line)} ${issue2.code}: ${issue2.message}`);
21752
- throw new CommandExit(1, [`\u274C ${scope.reportPath} does not parse:`, ...lines2].join(`
21078
+ const lines = parsed.issues.map((issue2) => ` line ${String(issue2.line)} ${issue2.code}: ${issue2.message}`);
21079
+ throw new CommandExit(1, [`\u274C ${scope.reportPath} does not parse:`, ...lines].join(`
21753
21080
  `));
21754
21081
  }
21755
21082
  if (parsed.report.status !== REVIEWED_STATUS) {
@@ -21787,16 +21114,16 @@ function runApply2(context, scope) {
21787
21114
  actions: plan2.actions,
21788
21115
  counts: plan2.counts
21789
21116
  };
21790
- const lines2 = renderApply(result2);
21117
+ const lines = renderApply(result2);
21791
21118
  if (plan2.counts.refused > 0) {
21792
- throw new CommandExit(Math.min(plan2.counts.refused, REFUSED_ROW_CAP), lines2.join(`
21119
+ throw new CommandExit(Math.min(plan2.counts.refused, REFUSED_ROW_CAP), lines.join(`
21793
21120
  `));
21794
21121
  }
21795
21122
  if (context.outputMode === "json") {
21796
21123
  context.output.result(result2);
21797
21124
  return;
21798
21125
  }
21799
- for (const line of lines2)
21126
+ for (const line of lines)
21800
21127
  context.output.info(line);
21801
21128
  }
21802
21129
  function runDemote(context, seams) {
@@ -21990,8 +21317,8 @@ var VERDICT_WORDS = Object.freeze({
21990
21317
  pending: "checks pending",
21991
21318
  none: "no checks"
21992
21319
  });
21993
- function counted3(count, noun, plural5 = `${noun}s`) {
21994
- return `${String(count)} ${count === 1 ? noun : plural5}`;
21320
+ function counted3(count, noun, plural3 = `${noun}s`) {
21321
+ return `${String(count)} ${count === 1 ? noun : plural3}`;
21995
21322
  }
21996
21323
  function info2(text) {
21997
21324
  return { level: "info", text };
@@ -22119,7 +21446,7 @@ function statusData(sections) {
22119
21446
  }
22120
21447
 
22121
21448
  // src/status/sections.ts
22122
- import { existsSync as existsSync15 } from "fs";
21449
+ import { existsSync as existsSync14 } from "fs";
22123
21450
 
22124
21451
  // src/status/blocked-count.ts
22125
21452
  async function labelsCount(options) {
@@ -22379,7 +21706,7 @@ function nextOfPlace(current, epic, walk) {
22379
21706
  return epic?.members.some((member) => member.number === line.issue) === true ? line.issue : null;
22380
21707
  }
22381
21708
  function hasBoards(root, listing2) {
22382
- if (existsSync15(positionFilePath(root)))
21709
+ if (existsSync14(positionFilePath(root)))
22383
21710
  return true;
22384
21711
  return listing2.some((issue2) => issue2.state === "OPEN" && issue2.labels.includes(ROADMAP_LABEL));
22385
21712
  }
@@ -22510,6 +21837,10 @@ async function runStatus2(context, seams) {
22510
21837
  const config = resolveProjectConfig(project, COMMAND_NAME14, (message) => {
22511
21838
  context.output.warn(message);
22512
21839
  });
21840
+ const warn3 = (message) => {
21841
+ context.output.warn(message);
21842
+ };
21843
+ await (seams.pull ?? pullBeforeRead)({ roots: { root: project.root, home: project.home }, warn: warn3 });
22513
21844
  const read3 = seams.read ?? ((input) => readStatusSections(input));
22514
21845
  const sections = await read3({ root: project.root, home: project.home, config });
22515
21846
  if (context.outputMode === "json") {