@open-tomato/rafa 0.43.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.
- package/README.md +25 -19
- package/dist/bundled/skills/dev-planner/SKILL.md +25 -12
- package/dist/bundled/skills/documentation/SKILL.md +2 -12
- package/dist/cli.js +11580 -10370
- package/dist/effort/store/index.js +2 -2
- package/dist/index-mgh2s9bj.js +937 -0
- package/dist/{index-122k5m60.js → index-nsr78ct2.js} +6974 -5097
- package/dist/{index-57mmzkcn.js → index-r28fzsvc.js} +191 -77
- package/dist/index.js +1307 -1213
- package/dist/plan/index.js +2 -2
- package/dist/plan-prompt.md +8 -1
- package/package.json +2 -2
- package/dist/index-cknayrpc.js +0 -2414
|
@@ -0,0 +1,937 @@
|
|
|
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/core/config/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/core/config/config-readers.ts
|
|
451
|
+
import { isAbsolute, normalize } from "path";
|
|
452
|
+
function mapOf(value, expected) {
|
|
453
|
+
return (raw, at) => {
|
|
454
|
+
if (!isMapping(raw)) {
|
|
455
|
+
const problem = `${at.label} is ${describeValue(raw)}, expected a mapping of names to ${expected}`;
|
|
456
|
+
return { value: undefined, problems: [problem], extras: [] };
|
|
457
|
+
}
|
|
458
|
+
const readings = Object.entries(raw).map(([name, entry]) => [
|
|
459
|
+
name,
|
|
460
|
+
name.trim() === "" ? {
|
|
461
|
+
value: undefined,
|
|
462
|
+
problems: [`${at.label} names ${JSON.stringify(name)}, expected a non-empty name`],
|
|
463
|
+
extras: []
|
|
464
|
+
} : value(entry, { label: `${at.label}.${name}`, key: `${at.key}.${name}` })
|
|
465
|
+
]);
|
|
466
|
+
const problems = readings.flatMap(([, reading]) => reading.problems);
|
|
467
|
+
const extras = readings.flatMap(([, reading]) => reading.extras);
|
|
468
|
+
if (problems.length > 0)
|
|
469
|
+
return { value: undefined, problems, extras };
|
|
470
|
+
const read = new Map;
|
|
471
|
+
for (const [name, reading] of readings) {
|
|
472
|
+
if (reading.value !== undefined)
|
|
473
|
+
read.set(name, reading.value);
|
|
474
|
+
}
|
|
475
|
+
return { value: read, problems, extras };
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
var directory = text("a directory path");
|
|
479
|
+
var trackerKind = text("a tracker kind name");
|
|
480
|
+
var releaseFile = text("a file path");
|
|
481
|
+
var tierPins = mapOf(tierPin, "false or a tier");
|
|
482
|
+
var routeTable = mapOf(routeTarget, "false or an agent name");
|
|
483
|
+
var RELEASE_STRATEGIES = ["semver-by-level"];
|
|
484
|
+
var RELEASE_SETTLE_MODES = ["push", "pr"];
|
|
485
|
+
var RELEASE_TAG_MODES = ["manual", "settle"];
|
|
486
|
+
var VERSION_COLLISION_MODES = ["allow", "report", "ask", "refuse"];
|
|
487
|
+
var releaseStrategy = oneOf(RELEASE_STRATEGIES);
|
|
488
|
+
var releaseSettleMode = oneOf(RELEASE_SETTLE_MODES);
|
|
489
|
+
var releaseTagMode = oneOf(RELEASE_TAG_MODES);
|
|
490
|
+
var versionCollisionMode = oneOf(VERSION_COLLISION_MODES);
|
|
491
|
+
var UNTRACKED_DIRECTORY = ".rafa";
|
|
492
|
+
var FRAGMENTS_EXPECTED = `a relative directory path not under ${UNTRACKED_DIRECTORY}/`;
|
|
493
|
+
function isUnderUntracked(path) {
|
|
494
|
+
const [first] = normalize(path).split(/[\\/]/);
|
|
495
|
+
return first === UNTRACKED_DIRECTORY;
|
|
496
|
+
}
|
|
497
|
+
var fragmentsDirectory = (raw, at) => {
|
|
498
|
+
const reading = text(FRAGMENTS_EXPECTED)(raw, at);
|
|
499
|
+
const path = reading.value;
|
|
500
|
+
if (path === undefined || !isAbsolute(path) && !isUnderUntracked(path))
|
|
501
|
+
return reading;
|
|
502
|
+
const problem = `${at.label} is ${describeValue(raw)}, expected ${FRAGMENTS_EXPECTED}`;
|
|
503
|
+
return { value: undefined, problems: [problem], extras: [] };
|
|
504
|
+
};
|
|
505
|
+
// src/core/plan/blocks.ts
|
|
506
|
+
var RAFA_BLOCK_KINDS = [
|
|
507
|
+
"plan",
|
|
508
|
+
"context",
|
|
509
|
+
"stage-context",
|
|
510
|
+
"report"
|
|
511
|
+
];
|
|
512
|
+
var RAFA_PREFIX = "rafa:";
|
|
513
|
+
var FENCE_RUN = /^( {0,3})(`{3,}|~{3,})/;
|
|
514
|
+
var CLOSING_FENCE = /^ {0,3}(`{3,}|~{3,})[ \t]*$/;
|
|
515
|
+
function isRafaBlockKind(kind) {
|
|
516
|
+
return RAFA_BLOCK_KINDS.includes(kind);
|
|
517
|
+
}
|
|
518
|
+
function linesOf(markdown) {
|
|
519
|
+
const lines = markdown.split(`
|
|
520
|
+
`);
|
|
521
|
+
if (lines[lines.length - 1] === "")
|
|
522
|
+
lines.pop();
|
|
523
|
+
return lines.map((line) => line.endsWith("\r") ? line.slice(0, -1) : line);
|
|
524
|
+
}
|
|
525
|
+
function kindOf(info) {
|
|
526
|
+
const word = info.trim().split(/\s+/)[0] ?? "";
|
|
527
|
+
return word.startsWith(RAFA_PREFIX) ? word.slice(RAFA_PREFIX.length) : null;
|
|
528
|
+
}
|
|
529
|
+
function openingFence(line) {
|
|
530
|
+
const match = FENCE_RUN.exec(line);
|
|
531
|
+
const opener = match?.[0];
|
|
532
|
+
const indent = match?.[1];
|
|
533
|
+
const run = match?.[2];
|
|
534
|
+
if (opener === undefined || indent === undefined || run === undefined) {
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
const marker = run.charAt(0);
|
|
538
|
+
const info = line.slice(opener.length);
|
|
539
|
+
if (marker === "`" && info.includes("`"))
|
|
540
|
+
return null;
|
|
541
|
+
return { marker, length: run.length, indent: indent.length, kind: kindOf(info) };
|
|
542
|
+
}
|
|
543
|
+
function closes(line, fence) {
|
|
544
|
+
const run = CLOSING_FENCE.exec(line)?.[1];
|
|
545
|
+
return run !== undefined && run.charAt(0) === fence.marker && run.length >= fence.length;
|
|
546
|
+
}
|
|
547
|
+
function closingIndex(lines, from, fence) {
|
|
548
|
+
for (let index = from;index < lines.length; index += 1) {
|
|
549
|
+
if (closes(lines[index] ?? "", fence))
|
|
550
|
+
return index;
|
|
551
|
+
}
|
|
552
|
+
return lines.length;
|
|
553
|
+
}
|
|
554
|
+
function dedent(line, indent) {
|
|
555
|
+
let cut = 0;
|
|
556
|
+
while (cut < indent && line.charAt(cut) === " ")
|
|
557
|
+
cut += 1;
|
|
558
|
+
return line.slice(cut);
|
|
559
|
+
}
|
|
560
|
+
function readRafaBlocks(markdown) {
|
|
561
|
+
const lines = linesOf(markdown);
|
|
562
|
+
const blocks = [];
|
|
563
|
+
let index = 0;
|
|
564
|
+
while (index < lines.length) {
|
|
565
|
+
const fence = openingFence(lines[index] ?? "");
|
|
566
|
+
if (fence === null) {
|
|
567
|
+
index += 1;
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
const closing = closingIndex(lines, index + 1, fence);
|
|
571
|
+
const closed = closing < lines.length;
|
|
572
|
+
if (fence.kind !== null) {
|
|
573
|
+
const body = lines.slice(index + 1, closing).map((line) => dedent(line, fence.indent)).join(`
|
|
574
|
+
`);
|
|
575
|
+
const last = closed ? closing + 1 : lines.length;
|
|
576
|
+
blocks.push({ kind: fence.kind, body, span: { first: index + 1, last }, closed });
|
|
577
|
+
}
|
|
578
|
+
index = closing + 1;
|
|
579
|
+
}
|
|
580
|
+
return blocks;
|
|
581
|
+
}
|
|
582
|
+
// src/report/parse.ts
|
|
583
|
+
var REPORT_STATUSES = ["done", "blocked"];
|
|
584
|
+
var FINDING_KINDS = ["gotcha", "pattern", "location", "skill-suggestion"];
|
|
585
|
+
var FINDING_SIGNALS = ["loud", "silent"];
|
|
586
|
+
var FINDING_DOMAINS = [
|
|
587
|
+
"code-style",
|
|
588
|
+
"testing",
|
|
589
|
+
"git",
|
|
590
|
+
"debugging",
|
|
591
|
+
"workflow",
|
|
592
|
+
"security",
|
|
593
|
+
"general-best-practices"
|
|
594
|
+
];
|
|
595
|
+
var CHANGE_LEVELS = ["patch", "minor", "major", "none"];
|
|
596
|
+
var REPORT_KIND = "report";
|
|
597
|
+
var REQUIRED = true;
|
|
598
|
+
var OPTIONAL = false;
|
|
599
|
+
var REPORT_KEYS = [
|
|
600
|
+
"status",
|
|
601
|
+
"feedback",
|
|
602
|
+
"findings",
|
|
603
|
+
"skills_used",
|
|
604
|
+
"blockers",
|
|
605
|
+
"out_of_scope_bugs",
|
|
606
|
+
"changes"
|
|
607
|
+
];
|
|
608
|
+
var FINDING_KEYS = [
|
|
609
|
+
"trigger",
|
|
610
|
+
"kind",
|
|
611
|
+
"what",
|
|
612
|
+
"cause",
|
|
613
|
+
"resolution",
|
|
614
|
+
"artifact",
|
|
615
|
+
"signal",
|
|
616
|
+
"domain"
|
|
617
|
+
];
|
|
618
|
+
var BLOCKER_KEYS = ["what", "artifact"];
|
|
619
|
+
var BUG_KEYS = ["what", "artifact", "security"];
|
|
620
|
+
var CHANGE_KEYS = ["level", "area", "summary"];
|
|
621
|
+
function isMapping2(value) {
|
|
622
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
623
|
+
}
|
|
624
|
+
function isOneOf(choices, value) {
|
|
625
|
+
return choices.includes(value);
|
|
626
|
+
}
|
|
627
|
+
function describeValue2(value) {
|
|
628
|
+
if (typeof value === "string")
|
|
629
|
+
return JSON.stringify(value);
|
|
630
|
+
if (value === null)
|
|
631
|
+
return "nothing";
|
|
632
|
+
if (Array.isArray(value))
|
|
633
|
+
return "a list";
|
|
634
|
+
if (typeof value === "object")
|
|
635
|
+
return "a mapping";
|
|
636
|
+
return `the ${typeof value} ${String(value)}`;
|
|
637
|
+
}
|
|
638
|
+
function messageOf2(error) {
|
|
639
|
+
return error instanceof Error ? error.message : String(error);
|
|
640
|
+
}
|
|
641
|
+
function reportIssue(reason, field, text2) {
|
|
642
|
+
return { reason, field, text: text2 };
|
|
643
|
+
}
|
|
644
|
+
function absent(reason, block, text2) {
|
|
645
|
+
return { present: false, reason, block, text: text2 };
|
|
646
|
+
}
|
|
647
|
+
function scopeOf(mapping, keys, prefix, issues) {
|
|
648
|
+
const fields = new Map;
|
|
649
|
+
const extras = [];
|
|
650
|
+
for (const [key, value] of Object.entries(mapping)) {
|
|
651
|
+
if (keys.includes(key))
|
|
652
|
+
fields.set(key, value);
|
|
653
|
+
else
|
|
654
|
+
extras.push({ key, value });
|
|
655
|
+
}
|
|
656
|
+
return { fields, extras, prefix, issues };
|
|
657
|
+
}
|
|
658
|
+
function valueOf(scope, key, required) {
|
|
659
|
+
const value = scope.fields.get(key) ?? null;
|
|
660
|
+
if (value === null && required) {
|
|
661
|
+
const field = `${scope.prefix}${key}`;
|
|
662
|
+
scope.issues.push(reportIssue("missing-field", field, `${field} is missing`));
|
|
663
|
+
}
|
|
664
|
+
return value;
|
|
665
|
+
}
|
|
666
|
+
function unusable(scope, key, why) {
|
|
667
|
+
const field = `${scope.prefix}${key}`;
|
|
668
|
+
scope.issues.push(reportIssue("unusable-field", field, `${field} ${why}`));
|
|
669
|
+
return null;
|
|
670
|
+
}
|
|
671
|
+
function stringProblem(value) {
|
|
672
|
+
if (typeof value !== "string")
|
|
673
|
+
return `is ${describeValue2(value)}, not a string; quote it`;
|
|
674
|
+
if (value.trim().length === 0)
|
|
675
|
+
return "is blank";
|
|
676
|
+
return null;
|
|
677
|
+
}
|
|
678
|
+
function stringField(scope, key, required) {
|
|
679
|
+
const value = valueOf(scope, key, required);
|
|
680
|
+
if (value === null)
|
|
681
|
+
return null;
|
|
682
|
+
const problem = stringProblem(value);
|
|
683
|
+
return problem === null ? value : unusable(scope, key, problem);
|
|
684
|
+
}
|
|
685
|
+
function choiceField(scope, key, choices, required) {
|
|
686
|
+
const value = valueOf(scope, key, required);
|
|
687
|
+
if (value === null || isOneOf(choices, value))
|
|
688
|
+
return value;
|
|
689
|
+
return unusable(scope, key, `is ${describeValue2(value)}, not one of ${choices.join(", ")}`);
|
|
690
|
+
}
|
|
691
|
+
function booleanField(scope, key, required) {
|
|
692
|
+
const value = valueOf(scope, key, required);
|
|
693
|
+
if (value === null || typeof value === "boolean")
|
|
694
|
+
return value;
|
|
695
|
+
return unusable(scope, key, `is ${describeValue2(value)}, not true or false; write it unquoted`);
|
|
696
|
+
}
|
|
697
|
+
function listField(scope, key, readEntry) {
|
|
698
|
+
const value = valueOf(scope, key, OPTIONAL);
|
|
699
|
+
if (value === null)
|
|
700
|
+
return [];
|
|
701
|
+
if (!Array.isArray(value)) {
|
|
702
|
+
unusable(scope, key, `is ${describeValue2(value)}, not a list`);
|
|
703
|
+
return [];
|
|
704
|
+
}
|
|
705
|
+
const entries = [];
|
|
706
|
+
const items = value;
|
|
707
|
+
for (const [index, item] of items.entries()) {
|
|
708
|
+
const entry = readEntry(item, `${scope.prefix}${key}[${index}]`, scope.issues);
|
|
709
|
+
if (entry !== null)
|
|
710
|
+
entries.push(entry);
|
|
711
|
+
}
|
|
712
|
+
return entries;
|
|
713
|
+
}
|
|
714
|
+
function entryScope(item, field, keys, issues) {
|
|
715
|
+
if (isMapping2(item))
|
|
716
|
+
return scopeOf(item, keys, `${field}.`, issues);
|
|
717
|
+
const text2 = `${field} is ${describeValue2(item)}, not a mapping of ${keys.join(", ")}; dropped`;
|
|
718
|
+
issues.push(reportIssue("unusable-field", field, text2));
|
|
719
|
+
return null;
|
|
720
|
+
}
|
|
721
|
+
function readSkill(item, field, issues) {
|
|
722
|
+
const problem = stringProblem(item);
|
|
723
|
+
if (problem === null)
|
|
724
|
+
return item;
|
|
725
|
+
issues.push(reportIssue("unusable-field", field, `${field} ${problem}; dropped`));
|
|
726
|
+
return null;
|
|
727
|
+
}
|
|
728
|
+
function readFinding(item, field, issues) {
|
|
729
|
+
const scope = entryScope(item, field, FINDING_KEYS, issues);
|
|
730
|
+
if (scope === null)
|
|
731
|
+
return null;
|
|
732
|
+
return {
|
|
733
|
+
trigger: stringField(scope, "trigger", REQUIRED),
|
|
734
|
+
kind: choiceField(scope, "kind", FINDING_KINDS, REQUIRED),
|
|
735
|
+
what: stringField(scope, "what", REQUIRED),
|
|
736
|
+
cause: stringField(scope, "cause", OPTIONAL),
|
|
737
|
+
resolution: stringField(scope, "resolution", OPTIONAL),
|
|
738
|
+
artifact: stringField(scope, "artifact", OPTIONAL),
|
|
739
|
+
signal: choiceField(scope, "signal", FINDING_SIGNALS, REQUIRED),
|
|
740
|
+
domain: choiceField(scope, "domain", FINDING_DOMAINS, OPTIONAL),
|
|
741
|
+
extras: scope.extras
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
function readBlocker(item, field, issues) {
|
|
745
|
+
const scope = entryScope(item, field, BLOCKER_KEYS, issues);
|
|
746
|
+
if (scope === null)
|
|
747
|
+
return null;
|
|
748
|
+
return {
|
|
749
|
+
what: stringField(scope, "what", REQUIRED),
|
|
750
|
+
artifact: stringField(scope, "artifact", OPTIONAL),
|
|
751
|
+
extras: scope.extras
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
function readBug(item, field, issues) {
|
|
755
|
+
const scope = entryScope(item, field, BUG_KEYS, issues);
|
|
756
|
+
if (scope === null)
|
|
757
|
+
return null;
|
|
758
|
+
return {
|
|
759
|
+
what: stringField(scope, "what", REQUIRED),
|
|
760
|
+
artifact: stringField(scope, "artifact", OPTIONAL),
|
|
761
|
+
security: booleanField(scope, "security", REQUIRED),
|
|
762
|
+
extras: scope.extras
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
function readChange(item, field, issues) {
|
|
766
|
+
const scope = entryScope(item, field, CHANGE_KEYS, issues);
|
|
767
|
+
if (scope === null)
|
|
768
|
+
return null;
|
|
769
|
+
return {
|
|
770
|
+
level: choiceField(scope, "level", CHANGE_LEVELS, REQUIRED),
|
|
771
|
+
area: stringField(scope, "area", OPTIONAL),
|
|
772
|
+
summary: stringField(scope, "summary", REQUIRED),
|
|
773
|
+
extras: scope.extras
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
function readReport(document, issues) {
|
|
777
|
+
const scope = scopeOf(document, REPORT_KEYS, "", issues);
|
|
778
|
+
return {
|
|
779
|
+
status: choiceField(scope, "status", REPORT_STATUSES, REQUIRED),
|
|
780
|
+
feedback: stringField(scope, "feedback", OPTIONAL),
|
|
781
|
+
findings: listField(scope, "findings", readFinding),
|
|
782
|
+
skillsUsed: listField(scope, "skills_used", readSkill),
|
|
783
|
+
blockers: listField(scope, "blockers", readBlocker),
|
|
784
|
+
outOfScopeBugs: listField(scope, "out_of_scope_bugs", readBug),
|
|
785
|
+
changes: listField(scope, "changes", readChange),
|
|
786
|
+
extras: scope.extras
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
function parseReport(output) {
|
|
790
|
+
const reports = readRafaBlocks(output).filter((block2) => block2.kind === REPORT_KIND);
|
|
791
|
+
const block = reports.at(-1);
|
|
792
|
+
if (block === undefined) {
|
|
793
|
+
return absent("no-block", null, "the session output holds no rafa:report block");
|
|
794
|
+
}
|
|
795
|
+
const line = block.span.first;
|
|
796
|
+
if (!block.closed) {
|
|
797
|
+
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");
|
|
798
|
+
}
|
|
799
|
+
let document;
|
|
800
|
+
try {
|
|
801
|
+
document = Bun.YAML.parse(block.body);
|
|
802
|
+
} catch (error) {
|
|
803
|
+
const text2 = `rafa:report block at line ${line} is not valid YAML (${messageOf2(error)})`;
|
|
804
|
+
return absent("malformed-block", block, text2);
|
|
805
|
+
}
|
|
806
|
+
if (!isMapping2(document)) {
|
|
807
|
+
const text2 = `rafa:report block at line ${line} holds ${describeValue2(document)}, ` + "not a mapping of report fields";
|
|
808
|
+
return absent("malformed-block", block, text2);
|
|
809
|
+
}
|
|
810
|
+
const issues = [];
|
|
811
|
+
const report = readReport(document, issues);
|
|
812
|
+
return { present: true, report, block, issues };
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// src/adapters/output/text.ts
|
|
816
|
+
var DEBUG_VERBOSITY = 2;
|
|
817
|
+
function formatPayload(payload) {
|
|
818
|
+
return typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
819
|
+
}
|
|
820
|
+
function resultLine(event) {
|
|
821
|
+
if (event.ok) {
|
|
822
|
+
return event.data === undefined ? "result: ok" : `result: ok ${formatPayload(event.data)}`;
|
|
823
|
+
}
|
|
824
|
+
const code = event.error?.code ?? "unknown";
|
|
825
|
+
const message = event.error?.message ?? "";
|
|
826
|
+
return message === "" ? `result: error ${code}` : `result: error ${code}: ${message}`;
|
|
827
|
+
}
|
|
828
|
+
function createTextOutput({ verbosity, stream }) {
|
|
829
|
+
const writeLine = (line) => {
|
|
830
|
+
stream.write(`${line}
|
|
831
|
+
`);
|
|
832
|
+
};
|
|
833
|
+
const debug = (message) => {
|
|
834
|
+
if (verbosity >= DEBUG_VERBOSITY)
|
|
835
|
+
writeLine(`debug: ${message}`);
|
|
836
|
+
};
|
|
837
|
+
const info = (message) => {
|
|
838
|
+
writeLine(message);
|
|
839
|
+
};
|
|
840
|
+
const warn = (message) => {
|
|
841
|
+
writeLine(`warn: ${message}`);
|
|
842
|
+
};
|
|
843
|
+
const error = (message) => {
|
|
844
|
+
writeLine(`error: ${message}`);
|
|
845
|
+
};
|
|
846
|
+
const result = (payload) => {
|
|
847
|
+
writeLine(`result: ${formatPayload(payload)}`);
|
|
848
|
+
};
|
|
849
|
+
const writeLog = (event) => {
|
|
850
|
+
switch (event.level) {
|
|
851
|
+
case "debug": {
|
|
852
|
+
debug(event.message);
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
case "info": {
|
|
856
|
+
info(event.message);
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
case "warn": {
|
|
860
|
+
warn(event.message);
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
case "error": {
|
|
864
|
+
error(event.message);
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
};
|
|
869
|
+
const emit = (event) => {
|
|
870
|
+
switch (event.type) {
|
|
871
|
+
case "start": {
|
|
872
|
+
writeLine(`start: ${event.command}`);
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
case "step": {
|
|
876
|
+
writeLine(`step: ${event.name}`);
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
case "log": {
|
|
880
|
+
writeLog(event);
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
case "result": {
|
|
884
|
+
writeLine(resultLine(event));
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
case "event": {
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
};
|
|
892
|
+
return Object.freeze({ info, warn, error, debug, emit, result });
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// src/core/output/active.ts
|
|
896
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
897
|
+
var scopes = new AsyncLocalStorage;
|
|
898
|
+
var DEFAULT_OUTPUT = createTextOutput({ verbosity: 0, stream: process.stdout });
|
|
899
|
+
var active = DEFAULT_OUTPUT;
|
|
900
|
+
var activeMode = "text";
|
|
901
|
+
function activeOutput() {
|
|
902
|
+
return scopes.getStore()?.output ?? active;
|
|
903
|
+
}
|
|
904
|
+
function activeOutputMode() {
|
|
905
|
+
return scopes.getStore()?.mode ?? activeMode;
|
|
906
|
+
}
|
|
907
|
+
// src/utils/plan-stamp.ts
|
|
908
|
+
var STUB_PATTERN = "[A-Za-z0-9._-]+";
|
|
909
|
+
var STAMP_PATTERN = new RegExp(`<!--\\s*ralph:plan=(${STUB_PATTERN})\\s*-->`);
|
|
910
|
+
function isStampableStub(stub) {
|
|
911
|
+
return new RegExp(`^${STUB_PATTERN}$`).test(stub);
|
|
912
|
+
}
|
|
913
|
+
function planStampLine(stub) {
|
|
914
|
+
if (!isStampableStub(stub)) {
|
|
915
|
+
throw new Error(`rafa plan stamp: unusable stub ${JSON.stringify(stub)}`);
|
|
916
|
+
}
|
|
917
|
+
return `<!-- ralph:plan=${stub} -->`;
|
|
918
|
+
}
|
|
919
|
+
function planStubFromPrompt(content) {
|
|
920
|
+
if (typeof content !== "string" || content.length === 0)
|
|
921
|
+
return null;
|
|
922
|
+
const match = STAMP_PATTERN.exec(content);
|
|
923
|
+
const stub = match?.[1];
|
|
924
|
+
return stub !== undefined && isStampableStub(stub) ? stub : null;
|
|
925
|
+
}
|
|
926
|
+
function stampPrompt(stub, prompt) {
|
|
927
|
+
return `${prompt}
|
|
928
|
+
${planStampLine(stub)}`;
|
|
929
|
+
}
|
|
930
|
+
function planStubFromPath(planPath) {
|
|
931
|
+
const base = planPath.split("/").pop() ?? planPath;
|
|
932
|
+
const match = /^PLAN(?:_TRACKER)?-(.+)\.md$/.exec(base);
|
|
933
|
+
const stub = match?.[1];
|
|
934
|
+
return stub !== undefined && isStampableStub(stub) ? stub : null;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
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, refused, refusedWith, mapReading, below, oneOf, text, flag, mergeMethod, usdAmount, issueNumber, dayCount, BUSY_TIMEOUT_MIN_MS, BUSY_TIMEOUT_MAX_MS, isBusyTimeoutMs, busyTimeoutMs, confidence, recurrenceCount, releaseEnabled, tierSwitch, skillResolverName, lessonSwitch, claimDurationHours, claimsStaleAfter, claimsAhead, isGitHubLogin, githubLogin, BOARD_PROJECT_TEMPLATE_DEFAULT, projectUrl, projectNumber, listOf, subsetOf, mapOf, directory, trackerKind, releaseFile, tierPins, routeTable, releaseStrategy, releaseSettleMode, releaseTagMode, versionCollisionMode, fragmentsDirectory, createTextOutput, activeOutput, activeOutputMode, RAFA_BLOCK_KINDS, isRafaBlockKind, readRafaBlocks, REPORT_STATUSES, FINDING_KINDS, FINDING_SIGNALS, FINDING_DOMAINS, CHANGE_LEVELS, parseReport, isStampableStub, planStubFromPrompt, stampPrompt, planStubFromPath };
|