@open-tomato/rafa 0.19.0 → 0.21.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.
@@ -34,6 +34,7 @@ import {
34
34
  SESSION_ID_FLAG,
35
35
  SKILL_FILE as SKILL_FILE2,
36
36
  SKILL_SIGNALS,
37
+ SKILL_USE_CLI_VERSION,
37
38
  SPEC_BLOCKED_LABEL,
38
39
  SPEC_EXTENSION,
39
40
  SPEC_LABEL,
@@ -148,6 +149,7 @@ import {
148
149
  previousDir,
149
150
  projectRoot,
150
151
  provenanceBlock,
152
+ rankCandidates,
151
153
  readAgentDirectory,
152
154
  readAuthorTrust,
153
155
  readBlockedBy,
@@ -156,7 +158,6 @@ import {
156
158
  readChoice,
157
159
  readChoiceFlag,
158
160
  readCopyRefs,
159
- readInventoryFrontmatter,
160
161
  readManifestVersion,
161
162
  readMergeRefusal,
162
163
  readNextState,
@@ -164,6 +165,7 @@ import {
164
165
  readOpenPull,
165
166
  readPlanChanges,
166
167
  readPlans,
168
+ readRankCandidate,
167
169
  readRecords,
168
170
  readRefsText,
169
171
  readRequiredFlag,
@@ -175,6 +177,7 @@ import {
175
177
  readTextFlag,
176
178
  readTrees,
177
179
  ready_default,
180
+ recordPlanCi,
178
181
  refusal,
179
182
  refuseMisplacedRuntime,
180
183
  remainingFrom,
@@ -225,7 +228,7 @@ import {
225
228
  versionLine,
226
229
  waitForChecks,
227
230
  writeSession
228
- } from "./index-ca0fccvr.js";
231
+ } from "./index-pzpk59ma.js";
229
232
  import {
230
233
  PROBE_TIMEOUT_MS,
231
234
  findNextTask,
@@ -239,7 +242,7 @@ import {
239
242
  runPreflight,
240
243
  splitBlockerComment,
241
244
  trackerPathFor
242
- } from "./index-09p41s0n.js";
245
+ } from "./index-21qkxk63.js";
243
246
  import {
244
247
  ACTION_HEADING,
245
248
  CAUSE_HEADING,
@@ -252,6 +255,7 @@ import {
252
255
  ISSUE_TYPES,
253
256
  MARKDOWN_EXTENSION,
254
257
  SKILL_FILE,
258
+ SQLITE_MIGRATIONS,
255
259
  allRecords,
256
260
  claudeArgs,
257
261
  createGhRunner,
@@ -260,6 +264,7 @@ import {
260
264
  gitRemoteUrl,
261
265
  instinctProject,
262
266
  mergeFrontmatter,
267
+ migrateSchema,
263
268
  moduleOfLabels,
264
269
  normalizeRemote,
265
270
  readFrontmatter,
@@ -275,7 +280,7 @@ import {
275
280
  toHeldRecords,
276
281
  typeOfLabels,
277
282
  writeFrontmatter
278
- } from "./index-gb51x34c.js";
283
+ } from "./index-am92b8qp.js";
279
284
  import {
280
285
  CONFIG_DEFAULTS,
281
286
  CONFIG_FILE,
@@ -289,7 +294,7 @@ import {
289
294
  readItemBytes,
290
295
  readRafaBlocks,
291
296
  resolveTiers
292
- } from "./index-pqrpcfaz.js";
297
+ } from "./index-twbp38kz.js";
293
298
  import {
294
299
  promotable
295
300
  } from "./index-d6d0942q.js";
@@ -297,10 +302,10 @@ import {
297
302
  createCommandRegistry,
298
303
  describeRegistry,
299
304
  mountKey
300
- } from "./index-c5cecsga.js";
305
+ } from "./index-4a45ffqh.js";
301
306
  import {
302
307
  CommandExit
303
- } from "./index-vg7ycs94.js";
308
+ } from "./index-28jzpyks.js";
304
309
  import {
305
310
  MERGE_METHODS,
306
311
  RELEASE_AUTO,
@@ -312,7 +317,7 @@ import {
312
317
  isSkillTier,
313
318
  messageOf,
314
319
  parseBudgetUsd
315
- } from "./index-cedghpva.js";
320
+ } from "./index-pmkgvtfe.js";
316
321
  import {
317
322
  triggerKey
318
323
  } from "./index-wp8c28fd.js";
@@ -2287,148 +2292,6 @@ async function checkQuotes(matches, files) {
2287
2292
  return { kept, dropped: matches.length - kept.length };
2288
2293
  }
2289
2294
 
2290
- // src/inventory/search/rank.ts
2291
- import { readFileSync as readFileSync5 } from "fs";
2292
- var SEARCH_LIMIT = 12;
2293
- var MIN_WORD_LENGTH = 2;
2294
- var MIN_PREFIX_LENGTH = 4;
2295
- var FIELD_WEIGHTS = {
2296
- tags: 4,
2297
- prevents: 3,
2298
- whenToUse: 3,
2299
- description: 2,
2300
- body: 1
2301
- };
2302
- var FIELDS = ["tags", "prevents", "whenToUse", "description", "body"];
2303
- var STOP_WORDS = new Set([
2304
- "a",
2305
- "about",
2306
- "an",
2307
- "and",
2308
- "any",
2309
- "are",
2310
- "as",
2311
- "at",
2312
- "be",
2313
- "by",
2314
- "can",
2315
- "could",
2316
- "do",
2317
- "does",
2318
- "for",
2319
- "from",
2320
- "has",
2321
- "have",
2322
- "how",
2323
- "if",
2324
- "in",
2325
- "into",
2326
- "is",
2327
- "it",
2328
- "its",
2329
- "me",
2330
- "my",
2331
- "of",
2332
- "on",
2333
- "or",
2334
- "should",
2335
- "so",
2336
- "that",
2337
- "the",
2338
- "their",
2339
- "there",
2340
- "this",
2341
- "to",
2342
- "use",
2343
- "was",
2344
- "we",
2345
- "what",
2346
- "when",
2347
- "where",
2348
- "which",
2349
- "who",
2350
- "why",
2351
- "will",
2352
- "with",
2353
- "would",
2354
- "you",
2355
- "your",
2356
- "agent",
2357
- "agents",
2358
- "skill",
2359
- "skills"
2360
- ]);
2361
- function wordsOf(text) {
2362
- return text.toLowerCase().match(/[\p{L}\p{N}]+/gu) ?? [];
2363
- }
2364
- function questionWords(question) {
2365
- const kept = wordsOf(question).filter((word) => word.length >= MIN_WORD_LENGTH && !STOP_WORDS.has(word));
2366
- return [...new Set(kept)];
2367
- }
2368
- function wordsMatch(word, other) {
2369
- if (word === other)
2370
- return true;
2371
- const [shorter, longer] = word.length <= other.length ? [word, other] : [other, word];
2372
- return shorter.length >= MIN_PREFIX_LENGTH && longer.startsWith(shorter);
2373
- }
2374
- function fieldWords(candidate) {
2375
- const { record } = candidate;
2376
- const texts = {
2377
- tags: record.tags.join(" "),
2378
- prevents: record.prevents ?? "",
2379
- whenToUse: record.whenToUse ?? "",
2380
- description: candidate.description ?? "",
2381
- body: candidate.body
2382
- };
2383
- return new Map(FIELDS.map((field) => [field, [...new Set(wordsOf(texts[field]))]]));
2384
- }
2385
- function scoreCandidate(candidate, words) {
2386
- const byField = fieldWords(candidate);
2387
- const matchedWords = new Set;
2388
- const matchedFields = new Set;
2389
- let score = 0;
2390
- for (const word of words) {
2391
- for (const field of FIELDS) {
2392
- const found = (byField.get(field) ?? []).some((other) => wordsMatch(word, other));
2393
- if (!found)
2394
- continue;
2395
- score += FIELD_WEIGHTS[field];
2396
- matchedWords.add(word);
2397
- matchedFields.add(field);
2398
- }
2399
- }
2400
- return {
2401
- record: candidate.record,
2402
- score,
2403
- words: words.filter((word) => matchedWords.has(word)),
2404
- fields: FIELDS.filter((field) => matchedFields.has(field))
2405
- };
2406
- }
2407
- function rankCandidates(question, candidates, limit = SEARCH_LIMIT) {
2408
- const words = questionWords(question);
2409
- if (words.length === 0)
2410
- return [];
2411
- return candidates.map((candidate) => scoreCandidate(candidate, words)).filter((ranked) => ranked.score > 0).sort((a, b) => b.score - a.score).slice(0, limit);
2412
- }
2413
- function readRankCandidate(record, read = (path) => readFileSync5(path, "utf8")) {
2414
- let text;
2415
- try {
2416
- text = read(record.path);
2417
- } catch {
2418
- return {
2419
- record,
2420
- description: record.summary === "" ? null : record.summary,
2421
- body: ""
2422
- };
2423
- }
2424
- const document = readFrontmatterDocument(text);
2425
- return {
2426
- record,
2427
- description: readInventoryFrontmatter(document?.data ?? null).description,
2428
- body: document?.body ?? text
2429
- };
2430
- }
2431
-
2432
2295
  // src/inventory/search/scratch.ts
2433
2296
  import { copyFileSync, mkdirSync, mkdtempSync, rmSync } from "fs";
2434
2297
  import { tmpdir } from "os";
@@ -2851,7 +2714,7 @@ function createAgentShowCommand(seams = DEFAULT_AGENT_SHOW_SEAMS) {
2851
2714
  var show_default = createAgentShowCommand();
2852
2715
 
2853
2716
  // src/commands/agent/vendor.ts
2854
- import { copyFileSync as copyFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync } from "fs";
2717
+ import { copyFileSync as copyFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync } from "fs";
2855
2718
  import { basename as basename2, join as join6 } from "path";
2856
2719
  var USAGE4 = "rafa agent vendor <name>... [--force]";
2857
2720
  var NOTHING_WRITTEN = "Nothing was written.";
@@ -2938,7 +2801,7 @@ function planCopies(names, project, force, entry) {
2938
2801
  function copyOne(copy, date) {
2939
2802
  try {
2940
2803
  copyFileSync2(copy.from, copy.to);
2941
- writeFileSync(copy.to, withSourceHeader(readFileSync6(copy.to, "utf8"), copy.from, date), "utf8");
2804
+ writeFileSync(copy.to, withSourceHeader(readFileSync5(copy.to, "utf8"), copy.from, date), "utf8");
2942
2805
  } catch (error) {
2943
2806
  throw new CommandExit(1, `\u274C ${USAGE4}: agent "${copy.name}" could not be written to ${copy.to}: ${String(error)}`);
2944
2807
  }
@@ -4287,7 +4150,7 @@ import { statSync as statSync5 } from "fs";
4287
4150
  import { join as join10 } from "path";
4288
4151
 
4289
4152
  // src/board/setup-config.ts
4290
- import { readFileSync as readFileSync7 } from "fs";
4153
+ import { readFileSync as readFileSync6 } from "fs";
4291
4154
  function roadmapIssueIn(text, path) {
4292
4155
  return parseConfigText(text, path).values.roadmapIssue ?? null;
4293
4156
  }
@@ -4295,7 +4158,7 @@ function readRoadmapSetting(root) {
4295
4158
  const path = configFilePath(root);
4296
4159
  let text;
4297
4160
  try {
4298
- text = readFileSync7(path, "utf8");
4161
+ text = readFileSync6(path, "utf8");
4299
4162
  } catch (error) {
4300
4163
  return { issue: null, text: "", problem: `${path} could not be read: ${messageOf(error)}` };
4301
4164
  }
@@ -4344,7 +4207,7 @@ function withRoadmapIssue(text, issue) {
4344
4207
  }
4345
4208
 
4346
4209
  // src/board/setup.ts
4347
- import { existsSync as existsSync4, lstatSync, mkdirSync as mkdirSync3, readFileSync as readFileSync8, statSync as statSync4, writeFileSync as writeFileSync2 } from "fs";
4210
+ import { existsSync as existsSync4, lstatSync, mkdirSync as mkdirSync3, readFileSync as readFileSync7, statSync as statSync4, writeFileSync as writeFileSync2 } from "fs";
4348
4211
  import { dirname, join as join9 } from "path";
4349
4212
  import { fileURLToPath } from "url";
4350
4213
  var PREFIX = "board setup";
@@ -4479,7 +4342,7 @@ function readSpecTemplate(moduleDir = MODULE_DIR) {
4479
4342
  if (!existsSync4(source)) {
4480
4343
  throw new Error(`${PREFIX}: the spec issue template is missing: no file at ${source}`);
4481
4344
  }
4482
- return readFileSync8(source, "utf8");
4345
+ return readFileSync7(source, "utf8");
4483
4346
  }
4484
4347
  function writeSpecTemplate(root, moduleDir = MODULE_DIR) {
4485
4348
  const path = join9(root, SPEC_TEMPLATE_PATH);
@@ -6439,7 +6302,7 @@ function writeInstall(context, install) {
6439
6302
  }
6440
6303
 
6441
6304
  // src/commands/doctor-refs.ts
6442
- import { readdirSync, readFileSync as readFileSync9, statSync as statSync8 } from "fs";
6305
+ import { readdirSync, readFileSync as readFileSync8, statSync as statSync8 } from "fs";
6443
6306
  import { join as join16, resolve as resolve5 } from "path";
6444
6307
  function issueCheckCommand(issue) {
6445
6308
  return `rafa issue check ${String(issue)}`;
@@ -6509,7 +6372,7 @@ async function readCopy(file, input, verify) {
6509
6372
  const path = join16(input.specsDir, file.name);
6510
6373
  const empty = { issue: file.issue, path, suspect: 0, dangling: 0, unknown: 0 };
6511
6374
  try {
6512
- const copy = readFileSync9(resolve5(input.root, path), "utf8");
6375
+ const copy = readFileSync8(resolve5(input.root, path), "utf8");
6513
6376
  const { rows } = await readRefsText({ copy, issue: file.issue, verify: await verify() });
6514
6377
  const states = rows.map((row) => row.state);
6515
6378
  return Object.freeze({
@@ -6801,14 +6664,14 @@ function renderBoard(board) {
6801
6664
  }
6802
6665
 
6803
6666
  // src/commands/doctor-tiers.ts
6804
- import { readFileSync as readFileSync10, realpathSync as realpathSync4 } from "fs";
6667
+ import { readFileSync as readFileSync9, realpathSync as realpathSync4 } from "fs";
6805
6668
  import { basename as basename5, dirname as dirname2 } from "path";
6806
6669
  var TIERS_SECTION_TITLE = "Skill tiers";
6807
6670
  var CLAUDE_VERSION_TIMEOUT_MS = 1e4;
6808
6671
  var VERSION_PATTERN = /^\s*(\d+\.\d+\.\d+)/;
6809
6672
  function readTextFile(path) {
6810
6673
  try {
6811
- return readFileSync10(path, "utf8");
6674
+ return readFileSync9(path, "utf8");
6812
6675
  } catch {
6813
6676
  return null;
6814
6677
  }
@@ -6895,12 +6758,18 @@ function cliVersionRows(installed, pinned = SERVE_CLI_VERSION) {
6895
6758
  }
6896
6759
  return [row("cli-version", "warn", name2, `${installed} is installed, and skill delivery was probed against ${pinned}`, 'run the delivery probe again (context/inventory.md, "Serving") before trusting SERVE_CLI_VERSION')];
6897
6760
  }
6761
+ function skillUseVersionRows(installed, pinned = SKILL_USE_CLI_VERSION) {
6762
+ if (installed === null || installed === pinned)
6763
+ return [];
6764
+ return [row("skill-use-version", "warn", "Claude Code", `${installed} is installed, and the skill-use collector reads logs of ${pinned}, ` + "so skill_invocations stores the skill counts of the sessions it logs as unknown", "record the skill-use fixture again under the installed version (src/effort/skill-use.ts) before moving SKILL_USE_CLI_VERSION")];
6765
+ }
6898
6766
  function doctorTierRows(inventory, installed, readers = {}) {
6899
6767
  const readText2 = readers.readText ?? readTextFile;
6900
6768
  return [
6901
6769
  ...collisionRows(inventory.resolution.collisions),
6902
6770
  ...unreviewedRows(inventory.records, readText2),
6903
6771
  ...cliVersionRows(installed),
6772
+ ...skillUseVersionRows(installed),
6904
6773
  ...copyRows(inventory.resolution.items, readers.realPath ?? realPathOf),
6905
6774
  ...noProvenanceRows(inventory, readText2)
6906
6775
  ];
@@ -7256,7 +7125,7 @@ var collect_default = wrapPhaseZeroCommand({
7256
7125
  subject: "effort",
7257
7126
  action: "collect",
7258
7127
  summary: "collect session and commit rows into the effort store",
7259
- description: "Reads the Claude Code session logs of this repo and its commit history, and appends every" + " session and commit the effort store does not hold yet to the store `store` in" + " `.rafa/config.yaml` selects. A run over an unchanged history appends nothing. An unrecognised" + " argument, a `--since` that is no date and a config the loop cannot run on are each refused," + " one line per problem.",
7128
+ description: "Reads the Claude Code session logs of this repo and its commit history, and appends every" + " session and commit the effort store does not hold yet to the store `store` in" + " `.rafa/config.yaml` selects, then counts the skills each appended session invoked into" + " `effort.sqlite`. A run over an unchanged history appends nothing. An unrecognised" + " argument, a `--since` that is no date and a config the loop cannot run on are each refused," + " one line per problem.",
7260
7129
  args: [],
7261
7130
  flags: [
7262
7131
  {
@@ -7272,10 +7141,15 @@ var collect_default = wrapPhaseZeroCommand({
7272
7141
  },
7273
7142
  {
7274
7143
  name: "sessions",
7275
- description: "Collects session rows; `--no-sessions` skips them. Refused beside `--no-git`," + " which would leave nothing to collect.",
7144
+ description: "Collects session rows; `--no-sessions` skips them. Refused beside `--no-git`," + " which would leave nothing to collect, unless `--skills` is given.",
7276
7145
  type: "boolean",
7277
7146
  default: true
7278
7147
  },
7148
+ {
7149
+ name: "skills",
7150
+ description: "Counts the `Skill` calls of every session the store already holds, not only the ones" + " this run appends. Lifts the refusal of `--no-git` beside `--no-sessions`.",
7151
+ type: "boolean"
7152
+ },
7279
7153
  {
7280
7154
  name: "verbose",
7281
7155
  description: "Prints what each half found before the summary: the logs and plan stubs it read," + " each session read, and the commits parsed.",
@@ -7295,13 +7169,282 @@ var collect_default = wrapPhaseZeroCommand({
7295
7169
  outputs: ["text", "json"]
7296
7170
  }, collect);
7297
7171
 
7172
+ // src/effort/store/fix-schema.ts
7173
+ import { existsSync as existsSync5, renameSync, rmSync as rmSync2 } from "fs";
7174
+ import { Database } from "bun:sqlite";
7175
+ class SchemaFixRefusal extends Error {
7176
+ constructor(message) {
7177
+ super(message);
7178
+ this.name = "SchemaFixRefusal";
7179
+ }
7180
+ }
7181
+ var LIVE = "live";
7182
+ function quoted(name2) {
7183
+ return `"${name2.replaceAll('"', '""')}"`;
7184
+ }
7185
+ function tableNames(db, schema) {
7186
+ return db.query(`SELECT name FROM ${schema}.sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name`).all().map((row2) => row2.name);
7187
+ }
7188
+ function columnNames(db, schema, table) {
7189
+ return db.query("SELECT name FROM pragma_table_info(?, ?)").all(table, schema).map((row2) => row2.name);
7190
+ }
7191
+ function count(db, sql) {
7192
+ return db.query(sql).get()?.n ?? 0;
7193
+ }
7194
+ function versionOf(path) {
7195
+ const db = new Database(path, { readonly: true });
7196
+ try {
7197
+ return db.query("PRAGMA user_version").get()?.user_version ?? 0;
7198
+ } finally {
7199
+ db.close();
7200
+ }
7201
+ }
7202
+ function refuseInFlight(path) {
7203
+ for (const suffix of ["-journal", "-wal"]) {
7204
+ if (existsSync5(`${path}${suffix}`)) {
7205
+ throw new SchemaFixRefusal(`${path}${suffix} is there, so a write is in flight or was interrupted; stop whatever uses the store` + " and open it once with rafa before repairing it");
7206
+ }
7207
+ }
7208
+ }
7209
+ function refuseRemoved(db, known, live) {
7210
+ for (const table of known) {
7211
+ if (!live.has(table)) {
7212
+ throw new SchemaFixRefusal(`the store holds no table ${table}, which this rafa writes; its newer schema is not additive`);
7213
+ }
7214
+ const liveColumns = new Set(columnNames(db, LIVE, table));
7215
+ const missing = columnNames(db, "main", table).filter((column) => !liveColumns.has(column));
7216
+ if (missing.length > 0) {
7217
+ throw new SchemaFixRefusal(`the store holds no column ${missing.map((column) => `${table}.${column}`).join(", ")}, which this rafa` + " writes; its newer schema is not additive");
7218
+ }
7219
+ }
7220
+ }
7221
+ function copyKnown(db, known) {
7222
+ db.transaction(() => {
7223
+ for (const table of known) {
7224
+ const columns2 = columnNames(db, "main", table).map(quoted).join(", ");
7225
+ db.run(`INSERT INTO main.${quoted(table)} (${columns2}) SELECT ${columns2} FROM ${LIVE}.${quoted(table)}`);
7226
+ }
7227
+ })();
7228
+ }
7229
+ function checkedCounts(db, known) {
7230
+ return known.map((table) => {
7231
+ const rows = count(db, `SELECT count(*) AS n FROM main.${quoted(table)}`);
7232
+ const liveRows = count(db, `SELECT count(*) AS n FROM ${LIVE}.${quoted(table)}`);
7233
+ if (rows !== liveRows) {
7234
+ throw new SchemaFixRefusal(`the rebuilt ${table} holds ${String(rows)} rows, the store ${String(liveRows)}`);
7235
+ }
7236
+ return { table, rows };
7237
+ });
7238
+ }
7239
+ function leftBehind(db, known, live) {
7240
+ const leftTables = live.filter((table) => !known.has(table)).map((table) => ({ table, rows: count(db, `SELECT count(*) AS n FROM ${LIVE}.${quoted(table)}`) }));
7241
+ const leftColumns = live.filter((table) => known.has(table)).flatMap((table) => {
7242
+ const kept = new Set(columnNames(db, "main", table));
7243
+ return columnNames(db, LIVE, table).filter((column) => !kept.has(column)).map((column) => ({
7244
+ table,
7245
+ column,
7246
+ values: count(db, `SELECT count(*) AS n FROM ${LIVE}.${quoted(table)} WHERE ${quoted(column)} IS NOT NULL`)
7247
+ }));
7248
+ });
7249
+ return { leftTables, leftColumns };
7250
+ }
7251
+ function refuseCorrupt(db) {
7252
+ const answer = db.query("PRAGMA main.integrity_check").get()?.integrity_check;
7253
+ if (answer !== "ok")
7254
+ throw new SchemaFixRefusal(`the rebuilt store failed integrity_check: ${answer ?? "no answer"}`);
7255
+ }
7256
+ function buildParallel(path, parallelPath, migrations) {
7257
+ const db = new Database(parallelPath, { create: true, readwrite: true });
7258
+ try {
7259
+ migrateSchema(db, parallelPath, migrations);
7260
+ db.run(`ATTACH DATABASE ? AS ${LIVE}`, [path]);
7261
+ const known = tableNames(db, "main");
7262
+ const live = tableNames(db, LIVE);
7263
+ refuseRemoved(db, known, new Set(live));
7264
+ copyKnown(db, known);
7265
+ const kept = checkedCounts(db, known);
7266
+ const left = leftBehind(db, new Set(known), live);
7267
+ db.run(`DETACH DATABASE ${LIVE}`);
7268
+ refuseCorrupt(db);
7269
+ return { kept, ...left };
7270
+ } finally {
7271
+ db.close();
7272
+ }
7273
+ }
7274
+ function removeParallel(parallelPath) {
7275
+ rmSync2(parallelPath, { force: true });
7276
+ rmSync2(`${parallelPath}-journal`, { force: true });
7277
+ }
7278
+ function swapIn(path, parallelPath, backupPath) {
7279
+ renameSync(path, backupPath);
7280
+ try {
7281
+ renameSync(parallelPath, path);
7282
+ } catch (error) {
7283
+ renameSync(backupPath, path);
7284
+ throw error;
7285
+ }
7286
+ }
7287
+ function freshPaths(path, version2, stamp) {
7288
+ const parallelPath = `${path}.fix-${stamp}`;
7289
+ const backupPath = `${path}.v${String(version2)}-${stamp}.bak`;
7290
+ for (const taken of [parallelPath, backupPath]) {
7291
+ if (existsSync5(taken))
7292
+ throw new SchemaFixRefusal(`${taken} is already there; move it aside or pass another stamp`);
7293
+ }
7294
+ return { parallelPath, backupPath };
7295
+ }
7296
+ function fixStoreSchema(options) {
7297
+ const { path, dryRun, stamp } = options;
7298
+ const migrations = options.migrations ?? SQLITE_MIGRATIONS;
7299
+ const knownVersion = migrations.length;
7300
+ const nothing = { kept: [], leftTables: [], leftColumns: [], backupPath: null };
7301
+ if (!existsSync5(path))
7302
+ return { path, status: "missing", storeVersion: null, knownVersion, ...nothing };
7303
+ refuseInFlight(path);
7304
+ const storeVersion = versionOf(path);
7305
+ if (storeVersion === knownVersion)
7306
+ return { path, status: "current", storeVersion, knownVersion, ...nothing };
7307
+ if (storeVersion < knownVersion)
7308
+ return { path, status: "behind", storeVersion, knownVersion, ...nothing };
7309
+ const { parallelPath, backupPath } = freshPaths(path, storeVersion, stamp);
7310
+ let built;
7311
+ try {
7312
+ built = buildParallel(path, parallelPath, migrations);
7313
+ } catch (error) {
7314
+ removeParallel(parallelPath);
7315
+ throw error;
7316
+ }
7317
+ if (dryRun) {
7318
+ removeParallel(parallelPath);
7319
+ return { path, status: "would-rebuild", storeVersion, knownVersion, ...built, backupPath: null };
7320
+ }
7321
+ swapIn(path, parallelPath, backupPath);
7322
+ return { path, status: "rebuilt", storeVersion, knownVersion, ...built, backupPath };
7323
+ }
7324
+
7325
+ // src/commands/effort/fix-schema.ts
7326
+ var COMMAND_NAME5 = "rafa effort fix-schema";
7327
+ var FIX_SCHEMA_USAGE = "rafa effort fix-schema [--dry-run]";
7328
+ var DRY_RUN_FLAG2 = "dry-run";
7329
+ function fileStamp(date) {
7330
+ return date.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
7331
+ }
7332
+ function refuseLiveLoop(root, isAlive) {
7333
+ let records;
7334
+ try {
7335
+ records = readSessions(root, isAlive === undefined ? {} : { isAlive });
7336
+ } catch (error) {
7337
+ throw new CommandExit(1, `\u274C ${COMMAND_NAME5}: the loop's run records cannot be read, so a live loop cannot be ruled out: ${String(error)}`);
7338
+ }
7339
+ const live = records.find((record) => record.state === "running" || record.state === "paused");
7340
+ if (live === undefined)
7341
+ return;
7342
+ throw new CommandExit(1, `\u274C ${COMMAND_NAME5}: loop session ${live.sessionId} on ${live.branch} is ${live.state} and writes the store;` + ` stop it with \`rafa loop stop -s ${live.sessionId}\` first. \`--dry-run\` runs beside it.`);
7343
+ }
7344
+ function rebuildLines(result) {
7345
+ const rows = result.kept.reduce((sum, table) => sum + table.rows, 0);
7346
+ const kept = result.kept.map((table) => `${table.table} ${String(table.rows)}`).join(", ");
7347
+ const left = [
7348
+ ...result.leftTables.map((table) => `table ${table.table} (${String(table.rows)} rows)`),
7349
+ ...result.leftColumns.map((column) => `column ${column.table}.${column.column} (${String(column.values)} values)`)
7350
+ ];
7351
+ return [
7352
+ `${result.path} is at schema version ${String(result.storeVersion)}, past the ${String(result.knownVersion)} this rafa knows.`,
7353
+ `Keeps ${String(result.kept.length)} tables, ${String(rows)} rows: ${kept}.`,
7354
+ left.length === 0 ? "Leaves nothing behind." : `Leaves behind, kept only in the backup: ${left.join("; ")}.`
7355
+ ];
7356
+ }
7357
+ function renderFixSchema(result) {
7358
+ switch (result.status) {
7359
+ case "missing":
7360
+ return [`No effort store at ${result.path}: nothing to repair.`];
7361
+ case "current":
7362
+ return [`\u2705 ${result.path} is at schema version ${String(result.knownVersion)}, the one this rafa knows: nothing to repair.`];
7363
+ case "behind":
7364
+ return [
7365
+ `${result.path} is at schema version ${String(result.storeVersion)}, behind the ${String(result.knownVersion)} this rafa` + " knows; the next command that opens it migrates it. Nothing to repair."
7366
+ ];
7367
+ case "would-rebuild":
7368
+ return [
7369
+ ...rebuildLines(result),
7370
+ `\uD83D\uDD0D Dry run: the rebuild at version ${String(result.knownVersion)} was built, checked (row counts, integrity_check)` + " and deleted. Run without `--dry-run` to swap it in."
7371
+ ];
7372
+ case "rebuilt":
7373
+ return [
7374
+ ...rebuildLines(result),
7375
+ `\u2705 Rebuilt at version ${String(result.knownVersion)}. The original is kept whole at ${String(result.backupPath)};` + " rename it back to undo."
7376
+ ];
7377
+ }
7378
+ }
7379
+ function runFixSchema(context, seams) {
7380
+ expectNoArgument(context.args, FIX_SCHEMA_USAGE);
7381
+ const dryRun = readSwitch(DRY_RUN_FLAG2, context.flags[DRY_RUN_FLAG2], `Usage: ${FIX_SCHEMA_USAGE}`);
7382
+ if (context.project === null)
7383
+ throw new Error(`${COMMAND_NAME5} runs inside a project, and was handed none`);
7384
+ const root = context.project.root;
7385
+ if (!dryRun)
7386
+ refuseLiveLoop(root, seams.isAlive);
7387
+ let result;
7388
+ try {
7389
+ result = fixStoreSchema({
7390
+ path: sqliteStorePath(root),
7391
+ dryRun,
7392
+ stamp: fileStamp((seams.now ?? (() => new Date))())
7393
+ });
7394
+ } catch (error) {
7395
+ if (error instanceof SchemaFixRefusal)
7396
+ throw new CommandExit(1, `\u274C ${COMMAND_NAME5}: ${error.message}`);
7397
+ throw error;
7398
+ }
7399
+ if (context.outputMode === "json") {
7400
+ context.output.result(result);
7401
+ return;
7402
+ }
7403
+ for (const line of renderFixSchema(result))
7404
+ context.output.info(line);
7405
+ }
7406
+ function createFixSchemaCommand(seams = {}) {
7407
+ const command = {
7408
+ name: "effort fix-schema",
7409
+ subject: "effort",
7410
+ action: "fix-schema",
7411
+ summary: "rebuild an effort store a newer rafa migrated at the version this rafa knows, keeping the original",
7412
+ description: "Repairs `.rafa/effort/effort.sqlite` when it is past the schema version this rafa knows, which" + " happens when newer code opens it, such as a plan's own branch code run while an older installed rafa" + " drives the loop. It builds a parallel store beside it at this rafa's version, copies every table and" + " column this rafa knows, checks the row counts and SQLite's integrity_check, and lists what only the" + " newer schema holds. Then it renames the original to `effort.sqlite.v<version>-<stamp>.bak`, whole, and" + " moves the rebuild into its place. A store at or behind this version, or no store, is left alone. It" + " refuses, changing nothing, while a loop session under the project is running or paused, while a" + " journal beside the store shows a write in flight, and when the newer schema lacks a table or column" + " this rafa writes. With `--output=json` the outcome is the data of the terminal result event. Starts no" + " session.",
7413
+ args: [],
7414
+ flags: [
7415
+ {
7416
+ name: DRY_RUN_FLAG2,
7417
+ description: "Build and check the rebuild, print what it keeps and leaves behind, then delete it. The" + " live store is only read, so this runs beside a live loop and can be repeated.",
7418
+ type: "boolean"
7419
+ }
7420
+ ],
7421
+ examples: [
7422
+ {
7423
+ cmd: "rafa effort fix-schema --dry-run",
7424
+ note: "Prints what a rebuild would keep and leave behind, changing nothing."
7425
+ },
7426
+ {
7427
+ cmd: "rafa effort fix-schema",
7428
+ note: "Swaps the rebuild in and prints where the original was kept."
7429
+ }
7430
+ ],
7431
+ outputs: ["text", "json"],
7432
+ run: async (context) => {
7433
+ runFixSchema(context, seams);
7434
+ await Promise.resolve();
7435
+ }
7436
+ };
7437
+ return Object.freeze(command);
7438
+ }
7439
+ var fix_schema_default = createFixSchemaCommand();
7440
+
7298
7441
  // src/commands/effort/report.ts
7299
7442
  var report_default = wrapPhaseZeroCommand({
7300
7443
  name: "effort report",
7301
7444
  subject: "effort",
7302
7445
  action: "report",
7303
7446
  summary: "roll the stored session rows up per plan, then tally the task reports and list preflight halts",
7304
- description: "Rolls the session rows `rafa effort collect` stored up per plan, or per branch for a" + " session no plan claims, then tallies the stored task reports by plan, status and outcome, and" + " lists the runs whose preflight halted, one row per required item that failed. It" + " reads only what is stored, so two runs over an unchanged store print the same bytes. With" + " `--output=json` the report is the data of the terminal result event. An unrecognised argument" + " and a config the loop cannot run on are each refused, one line per problem.",
7447
+ description: "Rolls the session rows `rafa effort collect` stored up per plan, or per branch for a" + " session no plan claims, then tallies the stored task reports by plan, status and outcome, and" + " lists the runs whose preflight halted, one row per required item that failed. It" + " reads only what is stored, so two runs over an unchanged store print the same bytes. With" + " `--output=json` the report is the data of the terminal result event. With `--skills` it prints" + " the skills report instead: per plan, and per resolver within one, whether each offered skill and" + " injected lesson co-occurred with a recurrence of the failure it should prevent, read from the stored" + " rows and the skills' frontmatter, never from a session log. An unrecognised argument" + " and a config the loop cannot run on are each refused, one line per problem.",
7305
7448
  args: [],
7306
7449
  flags: [
7307
7450
  {
@@ -7314,6 +7457,16 @@ var report_default = wrapPhaseZeroCommand({
7314
7457
  description: "Keeps only the sessions whose dominant entrypoint is one of these, comma-separated." + " `sdk-cli` is the loop.",
7315
7458
  type: "string"
7316
7459
  },
7460
+ {
7461
+ name: "skills",
7462
+ description: "Prints the skills report in place of the session tables: per plan and per resolver," + " each skill's and lesson's signal, the skills invoked and never offered, M1, M2 and plan CI." + " Refused beside `--kind` or `--entrypoint`, which narrow session rows it does not read.",
7463
+ type: "boolean"
7464
+ },
7465
+ {
7466
+ name: "plan",
7467
+ description: "Keeps only these plan stubs in the skills report, comma-separated, and may be" + " repeated. A stub the store holds no row for reads as no plan. Refused without `--skills`.",
7468
+ type: "string"
7469
+ },
7317
7470
  {
7318
7471
  name: "json",
7319
7472
  description: "Deprecated: the phase 0 spelling of `--output=json`, read as that flag after one" + " deprecation line on stderr.",
@@ -7326,6 +7479,10 @@ var report_default = wrapPhaseZeroCommand({
7326
7479
  cmd: "rafa effort report",
7327
7480
  note: "Prints the per-plan tables, the task report tallies, then the preflight halts."
7328
7481
  },
7482
+ {
7483
+ cmd: "rafa effort report --skills --plan=rafa-24-know-which-skills-earn",
7484
+ note: "Prints one plan's skills and lessons with their signals, per resolver, under the fixed" + " co-occurrence line."
7485
+ },
7329
7486
  {
7330
7487
  cmd: "rafa effort report --entrypoint=sdk-cli --kind=task --output=json",
7331
7488
  note: "Writes a start event, then a result event whose data is the report of the task sessions" + " the loop ran."
@@ -7335,12 +7492,12 @@ var report_default = wrapPhaseZeroCommand({
7335
7492
  }, report);
7336
7493
 
7337
7494
  // src/commands/init.ts
7338
- import { existsSync as existsSync7, readFileSync as readFileSync15 } from "fs";
7495
+ import { existsSync as existsSync8, readFileSync as readFileSync14 } from "fs";
7339
7496
  import { homedir } from "os";
7340
7497
  import { join as join21, relative as relative6, sep as sep4 } from "path";
7341
7498
 
7342
7499
  // src/agents/vendorable.ts
7343
- import { readdirSync as readdirSync2, readFileSync as readFileSync11 } from "fs";
7500
+ import { readdirSync as readdirSync2, readFileSync as readFileSync10 } from "fs";
7344
7501
  import { isAbsolute as isAbsolute5, join as join17 } from "path";
7345
7502
  function planFiles(dir) {
7346
7503
  let entries;
@@ -7353,7 +7510,7 @@ function planFiles(dir) {
7353
7510
  }
7354
7511
  function readText2(path) {
7355
7512
  try {
7356
- return readFileSync11(path, "utf8");
7513
+ return readFileSync10(path, "utf8");
7357
7514
  } catch {
7358
7515
  return null;
7359
7516
  }
@@ -7398,7 +7555,7 @@ function vendorableAgentWarnings(agents, root) {
7398
7555
 
7399
7556
  // src/project/gitignore.ts
7400
7557
  import { createHash } from "crypto";
7401
- import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync12, writeFileSync as writeFileSync3 } from "fs";
7558
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync11, writeFileSync as writeFileSync3 } from "fs";
7402
7559
  import { dirname as dirname3, isAbsolute as isAbsolute6, join as join18 } from "path";
7403
7560
  var GITIGNORE_FILE = ".gitignore";
7404
7561
  var DIGEST_NAME = "tracking.digest";
@@ -7485,9 +7642,9 @@ function attempt(file, failure3, act) {
7485
7642
  }
7486
7643
  }
7487
7644
  function readIfPresent2(file) {
7488
- if (!existsSync5(file))
7645
+ if (!existsSync6(file))
7489
7646
  return null;
7490
- return attempt(file, "cannot be read", () => readFileSync12(file, "utf8"));
7647
+ return attempt(file, "cannot be read", () => readFileSync11(file, "utf8"));
7491
7648
  }
7492
7649
  function writeTrackingGitignore(root, flags) {
7493
7650
  requireAbsolute(root);
@@ -7550,7 +7707,7 @@ import { resolve as resolve8 } from "path";
7550
7707
 
7551
7708
  // src/project/roots.ts
7552
7709
  import { spawnSync } from "child_process";
7553
- import { accessSync, constants, readFileSync as readFileSync13, statSync as statSync9 } from "fs";
7710
+ import { accessSync, constants, readFileSync as readFileSync12, statSync as statSync9 } from "fs";
7554
7711
  import { isAbsolute as isAbsolute7, join as join19, resolve as resolve7 } from "path";
7555
7712
  var REFUSED_ROOTS = Object.freeze([
7556
7713
  { path: "/", why: "is the filesystem root" },
@@ -7571,7 +7728,7 @@ var PACKAGE_JSON = "package.json";
7571
7728
  var NOT_A_REPOSITORY = "fatal: not a git repository (or any ";
7572
7729
  var DISK_ROOTS_FILE_SYSTEM = Object.freeze({
7573
7730
  ...DISK_FILE_SYSTEM,
7574
- readFile: (path) => readFileSync13(path, "utf8"),
7731
+ readFile: (path) => readFileSync12(path, "utf8"),
7575
7732
  isDirectory: (path) => statSync9(path).isDirectory(),
7576
7733
  assertWritable: (path) => accessSync(path, constants.W_OK)
7577
7734
  });
@@ -7763,10 +7920,10 @@ function namedRoot(typed, start2, source, seams) {
7763
7920
  return chosen(fs.realpath(path), source);
7764
7921
  }
7765
7922
  function numberedCandidate(digits, found) {
7766
- const count = found.candidates.length;
7923
+ const count2 = found.candidates.length;
7767
7924
  const candidate = found.candidates[Number(digits) - 1];
7768
7925
  if (candidate === undefined) {
7769
- return problem(`no candidate is numbered ${digits}; type 1 to ${String(count)}, or a path`);
7926
+ return problem(`no candidate is numbered ${digits}; type 1 to ${String(count2)}, or a path`);
7770
7927
  }
7771
7928
  return candidate.refusal === null ? chosen(candidate.path, candidate.source) : problem(candidate.refusal.reason);
7772
7929
  }
@@ -7796,7 +7953,7 @@ async function promptForRoot(found, prompter, seams) {
7796
7953
  }
7797
7954
 
7798
7955
  // src/project/scaffold.ts
7799
- import { existsSync as existsSync6, lstatSync as lstatSync2, mkdirSync as mkdirSync5, statSync as statSync10, writeFileSync as writeFileSync4 } from "fs";
7956
+ import { existsSync as existsSync7, lstatSync as lstatSync2, mkdirSync as mkdirSync5, statSync as statSync10, writeFileSync as writeFileSync4 } from "fs";
7800
7957
  import { join as join20 } from "path";
7801
7958
  var PROJECT_TREE = Object.freeze(["specs", "plans", "runs", "effort", "instincts"]);
7802
7959
  var USER_TREE = Object.freeze(["instincts"]);
@@ -7857,7 +8014,10 @@ var CONFIG_SETTINGS_LINES = Object.freeze([
7857
8014
  "# skills: {} # name: false turns a skill off; name: project | rafa | user pins its tier",
7858
8015
  "# agents: {} # name: false turns an agent off; name: project | rafa | user pins its tier",
7859
8016
  "# routing:",
7860
- ...DEFAULT_ROUTES.map(([shape, agent]) => `# ${shape}: ${agent}`)
8017
+ ...DEFAULT_ROUTES.map(([shape, agent]) => `# ${shape}: ${agent}`),
8018
+ "# task:",
8019
+ `# skills: ${CONFIG_DEFAULTS.taskSkills} # planner | tag | none, the resolver that picks a task's skills`,
8020
+ `# lessons: ${CONFIG_DEFAULTS.taskLessons} # on | off, whether blessed lessons join a task's prompt`
7861
8021
  ]);
7862
8022
  var VERSION_LINE = `version: ${String(CONFIG_DEFAULTS.version)}`;
7863
8023
  var PROJECT_HEADER = Object.freeze([
@@ -7906,7 +8066,7 @@ function isDirectory2(path) {
7906
8066
  function directoryConflict(dir) {
7907
8067
  if (!occupied(dir) || isDirectory2(dir))
7908
8068
  return null;
7909
- return existsSync6(dir) ? `${dir} is not a directory` : `${dir} is a link to nothing`;
8069
+ return existsSync7(dir) ? `${dir} is not a directory` : `${dir} is a link to nothing`;
7910
8070
  }
7911
8071
  function layoutConflicts(layout) {
7912
8072
  const [scopeDir, ...tree] = layout.dirs;
@@ -7914,7 +8074,7 @@ function layoutConflicts(layout) {
7914
8074
  if (opening !== null)
7915
8075
  return [opening];
7916
8076
  const dirs = tree.flatMap((dir) => directoryConflict(dir) ?? []);
7917
- const file = occupied(layout.configFile) && !existsSync6(layout.configFile) ? [`${layout.configFile} is a link to nothing`] : [];
8077
+ const file = occupied(layout.configFile) && !existsSync7(layout.configFile) ? [`${layout.configFile} is a link to nothing`] : [];
7918
8078
  return [...dirs, ...file];
7919
8079
  }
7920
8080
  function scaffoldConflicts(root, home) {
@@ -7928,7 +8088,7 @@ function attempt2(path, failure3, act) {
7928
8088
  }
7929
8089
  }
7930
8090
  function ensureDirectory(path) {
7931
- if (existsSync6(path))
8091
+ if (existsSync7(path))
7932
8092
  return { path, kind: "directory", change: "unchanged" };
7933
8093
  attempt2(path, "cannot be created", () => {
7934
8094
  mkdirSync5(path, { recursive: true });
@@ -7936,7 +8096,7 @@ function ensureDirectory(path) {
7936
8096
  return { path, kind: "directory", change: "created" };
7937
8097
  }
7938
8098
  function ensureFile(path, text) {
7939
- if (existsSync6(path))
8099
+ if (existsSync7(path))
7940
8100
  return { path, kind: "file", change: "unchanged" };
7941
8101
  attempt2(path, "cannot be written", () => {
7942
8102
  writeFileSync4(path, text, { encoding: "utf8", flag: "wx" });
@@ -7959,7 +8119,7 @@ function writeUserScope(home) {
7959
8119
  import { writeFileSync as writeFileSync5 } from "fs";
7960
8120
 
7961
8121
  // src/release/setting.ts
7962
- import { readFileSync as readFileSync14 } from "fs";
8122
+ import { readFileSync as readFileSync13 } from "fs";
7963
8123
  var RELEASE_ENABLED_SETTING = "release.enabled";
7964
8124
  function releaseEnabledIn(text, path) {
7965
8125
  return parseConfigText(text, path).values.releaseEnabled ?? null;
@@ -7968,7 +8128,7 @@ function readReleaseSetting(root) {
7968
8128
  const path = configFilePath(root);
7969
8129
  let text;
7970
8130
  try {
7971
- text = readFileSync14(path, "utf8");
8131
+ text = readFileSync13(path, "utf8");
7972
8132
  } catch (error) {
7973
8133
  return { enabled: null, text: "", problem: `${path} could not be read: ${messageOf(error)}` };
7974
8134
  }
@@ -8245,7 +8405,7 @@ function resolvedConfig2(root, home, warn) {
8245
8405
  function checkGitignore(root, flags) {
8246
8406
  const file = join21(root, GITIGNORE_FILE);
8247
8407
  try {
8248
- const text = existsSync7(file) ? readFileSync15(file, "utf8") : null;
8408
+ const text = existsSync8(file) ? readFileSync14(file, "utf8") : null;
8249
8409
  withTrackingBlock(text, flags, file);
8250
8410
  } catch (error) {
8251
8411
  const message = error instanceof GitignoreError ? error.message : `rafa init: ${file} cannot be read (${messageOf(error)})`;
@@ -8276,8 +8436,8 @@ function initialise(root, start2, home, context, entry) {
8276
8436
  checkConflicts(root.path, home);
8277
8437
  const config = resolvedConfig2(root.path, home, warn);
8278
8438
  checkGitignore(root.path, config);
8279
- const configExisted = existsSync7(configFilePath(root.path));
8280
- const digestExisted = existsSync7(join21(root.path, TRACKING_DIGEST_FILE));
8439
+ const configExisted = existsSync8(configFilePath(root.path));
8440
+ const digestExisted = existsSync8(join21(root.path, TRACKING_DIGEST_FILE));
8281
8441
  const project = writeProjectScope(root.path);
8282
8442
  const tracking = applyTracking(root.path, config, warn);
8283
8443
  const writes = [...project, ...trackingWrites(tracking, digestExisted), ...writeUserScope(home)];
@@ -9100,7 +9260,7 @@ function createInstinctShowCommand() {
9100
9260
  var show_default2 = createInstinctShowCommand();
9101
9261
 
9102
9262
  // src/commands/issue/check.ts
9103
- import { readdirSync as readdirSync3, readFileSync as readFileSync16, statSync as statSync11 } from "fs";
9263
+ import { readdirSync as readdirSync3, readFileSync as readFileSync15, statSync as statSync11 } from "fs";
9104
9264
  import { join as join22, resolve as resolve10 } from "path";
9105
9265
  var CHECK_USAGE = "rafa issue check <n> [--stamp]";
9106
9266
  var STAMP_FLAG = "stamp";
@@ -9177,7 +9337,7 @@ function checkedRefLine(ref) {
9177
9337
  }
9178
9338
  function countLine(references) {
9179
9339
  const noun = references.length === 1 ? "reference" : "references";
9180
- const counts = STATE_ORDER.map((state) => [state, references.filter((ref) => ref.state === state).length]).filter(([, count]) => count > 0).map(([state, count]) => `${String(count)} ${state}`);
9340
+ const counts = STATE_ORDER.map((state) => [state, references.filter((ref) => ref.state === state).length]).filter(([, count2]) => count2 > 0).map(([state, count2]) => `${String(count2)} ${state}`);
9181
9341
  return counts.length === 0 ? "No references." : `${String(references.length)} ${noun}: ${counts.join(", ")}`;
9182
9342
  }
9183
9343
  function renderCheck2(result) {
@@ -9196,7 +9356,7 @@ async function readReferences(absolute, issue, verify, stamp) {
9196
9356
  return rows.map((row2) => checkedRef(row2, row2.stamp));
9197
9357
  }
9198
9358
  const once = memoiseVerifier(verify);
9199
- const before = await readRefsText({ copy: readFileSync16(absolute, "utf8"), issue, verify: once });
9359
+ const before = await readRefsText({ copy: readFileSync15(absolute, "utf8"), issue, verify: once });
9200
9360
  const after = await restampCopyRefs({ path: absolute, issue, verify: once });
9201
9361
  return before.rows.map((row2, index) => checkedRef(row2, after.rows[index]?.stamp ?? null));
9202
9362
  }
@@ -10603,6 +10763,11 @@ var wrapped = wrapPhaseZeroCommand({
10603
10763
  description: "How much of the plan each task session is handed: `full`, `stage` or `task`." + " Outranks `plan.inject` in `.rafa/config.yaml`. The wrap-up is handed the whole plan.",
10604
10764
  type: "string"
10605
10765
  },
10766
+ {
10767
+ name: "skills-resolver",
10768
+ description: "The resolver that picks the skills each task session is handed: `planner`, `tag`" + " or `none`. Outranks `task.skills` in `.rafa/config.yaml` for this run only.",
10769
+ type: "string"
10770
+ },
10606
10771
  {
10607
10772
  name: "runtime",
10608
10773
  description: "The installed rafa the run goes on in: a version under `~/.rafa/runtime/`, or a path" + " against the working directory to a `cli.js` or the directory holding it. Refused inside the" + " `src/` of the working directory or the project root. Typed after `loop start`.",
@@ -11082,7 +11247,7 @@ function fastForwardBase(git, plan2) {
11082
11247
 
11083
11248
  // src/commands/next.ts
11084
11249
  var NEXT_USAGE = "rafa next [--dry-run] [--yes[=<action ids>]]";
11085
- var DRY_RUN_FLAG2 = "dry-run";
11250
+ var DRY_RUN_FLAG3 = "dry-run";
11086
11251
  var STATE_MARK = "\uD83D\uDCCD";
11087
11252
  var PROPOSAL_MARK = "\uD83D\uDC49";
11088
11253
  var STOP_MARK = "\u23F9";
@@ -11103,7 +11268,7 @@ function dryRunLine(dryRun) {
11103
11268
  if (dryRun === "no-terminal") {
11104
11269
  return `${STOP_MARK} There is no terminal to answer on, so nothing ran; run rafa next where you can answer,` + ` or type --${YES_FLAG}=${BARE_YES_ACTIONS.join(",")} to allow those steps unasked.`;
11105
11270
  }
11106
- return `${STOP_MARK} --${DRY_RUN_FLAG2}: nothing ran.`;
11271
+ return `${STOP_MARK} --${DRY_RUN_FLAG3}: nothing ran.`;
11107
11272
  }
11108
11273
  function stopLine(stop, state, ceiling, dryRun) {
11109
11274
  if (stop === "dry-run")
@@ -11199,14 +11364,14 @@ function lineRefusal2(problem2) {
11199
11364
  Usage: ${NEXT_USAGE}`);
11200
11365
  }
11201
11366
  function readDryRun(flags) {
11202
- const value = flags[DRY_RUN_FLAG2];
11367
+ const value = flags[DRY_RUN_FLAG3];
11203
11368
  if (value === undefined)
11204
11369
  return false;
11205
11370
  if (typeof value === "boolean")
11206
11371
  return value;
11207
11372
  if (value === "true" || value === "false")
11208
11373
  return value === "true";
11209
- throw lineRefusal2(`--${DRY_RUN_FLAG2} takes no value, and read "${value}" as one`);
11374
+ throw lineRefusal2(`--${DRY_RUN_FLAG3} takes no value, and read "${value}" as one`);
11210
11375
  }
11211
11376
  function dryRunOf(flag, ceiling, hasTerminal) {
11212
11377
  if (flag)
@@ -11258,7 +11423,7 @@ async function runNext(context, seams) {
11258
11423
  var YES_ID_LIST = YES_ACTIONS.join(", ");
11259
11424
  var NEXT_FLAGS = Object.freeze([
11260
11425
  {
11261
- name: DRY_RUN_FLAG2,
11426
+ name: DRY_RUN_FLAG3,
11262
11427
  description: "Print where the project stands and what to do about it, and stop without asking or running it." + " A run with no terminal to answer on and no --yes does this whether or not the flag is typed.",
11263
11428
  type: "boolean"
11264
11429
  },
@@ -11395,7 +11560,7 @@ var wrapped2 = wrapPhaseZeroCommand({
11395
11560
  var create_default2 = endingWith(wrapped2);
11396
11561
 
11397
11562
  // src/commands/plan/risk.ts
11398
- import { readFileSync as readFileSync17 } from "fs";
11563
+ import { readFileSync as readFileSync16 } from "fs";
11399
11564
  import { resolve as resolve11 } from "path";
11400
11565
  var COMMAND2 = "rafa plan risk";
11401
11566
  var USAGE22 = "rafa plan risk [<plan>] [--strict]";
@@ -11423,7 +11588,7 @@ function strictRefusal(report2) {
11423
11588
  }
11424
11589
  async function readRisk(path, project, config, context, seams) {
11425
11590
  const makeRunners = seams.runners ?? spawningRunners;
11426
- return assessPlanRisk({ path, text: readFileSync17(path, "utf8") }, {
11591
+ return assessPlanRisk({ path, text: readFileSync16(path, "utf8") }, {
11427
11592
  repoRoot: project.root,
11428
11593
  home: project.home,
11429
11594
  settingSources: config.settingSources,
@@ -11506,7 +11671,7 @@ function createPlanRiskCommand(seams = {}) {
11506
11671
  var risk_default = createPlanRiskCommand();
11507
11672
 
11508
11673
  // src/commands/plan/show.ts
11509
- import { readFileSync as readFileSync18 } from "fs";
11674
+ import { readFileSync as readFileSync17 } from "fs";
11510
11675
  import { join as join23 } from "path";
11511
11676
  var USAGE23 = "rafa plan show <stub> [--tracker]";
11512
11677
  var COMMAND3 = "rafa plan show";
@@ -11528,7 +11693,7 @@ function showPlan(plans, stub, tracker) {
11528
11693
  const file = join23(plans.path, name2);
11529
11694
  if (!isFile(file))
11530
11695
  throw new CommandExit(1, missingText(stub, tracker, join23(plans.label, name2)));
11531
- const model = parsePlan(readFileSync18(file, "utf8"));
11696
+ const model = parsePlan(readFileSync17(file, "utf8"));
11532
11697
  const { header } = model;
11533
11698
  return {
11534
11699
  stub,
@@ -11707,6 +11872,7 @@ function openPrContext(context, seams = DEFAULT_PR_SEAMS) {
11707
11872
  resolveBudget: config.prResolveBudget,
11708
11873
  trustedAuthors: config.boardTrustedAuthors,
11709
11874
  roadmapIssue: config.roadmapIssue,
11875
+ planDir: config.planDir,
11710
11876
  readBranch: () => readBranch2(project.root)
11711
11877
  };
11712
11878
  }
@@ -11872,8 +12038,8 @@ function titleCell(title) {
11872
12038
  return "(no title)";
11873
12039
  return trimmed.length > TITLE_WIDTH ? `${trimmed.slice(0, TITLE_WIDTH - CUT.length)}${CUT}` : trimmed;
11874
12040
  }
11875
- function headLine3(count) {
11876
- return count === 1 ? "1 open pull request" : `${count} open pull requests`;
12041
+ function headLine3(count2) {
12042
+ return count2 === 1 ? "1 open pull request" : `${count2} open pull requests`;
11877
12043
  }
11878
12044
  function cellsOf(row2, now) {
11879
12045
  return [
@@ -11970,7 +12136,7 @@ function createPrListCommand(seams = DEFAULT_PR_SEAMS) {
11970
12136
  var list_default8 = createPrListCommand();
11971
12137
 
11972
12138
  // src/commands/pr/merge.ts
11973
- import { existsSync as existsSync9, readFileSync as readFileSync20 } from "fs";
12139
+ import { existsSync as existsSync10, readFileSync as readFileSync19 } from "fs";
11974
12140
  import { join as join25 } from "path";
11975
12141
 
11976
12142
  // src/board/roadmap-tick.ts
@@ -12126,13 +12292,13 @@ function tickSentence(result) {
12126
12292
  import {
12127
12293
  chmodSync,
12128
12294
  copyFileSync as copyFileSync3,
12129
- existsSync as existsSync8,
12295
+ existsSync as existsSync9,
12130
12296
  mkdirSync as mkdirSync6,
12131
12297
  readdirSync as readdirSync5,
12132
- readFileSync as readFileSync19,
12298
+ readFileSync as readFileSync18,
12133
12299
  realpathSync as realpathSync5,
12134
- renameSync,
12135
- rmSync as rmSync2,
12300
+ renameSync as renameSync2,
12301
+ rmSync as rmSync3,
12136
12302
  statSync as statSync12,
12137
12303
  symlinkSync
12138
12304
  } from "fs";
@@ -12202,12 +12368,12 @@ function listTrackers(repoRoot, planDir) {
12202
12368
  }
12203
12369
  function openTrackers(repoRoot, planDir) {
12204
12370
  return listTrackers(repoRoot, planDir).flatMap((path) => {
12205
- const task = findNextTask(readFileSync19(join24(repoRoot, path), "utf8"));
12371
+ const task = findNextTask(readFileSync18(join24(repoRoot, path), "utf8"));
12206
12372
  return task ? [{ path, task }] : [];
12207
12373
  });
12208
12374
  }
12209
12375
  function readManifestVersion2(repoRoot) {
12210
- const manifest = JSON.parse(readFileSync19(join24(repoRoot, "package.json"), "utf8"));
12376
+ const manifest = JSON.parse(readFileSync18(join24(repoRoot, "package.json"), "utf8"));
12211
12377
  const record = typeof manifest === "object" && manifest !== null ? manifest : {};
12212
12378
  if (record["name"] !== name) {
12213
12379
  throw new Error(`${join24(repoRoot, "package.json")} names ${JSON.stringify(record["name"])}, not ${name}: this is no rafa checkout`);
@@ -12238,41 +12404,41 @@ function copyTree(from, to) {
12238
12404
  }
12239
12405
  function replaceTree(from, to) {
12240
12406
  const staging = temporaryName(to);
12241
- rmSync2(staging, { recursive: true, force: true });
12407
+ rmSync3(staging, { recursive: true, force: true });
12242
12408
  try {
12243
12409
  const copied = copyTree(from, staging);
12244
- swapIn(staging, to);
12410
+ swapIn2(staging, to);
12245
12411
  return copied;
12246
12412
  } catch (err) {
12247
- rmSync2(staging, { recursive: true, force: true });
12413
+ rmSync3(staging, { recursive: true, force: true });
12248
12414
  throw err;
12249
12415
  }
12250
12416
  }
12251
- function swapIn(staging, to) {
12252
- if (!existsSync8(to)) {
12253
- renameSync(staging, to);
12417
+ function swapIn2(staging, to) {
12418
+ if (!existsSync9(to)) {
12419
+ renameSync2(staging, to);
12254
12420
  return;
12255
12421
  }
12256
12422
  const outgoing = `${temporaryName(to)}.outgoing`;
12257
- rmSync2(outgoing, { recursive: true, force: true });
12258
- renameSync(to, outgoing);
12423
+ rmSync3(outgoing, { recursive: true, force: true });
12424
+ renameSync2(to, outgoing);
12259
12425
  try {
12260
- renameSync(staging, to);
12426
+ renameSync2(staging, to);
12261
12427
  } catch (err) {
12262
- renameSync(outgoing, to);
12428
+ renameSync2(outgoing, to);
12263
12429
  throw err;
12264
12430
  }
12265
- rmSync2(outgoing, { recursive: true, force: true });
12431
+ rmSync3(outgoing, { recursive: true, force: true });
12266
12432
  }
12267
12433
  function relink(linkPath, target) {
12268
12434
  mkdirSync6(dirname4(linkPath), { recursive: true });
12269
12435
  const temporary = temporaryName(linkPath);
12270
- rmSync2(temporary, { force: true });
12436
+ rmSync3(temporary, { force: true });
12271
12437
  try {
12272
12438
  symlinkSync(target, temporary);
12273
- renameSync(temporary, linkPath);
12439
+ renameSync2(temporary, linkPath);
12274
12440
  } catch (err) {
12275
- rmSync2(temporary, { force: true });
12441
+ rmSync3(temporary, { force: true });
12276
12442
  throw err;
12277
12443
  }
12278
12444
  const resolved = realpathSync5(linkPath);
@@ -12332,7 +12498,7 @@ function runInstall(seams, options) {
12332
12498
  return { kind: "refused", reason: "trackers", planDir, trackers: open };
12333
12499
  const distDir = join24(repoRoot, "dist");
12334
12500
  const runtimeDir = join24(runtimeRoot, version2);
12335
- const installed = existsSync8(runtimeDir);
12501
+ const installed = existsSync9(runtimeDir);
12336
12502
  if (installed && options.force !== true) {
12337
12503
  return { kind: "refused", reason: "runtime-exists", version: version2, runtimeDir };
12338
12504
  }
@@ -12340,7 +12506,7 @@ function runInstall(seams, options) {
12340
12506
  const code = inStage("build", () => seams.build(repoRoot));
12341
12507
  if (code !== 0)
12342
12508
  throw new StageFailure("build", `the build exited ${code}`);
12343
- if (!existsSync8(join24(distDir, RUNTIME_ENTRY))) {
12509
+ if (!existsSync9(join24(distDir, RUNTIME_ENTRY))) {
12344
12510
  throw new StageFailure("build", `the build exited 0 and left no ${join24(distDir, RUNTIME_ENTRY)}`);
12345
12511
  }
12346
12512
  log(installed ? `copying ${distDir} over ${runtimeDir}, replacing it whole` : `copying ${distDir} into ${runtimeDir}`);
@@ -12373,9 +12539,9 @@ function replaceFile(source, target) {
12373
12539
  try {
12374
12540
  copyFileSync3(source, temporary);
12375
12541
  chmodSync(temporary, statSync12(source).mode & 4095);
12376
- renameSync(temporary, target);
12542
+ renameSync2(temporary, target);
12377
12543
  } catch (err) {
12378
- rmSync2(temporary, { force: true });
12544
+ rmSync3(temporary, { force: true });
12379
12545
  throw err;
12380
12546
  }
12381
12547
  }
@@ -12573,11 +12739,11 @@ async function confirmUncheckedMerge(unchecked, options) {
12573
12739
  }
12574
12740
  }
12575
12741
  function commentProblemLine(number, problem2, body) {
12576
- const quoted = body.split(`
12742
+ const quoted2 = body.split(`
12577
12743
  `).map((line) => `${INDENT3}${line}`).join(`
12578
12744
  `);
12579
12745
  return `the unchecked-merge comment was not posted on #${number}: ${problem2}. Post it yourself:
12580
- ${quoted}`;
12746
+ ${quoted2}`;
12581
12747
  }
12582
12748
  async function postUncheckedComment(pulls, number, unchecked, warn) {
12583
12749
  const body = unchecked.reading.comment;
@@ -12671,7 +12837,7 @@ function remoteHoldsBranch(git, branch, warn) {
12671
12837
  }
12672
12838
  function textOf(path) {
12673
12839
  try {
12674
- return readFileSync20(path, "utf8");
12840
+ return readFileSync19(path, "utf8");
12675
12841
  } catch {
12676
12842
  return "";
12677
12843
  }
@@ -12685,7 +12851,7 @@ function followUpsFor(pr, git) {
12685
12851
  version: facts.version,
12686
12852
  tagged: tags.ok && tags.stdout.trim() !== "",
12687
12853
  rafaCheckout: facts.rafaCheckout,
12688
- runtimeInstalled: existsSync9(join25(pr.project.home, RUNTIME_SUBDIR, facts.version))
12854
+ runtimeInstalled: existsSync10(join25(pr.project.home, RUNTIME_SUBDIR, facts.version))
12689
12855
  });
12690
12856
  }
12691
12857
  function openGh(pr, seams) {
@@ -12810,6 +12976,15 @@ async function runMerge(context, seams) {
12810
12976
  throw lineRefusal3(`No pull request #${pick.number} at ${pr.project.root}`, USAGE26);
12811
12977
  }
12812
12978
  const checks = await onProvider(`read the checks of #${pick.number}`, () => pr.pulls.checks(pick.number));
12979
+ recordPlanCi({
12980
+ repoRoot: pr.project.root,
12981
+ planDir: pr.planDir,
12982
+ pullRequest: detail,
12983
+ rows: checks.rows,
12984
+ warn: (message) => {
12985
+ context.output.warn(message);
12986
+ }
12987
+ });
12813
12988
  refuseFromGit(git, detail, checks, skipChecks);
12814
12989
  const summary = summaryLine2(detail, method);
12815
12990
  const answer2 = await askToMerge(context, pr, seams, { number: detail.number, summary, yes, skipChecks });
@@ -13066,8 +13241,8 @@ function fencedBlock(body, language) {
13066
13241
  return `${fence}${language}
13067
13242
  ${text}${fence}`;
13068
13243
  }
13069
- function counted2(count, noun) {
13070
- return count === 1 ? `1 ${noun}` : `${count} ${noun}s`;
13244
+ function counted2(count2, noun) {
13245
+ return count2 === 1 ? `1 ${noun}` : `${count2} ${noun}s`;
13071
13246
  }
13072
13247
  function excerptCaption(reading) {
13073
13248
  const shown = counted2(reading.lines.length, "line");
@@ -13236,17 +13411,17 @@ function readTriageBlock(body) {
13236
13411
  }
13237
13412
  return readFields(document);
13238
13413
  }
13239
- function quoted(value) {
13414
+ function quoted2(value) {
13240
13415
  return JSON.stringify(value);
13241
13416
  }
13242
13417
  function writeTriageBlock(fields) {
13243
13418
  return [
13244
- `head: ${quoted(fields.head)}`,
13245
- `at: ${quoted(fields.at)}`,
13246
- `class: ${quoted(fields.triageClass)}`,
13419
+ `head: ${quoted2(fields.head)}`,
13420
+ `at: ${quoted2(fields.at)}`,
13421
+ `class: ${quoted2(fields.triageClass)}`,
13247
13422
  `simple: ${String(fields.simple)}`,
13248
13423
  `attempts: ${String(fields.attempts)}`,
13249
- `files: [${fields.files.map((file) => quoted(file)).join(", ")}]`
13424
+ `files: [${fields.files.map((file) => quoted2(file)).join(", ")}]`
13250
13425
  ].join(`
13251
13426
  `);
13252
13427
  }
@@ -13276,10 +13451,10 @@ function noChecksLines(input) {
13276
13451
  return [];
13277
13452
  const number = input.pr.number;
13278
13453
  const unchecked = readUnchecked(number, input.workflowCount ?? null);
13279
- const [count = "", warning = ""] = unchecked.warning;
13454
+ const [count2 = "", warning = ""] = unchecked.warning;
13280
13455
  const yes = unchecked.yesMayAnswer ? "`--yes` may answer it" : "`--yes` is refused, so a person must answer it";
13281
13456
  return [
13282
- `- Workflows: ${count}`,
13457
+ `- Workflows: ${count2}`,
13283
13458
  `- Warning: ${warning}`,
13284
13459
  `- To merge it anyway: \`${skipChecksCommand(number)}\` (it asks first; ${yes})`
13285
13460
  ];
@@ -13671,8 +13846,8 @@ function readsAsConflicting(pr, conflictFiles) {
13671
13846
  return true;
13672
13847
  return conflictFiles.length > 0;
13673
13848
  }
13674
- function checkCount(count) {
13675
- return count === 1 ? "1 check" : `${count} checks`;
13849
+ function checkCount(count2) {
13850
+ return count2 === 1 ? "1 check" : `${count2} checks`;
13676
13851
  }
13677
13852
  function fileList(files) {
13678
13853
  const shown = files.slice(0, REASON_FILE_LIMIT).join(", ");
@@ -13683,12 +13858,12 @@ function conflictReason(files) {
13683
13858
  return files.length === 0 ? "the head conflicts with the base and no conflicting file was read" : `the head conflicts with the base on ${fileList(files)}`;
13684
13859
  }
13685
13860
  function failureReason(step, failing) {
13686
- const count = checkCount(failing.length);
13687
- return step === undefined ? `${count} failing, and no failing step was named` : `${count} failing, the failing step being "${step.name}"`;
13861
+ const count2 = checkCount(failing.length);
13862
+ return step === undefined ? `${count2} failing, and no failing step was named` : `${count2} failing, the failing step being "${step.name}"`;
13688
13863
  }
13689
13864
  function noChecksReason(number, workflowCount) {
13690
- const count = workflowCountLine(workflowCount).replace(/\.$/, "");
13691
- return `the head reports no checks at all; ${count.charAt(0).toLowerCase()}${count.slice(1)};` + ` to merge it anyway, run rafa pr merge ${number} --skip-checks`;
13865
+ const count2 = workflowCountLine(workflowCount).replace(/\.$/, "");
13866
+ return `the head reports no checks at all; ${count2.charAt(0).toLowerCase()}${count2.slice(1)};` + ` to merge it anyway, run rafa pr merge ${number} --skip-checks`;
13692
13867
  }
13693
13868
  function decideClass(input, verdict, conflicting) {
13694
13869
  const { conflictFiles, rows, step } = input;
@@ -13770,8 +13945,8 @@ function isSameHead(stored, head) {
13770
13945
  function shortHead(head) {
13771
13946
  return head.trim().slice(0, HEADLINE_HEAD_LENGTH);
13772
13947
  }
13773
- function checkCount2(count) {
13774
- return count === 1 ? "1 check" : `${count} checks`;
13948
+ function checkCount2(count2) {
13949
+ return count2 === 1 ? "1 check" : `${count2} checks`;
13775
13950
  }
13776
13951
  function storedTime(comment, block) {
13777
13952
  if (comment === null)
@@ -13873,8 +14048,8 @@ function isFreshCandidate(updatedAt, now) {
13873
14048
  const age = ageMs(updatedAt, now);
13874
14049
  return age !== null && age <= TRIAGE_FRESH_WINDOW_MS;
13875
14050
  }
13876
- function plural4(count, singular, many) {
13877
- return count === 1 ? `1 ${singular}` : `${count} ${many}`;
14051
+ function plural4(count2, singular, many) {
14052
+ return count2 === 1 ? `1 ${singular}` : `${count2} ${many}`;
13878
14053
  }
13879
14054
  function describeAge(updatedAt, now) {
13880
14055
  const age = ageMs(updatedAt, now);
@@ -13938,17 +14113,17 @@ function listing(decision, assess, rest, headline, exitCode = 0) {
13938
14113
  `)
13939
14114
  });
13940
14115
  }
13941
- function refusal6(rest, count) {
13942
- const headline = `${REFUSAL_PREFIX2}: ${plural4(count, "red pull request", "red pull requests")}, ` + "and --resolve acts on one. Run one of:";
14116
+ function refusal6(rest, count2) {
14117
+ const headline = `${REFUSAL_PREFIX2}: ${plural4(count2, "red pull request", "red pull requests")}, ` + "and --resolve acts on one. Run one of:";
13943
14118
  return listing("refuse", Object.freeze([]), rest, headline, REFUSE_EXIT_CODE);
13944
14119
  }
13945
- function someReading(fresh, stale, count) {
14120
+ function someReading(fresh, stale, count2) {
13946
14121
  const older = stale.length === 0 ? "" : `; ${plural4(stale.length, "older one is", "older ones are")} skipped`;
13947
- const headline = `${count} red: assessing ${joinNumbers(fresh)}${older}`;
14122
+ const headline = `${count2} red: assessing ${joinNumbers(fresh)}${older}`;
13948
14123
  return listing("some", fresh, stale, headline);
13949
14124
  }
13950
- function listReading(rest, count) {
13951
- const headline = count > MAX_TRIAGE_CANDIDATES ? `${count} red, more than the ${MAX_TRIAGE_CANDIDATES} a bare rafa pr triage assesses at once. ` + "Run one of:" : `${count} red, none moved in the last ${TRIAGE_FRESH_HOURS} hours. Run one of:`;
14125
+ function listReading(rest, count2) {
14126
+ const headline = count2 > MAX_TRIAGE_CANDIDATES ? `${count2} red, more than the ${MAX_TRIAGE_CANDIDATES} a bare rafa pr triage assesses at once. ` + "Run one of:" : `${count2} red, none moved in the last ${TRIAGE_FRESH_HOURS} hours. Run one of:`;
13952
14127
  return listing("list", Object.freeze([]), rest, headline);
13953
14128
  }
13954
14129
  function selectTriagePullRequests(input) {
@@ -13958,24 +14133,24 @@ function selectTriagePullRequests(input) {
13958
14133
  return plain("branch", Object.freeze([current]), `assessing #${current.number}, the pull request open on ${current.headRefName}`);
13959
14134
  }
13960
14135
  const candidates = byNumber(input.candidates);
13961
- const count = candidates.length;
14136
+ const count2 = candidates.length;
13962
14137
  const lines2 = candidates.map((pull) => skipped(pull, now, resolve13));
13963
- if (resolve13 && count > 1)
13964
- return refusal6(lines2, count);
13965
- if (count === 0) {
14138
+ if (resolve13 && count2 > 1)
14139
+ return refusal6(lines2, count2);
14140
+ if (count2 === 0) {
13966
14141
  return plain("none", Object.freeze([]), "no red pull request to triage, and no pull request on the current branch");
13967
14142
  }
13968
- if (count === 1) {
14143
+ if (count2 === 1) {
13969
14144
  const only = candidates[0];
13970
14145
  return plain("single", Object.freeze([only]), `1 red: assessing #${only.number}, last moved ${describeAge(only.updatedAt, now)}`);
13971
14146
  }
13972
- if (count > MAX_TRIAGE_CANDIDATES)
13973
- return listReading(lines2, count);
14147
+ if (count2 > MAX_TRIAGE_CANDIDATES)
14148
+ return listReading(lines2, count2);
13974
14149
  const fresh = candidates.filter((pull) => isFreshCandidate(pull.updatedAt, now));
13975
14150
  const stale = lines2.filter((one) => !one.fresh);
13976
14151
  if (fresh.length === 0)
13977
- return listReading(stale, count);
13978
- return someReading(Object.freeze(fresh), stale, count);
14152
+ return listReading(stale, count2);
14153
+ return someReading(Object.freeze(fresh), stale, count2);
13979
14154
  }
13980
14155
 
13981
14156
  // src/pr/triage/conflict.ts
@@ -13994,10 +14169,10 @@ function parseMessages(records, from) {
13994
14169
  const countRecord = records[at];
13995
14170
  if (countRecord === undefined || countRecord === "")
13996
14171
  break;
13997
- const count = Number(countRecord);
13998
- if (!Number.isInteger(count) || count < 0)
14172
+ const count2 = Number(countRecord);
14173
+ if (!Number.isInteger(count2) || count2 < 0)
13999
14174
  break;
14000
- const typeAt = at + 1 + count;
14175
+ const typeAt = at + 1 + count2;
14001
14176
  const messageAt = typeAt + 1;
14002
14177
  const type = records[typeAt];
14003
14178
  const message = records[messageAt];
@@ -14176,10 +14351,10 @@ function noChecksLines2(reading, assessment) {
14176
14351
  return [];
14177
14352
  const number = reading.detail.number;
14178
14353
  const unchecked = readUnchecked(number, workflowCountOf2(reading) ?? null);
14179
- const [count = "", warning = ""] = unchecked.warning;
14354
+ const [count2 = "", warning = ""] = unchecked.warning;
14180
14355
  const yes = unchecked.yesMayAnswer ? "--yes may answer it" : "--yes is refused, so a person must answer it";
14181
14356
  return [
14182
- `${INDENT7}Workflows: ${count}`,
14357
+ `${INDENT7}Workflows: ${count2}`,
14183
14358
  `${INDENT7}Warning: ${warning}`,
14184
14359
  `${INDENT7}To merge it anyway: ${skipChecksCommand(number)} (it asks first; ${yes})`
14185
14360
  ];
@@ -14295,7 +14470,7 @@ function withTaskBudget(plan2, usd) {
14295
14470
  }
14296
14471
 
14297
14472
  // src/pr/plans/load.ts
14298
- import { existsSync as existsSync10, readFileSync as readFileSync21 } from "fs";
14473
+ import { existsSync as existsSync11, readFileSync as readFileSync20 } from "fs";
14299
14474
  import { dirname as dirname5, join as join26 } from "path";
14300
14475
  import { fileURLToPath as fileURLToPath2 } from "url";
14301
14476
  var MODULE_DIR2 = dirname5(fileURLToPath2(import.meta.url));
@@ -14322,11 +14497,11 @@ function readPinnedPlan(triageClass, lookup = {}) {
14322
14497
  throw new Error(`No pinned resolve plan ships for ${triageClass}; ` + `one ships for ${PINNED_PLAN_CLASSES.join(", ")}`);
14323
14498
  }
14324
14499
  const candidates = pinnedPlanCandidates(lookup.moduleDir ?? MODULE_DIR2, triageClass);
14325
- const found = candidates.find((candidate) => existsSync10(candidate));
14500
+ const found = candidates.find((candidate) => existsSync11(candidate));
14326
14501
  if (found === undefined) {
14327
14502
  throw new Error(`The pinned resolve plan for ${triageClass} is missing: ` + `no file at ${candidates.join(" or ")}`);
14328
14503
  }
14329
- return readFileSync21(found, "utf8");
14504
+ return readFileSync20(found, "utf8");
14330
14505
  }
14331
14506
  var SLOT_PATTERN = /\{[A-Z][A-Z0-9_]*\}/g;
14332
14507
  var CONFLICT_SENTENCE_SLOT = "CONFLICT_SENTENCE";
@@ -14848,11 +15023,11 @@ function readMaxAttempts(flags, usage2) {
14848
15023
  if (value === undefined)
14849
15024
  return DEFAULT_MAX_ATTEMPTS;
14850
15025
  const spelled = typeof value === "boolean" ? "--max-attempts with no value" : `"${value}"`;
14851
- const count = typeof value === "string" ? Number(value) : Number.NaN;
14852
- if (!Number.isSafeInteger(count) || count < 1) {
15026
+ const count2 = typeof value === "string" ? Number(value) : Number.NaN;
15027
+ if (!Number.isSafeInteger(count2) || count2 < 1) {
14853
15028
  throw lineRefusal3(`${spelled} is no attempt count, which is a whole number from 1;` + " type the pull request number before the flags", usage2);
14854
15029
  }
14855
- return count;
15030
+ return count2;
14856
15031
  }
14857
15032
  function readBranchOrNone(pr, warn) {
14858
15033
  const fallback = "so the red pull requests are the candidates";
@@ -14950,6 +15125,14 @@ async function assessOne(options) {
14950
15125
  const { git, maxAttempts, number, pr } = options;
14951
15126
  const detail = await detailOf6(pr, number);
14952
15127
  const checks = await onProvider(`read the checks of #${number}`, () => pr.pulls.checks(number));
15128
+ recordPlanCi({
15129
+ repoRoot: pr.project.root,
15130
+ planDir: pr.planDir,
15131
+ pullRequest: detail,
15132
+ rows: checks.rows,
15133
+ warn: options.warn,
15134
+ now: () => new Date(options.at)
15135
+ });
14953
15136
  const found = await triageCommentOf(options, detail);
14954
15137
  const existing = found.comment;
14955
15138
  const rerun = readTriageRerun({
@@ -15018,7 +15201,10 @@ async function runTriage(context, seams) {
15018
15201
  wantsComment,
15019
15202
  maxAttempts,
15020
15203
  at: now(),
15021
- permissions
15204
+ permissions,
15205
+ warn: (message) => {
15206
+ context.output.warn(message);
15207
+ }
15022
15208
  });
15023
15209
  const reading = await assess();
15024
15210
  if (!wantsResolve) {
@@ -15344,7 +15530,7 @@ function createPrWaitCommand(seams = DEFAULT_WAIT_SEAMS) {
15344
15530
  var wait_default = createPrWaitCommand();
15345
15531
 
15346
15532
  // src/commands/release/status.ts
15347
- import { existsSync as existsSync11, readdirSync as readdirSync6, readFileSync as readFileSync22 } from "fs";
15533
+ import { existsSync as existsSync12, readdirSync as readdirSync6, readFileSync as readFileSync21 } from "fs";
15348
15534
  import { join as join28 } from "path";
15349
15535
  var RELEASE_STATUS_USAGE = "rafa release status [--plan=<stub>]";
15350
15536
  var UNREADABLE3 = "?";
@@ -15446,7 +15632,7 @@ function untaggedVersions(released, tags) {
15446
15632
  }
15447
15633
  function textOf2(path) {
15448
15634
  try {
15449
- return readFileSync22(path, "utf8");
15635
+ return readFileSync21(path, "utf8");
15450
15636
  } catch {
15451
15637
  return null;
15452
15638
  }
@@ -15489,7 +15675,7 @@ function readBranch2(git) {
15489
15675
  }
15490
15676
  function planStubsUnder(root, planDir) {
15491
15677
  const resolved = join28(root, planDir);
15492
- if (!existsSync11(resolved))
15678
+ if (!existsSync12(resolved))
15493
15679
  return [];
15494
15680
  try {
15495
15681
  return planStubsFromFileNames(readdirSync6(resolved));
@@ -15560,10 +15746,10 @@ function levelPhrase(level) {
15560
15746
  function pendingCell(reading) {
15561
15747
  if (reading.stub === null)
15562
15748
  return `${NOTHING}: no current plan`;
15563
- const count = reading.notes.length === 1 ? "1 note" : `${reading.notes.length} notes`;
15749
+ const count2 = reading.notes.length === 1 ? "1 note" : `${reading.notes.length} notes`;
15564
15750
  if (reading.notes.length === 0)
15565
15751
  return `${NOTHING} for ${reading.stub}`;
15566
- return `${count} for ${reading.stub}, ${levelPhrase(reading.level ?? "none")}`;
15752
+ return `${count2} for ${reading.stub}, ${levelPhrase(reading.level ?? "none")}`;
15567
15753
  }
15568
15754
  function pendingLines(reading) {
15569
15755
  return renderNoteLines(groupChangeNotes(reading.notes));
@@ -15690,7 +15876,7 @@ function createReleaseStatusCommand(seams = DEFAULT_RELEASE_SEAMS) {
15690
15876
  var status_default2 = createReleaseStatusCommand();
15691
15877
 
15692
15878
  // src/commands/release/tag.ts
15693
- import { readFileSync as readFileSync23 } from "fs";
15879
+ import { readFileSync as readFileSync22 } from "fs";
15694
15880
  import { join as join29 } from "path";
15695
15881
  var RELEASE_TAG_USAGE = "rafa release tag";
15696
15882
  var DEFAULT_RELEASE_BRANCH = "main";
@@ -15710,7 +15896,7 @@ function readBranch3(git) {
15710
15896
  }
15711
15897
  function textOf3(path) {
15712
15898
  try {
15713
- return readFileSync23(path, "utf8");
15899
+ return readFileSync22(path, "utf8");
15714
15900
  } catch {
15715
15901
  return null;
15716
15902
  }
@@ -16028,11 +16214,11 @@ import { statSync as statSync14 } from "fs";
16028
16214
  import { join as join34, resolve as resolve15 } from "path";
16029
16215
 
16030
16216
  // src/backfill/backup.ts
16031
- import { copyFileSync as copyFileSync4, existsSync as existsSync12, mkdirSync as mkdirSync8 } from "fs";
16217
+ import { copyFileSync as copyFileSync4, existsSync as existsSync13, mkdirSync as mkdirSync8 } from "fs";
16032
16218
  import { dirname as dirname6, isAbsolute as isAbsolute8, join as join31, relative as relative8, resolve as resolve13, sep as sep5 } from "path";
16033
16219
 
16034
16220
  // src/backfill/proposal-file.ts
16035
- import { readdirSync as readdirSync7, readFileSync as readFileSync24 } from "fs";
16221
+ import { readdirSync as readdirSync7, readFileSync as readFileSync23 } from "fs";
16036
16222
  import { join as join30 } from "path";
16037
16223
  var BACKFILL_PATH = join30(".rafa", "backfill");
16038
16224
  var PROPOSAL_FILE_PREFIX = "proposals-";
@@ -16198,7 +16384,7 @@ function readProposalFiles(dir) {
16198
16384
  return names.filter((name2) => isProposalFileName(name2)).sort((left, right) => left.localeCompare(right)).map((name2) => {
16199
16385
  const path = join30(dir, name2);
16200
16386
  try {
16201
- return { path, result: parseProposalFile(readFileSync24(path, "utf8")) };
16387
+ return { path, result: parseProposalFile(readFileSync23(path, "utf8")) };
16202
16388
  } catch (error) {
16203
16389
  return { path, result: unreadable(`the file cannot be read (${error instanceof Error ? error.message : String(error)})`) };
16204
16390
  }
@@ -16227,9 +16413,9 @@ function backupFile(path, options) {
16227
16413
  const backup = backupPathFor(path, options.root, options.backupDir);
16228
16414
  if (backup === null)
16229
16415
  return { path, kind: "outside", backup: null };
16230
- if (!existsSync12(path))
16416
+ if (!existsSync13(path))
16231
16417
  return { path, kind: "missing", backup: null };
16232
- if (existsSync12(backup))
16418
+ if (existsSync13(backup))
16233
16419
  return { path, kind: "kept", backup };
16234
16420
  mkdirSync8(dirname6(backup), { recursive: true });
16235
16421
  copyFileSync4(path, backup);
@@ -16254,7 +16440,7 @@ function countBackups(results) {
16254
16440
  }
16255
16441
 
16256
16442
  // src/backfill/derive.ts
16257
- import { readFileSync as readFileSync25, writeFileSync as writeFileSync7 } from "fs";
16443
+ import { readFileSync as readFileSync24, writeFileSync as writeFileSync7 } from "fs";
16258
16444
  import { basename as basename8 } from "path";
16259
16445
  var DERIVED_FIELDS = ["stack", "paths", "when_to_use"];
16260
16446
  function renderWhenToUse(trigger, prevents) {
@@ -16380,7 +16566,7 @@ function planFile(entry, options) {
16380
16566
  }
16381
16567
  let original;
16382
16568
  try {
16383
- original = readFileSync25(entry.path, "utf8");
16569
+ original = readFileSync24(entry.path, "utf8");
16384
16570
  } catch (error) {
16385
16571
  return inert(entry, "skipped", `the file cannot be read (${error instanceof Error ? error.message : String(error)})`);
16386
16572
  }
@@ -16424,7 +16610,7 @@ function planDerivation(root, options) {
16424
16610
  function applyFile(action, options) {
16425
16611
  if (action.text === null || action.original === null)
16426
16612
  return action;
16427
- const current = readFileSync25(action.path, "utf8");
16613
+ const current = readFileSync24(action.path, "utf8");
16428
16614
  if (current !== action.original) {
16429
16615
  return {
16430
16616
  ...action,
@@ -16455,7 +16641,7 @@ function applyDerivation(plan2, options) {
16455
16641
  }
16456
16642
 
16457
16643
  // src/backfill/proposal-batch.ts
16458
- import { readFileSync as readFileSync26 } from "fs";
16644
+ import { readFileSync as readFileSync25 } from "fs";
16459
16645
  import { relative as relative9 } from "path";
16460
16646
 
16461
16647
  // src/demote/report.ts
@@ -16754,7 +16940,7 @@ function readCandidate(root, entry) {
16754
16940
  return null;
16755
16941
  let text;
16756
16942
  try {
16757
- text = readFileSync26(entry.path, "utf8");
16943
+ text = readFileSync25(entry.path, "utf8");
16758
16944
  } catch {
16759
16945
  return null;
16760
16946
  }
@@ -16860,7 +17046,7 @@ function parseSessionAnswer(stdout) {
16860
17046
  }
16861
17047
 
16862
17048
  // src/backfill/propose.ts
16863
- import { mkdirSync as mkdirSync9, readFileSync as readFileSync27, writeFileSync as writeFileSync8 } from "fs";
17049
+ import { mkdirSync as mkdirSync9, readFileSync as readFileSync26, writeFileSync as writeFileSync8 } from "fs";
16864
17050
  import { join as join32, resolve as resolve14 } from "path";
16865
17051
  var PROPOSAL_ACTION_KINDS = [
16866
17052
  "applied",
@@ -17019,7 +17205,7 @@ function applyRow(root, row2, options) {
17019
17205
  }
17020
17206
  let original;
17021
17207
  try {
17022
- original = readFileSync27(path, "utf8");
17208
+ original = readFileSync26(path, "utf8");
17023
17209
  } catch {
17024
17210
  return inert2(path, row2, "refused", "the file the row names is gone");
17025
17211
  }
@@ -17058,7 +17244,7 @@ function applyProposals(files, root, options) {
17058
17244
  }
17059
17245
 
17060
17246
  // src/demote/select.ts
17061
- import { readdirSync as readdirSync8, readFileSync as readFileSync28, realpathSync as realpathSync6, statSync as statSync13 } from "fs";
17247
+ import { readdirSync as readdirSync8, readFileSync as readFileSync27, realpathSync as realpathSync6, statSync as statSync13 } from "fs";
17062
17248
  import { basename as basename9, dirname as dirname7, join as join33 } from "path";
17063
17249
  var SKILLS_DIRECTORY = "skills";
17064
17250
  var CLAUDE_DIRECTORY = ".claude";
@@ -17120,7 +17306,7 @@ function readSelected(absolute, path, entries) {
17120
17306
  let text;
17121
17307
  let mtime;
17122
17308
  try {
17123
- text = readFileSync28(absolute, "utf8");
17309
+ text = readFileSync27(absolute, "utf8");
17124
17310
  mtime = statSync13(absolute).mtime.toISOString();
17125
17311
  } catch {
17126
17312
  return null;
@@ -17505,11 +17691,11 @@ function createSkillCheckCommand(seams = DEFAULT_CHECK_SEAMS) {
17505
17691
  var check_default3 = createSkillCheckCommand();
17506
17692
 
17507
17693
  // src/commands/skill/demote.ts
17508
- import { existsSync as existsSync14, mkdirSync as mkdirSync11, readFileSync as readFileSync30, statSync as statSync16, writeFileSync as writeFileSync10 } from "fs";
17694
+ import { existsSync as existsSync15, mkdirSync as mkdirSync11, readFileSync as readFileSync29, statSync as statSync16, writeFileSync as writeFileSync10 } from "fs";
17509
17695
  import { dirname as dirname9, resolve as resolve16 } from "path";
17510
17696
 
17511
17697
  // src/demote/apply.ts
17512
- import { copyFileSync as copyFileSync5, existsSync as existsSync13, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync2, readdirSync as readdirSync9, readFileSync as readFileSync29, renameSync as renameSync2, rmSync as rmSync3, statSync as statSync15, unlinkSync, writeFileSync as writeFileSync9 } from "fs";
17698
+ import { copyFileSync as copyFileSync5, existsSync as existsSync14, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync2, readdirSync as readdirSync9, readFileSync as readFileSync28, renameSync as renameSync3, rmSync as rmSync4, statSync as statSync15, unlinkSync, writeFileSync as writeFileSync9 } from "fs";
17513
17699
  import { tmpdir as tmpdir2 } from "os";
17514
17700
  import { dirname as dirname8, join as join35 } from "path";
17515
17701
  var DEMOTION_ACTION_KINDS = [
@@ -17551,7 +17737,7 @@ function takenInstinctIds(dir) {
17551
17737
  }
17552
17738
  function readText4(path) {
17553
17739
  try {
17554
- return statSync15(path).isFile() ? readFileSync29(path, "utf8") : null;
17740
+ return statSync15(path).isFile() ? readFileSync28(path, "utf8") : null;
17555
17741
  } catch {
17556
17742
  return null;
17557
17743
  }
@@ -17579,7 +17765,7 @@ function planKept(scope, row2, file) {
17579
17765
  return inert3(row2.path, "procedure", "kept", "procedure-shaped: left where it is, for the backfill");
17580
17766
  }
17581
17767
  const to = promotedPath(scope, row2.path);
17582
- if (existsSync13(to) || existsSync13(dirname8(to))) {
17768
+ if (existsSync14(to) || existsSync14(dirname8(to))) {
17583
17769
  return inert3(row2.path, "procedure", "refused", `kept as a skill, but ${to} is already there`);
17584
17770
  }
17585
17771
  return {
@@ -17616,9 +17802,9 @@ function planDemoted(scope, row2, file, context) {
17616
17802
  }
17617
17803
  const destination = join35(scope.instinctsDir, `${id}${MARKDOWN_EXTENSION}`);
17618
17804
  const kept = demotedPath(scope, row2.path);
17619
- if (existsSync13(destination))
17805
+ if (existsSync14(destination))
17620
17806
  return inert3(row2.path, "observation", "refused", `${destination} is already there`);
17621
- if (existsSync13(kept))
17807
+ if (existsSync14(kept))
17622
17808
  return inert3(row2.path, "observation", "refused", `${kept} is already there`);
17623
17809
  context.taken.push(id);
17624
17810
  return {
@@ -17667,13 +17853,13 @@ function planDemotion(report3, scope, seams = DEFAULT_APPLY_SEAMS) {
17667
17853
  const actions = report3.rows.map((row2) => planRow(scope, row2, { temp, taken, seams }));
17668
17854
  return { scope, actions, counts: countActions(actions) };
17669
17855
  } finally {
17670
- rmSync3(temp, { recursive: true, force: true });
17856
+ rmSync4(temp, { recursive: true, force: true });
17671
17857
  }
17672
17858
  }
17673
17859
  function moveFile(move3) {
17674
17860
  mkdirSync10(dirname8(move3.to), { recursive: true });
17675
17861
  try {
17676
- renameSync2(move3.from, move3.to);
17862
+ renameSync3(move3.from, move3.to);
17677
17863
  } catch {
17678
17864
  copyFileSync5(move3.from, move3.to);
17679
17865
  unlinkSync(move3.from);
@@ -17780,9 +17966,9 @@ function verdictLine(report3) {
17780
17966
  return `${String(report3.rows.length)} file(s): ${parts.join(", ")}`;
17781
17967
  }
17782
17968
  function previousReport(path, context) {
17783
- if (!existsSync14(path))
17969
+ if (!existsSync15(path))
17784
17970
  return null;
17785
- const parsed = parseDemotionReport(readFileSync30(path, "utf8"));
17971
+ const parsed = parseDemotionReport(readFileSync29(path, "utf8"));
17786
17972
  if (parsed.report !== null)
17787
17973
  return parsed.report;
17788
17974
  context.output.warn(`the report already at ${path} does not parse, so no override in it is carried forward:` + ` ${parsed.issues.map((issue2) => `line ${String(issue2.line)} ${issue2.code}`).join(", ")}`);
@@ -17815,10 +18001,10 @@ function runWrite(context, scope) {
17815
18001
  context.output.info(line);
17816
18002
  }
17817
18003
  function reviewedReport(scope) {
17818
- if (!existsSync14(scope.reportPath)) {
18004
+ if (!existsSync15(scope.reportPath)) {
17819
18005
  throw new CommandExit(1, `\u274C No report at ${scope.reportPath}; run "rafa skill demote ${scope.dir}" first, then review it`);
17820
18006
  }
17821
- const parsed = parseDemotionReport(readFileSync30(scope.reportPath, "utf8"));
18007
+ const parsed = parseDemotionReport(readFileSync29(scope.reportPath, "utf8"));
17822
18008
  if (parsed.report === null) {
17823
18009
  const lines2 = parsed.issues.map((issue2) => ` line ${String(issue2.line)} ${issue2.code}: ${issue2.message}`);
17824
18010
  throw new CommandExit(1, [`\u274C ${scope.reportPath} does not parse:`, ...lines2].join(`
@@ -17924,7 +18110,7 @@ var demote_default = createSkillDemoteCommand();
17924
18110
 
17925
18111
  // src/commands/skill/show.ts
17926
18112
  var DEFAULT_SKILL_SHOW_SEAMS = DEFAULT_SKILL_LIST_SEAMS;
17927
- var COMMAND_NAME5 = "rafa skill show";
18113
+ var COMMAND_NAME6 = "rafa skill show";
17928
18114
  var USAGE32 = "rafa skill show <name> [--full]";
17929
18115
  function unknownSkillMessage(name2, inventory) {
17930
18116
  const agent = findShown(inventory.records, "agent", name2);
@@ -17952,7 +18138,7 @@ async function runShow3(context, seams) {
17952
18138
  const full = readSwitch("full", context.flags["full"], `Type the name ahead of it: ${USAGE32}`);
17953
18139
  const name2 = expectOneArgument(context.args, USAGE32);
17954
18140
  const project = projectOf14(context);
17955
- const { inventory, settingSources } = await projectInventory(project, context, seams, COMMAND_NAME5);
18141
+ const { inventory, settingSources } = await projectInventory(project, context, seams, COMMAND_NAME6);
17956
18142
  const warnings = warningLines(inventory);
17957
18143
  for (const warning of warnings)
17958
18144
  context.output.warn(warning);
@@ -18030,8 +18216,8 @@ var VERDICT_WORDS = Object.freeze({
18030
18216
  pending: "checks pending",
18031
18217
  none: "no checks"
18032
18218
  });
18033
- function counted3(count, noun, plural5 = `${noun}s`) {
18034
- return `${String(count)} ${count === 1 ? noun : plural5}`;
18219
+ function counted3(count2, noun, plural5 = `${noun}s`) {
18220
+ return `${String(count2)} ${count2 === 1 ? noun : plural5}`;
18035
18221
  }
18036
18222
  function info(text) {
18037
18223
  return { level: "info", text };
@@ -18279,7 +18465,7 @@ async function readStatusSections(input, seams = {}) {
18279
18465
  }
18280
18466
 
18281
18467
  // src/commands/status.ts
18282
- var COMMAND_NAME6 = "rafa status";
18468
+ var COMMAND_NAME7 = "rafa status";
18283
18469
  var STATUS_USAGE = "rafa status";
18284
18470
  var STATUS_ARGUMENT_EXIT = 2;
18285
18471
  var DEFAULT_STATUS_SEAMS = Object.freeze({});
@@ -18291,13 +18477,13 @@ function projectOf15(context) {
18291
18477
  function expectNoWord(args) {
18292
18478
  if (args.length === 0)
18293
18479
  return;
18294
- throw new CommandExit(STATUS_ARGUMENT_EXIT, `\u274C ${COMMAND_NAME6}: expected no argument, got ${String(args.length)}: ${args.join(" ")}
18480
+ throw new CommandExit(STATUS_ARGUMENT_EXIT, `\u274C ${COMMAND_NAME7}: expected no argument, got ${String(args.length)}: ${args.join(" ")}
18295
18481
  Usage: ${STATUS_USAGE}`);
18296
18482
  }
18297
18483
  async function runStatus2(context, seams) {
18298
18484
  expectNoWord(context.args);
18299
18485
  const project = projectOf15(context);
18300
- const config = resolveProjectConfig(project, COMMAND_NAME6, (message) => {
18486
+ const config = resolveProjectConfig(project, COMMAND_NAME7, (message) => {
18301
18487
  context.output.warn(message);
18302
18488
  });
18303
18489
  const read3 = seams.read ?? ((input) => readStatusSections(input));
@@ -18363,7 +18549,7 @@ var CORE_SUBJECTS = Object.freeze([
18363
18549
  { name: "loop", summary: "start a plan; stop, pause, resume, show and list its sessions" },
18364
18550
  { name: "issue", summary: "the tracker: list, show, create, comment on and move issues; mark one ready, unblock it and check its references" },
18365
18551
  { name: "pr", summary: "the pull request of a branch: one line, in full or in the browser; list, wait on, merge and triage them" },
18366
- { name: "effort", summary: "collect session and commit rows; report per plan" },
18552
+ { name: "effort", summary: "collect session and commit rows; report per plan; repair a store a newer rafa migrated" },
18367
18553
  { name: "module", summary: "list the configured modules; run an action a module provides" },
18368
18554
  { name: "agent", summary: "copy an agent definition into the project; list what a session sees" },
18369
18555
  { name: "skill", summary: "check a skills directory; list each tier; demote and backfill it" },
@@ -18400,6 +18586,7 @@ var CORE_COMMANDS = Object.freeze([
18400
18586
  triage_default,
18401
18587
  collect_default,
18402
18588
  report_default,
18589
+ fix_schema_default,
18403
18590
  list_default7,
18404
18591
  exec_default,
18405
18592
  vendor_default,