@open-tomato/rafa 0.40.0 → 0.42.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,48 +1,451 @@
1
1
  // @bun
2
2
  import {
3
- BOARD_PROJECT_TEMPLATE_DEFAULT,
4
- BOARD_RELATIONSHIP_MODES,
5
- CLAUDE_SETTING_SOURCES,
6
- CONFIG_VERSIONS,
7
- INJECT_MODES,
8
- OUTPUT_MODES,
9
- PR_PROVIDERS,
10
- RELEASE_AUTO,
11
- SKILL_TIERS,
12
- STORE_BACKENDS,
13
- SYNC_STRATEGIES,
14
- below,
15
- busyTimeoutMs,
16
- claimsAhead,
17
- claimsStaleAfter,
18
- confidence,
19
- dayCount,
20
- describeValue,
21
- flag,
22
- githubLogin,
23
- isMapping,
24
- isSkillTier,
25
- issueNumber,
26
- lessonSwitch,
27
- listOf,
28
- mapReading,
29
- mergeMethod,
30
- messageOf,
31
- oneOf,
32
- projectNumber,
33
- projectUrl,
34
- recurrenceCount,
35
- refused,
36
- refusedWith,
37
- releaseEnabled,
38
- routeTarget,
39
- skillResolverName,
40
- subsetOf,
41
- text,
42
- tierPin,
43
- tierSwitch,
44
- usdAmount
45
- } from "./index-m2ahxjw8.js";
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
+ }
46
449
 
47
450
  // src/config-items.ts
48
451
  var itemText = text("a non-empty string");
@@ -117,7 +520,7 @@ var moduleSource = (raw, at) => {
117
520
  };
118
521
 
119
522
  // src/config-schema.ts
120
- import { join } from "path";
523
+ import { join as join3 } from "path";
121
524
 
122
525
  // src/config-readers.ts
123
526
  import { isAbsolute, normalize } from "path";
@@ -289,6 +692,52 @@ function hubContextOf(config) {
289
692
  return Object.freeze({ url, tokenSecret: config.hubTokenSecret ?? null, timeoutMs });
290
693
  }
291
694
 
695
+ // src/config-schema-loop-continue.ts
696
+ import { join as join2 } from "path";
697
+ var RETRIES_ON_CONTINUE_MIN = 1;
698
+ var RETRIES_ON_CONTINUE_MAX = 3;
699
+ var FORCE_WRAP_UP_MAX_NEW_FAILURES_MIN = 1;
700
+ var FORCE_WRAP_UP_MAX_NEW_FAILURES_MAX = 50;
701
+ var CONTINUE_CRITERIA_MODES = ["extend", "replace"];
702
+ function countOrFalse(min, max) {
703
+ const expected = `false or a whole number from ${String(min)} to ${String(max)}`;
704
+ return (raw, at) => raw === false || typeof raw === "number" && Number.isSafeInteger(raw) && raw >= min && raw <= max ? { value: raw, problems: [], extras: [] } : refused(at, raw, expected);
705
+ }
706
+ var retriesOnContinue = countOrFalse(RETRIES_ON_CONTINUE_MIN, RETRIES_ON_CONTINUE_MAX);
707
+ var forceWrapUpMaxNewFailures = countOrFalse(FORCE_WRAP_UP_MAX_NEW_FAILURES_MIN, FORCE_WRAP_UP_MAX_NEW_FAILURES_MAX);
708
+ var continueCriteria = text("a file path");
709
+ var continueCriteriaMode = oneOf(CONTINUE_CRITERIA_MODES);
710
+ var LOOP_CONTINUE_DEFAULTS = Object.freeze({
711
+ loopRetriesOnContinue: 1,
712
+ loopContinueCriteria: join2(".rafa", "continue-criteria.md"),
713
+ loopContinueCriteriaMode: "extend",
714
+ loopForceWrapUpMaxNewFailures: false
715
+ });
716
+ var LOOP_CONTINUE_SETTINGS = {
717
+ loopRetriesOnContinue: { key: "loop.retriesOnContinue", read: retriesOnContinue, cli: false },
718
+ loopContinueCriteria: { key: "loop.continue.criteria", read: continueCriteria, cli: false },
719
+ loopContinueCriteriaMode: { key: "loop.continue.criteriaMode", read: continueCriteriaMode, cli: false },
720
+ loopForceWrapUpMaxNewFailures: {
721
+ key: "loop.forceWrapUp.maxNewFailures",
722
+ read: forceWrapUpMaxNewFailures,
723
+ cli: false
724
+ }
725
+ };
726
+
727
+ // src/config-schema-loop-retries.ts
728
+ var LOOP_RETRIES_MIN = 1;
729
+ var LOOP_RETRIES_MAX = 3;
730
+ function isLoopRetryCount(raw) {
731
+ return typeof raw === "number" && Number.isSafeInteger(raw) && raw >= LOOP_RETRIES_MIN && raw <= LOOP_RETRIES_MAX;
732
+ }
733
+ 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)}`);
734
+ var LOOP_RETRIES_DEFAULTS = Object.freeze({
735
+ loopRetries: false
736
+ });
737
+ var LOOP_RETRIES_SETTINGS = {
738
+ loopRetries: { key: "loop.retries", read: loopRetries, cli: false }
739
+ };
740
+
292
741
  // src/config-schema-release.ts
293
742
  var PR_DEFAULTS = Object.freeze({
294
743
  prProvider: null,
@@ -596,7 +1045,7 @@ var DEFAULT_ROUTES = Object.freeze([
596
1045
  var DEFAULT_ROUTING = new Map(DEFAULT_ROUTES);
597
1046
 
598
1047
  // src/config-schema.ts
599
- var CONFIG_FILE = join(".rafa", "config.yaml");
1048
+ var CONFIG_FILE = join3(".rafa", "config.yaml");
600
1049
  var CONFIG_DEFAULTS = Object.freeze({
601
1050
  version: 1,
602
1051
  store: "sqlite",
@@ -604,8 +1053,8 @@ var CONFIG_DEFAULTS = Object.freeze({
604
1053
  effortSync: "local",
605
1054
  ...HUB_DEFAULTS,
606
1055
  inject: "stage",
607
- planDir: join(".rafa", "plans"),
608
- specsDir: join(".rafa", "specs"),
1056
+ planDir: join3(".rafa", "plans"),
1057
+ specsDir: join3(".rafa", "specs"),
609
1058
  trackerDefault: "github",
610
1059
  trackerFallback: Object.freeze(["local"]),
611
1060
  learningAdapter: "local",
@@ -621,7 +1070,9 @@ var CONFIG_DEFAULTS = Object.freeze({
621
1070
  modules: Object.freeze([]),
622
1071
  allowList: Object.freeze([]),
623
1072
  settingSources: Object.freeze(["project", "local"]),
624
- loopWorktreeDir: join(".rafa", "worktrees"),
1073
+ loopWorktreeDir: join3(".rafa", "worktrees"),
1074
+ ...LOOP_RETRIES_DEFAULTS,
1075
+ ...LOOP_CONTINUE_DEFAULTS,
625
1076
  ...WRAP_UP_DEFAULTS,
626
1077
  ...PR_DEFAULTS,
627
1078
  boardTrustedAuthors: Object.freeze([]),
@@ -705,6 +1156,8 @@ var SETTINGS = {
705
1156
  cli: true
706
1157
  },
707
1158
  loopWorktreeDir: { key: "loop.worktreeDir", read: directory, cli: false },
1159
+ ...LOOP_RETRIES_SETTINGS,
1160
+ ...LOOP_CONTINUE_SETTINGS,
708
1161
  ...WRAP_UP_SETTINGS,
709
1162
  ...PR_SETTINGS,
710
1163
  boardTrustedAuthors: {
@@ -800,7 +1253,7 @@ function knownKeysAbove(key) {
800
1253
  }
801
1254
 
802
1255
  // src/config.ts
803
- import { join as join2 } from "path";
1256
+ import { join as join4 } from "path";
804
1257
  class ConfigError extends Error {
805
1258
  problems;
806
1259
  constructor(problems, options) {
@@ -848,6 +1301,11 @@ function readLayer(rawOf, atOf) {
848
1301
  allowList: read("allowList"),
849
1302
  settingSources: read("settingSources"),
850
1303
  loopWorktreeDir: read("loopWorktreeDir"),
1304
+ loopRetries: read("loopRetries"),
1305
+ loopRetriesOnContinue: read("loopRetriesOnContinue"),
1306
+ loopContinueCriteria: read("loopContinueCriteria"),
1307
+ loopContinueCriteriaMode: read("loopContinueCriteriaMode"),
1308
+ loopForceWrapUpMaxNewFailures: read("loopForceWrapUpMaxNewFailures"),
851
1309
  loopWrapUpRetries: read("loopWrapUpRetries"),
852
1310
  prProvider: read("prProvider"),
853
1311
  prMergeMethod: read("prMergeMethod"),
@@ -948,7 +1406,7 @@ function parseYaml(text2, path) {
948
1406
  }
949
1407
  }
950
1408
  function configFilePath(root) {
951
- return join2(root, CONFIG_FILE);
1409
+ return join4(root, CONFIG_FILE);
952
1410
  }
953
1411
  function parseConfigText(text2, path) {
954
1412
  const document = parseYaml(text2, path) ?? {};
@@ -1478,4 +1936,4 @@ function activeOutputMode() {
1478
1936
  return activeMode;
1479
1937
  }
1480
1938
 
1481
- export { hubContextOf, PREREQUISITE_KINDS, requiredPrerequisite, optionalPrerequisite, MODULE_SOURCE_KINDS, RELEASE_DEFAULTS, pinKey, pinLine, collisionMessage, readItemBytes, resolveTiers, findTierItem, DEFAULT_ROUTES, DEFAULT_ROUTING, CONFIG_FILE, CONFIG_DEFAULTS, SETTINGS, SETTING_NAMES, SETTING_BY_KEY, SECTIONS, knownKeysAbove, ConfigError, configFilePath, parseConfigText, resolveConfig, isStampableStub, planStubFromPrompt, stampPrompt, planStubFromPath, createTextOutput, activeOutput, activeOutputMode, RAFA_BLOCK_KINDS, isRafaBlockKind, readRafaBlocks, REPORT_STATUSES, FINDING_KINDS, FINDING_SIGNALS, FINDING_DOMAINS, CHANGE_LEVELS, parseReport };
1939
+ 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, isLoopRetryCount, RELEASE_DEFAULTS, pinKey, pinLine, collisionMessage, readItemBytes, resolveTiers, findTierItem, DEFAULT_ROUTING, CONFIG_FILE, CONFIG_DEFAULTS, SETTINGS, ConfigError, configFilePath, parseConfigText, resolveConfig, isStampableStub, planStubFromPrompt, stampPrompt, planStubFromPath, createTextOutput, activeOutput, activeOutputMode, RAFA_BLOCK_KINDS, isRafaBlockKind, readRafaBlocks, REPORT_STATUSES, FINDING_KINDS, FINDING_SIGNALS, FINDING_DOMAINS, CHANGE_LEVELS, parseReport };
@@ -1,6 +1,4 @@
1
1
  // @bun
2
- var __require = import.meta.require;
3
-
4
2
  // src/learning/identity.ts
5
3
  import { createHash } from "crypto";
6
4
  var GAP = 0.1;
@@ -15,4 +13,4 @@ function triggerKey(trigger) {
15
13
  return trigger.trim().toLowerCase().replace(WHITESPACE_RUN, " ");
16
14
  }
17
15
 
18
- export { __require, GAP, SOURCE_STEP, CONFIDENCE_MIN, CONFIDENCE_MAX, actionHash, triggerKey };
16
+ export { GAP, SOURCE_STEP, CONFIDENCE_MIN, CONFIDENCE_MAX, actionHash, triggerKey };
@@ -5,7 +5,7 @@ import {
5
5
  GAP,
6
6
  SOURCE_STEP,
7
7
  triggerKey
8
- } from "./index-wp8c28fd.js";
8
+ } from "./index-hf2k497d.js";
9
9
 
10
10
  // src/learning/bless.ts
11
11
  import { createHash } from "crypto";