@open-tomato/rafa 0.18.0 → 0.19.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.
@@ -1,16 +1,11 @@
1
1
  // @bun
2
2
  import {
3
- CHANGE_LEVELS,
4
- FINDING_KINDS,
5
- FINDING_SIGNALS,
6
- REPORT_STATUSES,
7
3
  escapeBlockerText,
8
4
  findNextTask,
9
5
  isStampableStub,
10
6
  loadPlanPrerequisites,
11
7
  malformedPrerequisiteLines,
12
8
  parsePlan,
13
- parseReport,
14
9
  planStubFromPath,
15
10
  planStubFromPrompt,
16
11
  prerequisitesPathForPlan,
@@ -20,30 +15,46 @@ import {
20
15
  trackerPathFor,
21
16
  updateTrackerLine,
22
17
  writeTrackerBlocker
23
- } from "./index-0pdsq2hy.js";
18
+ } from "./index-09p41s0n.js";
24
19
  import {
25
20
  CORE_ADAPTER_REGISTRY,
26
21
  ClaudePlannerError,
27
22
  LONE_SURROGATE,
23
+ MARKDOWN_EXTENSION,
28
24
  PORT_VERSIONS,
25
+ SKILL_FILE,
29
26
  checkUsage,
30
27
  createGhRunner,
31
28
  createLocalTracker,
29
+ declaredNameIssue,
32
30
  getClaudeUsagePercent,
31
+ gitRemoteUrl,
33
32
  interruptClaudeSessions,
33
+ mergeFrontmatter,
34
+ normalizeRemote,
35
+ parseInstinct,
34
36
  planFilePath,
37
+ readFrontmatter,
38
+ readFrontmatterDocument,
35
39
  runClaude,
36
40
  runClaudeCaptured,
41
+ scanLayout,
37
42
  selectEffortStore,
38
43
  sqliteStorePath,
39
44
  withSqliteStore,
45
+ writeFrontmatter,
46
+ writeInstinct,
40
47
  writeSqliteStore
41
- } from "./index-at9f1z2z.js";
48
+ } from "./index-gb51x34c.js";
42
49
  import {
50
+ CHANGE_LEVELS,
43
51
  CONFIG_DEFAULTS,
44
52
  CONFIG_FILE,
45
53
  ConfigError,
46
54
  DEFAULT_ROUTING,
55
+ FINDING_KINDS,
56
+ FINDING_SIGNALS,
57
+ REPORT_STATUSES,
47
58
  SETTINGS,
48
59
  activeOutput,
49
60
  activeOutputMode,
@@ -51,23 +62,27 @@ import {
51
62
  configFilePath,
52
63
  findTierItem,
53
64
  parseConfigText,
65
+ parseReport,
54
66
  pinKey,
55
67
  pinLine,
56
68
  readItemBytes,
57
69
  readRafaBlocks,
58
70
  resolveConfig,
59
71
  resolveTiers
60
- } from "./index-jva32qe6.js";
72
+ } from "./index-pqrpcfaz.js";
73
+ import {
74
+ promotable
75
+ } from "./index-d6d0942q.js";
61
76
  import {
62
77
  DESCRIBE_BINARY,
63
78
  DESCRIBE_SCHEMA_VERSION,
64
79
  pluralOf
65
- } from "./index-kvnggvyk.js";
80
+ } from "./index-c5cecsga.js";
66
81
  import {
67
82
  CommandExit,
68
83
  SPENDS_GLYPH,
69
84
  commandSpelling
70
- } from "./index-ddf85qac.js";
85
+ } from "./index-vg7ycs94.js";
71
86
  import {
72
87
  BUDGET_FLAG,
73
88
  BUNDLED_SKILLS_DIR,
@@ -75,7 +90,6 @@ import {
75
90
  PREREQUISITE_KINDS,
76
91
  RELEASE_AUTO,
77
92
  SKILL_TIERS,
78
- __require,
79
93
  describeValue,
80
94
  isAgentName,
81
95
  isGitHubLogin,
@@ -93,7 +107,12 @@ import {
93
107
  skillTierDirectory,
94
108
  stripTaskDeclaration,
95
109
  tierExists
96
- } from "./index-944pncd8.js";
110
+ } from "./index-cedghpva.js";
111
+ import {
112
+ __require,
113
+ actionHash,
114
+ triggerKey
115
+ } from "./index-wp8c28fd.js";
97
116
 
98
117
  // src/config-load.ts
99
118
  import { existsSync, readFileSync, realpathSync } from "fs";
@@ -1914,7 +1933,7 @@ async function report(args, repoRoot) {
1914
1933
 
1915
1934
  // package.json
1916
1935
  var name = "@open-tomato/rafa";
1917
- var version = "0.18.0";
1936
+ var version = "0.19.0";
1918
1937
 
1919
1938
  // src/modules/manifest.ts
1920
1939
  var MANIFEST_VERSION = 1;
@@ -2264,429 +2283,27 @@ import { readFileSync as readFileSync8 } from "fs";
2264
2283
  import { resolve as resolve4 } from "path";
2265
2284
 
2266
2285
  // src/agents/roster.ts
2267
- import { readdirSync as readdirSync4, readFileSync as readFileSync7, statSync as statSync4 } from "fs";
2268
- import { join as join8 } from "path";
2286
+ import { readdirSync as readdirSync3, readFileSync as readFileSync7, statSync as statSync3 } from "fs";
2287
+ import { join as join7 } from "path";
2269
2288
 
2270
2289
  // src/inventory/trees.ts
2271
2290
  import { readFileSync as readFileSync4 } from "fs";
2272
- import { dirname as dirname3, join as join5, relative as relative3 } from "path";
2291
+ import { dirname as dirname3, join as join4, relative as relative2 } from "path";
2273
2292
 
2274
2293
  // src/check/run.ts
2275
2294
  import { readFileSync as readFileSync2, writeFileSync } from "fs";
2276
2295
  import { basename as basename2, dirname as dirname2 } from "path";
2277
2296
 
2278
- // src/schema/frontmatter.ts
2279
- var FRONTMATTER_FENCE = "---";
2280
- var FRONTMATTER_INDENT = 2;
2281
- function scanLine(text, offset) {
2282
- const index = text.indexOf(`
2283
- `, offset);
2284
- if (index === -1) {
2285
- return { line: text.slice(offset), newline: "", next: text.length };
2286
- }
2287
- const carriage = index > offset && text[index - 1] === "\r";
2288
- const end = carriage ? index - 1 : index;
2289
- return {
2290
- line: text.slice(offset, end),
2291
- newline: text.slice(end, index + 1),
2292
- next: index + 1
2293
- };
2294
- }
2295
- function isRecord(value) {
2296
- return typeof value === "object" && value !== null && !Array.isArray(value);
2297
- }
2298
- function parseBlock(yaml) {
2299
- let parsed;
2300
- try {
2301
- parsed = Bun.YAML.parse(yaml.replace(/\r\n/g, `
2302
- `));
2303
- } catch {
2304
- return null;
2305
- }
2306
- return isRecord(parsed) ? parsed : null;
2307
- }
2308
- function readFrontmatterDocument(text) {
2309
- const opening = scanLine(text, 0);
2310
- if (opening.line !== FRONTMATTER_FENCE || opening.newline === "")
2311
- return null;
2312
- let offset = opening.next;
2313
- while (offset < text.length) {
2314
- const scanned = scanLine(text, offset);
2315
- if (scanned.line === FRONTMATTER_FENCE) {
2316
- const data = parseBlock(text.slice(opening.next, offset));
2317
- return data === null ? null : {
2318
- data,
2319
- yaml: text.slice(opening.next, offset),
2320
- body: text.slice(scanned.next),
2321
- newline: opening.newline,
2322
- closingNewline: scanned.newline
2323
- };
2324
- }
2325
- offset = scanned.next;
2326
- }
2327
- return null;
2328
- }
2329
- function readFrontmatter(text) {
2330
- return readFrontmatterDocument(text)?.data ?? null;
2331
- }
2332
- function renderFrontmatter(data, newline = `
2333
- `) {
2334
- return Bun.YAML.stringify(data, null, FRONTMATTER_INDENT).split(`
2335
- `).map((line) => line.replace(/[ \t]+$/, "")).join(newline);
2336
- }
2337
- function mergeFrontmatter(data, changes) {
2338
- const merged = {};
2339
- for (const [key, value] of Object.entries(data)) {
2340
- if (!Object.hasOwn(changes, key))
2341
- merged[key] = value;
2342
- else if (changes[key] !== undefined)
2343
- merged[key] = changes[key];
2344
- }
2345
- for (const [key, value] of Object.entries(changes)) {
2346
- if (value !== undefined && !Object.hasOwn(data, key))
2347
- merged[key] = value;
2348
- }
2349
- return merged;
2350
- }
2351
- function writeFrontmatter(document, data) {
2352
- const block = data === undefined ? document.yaml : `${renderFrontmatter(data, document.newline)}${document.newline}`;
2353
- return [
2354
- FRONTMATTER_FENCE,
2355
- document.newline,
2356
- block,
2357
- FRONTMATTER_FENCE,
2358
- document.closingNewline,
2359
- document.body
2360
- ].join("");
2361
- }
2362
-
2363
- // src/schema/instinct.ts
2364
- import { createHash } from "crypto";
2365
-
2366
- // src/schema/project-id.ts
2367
- import { spawnSync } from "child_process";
2368
- var PROJECT_ID_LENGTH = 12;
2369
- var PROJECT_ID_PATTERN = new RegExp(`^[0-9a-f]{${PROJECT_ID_LENGTH}}$`);
2370
- function hasColonAfterAt(url) {
2371
- const at = url.indexOf("@");
2372
- return at !== -1 && url.lastIndexOf(":") > at;
2373
- }
2374
- function normalizeRemote(url) {
2375
- const stripped = url.replace(/:\/\/[^@]+@/, "://");
2376
- const network = !stripped.startsWith("file://") && (stripped.includes("://") || hasColonAfterAt(stripped));
2377
- const trimmed = stripped.replace(/^[A-Za-z][A-Za-z0-9+.-]*:\/\//, "").replace(/^[^@/:]+@([^:/]+):/, "$1/").replace(/\.git\/?$/, "").replace(/\/+$/, "");
2378
- return network ? trimmed.toLowerCase() : trimmed;
2379
- }
2380
- function gitRemoteUrl(dir) {
2381
- const result = spawnSync("git", ["remote", "get-url", "origin"], {
2382
- cwd: dir,
2383
- encoding: "utf8",
2384
- env: { ...process.env, LC_ALL: "C" }
2385
- });
2386
- if (result.error !== undefined || result.status !== 0)
2387
- return null;
2388
- const url = result.stdout.trim();
2389
- return url === "" ? null : url;
2390
- }
2391
-
2392
- // src/schema/instinct.ts
2393
- var INSTINCT_KINDS = FINDING_KINDS;
2394
- var INSTINCT_SIGNALS = FINDING_SIGNALS;
2395
- var INSTINCT_DOMAINS = [
2396
- "code-style",
2397
- "testing",
2398
- "git",
2399
- "debugging",
2400
- "workflow",
2401
- "security",
2402
- "general-best-practices"
2403
- ];
2404
- var INSTINCT_SCOPES = ["project", "user"];
2405
- var INSTINCT_SOURCES = ["task-report", "loop-observed", "imported", "demoted"];
2406
- var CONFIDENCE_MIN = 0.3;
2407
- var CONFIDENCE_MAX = 0.9;
2408
- var DEFAULT_USAGE_COUNT = 1;
2409
- var EVIDENCE_REQUIRED_SOURCE = "task-report";
2410
- var ACTION_HEADING = "## Action";
2411
- var CAUSE_HEADING = "## Cause";
2412
- var INSTINCT_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
2413
- var TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
2414
- var INSTINCT_KEY_ORDER = [
2415
- "id",
2416
- "trigger",
2417
- "kind",
2418
- "domain",
2419
- "confidence",
2420
- "usage_count",
2421
- "artifact",
2422
- "signal",
2423
- "scope",
2424
- "project_id",
2425
- "source",
2426
- "evidence",
2427
- "created_at",
2428
- "updated_at"
2429
- ];
2430
- function actionHash(action) {
2431
- return createHash("sha256").update(action.trim().toLowerCase()).digest("hex");
2432
- }
2433
- function issue(code, field2, message) {
2434
- return { code, field: field2, message };
2435
- }
2436
- function typeName(value) {
2437
- if (value === null)
2438
- return "an empty value";
2439
- if (Array.isArray(value))
2440
- return "a list";
2441
- if (typeof value === "object")
2442
- return "a mapping";
2443
- if (typeof value === "boolean")
2444
- return "a boolean";
2445
- if (typeof value === "number")
2446
- return "a number";
2447
- return `a ${typeof value}`;
2448
- }
2449
- function requiredString(data, field2) {
2450
- const value = data[field2];
2451
- if (value === undefined || value === null) {
2452
- return [issue("missing-field", field2, `${field2} is required and is absent`)];
2453
- }
2454
- if (typeof value !== "string") {
2455
- return [issue("wrong-type", field2, `${field2} must be a string, not ${typeName(value)}`)];
2456
- }
2457
- if (value.trim() === "") {
2458
- return [issue("missing-field", field2, `${field2} is required and is empty`)];
2459
- }
2460
- return [];
2461
- }
2462
- function requiredChoice(data, field2, allowed, code) {
2463
- const issues = requiredString(data, field2);
2464
- if (issues.length > 0)
2465
- return issues;
2466
- const value = data[field2];
2467
- return allowed.includes(value) ? [] : [issue(code, field2, `${field2} "${value}" is not one of ${allowed.join(", ")}`)];
2468
- }
2469
- function checkId(data) {
2470
- const issues = requiredString(data, "id");
2471
- if (issues.length > 0)
2472
- return issues;
2473
- const id = data["id"];
2474
- return INSTINCT_ID_PATTERN.test(id) ? [] : [issue("invalid-id", "id", `id "${id}" is not lowercase words joined by single hyphens`)];
2475
- }
2476
- function checkConfidence(data) {
2477
- const value = data["confidence"];
2478
- if (value === undefined || value === null) {
2479
- return [issue("missing-field", "confidence", "confidence is required and is absent")];
2480
- }
2481
- if (typeof value !== "number" || Number.isNaN(value)) {
2482
- return [issue("wrong-type", "confidence", `confidence must be a number, not ${typeName(value)}`)];
2483
- }
2484
- return value >= CONFIDENCE_MIN && value <= CONFIDENCE_MAX ? [] : [issue("confidence-out-of-range", "confidence", `confidence ${value} is outside ${CONFIDENCE_MIN} to ${CONFIDENCE_MAX}`)];
2485
- }
2486
- function checkUsageCount(data) {
2487
- const value = data["usage_count"];
2488
- if (value === undefined)
2489
- return [];
2490
- if (typeof value !== "number") {
2491
- return [issue("wrong-type", "usage_count", `usage_count must be a number, not ${typeName(value)}`)];
2492
- }
2493
- return Number.isInteger(value) && value >= DEFAULT_USAGE_COUNT ? [] : [issue("invalid-usage-count", "usage_count", `usage_count ${value} is not a whole number of at least ${DEFAULT_USAGE_COUNT}`)];
2494
- }
2495
- function checkArtifact(data) {
2496
- const value = data["artifact"];
2497
- if (value === undefined || typeof value === "string")
2498
- return [];
2499
- return [issue("wrong-type", "artifact", `artifact must be a string, not ${typeName(value)}`)];
2500
- }
2501
- function checkProjectId(data) {
2502
- const value = data["project_id"];
2503
- if (value === undefined)
2504
- return [];
2505
- if (typeof value !== "string") {
2506
- return [issue("wrong-type", "project_id", `project_id must be a string, not ${typeName(value)}`)];
2507
- }
2508
- return PROJECT_ID_PATTERN.test(value) ? [] : [issue("invalid-project-id", "project_id", `project_id "${value}" is not ${PROJECT_ID_LENGTH} lowercase hex characters`)];
2509
- }
2510
- function checkTimestamp(data, field2) {
2511
- const issues = requiredString(data, field2);
2512
- if (issues.length > 0)
2513
- return issues;
2514
- const value = data[field2];
2515
- return TIMESTAMP_PATTERN.test(value) && !Number.isNaN(Date.parse(value)) ? [] : [issue("invalid-timestamp", field2, `${field2} "${value}" is not an ISO 8601 instant such as 2026-09-11T10:00:00Z`)];
2516
- }
2517
- function isScalar(value) {
2518
- return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
2519
- }
2520
- function checkEvidenceEntry(entry, index) {
2521
- const field2 = `evidence[${index}]`;
2522
- if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
2523
- return [issue("wrong-type", field2, `evidence entries must be mappings, and this one is ${typeName(entry)}`)];
2524
- }
2525
- const entries = Object.entries(entry);
2526
- if (entries.length === 0) {
2527
- return [issue("wrong-type", field2, "evidence entries must say something, and this one is empty")];
2528
- }
2529
- return entries.flatMap(([key, value]) => isScalar(value) ? [] : [issue("wrong-type", `${field2}.${key}`, `evidence values must be text, numbers or booleans, and this one is ${typeName(value)}`)]);
2530
- }
2531
- function checkEvidence(data) {
2532
- const value = data["evidence"];
2533
- const owed = data["source"] === EVIDENCE_REQUIRED_SOURCE;
2534
- if (value === undefined || value === null)
2535
- return owed ? [missingEvidence("is absent")] : [];
2536
- if (!Array.isArray(value)) {
2537
- return [issue("wrong-type", "evidence", `evidence must be a list, not ${typeName(value)}`)];
2538
- }
2539
- if (value.length === 0)
2540
- return owed ? [missingEvidence("is an empty list")] : [];
2541
- return value.flatMap((entry, index) => checkEvidenceEntry(entry, index));
2542
- }
2543
- function missingEvidence(state) {
2544
- return issue("missing-evidence", "evidence", `a ${EVIDENCE_REQUIRED_SOURCE} record must name what it was learned from, and evidence ${state}`);
2545
- }
2546
- function checkStoredHash(data) {
2547
- if (!Object.hasOwn(data, "action_hash"))
2548
- return [];
2549
- return [issue("stored-action-hash", "action_hash", "action_hash is computed from the Action section at read time and is never stored, " + "because a hand edit would leave a stored one stale")];
2550
- }
2551
- function checkInstinctFrontmatter(data) {
2552
- return [
2553
- ...checkId(data),
2554
- ...requiredString(data, "trigger"),
2555
- ...requiredChoice(data, "kind", INSTINCT_KINDS, "unknown-kind"),
2556
- ...requiredChoice(data, "domain", INSTINCT_DOMAINS, "unknown-domain"),
2557
- ...checkConfidence(data),
2558
- ...checkUsageCount(data),
2559
- ...checkArtifact(data),
2560
- ...requiredChoice(data, "signal", INSTINCT_SIGNALS, "unknown-signal"),
2561
- ...requiredChoice(data, "scope", INSTINCT_SCOPES, "unknown-scope"),
2562
- ...checkProjectId(data),
2563
- ...requiredChoice(data, "source", INSTINCT_SOURCES, "unknown-source"),
2564
- ...checkEvidence(data),
2565
- ...checkTimestamp(data, "created_at"),
2566
- ...checkTimestamp(data, "updated_at"),
2567
- ...checkStoredHash(data)
2568
- ];
2569
- }
2570
- function isFence(line) {
2571
- return /^\s*(?:```|~~~)/.test(line);
2572
- }
2573
- function sectionText(body, heading) {
2574
- const lines = body.split(`
2575
- `).map((line) => line.replace(/\r$/, ""));
2576
- const collected = [];
2577
- let fenced = false;
2578
- let inside = false;
2579
- for (const line of lines) {
2580
- if (isFence(line))
2581
- fenced = !fenced;
2582
- else if (!fenced && /^##\s/.test(line)) {
2583
- if (inside)
2584
- break;
2585
- inside = line.trim() === heading;
2586
- continue;
2587
- }
2588
- if (inside)
2589
- collected.push(line);
2590
- }
2591
- return inside ? collected.join(`
2592
- `).trim() : null;
2593
- }
2594
- function checkSection(body, heading) {
2595
- const text = sectionText(body, heading);
2596
- if (text === null) {
2597
- return [issue("missing-section", heading, `the body carries no ${heading} section`)];
2598
- }
2599
- return text === "" ? [issue("missing-section", heading, `the ${heading} section is empty`)] : [];
2600
- }
2601
- function checkInstinctBody(body) {
2602
- return [...checkSection(body, ACTION_HEADING), ...checkSection(body, CAUSE_HEADING)];
2603
- }
2604
- function parseInstinct(text) {
2605
- const document = readFrontmatterDocument(text);
2606
- if (document === null) {
2607
- return {
2608
- issues: [issue("missing-frontmatter", "frontmatter", "the file opens with no --- block holding a YAML mapping")],
2609
- instinct: null
2610
- };
2611
- }
2612
- const issues = [
2613
- ...checkInstinctFrontmatter(document.data),
2614
- ...checkInstinctBody(document.body)
2615
- ];
2616
- return issues.length > 0 ? { issues, instinct: null } : { issues, instinct: buildInstinct(document.data, document.body) };
2617
- }
2618
- function buildInstinct(data, body) {
2619
- const action = sectionText(body, ACTION_HEADING) ?? "";
2620
- const artifact = typeof data["artifact"] === "string" && data["artifact"].trim() !== "" ? data["artifact"] : null;
2621
- return {
2622
- id: data["id"],
2623
- trigger: data["trigger"],
2624
- kind: data["kind"],
2625
- domain: data["domain"],
2626
- confidence: data["confidence"],
2627
- usageCount: typeof data["usage_count"] === "number" ? data["usage_count"] : DEFAULT_USAGE_COUNT,
2628
- artifact,
2629
- signal: data["signal"],
2630
- scope: data["scope"],
2631
- projectId: typeof data["project_id"] === "string" ? data["project_id"] : null,
2632
- source: data["source"],
2633
- evidence: Array.isArray(data["evidence"]) ? data["evidence"] : [],
2634
- createdAt: data["created_at"],
2635
- updatedAt: data["updated_at"],
2636
- action,
2637
- cause: sectionText(body, CAUSE_HEADING) ?? "",
2638
- actionHash: actionHash(action)
2639
- };
2640
- }
2641
- function instinctFrontmatter(instinct) {
2642
- const written = {
2643
- id: instinct.id,
2644
- trigger: instinct.trigger,
2645
- kind: instinct.kind,
2646
- domain: instinct.domain,
2647
- confidence: instinct.confidence,
2648
- usage_count: instinct.usageCount,
2649
- signal: instinct.signal,
2650
- scope: instinct.scope,
2651
- source: instinct.source,
2652
- created_at: instinct.createdAt,
2653
- updated_at: instinct.updatedAt
2654
- };
2655
- if (instinct.artifact !== null)
2656
- written["artifact"] = instinct.artifact;
2657
- if (instinct.projectId !== null)
2658
- written["project_id"] = instinct.projectId;
2659
- if (instinct.evidence.length > 0)
2660
- written["evidence"] = instinct.evidence;
2661
- return Object.fromEntries(INSTINCT_KEY_ORDER.filter((key) => Object.hasOwn(written, key)).map((key) => [key, written[key]]));
2662
- }
2663
- function writeInstinct(instinct, newline = `
2664
- `) {
2665
- const block = renderFrontmatter(instinctFrontmatter(instinct), newline);
2666
- return [
2667
- "---",
2668
- block,
2669
- "---",
2670
- "",
2671
- ACTION_HEADING,
2672
- instinct.action,
2673
- "",
2674
- CAUSE_HEADING,
2675
- instinct.cause,
2676
- ""
2677
- ].join(newline);
2678
- }
2679
-
2680
2297
  // src/schema/provenance.ts
2681
2298
  var PROVENANCE_FIELD = "provenance";
2682
2299
  var FIRST_PARTY = "first-party";
2683
2300
  var PROVENANCE_KEYS = ["origin", "license", "reviewed"];
2684
2301
  var REQUIRED_PROVENANCE_KEYS = ["origin", "license"];
2685
2302
  var REVIEWED_PATTERN = /^(\S(?:.*\S)?)\s+(\d{4}-\d{2}-\d{2})$/;
2686
- function issue2(code, field2, message) {
2303
+ function issue(code, field2, message) {
2687
2304
  return { code, field: field2, message };
2688
2305
  }
2689
- function typeName2(value) {
2306
+ function typeName(value) {
2690
2307
  if (value === null)
2691
2308
  return "an empty value";
2692
2309
  if (Array.isArray(value))
@@ -2715,13 +2332,13 @@ function requiredEntry(value, key) {
2715
2332
  const field2 = `${PROVENANCE_FIELD}.${key}`;
2716
2333
  const entry = value[key];
2717
2334
  if (entry === undefined || entry === null) {
2718
- return [issue2("missing-field", field2, `${field2} is required on a third-party item and is absent`)];
2335
+ return [issue("missing-field", field2, `${field2} is required on a third-party item and is absent`)];
2719
2336
  }
2720
2337
  if (typeof entry !== "string") {
2721
- return [issue2("wrong-type", field2, `${field2} must be a string, not ${typeName2(entry)}`)];
2338
+ return [issue("wrong-type", field2, `${field2} must be a string, not ${typeName(entry)}`)];
2722
2339
  }
2723
2340
  if (entry.trim() === "") {
2724
- return [issue2("missing-field", field2, `${field2} is required on a third-party item and is empty`)];
2341
+ return [issue("missing-field", field2, `${field2} is required on a third-party item and is empty`)];
2725
2342
  }
2726
2343
  return [];
2727
2344
  }
@@ -2731,33 +2348,33 @@ function reviewedEntry(value) {
2731
2348
  const field2 = `${PROVENANCE_FIELD}.reviewed`;
2732
2349
  const entry = value["reviewed"];
2733
2350
  if (typeof entry !== "string") {
2734
- return [issue2("wrong-type", field2, `${field2} must be a string, not ${typeName2(entry)}`)];
2351
+ return [issue("wrong-type", field2, `${field2} must be a string, not ${typeName(entry)}`)];
2735
2352
  }
2736
2353
  const problem = reviewProblem(entry);
2737
- return problem === null ? [] : [issue2("invalid-reviewed", field2, `${field2} "${entry}" ${problem}`)];
2354
+ return problem === null ? [] : [issue("invalid-reviewed", field2, `${field2} "${entry}" ${problem}`)];
2738
2355
  }
2739
2356
  function unknownKeys(value) {
2740
2357
  const known = PROVENANCE_KEYS;
2741
2358
  const unknown = Object.keys(value).filter((key) => !known.includes(key));
2742
- return unknown.map((key) => issue2("unknown-provenance", `${PROVENANCE_FIELD}.${key}`, `${key} is not a provenance key; the keys are ${PROVENANCE_KEYS.join(", ")}`));
2359
+ return unknown.map((key) => issue("unknown-provenance", `${PROVENANCE_FIELD}.${key}`, `${key} is not a provenance key; the keys are ${PROVENANCE_KEYS.join(", ")}`));
2743
2360
  }
2744
2361
  function stringProblems(value) {
2745
2362
  if (value.trim() === "") {
2746
- return [issue2("missing-field", PROVENANCE_FIELD, `${PROVENANCE_FIELD} is present and empty`)];
2363
+ return [issue("missing-field", PROVENANCE_FIELD, `${PROVENANCE_FIELD} is present and empty`)];
2747
2364
  }
2748
- return value === FIRST_PARTY ? [] : [issue2("unknown-provenance", PROVENANCE_FIELD, `${PROVENANCE_FIELD} "${value}" is neither ${FIRST_PARTY} nor a mapping of origin and license`)];
2365
+ return value === FIRST_PARTY ? [] : [issue("unknown-provenance", PROVENANCE_FIELD, `${PROVENANCE_FIELD} "${value}" is neither ${FIRST_PARTY} nor a mapping of origin and license`)];
2749
2366
  }
2750
2367
  function checkProvenance(data) {
2751
2368
  if (!Object.hasOwn(data, PROVENANCE_FIELD))
2752
2369
  return [];
2753
2370
  const value = data[PROVENANCE_FIELD];
2754
2371
  if (value === null) {
2755
- return [issue2("missing-field", PROVENANCE_FIELD, `${PROVENANCE_FIELD} is present and empty`)];
2372
+ return [issue("missing-field", PROVENANCE_FIELD, `${PROVENANCE_FIELD} is present and empty`)];
2756
2373
  }
2757
2374
  if (typeof value === "string")
2758
2375
  return stringProblems(value);
2759
2376
  if (!isMapping2(value)) {
2760
- return [issue2("wrong-type", PROVENANCE_FIELD, `${PROVENANCE_FIELD} must be ${FIRST_PARTY} or a mapping, not ${typeName2(value)}`)];
2377
+ return [issue("wrong-type", PROVENANCE_FIELD, `${PROVENANCE_FIELD} must be ${FIRST_PARTY} or a mapping, not ${typeName(value)}`)];
2761
2378
  }
2762
2379
  return [
2763
2380
  ...REQUIRED_PROVENANCE_KEYS.flatMap((key) => requiredEntry(value, key)),
@@ -3074,26 +2691,26 @@ function classLiteralEnd(pattern, start) {
3074
2691
  const negated = pattern[start + 1] === "!" || pattern[start + 1] === "^";
3075
2692
  return negated ? start + 2 : start + 1;
3076
2693
  }
3077
- function issue3(code, field2, message) {
2694
+ function issue2(code, field2, message) {
3078
2695
  return { code, field: field2, message };
3079
2696
  }
3080
2697
  function isFilled(value) {
3081
2698
  return typeof value === "string" && value.trim() !== "";
3082
2699
  }
3083
- function requiredString2(data, field2) {
2700
+ function requiredString(data, field2) {
3084
2701
  const value = data[field2];
3085
2702
  if (value === undefined || value === null) {
3086
- return [issue3("missing-field", field2, `${field2} is required and is absent`)];
2703
+ return [issue2("missing-field", field2, `${field2} is required and is absent`)];
3087
2704
  }
3088
2705
  if (typeof value !== "string") {
3089
- return [issue3("wrong-type", field2, `${field2} must be a string, not ${typeName3(value)}`)];
2706
+ return [issue2("wrong-type", field2, `${field2} must be a string, not ${typeName2(value)}`)];
3090
2707
  }
3091
2708
  if (value.trim() === "") {
3092
- return [issue3("missing-field", field2, `${field2} is required and is empty`)];
2709
+ return [issue2("missing-field", field2, `${field2} is required and is empty`)];
3093
2710
  }
3094
2711
  return [];
3095
2712
  }
3096
- function typeName3(value) {
2713
+ function typeName2(value) {
3097
2714
  if (value === null)
3098
2715
  return "an empty value";
3099
2716
  if (Array.isArray(value))
@@ -3107,29 +2724,29 @@ function typeName3(value) {
3107
2724
  return `a ${typeof value}`;
3108
2725
  }
3109
2726
  function checkName(data) {
3110
- const issues = requiredString2(data, "name");
2727
+ const issues = requiredString(data, "name");
3111
2728
  if (issues.length > 0)
3112
2729
  return issues;
3113
2730
  const name2 = data["name"];
3114
- return isSkillName(name2) ? [] : [issue3("invalid-name", "name", `name "${name2}" is not lowercase words joined by single hyphens`)];
2731
+ return isSkillName(name2) ? [] : [issue2("invalid-name", "name", `name "${name2}" is not lowercase words joined by single hyphens`)];
3115
2732
  }
3116
2733
  function checkDescription(data) {
3117
- const issues = requiredString2(data, "description");
2734
+ const issues = requiredString(data, "description");
3118
2735
  if (issues.length > 0)
3119
2736
  return issues;
3120
2737
  const length = countCharacters(data["description"]);
3121
- return length < DESCRIPTION_LIMIT ? [] : [issue3("description-too-long", "description", `description is ${length} characters, and the limit is ${DESCRIPTION_LIMIT}`)];
2738
+ return length < DESCRIPTION_LIMIT ? [] : [issue2("description-too-long", "description", `description is ${length} characters, and the limit is ${DESCRIPTION_LIMIT}`)];
3122
2739
  }
3123
2740
  function requiredStringList(data, field2) {
3124
2741
  const value = data[field2];
3125
2742
  if (value === undefined || value === null) {
3126
- return [issue3("missing-field", field2, `${field2} is required and is absent`)];
2743
+ return [issue2("missing-field", field2, `${field2} is required and is absent`)];
3127
2744
  }
3128
2745
  if (!Array.isArray(value)) {
3129
- return [issue3("wrong-type", field2, `${field2} must be a list, not ${typeName3(value)}`)];
2746
+ return [issue2("wrong-type", field2, `${field2} must be a list, not ${typeName2(value)}`)];
3130
2747
  }
3131
2748
  if (value.length === 0) {
3132
- return [issue3("missing-field", field2, `${field2} is required and is an empty list`)];
2749
+ return [issue2("missing-field", field2, `${field2} is required and is an empty list`)];
3133
2750
  }
3134
2751
  return entryTypeIssues(value, field2);
3135
2752
  }
@@ -3137,7 +2754,7 @@ function entryTypeIssues(values, field2) {
3137
2754
  const issues = [];
3138
2755
  for (const [index, entry] of values.entries()) {
3139
2756
  if (!isFilled(entry)) {
3140
- issues.push(issue3("wrong-type", `${field2}[${index}]`, `${field2} entries must be non-empty strings, and this one is ${typeName3(entry)}`));
2757
+ issues.push(issue2("wrong-type", `${field2}[${index}]`, `${field2} entries must be non-empty strings, and this one is ${typeName2(entry)}`));
3141
2758
  }
3142
2759
  }
3143
2760
  return issues;
@@ -3148,36 +2765,36 @@ function checkStack(data) {
3148
2765
  return issues;
3149
2766
  const values = data["stack"];
3150
2767
  if (values.length > 1 && values.includes(AGNOSTIC_STACK)) {
3151
- return [issue3("unknown-stack", "stack", `${AGNOSTIC_STACK} is either the whole stack list or no part of it`)];
2768
+ return [issue2("unknown-stack", "stack", `${AGNOSTIC_STACK} is either the whole stack list or no part of it`)];
3152
2769
  }
3153
2770
  const unknown = new Set(unknownStacks(values));
3154
- return values.flatMap((name2, index) => unknown.has(name2) ? [issue3("unknown-stack", `stack[${index}]`, `stack "${name2}" is not a vocabulary name`)] : []);
2771
+ return values.flatMap((name2, index) => unknown.has(name2) ? [issue2("unknown-stack", `stack[${index}]`, `stack "${name2}" is not a vocabulary name`)] : []);
3155
2772
  }
3156
2773
  function checkWhenToUse(data) {
3157
2774
  const issues = optionalString(data, "when_to_use");
3158
2775
  if (issues.length > 0)
3159
2776
  return issues;
3160
2777
  const length = listingLength(data);
3161
- return length < LISTING_LIMIT ? [] : [issue3("listing-too-long", "description + when_to_use", `description and when_to_use are ${length} characters together, ` + `and Claude Code truncates the listing at ${LISTING_LIMIT}`)];
2778
+ return length < LISTING_LIMIT ? [] : [issue2("listing-too-long", "description + when_to_use", `description and when_to_use are ${length} characters together, ` + `and Claude Code truncates the listing at ${LISTING_LIMIT}`)];
3162
2779
  }
3163
2780
  function optionalString(data, field2) {
3164
2781
  if (!Object.hasOwn(data, field2))
3165
2782
  return [];
3166
- return requiredString2(data, field2);
2783
+ return requiredString(data, field2);
3167
2784
  }
3168
2785
  function checkTrigger(data) {
3169
2786
  const issues = [...optionalString(data, "prevents"), ...optionalString(data, "signal")];
3170
2787
  const hasPrevents = Object.hasOwn(data, "prevents");
3171
2788
  const hasSignal = Object.hasOwn(data, "signal");
3172
2789
  if (hasPrevents && !hasSignal) {
3173
- issues.push(issue3("unpaired-prevents", "signal", "prevents names a failure, so signal must say whether it announces itself"));
2790
+ issues.push(issue2("unpaired-prevents", "signal", "prevents names a failure, so signal must say whether it announces itself"));
3174
2791
  }
3175
2792
  if (hasSignal && !hasPrevents) {
3176
- issues.push(issue3("unpaired-prevents", "prevents", "signal describes a failure, so prevents must name the failure it describes"));
2793
+ issues.push(issue2("unpaired-prevents", "prevents", "signal describes a failure, so prevents must name the failure it describes"));
3177
2794
  }
3178
2795
  const signal = stringAt(data, "signal");
3179
2796
  if (signal !== null && signal.trim() !== "" && !isSkillSignal(signal)) {
3180
- issues.push(issue3("unknown-signal", "signal", `signal "${signal}" is neither ${SKILL_SIGNALS.join(" nor ")}`));
2797
+ issues.push(issue2("unknown-signal", "signal", `signal "${signal}" is neither ${SKILL_SIGNALS.join(" nor ")}`));
3181
2798
  }
3182
2799
  return issues;
3183
2800
  }
@@ -3189,29 +2806,29 @@ function checkNameList(data, field2) {
3189
2806
  if (value === undefined)
3190
2807
  return [];
3191
2808
  if (!Array.isArray(value)) {
3192
- return [issue3("wrong-type", field2, `${field2} must be a list, not ${typeName3(value)}`)];
2809
+ return [issue2("wrong-type", field2, `${field2} must be a list, not ${typeName2(value)}`)];
3193
2810
  }
3194
2811
  const issues = entryTypeIssues(value, field2);
3195
2812
  if (issues.length > 0)
3196
2813
  return issues;
3197
- return value.flatMap((entry, index) => isSkillName(entry) ? [] : [issue3("invalid-name", `${field2}[${index}]`, `${field2} entry "${entry}" is not a skill name`)]);
2814
+ return value.flatMap((entry, index) => isSkillName(entry) ? [] : [issue2("invalid-name", `${field2}[${index}]`, `${field2} entry "${entry}" is not a skill name`)]);
3198
2815
  }
3199
2816
  function checkPaths(data) {
3200
2817
  const value = data["paths"];
3201
2818
  if (value === undefined)
3202
2819
  return [];
3203
2820
  if (!Array.isArray(value)) {
3204
- return [issue3("wrong-type", "paths", `paths must be a list, not ${typeName3(value)}`)];
2821
+ return [issue2("wrong-type", "paths", `paths must be a list, not ${typeName2(value)}`)];
3205
2822
  }
3206
2823
  const issues = [];
3207
2824
  for (const [index, entry] of value.entries()) {
3208
2825
  if (typeof entry !== "string") {
3209
- issues.push(issue3("wrong-type", `paths[${index}]`, `paths entries must be strings, and this one is ${typeName3(entry)}`));
2826
+ issues.push(issue2("wrong-type", `paths[${index}]`, `paths entries must be strings, and this one is ${typeName2(entry)}`));
3210
2827
  continue;
3211
2828
  }
3212
2829
  const problem = globProblem(entry);
3213
2830
  if (problem !== null) {
3214
- issues.push(issue3("unusable-glob", `paths[${index}]`, `the glob "${entry}" ${problem}`));
2831
+ issues.push(issue2("unusable-glob", `paths[${index}]`, `the glob "${entry}" ${problem}`));
3215
2832
  }
3216
2833
  }
3217
2834
  return issues;
@@ -3220,10 +2837,10 @@ function checkBoolean(data, field2) {
3220
2837
  const value = data[field2];
3221
2838
  if (value === undefined || typeof value === "boolean")
3222
2839
  return [];
3223
- return [issue3("wrong-type", field2, `${field2} must be true or false unquoted, and this is ${typeName3(value)}`)];
2840
+ return [issue2("wrong-type", field2, `${field2} must be true or false unquoted, and this is ${typeName2(value)}`)];
3224
2841
  }
3225
2842
  function checkForbidden(data) {
3226
- return FORBIDDEN_SKILL_FIELDS.filter((field2) => Object.hasOwn(data, field2)).map((field2) => issue3("forbidden-field", field2, `${field2} belongs to an instinct record and never to a skill`));
2843
+ return FORBIDDEN_SKILL_FIELDS.filter((field2) => Object.hasOwn(data, field2)).map((field2) => issue2("forbidden-field", field2, `${field2} belongs to an instinct record and never to a skill`));
3227
2844
  }
3228
2845
  function checkSkillFrontmatter(data) {
3229
2846
  return [
@@ -3241,248 +2858,9 @@ function checkSkillFrontmatter(data) {
3241
2858
  ];
3242
2859
  }
3243
2860
 
3244
- // src/check/layout.ts
3245
- import { readdirSync as readdirSync2, statSync as statSync2 } from "fs";
3246
- import { join as join3, relative } from "path";
3247
- var SKILL_FILE = "SKILL.md";
3248
- var MARKDOWN_EXTENSION = ".md";
3249
- var LAYOUT_SEVERITY = Object.freeze({
3250
- "flat-grouped-layout": "failure",
3251
- "nested-group-layout": "failure",
3252
- "loose-file": "failure",
3253
- "missing-skill-file": "failure",
3254
- "nested-instinct-file": "failure",
3255
- "unreadable-entry": "failure",
3256
- "name-mismatch": "failure",
3257
- "dotfile-only-directory": "warning"
3258
- });
3259
- function layoutIssue(code, message) {
3260
- return { code, severity: LAYOUT_SEVERITY[code], message };
3261
- }
3262
- function entryKind(path) {
3263
- try {
3264
- const stats = statSync2(path);
3265
- if (stats.isFile())
3266
- return "file";
3267
- if (stats.isDirectory())
3268
- return "directory";
3269
- return null;
3270
- } catch {
3271
- return null;
3272
- }
3273
- }
3274
- function visibleNames(dir) {
3275
- return readdirSync2(dir).filter((name2) => !name2.startsWith(".")).sort((left, right) => left.localeCompare(right));
3276
- }
3277
- function isMarkdown(name2) {
3278
- return name2.endsWith(MARKDOWN_EXTENSION);
3279
- }
3280
- function markdownStem(name2) {
3281
- return name2.slice(0, -MARKDOWN_EXTENSION.length);
3282
- }
3283
- function shown(root, path) {
3284
- return relative(root, path) || ".";
3285
- }
3286
- function dotfileOnlyEntry(kind, root, dir) {
3287
- return {
3288
- kind,
3289
- path: dir,
3290
- isFile: false,
3291
- shape: "dotfile-only",
3292
- name: null,
3293
- issues: [layoutIssue("dotfile-only-directory", `${shown(root, dir)}/ holds nothing but dotfiles, so it registers nothing`)]
3294
- };
3295
- }
3296
- function unreadableEntry(kind, root, path) {
3297
- return {
3298
- kind,
3299
- path,
3300
- isFile: false,
3301
- shape: "unreadable",
3302
- name: null,
3303
- issues: [layoutIssue("unreadable-entry", `${shown(root, path)} cannot be read (a broken symlink, or a path stat refuses)`)]
3304
- };
3305
- }
3306
- function skillFileEntry(path, name2) {
3307
- return { kind: "skill", path, isFile: true, shape: "skill-file", name: name2, issues: [] };
3308
- }
3309
- function flatGroupedEntry(root, path, name2) {
3310
- return {
3311
- kind: "skill",
3312
- path,
3313
- isFile: true,
3314
- shape: "flat-grouped",
3315
- name: name2,
3316
- issues: [layoutIssue("flat-grouped-layout", `${shown(root, path)} is the flat grouped layout <dir>/<group>/<name>.md,` + ` which does not register; move it to <dir>/${name2}/${SKILL_FILE}`)]
3317
- };
3318
- }
3319
- function nestedGroupEntry(root, path, name2) {
3320
- return {
3321
- kind: "skill",
3322
- path,
3323
- isFile: true,
3324
- shape: "nested-group",
3325
- name: name2,
3326
- issues: [layoutIssue("nested-group-layout", `${shown(root, path)} is the nested layout <dir>/<group>/<name>/${SKILL_FILE},` + ` which does not register; move it to <dir>/${name2}/${SKILL_FILE}`)]
3327
- };
3328
- }
3329
- function looseFileEntry(root, path, name2) {
3330
- return {
3331
- kind: "skill",
3332
- path,
3333
- isFile: true,
3334
- shape: "loose-file",
3335
- name: name2,
3336
- issues: [layoutIssue("loose-file", `${shown(root, path)} sits directly under the skills directory, which registers` + ` only <dir>/<name>/${SKILL_FILE}; move it to <dir>/${name2}/${SKILL_FILE}`)]
3337
- };
3338
- }
3339
- function noSkillFileEntry(root, dir) {
3340
- return {
3341
- kind: "skill",
3342
- path: dir,
3343
- isFile: false,
3344
- shape: "no-skill-file",
3345
- name: null,
3346
- issues: [layoutIssue("missing-skill-file", `${shown(root, dir)}/ holds no ${SKILL_FILE}, so it registers no skill`)]
3347
- };
3348
- }
3349
- function nestedInstinctEntry(root, path, name2) {
3350
- return {
3351
- kind: "instinct",
3352
- path,
3353
- isFile: true,
3354
- shape: "nested-instinct",
3355
- name: name2,
3356
- issues: [layoutIssue("nested-instinct-file", `${shown(root, path)} sits below the instincts directory, which is flat;` + ` move it to <dir>/${name2}${MARKDOWN_EXTENSION}`)]
3357
- };
3358
- }
3359
- function scanGroupMember(root, dir, name2) {
3360
- const children = visibleNames(dir);
3361
- if (children.length === 0)
3362
- return [dotfileOnlyEntry("skill", root, dir)];
3363
- const skillPath = join3(dir, SKILL_FILE);
3364
- if (children.includes(SKILL_FILE) && entryKind(skillPath) === "file") {
3365
- return [nestedGroupEntry(root, skillPath, name2)];
3366
- }
3367
- return [noSkillFileEntry(root, dir)];
3368
- }
3369
- function scanSkillGroup(root, dir, children) {
3370
- const entries = [];
3371
- for (const child of children) {
3372
- const path = join3(dir, child);
3373
- const found = entryKind(path);
3374
- if (found === null) {
3375
- entries.push(unreadableEntry("skill", root, path));
3376
- continue;
3377
- }
3378
- if (found === "directory") {
3379
- entries.push(...scanGroupMember(root, path, child));
3380
- continue;
3381
- }
3382
- if (isMarkdown(child))
3383
- entries.push(flatGroupedEntry(root, path, markdownStem(child)));
3384
- }
3385
- if (entries.length === 0)
3386
- return [noSkillFileEntry(root, dir)];
3387
- return entries;
3388
- }
3389
- function scanSkillDirectory(root, dir, name2) {
3390
- const children = visibleNames(dir);
3391
- if (children.length === 0)
3392
- return [dotfileOnlyEntry("skill", root, dir)];
3393
- const skillPath = join3(dir, SKILL_FILE);
3394
- if (children.includes(SKILL_FILE) && entryKind(skillPath) === "file") {
3395
- return [skillFileEntry(skillPath, name2)];
3396
- }
3397
- return scanSkillGroup(root, dir, children);
3398
- }
3399
- function scanSkillRoot(root) {
3400
- const entries = [];
3401
- for (const name2 of visibleNames(root)) {
3402
- const path = join3(root, name2);
3403
- const found = entryKind(path);
3404
- if (found === null) {
3405
- entries.push(unreadableEntry("skill", root, path));
3406
- continue;
3407
- }
3408
- if (found === "directory") {
3409
- entries.push(...scanSkillDirectory(root, path, name2));
3410
- continue;
3411
- }
3412
- if (isMarkdown(name2))
3413
- entries.push(looseFileEntry(root, path, markdownStem(name2)));
3414
- }
3415
- return entries;
3416
- }
3417
- function scanInstinctSubdirectory(root, dir) {
3418
- const children = visibleNames(dir);
3419
- if (children.length === 0)
3420
- return [dotfileOnlyEntry("instinct", root, dir)];
3421
- const entries = [];
3422
- for (const child of children) {
3423
- const path = join3(dir, child);
3424
- const found = entryKind(path);
3425
- if (found === null) {
3426
- entries.push(unreadableEntry("instinct", root, path));
3427
- continue;
3428
- }
3429
- if (found === "directory") {
3430
- entries.push(...scanInstinctSubdirectory(root, path));
3431
- continue;
3432
- }
3433
- if (isMarkdown(child))
3434
- entries.push(nestedInstinctEntry(root, path, markdownStem(child)));
3435
- }
3436
- return entries;
3437
- }
3438
- function scanInstinctRoot(root) {
3439
- const entries = [];
3440
- for (const name2 of visibleNames(root)) {
3441
- const path = join3(root, name2);
3442
- const found = entryKind(path);
3443
- if (found === null) {
3444
- entries.push(unreadableEntry("instinct", root, path));
3445
- continue;
3446
- }
3447
- if (found === "directory") {
3448
- entries.push(...scanInstinctSubdirectory(root, path));
3449
- continue;
3450
- }
3451
- if (!isMarkdown(name2))
3452
- continue;
3453
- entries.push({
3454
- kind: "instinct",
3455
- path,
3456
- isFile: true,
3457
- shape: "instinct-file",
3458
- name: markdownStem(name2),
3459
- issues: []
3460
- });
3461
- }
3462
- return entries;
3463
- }
3464
- function scanLayout(root, kind) {
3465
- if (entryKind(root) !== "directory") {
3466
- throw new Error(`check layout: no ${kind} directory at ${root}`);
3467
- }
3468
- const entries = kind === "skill" ? scanSkillRoot(root) : scanInstinctRoot(root);
3469
- return {
3470
- kind,
3471
- root,
3472
- entries: entries.sort((left, right) => left.path.localeCompare(right.path))
3473
- };
3474
- }
3475
- function declaredNameIssue(entry, declared) {
3476
- if (entry.name === null || declared === null || declared === entry.name)
3477
- return null;
3478
- const field2 = entry.kind === "skill" ? "name" : "id";
3479
- const source = entry.kind === "skill" ? "directory name" : "file name";
3480
- return layoutIssue("name-mismatch", `frontmatter ${field2} "${declared}" is not the ${source} "${entry.name}"`);
3481
- }
3482
-
3483
2861
  // src/check/references.ts
3484
- import { existsSync as existsSync7, statSync as statSync3 } from "fs";
3485
- import { delimiter, isAbsolute as isAbsolute3, relative as relative2, resolve as resolve2 } from "path";
2862
+ import { existsSync as existsSync7, statSync as statSync2 } from "fs";
2863
+ import { delimiter, isAbsolute as isAbsolute3, relative, resolve as resolve2 } from "path";
3486
2864
 
3487
2865
  // src/check/shell-lines.ts
3488
2866
  var SHELL_FENCE_LANGUAGES = ["bash", "sh", "shell", "zsh", "console"];
@@ -3702,12 +3080,12 @@ function isAbsoluteReference(token) {
3702
3080
  return KNOWN_ROOTS.includes(`/${first}`) || EXTENSION.test(token);
3703
3081
  }
3704
3082
  function isInside(root, target) {
3705
- const step = relative2(resolve2(root), resolve2(target));
3083
+ const step = relative(resolve2(root), resolve2(target));
3706
3084
  return step !== "" && !step.startsWith("..") && !isAbsolute3(step);
3707
3085
  }
3708
3086
  function isExecutableFile(path) {
3709
3087
  try {
3710
- const stats = statSync3(path);
3088
+ const stats = statSync2(path);
3711
3089
  return stats.isFile() && (stats.mode & 73) !== 0;
3712
3090
  } catch {
3713
3091
  return false;
@@ -3910,9 +3288,9 @@ function checkReferences(body, seams) {
3910
3288
  if (seen.has(key))
3911
3289
  continue;
3912
3290
  seen.add(key);
3913
- const issue4 = referenceIssue(ref, seams, functions);
3914
- if (issue4 !== null)
3915
- issues.push(issue4);
3291
+ const issue3 = referenceIssue(ref, seams, functions);
3292
+ if (issue3 !== null)
3293
+ issues.push(issue3);
3916
3294
  }
3917
3295
  return { references, issues };
3918
3296
  }
@@ -3929,66 +3307,66 @@ var CHECK_STAGES = [
3929
3307
  var LOCALITY_CODES = ["home-path", "foreign-path"];
3930
3308
  var FIXABLE_FIELDS = ["tags", "stack"];
3931
3309
  function hasCheckFailure(issues) {
3932
- return issues.some((issue4) => issue4.severity === "failure");
3310
+ return issues.some((issue3) => issue3.severity === "failure");
3933
3311
  }
3934
3312
  function failureExitCode(count) {
3935
3313
  return Math.min(count, FAILING_FILE_CAP);
3936
3314
  }
3937
- function fromLayout(issue4) {
3315
+ function fromLayout(issue3) {
3938
3316
  return {
3939
3317
  stage: "layout",
3940
- code: issue4.code,
3941
- severity: issue4.severity,
3318
+ code: issue3.code,
3319
+ severity: issue3.severity,
3942
3320
  field: null,
3943
3321
  line: null,
3944
- message: issue4.message
3322
+ message: issue3.message
3945
3323
  };
3946
3324
  }
3947
- function fromSkill(issue4) {
3325
+ function fromSkill(issue3) {
3948
3326
  return {
3949
3327
  stage: "schema",
3950
- code: issue4.code,
3328
+ code: issue3.code,
3951
3329
  severity: "failure",
3952
- field: issue4.field,
3330
+ field: issue3.field,
3953
3331
  line: null,
3954
- message: issue4.message
3332
+ message: issue3.message
3955
3333
  };
3956
3334
  }
3957
- function fromInstinct(issue4) {
3335
+ function fromInstinct(issue3) {
3958
3336
  return {
3959
3337
  stage: "instinct",
3960
- code: issue4.code,
3338
+ code: issue3.code,
3961
3339
  severity: "failure",
3962
- field: issue4.field,
3340
+ field: issue3.field,
3963
3341
  line: null,
3964
- message: issue4.message
3342
+ message: issue3.message
3965
3343
  };
3966
3344
  }
3967
- function fromReference(issue4) {
3345
+ function fromReference(issue3) {
3968
3346
  return {
3969
- stage: LOCALITY_CODES.includes(issue4.code) ? "locality" : "resolution",
3970
- code: issue4.code,
3971
- severity: issue4.severity,
3347
+ stage: LOCALITY_CODES.includes(issue3.code) ? "locality" : "resolution",
3348
+ code: issue3.code,
3349
+ severity: issue3.severity,
3972
3350
  field: null,
3973
- line: issue4.reference.line,
3974
- message: issue4.message
3351
+ line: issue3.reference.line,
3352
+ message: issue3.message
3975
3353
  };
3976
3354
  }
3977
3355
  function checkerIssue(stage, code, message) {
3978
3356
  return { stage, code, severity: "failure", field: null, line: null, message };
3979
3357
  }
3980
- function isMarkdown2(name2) {
3358
+ function isMarkdown(name2) {
3981
3359
  return name2.endsWith(MARKDOWN_EXTENSION);
3982
3360
  }
3983
3361
  function fileEntry(path, kind) {
3984
3362
  const base = basename2(path);
3985
- const stem = isMarkdown2(base) ? base.slice(0, -MARKDOWN_EXTENSION.length) : base;
3363
+ const stem = isMarkdown(base) ? base.slice(0, -MARKDOWN_EXTENSION.length) : base;
3986
3364
  if (kind === "instinct") {
3987
3365
  return {
3988
3366
  kind,
3989
3367
  path,
3990
3368
  isFile: true,
3991
- shape: isMarkdown2(base) ? "instinct-file" : "loose-file",
3369
+ shape: isMarkdown(base) ? "instinct-file" : "loose-file",
3992
3370
  name: stem,
3993
3371
  issues: []
3994
3372
  };
@@ -3998,7 +3376,7 @@ function fileEntry(path, kind) {
3998
3376
  function unregisteredIssue(path, kind) {
3999
3377
  const base = basename2(path);
4000
3378
  if (kind === "instinct") {
4001
- return isMarkdown2(base) ? null : checkerIssue("layout", "unregistered-path", `${base} is not a ${MARKDOWN_EXTENSION} file, so it is no instinct record`);
3379
+ return isMarkdown(base) ? null : checkerIssue("layout", "unregistered-path", `${base} is not a ${MARKDOWN_EXTENSION} file, so it is no instinct record`);
4002
3380
  }
4003
3381
  return base === SKILL_FILE ? null : checkerIssue("layout", "unregistered-path", `${base} is not a ${SKILL_FILE}, so it registers no skill wherever it sits`);
4004
3382
  }
@@ -4019,7 +3397,7 @@ function inStageOrder(issues) {
4019
3397
  return [...issues].sort((left, right) => CHECK_STAGES.indexOf(left.stage) - CHECK_STAGES.indexOf(right.stage));
4020
3398
  }
4021
3399
  function fixableFields(issues) {
4022
- const failures = issues.filter((issue4) => issue4.severity === "failure");
3400
+ const failures = issues.filter((issue3) => issue3.severity === "failure");
4023
3401
  if (failures.length === 0)
4024
3402
  return null;
4025
3403
  const found = new Set;
@@ -4152,19 +3530,19 @@ function checkDirectory(root, kind, options) {
4152
3530
 
4153
3531
  // src/utils/agent-definition.ts
4154
3532
  import { readFileSync as readFileSync3 } from "fs";
4155
- import { join as join4 } from "path";
4156
- var AGENT_DEFINITION_DIR = join4(".claude", "agents");
3533
+ import { join as join3 } from "path";
3534
+ var AGENT_DEFINITION_DIR = join3(".claude", "agents");
4157
3535
  function agentDefinitionCandidates(name2, roots, settingSources) {
4158
3536
  if (!isAgentName(name2))
4159
3537
  return [];
4160
3538
  const file = `${name2}.md`;
4161
3539
  const project = {
4162
3540
  scope: "project",
4163
- path: join4(roots.repoRoot, AGENT_DEFINITION_DIR, file)
3541
+ path: join3(roots.repoRoot, AGENT_DEFINITION_DIR, file)
4164
3542
  };
4165
3543
  if (!settingSources.includes("user"))
4166
3544
  return [project];
4167
- return [project, { scope: "user", path: join4(roots.home, AGENT_DEFINITION_DIR, file) }];
3545
+ return [project, { scope: "user", path: join3(roots.home, AGENT_DEFINITION_DIR, file) }];
4168
3546
  }
4169
3547
  function readText2(path) {
4170
3548
  try {
@@ -4246,16 +3624,16 @@ function checkVerdict(issues) {
4246
3624
  }
4247
3625
 
4248
3626
  // src/inventory/trees.ts
4249
- var BUNDLED_AGENTS_DIR = join5("bundled", "agents");
3627
+ var BUNDLED_AGENTS_DIR = join4("bundled", "agents");
4250
3628
  function bundledAgentsDirectory(entry) {
4251
- return join5(dirname3(realEntry(entry ?? Bun.main)), BUNDLED_AGENTS_DIR);
3629
+ return join4(dirname3(realEntry(entry ?? Bun.main)), BUNDLED_AGENTS_DIR);
4252
3630
  }
4253
3631
  function agentTreeDirectory(tier, seams) {
4254
3632
  if (tier === "user")
4255
- return join5(seams.home, AGENT_DEFINITION_DIR);
3633
+ return join4(seams.home, AGENT_DEFINITION_DIR);
4256
3634
  if (tier === "rafa")
4257
3635
  return bundledAgentsDirectory(seams.entry);
4258
- return seams.projectRoot === null ? null : join5(seams.projectRoot, AGENT_DEFINITION_DIR);
3636
+ return seams.projectRoot === null ? null : join4(seams.projectRoot, AGENT_DEFINITION_DIR);
4259
3637
  }
4260
3638
  function readText3(path) {
4261
3639
  try {
@@ -4284,7 +3662,7 @@ function byName(items) {
4284
3662
  return [...items].sort((a, b) => a.name.localeCompare(b.name) || a.path.localeCompare(b.path));
4285
3663
  }
4286
3664
  function skillItem(report2, source, dir) {
4287
- const name2 = report2.name ?? relative3(dir, report2.path);
3665
+ const name2 = report2.name ?? relative2(dir, report2.path);
4288
3666
  return sourceItem("skill", source, name2, report2.path, checkVerdict(report2.issues));
4289
3667
  }
4290
3668
  function readSkillTree(source, seams) {
@@ -4316,19 +3694,19 @@ function readTrees(seams) {
4316
3694
 
4317
3695
  // src/tiers/serve.ts
4318
3696
  import { cpSync, mkdirSync as mkdirSync2, readFileSync as readFileSync6, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
4319
- import { basename as basename4, dirname as dirname5, join as join7 } from "path";
3697
+ import { basename as basename4, dirname as dirname5, join as join6 } from "path";
4320
3698
 
4321
3699
  // src/loop/sessions.ts
4322
3700
  import {
4323
3701
  linkSync,
4324
3702
  mkdirSync,
4325
- readdirSync as readdirSync3,
3703
+ readdirSync as readdirSync2,
4326
3704
  readFileSync as readFileSync5,
4327
3705
  renameSync,
4328
3706
  rmSync,
4329
3707
  writeFileSync as writeFileSync2
4330
3708
  } from "fs";
4331
- import { basename as basename3, dirname as dirname4, join as join6 } from "path";
3709
+ import { basename as basename3, dirname as dirname4, join as join5 } from "path";
4332
3710
  var SESSION_STATES = Object.freeze(["running", "paused", "stopped", "done"]);
4333
3711
  var RUNS_DIR = "runs";
4334
3712
  var RECORD_EXTENSION = ".json";
@@ -4368,13 +3746,13 @@ function isSessionId(value) {
4368
3746
  return SESSION_ID_PATTERN.test(value);
4369
3747
  }
4370
3748
  function runsDir(root) {
4371
- return join6(scopeAt(root).dir, RUNS_DIR);
3749
+ return join5(scopeAt(root).dir, RUNS_DIR);
4372
3750
  }
4373
3751
  function sessionFilePath(root, sessionId) {
4374
3752
  if (!isSessionId(sessionId)) {
4375
3753
  throw new Error(`session record: unusable session id ${JSON.stringify(sessionId)}`);
4376
3754
  }
4377
- return join6(runsDir(root), `${sessionId}${RECORD_EXTENSION}`);
3755
+ return join5(runsDir(root), `${sessionId}${RECORD_EXTENSION}`);
4378
3756
  }
4379
3757
  function describeValue3(value) {
4380
3758
  return value === undefined ? "missing" : JSON.stringify(value) ?? String(value);
@@ -4484,7 +3862,7 @@ function readRecordFile(file) {
4484
3862
  }
4485
3863
  function recordNames(dir) {
4486
3864
  try {
4487
- return readdirSync3(dir);
3865
+ return readdirSync2(dir);
4488
3866
  } catch (error) {
4489
3867
  if (errorCode(error) === "ENOENT")
4490
3868
  return [];
@@ -4497,7 +3875,7 @@ function byStart(a, b) {
4497
3875
  function readSessions(root, seams = {}) {
4498
3876
  const dir = runsDir(root);
4499
3877
  const isAlive = seams.isAlive ?? isPidAlive;
4500
- const records = recordNames(dir).filter((name2) => name2.endsWith(RECORD_EXTENSION)).map((name2) => readRecordFile(join6(dir, name2))).map((record) => Object.freeze({ ...record, state: readState(record, isAlive) }));
3878
+ const records = recordNames(dir).filter((name2) => name2.endsWith(RECORD_EXTENSION)).map((name2) => readRecordFile(join5(dir, name2))).map((record) => Object.freeze({ ...record, state: readState(record, isAlive) }));
4501
3879
  return Object.freeze(records.sort(byStart));
4502
3880
  }
4503
3881
  function readSession(root, sessionId, seams = {}) {
@@ -4570,7 +3948,7 @@ var SERVED_AGENTS_FILE = "agents.json";
4570
3948
  var SERVED_PLUGIN_NAME = "rafa";
4571
3949
  var SKILL_FILE2 = "SKILL.md";
4572
3950
  var SERVED_SKILLS_PATH = {
4573
- "add-dir": join7(".claude", "skills"),
3951
+ "add-dir": join6(".claude", "skills"),
4574
3952
  "plugin-dir": "skills"
4575
3953
  };
4576
3954
  var SKILL_DELIVERY_FLAG = {
@@ -4581,7 +3959,7 @@ function servedDirectory(root, run) {
4581
3959
  if (!isSessionId(run)) {
4582
3960
  throw new Error(`served directory: unusable run id ${JSON.stringify(run)}`);
4583
3961
  }
4584
- return join7(runsDir(root), run, SERVED_DIR);
3962
+ return join6(runsDir(root), run, SERVED_DIR);
4585
3963
  }
4586
3964
  function rafaWinners(resolution) {
4587
3965
  return resolution.items.flatMap((item) => item.state === "served" && item.winner.source === "rafa" ? [item] : []);
@@ -4651,15 +4029,15 @@ function admit(row) {
4651
4029
  return block.ok ? read : block;
4652
4030
  }
4653
4031
  function copySkill(row, skills) {
4654
- const to = join7(skills, row.name);
4032
+ const to = join6(skills, row.name);
4655
4033
  cpSync(dirname5(row.path), to, { recursive: true, dereference: true });
4656
4034
  return { kind: "skill", name: row.name, from: row.path, to };
4657
4035
  }
4658
4036
  function writePluginManifest(dir) {
4659
- const manifestDir = join7(dir, ".claude-plugin");
4037
+ const manifestDir = join6(dir, ".claude-plugin");
4660
4038
  mkdirSync2(manifestDir, { recursive: true });
4661
4039
  const manifest = { name: SERVED_PLUGIN_NAME, description: "rafa-tier skills served to one loop session" };
4662
- writeFileSync3(join7(manifestDir, "plugin.json"), `${JSON.stringify(manifest, null, 2)}
4040
+ writeFileSync3(join6(manifestDir, "plugin.json"), `${JSON.stringify(manifest, null, 2)}
4663
4041
  `);
4664
4042
  }
4665
4043
  function servedFlags(dir, delivery, skillCount, agents) {
@@ -4679,11 +4057,11 @@ function serveResolution(resolution, options) {
4679
4057
  rmSync2(dir, { recursive: true, force: true });
4680
4058
  mkdirSync2(dir, { recursive: true });
4681
4059
  const judged = rafaWinners(resolution).map((item) => ({ row: item.winner, verdict: serveVerdict(item.winner) }));
4682
- const skillsDir = join7(dir, SERVED_SKILLS_PATH[delivery]);
4060
+ const skillsDir = join6(dir, SERVED_SKILLS_PATH[delivery]);
4683
4061
  const skills = judged.flatMap(({ row, verdict }) => verdict.ok && row.kind === "skill" ? [copySkill(row, skillsDir)] : []);
4684
4062
  if (skills.length > 0 && delivery === "plugin-dir")
4685
4063
  writePluginManifest(dir);
4686
- const agentsFile = join7(dir, SERVED_AGENTS_FILE);
4064
+ const agentsFile = join6(dir, SERVED_AGENTS_FILE);
4687
4065
  const admitted = judged.flatMap(({ row, verdict }) => verdict.ok && verdict.value !== null ? [{ row, definition: verdict.value }] : []);
4688
4066
  const agentMap = Object.fromEntries(admitted.map(({ row, definition }) => [row.name, definition]));
4689
4067
  if (admitted.length > 0)
@@ -4720,12 +4098,12 @@ function isFileEntry(dir, entry) {
4720
4098
  return true;
4721
4099
  if (!entry.isSymbolicLink())
4722
4100
  return false;
4723
- return statSync4(join8(dir, entry.name), { throwIfNoEntry: false })?.isFile() === true;
4101
+ return statSync3(join7(dir, entry.name), { throwIfNoEntry: false })?.isFile() === true;
4724
4102
  }
4725
4103
  function markdownFiles(dir) {
4726
4104
  let entries;
4727
4105
  try {
4728
- entries = readdirSync4(dir, { withFileTypes: true });
4106
+ entries = readdirSync3(dir, { withFileTypes: true });
4729
4107
  } catch {
4730
4108
  return [];
4731
4109
  }
@@ -4741,7 +4119,7 @@ function definitionName(text) {
4741
4119
  function readAgentDirectory(dir) {
4742
4120
  const found = [];
4743
4121
  for (const file of markdownFiles(dir)) {
4744
- const path = join8(dir, file);
4122
+ const path = join7(dir, file);
4745
4123
  const text = readText4(path);
4746
4124
  if (text === null)
4747
4125
  continue;
@@ -4883,7 +4261,7 @@ function skillCollisionLine(colliding) {
4883
4261
  }
4884
4262
 
4885
4263
  // src/commands/plan/plan-files.ts
4886
- import { statSync as statSync5 } from "fs";
4264
+ import { statSync as statSync4 } from "fs";
4887
4265
  import { resolve as resolve3 } from "path";
4888
4266
  function plansDirAt(root, planDir) {
4889
4267
  return { label: planDir, path: resolve3(root, planDir) };
@@ -4933,10 +4311,10 @@ function stubOfPlanFile(name2) {
4933
4311
  return stub !== undefined && isStampableStub(stub) ? stub : null;
4934
4312
  }
4935
4313
  function isFile(path) {
4936
- return statSync5(path, { throwIfNoEntry: false })?.isFile() ?? false;
4314
+ return statSync4(path, { throwIfNoEntry: false })?.isFile() ?? false;
4937
4315
  }
4938
- function issueLine(file, issue4) {
4939
- return `${file}:${issue4.line}: ${issue4.reason}: ${issue4.text}`;
4316
+ function issueLine(file, issue3) {
4317
+ return `${file}:${issue3.line}: ${issue3.reason}: ${issue3.text}`;
4940
4318
  }
4941
4319
  function plural(count, noun) {
4942
4320
  return count === 1 ? `${count} ${noun}` : `${count} ${noun}s`;
@@ -5053,8 +4431,8 @@ function createPlanValidateCommand(workingDirectory = () => process.cwd()) {
5053
4431
  const { missingAgents, skillCollisions } = checkRoster(context, readFileSync8(file, "utf8"));
5054
4432
  const result = { ...validation, missingAgents, skillCollisions };
5055
4433
  if (issues.length > 0 || missingAgents.length > 0 || skillCollisions.length > 0) {
5056
- for (const issue4 of issues)
5057
- context.output.error(issueLine(typed, issue4));
4434
+ for (const issue3 of issues)
4435
+ context.output.error(issueLine(typed, issue3));
5058
4436
  for (const agent of missingAgents)
5059
4437
  context.output.error(`${typed}: ${missingAgentLine(agent)}`);
5060
4438
  for (const skill of skillCollisions)
@@ -5073,12 +4451,12 @@ function createPlanValidateCommand(workingDirectory = () => process.cwd()) {
5073
4451
  var validate_default = createPlanValidateCommand();
5074
4452
 
5075
4453
  // src/commands/plan/list.ts
5076
- import { existsSync as existsSync8, readdirSync as readdirSync5, readFileSync as readFileSync9 } from "fs";
5077
- import { join as join9 } from "path";
4454
+ import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync9 } from "fs";
4455
+ import { join as join8 } from "path";
5078
4456
  var USAGE2 = "rafa plan list";
5079
4457
  function listing(dir, stub) {
5080
- const plan = join9(dir, planFileName(stub, false));
5081
- const trackerPath = join9(dir, planFileName(stub, true));
4458
+ const plan = join8(dir, planFileName(stub, false));
4459
+ const trackerPath = join8(dir, planFileName(stub, true));
5082
4460
  const tracker = isFile(trackerPath) ? trackerPath : null;
5083
4461
  const planModel = parsePlan(readFileSync9(plan, "utf8"));
5084
4462
  const counted = tracker === null ? planModel : parsePlan(readFileSync9(tracker, "utf8"));
@@ -5088,7 +4466,7 @@ function listPlans(plans) {
5088
4466
  const dir = plans.path;
5089
4467
  if (!existsSync8(dir))
5090
4468
  return { dir, plans: [] };
5091
- const stubs = readdirSync5(dir).filter((name2) => isFile(join9(dir, name2))).map((name2) => stubOfPlanFile(name2)).filter((stub) => stub !== null).sort((a, b) => a.localeCompare(b));
4469
+ const stubs = readdirSync4(dir).filter((name2) => isFile(join8(dir, name2))).map((name2) => stubOfPlanFile(name2)).filter((stub) => stub !== null).sort((a, b) => a.localeCompare(b));
5092
4470
  return { dir, plans: stubs.map((stub) => listing(dir, stub)) };
5093
4471
  }
5094
4472
  function rowNotes(plan) {
@@ -5142,8 +4520,8 @@ var planListCommand = {
5142
4520
  var list_default = Object.freeze(planListCommand);
5143
4521
 
5144
4522
  // src/board/gate.ts
5145
- import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync10, renameSync as renameSync2, statSync as statSync6, writeFileSync as writeFileSync4 } from "fs";
5146
- import { basename as basename5, dirname as dirname6, join as join10, resolve as resolve5 } from "path";
4523
+ import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync10, renameSync as renameSync2, statSync as statSync5, writeFileSync as writeFileSync4 } from "fs";
4524
+ import { basename as basename5, dirname as dirname6, join as join9, resolve as resolve5 } from "path";
5147
4525
 
5148
4526
  // src/board/flags.ts
5149
4527
  var SPEC_FLAG = "--spec";
@@ -5252,9 +4630,9 @@ function readLines2(body) {
5252
4630
  let fenced = false;
5253
4631
  raw.forEach((text, index) => {
5254
4632
  const number = index + 1;
5255
- const isFence2 = FENCE_LINE.test(text);
5256
- if (fenced || !carried && isFence2) {
5257
- fenced = !fenced || !isFence2;
4633
+ const isFence = FENCE_LINE.test(text);
4634
+ if (fenced || !carried && isFence) {
4635
+ fenced = !fenced || !isFence;
5258
4636
  lines.push(fencedLine(number, text));
5259
4637
  return;
5260
4638
  }
@@ -5367,13 +4745,13 @@ function findReadinessGaps(body) {
5367
4745
  function hasSpecReadyLabel(labels) {
5368
4746
  return labels.some((label) => label.trim().toLowerCase() === SPEC_READY_LABEL);
5369
4747
  }
5370
- function specReadyRefusalMessage(issue4) {
5371
- return `issue #${String(issue4)} is not marked ${SPEC_READY_LABEL}`;
4748
+ function specReadyRefusalMessage(issue3) {
4749
+ return `issue #${String(issue3)} is not marked ${SPEC_READY_LABEL}`;
5372
4750
  }
5373
- function requireSpecReadyLabel(issue4, labels) {
4751
+ function requireSpecReadyLabel(issue3, labels) {
5374
4752
  if (hasSpecReadyLabel(labels))
5375
4753
  return;
5376
- throw new CommandExit(READINESS_REFUSAL_EXIT, specReadyRefusalMessage(issue4));
4754
+ throw new CommandExit(READINESS_REFUSAL_EXIT, specReadyRefusalMessage(issue3));
5377
4755
  }
5378
4756
  function describeGap(gap) {
5379
4757
  return `"${gap.heading}" ${gap.what}`;
@@ -5569,11 +4947,11 @@ async function readTrustedSpecReviewComment(comments, trust) {
5569
4947
  return { comment: null, ignored };
5570
4948
  }
5571
4949
  async function writeSpecReviewComment(options) {
5572
- const { body, issue: issue4, board, trust } = options;
5573
- const found = await readTrustedSpecReviewComment(await board.comments(issue4), trust);
4950
+ const { body, issue: issue3, board, trust } = options;
4951
+ const found = await readTrustedSpecReviewComment(await board.comments(issue3), trust);
5574
4952
  const { comment: existing, ignored } = found;
5575
4953
  if (existing === null) {
5576
- return { action: "posted", comment: await board.comment(issue4, body), ignored };
4954
+ return { action: "posted", comment: await board.comment(issue3, body), ignored };
5577
4955
  }
5578
4956
  return { action: "edited", comment: await board.editComment(existing.id, body), ignored };
5579
4957
  }
@@ -5647,14 +5025,14 @@ function stampPlanField(plan, field3, value) {
5647
5025
  note: `line ${String(at + 1)} of the plan now records ${line}`
5648
5026
  };
5649
5027
  }
5650
- function issueFieldLine(issue4) {
5651
- if (!Number.isSafeInteger(issue4) || issue4 < 1) {
5652
- throw new TypeError(`board plan field: issue ${String(issue4)} is no issue number, expected a positive whole one`);
5028
+ function issueFieldLine(issue3) {
5029
+ if (!Number.isSafeInteger(issue3) || issue3 < 1) {
5030
+ throw new TypeError(`board plan field: issue ${String(issue3)} is no issue number, expected a positive whole one`);
5653
5031
  }
5654
- return `${ISSUE_FIELD}: ${JSON.stringify(String(issue4))}`;
5032
+ return `${ISSUE_FIELD}: ${JSON.stringify(String(issue3))}`;
5655
5033
  }
5656
- function stampPlanIssue(plan, issue4) {
5657
- return stampPlanField(plan, ISSUE_FIELD, JSON.stringify(String(issue4)));
5034
+ function stampPlanIssue(plan, issue3) {
5035
+ return stampPlanField(plan, ISSUE_FIELD, JSON.stringify(String(issue3)));
5658
5036
  }
5659
5037
 
5660
5038
  // src/board/review-stamp.ts
@@ -5701,14 +5079,14 @@ function readGateFlags(args) {
5701
5079
  function describeGap2(gap) {
5702
5080
  return ` \u2022 "${gap.heading}": ${gap.what}`;
5703
5081
  }
5704
- function remedyFor(issue4) {
5705
- return issue4 === null ? " Close the gaps in the spec, then plan from it again." : ` Close the gaps, label the issue ${SPEC_READY_LABEL} again, and plan from it again.`;
5082
+ function remedyFor(issue3) {
5083
+ return issue3 === null ? " Close the gaps in the spec, then plan from it again." : ` Close the gaps, label the issue ${SPEC_READY_LABEL} again, and plan from it again.`;
5706
5084
  }
5707
- function specNotReadyMessage(source, reading, gaps, issue4 = null) {
5085
+ function specNotReadyMessage(source, reading, gaps, issue3 = null) {
5708
5086
  return [
5709
5087
  `\u274C ${source} is not ready to plan from: ${reading}`,
5710
5088
  ...gaps.map(describeGap2),
5711
- remedyFor(issue4)
5089
+ remedyFor(issue3)
5712
5090
  ].join(`
5713
5091
  `);
5714
5092
  }
@@ -5728,14 +5106,14 @@ function unreadReviewMessage(source, reading, planIssues) {
5728
5106
  }
5729
5107
  function isWrittenFile(repoRoot, path) {
5730
5108
  const full = resolve5(repoRoot, path);
5731
- return existsSync9(full) && statSync6(full).isFile();
5109
+ return existsSync9(full) && statSync5(full).isFile();
5732
5110
  }
5733
5111
  var NOT_READY_REASON = "the planner judged the spec not ready, so no plan stands.";
5734
5112
  var UNREAD_REASON = "no review came back and the plan does not read as written, so no plan stands.";
5735
5113
  var REJECTED_DIR = "rejected";
5736
5114
  function rejectedPath(path) {
5737
5115
  const parent = dirname6(path);
5738
- return parent === "." ? join10(REJECTED_DIR, basename5(path)) : join10(parent, REJECTED_DIR, basename5(path));
5116
+ return parent === "." ? join9(REJECTED_DIR, basename5(path)) : join9(parent, REJECTED_DIR, basename5(path));
5739
5117
  }
5740
5118
  function moveWritten(options, output, reason) {
5741
5119
  for (const path of [options.planPath, options.prerequisitesPath]) {
@@ -5758,7 +5136,7 @@ function readWrittenPlan(options) {
5758
5136
  }
5759
5137
  try {
5760
5138
  const { issues } = validatePlan(resolve5(repoRoot, planPath));
5761
- return { reads: issues.length === 0, issues: issues.map((issue4) => issueLine(planPath, issue4)) };
5139
+ return { reads: issues.length === 0, issues: issues.map((issue3) => issueLine(planPath, issue3)) };
5762
5140
  } catch (error) {
5763
5141
  return { reads: false, issues: [`${planPath}: ${messageOf(error)}`] };
5764
5142
  }
@@ -5796,27 +5174,27 @@ function recordAssumptions(options, gaps, output) {
5796
5174
  }
5797
5175
  output.info(`\uD83E\uDDED ${planPath} opens under the review's assumptions and records ${REVIEW_ASSUMED_LINE}.`);
5798
5176
  }
5799
- async function publishGaps(issue4, gaps, body, output) {
5177
+ async function publishGaps(issue3, gaps, body, output) {
5800
5178
  try {
5801
5179
  const write = await writeSpecReviewComment({
5802
- issue: issue4.number,
5180
+ issue: issue3.number,
5803
5181
  body: body(gaps),
5804
- board: issue4.board,
5805
- trust: issue4.trust
5182
+ board: issue3.board,
5183
+ trust: issue3.trust
5806
5184
  });
5807
5185
  for (const passed2 of write.ignored)
5808
5186
  output.warn(passed2.reason);
5809
- output.info(`\uD83D\uDCAC ${write.action === "posted" ? "Posted" : "Edited"} the review comment on issue #${String(issue4.number)}.`);
5187
+ output.info(`\uD83D\uDCAC ${write.action === "posted" ? "Posted" : "Edited"} the review comment on issue #${String(issue3.number)}.`);
5810
5188
  } catch (error) {
5811
- output.warn(`the review comment on issue #${String(issue4.number)} was not written: ${messageOf(error)}`);
5189
+ output.warn(`the review comment on issue #${String(issue3.number)} was not written: ${messageOf(error)}`);
5812
5190
  }
5813
5191
  }
5814
- async function swapLabels(issue4, output) {
5192
+ async function swapLabels(issue3, output) {
5815
5193
  try {
5816
- await issue4.board.swapLabels(issue4.number, SPEC_READY_LABEL, SPEC_NEEDS_WORK_LABEL);
5817
- output.info(`\uD83C\uDFF7 Swapped ${SPEC_READY_LABEL} for ${SPEC_NEEDS_WORK_LABEL} on issue #${String(issue4.number)}.`);
5194
+ await issue3.board.swapLabels(issue3.number, SPEC_READY_LABEL, SPEC_NEEDS_WORK_LABEL);
5195
+ output.info(`\uD83C\uDFF7 Swapped ${SPEC_READY_LABEL} for ${SPEC_NEEDS_WORK_LABEL} on issue #${String(issue3.number)}.`);
5818
5196
  } catch (error) {
5819
- output.warn(`${SPEC_READY_LABEL} was not swapped for ${SPEC_NEEDS_WORK_LABEL} on issue` + ` #${String(issue4.number)}: ${messageOf(error)}`);
5197
+ output.warn(`${SPEC_READY_LABEL} was not swapped for ${SPEC_NEEDS_WORK_LABEL} on issue` + ` #${String(issue3.number)}: ${messageOf(error)}`);
5820
5198
  }
5821
5199
  }
5822
5200
  function nothingBlocks(review) {
@@ -5825,18 +5203,18 @@ function nothingBlocks(review) {
5825
5203
  async function planUnderAssumptions(options, review, output) {
5826
5204
  output.warn(assumedReviewWarning(review.text, options.planPath));
5827
5205
  recordAssumptions(options, review.gaps, output);
5828
- const { issue: issue4 } = options;
5829
- if (issue4 !== null) {
5206
+ const { issue: issue3 } = options;
5207
+ if (issue3 !== null) {
5830
5208
  if (options.comment) {
5831
- await publishGaps(issue4, review.gaps, assumedReviewCommentBody, output);
5209
+ await publishGaps(issue3, review.gaps, assumedReviewCommentBody, output);
5832
5210
  } else {
5833
- output.info(`\uD83D\uDCAC ${NO_COMMENT_FLAG}: the gaps were not posted on issue #${String(issue4.number)}.`);
5211
+ output.info(`\uD83D\uDCAC ${NO_COMMENT_FLAG}: the gaps were not posted on issue #${String(issue3.number)}.`);
5834
5212
  }
5835
5213
  }
5836
5214
  return "assumed";
5837
5215
  }
5838
5216
  async function enforceSpecReview(options) {
5839
- const { review, issue: issue4 } = options;
5217
+ const { review, issue: issue3 } = options;
5840
5218
  if (review === undefined)
5841
5219
  return "unjudged";
5842
5220
  if (review.ready)
@@ -5847,15 +5225,15 @@ async function enforceSpecReview(options) {
5847
5225
  if (nothingBlocks(review))
5848
5226
  return planUnderAssumptions(options, review, output);
5849
5227
  moveWritten(options, output, NOT_READY_REASON);
5850
- if (issue4 !== null) {
5228
+ if (issue3 !== null) {
5851
5229
  if (options.comment) {
5852
- await publishGaps(issue4, review.gaps, specReviewCommentBody, output);
5230
+ await publishGaps(issue3, review.gaps, specReviewCommentBody, output);
5853
5231
  } else {
5854
- output.info(`\uD83D\uDCAC ${NO_COMMENT_FLAG}: the gaps were not posted on issue #${String(issue4.number)}.`);
5232
+ output.info(`\uD83D\uDCAC ${NO_COMMENT_FLAG}: the gaps were not posted on issue #${String(issue3.number)}.`);
5855
5233
  }
5856
- await swapLabels(issue4, output);
5234
+ await swapLabels(issue3, output);
5857
5235
  }
5858
- throw new CommandExit(SPEC_NOT_READY_EXIT, specNotReadyMessage(options.source, review.text, review.gaps, issue4));
5236
+ throw new CommandExit(SPEC_NOT_READY_EXIT, specNotReadyMessage(options.source, review.text, review.gaps, issue3));
5859
5237
  }
5860
5238
 
5861
5239
  // src/board/issue-board.ts
@@ -5930,8 +5308,8 @@ function createGhIssueBoard(options) {
5930
5308
  }
5931
5309
  const commentsPath = (number) => `repos/${REPO_PATH2}/issues/${number}/comments`;
5932
5310
  const board = {
5933
- comments: async (issue4) => {
5934
- const path = commentsPath(issueNumber(issue4, "comments"));
5311
+ comments: async (issue3) => {
5312
+ const path = commentsPath(issueNumber(issue3, "comments"));
5935
5313
  const command = `gh api ${path}`;
5936
5314
  const rows = parseJson(await succeed(["api", path], command), command);
5937
5315
  if (!Array.isArray(rows)) {
@@ -5939,8 +5317,8 @@ function createGhIssueBoard(options) {
5939
5317
  }
5940
5318
  return rows.map((row, index) => readComment(row, command, `comment ${String(index)}`));
5941
5319
  },
5942
- comment: async (issue4, body) => {
5943
- const path = commentsPath(issueNumber(issue4, "comment"));
5320
+ comment: async (issue3, body) => {
5321
+ const path = commentsPath(issueNumber(issue3, "comment"));
5944
5322
  const text = commentBody2(body, "comment");
5945
5323
  const command = `gh api -X POST ${path}`;
5946
5324
  const stdout = await succeed(["api", path, "-X", "POST", "-f", `body=${text}`], command);
@@ -5953,14 +5331,14 @@ function createGhIssueBoard(options) {
5953
5331
  const stdout = await succeed(["api", path, "-X", "PATCH", "-f", `body=${text}`], command);
5954
5332
  return readComment(parseJson(stdout, command), command, "the comment");
5955
5333
  },
5956
- swapLabels: async (issue4, removed, added) => {
5957
- const number = issueNumber(issue4, "swapLabels");
5334
+ swapLabels: async (issue3, removed, added) => {
5335
+ const number = issueNumber(issue3, "swapLabels");
5958
5336
  const off = labelName(removed, "swapLabels", "the removed label");
5959
5337
  const on = labelName(added, "swapLabels", "the added label");
5960
5338
  await succeed(["issue", "edit", number, "--remove-label", off, "--add-label", on], `gh issue edit ${number} --remove-label ${off} --add-label ${on}`);
5961
5339
  },
5962
- removeLabel: async (issue4, label) => {
5963
- const number = issueNumber(issue4, "removeLabel");
5340
+ removeLabel: async (issue3, label) => {
5341
+ const number = issueNumber(issue3, "removeLabel");
5964
5342
  const off = labelName(label, "removeLabel", "the label");
5965
5343
  await succeed(["issue", "edit", number, "--remove-label", off], `gh issue edit ${number} --remove-label ${off}`);
5966
5344
  }
@@ -5969,11 +5347,11 @@ function createGhIssueBoard(options) {
5969
5347
  }
5970
5348
 
5971
5349
  // src/board/issue.ts
5972
- import { existsSync as existsSync10, mkdirSync as mkdirSync5, readFileSync as readFileSync11, statSync as statSync8, writeFileSync as writeFileSync5 } from "fs";
5350
+ import { existsSync as existsSync10, mkdirSync as mkdirSync5, readFileSync as readFileSync11, statSync as statSync7, writeFileSync as writeFileSync5 } from "fs";
5973
5351
  import { dirname as dirname7, resolve as resolve7 } from "path";
5974
5352
 
5975
5353
  // src/refs/stamp.ts
5976
- import { createHash as createHash2 } from "crypto";
5354
+ import { createHash } from "crypto";
5977
5355
  var PRESENT = Object.freeze({ kind: "present" });
5978
5356
  var ABSENT = Object.freeze({ kind: "absent" });
5979
5357
  var UNREADABLE = Object.freeze({ kind: "unreadable" });
@@ -6007,7 +5385,7 @@ function normaliseIssueText(text) {
6007
5385
  `).trimEnd();
6008
5386
  }
6009
5387
  function sha256(text) {
6010
- return createHash2("sha256").update(text, "utf8").digest("hex");
5388
+ return createHash("sha256").update(text, "utf8").digest("hex");
6011
5389
  }
6012
5390
  function sectionName(line) {
6013
5391
  const text = line.trim().replace(/^##/u, "").replace(/[ \t]+#+$/u, "").trim();
@@ -6034,15 +5412,15 @@ function sections(body) {
6034
5412
  }
6035
5413
  return byName2;
6036
5414
  }
6037
- function issueFingerprint(issue4) {
6038
- const title = normaliseIssueText(issue4.title);
6039
- const body = normaliseIssueText(issue4.body);
5415
+ function issueFingerprint(issue3) {
5416
+ const title = normaliseIssueText(issue3.title);
5417
+ const body = normaliseIssueText(issue3.body);
6040
5418
  const headings = [...sections(body)].map(([name2, lines]) => Object.freeze({ name: name2, digest: sha256(lines.join(`
6041
5419
  `)) }));
6042
5420
  return Object.freeze({
6043
5421
  kind: "issue",
6044
5422
  digest: sha256(JSON.stringify([title, body])),
6045
- state: issue4.state,
5423
+ state: issue3.state,
6046
5424
  headings: Object.freeze(headings)
6047
5425
  });
6048
5426
  }
@@ -6264,11 +5642,11 @@ var STOP_WORDS = new Set([
6264
5642
  var CLAUSE_BREAK = /[,;:([\u2013\u2014]|\s-\s/u;
6265
5643
  var LEADING_ID = /^\s*#?rafa[-\s]?\d+\s*[-:.\u2013\u2014]?\s*/iu;
6266
5644
  var NON_WORD = /[^a-z0-9]+/gu;
6267
- function requireIssueNumber(issue4) {
6268
- if (!Number.isInteger(issue4) || issue4 <= 0) {
6269
- throw new RangeError(`An issue number must be a positive integer, not ${String(issue4)}.`);
5645
+ function requireIssueNumber(issue3) {
5646
+ if (!Number.isInteger(issue3) || issue3 <= 0) {
5647
+ throw new RangeError(`An issue number must be a positive integer, not ${String(issue3)}.`);
6270
5648
  }
6271
- return issue4;
5649
+ return issue3;
6272
5650
  }
6273
5651
  function wordsOf(title) {
6274
5652
  return title.toLowerCase().replace(NON_WORD, " ").split(" ").filter((word) => word !== "");
@@ -6287,35 +5665,35 @@ function slugFromTitle(title) {
6287
5665
  return FALLBACK_SLUG;
6288
5666
  return chosen.slice(0, MAX_SLUG_WORDS).join("-");
6289
5667
  }
6290
- function boardId(issue4) {
6291
- return `${ID_PREFIX}-${requireIssueNumber(issue4)}`;
5668
+ function boardId(issue3) {
5669
+ return `${ID_PREFIX}-${requireIssueNumber(issue3)}`;
6292
5670
  }
6293
- function planStub(issue4, title) {
6294
- return `${boardId(issue4)}-${slugFromTitle(title)}`;
5671
+ function planStub(issue3, title) {
5672
+ return `${boardId(issue3)}-${slugFromTitle(title)}`;
6295
5673
  }
6296
- function specFileName(issue4, title) {
6297
- return `${planStub(issue4, title)}${SPEC_EXTENSION}`;
5674
+ function specFileName(issue3, title) {
5675
+ return `${planStub(issue3, title)}${SPEC_EXTENSION}`;
6298
5676
  }
6299
- function specPath(specsDir, issue4, title) {
6300
- return path.join(specsDir, specFileName(issue4, title));
5677
+ function specPath(specsDir, issue3, title) {
5678
+ return path.join(specsDir, specFileName(issue3, title));
6301
5679
  }
6302
- function notesFileName(issue4) {
6303
- return `${boardId(issue4)}${NOTES_SUFFIX}${SPEC_EXTENSION}`;
5680
+ function notesFileName(issue3) {
5681
+ return `${boardId(issue3)}${NOTES_SUFFIX}${SPEC_EXTENSION}`;
6304
5682
  }
6305
- function notesPath(specsDir, issue4) {
6306
- return path.join(specsDir, notesFileName(issue4));
5683
+ function notesPath(specsDir, issue3) {
5684
+ return path.join(specsDir, notesFileName(issue3));
6307
5685
  }
6308
5686
 
6309
5687
  // src/board/previous-copy.ts
6310
- import { linkSync as linkSync2, mkdirSync as mkdirSync4, readdirSync as readdirSync6, statSync as statSync7, unlinkSync } from "fs";
6311
- import { basename as basename6, extname, join as join11, resolve as resolve6 } from "path";
5688
+ import { linkSync as linkSync2, mkdirSync as mkdirSync4, readdirSync as readdirSync5, statSync as statSync6, unlinkSync } from "fs";
5689
+ import { basename as basename6, extname, join as join10, resolve as resolve6 } from "path";
6312
5690
  var PREFIX2 = "board previous copy";
6313
5691
  var PREVIOUS_DIR_NAME = "previous";
6314
5692
  var PREVIOUS_COPY_WARN_ABOVE = 50;
6315
5693
  var COPY_EXTENSION = ".md";
6316
5694
  var FIRST_SUFFIX = 2;
6317
5695
  function previousDir(specsDir) {
6318
- return join11(specsDir, PREVIOUS_DIR_NAME);
5696
+ return join10(specsDir, PREVIOUS_DIR_NAME);
6319
5697
  }
6320
5698
  function two(value) {
6321
5699
  return String(value).padStart(2, "0");
@@ -6337,7 +5715,7 @@ function linkUnderFreeName(source, directory, savedName, timestamp) {
6337
5715
  for (let attempt = 1;; attempt += 1) {
6338
5716
  const name2 = previousCopyName(savedName, timestamp, attempt);
6339
5717
  try {
6340
- linkSync2(source, join11(directory, name2));
5718
+ linkSync2(source, join10(directory, name2));
6341
5719
  return name2;
6342
5720
  } catch (error) {
6343
5721
  if (!isTaken(error))
@@ -6353,15 +5731,15 @@ function movePreviousCopy(options) {
6353
5731
  const savedName = basename6(path2);
6354
5732
  let name2;
6355
5733
  try {
6356
- const timestamp = previousTimestamp(statSync7(source).mtime);
5734
+ const timestamp = previousTimestamp(statSync6(source).mtime);
6357
5735
  mkdirSync4(absoluteDirectory, { recursive: true });
6358
5736
  name2 = linkUnderFreeName(source, absoluteDirectory, savedName, timestamp);
6359
5737
  } catch (error) {
6360
5738
  throw new Error(`${PREFIX2}: ${path2} could not be moved to ${directory}: ${messageOf(error)}`, { cause: error });
6361
5739
  }
6362
5740
  const moved = {
6363
- path: join11(directory, name2),
6364
- absolute: join11(absoluteDirectory, name2)
5741
+ path: join10(directory, name2),
5742
+ absolute: join10(absoluteDirectory, name2)
6365
5743
  };
6366
5744
  try {
6367
5745
  unlinkSync(source);
@@ -6376,7 +5754,7 @@ function isMissing(error) {
6376
5754
  function countPreviousCopies(repoRoot, specsDir) {
6377
5755
  const directory = previousDir(specsDir);
6378
5756
  try {
6379
- return readdirSync6(resolve6(repoRoot, directory), { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(COPY_EXTENSION)).length;
5757
+ return readdirSync5(resolve6(repoRoot, directory), { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(COPY_EXTENSION)).length;
6380
5758
  } catch (error) {
6381
5759
  if (isMissing(error))
6382
5760
  return 0;
@@ -6448,30 +5826,30 @@ function readViewedIssue(stdout, number, command) {
6448
5826
  }
6449
5827
  function createGhSpecIssueReader(options) {
6450
5828
  const { gh } = options;
6451
- return async (issue4) => {
6452
- const number = issueNumber2(issue4);
5829
+ return async (issue3) => {
5830
+ const number = issueNumber2(issue3);
6453
5831
  const command = `gh issue view ${number} --json ${ISSUE_VIEW_FIELDS}`;
6454
5832
  const result = await gh(["issue", "view", number, "--json", ISSUE_VIEW_FIELDS]);
6455
5833
  if (!result.ok)
6456
5834
  throw new Error(`${PREFIX3}: ${detailOf3(result, command)}`);
6457
- return readViewedIssue(result.stdout, issue4, command);
5835
+ return readViewedIssue(result.stdout, issue3, command);
6458
5836
  };
6459
5837
  }
6460
5838
  function hasSpecLabel(labels) {
6461
5839
  return labels.some((label) => label.trim().toLowerCase() === SPEC_LABEL);
6462
5840
  }
6463
- function closedIssueMessage(issue4) {
6464
- return `issue #${String(issue4)} is closed, and a plan is written from an issue that is open;` + " reopen it, or plan from the issue that took its place";
5841
+ function closedIssueMessage(issue3) {
5842
+ return `issue #${String(issue3)} is closed, and a plan is written from an issue that is open;` + " reopen it, or plan from the issue that took its place";
6465
5843
  }
6466
- function missingSpecLabelMessage(issue4) {
6467
- return `issue #${String(issue4)} is not labelled ${SPEC_LABEL}, so it is not a spec;` + ` label it ${SPEC_LABEL}, or plan from the spec issue instead`;
5844
+ function missingSpecLabelMessage(issue3) {
5845
+ return `issue #${String(issue3)} is not labelled ${SPEC_LABEL}, so it is not a spec;` + ` label it ${SPEC_LABEL}, or plan from the spec issue instead`;
6468
5846
  }
6469
- function requireSpecIssue(issue4) {
6470
- if (issue4.state === "CLOSED") {
6471
- throw new CommandExit(ISSUE_REFUSAL_EXIT, closedIssueMessage(issue4.number));
5847
+ function requireSpecIssue(issue3) {
5848
+ if (issue3.state === "CLOSED") {
5849
+ throw new CommandExit(ISSUE_REFUSAL_EXIT, closedIssueMessage(issue3.number));
6472
5850
  }
6473
- if (!hasSpecLabel(issue4.labels)) {
6474
- throw new CommandExit(ISSUE_REFUSAL_EXIT, missingSpecLabelMessage(issue4.number));
5851
+ if (!hasSpecLabel(issue3.labels)) {
5852
+ throw new CommandExit(ISSUE_REFUSAL_EXIT, missingSpecLabelMessage(issue3.number));
6475
5853
  }
6476
5854
  }
6477
5855
  function normalise(text) {
@@ -6576,7 +5954,7 @@ function readSnapshotChange(options) {
6576
5954
  function readIfFile(file, what) {
6577
5955
  if (!existsSync10(file))
6578
5956
  return null;
6579
- if (!statSync8(file).isFile()) {
5957
+ if (!statSync7(file).isFile()) {
6580
5958
  throw new CommandExit(ISSUE_REFUSAL_EXIT, `${what} ${file} is not a file, and a spec is one`);
6581
5959
  }
6582
5960
  try {
@@ -6585,26 +5963,26 @@ function readIfFile(file, what) {
6585
5963
  throw new CommandExit(ISSUE_REFUSAL_EXIT, `${what} ${file} could not be read: ${messageOf(error)}`);
6586
5964
  }
6587
5965
  }
6588
- function readLocalNotes(repoRoot, specsDir, issue4) {
6589
- return readIfFile(resolve7(repoRoot, notesPath(specsDir, issue4)), "the local notes");
5966
+ function readLocalNotes(repoRoot, specsDir, issue3) {
5967
+ return readIfFile(resolve7(repoRoot, notesPath(specsDir, issue3)), "the local notes");
6590
5968
  }
6591
- function snapshotDiffersMessage(path2, issue4) {
6592
- return `${path2} is a snapshot of issue #${String(issue4)} that no longer matches it:` + " the issue body, or the local notes appended to it, changed since the snapshot was written;" + ` pass ${REFRESH_FLAG} to plan from the issue as it reads now, or --spec=${path2} to plan from` + " the snapshot as it stands";
5969
+ function snapshotDiffersMessage(path2, issue3) {
5970
+ return `${path2} is a snapshot of issue #${String(issue3)} that no longer matches it:` + " the issue body, or the local notes appended to it, changed since the snapshot was written;" + ` pass ${REFRESH_FLAG} to plan from the issue as it reads now, or --spec=${path2} to plan from` + " the snapshot as it stands";
6593
5971
  }
6594
- function notesCollisionMessage(path2, issue4) {
6595
- return `the snapshot of issue #${String(issue4)} and its local notes are both ${path2},` + " so writing the snapshot would overwrite the notes; retitle the issue";
5972
+ function notesCollisionMessage(path2, issue3) {
5973
+ return `the snapshot of issue #${String(issue3)} and its local notes are both ${path2},` + " so writing the snapshot would overwrite the notes; retitle the issue";
6596
5974
  }
6597
5975
  function writeSpecSnapshot(options) {
6598
- const { repoRoot, specsDir, issue: issue4, refresh } = options;
6599
- const path2 = specPath(specsDir, issue4.number, issue4.title);
6600
- const notes = notesPath(specsDir, issue4.number);
5976
+ const { repoRoot, specsDir, issue: issue3, refresh } = options;
5977
+ const path2 = specPath(specsDir, issue3.number, issue3.title);
5978
+ const notes = notesPath(specsDir, issue3.number);
6601
5979
  if (path2 === notes) {
6602
- throw new CommandExit(ISSUE_REFUSAL_EXIT, notesCollisionMessage(path2, issue4.number));
5980
+ throw new CommandExit(ISSUE_REFUSAL_EXIT, notesCollisionMessage(path2, issue3.number));
6603
5981
  }
6604
- const local = readLocalNotes(repoRoot, specsDir, issue4.number);
5982
+ const local = readLocalNotes(repoRoot, specsDir, issue3.number);
6605
5983
  const absolute = resolve7(repoRoot, path2);
6606
5984
  const existing = readIfFile(absolute, "the spec snapshot");
6607
- const text = carryRefsBlock(existing, snapshotText(issue4.body, local));
5985
+ const text = carryRefsBlock(existing, snapshotText(issue3.body, local));
6608
5986
  const written = {
6609
5987
  path: path2,
6610
5988
  absolute,
@@ -6613,11 +5991,11 @@ function writeSpecSnapshot(options) {
6613
5991
  previous: null,
6614
5992
  text
6615
5993
  };
6616
- const action = rewriteAction(existing, issue4.body, local, notes, refresh);
5994
+ const action = rewriteAction(existing, issue3.body, local, notes, refresh);
6617
5995
  if (action === "unchanged")
6618
5996
  return { ...written, action, text: existing ?? text };
6619
5997
  if (action === null) {
6620
- throw new CommandExit(ISSUE_REFUSAL_EXIT, snapshotDiffersMessage(path2, issue4.number));
5998
+ throw new CommandExit(ISSUE_REFUSAL_EXIT, snapshotDiffersMessage(path2, issue3.number));
6621
5999
  }
6622
6000
  const previous = action === "created" ? null : movePreviousCopy({ repoRoot, specsDir, path: path2 }).path;
6623
6001
  try {
@@ -6640,10 +6018,10 @@ function rewriteAction(existing, body, notes, notesFile, refresh) {
6640
6018
  }
6641
6019
 
6642
6020
  // src/pr/git.ts
6643
- import { spawnSync as spawnSync2 } from "child_process";
6021
+ import { spawnSync } from "child_process";
6644
6022
  function createGitRunner(cwd) {
6645
6023
  return (args) => {
6646
- const result = spawnSync2("git", [...args], {
6024
+ const result = spawnSync("git", [...args], {
6647
6025
  cwd,
6648
6026
  encoding: "utf8",
6649
6027
  env: { ...process.env, LC_ALL: "C" }
@@ -6787,11 +6165,11 @@ function parseRoadmapBody(body) {
6787
6165
  if (found === null)
6788
6166
  return;
6789
6167
  const [, box = "", digits = "", rest = ""] = found;
6790
- const issue4 = Number.parseInt(digits, 10);
6791
- if (!Number.isSafeInteger(issue4) || issue4 < 1)
6168
+ const issue3 = Number.parseInt(digits, 10);
6169
+ if (!Number.isSafeInteger(issue3) || issue3 < 1)
6792
6170
  return;
6793
6171
  lines = [...lines, {
6794
- issue: issue4,
6172
+ issue: issue3,
6795
6173
  ticked: box.trim() !== "",
6796
6174
  why: whyOf(rest),
6797
6175
  lineNumber: index + 1
@@ -6856,11 +6234,11 @@ async function resolveRoadmapIssue(options) {
6856
6234
  if (configured !== null)
6857
6235
  return configured;
6858
6236
  const wanted = ROADMAP_TITLE.toLowerCase();
6859
- const titled = (await search()).filter((issue4) => issue4.title.trim().toLowerCase() === wanted);
6237
+ const titled = (await search()).filter((issue3) => issue3.title.trim().toLowerCase() === wanted);
6860
6238
  if (titled.length === 0)
6861
6239
  throw new CommandExit(ROADMAP_REFUSAL_EXIT, noRoadmapMessage());
6862
6240
  if (titled.length > 1) {
6863
- throw new CommandExit(ROADMAP_REFUSAL_EXIT, severalRoadmapsMessage(titled.map((issue4) => issue4.number)));
6241
+ throw new CommandExit(ROADMAP_REFUSAL_EXIT, severalRoadmapsMessage(titled.map((issue3) => issue3.number)));
6864
6242
  }
6865
6243
  return titled[0]?.number ?? 0;
6866
6244
  }
@@ -6881,10 +6259,10 @@ function createGhOpenPullRequests(options) {
6881
6259
  };
6882
6260
  }
6883
6261
  function closedIssuesIn(body) {
6884
- return Object.freeze([...body.matchAll(CLOSING)].map((found) => Number.parseInt(found[1] ?? "", 10)).filter((issue4) => Number.isSafeInteger(issue4) && issue4 > 0));
6262
+ return Object.freeze([...body.matchAll(CLOSING)].map((found) => Number.parseInt(found[1] ?? "", 10)).filter((issue3) => Number.isSafeInteger(issue3) && issue3 > 0));
6885
6263
  }
6886
- function branchClaims(ref, issue4) {
6887
- const claim = `${BRANCH_PREFIX}/${boardId(issue4)}`;
6264
+ function branchClaims(ref, issue3) {
6265
+ const claim = `${BRANCH_PREFIX}/${boardId(issue3)}`;
6888
6266
  return new RegExp(`(?:^|/)${claim}(?:-|$)`, "u").test(ref);
6889
6267
  }
6890
6268
  function outputLines(stdout) {
@@ -6909,11 +6287,11 @@ function createRoadmapReadings(options) {
6909
6287
  const { issues, branches, pullRequests } = options;
6910
6288
  let open = null;
6911
6289
  return Object.freeze({
6912
- isClosed: async (issue4) => (await issues(issue4)).state === "CLOSED",
6913
- branchFor: (issue4) => branches.refs.find((ref) => branchClaims(ref, issue4)) ?? null,
6914
- pullRequestFor: async (issue4) => {
6290
+ isClosed: async (issue3) => (await issues(issue3)).state === "CLOSED",
6291
+ branchFor: (issue3) => branches.refs.find((ref) => branchClaims(ref, issue3)) ?? null,
6292
+ pullRequestFor: async (issue3) => {
6915
6293
  open = open ?? pullRequests();
6916
- const closing = (await open).find((pull) => closedIssuesIn(pull.body).includes(issue4));
6294
+ const closing = (await open).find((pull) => closedIssuesIn(pull.body).includes(issue3));
6917
6295
  return closing?.number ?? null;
6918
6296
  }
6919
6297
  });
@@ -7000,10 +6378,10 @@ function foreignIn(text) {
7000
6378
  return found;
7001
6379
  }
7002
6380
  var NAMED_NOTHING = { ids: [], foreign: [] };
7003
- function reading(kind, issue4, found, named, unknown) {
6381
+ function reading(kind, issue3, found, named, unknown) {
7004
6382
  return {
7005
6383
  kind,
7006
- issue: issue4,
6384
+ issue: issue3,
7007
6385
  line: found?.number ?? null,
7008
6386
  text: found?.text ?? null,
7009
6387
  blockers: named.ids,
@@ -7011,21 +6389,21 @@ function reading(kind, issue4, found, named, unknown) {
7011
6389
  unknown
7012
6390
  };
7013
6391
  }
7014
- function readBlockedBy(issue4, body, known) {
6392
+ function readBlockedBy(issue3, body, known) {
7015
6393
  const found = findFieldLine(body);
7016
6394
  if (found === null)
7017
- return reading("no-line", issue4, null, NAMED_NOTHING, []);
6395
+ return reading("no-line", issue3, null, NAMED_NOTHING, []);
7018
6396
  const foreign = foreignIn(found.text);
7019
6397
  const ids = idsIn(found.text.replace(FOREIGN_ID, " "));
7020
6398
  const named = { ids, foreign };
7021
6399
  if (ids.length === 0)
7022
- return reading("no-ids", issue4, found, named, []);
7023
- const unknown = known === undefined ? [] : ids.filter((id) => id !== issue4 && !known.has(id));
7024
- if (ids.includes(issue4))
7025
- return reading("self-reference", issue4, found, named, unknown);
6400
+ return reading("no-ids", issue3, found, named, []);
6401
+ const unknown = known === undefined ? [] : ids.filter((id) => id !== issue3 && !known.has(id));
6402
+ if (ids.includes(issue3))
6403
+ return reading("self-reference", issue3, found, named, unknown);
7026
6404
  if (unknown.length > 0)
7027
- return reading("unknown-issue", issue4, found, named, unknown);
7028
- return reading("blocked", issue4, found, named, unknown);
6405
+ return reading("unknown-issue", issue3, found, named, unknown);
6406
+ return reading("blocked", issue3, found, named, unknown);
7029
6407
  }
7030
6408
  function nameIds(ids) {
7031
6409
  return ids.map((id) => `#${String(id)}`).join(" ");
@@ -7055,17 +6433,17 @@ function blockedFaultMessage(read) {
7055
6433
 
7056
6434
  // src/board/blocked-line.ts
7057
6435
  function blockerStatesOf(issues) {
7058
- return async (issue4) => {
6436
+ return async (issue3) => {
7059
6437
  try {
7060
- return (await issues(issue4)).state;
6438
+ return (await issues(issue3)).state;
7061
6439
  } catch {
7062
6440
  return null;
7063
6441
  }
7064
6442
  };
7065
6443
  }
7066
- function blocked(issue4, blockers, open, unread, fault) {
6444
+ function blocked(issue3, blockers, open, unread, fault) {
7067
6445
  return Object.freeze({
7068
- issue: issue4,
6446
+ issue: issue3,
7069
6447
  blockers: Object.freeze([...blockers]),
7070
6448
  open: Object.freeze([...open]),
7071
6449
  unread: Object.freeze([...unread]),
@@ -7079,17 +6457,17 @@ async function heldStates(blockers, states) {
7079
6457
  }
7080
6458
  return held;
7081
6459
  }
7082
- async function readBlockedLine(issue4, states) {
7083
- if (!hasSpecBlockedLabel(issue4.labels))
6460
+ async function readBlockedLine(issue3, states) {
6461
+ if (!hasSpecBlockedLabel(issue3.labels))
7084
6462
  return null;
7085
- const read = readBlockedBy(issue4.number, issue4.body);
6463
+ const read = readBlockedBy(issue3.number, issue3.body);
7086
6464
  if (read.kind !== "blocked") {
7087
- return blocked(issue4.number, read.blockers, [], [], blockedFaultMessage(read));
6465
+ return blocked(issue3.number, read.blockers, [], [], blockedFaultMessage(read));
7088
6466
  }
7089
6467
  const held = await heldStates(read.blockers, states);
7090
6468
  const open = held.filter((one) => one.state === "OPEN").map((one) => one.id);
7091
6469
  const unread = held.filter((one) => one.state === null).map((one) => one.id);
7092
- return open.length + unread.length === 0 ? null : blocked(issue4.number, read.blockers, open, unread, null);
6470
+ return open.length + unread.length === 0 ? null : blocked(issue3.number, read.blockers, open, unread, null);
7093
6471
  }
7094
6472
  function nameBlockers(line) {
7095
6473
  return [
@@ -7105,12 +6483,12 @@ function plannableReadings(options) {
7105
6483
  const states = blockerStatesOf(issues);
7106
6484
  return Object.freeze({
7107
6485
  skip: (line) => readRoadmapSkip(line, readings),
7108
- blocking: async (issue4) => readBlockedLine(await issues(issue4), states),
7109
- isReady: async (issue4) => hasSpecReadyLabel((await issues(issue4)).labels)
6486
+ blocking: async (issue3) => readBlockedLine(await issues(issue3), states),
6487
+ isReady: async (issue3) => hasSpecReadyLabel((await issues(issue3)).labels)
7110
6488
  });
7111
6489
  }
7112
- function notReadySentence(issue4) {
7113
- return `#${String(issue4)} not ready: it carries no ${SPEC_READY_LABEL} label`;
6490
+ function notReadySentence(issue3) {
6491
+ return `#${String(issue3)} not ready: it carries no ${SPEC_READY_LABEL} label`;
7114
6492
  }
7115
6493
  async function passOf(line, readings) {
7116
6494
  const skip = await readings.skip(line);
@@ -7132,40 +6510,40 @@ async function pickPlannableLine(lines, after, readings) {
7132
6510
  }
7133
6511
  return Object.freeze({ line: null, passed: Object.freeze(passed2) });
7134
6512
  }
7135
- function alternativeQuestion(issue4) {
7136
- return `Plan #${String(issue4)} instead? [y/N] `;
6513
+ function alternativeQuestion(issue3) {
6514
+ return `Plan #${String(issue3)} instead? [y/N] `;
7137
6515
  }
7138
- function noAlternativeMessage(issue4) {
7139
- const number = String(issue4);
6516
+ function noAlternativeMessage(issue3) {
6517
+ const number = String(issue3);
7140
6518
  return `no line under #${number} is ready, not blocked and not taken, so there is nothing to plan instead;` + ` take ${SPEC_BLOCKED_LABEL} off #${number} with rafa issue unblock ${number} once its blockers close`;
7141
6519
  }
7142
- function unaskedMessage(issue4, alternative) {
6520
+ function unaskedMessage(issue3, alternative) {
7143
6521
  const number = String(alternative);
7144
- return `#${number} takes a yes and this run has nobody to ask, so nothing was planned;` + ` plan it with rafa plan create --issue=${number},` + ` or clear #${String(issue4)} with rafa issue unblock ${String(issue4)}`;
6522
+ return `#${number} takes a yes and this run has nobody to ask, so nothing was planned;` + ` plan it with rafa plan create --issue=${number},` + ` or clear #${String(issue3)} with rafa issue unblock ${String(issue3)}`;
7145
6523
  }
7146
6524
  function declinedMessage(alternative) {
7147
6525
  return `#${String(alternative)} was not planned, and nothing was written`;
7148
6526
  }
7149
6527
 
7150
6528
  // src/board/snapshot-settle.ts
7151
- import { existsSync as existsSync11, readFileSync as readFileSync12, statSync as statSync9 } from "fs";
6529
+ import { existsSync as existsSync11, readFileSync as readFileSync12, statSync as statSync8 } from "fs";
7152
6530
  import { resolve as resolve8 } from "path";
7153
6531
  var PREFIX5 = "board snapshot settle";
7154
6532
  var DATE_LENGTH = 10;
7155
6533
  function savedCopyDate(savedAt) {
7156
6534
  return savedAt.toISOString().slice(0, DATE_LENGTH);
7157
6535
  }
7158
- function refreshQuestion(issue4, savedAt) {
7159
- return `Issue #${String(issue4)} changed since the saved copy of ${savedCopyDate(savedAt)}.` + " Plan from it as it reads now? [y/N] ";
6536
+ function refreshQuestion(issue3, savedAt) {
6537
+ return `Issue #${String(issue3)} changed since the saved copy of ${savedCopyDate(savedAt)}.` + " Plan from it as it reads now? [y/N] ";
7160
6538
  }
7161
- function notesRebuiltLine(path2, issue4, previous) {
7162
- return `local notes changed; rebuilt ${path2} from issue #${String(issue4)} and the notes,` + ` the old copy is at ${previous}`;
6539
+ function notesRebuiltLine(path2, issue3, previous) {
6540
+ return `local notes changed; rebuilt ${path2} from issue #${String(issue3)} and the notes,` + ` the old copy is at ${previous}`;
7163
6541
  }
7164
6542
  function readSavedCopy(absolute, path2) {
7165
6543
  if (!existsSync11(absolute))
7166
6544
  return null;
7167
6545
  try {
7168
- const stat = statSync9(absolute);
6546
+ const stat = statSync8(absolute);
7169
6547
  if (!stat.isFile())
7170
6548
  return null;
7171
6549
  return { text: readFileSync12(absolute, "utf8"), savedAt: stat.mtime };
@@ -7180,10 +6558,10 @@ function printChange(change, output) {
7180
6558
  }
7181
6559
  }
7182
6560
  async function settleSpecSnapshot(options) {
7183
- const { repoRoot, specsDir, issue: issue4, refresh, output } = options;
7184
- const write = (rewrite) => writeSpecSnapshot({ repoRoot, specsDir, issue: issue4, refresh: rewrite });
7185
- const path2 = specPath(specsDir, issue4.number, issue4.title);
7186
- const notes = notesPath(specsDir, issue4.number);
6561
+ const { repoRoot, specsDir, issue: issue3, refresh, output } = options;
6562
+ const write = (rewrite) => writeSpecSnapshot({ repoRoot, specsDir, issue: issue3, refresh: rewrite });
6563
+ const path2 = specPath(specsDir, issue3.number, issue3.title);
6564
+ const notes = notesPath(specsDir, issue3.number);
7187
6565
  if (path2 === notes)
7188
6566
  return write(refresh);
7189
6567
  const saved = readSavedCopy(resolve8(repoRoot, path2), path2);
@@ -7191,8 +6569,8 @@ async function settleSpecSnapshot(options) {
7191
6569
  return write(refresh);
7192
6570
  const change = readSnapshotChange({
7193
6571
  saved: saved.text,
7194
- body: issue4.body,
7195
- notes: readLocalNotes(repoRoot, specsDir, issue4.number),
6572
+ body: issue3.body,
6573
+ notes: readLocalNotes(repoRoot, specsDir, issue3.number),
7196
6574
  notesPath: notes
7197
6575
  });
7198
6576
  if (change.kind === "unchanged")
@@ -7201,15 +6579,15 @@ async function settleSpecSnapshot(options) {
7201
6579
  if (change.kind === "notes") {
7202
6580
  const rebuilt = write(refresh);
7203
6581
  if (rebuilt.previous !== null)
7204
- output.info(notesRebuiltLine(rebuilt.path, issue4.number, rebuilt.previous));
6582
+ output.info(notesRebuiltLine(rebuilt.path, issue3.number, rebuilt.previous));
7205
6583
  return rebuilt;
7206
6584
  }
7207
6585
  if (refresh)
7208
6586
  return write(true);
7209
6587
  const offer = options.offerRefresh ?? null;
7210
- const accepted = offer !== null && await offer({ issue: issue4.number, path: path2, savedAt: saved.savedAt });
6588
+ const accepted = offer !== null && await offer({ issue: issue3.number, path: path2, savedAt: saved.savedAt });
7211
6589
  if (!accepted)
7212
- throw new CommandExit(ISSUE_REFUSAL_EXIT, snapshotDiffersMessage(path2, issue4.number));
6590
+ throw new CommandExit(ISSUE_REFUSAL_EXIT, snapshotDiffersMessage(path2, issue3.number));
7213
6591
  return write(true);
7214
6592
  }
7215
6593
 
@@ -7238,11 +6616,11 @@ function readFlagWord(args, flag) {
7238
6616
  }
7239
6617
  function issueNumberOf(word, flag) {
7240
6618
  const trimmed = word.trim();
7241
- const issue4 = Number.parseInt(trimmed, 10);
7242
- if (!/^\d+$/u.test(trimmed) || !Number.isSafeInteger(issue4) || issue4 < 1) {
6619
+ const issue3 = Number.parseInt(trimmed, 10);
6620
+ if (!/^\d+$/u.test(trimmed) || !Number.isSafeInteger(issue3) || issue3 < 1) {
7243
6621
  throw new CommandExit(SOURCE_REFUSAL_EXIT, notAnIssueMessage(flag, word));
7244
6622
  }
7245
- return issue4;
6623
+ return issue3;
7246
6624
  }
7247
6625
  function requestOf(flag, word) {
7248
6626
  if (flag === NEXT_FLAG) {
@@ -7292,8 +6670,8 @@ function alternativeLine(line) {
7292
6670
  const id = `\u25B6 Ready instead: issue #${String(line.issue)}`;
7293
6671
  return line.why === "" ? id : `${id} \u2014 ${line.why}`;
7294
6672
  }
7295
- function describeIssue(issue4) {
7296
- return `issue #${String(issue4.number)} "${issue4.title}"`;
6673
+ function describeIssue(issue3) {
6674
+ return `issue #${String(issue3.number)} "${issue3.title}"`;
7297
6675
  }
7298
6676
  function dryRunLine(what) {
7299
6677
  return `\uD83D\uDD0E ${DRY_RUN_FLAG}: would plan from ${what}. Nothing was written.`;
@@ -7303,9 +6681,9 @@ function stopped(reason) {
7303
6681
  }
7304
6682
  function memoiseIssues(issues) {
7305
6683
  const read = new Map;
7306
- return (issue4) => {
7307
- const taken = read.get(issue4) ?? issues(issue4);
7308
- read.set(issue4, taken);
6684
+ return (issue3) => {
6685
+ const taken = read.get(issue3) ?? issues(issue3);
6686
+ read.set(issue3, taken);
7309
6687
  return taken;
7310
6688
  };
7311
6689
  }
@@ -7429,8 +6807,8 @@ async function resolveSpecSource(options) {
7429
6807
 
7430
6808
  // src/board/plan-spec.ts
7431
6809
  var BOARD_REFUSAL_EXIT = 2;
7432
- function issueSource(issue4) {
7433
- return `issue #${String(issue4)}`;
6810
+ function issueSource(issue3) {
6811
+ return `issue #${String(issue3)}`;
7434
6812
  }
7435
6813
  var UNNAMED_REPO = "this repository";
7436
6814
  function boardRepoLabel(git) {
@@ -7438,25 +6816,25 @@ function boardRepoLabel(git) {
7438
6816
  const normalized = result.ok ? normalizeRemote(result.stdout.trim()) : "";
7439
6817
  return normalized === "" ? UNNAMED_REPO : normalized;
7440
6818
  }
7441
- async function passReadyLabel(issue4, offer) {
7442
- if (hasSpecReadyLabel(issue4.labels))
6819
+ async function passReadyLabel(issue3, offer) {
6820
+ if (hasSpecReadyLabel(issue3.labels))
7443
6821
  return;
7444
6822
  if (offer !== undefined) {
7445
- requireNoLeak(issueSource(issue4.number), issue4.body);
7446
- if (await offer(issue4))
6823
+ requireNoLeak(issueSource(issue3.number), issue3.body);
6824
+ if (await offer(issue3))
7447
6825
  return;
7448
6826
  }
7449
- requireSpecReadyLabel(issue4.number, issue4.labels);
6827
+ requireSpecReadyLabel(issue3.number, issue3.labels);
7450
6828
  }
7451
- async function inspectSpecIssue(issue4, trust, offer) {
7452
- const source = issueSource(issue4.number);
7453
- await requireTrustedBoardAuthor({ kind: "issue", number: issue4.number }, trust, issue4.author);
7454
- await passReadyLabel(issue4, offer);
7455
- requireNoLeak(source, issue4.body);
7456
- requireCompleteSpec(source, issue4.body);
6829
+ async function inspectSpecIssue(issue3, trust, offer) {
6830
+ const source = issueSource(issue3.number);
6831
+ await requireTrustedBoardAuthor({ kind: "issue", number: issue3.number }, trust, issue3.author);
6832
+ await passReadyLabel(issue3, offer);
6833
+ requireNoLeak(source, issue3.body);
6834
+ requireCompleteSpec(source, issue3.body);
7457
6835
  }
7458
- async function inspectRoadmapIssue(issue4, trust) {
7459
- await requireTrustedBoardAuthor({ kind: "issue", number: issue4.number }, trust, issue4.author);
6836
+ async function inspectRoadmapIssue(issue3, trust) {
6837
+ await requireTrustedBoardAuthor({ kind: "issue", number: issue3.number }, trust, issue3.author);
7460
6838
  }
7461
6839
  function memoisePermissions(permissions) {
7462
6840
  const read = new Map;
@@ -7484,7 +6862,7 @@ async function resolvePlanSpec(options) {
7484
6862
  return board;
7485
6863
  };
7486
6864
  const offerReady = options.offerReady ?? null;
7487
- const offer = offerReady === null || options.dryRun ? undefined : (issue4) => offerReady({ issue: issue4, gh, trust: trust(), output });
6865
+ const offer = offerReady === null || options.dryRun ? undefined : (issue3) => offerReady({ issue: issue3, gh, trust: trust(), output });
7488
6866
  const offerAlternative = options.offerAlternative ?? null;
7489
6867
  const alternative = offerAlternative === null || options.dryRun ? undefined : offerAlternative;
7490
6868
  const offerRefresh = options.dryRun ? null : options.offerRefresh ?? null;
@@ -7496,14 +6874,14 @@ async function resolvePlanSpec(options) {
7496
6874
  specsDir: options.specsDir,
7497
6875
  findSpec: options.findSpec,
7498
6876
  issues: createGhSpecIssueReader({ gh }),
7499
- inspect: (issue4) => inspectSpecIssue(issue4, trust(), offer),
6877
+ inspect: (issue3) => inspectSpecIssue(issue3, trust(), offer),
7500
6878
  offerRefresh,
7501
6879
  roadmap: {
7502
6880
  configured: options.roadmapIssue,
7503
6881
  search: createGhRoadmapSearch({ gh }),
7504
6882
  git,
7505
6883
  pullRequests: createGhOpenPullRequests({ gh }),
7506
- inspectRoadmap: (issue4) => inspectRoadmapIssue(issue4, trust()),
6884
+ inspectRoadmap: (issue3) => inspectRoadmapIssue(issue3, trust()),
7507
6885
  offerAlternative: alternative
7508
6886
  },
7509
6887
  output
@@ -7753,9 +7131,9 @@ function plural2(count, singular, many) {
7753
7131
  return count === 1 ? `1 ${singular}` : `${count} ${many}`;
7754
7132
  }
7755
7133
  function listChanges(entries) {
7756
- const shown2 = entries.slice(0, MAX_LISTED_CHANGES).map((entry) => `${INDENT}${entry}`);
7757
- const hidden = entries.length - shown2.length;
7758
- return hidden > 0 ? [...shown2, `${INDENT}... and ${hidden} more`] : shown2;
7134
+ const shown = entries.slice(0, MAX_LISTED_CHANGES).map((entry) => `${INDENT}${entry}`);
7135
+ const hidden = entries.length - shown.length;
7136
+ return hidden > 0 ? [...shown, `${INDENT}... and ${hidden} more`] : shown;
7759
7137
  }
7760
7138
  function triagePointer(number) {
7761
7139
  return `Run rafa pr triage ${number} to see why.`;
@@ -7897,7 +7275,7 @@ function remainingFrom(steps, failed2) {
7897
7275
  return at === -1 ? Object.freeze([]) : Object.freeze(steps.slice(at));
7898
7276
  }
7899
7277
  // src/pr/none.ts
7900
- import { spawnSync as spawnSync3 } from "child_process";
7278
+ import { spawnSync as spawnSync2 } from "child_process";
7901
7279
 
7902
7280
  // src/pr/provider.ts
7903
7281
  var GITHUB_HOST = "github.com";
@@ -7938,7 +7316,7 @@ function requireGhProvider(reading2) {
7938
7316
  // src/pr/none.ts
7939
7317
  var COMPARE_QUERY = "?expand=1";
7940
7318
  function pushBranch(dir, branch) {
7941
- const result = spawnSync3("git", ["push", "--set-upstream", "origin", branch], {
7319
+ const result = spawnSync2("git", ["push", "--set-upstream", "origin", branch], {
7942
7320
  cwd: dir,
7943
7321
  encoding: "utf8",
7944
7322
  env: { ...process.env, LC_ALL: "C" }
@@ -8324,9 +7702,9 @@ function plural3(count, singular, many) {
8324
7702
  return count === 1 ? `1 ${singular}` : `${count} ${many}`;
8325
7703
  }
8326
7704
  function listChanges2(entries) {
8327
- const shown2 = entries.slice(0, MAX_LISTED_CHANGES2).map((entry) => `${INDENT2}${entry}`);
8328
- const hidden = entries.length - shown2.length;
8329
- return hidden > 0 ? [...shown2, `${INDENT2}... and ${hidden} more`] : shown2;
7705
+ const shown = entries.slice(0, MAX_LISTED_CHANGES2).map((entry) => `${INDENT2}${entry}`);
7706
+ const hidden = entries.length - shown.length;
7707
+ return hidden > 0 ? [...shown, `${INDENT2}... and ${hidden} more`] : shown;
8330
7708
  }
8331
7709
  function trackedChanges(tree) {
8332
7710
  return Object.freeze(tree.entries.filter((entry) => !entry.startsWith(UNTRACKED_PREFIX)));
@@ -8525,7 +7903,7 @@ function parseArgs(argv, spec = {}) {
8525
7903
  // src/commands/loop/loop-sessions.ts
8526
7904
  import { execFileSync as execFileSync2 } from "child_process";
8527
7905
  import { readFileSync as readFileSync13 } from "fs";
8528
- import { join as join12, relative as relative4 } from "path";
7906
+ import { join as join11, relative as relative3 } from "path";
8529
7907
 
8530
7908
  // src/effort/store/task-finishes.ts
8531
7909
  import { existsSync as existsSync12 } from "fs";
@@ -8612,7 +7990,7 @@ function readSessionIdFlag(context, usage) {
8612
7990
  return value;
8613
7991
  }
8614
7992
  function runsLabel(root) {
8615
- return `${relative4(root, runsDir(root))}/`;
7993
+ return `${relative3(root, runsDir(root))}/`;
8616
7994
  }
8617
7995
  function readRecords(root, seams) {
8618
7996
  try {
@@ -8669,7 +8047,7 @@ function writeSession(root, record, change) {
8669
8047
  }
8670
8048
  }
8671
8049
  function readSessionChecklist(root, record) {
8672
- const plan = join12(root, record.plan);
8050
+ const plan = join11(root, record.plan);
8673
8051
  const file = [trackerPathFor(plan), plan].find((path2) => isFile(path2));
8674
8052
  if (file === undefined)
8675
8053
  return null;
@@ -8716,9 +8094,9 @@ var DEFAULT_BASE_BRANCH = "main";
8716
8094
  var DEFAULT_NEXT_SOURCE_SEAMS = Object.freeze({});
8717
8095
  function memoiseIssues2(issues) {
8718
8096
  const read = new Map;
8719
- return (issue4) => {
8720
- const taken = read.get(issue4) ?? issues(issue4);
8721
- read.set(issue4, taken);
8097
+ return (issue3) => {
8098
+ const taken = read.get(issue3) ?? issues(issue3);
8099
+ read.set(issue3, taken);
8722
8100
  return taken;
8723
8101
  };
8724
8102
  }
@@ -8743,8 +8121,8 @@ function ghNextBoard(options) {
8743
8121
  problems: branches.problems
8744
8122
  };
8745
8123
  },
8746
- blocking: async (issue4) => readBlockedLine(await issues(issue4), blockerStatesOf(issues)),
8747
- isReady: async (issue4) => hasSpecReadyLabel((await issues(issue4)).labels)
8124
+ blocking: async (issue3) => readBlockedLine(await issues(issue3), blockerStatesOf(issues)),
8125
+ isReady: async (issue3) => hasSpecReadyLabel((await issues(issue3)).labels)
8748
8126
  });
8749
8127
  }
8750
8128
  function nextProject(context) {
@@ -8942,9 +8320,9 @@ var NEXT_PRECONDITIONS = Object.freeze([
8942
8320
  "pulls-unusable"
8943
8321
  ]);
8944
8322
  function nameFiles(paths) {
8945
- const shown2 = paths.slice(0, MAX_NAMED_FILES).map((path2) => `\`${path2}\``);
8946
- const hidden = paths.length - shown2.length;
8947
- return hidden > 0 ? `${shown2.join(", ")} and ${hidden} more` : shown2.join(", ");
8323
+ const shown = paths.slice(0, MAX_NAMED_FILES).map((path2) => `\`${path2}\``);
8324
+ const hidden = paths.length - shown.length;
8325
+ return hidden > 0 ? `${shown.join(", ")} and ${hidden} more` : shown.join(", ");
8948
8326
  }
8949
8327
  function oneLine2(text) {
8950
8328
  return text.replace(/\s+/g, " ").trim();
@@ -9111,39 +8489,39 @@ async function readIssueReady(world) {
9111
8489
  const picked = await world.picked();
9112
8490
  if (picked === null || !picked.ready || picked.blocked !== null)
9113
8491
  return null;
9114
- const { issue: issue4 } = picked.line;
8492
+ const { issue: issue3 } = picked.line;
9115
8493
  return {
9116
8494
  id: "issue-ready",
9117
8495
  action: "plan",
9118
- reading: `#${issue4} is next on the roadmap and carries \`${SPEC_READY_LABEL}\``,
9119
- proposal: `create the plan for #${issue4}`,
9120
- issue: issue4
8496
+ reading: `#${issue3} is next on the roadmap and carries \`${SPEC_READY_LABEL}\``,
8497
+ proposal: `create the plan for #${issue3}`,
8498
+ issue: issue3
9121
8499
  };
9122
8500
  }
9123
8501
  async function readIssueBlocked(world) {
9124
8502
  const picked = await world.picked();
9125
8503
  if (picked === null || picked.blocked === null)
9126
8504
  return null;
9127
- const { issue: issue4 } = picked.line;
8505
+ const { issue: issue3 } = picked.line;
9128
8506
  return {
9129
8507
  id: "issue-blocked",
9130
8508
  action: "unblock",
9131
8509
  reading: blockedLineSentence(picked.blocked),
9132
- proposal: `re-read the blockers of #${issue4} and take \`${SPEC_BLOCKED_LABEL}\` off once they have all closed`,
9133
- issue: issue4
8510
+ proposal: `re-read the blockers of #${issue3} and take \`${SPEC_BLOCKED_LABEL}\` off once they have all closed`,
8511
+ issue: issue3
9134
8512
  };
9135
8513
  }
9136
8514
  async function readIssueNotReady(world) {
9137
8515
  const picked = await world.picked();
9138
8516
  if (picked === null)
9139
8517
  return null;
9140
- const { issue: issue4 } = picked.line;
8518
+ const { issue: issue3 } = picked.line;
9141
8519
  return {
9142
8520
  id: "issue-not-ready",
9143
8521
  action: "ready",
9144
- reading: notReadySentence(issue4),
9145
- proposal: `check the spec of #${issue4} and mark it ready`,
9146
- issue: issue4
8522
+ reading: notReadySentence(issue3),
8523
+ proposal: `check the spec of #${issue3} and mark it ready`,
8524
+ issue: issue3
9147
8525
  };
9148
8526
  }
9149
8527
  function readNothingHere(world) {
@@ -9543,26 +8921,26 @@ async function onTracker(tracker, doing, call) {
9543
8921
  var READY_USAGE = "rafa issue ready <n>";
9544
8922
  var READY_WRITE_EXIT = 1;
9545
8923
  var ISSUE_NUMBER = /^[1-9]\d*$/u;
9546
- function readyQuestion(issue4) {
9547
- return `Mark #${String(issue4)} ${SPEC_READY_LABEL}? [y/N] `;
8924
+ function readyQuestion(issue3) {
8925
+ return `Mark #${String(issue3)} ${SPEC_READY_LABEL}? [y/N] `;
9548
8926
  }
9549
- function trustPassLine(issue4, repo, reading2) {
8927
+ function trustPassLine(issue3, repo, reading2) {
9550
8928
  if (!reading2.trusted) {
9551
8929
  throw new TypeError(`issue ready: ${reading2.login} is not trusted, and has no pass to name`);
9552
8930
  }
9553
8931
  const because = reading2.source === "allow-list" ? "listed in board.trustedAuthors" : `who has write access to ${repo}`;
9554
- return `#${String(issue4)} was opened by ${reading2.login}, ${because}`;
8932
+ return `#${String(issue3)} was opened by ${reading2.login}, ${because}`;
9555
8933
  }
9556
- function completeLine(issue4) {
9557
- return `#${String(issue4)} fills every heading the spec template asks for, with no placeholder left`;
8934
+ function completeLine(issue3) {
8935
+ return `#${String(issue3)} fills every heading the spec template asks for, with no placeholder left`;
9558
8936
  }
9559
- function unaskedMessage2(issue4) {
9560
- const number = String(issue4);
8937
+ function unaskedMessage2(issue3) {
8938
+ const number = String(issue3);
9561
8939
  return `#${number} is ready to mark; there is no terminal to ask on, so ${SPEC_READY_LABEL} was not added.` + ` Run rafa issue ready ${number} where an answer can be typed`;
9562
8940
  }
9563
- function readyReport(issue4, status, reading2, lines) {
8941
+ function readyReport(issue3, status, reading2, lines) {
9564
8942
  return Object.freeze({
9565
- issue: issue4,
8943
+ issue: issue3,
9566
8944
  status,
9567
8945
  author: reading2.login,
9568
8946
  trustedBy: reading2.source,
@@ -9572,36 +8950,36 @@ function readyReport(issue4, status, reading2, lines) {
9572
8950
  });
9573
8951
  }
9574
8952
  async function runIssueReady(options) {
9575
- const { gh, issue: issue4, trust, ask } = options;
8953
+ const { gh, issue: issue3, trust, ask } = options;
9576
8954
  const board = options.board ?? createGhIssueBoard({ gh });
9577
8955
  const readIssue = options.readIssue ?? createGhSpecIssueReader({ gh });
9578
- const found = await readIssue(issue4);
9579
- const reading2 = await requireTrustedBoardAuthor({ kind: "issue", number: issue4 }, trust, found.author);
9580
- requireCompleteSpec(issueSource(issue4), found.body);
9581
- const lines = { trust: trustPassLine(issue4, trust.repo, reading2), checked: completeLine(issue4) };
8956
+ const found = await readIssue(issue3);
8957
+ const reading2 = await requireTrustedBoardAuthor({ kind: "issue", number: issue3 }, trust, found.author);
8958
+ requireCompleteSpec(issueSource(issue3), found.body);
8959
+ const lines = { trust: trustPassLine(issue3, trust.repo, reading2), checked: completeLine(issue3) };
9582
8960
  if (hasSpecReadyLabel(found.labels)) {
9583
- return readyReport(issue4, "already", reading2, {
8961
+ return readyReport(issue3, "already", reading2, {
9584
8962
  ...lines,
9585
- message: `#${String(issue4)} is already marked ${SPEC_READY_LABEL}`
8963
+ message: `#${String(issue3)} is already marked ${SPEC_READY_LABEL}`
9586
8964
  });
9587
8965
  }
9588
8966
  if (ask === null) {
9589
- return readyReport(issue4, "unasked", reading2, { ...lines, message: unaskedMessage2(issue4) });
8967
+ return readyReport(issue3, "unasked", reading2, { ...lines, message: unaskedMessage2(issue3) });
9590
8968
  }
9591
- if (!await ask(readyQuestion(issue4))) {
9592
- return readyReport(issue4, "declined", reading2, {
8969
+ if (!await ask(readyQuestion(issue3))) {
8970
+ return readyReport(issue3, "declined", reading2, {
9593
8971
  ...lines,
9594
- message: `#${String(issue4)} was left unmarked`
8972
+ message: `#${String(issue3)} was left unmarked`
9595
8973
  });
9596
8974
  }
9597
8975
  try {
9598
- await board.swapLabels(issue4, SPEC_NEEDS_WORK_LABEL, SPEC_READY_LABEL);
8976
+ await board.swapLabels(issue3, SPEC_NEEDS_WORK_LABEL, SPEC_READY_LABEL);
9599
8977
  } catch (error) {
9600
- throw new CommandExit(READY_WRITE_EXIT, `\u274C ${SPEC_READY_LABEL} could not be put on #${String(issue4)}: ${messageOf(error)}`);
8978
+ throw new CommandExit(READY_WRITE_EXIT, `\u274C ${SPEC_READY_LABEL} could not be put on #${String(issue3)}: ${messageOf(error)}`);
9601
8979
  }
9602
- return readyReport(issue4, "marked", reading2, {
8980
+ return readyReport(issue3, "marked", reading2, {
9603
8981
  ...lines,
9604
- message: `Marked #${String(issue4)} ${SPEC_READY_LABEL}, and took ${SPEC_NEEDS_WORK_LABEL} off it`
8982
+ message: `Marked #${String(issue3)} ${SPEC_READY_LABEL}, and took ${SPEC_NEEDS_WORK_LABEL} off it`
9605
8983
  });
9606
8984
  }
9607
8985
  function readReadyIssue(context) {
@@ -9633,7 +9011,7 @@ function lazyPrompter(open) {
9633
9011
  };
9634
9012
  }
9635
9013
  async function markIssueReady(context, seams) {
9636
- const issue4 = readReadyIssue(context);
9014
+ const issue3 = readReadyIssue(context);
9637
9015
  const project = issueProject(context);
9638
9016
  const config = issueSubjectConfig(project, (message) => {
9639
9017
  context.output.warn(message);
@@ -9652,7 +9030,7 @@ async function markIssueReady(context, seams) {
9652
9030
  try {
9653
9031
  return await runIssueReady({
9654
9032
  gh,
9655
- issue: issue4,
9033
+ issue: issue3,
9656
9034
  trust,
9657
9035
  ask: isTerminal() ? prompter.ask : null
9658
9036
  });
@@ -9720,12 +9098,12 @@ function versionLine() {
9720
9098
 
9721
9099
  // src/notices/notices.ts
9722
9100
  import { mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync6 } from "fs";
9723
- import { dirname as dirname8, join as join13 } from "path";
9101
+ import { dirname as dirname8, join as join12 } from "path";
9724
9102
  var NOTICE_IDS = Object.freeze(["alpha", "danger"]);
9725
9103
  var FEEDBACK_URL = "https://github.com/open-tomato/rafa/issues/new/choose";
9726
9104
  var ROADMAP_URL = "https://github.com/open-tomato/rafa/issues/31";
9727
9105
  function noticesPath(home) {
9728
- return join13(home, ".rafa", "notices.json");
9106
+ return join12(home, ".rafa", "notices.json");
9729
9107
  }
9730
9108
  function isNoticeId(value) {
9731
9109
  return typeof value === "string" && NOTICE_IDS.includes(value);
@@ -9934,7 +9312,7 @@ function markBudgetExit(options) {
9934
9312
  }
9935
9313
 
9936
9314
  // src/utils/commit.ts
9937
- import { spawnSync as spawnSync4 } from "child_process";
9315
+ import { spawnSync as spawnSync3 } from "child_process";
9938
9316
  var DEFAULT_COMMIT_TYPE = "chore";
9939
9317
  var TEST_NOUN_WINDOW = 40;
9940
9318
  var FIX_VERBS = ["fix", "repair", "correct", "resolve"];
@@ -10146,7 +9524,7 @@ function failed2(step, subject, result) {
10146
9524
  }
10147
9525
  function spawnGit(cwd) {
10148
9526
  return (args) => {
10149
- const result = spawnSync4("git", args, {
9527
+ const result = spawnSync3("git", args, {
10150
9528
  cwd,
10151
9529
  encoding: "utf8",
10152
9530
  maxBuffer: MAX_GIT_BUFFER
@@ -10217,10 +9595,10 @@ function holdsOf(reading2) {
10217
9595
  for (const blocker of report2.blockers) {
10218
9596
  holds.push(`blocker: ${blocker.what ?? "an entry with no usable what"}`);
10219
9597
  }
10220
- for (const issue4 of issues) {
10221
- if (!DROPPED_BLOCKER_FIELD.test(issue4.field))
9598
+ for (const issue3 of issues) {
9599
+ if (!DROPPED_BLOCKER_FIELD.test(issue3.field))
10222
9600
  continue;
10223
- holds.push(`blocker: ${issue4.field}, an entry the report parser dropped`);
9601
+ holds.push(`blocker: ${issue3.field}, an entry the report parser dropped`);
10224
9602
  }
10225
9603
  return holds;
10226
9604
  }
@@ -10293,6 +9671,411 @@ function finishCleanExit(options) {
10293
9671
  // src/start/dispatch.ts
10294
9672
  import { randomUUID as randomUUID7 } from "crypto";
10295
9673
 
9674
+ // src/report/lessons.ts
9675
+ import { createHash as createHash2 } from "crypto";
9676
+
9677
+ // src/demote/classify.ts
9678
+ var LEARNED_DIRECTORY = "learned";
9679
+ var ORIGIN_FIELD = "origin";
9680
+ var DEMOTION_VERDICTS = [
9681
+ "observation",
9682
+ "procedure",
9683
+ "unclassified"
9684
+ ];
9685
+ var RULE_VERDICTS = Object.freeze({
9686
+ "no-frontmatter": "unclassified",
9687
+ "learned-without-origin": "unclassified",
9688
+ "no-section-pair": "unclassified",
9689
+ "repeated-section": "procedure",
9690
+ "numbered-procedure": "procedure",
9691
+ "referenced-script": "procedure",
9692
+ "observation-shape": "observation"
9693
+ });
9694
+ var PROCEDURE_STEP_MINIMUM = 3;
9695
+ var PROBLEM_HEADING = /^(?:the\s+)?problem\b/i;
9696
+ var SOLUTION_HEADING = /^(?:the\s+)?solution\b/i;
9697
+ var WHEN_TO_USE_HEADING = /^when\s+to\s+use\b/i;
9698
+ var EXTRACTED_LINE = /^\s*\*\*Extracted:\*\*\s*(\S+)/m;
9699
+ var SCRIPT_EXTENSIONS = [
9700
+ ".sh",
9701
+ ".bash",
9702
+ ".zsh",
9703
+ ".py",
9704
+ ".rb",
9705
+ ".pl",
9706
+ ".js",
9707
+ ".mjs",
9708
+ ".cjs",
9709
+ ".ts"
9710
+ ];
9711
+ var FENCE_LINE4 = /^ {0,3}(?:```|~~~)/;
9712
+ var HEADING_LINE2 = /^ {0,3}(#{1,6})\s+(.*)$/;
9713
+ var NUMBERED_ITEM = /^ {0,3}\d+[.)]\s+\S/;
9714
+ var CONTINUATION = /^ {2,}\S/;
9715
+ var DEMOTED_SOURCE = "demoted";
9716
+ var DEMOTED_CONFIDENCE = 0.5;
9717
+ var DEMOTED_KIND = "gotcha";
9718
+ var DEMOTED_SIGNAL = "silent";
9719
+ var DEMOTED_USAGE_COUNT = 1;
9720
+ var EVIDENCE_PATH_KEY = "path";
9721
+ var EVIDENCE_EXTRACTED_KEY = "extracted";
9722
+ var DEFAULT_DOMAIN = "general-best-practices";
9723
+ var DOMAIN_MARKERS = [
9724
+ ["security", /\b(?:security|secret|credential|token|auth|csrf|xss|injection)\b/i],
9725
+ ["testing", /\b(?:test|tests|testing|spec|assertion|assert|mock|stub|fixture|coverage)\b/i],
9726
+ ["git", /\b(?:git|commit|rebase|worktree|branch|merge|checkout)\b/i],
9727
+ ["debugging", /\b(?:debug|debugging|traceback|reproduce|bisect)\b/i],
9728
+ ["code-style", /\b(?:lint|eslint|format|formatter|prettier|naming)\b/i],
9729
+ ["workflow", /\b(?:workflow|pipeline|release|deploy|build|ci|loop|dispatch)\b/i]
9730
+ ];
9731
+ var ERROR_WORDS = /\b(?:error|exception|traceback|panic|fatal|refused|denied|not found|no such|cannot|could not|failed|failure|unexpected|undefined)\b/i;
9732
+ var SENTENCE_ABBREVIATIONS = ["e.g", "i.e", "vs", "cf", "etc"];
9733
+ function isFilled2(value) {
9734
+ return typeof value === "string" && value.trim() !== "";
9735
+ }
9736
+ function bodyLines(body) {
9737
+ return body.split(`
9738
+ `).map((line) => line.replace(/\r$/, ""));
9739
+ }
9740
+ function bodySections(body) {
9741
+ const lines = bodyLines(body);
9742
+ const headings = [];
9743
+ let fenced = false;
9744
+ lines.forEach((line, index) => {
9745
+ if (FENCE_LINE4.test(line)) {
9746
+ fenced = !fenced;
9747
+ return;
9748
+ }
9749
+ if (fenced)
9750
+ return;
9751
+ const match = HEADING_LINE2.exec(line);
9752
+ if (match === null)
9753
+ return;
9754
+ const [, hashes = "", title = ""] = match;
9755
+ headings.push({
9756
+ level: hashes.length,
9757
+ title: title.replace(/\s+#+\s*$/, "").trim(),
9758
+ index
9759
+ });
9760
+ });
9761
+ return headings.map((heading, position) => {
9762
+ const next = headings.slice(position + 1).find((other) => other.level <= heading.level);
9763
+ const end = next === undefined ? lines.length : next.index;
9764
+ return {
9765
+ level: heading.level,
9766
+ title: heading.title,
9767
+ line: heading.index + 1,
9768
+ text: lines.slice(heading.index + 1, end).join(`
9769
+ `).trim()
9770
+ };
9771
+ });
9772
+ }
9773
+ function numberedRuns(body) {
9774
+ const runs = [];
9775
+ let fenced = false;
9776
+ let steps = 0;
9777
+ let line = 0;
9778
+ const close = () => {
9779
+ if (steps > 0)
9780
+ runs.push({ steps, line });
9781
+ steps = 0;
9782
+ };
9783
+ bodyLines(body).forEach((text, index) => {
9784
+ if (FENCE_LINE4.test(text)) {
9785
+ fenced = !fenced;
9786
+ return;
9787
+ }
9788
+ if (fenced || text.trim() === "")
9789
+ return;
9790
+ if (NUMBERED_ITEM.test(text)) {
9791
+ if (steps === 0)
9792
+ line = index + 1;
9793
+ steps += 1;
9794
+ return;
9795
+ }
9796
+ if (CONTINUATION.test(text))
9797
+ return;
9798
+ close();
9799
+ });
9800
+ close();
9801
+ return runs;
9802
+ }
9803
+ function isScriptPath(token) {
9804
+ if (token.startsWith("/"))
9805
+ return false;
9806
+ const last = token.split("/").at(-1) ?? "";
9807
+ return SCRIPT_EXTENSIONS.some((extension) => last.endsWith(extension));
9808
+ }
9809
+ function referencedScript(body, entries = []) {
9810
+ const owned = new Set(entries.map((entry) => entry.replace(/^\.\//, "")));
9811
+ for (const reference2 of collectReferences(body)) {
9812
+ if (reference2.kind !== "path")
9813
+ continue;
9814
+ const token = reference2.text.replace(/^\.\//, "");
9815
+ const [first = ""] = token.split("/");
9816
+ if (owned.has(first) && isScriptPath(token))
9817
+ return reference2;
9818
+ }
9819
+ return null;
9820
+ }
9821
+ function isLearnedFile(path2) {
9822
+ const [first] = path2.split(/[\\/]/);
9823
+ return first === LEARNED_DIRECTORY;
9824
+ }
9825
+ function decide(rule, reason) {
9826
+ return { verdict: RULE_VERDICTS[rule], rule, reason };
9827
+ }
9828
+ function pairReason(problems, solutions) {
9829
+ if (problems === 0 && solutions === 0) {
9830
+ return "the body carries no Problem section and no Solution section";
9831
+ }
9832
+ return problems === 0 ? "the body carries a Solution section and no Problem section, so there is no pair" : "the body carries a Problem section and no Solution section, so there is no pair";
9833
+ }
9834
+ function repeatReason(problems, solutions) {
9835
+ const counted2 = problems > 1 ? `${problems} Problem sections` : `${solutions} Solution sections`;
9836
+ return `the body carries ${counted2}, and an observation carries exactly one of each`;
9837
+ }
9838
+ function classifySkill(source) {
9839
+ const document = readFrontmatterDocument(source.text);
9840
+ if (document === null) {
9841
+ return decide("no-frontmatter", "the file opens with no --- block holding a YAML mapping");
9842
+ }
9843
+ if (isLearnedFile(source.path) && !isFilled2(document.data[ORIGIN_FIELD])) {
9844
+ return decide("learned-without-origin", `a ${LEARNED_DIRECTORY}/ file whose frontmatter carries no ${ORIGIN_FIELD}`);
9845
+ }
9846
+ const sections2 = bodySections(document.body);
9847
+ const problems = sections2.filter((section) => PROBLEM_HEADING.test(section.title));
9848
+ const solutions = sections2.filter((section) => SOLUTION_HEADING.test(section.title));
9849
+ if (problems.length === 0 || solutions.length === 0) {
9850
+ return decide("no-section-pair", pairReason(problems.length, solutions.length));
9851
+ }
9852
+ if (problems.length > 1 || solutions.length > 1) {
9853
+ return decide("repeated-section", repeatReason(problems.length, solutions.length));
9854
+ }
9855
+ const run = numberedRuns(document.body).find((each) => each.steps >= PROCEDURE_STEP_MINIMUM);
9856
+ if (run !== undefined) {
9857
+ return decide("numbered-procedure", `a numbered procedure of ${run.steps} consecutive steps at line ${run.line}`);
9858
+ }
9859
+ const script = referencedScript(document.body, source.entries ?? []);
9860
+ if (script !== null) {
9861
+ return decide("referenced-script", `the body names the script ${script.text} at line ${script.line}`);
9862
+ }
9863
+ return decide("observation-shape", "one Problem section and one Solution section, no numbered procedure of " + `${PROCEDURE_STEP_MINIMUM} or more consecutive steps, and no referenced script`);
9864
+ }
9865
+ function firstSection(sections2, heading) {
9866
+ return sections2.find((section) => heading.test(section.title));
9867
+ }
9868
+ function withoutFences(text) {
9869
+ const kept = [];
9870
+ let fenced = false;
9871
+ for (const line of bodyLines(text)) {
9872
+ if (FENCE_LINE4.test(line)) {
9873
+ fenced = !fenced;
9874
+ continue;
9875
+ }
9876
+ if (!fenced)
9877
+ kept.push(line);
9878
+ }
9879
+ return kept;
9880
+ }
9881
+ function stripMarkers(line) {
9882
+ return line.replace(/^\s*(?:[-*+]|\d+[.)]|>)\s+/, "").replace(/\*\*|__|\*|`/g, "").trim();
9883
+ }
9884
+ function isAbbreviation(text, index) {
9885
+ const before = text.slice(0, index);
9886
+ return SENTENCE_ABBREVIATIONS.some((word) => before.toLowerCase().endsWith(word));
9887
+ }
9888
+ function firstSentence(text) {
9889
+ const lines = withoutFences(text);
9890
+ const start = lines.findIndex((line) => line.trim() !== "");
9891
+ if (start === -1)
9892
+ return "";
9893
+ const collected = [lines[start] ?? ""];
9894
+ for (const line of lines.slice(start + 1)) {
9895
+ if (line.trim() === "" || /^\s*(?:[-*+]|\d+[.)]|#)/.test(line))
9896
+ break;
9897
+ collected.push(line);
9898
+ }
9899
+ const joined = stripMarkers(collected.join(" ")).replace(/\s+/g, " ");
9900
+ for (let index = 0;index < joined.length; index += 1) {
9901
+ if (!".!?".includes(joined[index] ?? ""))
9902
+ continue;
9903
+ if (isAbbreviation(joined, index))
9904
+ continue;
9905
+ const next = joined[index + 1];
9906
+ if (next === undefined)
9907
+ return joined.slice(0, index + 1).trim();
9908
+ if (next === " ")
9909
+ return joined.slice(0, index + 1).trim();
9910
+ }
9911
+ return joined.trim();
9912
+ }
9913
+ function demotedTrigger(sections2, data) {
9914
+ const whenToUse = firstSection(sections2, WHEN_TO_USE_HEADING);
9915
+ const fromSection = whenToUse === undefined ? "" : firstSentence(whenToUse.text);
9916
+ if (fromSection !== "")
9917
+ return fromSection;
9918
+ const description = data["description"];
9919
+ if (isFilled2(description))
9920
+ return description.trim();
9921
+ const problem = firstSection(sections2, PROBLEM_HEADING);
9922
+ return problem === undefined ? "" : firstSentence(problem.text);
9923
+ }
9924
+ function extractedDate(body) {
9925
+ const match = EXTRACTED_LINE.exec(body);
9926
+ return match?.[1] ?? null;
9927
+ }
9928
+ function errorString(body) {
9929
+ let fenced = false;
9930
+ for (const line of bodyLines(body)) {
9931
+ if (FENCE_LINE4.test(line)) {
9932
+ fenced = !fenced;
9933
+ continue;
9934
+ }
9935
+ if (fenced) {
9936
+ if (ERROR_WORDS.test(line))
9937
+ return line.trim();
9938
+ continue;
9939
+ }
9940
+ for (const span of line.matchAll(/`([^`\n]+)`/g)) {
9941
+ const inner = span[1] ?? "";
9942
+ if (ERROR_WORDS.test(inner))
9943
+ return inner.trim();
9944
+ }
9945
+ }
9946
+ return null;
9947
+ }
9948
+ function inferDomain(name2, body) {
9949
+ for (const [domain, pattern] of DOMAIN_MARKERS) {
9950
+ if (pattern.test(name2))
9951
+ return domain;
9952
+ }
9953
+ for (const [domain, pattern] of DOMAIN_MARKERS) {
9954
+ if (pattern.test(body))
9955
+ return domain;
9956
+ }
9957
+ return DEFAULT_DOMAIN;
9958
+ }
9959
+ function slugifyId(value) {
9960
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
9961
+ }
9962
+ function uniqueInstinctId(base, taken) {
9963
+ const used = new Set(taken);
9964
+ if (!used.has(base))
9965
+ return base;
9966
+ let suffix = 2;
9967
+ while (used.has(`${base}-${suffix}`))
9968
+ suffix += 1;
9969
+ return `${base}-${suffix}`;
9970
+ }
9971
+ function sourceName(source, data) {
9972
+ const declared = data["name"];
9973
+ if (isFilled2(declared))
9974
+ return slugifyId(declared);
9975
+ const segments = source.path.split(/[\\/]/).filter((segment) => segment !== "");
9976
+ const last = segments.at(-1) ?? "";
9977
+ const stem = last.toUpperCase() === "SKILL.MD" ? segments.at(-2) ?? "" : last.replace(/\.md$/i, "");
9978
+ return slugifyId(stem);
9979
+ }
9980
+ function convertToInstinct(source, options) {
9981
+ const document = readFrontmatterDocument(source.text);
9982
+ const data = document?.data ?? {};
9983
+ const body = document?.body ?? source.text;
9984
+ const sections2 = bodySections(body);
9985
+ const action = firstSection(sections2, SOLUTION_HEADING)?.text ?? "";
9986
+ const named = errorString(body);
9987
+ const name2 = sourceName(source, data);
9988
+ const draft = {
9989
+ id: uniqueInstinctId(name2, options.takenIds ?? []),
9990
+ trigger: demotedTrigger(sections2, data),
9991
+ kind: DEMOTED_KIND,
9992
+ domain: inferDomain(name2, body),
9993
+ confidence: DEMOTED_CONFIDENCE,
9994
+ usageCount: DEMOTED_USAGE_COUNT,
9995
+ sources: [],
9996
+ artifact: null,
9997
+ signal: named === null ? DEMOTED_SIGNAL : "loud",
9998
+ scope: options.scope,
9999
+ projectId: options.projectId ?? null,
10000
+ source: DEMOTED_SOURCE,
10001
+ evidence: [{
10002
+ [EVIDENCE_PATH_KEY]: source.path,
10003
+ [EVIDENCE_EXTRACTED_KEY]: extractedDate(body) ?? options.mtime
10004
+ }],
10005
+ promotedTo: null,
10006
+ createdAt: options.now,
10007
+ updatedAt: options.now,
10008
+ action,
10009
+ cause: firstSection(sections2, PROBLEM_HEADING)?.text ?? "",
10010
+ actionHash: actionHash(action)
10011
+ };
10012
+ const text = writeInstinct(draft);
10013
+ const parsed = parseInstinct(text);
10014
+ return parsed.instinct === null ? { instinct: null, text: null, issues: parsed.issues } : { instinct: parsed.instinct, text, issues: parsed.issues };
10015
+ }
10016
+
10017
+ // src/report/lessons.ts
10018
+ var LESSON_CONFIDENCE = 0.5;
10019
+ var BLOCKED_LESSON_CONFIDENCE = 0.4;
10020
+ var DEFAULT_LESSON_DOMAIN = "workflow";
10021
+ var LESSON_SLUG_LENGTH = 48;
10022
+ var LESSON_HASH_LENGTH = 8;
10023
+ var LESSON_ID_FALLBACK = "lesson";
10024
+ function lessonId(trigger, action) {
10025
+ const slug = slugifyId(trigger).slice(0, LESSON_SLUG_LENGTH).replace(/-+$/, "");
10026
+ const digest = createHash2("sha256").update(`${triggerKey(trigger)}
10027
+ ${actionHash(action)}`).digest("hex").slice(0, LESSON_HASH_LENGTH);
10028
+ return `${slug === "" ? LESSON_ID_FALLBACK : slug}-${digest}`;
10029
+ }
10030
+ function lessonEvidence(context) {
10031
+ const { dispatch, outcome } = context;
10032
+ return {
10033
+ ...dispatch.planStub === null ? {} : { plan: dispatch.planStub },
10034
+ task: dispatch.taskLine,
10035
+ session: dispatch.sessionId,
10036
+ outcome
10037
+ };
10038
+ }
10039
+ function toLesson(finding, context, at) {
10040
+ const { trigger, kind, signal, resolution, artifact } = finding;
10041
+ const cause = finding.cause ?? finding.what;
10042
+ if (resolution === null || trigger === null || kind === null || signal === null)
10043
+ return null;
10044
+ if (cause === null)
10045
+ return null;
10046
+ const { sessionId } = context.dispatch;
10047
+ return {
10048
+ id: lessonId(trigger, resolution),
10049
+ trigger,
10050
+ action: resolution,
10051
+ action_hash: actionHash(resolution),
10052
+ confidence: context.outcome === "blocked" ? BLOCKED_LESSON_CONFIDENCE : LESSON_CONFIDENCE,
10053
+ usage_count: 1,
10054
+ sources: [sessionId],
10055
+ ...artifact === null ? {} : { artifact },
10056
+ signal,
10057
+ status: "active",
10058
+ created_at: at,
10059
+ updated_at: at,
10060
+ description: {
10061
+ kind,
10062
+ domain: finding.domain ?? DEFAULT_LESSON_DOMAIN,
10063
+ scope: "project",
10064
+ source: "task-report",
10065
+ evidence: [lessonEvidence(context)],
10066
+ cause,
10067
+ projectId: null
10068
+ }
10069
+ };
10070
+ }
10071
+ function reportLessons(record, context, now) {
10072
+ if (!record.present)
10073
+ return null;
10074
+ const at = now.toISOString();
10075
+ const instincts = record.reading.report.findings.map((finding) => toLesson(finding, context, at)).filter((lesson) => lesson !== null);
10076
+ return instincts.length === 0 ? null : { source_id: context.dispatch.sessionId, instincts };
10077
+ }
10078
+
10296
10079
  // src/effort/store/absences.ts
10297
10080
  import { randomUUID as randomUUID3 } from "crypto";
10298
10081
  import { existsSync as existsSync13 } from "fs";
@@ -10673,7 +10456,7 @@ function describeTaskReportRecord(record) {
10673
10456
 
10674
10457
  // src/utils/progress.ts
10675
10458
  import { existsSync as existsSync15, writeFileSync as writeFileSync7 } from "fs";
10676
- import { join as join14 } from "path";
10459
+ import { join as join13 } from "path";
10677
10460
  var PROGRESS_FILE_NAME = "progress.txt";
10678
10461
  var PROGRESS_CAP_BYTES = 16000;
10679
10462
  var LISTED_FIELDS = [
@@ -10715,7 +10498,7 @@ var ESCAPED_CODE_POINTS = new Set([
10715
10498
  8233
10716
10499
  ]);
10717
10500
  function progressFilePath(repoRoot) {
10718
- return join14(repoRoot, PROGRESS_FILE_NAME);
10501
+ return join13(repoRoot, PROGRESS_FILE_NAME);
10719
10502
  }
10720
10503
  function escapeValue(value) {
10721
10504
  let escaped = "";
@@ -11073,8 +10856,8 @@ async function dispatchTask(options) {
11073
10856
  const note = suppressed.length === 0 ? "" : ` (${suppressed.join(", ")} left to the agent)`;
11074
10857
  activeOutput().info(` Routed as: ${flags.join(" ")}${note}`);
11075
10858
  }
11076
- for (const issue4 of declaration?.issues ?? []) {
11077
- activeOutput().warn(` Declaration: ignoring ${issue4.reason} \`${issue4.text}\`.`);
10859
+ for (const issue3 of declaration?.issues ?? []) {
10860
+ activeOutput().warn(` Declaration: ignoring ${issue3.reason} \`${issue3.text}\`.`);
11078
10861
  }
11079
10862
  const injection = renderInjection({
11080
10863
  mode: options.inject,
@@ -11126,8 +10909,37 @@ function renderProgressForDispatch(repoRoot, planStub2) {
11126
10909
  return false;
11127
10910
  }
11128
10911
  }
11129
- function storeTaskReport(options) {
10912
+ async function pushTaskLessons(options, record) {
10913
+ const { learning, dispatch } = options;
10914
+ if (learning === null)
10915
+ return;
10916
+ const now = options.now ?? (() => new Date);
10917
+ const context = {
10918
+ dispatch: { sessionId: dispatch.sessionId, planStub: options.planStub, taskLine: dispatch.taskText },
10919
+ outcome: options.outcome
10920
+ };
10921
+ const payload = reportLessons(record, context, now());
10922
+ if (payload === null)
10923
+ return;
10924
+ const count = payload.instincts.length;
10925
+ try {
10926
+ const registry = learning.registry ?? CORE_ADAPTER_REGISTRY;
10927
+ const adapter = registry.resolve("learning", learning.kind).create({
10928
+ repoRoot: options.repoRoot,
10929
+ home: learning.home,
10930
+ learningBlessMinConfidence: learning.blessMinConfidence
10931
+ });
10932
+ const result = await adapter.push(payload);
10933
+ const rules = result.decisions.map((decision) => decision.rule).join(", ");
10934
+ activeOutput().info(` Pushed ${count} lesson(s) to the \`${learning.kind}\` learning adapter: ${rules}.`);
10935
+ } catch (error) {
10936
+ activeOutput().warn(` ${count} lesson(s) of session ${dispatch.sessionId} were not pushed to the \`${learning.kind}\` learning adapter: ${messageOf3(error)}`);
10937
+ activeOutput().warn(" The task is not failed for it: its report is stored, findings and all.");
10938
+ }
10939
+ }
10940
+ async function storeTaskReport(options) {
11130
10941
  const { dispatch } = options;
10942
+ let record;
11131
10943
  try {
11132
10944
  writeDispatch(options.repoRoot, {
11133
10945
  sessionId: dispatch.sessionId,
@@ -11136,7 +10948,7 @@ function storeTaskReport(options) {
11136
10948
  declaration: dispatch.declaration,
11137
10949
  flags: dispatch.flags
11138
10950
  });
11139
- const record = recordTaskReport(options.repoRoot, {
10951
+ record = recordTaskReport(options.repoRoot, {
11140
10952
  dispatch: {
11141
10953
  sessionId: dispatch.sessionId,
11142
10954
  planStub: options.planStub,
@@ -11150,13 +10962,14 @@ function storeTaskReport(options) {
11150
10962
  activeOutput().info(` ${note}`);
11151
10963
  for (const warning of warnings)
11152
10964
  activeOutput().warn(` ${warning}`);
11153
- return true;
11154
10965
  } catch (error) {
11155
10966
  activeOutput().error(`
11156
10967
  \u274C The report of session ${dispatch.sessionId} was not stored: ${messageOf3(error)}`);
11157
10968
  activeOutput().error(" The session printed it above as it ran.");
11158
10969
  return false;
11159
10970
  }
10971
+ await pushTaskLessons(options, record);
10972
+ return true;
11160
10973
  }
11161
10974
 
11162
10975
  // src/start/pause.ts
@@ -11583,11 +11396,11 @@ function changelogDate(now) {
11583
11396
  }
11584
11397
 
11585
11398
  // src/release/enabled.ts
11586
- import { statSync as statSync10 } from "fs";
11399
+ import { statSync as statSync9 } from "fs";
11587
11400
  import { resolve as resolve10 } from "path";
11588
11401
  function isRegularFile(full) {
11589
11402
  try {
11590
- return statSync10(full).isFile();
11403
+ return statSync9(full).isFile();
11591
11404
  } catch {
11592
11405
  return false;
11593
11406
  }
@@ -11758,12 +11571,12 @@ function readBaseVersion(git, versionFile, options = {}) {
11758
11571
  if (!fetched.ok) {
11759
11572
  problems.push(`${branch} could not be fetched from ${remote}, so the base version is whatever this clone already holds for ${ref}: ${gitSaid(fetched)}`);
11760
11573
  }
11761
- const shown2 = git(["show", `${ref}:${path2}`]);
11762
- if (!shown2.ok) {
11763
- problems.push(`${ref}:${path2} could not be read: ${gitSaid(shown2)}`);
11574
+ const shown = git(["show", `${ref}:${path2}`]);
11575
+ if (!shown.ok) {
11576
+ problems.push(`${ref}:${path2} could not be read: ${gitSaid(shown)}`);
11764
11577
  return { ref, path: path2, version: null, fetched: fetched.ok, problems };
11765
11578
  }
11766
- const version2 = readManifestVersion(shown2.stdout);
11579
+ const version2 = readManifestVersion(shown.stdout);
11767
11580
  if (version2 === null)
11768
11581
  problems.push(`${ref}:${path2} declares no version`);
11769
11582
  return { ref, path: path2, version: version2, fetched: fetched.ok, problems };
@@ -12294,7 +12107,7 @@ import { readFileSync as readFileSync21 } from "fs";
12294
12107
 
12295
12108
  // src/plan/risk.ts
12296
12109
  import { readFileSync as readFileSync20 } from "fs";
12297
- import { isAbsolute as isAbsolute4, join as join15, relative as relative5 } from "path";
12110
+ import { isAbsolute as isAbsolute4, join as join14, relative as relative4 } from "path";
12298
12111
 
12299
12112
  // src/plan/risk/accounts.ts
12300
12113
  var PUSH_REMOTE = "origin";
@@ -12836,9 +12649,9 @@ function toolsFinding(task, plan, seams) {
12836
12649
  return null;
12837
12650
  }
12838
12651
  function displayPath(path2, seams) {
12839
- const absolute = isAbsolute4(path2) ? path2 : join15(seams.repoRoot, path2);
12652
+ const absolute = isAbsolute4(path2) ? path2 : join14(seams.repoRoot, path2);
12840
12653
  const within = (root) => {
12841
- const step = relative5(root, absolute);
12654
+ const step = relative4(root, absolute);
12842
12655
  return step === "" || step.startsWith("..") || isAbsolute4(step) ? null : step;
12843
12656
  };
12844
12657
  const inRepo = within(seams.repoRoot);
@@ -12861,7 +12674,7 @@ function readText5(path2) {
12861
12674
  function findSkillFile(name2, seams) {
12862
12675
  const tiers = resolveSkillTiers({ home: seams.home, projectRoot: seams.repoRoot, entry: seams.entry });
12863
12676
  for (const { dir } of tiers) {
12864
- const path2 = join15(dir, name2, "SKILL.md");
12677
+ const path2 = join14(dir, name2, "SKILL.md");
12865
12678
  if (readText5(path2) !== null)
12866
12679
  return path2;
12867
12680
  }
@@ -12887,8 +12700,8 @@ function commandFindings(plan, display, tasks, seams) {
12887
12700
  if (read.has(file))
12888
12701
  continue;
12889
12702
  read.add(file);
12890
- const shown2 = displayPath(file, seams);
12891
- found.push(...readBodyCommands(readText5(file) ?? "", paths).map((finding2) => located(finding2, shown2)));
12703
+ const shown = displayPath(file, seams);
12704
+ found.push(...readBodyCommands(readText5(file) ?? "", paths).map((finding2) => located(finding2, shown)));
12892
12705
  }
12893
12706
  }
12894
12707
  return found;
@@ -12981,10 +12794,10 @@ async function announceRiskTotal(input, seams = {}) {
12981
12794
 
12982
12795
  // src/start/session.ts
12983
12796
  import { randomUUID as randomUUID8 } from "crypto";
12984
- import { relative as relative6 } from "path";
12797
+ import { relative as relative5 } from "path";
12985
12798
  var NOTHING_DISPATCHED = " Nothing was checked and nothing was dispatched.";
12986
12799
  function recordLabel(repoRoot, sessionId) {
12987
- return relative6(repoRoot, sessionFilePath(repoRoot, sessionId));
12800
+ return relative5(repoRoot, sessionFilePath(repoRoot, sessionId));
12988
12801
  }
12989
12802
  function conflictLine(repoRoot, conflict) {
12990
12803
  const { record } = conflict;
@@ -13014,7 +12827,7 @@ function openRefusal(repoRoot, draft, error) {
13014
12827
  if (!(error instanceof SessionRecordError) && !isSystemError)
13015
12828
  return null;
13016
12829
  return [
13017
- `\u274C Refusing to start: the session records under ${relative6(repoRoot, runsDir(repoRoot))}/ cannot be read or written.`,
12830
+ `\u274C Refusing to start: the session records under ${relative5(repoRoot, runsDir(repoRoot))}/ cannot be read or written.`,
13018
12831
  ` ${messageOf(error)}`,
13019
12832
  NOTHING_DISPATCHED
13020
12833
  ].join(`
@@ -13050,7 +12863,7 @@ function openRunSession(options) {
13050
12863
  const draft = {
13051
12864
  sessionId: (seams.newSessionId ?? randomUUID8)(),
13052
12865
  planStub: options.planStub,
13053
- plan: relative6(repoRoot, options.planPath),
12866
+ plan: relative5(repoRoot, options.planPath),
13054
12867
  branch: options.branch,
13055
12868
  pid: seams.pid ?? process.pid,
13056
12869
  startedAt: (seams.now ?? (() => new Date))().toISOString()
@@ -13121,17 +12934,17 @@ function announcePlanIssues(planContent) {
13121
12934
  const output = activeOutput();
13122
12935
  output.warn(`
13123
12936
  \u26A0\uFE0F The plan holds ${issues.length} part(s) the loop does not read as written:`);
13124
- for (const issue4 of issues)
13125
- output.warn(` line ${issue4.line}: ${issue4.text}`);
12937
+ for (const issue3 of issues)
12938
+ output.warn(` line ${issue3.line}: ${issue3.text}`);
13126
12939
  return issues;
13127
12940
  }
13128
12941
 
13129
12942
  // src/start/runtime.ts
13130
- import { existsSync as existsSync18, readdirSync as readdirSync7, realpathSync as realpathSync3, statSync as statSync11 } from "fs";
12943
+ import { existsSync as existsSync18, readdirSync as readdirSync6, realpathSync as realpathSync3, statSync as statSync10 } from "fs";
13131
12944
  import { homedir as homedir5 } from "os";
13132
- import { isAbsolute as isAbsolute5, join as join16, relative as relative7, resolve as resolve11, sep } from "path";
12945
+ import { isAbsolute as isAbsolute5, join as join15, relative as relative6, resolve as resolve11, sep } from "path";
13133
12946
  var RUNTIME_FLAG = "--runtime";
13134
- var RUNTIME_SUBDIR = join16(".rafa", "runtime");
12947
+ var RUNTIME_SUBDIR = join15(".rafa", "runtime");
13135
12948
  var RUNTIME_ENTRY = "cli.js";
13136
12949
  var SOURCE_DIR = "src";
13137
12950
  var RUNTIME_COMMAND = ["start"];
@@ -13158,7 +12971,7 @@ function resolveRuntime(value, places) {
13158
12971
  throw refusal3([`\u274C Refusing to start: \`--runtime\` names no runtime. Spell it ${SPELLING}.`]);
13159
12972
  }
13160
12973
  const isVersion = VERSION_SHAPE2.test(value);
13161
- const named = isVersion ? join16(places.home, RUNTIME_SUBDIR, value, RUNTIME_ENTRY) : resolve11(places.cwd, value);
12974
+ const named = isVersion ? join15(places.home, RUNTIME_SUBDIR, value, RUNTIME_ENTRY) : resolve11(places.cwd, value);
13162
12975
  refuseInsideSource(value, named, places);
13163
12976
  const entry = entryOf(value, named, isVersion, places.home);
13164
12977
  const real = realpathSync3(entry);
@@ -13169,7 +12982,7 @@ function entryOf(value, named, isVersion, home) {
13169
12982
  if (isVersion) {
13170
12983
  if (isFile2(named))
13171
12984
  return named;
13172
- const root = join16(home, RUNTIME_SUBDIR);
12985
+ const root = join15(home, RUNTIME_SUBDIR);
13173
12986
  const installed = installedVersions(root);
13174
12987
  throw refusal3([
13175
12988
  `\u274C Refusing to start: \`--runtime=${value}\` names no installed runtime: ${named} is no file.`,
@@ -13178,7 +12991,7 @@ function entryOf(value, named, isVersion, home) {
13178
12991
  }
13179
12992
  if (isFile2(named))
13180
12993
  return named;
13181
- const inside = join16(named, RUNTIME_ENTRY);
12994
+ const inside = join15(named, RUNTIME_ENTRY);
13182
12995
  if (isDirectory(named) && isFile2(inside))
13183
12996
  return inside;
13184
12997
  throw refusal3([
@@ -13196,31 +13009,31 @@ function refuseInsideSource(value, path2, places) {
13196
13009
  ]);
13197
13010
  }
13198
13011
  function sourceDirs({ cwd, root }) {
13199
- const dirs = [join16(cwd, SOURCE_DIR), join16(root, SOURCE_DIR)];
13012
+ const dirs = [join15(cwd, SOURCE_DIR), join15(root, SOURCE_DIR)];
13200
13013
  const real = dirs.filter((dir) => existsSync18(dir)).map((dir) => realpathSync3(dir));
13201
13014
  return [...new Set([...dirs, ...real])];
13202
13015
  }
13203
13016
  function isInside2(path2, dir) {
13204
- const rel = relative7(dir, path2);
13017
+ const rel = relative6(dir, path2);
13205
13018
  return rel === "" || !isAbsolute5(rel) && rel !== ".." && !rel.startsWith(`..${sep}`);
13206
13019
  }
13207
13020
  function installedVersions(root) {
13208
13021
  try {
13209
- return readdirSync7(root).filter((name2) => isFile2(join16(root, name2, RUNTIME_ENTRY))).sort((a, b) => a.localeCompare(b));
13022
+ return readdirSync6(root).filter((name2) => isFile2(join15(root, name2, RUNTIME_ENTRY))).sort((a, b) => a.localeCompare(b));
13210
13023
  } catch {
13211
13024
  return [];
13212
13025
  }
13213
13026
  }
13214
13027
  function isFile2(path2) {
13215
13028
  try {
13216
- return statSync11(path2).isFile();
13029
+ return statSync10(path2).isFile();
13217
13030
  } catch {
13218
13031
  return false;
13219
13032
  }
13220
13033
  }
13221
13034
  function isDirectory(path2) {
13222
13035
  try {
13223
- return statSync11(path2).isDirectory();
13036
+ return statSync10(path2).isDirectory();
13224
13037
  } catch {
13225
13038
  return false;
13226
13039
  }
@@ -13337,7 +13150,7 @@ function eventOf(line) {
13337
13150
  }
13338
13151
 
13339
13152
  // src/triage/triage.ts
13340
- import { basename as basename8, join as join17 } from "path";
13153
+ import { basename as basename8, join as join16 } from "path";
13341
13154
 
13342
13155
  // src/effort/store/tracker-refs.ts
13343
13156
  import { randomUUID as randomUUID9 } from "crypto";
@@ -13365,7 +13178,7 @@ var REFUSAL_TAILS = { write: "nothing written", read: "nothing opened" };
13365
13178
  function refused(call, reason) {
13366
13179
  return new Error(`effort store: tracker ref ${call} ${reason}; ${REFUSAL_TAILS[call]}`);
13367
13180
  }
13368
- function checkArtifact2(call, artifact) {
13181
+ function checkArtifact(call, artifact) {
13369
13182
  if (artifact === null || artifact === undefined) {
13370
13183
  throw refused(call, "has no artifact, the key a reference is kept under");
13371
13184
  }
@@ -13446,7 +13259,7 @@ function placeRef(db, path2, write, wanted, insertValues) {
13446
13259
  }
13447
13260
  function writeTrackerRef(repoRoot, write, seams = {}) {
13448
13261
  checkDispatch("tracker ref", write.dispatch, write.outcome);
13449
- checkArtifact2("write", write.artifact);
13262
+ checkArtifact("write", write.artifact);
13450
13263
  const wanted = storedRefOf(write.ref);
13451
13264
  if (typeof wanted === "string")
13452
13265
  throw refused("write", `has a reference ${wanted}`);
@@ -13471,7 +13284,7 @@ function writeTrackerRef(repoRoot, write, seams = {}) {
13471
13284
  return { path: path2, action, stored };
13472
13285
  }
13473
13286
  function readTrackerRef(repoRoot, artifact) {
13474
- checkArtifact2("read", artifact);
13287
+ checkArtifact("read", artifact);
13475
13288
  const path2 = sqliteStorePath(repoRoot);
13476
13289
  if (!existsSync19(path2))
13477
13290
  return null;
@@ -13519,7 +13332,7 @@ var FOREIGN_PROGRAMS = new Set([
13519
13332
  "yarn"
13520
13333
  ]);
13521
13334
  var CONFIG_SECTIONS = new Set(Object.values(SETTINGS).map((setting) => setting.key).filter((key) => key.includes(".")).map((key) => key.slice(0, key.indexOf("."))));
13522
- var FENCE_LINE4 = /^ {0,3}(`{3,}|~{3,})(.*)$/u;
13335
+ var FENCE_LINE5 = /^ {0,3}(`{3,}|~{3,})(.*)$/u;
13523
13336
  var QUOTE_LINE = /^ {0,3}>/u;
13524
13337
  var CODE_SPAN4 = /(?<!`)(`+)(?!`)(.+?)(?<!`)\1(?!`)/gu;
13525
13338
  var CROSS_ISSUE = /(?<![\w./-])[A-Za-z0-9][A-Za-z0-9-]*\/[A-Za-z0-9._-]+#\d+(?!\w)/gu;
@@ -13532,7 +13345,7 @@ var KEY_SHAPE = /^[a-z]+\.[a-z][A-Za-z0-9]*$/u;
13532
13345
  var IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/u;
13533
13346
  var UPPER_SNAKE = /^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+$/u;
13534
13347
  function openingFence(line) {
13535
- const found = FENCE_LINE4.exec(line);
13348
+ const found = FENCE_LINE5.exec(line);
13536
13349
  if (found === null)
13537
13350
  return null;
13538
13351
  const run = found[1] ?? "";
@@ -13542,7 +13355,7 @@ function openingFence(line) {
13542
13355
  return { char: run.charAt(0), length: run.length };
13543
13356
  }
13544
13357
  function closesFence3(line, fence) {
13545
- const found = FENCE_LINE4.exec(line);
13358
+ const found = FENCE_LINE5.exec(line);
13546
13359
  if (found === null)
13547
13360
  return false;
13548
13361
  const run = found[1] ?? "";
@@ -13968,7 +13781,7 @@ function createArtifactRefsVerifier(repoRoot, env) {
13968
13781
  }
13969
13782
 
13970
13783
  // src/triage/triage.ts
13971
- var PRIVATE_TRIAGE_DIR = join17(".rafa", "triage", "private");
13784
+ var PRIVATE_TRIAGE_DIR = join16(".rafa", "triage", "private");
13972
13785
  var SECRET_ENV_NAMES = ["GITHUB_TOKEN", "ANTHROPIC_API_KEY", "LINEAR_API_KEY"];
13973
13786
  var TRIAGE_MODULE = "unassigned";
13974
13787
  var BLOCKER_SEPARATOR = "; ";
@@ -13979,7 +13792,7 @@ var MIN_FENCE_LENGTH = 3;
13979
13792
  var ISSUE_OPENING = "An out-of-scope bug a rafa task session reported. The loop filed it and" + " dispatches no task for it: a plan that wants it fixed declares a task.";
13980
13793
  var COMMENT_OPENING = "Reported again by a rafa task session.";
13981
13794
  function privateTriageDir(repoRoot) {
13982
- return join17(repoRoot, PRIVATE_TRIAGE_DIR);
13795
+ return join16(repoRoot, PRIVATE_TRIAGE_DIR);
13983
13796
  }
13984
13797
  function createPrivateTriageTracker(repoRoot, options = {}) {
13985
13798
  return createLocalTracker({
@@ -14062,10 +13875,10 @@ function issueTitle(redactedWhat) {
14062
13875
  return `${kept.trimEnd()}${TITLE_CUT}`;
14063
13876
  }
14064
13877
  function section(heading, value, absent, redact) {
14065
- const shown2 = value === null ? absent : fenced(redact(value));
13878
+ const shown = value === null ? absent : fenced(redact(value));
14066
13879
  return `## ${heading}
14067
13880
 
14068
- ${shown2}`;
13881
+ ${shown}`;
14069
13882
  }
14070
13883
  function issueText(opening, values, redact) {
14071
13884
  return [
@@ -14262,10 +14075,10 @@ function bugLines(bug) {
14262
14075
  const warning = bug.ref === null ? `${name2}: not filed on ${where}: ${bug.problem}` : `${name2}: recurs in ${issueName2(bug.ref)} on ${where}, not commented on: ${bug.problem}`;
14263
14076
  return { notes: [], warnings: [warning] };
14264
14077
  }
14265
- const issue4 = bug.ref === null ? "an issue" : issueName2(bug.ref);
14266
- const note = bug.action === "filed" ? `${name2}: filed on ${where} as ${issue4}.` : `${name2}: recurs in ${issue4} on ${where}, found by ${FOUND_BY[bug.foundBy ?? "find"]}; commented on it.`;
14078
+ const issue3 = bug.ref === null ? "an issue" : issueName2(bug.ref);
14079
+ const note = bug.action === "filed" ? `${name2}: filed on ${where} as ${issue3}.` : `${name2}: recurs in ${issue3} on ${where}, found by ${FOUND_BY[bug.foundBy ?? "find"]}; commented on it.`;
14267
14080
  const problem = bug.problem === null ? [] : [`${name2}: ${bug.problem}`];
14268
- const conflict = bug.stored === "conflict" ? [`${name2}: the store keeps another reference for its artifact, in place of ${issue4}.`] : [];
14081
+ const conflict = bug.stored === "conflict" ? [`${name2}: the store keeps another reference for its artifact, in place of ${issue3}.`] : [];
14269
14082
  return { notes: [note], warnings: [...problem, ...conflict] };
14270
14083
  }
14271
14084
  function describeStartTriage(result) {
@@ -14351,14 +14164,231 @@ function mechanicalConflictBullet() {
14351
14164
  return `* ${MECHANICAL_CONFLICT_SENTENCE}`;
14352
14165
  }
14353
14166
 
14167
+ // src/start/promoted-check.ts
14168
+ import { isAbsolute as isAbsolute6, posix as posix2, relative as relative7 } from "path";
14169
+
14170
+ // src/start/promoted.ts
14171
+ var PROMOTED_BLOCK_KIND = "promoted";
14172
+ var ARROW = /\u2192|->/;
14173
+ var SKIPPED = /^skipped:/i;
14174
+ var LIST_MARKER = /^[-*]\s+/;
14175
+ var WHITESPACE2 = /\s/;
14176
+ function unquoted(text) {
14177
+ const trimmed = text.trim();
14178
+ return trimmed.length >= 2 && trimmed.startsWith("`") && trimmed.endsWith("`") ? trimmed.slice(1, -1).trim() : trimmed;
14179
+ }
14180
+ function wordProblem(word, what) {
14181
+ if (word === "")
14182
+ return `it names no ${what}`;
14183
+ if (WHITESPACE2.test(word))
14184
+ return `its ${what} \`${word}\` holds whitespace`;
14185
+ return null;
14186
+ }
14187
+ function readLine2(text, line) {
14188
+ const content = text.trim().replace(LIST_MARKER, "");
14189
+ const arrow = ARROW.exec(content);
14190
+ if (arrow === null)
14191
+ return { reason: "it holds no `\u2192` or `->`" };
14192
+ const id = unquoted(content.slice(0, arrow.index));
14193
+ const idProblem = wordProblem(id, "id");
14194
+ if (idProblem !== null)
14195
+ return { reason: idProblem };
14196
+ const right = content.slice(arrow.index + arrow[0].length).trim();
14197
+ if (SKIPPED.test(right)) {
14198
+ const reason = right.replace(SKIPPED, "").trim();
14199
+ return reason === "" ? { reason: `it skips \`${id}\` without a reason` } : { answer: { kind: "skipped", id, reason, line } };
14200
+ }
14201
+ const path2 = unquoted(right);
14202
+ const pathProblem = wordProblem(path2, "path");
14203
+ return pathProblem === null ? { answer: { kind: "promoted", id, path: path2, line } } : { reason: pathProblem };
14204
+ }
14205
+ function parsePromoted(output) {
14206
+ const block = readRafaBlocks(output).filter((found) => found.kind === PROMOTED_BLOCK_KIND).at(-1);
14207
+ if (block === undefined)
14208
+ return { status: "absent", answers: [], unreadable: [] };
14209
+ if (!block.closed)
14210
+ return { status: "unclosed", answers: [], unreadable: [] };
14211
+ const answers = [];
14212
+ const unreadable = [];
14213
+ const firstLine2 = new Map;
14214
+ block.body.split(`
14215
+ `).forEach((text, index) => {
14216
+ if (text.trim() === "")
14217
+ return;
14218
+ const line = block.span.first + 1 + index;
14219
+ const reading2 = readLine2(text, line);
14220
+ if ("reason" in reading2) {
14221
+ unreadable.push({ line, text, reason: reading2.reason });
14222
+ return;
14223
+ }
14224
+ const { id } = reading2.answer;
14225
+ const earlier = firstLine2.get(id);
14226
+ if (earlier !== undefined) {
14227
+ const reason = `\`${id}\` was already answered at line ${String(earlier)}`;
14228
+ unreadable.push({ line, text, reason });
14229
+ return;
14230
+ }
14231
+ firstLine2.set(id, line);
14232
+ answers.push(reading2.answer);
14233
+ });
14234
+ return { status: "read", answers, unreadable };
14235
+ }
14236
+
14237
+ // src/start/promoted-check.ts
14238
+ function saidClause2(result) {
14239
+ const said = gitSaid(result).split(`
14240
+ `)[0] ?? "";
14241
+ return said === "" ? "and git wrote nothing" : `git said: ${said}`;
14242
+ }
14243
+ function readHead(git) {
14244
+ const result = git(["rev-parse", "--verify", "HEAD"]);
14245
+ const head = result.stdout.trim();
14246
+ return result.ok && head !== "" ? head : null;
14247
+ }
14248
+ function changedSince(git, head) {
14249
+ if (head === null)
14250
+ return { ok: false, reason: "the HEAD before the session could not be read" };
14251
+ const result = git(["diff", "--name-only", "-z", head, "--"]);
14252
+ if (!result.ok) {
14253
+ return { ok: false, reason: `the changes since ${head.slice(0, 7)} could not be read, ${saidClause2(result)}` };
14254
+ }
14255
+ const paths = result.stdout.split("\x00").filter((path2) => path2 !== "");
14256
+ return { ok: true, head, paths: new Set(paths) };
14257
+ }
14258
+ function repoPath(path2, repoRoot) {
14259
+ const inside = isAbsolute6(path2) ? relative7(repoRoot, path2) : path2;
14260
+ const normal = posix2.normalize(inside.replaceAll("\\", "/"));
14261
+ if (normal === "." || normal === ".." || normal.startsWith("../") || isAbsolute6(normal))
14262
+ return null;
14263
+ return normal.replace(/^(\.\/)+/, "");
14264
+ }
14265
+ function promotedOutcome(lesson, named, changed, repoRoot) {
14266
+ if (!changed.ok)
14267
+ return { kind: "unchecked", id: lesson.id, path: named, reason: changed.reason };
14268
+ const path2 = repoPath(named, repoRoot);
14269
+ return path2 !== null && changed.paths.has(path2) ? { lesson, path: path2 } : { kind: "unchanged", id: lesson.id, path: named };
14270
+ }
14271
+ function checkPromoted(lessons, reading2, changed, repoRoot) {
14272
+ const answers = new Map(reading2.answers.map((answer2) => [answer2.id, answer2]));
14273
+ const listed = new Set(lessons.map((lesson) => lesson.id));
14274
+ const promoted = [];
14275
+ const skipped2 = [];
14276
+ const unpromoted = [];
14277
+ for (const lesson of lessons) {
14278
+ const answer2 = answers.get(lesson.id);
14279
+ if (answer2 === undefined) {
14280
+ unpromoted.push({ kind: "unanswered", id: lesson.id });
14281
+ } else if (answer2.kind === "skipped") {
14282
+ skipped2.push(answer2);
14283
+ } else {
14284
+ const outcome = promotedOutcome(lesson, answer2.path, changed, repoRoot);
14285
+ if ("lesson" in outcome)
14286
+ promoted.push(outcome);
14287
+ else
14288
+ unpromoted.push(outcome);
14289
+ }
14290
+ }
14291
+ const unlisted = reading2.answers.map((answer2) => answer2.id).filter((id) => !listed.has(id));
14292
+ return { promoted, skipped: skipped2, unpromoted, unlisted };
14293
+ }
14294
+ function unpromotedPhrase(lesson, head) {
14295
+ if (lesson.kind === "unanswered")
14296
+ return `\`${lesson.id}\` (no answer in the \`rafa:promoted\` block)`;
14297
+ if (lesson.kind === "unchecked")
14298
+ return `\`${lesson.id}\` (names \`${lesson.path}\`, which could not be checked: ${lesson.reason})`;
14299
+ const since = head === null ? "before the session" : head.slice(0, 7);
14300
+ return `\`${lesson.id}\` (names \`${lesson.path}\`, which has not changed since ${since})`;
14301
+ }
14302
+ function unpromotedLine(check, head) {
14303
+ if (check.unpromoted.length === 0)
14304
+ return null;
14305
+ const named = check.unpromoted.map((lesson) => unpromotedPhrase(lesson, head)).join("; ");
14306
+ return `Lessons listed for promotion that this pull request does not carry: ${named}.`;
14307
+ }
14308
+ async function recordPromotions(learning, promoted) {
14309
+ const problems = [];
14310
+ for (const { lesson, path: path2 } of promoted) {
14311
+ const sourceId = lesson.sources?.[0] ?? lesson.id;
14312
+ try {
14313
+ await learning.push({ source_id: sourceId, instincts: [{ ...lesson, promoted_to: path2 }] });
14314
+ } catch (error) {
14315
+ problems.push(`\`${lesson.id}\` is in ${path2}, but its promoted_to could not be recorded: ${messageOf(error)}`);
14316
+ }
14317
+ }
14318
+ return problems;
14319
+ }
14320
+ async function reportUnpromoted(pulls, branch, line) {
14321
+ try {
14322
+ const found = await pulls.findOpen(branch);
14323
+ if (found === null)
14324
+ return { written: false, problem: `no open pull request was found for ${branch}` };
14325
+ const detail = await pulls.get(found.number);
14326
+ if (detail === null)
14327
+ return { written: false, problem: `pull request #${found.number} could not be read back` };
14328
+ if (detail.body.includes(line))
14329
+ return { written: true, number: found.number, already: true };
14330
+ await pulls.editBody(found.number, bodyWithSentence(detail.body, line));
14331
+ return { written: true, number: found.number, already: false };
14332
+ } catch (error) {
14333
+ return { written: false, problem: `the pull request body could not be written: ${messageOf(error)}` };
14334
+ }
14335
+ }
14336
+ function warnAboutBlock(reading2, check) {
14337
+ const out = activeOutput();
14338
+ if (reading2.status === "absent")
14339
+ out.warn(" The wrap-up session wrote no `rafa:promoted` block.");
14340
+ if (reading2.status === "unclosed")
14341
+ out.warn(" The wrap-up session's `rafa:promoted` block is never closed, so none of it was read.");
14342
+ for (const line of reading2.unreadable)
14343
+ out.warn(` \`rafa:promoted\` line ${line.line} was not read: ${line.reason}.`);
14344
+ for (const id of check.unlisted)
14345
+ out.warn(` The \`rafa:promoted\` block answers \`${id}\`, which the wrap-up did not list.`);
14346
+ }
14347
+ async function recordConfirmed(input, check) {
14348
+ const out = activeOutput();
14349
+ if (check.promoted.length === 0)
14350
+ return;
14351
+ let learning;
14352
+ try {
14353
+ learning = input.learning();
14354
+ } catch (error) {
14355
+ out.warn(` No lesson's promoted_to was recorded: the learning adapter could not be made: ${messageOf(error)}`);
14356
+ return;
14357
+ }
14358
+ for (const problem of await recordPromotions(learning, check.promoted))
14359
+ out.warn(` ${problem}`);
14360
+ for (const { lesson, path: path2 } of check.promoted)
14361
+ out.info(` Lesson \`${lesson.id}\` promoted to ${path2}.`);
14362
+ }
14363
+ async function checkWrapUpAnswer(input) {
14364
+ const out = activeOutput();
14365
+ const reading2 = parsePromoted(input.output);
14366
+ const changed = changedSince(input.git, input.head);
14367
+ const check = checkPromoted(input.lessons, reading2, changed, input.repoRoot);
14368
+ warnAboutBlock(reading2, check);
14369
+ if (!changed.ok && reading2.answers.some((answer2) => answer2.kind === "promoted")) {
14370
+ out.warn(` No promoted path could be checked: ${changed.reason}.`);
14371
+ }
14372
+ await recordConfirmed(input, check);
14373
+ const line = unpromotedLine(check, input.head);
14374
+ if (line === null)
14375
+ return check;
14376
+ const report2 = await reportUnpromoted(input.pulls, input.branch, line);
14377
+ if (!report2.written) {
14378
+ out.warn(` This line is not in the pull request body, as ${report2.problem}: ${line}`);
14379
+ } else if (report2.already) {
14380
+ out.info(` Pull request #${report2.number} already names the lessons it does not carry.`);
14381
+ } else {
14382
+ out.info(` Pull request #${report2.number} now names the lessons it does not carry.`);
14383
+ }
14384
+ return check;
14385
+ }
14386
+
14354
14387
  // src/start/wrap-up.ts
14355
- function buildWrapUpPrompt(branch, planContent, openPullRequest = null, release = null) {
14388
+ function buildWrapUpPrompt(branch, planContent, openPullRequest = null, release = null, lessons = []) {
14356
14389
  return [
14357
14390
  "* Read `@progress.txt` in full.",
14358
14391
  "* `@progress.txt` above names the file `progress.txt` at the repo root; the `@` is a reference marker, never part of a path. Do not create or write a file whose name starts with `@`.",
14359
- "* If there's anything worth keeping, take what's generally relevant from that file into the `context/` page that owns its subject, `README.md` or a pertinent skill under `.claude/skills/`. The root `AGENTS.md` is a capped map read into every turn of every session: point at the page from there if a new one is needed, never inline the finding itself.",
14360
- "* Promote a finding ONLY when all three hold, and delete or keep it rather than promoting it when any one fails. It is PROJECT-SPECIFIC \u2014 a fact about THIS tree (its layout, its gates, its conventions, what a command here actually answers) and not a general technique, which belongs in a skill and not in this repo's docs. It is NOT ALREADY COVERED by a skill under `.claude/skills/` \u2014 read the skill that matches the finding's subject before writing anything, and extend that skill in place rather than restating it in a second document. And it NAMES WHAT IT REPLACES \u2014 the sentence, bullet or table row it supersedes, deleted in the SAME edit \u2014 or, when it replaces nothing, says so. A promotion landing beside the claim it should have replaced leaves two authorities on one subject, and nothing here compares two documents, so the stale one is never reported again.",
14361
- "* If a learn/learn-eval skill is available in this session, invoke it now so reusable patterns from this run are persisted as skills.",
14362
14392
  "* Find the plan's issue number `<n>`: the plan below carries it in its `rafa:plan` block as `issue: <n>`.",
14363
14393
  `* If the plan carries no \`issue:\` field, read the number from the branch name (${branch}), which is spelled \`feat/rafa-<n>-<slug>\`.`,
14364
14394
  '* Title the PR `rafa-<n>: <title>`, taking `<title>` from the plan title, e.g. "rafa-20: Add pull-request commands". Open the PR body with `Closes #<n>` \u2014 the GitHub issue number on its own, never `#rafa-<n>`, since the `rafa-` prefix is this project\'s naming convention and not a GitHub alias. If no number was found, title the PR with the plan title alone and write no closing line rather than inventing one.',
@@ -14370,6 +14400,7 @@ function buildWrapUpPrompt(branch, planContent, openPullRequest = null, release
14370
14400
  `* Commit these changes and push them to the CURRENT branch (${branch}). Never create a branch here: the work under review is this branch's, and a second branch splits one plan across two reviews.`,
14371
14401
  pullRequestStep(branch, openPullRequest),
14372
14402
  "* Do not include Claude attribution in the commit or PR message.",
14403
+ ...lessonsSection(lessons),
14373
14404
  "",
14374
14405
  "The plan this run executed follows, in full.",
14375
14406
  "",
@@ -14377,6 +14408,39 @@ function buildWrapUpPrompt(branch, planContent, openPullRequest = null, release
14377
14408
  ].join(`
14378
14409
  `);
14379
14410
  }
14411
+ function oneLine4(text) {
14412
+ return text.trim().replace(/\s+/g, " ");
14413
+ }
14414
+ function lessonLines(lesson) {
14415
+ const artifact = lesson.artifact === undefined || oneLine4(lesson.artifact) === "" ? "none" : oneLine4(lesson.artifact);
14416
+ return [
14417
+ `- \`${lesson.id}\``,
14418
+ ` - trigger: ${oneLine4(lesson.trigger)}`,
14419
+ ` - action: ${oneLine4(lesson.action)}`,
14420
+ ` - artifact: ${artifact}`
14421
+ ];
14422
+ }
14423
+ function lessonsSection(lessons) {
14424
+ if (lessons.length === 0)
14425
+ return [];
14426
+ return [
14427
+ "",
14428
+ "## Lessons to promote",
14429
+ "",
14430
+ "These lessons recurred across enough task sessions of this project, at a high enough confidence, to belong in its tracked docs. Handle them BEFORE the commit and push above, so the pages you write are in that commit.",
14431
+ "",
14432
+ ...lessons.flatMap(lessonLines),
14433
+ "",
14434
+ "For each lesson, EITHER write it into the tracked page that owns its subject \u2014 a `context/` page, `README.md`, or a skill under `.claude/skills/` \u2014 replacing in the same edit any sentence, bullet or table row it supersedes, OR leave it out and say why. The root `AGENTS.md` is a capped map read into every turn of every session: point at a new page from there if one is needed, never inline the lesson itself.",
14435
+ "",
14436
+ "Then answer every lesson listed above, and no other, in ONE `rafa:promoted` block in your final message: one line per lesson, its id, an arrow, and either the path you changed or `skipped:` and the reason. A promoted path must be a file this session changed, and a lesson the block does not answer is reported in the pull request body.",
14437
+ "",
14438
+ "```rafa:promoted",
14439
+ "<id> \u2192 <path>",
14440
+ "<id> \u2192 skipped: <reason>",
14441
+ "```"
14442
+ ];
14443
+ }
14380
14444
  function pullRequestStep(branch, openPullRequest) {
14381
14445
  return openPullRequest === null ? `* No open PR was found for ${branch}: open one with \`gh pr create\`. If it answers that a PR already exists, push to that PR and update its body with \`gh pr edit\`; never open a second PR from a new branch.` : `* PR #${openPullRequest} is already open for ${branch}, likely opened by the plan's close-out: push to it and update its body with \`gh pr edit ${openPullRequest}\` so the description covers the promotions this session committed.`;
14382
14446
  }
@@ -14421,20 +14485,61 @@ async function openPullRequestNumber(branch) {
14421
14485
  return null;
14422
14486
  }
14423
14487
  }
14424
- async function preserveProgress(planContent, settingSources, release, serving) {
14488
+ function messageOf4(error) {
14489
+ return error instanceof Error ? error.message : String(error);
14490
+ }
14491
+ function wrapUpAdapter(learning) {
14492
+ const registry = learning.registry ?? CORE_ADAPTER_REGISTRY;
14493
+ return registry.resolve("learning", learning.kind).create({
14494
+ repoRoot: learning.repoRoot,
14495
+ home: learning.home,
14496
+ learningBlessMinConfidence: learning.blessMinConfidence
14497
+ });
14498
+ }
14499
+ async function lessonsToPromote(learning) {
14500
+ if (learning === null)
14501
+ return [];
14502
+ try {
14503
+ const bundle = await wrapUpAdapter(learning).pullBlessed();
14504
+ return promotable(bundle.instincts, {
14505
+ after: learning.promoteAfter,
14506
+ minConfidence: learning.promoteMinConfidence
14507
+ });
14508
+ } catch (error) {
14509
+ activeOutput().warn(` The wrap-up lists no lesson to promote: the \`${learning.kind}\` learning adapter answered no blessed set: ${messageOf4(error)}`);
14510
+ return [];
14511
+ }
14512
+ }
14513
+ async function preserveProgress(planContent, settingSources, release, serving, learning) {
14425
14514
  const branch = getCurrentBranch();
14426
- const prompt = buildWrapUpPrompt(branch, planContent, await openPullRequestNumber(branch), release);
14515
+ const openPullRequest = await openPullRequestNumber(branch);
14516
+ const lessons = await lessonsToPromote(learning);
14517
+ const prompt = buildWrapUpPrompt(branch, planContent, openPullRequest, release, lessons);
14427
14518
  const served = serving === null ? null : serveSession(serving);
14428
14519
  for (const skipped2 of served?.skipped ?? [])
14429
14520
  activeOutput().warn(` ${skipped2.message}`);
14430
- const exitCode = await runClaude(withStamp(prompt), settingSources, [], undefined, served?.flags ?? []);
14431
- if (exitCode !== 0) {
14521
+ const git = learning === null || lessons.length === 0 ? null : createGitRunner(learning.repoRoot);
14522
+ const head = git === null ? null : readHead(git);
14523
+ const session = await runClaudeCaptured(withStamp(prompt), settingSources, [], undefined, served?.flags ?? []);
14524
+ if (session.exitCode !== 0) {
14432
14525
  activeOutput().error(`
14433
- \u274C Failed to preserve progress (exit ${exitCode}). Please try again.`);
14434
- } else {
14435
- activeOutput().info(`
14436
- \u2705 Progress preserved; PR opened or updated on this branch.`);
14526
+ \u274C Failed to preserve progress (exit ${session.exitCode}). Please try again.`);
14527
+ return;
14437
14528
  }
14529
+ activeOutput().info(`
14530
+ \u2705 Progress preserved; PR opened or updated on this branch.`);
14531
+ if (learning === null || git === null)
14532
+ return;
14533
+ await checkWrapUpAnswer({
14534
+ lessons,
14535
+ output: session.stdout,
14536
+ head,
14537
+ repoRoot: learning.repoRoot,
14538
+ branch,
14539
+ git,
14540
+ pulls: ghPullRequestsIn(process.cwd()),
14541
+ learning: () => wrapUpAdapter(learning)
14542
+ });
14438
14543
  }
14439
14544
 
14440
14545
  // src/utils/schedule.ts
@@ -14593,6 +14698,17 @@ async function start(args, repoRoot) {
14593
14698
  }
14594
14699
  });
14595
14700
  const serving = { root: repoRoot, run: session.id, home: homedir6(), settings: runConfig.config };
14701
+ const learning = {
14702
+ kind: runConfig.config.learningAdapter,
14703
+ home: homedir6(),
14704
+ blessMinConfidence: runConfig.config.learningBlessMinConfidence
14705
+ };
14706
+ const wrapUpLearning = {
14707
+ ...learning,
14708
+ repoRoot,
14709
+ promoteAfter: runConfig.config.learningPromoteAfter,
14710
+ promoteMinConfidence: runConfig.config.learningPromoteMinConfidence
14711
+ };
14596
14712
  const triageTask = createStartTriage({ repoRoot, config: runConfig.config });
14597
14713
  if (!fs.existsSync(trackerPath)) {
14598
14714
  activeOutput().info(`\uD83D\uDCCB Creating new plan tracker at ${path2.basename(trackerPath)}...`);
@@ -14626,7 +14742,7 @@ async function start(args, repoRoot) {
14626
14742
  planStub: planStub2,
14627
14743
  planContent
14628
14744
  });
14629
- await preserveProgress(planContent, settingSources, release, serving);
14745
+ await preserveProgress(planContent, settingSources, release, serving, wrapUpLearning);
14630
14746
  await finishRelease({ repoRoot, preparation: release }, {
14631
14747
  readProvider: () => resolvePrProvider({
14632
14748
  configured: runConfig.config.prProvider ?? null,
@@ -14658,7 +14774,7 @@ async function start(args, repoRoot) {
14658
14774
  });
14659
14775
  const { exitCode } = dispatch;
14660
14776
  const storeReport = async (outcome) => {
14661
- const stored2 = storeTaskReport({ repoRoot, planStub: planStub2, dispatch, outcome });
14777
+ const stored2 = await storeTaskReport({ repoRoot, planStub: planStub2, dispatch, outcome, learning });
14662
14778
  await triageTask({ trackerPath, lineNum: taskInfo.lineNum, planStub: planStub2, dispatch, outcome });
14663
14779
  return stored2;
14664
14780
  };
@@ -14766,9 +14882,9 @@ function recordMissingReview(repoRoot, planPath) {
14766
14882
  return;
14767
14883
  activeOutput().info(`\uD83D\uDD0D ${planPath} records ${REVIEW_MISSING_LINE}: the session returned no readable rafa:spec-review block.`);
14768
14884
  }
14769
- function recordPlanIssue(repoRoot, planPath, issue4) {
14770
- const line = issueFieldLine(issue4);
14771
- if (!recordInPlan(repoRoot, planPath, (written) => stampPlanIssue(written, issue4), line))
14885
+ function recordPlanIssue(repoRoot, planPath, issue3) {
14886
+ const line = issueFieldLine(issue3);
14887
+ if (!recordInPlan(repoRoot, planPath, (written) => stampPlanIssue(written, issue3), line))
14772
14888
  return;
14773
14889
  activeOutput().info(`\uD83D\uDD16 ${planPath} records ${line}, the issue it was planned from.`);
14774
14890
  }
@@ -14781,8 +14897,8 @@ import { readFileSync as readFileSync23 } from "fs";
14781
14897
 
14782
14898
  // src/refs/reading.ts
14783
14899
  import { readFileSync as readFileSync22, writeFileSync as writeFileSync11 } from "fs";
14784
- function unblockCommand(issue4) {
14785
- return `rafa issue unblock ${String(issue4)}`;
14900
+ function unblockCommand(issue3) {
14901
+ return `rafa issue unblock ${String(issue3)}`;
14786
14902
  }
14787
14903
  function firstStamp(live) {
14788
14904
  return live.kind === "unreadable" || live.kind === "absent" ? null : live;
@@ -14793,7 +14909,7 @@ async function liveReadings(body, verify) {
14793
14909
  read.push([ref, await verify(ref)]);
14794
14910
  return read;
14795
14911
  }
14796
- function rowOf(ref, live, stamp, heldTo, issue4) {
14912
+ function rowOf(ref, live, stamp, heldTo, issue3) {
14797
14913
  const { state, changedHeadings: changedHeadings2 } = compareToStamp({ live, stamp, blocker: ref.blocker });
14798
14914
  return Object.freeze({
14799
14915
  kind: ref.kind,
@@ -14803,7 +14919,7 @@ function rowOf(ref, live, stamp, heldTo, issue4) {
14803
14919
  fingerprint: live,
14804
14920
  stamp: heldTo,
14805
14921
  changedHeadings: changedHeadings2,
14806
- unblock: state === "resolved" ? unblockCommand(issue4) : null
14922
+ unblock: state === "resolved" ? unblockCommand(issue3) : null
14807
14923
  });
14808
14924
  }
14809
14925
  function answer2(rows, copy, body, stamps, write) {
@@ -14957,8 +15073,8 @@ async function enforceRefsGate(options) {
14957
15073
  // src/commands/plan/refs-check.ts
14958
15074
  async function coreRoster() {
14959
15075
  const [{ CORE_REGISTRY }, { describeRegistry }] = await Promise.all([
14960
- import("./index-cvpdvmtg.js"),
14961
- import("./describe-vq037dyt.js")
15076
+ import("./index-mh5hyw8g.js"),
15077
+ import("./describe-cenk70n5.js")
14962
15078
  ]);
14963
15079
  return describeRegistry(CORE_REGISTRY, version);
14964
15080
  }
@@ -15056,8 +15172,8 @@ function createPlanBlockedOffer(seams = DEFAULT_BLOCKED_SEAMS) {
15056
15172
 
15057
15173
  // src/commands/plan/ready-offer.ts
15058
15174
  var DEFAULT_OFFER_SEAMS = Object.freeze({});
15059
- function offerHeadLine(issue4) {
15060
- const number = String(issue4);
15175
+ function offerHeadLine(issue3) {
15176
+ const number = String(issue3);
15061
15177
  return `\uD83C\uDFF7 issue #${number} is not marked ${SPEC_READY_LABEL} \u2014 reading it as rafa issue ready ${number} would.`;
15062
15178
  }
15063
15179
  function writeOffer(request, report2) {
@@ -15072,16 +15188,16 @@ function createPlanReadyOffer(seams = DEFAULT_OFFER_SEAMS) {
15072
15188
  const openPrompter = seams.openPrompter ?? (() => createLinePrompter(process.stdin, process.stderr));
15073
15189
  const run = seams.run ?? runIssueReady;
15074
15190
  return async (request) => {
15075
- const { issue: issue4 } = request;
15076
- request.output.info(offerHeadLine(issue4.number));
15191
+ const { issue: issue3 } = request;
15192
+ request.output.info(offerHeadLine(issue3.number));
15077
15193
  const prompter = lazyPrompter(openPrompter);
15078
15194
  try {
15079
15195
  const report2 = await run({
15080
15196
  gh: request.gh,
15081
- issue: issue4.number,
15197
+ issue: issue3.number,
15082
15198
  trust: request.trust,
15083
15199
  ask: prompter.ask,
15084
- readIssue: () => Promise.resolve(issue4)
15200
+ readIssue: () => Promise.resolve(issue3)
15085
15201
  });
15086
15202
  writeOffer(request, report2);
15087
15203
  return report2.status === "marked";
@@ -15338,4 +15454,4 @@ async function plan(args, repoRoot, registry = CORE_ADAPTER_REGISTRY) {
15338
15454
  }
15339
15455
  }
15340
15456
 
15341
- export { readConfigFile, loadConfig, planStubsFromFileNames, resolvePlanStub, sessionLogDir, collectSessionRow, collect, report, name, version, MANIFEST_VERSION, FEATURE_TYPES, OUTPUT_CHANNELS, RUNNING_MANIFEST_SEAMS, validateManifest, plansDirAt, requireProject, resolvePlansDir, resolveProjectConfig, countTasks, formatCounts, checkbox, planFileName, stubOfPlanFile, isFile, issueLine, plural, expectNoArgument, expectOneArgument, expectAtMostOneArgument, readSwitch, readFrontmatterDocument, readFrontmatter, renderFrontmatter, mergeFrontmatter, writeFrontmatter, normalizeRemote, gitRemoteUrl, ACTION_HEADING, CAUSE_HEADING, actionHash, parseInstinct, writeInstinct, PROVENANCE_FIELD, isStackList, stackPaths, stacksFromEccSkillName, DESCRIPTION_LIMIT, LISTING_LIMIT, SKILL_SIGNALS, countCharacters, SKILL_FILE, MARKDOWN_EXTENSION, scanLayout, toolOf, pathDirectories, collectReferences, checkReferences, checkFile, checkDirectory, AGENT_DEFINITION_DIR, readInventoryFrontmatter, bundledAgentsDirectory, sourceItem, byName, skillItem, readTrees, SCOPE_DIR, INIT_COMMAND, DISK_FILE_SYSTEM, ScopeError, scopeAt, initHint, selfAndAncestors, resolveScope, SessionRecordError, runsDir, errorCode, readSessions, readSession, SERVE_CLI_VERSION, SKILL_FILE2 as SKILL_FILE1, provenanceBlock, serveVerdict, VENDOR_COMMAND, readAgentDirectory, resolveAgentRoster, missingPlanAgents, validate_default, SPEC_READY_LABEL, TEMPLATE_HEADINGS, findReadinessGaps, hasSpecReadyLabel, createGhPermissions, readAuthorTrust, trustRefusalClause, requireTrustedAuthor, SPEC_NEEDS_WORK_LABEL, SPEC_BLOCKED_LABEL, hasSpecBlockedLabel, readBlockedBy, blockedFaultMessage, UNREADABLE, RefsBlockError, fingerprintText, readRefsText, readCopyRefs, restampCopyRefs, createGitRunner, gitSaid, RefVerifyError, ghIssueReader, tsSymbolsOutliner, createRefVerifier, createGhIssueBoard, ID_PREFIX, BRANCH_PREFIX, SPEC_EXTENSION, boardId, notesFileName, PREVIOUS_COPY_WARN_ABOVE, previousDir, countPreviousCopies, SPEC_LABEL, createGhSpecIssueReader, ROADMAP_TITLE, ROADMAP_SETTING, ROADMAP_REFUSAL_EXIT, parseRoadmapBody, createGhRoadmapSearch, severalRoadmapsMessage, resolveRoadmapIssue, createGhOpenPullRequests, closedIssuesIn, branchClaims, scanClaimBranches, blockedLineSentence, memoiseVerifier, readPlanChanges, shellQuote, ghMissingMessage, ghUnauthenticatedMessage, ghHostOf, ghPreflightItems, resolvePrProvider, requireGhProvider, isFirstDispatch, SESSION_ID_FLAG2 as SESSION_ID_FLAG, resolveLoopSeams, sessionIdFlag, refusal2 as refusal, isLive, planLabel, sessionLine, projectRoot, readRecords, pickSession, writeSession, readSessionChecklist, checkboxAt, estimateEta, readSessionFinishes, etaLine, verdictOf, failingRows, formatRows, waitForChecks, parseWorkingTree, parseWorktrees, readMergeRefusal, cleanUpSteps, commandLine, remainingFrom, createGhPullRequests, ghPullRequestsIn, DEFAULT_BASE_BRANCH, ghNextBoard, openNextSources, createLinePrompter, REMOTE, parseBaseStanding, hasDiverged, list_default, readBranch, readPlans, readOpenPull, readBranchPlan, readNextState, commandWords, nextQuestion, NEXT_COMMAND_ACTIONS, actionInvocation, runAction, HINT_FLAG_SPEC, actionOutput, endWithNextStep, endingWith, DEFAULT_ISSUE_SEAMS, lineRefusal2 as lineRefusal, expectTwoArguments, readTextFlag, readNonBlankFlag, readRequiredFlag, readChoice, readChoiceFlag, issueProject, issueSubjectConfig, resolveIssueTracker, issueRef, issueName, urlLines, onTracker, lazyPrompter, ready_default, versionLine, DEFAULT_PLAN_FILE, resolvePlanPath, assessPlanRisk, renderRiskText, announceRiskTotal, resolveCreateSpec, resolveReleaseEnabled, TRIAGE_MODULE, DEFAULT_CI_TIMEOUT_MIN, CI_POLL_INTERVAL_MS, DEFAULT_CI_ATTEMPTS, RUNTIME_SUBDIR, RUNTIME_ENTRY, VERSION_SHAPE2 as VERSION_SHAPE, refuseMisplacedRuntime, groupChangeNotes, renderNoteLines, highestChangeLevel, parseSemanticVersion, readManifestVersion, MECHANICAL_CONFLICT_SENTENCE, start, usage, coreRoster, createPlanRefsVerifier, plan };
15457
+ export { readConfigFile, loadConfig, planStubsFromFileNames, resolvePlanStub, sessionLogDir, collectSessionRow, collect, report, name, version, MANIFEST_VERSION, FEATURE_TYPES, OUTPUT_CHANNELS, RUNNING_MANIFEST_SEAMS, validateManifest, plansDirAt, requireProject, resolvePlansDir, resolveProjectConfig, countTasks, formatCounts, checkbox, planFileName, stubOfPlanFile, isFile, issueLine, plural, expectNoArgument, expectOneArgument, expectAtMostOneArgument, readSwitch, PROVENANCE_FIELD, isStackList, stackPaths, stacksFromEccSkillName, DESCRIPTION_LIMIT, LISTING_LIMIT, SKILL_SIGNALS, countCharacters, toolOf, pathDirectories, checkReferences, checkFile, checkDirectory, AGENT_DEFINITION_DIR, readInventoryFrontmatter, bundledAgentsDirectory, sourceItem, byName, skillItem, readTrees, SCOPE_DIR, INIT_COMMAND, DISK_FILE_SYSTEM, ScopeError, scopeAt, initHint, selfAndAncestors, resolveScope, SessionRecordError, runsDir, errorCode, readSessions, readSession, SERVE_CLI_VERSION, SKILL_FILE2 as SKILL_FILE, provenanceBlock, serveVerdict, VENDOR_COMMAND, readAgentDirectory, resolveAgentRoster, missingPlanAgents, validate_default, SPEC_READY_LABEL, TEMPLATE_HEADINGS, findReadinessGaps, hasSpecReadyLabel, createGhPermissions, readAuthorTrust, trustRefusalClause, requireTrustedAuthor, SPEC_NEEDS_WORK_LABEL, SPEC_BLOCKED_LABEL, hasSpecBlockedLabel, readBlockedBy, blockedFaultMessage, UNREADABLE, RefsBlockError, fingerprintText, readRefsText, readCopyRefs, restampCopyRefs, createGitRunner, gitSaid, RefVerifyError, ghIssueReader, tsSymbolsOutliner, createRefVerifier, createGhIssueBoard, ID_PREFIX, BRANCH_PREFIX, SPEC_EXTENSION, boardId, notesFileName, PREVIOUS_COPY_WARN_ABOVE, previousDir, countPreviousCopies, SPEC_LABEL, createGhSpecIssueReader, ROADMAP_TITLE, ROADMAP_SETTING, ROADMAP_REFUSAL_EXIT, parseRoadmapBody, createGhRoadmapSearch, severalRoadmapsMessage, resolveRoadmapIssue, createGhOpenPullRequests, closedIssuesIn, branchClaims, scanClaimBranches, blockedLineSentence, memoiseVerifier, LEARNED_DIRECTORY, ORIGIN_FIELD, DEMOTION_VERDICTS, WHEN_TO_USE_HEADING, bodySections, classifySkill, firstSentence, convertToInstinct, readPlanChanges, shellQuote, ghMissingMessage, ghUnauthenticatedMessage, ghHostOf, ghPreflightItems, resolvePrProvider, requireGhProvider, isFirstDispatch, SESSION_ID_FLAG2 as SESSION_ID_FLAG, resolveLoopSeams, sessionIdFlag, refusal2 as refusal, isLive, planLabel, sessionLine, projectRoot, readRecords, pickSession, writeSession, readSessionChecklist, checkboxAt, estimateEta, readSessionFinishes, etaLine, verdictOf, failingRows, formatRows, waitForChecks, parseWorkingTree, parseWorktrees, readMergeRefusal, cleanUpSteps, commandLine, remainingFrom, createGhPullRequests, ghPullRequestsIn, DEFAULT_BASE_BRANCH, ghNextBoard, openNextSources, createLinePrompter, REMOTE, parseBaseStanding, hasDiverged, list_default, readBranch, readPlans, readOpenPull, readBranchPlan, readNextState, commandWords, nextQuestion, NEXT_COMMAND_ACTIONS, actionInvocation, runAction, HINT_FLAG_SPEC, actionOutput, endWithNextStep, endingWith, DEFAULT_ISSUE_SEAMS, lineRefusal2 as lineRefusal, expectTwoArguments, readTextFlag, readNonBlankFlag, readRequiredFlag, readChoice, readChoiceFlag, issueProject, issueSubjectConfig, resolveIssueTracker, issueRef, issueName, urlLines, onTracker, lazyPrompter, ready_default, versionLine, DEFAULT_PLAN_FILE, resolvePlanPath, assessPlanRisk, renderRiskText, announceRiskTotal, resolveCreateSpec, resolveReleaseEnabled, TRIAGE_MODULE, DEFAULT_CI_TIMEOUT_MIN, CI_POLL_INTERVAL_MS, DEFAULT_CI_ATTEMPTS, RUNTIME_SUBDIR, RUNTIME_ENTRY, VERSION_SHAPE2 as VERSION_SHAPE, refuseMisplacedRuntime, groupChangeNotes, renderNoteLines, highestChangeLevel, parseSemanticVersion, readManifestVersion, MECHANICAL_CONFLICT_SENTENCE, start, usage, coreRoster, createPlanRefsVerifier, plan };