@open-tomato/rafa 0.44.0 → 0.45.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,2414 +0,0 @@
1
- // @bun
2
- import {
3
- CONFIDENCE_MAX,
4
- CONFIDENCE_MIN
5
- } from "./index-hf2k497d.js";
6
-
7
- // src/pr/types.ts
8
- var MERGE_METHODS = Object.freeze([
9
- "squash",
10
- "merge",
11
- "rebase"
12
- ]);
13
- function isMergeMethod(value) {
14
- return typeof value === "string" && MERGE_METHODS.includes(value);
15
- }
16
-
17
- // src/schema/tiers.ts
18
- import { existsSync, realpathSync } from "fs";
19
- import { dirname, join } from "path";
20
- var SKILL_TIERS = ["project", "rafa", "user"];
21
- var INSTINCT_SCOPES = ["project", "user"];
22
- var CLAUDE_SKILLS_PATH = join(".claude", "skills");
23
- var RAFA_INSTINCTS_PATH = join(".rafa", "instincts");
24
- var BUNDLED_SKILLS_DIR = join("bundled", "skills");
25
- var BUNDLED_BIN_DIR = join("bundled", "bin");
26
- function isSkillTier(value) {
27
- return SKILL_TIERS.includes(value);
28
- }
29
- function realEntry(entry) {
30
- try {
31
- return realpathSync(entry);
32
- } catch {
33
- return entry;
34
- }
35
- }
36
- function bundledSkillsDirectory(entry) {
37
- return join(dirname(realEntry(entry ?? Bun.main)), BUNDLED_SKILLS_DIR);
38
- }
39
- function bundledBinDirectory(entry) {
40
- return join(dirname(realEntry(entry ?? Bun.main)), BUNDLED_BIN_DIR);
41
- }
42
- function skillTierDirectory(tier, seams) {
43
- if (tier === "user")
44
- return join(seams.home, CLAUDE_SKILLS_PATH);
45
- if (tier === "rafa")
46
- return bundledSkillsDirectory(seams.entry);
47
- return seams.projectRoot === null ? null : join(seams.projectRoot, CLAUDE_SKILLS_PATH);
48
- }
49
- function resolveSkillTiers(seams) {
50
- const located = [];
51
- for (const tier of SKILL_TIERS) {
52
- const dir = skillTierDirectory(tier, seams);
53
- if (dir !== null)
54
- located.push({ tier, dir });
55
- }
56
- return located;
57
- }
58
- function instinctScopeDirectory(scope, seams) {
59
- if (scope === "user")
60
- return join(seams.home, RAFA_INSTINCTS_PATH);
61
- return seams.projectRoot === null ? null : join(seams.projectRoot, RAFA_INSTINCTS_PATH);
62
- }
63
- function resolveInstinctScopes(seams) {
64
- const located = [];
65
- for (const scope of INSTINCT_SCOPES) {
66
- const dir = instinctScopeDirectory(scope, seams);
67
- if (dir !== null)
68
- located.push({ scope, dir });
69
- }
70
- return located;
71
- }
72
- function tierExists(dir) {
73
- return existsSync(dir);
74
- }
75
-
76
- // src/utils/declaration.ts
77
- var DECLARATION_KEYS = [
78
- "agent",
79
- "model",
80
- "effort",
81
- "budget",
82
- "tools",
83
- "skills",
84
- "tests"
85
- ];
86
- var GRANULAR_KEYS = ["model", "effort", "tools"];
87
- var AGENT_OWNED_KEYS = ["model", "tools"];
88
- var MODEL_ALIASES = ["opus", "sonnet", "haiku", "fable"];
89
- var EFFORT_LEVELS = [
90
- "low",
91
- "medium",
92
- "high",
93
- "xhigh",
94
- "max"
95
- ];
96
- var TEST_SCOPES = ["affected", "module", "full"];
97
- var DEFAULT_TEST_SCOPE = "affected";
98
- var DECLARATION_BLOCK = /\{([^{}]*)\}$/;
99
- var ENTRY_TOKEN = /^([A-Za-z_][A-Za-z0-9_-]*)=(.*)$/;
100
- var FULL_MODEL_NAME = /^claude-[a-z0-9][a-z0-9.-]*$/;
101
- var AGENT_NAME = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
102
- function isAgentName(value) {
103
- return AGENT_NAME.test(value);
104
- }
105
- var TOOL_NAME = /^[A-Za-z][A-Za-z0-9_-]*$/;
106
- var SKILL_NAME = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
107
- var BUDGET_FLAG = "--max-budget-usd";
108
- var BUDGET_VALUE = /^(?:0|[1-9]\d{0,5})(?:\.\d{1,6})?$/;
109
- function parseBudgetUsd(value) {
110
- if (!BUDGET_VALUE.test(value))
111
- return null;
112
- const usd = Number(value);
113
- return usd > 0 ? usd : null;
114
- }
115
- function isEffortLevel(value) {
116
- return EFFORT_LEVELS.includes(value);
117
- }
118
- function isTestScope(value) {
119
- return TEST_SCOPES.includes(value);
120
- }
121
- function readTestScope(declaration) {
122
- return declaration?.tests ?? DEFAULT_TEST_SCOPE;
123
- }
124
- function isModelValue(value) {
125
- if (MODEL_ALIASES.includes(value))
126
- return true;
127
- return FULL_MODEL_NAME.test(value);
128
- }
129
- function parseToolList(value) {
130
- const parts = value.split(",");
131
- if (parts.length === 0)
132
- return null;
133
- const tools = [];
134
- for (const part of parts) {
135
- if (!TOOL_NAME.test(part))
136
- return null;
137
- if (!tools.includes(part))
138
- tools.push(part);
139
- }
140
- return tools;
141
- }
142
- function parseSkillList(value) {
143
- const parts = value.split(",");
144
- if (parts.length === 0)
145
- return null;
146
- const skills = [];
147
- for (const part of parts) {
148
- if (!SKILL_NAME.test(part))
149
- return null;
150
- if (!skills.includes(part))
151
- skills.push(part);
152
- }
153
- return skills;
154
- }
155
- function unusableValue(key, token) {
156
- return { reason: "unusable-value", key, text: token };
157
- }
158
- function isDeclarationKey(key) {
159
- return DECLARATION_KEYS.includes(key);
160
- }
161
- function tokenise(body) {
162
- const trimmed = body.trim();
163
- if (trimmed.length === 0)
164
- return [];
165
- return trimmed.split(/\s+/);
166
- }
167
- function readBlock(raw, body) {
168
- const entries = [];
169
- const extras = [];
170
- const issues = [];
171
- const seen = new Set;
172
- let agent = null;
173
- let model = null;
174
- let effort = null;
175
- let budget = null;
176
- let tools = null;
177
- let skills = null;
178
- let tests = null;
179
- for (const token of tokenise(body)) {
180
- const match = token.match(ENTRY_TOKEN);
181
- const key = match?.[1];
182
- const value = match?.[2];
183
- if (key === undefined || value === undefined) {
184
- issues.push({ reason: "stray-token", key: "", text: token });
185
- continue;
186
- }
187
- const entry = { key, value };
188
- entries.push(entry);
189
- if (!isDeclarationKey(key)) {
190
- extras.push(entry);
191
- continue;
192
- }
193
- if (seen.has(key)) {
194
- issues.push({ reason: "duplicate-key", key, text: token });
195
- continue;
196
- }
197
- seen.add(key);
198
- if (key === "agent") {
199
- if (AGENT_NAME.test(value))
200
- agent = value;
201
- else
202
- issues.push(unusableValue(key, token));
203
- continue;
204
- }
205
- if (key === "model") {
206
- if (isModelValue(value))
207
- model = value;
208
- else
209
- issues.push(unusableValue(key, token));
210
- continue;
211
- }
212
- if (key === "effort") {
213
- if (isEffortLevel(value))
214
- effort = value;
215
- else
216
- issues.push(unusableValue(key, token));
217
- continue;
218
- }
219
- if (key === "budget") {
220
- const usd = parseBudgetUsd(value);
221
- if (usd === null)
222
- issues.push(unusableValue(key, token));
223
- else
224
- budget = usd;
225
- continue;
226
- }
227
- if (key === "tools") {
228
- const parsed = parseToolList(value);
229
- if (parsed === null)
230
- issues.push(unusableValue(key, token));
231
- else
232
- tools = parsed;
233
- continue;
234
- }
235
- if (key === "tests") {
236
- if (isTestScope(value))
237
- tests = value;
238
- else
239
- issues.push(unusableValue(key, token));
240
- continue;
241
- }
242
- const named = parseSkillList(value);
243
- if (named === null)
244
- issues.push(unusableValue(key, token));
245
- else
246
- skills = named;
247
- }
248
- if (seen.size === 0)
249
- return null;
250
- return {
251
- raw,
252
- entries,
253
- extras,
254
- issues,
255
- agent,
256
- model,
257
- effort,
258
- budget,
259
- tools,
260
- skills,
261
- tests
262
- };
263
- }
264
- function parseTaskDeclaration(taskText) {
265
- const trimmed = taskText.trimEnd();
266
- const match = trimmed.match(DECLARATION_BLOCK);
267
- const raw = match?.[0];
268
- const body = match?.[1];
269
- if (raw === undefined || body === undefined) {
270
- return { text: taskText, declaration: null };
271
- }
272
- const before = trimmed.slice(0, trimmed.length - raw.length).trimEnd();
273
- if (before.length === 0)
274
- return { text: taskText, declaration: null };
275
- const declaration = readBlock(raw, body);
276
- if (declaration === null)
277
- return { text: taskText, declaration: null };
278
- return { text: before, declaration };
279
- }
280
- function stripTaskDeclaration(taskText) {
281
- return parseTaskDeclaration(taskText).text;
282
- }
283
- function budgetArgs(budget) {
284
- return budget === null ? [] : [BUDGET_FLAG, String(budget)];
285
- }
286
- function resolveDeclarationFlags(declaration, agentDeclaresEffort) {
287
- if (declaration === null)
288
- return { args: [], suppressed: [] };
289
- const { agent, effort } = declaration;
290
- const isPresent = (key) => declaration[key] !== null;
291
- const budget = budgetArgs(declaration.budget);
292
- if (agent !== null) {
293
- if (effort === null || agentDeclaresEffort(agent)) {
294
- return { args: ["--agent", agent, ...budget], suppressed: GRANULAR_KEYS.filter(isPresent) };
295
- }
296
- return {
297
- args: ["--agent", agent, "--effort", effort, ...budget],
298
- suppressed: AGENT_OWNED_KEYS.filter(isPresent)
299
- };
300
- }
301
- const args = [];
302
- if (declaration.model !== null)
303
- args.push("--model", declaration.model);
304
- if (declaration.effort !== null)
305
- args.push("--effort", declaration.effort);
306
- args.push(...budget);
307
- if (declaration.tools !== null) {
308
- args.push("--tools", declaration.tools.join(","));
309
- }
310
- return { args, suppressed: [] };
311
- }
312
-
313
- // src/config-sections.ts
314
- function isMapping(value) {
315
- return typeof value === "object" && value !== null && !Array.isArray(value);
316
- }
317
- function describeValue(value) {
318
- if (typeof value === "string")
319
- return JSON.stringify(value);
320
- if (value === null)
321
- return "null";
322
- if (Array.isArray(value))
323
- return "a list";
324
- if (typeof value === "object")
325
- return "a mapping";
326
- return String(value);
327
- }
328
- function messageOf(error) {
329
- return error instanceof Error ? error.message : String(error);
330
- }
331
- var CONFIG_VERSIONS = [1];
332
- var STORE_BACKENDS = ["sqlite", "ndjson"];
333
- var SYNC_STRATEGIES = ["local", "file", "git", "service", "p2p"];
334
- var BOARD_RELATIONSHIP_MODES = ["labels", "native"];
335
- var INJECT_MODES = ["full", "stage", "task"];
336
- var OUTPUT_MODES = ["text", "json", "events"];
337
- var CLAUDE_SETTING_SOURCES = ["user", "project", "local"];
338
- var PR_PROVIDERS = ["gh", "none"];
339
- var RELEASE_AUTO = "auto";
340
- var TIER_SWITCHES = ["on", "off"];
341
- var SKILL_RESOLVERS = ["planner", "tag", "none"];
342
- var LESSON_SWITCHES = ["on", "off"];
343
- var CLAIMS_STALE_DISABLED = "disabled";
344
- var CLAIMS_AHEAD = ["off", "allow"];
345
- function accepted(value) {
346
- return { value, problems: [], extras: [] };
347
- }
348
- function refused(at, raw, expected) {
349
- const problem = `${at.label} is ${describeValue(raw)}, expected ${expected}`;
350
- return { value: undefined, problems: [problem], extras: [] };
351
- }
352
- function refusedWith(problems) {
353
- return { value: undefined, problems, extras: [] };
354
- }
355
- function mapReading(reading, map) {
356
- return reading.value === undefined ? { ...reading, value: undefined } : { ...reading, value: map(reading.value) };
357
- }
358
- function below(at, step) {
359
- return { label: `${at.label}${step}`, key: `${at.key}${step}` };
360
- }
361
- function oneOf(values) {
362
- const expected = `one of: ${values.join(", ")}`;
363
- return (raw, at) => {
364
- const hit = values.find((value) => value === raw);
365
- return hit === undefined ? refused(at, raw, expected) : accepted(hit);
366
- };
367
- }
368
- function text(expected) {
369
- return (raw, at) => typeof raw === "string" && raw.trim() !== "" ? accepted(raw) : refused(at, raw, expected);
370
- }
371
- var flag = (raw, at) => typeof raw === "boolean" ? accepted(raw) : refused(at, raw, "true or false");
372
- var mergeMethod = oneOf(MERGE_METHODS);
373
- var usdAmount = (raw, at) => {
374
- const usd = typeof raw === "number" ? parseBudgetUsd(String(raw)) : null;
375
- return usd === null ? refused(at, raw, "a number of US dollars above zero, at most six digits either side of the point") : accepted(usd);
376
- };
377
- var issueNumber = (raw, at) => typeof raw === "number" && Number.isSafeInteger(raw) && raw > 0 ? accepted(raw) : refused(at, raw, "an issue number, a whole number above zero");
378
- var dayCount = (raw, at) => typeof raw === "number" && Number.isSafeInteger(raw) && raw > 0 ? accepted(raw) : refused(at, raw, "a number of days, a whole number above zero");
379
- var BUSY_TIMEOUT_MIN_MS = 1;
380
- var BUSY_TIMEOUT_MAX_MS = 60000;
381
- function isBusyTimeoutMs(raw) {
382
- return typeof raw === "number" && Number.isSafeInteger(raw) && raw >= BUSY_TIMEOUT_MIN_MS && raw <= BUSY_TIMEOUT_MAX_MS;
383
- }
384
- var busyTimeoutMs = (raw, at) => isBusyTimeoutMs(raw) ? accepted(raw) : refused(at, raw, `a lock wait in milliseconds, a whole number from ${String(BUSY_TIMEOUT_MIN_MS)} to ${String(BUSY_TIMEOUT_MAX_MS)}`);
385
- var confidence = (raw, at) => typeof raw === "number" && Number.isFinite(raw) && toHundredths(raw) >= toHundredths(CONFIDENCE_MIN) && toHundredths(raw) <= toHundredths(CONFIDENCE_MAX) ? accepted(raw) : refused(at, raw, `a confidence from ${String(CONFIDENCE_MIN)} to ${String(CONFIDENCE_MAX)}`);
386
- function toHundredths(value) {
387
- return Math.round(value * 100);
388
- }
389
- var recurrenceCount = (raw, at) => typeof raw === "number" && Number.isSafeInteger(raw) && raw >= 1 ? accepted(raw) : refused(at, raw, "a count, a whole number of 1 or more");
390
- var releaseEnabled = (raw, at) => typeof raw === "boolean" || raw === RELEASE_AUTO ? accepted(raw) : refused(at, raw, `true, false or ${RELEASE_AUTO}`);
391
- var tierSwitch = oneOf(TIER_SWITCHES);
392
- var tierPin = (raw, at) => {
393
- if (raw === false)
394
- return accepted(false);
395
- const tier = SKILL_TIERS.find((name) => name === raw);
396
- return tier === undefined ? refused(at, raw, `false or one of: ${SKILL_TIERS.join(", ")}`) : accepted(tier);
397
- };
398
- var routeTarget = (raw, at) => raw === false || typeof raw === "string" && raw.trim() !== "" ? accepted(raw) : refused(at, raw, "false or an agent name");
399
- var skillResolverName = oneOf(SKILL_RESOLVERS);
400
- var lessonSwitch = oneOf(LESSON_SWITCHES);
401
- var CLAIM_DURATION = /^(\d+)([hd])$/;
402
- var HOURS_PER_DAY = 24;
403
- function claimDurationHours(raw) {
404
- const match = typeof raw === "string" ? CLAIM_DURATION.exec(raw) : null;
405
- if (match === null)
406
- return null;
407
- const hours = Number(match[1]) * (match[2] === "d" ? HOURS_PER_DAY : 1);
408
- return Number.isSafeInteger(hours) && hours > 0 ? hours : null;
409
- }
410
- var claimsStaleAfter = (raw, at) => raw === CLAIMS_STALE_DISABLED || claimDurationHours(raw) !== null ? accepted(raw) : refused(at, raw, `a duration of whole hours or days above zero, such as 36h or 3d, or ${CLAIMS_STALE_DISABLED}`);
411
- var claimsAhead = oneOf(CLAIMS_AHEAD);
412
- var GITHUB_LOGIN = /^[A-Za-z0-9][A-Za-z0-9-]*(?:\[bot\])?$/;
413
- function isGitHubLogin(value) {
414
- return typeof value === "string" && GITHUB_LOGIN.test(value);
415
- }
416
- var githubLogin = (raw, at) => isGitHubLogin(raw) ? accepted(raw) : refused(at, raw, "a GitHub login");
417
- var BOARD_PROJECT_TEMPLATE_DEFAULT = "https://github.com/orgs/open-tomato/projects/6";
418
- var PROJECT_URL = /^https:\/\/github\.com\/(?:orgs|users)\/[A-Za-z0-9][A-Za-z0-9-]*\/projects\/[1-9]\d*$/;
419
- var projectUrl = (raw, at) => typeof raw === "string" && PROJECT_URL.test(raw) ? accepted(raw) : refused(at, raw, "a GitHub project URL, https://github.com/orgs/<owner>/projects/<number> or /users/<owner>/\u2026");
420
- var projectNumber = (raw, at) => typeof raw === "number" && Number.isSafeInteger(raw) && raw > 0 ? accepted(raw) : refused(at, raw, "a project number, a whole number above zero");
421
- function listOf(item, expected) {
422
- return (raw, at) => {
423
- if (!Array.isArray(raw))
424
- return refused(at, raw, `a list of ${expected}`);
425
- const readings = raw.map((entry, index) => item(entry, below(at, `[${index}]`)));
426
- const problems = readings.flatMap((reading) => reading.problems);
427
- const extras = readings.flatMap((reading) => reading.extras);
428
- if (problems.length > 0)
429
- return { value: undefined, problems, extras };
430
- const values = readings.flatMap((reading) => reading.value === undefined ? [] : [reading.value]);
431
- return { value: Object.freeze(values), problems, extras };
432
- };
433
- }
434
- function subsetOf(values) {
435
- const expected = `a comma-separated subset of: ${values.join(", ")}`;
436
- return (raw, at) => {
437
- if (typeof raw !== "string")
438
- return refused(at, raw, expected);
439
- const named = [];
440
- for (const entry of raw.split(",")) {
441
- const hit = values.find((value) => value === entry.trim());
442
- if (hit === undefined || named.includes(hit))
443
- return refused(at, raw, expected);
444
- named.push(hit);
445
- }
446
- return accepted(Object.freeze(named));
447
- };
448
- }
449
-
450
- // src/config-items.ts
451
- var itemText = text("a non-empty string");
452
- function readKeyedItem(shape, raw, at) {
453
- const choices = shape.kinds.join(", ");
454
- if (!isMapping(raw))
455
- return refused(at, raw, `a mapping naming one of: ${choices}`);
456
- const entries = Object.entries(raw);
457
- const kinds = shape.kinds.filter((kind2) => entries.some(([name]) => name === kind2));
458
- const [kind] = kinds;
459
- if (kind === undefined)
460
- return refusedWith([`${at.label} names none of: ${choices}`]);
461
- if (kinds.length > 1) {
462
- const named2 = kinds.join(" and ");
463
- return refusedWith([`${at.label} names ${named2}, expected exactly one of: ${choices}`]);
464
- }
465
- const optionalKeys = shape.optionalFor(kind);
466
- const problems = [];
467
- const extras = [];
468
- const optional = new Map;
469
- let named;
470
- for (const [name, value] of entries) {
471
- if (name !== kind && !optionalKeys.includes(name)) {
472
- extras.push({ key: `${at.key}.${name}`, value });
473
- continue;
474
- }
475
- if (name !== kind && value === null)
476
- continue;
477
- const reading = itemText(value, below(at, `.${name}`));
478
- problems.push(...reading.problems);
479
- if (reading.value === undefined)
480
- continue;
481
- if (name === kind)
482
- named = reading.value;
483
- else
484
- optional.set(name, reading.value);
485
- }
486
- return problems.length > 0 || named === undefined ? { value: undefined, problems, extras } : { value: { kind, named, optional }, problems, extras };
487
- }
488
- var PREREQUISITE_KINDS = ["tool", "env", "service", "lsp"];
489
- var REQUIRED_ITEM_KEYS = [...PREREQUISITE_KINDS, "probe"];
490
- var OPTIONAL_ITEM_KEYS = [...REQUIRED_ITEM_KEYS, "reason"];
491
- var requiredPrerequisite = (raw, at) => {
492
- const shape = { kinds: PREREQUISITE_KINDS, optionalFor: () => ["probe"] };
493
- return mapReading(readKeyedItem(shape, raw, at), (item) => Object.freeze({
494
- kind: item.kind,
495
- name: item.named,
496
- probe: item.optional.get("probe") ?? null
497
- }));
498
- };
499
- var optionalPrerequisite = (raw, at) => {
500
- const shape = { kinds: PREREQUISITE_KINDS, optionalFor: () => ["probe", "reason"] };
501
- return mapReading(readKeyedItem(shape, raw, at), (item) => Object.freeze({
502
- kind: item.kind,
503
- name: item.named,
504
- probe: item.optional.get("probe") ?? null,
505
- reason: item.optional.get("reason") ?? null
506
- }));
507
- };
508
- var MODULE_SOURCE_KINDS = ["npm", "github", "path"];
509
- var MODULE_SOURCE_KEYS = [...MODULE_SOURCE_KINDS, "ref"];
510
- var moduleSource = (raw, at) => {
511
- const shape = {
512
- kinds: MODULE_SOURCE_KINDS,
513
- optionalFor: (kind) => kind === "github" ? ["ref"] : []
514
- };
515
- return mapReading(readKeyedItem(shape, raw, at), (item) => Object.freeze({
516
- kind: item.kind,
517
- location: item.named,
518
- ref: item.optional.get("ref") ?? null
519
- }));
520
- };
521
-
522
- // src/config-schema.ts
523
- import { join as join3 } from "path";
524
-
525
- // src/config-readers.ts
526
- import { isAbsolute, normalize } from "path";
527
- function mapOf(value, expected) {
528
- return (raw, at) => {
529
- if (!isMapping(raw)) {
530
- const problem = `${at.label} is ${describeValue(raw)}, expected a mapping of names to ${expected}`;
531
- return { value: undefined, problems: [problem], extras: [] };
532
- }
533
- const readings = Object.entries(raw).map(([name, entry]) => [
534
- name,
535
- name.trim() === "" ? {
536
- value: undefined,
537
- problems: [`${at.label} names ${JSON.stringify(name)}, expected a non-empty name`],
538
- extras: []
539
- } : value(entry, { label: `${at.label}.${name}`, key: `${at.key}.${name}` })
540
- ]);
541
- const problems = readings.flatMap(([, reading]) => reading.problems);
542
- const extras = readings.flatMap(([, reading]) => reading.extras);
543
- if (problems.length > 0)
544
- return { value: undefined, problems, extras };
545
- const read = new Map;
546
- for (const [name, reading] of readings) {
547
- if (reading.value !== undefined)
548
- read.set(name, reading.value);
549
- }
550
- return { value: read, problems, extras };
551
- };
552
- }
553
- var directory = text("a directory path");
554
- var trackerKind = text("a tracker kind name");
555
- var releaseFile = text("a file path");
556
- var tierPins = mapOf(tierPin, "false or a tier");
557
- var routeTable = mapOf(routeTarget, "false or an agent name");
558
- var RELEASE_STRATEGIES = ["semver-by-level"];
559
- var RELEASE_SETTLE_MODES = ["push", "pr"];
560
- var RELEASE_TAG_MODES = ["manual", "settle"];
561
- var VERSION_COLLISION_MODES = ["allow", "report", "ask", "refuse"];
562
- var releaseStrategy = oneOf(RELEASE_STRATEGIES);
563
- var releaseSettleMode = oneOf(RELEASE_SETTLE_MODES);
564
- var releaseTagMode = oneOf(RELEASE_TAG_MODES);
565
- var versionCollisionMode = oneOf(VERSION_COLLISION_MODES);
566
- var UNTRACKED_DIRECTORY = ".rafa";
567
- var FRAGMENTS_EXPECTED = `a relative directory path not under ${UNTRACKED_DIRECTORY}/`;
568
- function isUnderUntracked(path) {
569
- const [first] = normalize(path).split(/[\\/]/);
570
- return first === UNTRACKED_DIRECTORY;
571
- }
572
- var fragmentsDirectory = (raw, at) => {
573
- const reading = text(FRAGMENTS_EXPECTED)(raw, at);
574
- const path = reading.value;
575
- if (path === undefined || !isAbsolute(path) && !isUnderUntracked(path))
576
- return reading;
577
- const problem = `${at.label} is ${describeValue(raw)}, expected ${FRAGMENTS_EXPECTED}`;
578
- return { value: undefined, problems: [problem], extras: [] };
579
- };
580
-
581
- // src/config-schema-board-project.ts
582
- var BOARD_PROJECT_RETRIES_MIN = 1;
583
- var BOARD_PROJECT_RETRIES_MAX = 10;
584
- var BOARD_PROJECT_RETRY_WAIT_MIN_SECONDS = 1;
585
- var BOARD_PROJECT_RETRY_WAIT_MAX_SECONDS = 60;
586
- var BOARD_PROJECT_PROGRESS_MIN_SECONDS = 1;
587
- var BOARD_PROJECT_PROGRESS_MAX_SECONDS = 300;
588
- var BOARD_PROJECT_WRITE_BATCH_MIN = 1;
589
- var BOARD_PROJECT_WRITE_BATCH_MAX = 100;
590
- var BOARD_PROJECT_WRITE_PAUSE_MIN_MS = 0;
591
- var BOARD_PROJECT_WRITE_PAUSE_MAX_MS = 60000;
592
- function isWholeBetween(raw, min, max) {
593
- return typeof raw === "number" && Number.isSafeInteger(raw) && raw >= min && raw <= max;
594
- }
595
- function rangeWords(min, max) {
596
- return `a whole number from ${String(min)} to ${String(max)}`;
597
- }
598
- function wholeBetween(min, max) {
599
- return (raw, at) => isWholeBetween(raw, min, max) ? { value: raw, problems: [], extras: [] } : refused(at, raw, rangeWords(min, max));
600
- }
601
- function offOrWholeBetween(min, max) {
602
- return (raw, at) => raw === false || isWholeBetween(raw, min, max) ? { value: raw, problems: [], extras: [] } : refused(at, raw, `false or ${rangeWords(min, max)}`);
603
- }
604
- var boardProjectRetries = offOrWholeBetween(BOARD_PROJECT_RETRIES_MIN, BOARD_PROJECT_RETRIES_MAX);
605
- var boardProjectRetryWaitSeconds = wholeBetween(BOARD_PROJECT_RETRY_WAIT_MIN_SECONDS, BOARD_PROJECT_RETRY_WAIT_MAX_SECONDS);
606
- var boardProjectProgressSeconds = offOrWholeBetween(BOARD_PROJECT_PROGRESS_MIN_SECONDS, BOARD_PROJECT_PROGRESS_MAX_SECONDS);
607
- var boardProjectWriteBatchSize = wholeBetween(BOARD_PROJECT_WRITE_BATCH_MIN, BOARD_PROJECT_WRITE_BATCH_MAX);
608
- var boardProjectWritePauseMs = wholeBetween(BOARD_PROJECT_WRITE_PAUSE_MIN_MS, BOARD_PROJECT_WRITE_PAUSE_MAX_MS);
609
- var BOARD_PROJECT_LIMIT_DEFAULTS = Object.freeze({
610
- boardProjectRetries: 3,
611
- boardProjectRetryWaitSeconds: 2,
612
- boardProjectProgressSeconds: 10,
613
- boardProjectWriteBatchSize: 20,
614
- boardProjectWritePauseMs: 1000
615
- });
616
- var BOARD_PROJECT_LIMIT_SETTINGS = {
617
- boardProjectRetries: { key: "board.project.retries", read: boardProjectRetries, cli: false },
618
- boardProjectRetryWaitSeconds: {
619
- key: "board.project.retryWaitSeconds",
620
- read: boardProjectRetryWaitSeconds,
621
- cli: false
622
- },
623
- boardProjectProgressSeconds: {
624
- key: "board.project.progressSeconds",
625
- read: boardProjectProgressSeconds,
626
- cli: false
627
- },
628
- boardProjectWriteBatchSize: {
629
- key: "board.project.writeBatchSize",
630
- read: boardProjectWriteBatchSize,
631
- cli: false
632
- },
633
- boardProjectWritePauseMs: {
634
- key: "board.project.writePauseMs",
635
- read: boardProjectWritePauseMs,
636
- cli: false
637
- }
638
- };
639
-
640
- // src/errors/codes.ts
641
- var ERROR_LEVELS = Object.freeze(["error", "warn"]);
642
- var CODE_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*$/;
643
- var NEW_CONTEXT_LEAF = "new-context";
644
- var UNKNOWN_FAMILY = "unknown";
645
- var TEXT_FIELDS = ["description", "hint", "since"];
646
- var PREFIX = "error codes";
647
- function familyOf(code) {
648
- return code.slice(0, code.indexOf(":"));
649
- }
650
- function codeProblem(code) {
651
- if (!CODE_PATTERN.test(code))
652
- return `${JSON.stringify(code)} is not <family>:<leaf> in kebab-case`;
653
- const leaf = code.slice(code.indexOf(":") + 1);
654
- if (leaf === NEW_CONTEXT_LEAF || leaf === UNKNOWN_FAMILY) {
655
- return `${JSON.stringify(code)} uses the reserved leaf ${leaf}`;
656
- }
657
- if (familyOf(code) === UNKNOWN_FAMILY)
658
- return `${JSON.stringify(code)} uses the reserved family ${UNKNOWN_FAMILY}`;
659
- return null;
660
- }
661
- function entryProblem(entry, index) {
662
- const codeFault = codeProblem(entry.code);
663
- if (codeFault !== null)
664
- return `[${index}].code ${codeFault}`;
665
- const empty = TEXT_FIELDS.find((field) => (entry[field] ?? "").trim() === "");
666
- if (empty !== undefined)
667
- return `[${index}].${empty} is empty`;
668
- if (!ERROR_LEVELS.includes(entry.level)) {
669
- return `[${index}].level is ${JSON.stringify(entry.level)}, expected ${ERROR_LEVELS.join(" or ")}`;
670
- }
671
- return null;
672
- }
673
- function defineErrorCodes(entries) {
674
- entries.forEach((entry, index) => {
675
- const fault = entryProblem(entry, index);
676
- if (fault !== null)
677
- throw new TypeError(`${PREFIX}: ${fault}`);
678
- const first = entries.findIndex((other) => other.code === entry.code);
679
- if (first < index) {
680
- throw new TypeError(`${PREFIX}: ${JSON.stringify(entry.code)} is declared at [${first}] and [${index}]`);
681
- }
682
- });
683
- return Object.freeze(entries.map((entry) => Object.freeze({ ...entry })));
684
- }
685
-
686
- // src/errors/rafa-codes.ts
687
- var SEED = "#949";
688
- var FAMILY_DESCRIPTIONS = Object.freeze({
689
- api: "a call to a remote service",
690
- cli: "routing a command line and running the command",
691
- config: "reading the project or user config",
692
- fs: "reading or writing files",
693
- git: "running git",
694
- input: "a value given on the command line or in a file",
695
- skill: "a skill file's frontmatter",
696
- spawn: "starting a child process",
697
- tracker: "the issue tracker",
698
- tsc: "type-checking"
699
- });
700
- var RAFA_CODES = defineErrorCodes([
701
- { code: "cli:unknown-subject", description: "the first word names no subject", hint: "run rafa --help for the subjects", level: "error", since: SEED, legacy: "unknown_subject" },
702
- { code: "cli:missing-action", description: "a subject is named with no action after it", hint: "run rafa <subject> --help for its actions", level: "error", since: SEED, legacy: "missing_action" },
703
- { code: "cli:unknown-action", description: "the action names nothing the subject holds", hint: "run rafa <subject> --help for its actions", level: "error", since: SEED, legacy: "unknown_action" },
704
- { code: "cli:unknown-module", description: "a module command names a module that is not mounted", hint: "run rafa module list for the modules that are mounted", level: "error", since: SEED, legacy: "unknown_module" },
705
- { code: "cli:unexpected-version", description: "--version is typed beside a command, where it is read alone", hint: "run rafa --version alone, with no command beside it", level: "error", since: SEED, legacy: "unexpected_version" },
706
- { code: "cli:invalid-spec", description: "a command declares a shape the dispatcher refuses", hint: "fix the command declaration the message names", level: "error", since: SEED, legacy: "invalid_spec" },
707
- { code: "cli:no-project", description: "a command that needs a project ran outside one", hint: "run it inside a project, or run rafa init", level: "error", since: SEED, legacy: "no_project" },
708
- { code: "cli:command-exit", description: "a command refused and set its own exit code", hint: "read the message above it", level: "error", since: SEED, legacy: "command_exit" },
709
- { code: "cli:command-error", description: "a command threw an error it did not handle", hint: "file a bug with the stack", level: "error", since: SEED, legacy: "command_error" },
710
- { code: "cli:result-unwritable", description: "the command result cannot be written, as with data JSON refuses", hint: "file a bug: the command answered data that cannot be written as JSON", level: "error", since: SEED, legacy: "result_unwritable" },
711
- { code: "skill:missing-field", description: "a required frontmatter field is absent or empty", hint: "add the field the message names", level: "error", since: SEED, legacy: "missing-field" },
712
- { code: "skill:wrong-type", description: "a frontmatter field has a type the schema cannot read", hint: "write the field as the schema expects", level: "error", since: SEED, legacy: "wrong-type" },
713
- { code: "skill:invalid-name", description: "a skill name is not kebab-case", hint: "rename the skill in kebab-case", level: "error", since: SEED, legacy: "invalid-name" },
714
- { code: "skill:description-too-long", description: "the description is at or past its limit", hint: "shorten the description", level: "error", since: SEED, legacy: "description-too-long" },
715
- { code: "skill:listing-too-long", description: "description and when_to_use together pass the listing limit", hint: "shorten when_to_use", level: "error", since: SEED, legacy: "listing-too-long" },
716
- { code: "skill:unknown-stack", description: "a stack entry is outside the vocabulary", hint: "use a stack the schema lists", level: "error", since: SEED, legacy: "unknown-stack" },
717
- { code: "skill:unpaired-prevents", description: "prevents is set without signal, or signal without prevents", hint: "set both or neither", level: "error", since: SEED, legacy: "unpaired-prevents" },
718
- { code: "skill:unknown-signal", description: "signal holds a value the schema does not list", hint: "use a listed signal", level: "error", since: SEED, legacy: "unknown-signal" },
719
- { code: "skill:unusable-glob", description: "a paths glob gates on nothing it seems to gate on", hint: "rewrite the glob", level: "error", since: SEED, legacy: "unusable-glob" },
720
- { code: "skill:forbidden-field", description: "an instinct field appears on a skill", hint: "remove the field", level: "error", since: SEED, legacy: "forbidden-field" },
721
- { code: "skill:unknown-provenance", description: "provenance names neither first-party nor a known mapping", hint: "use first-party or a known mapping", level: "error", since: SEED, legacy: "unknown-provenance" },
722
- { code: "skill:invalid-reviewed", description: "provenance.reviewed is not who and a date", hint: "write <who> <YYYY-MM-DD>", level: "error", since: SEED, legacy: "invalid-reviewed" },
723
- { code: "skill:empty-failure-string", description: "a failure_strings entry is blank", hint: "remove the blank entry", level: "error", since: SEED, legacy: "empty-failure-string" },
724
- { code: "skill:short-failure-string", description: "a failure_strings entry is too short to name one failure", hint: "lengthen the entry", level: "warn", since: SEED, legacy: "short-failure-string" },
725
- { code: "git:no-identity", description: "a git commit runs with no author identity set, often in a scratch home", hint: "set user.name and user.email where the commit runs", level: "error", since: SEED },
726
- { code: "git:filesystem-boundary", description: "git prints an extra line when run outside a repository", hint: "run inside a repository, or read only the first line", level: "warn", since: SEED },
727
- { code: "tsc:tests-excluded", description: "type errors in test files stay hidden because tsc excludes tests", hint: "type-check the test files", level: "error", since: SEED },
728
- { code: "fs:gitignored-fixture", description: "a test reads a gitignored file a fresh checkout lacks", hint: "plant the file in the test, never read the live one", level: "error", since: SEED },
729
- { code: "fs:live-home", description: "a test reads the live home directory instead of a planted one", hint: "point HOME at a scratch folder in the test", level: "error", since: SEED },
730
- { code: "fs:not-found", description: "a file or folder the step needs is missing", hint: "create it, or fix the path", level: "error", since: SEED },
731
- { code: "input:missing-param", description: "a required value was not given", hint: "pass the value the message names", level: "error", since: SEED },
732
- { code: "input:invalid-value", description: "a given value is outside what the step accepts", hint: "pass one of the values the message lists", level: "error", since: SEED },
733
- { code: "api:http-status", description: "a remote service answered with an error status", hint: "read the status, then retry or fix the request", level: "error", since: SEED },
734
- { code: "config:invalid-value", description: "a config key holds a value its reader refuses", hint: "fix the key the message names", level: "error", since: SEED },
735
- { code: "tracker:unreachable", description: "the issue tracker could not be reached", hint: "check the network and gh auth status", level: "error", since: SEED },
736
- { code: "tracker:unavailable", description: "a tracker kind could not be made or failed its preflight, and the chain passed it over", hint: "read the reason given, or leave the fallback tracker in use", level: "warn", since: "#950" },
737
- { code: "spawn:nonzero-exit", description: "a child process exited nonzero", hint: "read the child's stderr above", level: "error", since: SEED }
738
- ]);
739
-
740
- // src/config-schema-errors.ts
741
- var ERROR_CODE_KEYS = ["code", "description", "hint", "level", "since"];
742
- var KNOWN_KEYS = new Set(ERROR_CODE_KEYS);
743
- var ERRORS_DEFAULTS = Object.freeze({ errorsCodes: Object.freeze([]) });
744
- var codeText = (raw, at) => {
745
- const read = text("a code, <family>:<leaf>")(raw, at);
746
- if (read.value === undefined)
747
- return read;
748
- const fault = codeProblem(read.value);
749
- return fault === null ? read : refused(at, raw, `a code: ${fault}`);
750
- };
751
- var EXPECTED = {
752
- description: "a one-line description, a non-empty string",
753
- hint: "the next action, a non-empty string",
754
- since: "the version or issue that added it, a non-empty string"
755
- };
756
- var NO_SINCE = { value: undefined, problems: [], extras: [] };
757
- var errorCodeEntry = (raw, at) => {
758
- if (!isMapping(raw))
759
- return refused(at, raw, `a mapping of: ${ERROR_CODE_KEYS.join(", ")}`);
760
- const code = codeText(raw.code, below(at, ".code"));
761
- const description = text(EXPECTED.description)(raw.description, below(at, ".description"));
762
- const hint = text(EXPECTED.hint)(raw.hint, below(at, ".hint"));
763
- const level = oneOf(ERROR_LEVELS)(raw.level, below(at, ".level"));
764
- const since = raw.since === undefined || raw.since === null ? NO_SINCE : text(EXPECTED.since)(raw.since, below(at, ".since"));
765
- const problems = [code, description, hint, level, since].flatMap((reading) => reading.problems);
766
- const extras = Object.entries(raw).filter(([key]) => !KNOWN_KEYS.has(key)).map(([key, value]) => ({ key: `${at.key}.${key}`, value }));
767
- if (code.value === undefined || description.value === undefined || hint.value === undefined || level.value === undefined || problems.length > 0)
768
- return { value: undefined, problems, extras };
769
- const entry = {
770
- code: code.value,
771
- description: description.value,
772
- hint: hint.value,
773
- level: level.value,
774
- ...since.value === undefined ? {} : { since: since.value }
775
- };
776
- return { value: Object.freeze(entry), problems: [], extras };
777
- };
778
- var errorCodes = (raw, at) => {
779
- const read = listOf(errorCodeEntry, "error code entries")(raw, at);
780
- const entries = read.value;
781
- if (entries === undefined)
782
- return read;
783
- const problems = entries.flatMap((entry, index) => {
784
- const label = `${below(at, `[${index}]`).label}.code is ${JSON.stringify(entry.code)}`;
785
- if (RAFA_CODES.some((own) => own.code === entry.code)) {
786
- return [`${label}, which rafa already declares; add a leaf of your own instead`];
787
- }
788
- const first = entries.findIndex((other) => other.code === entry.code);
789
- return first < index ? [`${label}, already given at [${first}]`] : [];
790
- });
791
- return problems.length === 0 ? read : { ...refusedWith(problems), extras: read.extras };
792
- };
793
- var ERRORS_SETTINGS = {
794
- errorsCodes: { key: "errors.codes", read: errorCodes, cli: false, itemKeys: ERROR_CODE_KEYS }
795
- };
796
-
797
- // src/config-schema-hub.ts
798
- var HUB_TIMEOUT_MIN_SECONDS = 1;
799
- var HUB_TIMEOUT_MAX_SECONDS = 30;
800
- var MS_PER_SECOND = 1000;
801
- var HUB_TIMEOUT = /^(\d+)s$/;
802
- var HUB_URL_PROTOCOLS = Object.freeze(["http:", "https:"]);
803
- var HUB_DEFAULTS = Object.freeze({
804
- hubUrl: null,
805
- hubTokenSecret: null,
806
- hubTimeout: "3s"
807
- });
808
- function hubTimeoutMs(raw) {
809
- const match = typeof raw === "string" ? HUB_TIMEOUT.exec(raw) : null;
810
- if (match === null)
811
- return null;
812
- const seconds = Number(match[1]);
813
- return seconds >= HUB_TIMEOUT_MIN_SECONDS && seconds <= HUB_TIMEOUT_MAX_SECONDS ? seconds * MS_PER_SECOND : null;
814
- }
815
- var hubTimeout = (raw, at) => hubTimeoutMs(raw) === null ? refused(at, raw, `a duration of whole seconds from ${String(HUB_TIMEOUT_MIN_SECONDS)}s to ${String(HUB_TIMEOUT_MAX_SECONDS)}s, such as 3s`) : { value: raw, problems: [], extras: [] };
816
- function isHubUrl(raw) {
817
- if (typeof raw !== "string" || raw.trim() !== raw)
818
- return false;
819
- const url = URL.parse(raw);
820
- return url !== null && HUB_URL_PROTOCOLS.includes(url.protocol) && url.username === "" && url.password === "";
821
- }
822
- var hubUrl = (raw, at) => isHubUrl(raw) ? { value: raw, problems: [], extras: [] } : refused(at, raw, "an http or https URL with no user name or password, such as https://hub.example.org");
823
- var HUB_SETTINGS = {
824
- hubUrl: { key: "hub.url", read: hubUrl, cli: false },
825
- hubTokenSecret: { key: "hub.tokenSecret", read: text("a secret store name"), cli: false },
826
- hubTimeout: { key: "hub.timeout", read: hubTimeout, cli: false }
827
- };
828
- var HUB_STRATEGY = "service";
829
- function labelOf(input) {
830
- const path = input.effortSyncSource === "user" ? input.paths.user : input.paths.file;
831
- return path ?? input.effortSyncSource;
832
- }
833
- function hubUrlProblems(input) {
834
- if (input.effortSync !== HUB_STRATEGY || input.hubUrl !== null)
835
- return [];
836
- return [
837
- `${labelOf(input)}: effort.sync is ${describeValue(HUB_STRATEGY)} and no config file names hub.url;` + " expected hub.url, the address of the hub, such as https://hub.example.org"
838
- ];
839
- }
840
- function hubContextOf(config) {
841
- const url = config.hubUrl ?? null;
842
- if (url === null)
843
- return;
844
- const timeout = config.hubTimeout ?? HUB_DEFAULTS.hubTimeout;
845
- const timeoutMs = hubTimeoutMs(timeout);
846
- if (timeoutMs === null) {
847
- throw new TypeError(`hub.timeout is ${describeValue(timeout)}, which no config accepts`);
848
- }
849
- return Object.freeze({ url, tokenSecret: config.hubTokenSecret ?? null, timeoutMs });
850
- }
851
-
852
- // src/bugs/places.ts
853
- var BUILT_IN_ENVIRONMENTS = ["local", "ci", "prod"];
854
-
855
- // src/bugs/title.ts
856
- var TITLE_VARIABLES = ["type", "project", "projectType", "module", "action", "summary"];
857
- var UNKNOWN_VALUE = "unknown";
858
- var DEFAULT_BUG_TITLE = "?(${module}?( - ${action}): )${summary}";
859
- var MODULE_SHAPE = "[a-z0-9][a-z0-9._-]*";
860
- var ACTION_SHAPE = "[a-z0-9][a-z0-9._ -]*";
861
- var SUMMARY = "summary";
862
- var VARIABLE_SHAPES = { module: MODULE_SHAPE, action: ACTION_SHAPE };
863
- var FREE_SHAPE = ".+";
864
- var GROUP_OPEN = "?(";
865
- var GROUP_CLOSE = ")";
866
- var VARIABLE_OPEN = "${";
867
- var VARIABLE_CLOSE = "}";
868
-
869
- class TitleTemplateError extends Error {
870
- constructor(message) {
871
- super(message);
872
- this.name = "TitleTemplateError";
873
- }
874
- }
875
- function isPrintable(value) {
876
- return typeof value === "string" && value.trim() !== "" && value.trim() !== UNKNOWN_VALUE;
877
- }
878
- function isTitleVariable(name) {
879
- return TITLE_VARIABLES.includes(name);
880
- }
881
- function readVariable(template, at) {
882
- const close = template.indexOf(VARIABLE_CLOSE, at);
883
- if (close === -1)
884
- throw new TitleTemplateError(`"${VARIABLE_OPEN}" at ${at} is never closed`);
885
- const name = template.slice(at + VARIABLE_OPEN.length, close);
886
- if (!isTitleVariable(name)) {
887
- throw new TitleTemplateError(`\${${name}} names no variable, expected one of: ${TITLE_VARIABLES.join(", ")}`);
888
- }
889
- return { node: { kind: "variable", name }, next: close + VARIABLE_CLOSE.length };
890
- }
891
- function readNodes(template, start, depth) {
892
- const nodes = [];
893
- let text2 = "";
894
- let at = start;
895
- const flush = () => {
896
- if (text2 !== "")
897
- nodes.push({ kind: "text", text: text2 });
898
- text2 = "";
899
- };
900
- while (at < template.length) {
901
- if (template.startsWith(GROUP_OPEN, at)) {
902
- flush();
903
- const inner = readNodes(template, at + GROUP_OPEN.length, depth + 1);
904
- nodes.push({ kind: "group", nodes: inner.nodes });
905
- at = inner.next;
906
- } else if (depth > 0 && template[at] === GROUP_CLOSE) {
907
- flush();
908
- return { nodes, next: at + GROUP_CLOSE.length };
909
- } else if (template.startsWith(VARIABLE_OPEN, at)) {
910
- flush();
911
- const variable = readVariable(template, at);
912
- nodes.push(variable.node);
913
- at = variable.next;
914
- } else {
915
- text2 += template[at];
916
- at += 1;
917
- }
918
- }
919
- if (depth > 0)
920
- throw new TitleTemplateError(`a "${GROUP_OPEN}" group is never closed`);
921
- flush();
922
- return { nodes, next: at };
923
- }
924
- function parseTitleTemplate(template) {
925
- return readNodes(template, 0, 0).nodes;
926
- }
927
- function variablesOf(nodes) {
928
- return nodes.flatMap((node) => {
929
- if (node.kind === "variable")
930
- return [node.name];
931
- return node.kind === "group" ? variablesOf(node.nodes) : [];
932
- });
933
- }
934
- function groupShows(nodes, values) {
935
- return nodes.every((node) => node.kind !== "variable" || isPrintable(values[node.name]));
936
- }
937
- function printed(values, name) {
938
- const value = values[name];
939
- return isPrintable(value) ? value.trim() : "";
940
- }
941
- function shownValues(nodes, values) {
942
- return Object.fromEntries(nodes.flatMap((node) => {
943
- if (node.kind === "text")
944
- return [];
945
- if (node.kind === "variable") {
946
- return printed(values, node.name) === "" ? [] : [[node.name, printed(values, node.name)]];
947
- }
948
- return groupShows(node.nodes, values) ? Object.entries(shownValues(node.nodes, values)) : [];
949
- }));
950
- }
951
- function renderNodes(nodes, values) {
952
- return nodes.map((node) => {
953
- if (node.kind === "text")
954
- return node.text;
955
- if (node.kind === "variable")
956
- return printed(values, node.name);
957
- return groupShows(node.nodes, values) ? renderNodes(node.nodes, values) : "";
958
- }).join("");
959
- }
960
- function renderTitle(template, values) {
961
- return renderNodes(parseTitleTemplate(template), values).trim();
962
- }
963
- function escapeLiteral(text2) {
964
- return text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
965
- }
966
- function patternOf(nodes, last) {
967
- return nodes.map((node) => {
968
- if (node.kind === "text")
969
- return escapeLiteral(node.text);
970
- if (node.kind === "group")
971
- return `(?:${patternOf(node.nodes, last)})?`;
972
- const shape = node.name === last ? FREE_SHAPE : `${VARIABLE_SHAPES[node.name] ?? FREE_SHAPE}?`;
973
- return `(?<${node.name}>${shape})`;
974
- }).join("");
975
- }
976
- function parseTitle(template, title) {
977
- const nodes = parseTitleTemplate(template);
978
- const names = variablesOf(nodes);
979
- const match = new RegExp(`^${patternOf(nodes, names.at(-1))}$`, "u").exec(title.trim());
980
- if (match === null)
981
- return null;
982
- return Object.fromEntries(names.flatMap((name) => {
983
- const value = match.groups?.[name];
984
- return value === undefined ? [] : [[name, value]];
985
- }));
986
- }
987
- function titleRoundTrips(template, values) {
988
- const nodes = parseTitleTemplate(template);
989
- const title = renderNodes(nodes, values).trim();
990
- if (title === "")
991
- return false;
992
- const read = parseTitle(template, title) ?? {};
993
- const shown = shownValues(nodes, values);
994
- return variablesOf(nodes).every((name) => (read[name] ?? null) === (shown[name] ?? null));
995
- }
996
- var SAMPLE = {
997
- type: "bug",
998
- project: "api",
999
- projectType: "cli",
1000
- module: "loop",
1001
- action: "start",
1002
- summary: "Does not find the branch: again"
1003
- };
1004
- function samplesOf(names) {
1005
- const optional = names.filter((name) => name !== SUMMARY);
1006
- const subsets = optional.reduce((found, name) => [...found, ...found.map((subset) => [...subset, name])], [[]]);
1007
- return subsets.map((subset) => Object.fromEntries([...subset, SUMMARY].map((name) => [name, SAMPLE[name]])));
1008
- }
1009
- function nodesProblem(template, nodes) {
1010
- const names = variablesOf(nodes);
1011
- const twice = names.find((name, index) => names.indexOf(name) !== index);
1012
- if (twice !== undefined)
1013
- return `\${${twice}} appears twice`;
1014
- if (names.at(-1) !== SUMMARY)
1015
- return `\${${SUMMARY}} must be the last variable`;
1016
- if (!nodes.some((node) => node.kind === "variable" && node.name === SUMMARY)) {
1017
- return `\${${SUMMARY}} must stand outside every ?( ) group`;
1018
- }
1019
- const failed = samplesOf(names).find((sample) => !titleRoundTrips(template, sample));
1020
- return failed === undefined ? null : `the title ${JSON.stringify(renderTitle(template, failed))} does not read back as the fields it was rendered from`;
1021
- }
1022
- function templateProblem(template) {
1023
- try {
1024
- return nodesProblem(template, parseTitleTemplate(template));
1025
- } catch (error) {
1026
- if (error instanceof TitleTemplateError)
1027
- return error.message;
1028
- throw error;
1029
- }
1030
- }
1031
-
1032
- // src/config-schema-issues.ts
1033
- var SUMMARY_MAX_DEFAULT = 80;
1034
- var SUMMARY_MAX_FLOOR = 20;
1035
- var SUMMARY_MAX_CEILING = 120;
1036
- var TEMPLATE_EXPECTED = "a title template";
1037
- var PLACE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
1038
- var MODULE = new RegExp(`^${MODULE_SHAPE}$`);
1039
- var ACTION = new RegExp(`^${ACTION_SHAPE}$`);
1040
- function accepted2(value) {
1041
- return { value, problems: [], extras: [] };
1042
- }
1043
- var titleTemplate = (raw, at) => {
1044
- if (typeof raw !== "string" || raw.trim() === "")
1045
- return refused(at, raw, TEMPLATE_EXPECTED);
1046
- const problem = templateProblem(raw);
1047
- return problem === null ? accepted2(raw) : refused(at, raw, `${TEMPLATE_EXPECTED}: ${problem}`);
1048
- };
1049
- var summaryMax = (raw, at) => typeof raw === "number" && Number.isSafeInteger(raw) && raw >= SUMMARY_MAX_FLOOR && raw <= SUMMARY_MAX_CEILING ? accepted2(raw) : refused(at, raw, `a whole number from ${SUMMARY_MAX_FLOOR} to ${SUMMARY_MAX_CEILING}`);
1050
- function nameOf(shape, reserved, expected) {
1051
- return (raw, at) => typeof raw === "string" && shape.test(raw) && !reserved.includes(raw) ? accepted2(raw) : refused(at, raw, expected);
1052
- }
1053
- var place = nameOf(PLACE, [...BUILT_IN_ENVIRONMENTS, UNKNOWN_VALUE], `a place name in lower-case kebab-case, other than ${BUILT_IN_ENVIRONMENTS.join(", ")} and ${UNKNOWN_VALUE}`);
1054
- var moduleName = nameOf(MODULE, [UNKNOWN_VALUE], `a module name in lower-case letters, digits, ".", "_" and "-", other than ${UNKNOWN_VALUE}`);
1055
- var actionName = nameOf(ACTION, [UNKNOWN_VALUE], `an action in lower-case letters, digits, ".", "_", "-" and spaces, other than ${UNKNOWN_VALUE}`);
1056
- var ISSUES_DEFAULTS = Object.freeze({
1057
- issuesTitleBug: DEFAULT_BUG_TITLE,
1058
- issuesSummaryMax: SUMMARY_MAX_DEFAULT,
1059
- issuesEnvironments: Object.freeze([]),
1060
- issuesVocabularyModules: Object.freeze([]),
1061
- issuesVocabularyActions: Object.freeze([])
1062
- });
1063
- var ISSUES_SETTINGS = {
1064
- issuesTitleBug: { key: "issues.titles.bug", read: titleTemplate, cli: false },
1065
- issuesSummaryMax: { key: "issues.titles.summaryMax", read: summaryMax, cli: false },
1066
- issuesEnvironments: { key: "issues.environments", read: listOf(place, "place names"), cli: false },
1067
- issuesVocabularyModules: { key: "issues.vocabulary.modules", read: listOf(moduleName, "module names"), cli: false },
1068
- issuesVocabularyActions: { key: "issues.vocabulary.actions", read: listOf(actionName, "actions"), cli: false }
1069
- };
1070
-
1071
- // src/adapters/logger/settings.ts
1072
- import { AsyncLocalStorage } from "async_hooks";
1073
- var LOG_LEVELS = Object.freeze(["error", "warn", "debug"]);
1074
- var LOGGER_THEMES = Object.freeze(["default", "plain"]);
1075
- var DEFAULT_LOGGER_KIND = "console";
1076
- var DEBUG_VERBOSITY = 2;
1077
- var API_VERBOSITY = 3;
1078
- var DEFAULT_LOGGER_SETTINGS = Object.freeze({
1079
- kind: DEFAULT_LOGGER_KIND,
1080
- level: "warn",
1081
- theme: "default",
1082
- modules: new Map,
1083
- callSite: false,
1084
- api: false
1085
- });
1086
- var scopes = new AsyncLocalStorage;
1087
- var moduleLevel = { settings: DEFAULT_LOGGER_SETTINGS };
1088
- function settingsOf(settings) {
1089
- return settings === null ? DEFAULT_LOGGER_SETTINGS : Object.freeze({ ...settings, modules: new Map(settings.modules) });
1090
- }
1091
- function cellHere() {
1092
- return scopes.getStore() ?? moduleLevel;
1093
- }
1094
- function setActiveLoggerSettings(settings) {
1095
- cellHere().settings = settingsOf(settings);
1096
- }
1097
- function activeLoggerSettings() {
1098
- return cellHere().settings;
1099
- }
1100
- function levelEnabled(level, module, settings, verbosity) {
1101
- if (level === "api")
1102
- return settings.api || verbosity >= API_VERBOSITY;
1103
- if (level === "debug" && verbosity >= DEBUG_VERBOSITY)
1104
- return true;
1105
- const threshold = (module === undefined ? undefined : settings.modules.get(module)) ?? settings.level;
1106
- return LOG_LEVELS.indexOf(level) <= LOG_LEVELS.indexOf(threshold);
1107
- }
1108
-
1109
- // src/config-schema-logger.ts
1110
- var MODULE_LEVEL_KEY = "level";
1111
- var MODULE_EXPECTED = `a mapping holding ${MODULE_LEVEL_KEY}`;
1112
- var moduleLevel2 = (raw, at) => {
1113
- if (!isMapping(raw))
1114
- return refused(at, raw, MODULE_EXPECTED);
1115
- const level = oneOf(LOG_LEVELS)(raw[MODULE_LEVEL_KEY], below(at, `.${MODULE_LEVEL_KEY}`));
1116
- const extras = Object.entries(raw).filter(([key]) => key !== MODULE_LEVEL_KEY).map(([key, value]) => ({ key: `${at.key}.${key}`, value }));
1117
- return { ...level, extras };
1118
- };
1119
- var LOGGER_DEFAULTS = Object.freeze({
1120
- loggerKind: DEFAULT_LOGGER_KIND,
1121
- loggerLevel: "warn",
1122
- loggerTheme: "default",
1123
- loggerModules: new Map,
1124
- loggerCallSite: false,
1125
- loggerApi: false
1126
- });
1127
- var LOGGER_SETTINGS = {
1128
- loggerKind: { key: "logger.kind", read: text("an adapter kind, such as console"), cli: false },
1129
- loggerLevel: { key: "logger.level", read: oneOf(LOG_LEVELS), cli: false },
1130
- loggerTheme: { key: "logger.theme", read: oneOf(LOGGER_THEMES), cli: false },
1131
- loggerModules: { key: "logger.modules", read: mapOf(moduleLevel2, MODULE_EXPECTED), cli: false },
1132
- loggerCallSite: { key: "logger.callSite", read: flag, cli: false },
1133
- loggerApi: { key: "logger.api", read: flag, cli: false }
1134
- };
1135
- function loggerSettingsOf(config) {
1136
- return {
1137
- kind: config.loggerKind,
1138
- level: config.loggerLevel,
1139
- theme: config.loggerTheme,
1140
- modules: config.loggerModules,
1141
- callSite: config.loggerCallSite,
1142
- api: config.loggerApi
1143
- };
1144
- }
1145
-
1146
- // src/config-schema-loop-continue.ts
1147
- import { join as join2 } from "path";
1148
- var RETRIES_ON_CONTINUE_MIN = 1;
1149
- var RETRIES_ON_CONTINUE_MAX = 3;
1150
- var FORCE_WRAP_UP_MAX_NEW_FAILURES_MIN = 1;
1151
- var FORCE_WRAP_UP_MAX_NEW_FAILURES_MAX = 50;
1152
- var CONTINUE_CRITERIA_MODES = ["extend", "replace"];
1153
- function countOrFalse(min, max) {
1154
- const expected = `false or a whole number from ${String(min)} to ${String(max)}`;
1155
- return (raw, at) => raw === false || typeof raw === "number" && Number.isSafeInteger(raw) && raw >= min && raw <= max ? { value: raw, problems: [], extras: [] } : refused(at, raw, expected);
1156
- }
1157
- var retriesOnContinue = countOrFalse(RETRIES_ON_CONTINUE_MIN, RETRIES_ON_CONTINUE_MAX);
1158
- var forceWrapUpMaxNewFailures = countOrFalse(FORCE_WRAP_UP_MAX_NEW_FAILURES_MIN, FORCE_WRAP_UP_MAX_NEW_FAILURES_MAX);
1159
- var continueCriteria = text("a file path");
1160
- var continueCriteriaMode = oneOf(CONTINUE_CRITERIA_MODES);
1161
- var LOOP_CONTINUE_DEFAULTS = Object.freeze({
1162
- loopRetriesOnContinue: 1,
1163
- loopContinueCriteria: join2(".rafa", "continue-criteria.md"),
1164
- loopContinueCriteriaMode: "extend",
1165
- loopForceWrapUpMaxNewFailures: false
1166
- });
1167
- var LOOP_CONTINUE_SETTINGS = {
1168
- loopRetriesOnContinue: { key: "loop.retriesOnContinue", read: retriesOnContinue, cli: false },
1169
- loopContinueCriteria: { key: "loop.continue.criteria", read: continueCriteria, cli: false },
1170
- loopContinueCriteriaMode: { key: "loop.continue.criteriaMode", read: continueCriteriaMode, cli: false },
1171
- loopForceWrapUpMaxNewFailures: {
1172
- key: "loop.forceWrapUp.maxNewFailures",
1173
- read: forceWrapUpMaxNewFailures,
1174
- cli: false
1175
- }
1176
- };
1177
-
1178
- // src/config-schema-loop-retries.ts
1179
- var LOOP_RETRIES_MIN = 1;
1180
- var LOOP_RETRIES_MAX = 3;
1181
- function isLoopRetryCount(raw) {
1182
- return typeof raw === "number" && Number.isSafeInteger(raw) && raw >= LOOP_RETRIES_MIN && raw <= LOOP_RETRIES_MAX;
1183
- }
1184
- var loopRetries = (raw, at) => raw === false || isLoopRetryCount(raw) ? { value: raw, problems: [], extras: [] } : refused(at, raw, `false or a whole number from ${String(LOOP_RETRIES_MIN)} to ${String(LOOP_RETRIES_MAX)}`);
1185
- var LOOP_RETRIES_DEFAULTS = Object.freeze({
1186
- loopRetries: false
1187
- });
1188
- var LOOP_RETRIES_SETTINGS = {
1189
- loopRetries: { key: "loop.retries", read: loopRetries, cli: false }
1190
- };
1191
-
1192
- // src/config-schema-release.ts
1193
- var PR_DEFAULTS = Object.freeze({
1194
- prProvider: null,
1195
- prMergeMethod: "squash",
1196
- prBase: null,
1197
- prResolveBudget: 2,
1198
- prVersionCollision: "report"
1199
- });
1200
- var RELEASE_DEFAULTS = Object.freeze({
1201
- releaseEnabled: RELEASE_AUTO,
1202
- releaseVersionFile: "package.json",
1203
- releaseChangelog: "CHANGELOG.md",
1204
- releaseHeading: "## {version} \u2014 {date}, {title}",
1205
- releaseFragments: ".changes",
1206
- releaseStrategy: "semver-by-level",
1207
- releaseSettle: "push",
1208
- releaseTag: "manual",
1209
- releasePublishCommand: "npm publish"
1210
- });
1211
- var DANGEROUS_RELEASE_DEFAULTS = Object.freeze({
1212
- dangerousAcceptVersionCollision: false
1213
- });
1214
- var PR_SETTINGS = {
1215
- prProvider: { key: "pr.provider", read: oneOf(PR_PROVIDERS), cli: false },
1216
- prMergeMethod: { key: "pr.mergeMethod", read: mergeMethod, cli: false },
1217
- prBase: { key: "pr.base", read: text("a branch name"), cli: false },
1218
- prResolveBudget: { key: "pr.resolveBudget", read: usdAmount, cli: false },
1219
- prVersionCollision: {
1220
- key: "pr.versionCollision",
1221
- read: versionCollisionMode,
1222
- cli: false
1223
- }
1224
- };
1225
- var RELEASE_SETTINGS = {
1226
- releaseEnabled: { key: "release.enabled", read: releaseEnabled, cli: false },
1227
- releaseVersionFile: {
1228
- key: "release.versionFile",
1229
- read: releaseFile,
1230
- cli: false
1231
- },
1232
- releaseChangelog: { key: "release.changelog", read: releaseFile, cli: false },
1233
- releaseHeading: {
1234
- key: "release.heading",
1235
- read: text("a changelog heading template"),
1236
- cli: false
1237
- },
1238
- releaseFragments: {
1239
- key: "release.fragments",
1240
- read: fragmentsDirectory,
1241
- cli: false
1242
- },
1243
- releaseStrategy: { key: "release.strategy", read: releaseStrategy, cli: false },
1244
- releaseSettle: { key: "release.settle", read: releaseSettleMode, cli: false },
1245
- releaseTag: { key: "release.tag", read: releaseTagMode, cli: false },
1246
- releasePublishCommand: {
1247
- key: "release.publishCommand",
1248
- read: text("a publish command"),
1249
- cli: false
1250
- }
1251
- };
1252
- var DANGEROUS_RELEASE_SETTINGS = {
1253
- dangerousAcceptVersionCollision: {
1254
- key: "dangerous.acceptVersionCollision",
1255
- read: flag,
1256
- cli: false
1257
- }
1258
- };
1259
-
1260
- // src/config-schema-tests.ts
1261
- import { isAbsolute as isAbsolute2 } from "path";
1262
- var GLOB_PATTERN = "a glob pattern relative to the repository root";
1263
- var repoGlob = (raw, at) => typeof raw === "string" && isAbsolute2(raw) ? refused(at, raw, GLOB_PATTERN) : text(GLOB_PATTERN)(raw, at);
1264
- var globList = listOf(repoGlob, "glob patterns");
1265
- var TESTS_DEFAULTS = Object.freeze({
1266
- testsFullSuiteTriggers: Object.freeze([
1267
- "bunfig.toml",
1268
- "tsconfig*.json",
1269
- "package.json",
1270
- "bun.lock",
1271
- "bun.lockb"
1272
- ]),
1273
- testsIntegration: Object.freeze([
1274
- "**/*-integration.test.ts",
1275
- "**/*.integration.test.ts",
1276
- "**/*-spawned*.test.ts",
1277
- "**/*-cli.test.ts"
1278
- ]),
1279
- testsAlwaysRun: Object.freeze(["src/**/*.sweep.test.ts"]),
1280
- testsRetakeRedAlone: true
1281
- });
1282
- var TESTS_SETTINGS = {
1283
- testsFullSuiteTriggers: {
1284
- key: "tests.fullSuiteTriggers",
1285
- read: globList,
1286
- cli: false
1287
- },
1288
- testsIntegration: { key: "tests.integration", read: globList, cli: false },
1289
- testsAlwaysRun: { key: "tests.alwaysRun", read: globList, cli: false },
1290
- testsRetakeRedAlone: { key: "tests.retakeRedAlone", read: flag, cli: false }
1291
- };
1292
-
1293
- // src/config-schema-triage.ts
1294
- var SIMILARITY_THRESHOLD_MAX = 1;
1295
- var SIMILARITY_CANDIDATES_MIN = 1;
1296
- var SIMILARITY_CANDIDATES_MAX = 10;
1297
- function isThreshold(raw) {
1298
- return typeof raw === "number" && Number.isFinite(raw) && raw > 0 && raw <= SIMILARITY_THRESHOLD_MAX;
1299
- }
1300
- function isCandidateCount(raw) {
1301
- return typeof raw === "number" && Number.isSafeInteger(raw) && raw >= SIMILARITY_CANDIDATES_MIN && raw <= SIMILARITY_CANDIDATES_MAX;
1302
- }
1303
- var similarityThreshold = (raw, at) => raw === false || isThreshold(raw) ? { value: raw, problems: [], extras: [] } : refused(at, raw, `false or a number above 0 and at most ${String(SIMILARITY_THRESHOLD_MAX)}`);
1304
- var similarityCandidates = (raw, at) => isCandidateCount(raw) ? { value: raw, problems: [], extras: [] } : refused(at, raw, `a whole number from ${String(SIMILARITY_CANDIDATES_MIN)} to ${String(SIMILARITY_CANDIDATES_MAX)}`);
1305
- var TRIAGE_DEFAULTS = Object.freeze({
1306
- triageSimilarityThreshold: 0.3,
1307
- triageSimilarityCandidates: 3
1308
- });
1309
- var TRIAGE_SETTINGS = {
1310
- triageSimilarityThreshold: {
1311
- key: "triage.similarity.threshold",
1312
- read: similarityThreshold,
1313
- cli: false
1314
- },
1315
- triageSimilarityCandidates: {
1316
- key: "triage.similarity.candidates",
1317
- read: similarityCandidates,
1318
- cli: false
1319
- }
1320
- };
1321
-
1322
- // src/config-schema-wrap-up.ts
1323
- var WRAP_UP_RETRIES_MIN = 1;
1324
- var WRAP_UP_RETRIES_MAX = 3;
1325
- function isRetryCount(raw) {
1326
- return typeof raw === "number" && Number.isSafeInteger(raw) && raw >= WRAP_UP_RETRIES_MIN && raw <= WRAP_UP_RETRIES_MAX;
1327
- }
1328
- var wrapUpRetries = (raw, at) => raw === false || isRetryCount(raw) ? { value: raw, problems: [], extras: [] } : refused(at, raw, `false or a whole number from ${String(WRAP_UP_RETRIES_MIN)} to ${String(WRAP_UP_RETRIES_MAX)}`);
1329
- var WRAP_UP_DEFAULTS = Object.freeze({
1330
- loopWrapUpRetries: 1
1331
- });
1332
- var WRAP_UP_SETTINGS = {
1333
- loopWrapUpRetries: { key: "loop.wrapUp.retries", read: wrapUpRetries, cli: false }
1334
- };
1335
-
1336
- // src/tiers/resolve.ts
1337
- import { readFileSync } from "fs";
1338
- var VENDORED_HEADER = /^<!-- vendored by rafa from .* on \d{4}-\d{2}-\d{2} -->$/;
1339
- var CLAUDE_SKILL_ORDER = ["user", "project", "rafa"];
1340
- var KIND_ORDER = ["skill", "agent"];
1341
- var PLAIN_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
1342
- function loadedTiers(settings) {
1343
- return SKILL_TIERS.filter((tier) => tier === "project" || tier === "rafa" && settings.tiersRafa !== "off" || tier === "user" && settings.settingSources.includes("user"));
1344
- }
1345
- function pinKey(kind) {
1346
- return kind === "skill" ? "tiers.skills" : "tiers.agents";
1347
- }
1348
- function pinLine(kind, name, tier) {
1349
- const key = PLAIN_NAME.test(name) ? name : JSON.stringify(name);
1350
- return `${pinKey(kind)}: { ${key}: ${tier} }`;
1351
- }
1352
- function collisionMessage(collision) {
1353
- const places = collision.holders.map((holder) => `${holder.source} ${holder.path}`).join(" and ");
1354
- const held = `${collision.kind} ${collision.name} is held by ${collision.holders.length} loaded tiers ` + `with different contents: ${places}; `;
1355
- const tier = collision.setAsidePin;
1356
- if (tier === null)
1357
- return `${held}pin the tier that serves it: ${collision.pinLine}`;
1358
- const over = collision.outrankedBy;
1359
- const tiers = over.map((holder) => holder.source).join(" and ");
1360
- const copies = over.length === 1 ? "copy" : "copies";
1361
- const paths = over.map((holder) => holder.path).join(" and ");
1362
- return `${held}${pinLine(collision.kind, collision.name, tier)} has no effect, because Claude Code loads ` + `the ${tiers} ${copies} over the ${tier} copy: pin the copy it loads (${collision.pinLine}), ` + `or delete or rename ${paths} to let the ${tier} copy serve`;
1363
- }
1364
- function readItemBytes(path) {
1365
- try {
1366
- return readFileSync(path);
1367
- } catch {
1368
- return null;
1369
- }
1370
- }
1371
- function comparableText(bytes) {
1372
- return Buffer.from(bytes).toString("latin1").split(`
1373
- `).filter((line) => !VENDORED_HEADER.test(line)).join(`
1374
- `);
1375
- }
1376
- function tierIndex(row) {
1377
- return SKILL_TIERS.indexOf(row.source);
1378
- }
1379
- function groupByName(rows) {
1380
- const groups = new Map;
1381
- for (const row of rows) {
1382
- if (!isSkillTier(row.source))
1383
- continue;
1384
- const key = `${row.kind}\x00${row.name}`;
1385
- groups.set(key, [...groups.get(key) ?? [], row]);
1386
- }
1387
- return new Map([...groups].map(([key, group]) => [
1388
- key,
1389
- [...group].sort((a, b) => tierIndex(a) - tierIndex(b))
1390
- ]));
1391
- }
1392
- function loadedHolders(holders, tiers) {
1393
- return tiers.flatMap((tier) => {
1394
- const holder = holders.find((row) => row.source === tier);
1395
- return holder === undefined ? [] : [holder];
1396
- });
1397
- }
1398
- function sameItemGroups(holders, read) {
1399
- if (holders.length < 2)
1400
- return [holders];
1401
- const groups = [];
1402
- for (const row of holders) {
1403
- const bytes = read(row.path);
1404
- const text2 = bytes === null ? null : comparableText(bytes);
1405
- const group = text2 === null ? undefined : groups.find((candidate) => candidate.text === text2);
1406
- if (group === undefined)
1407
- groups.push({ text: text2, rows: [row] });
1408
- else
1409
- group.rows.push(row);
1410
- }
1411
- return groups.map((group) => group.rows);
1412
- }
1413
- function served(base, winner, decidedBy, groups) {
1414
- const same = groups.find((group) => group.includes(winner)) ?? [winner];
1415
- return {
1416
- ...base,
1417
- state: "served",
1418
- winner,
1419
- decidedBy,
1420
- copies: same.filter((row) => row !== winner),
1421
- overridden: base.loaded.filter((row) => !same.includes(row))
1422
- };
1423
- }
1424
- function claudeRank(row) {
1425
- return CLAUDE_SKILL_ORDER.indexOf(row.source);
1426
- }
1427
- function outrankingHolders(pinned, loaded, groups) {
1428
- if (pinned.kind !== "skill")
1429
- return [];
1430
- const same = groups.find((group) => group.includes(pinned)) ?? [pinned];
1431
- return loaded.filter((row) => claudeRank(row) < claudeRank(pinned) && !same.includes(row)).sort((a, b) => claudeRank(a) - claudeRank(b));
1432
- }
1433
- function workingPinTier(kind, loaded) {
1434
- const [first] = kind === "skill" ? [...loaded].sort((a, b) => claudeRank(a) - claudeRank(b)) : loaded;
1435
- return first !== undefined && isSkillTier(first.source) ? first.source : "project";
1436
- }
1437
- function resolveItem(holders, settings, tiers, read) {
1438
- const [first] = holders;
1439
- if (first === undefined)
1440
- throw new Error("resolveItem: a name with no holder");
1441
- const { kind, name } = first;
1442
- const pins = kind === "skill" ? settings.tiersSkills : settings.tiersAgents;
1443
- const base = {
1444
- kind,
1445
- name,
1446
- holders,
1447
- loaded: loadedHolders(holders, tiers),
1448
- pin: pins.get(name) ?? null
1449
- };
1450
- if (base.pin === false)
1451
- return { ...base, state: "off" };
1452
- const [nearest] = base.loaded;
1453
- if (nearest === undefined)
1454
- return { ...base, state: "unloaded" };
1455
- const groups = sameItemGroups(base.loaded, read);
1456
- const pinned = base.loaded.find((row) => row.source === base.pin);
1457
- const outrankedBy = pinned === undefined ? [] : outrankingHolders(pinned, base.loaded, groups);
1458
- if (pinned !== undefined && outrankedBy.length === 0)
1459
- return served(base, pinned, "pin", groups);
1460
- if (groups.length === 1)
1461
- return served(base, nearest, "order", groups);
1462
- const distinct = groups.flatMap((group) => group.slice(0, 1));
1463
- const setAsidePin = pinned !== undefined && isSkillTier(pinned.source) ? pinned.source : null;
1464
- return {
1465
- ...base,
1466
- state: "collision",
1467
- collision: {
1468
- kind,
1469
- name,
1470
- holders: distinct,
1471
- pinLine: pinLine(kind, name, workingPinTier(kind, base.loaded)),
1472
- setAsidePin,
1473
- outrankedBy
1474
- }
1475
- };
1476
- }
1477
- function resolveTiers(rows, settings, read) {
1478
- const tiers = loadedTiers(settings);
1479
- const items = [...groupByName(rows).values()].map((holders) => resolveItem(holders, settings, tiers, read)).sort((a, b) => KIND_ORDER.indexOf(a.kind) - KIND_ORDER.indexOf(b.kind) || a.name.localeCompare(b.name));
1480
- return {
1481
- loadedTiers: tiers,
1482
- items,
1483
- collisions: items.flatMap((item) => item.state === "collision" ? [item.collision] : [])
1484
- };
1485
- }
1486
- function findTierItem(resolution, kind, name) {
1487
- return resolution.items.find((item) => item.kind === kind && item.name === name);
1488
- }
1489
-
1490
- // src/tiers/routing.ts
1491
- var DEFAULT_ROUTES = Object.freeze([
1492
- Object.freeze(["prose", "doc-updater"]),
1493
- Object.freeze(["tests", "tdd-guide"]),
1494
- Object.freeze(["repair", "build-error-resolver"]),
1495
- Object.freeze(["review", "code-reviewer"]),
1496
- Object.freeze(["implementation", "loop-implementer"])
1497
- ]);
1498
- var DEFAULT_ROUTING = new Map(DEFAULT_ROUTES);
1499
-
1500
- // src/config-schema.ts
1501
- var CONFIG_FILE = join3(".rafa", "config.yaml");
1502
- var CONFIG_DEFAULTS = Object.freeze({
1503
- version: 1,
1504
- store: "sqlite",
1505
- effortBusyTimeoutMs: 5000,
1506
- effortSync: "local",
1507
- ...HUB_DEFAULTS,
1508
- inject: "stage",
1509
- planDir: join3(".rafa", "plans"),
1510
- specsDir: join3(".rafa", "specs"),
1511
- trackerDefault: "github",
1512
- trackerFallback: Object.freeze(["local"]),
1513
- learningAdapter: "local",
1514
- learningBlessMinConfidence: 0.5,
1515
- learningPromoteAfter: 3,
1516
- learningPromoteMinConfidence: 0.7,
1517
- outputMode: "text",
1518
- prerequisitesRequired: Object.freeze([]),
1519
- prerequisitesOptional: Object.freeze([]),
1520
- trackingSpecs: false,
1521
- trackingPlans: false,
1522
- trackingAll: false,
1523
- modules: Object.freeze([]),
1524
- allowList: Object.freeze([]),
1525
- settingSources: Object.freeze(["project", "local"]),
1526
- loopWorktreeDir: join3(".rafa", "worktrees"),
1527
- ...LOOP_RETRIES_DEFAULTS,
1528
- ...LOOP_CONTINUE_DEFAULTS,
1529
- ...WRAP_UP_DEFAULTS,
1530
- ...PR_DEFAULTS,
1531
- boardTrustedAuthors: Object.freeze([]),
1532
- boardRelationships: "labels",
1533
- boardProjectTemplate: BOARD_PROJECT_TEMPLATE_DEFAULT,
1534
- boardProjectNumber: null,
1535
- ...BOARD_PROJECT_LIMIT_DEFAULTS,
1536
- roadmapIssue: null,
1537
- claimsStaleAfter: "3d",
1538
- claimsAhead: "off",
1539
- ...TRIAGE_DEFAULTS,
1540
- ...RELEASE_DEFAULTS,
1541
- ...ERRORS_DEFAULTS,
1542
- ...LOGGER_DEFAULTS,
1543
- ...ISSUES_DEFAULTS,
1544
- cleanupStaleDays: 30,
1545
- cleanupWorktreeIdleDays: 7,
1546
- cleanupKeep: Object.freeze([]),
1547
- dangerousAcceptStaleRefs: false,
1548
- ...DANGEROUS_RELEASE_DEFAULTS,
1549
- dangerousSelfUpdateDuringLoop: false,
1550
- statusNotice: true,
1551
- tiersRafa: "on",
1552
- tiersSkills: new Map,
1553
- tiersAgents: new Map,
1554
- routing: DEFAULT_ROUTING,
1555
- taskSkills: "planner",
1556
- taskLessons: "on",
1557
- ...TESTS_DEFAULTS
1558
- });
1559
- var SETTINGS = {
1560
- version: { key: "version", read: oneOf(CONFIG_VERSIONS), cli: false },
1561
- store: { key: "store", read: oneOf(STORE_BACKENDS), cli: true },
1562
- effortBusyTimeoutMs: { key: "effort.busyTimeoutMs", read: busyTimeoutMs, cli: false },
1563
- effortSync: { key: "effort.sync", read: oneOf(SYNC_STRATEGIES), cli: false },
1564
- ...HUB_SETTINGS,
1565
- inject: { key: "plan.inject", read: oneOf(INJECT_MODES), cli: true },
1566
- planDir: { key: "plan.dir", read: directory, cli: true },
1567
- specsDir: { key: "specs.dir", read: directory, cli: true },
1568
- trackerDefault: { key: "tracker.default", read: trackerKind, cli: true },
1569
- trackerFallback: {
1570
- key: "tracker.fallback",
1571
- read: listOf(trackerKind, "tracker kind names"),
1572
- cli: false
1573
- },
1574
- learningAdapter: {
1575
- key: "learning.adapter",
1576
- read: text("a learning adapter name"),
1577
- cli: true
1578
- },
1579
- learningBlessMinConfidence: { key: "learning.bless.minConfidence", read: confidence, cli: false },
1580
- learningPromoteAfter: { key: "learning.promote.after", read: recurrenceCount, cli: false },
1581
- learningPromoteMinConfidence: { key: "learning.promote.minConfidence", read: confidence, cli: false },
1582
- outputMode: { key: "output.mode", read: oneOf(OUTPUT_MODES), cli: true },
1583
- prerequisitesRequired: {
1584
- key: "prerequisites.required",
1585
- read: listOf(requiredPrerequisite, "prerequisite items"),
1586
- cli: false,
1587
- itemKeys: REQUIRED_ITEM_KEYS
1588
- },
1589
- prerequisitesOptional: {
1590
- key: "prerequisites.optional",
1591
- read: listOf(optionalPrerequisite, "prerequisite items"),
1592
- cli: false,
1593
- itemKeys: OPTIONAL_ITEM_KEYS
1594
- },
1595
- trackingSpecs: { key: "tracking.specs", read: flag, cli: false },
1596
- trackingPlans: { key: "tracking.plans", read: flag, cli: false },
1597
- trackingAll: { key: "tracking.all", read: flag, cli: false },
1598
- modules: {
1599
- key: "modules",
1600
- read: listOf(moduleSource, "module sources"),
1601
- cli: false,
1602
- itemKeys: MODULE_SOURCE_KEYS
1603
- },
1604
- allowList: {
1605
- key: "allowList",
1606
- read: listOf(text("a module name"), "module names"),
1607
- cli: false
1608
- },
1609
- settingSources: {
1610
- key: "loop.settingSources",
1611
- read: subsetOf(CLAUDE_SETTING_SOURCES),
1612
- cli: true
1613
- },
1614
- loopWorktreeDir: { key: "loop.worktreeDir", read: directory, cli: false },
1615
- ...LOOP_RETRIES_SETTINGS,
1616
- ...LOOP_CONTINUE_SETTINGS,
1617
- ...WRAP_UP_SETTINGS,
1618
- ...PR_SETTINGS,
1619
- boardTrustedAuthors: {
1620
- key: "board.trustedAuthors",
1621
- read: listOf(githubLogin, "GitHub logins"),
1622
- cli: false
1623
- },
1624
- boardRelationships: {
1625
- key: "board.relationships",
1626
- read: oneOf(BOARD_RELATIONSHIP_MODES),
1627
- cli: false
1628
- },
1629
- boardProjectTemplate: { key: "board.project.template", read: projectUrl, cli: false },
1630
- boardProjectNumber: { key: "board.project.number", read: projectNumber, cli: false },
1631
- ...BOARD_PROJECT_LIMIT_SETTINGS,
1632
- roadmapIssue: { key: "roadmap.issue", read: issueNumber, cli: false },
1633
- claimsStaleAfter: { key: "claims.staleAfter", read: claimsStaleAfter, cli: false },
1634
- claimsAhead: { key: "claims.ahead", read: claimsAhead, cli: false },
1635
- ...TRIAGE_SETTINGS,
1636
- ...RELEASE_SETTINGS,
1637
- ...ERRORS_SETTINGS,
1638
- ...LOGGER_SETTINGS,
1639
- ...ISSUES_SETTINGS,
1640
- cleanupStaleDays: { key: "cleanup.staleDays", read: dayCount, cli: false },
1641
- cleanupWorktreeIdleDays: {
1642
- key: "cleanup.worktreeIdleDays",
1643
- read: dayCount,
1644
- cli: false
1645
- },
1646
- cleanupKeep: {
1647
- key: "cleanup.keep",
1648
- read: listOf(text("a glob pattern"), "glob patterns"),
1649
- cli: false
1650
- },
1651
- dangerousAcceptStaleRefs: {
1652
- key: "dangerous.acceptStaleRefs",
1653
- read: flag,
1654
- cli: false
1655
- },
1656
- ...DANGEROUS_RELEASE_SETTINGS,
1657
- dangerousSelfUpdateDuringLoop: {
1658
- key: "dangerous.selfUpdateDuringLoop",
1659
- read: flag,
1660
- cli: false
1661
- },
1662
- statusNotice: { key: "status.notice", read: flag, cli: false },
1663
- tiersRafa: { key: "tiers.rafa", read: tierSwitch, cli: false },
1664
- tiersSkills: { key: "tiers.skills", read: tierPins, cli: false },
1665
- tiersAgents: { key: "tiers.agents", read: tierPins, cli: false },
1666
- routing: { key: "routing", read: routeTable, cli: false },
1667
- taskSkills: { key: "task.skills", read: skillResolverName, cli: true },
1668
- taskLessons: { key: "task.lessons", read: lessonSwitch, cli: false },
1669
- ...TESTS_SETTINGS
1670
- };
1671
- var SETTING_NAMES = Object.keys(SETTINGS);
1672
- var SETTING_BY_KEY = new Map(SETTING_NAMES.map((setting) => [
1673
- SETTINGS[setting].key,
1674
- setting
1675
- ]));
1676
- function isCommandLineSetting(setting) {
1677
- return SETTINGS[setting].cli;
1678
- }
1679
- function sectionsOf(key) {
1680
- const parts = key.split(".");
1681
- return parts.slice(1).map((_, index) => parts.slice(0, index + 1).join("."));
1682
- }
1683
- var SECTIONS = new Set([...SETTING_BY_KEY.keys()].flatMap(sectionsOf));
1684
- function parentOf(key) {
1685
- const cut = key.lastIndexOf(".");
1686
- return cut < 0 ? "" : key.slice(0, cut);
1687
- }
1688
- var TRAILING_INDEX = /\[\d+\]$/;
1689
- var KNOWN_UNDER = (() => {
1690
- const known = new Map;
1691
- const add = (parent, name) => {
1692
- const names = known.get(parent) ?? [];
1693
- if (!names.includes(name))
1694
- known.set(parent, [...names, name]);
1695
- };
1696
- for (const setting of SETTING_NAMES) {
1697
- const { key, itemKeys } = SETTINGS[setting];
1698
- const parts = key.split(".");
1699
- parts.forEach((part, index) => add(parts.slice(0, index).join("."), part));
1700
- for (const name of itemKeys ?? [])
1701
- add(`${key}[]`, name);
1702
- }
1703
- return known;
1704
- })();
1705
- function knownKeysAbove(key) {
1706
- for (let parent = parentOf(key);parent !== ""; parent = parentOf(parent)) {
1707
- const names = KNOWN_UNDER.get(parent.replace(TRAILING_INDEX, "[]"));
1708
- if (names !== undefined)
1709
- return [parent, names];
1710
- }
1711
- return ["", KNOWN_UNDER.get("") ?? []];
1712
- }
1713
-
1714
- // src/config.ts
1715
- import { join as join4 } from "path";
1716
- class ConfigError extends Error {
1717
- problems;
1718
- constructor(problems, options) {
1719
- super(`rafa config: ${problems.join("; ")}`, options);
1720
- this.name = "ConfigError";
1721
- this.problems = problems;
1722
- }
1723
- }
1724
- function readLayer(rawOf, atOf) {
1725
- const problems = [];
1726
- const extras = [];
1727
- const read = (setting) => {
1728
- const raw = rawOf(setting);
1729
- if (raw === undefined)
1730
- return;
1731
- const reading = SETTINGS[setting].read(raw, atOf(setting));
1732
- problems.push(...reading.problems);
1733
- extras.push(...reading.extras);
1734
- return reading.value;
1735
- };
1736
- const layer = {
1737
- version: read("version"),
1738
- store: read("store"),
1739
- effortBusyTimeoutMs: read("effortBusyTimeoutMs"),
1740
- effortSync: read("effortSync"),
1741
- hubUrl: read("hubUrl"),
1742
- hubTokenSecret: read("hubTokenSecret"),
1743
- hubTimeout: read("hubTimeout"),
1744
- inject: read("inject"),
1745
- planDir: read("planDir"),
1746
- specsDir: read("specsDir"),
1747
- trackerDefault: read("trackerDefault"),
1748
- trackerFallback: read("trackerFallback"),
1749
- learningAdapter: read("learningAdapter"),
1750
- learningBlessMinConfidence: read("learningBlessMinConfidence"),
1751
- learningPromoteAfter: read("learningPromoteAfter"),
1752
- learningPromoteMinConfidence: read("learningPromoteMinConfidence"),
1753
- outputMode: read("outputMode"),
1754
- prerequisitesRequired: read("prerequisitesRequired"),
1755
- prerequisitesOptional: read("prerequisitesOptional"),
1756
- trackingSpecs: read("trackingSpecs"),
1757
- trackingPlans: read("trackingPlans"),
1758
- trackingAll: read("trackingAll"),
1759
- modules: read("modules"),
1760
- allowList: read("allowList"),
1761
- settingSources: read("settingSources"),
1762
- loopWorktreeDir: read("loopWorktreeDir"),
1763
- loopRetries: read("loopRetries"),
1764
- loopRetriesOnContinue: read("loopRetriesOnContinue"),
1765
- loopContinueCriteria: read("loopContinueCriteria"),
1766
- loopContinueCriteriaMode: read("loopContinueCriteriaMode"),
1767
- loopForceWrapUpMaxNewFailures: read("loopForceWrapUpMaxNewFailures"),
1768
- loopWrapUpRetries: read("loopWrapUpRetries"),
1769
- prProvider: read("prProvider"),
1770
- prMergeMethod: read("prMergeMethod"),
1771
- prBase: read("prBase"),
1772
- prResolveBudget: read("prResolveBudget"),
1773
- prVersionCollision: read("prVersionCollision"),
1774
- boardTrustedAuthors: read("boardTrustedAuthors"),
1775
- boardRelationships: read("boardRelationships"),
1776
- boardProjectTemplate: read("boardProjectTemplate"),
1777
- boardProjectNumber: read("boardProjectNumber"),
1778
- boardProjectRetries: read("boardProjectRetries"),
1779
- boardProjectRetryWaitSeconds: read("boardProjectRetryWaitSeconds"),
1780
- boardProjectProgressSeconds: read("boardProjectProgressSeconds"),
1781
- boardProjectWriteBatchSize: read("boardProjectWriteBatchSize"),
1782
- boardProjectWritePauseMs: read("boardProjectWritePauseMs"),
1783
- roadmapIssue: read("roadmapIssue"),
1784
- claimsStaleAfter: read("claimsStaleAfter"),
1785
- claimsAhead: read("claimsAhead"),
1786
- triageSimilarityThreshold: read("triageSimilarityThreshold"),
1787
- triageSimilarityCandidates: read("triageSimilarityCandidates"),
1788
- releaseEnabled: read("releaseEnabled"),
1789
- releaseVersionFile: read("releaseVersionFile"),
1790
- releaseChangelog: read("releaseChangelog"),
1791
- releaseHeading: read("releaseHeading"),
1792
- releaseFragments: read("releaseFragments"),
1793
- releaseStrategy: read("releaseStrategy"),
1794
- releaseSettle: read("releaseSettle"),
1795
- releaseTag: read("releaseTag"),
1796
- releasePublishCommand: read("releasePublishCommand"),
1797
- errorsCodes: read("errorsCodes"),
1798
- loggerKind: read("loggerKind"),
1799
- loggerLevel: read("loggerLevel"),
1800
- loggerTheme: read("loggerTheme"),
1801
- loggerModules: read("loggerModules"),
1802
- loggerCallSite: read("loggerCallSite"),
1803
- loggerApi: read("loggerApi"),
1804
- issuesTitleBug: read("issuesTitleBug"),
1805
- issuesSummaryMax: read("issuesSummaryMax"),
1806
- issuesEnvironments: read("issuesEnvironments"),
1807
- issuesVocabularyModules: read("issuesVocabularyModules"),
1808
- issuesVocabularyActions: read("issuesVocabularyActions"),
1809
- cleanupStaleDays: read("cleanupStaleDays"),
1810
- cleanupWorktreeIdleDays: read("cleanupWorktreeIdleDays"),
1811
- cleanupKeep: read("cleanupKeep"),
1812
- dangerousAcceptStaleRefs: read("dangerousAcceptStaleRefs"),
1813
- dangerousAcceptVersionCollision: read("dangerousAcceptVersionCollision"),
1814
- dangerousSelfUpdateDuringLoop: read("dangerousSelfUpdateDuringLoop"),
1815
- statusNotice: read("statusNotice"),
1816
- tiersRafa: read("tiersRafa"),
1817
- tiersSkills: read("tiersSkills"),
1818
- tiersAgents: read("tiersAgents"),
1819
- routing: read("routing"),
1820
- taskSkills: read("taskSkills"),
1821
- taskLessons: read("taskLessons"),
1822
- testsFullSuiteTriggers: read("testsFullSuiteTriggers"),
1823
- testsIntegration: read("testsIntegration"),
1824
- testsAlwaysRun: read("testsAlwaysRun"),
1825
- testsRetakeRedAlone: read("testsRetakeRedAlone")
1826
- };
1827
- return { layer, problems, extras };
1828
- }
1829
- function* entriesOf(mapping, prefix) {
1830
- for (const [name, value] of Object.entries(mapping)) {
1831
- const key = prefix === "" ? name : `${prefix}.${name}`;
1832
- if (SECTIONS.has(key) && isMapping(value))
1833
- yield* entriesOf(value, key);
1834
- else
1835
- yield { key, value };
1836
- }
1837
- }
1838
- function sortKeys(document, path) {
1839
- const named = new Map;
1840
- const seen = new Set;
1841
- const extras = [];
1842
- const problems = [];
1843
- for (const entry of entriesOf(document, "")) {
1844
- const setting = SETTING_BY_KEY.get(entry.key);
1845
- if (setting !== undefined) {
1846
- if (seen.has(setting)) {
1847
- problems.push(`${path}: ${entry.key} is given more than once`);
1848
- } else if (entry.value !== null) {
1849
- named.set(setting, entry.value);
1850
- }
1851
- seen.add(setting);
1852
- continue;
1853
- }
1854
- if (!SECTIONS.has(entry.key)) {
1855
- extras.push(entry);
1856
- continue;
1857
- }
1858
- if (entry.value !== null) {
1859
- const found = describeValue(entry.value);
1860
- problems.push(`${path}: ${entry.key} must be a mapping, found ${found}`);
1861
- }
1862
- }
1863
- return { named, extras, problems };
1864
- }
1865
- var TAB_INDENTED_LINE = /^[ \t]*\t[ \t]*[^\s#]/;
1866
- function tabIndentProblems(text2, path) {
1867
- return text2.split(/\r?\n/).flatMap((line, index) => TAB_INDENTED_LINE.test(line) ? [`${path}: line ${index + 1} is indented with a tab; YAML indents with spaces only`] : []);
1868
- }
1869
- function parseYaml(text2, path) {
1870
- const tabProblems = tabIndentProblems(text2, path);
1871
- if (tabProblems.length > 0)
1872
- throw new ConfigError(tabProblems);
1873
- try {
1874
- return Bun.YAML.parse(text2);
1875
- } catch (error) {
1876
- const problem = `${path}: not valid YAML (${messageOf(error)})`;
1877
- throw new ConfigError([problem], { cause: error });
1878
- }
1879
- }
1880
- function configFilePath(root) {
1881
- return join4(root, CONFIG_FILE);
1882
- }
1883
- function parseConfigText(text2, path) {
1884
- const document = parseYaml(text2, path) ?? {};
1885
- if (!isMapping(document)) {
1886
- const found = describeValue(document);
1887
- throw new ConfigError([
1888
- `${path}: expected a mapping at the top level, found ${found}`
1889
- ]);
1890
- }
1891
- const sorted = sortKeys(document, path);
1892
- const read = readLayer((setting) => sorted.named.get(setting), (setting) => ({
1893
- label: `${path}: ${SETTINGS[setting].key}`,
1894
- key: SETTINGS[setting].key
1895
- }));
1896
- const all = [...sorted.problems, ...read.problems];
1897
- if (all.length > 0)
1898
- throw new ConfigError(all);
1899
- return { path, values: read.layer, extras: [...sorted.extras, ...read.extras] };
1900
- }
1901
- var MAP_SETTINGS = new Set([
1902
- "tiersSkills",
1903
- "tiersAgents",
1904
- "routing",
1905
- "loggerModules"
1906
- ]);
1907
- function mergeMaps(...layers) {
1908
- const merged = new Map;
1909
- for (const layer of layers) {
1910
- if (!(layer instanceof Map))
1911
- continue;
1912
- for (const [name, value] of layer)
1913
- merged.set(name, value);
1914
- }
1915
- return merged;
1916
- }
1917
- function unloadedPinWarnings(layer) {
1918
- const pins = [
1919
- ["tiers.skills", layer.values.tiersSkills],
1920
- ["tiers.agents", layer.values.tiersAgents]
1921
- ];
1922
- return pins.flatMap(([key, map]) => [...map ?? []].filter(([, pin]) => pin === "rafa").map(([name]) => `rafa config: ${key}.${name} in ${layer.path} pins the rafa tier, ` + "which is not loaded (tiers.rafa is off), so the pin has no effect"));
1923
- }
1924
- function unknownKeyWarning(key, path) {
1925
- const quoted = JSON.stringify(key);
1926
- const [under, names] = knownKeysAbove(key);
1927
- const where = under === "" ? "" : ` under ${under}`;
1928
- return `rafa config: unknown key ${quoted} in ${path} has no effect ` + `in this version (known keys${where}: ${names.join(", ")})`;
1929
- }
1930
- function resolveConfig(layers = {}) {
1931
- const file = layers.file ?? null;
1932
- const user = layers.user ?? null;
1933
- const overrides = layers.cli ?? {};
1934
- const { layer: cli, problems } = readLayer((setting) => isCommandLineSetting(setting) ? overrides[setting] : undefined, (setting) => ({ label: `command line: ${setting}`, key: setting }));
1935
- if (problems.length > 0)
1936
- throw new ConfigError(problems);
1937
- const rank = (setting) => {
1938
- const fromCli = cli[setting];
1939
- if (fromCli !== undefined)
1940
- return { value: fromCli, source: "cli" };
1941
- const fromFile = file?.values[setting];
1942
- const fromUser = user?.values[setting];
1943
- if (MAP_SETTINGS.has(setting)) {
1944
- const merged = mergeMaps(CONFIG_DEFAULTS[setting], fromUser, fromFile);
1945
- const source = fromFile !== undefined ? "file" : fromUser !== undefined ? "user" : "default";
1946
- return { value: merged, source };
1947
- }
1948
- if (fromFile !== undefined)
1949
- return { value: fromFile, source: "file" };
1950
- if (fromUser !== undefined)
1951
- return { value: fromUser, source: "user" };
1952
- return { value: CONFIG_DEFAULTS[setting], source: "default" };
1953
- };
1954
- const ranked = SETTING_NAMES.map((setting) => [setting, rank(setting)]);
1955
- const effortSync = rank("effortSync");
1956
- const hubProblems = hubUrlProblems({
1957
- effortSync: effortSync.value,
1958
- hubUrl: rank("hubUrl").value,
1959
- effortSyncSource: effortSync.source,
1960
- paths: { file: file?.path ?? null, user: user?.path ?? null }
1961
- });
1962
- if (hubProblems.length > 0)
1963
- throw new ConfigError(hubProblems);
1964
- const rafaLoaded = ranked.find(([setting]) => setting === "tiersRafa")?.[1].value !== "off";
1965
- const warnings = [user, file].flatMap((layer) => layer === null ? [] : [
1966
- ...layer.extras.map((extra) => unknownKeyWarning(extra.key, layer.path)),
1967
- ...rafaLoaded ? [] : unloadedPinWarnings(layer)
1968
- ]);
1969
- return {
1970
- config: Object.fromEntries(ranked.map(([setting, { value }]) => [setting, value])),
1971
- sources: Object.fromEntries(ranked.map(([setting, { source }]) => [setting, source])),
1972
- path: file?.path ?? null,
1973
- userPath: user?.path ?? null,
1974
- extras: file?.extras ?? [],
1975
- userExtras: user?.extras ?? [],
1976
- warnings
1977
- };
1978
- }
1979
-
1980
- // src/plan/blocks.ts
1981
- var RAFA_BLOCK_KINDS = [
1982
- "plan",
1983
- "context",
1984
- "stage-context",
1985
- "report"
1986
- ];
1987
- var RAFA_PREFIX = "rafa:";
1988
- var FENCE_RUN = /^( {0,3})(`{3,}|~{3,})/;
1989
- var CLOSING_FENCE = /^ {0,3}(`{3,}|~{3,})[ \t]*$/;
1990
- function isRafaBlockKind(kind) {
1991
- return RAFA_BLOCK_KINDS.includes(kind);
1992
- }
1993
- function linesOf(markdown) {
1994
- const lines = markdown.split(`
1995
- `);
1996
- if (lines[lines.length - 1] === "")
1997
- lines.pop();
1998
- return lines.map((line) => line.endsWith("\r") ? line.slice(0, -1) : line);
1999
- }
2000
- function kindOf(info) {
2001
- const word = info.trim().split(/\s+/)[0] ?? "";
2002
- return word.startsWith(RAFA_PREFIX) ? word.slice(RAFA_PREFIX.length) : null;
2003
- }
2004
- function openingFence(line) {
2005
- const match = FENCE_RUN.exec(line);
2006
- const opener = match?.[0];
2007
- const indent = match?.[1];
2008
- const run = match?.[2];
2009
- if (opener === undefined || indent === undefined || run === undefined) {
2010
- return null;
2011
- }
2012
- const marker = run.charAt(0);
2013
- const info = line.slice(opener.length);
2014
- if (marker === "`" && info.includes("`"))
2015
- return null;
2016
- return { marker, length: run.length, indent: indent.length, kind: kindOf(info) };
2017
- }
2018
- function closes(line, fence) {
2019
- const run = CLOSING_FENCE.exec(line)?.[1];
2020
- return run !== undefined && run.charAt(0) === fence.marker && run.length >= fence.length;
2021
- }
2022
- function closingIndex(lines, from, fence) {
2023
- for (let index = from;index < lines.length; index += 1) {
2024
- if (closes(lines[index] ?? "", fence))
2025
- return index;
2026
- }
2027
- return lines.length;
2028
- }
2029
- function dedent(line, indent) {
2030
- let cut = 0;
2031
- while (cut < indent && line.charAt(cut) === " ")
2032
- cut += 1;
2033
- return line.slice(cut);
2034
- }
2035
- function readRafaBlocks(markdown) {
2036
- const lines = linesOf(markdown);
2037
- const blocks = [];
2038
- let index = 0;
2039
- while (index < lines.length) {
2040
- const fence = openingFence(lines[index] ?? "");
2041
- if (fence === null) {
2042
- index += 1;
2043
- continue;
2044
- }
2045
- const closing = closingIndex(lines, index + 1, fence);
2046
- const closed = closing < lines.length;
2047
- if (fence.kind !== null) {
2048
- const body = lines.slice(index + 1, closing).map((line) => dedent(line, fence.indent)).join(`
2049
- `);
2050
- const last = closed ? closing + 1 : lines.length;
2051
- blocks.push({ kind: fence.kind, body, span: { first: index + 1, last }, closed });
2052
- }
2053
- index = closing + 1;
2054
- }
2055
- return blocks;
2056
- }
2057
-
2058
- // src/report/parse.ts
2059
- var REPORT_STATUSES = ["done", "blocked"];
2060
- var FINDING_KINDS = ["gotcha", "pattern", "location", "skill-suggestion"];
2061
- var FINDING_SIGNALS = ["loud", "silent"];
2062
- var FINDING_DOMAINS = [
2063
- "code-style",
2064
- "testing",
2065
- "git",
2066
- "debugging",
2067
- "workflow",
2068
- "security",
2069
- "general-best-practices"
2070
- ];
2071
- var CHANGE_LEVELS = ["patch", "minor", "major", "none"];
2072
- var REPORT_KIND = "report";
2073
- var REQUIRED = true;
2074
- var OPTIONAL = false;
2075
- var REPORT_KEYS = [
2076
- "status",
2077
- "feedback",
2078
- "findings",
2079
- "skills_used",
2080
- "blockers",
2081
- "out_of_scope_bugs",
2082
- "changes"
2083
- ];
2084
- var FINDING_KEYS = [
2085
- "trigger",
2086
- "kind",
2087
- "what",
2088
- "cause",
2089
- "resolution",
2090
- "artifact",
2091
- "signal",
2092
- "domain"
2093
- ];
2094
- var BLOCKER_KEYS = ["what", "artifact"];
2095
- var BUG_KEYS = ["what", "artifact", "security"];
2096
- var CHANGE_KEYS = ["level", "area", "summary"];
2097
- function isMapping2(value) {
2098
- return typeof value === "object" && value !== null && !Array.isArray(value);
2099
- }
2100
- function isOneOf(choices, value) {
2101
- return choices.includes(value);
2102
- }
2103
- function describeValue2(value) {
2104
- if (typeof value === "string")
2105
- return JSON.stringify(value);
2106
- if (value === null)
2107
- return "nothing";
2108
- if (Array.isArray(value))
2109
- return "a list";
2110
- if (typeof value === "object")
2111
- return "a mapping";
2112
- return `the ${typeof value} ${String(value)}`;
2113
- }
2114
- function messageOf2(error) {
2115
- return error instanceof Error ? error.message : String(error);
2116
- }
2117
- function reportIssue(reason, field, text2) {
2118
- return { reason, field, text: text2 };
2119
- }
2120
- function absent(reason, block, text2) {
2121
- return { present: false, reason, block, text: text2 };
2122
- }
2123
- function scopeOf(mapping, keys, prefix, issues) {
2124
- const fields = new Map;
2125
- const extras = [];
2126
- for (const [key, value] of Object.entries(mapping)) {
2127
- if (keys.includes(key))
2128
- fields.set(key, value);
2129
- else
2130
- extras.push({ key, value });
2131
- }
2132
- return { fields, extras, prefix, issues };
2133
- }
2134
- function valueOf(scope, key, required) {
2135
- const value = scope.fields.get(key) ?? null;
2136
- if (value === null && required) {
2137
- const field = `${scope.prefix}${key}`;
2138
- scope.issues.push(reportIssue("missing-field", field, `${field} is missing`));
2139
- }
2140
- return value;
2141
- }
2142
- function unusable(scope, key, why) {
2143
- const field = `${scope.prefix}${key}`;
2144
- scope.issues.push(reportIssue("unusable-field", field, `${field} ${why}`));
2145
- return null;
2146
- }
2147
- function stringProblem(value) {
2148
- if (typeof value !== "string")
2149
- return `is ${describeValue2(value)}, not a string; quote it`;
2150
- if (value.trim().length === 0)
2151
- return "is blank";
2152
- return null;
2153
- }
2154
- function stringField(scope, key, required) {
2155
- const value = valueOf(scope, key, required);
2156
- if (value === null)
2157
- return null;
2158
- const problem = stringProblem(value);
2159
- return problem === null ? value : unusable(scope, key, problem);
2160
- }
2161
- function choiceField(scope, key, choices, required) {
2162
- const value = valueOf(scope, key, required);
2163
- if (value === null || isOneOf(choices, value))
2164
- return value;
2165
- return unusable(scope, key, `is ${describeValue2(value)}, not one of ${choices.join(", ")}`);
2166
- }
2167
- function booleanField(scope, key, required) {
2168
- const value = valueOf(scope, key, required);
2169
- if (value === null || typeof value === "boolean")
2170
- return value;
2171
- return unusable(scope, key, `is ${describeValue2(value)}, not true or false; write it unquoted`);
2172
- }
2173
- function listField(scope, key, readEntry) {
2174
- const value = valueOf(scope, key, OPTIONAL);
2175
- if (value === null)
2176
- return [];
2177
- if (!Array.isArray(value)) {
2178
- unusable(scope, key, `is ${describeValue2(value)}, not a list`);
2179
- return [];
2180
- }
2181
- const entries = [];
2182
- const items = value;
2183
- for (const [index, item] of items.entries()) {
2184
- const entry = readEntry(item, `${scope.prefix}${key}[${index}]`, scope.issues);
2185
- if (entry !== null)
2186
- entries.push(entry);
2187
- }
2188
- return entries;
2189
- }
2190
- function entryScope(item, field, keys, issues) {
2191
- if (isMapping2(item))
2192
- return scopeOf(item, keys, `${field}.`, issues);
2193
- const text2 = `${field} is ${describeValue2(item)}, not a mapping of ${keys.join(", ")}; dropped`;
2194
- issues.push(reportIssue("unusable-field", field, text2));
2195
- return null;
2196
- }
2197
- function readSkill(item, field, issues) {
2198
- const problem = stringProblem(item);
2199
- if (problem === null)
2200
- return item;
2201
- issues.push(reportIssue("unusable-field", field, `${field} ${problem}; dropped`));
2202
- return null;
2203
- }
2204
- function readFinding(item, field, issues) {
2205
- const scope = entryScope(item, field, FINDING_KEYS, issues);
2206
- if (scope === null)
2207
- return null;
2208
- return {
2209
- trigger: stringField(scope, "trigger", REQUIRED),
2210
- kind: choiceField(scope, "kind", FINDING_KINDS, REQUIRED),
2211
- what: stringField(scope, "what", REQUIRED),
2212
- cause: stringField(scope, "cause", OPTIONAL),
2213
- resolution: stringField(scope, "resolution", OPTIONAL),
2214
- artifact: stringField(scope, "artifact", OPTIONAL),
2215
- signal: choiceField(scope, "signal", FINDING_SIGNALS, REQUIRED),
2216
- domain: choiceField(scope, "domain", FINDING_DOMAINS, OPTIONAL),
2217
- extras: scope.extras
2218
- };
2219
- }
2220
- function readBlocker(item, field, issues) {
2221
- const scope = entryScope(item, field, BLOCKER_KEYS, issues);
2222
- if (scope === null)
2223
- return null;
2224
- return {
2225
- what: stringField(scope, "what", REQUIRED),
2226
- artifact: stringField(scope, "artifact", OPTIONAL),
2227
- extras: scope.extras
2228
- };
2229
- }
2230
- function readBug(item, field, issues) {
2231
- const scope = entryScope(item, field, BUG_KEYS, issues);
2232
- if (scope === null)
2233
- return null;
2234
- return {
2235
- what: stringField(scope, "what", REQUIRED),
2236
- artifact: stringField(scope, "artifact", OPTIONAL),
2237
- security: booleanField(scope, "security", REQUIRED),
2238
- extras: scope.extras
2239
- };
2240
- }
2241
- function readChange(item, field, issues) {
2242
- const scope = entryScope(item, field, CHANGE_KEYS, issues);
2243
- if (scope === null)
2244
- return null;
2245
- return {
2246
- level: choiceField(scope, "level", CHANGE_LEVELS, REQUIRED),
2247
- area: stringField(scope, "area", OPTIONAL),
2248
- summary: stringField(scope, "summary", REQUIRED),
2249
- extras: scope.extras
2250
- };
2251
- }
2252
- function readReport(document, issues) {
2253
- const scope = scopeOf(document, REPORT_KEYS, "", issues);
2254
- return {
2255
- status: choiceField(scope, "status", REPORT_STATUSES, REQUIRED),
2256
- feedback: stringField(scope, "feedback", OPTIONAL),
2257
- findings: listField(scope, "findings", readFinding),
2258
- skillsUsed: listField(scope, "skills_used", readSkill),
2259
- blockers: listField(scope, "blockers", readBlocker),
2260
- outOfScopeBugs: listField(scope, "out_of_scope_bugs", readBug),
2261
- changes: listField(scope, "changes", readChange),
2262
- extras: scope.extras
2263
- };
2264
- }
2265
- function parseReport(output) {
2266
- const reports = readRafaBlocks(output).filter((block2) => block2.kind === REPORT_KIND);
2267
- const block = reports.at(-1);
2268
- if (block === undefined) {
2269
- return absent("no-block", null, "the session output holds no rafa:report block");
2270
- }
2271
- const line = block.span.first;
2272
- if (!block.closed) {
2273
- return absent("unclosed-block", block, `rafa:report block at line ${line} is never closed, so its body may have been cut short ` + "and is not read");
2274
- }
2275
- let document;
2276
- try {
2277
- document = Bun.YAML.parse(block.body);
2278
- } catch (error) {
2279
- const text2 = `rafa:report block at line ${line} is not valid YAML (${messageOf2(error)})`;
2280
- return absent("malformed-block", block, text2);
2281
- }
2282
- if (!isMapping2(document)) {
2283
- const text2 = `rafa:report block at line ${line} holds ${describeValue2(document)}, ` + "not a mapping of report fields";
2284
- return absent("malformed-block", block, text2);
2285
- }
2286
- const issues = [];
2287
- const report = readReport(document, issues);
2288
- return { present: true, report, block, issues };
2289
- }
2290
-
2291
- // src/utils/plan-stamp.ts
2292
- var STUB_PATTERN = "[A-Za-z0-9._-]+";
2293
- var STAMP_PATTERN = new RegExp(`<!--\\s*ralph:plan=(${STUB_PATTERN})\\s*-->`);
2294
- function isStampableStub(stub) {
2295
- return new RegExp(`^${STUB_PATTERN}$`).test(stub);
2296
- }
2297
- function planStampLine(stub) {
2298
- if (!isStampableStub(stub)) {
2299
- throw new Error(`rafa plan stamp: unusable stub ${JSON.stringify(stub)}`);
2300
- }
2301
- return `<!-- ralph:plan=${stub} -->`;
2302
- }
2303
- function planStubFromPrompt(content) {
2304
- if (typeof content !== "string" || content.length === 0)
2305
- return null;
2306
- const match = STAMP_PATTERN.exec(content);
2307
- const stub = match?.[1];
2308
- return stub !== undefined && isStampableStub(stub) ? stub : null;
2309
- }
2310
- function stampPrompt(stub, prompt) {
2311
- return `${prompt}
2312
- ${planStampLine(stub)}`;
2313
- }
2314
- function planStubFromPath(planPath) {
2315
- const base = planPath.split("/").pop() ?? planPath;
2316
- const match = /^PLAN(?:_TRACKER)?-(.+)\.md$/.exec(base);
2317
- const stub = match?.[1];
2318
- return stub !== undefined && isStampableStub(stub) ? stub : null;
2319
- }
2320
-
2321
- // src/adapters/output/text.ts
2322
- var DEBUG_VERBOSITY2 = 2;
2323
- function formatPayload(payload) {
2324
- return typeof payload === "string" ? payload : JSON.stringify(payload);
2325
- }
2326
- function resultLine(event) {
2327
- if (event.ok) {
2328
- return event.data === undefined ? "result: ok" : `result: ok ${formatPayload(event.data)}`;
2329
- }
2330
- const code = event.error?.code ?? "unknown";
2331
- const message = event.error?.message ?? "";
2332
- return message === "" ? `result: error ${code}` : `result: error ${code}: ${message}`;
2333
- }
2334
- function createTextOutput({ verbosity, stream }) {
2335
- const writeLine = (line) => {
2336
- stream.write(`${line}
2337
- `);
2338
- };
2339
- const debug = (message) => {
2340
- if (verbosity >= DEBUG_VERBOSITY2)
2341
- writeLine(`debug: ${message}`);
2342
- };
2343
- const info = (message) => {
2344
- writeLine(message);
2345
- };
2346
- const warn = (message) => {
2347
- writeLine(`warn: ${message}`);
2348
- };
2349
- const error = (message) => {
2350
- writeLine(`error: ${message}`);
2351
- };
2352
- const result = (payload) => {
2353
- writeLine(`result: ${formatPayload(payload)}`);
2354
- };
2355
- const writeLog = (event) => {
2356
- switch (event.level) {
2357
- case "debug": {
2358
- debug(event.message);
2359
- return;
2360
- }
2361
- case "info": {
2362
- info(event.message);
2363
- return;
2364
- }
2365
- case "warn": {
2366
- warn(event.message);
2367
- return;
2368
- }
2369
- case "error": {
2370
- error(event.message);
2371
- return;
2372
- }
2373
- }
2374
- };
2375
- const emit = (event) => {
2376
- switch (event.type) {
2377
- case "start": {
2378
- writeLine(`start: ${event.command}`);
2379
- return;
2380
- }
2381
- case "step": {
2382
- writeLine(`step: ${event.name}`);
2383
- return;
2384
- }
2385
- case "log": {
2386
- writeLog(event);
2387
- return;
2388
- }
2389
- case "result": {
2390
- writeLine(resultLine(event));
2391
- return;
2392
- }
2393
- case "event": {
2394
- return;
2395
- }
2396
- }
2397
- };
2398
- return Object.freeze({ info, warn, error, debug, emit, result });
2399
- }
2400
-
2401
- // src/adapters/output/active.ts
2402
- import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
2403
- var scopes2 = new AsyncLocalStorage2;
2404
- var DEFAULT_OUTPUT = createTextOutput({ verbosity: 0, stream: process.stdout });
2405
- var active = DEFAULT_OUTPUT;
2406
- var activeMode = "text";
2407
- function activeOutput() {
2408
- return scopes2.getStore()?.output ?? active;
2409
- }
2410
- function activeOutputMode() {
2411
- return scopes2.getStore()?.mode ?? activeMode;
2412
- }
2413
-
2414
- export { MERGE_METHODS, isMergeMethod, SKILL_TIERS, RAFA_INSTINCTS_PATH, BUNDLED_SKILLS_DIR, isSkillTier, realEntry, bundledBinDirectory, skillTierDirectory, resolveSkillTiers, resolveInstinctScopes, tierExists, TEST_SCOPES, isAgentName, BUDGET_FLAG, readTestScope, parseTaskDeclaration, stripTaskDeclaration, resolveDeclarationFlags, isMapping, describeValue, messageOf, CONFIG_VERSIONS, STORE_BACKENDS, SYNC_STRATEGIES, BOARD_RELATIONSHIP_MODES, INJECT_MODES, OUTPUT_MODES, CLAUDE_SETTING_SOURCES, PR_PROVIDERS, RELEASE_AUTO, SKILL_RESOLVERS, CLAIMS_STALE_DISABLED, BUSY_TIMEOUT_MIN_MS, BUSY_TIMEOUT_MAX_MS, isBusyTimeoutMs, claimDurationHours, isGitHubLogin, BOARD_PROJECT_TEMPLATE_DEFAULT, listOf, hubContextOf, PREREQUISITE_KINDS, requiredPrerequisite, optionalPrerequisite, MODULE_SOURCE_KINDS, DEFAULT_LOGGER_KIND, setActiveLoggerSettings, activeLoggerSettings, levelEnabled, loggerSettingsOf, isLoopRetryCount, RELEASE_DEFAULTS, pinKey, pinLine, collisionMessage, readItemBytes, resolveTiers, findTierItem, DEFAULT_ROUTING, CONFIG_FILE, CONFIG_DEFAULTS, SETTINGS, ConfigError, configFilePath, parseConfigText, resolveConfig, isStampableStub, planStubFromPrompt, stampPrompt, planStubFromPath, RAFA_BLOCK_KINDS, isRafaBlockKind, readRafaBlocks, REPORT_STATUSES, FINDING_KINDS, FINDING_SIGNALS, FINDING_DOMAINS, CHANGE_LEVELS, parseReport, createTextOutput, activeOutput, activeOutputMode };