@open-tomato/rafa 0.18.0 → 0.20.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 +36 -14
- package/dist/PROMPT.md +2 -1
- package/dist/bundled/skills/dev-planner/SKILL.md +9 -0
- package/dist/cli.js +3937 -1705
- package/dist/{describe-vq037dyt.js → describe-0tefrkk7.js} +4 -3
- package/dist/effort/store/index.js +6 -4
- package/dist/{index-6vkd153j.js → index-02fx0mzf.js} +4713 -3848
- package/dist/{index-0pdsq2hy.js → index-21qkxk63.js} +11 -224
- package/dist/{index-ddf85qac.js → index-28jzpyks.js} +1 -1
- package/dist/{index-kvnggvyk.js → index-4a45ffqh.js} +2 -2
- package/dist/{index-at9f1z2z.js → index-afersvp3.js} +1028 -51
- package/dist/index-d6d0942q.js +218 -0
- package/dist/{index-944pncd8.js → index-pmkgvtfe.js} +14 -2
- package/dist/{index-jva32qe6.js → index-twbp38kz.js} +290 -39
- package/dist/index-wp8c28fd.js +18 -0
- package/dist/{index-cvpdvmtg.js → index-zhh1h3h9.js} +1023 -935
- package/dist/index.js +13 -11
- package/dist/learning/index.js +25 -0
- package/dist/plan/index.js +8 -7
- package/dist/plan-prompt.md +2 -0
- package/package.json +4 -3
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
|
-
ACTION_HEADING,
|
|
4
3
|
AGENT_DEFINITION_DIR,
|
|
5
4
|
BRANCH_PREFIX,
|
|
6
|
-
CAUSE_HEADING,
|
|
7
5
|
CI_POLL_INTERVAL_MS,
|
|
8
6
|
DEFAULT_BASE_BRANCH,
|
|
9
7
|
DEFAULT_CI_ATTEMPTS,
|
|
10
8
|
DEFAULT_CI_TIMEOUT_MIN,
|
|
11
9
|
DEFAULT_ISSUE_SEAMS,
|
|
12
10
|
DEFAULT_PLAN_FILE,
|
|
11
|
+
DEMOTION_VERDICTS,
|
|
13
12
|
DESCRIPTION_LIMIT,
|
|
14
13
|
DISK_FILE_SYSTEM,
|
|
15
14
|
HINT_FLAG_SPEC,
|
|
16
15
|
ID_PREFIX,
|
|
16
|
+
LEARNED_DIRECTORY,
|
|
17
17
|
LISTING_LIMIT,
|
|
18
|
-
MARKDOWN_EXTENSION,
|
|
19
18
|
MECHANICAL_CONFLICT_SENTENCE,
|
|
20
19
|
NEXT_COMMAND_ACTIONS,
|
|
20
|
+
ORIGIN_FIELD,
|
|
21
21
|
PREVIOUS_COPY_WARN_ABOVE,
|
|
22
22
|
PROVENANCE_FIELD,
|
|
23
23
|
REMOTE,
|
|
@@ -32,9 +32,9 @@ import {
|
|
|
32
32
|
SCOPE_DIR,
|
|
33
33
|
SERVE_CLI_VERSION,
|
|
34
34
|
SESSION_ID_FLAG,
|
|
35
|
-
SKILL_FILE,
|
|
36
|
-
SKILL_FILE1 as SKILL_FILE2,
|
|
35
|
+
SKILL_FILE as SKILL_FILE2,
|
|
37
36
|
SKILL_SIGNALS,
|
|
37
|
+
SKILL_USE_CLI_VERSION,
|
|
38
38
|
SPEC_BLOCKED_LABEL,
|
|
39
39
|
SPEC_EXTENSION,
|
|
40
40
|
SPEC_LABEL,
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
UNREADABLE,
|
|
47
47
|
VENDOR_COMMAND,
|
|
48
48
|
VERSION_SHAPE,
|
|
49
|
-
|
|
49
|
+
WHEN_TO_USE_HEADING,
|
|
50
50
|
actionInvocation,
|
|
51
51
|
actionOutput,
|
|
52
52
|
announceRiskTotal,
|
|
@@ -54,6 +54,7 @@ import {
|
|
|
54
54
|
blockedFaultMessage,
|
|
55
55
|
blockedLineSentence,
|
|
56
56
|
boardId,
|
|
57
|
+
bodySections,
|
|
57
58
|
branchClaims,
|
|
58
59
|
bundledAgentsDirectory,
|
|
59
60
|
byName,
|
|
@@ -62,13 +63,14 @@ import {
|
|
|
62
63
|
checkReferences,
|
|
63
64
|
checkbox,
|
|
64
65
|
checkboxAt,
|
|
66
|
+
classifySkill,
|
|
65
67
|
cleanUpSteps,
|
|
66
68
|
closedIssuesIn,
|
|
67
69
|
collect,
|
|
68
|
-
collectReferences,
|
|
69
70
|
collectSessionRow,
|
|
70
71
|
commandLine,
|
|
71
72
|
commandWords,
|
|
73
|
+
convertToInstinct,
|
|
72
74
|
coreRoster,
|
|
73
75
|
countCharacters,
|
|
74
76
|
countPreviousCopies,
|
|
@@ -95,6 +97,7 @@ import {
|
|
|
95
97
|
failingRows,
|
|
96
98
|
findReadinessGaps,
|
|
97
99
|
fingerprintText,
|
|
100
|
+
firstSentence,
|
|
98
101
|
formatCounts,
|
|
99
102
|
formatRows,
|
|
100
103
|
ghHostOf,
|
|
@@ -104,7 +107,6 @@ import {
|
|
|
104
107
|
ghPreflightItems,
|
|
105
108
|
ghPullRequestsIn,
|
|
106
109
|
ghUnauthenticatedMessage,
|
|
107
|
-
gitRemoteUrl,
|
|
108
110
|
gitSaid,
|
|
109
111
|
groupChangeNotes,
|
|
110
112
|
hasDiverged,
|
|
@@ -125,16 +127,13 @@ import {
|
|
|
125
127
|
list_default,
|
|
126
128
|
loadConfig,
|
|
127
129
|
memoiseVerifier,
|
|
128
|
-
mergeFrontmatter,
|
|
129
130
|
missingPlanAgents,
|
|
130
131
|
name,
|
|
131
132
|
nextQuestion,
|
|
132
|
-
normalizeRemote,
|
|
133
133
|
notesFileName,
|
|
134
134
|
onTracker,
|
|
135
135
|
openNextSources,
|
|
136
136
|
parseBaseStanding,
|
|
137
|
-
parseInstinct,
|
|
138
137
|
parseRoadmapBody,
|
|
139
138
|
parseSemanticVersion,
|
|
140
139
|
parseWorkingTree,
|
|
@@ -150,6 +149,7 @@ import {
|
|
|
150
149
|
previousDir,
|
|
151
150
|
projectRoot,
|
|
152
151
|
provenanceBlock,
|
|
152
|
+
rankCandidates,
|
|
153
153
|
readAgentDirectory,
|
|
154
154
|
readAuthorTrust,
|
|
155
155
|
readBlockedBy,
|
|
@@ -158,9 +158,6 @@ import {
|
|
|
158
158
|
readChoice,
|
|
159
159
|
readChoiceFlag,
|
|
160
160
|
readCopyRefs,
|
|
161
|
-
readFrontmatter,
|
|
162
|
-
readFrontmatterDocument,
|
|
163
|
-
readInventoryFrontmatter,
|
|
164
161
|
readManifestVersion,
|
|
165
162
|
readMergeRefusal,
|
|
166
163
|
readNextState,
|
|
@@ -168,6 +165,7 @@ import {
|
|
|
168
165
|
readOpenPull,
|
|
169
166
|
readPlanChanges,
|
|
170
167
|
readPlans,
|
|
168
|
+
readRankCandidate,
|
|
171
169
|
readRecords,
|
|
172
170
|
readRefsText,
|
|
173
171
|
readRequiredFlag,
|
|
@@ -182,7 +180,6 @@ import {
|
|
|
182
180
|
refusal,
|
|
183
181
|
refuseMisplacedRuntime,
|
|
184
182
|
remainingFrom,
|
|
185
|
-
renderFrontmatter,
|
|
186
183
|
renderNoteLines,
|
|
187
184
|
renderRiskText,
|
|
188
185
|
report,
|
|
@@ -204,7 +201,6 @@ import {
|
|
|
204
201
|
runAction,
|
|
205
202
|
runsDir,
|
|
206
203
|
scanClaimBranches,
|
|
207
|
-
scanLayout,
|
|
208
204
|
scopeAt,
|
|
209
205
|
selfAndAncestors,
|
|
210
206
|
serveVerdict,
|
|
@@ -230,10 +226,8 @@ import {
|
|
|
230
226
|
version,
|
|
231
227
|
versionLine,
|
|
232
228
|
waitForChecks,
|
|
233
|
-
writeFrontmatter,
|
|
234
|
-
writeInstinct,
|
|
235
229
|
writeSession
|
|
236
|
-
} from "./index-
|
|
230
|
+
} from "./index-02fx0mzf.js";
|
|
237
231
|
import {
|
|
238
232
|
PROBE_TIMEOUT_MS,
|
|
239
233
|
findNextTask,
|
|
@@ -247,8 +241,10 @@ import {
|
|
|
247
241
|
runPreflight,
|
|
248
242
|
splitBlockerComment,
|
|
249
243
|
trackerPathFor
|
|
250
|
-
} from "./index-
|
|
244
|
+
} from "./index-21qkxk63.js";
|
|
251
245
|
import {
|
|
246
|
+
ACTION_HEADING,
|
|
247
|
+
CAUSE_HEADING,
|
|
252
248
|
CLAUDE_BIN,
|
|
253
249
|
CORE_ADAPTER_REGISTRY,
|
|
254
250
|
EFFORT_STORE_DIR,
|
|
@@ -256,17 +252,34 @@ import {
|
|
|
256
252
|
ISSUE_PRIORITIES,
|
|
257
253
|
ISSUE_STATES,
|
|
258
254
|
ISSUE_TYPES,
|
|
255
|
+
MARKDOWN_EXTENSION,
|
|
256
|
+
SKILL_FILE,
|
|
257
|
+
SQLITE_MIGRATIONS,
|
|
258
|
+
allRecords,
|
|
259
259
|
claudeArgs,
|
|
260
260
|
createGhRunner,
|
|
261
261
|
effortStorePath,
|
|
262
|
+
findRecords,
|
|
263
|
+
gitRemoteUrl,
|
|
264
|
+
instinctProject,
|
|
265
|
+
mergeFrontmatter,
|
|
266
|
+
migrateSchema,
|
|
262
267
|
moduleOfLabels,
|
|
268
|
+
normalizeRemote,
|
|
269
|
+
readFrontmatter,
|
|
270
|
+
readFrontmatterDocument,
|
|
271
|
+
readScopes,
|
|
272
|
+
renderFrontmatter,
|
|
263
273
|
runClaudeCaptured,
|
|
274
|
+
scanLayout,
|
|
264
275
|
selectEffortStore,
|
|
265
276
|
sessionSpawnEnv,
|
|
266
277
|
spawnClaudeCaptured,
|
|
267
278
|
sqliteStorePath,
|
|
268
|
-
|
|
269
|
-
|
|
279
|
+
toHeldRecords,
|
|
280
|
+
typeOfLabels,
|
|
281
|
+
writeFrontmatter
|
|
282
|
+
} from "./index-afersvp3.js";
|
|
270
283
|
import {
|
|
271
284
|
CONFIG_DEFAULTS,
|
|
272
285
|
CONFIG_FILE,
|
|
@@ -280,15 +293,18 @@ import {
|
|
|
280
293
|
readItemBytes,
|
|
281
294
|
readRafaBlocks,
|
|
282
295
|
resolveTiers
|
|
283
|
-
} from "./index-
|
|
296
|
+
} from "./index-twbp38kz.js";
|
|
297
|
+
import {
|
|
298
|
+
promotable
|
|
299
|
+
} from "./index-d6d0942q.js";
|
|
284
300
|
import {
|
|
285
301
|
createCommandRegistry,
|
|
286
302
|
describeRegistry,
|
|
287
303
|
mountKey
|
|
288
|
-
} from "./index-
|
|
304
|
+
} from "./index-4a45ffqh.js";
|
|
289
305
|
import {
|
|
290
306
|
CommandExit
|
|
291
|
-
} from "./index-
|
|
307
|
+
} from "./index-28jzpyks.js";
|
|
292
308
|
import {
|
|
293
309
|
MERGE_METHODS,
|
|
294
310
|
RELEASE_AUTO,
|
|
@@ -299,10 +315,11 @@ import {
|
|
|
299
315
|
isMergeMethod,
|
|
300
316
|
isSkillTier,
|
|
301
317
|
messageOf,
|
|
302
|
-
parseBudgetUsd
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
318
|
+
parseBudgetUsd
|
|
319
|
+
} from "./index-pmkgvtfe.js";
|
|
320
|
+
import {
|
|
321
|
+
triggerKey
|
|
322
|
+
} from "./index-wp8c28fd.js";
|
|
306
323
|
|
|
307
324
|
// src/inventory/disabled.ts
|
|
308
325
|
import { readFileSync } from "fs";
|
|
@@ -2274,148 +2291,6 @@ async function checkQuotes(matches, files) {
|
|
|
2274
2291
|
return { kept, dropped: matches.length - kept.length };
|
|
2275
2292
|
}
|
|
2276
2293
|
|
|
2277
|
-
// src/inventory/search/rank.ts
|
|
2278
|
-
import { readFileSync as readFileSync5 } from "fs";
|
|
2279
|
-
var SEARCH_LIMIT = 12;
|
|
2280
|
-
var MIN_WORD_LENGTH = 2;
|
|
2281
|
-
var MIN_PREFIX_LENGTH = 4;
|
|
2282
|
-
var FIELD_WEIGHTS = {
|
|
2283
|
-
tags: 4,
|
|
2284
|
-
prevents: 3,
|
|
2285
|
-
whenToUse: 3,
|
|
2286
|
-
description: 2,
|
|
2287
|
-
body: 1
|
|
2288
|
-
};
|
|
2289
|
-
var FIELDS = ["tags", "prevents", "whenToUse", "description", "body"];
|
|
2290
|
-
var STOP_WORDS = new Set([
|
|
2291
|
-
"a",
|
|
2292
|
-
"about",
|
|
2293
|
-
"an",
|
|
2294
|
-
"and",
|
|
2295
|
-
"any",
|
|
2296
|
-
"are",
|
|
2297
|
-
"as",
|
|
2298
|
-
"at",
|
|
2299
|
-
"be",
|
|
2300
|
-
"by",
|
|
2301
|
-
"can",
|
|
2302
|
-
"could",
|
|
2303
|
-
"do",
|
|
2304
|
-
"does",
|
|
2305
|
-
"for",
|
|
2306
|
-
"from",
|
|
2307
|
-
"has",
|
|
2308
|
-
"have",
|
|
2309
|
-
"how",
|
|
2310
|
-
"if",
|
|
2311
|
-
"in",
|
|
2312
|
-
"into",
|
|
2313
|
-
"is",
|
|
2314
|
-
"it",
|
|
2315
|
-
"its",
|
|
2316
|
-
"me",
|
|
2317
|
-
"my",
|
|
2318
|
-
"of",
|
|
2319
|
-
"on",
|
|
2320
|
-
"or",
|
|
2321
|
-
"should",
|
|
2322
|
-
"so",
|
|
2323
|
-
"that",
|
|
2324
|
-
"the",
|
|
2325
|
-
"their",
|
|
2326
|
-
"there",
|
|
2327
|
-
"this",
|
|
2328
|
-
"to",
|
|
2329
|
-
"use",
|
|
2330
|
-
"was",
|
|
2331
|
-
"we",
|
|
2332
|
-
"what",
|
|
2333
|
-
"when",
|
|
2334
|
-
"where",
|
|
2335
|
-
"which",
|
|
2336
|
-
"who",
|
|
2337
|
-
"why",
|
|
2338
|
-
"will",
|
|
2339
|
-
"with",
|
|
2340
|
-
"would",
|
|
2341
|
-
"you",
|
|
2342
|
-
"your",
|
|
2343
|
-
"agent",
|
|
2344
|
-
"agents",
|
|
2345
|
-
"skill",
|
|
2346
|
-
"skills"
|
|
2347
|
-
]);
|
|
2348
|
-
function wordsOf(text) {
|
|
2349
|
-
return text.toLowerCase().match(/[\p{L}\p{N}]+/gu) ?? [];
|
|
2350
|
-
}
|
|
2351
|
-
function questionWords(question) {
|
|
2352
|
-
const kept = wordsOf(question).filter((word) => word.length >= MIN_WORD_LENGTH && !STOP_WORDS.has(word));
|
|
2353
|
-
return [...new Set(kept)];
|
|
2354
|
-
}
|
|
2355
|
-
function wordsMatch(word, other) {
|
|
2356
|
-
if (word === other)
|
|
2357
|
-
return true;
|
|
2358
|
-
const [shorter, longer] = word.length <= other.length ? [word, other] : [other, word];
|
|
2359
|
-
return shorter.length >= MIN_PREFIX_LENGTH && longer.startsWith(shorter);
|
|
2360
|
-
}
|
|
2361
|
-
function fieldWords(candidate) {
|
|
2362
|
-
const { record } = candidate;
|
|
2363
|
-
const texts = {
|
|
2364
|
-
tags: record.tags.join(" "),
|
|
2365
|
-
prevents: record.prevents ?? "",
|
|
2366
|
-
whenToUse: record.whenToUse ?? "",
|
|
2367
|
-
description: candidate.description ?? "",
|
|
2368
|
-
body: candidate.body
|
|
2369
|
-
};
|
|
2370
|
-
return new Map(FIELDS.map((field) => [field, [...new Set(wordsOf(texts[field]))]]));
|
|
2371
|
-
}
|
|
2372
|
-
function scoreCandidate(candidate, words) {
|
|
2373
|
-
const byField = fieldWords(candidate);
|
|
2374
|
-
const matchedWords = new Set;
|
|
2375
|
-
const matchedFields = new Set;
|
|
2376
|
-
let score = 0;
|
|
2377
|
-
for (const word of words) {
|
|
2378
|
-
for (const field of FIELDS) {
|
|
2379
|
-
const found = (byField.get(field) ?? []).some((other) => wordsMatch(word, other));
|
|
2380
|
-
if (!found)
|
|
2381
|
-
continue;
|
|
2382
|
-
score += FIELD_WEIGHTS[field];
|
|
2383
|
-
matchedWords.add(word);
|
|
2384
|
-
matchedFields.add(field);
|
|
2385
|
-
}
|
|
2386
|
-
}
|
|
2387
|
-
return {
|
|
2388
|
-
record: candidate.record,
|
|
2389
|
-
score,
|
|
2390
|
-
words: words.filter((word) => matchedWords.has(word)),
|
|
2391
|
-
fields: FIELDS.filter((field) => matchedFields.has(field))
|
|
2392
|
-
};
|
|
2393
|
-
}
|
|
2394
|
-
function rankCandidates(question, candidates, limit = SEARCH_LIMIT) {
|
|
2395
|
-
const words = questionWords(question);
|
|
2396
|
-
if (words.length === 0)
|
|
2397
|
-
return [];
|
|
2398
|
-
return candidates.map((candidate) => scoreCandidate(candidate, words)).filter((ranked) => ranked.score > 0).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
2399
|
-
}
|
|
2400
|
-
function readRankCandidate(record, read = (path) => readFileSync5(path, "utf8")) {
|
|
2401
|
-
let text;
|
|
2402
|
-
try {
|
|
2403
|
-
text = read(record.path);
|
|
2404
|
-
} catch {
|
|
2405
|
-
return {
|
|
2406
|
-
record,
|
|
2407
|
-
description: record.summary === "" ? null : record.summary,
|
|
2408
|
-
body: ""
|
|
2409
|
-
};
|
|
2410
|
-
}
|
|
2411
|
-
const document = readFrontmatterDocument(text);
|
|
2412
|
-
return {
|
|
2413
|
-
record,
|
|
2414
|
-
description: readInventoryFrontmatter(document?.data ?? null).description,
|
|
2415
|
-
body: document?.body ?? text
|
|
2416
|
-
};
|
|
2417
|
-
}
|
|
2418
|
-
|
|
2419
2294
|
// src/inventory/search/scratch.ts
|
|
2420
2295
|
import { copyFileSync, mkdirSync, mkdtempSync, rmSync } from "fs";
|
|
2421
2296
|
import { tmpdir } from "os";
|
|
@@ -2838,7 +2713,7 @@ function createAgentShowCommand(seams = DEFAULT_AGENT_SHOW_SEAMS) {
|
|
|
2838
2713
|
var show_default = createAgentShowCommand();
|
|
2839
2714
|
|
|
2840
2715
|
// src/commands/agent/vendor.ts
|
|
2841
|
-
import { copyFileSync as copyFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as
|
|
2716
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync } from "fs";
|
|
2842
2717
|
import { basename as basename2, join as join6 } from "path";
|
|
2843
2718
|
var USAGE4 = "rafa agent vendor <name>... [--force]";
|
|
2844
2719
|
var NOTHING_WRITTEN = "Nothing was written.";
|
|
@@ -2925,7 +2800,7 @@ function planCopies(names, project, force, entry) {
|
|
|
2925
2800
|
function copyOne(copy, date) {
|
|
2926
2801
|
try {
|
|
2927
2802
|
copyFileSync2(copy.from, copy.to);
|
|
2928
|
-
writeFileSync(copy.to, withSourceHeader(
|
|
2803
|
+
writeFileSync(copy.to, withSourceHeader(readFileSync5(copy.to, "utf8"), copy.from, date), "utf8");
|
|
2929
2804
|
} catch (error) {
|
|
2930
2805
|
throw new CommandExit(1, `\u274C ${USAGE4}: agent "${copy.name}" could not be written to ${copy.to}: ${String(error)}`);
|
|
2931
2806
|
}
|
|
@@ -4274,7 +4149,7 @@ import { statSync as statSync5 } from "fs";
|
|
|
4274
4149
|
import { join as join10 } from "path";
|
|
4275
4150
|
|
|
4276
4151
|
// src/board/setup-config.ts
|
|
4277
|
-
import { readFileSync as
|
|
4152
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
4278
4153
|
function roadmapIssueIn(text, path) {
|
|
4279
4154
|
return parseConfigText(text, path).values.roadmapIssue ?? null;
|
|
4280
4155
|
}
|
|
@@ -4282,7 +4157,7 @@ function readRoadmapSetting(root) {
|
|
|
4282
4157
|
const path = configFilePath(root);
|
|
4283
4158
|
let text;
|
|
4284
4159
|
try {
|
|
4285
|
-
text =
|
|
4160
|
+
text = readFileSync6(path, "utf8");
|
|
4286
4161
|
} catch (error) {
|
|
4287
4162
|
return { issue: null, text: "", problem: `${path} could not be read: ${messageOf(error)}` };
|
|
4288
4163
|
}
|
|
@@ -4331,7 +4206,7 @@ function withRoadmapIssue(text, issue) {
|
|
|
4331
4206
|
}
|
|
4332
4207
|
|
|
4333
4208
|
// src/board/setup.ts
|
|
4334
|
-
import { existsSync as existsSync4, lstatSync, mkdirSync as mkdirSync3, readFileSync as
|
|
4209
|
+
import { existsSync as existsSync4, lstatSync, mkdirSync as mkdirSync3, readFileSync as readFileSync7, statSync as statSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
4335
4210
|
import { dirname, join as join9 } from "path";
|
|
4336
4211
|
import { fileURLToPath } from "url";
|
|
4337
4212
|
var PREFIX = "board setup";
|
|
@@ -4466,7 +4341,7 @@ function readSpecTemplate(moduleDir = MODULE_DIR) {
|
|
|
4466
4341
|
if (!existsSync4(source)) {
|
|
4467
4342
|
throw new Error(`${PREFIX}: the spec issue template is missing: no file at ${source}`);
|
|
4468
4343
|
}
|
|
4469
|
-
return
|
|
4344
|
+
return readFileSync7(source, "utf8");
|
|
4470
4345
|
}
|
|
4471
4346
|
function writeSpecTemplate(root, moduleDir = MODULE_DIR) {
|
|
4472
4347
|
const path = join9(root, SPEC_TEMPLATE_PATH);
|
|
@@ -4956,7 +4831,7 @@ function readScope2(scope, path, loaded) {
|
|
|
4956
4831
|
}
|
|
4957
4832
|
return { read: { scope, path, loaded, entries }, warnings };
|
|
4958
4833
|
}
|
|
4959
|
-
function
|
|
4834
|
+
function readScopes2(seams) {
|
|
4960
4835
|
const read = OVERRIDE_SCOPES.flatMap((scope) => {
|
|
4961
4836
|
const path = overrideSettingsPath(scope, { home: seams.home, projectRoot: seams.projectRoot });
|
|
4962
4837
|
return path === null ? [] : [readScope2(scope, path, seams.settingSources.includes(scope))];
|
|
@@ -5009,7 +4884,7 @@ function pathDifference(shell, session, layer) {
|
|
|
5009
4884
|
return Object.freeze({ layer, added: Object.freeze(added), removed: Object.freeze(removed), reordered });
|
|
5010
4885
|
}
|
|
5011
4886
|
function readSessionEnv(seams) {
|
|
5012
|
-
const { scopes, warnings } =
|
|
4887
|
+
const { scopes, warnings } = readScopes2(seams);
|
|
5013
4888
|
const { env, layers } = overlay(sessionSpawnEnv(seams.env), seams.env, scopes);
|
|
5014
4889
|
return Object.freeze({
|
|
5015
4890
|
env: Object.freeze(env),
|
|
@@ -6426,7 +6301,7 @@ function writeInstall(context, install) {
|
|
|
6426
6301
|
}
|
|
6427
6302
|
|
|
6428
6303
|
// src/commands/doctor-refs.ts
|
|
6429
|
-
import { readdirSync, readFileSync as
|
|
6304
|
+
import { readdirSync, readFileSync as readFileSync8, statSync as statSync8 } from "fs";
|
|
6430
6305
|
import { join as join16, resolve as resolve5 } from "path";
|
|
6431
6306
|
function issueCheckCommand(issue) {
|
|
6432
6307
|
return `rafa issue check ${String(issue)}`;
|
|
@@ -6496,7 +6371,7 @@ async function readCopy(file, input, verify) {
|
|
|
6496
6371
|
const path = join16(input.specsDir, file.name);
|
|
6497
6372
|
const empty = { issue: file.issue, path, suspect: 0, dangling: 0, unknown: 0 };
|
|
6498
6373
|
try {
|
|
6499
|
-
const copy =
|
|
6374
|
+
const copy = readFileSync8(resolve5(input.root, path), "utf8");
|
|
6500
6375
|
const { rows } = await readRefsText({ copy, issue: file.issue, verify: await verify() });
|
|
6501
6376
|
const states = rows.map((row) => row.state);
|
|
6502
6377
|
return Object.freeze({
|
|
@@ -6788,14 +6663,14 @@ function renderBoard(board) {
|
|
|
6788
6663
|
}
|
|
6789
6664
|
|
|
6790
6665
|
// src/commands/doctor-tiers.ts
|
|
6791
|
-
import { readFileSync as
|
|
6666
|
+
import { readFileSync as readFileSync9, realpathSync as realpathSync4 } from "fs";
|
|
6792
6667
|
import { basename as basename5, dirname as dirname2 } from "path";
|
|
6793
6668
|
var TIERS_SECTION_TITLE = "Skill tiers";
|
|
6794
6669
|
var CLAUDE_VERSION_TIMEOUT_MS = 1e4;
|
|
6795
6670
|
var VERSION_PATTERN = /^\s*(\d+\.\d+\.\d+)/;
|
|
6796
6671
|
function readTextFile(path) {
|
|
6797
6672
|
try {
|
|
6798
|
-
return
|
|
6673
|
+
return readFileSync9(path, "utf8");
|
|
6799
6674
|
} catch {
|
|
6800
6675
|
return null;
|
|
6801
6676
|
}
|
|
@@ -6882,12 +6757,18 @@ function cliVersionRows(installed, pinned = SERVE_CLI_VERSION) {
|
|
|
6882
6757
|
}
|
|
6883
6758
|
return [row("cli-version", "warn", name2, `${installed} is installed, and skill delivery was probed against ${pinned}`, 'run the delivery probe again (context/inventory.md, "Serving") before trusting SERVE_CLI_VERSION')];
|
|
6884
6759
|
}
|
|
6760
|
+
function skillUseVersionRows(installed, pinned = SKILL_USE_CLI_VERSION) {
|
|
6761
|
+
if (installed === null || installed === pinned)
|
|
6762
|
+
return [];
|
|
6763
|
+
return [row("skill-use-version", "warn", "Claude Code", `${installed} is installed, and the skill-use collector reads logs of ${pinned}, ` + "so skill_invocations stores the skill counts of the sessions it logs as unknown", "record the skill-use fixture again under the installed version (src/effort/skill-use.ts) before moving SKILL_USE_CLI_VERSION")];
|
|
6764
|
+
}
|
|
6885
6765
|
function doctorTierRows(inventory, installed, readers = {}) {
|
|
6886
6766
|
const readText2 = readers.readText ?? readTextFile;
|
|
6887
6767
|
return [
|
|
6888
6768
|
...collisionRows(inventory.resolution.collisions),
|
|
6889
6769
|
...unreviewedRows(inventory.records, readText2),
|
|
6890
6770
|
...cliVersionRows(installed),
|
|
6771
|
+
...skillUseVersionRows(installed),
|
|
6891
6772
|
...copyRows(inventory.resolution.items, readers.realPath ?? realPathOf),
|
|
6892
6773
|
...noProvenanceRows(inventory, readText2)
|
|
6893
6774
|
];
|
|
@@ -7243,7 +7124,7 @@ var collect_default = wrapPhaseZeroCommand({
|
|
|
7243
7124
|
subject: "effort",
|
|
7244
7125
|
action: "collect",
|
|
7245
7126
|
summary: "collect session and commit rows into the effort store",
|
|
7246
|
-
description: "Reads the Claude Code session logs of this repo and its commit history, and appends every" + " session and commit the effort store does not hold yet to the store `store` in" + " `.rafa/config.yaml` selects. A run over an unchanged history appends nothing. An unrecognised" + " argument, a `--since` that is no date and a config the loop cannot run on are each refused," + " one line per problem.",
|
|
7127
|
+
description: "Reads the Claude Code session logs of this repo and its commit history, and appends every" + " session and commit the effort store does not hold yet to the store `store` in" + " `.rafa/config.yaml` selects, then counts the skills each appended session invoked into" + " `effort.sqlite`. A run over an unchanged history appends nothing. An unrecognised" + " argument, a `--since` that is no date and a config the loop cannot run on are each refused," + " one line per problem.",
|
|
7247
7128
|
args: [],
|
|
7248
7129
|
flags: [
|
|
7249
7130
|
{
|
|
@@ -7259,10 +7140,15 @@ var collect_default = wrapPhaseZeroCommand({
|
|
|
7259
7140
|
},
|
|
7260
7141
|
{
|
|
7261
7142
|
name: "sessions",
|
|
7262
|
-
description: "Collects session rows; `--no-sessions` skips them. Refused beside `--no-git`," + " which would leave nothing to collect.",
|
|
7143
|
+
description: "Collects session rows; `--no-sessions` skips them. Refused beside `--no-git`," + " which would leave nothing to collect, unless `--skills` is given.",
|
|
7263
7144
|
type: "boolean",
|
|
7264
7145
|
default: true
|
|
7265
7146
|
},
|
|
7147
|
+
{
|
|
7148
|
+
name: "skills",
|
|
7149
|
+
description: "Counts the `Skill` calls of every session the store already holds, not only the ones" + " this run appends. Lifts the refusal of `--no-git` beside `--no-sessions`.",
|
|
7150
|
+
type: "boolean"
|
|
7151
|
+
},
|
|
7266
7152
|
{
|
|
7267
7153
|
name: "verbose",
|
|
7268
7154
|
description: "Prints what each half found before the summary: the logs and plan stubs it read," + " each session read, and the commits parsed.",
|
|
@@ -7282,6 +7168,275 @@ var collect_default = wrapPhaseZeroCommand({
|
|
|
7282
7168
|
outputs: ["text", "json"]
|
|
7283
7169
|
}, collect);
|
|
7284
7170
|
|
|
7171
|
+
// src/effort/store/fix-schema.ts
|
|
7172
|
+
import { existsSync as existsSync5, renameSync, rmSync as rmSync2 } from "fs";
|
|
7173
|
+
import { Database } from "bun:sqlite";
|
|
7174
|
+
class SchemaFixRefusal extends Error {
|
|
7175
|
+
constructor(message) {
|
|
7176
|
+
super(message);
|
|
7177
|
+
this.name = "SchemaFixRefusal";
|
|
7178
|
+
}
|
|
7179
|
+
}
|
|
7180
|
+
var LIVE = "live";
|
|
7181
|
+
function quoted(name2) {
|
|
7182
|
+
return `"${name2.replaceAll('"', '""')}"`;
|
|
7183
|
+
}
|
|
7184
|
+
function tableNames(db, schema) {
|
|
7185
|
+
return db.query(`SELECT name FROM ${schema}.sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name`).all().map((row2) => row2.name);
|
|
7186
|
+
}
|
|
7187
|
+
function columnNames(db, schema, table) {
|
|
7188
|
+
return db.query("SELECT name FROM pragma_table_info(?, ?)").all(table, schema).map((row2) => row2.name);
|
|
7189
|
+
}
|
|
7190
|
+
function count(db, sql) {
|
|
7191
|
+
return db.query(sql).get()?.n ?? 0;
|
|
7192
|
+
}
|
|
7193
|
+
function versionOf(path) {
|
|
7194
|
+
const db = new Database(path, { readonly: true });
|
|
7195
|
+
try {
|
|
7196
|
+
return db.query("PRAGMA user_version").get()?.user_version ?? 0;
|
|
7197
|
+
} finally {
|
|
7198
|
+
db.close();
|
|
7199
|
+
}
|
|
7200
|
+
}
|
|
7201
|
+
function refuseInFlight(path) {
|
|
7202
|
+
for (const suffix of ["-journal", "-wal"]) {
|
|
7203
|
+
if (existsSync5(`${path}${suffix}`)) {
|
|
7204
|
+
throw new SchemaFixRefusal(`${path}${suffix} is there, so a write is in flight or was interrupted; stop whatever uses the store` + " and open it once with rafa before repairing it");
|
|
7205
|
+
}
|
|
7206
|
+
}
|
|
7207
|
+
}
|
|
7208
|
+
function refuseRemoved(db, known, live) {
|
|
7209
|
+
for (const table of known) {
|
|
7210
|
+
if (!live.has(table)) {
|
|
7211
|
+
throw new SchemaFixRefusal(`the store holds no table ${table}, which this rafa writes; its newer schema is not additive`);
|
|
7212
|
+
}
|
|
7213
|
+
const liveColumns = new Set(columnNames(db, LIVE, table));
|
|
7214
|
+
const missing = columnNames(db, "main", table).filter((column) => !liveColumns.has(column));
|
|
7215
|
+
if (missing.length > 0) {
|
|
7216
|
+
throw new SchemaFixRefusal(`the store holds no column ${missing.map((column) => `${table}.${column}`).join(", ")}, which this rafa` + " writes; its newer schema is not additive");
|
|
7217
|
+
}
|
|
7218
|
+
}
|
|
7219
|
+
}
|
|
7220
|
+
function copyKnown(db, known) {
|
|
7221
|
+
db.transaction(() => {
|
|
7222
|
+
for (const table of known) {
|
|
7223
|
+
const columns2 = columnNames(db, "main", table).map(quoted).join(", ");
|
|
7224
|
+
db.run(`INSERT INTO main.${quoted(table)} (${columns2}) SELECT ${columns2} FROM ${LIVE}.${quoted(table)}`);
|
|
7225
|
+
}
|
|
7226
|
+
})();
|
|
7227
|
+
}
|
|
7228
|
+
function checkedCounts(db, known) {
|
|
7229
|
+
return known.map((table) => {
|
|
7230
|
+
const rows = count(db, `SELECT count(*) AS n FROM main.${quoted(table)}`);
|
|
7231
|
+
const liveRows = count(db, `SELECT count(*) AS n FROM ${LIVE}.${quoted(table)}`);
|
|
7232
|
+
if (rows !== liveRows) {
|
|
7233
|
+
throw new SchemaFixRefusal(`the rebuilt ${table} holds ${String(rows)} rows, the store ${String(liveRows)}`);
|
|
7234
|
+
}
|
|
7235
|
+
return { table, rows };
|
|
7236
|
+
});
|
|
7237
|
+
}
|
|
7238
|
+
function leftBehind(db, known, live) {
|
|
7239
|
+
const leftTables = live.filter((table) => !known.has(table)).map((table) => ({ table, rows: count(db, `SELECT count(*) AS n FROM ${LIVE}.${quoted(table)}`) }));
|
|
7240
|
+
const leftColumns = live.filter((table) => known.has(table)).flatMap((table) => {
|
|
7241
|
+
const kept = new Set(columnNames(db, "main", table));
|
|
7242
|
+
return columnNames(db, LIVE, table).filter((column) => !kept.has(column)).map((column) => ({
|
|
7243
|
+
table,
|
|
7244
|
+
column,
|
|
7245
|
+
values: count(db, `SELECT count(*) AS n FROM ${LIVE}.${quoted(table)} WHERE ${quoted(column)} IS NOT NULL`)
|
|
7246
|
+
}));
|
|
7247
|
+
});
|
|
7248
|
+
return { leftTables, leftColumns };
|
|
7249
|
+
}
|
|
7250
|
+
function refuseCorrupt(db) {
|
|
7251
|
+
const answer = db.query("PRAGMA main.integrity_check").get()?.integrity_check;
|
|
7252
|
+
if (answer !== "ok")
|
|
7253
|
+
throw new SchemaFixRefusal(`the rebuilt store failed integrity_check: ${answer ?? "no answer"}`);
|
|
7254
|
+
}
|
|
7255
|
+
function buildParallel(path, parallelPath, migrations) {
|
|
7256
|
+
const db = new Database(parallelPath, { create: true, readwrite: true });
|
|
7257
|
+
try {
|
|
7258
|
+
migrateSchema(db, parallelPath, migrations);
|
|
7259
|
+
db.run(`ATTACH DATABASE ? AS ${LIVE}`, [path]);
|
|
7260
|
+
const known = tableNames(db, "main");
|
|
7261
|
+
const live = tableNames(db, LIVE);
|
|
7262
|
+
refuseRemoved(db, known, new Set(live));
|
|
7263
|
+
copyKnown(db, known);
|
|
7264
|
+
const kept = checkedCounts(db, known);
|
|
7265
|
+
const left = leftBehind(db, new Set(known), live);
|
|
7266
|
+
db.run(`DETACH DATABASE ${LIVE}`);
|
|
7267
|
+
refuseCorrupt(db);
|
|
7268
|
+
return { kept, ...left };
|
|
7269
|
+
} finally {
|
|
7270
|
+
db.close();
|
|
7271
|
+
}
|
|
7272
|
+
}
|
|
7273
|
+
function removeParallel(parallelPath) {
|
|
7274
|
+
rmSync2(parallelPath, { force: true });
|
|
7275
|
+
rmSync2(`${parallelPath}-journal`, { force: true });
|
|
7276
|
+
}
|
|
7277
|
+
function swapIn(path, parallelPath, backupPath) {
|
|
7278
|
+
renameSync(path, backupPath);
|
|
7279
|
+
try {
|
|
7280
|
+
renameSync(parallelPath, path);
|
|
7281
|
+
} catch (error) {
|
|
7282
|
+
renameSync(backupPath, path);
|
|
7283
|
+
throw error;
|
|
7284
|
+
}
|
|
7285
|
+
}
|
|
7286
|
+
function freshPaths(path, version2, stamp) {
|
|
7287
|
+
const parallelPath = `${path}.fix-${stamp}`;
|
|
7288
|
+
const backupPath = `${path}.v${String(version2)}-${stamp}.bak`;
|
|
7289
|
+
for (const taken of [parallelPath, backupPath]) {
|
|
7290
|
+
if (existsSync5(taken))
|
|
7291
|
+
throw new SchemaFixRefusal(`${taken} is already there; move it aside or pass another stamp`);
|
|
7292
|
+
}
|
|
7293
|
+
return { parallelPath, backupPath };
|
|
7294
|
+
}
|
|
7295
|
+
function fixStoreSchema(options) {
|
|
7296
|
+
const { path, dryRun, stamp } = options;
|
|
7297
|
+
const migrations = options.migrations ?? SQLITE_MIGRATIONS;
|
|
7298
|
+
const knownVersion = migrations.length;
|
|
7299
|
+
const nothing = { kept: [], leftTables: [], leftColumns: [], backupPath: null };
|
|
7300
|
+
if (!existsSync5(path))
|
|
7301
|
+
return { path, status: "missing", storeVersion: null, knownVersion, ...nothing };
|
|
7302
|
+
refuseInFlight(path);
|
|
7303
|
+
const storeVersion = versionOf(path);
|
|
7304
|
+
if (storeVersion === knownVersion)
|
|
7305
|
+
return { path, status: "current", storeVersion, knownVersion, ...nothing };
|
|
7306
|
+
if (storeVersion < knownVersion)
|
|
7307
|
+
return { path, status: "behind", storeVersion, knownVersion, ...nothing };
|
|
7308
|
+
const { parallelPath, backupPath } = freshPaths(path, storeVersion, stamp);
|
|
7309
|
+
let built;
|
|
7310
|
+
try {
|
|
7311
|
+
built = buildParallel(path, parallelPath, migrations);
|
|
7312
|
+
} catch (error) {
|
|
7313
|
+
removeParallel(parallelPath);
|
|
7314
|
+
throw error;
|
|
7315
|
+
}
|
|
7316
|
+
if (dryRun) {
|
|
7317
|
+
removeParallel(parallelPath);
|
|
7318
|
+
return { path, status: "would-rebuild", storeVersion, knownVersion, ...built, backupPath: null };
|
|
7319
|
+
}
|
|
7320
|
+
swapIn(path, parallelPath, backupPath);
|
|
7321
|
+
return { path, status: "rebuilt", storeVersion, knownVersion, ...built, backupPath };
|
|
7322
|
+
}
|
|
7323
|
+
|
|
7324
|
+
// src/commands/effort/fix-schema.ts
|
|
7325
|
+
var COMMAND_NAME5 = "rafa effort fix-schema";
|
|
7326
|
+
var FIX_SCHEMA_USAGE = "rafa effort fix-schema [--dry-run]";
|
|
7327
|
+
var DRY_RUN_FLAG2 = "dry-run";
|
|
7328
|
+
function fileStamp(date) {
|
|
7329
|
+
return date.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
7330
|
+
}
|
|
7331
|
+
function refuseLiveLoop(root, isAlive) {
|
|
7332
|
+
let records;
|
|
7333
|
+
try {
|
|
7334
|
+
records = readSessions(root, isAlive === undefined ? {} : { isAlive });
|
|
7335
|
+
} catch (error) {
|
|
7336
|
+
throw new CommandExit(1, `\u274C ${COMMAND_NAME5}: the loop's run records cannot be read, so a live loop cannot be ruled out: ${String(error)}`);
|
|
7337
|
+
}
|
|
7338
|
+
const live = records.find((record) => record.state === "running" || record.state === "paused");
|
|
7339
|
+
if (live === undefined)
|
|
7340
|
+
return;
|
|
7341
|
+
throw new CommandExit(1, `\u274C ${COMMAND_NAME5}: loop session ${live.sessionId} on ${live.branch} is ${live.state} and writes the store;` + ` stop it with \`rafa loop stop -s ${live.sessionId}\` first. \`--dry-run\` runs beside it.`);
|
|
7342
|
+
}
|
|
7343
|
+
function rebuildLines(result) {
|
|
7344
|
+
const rows = result.kept.reduce((sum, table) => sum + table.rows, 0);
|
|
7345
|
+
const kept = result.kept.map((table) => `${table.table} ${String(table.rows)}`).join(", ");
|
|
7346
|
+
const left = [
|
|
7347
|
+
...result.leftTables.map((table) => `table ${table.table} (${String(table.rows)} rows)`),
|
|
7348
|
+
...result.leftColumns.map((column) => `column ${column.table}.${column.column} (${String(column.values)} values)`)
|
|
7349
|
+
];
|
|
7350
|
+
return [
|
|
7351
|
+
`${result.path} is at schema version ${String(result.storeVersion)}, past the ${String(result.knownVersion)} this rafa knows.`,
|
|
7352
|
+
`Keeps ${String(result.kept.length)} tables, ${String(rows)} rows: ${kept}.`,
|
|
7353
|
+
left.length === 0 ? "Leaves nothing behind." : `Leaves behind, kept only in the backup: ${left.join("; ")}.`
|
|
7354
|
+
];
|
|
7355
|
+
}
|
|
7356
|
+
function renderFixSchema(result) {
|
|
7357
|
+
switch (result.status) {
|
|
7358
|
+
case "missing":
|
|
7359
|
+
return [`No effort store at ${result.path}: nothing to repair.`];
|
|
7360
|
+
case "current":
|
|
7361
|
+
return [`\u2705 ${result.path} is at schema version ${String(result.knownVersion)}, the one this rafa knows: nothing to repair.`];
|
|
7362
|
+
case "behind":
|
|
7363
|
+
return [
|
|
7364
|
+
`${result.path} is at schema version ${String(result.storeVersion)}, behind the ${String(result.knownVersion)} this rafa` + " knows; the next command that opens it migrates it. Nothing to repair."
|
|
7365
|
+
];
|
|
7366
|
+
case "would-rebuild":
|
|
7367
|
+
return [
|
|
7368
|
+
...rebuildLines(result),
|
|
7369
|
+
`\uD83D\uDD0D Dry run: the rebuild at version ${String(result.knownVersion)} was built, checked (row counts, integrity_check)` + " and deleted. Run without `--dry-run` to swap it in."
|
|
7370
|
+
];
|
|
7371
|
+
case "rebuilt":
|
|
7372
|
+
return [
|
|
7373
|
+
...rebuildLines(result),
|
|
7374
|
+
`\u2705 Rebuilt at version ${String(result.knownVersion)}. The original is kept whole at ${String(result.backupPath)};` + " rename it back to undo."
|
|
7375
|
+
];
|
|
7376
|
+
}
|
|
7377
|
+
}
|
|
7378
|
+
function runFixSchema(context, seams) {
|
|
7379
|
+
expectNoArgument(context.args, FIX_SCHEMA_USAGE);
|
|
7380
|
+
const dryRun = readSwitch(DRY_RUN_FLAG2, context.flags[DRY_RUN_FLAG2], `Usage: ${FIX_SCHEMA_USAGE}`);
|
|
7381
|
+
if (context.project === null)
|
|
7382
|
+
throw new Error(`${COMMAND_NAME5} runs inside a project, and was handed none`);
|
|
7383
|
+
const root = context.project.root;
|
|
7384
|
+
if (!dryRun)
|
|
7385
|
+
refuseLiveLoop(root, seams.isAlive);
|
|
7386
|
+
let result;
|
|
7387
|
+
try {
|
|
7388
|
+
result = fixStoreSchema({
|
|
7389
|
+
path: sqliteStorePath(root),
|
|
7390
|
+
dryRun,
|
|
7391
|
+
stamp: fileStamp((seams.now ?? (() => new Date))())
|
|
7392
|
+
});
|
|
7393
|
+
} catch (error) {
|
|
7394
|
+
if (error instanceof SchemaFixRefusal)
|
|
7395
|
+
throw new CommandExit(1, `\u274C ${COMMAND_NAME5}: ${error.message}`);
|
|
7396
|
+
throw error;
|
|
7397
|
+
}
|
|
7398
|
+
if (context.outputMode === "json") {
|
|
7399
|
+
context.output.result(result);
|
|
7400
|
+
return;
|
|
7401
|
+
}
|
|
7402
|
+
for (const line of renderFixSchema(result))
|
|
7403
|
+
context.output.info(line);
|
|
7404
|
+
}
|
|
7405
|
+
function createFixSchemaCommand(seams = {}) {
|
|
7406
|
+
const command = {
|
|
7407
|
+
name: "effort fix-schema",
|
|
7408
|
+
subject: "effort",
|
|
7409
|
+
action: "fix-schema",
|
|
7410
|
+
summary: "rebuild an effort store a newer rafa migrated at the version this rafa knows, keeping the original",
|
|
7411
|
+
description: "Repairs `.rafa/effort/effort.sqlite` when it is past the schema version this rafa knows, which" + " happens when newer code opens it, such as a plan's own branch code run while an older installed rafa" + " drives the loop. It builds a parallel store beside it at this rafa's version, copies every table and" + " column this rafa knows, checks the row counts and SQLite's integrity_check, and lists what only the" + " newer schema holds. Then it renames the original to `effort.sqlite.v<version>-<stamp>.bak`, whole, and" + " moves the rebuild into its place. A store at or behind this version, or no store, is left alone. It" + " refuses, changing nothing, while a loop session under the project is running or paused, while a" + " journal beside the store shows a write in flight, and when the newer schema lacks a table or column" + " this rafa writes. With `--output=json` the outcome is the data of the terminal result event. Starts no" + " session.",
|
|
7412
|
+
args: [],
|
|
7413
|
+
flags: [
|
|
7414
|
+
{
|
|
7415
|
+
name: DRY_RUN_FLAG2,
|
|
7416
|
+
description: "Build and check the rebuild, print what it keeps and leaves behind, then delete it. The" + " live store is only read, so this runs beside a live loop and can be repeated.",
|
|
7417
|
+
type: "boolean"
|
|
7418
|
+
}
|
|
7419
|
+
],
|
|
7420
|
+
examples: [
|
|
7421
|
+
{
|
|
7422
|
+
cmd: "rafa effort fix-schema --dry-run",
|
|
7423
|
+
note: "Prints what a rebuild would keep and leave behind, changing nothing."
|
|
7424
|
+
},
|
|
7425
|
+
{
|
|
7426
|
+
cmd: "rafa effort fix-schema",
|
|
7427
|
+
note: "Swaps the rebuild in and prints where the original was kept."
|
|
7428
|
+
}
|
|
7429
|
+
],
|
|
7430
|
+
outputs: ["text", "json"],
|
|
7431
|
+
run: async (context) => {
|
|
7432
|
+
runFixSchema(context, seams);
|
|
7433
|
+
await Promise.resolve();
|
|
7434
|
+
}
|
|
7435
|
+
};
|
|
7436
|
+
return Object.freeze(command);
|
|
7437
|
+
}
|
|
7438
|
+
var fix_schema_default = createFixSchemaCommand();
|
|
7439
|
+
|
|
7285
7440
|
// src/commands/effort/report.ts
|
|
7286
7441
|
var report_default = wrapPhaseZeroCommand({
|
|
7287
7442
|
name: "effort report",
|
|
@@ -7322,12 +7477,12 @@ var report_default = wrapPhaseZeroCommand({
|
|
|
7322
7477
|
}, report);
|
|
7323
7478
|
|
|
7324
7479
|
// src/commands/init.ts
|
|
7325
|
-
import { existsSync as
|
|
7480
|
+
import { existsSync as existsSync8, readFileSync as readFileSync14 } from "fs";
|
|
7326
7481
|
import { homedir } from "os";
|
|
7327
7482
|
import { join as join21, relative as relative6, sep as sep4 } from "path";
|
|
7328
7483
|
|
|
7329
7484
|
// src/agents/vendorable.ts
|
|
7330
|
-
import { readdirSync as readdirSync2, readFileSync as
|
|
7485
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync10 } from "fs";
|
|
7331
7486
|
import { isAbsolute as isAbsolute5, join as join17 } from "path";
|
|
7332
7487
|
function planFiles(dir) {
|
|
7333
7488
|
let entries;
|
|
@@ -7340,7 +7495,7 @@ function planFiles(dir) {
|
|
|
7340
7495
|
}
|
|
7341
7496
|
function readText2(path) {
|
|
7342
7497
|
try {
|
|
7343
|
-
return
|
|
7498
|
+
return readFileSync10(path, "utf8");
|
|
7344
7499
|
} catch {
|
|
7345
7500
|
return null;
|
|
7346
7501
|
}
|
|
@@ -7385,7 +7540,7 @@ function vendorableAgentWarnings(agents, root) {
|
|
|
7385
7540
|
|
|
7386
7541
|
// src/project/gitignore.ts
|
|
7387
7542
|
import { createHash } from "crypto";
|
|
7388
|
-
import { existsSync as
|
|
7543
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync11, writeFileSync as writeFileSync3 } from "fs";
|
|
7389
7544
|
import { dirname as dirname3, isAbsolute as isAbsolute6, join as join18 } from "path";
|
|
7390
7545
|
var GITIGNORE_FILE = ".gitignore";
|
|
7391
7546
|
var DIGEST_NAME = "tracking.digest";
|
|
@@ -7472,9 +7627,9 @@ function attempt(file, failure3, act) {
|
|
|
7472
7627
|
}
|
|
7473
7628
|
}
|
|
7474
7629
|
function readIfPresent2(file) {
|
|
7475
|
-
if (!
|
|
7630
|
+
if (!existsSync6(file))
|
|
7476
7631
|
return null;
|
|
7477
|
-
return attempt(file, "cannot be read", () =>
|
|
7632
|
+
return attempt(file, "cannot be read", () => readFileSync11(file, "utf8"));
|
|
7478
7633
|
}
|
|
7479
7634
|
function writeTrackingGitignore(root, flags) {
|
|
7480
7635
|
requireAbsolute(root);
|
|
@@ -7537,7 +7692,7 @@ import { resolve as resolve8 } from "path";
|
|
|
7537
7692
|
|
|
7538
7693
|
// src/project/roots.ts
|
|
7539
7694
|
import { spawnSync } from "child_process";
|
|
7540
|
-
import { accessSync, constants, readFileSync as
|
|
7695
|
+
import { accessSync, constants, readFileSync as readFileSync12, statSync as statSync9 } from "fs";
|
|
7541
7696
|
import { isAbsolute as isAbsolute7, join as join19, resolve as resolve7 } from "path";
|
|
7542
7697
|
var REFUSED_ROOTS = Object.freeze([
|
|
7543
7698
|
{ path: "/", why: "is the filesystem root" },
|
|
@@ -7558,7 +7713,7 @@ var PACKAGE_JSON = "package.json";
|
|
|
7558
7713
|
var NOT_A_REPOSITORY = "fatal: not a git repository (or any ";
|
|
7559
7714
|
var DISK_ROOTS_FILE_SYSTEM = Object.freeze({
|
|
7560
7715
|
...DISK_FILE_SYSTEM,
|
|
7561
|
-
readFile: (path) =>
|
|
7716
|
+
readFile: (path) => readFileSync12(path, "utf8"),
|
|
7562
7717
|
isDirectory: (path) => statSync9(path).isDirectory(),
|
|
7563
7718
|
assertWritable: (path) => accessSync(path, constants.W_OK)
|
|
7564
7719
|
});
|
|
@@ -7750,10 +7905,10 @@ function namedRoot(typed, start2, source, seams) {
|
|
|
7750
7905
|
return chosen(fs.realpath(path), source);
|
|
7751
7906
|
}
|
|
7752
7907
|
function numberedCandidate(digits, found) {
|
|
7753
|
-
const
|
|
7908
|
+
const count2 = found.candidates.length;
|
|
7754
7909
|
const candidate = found.candidates[Number(digits) - 1];
|
|
7755
7910
|
if (candidate === undefined) {
|
|
7756
|
-
return problem(`no candidate is numbered ${digits}; type 1 to ${String(
|
|
7911
|
+
return problem(`no candidate is numbered ${digits}; type 1 to ${String(count2)}, or a path`);
|
|
7757
7912
|
}
|
|
7758
7913
|
return candidate.refusal === null ? chosen(candidate.path, candidate.source) : problem(candidate.refusal.reason);
|
|
7759
7914
|
}
|
|
@@ -7783,7 +7938,7 @@ async function promptForRoot(found, prompter, seams) {
|
|
|
7783
7938
|
}
|
|
7784
7939
|
|
|
7785
7940
|
// src/project/scaffold.ts
|
|
7786
|
-
import { existsSync as
|
|
7941
|
+
import { existsSync as existsSync7, lstatSync as lstatSync2, mkdirSync as mkdirSync5, statSync as statSync10, writeFileSync as writeFileSync4 } from "fs";
|
|
7787
7942
|
import { join as join20 } from "path";
|
|
7788
7943
|
var PROJECT_TREE = Object.freeze(["specs", "plans", "runs", "effort", "instincts"]);
|
|
7789
7944
|
var USER_TREE = Object.freeze(["instincts"]);
|
|
@@ -7799,6 +7954,11 @@ var CONFIG_SETTINGS_LINES = Object.freeze([
|
|
|
7799
7954
|
`# fallback: [${CONFIG_DEFAULTS.trackerFallback.join(", ")}]`,
|
|
7800
7955
|
"# learning:",
|
|
7801
7956
|
`# adapter: ${CONFIG_DEFAULTS.learningAdapter}`,
|
|
7957
|
+
"# bless:",
|
|
7958
|
+
`# minConfidence: ${String(CONFIG_DEFAULTS.learningBlessMinConfidence)} # 0.3..0.9, the least confidence of a lesson tasks may use`,
|
|
7959
|
+
"# promote:",
|
|
7960
|
+
`# after: ${String(CONFIG_DEFAULTS.learningPromoteAfter)} # distinct sources a lesson needs before the wrap-up promotes it`,
|
|
7961
|
+
`# minConfidence: ${String(CONFIG_DEFAULTS.learningPromoteMinConfidence)} # 0.3..0.9, the least confidence of a lesson to promote`,
|
|
7802
7962
|
"# output:",
|
|
7803
7963
|
`# mode: ${CONFIG_DEFAULTS.outputMode} # text | json`,
|
|
7804
7964
|
"# prerequisites:",
|
|
@@ -7839,7 +7999,10 @@ var CONFIG_SETTINGS_LINES = Object.freeze([
|
|
|
7839
7999
|
"# skills: {} # name: false turns a skill off; name: project | rafa | user pins its tier",
|
|
7840
8000
|
"# agents: {} # name: false turns an agent off; name: project | rafa | user pins its tier",
|
|
7841
8001
|
"# routing:",
|
|
7842
|
-
...DEFAULT_ROUTES.map(([shape, agent]) => `# ${shape}: ${agent}`)
|
|
8002
|
+
...DEFAULT_ROUTES.map(([shape, agent]) => `# ${shape}: ${agent}`),
|
|
8003
|
+
"# task:",
|
|
8004
|
+
`# skills: ${CONFIG_DEFAULTS.taskSkills} # planner | tag | none, the resolver that picks a task's skills`,
|
|
8005
|
+
`# lessons: ${CONFIG_DEFAULTS.taskLessons} # on | off, whether blessed lessons join a task's prompt`
|
|
7843
8006
|
]);
|
|
7844
8007
|
var VERSION_LINE = `version: ${String(CONFIG_DEFAULTS.version)}`;
|
|
7845
8008
|
var PROJECT_HEADER = Object.freeze([
|
|
@@ -7888,7 +8051,7 @@ function isDirectory2(path) {
|
|
|
7888
8051
|
function directoryConflict(dir) {
|
|
7889
8052
|
if (!occupied(dir) || isDirectory2(dir))
|
|
7890
8053
|
return null;
|
|
7891
|
-
return
|
|
8054
|
+
return existsSync7(dir) ? `${dir} is not a directory` : `${dir} is a link to nothing`;
|
|
7892
8055
|
}
|
|
7893
8056
|
function layoutConflicts(layout) {
|
|
7894
8057
|
const [scopeDir, ...tree] = layout.dirs;
|
|
@@ -7896,7 +8059,7 @@ function layoutConflicts(layout) {
|
|
|
7896
8059
|
if (opening !== null)
|
|
7897
8060
|
return [opening];
|
|
7898
8061
|
const dirs = tree.flatMap((dir) => directoryConflict(dir) ?? []);
|
|
7899
|
-
const file = occupied(layout.configFile) && !
|
|
8062
|
+
const file = occupied(layout.configFile) && !existsSync7(layout.configFile) ? [`${layout.configFile} is a link to nothing`] : [];
|
|
7900
8063
|
return [...dirs, ...file];
|
|
7901
8064
|
}
|
|
7902
8065
|
function scaffoldConflicts(root, home) {
|
|
@@ -7910,7 +8073,7 @@ function attempt2(path, failure3, act) {
|
|
|
7910
8073
|
}
|
|
7911
8074
|
}
|
|
7912
8075
|
function ensureDirectory(path) {
|
|
7913
|
-
if (
|
|
8076
|
+
if (existsSync7(path))
|
|
7914
8077
|
return { path, kind: "directory", change: "unchanged" };
|
|
7915
8078
|
attempt2(path, "cannot be created", () => {
|
|
7916
8079
|
mkdirSync5(path, { recursive: true });
|
|
@@ -7918,7 +8081,7 @@ function ensureDirectory(path) {
|
|
|
7918
8081
|
return { path, kind: "directory", change: "created" };
|
|
7919
8082
|
}
|
|
7920
8083
|
function ensureFile(path, text) {
|
|
7921
|
-
if (
|
|
8084
|
+
if (existsSync7(path))
|
|
7922
8085
|
return { path, kind: "file", change: "unchanged" };
|
|
7923
8086
|
attempt2(path, "cannot be written", () => {
|
|
7924
8087
|
writeFileSync4(path, text, { encoding: "utf8", flag: "wx" });
|
|
@@ -7941,7 +8104,7 @@ function writeUserScope(home) {
|
|
|
7941
8104
|
import { writeFileSync as writeFileSync5 } from "fs";
|
|
7942
8105
|
|
|
7943
8106
|
// src/release/setting.ts
|
|
7944
|
-
import { readFileSync as
|
|
8107
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
7945
8108
|
var RELEASE_ENABLED_SETTING = "release.enabled";
|
|
7946
8109
|
function releaseEnabledIn(text, path) {
|
|
7947
8110
|
return parseConfigText(text, path).values.releaseEnabled ?? null;
|
|
@@ -7950,7 +8113,7 @@ function readReleaseSetting(root) {
|
|
|
7950
8113
|
const path = configFilePath(root);
|
|
7951
8114
|
let text;
|
|
7952
8115
|
try {
|
|
7953
|
-
text =
|
|
8116
|
+
text = readFileSync13(path, "utf8");
|
|
7954
8117
|
} catch (error) {
|
|
7955
8118
|
return { enabled: null, text: "", problem: `${path} could not be read: ${messageOf(error)}` };
|
|
7956
8119
|
}
|
|
@@ -8227,7 +8390,7 @@ function resolvedConfig2(root, home, warn) {
|
|
|
8227
8390
|
function checkGitignore(root, flags) {
|
|
8228
8391
|
const file = join21(root, GITIGNORE_FILE);
|
|
8229
8392
|
try {
|
|
8230
|
-
const text =
|
|
8393
|
+
const text = existsSync8(file) ? readFileSync14(file, "utf8") : null;
|
|
8231
8394
|
withTrackingBlock(text, flags, file);
|
|
8232
8395
|
} catch (error) {
|
|
8233
8396
|
const message = error instanceof GitignoreError ? error.message : `rafa init: ${file} cannot be read (${messageOf(error)})`;
|
|
@@ -8258,8 +8421,8 @@ function initialise(root, start2, home, context, entry) {
|
|
|
8258
8421
|
checkConflicts(root.path, home);
|
|
8259
8422
|
const config = resolvedConfig2(root.path, home, warn);
|
|
8260
8423
|
checkGitignore(root.path, config);
|
|
8261
|
-
const configExisted =
|
|
8262
|
-
const digestExisted =
|
|
8424
|
+
const configExisted = existsSync8(configFilePath(root.path));
|
|
8425
|
+
const digestExisted = existsSync8(join21(root.path, TRACKING_DIGEST_FILE));
|
|
8263
8426
|
const project = writeProjectScope(root.path);
|
|
8264
8427
|
const tracking = applyTracking(root.path, config, warn);
|
|
8265
8428
|
const writes = [...project, ...trackingWrites(tracking, digestExisted), ...writeUserScope(home)];
|
|
@@ -8558,72 +8721,196 @@ function createInstinctCheckCommand(seams = DEFAULT_CHECK_SEAMS) {
|
|
|
8558
8721
|
}
|
|
8559
8722
|
var check_default = createInstinctCheckCommand();
|
|
8560
8723
|
|
|
8561
|
-
// src/commands/instinct/
|
|
8562
|
-
|
|
8563
|
-
|
|
8564
|
-
function
|
|
8565
|
-
return
|
|
8566
|
-
}
|
|
8567
|
-
|
|
8568
|
-
|
|
8724
|
+
// src/commands/instinct/flag.ts
|
|
8725
|
+
var NAME = "rafa instinct flag";
|
|
8726
|
+
var USAGE6 = "rafa instinct flag <id> <reason>";
|
|
8727
|
+
function refusedFlagMessage(id, kind, reason) {
|
|
8728
|
+
return [
|
|
8729
|
+
`\u274C ${NAME}: the \`${kind}\` learning adapter did not flag "${id}": ${reason}`,
|
|
8730
|
+
"Run `rafa instinct list` to see the ids the project holds."
|
|
8731
|
+
].join(`
|
|
8732
|
+
`);
|
|
8569
8733
|
}
|
|
8570
|
-
function
|
|
8571
|
-
return {
|
|
8734
|
+
function blankReasonMessage(id) {
|
|
8735
|
+
return `\u274C ${NAME}: the reason to flag "${id}" is blank; say why the lesson is wrong.
|
|
8736
|
+
Usage: ${USAGE6}`;
|
|
8572
8737
|
}
|
|
8573
|
-
function
|
|
8574
|
-
let text;
|
|
8738
|
+
function makeAdapter(registry, kind, context) {
|
|
8575
8739
|
try {
|
|
8576
|
-
|
|
8740
|
+
return registry.resolve("learning", kind).create({
|
|
8741
|
+
repoRoot: context.repoRoot,
|
|
8742
|
+
home: context.home,
|
|
8743
|
+
learningBlessMinConfidence: context.minConfidence
|
|
8744
|
+
});
|
|
8577
8745
|
} catch (error) {
|
|
8578
|
-
|
|
8579
|
-
return { scope, path, id, instinct: null, issues: [unreadableIssue(message)] };
|
|
8746
|
+
throw new CommandExit(1, `\u274C ${NAME}: the \`${kind}\` learning adapter cannot be made: ${messageOf(error)}`);
|
|
8580
8747
|
}
|
|
8581
|
-
const parsed = parseInstinct(text);
|
|
8582
|
-
return { scope, path, id, instinct: parsed.instinct, issues: parsed.issues };
|
|
8583
8748
|
}
|
|
8584
|
-
function
|
|
8749
|
+
async function flagInstinct(context, seams) {
|
|
8750
|
+
const [id, reason] = expectTwoArguments(context.args, USAGE6);
|
|
8751
|
+
if (reason.trim() === "")
|
|
8752
|
+
throw new CommandExit(1, blankReasonMessage(id));
|
|
8753
|
+
const project = requireProject(context, NAME);
|
|
8754
|
+
const config = resolveProjectConfig(project, NAME, (message) => {
|
|
8755
|
+
context.output.warn(message);
|
|
8756
|
+
});
|
|
8757
|
+
const kind = config.learningAdapter;
|
|
8758
|
+
const adapter = makeAdapter(seams.registry ?? CORE_ADAPTER_REGISTRY, kind, {
|
|
8759
|
+
repoRoot: project.root,
|
|
8760
|
+
home: project.home,
|
|
8761
|
+
minConfidence: config.learningBlessMinConfidence
|
|
8762
|
+
});
|
|
8585
8763
|
try {
|
|
8586
|
-
|
|
8587
|
-
} catch {
|
|
8588
|
-
|
|
8764
|
+
await adapter.flag(id, reason);
|
|
8765
|
+
} catch (error) {
|
|
8766
|
+
throw new CommandExit(1, refusedFlagMessage(id, kind, messageOf(error)));
|
|
8589
8767
|
}
|
|
8768
|
+
return { id, reason, adapter: kind };
|
|
8590
8769
|
}
|
|
8591
|
-
function
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
|
|
8595
|
-
|
|
8596
|
-
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
|
|
8601
|
-
|
|
8770
|
+
function createInstinctFlagCommand(seams = {}) {
|
|
8771
|
+
const command = {
|
|
8772
|
+
name: "instinct flag",
|
|
8773
|
+
subject: "instinct",
|
|
8774
|
+
action: "flag",
|
|
8775
|
+
summary: "flag one held lesson as wrong, so no later bundle blesses it",
|
|
8776
|
+
description: "Flags one lesson the project holds as wrong, through the learning adapter" + " `learning.adapter` names in `.rafa/config.yaml`. A flagged id is left out of every bundle the" + " loop pulls afterwards, whatever a later merge writes under it; under the `local` adapter the" + " flag is one line appended to `.rafa/instincts/flags.ndjson` and no record file changes. An id" + " no held record carries is refused with exit code 1 in the adapter's words, a user-scope id" + " included, as is a blank reason. With `--output=json` the id, the reason and the adapter kind" + " are the data of the terminal result event.",
|
|
8777
|
+
args: [
|
|
8778
|
+
{
|
|
8779
|
+
name: "id",
|
|
8780
|
+
description: "The id of a lesson the project holds, as `rafa instinct list` prints it.",
|
|
8781
|
+
type: "string",
|
|
8782
|
+
required: true
|
|
8783
|
+
},
|
|
8784
|
+
{
|
|
8785
|
+
name: "reason",
|
|
8786
|
+
description: "Why the lesson is wrong, quoted as one word; stored beside the flag.",
|
|
8787
|
+
type: "string",
|
|
8788
|
+
required: true
|
|
8789
|
+
}
|
|
8790
|
+
],
|
|
8791
|
+
flags: [],
|
|
8792
|
+
examples: [
|
|
8793
|
+
{
|
|
8794
|
+
cmd: 'rafa instinct flag worktree-missing-node-modules "bun install runs on fork since 0.9"',
|
|
8795
|
+
note: "Keeps that lesson out of every bundle the loop pulls from now on."
|
|
8796
|
+
},
|
|
8797
|
+
{
|
|
8798
|
+
cmd: 'rafa instinct flag worktree-missing-node-modules "stale" --output=json',
|
|
8799
|
+
note: "Gives the id, the reason and the adapter kind as the data of the terminal result event."
|
|
8800
|
+
}
|
|
8801
|
+
],
|
|
8802
|
+
outputs: ["text", "json"],
|
|
8803
|
+
run: async (context) => {
|
|
8804
|
+
const result = await flagInstinct(context, seams);
|
|
8805
|
+
if (context.outputMode === "json") {
|
|
8806
|
+
context.output.result(result);
|
|
8807
|
+
return;
|
|
8808
|
+
}
|
|
8809
|
+
context.output.info(`\uD83D\uDEA9 Flagged ${result.id}: ${result.reason}`);
|
|
8810
|
+
context.output.info(` The \`${result.adapter}\` learning adapter leaves it out of every later bundle.`);
|
|
8811
|
+
}
|
|
8602
8812
|
};
|
|
8813
|
+
return Object.freeze(command);
|
|
8603
8814
|
}
|
|
8604
|
-
|
|
8605
|
-
|
|
8815
|
+
var flag_default = createInstinctFlagCommand();
|
|
8816
|
+
|
|
8817
|
+
// src/commands/instinct/promote.ts
|
|
8818
|
+
var NAME2 = "rafa instinct promote";
|
|
8819
|
+
var USAGE7 = "rafa instinct promote";
|
|
8820
|
+
var CONFIDENCE_DIGITS = 2;
|
|
8821
|
+
function refusedPullMessage(kind, reason, name2 = NAME2) {
|
|
8822
|
+
return `\u274C ${name2}: the \`${kind}\` learning adapter answered no blessed set: ${reason}`;
|
|
8606
8823
|
}
|
|
8607
|
-
function
|
|
8608
|
-
|
|
8824
|
+
function makeLearningAdapter(registry, kind, context, name2 = NAME2) {
|
|
8825
|
+
try {
|
|
8826
|
+
return registry.resolve("learning", kind).create({
|
|
8827
|
+
repoRoot: context.repoRoot,
|
|
8828
|
+
home: context.home,
|
|
8829
|
+
learningBlessMinConfidence: context.minConfidence
|
|
8830
|
+
});
|
|
8831
|
+
} catch (error) {
|
|
8832
|
+
throw new CommandExit(1, `\u274C ${name2}: the \`${kind}\` learning adapter cannot be made: ${messageOf(error)}`);
|
|
8833
|
+
}
|
|
8609
8834
|
}
|
|
8610
|
-
function
|
|
8611
|
-
|
|
8835
|
+
async function promotableLessons(context, seams) {
|
|
8836
|
+
expectNoArgument(context.args, USAGE7);
|
|
8837
|
+
const project = requireProject(context, NAME2);
|
|
8838
|
+
const config = resolveProjectConfig(project, NAME2, (message) => {
|
|
8839
|
+
context.output.warn(message);
|
|
8840
|
+
});
|
|
8841
|
+
const kind = config.learningAdapter;
|
|
8842
|
+
const adapter = makeLearningAdapter(seams.registry ?? CORE_ADAPTER_REGISTRY, kind, {
|
|
8843
|
+
repoRoot: project.root,
|
|
8844
|
+
home: project.home,
|
|
8845
|
+
minConfidence: config.learningBlessMinConfidence
|
|
8846
|
+
});
|
|
8847
|
+
const after = config.learningPromoteAfter;
|
|
8848
|
+
const minConfidence = config.learningPromoteMinConfidence;
|
|
8849
|
+
let blessed;
|
|
8850
|
+
try {
|
|
8851
|
+
blessed = (await adapter.pullBlessed()).instincts;
|
|
8852
|
+
} catch (error) {
|
|
8853
|
+
throw new CommandExit(1, refusedPullMessage(kind, messageOf(error)));
|
|
8854
|
+
}
|
|
8855
|
+
return { adapter: kind, after, minConfidence, lessons: promotable(blessed, { after, minConfidence }) };
|
|
8612
8856
|
}
|
|
8613
|
-
function
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8857
|
+
function renderPromotable(result) {
|
|
8858
|
+
const keys = `learning.promote.after ${String(result.after)},` + ` learning.promote.minConfidence ${result.minConfidence.toFixed(CONFIDENCE_DIGITS)}`;
|
|
8859
|
+
if (result.lessons.length === 0) {
|
|
8860
|
+
return [`No lesson the \`${result.adapter}\` learning adapter holds is promotable at ${keys}.`];
|
|
8861
|
+
}
|
|
8862
|
+
return [
|
|
8863
|
+
`${String(result.lessons.length)} lesson(s) to promote from the \`${result.adapter}\` learning adapter, at ${keys}:`,
|
|
8864
|
+
...result.lessons.flatMap((lesson) => [
|
|
8865
|
+
` ${lesson.id} ${lesson.confidence.toFixed(CONFIDENCE_DIGITS)} used ${String(lesson.usage_count)} ${lesson.trigger}`,
|
|
8866
|
+
` ${lesson.action}`
|
|
8867
|
+
])
|
|
8868
|
+
];
|
|
8617
8869
|
}
|
|
8870
|
+
function createInstinctPromoteCommand(seams = {}) {
|
|
8871
|
+
const command = {
|
|
8872
|
+
name: "instinct promote",
|
|
8873
|
+
subject: "instinct",
|
|
8874
|
+
action: "promote",
|
|
8875
|
+
summary: "print the lessons that recurred enough to promote, writing nothing",
|
|
8876
|
+
description: "Prints the lessons the project holds that recurred enough to promote: the blessed set" + " of the learning adapter `learning.adapter` names in `.rafa/config.yaml`, narrowed to the" + " lessons at least `learning.promote.after` distinct sources confirmed at a confidence of at least" + " `learning.promote.minConfidence`, most trusted first. It is the list the next wrap-up session" + " would be asked to promote. Nothing is written: no lesson is marked promoted and no file" + " changes. A kind no registry holds, an adapter that cannot be made or a pull it refuses is exit" + " code 1. With `--output=json` the adapter kind, the two keys' values and the lessons are the" + " data of the terminal result event.",
|
|
8877
|
+
args: [],
|
|
8878
|
+
flags: [],
|
|
8879
|
+
examples: [
|
|
8880
|
+
{
|
|
8881
|
+
cmd: "rafa instinct promote",
|
|
8882
|
+
note: "Lists each promotable lesson with its confidence, usage, trigger and action."
|
|
8883
|
+
},
|
|
8884
|
+
{
|
|
8885
|
+
cmd: "rafa instinct promote --output=json",
|
|
8886
|
+
note: "Gives the adapter kind, the two keys' values and the lessons as the data of the terminal result event."
|
|
8887
|
+
}
|
|
8888
|
+
],
|
|
8889
|
+
outputs: ["text", "json"],
|
|
8890
|
+
run: async (context) => {
|
|
8891
|
+
const result = await promotableLessons(context, seams);
|
|
8892
|
+
if (context.outputMode === "json") {
|
|
8893
|
+
context.output.result(result);
|
|
8894
|
+
return;
|
|
8895
|
+
}
|
|
8896
|
+
for (const line of renderPromotable(result))
|
|
8897
|
+
context.output.info(line);
|
|
8898
|
+
}
|
|
8899
|
+
};
|
|
8900
|
+
return Object.freeze(command);
|
|
8901
|
+
}
|
|
8902
|
+
var promote_default = createInstinctPromoteCommand();
|
|
8618
8903
|
|
|
8619
8904
|
// src/commands/instinct/list.ts
|
|
8620
|
-
var
|
|
8621
|
-
var
|
|
8905
|
+
var NAME3 = "rafa instinct list";
|
|
8906
|
+
var USAGE8 = "rafa instinct list [--blessed | --conflicts]";
|
|
8907
|
+
var CONFIDENCE_DIGITS2 = 2;
|
|
8908
|
+
var HUNDREDTHS = 100;
|
|
8622
8909
|
function instinctRowLine(entry) {
|
|
8623
8910
|
const record = entry.instinct;
|
|
8624
8911
|
if (record === null)
|
|
8625
8912
|
return ` \u274C ${entry.id} ${String(entry.issues.length)} issue(s)`;
|
|
8626
|
-
const confidence = record.confidence.toFixed(
|
|
8913
|
+
const confidence = record.confidence.toFixed(CONFIDENCE_DIGITS2);
|
|
8627
8914
|
return ` \u2705 ${entry.id} ${record.kind}/${record.domain} ${record.signal}` + ` ${confidence} ${record.trigger}`;
|
|
8628
8915
|
}
|
|
8629
8916
|
function scopeLines(listing) {
|
|
@@ -8643,38 +8930,171 @@ function renderInstinctList(result) {
|
|
|
8643
8930
|
`${String(result.total)} instinct(s): ${String(result.clean)} read as records,` + ` ${String(result.total - result.clean)} do not`
|
|
8644
8931
|
];
|
|
8645
8932
|
}
|
|
8646
|
-
function
|
|
8647
|
-
|
|
8648
|
-
|
|
8649
|
-
|
|
8933
|
+
function readViewFlag(name2, value) {
|
|
8934
|
+
if (value === undefined || value === false || value === "false")
|
|
8935
|
+
return false;
|
|
8936
|
+
if (value === true || value === "true")
|
|
8937
|
+
return true;
|
|
8938
|
+
throw new CommandExit(1, `\u274C --${name2} takes no value, and read "${value}" as one
|
|
8939
|
+
Usage: ${USAGE8}`);
|
|
8940
|
+
}
|
|
8941
|
+
function readListView(flags) {
|
|
8942
|
+
const blessed = readViewFlag("blessed", flags["blessed"]);
|
|
8943
|
+
const conflicts = readViewFlag("conflicts", flags["conflicts"]);
|
|
8944
|
+
if (blessed && conflicts) {
|
|
8945
|
+
throw new CommandExit(1, `\u274C --blessed and --conflicts are two views; give one
|
|
8946
|
+
Usage: ${USAGE8}`);
|
|
8947
|
+
}
|
|
8948
|
+
if (blessed)
|
|
8949
|
+
return "blessed";
|
|
8950
|
+
return conflicts ? "conflicts" : "all";
|
|
8951
|
+
}
|
|
8952
|
+
function oneLine(text) {
|
|
8953
|
+
return text.trim().replace(/\s+/gu, " ");
|
|
8954
|
+
}
|
|
8955
|
+
function byConfidence(left, right) {
|
|
8956
|
+
const gap = Math.round(right.confidence * HUNDREDTHS) - Math.round(left.confidence * HUNDREDTHS);
|
|
8957
|
+
if (gap !== 0)
|
|
8958
|
+
return gap;
|
|
8959
|
+
if (left.id < right.id)
|
|
8960
|
+
return -1;
|
|
8961
|
+
return left.id > right.id ? 1 : 0;
|
|
8962
|
+
}
|
|
8963
|
+
function scopeConflicts(listing) {
|
|
8964
|
+
const clean = listing.records.map((entry) => entry.instinct).filter((instinct) => instinct !== null);
|
|
8965
|
+
const groups = new Map;
|
|
8966
|
+
for (const record of toHeldRecords(clean)) {
|
|
8967
|
+
if (record.status !== "flagged")
|
|
8968
|
+
continue;
|
|
8969
|
+
const key = triggerKey(record.trigger);
|
|
8970
|
+
groups.set(key, [...groups.get(key) ?? [], record]);
|
|
8971
|
+
}
|
|
8972
|
+
return [...groups.values()].map((records) => ({
|
|
8973
|
+
scope: listing.scope,
|
|
8974
|
+
trigger: records[0].trigger,
|
|
8975
|
+
records: [...records].sort(byConfidence)
|
|
8976
|
+
}));
|
|
8977
|
+
}
|
|
8978
|
+
function lessonLine(indent, record, width, tail) {
|
|
8979
|
+
return `${indent}${record.id.padEnd(width)} ${record.confidence.toFixed(CONFIDENCE_DIGITS2)}` + ` used ${String(record.usage_count)} ${tail}`;
|
|
8980
|
+
}
|
|
8981
|
+
function conflictLines(conflict) {
|
|
8982
|
+
const width = Math.max(...conflict.records.map((record) => record.id.length));
|
|
8983
|
+
return [
|
|
8984
|
+
` ${conflict.scope} ${conflict.trigger}`,
|
|
8985
|
+
...conflict.records.map((record) => lessonLine(" ", record, width, oneLine(record.action)))
|
|
8986
|
+
];
|
|
8987
|
+
}
|
|
8988
|
+
function renderConflicts(result) {
|
|
8989
|
+
if (result.conflicts.length === 0) {
|
|
8990
|
+
return [`No trigger either instinct scope holds carries more than one action (project: ${result.projectRoot}).`];
|
|
8991
|
+
}
|
|
8992
|
+
return [
|
|
8993
|
+
`Triggers held with more than one action (project: ${result.projectRoot}):`,
|
|
8994
|
+
...result.conflicts.flatMap((conflict) => conflictLines(conflict)),
|
|
8995
|
+
`${String(result.conflicts.length)} flagged trigger(s): no bundle blesses any of their actions`
|
|
8996
|
+
];
|
|
8997
|
+
}
|
|
8998
|
+
function renderBlessed(result) {
|
|
8999
|
+
const floor = `learning.bless.minConfidence ${result.minConfidence.toFixed(CONFIDENCE_DIGITS2)}`;
|
|
9000
|
+
if (result.lessons.length === 0) {
|
|
9001
|
+
return [`No lesson the \`${result.adapter}\` learning adapter holds is blessed at ${floor}.`];
|
|
9002
|
+
}
|
|
9003
|
+
return [
|
|
9004
|
+
`${String(result.lessons.length)} lesson(s) blessed by the \`${result.adapter}\` learning adapter, at ${floor}:`,
|
|
9005
|
+
...result.lessons.flatMap((lesson) => [
|
|
9006
|
+
lessonLine(" ", lesson, 0, lesson.trigger),
|
|
9007
|
+
` ${lesson.action}`
|
|
9008
|
+
])
|
|
9009
|
+
];
|
|
9010
|
+
}
|
|
9011
|
+
async function blessedLessons(context, seams) {
|
|
9012
|
+
const project = instinctProject(context, NAME3);
|
|
9013
|
+
const config = resolveProjectConfig(project, NAME3, (message) => {
|
|
9014
|
+
context.output.warn(message);
|
|
9015
|
+
});
|
|
9016
|
+
const kind = config.learningAdapter;
|
|
9017
|
+
const minConfidence = config.learningBlessMinConfidence;
|
|
9018
|
+
const adapter = makeLearningAdapter(seams.registry ?? CORE_ADAPTER_REGISTRY, kind, { repoRoot: project.root, home: project.home, minConfidence }, NAME3);
|
|
9019
|
+
try {
|
|
9020
|
+
return { adapter: kind, minConfidence, lessons: (await adapter.pullBlessed()).instincts };
|
|
9021
|
+
} catch (error) {
|
|
9022
|
+
throw new CommandExit(1, refusedPullMessage(kind, messageOf(error), NAME3));
|
|
9023
|
+
}
|
|
9024
|
+
}
|
|
9025
|
+
function conflicts(context) {
|
|
9026
|
+
const project = instinctProject(context, NAME3);
|
|
9027
|
+
const scopes = readScopes({ home: project.home, projectRoot: project.root });
|
|
9028
|
+
return { projectRoot: project.root, conflicts: scopes.flatMap((listing) => scopeConflicts(listing)) };
|
|
9029
|
+
}
|
|
9030
|
+
function everyRecord(context) {
|
|
9031
|
+
const project = instinctProject(context, NAME3);
|
|
9032
|
+
const scopes = readScopes({ home: project.home, projectRoot: project.root });
|
|
8650
9033
|
const records = allRecords(scopes);
|
|
8651
|
-
|
|
9034
|
+
return {
|
|
8652
9035
|
projectRoot: project.root,
|
|
8653
9036
|
scopes,
|
|
8654
9037
|
total: records.length,
|
|
8655
9038
|
clean: records.filter((entry) => entry.instinct !== null).length
|
|
8656
9039
|
};
|
|
9040
|
+
}
|
|
9041
|
+
function answer(context, result, lines2) {
|
|
8657
9042
|
if (context.outputMode === "json") {
|
|
8658
9043
|
context.output.result(result);
|
|
8659
9044
|
return;
|
|
8660
9045
|
}
|
|
8661
|
-
for (const line of
|
|
9046
|
+
for (const line of lines2())
|
|
8662
9047
|
context.output.info(line);
|
|
8663
9048
|
}
|
|
8664
|
-
function
|
|
9049
|
+
async function runList4(context, seams) {
|
|
9050
|
+
expectNoArgument(context.args, USAGE8);
|
|
9051
|
+
const view = readListView(context.flags);
|
|
9052
|
+
if (view === "blessed") {
|
|
9053
|
+
const result2 = await blessedLessons(context, seams);
|
|
9054
|
+
answer(context, result2, () => renderBlessed(result2));
|
|
9055
|
+
return;
|
|
9056
|
+
}
|
|
9057
|
+
if (view === "conflicts") {
|
|
9058
|
+
const result2 = conflicts(context);
|
|
9059
|
+
answer(context, result2, () => renderConflicts(result2));
|
|
9060
|
+
return;
|
|
9061
|
+
}
|
|
9062
|
+
const result = everyRecord(context);
|
|
9063
|
+
answer(context, result, () => renderInstinctList(result));
|
|
9064
|
+
}
|
|
9065
|
+
function createInstinctListCommand(seams = {}) {
|
|
8665
9066
|
const command = {
|
|
8666
9067
|
name: "instinct list",
|
|
8667
9068
|
subject: "instinct",
|
|
8668
9069
|
action: "list",
|
|
8669
9070
|
summary: "list the records the project and user instinct scopes hold",
|
|
8670
|
-
description: "Lists every instinct record the two scopes hold \u2014 this project's `.rafa/instincts` and" + " `~/.rafa/instincts` \u2014 with, for each, the id its file name gives it, its `kind/domain` pair, its" + " `signal`, its `confidence` and its `trigger`. A record that breaks a rule of the instinct schema" + " shows the number of rules it broke instead, which `rafa instinct check` then names. The local" + " Learning adapter's `instincts.ndjson` and `flags.ndjson` are its store and no records, and are" + " passed over. A scope whose directory is not there says so. The exit code is 0 whatever the rows" + " say: this command reports and `rafa instinct check` gates. With `--output=json` the scopes and" + "
|
|
9071
|
+
description: "Lists every instinct record the two scopes hold \u2014 this project's `.rafa/instincts` and" + " `~/.rafa/instincts` \u2014 with, for each, the id its file name gives it, its `kind/domain` pair, its" + " `signal`, its `confidence` and its `trigger`. A record that breaks a rule of the instinct schema" + " shows the number of rules it broke instead, which `rafa instinct check` then names. The local" + " Learning adapter's `instincts.ndjson` and `flags.ndjson` are its store and no records, and are" + " passed over. A scope whose directory is not there says so. The exit code is 0 whatever the rows" + " say: this command reports and `rafa instinct check` gates. `--blessed` lists instead the lessons" + " the learning adapter `learning.adapter` names would hand a task, at `learning.bless.minConfidence`," + " most trusted first, writing nothing; an adapter that cannot be made or a pull it refuses is exit" + " code 1. `--conflicts` lists instead each trigger a scope holds with more than one action, which a" + " merge flagged and no bundle blesses, its actions side by side with their confidence and usage." + " The two views are one at a time. With `--output=json` the scopes and their records, or the view" + " asked for, are the data of the terminal result event.",
|
|
8671
9072
|
args: [],
|
|
8672
|
-
flags: [
|
|
9073
|
+
flags: [
|
|
9074
|
+
{
|
|
9075
|
+
name: "blessed",
|
|
9076
|
+
description: "List the lessons the configured learning adapter blesses, each with its confidence, usage," + " trigger and action, instead of every record. Takes no value.",
|
|
9077
|
+
type: "boolean"
|
|
9078
|
+
},
|
|
9079
|
+
{
|
|
9080
|
+
name: "conflicts",
|
|
9081
|
+
description: "List each trigger held with more than one action, its actions side by side with their" + " confidence and usage, instead of every record. Takes no value.",
|
|
9082
|
+
type: "boolean"
|
|
9083
|
+
}
|
|
9084
|
+
],
|
|
8673
9085
|
examples: [
|
|
8674
9086
|
{
|
|
8675
9087
|
cmd: "rafa instinct list",
|
|
8676
9088
|
note: "Lists both scopes, each record with what it is about."
|
|
8677
9089
|
},
|
|
9090
|
+
{
|
|
9091
|
+
cmd: "rafa instinct list --blessed",
|
|
9092
|
+
note: "Lists the lessons a task would be handed, most trusted first."
|
|
9093
|
+
},
|
|
9094
|
+
{
|
|
9095
|
+
cmd: "rafa instinct list --conflicts",
|
|
9096
|
+
note: "Lists each flagged trigger with its actions side by side."
|
|
9097
|
+
},
|
|
8678
9098
|
{
|
|
8679
9099
|
cmd: "rafa instinct list --output=json",
|
|
8680
9100
|
note: "Gives the scopes and every record parsed as the data of the terminal result event."
|
|
@@ -8682,7 +9102,7 @@ function createInstinctListCommand() {
|
|
|
8682
9102
|
],
|
|
8683
9103
|
outputs: ["text", "json"],
|
|
8684
9104
|
run: async (context) => {
|
|
8685
|
-
runList4(context);
|
|
9105
|
+
await runList4(context, seams);
|
|
8686
9106
|
}
|
|
8687
9107
|
};
|
|
8688
9108
|
return Object.freeze(command);
|
|
@@ -8690,7 +9110,7 @@ function createInstinctListCommand() {
|
|
|
8690
9110
|
var list_default4 = createInstinctListCommand();
|
|
8691
9111
|
|
|
8692
9112
|
// src/commands/instinct/show.ts
|
|
8693
|
-
var
|
|
9113
|
+
var USAGE9 = "rafa instinct show <id>";
|
|
8694
9114
|
var FIELD_ORDER = [
|
|
8695
9115
|
"trigger",
|
|
8696
9116
|
"kind",
|
|
@@ -8762,13 +9182,13 @@ function unreadableMessage(entry) {
|
|
|
8762
9182
|
}
|
|
8763
9183
|
function unknownIdMessage(id, scopes) {
|
|
8764
9184
|
const looked = scopes.map((listing) => ` ${listing.scope} ${listing.dir}${listing.exists ? "" : " (no such directory)"}`);
|
|
8765
|
-
return [`\u274C No instinct is filed under "${id}". Looked in:`, ...looked, `Usage: ${
|
|
9185
|
+
return [`\u274C No instinct is filed under "${id}". Looked in:`, ...looked, `Usage: ${USAGE9}`].join(`
|
|
8766
9186
|
`);
|
|
8767
9187
|
}
|
|
8768
9188
|
function runShow2(context) {
|
|
8769
|
-
const id = expectOneArgument(context.args,
|
|
9189
|
+
const id = expectOneArgument(context.args, USAGE9);
|
|
8770
9190
|
const project = instinctProject(context, "rafa instinct show");
|
|
8771
|
-
const scopes =
|
|
9191
|
+
const scopes = readScopes({ home: project.home, projectRoot: project.root });
|
|
8772
9192
|
const found = findRecords(scopes, id);
|
|
8773
9193
|
if (found.length === 0)
|
|
8774
9194
|
throw new CommandExit(1, unknownIdMessage(id, scopes));
|
|
@@ -8825,8 +9245,8 @@ function createInstinctShowCommand() {
|
|
|
8825
9245
|
var show_default2 = createInstinctShowCommand();
|
|
8826
9246
|
|
|
8827
9247
|
// src/commands/issue/check.ts
|
|
8828
|
-
import { readdirSync as
|
|
8829
|
-
import { join as
|
|
9248
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync15, statSync as statSync11 } from "fs";
|
|
9249
|
+
import { join as join22, resolve as resolve10 } from "path";
|
|
8830
9250
|
var CHECK_USAGE = "rafa issue check <n> [--stamp]";
|
|
8831
9251
|
var STAMP_FLAG = "stamp";
|
|
8832
9252
|
var ISSUE_NUMBER = /^[1-9]\d*$/u;
|
|
@@ -8850,7 +9270,7 @@ function missingCopyMessage(issue, specsDir) {
|
|
|
8850
9270
|
function copyNames(dir, issue) {
|
|
8851
9271
|
let names;
|
|
8852
9272
|
try {
|
|
8853
|
-
names =
|
|
9273
|
+
names = readdirSync3(dir);
|
|
8854
9274
|
} catch (error) {
|
|
8855
9275
|
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
8856
9276
|
return [];
|
|
@@ -8858,7 +9278,7 @@ function copyNames(dir, issue) {
|
|
|
8858
9278
|
}
|
|
8859
9279
|
const prefix = `${boardId(issue)}-`;
|
|
8860
9280
|
const notes = notesFileName(issue);
|
|
8861
|
-
return names.filter((name2) => name2.startsWith(prefix) && name2.endsWith(SPEC_EXTENSION) && name2 !== notes).filter((name2) => name2.length > prefix.length + SPEC_EXTENSION.length).filter((name2) =>
|
|
9281
|
+
return names.filter((name2) => name2.startsWith(prefix) && name2.endsWith(SPEC_EXTENSION) && name2 !== notes).filter((name2) => name2.length > prefix.length + SPEC_EXTENSION.length).filter((name2) => statSync11(join22(dir, name2)).isFile()).sort();
|
|
8862
9282
|
}
|
|
8863
9283
|
function findSavedCopy(root, specsDir, issue) {
|
|
8864
9284
|
const names = copyNames(resolve10(root, specsDir), issue);
|
|
@@ -8868,11 +9288,11 @@ function findSavedCopy(root, specsDir, issue) {
|
|
|
8868
9288
|
if (names.length > 1) {
|
|
8869
9289
|
throw new CommandExit(1, [
|
|
8870
9290
|
`\u274C Issue #${String(issue)} has ${String(names.length)} saved copies under ${specsDir}; remove the stale one:`,
|
|
8871
|
-
...names.map((name2) => ` ${
|
|
9291
|
+
...names.map((name2) => ` ${join22(specsDir, name2)}`)
|
|
8872
9292
|
].join(`
|
|
8873
9293
|
`));
|
|
8874
9294
|
}
|
|
8875
|
-
const path =
|
|
9295
|
+
const path = join22(specsDir, only);
|
|
8876
9296
|
return { path, absolute: resolve10(root, path) };
|
|
8877
9297
|
}
|
|
8878
9298
|
function liveText(live) {
|
|
@@ -8902,7 +9322,7 @@ function checkedRefLine(ref) {
|
|
|
8902
9322
|
}
|
|
8903
9323
|
function countLine(references) {
|
|
8904
9324
|
const noun = references.length === 1 ? "reference" : "references";
|
|
8905
|
-
const counts = STATE_ORDER.map((state) => [state, references.filter((ref) => ref.state === state).length]).filter(([,
|
|
9325
|
+
const counts = STATE_ORDER.map((state) => [state, references.filter((ref) => ref.state === state).length]).filter(([, count2]) => count2 > 0).map(([state, count2]) => `${String(count2)} ${state}`);
|
|
8906
9326
|
return counts.length === 0 ? "No references." : `${String(references.length)} ${noun}: ${counts.join(", ")}`;
|
|
8907
9327
|
}
|
|
8908
9328
|
function renderCheck2(result) {
|
|
@@ -8921,7 +9341,7 @@ async function readReferences(absolute, issue, verify, stamp) {
|
|
|
8921
9341
|
return rows.map((row2) => checkedRef(row2, row2.stamp));
|
|
8922
9342
|
}
|
|
8923
9343
|
const once = memoiseVerifier(verify);
|
|
8924
|
-
const before = await readRefsText({ copy:
|
|
9344
|
+
const before = await readRefsText({ copy: readFileSync15(absolute, "utf8"), issue, verify: once });
|
|
8925
9345
|
const after = await restampCopyRefs({ path: absolute, issue, verify: once });
|
|
8926
9346
|
return before.rows.map((row2, index) => checkedRef(row2, after.rows[index]?.stamp ?? null));
|
|
8927
9347
|
}
|
|
@@ -8996,10 +9416,10 @@ function createIssueCheckCommand(seams = {}) {
|
|
|
8996
9416
|
var check_default2 = createIssueCheckCommand();
|
|
8997
9417
|
|
|
8998
9418
|
// src/commands/issue/comment.ts
|
|
8999
|
-
var
|
|
9419
|
+
var USAGE10 = "rafa issue comment <id> --body=<text>";
|
|
9000
9420
|
async function commentOnIssue(context, seams) {
|
|
9001
|
-
const id = expectOneArgument(context.args,
|
|
9002
|
-
const body = readRequiredFlag(context.flags, "body",
|
|
9421
|
+
const id = expectOneArgument(context.args, USAGE10);
|
|
9422
|
+
const body = readRequiredFlag(context.flags, "body", USAGE10);
|
|
9003
9423
|
const { tracker, data } = await resolveIssueTracker(context, seams);
|
|
9004
9424
|
const ref = issueRef(tracker, id);
|
|
9005
9425
|
await onTracker(tracker, `comment on issue ${id}`, () => tracker.comment(ref, body));
|
|
@@ -9053,16 +9473,16 @@ function createIssueCommentCommand(seams = DEFAULT_ISSUE_SEAMS) {
|
|
|
9053
9473
|
var comment_default = createIssueCommentCommand();
|
|
9054
9474
|
|
|
9055
9475
|
// src/commands/issue/create.ts
|
|
9056
|
-
var
|
|
9476
|
+
var USAGE11 = "rafa issue create --title=<text> [--body=<text>] [--type=<type>] [--module=<name>] [--priority=<priority>]";
|
|
9057
9477
|
var DEFAULT_ISSUE_TYPE = "code";
|
|
9058
9478
|
function readIssueDraft(flags) {
|
|
9059
9479
|
return {
|
|
9060
9480
|
opt: 0,
|
|
9061
|
-
title: readRequiredFlag(flags, "title",
|
|
9062
|
-
body: readTextFlag(flags, "body",
|
|
9063
|
-
type: readChoiceFlag(flags, "type", ISSUE_TYPES,
|
|
9064
|
-
module: readNonBlankFlag(flags, "module",
|
|
9065
|
-
priority: readChoiceFlag(flags, "priority", ISSUE_PRIORITIES,
|
|
9481
|
+
title: readRequiredFlag(flags, "title", USAGE11),
|
|
9482
|
+
body: readTextFlag(flags, "body", USAGE11) ?? "",
|
|
9483
|
+
type: readChoiceFlag(flags, "type", ISSUE_TYPES, USAGE11) ?? DEFAULT_ISSUE_TYPE,
|
|
9484
|
+
module: readNonBlankFlag(flags, "module", USAGE11) ?? TRIAGE_MODULE,
|
|
9485
|
+
priority: readChoiceFlag(flags, "priority", ISSUE_PRIORITIES, USAGE11) ?? null,
|
|
9066
9486
|
project: null,
|
|
9067
9487
|
blockedBy: []
|
|
9068
9488
|
};
|
|
@@ -9071,7 +9491,7 @@ function renderCreated(ref) {
|
|
|
9071
9491
|
return [`Created ${issueName(ref)}.`, ...urlLines(ref)];
|
|
9072
9492
|
}
|
|
9073
9493
|
async function createIssue(context, seams) {
|
|
9074
|
-
expectNoArgument(context.args,
|
|
9494
|
+
expectNoArgument(context.args, USAGE11);
|
|
9075
9495
|
const draft = readIssueDraft(context.flags);
|
|
9076
9496
|
const { tracker, data } = await resolveIssueTracker(context, seams);
|
|
9077
9497
|
const ref = await onTracker(tracker, "create the issue", () => tracker.create(draft));
|
|
@@ -9223,7 +9643,7 @@ function createGhBoardListing(options) {
|
|
|
9223
9643
|
}
|
|
9224
9644
|
|
|
9225
9645
|
// src/board/roadmap-rows.ts
|
|
9226
|
-
import { readdirSync as
|
|
9646
|
+
import { readdirSync as readdirSync4 } from "fs";
|
|
9227
9647
|
var OUTLINE_HEADINGS = 3;
|
|
9228
9648
|
var UNKNOWN_STATE = "unknown";
|
|
9229
9649
|
function isMissing(error) {
|
|
@@ -9232,7 +9652,7 @@ function isMissing(error) {
|
|
|
9232
9652
|
function createPlanDirNames(dir) {
|
|
9233
9653
|
return () => {
|
|
9234
9654
|
try {
|
|
9235
|
-
return Object.freeze(
|
|
9655
|
+
return Object.freeze(readdirSync4(dir));
|
|
9236
9656
|
} catch (error) {
|
|
9237
9657
|
if (isMissing(error))
|
|
9238
9658
|
return Object.freeze([]);
|
|
@@ -9463,24 +9883,24 @@ function renderRoadmapTable(rows, width) {
|
|
|
9463
9883
|
}
|
|
9464
9884
|
|
|
9465
9885
|
// src/commands/issue/list.ts
|
|
9466
|
-
var
|
|
9467
|
-
var SWITCH_HINT = `Type it bare: ${
|
|
9886
|
+
var USAGE12 = "rafa issue list [--roadmap [--all]] [--state=<state>] [--type=<type>] [--module=<name>]" + " [--search=<text>] [--limit=<n>]";
|
|
9887
|
+
var SWITCH_HINT = `Type it bare: ${USAGE12}`;
|
|
9468
9888
|
var LIMIT = /^[1-9]\d*$/;
|
|
9469
9889
|
function readLimit(flags) {
|
|
9470
|
-
const value = readTextFlag(flags, "limit",
|
|
9890
|
+
const value = readTextFlag(flags, "limit", USAGE12);
|
|
9471
9891
|
if (value === undefined)
|
|
9472
9892
|
return;
|
|
9473
9893
|
const limit = Number(value);
|
|
9474
9894
|
if (LIMIT.test(value) && Number.isSafeInteger(limit))
|
|
9475
9895
|
return limit;
|
|
9476
|
-
throw lineRefusal(`--limit is "${value}", expected a positive whole number`,
|
|
9896
|
+
throw lineRefusal(`--limit is "${value}", expected a positive whole number`, USAGE12);
|
|
9477
9897
|
}
|
|
9478
9898
|
function readIssueQuery(flags) {
|
|
9479
9899
|
const narrowed = {
|
|
9480
|
-
state: readChoiceFlag(flags, "state", ISSUE_STATES,
|
|
9481
|
-
type: readChoiceFlag(flags, "type", ISSUE_TYPES,
|
|
9482
|
-
module: readNonBlankFlag(flags, "module",
|
|
9483
|
-
text: readNonBlankFlag(flags, "search",
|
|
9900
|
+
state: readChoiceFlag(flags, "state", ISSUE_STATES, USAGE12),
|
|
9901
|
+
type: readChoiceFlag(flags, "type", ISSUE_TYPES, USAGE12),
|
|
9902
|
+
module: readNonBlankFlag(flags, "module", USAGE12),
|
|
9903
|
+
text: readNonBlankFlag(flags, "search", USAGE12),
|
|
9484
9904
|
limit: readLimit(flags)
|
|
9485
9905
|
};
|
|
9486
9906
|
return Object.fromEntries(Object.entries(narrowed).filter(([, value]) => value !== undefined));
|
|
@@ -9489,10 +9909,10 @@ function readIssueListLine(flags) {
|
|
|
9489
9909
|
const roadmap = readSwitch("roadmap", flags["roadmap"], SWITCH_HINT);
|
|
9490
9910
|
const all = readSwitch("all", flags["all"], SWITCH_HINT);
|
|
9491
9911
|
if (roadmap && flags["state"] !== undefined) {
|
|
9492
|
-
throw lineRefusal("--state cannot narrow --roadmap: the Roadmap is read off the GitHub board, which holds an issue open or" + " closed and so no state of the tracker",
|
|
9912
|
+
throw lineRefusal("--state cannot narrow --roadmap: the Roadmap is read off the GitHub board, which holds an issue open or" + " closed and so no state of the tracker", USAGE12);
|
|
9493
9913
|
}
|
|
9494
9914
|
if (all && !roadmap)
|
|
9495
|
-
throw lineRefusal("--all keeps the ticked Roadmap lines, so it needs --roadmap",
|
|
9915
|
+
throw lineRefusal("--all keeps the ticked Roadmap lines, so it needs --roadmap", USAGE12);
|
|
9496
9916
|
return { roadmap, all };
|
|
9497
9917
|
}
|
|
9498
9918
|
function renderIssueList(kind, issues) {
|
|
@@ -9571,7 +9991,7 @@ async function listRoadmap(context, seams, all, filter) {
|
|
|
9571
9991
|
});
|
|
9572
9992
|
}
|
|
9573
9993
|
async function listIssues(context, seams) {
|
|
9574
|
-
expectNoArgument(context.args,
|
|
9994
|
+
expectNoArgument(context.args, USAGE12);
|
|
9575
9995
|
const query = readIssueQuery(context.flags);
|
|
9576
9996
|
const { tracker, data } = await resolveIssueTracker(context, seams);
|
|
9577
9997
|
const refs = await onTracker(tracker, "list issues", () => tracker.find(query));
|
|
@@ -9590,7 +10010,7 @@ async function runIssueList(context, seams) {
|
|
|
9590
10010
|
context.output.info(text);
|
|
9591
10011
|
return;
|
|
9592
10012
|
}
|
|
9593
|
-
expectNoArgument(context.args,
|
|
10013
|
+
expectNoArgument(context.args, USAGE12);
|
|
9594
10014
|
const filter = readIssueQuery(context.flags);
|
|
9595
10015
|
const listed2 = await listRoadmap(context, seams, line.all, filter);
|
|
9596
10016
|
if (context.outputMode === "json") {
|
|
@@ -9677,10 +10097,10 @@ function createIssueListCommand(seams = DEFAULT_ISSUE_SEAMS) {
|
|
|
9677
10097
|
var list_default5 = createIssueListCommand();
|
|
9678
10098
|
|
|
9679
10099
|
// src/commands/issue/move.ts
|
|
9680
|
-
var
|
|
10100
|
+
var USAGE13 = "rafa issue move <id> <state>";
|
|
9681
10101
|
async function moveIssue(context, seams) {
|
|
9682
|
-
const [id, word] = expectTwoArguments(context.args,
|
|
9683
|
-
const state = readChoice(word, "The state", ISSUE_STATES,
|
|
10102
|
+
const [id, word] = expectTwoArguments(context.args, USAGE13);
|
|
10103
|
+
const state = readChoice(word, "The state", ISSUE_STATES, USAGE13);
|
|
9684
10104
|
const { tracker, data } = await resolveIssueTracker(context, seams);
|
|
9685
10105
|
const ref = issueRef(tracker, id);
|
|
9686
10106
|
const moved = await onTracker(tracker, `move issue ${id} to ${state}`, () => tracker.transition(ref, state));
|
|
@@ -9739,7 +10159,7 @@ function createIssueMoveCommand(seams = DEFAULT_ISSUE_SEAMS) {
|
|
|
9739
10159
|
var move_default = createIssueMoveCommand();
|
|
9740
10160
|
|
|
9741
10161
|
// src/commands/issue/show.ts
|
|
9742
|
-
var
|
|
10162
|
+
var USAGE14 = "rafa issue show <id>";
|
|
9743
10163
|
var NO_PRIORITY = "needs-triage";
|
|
9744
10164
|
function renderIssue(kind, issue) {
|
|
9745
10165
|
const project = issue.project === null ? [] : [`Project: ${issue.project}`];
|
|
@@ -9761,7 +10181,7 @@ function renderIssue(kind, issue) {
|
|
|
9761
10181
|
];
|
|
9762
10182
|
}
|
|
9763
10183
|
async function showIssue(context, seams) {
|
|
9764
|
-
const id = expectOneArgument(context.args,
|
|
10184
|
+
const id = expectOneArgument(context.args, USAGE14);
|
|
9765
10185
|
const { tracker, data } = await resolveIssueTracker(context, seams);
|
|
9766
10186
|
const issue = await onTracker(tracker, `read issue ${id}`, () => tracker.get(issueRef(tracker, id)));
|
|
9767
10187
|
return { tracker: data, issue };
|
|
@@ -10050,8 +10470,8 @@ function projectRoot2(context) {
|
|
|
10050
10470
|
throw new Error("rafa issue unblock runs inside a project, and was handed none");
|
|
10051
10471
|
return context.project.root;
|
|
10052
10472
|
}
|
|
10053
|
-
function isYes(
|
|
10054
|
-
return
|
|
10473
|
+
function isYes(answer2) {
|
|
10474
|
+
return answer2 !== null && YES_ANSWERS2.includes(answer2.trim().toLowerCase());
|
|
10055
10475
|
}
|
|
10056
10476
|
function lazyPrompter2(open) {
|
|
10057
10477
|
let prompter = null;
|
|
@@ -10151,7 +10571,7 @@ function createIssueUnblockCommand(seams = DEFAULT_UNBLOCK_SEAMS) {
|
|
|
10151
10571
|
var unblock_default = createIssueUnblockCommand();
|
|
10152
10572
|
|
|
10153
10573
|
// src/commands/loop/list.ts
|
|
10154
|
-
var
|
|
10574
|
+
var USAGE15 = "rafa loop list";
|
|
10155
10575
|
function renderSessionList(list) {
|
|
10156
10576
|
if (list.sessions.length === 0)
|
|
10157
10577
|
return ["No running sessions."];
|
|
@@ -10161,7 +10581,7 @@ function renderSessionList(list) {
|
|
|
10161
10581
|
];
|
|
10162
10582
|
}
|
|
10163
10583
|
async function runList5(context, seams) {
|
|
10164
|
-
expectNoArgument(context.args,
|
|
10584
|
+
expectNoArgument(context.args, USAGE15);
|
|
10165
10585
|
const root = projectRoot(context);
|
|
10166
10586
|
const sessions = readRecords(root, seams).filter(isLive).map((session) => {
|
|
10167
10587
|
const checklist = readSessionChecklist(root, session);
|
|
@@ -10206,7 +10626,7 @@ function createLoopListCommand(seams = {}) {
|
|
|
10206
10626
|
var list_default6 = createLoopListCommand();
|
|
10207
10627
|
|
|
10208
10628
|
// src/commands/loop/pause.ts
|
|
10209
|
-
var
|
|
10629
|
+
var USAGE16 = "rafa loop pause [-s|--session-id=<id>]";
|
|
10210
10630
|
function renderPause(result) {
|
|
10211
10631
|
const { session } = result;
|
|
10212
10632
|
if (!result.changed)
|
|
@@ -10215,8 +10635,8 @@ function renderPause(result) {
|
|
|
10215
10635
|
return [when, " Nothing is marked. `rafa loop resume` goes on, and `rafa loop stop` ends the run."];
|
|
10216
10636
|
}
|
|
10217
10637
|
async function runPause(context, seams) {
|
|
10218
|
-
expectNoArgument(context.args,
|
|
10219
|
-
const { root, record } = pickSession(context,
|
|
10638
|
+
expectNoArgument(context.args, USAGE16);
|
|
10639
|
+
const { root, record } = pickSession(context, USAGE16, "live", seams);
|
|
10220
10640
|
if (!isLive(record)) {
|
|
10221
10641
|
throw refusal(`Session ${record.sessionId} reads ${record.state}: no run is left to pause.`);
|
|
10222
10642
|
}
|
|
@@ -10256,7 +10676,7 @@ function createLoopPauseCommand(seams = {}) {
|
|
|
10256
10676
|
var pause_default = createLoopPauseCommand();
|
|
10257
10677
|
|
|
10258
10678
|
// src/commands/loop/resume.ts
|
|
10259
|
-
var
|
|
10679
|
+
var USAGE17 = "rafa loop resume [-s|--session-id=<id>]";
|
|
10260
10680
|
function renderResume(result) {
|
|
10261
10681
|
const { session } = result;
|
|
10262
10682
|
if (!result.changed)
|
|
@@ -10264,8 +10684,8 @@ function renderResume(result) {
|
|
|
10264
10684
|
return [session.task === null ? `Resumed session ${session.sessionId}: it goes on with its next task.` : `Resumed session ${session.sessionId}: it goes on past its running task, line ${session.task.line}, ${session.task.text}`];
|
|
10265
10685
|
}
|
|
10266
10686
|
async function runResume(context, seams) {
|
|
10267
|
-
expectNoArgument(context.args,
|
|
10268
|
-
const { root, record } = pickSession(context,
|
|
10687
|
+
expectNoArgument(context.args, USAGE17);
|
|
10688
|
+
const { root, record } = pickSession(context, USAGE17, "live", seams);
|
|
10269
10689
|
if (!isLive(record)) {
|
|
10270
10690
|
throw refusal(`Session ${record.sessionId} reads ${record.state}: its run has ended, so there is nothing to resume.`, `\`rafa loop start --plan=${record.plan}\` runs its plan again as a new session, its blocked tasks first.`);
|
|
10271
10691
|
}
|
|
@@ -10328,6 +10748,11 @@ var wrapped = wrapPhaseZeroCommand({
|
|
|
10328
10748
|
description: "How much of the plan each task session is handed: `full`, `stage` or `task`." + " Outranks `plan.inject` in `.rafa/config.yaml`. The wrap-up is handed the whole plan.",
|
|
10329
10749
|
type: "string"
|
|
10330
10750
|
},
|
|
10751
|
+
{
|
|
10752
|
+
name: "skills-resolver",
|
|
10753
|
+
description: "The resolver that picks the skills each task session is handed: `planner`, `tag`" + " or `none`. Outranks `task.skills` in `.rafa/config.yaml` for this run only.",
|
|
10754
|
+
type: "string"
|
|
10755
|
+
},
|
|
10331
10756
|
{
|
|
10332
10757
|
name: "runtime",
|
|
10333
10758
|
description: "The installed rafa the run goes on in: a version under `~/.rafa/runtime/`, or a path" + " against the working directory to a `cli.js` or the directory holding it. Refused inside the" + " `src/` of the working directory or the project root. Typed after `loop start`.",
|
|
@@ -10400,7 +10825,7 @@ var loopStart = Object.freeze({
|
|
|
10400
10825
|
var start_default = endingWith(loopStart);
|
|
10401
10826
|
|
|
10402
10827
|
// src/commands/loop/status.ts
|
|
10403
|
-
var
|
|
10828
|
+
var USAGE18 = "rafa loop status [-s|--session-id=<id>]";
|
|
10404
10829
|
var BLOCKED_TASK_LINE = /^- \[BLOCKED\] (.+)/;
|
|
10405
10830
|
var NO_BLOCKER = "the line trails no blocker comment";
|
|
10406
10831
|
function blockerAt(checklist, lineNum) {
|
|
@@ -10436,8 +10861,8 @@ function renderStatus(status) {
|
|
|
10436
10861
|
];
|
|
10437
10862
|
}
|
|
10438
10863
|
async function runStatus(context, seams) {
|
|
10439
|
-
expectNoArgument(context.args,
|
|
10440
|
-
const { root, record } = pickSession(context,
|
|
10864
|
+
expectNoArgument(context.args, USAGE18);
|
|
10865
|
+
const { root, record } = pickSession(context, USAGE18, "live-or-newest", seams);
|
|
10441
10866
|
const checklist = readSessionChecklist(root, record);
|
|
10442
10867
|
const tasks = checklist === null ? null : countTasks(checklist.tasks);
|
|
10443
10868
|
let eta = null;
|
|
@@ -10490,7 +10915,7 @@ function createLoopStatusCommand(seams = {}) {
|
|
|
10490
10915
|
var status_default = createLoopStatusCommand();
|
|
10491
10916
|
|
|
10492
10917
|
// src/commands/loop/stop.ts
|
|
10493
|
-
var
|
|
10918
|
+
var USAGE19 = "rafa loop stop [-s|--session-id=<id>]";
|
|
10494
10919
|
function signalRun(record, interrupt) {
|
|
10495
10920
|
try {
|
|
10496
10921
|
interrupt(record.pid);
|
|
@@ -10543,8 +10968,8 @@ function renderStop(result) {
|
|
|
10543
10968
|
];
|
|
10544
10969
|
}
|
|
10545
10970
|
async function runStop(context, seams) {
|
|
10546
|
-
expectNoArgument(context.args,
|
|
10547
|
-
const { root, record } = pickSession(context,
|
|
10971
|
+
expectNoArgument(context.args, USAGE19);
|
|
10972
|
+
const { root, record } = pickSession(context, USAGE19, "live", seams);
|
|
10548
10973
|
if (!isLive(record)) {
|
|
10549
10974
|
throw refusal(`Session ${record.sessionId} reads ${record.state}: no run is left to stop.`);
|
|
10550
10975
|
}
|
|
@@ -10595,7 +11020,7 @@ function createLoopStopCommand(seams = {}) {
|
|
|
10595
11020
|
var stop_default = createLoopStopCommand();
|
|
10596
11021
|
|
|
10597
11022
|
// src/commands/module/exec.ts
|
|
10598
|
-
var
|
|
11023
|
+
var USAGE20 = "rafa module exec <module> <action> [args] [flags]";
|
|
10599
11024
|
var execCommand = {
|
|
10600
11025
|
name: "module exec",
|
|
10601
11026
|
subject: "module",
|
|
@@ -10624,13 +11049,13 @@ var execCommand = {
|
|
|
10624
11049
|
const mounts = context.registry.mounts();
|
|
10625
11050
|
const mounted = mounts.length === 0 ? "none is mounted; a module is mounted when allowList: names it and modules: gives its path source" : mounts.map((mount) => `${mount.name} (${context.registry.actionsOf(mountKey(mount.name)).map((held) => held.action).join(", ")})`).join(", ");
|
|
10626
11051
|
throw new CommandExit(1, `\u274C Expected a module and one of its actions; mounted: ${mounted}
|
|
10627
|
-
Usage: ${
|
|
11052
|
+
Usage: ${USAGE20}`);
|
|
10628
11053
|
}
|
|
10629
11054
|
};
|
|
10630
11055
|
var exec_default = Object.freeze(execCommand);
|
|
10631
11056
|
|
|
10632
11057
|
// src/commands/module/list.ts
|
|
10633
|
-
var
|
|
11058
|
+
var USAGE21 = "rafa module list";
|
|
10634
11059
|
function sourceText(source) {
|
|
10635
11060
|
const ref = source.ref === null ? "" : `@${source.ref}`;
|
|
10636
11061
|
return `${source.kind} ${source.location}${ref}`;
|
|
@@ -10651,10 +11076,10 @@ function renderModuleList(list) {
|
|
|
10651
11076
|
return ["Modules:", ...list.modules.flatMap(moduleLines)];
|
|
10652
11077
|
}
|
|
10653
11078
|
async function runList6(context, seams) {
|
|
10654
|
-
expectNoArgument(context.args,
|
|
11079
|
+
expectNoArgument(context.args, USAGE21);
|
|
10655
11080
|
const { project } = context;
|
|
10656
11081
|
if (project === null)
|
|
10657
|
-
throw new CommandExit(1, `\u274C ${
|
|
11082
|
+
throw new CommandExit(1, `\u274C ${USAGE21} runs inside a project`);
|
|
10658
11083
|
let loaded;
|
|
10659
11084
|
try {
|
|
10660
11085
|
const resolved = loadConfig({ root: project.root, home: project.home });
|
|
@@ -10807,7 +11232,7 @@ function fastForwardBase(git, plan2) {
|
|
|
10807
11232
|
|
|
10808
11233
|
// src/commands/next.ts
|
|
10809
11234
|
var NEXT_USAGE = "rafa next [--dry-run] [--yes[=<action ids>]]";
|
|
10810
|
-
var
|
|
11235
|
+
var DRY_RUN_FLAG3 = "dry-run";
|
|
10811
11236
|
var STATE_MARK = "\uD83D\uDCCD";
|
|
10812
11237
|
var PROPOSAL_MARK = "\uD83D\uDC49";
|
|
10813
11238
|
var STOP_MARK = "\u23F9";
|
|
@@ -10828,7 +11253,7 @@ function dryRunLine(dryRun) {
|
|
|
10828
11253
|
if (dryRun === "no-terminal") {
|
|
10829
11254
|
return `${STOP_MARK} There is no terminal to answer on, so nothing ran; run rafa next where you can answer,` + ` or type --${YES_FLAG}=${BARE_YES_ACTIONS.join(",")} to allow those steps unasked.`;
|
|
10830
11255
|
}
|
|
10831
|
-
return `${STOP_MARK} --${
|
|
11256
|
+
return `${STOP_MARK} --${DRY_RUN_FLAG3}: nothing ran.`;
|
|
10832
11257
|
}
|
|
10833
11258
|
function stopLine(stop, state, ceiling, dryRun) {
|
|
10834
11259
|
if (stop === "dry-run")
|
|
@@ -10924,14 +11349,14 @@ function lineRefusal2(problem2) {
|
|
|
10924
11349
|
Usage: ${NEXT_USAGE}`);
|
|
10925
11350
|
}
|
|
10926
11351
|
function readDryRun(flags) {
|
|
10927
|
-
const value = flags[
|
|
11352
|
+
const value = flags[DRY_RUN_FLAG3];
|
|
10928
11353
|
if (value === undefined)
|
|
10929
11354
|
return false;
|
|
10930
11355
|
if (typeof value === "boolean")
|
|
10931
11356
|
return value;
|
|
10932
11357
|
if (value === "true" || value === "false")
|
|
10933
11358
|
return value === "true";
|
|
10934
|
-
throw lineRefusal2(`--${
|
|
11359
|
+
throw lineRefusal2(`--${DRY_RUN_FLAG3} takes no value, and read "${value}" as one`);
|
|
10935
11360
|
}
|
|
10936
11361
|
function dryRunOf(flag, ceiling, hasTerminal) {
|
|
10937
11362
|
if (flag)
|
|
@@ -10983,7 +11408,7 @@ async function runNext(context, seams) {
|
|
|
10983
11408
|
var YES_ID_LIST = YES_ACTIONS.join(", ");
|
|
10984
11409
|
var NEXT_FLAGS = Object.freeze([
|
|
10985
11410
|
{
|
|
10986
|
-
name:
|
|
11411
|
+
name: DRY_RUN_FLAG3,
|
|
10987
11412
|
description: "Print where the project stands and what to do about it, and stop without asking or running it." + " A run with no terminal to answer on and no --yes does this whether or not the flag is typed.",
|
|
10988
11413
|
type: "boolean"
|
|
10989
11414
|
},
|
|
@@ -11120,10 +11545,10 @@ var wrapped2 = wrapPhaseZeroCommand({
|
|
|
11120
11545
|
var create_default2 = endingWith(wrapped2);
|
|
11121
11546
|
|
|
11122
11547
|
// src/commands/plan/risk.ts
|
|
11123
|
-
import { readFileSync as
|
|
11548
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
11124
11549
|
import { resolve as resolve11 } from "path";
|
|
11125
11550
|
var COMMAND2 = "rafa plan risk";
|
|
11126
|
-
var
|
|
11551
|
+
var USAGE22 = "rafa plan risk [<plan>] [--strict]";
|
|
11127
11552
|
var STRICT_HINT = `Type the plan first: ${COMMAND2} <plan> --strict`;
|
|
11128
11553
|
function spawningRunners(root) {
|
|
11129
11554
|
return { git: createGitRunner(root), gh: createGhRunner({ cwd: root }) };
|
|
@@ -11136,7 +11561,7 @@ function riskPlanPath(root, planDir, typed) {
|
|
|
11136
11561
|
throw new CommandExit(1, `\u274C Plan file not found: ${path}`);
|
|
11137
11562
|
const lookedFor = [...new Set([resolve11(root, planDir, DEFAULT_PLAN_FILE), resolve11(root, DEFAULT_PLAN_FILE)])];
|
|
11138
11563
|
throw new CommandExit(1, `\u274C No plan named, and no default plan at ${lookedFor.join(" or ")}
|
|
11139
|
-
Usage: ${
|
|
11564
|
+
Usage: ${USAGE22}`);
|
|
11140
11565
|
}
|
|
11141
11566
|
function riskLine(finding) {
|
|
11142
11567
|
const line = finding.line === undefined ? "" : `:${String(finding.line)}`;
|
|
@@ -11148,7 +11573,7 @@ function strictRefusal(report2) {
|
|
|
11148
11573
|
}
|
|
11149
11574
|
async function readRisk(path, project, config, context, seams) {
|
|
11150
11575
|
const makeRunners = seams.runners ?? spawningRunners;
|
|
11151
|
-
return assessPlanRisk({ path, text:
|
|
11576
|
+
return assessPlanRisk({ path, text: readFileSync16(path, "utf8") }, {
|
|
11152
11577
|
repoRoot: project.root,
|
|
11153
11578
|
home: project.home,
|
|
11154
11579
|
settingSources: config.settingSources,
|
|
@@ -11200,7 +11625,7 @@ function createPlanRiskCommand(seams = {}) {
|
|
|
11200
11625
|
outputs: ["text", "json"],
|
|
11201
11626
|
run: async (context) => {
|
|
11202
11627
|
const strict = readSwitch("strict", context.flags["strict"], STRICT_HINT);
|
|
11203
|
-
const typed = expectAtMostOneArgument(context.args,
|
|
11628
|
+
const typed = expectAtMostOneArgument(context.args, USAGE22);
|
|
11204
11629
|
const project = requireProject(context, COMMAND2);
|
|
11205
11630
|
const config = resolveProjectConfig(project, COMMAND2, (message) => {
|
|
11206
11631
|
context.output.warn(message);
|
|
@@ -11231,9 +11656,9 @@ function createPlanRiskCommand(seams = {}) {
|
|
|
11231
11656
|
var risk_default = createPlanRiskCommand();
|
|
11232
11657
|
|
|
11233
11658
|
// src/commands/plan/show.ts
|
|
11234
|
-
import { readFileSync as
|
|
11235
|
-
import { join as
|
|
11236
|
-
var
|
|
11659
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
11660
|
+
import { join as join23 } from "path";
|
|
11661
|
+
var USAGE23 = "rafa plan show <stub> [--tracker]";
|
|
11237
11662
|
var COMMAND3 = "rafa plan show";
|
|
11238
11663
|
function readTrackerFlag(value) {
|
|
11239
11664
|
if (value === undefined || value === false || value === "false")
|
|
@@ -11250,10 +11675,10 @@ function showPlan(plans, stub, tracker) {
|
|
|
11250
11675
|
throw new CommandExit(1, `\u274C "${stub}" is no plan stub: a stub is letters, digits, ".", "_" and "-"`);
|
|
11251
11676
|
}
|
|
11252
11677
|
const name2 = planFileName(stub, tracker);
|
|
11253
|
-
const file =
|
|
11678
|
+
const file = join23(plans.path, name2);
|
|
11254
11679
|
if (!isFile(file))
|
|
11255
|
-
throw new CommandExit(1, missingText(stub, tracker,
|
|
11256
|
-
const model = parsePlan(
|
|
11680
|
+
throw new CommandExit(1, missingText(stub, tracker, join23(plans.label, name2)));
|
|
11681
|
+
const model = parsePlan(readFileSync17(file, "utf8"));
|
|
11257
11682
|
const { header } = model;
|
|
11258
11683
|
return {
|
|
11259
11684
|
stub,
|
|
@@ -11276,7 +11701,7 @@ function taskLine(task) {
|
|
|
11276
11701
|
return ` ${checkbox(task.status)} ${task.text}`;
|
|
11277
11702
|
}
|
|
11278
11703
|
function renderShownPlan(shown, dirLabel) {
|
|
11279
|
-
const relative7 =
|
|
11704
|
+
const relative7 = join23(dirLabel, planFileName(shown.stub, shown.tracker));
|
|
11280
11705
|
const fileLabel = shown.tracker ? "Tracker" : "File";
|
|
11281
11706
|
const lines2 = [`Plan: ${shown.stub}`, `${fileLabel}: ${relative7}`];
|
|
11282
11707
|
if (shown.header.issue !== null)
|
|
@@ -11329,7 +11754,7 @@ var planShowCommand = {
|
|
|
11329
11754
|
outputs: ["text", "json"],
|
|
11330
11755
|
run: async (context) => {
|
|
11331
11756
|
const tracker = readTrackerFlag(context.flags.tracker);
|
|
11332
|
-
const stub = expectOneArgument(context.args,
|
|
11757
|
+
const stub = expectOneArgument(context.args, USAGE23);
|
|
11333
11758
|
const plans = resolvePlansDir(requireProject(context, COMMAND3), COMMAND3, (message) => {
|
|
11334
11759
|
context.output.warn(message);
|
|
11335
11760
|
});
|
|
@@ -11468,7 +11893,7 @@ Run rafa pr list to see the open pull requests.`, usage2);
|
|
|
11468
11893
|
}
|
|
11469
11894
|
|
|
11470
11895
|
// src/commands/pr/current.ts
|
|
11471
|
-
var
|
|
11896
|
+
var USAGE24 = PR_USAGE.current;
|
|
11472
11897
|
var SEPARATOR = " \u2014 ";
|
|
11473
11898
|
function trimmedOrNull(value) {
|
|
11474
11899
|
const trimmed = value.trim();
|
|
@@ -11499,9 +11924,9 @@ async function probeChecks(pulls, number) {
|
|
|
11499
11924
|
}
|
|
11500
11925
|
}
|
|
11501
11926
|
async function readCurrent(context, seams) {
|
|
11502
|
-
expectNoArguments(context.args,
|
|
11927
|
+
expectNoArguments(context.args, USAGE24);
|
|
11503
11928
|
const pr = openPrContext(context, seams);
|
|
11504
|
-
const pick = await pickPullRequest(pr, null,
|
|
11929
|
+
const pick = await pickPullRequest(pr, null, USAGE24);
|
|
11505
11930
|
const summary = pick.summary;
|
|
11506
11931
|
const probe = await probeChecks(pr.pulls, pick.number);
|
|
11507
11932
|
const reading = {
|
|
@@ -11554,7 +11979,7 @@ function createPrCurrentCommand(seams = DEFAULT_PR_SEAMS) {
|
|
|
11554
11979
|
var current_default = createPrCurrentCommand();
|
|
11555
11980
|
|
|
11556
11981
|
// src/commands/pr/list.ts
|
|
11557
|
-
var
|
|
11982
|
+
var USAGE25 = PR_USAGE.list;
|
|
11558
11983
|
var UNREADABLE2 = "?";
|
|
11559
11984
|
var PROBE_LIMIT = 6;
|
|
11560
11985
|
var TITLE_WIDTH = 60;
|
|
@@ -11597,8 +12022,8 @@ function titleCell(title) {
|
|
|
11597
12022
|
return "(no title)";
|
|
11598
12023
|
return trimmed.length > TITLE_WIDTH ? `${trimmed.slice(0, TITLE_WIDTH - CUT.length)}${CUT}` : trimmed;
|
|
11599
12024
|
}
|
|
11600
|
-
function headLine3(
|
|
11601
|
-
return
|
|
12025
|
+
function headLine3(count2) {
|
|
12026
|
+
return count2 === 1 ? "1 open pull request" : `${count2} open pull requests`;
|
|
11602
12027
|
}
|
|
11603
12028
|
function cellsOf(row2, now) {
|
|
11604
12029
|
return [
|
|
@@ -11655,7 +12080,7 @@ async function rowOf3(pulls, pull) {
|
|
|
11655
12080
|
};
|
|
11656
12081
|
}
|
|
11657
12082
|
async function readList(context, seams, now) {
|
|
11658
|
-
expectNoArguments(context.args,
|
|
12083
|
+
expectNoArguments(context.args, USAGE25);
|
|
11659
12084
|
const pr = openPrContext(context, seams);
|
|
11660
12085
|
const open = await onProvider("list the open pull requests", () => pr.pulls.list());
|
|
11661
12086
|
const rows = await mapWithLimit(open, PROBE_LIMIT, (pull) => rowOf3(pr.pulls, pull));
|
|
@@ -11695,8 +12120,8 @@ function createPrListCommand(seams = DEFAULT_PR_SEAMS) {
|
|
|
11695
12120
|
var list_default8 = createPrListCommand();
|
|
11696
12121
|
|
|
11697
12122
|
// src/commands/pr/merge.ts
|
|
11698
|
-
import { existsSync as
|
|
11699
|
-
import { join as
|
|
12123
|
+
import { existsSync as existsSync10, readFileSync as readFileSync19 } from "fs";
|
|
12124
|
+
import { join as join25 } from "path";
|
|
11700
12125
|
|
|
11701
12126
|
// src/board/roadmap-tick.ts
|
|
11702
12127
|
var PREFIX5 = "board roadmap";
|
|
@@ -11851,17 +12276,17 @@ function tickSentence(result) {
|
|
|
11851
12276
|
import {
|
|
11852
12277
|
chmodSync,
|
|
11853
12278
|
copyFileSync as copyFileSync3,
|
|
11854
|
-
existsSync as
|
|
12279
|
+
existsSync as existsSync9,
|
|
11855
12280
|
mkdirSync as mkdirSync6,
|
|
11856
|
-
readdirSync as
|
|
11857
|
-
readFileSync as
|
|
12281
|
+
readdirSync as readdirSync5,
|
|
12282
|
+
readFileSync as readFileSync18,
|
|
11858
12283
|
realpathSync as realpathSync5,
|
|
11859
|
-
renameSync,
|
|
11860
|
-
rmSync as
|
|
11861
|
-
statSync as
|
|
12284
|
+
renameSync as renameSync2,
|
|
12285
|
+
rmSync as rmSync3,
|
|
12286
|
+
statSync as statSync12,
|
|
11862
12287
|
symlinkSync
|
|
11863
12288
|
} from "fs";
|
|
11864
|
-
import { basename as basename7, delimiter as delimiter2, dirname as dirname4, join as
|
|
12289
|
+
import { basename as basename7, delimiter as delimiter2, dirname as dirname4, join as join24, relative as relative7, resolve as resolve12 } from "path";
|
|
11865
12290
|
var LINK_NAME = "rafa";
|
|
11866
12291
|
var FORCE_FLAG = "--force";
|
|
11867
12292
|
var EXIT_DONE = 0;
|
|
@@ -11923,19 +12348,19 @@ function runtimeRefusalLines(runtimeDir, version2) {
|
|
|
11923
12348
|
}
|
|
11924
12349
|
function listTrackers(repoRoot, planDir) {
|
|
11925
12350
|
const dir = resolve12(repoRoot, planDir);
|
|
11926
|
-
return entryNames(dir).filter((entry) => TRACKER_NAME.test(entry)).sort((a, b) => a.localeCompare(b)).map((entry) =>
|
|
12351
|
+
return entryNames(dir).filter((entry) => TRACKER_NAME.test(entry)).sort((a, b) => a.localeCompare(b)).map((entry) => join24(dir, entry)).filter((path) => statSync12(path).isFile()).map((path) => relative7(repoRoot, path));
|
|
11927
12352
|
}
|
|
11928
12353
|
function openTrackers(repoRoot, planDir) {
|
|
11929
12354
|
return listTrackers(repoRoot, planDir).flatMap((path) => {
|
|
11930
|
-
const task = findNextTask(
|
|
12355
|
+
const task = findNextTask(readFileSync18(join24(repoRoot, path), "utf8"));
|
|
11931
12356
|
return task ? [{ path, task }] : [];
|
|
11932
12357
|
});
|
|
11933
12358
|
}
|
|
11934
12359
|
function readManifestVersion2(repoRoot) {
|
|
11935
|
-
const manifest = JSON.parse(
|
|
12360
|
+
const manifest = JSON.parse(readFileSync18(join24(repoRoot, "package.json"), "utf8"));
|
|
11936
12361
|
const record = typeof manifest === "object" && manifest !== null ? manifest : {};
|
|
11937
12362
|
if (record["name"] !== name) {
|
|
11938
|
-
throw new Error(`${
|
|
12363
|
+
throw new Error(`${join24(repoRoot, "package.json")} names ${JSON.stringify(record["name"])}, not ${name}: this is no rafa checkout`);
|
|
11939
12364
|
}
|
|
11940
12365
|
const version2 = record["version"];
|
|
11941
12366
|
if (typeof version2 !== "string" || !VERSION_SHAPE.test(version2)) {
|
|
@@ -11946,10 +12371,10 @@ function readManifestVersion2(repoRoot) {
|
|
|
11946
12371
|
function copyTree(from, to) {
|
|
11947
12372
|
mkdirSync6(to, { recursive: true });
|
|
11948
12373
|
let copied = 0;
|
|
11949
|
-
const entries =
|
|
12374
|
+
const entries = readdirSync5(from, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
11950
12375
|
for (const entry of entries) {
|
|
11951
|
-
const source =
|
|
11952
|
-
const target =
|
|
12376
|
+
const source = join24(from, entry.name);
|
|
12377
|
+
const target = join24(to, entry.name);
|
|
11953
12378
|
if (entry.isDirectory()) {
|
|
11954
12379
|
copied += copyTree(source, target);
|
|
11955
12380
|
} else if (entry.isFile()) {
|
|
@@ -11963,41 +12388,41 @@ function copyTree(from, to) {
|
|
|
11963
12388
|
}
|
|
11964
12389
|
function replaceTree(from, to) {
|
|
11965
12390
|
const staging = temporaryName(to);
|
|
11966
|
-
|
|
12391
|
+
rmSync3(staging, { recursive: true, force: true });
|
|
11967
12392
|
try {
|
|
11968
12393
|
const copied = copyTree(from, staging);
|
|
11969
|
-
|
|
12394
|
+
swapIn2(staging, to);
|
|
11970
12395
|
return copied;
|
|
11971
12396
|
} catch (err) {
|
|
11972
|
-
|
|
12397
|
+
rmSync3(staging, { recursive: true, force: true });
|
|
11973
12398
|
throw err;
|
|
11974
12399
|
}
|
|
11975
12400
|
}
|
|
11976
|
-
function
|
|
11977
|
-
if (!
|
|
11978
|
-
|
|
12401
|
+
function swapIn2(staging, to) {
|
|
12402
|
+
if (!existsSync9(to)) {
|
|
12403
|
+
renameSync2(staging, to);
|
|
11979
12404
|
return;
|
|
11980
12405
|
}
|
|
11981
12406
|
const outgoing = `${temporaryName(to)}.outgoing`;
|
|
11982
|
-
|
|
11983
|
-
|
|
12407
|
+
rmSync3(outgoing, { recursive: true, force: true });
|
|
12408
|
+
renameSync2(to, outgoing);
|
|
11984
12409
|
try {
|
|
11985
|
-
|
|
12410
|
+
renameSync2(staging, to);
|
|
11986
12411
|
} catch (err) {
|
|
11987
|
-
|
|
12412
|
+
renameSync2(outgoing, to);
|
|
11988
12413
|
throw err;
|
|
11989
12414
|
}
|
|
11990
|
-
|
|
12415
|
+
rmSync3(outgoing, { recursive: true, force: true });
|
|
11991
12416
|
}
|
|
11992
12417
|
function relink(linkPath, target) {
|
|
11993
12418
|
mkdirSync6(dirname4(linkPath), { recursive: true });
|
|
11994
12419
|
const temporary = temporaryName(linkPath);
|
|
11995
|
-
|
|
12420
|
+
rmSync3(temporary, { force: true });
|
|
11996
12421
|
try {
|
|
11997
12422
|
symlinkSync(target, temporary);
|
|
11998
|
-
|
|
12423
|
+
renameSync2(temporary, linkPath);
|
|
11999
12424
|
} catch (err) {
|
|
12000
|
-
|
|
12425
|
+
rmSync3(temporary, { force: true });
|
|
12001
12426
|
throw err;
|
|
12002
12427
|
}
|
|
12003
12428
|
const resolved = realpathSync5(linkPath);
|
|
@@ -12044,8 +12469,8 @@ function installRuntime(seams, options = {}) {
|
|
|
12044
12469
|
}
|
|
12045
12470
|
function runInstall(seams, options) {
|
|
12046
12471
|
const { repoRoot, home, log } = seams;
|
|
12047
|
-
const runtimeRoot =
|
|
12048
|
-
const linkPath =
|
|
12472
|
+
const runtimeRoot = join24(home, RUNTIME_SUBDIR);
|
|
12473
|
+
const linkPath = join24(home, RAFA_BIN_DIR, LINK_NAME);
|
|
12049
12474
|
log(`repo root: ${repoRoot}`);
|
|
12050
12475
|
log(`runtime root: ${runtimeRoot}`);
|
|
12051
12476
|
log(`link: ${linkPath}`);
|
|
@@ -12055,9 +12480,9 @@ function runInstall(seams, options) {
|
|
|
12055
12480
|
const open = inStage("trackers", () => openTrackers(repoRoot, planDir));
|
|
12056
12481
|
if (open.length > 0)
|
|
12057
12482
|
return { kind: "refused", reason: "trackers", planDir, trackers: open };
|
|
12058
|
-
const distDir =
|
|
12059
|
-
const runtimeDir =
|
|
12060
|
-
const installed =
|
|
12483
|
+
const distDir = join24(repoRoot, "dist");
|
|
12484
|
+
const runtimeDir = join24(runtimeRoot, version2);
|
|
12485
|
+
const installed = existsSync9(runtimeDir);
|
|
12061
12486
|
if (installed && options.force !== true) {
|
|
12062
12487
|
return { kind: "refused", reason: "runtime-exists", version: version2, runtimeDir };
|
|
12063
12488
|
}
|
|
@@ -12065,13 +12490,13 @@ function runInstall(seams, options) {
|
|
|
12065
12490
|
const code = inStage("build", () => seams.build(repoRoot));
|
|
12066
12491
|
if (code !== 0)
|
|
12067
12492
|
throw new StageFailure("build", `the build exited ${code}`);
|
|
12068
|
-
if (!
|
|
12069
|
-
throw new StageFailure("build", `the build exited 0 and left no ${
|
|
12493
|
+
if (!existsSync9(join24(distDir, RUNTIME_ENTRY))) {
|
|
12494
|
+
throw new StageFailure("build", `the build exited 0 and left no ${join24(distDir, RUNTIME_ENTRY)}`);
|
|
12070
12495
|
}
|
|
12071
12496
|
log(installed ? `copying ${distDir} over ${runtimeDir}, replacing it whole` : `copying ${distDir} into ${runtimeDir}`);
|
|
12072
12497
|
const copied = inStage("copy", () => replaceTree(distDir, runtimeDir));
|
|
12073
12498
|
log(`copied ${copied} file(s)`);
|
|
12074
|
-
const resolved = inStage("link", () => relink(linkPath,
|
|
12499
|
+
const resolved = inStage("link", () => relink(linkPath, join24(runtimeDir, RUNTIME_ENTRY)));
|
|
12075
12500
|
log(`${linkPath} resolves to ${resolved}`);
|
|
12076
12501
|
return { kind: "done", version: version2, runtimeDir, linkPath, resolved, copied };
|
|
12077
12502
|
}
|
|
@@ -12086,7 +12511,7 @@ function inStage(stage, run) {
|
|
|
12086
12511
|
}
|
|
12087
12512
|
function entryNames(dir) {
|
|
12088
12513
|
try {
|
|
12089
|
-
return
|
|
12514
|
+
return readdirSync5(dir);
|
|
12090
12515
|
} catch (err) {
|
|
12091
12516
|
if (typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT")
|
|
12092
12517
|
return [];
|
|
@@ -12097,15 +12522,15 @@ function replaceFile(source, target) {
|
|
|
12097
12522
|
const temporary = temporaryName(target);
|
|
12098
12523
|
try {
|
|
12099
12524
|
copyFileSync3(source, temporary);
|
|
12100
|
-
chmodSync(temporary,
|
|
12101
|
-
|
|
12525
|
+
chmodSync(temporary, statSync12(source).mode & 4095);
|
|
12526
|
+
renameSync2(temporary, target);
|
|
12102
12527
|
} catch (err) {
|
|
12103
|
-
|
|
12528
|
+
rmSync3(temporary, { force: true });
|
|
12104
12529
|
throw err;
|
|
12105
12530
|
}
|
|
12106
12531
|
}
|
|
12107
12532
|
function temporaryName(path) {
|
|
12108
|
-
return
|
|
12533
|
+
return join24(dirname4(path), `.${basename7(path)}.snapshot-${process.pid}`);
|
|
12109
12534
|
}
|
|
12110
12535
|
|
|
12111
12536
|
// src/commands/pr/merge-followups.ts
|
|
@@ -12281,8 +12706,8 @@ async function readUncheckedMerge(options) {
|
|
|
12281
12706
|
}
|
|
12282
12707
|
return Object.freeze({ workflowCount, reading, yes });
|
|
12283
12708
|
}
|
|
12284
|
-
function isYes2(
|
|
12285
|
-
return
|
|
12709
|
+
function isYes2(answer2) {
|
|
12710
|
+
return answer2 !== null && YES_ANSWERS3.includes(answer2.trim().toLowerCase());
|
|
12286
12711
|
}
|
|
12287
12712
|
async function confirmUncheckedMerge(unchecked, options) {
|
|
12288
12713
|
for (const line of unchecked.reading.warning)
|
|
@@ -12298,11 +12723,11 @@ async function confirmUncheckedMerge(unchecked, options) {
|
|
|
12298
12723
|
}
|
|
12299
12724
|
}
|
|
12300
12725
|
function commentProblemLine(number, problem2, body) {
|
|
12301
|
-
const
|
|
12726
|
+
const quoted2 = body.split(`
|
|
12302
12727
|
`).map((line) => `${INDENT3}${line}`).join(`
|
|
12303
12728
|
`);
|
|
12304
12729
|
return `the unchecked-merge comment was not posted on #${number}: ${problem2}. Post it yourself:
|
|
12305
|
-
${
|
|
12730
|
+
${quoted2}`;
|
|
12306
12731
|
}
|
|
12307
12732
|
async function postUncheckedComment(pulls, number, unchecked, warn) {
|
|
12308
12733
|
const body = unchecked.reading.comment;
|
|
@@ -12315,7 +12740,7 @@ async function postUncheckedComment(pulls, number, unchecked, warn) {
|
|
|
12315
12740
|
}
|
|
12316
12741
|
|
|
12317
12742
|
// src/commands/pr/merge.ts
|
|
12318
|
-
var
|
|
12743
|
+
var USAGE26 = PR_USAGE.merge;
|
|
12319
12744
|
var REMOTE3 = "origin";
|
|
12320
12745
|
var INDENT4 = " ";
|
|
12321
12746
|
var YES_ANSWERS4 = ["y", "yes"];
|
|
@@ -12373,8 +12798,8 @@ function requireTerminal(seams, summary) {
|
|
|
12373
12798
|
"Merge it without the question with --yes."
|
|
12374
12799
|
]);
|
|
12375
12800
|
}
|
|
12376
|
-
function isYes3(
|
|
12377
|
-
return
|
|
12801
|
+
function isYes3(answer2) {
|
|
12802
|
+
return answer2 !== null && YES_ANSWERS4.includes(answer2.trim().toLowerCase());
|
|
12378
12803
|
}
|
|
12379
12804
|
function prompterOf(seams) {
|
|
12380
12805
|
return seams.openPrompter ?? (() => createLinePrompter(process.stdin, process.stderr));
|
|
@@ -12396,13 +12821,13 @@ function remoteHoldsBranch(git, branch, warn) {
|
|
|
12396
12821
|
}
|
|
12397
12822
|
function textOf(path) {
|
|
12398
12823
|
try {
|
|
12399
|
-
return
|
|
12824
|
+
return readFileSync19(path, "utf8");
|
|
12400
12825
|
} catch {
|
|
12401
12826
|
return "";
|
|
12402
12827
|
}
|
|
12403
12828
|
}
|
|
12404
12829
|
function followUpsFor(pr, git) {
|
|
12405
|
-
const facts = readPackageFacts(textOf(
|
|
12830
|
+
const facts = readPackageFacts(textOf(join25(pr.project.root, "package.json")));
|
|
12406
12831
|
if (facts.version === null)
|
|
12407
12832
|
return [];
|
|
12408
12833
|
const tags = git(["tag", "--list", versionTag(facts.version)]);
|
|
@@ -12410,7 +12835,7 @@ function followUpsFor(pr, git) {
|
|
|
12410
12835
|
version: facts.version,
|
|
12411
12836
|
tagged: tags.ok && tags.stdout.trim() !== "",
|
|
12412
12837
|
rafaCheckout: facts.rafaCheckout,
|
|
12413
|
-
runtimeInstalled:
|
|
12838
|
+
runtimeInstalled: existsSync10(join25(pr.project.home, RUNTIME_SUBDIR, facts.version))
|
|
12414
12839
|
});
|
|
12415
12840
|
}
|
|
12416
12841
|
function openGh(pr, seams) {
|
|
@@ -12522,22 +12947,22 @@ async function commentIfUnchecked(context, pr, number, unchecked) {
|
|
|
12522
12947
|
return posted.url;
|
|
12523
12948
|
}
|
|
12524
12949
|
async function runMerge(context, seams) {
|
|
12525
|
-
const yes = readBooleanFlag(context.flags, "yes",
|
|
12526
|
-
const skipChecks = readBooleanFlag(context.flags, "skip-checks",
|
|
12527
|
-
const wanted = readMethodFlag(context.flags,
|
|
12528
|
-
const asked = readPullArgument(context.args,
|
|
12950
|
+
const yes = readBooleanFlag(context.flags, "yes", USAGE26);
|
|
12951
|
+
const skipChecks = readBooleanFlag(context.flags, "skip-checks", USAGE26);
|
|
12952
|
+
const wanted = readMethodFlag(context.flags, USAGE26);
|
|
12953
|
+
const asked = readPullArgument(context.args, USAGE26);
|
|
12529
12954
|
const pr = openPrContext(context, seams);
|
|
12530
12955
|
const git = (seams.git ?? createGitRunner)(pr.project.root);
|
|
12531
12956
|
const method = wanted ?? pr.mergeMethod;
|
|
12532
|
-
const pick = await pickPullRequest(pr, asked,
|
|
12957
|
+
const pick = await pickPullRequest(pr, asked, USAGE26);
|
|
12533
12958
|
const detail = await onProvider(`read pull request #${pick.number}`, () => pr.pulls.get(pick.number));
|
|
12534
12959
|
if (detail === null) {
|
|
12535
|
-
throw lineRefusal3(`No pull request #${pick.number} at ${pr.project.root}`,
|
|
12960
|
+
throw lineRefusal3(`No pull request #${pick.number} at ${pr.project.root}`, USAGE26);
|
|
12536
12961
|
}
|
|
12537
12962
|
const checks = await onProvider(`read the checks of #${pick.number}`, () => pr.pulls.checks(pick.number));
|
|
12538
12963
|
refuseFromGit(git, detail, checks, skipChecks);
|
|
12539
12964
|
const summary = summaryLine2(detail, method);
|
|
12540
|
-
const
|
|
12965
|
+
const answer2 = await askToMerge(context, pr, seams, { number: detail.number, summary, yes, skipChecks });
|
|
12541
12966
|
const answered3 = {
|
|
12542
12967
|
number: detail.number,
|
|
12543
12968
|
source: pick.source,
|
|
@@ -12552,9 +12977,9 @@ async function runMerge(context, seams) {
|
|
|
12552
12977
|
followUps: [],
|
|
12553
12978
|
roadmapTick: null,
|
|
12554
12979
|
unblocked: null,
|
|
12555
|
-
unchecked: uncheckedReport(
|
|
12980
|
+
unchecked: uncheckedReport(answer2.unchecked, null)
|
|
12556
12981
|
};
|
|
12557
|
-
if (!
|
|
12982
|
+
if (!answer2.go) {
|
|
12558
12983
|
context.output.info("Nothing was merged.");
|
|
12559
12984
|
return answered3;
|
|
12560
12985
|
}
|
|
@@ -12563,7 +12988,7 @@ async function runMerge(context, seams) {
|
|
|
12563
12988
|
throw refusal5([`\u274C ${pr.pulls.kind} would not merge #${detail.number}: ${outcome2.detail}`]);
|
|
12564
12989
|
}
|
|
12565
12990
|
context.output.info(`Merged #${detail.number} into ${detail.baseRefName} (${method}).`);
|
|
12566
|
-
const commentUrl = await commentIfUnchecked(context, pr, detail.number,
|
|
12991
|
+
const commentUrl = await commentIfUnchecked(context, pr, detail.number, answer2.unchecked);
|
|
12567
12992
|
const roadmapTick = await reportTick(context, pr, seams, detail);
|
|
12568
12993
|
const remoteBranchPresent = remoteHoldsBranch(git, detail.headRefName, (message) => {
|
|
12569
12994
|
context.output.warn(message);
|
|
@@ -12586,7 +13011,7 @@ async function runMerge(context, seams) {
|
|
|
12586
13011
|
followUps,
|
|
12587
13012
|
roadmapTick,
|
|
12588
13013
|
unblocked,
|
|
12589
|
-
unchecked: uncheckedReport(
|
|
13014
|
+
unchecked: uncheckedReport(answer2.unchecked, commentUrl)
|
|
12590
13015
|
};
|
|
12591
13016
|
}
|
|
12592
13017
|
function createPrMergeCommand(seams = DEFAULT_MERGE_SEAMS) {
|
|
@@ -12791,8 +13216,8 @@ function fencedBlock(body, language) {
|
|
|
12791
13216
|
return `${fence}${language}
|
|
12792
13217
|
${text}${fence}`;
|
|
12793
13218
|
}
|
|
12794
|
-
function counted2(
|
|
12795
|
-
return
|
|
13219
|
+
function counted2(count2, noun) {
|
|
13220
|
+
return count2 === 1 ? `1 ${noun}` : `${count2} ${noun}s`;
|
|
12796
13221
|
}
|
|
12797
13222
|
function excerptCaption(reading) {
|
|
12798
13223
|
const shown = counted2(reading.lines.length, "line");
|
|
@@ -12961,17 +13386,17 @@ function readTriageBlock(body) {
|
|
|
12961
13386
|
}
|
|
12962
13387
|
return readFields(document);
|
|
12963
13388
|
}
|
|
12964
|
-
function
|
|
13389
|
+
function quoted2(value) {
|
|
12965
13390
|
return JSON.stringify(value);
|
|
12966
13391
|
}
|
|
12967
13392
|
function writeTriageBlock(fields) {
|
|
12968
13393
|
return [
|
|
12969
|
-
`head: ${
|
|
12970
|
-
`at: ${
|
|
12971
|
-
`class: ${
|
|
13394
|
+
`head: ${quoted2(fields.head)}`,
|
|
13395
|
+
`at: ${quoted2(fields.at)}`,
|
|
13396
|
+
`class: ${quoted2(fields.triageClass)}`,
|
|
12972
13397
|
`simple: ${String(fields.simple)}`,
|
|
12973
13398
|
`attempts: ${String(fields.attempts)}`,
|
|
12974
|
-
`files: [${fields.files.map((file) =>
|
|
13399
|
+
`files: [${fields.files.map((file) => quoted2(file)).join(", ")}]`
|
|
12975
13400
|
].join(`
|
|
12976
13401
|
`);
|
|
12977
13402
|
}
|
|
@@ -13001,10 +13426,10 @@ function noChecksLines(input) {
|
|
|
13001
13426
|
return [];
|
|
13002
13427
|
const number = input.pr.number;
|
|
13003
13428
|
const unchecked = readUnchecked(number, input.workflowCount ?? null);
|
|
13004
|
-
const [
|
|
13429
|
+
const [count2 = "", warning = ""] = unchecked.warning;
|
|
13005
13430
|
const yes = unchecked.yesMayAnswer ? "`--yes` may answer it" : "`--yes` is refused, so a person must answer it";
|
|
13006
13431
|
return [
|
|
13007
|
-
`- Workflows: ${
|
|
13432
|
+
`- Workflows: ${count2}`,
|
|
13008
13433
|
`- Warning: ${warning}`,
|
|
13009
13434
|
`- To merge it anyway: \`${skipChecksCommand(number)}\` (it asks first; ${yes})`
|
|
13010
13435
|
];
|
|
@@ -13088,7 +13513,7 @@ function readLastTriage(comments) {
|
|
|
13088
13513
|
}
|
|
13089
13514
|
|
|
13090
13515
|
// src/commands/pr/show.ts
|
|
13091
|
-
var
|
|
13516
|
+
var USAGE27 = PR_USAGE.show;
|
|
13092
13517
|
var INDENT5 = " ";
|
|
13093
13518
|
var SHORT_SHA = 7;
|
|
13094
13519
|
function headLine4(detail) {
|
|
@@ -13186,12 +13611,12 @@ async function probe2(call) {
|
|
|
13186
13611
|
}
|
|
13187
13612
|
}
|
|
13188
13613
|
async function readShow(context, seams) {
|
|
13189
|
-
const asked = readPullArgument(context.args,
|
|
13614
|
+
const asked = readPullArgument(context.args, USAGE27);
|
|
13190
13615
|
const pr = openPrContext(context, seams);
|
|
13191
|
-
const pick = await pickPullRequest(pr, asked,
|
|
13616
|
+
const pick = await pickPullRequest(pr, asked, USAGE27);
|
|
13192
13617
|
const detail = await onProvider(`read pull request #${pick.number}`, () => pr.pulls.get(pick.number));
|
|
13193
13618
|
if (detail === null) {
|
|
13194
|
-
throw lineRefusal3(`No pull request #${pick.number} in the repository at ${pr.project.root}`,
|
|
13619
|
+
throw lineRefusal3(`No pull request #${pick.number} in the repository at ${pr.project.root}`, USAGE27);
|
|
13195
13620
|
}
|
|
13196
13621
|
const checks = await probe2(() => pr.pulls.checks(pick.number));
|
|
13197
13622
|
const comments = await probe2(() => pr.pulls.comments(pick.number));
|
|
@@ -13396,8 +13821,8 @@ function readsAsConflicting(pr, conflictFiles) {
|
|
|
13396
13821
|
return true;
|
|
13397
13822
|
return conflictFiles.length > 0;
|
|
13398
13823
|
}
|
|
13399
|
-
function checkCount(
|
|
13400
|
-
return
|
|
13824
|
+
function checkCount(count2) {
|
|
13825
|
+
return count2 === 1 ? "1 check" : `${count2} checks`;
|
|
13401
13826
|
}
|
|
13402
13827
|
function fileList(files) {
|
|
13403
13828
|
const shown = files.slice(0, REASON_FILE_LIMIT).join(", ");
|
|
@@ -13408,12 +13833,12 @@ function conflictReason(files) {
|
|
|
13408
13833
|
return files.length === 0 ? "the head conflicts with the base and no conflicting file was read" : `the head conflicts with the base on ${fileList(files)}`;
|
|
13409
13834
|
}
|
|
13410
13835
|
function failureReason(step, failing) {
|
|
13411
|
-
const
|
|
13412
|
-
return step === undefined ? `${
|
|
13836
|
+
const count2 = checkCount(failing.length);
|
|
13837
|
+
return step === undefined ? `${count2} failing, and no failing step was named` : `${count2} failing, the failing step being "${step.name}"`;
|
|
13413
13838
|
}
|
|
13414
13839
|
function noChecksReason(number, workflowCount) {
|
|
13415
|
-
const
|
|
13416
|
-
return `the head reports no checks at all; ${
|
|
13840
|
+
const count2 = workflowCountLine(workflowCount).replace(/\.$/, "");
|
|
13841
|
+
return `the head reports no checks at all; ${count2.charAt(0).toLowerCase()}${count2.slice(1)};` + ` to merge it anyway, run rafa pr merge ${number} --skip-checks`;
|
|
13417
13842
|
}
|
|
13418
13843
|
function decideClass(input, verdict, conflicting) {
|
|
13419
13844
|
const { conflictFiles, rows, step } = input;
|
|
@@ -13495,8 +13920,8 @@ function isSameHead(stored, head) {
|
|
|
13495
13920
|
function shortHead(head) {
|
|
13496
13921
|
return head.trim().slice(0, HEADLINE_HEAD_LENGTH);
|
|
13497
13922
|
}
|
|
13498
|
-
function checkCount2(
|
|
13499
|
-
return
|
|
13923
|
+
function checkCount2(count2) {
|
|
13924
|
+
return count2 === 1 ? "1 check" : `${count2} checks`;
|
|
13500
13925
|
}
|
|
13501
13926
|
function storedTime(comment, block) {
|
|
13502
13927
|
if (comment === null)
|
|
@@ -13598,8 +14023,8 @@ function isFreshCandidate(updatedAt, now) {
|
|
|
13598
14023
|
const age = ageMs(updatedAt, now);
|
|
13599
14024
|
return age !== null && age <= TRIAGE_FRESH_WINDOW_MS;
|
|
13600
14025
|
}
|
|
13601
|
-
function plural4(
|
|
13602
|
-
return
|
|
14026
|
+
function plural4(count2, singular, many) {
|
|
14027
|
+
return count2 === 1 ? `1 ${singular}` : `${count2} ${many}`;
|
|
13603
14028
|
}
|
|
13604
14029
|
function describeAge(updatedAt, now) {
|
|
13605
14030
|
const age = ageMs(updatedAt, now);
|
|
@@ -13663,17 +14088,17 @@ function listing(decision, assess, rest, headline, exitCode = 0) {
|
|
|
13663
14088
|
`)
|
|
13664
14089
|
});
|
|
13665
14090
|
}
|
|
13666
|
-
function refusal6(rest,
|
|
13667
|
-
const headline = `${REFUSAL_PREFIX2}: ${plural4(
|
|
14091
|
+
function refusal6(rest, count2) {
|
|
14092
|
+
const headline = `${REFUSAL_PREFIX2}: ${plural4(count2, "red pull request", "red pull requests")}, ` + "and --resolve acts on one. Run one of:";
|
|
13668
14093
|
return listing("refuse", Object.freeze([]), rest, headline, REFUSE_EXIT_CODE);
|
|
13669
14094
|
}
|
|
13670
|
-
function someReading(fresh, stale,
|
|
14095
|
+
function someReading(fresh, stale, count2) {
|
|
13671
14096
|
const older = stale.length === 0 ? "" : `; ${plural4(stale.length, "older one is", "older ones are")} skipped`;
|
|
13672
|
-
const headline = `${
|
|
14097
|
+
const headline = `${count2} red: assessing ${joinNumbers(fresh)}${older}`;
|
|
13673
14098
|
return listing("some", fresh, stale, headline);
|
|
13674
14099
|
}
|
|
13675
|
-
function listReading(rest,
|
|
13676
|
-
const headline =
|
|
14100
|
+
function listReading(rest, count2) {
|
|
14101
|
+
const headline = count2 > MAX_TRIAGE_CANDIDATES ? `${count2} red, more than the ${MAX_TRIAGE_CANDIDATES} a bare rafa pr triage assesses at once. ` + "Run one of:" : `${count2} red, none moved in the last ${TRIAGE_FRESH_HOURS} hours. Run one of:`;
|
|
13677
14102
|
return listing("list", Object.freeze([]), rest, headline);
|
|
13678
14103
|
}
|
|
13679
14104
|
function selectTriagePullRequests(input) {
|
|
@@ -13683,24 +14108,24 @@ function selectTriagePullRequests(input) {
|
|
|
13683
14108
|
return plain("branch", Object.freeze([current]), `assessing #${current.number}, the pull request open on ${current.headRefName}`);
|
|
13684
14109
|
}
|
|
13685
14110
|
const candidates = byNumber(input.candidates);
|
|
13686
|
-
const
|
|
14111
|
+
const count2 = candidates.length;
|
|
13687
14112
|
const lines2 = candidates.map((pull) => skipped(pull, now, resolve13));
|
|
13688
|
-
if (resolve13 &&
|
|
13689
|
-
return refusal6(lines2,
|
|
13690
|
-
if (
|
|
14113
|
+
if (resolve13 && count2 > 1)
|
|
14114
|
+
return refusal6(lines2, count2);
|
|
14115
|
+
if (count2 === 0) {
|
|
13691
14116
|
return plain("none", Object.freeze([]), "no red pull request to triage, and no pull request on the current branch");
|
|
13692
14117
|
}
|
|
13693
|
-
if (
|
|
14118
|
+
if (count2 === 1) {
|
|
13694
14119
|
const only = candidates[0];
|
|
13695
14120
|
return plain("single", Object.freeze([only]), `1 red: assessing #${only.number}, last moved ${describeAge(only.updatedAt, now)}`);
|
|
13696
14121
|
}
|
|
13697
|
-
if (
|
|
13698
|
-
return listReading(lines2,
|
|
14122
|
+
if (count2 > MAX_TRIAGE_CANDIDATES)
|
|
14123
|
+
return listReading(lines2, count2);
|
|
13699
14124
|
const fresh = candidates.filter((pull) => isFreshCandidate(pull.updatedAt, now));
|
|
13700
14125
|
const stale = lines2.filter((one) => !one.fresh);
|
|
13701
14126
|
if (fresh.length === 0)
|
|
13702
|
-
return listReading(stale,
|
|
13703
|
-
return someReading(Object.freeze(fresh), stale,
|
|
14127
|
+
return listReading(stale, count2);
|
|
14128
|
+
return someReading(Object.freeze(fresh), stale, count2);
|
|
13704
14129
|
}
|
|
13705
14130
|
|
|
13706
14131
|
// src/pr/triage/conflict.ts
|
|
@@ -13719,10 +14144,10 @@ function parseMessages(records, from) {
|
|
|
13719
14144
|
const countRecord = records[at];
|
|
13720
14145
|
if (countRecord === undefined || countRecord === "")
|
|
13721
14146
|
break;
|
|
13722
|
-
const
|
|
13723
|
-
if (!Number.isInteger(
|
|
14147
|
+
const count2 = Number(countRecord);
|
|
14148
|
+
if (!Number.isInteger(count2) || count2 < 0)
|
|
13724
14149
|
break;
|
|
13725
|
-
const typeAt = at + 1 +
|
|
14150
|
+
const typeAt = at + 1 + count2;
|
|
13726
14151
|
const messageAt = typeAt + 1;
|
|
13727
14152
|
const type = records[typeAt];
|
|
13728
14153
|
const message = records[messageAt];
|
|
@@ -13901,10 +14326,10 @@ function noChecksLines2(reading, assessment) {
|
|
|
13901
14326
|
return [];
|
|
13902
14327
|
const number = reading.detail.number;
|
|
13903
14328
|
const unchecked = readUnchecked(number, workflowCountOf2(reading) ?? null);
|
|
13904
|
-
const [
|
|
14329
|
+
const [count2 = "", warning = ""] = unchecked.warning;
|
|
13905
14330
|
const yes = unchecked.yesMayAnswer ? "--yes may answer it" : "--yes is refused, so a person must answer it";
|
|
13906
14331
|
return [
|
|
13907
|
-
`${INDENT7}Workflows: ${
|
|
14332
|
+
`${INDENT7}Workflows: ${count2}`,
|
|
13908
14333
|
`${INDENT7}Warning: ${warning}`,
|
|
13909
14334
|
`${INDENT7}To merge it anyway: ${skipChecksCommand(number)} (it asks first; ${yes})`
|
|
13910
14335
|
];
|
|
@@ -14020,8 +14445,8 @@ function withTaskBudget(plan2, usd) {
|
|
|
14020
14445
|
}
|
|
14021
14446
|
|
|
14022
14447
|
// src/pr/plans/load.ts
|
|
14023
|
-
import { existsSync as
|
|
14024
|
-
import { dirname as dirname5, join as
|
|
14448
|
+
import { existsSync as existsSync11, readFileSync as readFileSync20 } from "fs";
|
|
14449
|
+
import { dirname as dirname5, join as join26 } from "path";
|
|
14025
14450
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
14026
14451
|
var MODULE_DIR2 = dirname5(fileURLToPath2(import.meta.url));
|
|
14027
14452
|
var PINNED_PLANS_DIRNAME = "plans";
|
|
@@ -14038,8 +14463,8 @@ function hasPinnedPlan(triageClass) {
|
|
|
14038
14463
|
function pinnedPlanCandidates(moduleDir, triageClass) {
|
|
14039
14464
|
const name2 = pinnedPlanFileName(triageClass);
|
|
14040
14465
|
return [
|
|
14041
|
-
|
|
14042
|
-
|
|
14466
|
+
join26(moduleDir, PINNED_PLANS_DIRNAME, name2),
|
|
14467
|
+
join26(moduleDir, name2)
|
|
14043
14468
|
];
|
|
14044
14469
|
}
|
|
14045
14470
|
function readPinnedPlan(triageClass, lookup = {}) {
|
|
@@ -14047,11 +14472,11 @@ function readPinnedPlan(triageClass, lookup = {}) {
|
|
|
14047
14472
|
throw new Error(`No pinned resolve plan ships for ${triageClass}; ` + `one ships for ${PINNED_PLAN_CLASSES.join(", ")}`);
|
|
14048
14473
|
}
|
|
14049
14474
|
const candidates = pinnedPlanCandidates(lookup.moduleDir ?? MODULE_DIR2, triageClass);
|
|
14050
|
-
const found = candidates.find((candidate) =>
|
|
14475
|
+
const found = candidates.find((candidate) => existsSync11(candidate));
|
|
14051
14476
|
if (found === undefined) {
|
|
14052
14477
|
throw new Error(`The pinned resolve plan for ${triageClass} is missing: ` + `no file at ${candidates.join(" or ")}`);
|
|
14053
14478
|
}
|
|
14054
|
-
return
|
|
14479
|
+
return readFileSync20(found, "utf8");
|
|
14055
14480
|
}
|
|
14056
14481
|
var SLOT_PATTERN = /\{[A-Z][A-Z0-9_]*\}/g;
|
|
14057
14482
|
var CONFLICT_SENTENCE_SLOT = "CONFLICT_SENTENCE";
|
|
@@ -14192,19 +14617,19 @@ function readAttemptRepeat(input) {
|
|
|
14192
14617
|
|
|
14193
14618
|
// src/commands/pr/resolve-loop.ts
|
|
14194
14619
|
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
14195
|
-
import { join as
|
|
14196
|
-
var RESOLVE_SUBDIR =
|
|
14620
|
+
import { join as join27 } from "path";
|
|
14621
|
+
var RESOLVE_SUBDIR = join27(".rafa", "resolve");
|
|
14197
14622
|
var RUN_PREFIX = "pr-";
|
|
14198
14623
|
var ATTEMPT_PREFIX = "attempt-";
|
|
14199
14624
|
var LOOP_WORDS = Object.freeze(["loop", "start"]);
|
|
14200
14625
|
var CHILD_OUTPUT = "text";
|
|
14201
14626
|
function resolveRunDir(home, number, attempt3) {
|
|
14202
|
-
return
|
|
14627
|
+
return join27(home, RESOLVE_SUBDIR, `${RUN_PREFIX}${number}`, `${ATTEMPT_PREFIX}${attempt3}`);
|
|
14203
14628
|
}
|
|
14204
14629
|
function writeResolvePlan(write) {
|
|
14205
14630
|
const dir = resolveRunDir(write.home, write.number, write.attempt);
|
|
14206
14631
|
mkdirSync7(dir, { recursive: true });
|
|
14207
|
-
const path =
|
|
14632
|
+
const path = join27(dir, pinnedPlanFileName(write.triageClass));
|
|
14208
14633
|
writeFileSync6(path, write.plan, "utf8");
|
|
14209
14634
|
return path;
|
|
14210
14635
|
}
|
|
@@ -14563,7 +14988,7 @@ async function runAttempts(run, detail, first) {
|
|
|
14563
14988
|
}
|
|
14564
14989
|
|
|
14565
14990
|
// src/commands/pr/triage.ts
|
|
14566
|
-
var
|
|
14991
|
+
var USAGE28 = PR_USAGE.triage;
|
|
14567
14992
|
var DEFAULT_MAX_ATTEMPTS = 2;
|
|
14568
14993
|
var DETACHED_HEAD2 = "HEAD";
|
|
14569
14994
|
var RESOLVED = false;
|
|
@@ -14573,11 +14998,11 @@ function readMaxAttempts(flags, usage2) {
|
|
|
14573
14998
|
if (value === undefined)
|
|
14574
14999
|
return DEFAULT_MAX_ATTEMPTS;
|
|
14575
15000
|
const spelled = typeof value === "boolean" ? "--max-attempts with no value" : `"${value}"`;
|
|
14576
|
-
const
|
|
14577
|
-
if (!Number.isSafeInteger(
|
|
15001
|
+
const count2 = typeof value === "string" ? Number(value) : Number.NaN;
|
|
15002
|
+
if (!Number.isSafeInteger(count2) || count2 < 1) {
|
|
14578
15003
|
throw lineRefusal3(`${spelled} is no attempt count, which is a whole number from 1;` + " type the pull request number before the flags", usage2);
|
|
14579
15004
|
}
|
|
14580
|
-
return
|
|
15005
|
+
return count2;
|
|
14581
15006
|
}
|
|
14582
15007
|
function readBranchOrNone(pr, warn) {
|
|
14583
15008
|
const fallback = "so the red pull requests are the candidates";
|
|
@@ -14619,7 +15044,7 @@ async function selectTargets(pr, warn, now, resolve13) {
|
|
|
14619
15044
|
async function detailOf6(pr, number) {
|
|
14620
15045
|
const detail = await onProvider(`read pull request #${number}`, () => pr.pulls.get(number));
|
|
14621
15046
|
if (detail === null) {
|
|
14622
|
-
throw lineRefusal3(`No pull request #${number} in the repository at ${pr.project.root}`,
|
|
15047
|
+
throw lineRefusal3(`No pull request #${number} in the repository at ${pr.project.root}`, USAGE28);
|
|
14623
15048
|
}
|
|
14624
15049
|
return detail;
|
|
14625
15050
|
}
|
|
@@ -14714,14 +15139,14 @@ async function assessOne(options) {
|
|
|
14714
15139
|
function refuseResolveWithoutComment(wantsResolve, wantsComment) {
|
|
14715
15140
|
if (!wantsResolve || wantsComment)
|
|
14716
15141
|
return;
|
|
14717
|
-
throw lineRefusal3("--resolve writes the attempt count into the triage comment, and --no-comment writes none;" + " run one or the other",
|
|
15142
|
+
throw lineRefusal3("--resolve writes the attempt count into the triage comment, and --no-comment writes none;" + " run one or the other", USAGE28);
|
|
14718
15143
|
}
|
|
14719
15144
|
async function runTriage(context, seams) {
|
|
14720
|
-
const wantsComment = readBooleanFlag(context.flags, "comment",
|
|
14721
|
-
const wantsResolve = readBooleanFlag(context.flags, "resolve",
|
|
14722
|
-
const maxAttempts = readMaxAttempts(context.flags,
|
|
15145
|
+
const wantsComment = readBooleanFlag(context.flags, "comment", USAGE28, true);
|
|
15146
|
+
const wantsResolve = readBooleanFlag(context.flags, "resolve", USAGE28, false);
|
|
15147
|
+
const maxAttempts = readMaxAttempts(context.flags, USAGE28);
|
|
14723
15148
|
refuseResolveWithoutComment(wantsResolve, wantsComment);
|
|
14724
|
-
const asked = readPullArgument(context.args,
|
|
15149
|
+
const asked = readPullArgument(context.args, USAGE28);
|
|
14725
15150
|
const pr = openPrContext(context, seams);
|
|
14726
15151
|
const git = (seams.git ?? createGitRunner)(pr.project.root);
|
|
14727
15152
|
const permissions = (seams.permissions ?? ghPermissionsIn)(pr.project.root);
|
|
@@ -14854,7 +15279,7 @@ function createPrTriageCommand(seams = DEFAULT_TRIAGE_SEAMS) {
|
|
|
14854
15279
|
var triage_default = createPrTriageCommand();
|
|
14855
15280
|
|
|
14856
15281
|
// src/commands/pr/view.ts
|
|
14857
|
-
var
|
|
15282
|
+
var USAGE29 = PR_USAGE.view;
|
|
14858
15283
|
function trimmedOrNull3(value) {
|
|
14859
15284
|
const trimmed = value.trim();
|
|
14860
15285
|
return trimmed === "" ? null : trimmed;
|
|
@@ -14864,9 +15289,9 @@ function renderView(number, url) {
|
|
|
14864
15289
|
return url === null ? opened : `${opened}${SEPARATOR}${url}`;
|
|
14865
15290
|
}
|
|
14866
15291
|
async function runView2(context, seams) {
|
|
14867
|
-
const asked = readPullArgument(context.args,
|
|
15292
|
+
const asked = readPullArgument(context.args, USAGE29);
|
|
14868
15293
|
const pr = openPrContext(context, seams);
|
|
14869
|
-
const pick = await pickPullRequest(pr, asked,
|
|
15294
|
+
const pick = await pickPullRequest(pr, asked, USAGE29);
|
|
14870
15295
|
await onProvider(`open pull request #${pick.number} in the browser`, () => pr.pulls.browse(pick.number));
|
|
14871
15296
|
const url = trimmedOrNull3(pick.summary?.url ?? "");
|
|
14872
15297
|
return {
|
|
@@ -14921,7 +15346,7 @@ function createPrViewCommand(seams = DEFAULT_PR_SEAMS) {
|
|
|
14921
15346
|
var view_default = createPrViewCommand();
|
|
14922
15347
|
|
|
14923
15348
|
// src/commands/pr/wait.ts
|
|
14924
|
-
var
|
|
15349
|
+
var USAGE30 = PR_USAGE.wait;
|
|
14925
15350
|
var TIMEOUT_FLAG = "timeout";
|
|
14926
15351
|
var DEFAULT_WAIT_TIMEOUT_MIN = DEFAULT_CI_TIMEOUT_MIN;
|
|
14927
15352
|
var MS_PER_MIN = 60000;
|
|
@@ -14987,10 +15412,10 @@ function renderWait(reading) {
|
|
|
14987
15412
|
`);
|
|
14988
15413
|
}
|
|
14989
15414
|
async function runWait(context, seams) {
|
|
14990
|
-
const minutes = readTimeoutMinutes(context.flags,
|
|
14991
|
-
const asked = readPullArgument(context.args,
|
|
15415
|
+
const minutes = readTimeoutMinutes(context.flags, USAGE30);
|
|
15416
|
+
const asked = readPullArgument(context.args, USAGE30);
|
|
14992
15417
|
const pr = openPrContext(context, seams);
|
|
14993
|
-
const pick = await pickPullRequest(pr, asked,
|
|
15418
|
+
const pick = await pickPullRequest(pr, asked, USAGE30);
|
|
14994
15419
|
const timeoutMs = minutes * MS_PER_MIN;
|
|
14995
15420
|
context.output.info(`Waiting for the checks of #${pick.number}, up to ${minutes} min.`);
|
|
14996
15421
|
const waited = await waitForChecks({
|
|
@@ -15069,8 +15494,8 @@ function createPrWaitCommand(seams = DEFAULT_WAIT_SEAMS) {
|
|
|
15069
15494
|
var wait_default = createPrWaitCommand();
|
|
15070
15495
|
|
|
15071
15496
|
// src/commands/release/status.ts
|
|
15072
|
-
import { existsSync as
|
|
15073
|
-
import { join as
|
|
15497
|
+
import { existsSync as existsSync12, readdirSync as readdirSync6, readFileSync as readFileSync21 } from "fs";
|
|
15498
|
+
import { join as join28 } from "path";
|
|
15074
15499
|
var RELEASE_STATUS_USAGE = "rafa release status [--plan=<stub>]";
|
|
15075
15500
|
var UNREADABLE3 = "?";
|
|
15076
15501
|
var NOTHING = "none";
|
|
@@ -15171,13 +15596,13 @@ function untaggedVersions(released, tags) {
|
|
|
15171
15596
|
}
|
|
15172
15597
|
function textOf2(path) {
|
|
15173
15598
|
try {
|
|
15174
|
-
return
|
|
15599
|
+
return readFileSync21(path, "utf8");
|
|
15175
15600
|
} catch {
|
|
15176
15601
|
return null;
|
|
15177
15602
|
}
|
|
15178
15603
|
}
|
|
15179
15604
|
function readVersionFile(root, configured) {
|
|
15180
|
-
const resolved =
|
|
15605
|
+
const resolved = join28(root, configured);
|
|
15181
15606
|
const text = textOf2(resolved);
|
|
15182
15607
|
if (text === null) {
|
|
15183
15608
|
return { path: configured, version: null, problem: `the version file could not be read at ${resolved}` };
|
|
@@ -15194,7 +15619,7 @@ function readTags(git) {
|
|
|
15194
15619
|
return { tags, latest: tags[0] ?? null, problem: null };
|
|
15195
15620
|
}
|
|
15196
15621
|
function readUntagged(root, configured, tags) {
|
|
15197
|
-
const resolved =
|
|
15622
|
+
const resolved = join28(root, configured);
|
|
15198
15623
|
const text = textOf2(resolved);
|
|
15199
15624
|
if (text === null) {
|
|
15200
15625
|
return {
|
|
@@ -15213,11 +15638,11 @@ function readBranch2(git) {
|
|
|
15213
15638
|
return result.ok && branch !== "" && branch !== "HEAD" ? branch : null;
|
|
15214
15639
|
}
|
|
15215
15640
|
function planStubsUnder(root, planDir) {
|
|
15216
|
-
const resolved =
|
|
15217
|
-
if (!
|
|
15641
|
+
const resolved = join28(root, planDir);
|
|
15642
|
+
if (!existsSync12(resolved))
|
|
15218
15643
|
return [];
|
|
15219
15644
|
try {
|
|
15220
|
-
return planStubsFromFileNames(
|
|
15645
|
+
return planStubsFromFileNames(readdirSync6(resolved));
|
|
15221
15646
|
} catch {
|
|
15222
15647
|
return [];
|
|
15223
15648
|
}
|
|
@@ -15285,10 +15710,10 @@ function levelPhrase(level) {
|
|
|
15285
15710
|
function pendingCell(reading) {
|
|
15286
15711
|
if (reading.stub === null)
|
|
15287
15712
|
return `${NOTHING}: no current plan`;
|
|
15288
|
-
const
|
|
15713
|
+
const count2 = reading.notes.length === 1 ? "1 note" : `${reading.notes.length} notes`;
|
|
15289
15714
|
if (reading.notes.length === 0)
|
|
15290
15715
|
return `${NOTHING} for ${reading.stub}`;
|
|
15291
|
-
return `${
|
|
15716
|
+
return `${count2} for ${reading.stub}, ${levelPhrase(reading.level ?? "none")}`;
|
|
15292
15717
|
}
|
|
15293
15718
|
function pendingLines(reading) {
|
|
15294
15719
|
return renderNoteLines(groupChangeNotes(reading.notes));
|
|
@@ -15415,8 +15840,8 @@ function createReleaseStatusCommand(seams = DEFAULT_RELEASE_SEAMS) {
|
|
|
15415
15840
|
var status_default2 = createReleaseStatusCommand();
|
|
15416
15841
|
|
|
15417
15842
|
// src/commands/release/tag.ts
|
|
15418
|
-
import { readFileSync as
|
|
15419
|
-
import { join as
|
|
15843
|
+
import { readFileSync as readFileSync22 } from "fs";
|
|
15844
|
+
import { join as join29 } from "path";
|
|
15420
15845
|
var RELEASE_TAG_USAGE = "rafa release tag";
|
|
15421
15846
|
var DEFAULT_RELEASE_BRANCH = "main";
|
|
15422
15847
|
var DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
@@ -15435,13 +15860,13 @@ function readBranch3(git) {
|
|
|
15435
15860
|
}
|
|
15436
15861
|
function textOf3(path) {
|
|
15437
15862
|
try {
|
|
15438
|
-
return
|
|
15863
|
+
return readFileSync22(path, "utf8");
|
|
15439
15864
|
} catch {
|
|
15440
15865
|
return null;
|
|
15441
15866
|
}
|
|
15442
15867
|
}
|
|
15443
15868
|
function readVersion(root, configured) {
|
|
15444
|
-
const resolved =
|
|
15869
|
+
const resolved = join29(root, configured);
|
|
15445
15870
|
const text = textOf3(resolved);
|
|
15446
15871
|
if (text === null) {
|
|
15447
15872
|
return {
|
|
@@ -15455,7 +15880,7 @@ function readVersion(root, configured) {
|
|
|
15455
15880
|
return version2 === null ? { path: configured, text, version: null, problem: `${resolved} declares no version this can read` } : { path: configured, text, version: version2, problem: null };
|
|
15456
15881
|
}
|
|
15457
15882
|
function readNewestRelease(root, configured) {
|
|
15458
|
-
const resolved =
|
|
15883
|
+
const resolved = join29(root, configured);
|
|
15459
15884
|
const text = textOf3(resolved);
|
|
15460
15885
|
if (text === null) {
|
|
15461
15886
|
return { path: configured, version: null, problem: `the changelog could not be read at ${resolved}` };
|
|
@@ -15676,14 +16101,14 @@ var roadmap_default = createRoadmapCommand();
|
|
|
15676
16101
|
|
|
15677
16102
|
// src/commands/self-update.ts
|
|
15678
16103
|
var DEFAULT_SELF_UPDATE_SEAMS = Object.freeze({ build: runBuild });
|
|
15679
|
-
var
|
|
16104
|
+
var USAGE31 = "rafa self-update [--force]";
|
|
15680
16105
|
function readForceFlag(value) {
|
|
15681
16106
|
if (value === undefined || value === false || value === "false")
|
|
15682
16107
|
return false;
|
|
15683
16108
|
if (value === true || value === "true")
|
|
15684
16109
|
return true;
|
|
15685
16110
|
throw new CommandExit(1, `\u274C --force takes no value, and read "${value}" as one
|
|
15686
|
-
Usage: ${
|
|
16111
|
+
Usage: ${USAGE31}`);
|
|
15687
16112
|
}
|
|
15688
16113
|
function projectOf11(context) {
|
|
15689
16114
|
if (context.project === null)
|
|
@@ -15691,7 +16116,7 @@ function projectOf11(context) {
|
|
|
15691
16116
|
return context.project;
|
|
15692
16117
|
}
|
|
15693
16118
|
async function runSelfUpdate(context, seams) {
|
|
15694
|
-
expectNoArgument(context.args,
|
|
16119
|
+
expectNoArgument(context.args, USAGE31);
|
|
15695
16120
|
const force = readForceFlag(context.flags["force"]);
|
|
15696
16121
|
const project = projectOf11(context);
|
|
15697
16122
|
const info = (line) => context.output.info(line);
|
|
@@ -15749,17 +16174,17 @@ function createSelfUpdateCommand(seams = DEFAULT_SELF_UPDATE_SEAMS) {
|
|
|
15749
16174
|
var self_update_default = createSelfUpdateCommand();
|
|
15750
16175
|
|
|
15751
16176
|
// src/commands/skill/backfill.ts
|
|
15752
|
-
import { statSync as
|
|
15753
|
-
import { join as
|
|
16177
|
+
import { statSync as statSync14 } from "fs";
|
|
16178
|
+
import { join as join34, resolve as resolve15 } from "path";
|
|
15754
16179
|
|
|
15755
16180
|
// src/backfill/backup.ts
|
|
15756
|
-
import { copyFileSync as copyFileSync4, existsSync as
|
|
15757
|
-
import { dirname as dirname6, isAbsolute as isAbsolute8, join as
|
|
16181
|
+
import { copyFileSync as copyFileSync4, existsSync as existsSync13, mkdirSync as mkdirSync8 } from "fs";
|
|
16182
|
+
import { dirname as dirname6, isAbsolute as isAbsolute8, join as join31, relative as relative8, resolve as resolve13, sep as sep5 } from "path";
|
|
15758
16183
|
|
|
15759
16184
|
// src/backfill/proposal-file.ts
|
|
15760
|
-
import { readdirSync as
|
|
15761
|
-
import { join as
|
|
15762
|
-
var BACKFILL_PATH =
|
|
16185
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync23 } from "fs";
|
|
16186
|
+
import { join as join30 } from "path";
|
|
16187
|
+
var BACKFILL_PATH = join30(".rafa", "backfill");
|
|
15763
16188
|
var PROPOSAL_FILE_PREFIX = "proposals-";
|
|
15764
16189
|
var PROPOSAL_FILE_EXTENSION = ".yaml";
|
|
15765
16190
|
var SHA256_PATTERN = /^[0-9a-f]{64}$/;
|
|
@@ -15785,7 +16210,7 @@ var PROPOSAL_FILE_HEADER = [
|
|
|
15785
16210
|
].join(`
|
|
15786
16211
|
`);
|
|
15787
16212
|
function proposalPath(dir, batch) {
|
|
15788
|
-
return
|
|
16213
|
+
return join30(dir, `${PROPOSAL_FILE_PREFIX}${String(batch).padStart(2, "0")}${PROPOSAL_FILE_EXTENSION}`);
|
|
15789
16214
|
}
|
|
15790
16215
|
function isProposalFileName(name2) {
|
|
15791
16216
|
return name2.startsWith(PROPOSAL_FILE_PREFIX) && name2.endsWith(PROPOSAL_FILE_EXTENSION);
|
|
@@ -15916,14 +16341,14 @@ function parseProposalFile(text) {
|
|
|
15916
16341
|
function readProposalFiles(dir) {
|
|
15917
16342
|
let names;
|
|
15918
16343
|
try {
|
|
15919
|
-
names =
|
|
16344
|
+
names = readdirSync7(dir);
|
|
15920
16345
|
} catch {
|
|
15921
16346
|
return [];
|
|
15922
16347
|
}
|
|
15923
16348
|
return names.filter((name2) => isProposalFileName(name2)).sort((left, right) => left.localeCompare(right)).map((name2) => {
|
|
15924
|
-
const path =
|
|
16349
|
+
const path = join30(dir, name2);
|
|
15925
16350
|
try {
|
|
15926
|
-
return { path, result: parseProposalFile(
|
|
16351
|
+
return { path, result: parseProposalFile(readFileSync23(path, "utf8")) };
|
|
15927
16352
|
} catch (error) {
|
|
15928
16353
|
return { path, result: unreadable(`the file cannot be read (${error instanceof Error ? error.message : String(error)})`) };
|
|
15929
16354
|
}
|
|
@@ -15931,10 +16356,10 @@ function readProposalFiles(dir) {
|
|
|
15931
16356
|
}
|
|
15932
16357
|
|
|
15933
16358
|
// src/backfill/backup.ts
|
|
15934
|
-
var BACKUP_PATH =
|
|
16359
|
+
var BACKUP_PATH = join31(BACKFILL_PATH, "backup");
|
|
15935
16360
|
var BACKUP_KINDS = ["copied", "kept", "tracked", "outside", "missing"];
|
|
15936
16361
|
function backupDirectory(base) {
|
|
15937
|
-
return
|
|
16362
|
+
return join31(base, BACKUP_PATH);
|
|
15938
16363
|
}
|
|
15939
16364
|
function isUnder(root, path) {
|
|
15940
16365
|
const step = relative8(resolve13(root), resolve13(path));
|
|
@@ -15943,7 +16368,7 @@ function isUnder(root, path) {
|
|
|
15943
16368
|
function backupPathFor(path, root, backupDir) {
|
|
15944
16369
|
if (!isUnder(root, path))
|
|
15945
16370
|
return null;
|
|
15946
|
-
return
|
|
16371
|
+
return join31(backupDir, relative8(resolve13(root), resolve13(path)));
|
|
15947
16372
|
}
|
|
15948
16373
|
function backupFile(path, options) {
|
|
15949
16374
|
if (options.repoRoot !== null && isUnder(options.repoRoot, path)) {
|
|
@@ -15952,9 +16377,9 @@ function backupFile(path, options) {
|
|
|
15952
16377
|
const backup = backupPathFor(path, options.root, options.backupDir);
|
|
15953
16378
|
if (backup === null)
|
|
15954
16379
|
return { path, kind: "outside", backup: null };
|
|
15955
|
-
if (!
|
|
16380
|
+
if (!existsSync13(path))
|
|
15956
16381
|
return { path, kind: "missing", backup: null };
|
|
15957
|
-
if (
|
|
16382
|
+
if (existsSync13(backup))
|
|
15958
16383
|
return { path, kind: "kept", backup };
|
|
15959
16384
|
mkdirSync8(dirname6(backup), { recursive: true });
|
|
15960
16385
|
copyFileSync4(path, backup);
|
|
@@ -15979,348 +16404,8 @@ function countBackups(results) {
|
|
|
15979
16404
|
}
|
|
15980
16405
|
|
|
15981
16406
|
// src/backfill/derive.ts
|
|
15982
|
-
import { readFileSync as
|
|
16407
|
+
import { readFileSync as readFileSync24, writeFileSync as writeFileSync7 } from "fs";
|
|
15983
16408
|
import { basename as basename8 } from "path";
|
|
15984
|
-
|
|
15985
|
-
// src/demote/classify.ts
|
|
15986
|
-
var LEARNED_DIRECTORY = "learned";
|
|
15987
|
-
var ORIGIN_FIELD = "origin";
|
|
15988
|
-
var DEMOTION_VERDICTS = [
|
|
15989
|
-
"observation",
|
|
15990
|
-
"procedure",
|
|
15991
|
-
"unclassified"
|
|
15992
|
-
];
|
|
15993
|
-
var RULE_VERDICTS = Object.freeze({
|
|
15994
|
-
"no-frontmatter": "unclassified",
|
|
15995
|
-
"learned-without-origin": "unclassified",
|
|
15996
|
-
"no-section-pair": "unclassified",
|
|
15997
|
-
"repeated-section": "procedure",
|
|
15998
|
-
"numbered-procedure": "procedure",
|
|
15999
|
-
"referenced-script": "procedure",
|
|
16000
|
-
"observation-shape": "observation"
|
|
16001
|
-
});
|
|
16002
|
-
var PROCEDURE_STEP_MINIMUM = 3;
|
|
16003
|
-
var PROBLEM_HEADING = /^(?:the\s+)?problem\b/i;
|
|
16004
|
-
var SOLUTION_HEADING = /^(?:the\s+)?solution\b/i;
|
|
16005
|
-
var WHEN_TO_USE_HEADING = /^when\s+to\s+use\b/i;
|
|
16006
|
-
var EXTRACTED_LINE = /^\s*\*\*Extracted:\*\*\s*(\S+)/m;
|
|
16007
|
-
var SCRIPT_EXTENSIONS = [
|
|
16008
|
-
".sh",
|
|
16009
|
-
".bash",
|
|
16010
|
-
".zsh",
|
|
16011
|
-
".py",
|
|
16012
|
-
".rb",
|
|
16013
|
-
".pl",
|
|
16014
|
-
".js",
|
|
16015
|
-
".mjs",
|
|
16016
|
-
".cjs",
|
|
16017
|
-
".ts"
|
|
16018
|
-
];
|
|
16019
|
-
var FENCE_LINE = /^ {0,3}(?:```|~~~)/;
|
|
16020
|
-
var HEADING_LINE = /^ {0,3}(#{1,6})\s+(.*)$/;
|
|
16021
|
-
var NUMBERED_ITEM = /^ {0,3}\d+[.)]\s+\S/;
|
|
16022
|
-
var CONTINUATION = /^ {2,}\S/;
|
|
16023
|
-
var DEMOTED_SOURCE = "demoted";
|
|
16024
|
-
var DEMOTED_CONFIDENCE = 0.5;
|
|
16025
|
-
var DEMOTED_KIND = "gotcha";
|
|
16026
|
-
var DEMOTED_SIGNAL = "silent";
|
|
16027
|
-
var DEMOTED_USAGE_COUNT = 1;
|
|
16028
|
-
var EVIDENCE_PATH_KEY = "path";
|
|
16029
|
-
var EVIDENCE_EXTRACTED_KEY = "extracted";
|
|
16030
|
-
var DEFAULT_DOMAIN = "general-best-practices";
|
|
16031
|
-
var DOMAIN_MARKERS = [
|
|
16032
|
-
["security", /\b(?:security|secret|credential|token|auth|csrf|xss|injection)\b/i],
|
|
16033
|
-
["testing", /\b(?:test|tests|testing|spec|assertion|assert|mock|stub|fixture|coverage)\b/i],
|
|
16034
|
-
["git", /\b(?:git|commit|rebase|worktree|branch|merge|checkout)\b/i],
|
|
16035
|
-
["debugging", /\b(?:debug|debugging|traceback|reproduce|bisect)\b/i],
|
|
16036
|
-
["code-style", /\b(?:lint|eslint|format|formatter|prettier|naming)\b/i],
|
|
16037
|
-
["workflow", /\b(?:workflow|pipeline|release|deploy|build|ci|loop|dispatch)\b/i]
|
|
16038
|
-
];
|
|
16039
|
-
var ERROR_WORDS = /\b(?:error|exception|traceback|panic|fatal|refused|denied|not found|no such|cannot|could not|failed|failure|unexpected|undefined)\b/i;
|
|
16040
|
-
var SENTENCE_ABBREVIATIONS = ["e.g", "i.e", "vs", "cf", "etc"];
|
|
16041
|
-
function isFilled(value) {
|
|
16042
|
-
return typeof value === "string" && value.trim() !== "";
|
|
16043
|
-
}
|
|
16044
|
-
function bodyLines2(body) {
|
|
16045
|
-
return body.split(`
|
|
16046
|
-
`).map((line) => line.replace(/\r$/, ""));
|
|
16047
|
-
}
|
|
16048
|
-
function bodySections(body) {
|
|
16049
|
-
const lines2 = bodyLines2(body);
|
|
16050
|
-
const headings = [];
|
|
16051
|
-
let fenced = false;
|
|
16052
|
-
lines2.forEach((line, index) => {
|
|
16053
|
-
if (FENCE_LINE.test(line)) {
|
|
16054
|
-
fenced = !fenced;
|
|
16055
|
-
return;
|
|
16056
|
-
}
|
|
16057
|
-
if (fenced)
|
|
16058
|
-
return;
|
|
16059
|
-
const match = HEADING_LINE.exec(line);
|
|
16060
|
-
if (match === null)
|
|
16061
|
-
return;
|
|
16062
|
-
const [, hashes = "", title = ""] = match;
|
|
16063
|
-
headings.push({
|
|
16064
|
-
level: hashes.length,
|
|
16065
|
-
title: title.replace(/\s+#+\s*$/, "").trim(),
|
|
16066
|
-
index
|
|
16067
|
-
});
|
|
16068
|
-
});
|
|
16069
|
-
return headings.map((heading, position) => {
|
|
16070
|
-
const next = headings.slice(position + 1).find((other) => other.level <= heading.level);
|
|
16071
|
-
const end = next === undefined ? lines2.length : next.index;
|
|
16072
|
-
return {
|
|
16073
|
-
level: heading.level,
|
|
16074
|
-
title: heading.title,
|
|
16075
|
-
line: heading.index + 1,
|
|
16076
|
-
text: lines2.slice(heading.index + 1, end).join(`
|
|
16077
|
-
`).trim()
|
|
16078
|
-
};
|
|
16079
|
-
});
|
|
16080
|
-
}
|
|
16081
|
-
function numberedRuns(body) {
|
|
16082
|
-
const runs = [];
|
|
16083
|
-
let fenced = false;
|
|
16084
|
-
let steps = 0;
|
|
16085
|
-
let line = 0;
|
|
16086
|
-
const close = () => {
|
|
16087
|
-
if (steps > 0)
|
|
16088
|
-
runs.push({ steps, line });
|
|
16089
|
-
steps = 0;
|
|
16090
|
-
};
|
|
16091
|
-
bodyLines2(body).forEach((text, index) => {
|
|
16092
|
-
if (FENCE_LINE.test(text)) {
|
|
16093
|
-
fenced = !fenced;
|
|
16094
|
-
return;
|
|
16095
|
-
}
|
|
16096
|
-
if (fenced || text.trim() === "")
|
|
16097
|
-
return;
|
|
16098
|
-
if (NUMBERED_ITEM.test(text)) {
|
|
16099
|
-
if (steps === 0)
|
|
16100
|
-
line = index + 1;
|
|
16101
|
-
steps += 1;
|
|
16102
|
-
return;
|
|
16103
|
-
}
|
|
16104
|
-
if (CONTINUATION.test(text))
|
|
16105
|
-
return;
|
|
16106
|
-
close();
|
|
16107
|
-
});
|
|
16108
|
-
close();
|
|
16109
|
-
return runs;
|
|
16110
|
-
}
|
|
16111
|
-
function isScriptPath(token) {
|
|
16112
|
-
if (token.startsWith("/"))
|
|
16113
|
-
return false;
|
|
16114
|
-
const last = token.split("/").at(-1) ?? "";
|
|
16115
|
-
return SCRIPT_EXTENSIONS.some((extension) => last.endsWith(extension));
|
|
16116
|
-
}
|
|
16117
|
-
function referencedScript(body, entries = []) {
|
|
16118
|
-
const owned = new Set(entries.map((entry) => entry.replace(/^\.\//, "")));
|
|
16119
|
-
for (const reference of collectReferences(body)) {
|
|
16120
|
-
if (reference.kind !== "path")
|
|
16121
|
-
continue;
|
|
16122
|
-
const token = reference.text.replace(/^\.\//, "");
|
|
16123
|
-
const [first = ""] = token.split("/");
|
|
16124
|
-
if (owned.has(first) && isScriptPath(token))
|
|
16125
|
-
return reference;
|
|
16126
|
-
}
|
|
16127
|
-
return null;
|
|
16128
|
-
}
|
|
16129
|
-
function isLearnedFile(path) {
|
|
16130
|
-
const [first] = path.split(/[\\/]/);
|
|
16131
|
-
return first === LEARNED_DIRECTORY;
|
|
16132
|
-
}
|
|
16133
|
-
function decide2(rule, reason) {
|
|
16134
|
-
return { verdict: RULE_VERDICTS[rule], rule, reason };
|
|
16135
|
-
}
|
|
16136
|
-
function pairReason(problems, solutions) {
|
|
16137
|
-
if (problems === 0 && solutions === 0) {
|
|
16138
|
-
return "the body carries no Problem section and no Solution section";
|
|
16139
|
-
}
|
|
16140
|
-
return problems === 0 ? "the body carries a Solution section and no Problem section, so there is no pair" : "the body carries a Problem section and no Solution section, so there is no pair";
|
|
16141
|
-
}
|
|
16142
|
-
function repeatReason(problems, solutions) {
|
|
16143
|
-
const counted3 = problems > 1 ? `${problems} Problem sections` : `${solutions} Solution sections`;
|
|
16144
|
-
return `the body carries ${counted3}, and an observation carries exactly one of each`;
|
|
16145
|
-
}
|
|
16146
|
-
function classifySkill(source) {
|
|
16147
|
-
const document = readFrontmatterDocument(source.text);
|
|
16148
|
-
if (document === null) {
|
|
16149
|
-
return decide2("no-frontmatter", "the file opens with no --- block holding a YAML mapping");
|
|
16150
|
-
}
|
|
16151
|
-
if (isLearnedFile(source.path) && !isFilled(document.data[ORIGIN_FIELD])) {
|
|
16152
|
-
return decide2("learned-without-origin", `a ${LEARNED_DIRECTORY}/ file whose frontmatter carries no ${ORIGIN_FIELD}`);
|
|
16153
|
-
}
|
|
16154
|
-
const sections = bodySections(document.body);
|
|
16155
|
-
const problems = sections.filter((section) => PROBLEM_HEADING.test(section.title));
|
|
16156
|
-
const solutions = sections.filter((section) => SOLUTION_HEADING.test(section.title));
|
|
16157
|
-
if (problems.length === 0 || solutions.length === 0) {
|
|
16158
|
-
return decide2("no-section-pair", pairReason(problems.length, solutions.length));
|
|
16159
|
-
}
|
|
16160
|
-
if (problems.length > 1 || solutions.length > 1) {
|
|
16161
|
-
return decide2("repeated-section", repeatReason(problems.length, solutions.length));
|
|
16162
|
-
}
|
|
16163
|
-
const run = numberedRuns(document.body).find((each) => each.steps >= PROCEDURE_STEP_MINIMUM);
|
|
16164
|
-
if (run !== undefined) {
|
|
16165
|
-
return decide2("numbered-procedure", `a numbered procedure of ${run.steps} consecutive steps at line ${run.line}`);
|
|
16166
|
-
}
|
|
16167
|
-
const script = referencedScript(document.body, source.entries ?? []);
|
|
16168
|
-
if (script !== null) {
|
|
16169
|
-
return decide2("referenced-script", `the body names the script ${script.text} at line ${script.line}`);
|
|
16170
|
-
}
|
|
16171
|
-
return decide2("observation-shape", "one Problem section and one Solution section, no numbered procedure of " + `${PROCEDURE_STEP_MINIMUM} or more consecutive steps, and no referenced script`);
|
|
16172
|
-
}
|
|
16173
|
-
function firstSection(sections, heading) {
|
|
16174
|
-
return sections.find((section) => heading.test(section.title));
|
|
16175
|
-
}
|
|
16176
|
-
function withoutFences(text) {
|
|
16177
|
-
const kept = [];
|
|
16178
|
-
let fenced = false;
|
|
16179
|
-
for (const line of bodyLines2(text)) {
|
|
16180
|
-
if (FENCE_LINE.test(line)) {
|
|
16181
|
-
fenced = !fenced;
|
|
16182
|
-
continue;
|
|
16183
|
-
}
|
|
16184
|
-
if (!fenced)
|
|
16185
|
-
kept.push(line);
|
|
16186
|
-
}
|
|
16187
|
-
return kept;
|
|
16188
|
-
}
|
|
16189
|
-
function stripMarkers(line) {
|
|
16190
|
-
return line.replace(/^\s*(?:[-*+]|\d+[.)]|>)\s+/, "").replace(/\*\*|__|\*|`/g, "").trim();
|
|
16191
|
-
}
|
|
16192
|
-
function isAbbreviation(text, index) {
|
|
16193
|
-
const before = text.slice(0, index);
|
|
16194
|
-
return SENTENCE_ABBREVIATIONS.some((word) => before.toLowerCase().endsWith(word));
|
|
16195
|
-
}
|
|
16196
|
-
function firstSentence(text) {
|
|
16197
|
-
const lines2 = withoutFences(text);
|
|
16198
|
-
const start2 = lines2.findIndex((line) => line.trim() !== "");
|
|
16199
|
-
if (start2 === -1)
|
|
16200
|
-
return "";
|
|
16201
|
-
const collected = [lines2[start2] ?? ""];
|
|
16202
|
-
for (const line of lines2.slice(start2 + 1)) {
|
|
16203
|
-
if (line.trim() === "" || /^\s*(?:[-*+]|\d+[.)]|#)/.test(line))
|
|
16204
|
-
break;
|
|
16205
|
-
collected.push(line);
|
|
16206
|
-
}
|
|
16207
|
-
const joined2 = stripMarkers(collected.join(" ")).replace(/\s+/g, " ");
|
|
16208
|
-
for (let index = 0;index < joined2.length; index += 1) {
|
|
16209
|
-
if (!".!?".includes(joined2[index] ?? ""))
|
|
16210
|
-
continue;
|
|
16211
|
-
if (isAbbreviation(joined2, index))
|
|
16212
|
-
continue;
|
|
16213
|
-
const next = joined2[index + 1];
|
|
16214
|
-
if (next === undefined)
|
|
16215
|
-
return joined2.slice(0, index + 1).trim();
|
|
16216
|
-
if (next === " ")
|
|
16217
|
-
return joined2.slice(0, index + 1).trim();
|
|
16218
|
-
}
|
|
16219
|
-
return joined2.trim();
|
|
16220
|
-
}
|
|
16221
|
-
function demotedTrigger(sections, data) {
|
|
16222
|
-
const whenToUse = firstSection(sections, WHEN_TO_USE_HEADING);
|
|
16223
|
-
const fromSection = whenToUse === undefined ? "" : firstSentence(whenToUse.text);
|
|
16224
|
-
if (fromSection !== "")
|
|
16225
|
-
return fromSection;
|
|
16226
|
-
const description = data["description"];
|
|
16227
|
-
if (isFilled(description))
|
|
16228
|
-
return description.trim();
|
|
16229
|
-
const problem2 = firstSection(sections, PROBLEM_HEADING);
|
|
16230
|
-
return problem2 === undefined ? "" : firstSentence(problem2.text);
|
|
16231
|
-
}
|
|
16232
|
-
function extractedDate(body) {
|
|
16233
|
-
const match = EXTRACTED_LINE.exec(body);
|
|
16234
|
-
return match?.[1] ?? null;
|
|
16235
|
-
}
|
|
16236
|
-
function errorString(body) {
|
|
16237
|
-
let fenced = false;
|
|
16238
|
-
for (const line of bodyLines2(body)) {
|
|
16239
|
-
if (FENCE_LINE.test(line)) {
|
|
16240
|
-
fenced = !fenced;
|
|
16241
|
-
continue;
|
|
16242
|
-
}
|
|
16243
|
-
if (fenced) {
|
|
16244
|
-
if (ERROR_WORDS.test(line))
|
|
16245
|
-
return line.trim();
|
|
16246
|
-
continue;
|
|
16247
|
-
}
|
|
16248
|
-
for (const span of line.matchAll(/`([^`\n]+)`/g)) {
|
|
16249
|
-
const inner = span[1] ?? "";
|
|
16250
|
-
if (ERROR_WORDS.test(inner))
|
|
16251
|
-
return inner.trim();
|
|
16252
|
-
}
|
|
16253
|
-
}
|
|
16254
|
-
return null;
|
|
16255
|
-
}
|
|
16256
|
-
function inferDomain(name2, body) {
|
|
16257
|
-
for (const [domain, pattern] of DOMAIN_MARKERS) {
|
|
16258
|
-
if (pattern.test(name2))
|
|
16259
|
-
return domain;
|
|
16260
|
-
}
|
|
16261
|
-
for (const [domain, pattern] of DOMAIN_MARKERS) {
|
|
16262
|
-
if (pattern.test(body))
|
|
16263
|
-
return domain;
|
|
16264
|
-
}
|
|
16265
|
-
return DEFAULT_DOMAIN;
|
|
16266
|
-
}
|
|
16267
|
-
function slugifyId(value) {
|
|
16268
|
-
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
16269
|
-
}
|
|
16270
|
-
function uniqueInstinctId(base, taken) {
|
|
16271
|
-
const used = new Set(taken);
|
|
16272
|
-
if (!used.has(base))
|
|
16273
|
-
return base;
|
|
16274
|
-
let suffix = 2;
|
|
16275
|
-
while (used.has(`${base}-${suffix}`))
|
|
16276
|
-
suffix += 1;
|
|
16277
|
-
return `${base}-${suffix}`;
|
|
16278
|
-
}
|
|
16279
|
-
function sourceName(source, data) {
|
|
16280
|
-
const declared = data["name"];
|
|
16281
|
-
if (isFilled(declared))
|
|
16282
|
-
return slugifyId(declared);
|
|
16283
|
-
const segments = source.path.split(/[\\/]/).filter((segment) => segment !== "");
|
|
16284
|
-
const last = segments.at(-1) ?? "";
|
|
16285
|
-
const stem = last.toUpperCase() === "SKILL.MD" ? segments.at(-2) ?? "" : last.replace(/\.md$/i, "");
|
|
16286
|
-
return slugifyId(stem);
|
|
16287
|
-
}
|
|
16288
|
-
function convertToInstinct(source, options) {
|
|
16289
|
-
const document = readFrontmatterDocument(source.text);
|
|
16290
|
-
const data = document?.data ?? {};
|
|
16291
|
-
const body = document?.body ?? source.text;
|
|
16292
|
-
const sections = bodySections(body);
|
|
16293
|
-
const action = firstSection(sections, SOLUTION_HEADING)?.text ?? "";
|
|
16294
|
-
const named3 = errorString(body);
|
|
16295
|
-
const name2 = sourceName(source, data);
|
|
16296
|
-
const draft = {
|
|
16297
|
-
id: uniqueInstinctId(name2, options.takenIds ?? []),
|
|
16298
|
-
trigger: demotedTrigger(sections, data),
|
|
16299
|
-
kind: DEMOTED_KIND,
|
|
16300
|
-
domain: inferDomain(name2, body),
|
|
16301
|
-
confidence: DEMOTED_CONFIDENCE,
|
|
16302
|
-
usageCount: DEMOTED_USAGE_COUNT,
|
|
16303
|
-
artifact: null,
|
|
16304
|
-
signal: named3 === null ? DEMOTED_SIGNAL : "loud",
|
|
16305
|
-
scope: options.scope,
|
|
16306
|
-
projectId: options.projectId ?? null,
|
|
16307
|
-
source: DEMOTED_SOURCE,
|
|
16308
|
-
evidence: [{
|
|
16309
|
-
[EVIDENCE_PATH_KEY]: source.path,
|
|
16310
|
-
[EVIDENCE_EXTRACTED_KEY]: extractedDate(body) ?? options.mtime
|
|
16311
|
-
}],
|
|
16312
|
-
createdAt: options.now,
|
|
16313
|
-
updatedAt: options.now,
|
|
16314
|
-
action,
|
|
16315
|
-
cause: firstSection(sections, PROBLEM_HEADING)?.text ?? "",
|
|
16316
|
-
actionHash: actionHash(action)
|
|
16317
|
-
};
|
|
16318
|
-
const text = writeInstinct(draft);
|
|
16319
|
-
const parsed = parseInstinct(text);
|
|
16320
|
-
return parsed.instinct === null ? { instinct: null, text: null, issues: parsed.issues } : { instinct: parsed.instinct, text, issues: parsed.issues };
|
|
16321
|
-
}
|
|
16322
|
-
|
|
16323
|
-
// src/backfill/derive.ts
|
|
16324
16409
|
var DERIVED_FIELDS = ["stack", "paths", "when_to_use"];
|
|
16325
16410
|
function renderWhenToUse(trigger, prevents) {
|
|
16326
16411
|
return `${sentenceEnded(trigger)} Prevents: ${prevents}`;
|
|
@@ -16445,7 +16530,7 @@ function planFile(entry, options) {
|
|
|
16445
16530
|
}
|
|
16446
16531
|
let original;
|
|
16447
16532
|
try {
|
|
16448
|
-
original =
|
|
16533
|
+
original = readFileSync24(entry.path, "utf8");
|
|
16449
16534
|
} catch (error) {
|
|
16450
16535
|
return inert(entry, "skipped", `the file cannot be read (${error instanceof Error ? error.message : String(error)})`);
|
|
16451
16536
|
}
|
|
@@ -16489,7 +16574,7 @@ function planDerivation(root, options) {
|
|
|
16489
16574
|
function applyFile(action, options) {
|
|
16490
16575
|
if (action.text === null || action.original === null)
|
|
16491
16576
|
return action;
|
|
16492
|
-
const current =
|
|
16577
|
+
const current = readFileSync24(action.path, "utf8");
|
|
16493
16578
|
if (current !== action.original) {
|
|
16494
16579
|
return {
|
|
16495
16580
|
...action,
|
|
@@ -16520,7 +16605,7 @@ function applyDerivation(plan2, options) {
|
|
|
16520
16605
|
}
|
|
16521
16606
|
|
|
16522
16607
|
// src/backfill/proposal-batch.ts
|
|
16523
|
-
import { readFileSync as
|
|
16608
|
+
import { readFileSync as readFileSync25 } from "fs";
|
|
16524
16609
|
import { relative as relative9 } from "path";
|
|
16525
16610
|
|
|
16526
16611
|
// src/demote/report.ts
|
|
@@ -16819,7 +16904,7 @@ function readCandidate(root, entry) {
|
|
|
16819
16904
|
return null;
|
|
16820
16905
|
let text;
|
|
16821
16906
|
try {
|
|
16822
|
-
text =
|
|
16907
|
+
text = readFileSync25(entry.path, "utf8");
|
|
16823
16908
|
} catch {
|
|
16824
16909
|
return null;
|
|
16825
16910
|
}
|
|
@@ -16925,8 +17010,8 @@ function parseSessionAnswer(stdout) {
|
|
|
16925
17010
|
}
|
|
16926
17011
|
|
|
16927
17012
|
// src/backfill/propose.ts
|
|
16928
|
-
import { mkdirSync as mkdirSync9, readFileSync as
|
|
16929
|
-
import { join as
|
|
17013
|
+
import { mkdirSync as mkdirSync9, readFileSync as readFileSync26, writeFileSync as writeFileSync8 } from "fs";
|
|
17014
|
+
import { join as join32, resolve as resolve14 } from "path";
|
|
16930
17015
|
var PROPOSAL_ACTION_KINDS = [
|
|
16931
17016
|
"applied",
|
|
16932
17017
|
"unchanged",
|
|
@@ -16947,47 +17032,47 @@ function unanswered(candidate, note) {
|
|
|
16947
17032
|
note
|
|
16948
17033
|
};
|
|
16949
17034
|
}
|
|
16950
|
-
function answerProblem(needs,
|
|
17035
|
+
function answerProblem(needs, answer2) {
|
|
16951
17036
|
if (needs.includes("prevents")) {
|
|
16952
|
-
if (
|
|
17037
|
+
if (answer2.prevents === null)
|
|
16953
17038
|
return "the answer carries no prevents";
|
|
16954
|
-
if (
|
|
17039
|
+
if (answer2.signal === null)
|
|
16955
17040
|
return "the answer carries prevents with no signal";
|
|
16956
|
-
if (!isSkillSignal(
|
|
16957
|
-
return `the answer signal ${
|
|
17041
|
+
if (!isSkillSignal(answer2.signal)) {
|
|
17042
|
+
return `the answer signal ${answer2.signal} is neither ${SKILL_SIGNALS.join(" nor ")}`;
|
|
16958
17043
|
}
|
|
16959
17044
|
}
|
|
16960
|
-
if (needs.includes("trigger") &&
|
|
17045
|
+
if (needs.includes("trigger") && answer2.trigger === null) {
|
|
16961
17046
|
return "the answer carries no trigger sentence";
|
|
16962
17047
|
}
|
|
16963
17048
|
if (needs.includes("description")) {
|
|
16964
|
-
if (
|
|
17049
|
+
if (answer2.description === null)
|
|
16965
17050
|
return "the answer carries no description";
|
|
16966
|
-
if (countCharacters(
|
|
16967
|
-
return `the answer description is ${countCharacters(
|
|
17051
|
+
if (countCharacters(answer2.description) >= DESCRIPTION_LIMIT) {
|
|
17052
|
+
return `the answer description is ${countCharacters(answer2.description)} characters, at or over the ${DESCRIPTION_LIMIT} cap`;
|
|
16968
17053
|
}
|
|
16969
17054
|
}
|
|
16970
17055
|
return null;
|
|
16971
17056
|
}
|
|
16972
|
-
function proposalRow(candidate,
|
|
16973
|
-
if (
|
|
17057
|
+
function proposalRow(candidate, answer2) {
|
|
17058
|
+
if (answer2 === undefined) {
|
|
16974
17059
|
return unanswered(candidate, "the session answered nothing about this file");
|
|
16975
17060
|
}
|
|
16976
|
-
const problem2 = answerProblem(candidate.needs,
|
|
17061
|
+
const problem2 = answerProblem(candidate.needs, answer2);
|
|
16977
17062
|
if (problem2 !== null)
|
|
16978
17063
|
return unanswered(candidate, problem2);
|
|
16979
17064
|
const wants = (need) => candidate.needs.includes(need);
|
|
16980
|
-
const signal =
|
|
17065
|
+
const signal = answer2.signal;
|
|
16981
17066
|
return {
|
|
16982
17067
|
path: candidate.relative,
|
|
16983
17068
|
name: candidate.name,
|
|
16984
17069
|
hash: candidate.hash,
|
|
16985
17070
|
needs: candidate.needs,
|
|
16986
17071
|
status: ANSWERED_ROW,
|
|
16987
|
-
prevents: wants("prevents") ?
|
|
17072
|
+
prevents: wants("prevents") ? answer2.prevents : null,
|
|
16988
17073
|
signal: wants("prevents") && signal !== null && isSkillSignal(signal) ? signal : null,
|
|
16989
|
-
trigger: wants("trigger") ?
|
|
16990
|
-
description: wants("description") ?
|
|
17074
|
+
trigger: wants("trigger") ? answer2.trigger : null,
|
|
17075
|
+
description: wants("description") ? answer2.description : null,
|
|
16991
17076
|
note: null
|
|
16992
17077
|
};
|
|
16993
17078
|
}
|
|
@@ -17075,7 +17160,7 @@ function writeRow(path, row2, original, options) {
|
|
|
17075
17160
|
};
|
|
17076
17161
|
}
|
|
17077
17162
|
function applyRow(root, row2, options) {
|
|
17078
|
-
const path =
|
|
17163
|
+
const path = join32(root, ...row2.path.split("/"));
|
|
17079
17164
|
if (row2.status === UNANSWERED_ROW) {
|
|
17080
17165
|
return inert2(path, row2, "unanswered", row2.note ?? "the row carries no answer");
|
|
17081
17166
|
}
|
|
@@ -17084,7 +17169,7 @@ function applyRow(root, row2, options) {
|
|
|
17084
17169
|
}
|
|
17085
17170
|
let original;
|
|
17086
17171
|
try {
|
|
17087
|
-
original =
|
|
17172
|
+
original = readFileSync26(path, "utf8");
|
|
17088
17173
|
} catch {
|
|
17089
17174
|
return inert2(path, row2, "refused", "the file the row names is gone");
|
|
17090
17175
|
}
|
|
@@ -17112,7 +17197,7 @@ function applyProposals(files, root, options) {
|
|
|
17112
17197
|
for (const file of files) {
|
|
17113
17198
|
const refusal7 = fileRefusal(file, root);
|
|
17114
17199
|
for (const row2 of file.rows) {
|
|
17115
|
-
const action = refusal7 === null ? applyRow(root, row2, options) : inert2(
|
|
17200
|
+
const action = refusal7 === null ? applyRow(root, row2, options) : inert2(join32(root, ...row2.path.split("/")), row2, "refused", refusal7);
|
|
17116
17201
|
actions.push(action);
|
|
17117
17202
|
if (row2.trigger !== null && action.kind !== "refused" && action.kind !== "unanswered") {
|
|
17118
17203
|
triggers[row2.name] = row2.trigger;
|
|
@@ -17123,13 +17208,13 @@ function applyProposals(files, root, options) {
|
|
|
17123
17208
|
}
|
|
17124
17209
|
|
|
17125
17210
|
// src/demote/select.ts
|
|
17126
|
-
import { readdirSync as
|
|
17127
|
-
import { basename as basename9, dirname as dirname7, join as
|
|
17211
|
+
import { readdirSync as readdirSync8, readFileSync as readFileSync27, realpathSync as realpathSync6, statSync as statSync13 } from "fs";
|
|
17212
|
+
import { basename as basename9, dirname as dirname7, join as join33 } from "path";
|
|
17128
17213
|
var SKILLS_DIRECTORY = "skills";
|
|
17129
17214
|
var CLAUDE_DIRECTORY = ".claude";
|
|
17130
17215
|
var AUTO_EXTRACTED_ORIGIN = "auto-extracted";
|
|
17131
|
-
var DEMOTED_PATH =
|
|
17132
|
-
var INSTINCTS_PATH =
|
|
17216
|
+
var DEMOTED_PATH = join33(".rafa", "demoted");
|
|
17217
|
+
var INSTINCTS_PATH = join33(".rafa", "instincts");
|
|
17133
17218
|
var REPORT_FILE = "report.md";
|
|
17134
17219
|
function realOrSelf(path) {
|
|
17135
17220
|
try {
|
|
@@ -17152,19 +17237,19 @@ function resolveDemotionScope(dir, home) {
|
|
|
17152
17237
|
if (basename9(claude) !== CLAUDE_DIRECTORY)
|
|
17153
17238
|
return null;
|
|
17154
17239
|
const base = dirname7(claude);
|
|
17155
|
-
const demotedDir =
|
|
17240
|
+
const demotedDir = join33(base, DEMOTED_PATH);
|
|
17156
17241
|
return {
|
|
17157
17242
|
scope: samePath(base, home) ? "user" : "project",
|
|
17158
17243
|
dir,
|
|
17159
17244
|
base,
|
|
17160
17245
|
demotedDir,
|
|
17161
|
-
instinctsDir:
|
|
17162
|
-
reportPath:
|
|
17246
|
+
instinctsDir: join33(base, INSTINCTS_PATH),
|
|
17247
|
+
reportPath: join33(demotedDir, REPORT_FILE)
|
|
17163
17248
|
};
|
|
17164
17249
|
}
|
|
17165
17250
|
function entryKind(path) {
|
|
17166
17251
|
try {
|
|
17167
|
-
const stats =
|
|
17252
|
+
const stats = statSync13(path);
|
|
17168
17253
|
if (stats.isFile())
|
|
17169
17254
|
return "file";
|
|
17170
17255
|
if (stats.isDirectory())
|
|
@@ -17176,7 +17261,7 @@ function entryKind(path) {
|
|
|
17176
17261
|
}
|
|
17177
17262
|
function visibleNames(dir) {
|
|
17178
17263
|
try {
|
|
17179
|
-
return
|
|
17264
|
+
return readdirSync8(dir).filter((name2) => !name2.startsWith(".")).sort((left, right) => left.localeCompare(right));
|
|
17180
17265
|
} catch {
|
|
17181
17266
|
return [];
|
|
17182
17267
|
}
|
|
@@ -17185,8 +17270,8 @@ function readSelected(absolute, path, entries) {
|
|
|
17185
17270
|
let text;
|
|
17186
17271
|
let mtime;
|
|
17187
17272
|
try {
|
|
17188
|
-
text =
|
|
17189
|
-
mtime =
|
|
17273
|
+
text = readFileSync27(absolute, "utf8");
|
|
17274
|
+
mtime = statSync13(absolute).mtime.toISOString();
|
|
17190
17275
|
} catch {
|
|
17191
17276
|
return null;
|
|
17192
17277
|
}
|
|
@@ -17201,14 +17286,14 @@ function selectsLearned(text, scope) {
|
|
|
17201
17286
|
return origin === AUTO_EXTRACTED_ORIGIN;
|
|
17202
17287
|
}
|
|
17203
17288
|
function selectLearned(scope) {
|
|
17204
|
-
const dir =
|
|
17289
|
+
const dir = join33(scope.dir, LEARNED_DIRECTORY);
|
|
17205
17290
|
if (entryKind(dir) !== "directory")
|
|
17206
17291
|
return [];
|
|
17207
17292
|
const selected = [];
|
|
17208
17293
|
for (const name2 of visibleNames(dir)) {
|
|
17209
17294
|
if (!name2.endsWith(MARKDOWN_EXTENSION) || name2 === MARKDOWN_EXTENSION)
|
|
17210
17295
|
continue;
|
|
17211
|
-
const absolute =
|
|
17296
|
+
const absolute = join33(dir, name2);
|
|
17212
17297
|
if (entryKind(absolute) !== "file")
|
|
17213
17298
|
continue;
|
|
17214
17299
|
const file = readSelected(absolute, `${LEARNED_DIRECTORY}/${name2}`, undefined);
|
|
@@ -17222,10 +17307,10 @@ function selectSkillFiles(scope) {
|
|
|
17222
17307
|
for (const name2 of visibleNames(scope.dir)) {
|
|
17223
17308
|
if (name2 === LEARNED_DIRECTORY)
|
|
17224
17309
|
continue;
|
|
17225
|
-
const skillDir =
|
|
17310
|
+
const skillDir = join33(scope.dir, name2);
|
|
17226
17311
|
if (entryKind(skillDir) !== "directory")
|
|
17227
17312
|
continue;
|
|
17228
|
-
const absolute =
|
|
17313
|
+
const absolute = join33(skillDir, SKILL_FILE);
|
|
17229
17314
|
if (entryKind(absolute) !== "file")
|
|
17230
17315
|
continue;
|
|
17231
17316
|
const file = readSelected(absolute, `${name2}/${SKILL_FILE}`, visibleNames(skillDir));
|
|
@@ -17286,7 +17371,7 @@ Usage: ${BACKFILL_USAGE}`);
|
|
|
17286
17371
|
}
|
|
17287
17372
|
function isDirectory3(path) {
|
|
17288
17373
|
try {
|
|
17289
|
-
return
|
|
17374
|
+
return statSync14(path).isDirectory();
|
|
17290
17375
|
} catch {
|
|
17291
17376
|
return false;
|
|
17292
17377
|
}
|
|
@@ -17410,7 +17495,7 @@ function reviewedProposals(dir) {
|
|
|
17410
17495
|
return read2.flatMap((entry) => entry.result.file === null ? [] : [entry.result.file]);
|
|
17411
17496
|
}
|
|
17412
17497
|
function rowPaths(files, root) {
|
|
17413
|
-
return files.flatMap((file) => file.status !== REVIEWED_FILE ? [] : file.rows.filter((row2) => row2.status === ANSWERED_ROW).map((row2) =>
|
|
17498
|
+
return files.flatMap((file) => file.status !== REVIEWED_FILE ? [] : file.rows.filter((row2) => row2.status === ANSWERED_ROW).map((row2) => join34(root, ...row2.path.split("/"))));
|
|
17414
17499
|
}
|
|
17415
17500
|
function runApply(context, scope, backfillDir, projectRoot3) {
|
|
17416
17501
|
const options = checkOptions(context, projectRoot3);
|
|
@@ -17456,7 +17541,7 @@ async function runBackfill(context, seams) {
|
|
|
17456
17541
|
const dir = readBackfillDirectory(context.args, seams.cwd);
|
|
17457
17542
|
const projectRoot3 = readConsumerRoot(context.flags["project"], seams.cwd);
|
|
17458
17543
|
const scope = backfillScopeOf(dir, projectOf12(context).home);
|
|
17459
|
-
const backfillDir =
|
|
17544
|
+
const backfillDir = join34(scope.base, BACKFILL_PATH);
|
|
17460
17545
|
if (mode === "propose") {
|
|
17461
17546
|
await runPropose(context, scope, backfillDir, seams);
|
|
17462
17547
|
return;
|
|
@@ -17570,13 +17655,13 @@ function createSkillCheckCommand(seams = DEFAULT_CHECK_SEAMS) {
|
|
|
17570
17655
|
var check_default3 = createSkillCheckCommand();
|
|
17571
17656
|
|
|
17572
17657
|
// src/commands/skill/demote.ts
|
|
17573
|
-
import { existsSync as
|
|
17658
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync11, readFileSync as readFileSync29, statSync as statSync16, writeFileSync as writeFileSync10 } from "fs";
|
|
17574
17659
|
import { dirname as dirname9, resolve as resolve16 } from "path";
|
|
17575
17660
|
|
|
17576
17661
|
// src/demote/apply.ts
|
|
17577
|
-
import { copyFileSync as copyFileSync5, existsSync as
|
|
17662
|
+
import { copyFileSync as copyFileSync5, existsSync as existsSync14, mkdirSync as mkdirSync10, mkdtempSync as mkdtempSync2, readdirSync as readdirSync9, readFileSync as readFileSync28, renameSync as renameSync3, rmSync as rmSync4, statSync as statSync15, unlinkSync, writeFileSync as writeFileSync9 } from "fs";
|
|
17578
17663
|
import { tmpdir as tmpdir2 } from "os";
|
|
17579
|
-
import { dirname as dirname8, join as
|
|
17664
|
+
import { dirname as dirname8, join as join35 } from "path";
|
|
17580
17665
|
var DEMOTION_ACTION_KINDS = [
|
|
17581
17666
|
"demoted",
|
|
17582
17667
|
"kept",
|
|
@@ -17597,18 +17682,18 @@ function learnedSkillName(path) {
|
|
|
17597
17682
|
return base.slice(0, -MARKDOWN_EXTENSION.length);
|
|
17598
17683
|
}
|
|
17599
17684
|
function sourcePath(scope, path) {
|
|
17600
|
-
return
|
|
17685
|
+
return join35(scope.dir, ...path.split("/"));
|
|
17601
17686
|
}
|
|
17602
17687
|
function demotedPath(scope, path) {
|
|
17603
|
-
return
|
|
17688
|
+
return join35(scope.demotedDir, ...path.split("/"));
|
|
17604
17689
|
}
|
|
17605
17690
|
function promotedPath(scope, path) {
|
|
17606
|
-
return
|
|
17691
|
+
return join35(scope.dir, learnedSkillName(path), SKILL_FILE);
|
|
17607
17692
|
}
|
|
17608
17693
|
function takenInstinctIds(dir) {
|
|
17609
17694
|
let names;
|
|
17610
17695
|
try {
|
|
17611
|
-
names =
|
|
17696
|
+
names = readdirSync9(dir);
|
|
17612
17697
|
} catch {
|
|
17613
17698
|
return [];
|
|
17614
17699
|
}
|
|
@@ -17616,7 +17701,7 @@ function takenInstinctIds(dir) {
|
|
|
17616
17701
|
}
|
|
17617
17702
|
function readText4(path) {
|
|
17618
17703
|
try {
|
|
17619
|
-
return
|
|
17704
|
+
return statSync15(path).isFile() ? readFileSync28(path, "utf8") : null;
|
|
17620
17705
|
} catch {
|
|
17621
17706
|
return null;
|
|
17622
17707
|
}
|
|
@@ -17644,7 +17729,7 @@ function planKept(scope, row2, file) {
|
|
|
17644
17729
|
return inert3(row2.path, "procedure", "kept", "procedure-shaped: left where it is, for the backfill");
|
|
17645
17730
|
}
|
|
17646
17731
|
const to = promotedPath(scope, row2.path);
|
|
17647
|
-
if (
|
|
17732
|
+
if (existsSync14(to) || existsSync14(dirname8(to))) {
|
|
17648
17733
|
return inert3(row2.path, "procedure", "refused", `kept as a skill, but ${to} is already there`);
|
|
17649
17734
|
}
|
|
17650
17735
|
return {
|
|
@@ -17658,7 +17743,7 @@ function planKept(scope, row2, file) {
|
|
|
17658
17743
|
};
|
|
17659
17744
|
}
|
|
17660
17745
|
function checkCandidate(dir, id, text, pathDirs) {
|
|
17661
|
-
const path =
|
|
17746
|
+
const path = join35(dir, `${id}${MARKDOWN_EXTENSION}`);
|
|
17662
17747
|
writeFileSync9(path, text, "utf8");
|
|
17663
17748
|
const report3 = checkFile(path, "instinct", { projectRoot: null, pathDirs });
|
|
17664
17749
|
return failureLines2(report3.issues);
|
|
@@ -17679,11 +17764,11 @@ function planDemoted(scope, row2, file, context) {
|
|
|
17679
17764
|
if (failures.length > 0) {
|
|
17680
17765
|
return inert3(row2.path, "observation", "refused", `the record ${id} fails the checker \u2014 ${failures.join("; ")}`);
|
|
17681
17766
|
}
|
|
17682
|
-
const destination =
|
|
17767
|
+
const destination = join35(scope.instinctsDir, `${id}${MARKDOWN_EXTENSION}`);
|
|
17683
17768
|
const kept = demotedPath(scope, row2.path);
|
|
17684
|
-
if (
|
|
17769
|
+
if (existsSync14(destination))
|
|
17685
17770
|
return inert3(row2.path, "observation", "refused", `${destination} is already there`);
|
|
17686
|
-
if (
|
|
17771
|
+
if (existsSync14(kept))
|
|
17687
17772
|
return inert3(row2.path, "observation", "refused", `${kept} is already there`);
|
|
17688
17773
|
context.taken.push(id);
|
|
17689
17774
|
return {
|
|
@@ -17715,7 +17800,7 @@ function planRow(scope, row2, context) {
|
|
|
17715
17800
|
entries: undefined,
|
|
17716
17801
|
absolute,
|
|
17717
17802
|
hash,
|
|
17718
|
-
mtime:
|
|
17803
|
+
mtime: statSync15(absolute).mtime.toISOString()
|
|
17719
17804
|
};
|
|
17720
17805
|
return verdict === "procedure" ? planKept(scope, row2, file) : planDemoted(scope, row2, file, context);
|
|
17721
17806
|
}
|
|
@@ -17726,19 +17811,19 @@ function countActions(actions) {
|
|
|
17726
17811
|
return counts;
|
|
17727
17812
|
}
|
|
17728
17813
|
function planDemotion(report3, scope, seams = DEFAULT_APPLY_SEAMS) {
|
|
17729
|
-
const temp = mkdtempSync2(
|
|
17814
|
+
const temp = mkdtempSync2(join35(tmpdir2(), "rafa-demote-"));
|
|
17730
17815
|
const taken = [...takenInstinctIds(scope.instinctsDir)];
|
|
17731
17816
|
try {
|
|
17732
17817
|
const actions = report3.rows.map((row2) => planRow(scope, row2, { temp, taken, seams }));
|
|
17733
17818
|
return { scope, actions, counts: countActions(actions) };
|
|
17734
17819
|
} finally {
|
|
17735
|
-
|
|
17820
|
+
rmSync4(temp, { recursive: true, force: true });
|
|
17736
17821
|
}
|
|
17737
17822
|
}
|
|
17738
17823
|
function moveFile(move3) {
|
|
17739
17824
|
mkdirSync10(dirname8(move3.to), { recursive: true });
|
|
17740
17825
|
try {
|
|
17741
|
-
|
|
17826
|
+
renameSync3(move3.from, move3.to);
|
|
17742
17827
|
} catch {
|
|
17743
17828
|
copyFileSync5(move3.from, move3.to);
|
|
17744
17829
|
unlinkSync(move3.from);
|
|
@@ -17817,7 +17902,7 @@ Usage: ${DEMOTE_USAGE}`);
|
|
|
17817
17902
|
}
|
|
17818
17903
|
function isDirectory4(path) {
|
|
17819
17904
|
try {
|
|
17820
|
-
return
|
|
17905
|
+
return statSync16(path).isDirectory();
|
|
17821
17906
|
} catch {
|
|
17822
17907
|
return false;
|
|
17823
17908
|
}
|
|
@@ -17845,9 +17930,9 @@ function verdictLine(report3) {
|
|
|
17845
17930
|
return `${String(report3.rows.length)} file(s): ${parts.join(", ")}`;
|
|
17846
17931
|
}
|
|
17847
17932
|
function previousReport(path, context) {
|
|
17848
|
-
if (!
|
|
17933
|
+
if (!existsSync15(path))
|
|
17849
17934
|
return null;
|
|
17850
|
-
const parsed = parseDemotionReport(
|
|
17935
|
+
const parsed = parseDemotionReport(readFileSync29(path, "utf8"));
|
|
17851
17936
|
if (parsed.report !== null)
|
|
17852
17937
|
return parsed.report;
|
|
17853
17938
|
context.output.warn(`the report already at ${path} does not parse, so no override in it is carried forward:` + ` ${parsed.issues.map((issue2) => `line ${String(issue2.line)} ${issue2.code}`).join(", ")}`);
|
|
@@ -17880,10 +17965,10 @@ function runWrite(context, scope) {
|
|
|
17880
17965
|
context.output.info(line);
|
|
17881
17966
|
}
|
|
17882
17967
|
function reviewedReport(scope) {
|
|
17883
|
-
if (!
|
|
17968
|
+
if (!existsSync15(scope.reportPath)) {
|
|
17884
17969
|
throw new CommandExit(1, `\u274C No report at ${scope.reportPath}; run "rafa skill demote ${scope.dir}" first, then review it`);
|
|
17885
17970
|
}
|
|
17886
|
-
const parsed = parseDemotionReport(
|
|
17971
|
+
const parsed = parseDemotionReport(readFileSync29(scope.reportPath, "utf8"));
|
|
17887
17972
|
if (parsed.report === null) {
|
|
17888
17973
|
const lines2 = parsed.issues.map((issue2) => ` line ${String(issue2.line)} ${issue2.code}: ${issue2.message}`);
|
|
17889
17974
|
throw new CommandExit(1, [`\u274C ${scope.reportPath} does not parse:`, ...lines2].join(`
|
|
@@ -17989,8 +18074,8 @@ var demote_default = createSkillDemoteCommand();
|
|
|
17989
18074
|
|
|
17990
18075
|
// src/commands/skill/show.ts
|
|
17991
18076
|
var DEFAULT_SKILL_SHOW_SEAMS = DEFAULT_SKILL_LIST_SEAMS;
|
|
17992
|
-
var
|
|
17993
|
-
var
|
|
18077
|
+
var COMMAND_NAME6 = "rafa skill show";
|
|
18078
|
+
var USAGE32 = "rafa skill show <name> [--full]";
|
|
17994
18079
|
function unknownSkillMessage(name2, inventory) {
|
|
17995
18080
|
const agent = findShown(inventory.records, "agent", name2);
|
|
17996
18081
|
const hint = agent === null ? [] : [`An agent is named "${name2}" (${agent.source}): rafa agent show ${name2}`];
|
|
@@ -17998,7 +18083,7 @@ function unknownSkillMessage(name2, inventory) {
|
|
|
17998
18083
|
`\u274C No skill is named "${name2}".`,
|
|
17999
18084
|
...hint,
|
|
18000
18085
|
"Run `rafa skill list` to see every skill and where it comes from.",
|
|
18001
|
-
`Usage: ${
|
|
18086
|
+
`Usage: ${USAGE32}`
|
|
18002
18087
|
].join(`
|
|
18003
18088
|
`);
|
|
18004
18089
|
}
|
|
@@ -18014,10 +18099,10 @@ function projectOf14(context) {
|
|
|
18014
18099
|
return context.project;
|
|
18015
18100
|
}
|
|
18016
18101
|
async function runShow3(context, seams) {
|
|
18017
|
-
const full = readSwitch("full", context.flags["full"], `Type the name ahead of it: ${
|
|
18018
|
-
const name2 = expectOneArgument(context.args,
|
|
18102
|
+
const full = readSwitch("full", context.flags["full"], `Type the name ahead of it: ${USAGE32}`);
|
|
18103
|
+
const name2 = expectOneArgument(context.args, USAGE32);
|
|
18019
18104
|
const project = projectOf14(context);
|
|
18020
|
-
const { inventory, settingSources } = await projectInventory(project, context, seams,
|
|
18105
|
+
const { inventory, settingSources } = await projectInventory(project, context, seams, COMMAND_NAME6);
|
|
18021
18106
|
const warnings = warningLines(inventory);
|
|
18022
18107
|
for (const warning of warnings)
|
|
18023
18108
|
context.output.warn(warning);
|
|
@@ -18095,8 +18180,8 @@ var VERDICT_WORDS = Object.freeze({
|
|
|
18095
18180
|
pending: "checks pending",
|
|
18096
18181
|
none: "no checks"
|
|
18097
18182
|
});
|
|
18098
|
-
function counted3(
|
|
18099
|
-
return `${String(
|
|
18183
|
+
function counted3(count2, noun, plural5 = `${noun}s`) {
|
|
18184
|
+
return `${String(count2)} ${count2 === 1 ? noun : plural5}`;
|
|
18100
18185
|
}
|
|
18101
18186
|
function info(text) {
|
|
18102
18187
|
return { level: "info", text };
|
|
@@ -18344,7 +18429,7 @@ async function readStatusSections(input, seams = {}) {
|
|
|
18344
18429
|
}
|
|
18345
18430
|
|
|
18346
18431
|
// src/commands/status.ts
|
|
18347
|
-
var
|
|
18432
|
+
var COMMAND_NAME7 = "rafa status";
|
|
18348
18433
|
var STATUS_USAGE = "rafa status";
|
|
18349
18434
|
var STATUS_ARGUMENT_EXIT = 2;
|
|
18350
18435
|
var DEFAULT_STATUS_SEAMS = Object.freeze({});
|
|
@@ -18356,13 +18441,13 @@ function projectOf15(context) {
|
|
|
18356
18441
|
function expectNoWord(args) {
|
|
18357
18442
|
if (args.length === 0)
|
|
18358
18443
|
return;
|
|
18359
|
-
throw new CommandExit(STATUS_ARGUMENT_EXIT, `\u274C ${
|
|
18444
|
+
throw new CommandExit(STATUS_ARGUMENT_EXIT, `\u274C ${COMMAND_NAME7}: expected no argument, got ${String(args.length)}: ${args.join(" ")}
|
|
18360
18445
|
Usage: ${STATUS_USAGE}`);
|
|
18361
18446
|
}
|
|
18362
18447
|
async function runStatus2(context, seams) {
|
|
18363
18448
|
expectNoWord(context.args);
|
|
18364
18449
|
const project = projectOf15(context);
|
|
18365
|
-
const config = resolveProjectConfig(project,
|
|
18450
|
+
const config = resolveProjectConfig(project, COMMAND_NAME7, (message) => {
|
|
18366
18451
|
context.output.warn(message);
|
|
18367
18452
|
});
|
|
18368
18453
|
const read3 = seams.read ?? ((input) => readStatusSections(input));
|
|
@@ -18428,11 +18513,11 @@ var CORE_SUBJECTS = Object.freeze([
|
|
|
18428
18513
|
{ name: "loop", summary: "start a plan; stop, pause, resume, show and list its sessions" },
|
|
18429
18514
|
{ name: "issue", summary: "the tracker: list, show, create, comment on and move issues; mark one ready, unblock it and check its references" },
|
|
18430
18515
|
{ name: "pr", summary: "the pull request of a branch: one line, in full or in the browser; list, wait on, merge and triage them" },
|
|
18431
|
-
{ name: "effort", summary: "collect session and commit rows; report per plan" },
|
|
18516
|
+
{ name: "effort", summary: "collect session and commit rows; report per plan; repair a store a newer rafa migrated" },
|
|
18432
18517
|
{ name: "module", summary: "list the configured modules; run an action a module provides" },
|
|
18433
18518
|
{ name: "agent", summary: "copy an agent definition into the project; list what a session sees" },
|
|
18434
18519
|
{ name: "skill", summary: "check a skills directory; list each tier; demote and backfill it" },
|
|
18435
|
-
{ name: "instinct", summary: "check an instincts directory; list and
|
|
18520
|
+
{ name: "instinct", summary: "check an instincts directory; list, show, flag and promote its records" },
|
|
18436
18521
|
{ name: "release", summary: "read the release state of the project; tag the release branch's HEAD" }
|
|
18437
18522
|
]);
|
|
18438
18523
|
var CORE_COMMANDS = Object.freeze([
|
|
@@ -18465,6 +18550,7 @@ var CORE_COMMANDS = Object.freeze([
|
|
|
18465
18550
|
triage_default,
|
|
18466
18551
|
collect_default,
|
|
18467
18552
|
report_default,
|
|
18553
|
+
fix_schema_default,
|
|
18468
18554
|
list_default7,
|
|
18469
18555
|
exec_default,
|
|
18470
18556
|
vendor_default,
|
|
@@ -18480,6 +18566,8 @@ var CORE_COMMANDS = Object.freeze([
|
|
|
18480
18566
|
check_default,
|
|
18481
18567
|
list_default4,
|
|
18482
18568
|
show_default2,
|
|
18569
|
+
flag_default,
|
|
18570
|
+
promote_default,
|
|
18483
18571
|
status_default2,
|
|
18484
18572
|
tag_default,
|
|
18485
18573
|
status_default3,
|