@open-tomato/rafa 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/NOTICE +9 -1
  2. package/README.md +40 -26
  3. package/dist/PROMPT.md +2 -1
  4. package/dist/bundled/agents/build-error-resolver.md +144 -0
  5. package/dist/bundled/agents/code-reviewer.md +335 -0
  6. package/dist/bundled/agents/doc-updater.md +108 -0
  7. package/dist/bundled/agents/loop-implementer.md +57 -0
  8. package/dist/bundled/agents/qa-bug-reporter.md +164 -0
  9. package/dist/bundled/agents/tdd-guide.md +121 -0
  10. package/dist/bundled/bin/ts-symbols +272 -0
  11. package/dist/{SKILL.md → bundled/skills/dev-planner/SKILL.md} +7 -5
  12. package/dist/bundled/skills/documentation/SKILL.md +93 -0
  13. package/dist/bundled/skills/git-workflow/SKILL.md +242 -0
  14. package/dist/bundled/skills/ts-symbols/SKILL.md +82 -0
  15. package/dist/cli.js +6952 -4721
  16. package/dist/{describe-wbn759vf.js → describe-cenk70n5.js} +4 -3
  17. package/dist/effort/store/index.js +6 -4
  18. package/dist/{index-64wec0k7.js → index-09p41s0n.js} +11 -224
  19. package/dist/{index-qzhpqmfy.js → index-c5cecsga.js} +2 -2
  20. package/dist/{index-4qd537z6.js → index-ca0fccvr.js} +3527 -1402
  21. package/dist/{index-n3rw8rhr.js → index-cedghpva.js} +78 -2
  22. package/dist/index-d6d0942q.js +218 -0
  23. package/dist/{index-7t0qz29r.js → index-gb51x34c.js} +1020 -58
  24. package/dist/{index-r7cnr44m.js → index-mh5hyw8g.js} +1681 -3101
  25. package/dist/index-pqrpcfaz.js +1020 -0
  26. package/dist/{index-p901zs2p.js → index-vg7ycs94.js} +1 -1
  27. package/dist/index-wp8c28fd.js +18 -0
  28. package/dist/index.js +13 -11
  29. package/dist/learning/index.js +25 -0
  30. package/dist/plan/index.js +8 -7
  31. package/dist/plan-prompt.md +2 -0
  32. package/package.json +4 -3
  33. package/dist/index-ps5sac9p.js +0 -531
@@ -0,0 +1,1020 @@
1
+ // @bun
2
+ import {
3
+ CLAUDE_SETTING_SOURCES,
4
+ CONFIG_VERSIONS,
5
+ INJECT_MODES,
6
+ MODULE_SOURCE_KEYS,
7
+ OPTIONAL_ITEM_KEYS,
8
+ OUTPUT_MODES,
9
+ PR_PROVIDERS,
10
+ RELEASE_AUTO,
11
+ REQUIRED_ITEM_KEYS,
12
+ SKILL_TIERS,
13
+ STORE_BACKENDS,
14
+ confidence,
15
+ dayCount,
16
+ describeValue,
17
+ flag,
18
+ githubLogin,
19
+ isMapping,
20
+ isSkillTier,
21
+ issueNumber,
22
+ listOf,
23
+ mergeMethod,
24
+ messageOf,
25
+ moduleSource,
26
+ oneOf,
27
+ optionalPrerequisite,
28
+ recurrenceCount,
29
+ releaseEnabled,
30
+ requiredPrerequisite,
31
+ routeTarget,
32
+ subsetOf,
33
+ text,
34
+ tierPin,
35
+ tierSwitch,
36
+ usdAmount
37
+ } from "./index-cedghpva.js";
38
+
39
+ // src/config-schema.ts
40
+ import { join } from "path";
41
+
42
+ // src/tiers/resolve.ts
43
+ import { readFileSync } from "fs";
44
+ var VENDORED_HEADER = /^<!-- vendored by rafa from .* on \d{4}-\d{2}-\d{2} -->$/;
45
+ var CLAUDE_SKILL_ORDER = ["user", "project", "rafa"];
46
+ var KIND_ORDER = ["skill", "agent"];
47
+ var PLAIN_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
48
+ function loadedTiers(settings) {
49
+ return SKILL_TIERS.filter((tier) => tier === "project" || tier === "rafa" && settings.tiersRafa !== "off" || tier === "user" && settings.settingSources.includes("user"));
50
+ }
51
+ function pinKey(kind) {
52
+ return kind === "skill" ? "tiers.skills" : "tiers.agents";
53
+ }
54
+ function pinLine(kind, name, tier) {
55
+ const key = PLAIN_NAME.test(name) ? name : JSON.stringify(name);
56
+ return `${pinKey(kind)}: { ${key}: ${tier} }`;
57
+ }
58
+ function collisionMessage(collision) {
59
+ const places = collision.holders.map((holder) => `${holder.source} ${holder.path}`).join(" and ");
60
+ const held = `${collision.kind} ${collision.name} is held by ${collision.holders.length} loaded tiers ` + `with different contents: ${places}; `;
61
+ const tier = collision.setAsidePin;
62
+ if (tier === null)
63
+ return `${held}pin the tier that serves it: ${collision.pinLine}`;
64
+ const over = collision.outrankedBy;
65
+ const tiers = over.map((holder) => holder.source).join(" and ");
66
+ const copies = over.length === 1 ? "copy" : "copies";
67
+ const paths = over.map((holder) => holder.path).join(" and ");
68
+ 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`;
69
+ }
70
+ function readItemBytes(path) {
71
+ try {
72
+ return readFileSync(path);
73
+ } catch {
74
+ return null;
75
+ }
76
+ }
77
+ function comparableText(bytes) {
78
+ return Buffer.from(bytes).toString("latin1").split(`
79
+ `).filter((line) => !VENDORED_HEADER.test(line)).join(`
80
+ `);
81
+ }
82
+ function tierIndex(row) {
83
+ return SKILL_TIERS.indexOf(row.source);
84
+ }
85
+ function groupByName(rows) {
86
+ const groups = new Map;
87
+ for (const row of rows) {
88
+ if (!isSkillTier(row.source))
89
+ continue;
90
+ const key = `${row.kind}\x00${row.name}`;
91
+ groups.set(key, [...groups.get(key) ?? [], row]);
92
+ }
93
+ return new Map([...groups].map(([key, group]) => [
94
+ key,
95
+ [...group].sort((a, b) => tierIndex(a) - tierIndex(b))
96
+ ]));
97
+ }
98
+ function loadedHolders(holders, tiers) {
99
+ return tiers.flatMap((tier) => {
100
+ const holder = holders.find((row) => row.source === tier);
101
+ return holder === undefined ? [] : [holder];
102
+ });
103
+ }
104
+ function sameItemGroups(holders, read) {
105
+ if (holders.length < 2)
106
+ return [holders];
107
+ const groups = [];
108
+ for (const row of holders) {
109
+ const bytes = read(row.path);
110
+ const text2 = bytes === null ? null : comparableText(bytes);
111
+ const group = text2 === null ? undefined : groups.find((candidate) => candidate.text === text2);
112
+ if (group === undefined)
113
+ groups.push({ text: text2, rows: [row] });
114
+ else
115
+ group.rows.push(row);
116
+ }
117
+ return groups.map((group) => group.rows);
118
+ }
119
+ function served(base, winner, decidedBy, groups) {
120
+ const same = groups.find((group) => group.includes(winner)) ?? [winner];
121
+ return {
122
+ ...base,
123
+ state: "served",
124
+ winner,
125
+ decidedBy,
126
+ copies: same.filter((row) => row !== winner),
127
+ overridden: base.loaded.filter((row) => !same.includes(row))
128
+ };
129
+ }
130
+ function claudeRank(row) {
131
+ return CLAUDE_SKILL_ORDER.indexOf(row.source);
132
+ }
133
+ function outrankingHolders(pinned, loaded, groups) {
134
+ if (pinned.kind !== "skill")
135
+ return [];
136
+ const same = groups.find((group) => group.includes(pinned)) ?? [pinned];
137
+ return loaded.filter((row) => claudeRank(row) < claudeRank(pinned) && !same.includes(row)).sort((a, b) => claudeRank(a) - claudeRank(b));
138
+ }
139
+ function workingPinTier(kind, loaded) {
140
+ const [first] = kind === "skill" ? [...loaded].sort((a, b) => claudeRank(a) - claudeRank(b)) : loaded;
141
+ return first !== undefined && isSkillTier(first.source) ? first.source : "project";
142
+ }
143
+ function resolveItem(holders, settings, tiers, read) {
144
+ const [first] = holders;
145
+ if (first === undefined)
146
+ throw new Error("resolveItem: a name with no holder");
147
+ const { kind, name } = first;
148
+ const pins = kind === "skill" ? settings.tiersSkills : settings.tiersAgents;
149
+ const base = {
150
+ kind,
151
+ name,
152
+ holders,
153
+ loaded: loadedHolders(holders, tiers),
154
+ pin: pins.get(name) ?? null
155
+ };
156
+ if (base.pin === false)
157
+ return { ...base, state: "off" };
158
+ const [nearest] = base.loaded;
159
+ if (nearest === undefined)
160
+ return { ...base, state: "unloaded" };
161
+ const groups = sameItemGroups(base.loaded, read);
162
+ const pinned = base.loaded.find((row) => row.source === base.pin);
163
+ const outrankedBy = pinned === undefined ? [] : outrankingHolders(pinned, base.loaded, groups);
164
+ if (pinned !== undefined && outrankedBy.length === 0)
165
+ return served(base, pinned, "pin", groups);
166
+ if (groups.length === 1)
167
+ return served(base, nearest, "order", groups);
168
+ const distinct = groups.flatMap((group) => group.slice(0, 1));
169
+ const setAsidePin = pinned !== undefined && isSkillTier(pinned.source) ? pinned.source : null;
170
+ return {
171
+ ...base,
172
+ state: "collision",
173
+ collision: {
174
+ kind,
175
+ name,
176
+ holders: distinct,
177
+ pinLine: pinLine(kind, name, workingPinTier(kind, base.loaded)),
178
+ setAsidePin,
179
+ outrankedBy
180
+ }
181
+ };
182
+ }
183
+ function resolveTiers(rows, settings, read) {
184
+ const tiers = loadedTiers(settings);
185
+ 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));
186
+ return {
187
+ loadedTiers: tiers,
188
+ items,
189
+ collisions: items.flatMap((item) => item.state === "collision" ? [item.collision] : [])
190
+ };
191
+ }
192
+ function findTierItem(resolution, kind, name) {
193
+ return resolution.items.find((item) => item.kind === kind && item.name === name);
194
+ }
195
+
196
+ // src/tiers/routing.ts
197
+ var DEFAULT_ROUTES = Object.freeze([
198
+ Object.freeze(["prose", "doc-updater"]),
199
+ Object.freeze(["tests", "tdd-guide"]),
200
+ Object.freeze(["repair", "build-error-resolver"]),
201
+ Object.freeze(["review", "code-reviewer"]),
202
+ Object.freeze(["implementation", "loop-implementer"])
203
+ ]);
204
+ var DEFAULT_ROUTING = new Map(DEFAULT_ROUTES);
205
+
206
+ // src/config-schema.ts
207
+ var CONFIG_FILE = join(".rafa", "config.yaml");
208
+ function mapOf(value, expected) {
209
+ return (raw, at) => {
210
+ if (!isMapping(raw)) {
211
+ const problem = `${at.label} is ${describeValue(raw)}, expected a mapping of names to ${expected}`;
212
+ return { value: undefined, problems: [problem], extras: [] };
213
+ }
214
+ const readings = Object.entries(raw).map(([name, entry]) => [
215
+ name,
216
+ name.trim() === "" ? {
217
+ value: undefined,
218
+ problems: [`${at.label} names ${JSON.stringify(name)}, expected a non-empty name`],
219
+ extras: []
220
+ } : value(entry, { label: `${at.label}.${name}`, key: `${at.key}.${name}` })
221
+ ]);
222
+ const problems = readings.flatMap(([, reading]) => reading.problems);
223
+ const extras = readings.flatMap(([, reading]) => reading.extras);
224
+ if (problems.length > 0)
225
+ return { value: undefined, problems, extras };
226
+ const read = new Map;
227
+ for (const [name, reading] of readings) {
228
+ if (reading.value !== undefined)
229
+ read.set(name, reading.value);
230
+ }
231
+ return { value: read, problems, extras };
232
+ };
233
+ }
234
+ var CONFIG_DEFAULTS = Object.freeze({
235
+ version: 1,
236
+ store: "sqlite",
237
+ inject: "stage",
238
+ planDir: join(".rafa", "plans"),
239
+ specsDir: join(".rafa", "specs"),
240
+ trackerDefault: "github",
241
+ trackerFallback: Object.freeze(["local"]),
242
+ learningAdapter: "local",
243
+ learningBlessMinConfidence: 0.5,
244
+ learningPromoteAfter: 3,
245
+ learningPromoteMinConfidence: 0.7,
246
+ outputMode: "text",
247
+ prerequisitesRequired: Object.freeze([]),
248
+ prerequisitesOptional: Object.freeze([]),
249
+ trackingSpecs: false,
250
+ trackingPlans: false,
251
+ trackingAll: false,
252
+ modules: Object.freeze([]),
253
+ allowList: Object.freeze([]),
254
+ settingSources: Object.freeze(["project", "local"]),
255
+ prProvider: null,
256
+ prMergeMethod: "squash",
257
+ prBase: null,
258
+ prResolveBudget: 2,
259
+ boardTrustedAuthors: Object.freeze([]),
260
+ roadmapIssue: null,
261
+ releaseEnabled: RELEASE_AUTO,
262
+ releaseVersionFile: "package.json",
263
+ releaseChangelog: "CHANGELOG.md",
264
+ releaseHeading: "## {version} \u2014 {date}, {title}",
265
+ cleanupStaleDays: 30,
266
+ cleanupWorktreeIdleDays: 7,
267
+ cleanupKeep: Object.freeze([]),
268
+ dangerousAcceptStaleRefs: false,
269
+ statusNotice: true,
270
+ tiersRafa: "on",
271
+ tiersSkills: new Map,
272
+ tiersAgents: new Map,
273
+ routing: DEFAULT_ROUTING
274
+ });
275
+ var directory = text("a directory path");
276
+ var trackerKind = text("a tracker kind name");
277
+ var releaseFile = text("a file path");
278
+ var tierPins = mapOf(tierPin, "false or a tier");
279
+ var SETTINGS = {
280
+ version: { key: "version", read: oneOf(CONFIG_VERSIONS), cli: false },
281
+ store: { key: "store", read: oneOf(STORE_BACKENDS), cli: true },
282
+ inject: { key: "plan.inject", read: oneOf(INJECT_MODES), cli: true },
283
+ planDir: { key: "plan.dir", read: directory, cli: true },
284
+ specsDir: { key: "specs.dir", read: directory, cli: true },
285
+ trackerDefault: { key: "tracker.default", read: trackerKind, cli: true },
286
+ trackerFallback: {
287
+ key: "tracker.fallback",
288
+ read: listOf(trackerKind, "tracker kind names"),
289
+ cli: false
290
+ },
291
+ learningAdapter: {
292
+ key: "learning.adapter",
293
+ read: text("a learning adapter name"),
294
+ cli: true
295
+ },
296
+ learningBlessMinConfidence: { key: "learning.bless.minConfidence", read: confidence, cli: false },
297
+ learningPromoteAfter: { key: "learning.promote.after", read: recurrenceCount, cli: false },
298
+ learningPromoteMinConfidence: { key: "learning.promote.minConfidence", read: confidence, cli: false },
299
+ outputMode: { key: "output.mode", read: oneOf(OUTPUT_MODES), cli: true },
300
+ prerequisitesRequired: {
301
+ key: "prerequisites.required",
302
+ read: listOf(requiredPrerequisite, "prerequisite items"),
303
+ cli: false,
304
+ itemKeys: REQUIRED_ITEM_KEYS
305
+ },
306
+ prerequisitesOptional: {
307
+ key: "prerequisites.optional",
308
+ read: listOf(optionalPrerequisite, "prerequisite items"),
309
+ cli: false,
310
+ itemKeys: OPTIONAL_ITEM_KEYS
311
+ },
312
+ trackingSpecs: { key: "tracking.specs", read: flag, cli: false },
313
+ trackingPlans: { key: "tracking.plans", read: flag, cli: false },
314
+ trackingAll: { key: "tracking.all", read: flag, cli: false },
315
+ modules: {
316
+ key: "modules",
317
+ read: listOf(moduleSource, "module sources"),
318
+ cli: false,
319
+ itemKeys: MODULE_SOURCE_KEYS
320
+ },
321
+ allowList: {
322
+ key: "allowList",
323
+ read: listOf(text("a module name"), "module names"),
324
+ cli: false
325
+ },
326
+ settingSources: {
327
+ key: "loop.settingSources",
328
+ read: subsetOf(CLAUDE_SETTING_SOURCES),
329
+ cli: true
330
+ },
331
+ prProvider: { key: "pr.provider", read: oneOf(PR_PROVIDERS), cli: false },
332
+ prMergeMethod: { key: "pr.mergeMethod", read: mergeMethod, cli: false },
333
+ prBase: { key: "pr.base", read: text("a branch name"), cli: false },
334
+ prResolveBudget: { key: "pr.resolveBudget", read: usdAmount, cli: false },
335
+ boardTrustedAuthors: {
336
+ key: "board.trustedAuthors",
337
+ read: listOf(githubLogin, "GitHub logins"),
338
+ cli: false
339
+ },
340
+ roadmapIssue: { key: "roadmap.issue", read: issueNumber, cli: false },
341
+ releaseEnabled: { key: "release.enabled", read: releaseEnabled, cli: false },
342
+ releaseVersionFile: {
343
+ key: "release.versionFile",
344
+ read: releaseFile,
345
+ cli: false
346
+ },
347
+ releaseChangelog: { key: "release.changelog", read: releaseFile, cli: false },
348
+ releaseHeading: {
349
+ key: "release.heading",
350
+ read: text("a changelog heading template"),
351
+ cli: false
352
+ },
353
+ cleanupStaleDays: { key: "cleanup.staleDays", read: dayCount, cli: false },
354
+ cleanupWorktreeIdleDays: {
355
+ key: "cleanup.worktreeIdleDays",
356
+ read: dayCount,
357
+ cli: false
358
+ },
359
+ cleanupKeep: {
360
+ key: "cleanup.keep",
361
+ read: listOf(text("a glob pattern"), "glob patterns"),
362
+ cli: false
363
+ },
364
+ dangerousAcceptStaleRefs: {
365
+ key: "dangerous.acceptStaleRefs",
366
+ read: flag,
367
+ cli: false
368
+ },
369
+ statusNotice: { key: "status.notice", read: flag, cli: false },
370
+ tiersRafa: { key: "tiers.rafa", read: tierSwitch, cli: false },
371
+ tiersSkills: { key: "tiers.skills", read: tierPins, cli: false },
372
+ tiersAgents: { key: "tiers.agents", read: tierPins, cli: false },
373
+ routing: {
374
+ key: "routing",
375
+ read: mapOf(routeTarget, "false or an agent name"),
376
+ cli: false
377
+ }
378
+ };
379
+ var SETTING_NAMES = Object.keys(SETTINGS);
380
+ var SETTING_BY_KEY = new Map(SETTING_NAMES.map((setting) => [
381
+ SETTINGS[setting].key,
382
+ setting
383
+ ]));
384
+ function isCommandLineSetting(setting) {
385
+ return SETTINGS[setting].cli;
386
+ }
387
+ function sectionsOf(key) {
388
+ const parts = key.split(".");
389
+ return parts.slice(1).map((_, index) => parts.slice(0, index + 1).join("."));
390
+ }
391
+ var SECTIONS = new Set([...SETTING_BY_KEY.keys()].flatMap(sectionsOf));
392
+ function parentOf(key) {
393
+ const cut = key.lastIndexOf(".");
394
+ return cut < 0 ? "" : key.slice(0, cut);
395
+ }
396
+ var TRAILING_INDEX = /\[\d+\]$/;
397
+ var KNOWN_UNDER = (() => {
398
+ const known = new Map;
399
+ const add = (parent, name) => {
400
+ const names = known.get(parent) ?? [];
401
+ if (!names.includes(name))
402
+ known.set(parent, [...names, name]);
403
+ };
404
+ for (const setting of SETTING_NAMES) {
405
+ const { key, itemKeys } = SETTINGS[setting];
406
+ const parts = key.split(".");
407
+ parts.forEach((part, index) => add(parts.slice(0, index).join("."), part));
408
+ for (const name of itemKeys ?? [])
409
+ add(`${key}[]`, name);
410
+ }
411
+ return known;
412
+ })();
413
+ function knownKeysAbove(key) {
414
+ for (let parent = parentOf(key);parent !== ""; parent = parentOf(parent)) {
415
+ const names = KNOWN_UNDER.get(parent.replace(TRAILING_INDEX, "[]"));
416
+ if (names !== undefined)
417
+ return [parent, names];
418
+ }
419
+ return ["", KNOWN_UNDER.get("") ?? []];
420
+ }
421
+
422
+ // src/config.ts
423
+ import { join as join2 } from "path";
424
+ class ConfigError extends Error {
425
+ problems;
426
+ constructor(problems, options) {
427
+ super(`rafa config: ${problems.join("; ")}`, options);
428
+ this.name = "ConfigError";
429
+ this.problems = problems;
430
+ }
431
+ }
432
+ function readLayer(rawOf, atOf) {
433
+ const problems = [];
434
+ const extras = [];
435
+ const read = (setting) => {
436
+ const raw = rawOf(setting);
437
+ if (raw === undefined)
438
+ return;
439
+ const reading = SETTINGS[setting].read(raw, atOf(setting));
440
+ problems.push(...reading.problems);
441
+ extras.push(...reading.extras);
442
+ return reading.value;
443
+ };
444
+ const layer = {
445
+ version: read("version"),
446
+ store: read("store"),
447
+ inject: read("inject"),
448
+ planDir: read("planDir"),
449
+ specsDir: read("specsDir"),
450
+ trackerDefault: read("trackerDefault"),
451
+ trackerFallback: read("trackerFallback"),
452
+ learningAdapter: read("learningAdapter"),
453
+ learningBlessMinConfidence: read("learningBlessMinConfidence"),
454
+ learningPromoteAfter: read("learningPromoteAfter"),
455
+ learningPromoteMinConfidence: read("learningPromoteMinConfidence"),
456
+ outputMode: read("outputMode"),
457
+ prerequisitesRequired: read("prerequisitesRequired"),
458
+ prerequisitesOptional: read("prerequisitesOptional"),
459
+ trackingSpecs: read("trackingSpecs"),
460
+ trackingPlans: read("trackingPlans"),
461
+ trackingAll: read("trackingAll"),
462
+ modules: read("modules"),
463
+ allowList: read("allowList"),
464
+ settingSources: read("settingSources"),
465
+ prProvider: read("prProvider"),
466
+ prMergeMethod: read("prMergeMethod"),
467
+ prBase: read("prBase"),
468
+ prResolveBudget: read("prResolveBudget"),
469
+ boardTrustedAuthors: read("boardTrustedAuthors"),
470
+ roadmapIssue: read("roadmapIssue"),
471
+ releaseEnabled: read("releaseEnabled"),
472
+ releaseVersionFile: read("releaseVersionFile"),
473
+ releaseChangelog: read("releaseChangelog"),
474
+ releaseHeading: read("releaseHeading"),
475
+ cleanupStaleDays: read("cleanupStaleDays"),
476
+ cleanupWorktreeIdleDays: read("cleanupWorktreeIdleDays"),
477
+ cleanupKeep: read("cleanupKeep"),
478
+ dangerousAcceptStaleRefs: read("dangerousAcceptStaleRefs"),
479
+ statusNotice: read("statusNotice"),
480
+ tiersRafa: read("tiersRafa"),
481
+ tiersSkills: read("tiersSkills"),
482
+ tiersAgents: read("tiersAgents"),
483
+ routing: read("routing")
484
+ };
485
+ return { layer, problems, extras };
486
+ }
487
+ function* entriesOf(mapping, prefix) {
488
+ for (const [name, value] of Object.entries(mapping)) {
489
+ const key = prefix === "" ? name : `${prefix}.${name}`;
490
+ if (SECTIONS.has(key) && isMapping(value))
491
+ yield* entriesOf(value, key);
492
+ else
493
+ yield { key, value };
494
+ }
495
+ }
496
+ function sortKeys(document, path) {
497
+ const named = new Map;
498
+ const seen = new Set;
499
+ const extras = [];
500
+ const problems = [];
501
+ for (const entry of entriesOf(document, "")) {
502
+ const setting = SETTING_BY_KEY.get(entry.key);
503
+ if (setting !== undefined) {
504
+ if (seen.has(setting)) {
505
+ problems.push(`${path}: ${entry.key} is given more than once`);
506
+ } else if (entry.value !== null) {
507
+ named.set(setting, entry.value);
508
+ }
509
+ seen.add(setting);
510
+ continue;
511
+ }
512
+ if (!SECTIONS.has(entry.key)) {
513
+ extras.push(entry);
514
+ continue;
515
+ }
516
+ if (entry.value !== null) {
517
+ const found = describeValue(entry.value);
518
+ problems.push(`${path}: ${entry.key} must be a mapping, found ${found}`);
519
+ }
520
+ }
521
+ return { named, extras, problems };
522
+ }
523
+ function parseYaml(text2, path) {
524
+ try {
525
+ return Bun.YAML.parse(text2);
526
+ } catch (error) {
527
+ const problem = `${path}: not valid YAML (${messageOf(error)})`;
528
+ throw new ConfigError([problem], { cause: error });
529
+ }
530
+ }
531
+ function configFilePath(root) {
532
+ return join2(root, CONFIG_FILE);
533
+ }
534
+ function parseConfigText(text2, path) {
535
+ const document = parseYaml(text2, path) ?? {};
536
+ if (!isMapping(document)) {
537
+ const found = describeValue(document);
538
+ throw new ConfigError([
539
+ `${path}: expected a mapping at the top level, found ${found}`
540
+ ]);
541
+ }
542
+ const sorted = sortKeys(document, path);
543
+ const read = readLayer((setting) => sorted.named.get(setting), (setting) => ({
544
+ label: `${path}: ${SETTINGS[setting].key}`,
545
+ key: SETTINGS[setting].key
546
+ }));
547
+ const all = [...sorted.problems, ...read.problems];
548
+ if (all.length > 0)
549
+ throw new ConfigError(all);
550
+ return { path, values: read.layer, extras: [...sorted.extras, ...read.extras] };
551
+ }
552
+ var MAP_SETTINGS = new Set([
553
+ "tiersSkills",
554
+ "tiersAgents",
555
+ "routing"
556
+ ]);
557
+ function mergeMaps(...layers) {
558
+ const merged = new Map;
559
+ for (const layer of layers) {
560
+ if (!(layer instanceof Map))
561
+ continue;
562
+ for (const [name, value] of layer)
563
+ merged.set(name, value);
564
+ }
565
+ return merged;
566
+ }
567
+ function unloadedPinWarnings(layer) {
568
+ const pins = [
569
+ ["tiers.skills", layer.values.tiersSkills],
570
+ ["tiers.agents", layer.values.tiersAgents]
571
+ ];
572
+ 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"));
573
+ }
574
+ function unknownKeyWarning(key, path) {
575
+ const quoted = JSON.stringify(key);
576
+ const [under, names] = knownKeysAbove(key);
577
+ const where = under === "" ? "" : ` under ${under}`;
578
+ return `rafa config: unknown key ${quoted} in ${path} has no effect ` + `in this version (known keys${where}: ${names.join(", ")})`;
579
+ }
580
+ function resolveConfig(layers = {}) {
581
+ const file = layers.file ?? null;
582
+ const user = layers.user ?? null;
583
+ const overrides = layers.cli ?? {};
584
+ const { layer: cli, problems } = readLayer((setting) => isCommandLineSetting(setting) ? overrides[setting] : undefined, (setting) => ({ label: `command line: ${setting}`, key: setting }));
585
+ if (problems.length > 0)
586
+ throw new ConfigError(problems);
587
+ const rank = (setting) => {
588
+ const fromCli = cli[setting];
589
+ if (fromCli !== undefined)
590
+ return { value: fromCli, source: "cli" };
591
+ const fromFile = file?.values[setting];
592
+ const fromUser = user?.values[setting];
593
+ if (MAP_SETTINGS.has(setting)) {
594
+ const merged = mergeMaps(CONFIG_DEFAULTS[setting], fromUser, fromFile);
595
+ const source = fromFile !== undefined ? "file" : fromUser !== undefined ? "user" : "default";
596
+ return { value: merged, source };
597
+ }
598
+ if (fromFile !== undefined)
599
+ return { value: fromFile, source: "file" };
600
+ if (fromUser !== undefined)
601
+ return { value: fromUser, source: "user" };
602
+ return { value: CONFIG_DEFAULTS[setting], source: "default" };
603
+ };
604
+ const ranked = SETTING_NAMES.map((setting) => [setting, rank(setting)]);
605
+ const rafaLoaded = ranked.find(([setting]) => setting === "tiersRafa")?.[1].value !== "off";
606
+ const warnings = [user, file].flatMap((layer) => layer === null ? [] : [
607
+ ...layer.extras.map((extra) => unknownKeyWarning(extra.key, layer.path)),
608
+ ...rafaLoaded ? [] : unloadedPinWarnings(layer)
609
+ ]);
610
+ return {
611
+ config: Object.fromEntries(ranked.map(([setting, { value }]) => [setting, value])),
612
+ sources: Object.fromEntries(ranked.map(([setting, { source }]) => [setting, source])),
613
+ path: file?.path ?? null,
614
+ userPath: user?.path ?? null,
615
+ extras: file?.extras ?? [],
616
+ userExtras: user?.extras ?? [],
617
+ warnings
618
+ };
619
+ }
620
+
621
+ // src/plan/blocks.ts
622
+ var RAFA_BLOCK_KINDS = [
623
+ "plan",
624
+ "context",
625
+ "stage-context",
626
+ "report"
627
+ ];
628
+ var RAFA_PREFIX = "rafa:";
629
+ var FENCE_RUN = /^( {0,3})(`{3,}|~{3,})/;
630
+ var CLOSING_FENCE = /^ {0,3}(`{3,}|~{3,})[ \t]*$/;
631
+ function isRafaBlockKind(kind) {
632
+ return RAFA_BLOCK_KINDS.includes(kind);
633
+ }
634
+ function linesOf(markdown) {
635
+ const lines = markdown.split(`
636
+ `);
637
+ if (lines[lines.length - 1] === "")
638
+ lines.pop();
639
+ return lines.map((line) => line.endsWith("\r") ? line.slice(0, -1) : line);
640
+ }
641
+ function kindOf(info) {
642
+ const word = info.trim().split(/\s+/)[0] ?? "";
643
+ return word.startsWith(RAFA_PREFIX) ? word.slice(RAFA_PREFIX.length) : null;
644
+ }
645
+ function openingFence(line) {
646
+ const match = FENCE_RUN.exec(line);
647
+ const opener = match?.[0];
648
+ const indent = match?.[1];
649
+ const run = match?.[2];
650
+ if (opener === undefined || indent === undefined || run === undefined) {
651
+ return null;
652
+ }
653
+ const marker = run.charAt(0);
654
+ const info = line.slice(opener.length);
655
+ if (marker === "`" && info.includes("`"))
656
+ return null;
657
+ return { marker, length: run.length, indent: indent.length, kind: kindOf(info) };
658
+ }
659
+ function closes(line, fence) {
660
+ const run = CLOSING_FENCE.exec(line)?.[1];
661
+ return run !== undefined && run.charAt(0) === fence.marker && run.length >= fence.length;
662
+ }
663
+ function closingIndex(lines, from, fence) {
664
+ for (let index = from;index < lines.length; index += 1) {
665
+ if (closes(lines[index] ?? "", fence))
666
+ return index;
667
+ }
668
+ return lines.length;
669
+ }
670
+ function dedent(line, indent) {
671
+ let cut = 0;
672
+ while (cut < indent && line.charAt(cut) === " ")
673
+ cut += 1;
674
+ return line.slice(cut);
675
+ }
676
+ function readRafaBlocks(markdown) {
677
+ const lines = linesOf(markdown);
678
+ const blocks = [];
679
+ let index = 0;
680
+ while (index < lines.length) {
681
+ const fence = openingFence(lines[index] ?? "");
682
+ if (fence === null) {
683
+ index += 1;
684
+ continue;
685
+ }
686
+ const closing = closingIndex(lines, index + 1, fence);
687
+ const closed = closing < lines.length;
688
+ if (fence.kind !== null) {
689
+ const body = lines.slice(index + 1, closing).map((line) => dedent(line, fence.indent)).join(`
690
+ `);
691
+ const last = closed ? closing + 1 : lines.length;
692
+ blocks.push({ kind: fence.kind, body, span: { first: index + 1, last }, closed });
693
+ }
694
+ index = closing + 1;
695
+ }
696
+ return blocks;
697
+ }
698
+
699
+ // src/report/parse.ts
700
+ var REPORT_STATUSES = ["done", "blocked"];
701
+ var FINDING_KINDS = ["gotcha", "pattern", "location", "skill-suggestion"];
702
+ var FINDING_SIGNALS = ["loud", "silent"];
703
+ var FINDING_DOMAINS = [
704
+ "code-style",
705
+ "testing",
706
+ "git",
707
+ "debugging",
708
+ "workflow",
709
+ "security",
710
+ "general-best-practices"
711
+ ];
712
+ var CHANGE_LEVELS = ["patch", "minor", "major", "none"];
713
+ var REPORT_KIND = "report";
714
+ var REQUIRED = true;
715
+ var OPTIONAL = false;
716
+ var REPORT_KEYS = [
717
+ "status",
718
+ "feedback",
719
+ "findings",
720
+ "skills_used",
721
+ "blockers",
722
+ "out_of_scope_bugs",
723
+ "changes"
724
+ ];
725
+ var FINDING_KEYS = [
726
+ "trigger",
727
+ "kind",
728
+ "what",
729
+ "cause",
730
+ "resolution",
731
+ "artifact",
732
+ "signal",
733
+ "domain"
734
+ ];
735
+ var BLOCKER_KEYS = ["what", "artifact"];
736
+ var BUG_KEYS = ["what", "artifact", "security"];
737
+ var CHANGE_KEYS = ["level", "area", "summary"];
738
+ function isMapping2(value) {
739
+ return typeof value === "object" && value !== null && !Array.isArray(value);
740
+ }
741
+ function isOneOf(choices, value) {
742
+ return choices.includes(value);
743
+ }
744
+ function describeValue2(value) {
745
+ if (typeof value === "string")
746
+ return JSON.stringify(value);
747
+ if (value === null)
748
+ return "nothing";
749
+ if (Array.isArray(value))
750
+ return "a list";
751
+ if (typeof value === "object")
752
+ return "a mapping";
753
+ return `the ${typeof value} ${String(value)}`;
754
+ }
755
+ function messageOf2(error) {
756
+ return error instanceof Error ? error.message : String(error);
757
+ }
758
+ function reportIssue(reason, field, text2) {
759
+ return { reason, field, text: text2 };
760
+ }
761
+ function absent(reason, block, text2) {
762
+ return { present: false, reason, block, text: text2 };
763
+ }
764
+ function scopeOf(mapping, keys, prefix, issues) {
765
+ const fields = new Map;
766
+ const extras = [];
767
+ for (const [key, value] of Object.entries(mapping)) {
768
+ if (keys.includes(key))
769
+ fields.set(key, value);
770
+ else
771
+ extras.push({ key, value });
772
+ }
773
+ return { fields, extras, prefix, issues };
774
+ }
775
+ function valueOf(scope, key, required) {
776
+ const value = scope.fields.get(key) ?? null;
777
+ if (value === null && required) {
778
+ const field = `${scope.prefix}${key}`;
779
+ scope.issues.push(reportIssue("missing-field", field, `${field} is missing`));
780
+ }
781
+ return value;
782
+ }
783
+ function unusable(scope, key, why) {
784
+ const field = `${scope.prefix}${key}`;
785
+ scope.issues.push(reportIssue("unusable-field", field, `${field} ${why}`));
786
+ return null;
787
+ }
788
+ function stringProblem(value) {
789
+ if (typeof value !== "string")
790
+ return `is ${describeValue2(value)}, not a string; quote it`;
791
+ if (value.trim().length === 0)
792
+ return "is blank";
793
+ return null;
794
+ }
795
+ function stringField(scope, key, required) {
796
+ const value = valueOf(scope, key, required);
797
+ if (value === null)
798
+ return null;
799
+ const problem = stringProblem(value);
800
+ return problem === null ? value : unusable(scope, key, problem);
801
+ }
802
+ function choiceField(scope, key, choices, required) {
803
+ const value = valueOf(scope, key, required);
804
+ if (value === null || isOneOf(choices, value))
805
+ return value;
806
+ return unusable(scope, key, `is ${describeValue2(value)}, not one of ${choices.join(", ")}`);
807
+ }
808
+ function booleanField(scope, key, required) {
809
+ const value = valueOf(scope, key, required);
810
+ if (value === null || typeof value === "boolean")
811
+ return value;
812
+ return unusable(scope, key, `is ${describeValue2(value)}, not true or false; write it unquoted`);
813
+ }
814
+ function listField(scope, key, readEntry) {
815
+ const value = valueOf(scope, key, OPTIONAL);
816
+ if (value === null)
817
+ return [];
818
+ if (!Array.isArray(value)) {
819
+ unusable(scope, key, `is ${describeValue2(value)}, not a list`);
820
+ return [];
821
+ }
822
+ const entries = [];
823
+ const items = value;
824
+ for (const [index, item] of items.entries()) {
825
+ const entry = readEntry(item, `${scope.prefix}${key}[${index}]`, scope.issues);
826
+ if (entry !== null)
827
+ entries.push(entry);
828
+ }
829
+ return entries;
830
+ }
831
+ function entryScope(item, field, keys, issues) {
832
+ if (isMapping2(item))
833
+ return scopeOf(item, keys, `${field}.`, issues);
834
+ const text2 = `${field} is ${describeValue2(item)}, not a mapping of ${keys.join(", ")}; dropped`;
835
+ issues.push(reportIssue("unusable-field", field, text2));
836
+ return null;
837
+ }
838
+ function readSkill(item, field, issues) {
839
+ const problem = stringProblem(item);
840
+ if (problem === null)
841
+ return item;
842
+ issues.push(reportIssue("unusable-field", field, `${field} ${problem}; dropped`));
843
+ return null;
844
+ }
845
+ function readFinding(item, field, issues) {
846
+ const scope = entryScope(item, field, FINDING_KEYS, issues);
847
+ if (scope === null)
848
+ return null;
849
+ return {
850
+ trigger: stringField(scope, "trigger", REQUIRED),
851
+ kind: choiceField(scope, "kind", FINDING_KINDS, REQUIRED),
852
+ what: stringField(scope, "what", REQUIRED),
853
+ cause: stringField(scope, "cause", OPTIONAL),
854
+ resolution: stringField(scope, "resolution", OPTIONAL),
855
+ artifact: stringField(scope, "artifact", OPTIONAL),
856
+ signal: choiceField(scope, "signal", FINDING_SIGNALS, REQUIRED),
857
+ domain: choiceField(scope, "domain", FINDING_DOMAINS, OPTIONAL),
858
+ extras: scope.extras
859
+ };
860
+ }
861
+ function readBlocker(item, field, issues) {
862
+ const scope = entryScope(item, field, BLOCKER_KEYS, issues);
863
+ if (scope === null)
864
+ return null;
865
+ return {
866
+ what: stringField(scope, "what", REQUIRED),
867
+ artifact: stringField(scope, "artifact", OPTIONAL),
868
+ extras: scope.extras
869
+ };
870
+ }
871
+ function readBug(item, field, issues) {
872
+ const scope = entryScope(item, field, BUG_KEYS, issues);
873
+ if (scope === null)
874
+ return null;
875
+ return {
876
+ what: stringField(scope, "what", REQUIRED),
877
+ artifact: stringField(scope, "artifact", OPTIONAL),
878
+ security: booleanField(scope, "security", REQUIRED),
879
+ extras: scope.extras
880
+ };
881
+ }
882
+ function readChange(item, field, issues) {
883
+ const scope = entryScope(item, field, CHANGE_KEYS, issues);
884
+ if (scope === null)
885
+ return null;
886
+ return {
887
+ level: choiceField(scope, "level", CHANGE_LEVELS, REQUIRED),
888
+ area: stringField(scope, "area", OPTIONAL),
889
+ summary: stringField(scope, "summary", REQUIRED),
890
+ extras: scope.extras
891
+ };
892
+ }
893
+ function readReport(document, issues) {
894
+ const scope = scopeOf(document, REPORT_KEYS, "", issues);
895
+ return {
896
+ status: choiceField(scope, "status", REPORT_STATUSES, REQUIRED),
897
+ feedback: stringField(scope, "feedback", OPTIONAL),
898
+ findings: listField(scope, "findings", readFinding),
899
+ skillsUsed: listField(scope, "skills_used", readSkill),
900
+ blockers: listField(scope, "blockers", readBlocker),
901
+ outOfScopeBugs: listField(scope, "out_of_scope_bugs", readBug),
902
+ changes: listField(scope, "changes", readChange),
903
+ extras: scope.extras
904
+ };
905
+ }
906
+ function parseReport(output) {
907
+ const reports = readRafaBlocks(output).filter((block2) => block2.kind === REPORT_KIND);
908
+ const block = reports.at(-1);
909
+ if (block === undefined) {
910
+ return absent("no-block", null, "the session output holds no rafa:report block");
911
+ }
912
+ const line = block.span.first;
913
+ if (!block.closed) {
914
+ 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");
915
+ }
916
+ let document;
917
+ try {
918
+ document = Bun.YAML.parse(block.body);
919
+ } catch (error) {
920
+ const text2 = `rafa:report block at line ${line} is not valid YAML (${messageOf2(error)})`;
921
+ return absent("malformed-block", block, text2);
922
+ }
923
+ if (!isMapping2(document)) {
924
+ const text2 = `rafa:report block at line ${line} holds ${describeValue2(document)}, ` + "not a mapping of report fields";
925
+ return absent("malformed-block", block, text2);
926
+ }
927
+ const issues = [];
928
+ const report = readReport(document, issues);
929
+ return { present: true, report, block, issues };
930
+ }
931
+
932
+ // src/adapters/output/text.ts
933
+ var DEBUG_VERBOSITY = 2;
934
+ function formatPayload(payload) {
935
+ return typeof payload === "string" ? payload : JSON.stringify(payload);
936
+ }
937
+ function resultLine(event) {
938
+ if (event.ok) {
939
+ return event.data === undefined ? "result: ok" : `result: ok ${formatPayload(event.data)}`;
940
+ }
941
+ const code = event.error?.code ?? "unknown";
942
+ const message = event.error?.message ?? "";
943
+ return message === "" ? `result: error ${code}` : `result: error ${code}: ${message}`;
944
+ }
945
+ function createTextOutput({ verbosity, stream }) {
946
+ const writeLine = (line) => {
947
+ stream.write(`${line}
948
+ `);
949
+ };
950
+ const debug = (message) => {
951
+ if (verbosity >= DEBUG_VERBOSITY)
952
+ writeLine(`debug: ${message}`);
953
+ };
954
+ const info = (message) => {
955
+ writeLine(message);
956
+ };
957
+ const warn = (message) => {
958
+ writeLine(`warn: ${message}`);
959
+ };
960
+ const error = (message) => {
961
+ writeLine(`error: ${message}`);
962
+ };
963
+ const result = (payload) => {
964
+ writeLine(`result: ${formatPayload(payload)}`);
965
+ };
966
+ const writeLog = (event) => {
967
+ switch (event.level) {
968
+ case "debug": {
969
+ debug(event.message);
970
+ return;
971
+ }
972
+ case "info": {
973
+ info(event.message);
974
+ return;
975
+ }
976
+ case "warn": {
977
+ warn(event.message);
978
+ return;
979
+ }
980
+ case "error": {
981
+ error(event.message);
982
+ return;
983
+ }
984
+ }
985
+ };
986
+ const emit = (event) => {
987
+ switch (event.type) {
988
+ case "start": {
989
+ writeLine(`start: ${event.command}`);
990
+ return;
991
+ }
992
+ case "step": {
993
+ writeLine(`step: ${event.name}`);
994
+ return;
995
+ }
996
+ case "log": {
997
+ writeLog(event);
998
+ return;
999
+ }
1000
+ case "result": {
1001
+ writeLine(resultLine(event));
1002
+ return;
1003
+ }
1004
+ }
1005
+ };
1006
+ return Object.freeze({ info, warn, error, debug, emit, result });
1007
+ }
1008
+
1009
+ // src/adapters/output/active.ts
1010
+ var DEFAULT_OUTPUT = createTextOutput({ verbosity: 0, stream: process.stdout });
1011
+ var active = DEFAULT_OUTPUT;
1012
+ var activeMode = "text";
1013
+ function activeOutput() {
1014
+ return active;
1015
+ }
1016
+ function activeOutputMode() {
1017
+ return activeMode;
1018
+ }
1019
+
1020
+ export { pinKey, pinLine, collisionMessage, readItemBytes, resolveTiers, findTierItem, DEFAULT_ROUTES, DEFAULT_ROUTING, CONFIG_FILE, CONFIG_DEFAULTS, SETTINGS, ConfigError, configFilePath, parseConfigText, resolveConfig, RAFA_BLOCK_KINDS, isRafaBlockKind, readRafaBlocks, REPORT_STATUSES, FINDING_KINDS, FINDING_SIGNALS, FINDING_DOMAINS, CHANGE_LEVELS, parseReport, createTextOutput, activeOutput, activeOutputMode };