@open-tomato/rafa 0.9.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -8
- package/dist/cli.js +800 -505
- package/dist/index.js +401 -146
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1534,7 +1534,7 @@ function routeLine(registry, argv) {
|
|
|
1534
1534
|
|
|
1535
1535
|
// package.json
|
|
1536
1536
|
var name = "@open-tomato/rafa";
|
|
1537
|
-
var version = "0.9.
|
|
1537
|
+
var version = "0.9.2";
|
|
1538
1538
|
|
|
1539
1539
|
// src/cli/version.ts
|
|
1540
1540
|
var RAFA_VERSION = version;
|
|
@@ -3146,7 +3146,7 @@ var describeCommand2 = {
|
|
|
3146
3146
|
var describe_default = Object.freeze(describeCommand2);
|
|
3147
3147
|
|
|
3148
3148
|
// src/commands/doctor.ts
|
|
3149
|
-
import { basename as
|
|
3149
|
+
import { basename as basename6, relative, resolve as resolve8, sep as sep2 } from "path";
|
|
3150
3150
|
|
|
3151
3151
|
// src/adapters/tracker/issue-values.ts
|
|
3152
3152
|
var TYPE_MEMBERS = {
|
|
@@ -3508,8 +3508,8 @@ function createGithubTracker(options) {
|
|
|
3508
3508
|
}
|
|
3509
3509
|
|
|
3510
3510
|
// src/board/status.ts
|
|
3511
|
-
import { statSync as
|
|
3512
|
-
import { join as
|
|
3511
|
+
import { statSync as statSync6 } from "fs";
|
|
3512
|
+
import { join as join11 } from "path";
|
|
3513
3513
|
|
|
3514
3514
|
// src/board/naming.ts
|
|
3515
3515
|
import path from "path";
|
|
@@ -3842,8 +3842,8 @@ function withRoadmapIssue(text2, issue) {
|
|
|
3842
3842
|
}
|
|
3843
3843
|
|
|
3844
3844
|
// src/board/setup.ts
|
|
3845
|
-
import { existsSync as existsSync6, lstatSync, mkdirSync as
|
|
3846
|
-
import { dirname as dirname5, join as
|
|
3845
|
+
import { existsSync as existsSync6, lstatSync, mkdirSync as mkdirSync5, readFileSync as readFileSync9, statSync as statSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
3846
|
+
import { dirname as dirname5, join as join10 } from "path";
|
|
3847
3847
|
import { fileURLToPath } from "url";
|
|
3848
3848
|
|
|
3849
3849
|
// src/board/blocked.ts
|
|
@@ -5394,9 +5394,89 @@ async function enforceSpecReview(options) {
|
|
|
5394
5394
|
}
|
|
5395
5395
|
|
|
5396
5396
|
// src/board/issue.ts
|
|
5397
|
-
import { existsSync as existsSync5, mkdirSync as
|
|
5398
|
-
import { dirname as dirname4, resolve as
|
|
5399
|
-
|
|
5397
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync8, statSync as statSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
5398
|
+
import { dirname as dirname4, resolve as resolve5 } from "path";
|
|
5399
|
+
|
|
5400
|
+
// src/board/previous-copy.ts
|
|
5401
|
+
import { linkSync, mkdirSync as mkdirSync3, readdirSync as readdirSync2, statSync as statSync3, unlinkSync } from "fs";
|
|
5402
|
+
import { basename as basename4, extname, join as join9, resolve as resolve4 } from "path";
|
|
5403
|
+
var PREFIX3 = "board previous copy";
|
|
5404
|
+
var PREVIOUS_DIR_NAME = "previous";
|
|
5405
|
+
var PREVIOUS_COPY_WARN_ABOVE = 50;
|
|
5406
|
+
var COPY_EXTENSION = ".md";
|
|
5407
|
+
var FIRST_SUFFIX = 2;
|
|
5408
|
+
function previousDir(specsDir) {
|
|
5409
|
+
return join9(specsDir, PREVIOUS_DIR_NAME);
|
|
5410
|
+
}
|
|
5411
|
+
function two(value) {
|
|
5412
|
+
return String(value).padStart(2, "0");
|
|
5413
|
+
}
|
|
5414
|
+
function previousTimestamp(mtime) {
|
|
5415
|
+
const date = `${String(mtime.getUTCFullYear()).padStart(4, "0")}${two(mtime.getUTCMonth() + 1)}${two(mtime.getUTCDate())}`;
|
|
5416
|
+
const time = `${two(mtime.getUTCHours())}${two(mtime.getUTCMinutes())}${two(mtime.getUTCSeconds())}`;
|
|
5417
|
+
return `${date}T${time}Z`;
|
|
5418
|
+
}
|
|
5419
|
+
function previousCopyName(savedName, timestamp, attempt) {
|
|
5420
|
+
const stem = basename4(savedName, extname(savedName));
|
|
5421
|
+
const suffix = attempt < FIRST_SUFFIX ? "" : `-${String(attempt)}`;
|
|
5422
|
+
return `${stem}.${timestamp}${suffix}${COPY_EXTENSION}`;
|
|
5423
|
+
}
|
|
5424
|
+
function isTaken(error) {
|
|
5425
|
+
return error instanceof Error && "code" in error && error.code === "EEXIST";
|
|
5426
|
+
}
|
|
5427
|
+
function linkUnderFreeName(source, directory2, savedName, timestamp) {
|
|
5428
|
+
for (let attempt = 1;; attempt += 1) {
|
|
5429
|
+
const name2 = previousCopyName(savedName, timestamp, attempt);
|
|
5430
|
+
try {
|
|
5431
|
+
linkSync(source, join9(directory2, name2));
|
|
5432
|
+
return name2;
|
|
5433
|
+
} catch (error) {
|
|
5434
|
+
if (!isTaken(error))
|
|
5435
|
+
throw error;
|
|
5436
|
+
}
|
|
5437
|
+
}
|
|
5438
|
+
}
|
|
5439
|
+
function movePreviousCopy(options) {
|
|
5440
|
+
const { repoRoot, specsDir, path: path2 } = options;
|
|
5441
|
+
const source = resolve4(repoRoot, path2);
|
|
5442
|
+
const directory2 = previousDir(specsDir);
|
|
5443
|
+
const absoluteDirectory = resolve4(repoRoot, directory2);
|
|
5444
|
+
const savedName = basename4(path2);
|
|
5445
|
+
let name2;
|
|
5446
|
+
try {
|
|
5447
|
+
const timestamp = previousTimestamp(statSync3(source).mtime);
|
|
5448
|
+
mkdirSync3(absoluteDirectory, { recursive: true });
|
|
5449
|
+
name2 = linkUnderFreeName(source, absoluteDirectory, savedName, timestamp);
|
|
5450
|
+
} catch (error) {
|
|
5451
|
+
throw new Error(`${PREFIX3}: ${path2} could not be moved to ${directory2}: ${messageOf(error)}`, { cause: error });
|
|
5452
|
+
}
|
|
5453
|
+
const moved = {
|
|
5454
|
+
path: join9(directory2, name2),
|
|
5455
|
+
absolute: join9(absoluteDirectory, name2)
|
|
5456
|
+
};
|
|
5457
|
+
try {
|
|
5458
|
+
unlinkSync(source);
|
|
5459
|
+
} catch (error) {
|
|
5460
|
+
throw new Error(`${PREFIX3}: ${path2} was copied to ${moved.path} but could not be removed: ${messageOf(error)}`, { cause: error });
|
|
5461
|
+
}
|
|
5462
|
+
return moved;
|
|
5463
|
+
}
|
|
5464
|
+
function isMissing(error) {
|
|
5465
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
5466
|
+
}
|
|
5467
|
+
function countPreviousCopies(repoRoot, specsDir) {
|
|
5468
|
+
const directory2 = previousDir(specsDir);
|
|
5469
|
+
try {
|
|
5470
|
+
return readdirSync2(resolve4(repoRoot, directory2), { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(COPY_EXTENSION)).length;
|
|
5471
|
+
} catch (error) {
|
|
5472
|
+
if (isMissing(error))
|
|
5473
|
+
return 0;
|
|
5474
|
+
throw new Error(`${PREFIX3}: ${directory2} could not be read: ${messageOf(error)}`, { cause: error });
|
|
5475
|
+
}
|
|
5476
|
+
}
|
|
5477
|
+
|
|
5478
|
+
// src/board/issue.ts
|
|
5479
|
+
var PREFIX4 = "board issue";
|
|
5400
5480
|
var ISSUE_VIEW_FIELDS = "number,title,body,state,labels,author";
|
|
5401
5481
|
var SPEC_LABEL = "type:spec";
|
|
5402
5482
|
var ISSUE_REFUSAL_EXIT = 2;
|
|
@@ -5407,7 +5487,7 @@ function detailOf4(result, command) {
|
|
|
5407
5487
|
}
|
|
5408
5488
|
function issueNumber2(value) {
|
|
5409
5489
|
if (!Number.isSafeInteger(value) || value < 1) {
|
|
5410
|
-
throw new TypeError(`${
|
|
5490
|
+
throw new TypeError(`${PREFIX4}: the read refused issue number ${describeValue(value)}, expected a positive whole number`);
|
|
5411
5491
|
}
|
|
5412
5492
|
return String(value);
|
|
5413
5493
|
}
|
|
@@ -5442,11 +5522,11 @@ function readViewedIssue(stdout, number, command) {
|
|
|
5442
5522
|
try {
|
|
5443
5523
|
payload = JSON.parse(stdout);
|
|
5444
5524
|
} catch (error) {
|
|
5445
|
-
throw new Error(`${
|
|
5525
|
+
throw new Error(`${PREFIX4}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
|
|
5446
5526
|
}
|
|
5447
5527
|
const problem = viewProblem2(payload, number);
|
|
5448
5528
|
if (problem !== null)
|
|
5449
|
-
throw new Error(`${
|
|
5529
|
+
throw new Error(`${PREFIX4}: ${command} answered ${problem}`);
|
|
5450
5530
|
const checked = payload;
|
|
5451
5531
|
return {
|
|
5452
5532
|
number,
|
|
@@ -5464,7 +5544,7 @@ function createGhSpecIssueReader(options) {
|
|
|
5464
5544
|
const command = `gh issue view ${number} --json ${ISSUE_VIEW_FIELDS}`;
|
|
5465
5545
|
const result = await gh(["issue", "view", number, "--json", ISSUE_VIEW_FIELDS]);
|
|
5466
5546
|
if (!result.ok)
|
|
5467
|
-
throw new Error(`${
|
|
5547
|
+
throw new Error(`${PREFIX4}: ${detailOf4(result, command)}`);
|
|
5468
5548
|
return readViewedIssue(result.stdout, issue, command);
|
|
5469
5549
|
};
|
|
5470
5550
|
}
|
|
@@ -5502,10 +5582,91 @@ ${local}`
|
|
|
5502
5582
|
`)}
|
|
5503
5583
|
`;
|
|
5504
5584
|
}
|
|
5585
|
+
var NOTES_SEPARATOR = `
|
|
5586
|
+
|
|
5587
|
+
${LOCAL_NOTES_HEADING}
|
|
5588
|
+
|
|
5589
|
+
`;
|
|
5590
|
+
var HEADING = /^#{1,6}(?:[ \t]|$)/u;
|
|
5591
|
+
var FENCE2 = /^ {0,3}(?:`{3,}|~{3,})/u;
|
|
5592
|
+
function commonLines(before, after) {
|
|
5593
|
+
let previous = new Uint32Array(after.length + 1);
|
|
5594
|
+
for (const line of before) {
|
|
5595
|
+
const row2 = new Uint32Array(after.length + 1);
|
|
5596
|
+
after.forEach((other, index) => {
|
|
5597
|
+
row2[index + 1] = line === other ? (previous[index] ?? 0) + 1 : Math.max(previous[index + 1] ?? 0, row2[index] ?? 0);
|
|
5598
|
+
});
|
|
5599
|
+
previous = row2;
|
|
5600
|
+
}
|
|
5601
|
+
return previous[after.length] ?? 0;
|
|
5602
|
+
}
|
|
5603
|
+
function headingName(line) {
|
|
5604
|
+
const text2 = line.replace(/^#+/u, "").replace(/[ \t]+#+[ \t]*$/u, "").trim();
|
|
5605
|
+
return text2 === "" ? line.trim() : text2;
|
|
5606
|
+
}
|
|
5607
|
+
function headingSections(lines) {
|
|
5608
|
+
const sections = new Map;
|
|
5609
|
+
let fenced = false;
|
|
5610
|
+
let current = null;
|
|
5611
|
+
const close = () => {
|
|
5612
|
+
if (current === null)
|
|
5613
|
+
return;
|
|
5614
|
+
sections.set(current.key, [...sections.get(current.key) ?? [], current.lines.join(`
|
|
5615
|
+
`)]);
|
|
5616
|
+
};
|
|
5617
|
+
for (const line of lines) {
|
|
5618
|
+
if (FENCE2.test(line))
|
|
5619
|
+
fenced = !fenced;
|
|
5620
|
+
if (fenced || !HEADING.test(line)) {
|
|
5621
|
+
current?.lines.push(line);
|
|
5622
|
+
continue;
|
|
5623
|
+
}
|
|
5624
|
+
close();
|
|
5625
|
+
current = { key: line.trim(), lines: [] };
|
|
5626
|
+
}
|
|
5627
|
+
close();
|
|
5628
|
+
return sections;
|
|
5629
|
+
}
|
|
5630
|
+
function changedHeadings(before, after) {
|
|
5631
|
+
const old = headingSections(before);
|
|
5632
|
+
const now = headingSections(after);
|
|
5633
|
+
const keys = [...now.keys(), ...[...old.keys()].filter((key) => !now.has(key))];
|
|
5634
|
+
const changed = keys.filter((key) => JSON.stringify(old.get(key) ?? []) !== JSON.stringify(now.get(key) ?? []));
|
|
5635
|
+
return [...new Set(changed.map(headingName))];
|
|
5636
|
+
}
|
|
5637
|
+
function bodyChangeLine(before, after) {
|
|
5638
|
+
const old = before.split(`
|
|
5639
|
+
`);
|
|
5640
|
+
const now = after.split(`
|
|
5641
|
+
`);
|
|
5642
|
+
const common = commonLines(old, now);
|
|
5643
|
+
const headings = changedHeadings(old, now);
|
|
5644
|
+
const clause = headings.length === 0 ? "no heading changed" : `headings changed: ${headings.join(", ")}`;
|
|
5645
|
+
return `issue body: +${String(now.length - common)} -${String(old.length - common)} lines; ${clause}`;
|
|
5646
|
+
}
|
|
5647
|
+
function notesChangeLine(path2) {
|
|
5648
|
+
return `local notes: ${path2} changed since the saved copy`;
|
|
5649
|
+
}
|
|
5650
|
+
function readSnapshotChange(options) {
|
|
5651
|
+
const { saved, body, notes, notesPath: path2 } = options;
|
|
5652
|
+
if (saved === snapshotText(body, notes))
|
|
5653
|
+
return { kind: "unchanged", bodyLine: null, notesLine: null };
|
|
5654
|
+
const now = normalise(body);
|
|
5655
|
+
if (saved === `${now}
|
|
5656
|
+
` || saved.startsWith(`${now}${NOTES_SEPARATOR}`)) {
|
|
5657
|
+
return { kind: "notes", bodyLine: null, notesLine: notesChangeLine(path2) };
|
|
5658
|
+
}
|
|
5659
|
+
const split = saved.lastIndexOf(NOTES_SEPARATOR);
|
|
5660
|
+
const oldBody = split === -1 ? saved : saved.slice(0, split);
|
|
5661
|
+
const oldNotes = split === -1 ? "" : normalise(saved.slice(split + NOTES_SEPARATOR.length)).trim();
|
|
5662
|
+
const newNotes = notes === null ? "" : normalise(notes).trim();
|
|
5663
|
+
const bodyLine = bodyChangeLine(normalise(oldBody), now);
|
|
5664
|
+
return oldNotes === newNotes ? { kind: "body", bodyLine, notesLine: null } : { kind: "both", bodyLine, notesLine: notesChangeLine(path2) };
|
|
5665
|
+
}
|
|
5505
5666
|
function readIfFile(file, what) {
|
|
5506
5667
|
if (!existsSync5(file))
|
|
5507
5668
|
return null;
|
|
5508
|
-
if (!
|
|
5669
|
+
if (!statSync4(file).isFile()) {
|
|
5509
5670
|
throw new CommandExit(ISSUE_REFUSAL_EXIT, `${what} ${file} is not a file, and a spec is one`);
|
|
5510
5671
|
}
|
|
5511
5672
|
try {
|
|
@@ -5515,7 +5676,7 @@ function readIfFile(file, what) {
|
|
|
5515
5676
|
}
|
|
5516
5677
|
}
|
|
5517
5678
|
function readLocalNotes(repoRoot, specsDir, issue) {
|
|
5518
|
-
return readIfFile(
|
|
5679
|
+
return readIfFile(resolve5(repoRoot, notesPath(specsDir, issue)), "the local notes");
|
|
5519
5680
|
}
|
|
5520
5681
|
function snapshotDiffersMessage(path2, issue) {
|
|
5521
5682
|
return `${path2} is a snapshot of issue #${String(issue)} that no longer matches it:` + " the issue body, or the local notes appended to it, changed since the snapshot was written;" + ` pass ${REFRESH_FLAG} to plan from the issue as it reads now, or --spec=${path2} to plan from` + " the snapshot as it stands";
|
|
@@ -5532,31 +5693,42 @@ function writeSpecSnapshot(options) {
|
|
|
5532
5693
|
}
|
|
5533
5694
|
const local = readLocalNotes(repoRoot, specsDir, issue.number);
|
|
5534
5695
|
const text2 = snapshotText(issue.body, local);
|
|
5535
|
-
const absolute =
|
|
5696
|
+
const absolute = resolve5(repoRoot, path2);
|
|
5536
5697
|
const existing = readIfFile(absolute, "the spec snapshot");
|
|
5537
5698
|
const written = {
|
|
5538
5699
|
path: path2,
|
|
5539
5700
|
absolute,
|
|
5540
5701
|
action: "created",
|
|
5541
5702
|
notes: local === null ? null : notes,
|
|
5703
|
+
previous: null,
|
|
5542
5704
|
text: text2
|
|
5543
5705
|
};
|
|
5544
5706
|
if (existing === text2)
|
|
5545
5707
|
return { ...written, action: "unchanged" };
|
|
5546
|
-
|
|
5708
|
+
const action = rewriteAction(existing, issue.body, local, notes, refresh);
|
|
5709
|
+
if (action === null) {
|
|
5547
5710
|
throw new CommandExit(ISSUE_REFUSAL_EXIT, snapshotDiffersMessage(path2, issue.number));
|
|
5548
5711
|
}
|
|
5712
|
+
const previous = action === "created" ? null : movePreviousCopy({ repoRoot, specsDir, path: path2 }).path;
|
|
5549
5713
|
try {
|
|
5550
|
-
|
|
5714
|
+
mkdirSync4(dirname4(absolute), { recursive: true });
|
|
5551
5715
|
writeFileSync3(absolute, text2);
|
|
5552
5716
|
} catch (error) {
|
|
5553
|
-
throw new Error(`${
|
|
5717
|
+
throw new Error(`${PREFIX4}: the snapshot ${path2} could not be written: ${messageOf(error)}`, { cause: error });
|
|
5554
5718
|
}
|
|
5555
|
-
return
|
|
5719
|
+
return { ...written, action, previous };
|
|
5720
|
+
}
|
|
5721
|
+
function rewriteAction(existing, body, notes, notesFile, refresh) {
|
|
5722
|
+
if (existing === null)
|
|
5723
|
+
return "created";
|
|
5724
|
+
const { kind } = readSnapshotChange({ saved: existing, body, notes, notesPath: notesFile });
|
|
5725
|
+
if (kind === "notes")
|
|
5726
|
+
return "notes-rebuilt";
|
|
5727
|
+
return refresh ? "refreshed" : null;
|
|
5556
5728
|
}
|
|
5557
5729
|
|
|
5558
5730
|
// src/board/setup.ts
|
|
5559
|
-
var
|
|
5731
|
+
var PREFIX5 = "board setup";
|
|
5560
5732
|
var MODULE_DIR = dirname5(fileURLToPath(import.meta.url));
|
|
5561
5733
|
var BOARD_LABELS = Object.freeze([
|
|
5562
5734
|
{
|
|
@@ -5589,7 +5761,7 @@ var BOARD_LABELS = Object.freeze([
|
|
|
5589
5761
|
}
|
|
5590
5762
|
]);
|
|
5591
5763
|
var LABEL_LIST_LIMIT = 100;
|
|
5592
|
-
var SPEC_TEMPLATE_PATH =
|
|
5764
|
+
var SPEC_TEMPLATE_PATH = join10(".github", "ISSUE_TEMPLATE", "spec.md");
|
|
5593
5765
|
var TEMPLATES_DIRNAME = "templates";
|
|
5594
5766
|
var SPEC_TEMPLATE_FILE = "spec.md";
|
|
5595
5767
|
function boardChanged(report) {
|
|
@@ -5607,24 +5779,24 @@ function parseRows2(stdout, command) {
|
|
|
5607
5779
|
try {
|
|
5608
5780
|
payload = JSON.parse(stdout);
|
|
5609
5781
|
} catch (error) {
|
|
5610
|
-
throw new Error(`${
|
|
5782
|
+
throw new Error(`${PREFIX5}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
|
|
5611
5783
|
}
|
|
5612
5784
|
if (!Array.isArray(payload)) {
|
|
5613
|
-
throw new Error(`${
|
|
5785
|
+
throw new Error(`${PREFIX5}: ${command} answered ${describeValue(payload)}, expected a list`);
|
|
5614
5786
|
}
|
|
5615
5787
|
return payload;
|
|
5616
5788
|
}
|
|
5617
5789
|
function rowText2(row2, key, command, where) {
|
|
5618
5790
|
const value = isMapping(row2) ? row2[key] : null;
|
|
5619
5791
|
if (typeof value !== "string") {
|
|
5620
|
-
throw new Error(`${
|
|
5792
|
+
throw new Error(`${PREFIX5}: ${command} answered ${where}.${key} as ${describeValue(value)}, expected a string`);
|
|
5621
5793
|
}
|
|
5622
5794
|
return value;
|
|
5623
5795
|
}
|
|
5624
5796
|
function rowNumber2(row2, key, command, where) {
|
|
5625
5797
|
const value = isMapping(row2) ? row2[key] : null;
|
|
5626
5798
|
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1) {
|
|
5627
|
-
throw new Error(`${
|
|
5799
|
+
throw new Error(`${PREFIX5}: ${command} answered ${where}.${key} as ${describeValue(value)}, expected a positive whole number`);
|
|
5628
5800
|
}
|
|
5629
5801
|
return value;
|
|
5630
5802
|
}
|
|
@@ -5634,7 +5806,7 @@ async function listBoardLabels(gh) {
|
|
|
5634
5806
|
const command = `gh label list --limit ${limit} --json name`;
|
|
5635
5807
|
const result = await gh(args);
|
|
5636
5808
|
if (!result.ok)
|
|
5637
|
-
throw new Error(`${
|
|
5809
|
+
throw new Error(`${PREFIX5}: ${detailOf5(result, command)}`);
|
|
5638
5810
|
return Object.freeze(parseRows2(result.stdout, command).map((row2, index) => rowText2(row2, "name", command, `label ${String(index)}`)));
|
|
5639
5811
|
}
|
|
5640
5812
|
function holdsLabel(held, name2) {
|
|
@@ -5667,7 +5839,7 @@ async function setUpLabels(gh) {
|
|
|
5667
5839
|
}
|
|
5668
5840
|
function statOrNull(path2) {
|
|
5669
5841
|
try {
|
|
5670
|
-
return
|
|
5842
|
+
return statSync5(path2);
|
|
5671
5843
|
} catch {
|
|
5672
5844
|
return null;
|
|
5673
5845
|
}
|
|
@@ -5681,17 +5853,17 @@ function anythingAt(path2) {
|
|
|
5681
5853
|
}
|
|
5682
5854
|
}
|
|
5683
5855
|
function specTemplateSource(moduleDir = MODULE_DIR) {
|
|
5684
|
-
return
|
|
5856
|
+
return join10(moduleDir, TEMPLATES_DIRNAME, SPEC_TEMPLATE_FILE);
|
|
5685
5857
|
}
|
|
5686
5858
|
function readSpecTemplate(moduleDir = MODULE_DIR) {
|
|
5687
5859
|
const source = specTemplateSource(moduleDir);
|
|
5688
5860
|
if (!existsSync6(source)) {
|
|
5689
|
-
throw new Error(`${
|
|
5861
|
+
throw new Error(`${PREFIX5}: the spec issue template is missing: no file at ${source}`);
|
|
5690
5862
|
}
|
|
5691
5863
|
return readFileSync9(source, "utf8");
|
|
5692
5864
|
}
|
|
5693
5865
|
function writeSpecTemplate(root, moduleDir = MODULE_DIR) {
|
|
5694
|
-
const path2 =
|
|
5866
|
+
const path2 = join10(root, SPEC_TEMPLATE_PATH);
|
|
5695
5867
|
const name2 = SPEC_TEMPLATE_PATH;
|
|
5696
5868
|
const found = statOrNull(path2);
|
|
5697
5869
|
if (found !== null) {
|
|
@@ -5702,7 +5874,7 @@ function writeSpecTemplate(root, moduleDir = MODULE_DIR) {
|
|
|
5702
5874
|
}
|
|
5703
5875
|
try {
|
|
5704
5876
|
const text2 = readSpecTemplate(moduleDir);
|
|
5705
|
-
|
|
5877
|
+
mkdirSync5(dirname5(path2), { recursive: true });
|
|
5706
5878
|
writeFileSync4(path2, text2, { encoding: "utf8", flag: "wx" });
|
|
5707
5879
|
return partOf("template", name2, "created", `written from ${specTemplateSource(moduleDir)}`);
|
|
5708
5880
|
} catch (error) {
|
|
@@ -5776,7 +5948,7 @@ async function searchRoadmapIssues(gh) {
|
|
|
5776
5948
|
const command = `gh issue list --state open --search "${search}" --json number,title`;
|
|
5777
5949
|
const result = await gh(args);
|
|
5778
5950
|
if (!result.ok)
|
|
5779
|
-
throw new Error(`${
|
|
5951
|
+
throw new Error(`${PREFIX5}: ${detailOf5(result, command)}`);
|
|
5780
5952
|
const wanted = ROADMAP_TITLE.toLowerCase();
|
|
5781
5953
|
return Object.freeze(parseRows2(result.stdout, command).map((row2, index) => ({
|
|
5782
5954
|
number: rowNumber2(row2, "number", command, `issue ${String(index)}`),
|
|
@@ -5787,12 +5959,12 @@ async function createRoadmapIssue(gh) {
|
|
|
5787
5959
|
const command = `gh issue create --title ${ROADMAP_TITLE}`;
|
|
5788
5960
|
const result = await gh(["issue", "create", "--title", ROADMAP_TITLE, "--body", roadmapIssueBody()]);
|
|
5789
5961
|
if (!result.ok)
|
|
5790
|
-
throw new Error(`${
|
|
5962
|
+
throw new Error(`${PREFIX5}: ${detailOf5(result, command)}`);
|
|
5791
5963
|
const url = result.stdout.trim().split(`
|
|
5792
5964
|
`).at(-1) ?? "";
|
|
5793
5965
|
const number = CREATED_ISSUE_URL2.exec(url)?.[1];
|
|
5794
5966
|
if (number === undefined) {
|
|
5795
|
-
throw new Error(`${
|
|
5967
|
+
throw new Error(`${PREFIX5}: ${command} exited 0 and printed no issue URL, so the issue may exist` + ` unrecorded; it wrote ${describeValue(result.stdout)}`);
|
|
5796
5968
|
}
|
|
5797
5969
|
return Number.parseInt(number, 10);
|
|
5798
5970
|
}
|
|
@@ -5882,11 +6054,11 @@ async function readLabelRows(gh) {
|
|
|
5882
6054
|
return Object.freeze(BOARD_LABELS.map((label) => missing2.includes(label) ? rowOf("label", label.name, "missing", "the repository carries no label of that name") : rowOf("label", label.name, "present", "the repository carries it")));
|
|
5883
6055
|
}
|
|
5884
6056
|
function readTemplateRow(root) {
|
|
5885
|
-
const path2 =
|
|
6057
|
+
const path2 = join11(root, SPEC_TEMPLATE_PATH);
|
|
5886
6058
|
const name2 = SPEC_TEMPLATE_PATH;
|
|
5887
6059
|
let found;
|
|
5888
6060
|
try {
|
|
5889
|
-
found =
|
|
6061
|
+
found = statSync6(path2);
|
|
5890
6062
|
} catch {
|
|
5891
6063
|
return rowOf("template", name2, "missing", "the repository carries no spec issue template");
|
|
5892
6064
|
}
|
|
@@ -5954,18 +6126,18 @@ async function readBoardStatus(options) {
|
|
|
5954
6126
|
}
|
|
5955
6127
|
|
|
5956
6128
|
// src/effort/store/legacy.ts
|
|
5957
|
-
import { statSync as
|
|
5958
|
-
import { basename as
|
|
6129
|
+
import { statSync as statSync7 } from "fs";
|
|
6130
|
+
import { basename as basename5, join as join14 } from "path";
|
|
5959
6131
|
|
|
5960
6132
|
// src/effort/store.ts
|
|
5961
6133
|
import {
|
|
5962
6134
|
appendFileSync,
|
|
5963
6135
|
existsSync as existsSync7,
|
|
5964
|
-
mkdirSync as
|
|
6136
|
+
mkdirSync as mkdirSync6,
|
|
5965
6137
|
readFileSync as readFileSync10
|
|
5966
6138
|
} from "fs";
|
|
5967
|
-
import { dirname as dirname6, join as
|
|
5968
|
-
var EFFORT_STORE_DIR =
|
|
6139
|
+
import { dirname as dirname6, join as join12 } from "path";
|
|
6140
|
+
var EFFORT_STORE_DIR = join12(".rafa", "effort");
|
|
5969
6141
|
var STORE_FILE_NAMES = {
|
|
5970
6142
|
sessions: "sessions.ndjson",
|
|
5971
6143
|
commits: "commits.ndjson"
|
|
@@ -5977,7 +6149,7 @@ function normaliseKey(key) {
|
|
|
5977
6149
|
return typeof key === "string" && key.length > 0 ? key : null;
|
|
5978
6150
|
}
|
|
5979
6151
|
function effortStorePath(repoRoot, kind) {
|
|
5980
|
-
return
|
|
6152
|
+
return join12(repoRoot, EFFORT_STORE_DIR, STORE_FILE_NAMES[kind]);
|
|
5981
6153
|
}
|
|
5982
6154
|
function readStoreText(path2) {
|
|
5983
6155
|
return existsSync7(path2) ? readFileSync10(path2, "utf8") : null;
|
|
@@ -6052,7 +6224,7 @@ function appendRows(path2, rows2, keyOf) {
|
|
|
6052
6224
|
`);
|
|
6053
6225
|
const lead = separatedPartialLine ? `
|
|
6054
6226
|
` : "";
|
|
6055
|
-
|
|
6227
|
+
mkdirSync6(dirname6(path2), { recursive: true });
|
|
6056
6228
|
appendFileSync(path2, `${lead}${lines.join(`
|
|
6057
6229
|
`)}
|
|
6058
6230
|
`, "utf8");
|
|
@@ -6065,8 +6237,8 @@ function appendRows(path2, rows2, keyOf) {
|
|
|
6065
6237
|
}
|
|
6066
6238
|
|
|
6067
6239
|
// src/effort/store/sqlite.ts
|
|
6068
|
-
import { existsSync as existsSync8, mkdirSync as
|
|
6069
|
-
import { dirname as dirname7, join as
|
|
6240
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync7 } from "fs";
|
|
6241
|
+
import { dirname as dirname7, join as join13 } from "path";
|
|
6070
6242
|
import { Database } from "bun:sqlite";
|
|
6071
6243
|
|
|
6072
6244
|
// src/effort/store/types.ts
|
|
@@ -6081,7 +6253,7 @@ var EFFORT_KEY_PROJECTIONS = {
|
|
|
6081
6253
|
// src/effort/store/sqlite.ts
|
|
6082
6254
|
var STORE_FILE_NAME = "effort.sqlite";
|
|
6083
6255
|
function sqliteStorePath(repoRoot) {
|
|
6084
|
-
return
|
|
6256
|
+
return join13(repoRoot, EFFORT_STORE_DIR, STORE_FILE_NAME);
|
|
6085
6257
|
}
|
|
6086
6258
|
var SQLITE_MIGRATIONS = [
|
|
6087
6259
|
`
|
|
@@ -6291,7 +6463,7 @@ function migrateSchema(db, path2, migrations = SQLITE_MIGRATIONS) {
|
|
|
6291
6463
|
}
|
|
6292
6464
|
function withSqliteStore(path2, create, use) {
|
|
6293
6465
|
if (create)
|
|
6294
|
-
|
|
6466
|
+
mkdirSync7(dirname7(path2), { recursive: true });
|
|
6295
6467
|
const db = new Database(path2, { readwrite: true, create });
|
|
6296
6468
|
try {
|
|
6297
6469
|
migrateSchema(db, path2);
|
|
@@ -6361,11 +6533,11 @@ function openSqliteStore(repoRoot) {
|
|
|
6361
6533
|
}
|
|
6362
6534
|
|
|
6363
6535
|
// src/effort/store/legacy.ts
|
|
6364
|
-
var LEGACY_EFFORT_STORE_DIR =
|
|
6536
|
+
var LEGACY_EFFORT_STORE_DIR = join14(".ralph", "effort");
|
|
6365
6537
|
var NDJSON_KINDS = { sessions: true, commits: true };
|
|
6366
6538
|
var STORE_FILE_NAMES2 = Object.freeze([
|
|
6367
|
-
|
|
6368
|
-
...Object.keys(NDJSON_KINDS).map((kind) =>
|
|
6539
|
+
basename5(sqliteStorePath("")),
|
|
6540
|
+
...Object.keys(NDJSON_KINDS).map((kind) => basename5(effortStorePath("", kind)))
|
|
6369
6541
|
]);
|
|
6370
6542
|
function isAbsence(error) {
|
|
6371
6543
|
if (typeof error !== "object" || error === null || !("code" in error))
|
|
@@ -6374,7 +6546,7 @@ function isAbsence(error) {
|
|
|
6374
6546
|
}
|
|
6375
6547
|
function isStoreFile(path2) {
|
|
6376
6548
|
try {
|
|
6377
|
-
return
|
|
6549
|
+
return statSync7(path2).isFile();
|
|
6378
6550
|
} catch (error) {
|
|
6379
6551
|
if (isAbsence(error))
|
|
6380
6552
|
return false;
|
|
@@ -6382,7 +6554,7 @@ function isStoreFile(path2) {
|
|
|
6382
6554
|
}
|
|
6383
6555
|
}
|
|
6384
6556
|
function storeFilesIn(dir) {
|
|
6385
|
-
return Object.freeze(STORE_FILE_NAMES2.filter((name2) => isStoreFile(
|
|
6557
|
+
return Object.freeze(STORE_FILE_NAMES2.filter((name2) => isStoreFile(join14(dir, name2))));
|
|
6386
6558
|
}
|
|
6387
6559
|
function shellQuoted(path2) {
|
|
6388
6560
|
return `'${path2.replaceAll("'", "'\\''")}'`;
|
|
@@ -6392,8 +6564,8 @@ function legacyWarning(legacyDir, storeDir, legacyFiles) {
|
|
|
6392
6564
|
return `${legacyDir} holds an effort store (${legacyFiles.join(", ")}) and ${storeDir} holds none;` + ` rafa reads ${storeDir} alone, so its reports start from an empty store. Once no loop is running,` + ` move it: ${move}`;
|
|
6393
6565
|
}
|
|
6394
6566
|
function readLegacyStore(root) {
|
|
6395
|
-
const legacyDir =
|
|
6396
|
-
const storeDir =
|
|
6567
|
+
const legacyDir = join14(root, LEGACY_EFFORT_STORE_DIR);
|
|
6568
|
+
const storeDir = join14(root, EFFORT_STORE_DIR);
|
|
6397
6569
|
const legacyFiles = storeFilesIn(legacyDir);
|
|
6398
6570
|
const storeFiles = storeFilesIn(storeDir);
|
|
6399
6571
|
const warning = legacyFiles.length > 0 && storeFiles.length === 0 ? legacyWarning(legacyDir, storeDir, legacyFiles) : null;
|
|
@@ -6541,9 +6713,9 @@ function isFirstDispatch(planPath) {
|
|
|
6541
6713
|
}
|
|
6542
6714
|
|
|
6543
6715
|
// src/project/bin-path.ts
|
|
6544
|
-
import { delimiter, isAbsolute as isAbsolute4, join as
|
|
6545
|
-
var RAFA_BIN_DIR =
|
|
6546
|
-
var BUN_BIN_DIR =
|
|
6716
|
+
import { delimiter, isAbsolute as isAbsolute4, join as join15, resolve as resolve6 } from "path";
|
|
6717
|
+
var RAFA_BIN_DIR = join15(SCOPE_DIR, "bin");
|
|
6718
|
+
var BUN_BIN_DIR = join15(".bun", "bin");
|
|
6547
6719
|
function exportLine(rafaBin) {
|
|
6548
6720
|
return `export PATH="${rafaBin}:$PATH"`;
|
|
6549
6721
|
}
|
|
@@ -6560,7 +6732,7 @@ function canonical(path2, fs2) {
|
|
|
6560
6732
|
try {
|
|
6561
6733
|
return fs2.realpath(path2);
|
|
6562
6734
|
} catch {
|
|
6563
|
-
return
|
|
6735
|
+
return resolve6(path2);
|
|
6564
6736
|
}
|
|
6565
6737
|
}
|
|
6566
6738
|
function indexOf(entries, dir, fs2) {
|
|
@@ -6576,8 +6748,8 @@ function stateOf2(rafaIndex, bunIndex) {
|
|
|
6576
6748
|
return "ahead";
|
|
6577
6749
|
}
|
|
6578
6750
|
function readBinPath(pathValue, home, fs2 = DISK_FILE_SYSTEM) {
|
|
6579
|
-
const rafaBin =
|
|
6580
|
-
const bunBin =
|
|
6751
|
+
const rafaBin = join15(home, RAFA_BIN_DIR);
|
|
6752
|
+
const bunBin = join15(home, BUN_BIN_DIR);
|
|
6581
6753
|
const entries = (pathValue ?? "").split(delimiter);
|
|
6582
6754
|
const rafaIndex = indexOf(entries, rafaBin, fs2);
|
|
6583
6755
|
const bunIndex = indexOf(entries, bunBin, fs2);
|
|
@@ -6620,20 +6792,20 @@ function readPreInitDirs(config) {
|
|
|
6620
6792
|
|
|
6621
6793
|
// src/start/plan-path.ts
|
|
6622
6794
|
import { existsSync as existsSync9 } from "fs";
|
|
6623
|
-
import { resolve as
|
|
6795
|
+
import { resolve as resolve7 } from "path";
|
|
6624
6796
|
var DEFAULT_PLAN_FILE = "PLAN.md";
|
|
6625
6797
|
function resolvePlanPath(repoRoot, planDir, planArg) {
|
|
6626
6798
|
if (planArg)
|
|
6627
|
-
return
|
|
6628
|
-
const inPlanDir =
|
|
6629
|
-
return existsSync9(inPlanDir) ? inPlanDir :
|
|
6799
|
+
return resolve7(repoRoot, planArg);
|
|
6800
|
+
const inPlanDir = resolve7(repoRoot, planDir, DEFAULT_PLAN_FILE);
|
|
6801
|
+
return existsSync9(inPlanDir) ? inPlanDir : resolve7(repoRoot, DEFAULT_PLAN_FILE);
|
|
6630
6802
|
}
|
|
6631
6803
|
|
|
6632
6804
|
// src/commands/doctor-blocked.ts
|
|
6633
6805
|
var BLOCKED_HEADING = "Blocked issues:";
|
|
6634
6806
|
var BLOCKED_LIST_LIMIT = 100;
|
|
6635
6807
|
var KNOWN_LIST_LIMIT = 500;
|
|
6636
|
-
var
|
|
6808
|
+
var PREFIX6 = "board blocked";
|
|
6637
6809
|
function detailOf6(result, command) {
|
|
6638
6810
|
const written = result.stderr.trim() || result.stdout.trim();
|
|
6639
6811
|
return written === "" ? `${command} failed and wrote nothing` : `${command} failed: ${written}`;
|
|
@@ -6641,32 +6813,32 @@ function detailOf6(result, command) {
|
|
|
6641
6813
|
async function rowsOf(gh, args, command) {
|
|
6642
6814
|
const result = await gh(args);
|
|
6643
6815
|
if (!result.ok)
|
|
6644
|
-
throw new Error(`${
|
|
6816
|
+
throw new Error(`${PREFIX6}: ${detailOf6(result, command)}`);
|
|
6645
6817
|
let parsed;
|
|
6646
6818
|
try {
|
|
6647
6819
|
parsed = JSON.parse(result.stdout);
|
|
6648
6820
|
} catch (error) {
|
|
6649
|
-
throw new Error(`${
|
|
6821
|
+
throw new Error(`${PREFIX6}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
|
|
6650
6822
|
}
|
|
6651
6823
|
if (!Array.isArray(parsed)) {
|
|
6652
|
-
throw new Error(`${
|
|
6824
|
+
throw new Error(`${PREFIX6}: ${command} answered ${describeValue(parsed)}, expected a list of issues`);
|
|
6653
6825
|
}
|
|
6654
6826
|
return parsed;
|
|
6655
6827
|
}
|
|
6656
6828
|
function rowNumber3(row2, command, where) {
|
|
6657
6829
|
if (!isMapping(row2)) {
|
|
6658
|
-
throw new Error(`${
|
|
6830
|
+
throw new Error(`${PREFIX6}: ${command} answered ${where} as ${describeValue(row2)}, expected a mapping`);
|
|
6659
6831
|
}
|
|
6660
6832
|
const number = row2["number"];
|
|
6661
6833
|
if (typeof number !== "number" || !Number.isSafeInteger(number) || number < 1) {
|
|
6662
|
-
throw new Error(`${
|
|
6834
|
+
throw new Error(`${PREFIX6}: ${command} answered ${where}.number as ${describeValue(number)}, expected a positive whole number`);
|
|
6663
6835
|
}
|
|
6664
6836
|
return number;
|
|
6665
6837
|
}
|
|
6666
6838
|
function rowBody(row2, command, where) {
|
|
6667
6839
|
const body = isMapping(row2) ? row2["body"] : null;
|
|
6668
6840
|
if (typeof body !== "string") {
|
|
6669
|
-
throw new Error(`${
|
|
6841
|
+
throw new Error(`${PREFIX6}: ${command} answered ${where}.body as ${describeValue(body)}, expected a string`);
|
|
6670
6842
|
}
|
|
6671
6843
|
return body;
|
|
6672
6844
|
}
|
|
@@ -6756,6 +6928,16 @@ function renderBlockedIssues(report) {
|
|
|
6756
6928
|
return [BLOCKED_HEADING, ...body, ...unchecked];
|
|
6757
6929
|
}
|
|
6758
6930
|
|
|
6931
|
+
// src/commands/doctor-previous.ts
|
|
6932
|
+
function previousCopiesWarning(specsDir, count) {
|
|
6933
|
+
return `rafa doctor: ${previousDir(specsDir)}/ holds ${count} previous copies of issue specs; they are safe to delete`;
|
|
6934
|
+
}
|
|
6935
|
+
function readPreviousCopies(repoRoot, config) {
|
|
6936
|
+
const count = countPreviousCopies(repoRoot, config.specsDir);
|
|
6937
|
+
const warning = count > PREVIOUS_COPY_WARN_ABOVE ? previousCopiesWarning(config.specsDir, count) : null;
|
|
6938
|
+
return { count, warning };
|
|
6939
|
+
}
|
|
6940
|
+
|
|
6759
6941
|
// src/commands/init-board.ts
|
|
6760
6942
|
var YES_ANSWERS = ["y", "yes"];
|
|
6761
6943
|
var BOARD_QUESTION = "Set up the GitHub board for this repo? [y/N] ";
|
|
@@ -6896,7 +7078,7 @@ function resolvedConfig3(project, warn) {
|
|
|
6896
7078
|
}
|
|
6897
7079
|
}
|
|
6898
7080
|
function defaultPlanPaths(root, planDir) {
|
|
6899
|
-
return [...new Set([
|
|
7081
|
+
return [...new Set([resolve8(root, planDir, DEFAULT_PLAN_FILE), resolve8(root, DEFAULT_PLAN_FILE)])];
|
|
6900
7082
|
}
|
|
6901
7083
|
function planFileAt(path2) {
|
|
6902
7084
|
try {
|
|
@@ -6941,7 +7123,7 @@ function readStartTier(plan, items) {
|
|
|
6941
7123
|
return { tier: NO_START_TIER, items: NO_ITEMS2 };
|
|
6942
7124
|
if (isFirstDispatch(plan))
|
|
6943
7125
|
return { tier: { checked: start.length, skipped: 0, tracker: null }, items: start };
|
|
6944
|
-
const tracker =
|
|
7126
|
+
const tracker = basename6(trackerPathFor(plan));
|
|
6945
7127
|
return { tier: { checked: 0, skipped: start.length, tracker }, items: NO_ITEMS2 };
|
|
6946
7128
|
}
|
|
6947
7129
|
async function checkPreflight(context, project, seams) {
|
|
@@ -6998,24 +7180,37 @@ function readPreInit(project) {
|
|
|
6998
7180
|
return null;
|
|
6999
7181
|
}
|
|
7000
7182
|
}
|
|
7183
|
+
function readPrevious(project) {
|
|
7184
|
+
try {
|
|
7185
|
+
const { config } = loadConfig({ root: project.root, home: project.home }, {}, () => {
|
|
7186
|
+
return;
|
|
7187
|
+
});
|
|
7188
|
+
return readPreviousCopies(project.root, config);
|
|
7189
|
+
} catch {
|
|
7190
|
+
return null;
|
|
7191
|
+
}
|
|
7192
|
+
}
|
|
7001
7193
|
function readInstall(context, project) {
|
|
7002
7194
|
const binPath = readBinPath(context.env["PATH"], project.home);
|
|
7003
7195
|
const preInitDirs = readPreInit(project);
|
|
7196
|
+
const previousCopies = readPrevious(project);
|
|
7004
7197
|
try {
|
|
7005
|
-
return { binPath, legacyStore: readLegacyStore(project.root), preInitDirs, storeProblem: null };
|
|
7198
|
+
return { binPath, legacyStore: readLegacyStore(project.root), preInitDirs, previousCopies, storeProblem: null };
|
|
7006
7199
|
} catch (error) {
|
|
7007
7200
|
const storeProblem = `rafa doctor: the effort store directories could not be checked: ${messageOf(error)}`;
|
|
7008
|
-
return { binPath, legacyStore: null, preInitDirs, storeProblem };
|
|
7201
|
+
return { binPath, legacyStore: null, preInitDirs, previousCopies, storeProblem };
|
|
7009
7202
|
}
|
|
7010
7203
|
}
|
|
7011
7204
|
function writeInstall(context, install) {
|
|
7012
|
-
const { binPath, legacyStore, preInitDirs, storeProblem } = install;
|
|
7205
|
+
const { binPath, legacyStore, preInitDirs, previousCopies, storeProblem } = install;
|
|
7013
7206
|
if (storeProblem !== null)
|
|
7014
7207
|
context.output.warn(storeProblem);
|
|
7015
7208
|
if (legacyStore !== null && legacyStore.warning !== null)
|
|
7016
7209
|
context.output.warn(legacyStore.warning);
|
|
7017
7210
|
if (preInitDirs !== null && preInitDirs.warning !== null)
|
|
7018
7211
|
context.output.warn(preInitDirs.warning);
|
|
7212
|
+
if (previousCopies !== null && previousCopies.warning !== null)
|
|
7213
|
+
context.output.warn(previousCopies.warning);
|
|
7019
7214
|
if (binPath.warning !== null) {
|
|
7020
7215
|
context.output.warn(binPath.warning);
|
|
7021
7216
|
return;
|
|
@@ -7042,7 +7237,7 @@ function headLine(preflight) {
|
|
|
7042
7237
|
const checked2 = checkedPhrase(count, automatic, "from the config ");
|
|
7043
7238
|
return `Preflight with no plan, none being at ${where}: ${checked2}, no run started.`;
|
|
7044
7239
|
}
|
|
7045
|
-
const merged = prerequisitesFile === null ? "" : `, with ${
|
|
7240
|
+
const merged = prerequisitesFile === null ? "" : `, with ${basename6(prerequisitesFile)} merged in`;
|
|
7046
7241
|
const checked = checkedPhrase(count, automatic, "");
|
|
7047
7242
|
return `Preflight for ${shownPath(plan, root)}${merged}: ${checked}, no run started.`;
|
|
7048
7243
|
}
|
|
@@ -7064,7 +7259,7 @@ function reminderLines(preflight) {
|
|
|
7064
7259
|
if (reminders.length === 0 || prerequisitesFile === null)
|
|
7065
7260
|
return [];
|
|
7066
7261
|
return [
|
|
7067
|
-
`${
|
|
7262
|
+
`${basename6(prerequisitesFile)} names ${plural(reminders.length, "step")} the preflight does not check:`,
|
|
7068
7263
|
...reminders.map((reminder) => ` line ${String(reminder.line)}: ${reminder.description}`)
|
|
7069
7264
|
];
|
|
7070
7265
|
}
|
|
@@ -7123,6 +7318,7 @@ function resultOf(preflight, install, readings) {
|
|
|
7123
7318
|
binPath: install.binPath,
|
|
7124
7319
|
legacyStore: install.legacyStore,
|
|
7125
7320
|
preInitDirs: install.preInitDirs,
|
|
7321
|
+
previousCopies: install.previousCopies,
|
|
7126
7322
|
board: readings.board,
|
|
7127
7323
|
blocked: readings.blocked
|
|
7128
7324
|
};
|
|
@@ -7164,7 +7360,7 @@ function createDoctorCommand(seams = DEFAULT_DOCTOR_SEAMS) {
|
|
|
7164
7360
|
subject: "doctor",
|
|
7165
7361
|
action: "doctor",
|
|
7166
7362
|
summary: "check the prerequisites rafa loop start checks, and the install, starting no run",
|
|
7167
|
-
description: "Checks the prerequisites `rafa loop start` checks before its first session and prints each" + " check, starting no run and storing nothing: the required and optional items of" + " `.rafa/config.yaml`, with the `PREREQUISITES-<stub>.md` beside the plan merged in, and, when the" + " repository resolves to `pr.provider: gh`, the two required items that provider adds ahead of them:" + " `gh` on PATH and `gh auth status` for the remote's host. A plan's `[start]` items are checked" + " after those and ahead of the configured tiers, on a first dispatch alone: a" + " `PLAN_TRACKER-<stub>.md` beside the plan already holding a ticked task makes the next run a" + " resume, which checks none of them and says in one line how many it passed over." + " The plan is the" + " one `--plan=<file>` names, relative to the project root, or the default plan `rafa loop start`" + ` runs. Each probe runs in the project root with stdin closed and a ${String(PROBE_TIMEOUT_MS / 1000)}-second` + " timeout. It exits 1 when a required item fails, naming the item, its probe and its exit code or" + " first line of stderr, where `rafa loop start` would halt, and 0 otherwise. It warns when" + " `.ralph/effort/` holds an effort store and `.rafa/effort/` holds none, and when `~/.rafa/bin` is" + " not on PATH ahead of `~/.bun/bin
|
|
7363
|
+
description: "Checks the prerequisites `rafa loop start` checks before its first session and prints each" + " check, starting no run and storing nothing: the required and optional items of" + " `.rafa/config.yaml`, with the `PREREQUISITES-<stub>.md` beside the plan merged in, and, when the" + " repository resolves to `pr.provider: gh`, the two required items that provider adds ahead of them:" + " `gh` on PATH and `gh auth status` for the remote's host. A plan's `[start]` items are checked" + " after those and ahead of the configured tiers, on a first dispatch alone: a" + " `PLAN_TRACKER-<stub>.md` beside the plan already holding a ticked task makes the next run a" + " resume, which checks none of them and says in one line how many it passed over." + " The plan is the" + " one `--plan=<file>` names, relative to the project root, or the default plan `rafa loop start`" + ` runs. Each probe runs in the project root with stdin closed and a ${String(PROBE_TIMEOUT_MS / 1000)}-second` + " timeout. It exits 1 when a required item fails, naming the item, its probe and its exit code or" + " first line of stderr, where `rafa loop start` would halt, and 0 otherwise. It warns when" + " `.ralph/effort/` holds an effort store and `.rafa/effort/` holds none, and when `~/.rafa/bin` is" + " not on PATH ahead of `~/.bun/bin`, and when `previous/` under `specs.dir` holds more than fifty" + " previous copies of issue specs, which are safe to delete; a warning never changes the exit code. On a repository whose" + " provider is `gh` it also reads the GitHub board `rafa init --board` sets up and prints one row per" + " part \u2014 the seven labels, the spec issue template, the Roadmap issue and `roadmap.issue` \u2014 as present," + " missing, or unknown for a reading that failed, naming `rafa init --board` as the fix; it writes" + " nothing to the board and a row never changes the exit code. It then names, under `Blocked" + " issues:`, every open issue labelled `spec:blocked` whose `Blocked by:` line is missing, names no" + " issue, names itself, or names an id the board has no issue for, with what an author does about" + " it; that reading writes nothing and never changes the exit code either. With `--output=json` the" + " checks, both readings, those rows and those issues are the data of the terminal result event," + " unless a required item failed.",
|
|
7168
7364
|
args: [],
|
|
7169
7365
|
flags: [
|
|
7170
7366
|
{
|
|
@@ -7191,13 +7387,13 @@ function createDoctorCommand(seams = DEFAULT_DOCTOR_SEAMS) {
|
|
|
7191
7387
|
var doctor_default = createDoctorCommand();
|
|
7192
7388
|
|
|
7193
7389
|
// src/effort/collect.ts
|
|
7194
|
-
import { existsSync as existsSync11, readdirSync as
|
|
7390
|
+
import { existsSync as existsSync11, readdirSync as readdirSync3, statSync as statSync8 } from "fs";
|
|
7195
7391
|
import { homedir as homedir2 } from "os";
|
|
7196
|
-
import { join as
|
|
7392
|
+
import { join as join18, resolve as resolve10 } from "path";
|
|
7197
7393
|
|
|
7198
7394
|
// src/effort/session-log.ts
|
|
7199
7395
|
import { createReadStream } from "fs";
|
|
7200
|
-
import { basename as
|
|
7396
|
+
import { basename as basename7 } from "path";
|
|
7201
7397
|
import { createInterface } from "readline";
|
|
7202
7398
|
function emptyUsageTotals() {
|
|
7203
7399
|
return {
|
|
@@ -7225,7 +7421,7 @@ function bump(counts, key) {
|
|
|
7225
7421
|
counts[key] = (counts[key] ?? 0) + 1;
|
|
7226
7422
|
}
|
|
7227
7423
|
function sessionIdFromPath(filePath) {
|
|
7228
|
-
return
|
|
7424
|
+
return basename7(filePath).replace(/\.jsonl$/i, "");
|
|
7229
7425
|
}
|
|
7230
7426
|
async function* readLines2(filePath) {
|
|
7231
7427
|
const stream = createReadStream(filePath, { encoding: "utf8" });
|
|
@@ -7782,8 +7978,8 @@ function openNdjsonStore(repoRoot) {
|
|
|
7782
7978
|
|
|
7783
7979
|
// src/adapters/learning/local.ts
|
|
7784
7980
|
import { appendFile, mkdir, readFile } from "fs/promises";
|
|
7785
|
-
import { join as
|
|
7786
|
-
var
|
|
7981
|
+
import { join as join16 } from "path";
|
|
7982
|
+
var PREFIX7 = "local learning";
|
|
7787
7983
|
var INSTINCTS_FILE = "instincts.ndjson";
|
|
7788
7984
|
var FLAGS_FILE = "flags.ndjson";
|
|
7789
7985
|
var MIN_CONFIDENCE = 0.3;
|
|
@@ -7910,21 +8106,21 @@ function warnThroughActiveOutput2(message) {
|
|
|
7910
8106
|
activeOutput().warn(message);
|
|
7911
8107
|
}
|
|
7912
8108
|
function localInstinctsDir(repoRoot) {
|
|
7913
|
-
return
|
|
8109
|
+
return join16(repoRoot, ".rafa", "instincts");
|
|
7914
8110
|
}
|
|
7915
8111
|
function createLocalLearning(options) {
|
|
7916
8112
|
const { instinctsDir } = options;
|
|
7917
8113
|
const now = options.now ?? (() => new Date().toISOString());
|
|
7918
8114
|
const warn = options.warn ?? warnThroughActiveOutput2;
|
|
7919
|
-
const instinctsPath =
|
|
7920
|
-
const flagsPath =
|
|
8115
|
+
const instinctsPath = join16(instinctsDir, INSTINCTS_FILE);
|
|
8116
|
+
const flagsPath = join16(instinctsDir, FLAGS_FILE);
|
|
7921
8117
|
async function heldInstincts() {
|
|
7922
8118
|
const lines = numberedLines(await readIfPresent2(instinctsPath));
|
|
7923
8119
|
return lines.flatMap(([number, line]) => {
|
|
7924
8120
|
const reading2 = readLine2(line, heldInstinctProblem);
|
|
7925
8121
|
if (reading2.ok)
|
|
7926
8122
|
return [copyInstinct(reading2.value.instinct)];
|
|
7927
|
-
warn(`${
|
|
8123
|
+
warn(`${PREFIX7}: line ${number} of ${instinctsPath} is not a held instinct, so it was skipped: ${reading2.problem}`);
|
|
7928
8124
|
return [];
|
|
7929
8125
|
});
|
|
7930
8126
|
}
|
|
@@ -7934,14 +8130,14 @@ function createLocalLearning(options) {
|
|
|
7934
8130
|
const reading2 = readLine2(line, flagProblem);
|
|
7935
8131
|
if (reading2.ok)
|
|
7936
8132
|
return reading2.value.id;
|
|
7937
|
-
throw new Error(`${
|
|
8133
|
+
throw new Error(`${PREFIX7}: line ${number} of ${flagsPath} is not a flag,` + ` and a bundle skipping it could hold an instinct it flags: ${reading2.problem}`);
|
|
7938
8134
|
}));
|
|
7939
8135
|
}
|
|
7940
8136
|
const learning = {
|
|
7941
8137
|
push: async (payload) => {
|
|
7942
8138
|
const problem = payloadProblem(payload);
|
|
7943
8139
|
if (problem !== null) {
|
|
7944
|
-
throw new TypeError(`${
|
|
8140
|
+
throw new TypeError(`${PREFIX7}: refused to store a push: ${problem}`);
|
|
7945
8141
|
}
|
|
7946
8142
|
const decisions = payload.instincts.map((incoming) => ({
|
|
7947
8143
|
incoming,
|
|
@@ -7967,11 +8163,11 @@ function createLocalLearning(options) {
|
|
|
7967
8163
|
const flag2 = { id, reason, flagged_at: now() };
|
|
7968
8164
|
const problem = flagProblem(flag2);
|
|
7969
8165
|
if (problem !== null) {
|
|
7970
|
-
throw new TypeError(`${
|
|
8166
|
+
throw new TypeError(`${PREFIX7}: refused to write an invalid flag: ${problem}`);
|
|
7971
8167
|
}
|
|
7972
8168
|
const held = await heldInstincts();
|
|
7973
8169
|
if (!held.some((instinct) => instinct.id === id)) {
|
|
7974
|
-
throw new Error(`${
|
|
8170
|
+
throw new Error(`${PREFIX7}: no instinct ${describeValue(id)} is held under ${instinctsDir}`);
|
|
7975
8171
|
}
|
|
7976
8172
|
await appendLines(flagsPath, [JSON.stringify(flag2)]);
|
|
7977
8173
|
}
|
|
@@ -7982,7 +8178,7 @@ function createLocalLearning(options) {
|
|
|
7982
8178
|
// src/adapters/planner/claude.ts
|
|
7983
8179
|
import { existsSync as existsSync10 } from "fs";
|
|
7984
8180
|
import { mkdir as mkdir2, readFile as readFile2 } from "fs/promises";
|
|
7985
|
-
import { posix, resolve as
|
|
8181
|
+
import { posix, resolve as resolve9 } from "path";
|
|
7986
8182
|
|
|
7987
8183
|
// src/board/spec-review.ts
|
|
7988
8184
|
var SPEC_REVIEW_KIND = "spec-review";
|
|
@@ -8293,12 +8489,12 @@ function createClaudePlanner(options) {
|
|
|
8293
8489
|
const create = async ({ specPath: specPath2, stub }) => {
|
|
8294
8490
|
const planPath = planFilePath(planDir, `PLAN-${stub}.md`);
|
|
8295
8491
|
const prerequisitesPath = planFilePath(planDir, `PREREQUISITES-${stub}.md`);
|
|
8296
|
-
const isWritten = (path2) => existsSync10(
|
|
8492
|
+
const isWritten = (path2) => existsSync10(resolve9(repoRoot, path2));
|
|
8297
8493
|
if (isWritten(planPath)) {
|
|
8298
8494
|
throw new ClaudePlannerError(`${planPath} already exists`, FAILURE_EXIT_CODE);
|
|
8299
8495
|
}
|
|
8300
|
-
const specContent = await readFile2(
|
|
8301
|
-
await mkdir2(
|
|
8496
|
+
const specContent = await readFile2(resolve9(repoRoot, specPath2), "utf8");
|
|
8497
|
+
await mkdir2(resolve9(repoRoot, planDir), { recursive: true });
|
|
8302
8498
|
const session = await runClaudeCaptured(buildPrompt(specContent, stub), settingSources, [], spawn);
|
|
8303
8499
|
const review = parseSpecReview(session.stdout);
|
|
8304
8500
|
const { exitCode } = session;
|
|
@@ -8319,8 +8515,8 @@ function createClaudePlanner(options) {
|
|
|
8319
8515
|
|
|
8320
8516
|
// src/adapters/tracker/local.ts
|
|
8321
8517
|
import { mkdir as mkdir3, readdir, readFile as readFile3, writeFile } from "fs/promises";
|
|
8322
|
-
import { join as
|
|
8323
|
-
var
|
|
8518
|
+
import { join as join17 } from "path";
|
|
8519
|
+
var PREFIX8 = "local tracker";
|
|
8324
8520
|
var FRONTMATTER = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/;
|
|
8325
8521
|
var ISSUE_NUMBER2 = /^[1-9]\d*$/;
|
|
8326
8522
|
var ISSUE_EXTENSION = ".md";
|
|
@@ -8376,7 +8572,7 @@ function issueNumberOfFile(name2) {
|
|
|
8376
8572
|
return name2.endsWith(ISSUE_EXTENSION) ? parseIssueNumber(name2.slice(0, -ISSUE_EXTENSION.length)) : null;
|
|
8377
8573
|
}
|
|
8378
8574
|
function issuePath(issuesDir, number) {
|
|
8379
|
-
return
|
|
8575
|
+
return join17(issuesDir, `${number}${ISSUE_EXTENSION}`);
|
|
8380
8576
|
}
|
|
8381
8577
|
async function issueNumbers(issuesDir) {
|
|
8382
8578
|
let names;
|
|
@@ -8390,7 +8586,7 @@ async function issueNumbers(issuesDir) {
|
|
|
8390
8586
|
return names.map(issueNumberOfFile).filter((number) => number !== null).sort((a, b) => a - b);
|
|
8391
8587
|
}
|
|
8392
8588
|
function localIssuesDir(repoRoot) {
|
|
8393
|
-
return
|
|
8589
|
+
return join17(repoRoot, ".rafa", "issues");
|
|
8394
8590
|
}
|
|
8395
8591
|
function renderLocalIssue(record) {
|
|
8396
8592
|
const { draft } = record;
|
|
@@ -8409,7 +8605,7 @@ function renderLocalIssue(record) {
|
|
|
8409
8605
|
};
|
|
8410
8606
|
const problem = frontmatterProblem(frontmatter) ?? (typeof draft.body === "string" ? null : `body is ${describeValue(draft.body)}, expected a string`);
|
|
8411
8607
|
if (problem !== null) {
|
|
8412
|
-
throw new TypeError(`${
|
|
8608
|
+
throw new TypeError(`${PREFIX8}: refused to write an invalid issue: ${problem}`);
|
|
8413
8609
|
}
|
|
8414
8610
|
return `---
|
|
8415
8611
|
${Bun.YAML.stringify(frontmatter, null, 2)}
|
|
@@ -8420,18 +8616,18 @@ function parseLocalIssue(contents, sourcePath) {
|
|
|
8420
8616
|
const at = sourcePath === undefined ? "" : ` at ${sourcePath}`;
|
|
8421
8617
|
const match = FRONTMATTER.exec(contents);
|
|
8422
8618
|
if (match === null) {
|
|
8423
|
-
throw new Error(`${
|
|
8619
|
+
throw new Error(`${PREFIX8}: the issue${at} has no YAML frontmatter block`);
|
|
8424
8620
|
}
|
|
8425
8621
|
const [, block2 = "", body = ""] = match;
|
|
8426
8622
|
let meta;
|
|
8427
8623
|
try {
|
|
8428
8624
|
meta = Bun.YAML.parse(block2);
|
|
8429
8625
|
} catch (error) {
|
|
8430
|
-
throw new Error(`${
|
|
8626
|
+
throw new Error(`${PREFIX8}: invalid issue${at}: ${messageOf(error)}`, { cause: error });
|
|
8431
8627
|
}
|
|
8432
8628
|
const problem = frontmatterProblem(meta);
|
|
8433
8629
|
if (problem !== null) {
|
|
8434
|
-
throw new Error(`${
|
|
8630
|
+
throw new Error(`${PREFIX8}: invalid issue${at}: ${problem}`);
|
|
8435
8631
|
}
|
|
8436
8632
|
const fields = meta;
|
|
8437
8633
|
return {
|
|
@@ -8462,7 +8658,7 @@ async function listLocalIssues(issuesDir, warn = warnThroughActiveOutput3) {
|
|
|
8462
8658
|
try {
|
|
8463
8659
|
contents = await readFile3(path2, "utf8");
|
|
8464
8660
|
} catch (error) {
|
|
8465
|
-
throw new Error(`${
|
|
8661
|
+
throw new Error(`${PREFIX8}: could not read ${path2}: ${messageOf(error)}`, { cause: error });
|
|
8466
8662
|
}
|
|
8467
8663
|
return { number, path: path2, record: parseLocalIssue(contents, path2) };
|
|
8468
8664
|
}));
|
|
@@ -8491,11 +8687,11 @@ function refFor(number, opt) {
|
|
|
8491
8687
|
}
|
|
8492
8688
|
function issueNumberOfRef2(ref) {
|
|
8493
8689
|
if (ref.kind !== "local") {
|
|
8494
|
-
throw new Error(`${
|
|
8690
|
+
throw new Error(`${PREFIX8}: refused a ref of kind ${describeValue(ref.kind)}; this tracker reads local refs only`);
|
|
8495
8691
|
}
|
|
8496
8692
|
const number = typeof ref.externalId === "string" ? parseIssueNumber(ref.externalId) : null;
|
|
8497
8693
|
if (number === null) {
|
|
8498
|
-
throw new Error(`${
|
|
8694
|
+
throw new Error(`${PREFIX8}: externalId ${describeValue(ref.externalId)} is not a local issue number,` + " expected a positive whole number with no leading zero");
|
|
8499
8695
|
}
|
|
8500
8696
|
return number;
|
|
8501
8697
|
}
|
|
@@ -8510,7 +8706,7 @@ function createLocalTracker(options) {
|
|
|
8510
8706
|
try {
|
|
8511
8707
|
contents = await readFile3(path2, "utf8");
|
|
8512
8708
|
} catch (cause) {
|
|
8513
|
-
throw new Error(`${
|
|
8709
|
+
throw new Error(`${PREFIX8}: no issue ${number} under ${issuesDir}`, { cause });
|
|
8514
8710
|
}
|
|
8515
8711
|
return { number, record: parseLocalIssue(contents, path2) };
|
|
8516
8712
|
}
|
|
@@ -8545,7 +8741,7 @@ function createLocalTracker(options) {
|
|
|
8545
8741
|
throw error;
|
|
8546
8742
|
}
|
|
8547
8743
|
}
|
|
8548
|
-
throw new Error(`${
|
|
8744
|
+
throw new Error(`${PREFIX8}: no issue number is left under ${issuesDir}`);
|
|
8549
8745
|
},
|
|
8550
8746
|
get: async (ref) => {
|
|
8551
8747
|
const { record } = await read(ref);
|
|
@@ -8728,20 +8924,20 @@ function projectLogDirName(repoRoot) {
|
|
|
8728
8924
|
return repoRoot.replace(LOG_DIR_SEPARATORS, "-");
|
|
8729
8925
|
}
|
|
8730
8926
|
function sessionLogDir(repoRoot, home = homedir2()) {
|
|
8731
|
-
return
|
|
8927
|
+
return join18(home, ...PROJECT_LOG_ROOT, projectLogDirName(repoRoot));
|
|
8732
8928
|
}
|
|
8733
8929
|
function listSessionLogs(dir) {
|
|
8734
8930
|
if (!existsSync11(dir)) {
|
|
8735
8931
|
throw new Error(`effort collect: no session log directory at ${dir}` + " (pass logDir, or run with --no-sessions)");
|
|
8736
8932
|
}
|
|
8737
8933
|
const candidates = [];
|
|
8738
|
-
for (const entry of
|
|
8934
|
+
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
8739
8935
|
if (!entry.isFile())
|
|
8740
8936
|
continue;
|
|
8741
8937
|
if (!SESSION_LOG_NAME.test(entry.name))
|
|
8742
8938
|
continue;
|
|
8743
|
-
const path2 =
|
|
8744
|
-
const stats =
|
|
8939
|
+
const path2 = join18(dir, entry.name);
|
|
8940
|
+
const stats = statSync8(path2);
|
|
8745
8941
|
candidates.push({
|
|
8746
8942
|
path: path2,
|
|
8747
8943
|
sessionId: sessionIdFromPath(path2),
|
|
@@ -8839,7 +9035,7 @@ function parseCollectArgs(args) {
|
|
|
8839
9035
|
};
|
|
8840
9036
|
}
|
|
8841
9037
|
function readPlanStubs(plansDir) {
|
|
8842
|
-
return existsSync11(plansDir) ? planStubsFromFileNames(
|
|
9038
|
+
return existsSync11(plansDir) ? planStubsFromFileNames(readdirSync3(plansDir)) : [];
|
|
8843
9039
|
}
|
|
8844
9040
|
async function collectSessionRow(candidate, planStubs) {
|
|
8845
9041
|
const enqueue = await findFirstEnqueue(readLines2(candidate.path));
|
|
@@ -8920,7 +9116,7 @@ function resolveSources(options, log) {
|
|
|
8920
9116
|
if (store !== undefined && plansDir !== undefined)
|
|
8921
9117
|
return { store, plansDir };
|
|
8922
9118
|
const { config } = loadConfig({ root, home: options.home }, {}, log);
|
|
8923
|
-
return { store: store ?? selectEffortStore(root, config), plansDir: plansDir ??
|
|
9119
|
+
return { store: store ?? selectEffortStore(root, config), plansDir: plansDir ?? resolve10(root, config.planDir) };
|
|
8924
9120
|
}
|
|
8925
9121
|
async function collectEffort(options) {
|
|
8926
9122
|
const { repoRoot } = options;
|
|
@@ -10048,15 +10244,15 @@ var report_default = wrapPhaseZeroCommand({
|
|
|
10048
10244
|
// src/commands/init.ts
|
|
10049
10245
|
import { existsSync as existsSync17, readFileSync as readFileSync16 } from "fs";
|
|
10050
10246
|
import { homedir as homedir4 } from "os";
|
|
10051
|
-
import { join as
|
|
10247
|
+
import { join as join23, relative as relative2, sep as sep3 } from "path";
|
|
10052
10248
|
|
|
10053
10249
|
// src/agents/vendorable.ts
|
|
10054
|
-
import { readdirSync as
|
|
10055
|
-
import { isAbsolute as isAbsolute5, join as
|
|
10250
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync12 } from "fs";
|
|
10251
|
+
import { isAbsolute as isAbsolute5, join as join19 } from "path";
|
|
10056
10252
|
function planFiles(dir) {
|
|
10057
10253
|
let entries;
|
|
10058
10254
|
try {
|
|
10059
|
-
entries =
|
|
10255
|
+
entries = readdirSync4(dir, { withFileTypes: true });
|
|
10060
10256
|
} catch {
|
|
10061
10257
|
return [];
|
|
10062
10258
|
}
|
|
@@ -10070,11 +10266,11 @@ function readText3(path2) {
|
|
|
10070
10266
|
}
|
|
10071
10267
|
}
|
|
10072
10268
|
function vendorableAgents(scan) {
|
|
10073
|
-
const dir = isAbsolute5(scan.planDir) ? scan.planDir :
|
|
10269
|
+
const dir = isAbsolute5(scan.planDir) ? scan.planDir : join19(scan.repoRoot, scan.planDir);
|
|
10074
10270
|
const roster = resolveAgentRoster({ repoRoot: scan.repoRoot, home: scan.home }, scan.settingSources);
|
|
10075
10271
|
const found = [];
|
|
10076
10272
|
for (const file of planFiles(dir)) {
|
|
10077
|
-
const path2 =
|
|
10273
|
+
const path2 = join19(dir, file);
|
|
10078
10274
|
const markdown = readText3(path2);
|
|
10079
10275
|
if (markdown === null)
|
|
10080
10276
|
continue;
|
|
@@ -10100,12 +10296,12 @@ function vendorableAgentWarnings(agents, root) {
|
|
|
10100
10296
|
|
|
10101
10297
|
// src/project/gitignore.ts
|
|
10102
10298
|
import { createHash } from "crypto";
|
|
10103
|
-
import { existsSync as existsSync15, mkdirSync as
|
|
10104
|
-
import { dirname as dirname8, isAbsolute as isAbsolute6, join as
|
|
10299
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync8, readFileSync as readFileSync13, writeFileSync as writeFileSync5 } from "fs";
|
|
10300
|
+
import { dirname as dirname8, isAbsolute as isAbsolute6, join as join20 } from "path";
|
|
10105
10301
|
var GITIGNORE_FILE = ".gitignore";
|
|
10106
10302
|
var DIGEST_NAME = "tracking.digest";
|
|
10107
10303
|
var PRIVATE_TRIAGE = "triage/private";
|
|
10108
|
-
var TRACKING_DIGEST_FILE =
|
|
10304
|
+
var TRACKING_DIGEST_FILE = join20(SCOPE_DIR, DIGEST_NAME);
|
|
10109
10305
|
var BLOCK_BEGIN = "# >>> rafa tracking: rafa rewrites the lines up to the end marker";
|
|
10110
10306
|
var BLOCK_END = "# <<< rafa tracking";
|
|
10111
10307
|
var SUB_PATHS = [
|
|
@@ -10193,7 +10389,7 @@ function readIfPresent3(file) {
|
|
|
10193
10389
|
}
|
|
10194
10390
|
function writeTrackingGitignore(root, flags) {
|
|
10195
10391
|
requireAbsolute2(root);
|
|
10196
|
-
const file =
|
|
10392
|
+
const file = join20(root, GITIGNORE_FILE);
|
|
10197
10393
|
const text2 = readIfPresent3(file);
|
|
10198
10394
|
const next = withTrackingBlock(text2, flags, file);
|
|
10199
10395
|
if (next === text2)
|
|
@@ -10215,7 +10411,7 @@ function trackingDigest(flags) {
|
|
|
10215
10411
|
}
|
|
10216
10412
|
function exposureNotice(root) {
|
|
10217
10413
|
return [
|
|
10218
|
-
`tracking.all is true, so ${
|
|
10414
|
+
`tracking.all is true, so ${join20(root, GITIGNORE_FILE)} no longer ignores ${SCOPE_DIR}/` + " and a commit can carry what it holds, including:",
|
|
10219
10415
|
" - unpatched issues described in specs and plans",
|
|
10220
10416
|
" - effort rows with model names and token counts",
|
|
10221
10417
|
" - instincts that may quote error output",
|
|
@@ -10228,7 +10424,7 @@ function printWarning2(message) {
|
|
|
10228
10424
|
}
|
|
10229
10425
|
function noticeTrackingChange(root, flags, print = printWarning2) {
|
|
10230
10426
|
requireAbsolute2(root);
|
|
10231
|
-
const digestFile =
|
|
10427
|
+
const digestFile = join20(root, TRACKING_DIGEST_FILE);
|
|
10232
10428
|
const recorded = `${trackingDigest(flags)}
|
|
10233
10429
|
`;
|
|
10234
10430
|
if (readIfPresent3(digestFile) === recorded) {
|
|
@@ -10237,7 +10433,7 @@ function noticeTrackingChange(root, flags, print = printWarning2) {
|
|
|
10237
10433
|
if (flags.trackingAll)
|
|
10238
10434
|
print(exposureNotice(root));
|
|
10239
10435
|
attempt(digestFile, "cannot be written", () => {
|
|
10240
|
-
|
|
10436
|
+
mkdirSync8(dirname8(digestFile), { recursive: true });
|
|
10241
10437
|
writeFileSync5(digestFile, recorded);
|
|
10242
10438
|
});
|
|
10243
10439
|
return { digestFile, changed: true, printed: flags.trackingAll };
|
|
@@ -10248,13 +10444,13 @@ function applyTracking(root, flags, print = printWarning2) {
|
|
|
10248
10444
|
}
|
|
10249
10445
|
|
|
10250
10446
|
// src/project/root-choice.ts
|
|
10251
|
-
import { resolve as
|
|
10447
|
+
import { resolve as resolve12 } from "path";
|
|
10252
10448
|
import { createInterface as createInterface2 } from "readline";
|
|
10253
10449
|
|
|
10254
10450
|
// src/project/roots.ts
|
|
10255
10451
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
10256
|
-
import { accessSync, constants, readFileSync as readFileSync14, statSync as
|
|
10257
|
-
import { isAbsolute as isAbsolute7, join as
|
|
10452
|
+
import { accessSync, constants, readFileSync as readFileSync14, statSync as statSync9 } from "fs";
|
|
10453
|
+
import { isAbsolute as isAbsolute7, join as join21, resolve as resolve11 } from "path";
|
|
10258
10454
|
var REFUSED_ROOTS = Object.freeze([
|
|
10259
10455
|
{ path: "/", why: "is the filesystem root" },
|
|
10260
10456
|
{ path: "/var", why: "holds system state" },
|
|
@@ -10275,7 +10471,7 @@ var NOT_A_REPOSITORY = "fatal: not a git repository (or any ";
|
|
|
10275
10471
|
var DISK_ROOTS_FILE_SYSTEM = Object.freeze({
|
|
10276
10472
|
...DISK_FILE_SYSTEM,
|
|
10277
10473
|
readFile: (path2) => readFileSync14(path2, "utf8"),
|
|
10278
|
-
isDirectory: (path2) =>
|
|
10474
|
+
isDirectory: (path2) => statSync9(path2).isDirectory(),
|
|
10279
10475
|
assertWritable: (path2) => accessSync(path2, constants.W_OK)
|
|
10280
10476
|
});
|
|
10281
10477
|
|
|
@@ -10317,7 +10513,7 @@ function realOrSpelled(path2, fs2) {
|
|
|
10317
10513
|
try {
|
|
10318
10514
|
return fs2.realpath(path2);
|
|
10319
10515
|
} catch {
|
|
10320
|
-
return
|
|
10516
|
+
return resolve11(path2);
|
|
10321
10517
|
}
|
|
10322
10518
|
}
|
|
10323
10519
|
function namedRoots(home, fs2) {
|
|
@@ -10359,7 +10555,7 @@ function writeRefusal(given, real, fs2) {
|
|
|
10359
10555
|
}
|
|
10360
10556
|
function refusalOf(given, named, fs2) {
|
|
10361
10557
|
const resolved = resolveReal(given, fs2);
|
|
10362
|
-
const byName = namedRefusal(given, resolved.real ??
|
|
10558
|
+
const byName = namedRefusal(given, resolved.real ?? resolve11(given), named);
|
|
10363
10559
|
if (byName !== null)
|
|
10364
10560
|
return byName;
|
|
10365
10561
|
if (resolved.real === null) {
|
|
@@ -10387,8 +10583,8 @@ function readManifest(file, fs2) {
|
|
|
10387
10583
|
}
|
|
10388
10584
|
}
|
|
10389
10585
|
function readDir(dir, fs2) {
|
|
10390
|
-
const manifest = readManifest(
|
|
10391
|
-
const files = MONOREPO_MARKER_FILES.filter((name2) => fs2.exists(
|
|
10586
|
+
const manifest = readManifest(join21(dir, PACKAGE_JSON), fs2);
|
|
10587
|
+
const files = MONOREPO_MARKER_FILES.filter((name2) => fs2.exists(join21(dir, name2)));
|
|
10392
10588
|
const markers = manifest.marks ? [PACKAGE_JSON, ...files] : files;
|
|
10393
10589
|
return { ...manifest, dir, markers };
|
|
10394
10590
|
}
|
|
@@ -10458,7 +10654,7 @@ function firstCandidate(found) {
|
|
|
10458
10654
|
return first.refusal === null ? chosen(first.path, first.source) : problem(first.refusal.reason);
|
|
10459
10655
|
}
|
|
10460
10656
|
function namedRoot(typed, start, source, seams) {
|
|
10461
|
-
const path2 =
|
|
10657
|
+
const path2 = resolve12(start, typed);
|
|
10462
10658
|
const refusal3 = rootRefusal(path2, seams);
|
|
10463
10659
|
if (refusal3 !== null)
|
|
10464
10660
|
return problem(refusal3.reason);
|
|
@@ -10537,8 +10733,8 @@ function createLinePrompter(input, output) {
|
|
|
10537
10733
|
}
|
|
10538
10734
|
|
|
10539
10735
|
// src/project/scaffold.ts
|
|
10540
|
-
import { existsSync as existsSync16, lstatSync as lstatSync2, mkdirSync as
|
|
10541
|
-
import { join as
|
|
10736
|
+
import { existsSync as existsSync16, lstatSync as lstatSync2, mkdirSync as mkdirSync9, statSync as statSync10, writeFileSync as writeFileSync6 } from "fs";
|
|
10737
|
+
import { join as join22 } from "path";
|
|
10542
10738
|
var PROJECT_TREE = Object.freeze(["specs", "plans", "runs", "effort", "instincts"]);
|
|
10543
10739
|
var USER_TREE = Object.freeze(["instincts"]);
|
|
10544
10740
|
var CONFIG_SETTINGS_LINES = Object.freeze([
|
|
@@ -10617,13 +10813,13 @@ class ScaffoldError extends Error {
|
|
|
10617
10813
|
}
|
|
10618
10814
|
function layoutOf(base, tree) {
|
|
10619
10815
|
const scope = scopeAt(base);
|
|
10620
|
-
return { dirs: [scope.dir, ...tree.map((name2) =>
|
|
10816
|
+
return { dirs: [scope.dir, ...tree.map((name2) => join22(scope.dir, name2))], configFile: configFilePath(base) };
|
|
10621
10817
|
}
|
|
10622
10818
|
function occupied(path2) {
|
|
10623
10819
|
return lstatSync2(path2, { throwIfNoEntry: false }) !== undefined;
|
|
10624
10820
|
}
|
|
10625
10821
|
function isDirectory(path2) {
|
|
10626
|
-
return
|
|
10822
|
+
return statSync10(path2, { throwIfNoEntry: false })?.isDirectory() === true;
|
|
10627
10823
|
}
|
|
10628
10824
|
function directoryConflict(dir) {
|
|
10629
10825
|
if (!occupied(dir) || isDirectory(dir))
|
|
@@ -10653,7 +10849,7 @@ function ensureDirectory(path2) {
|
|
|
10653
10849
|
if (existsSync16(path2))
|
|
10654
10850
|
return { path: path2, kind: "directory", change: "unchanged" };
|
|
10655
10851
|
attempt2(path2, "cannot be created", () => {
|
|
10656
|
-
|
|
10852
|
+
mkdirSync9(path2, { recursive: true });
|
|
10657
10853
|
});
|
|
10658
10854
|
return { path: path2, kind: "directory", change: "created" };
|
|
10659
10855
|
}
|
|
@@ -10681,17 +10877,17 @@ function writeUserScope(home) {
|
|
|
10681
10877
|
import { writeFileSync as writeFileSync7 } from "fs";
|
|
10682
10878
|
|
|
10683
10879
|
// src/release/enabled.ts
|
|
10684
|
-
import { statSync as
|
|
10685
|
-
import { resolve as
|
|
10880
|
+
import { statSync as statSync11 } from "fs";
|
|
10881
|
+
import { resolve as resolve13 } from "path";
|
|
10686
10882
|
function isRegularFile(full) {
|
|
10687
10883
|
try {
|
|
10688
|
-
return
|
|
10884
|
+
return statSync11(full).isFile();
|
|
10689
10885
|
} catch {
|
|
10690
10886
|
return false;
|
|
10691
10887
|
}
|
|
10692
10888
|
}
|
|
10693
10889
|
function readFile4(repoRoot, path2) {
|
|
10694
|
-
const resolved =
|
|
10890
|
+
const resolved = resolve13(repoRoot, path2);
|
|
10695
10891
|
return { path: path2, resolved, present: isRegularFile(resolved) };
|
|
10696
10892
|
}
|
|
10697
10893
|
function resolveReleaseEnabled(settings, repoRoot) {
|
|
@@ -10992,7 +11188,7 @@ function resolvedConfig4(root, home, warn) {
|
|
|
10992
11188
|
}
|
|
10993
11189
|
}
|
|
10994
11190
|
function checkGitignore(root, flags) {
|
|
10995
|
-
const file =
|
|
11191
|
+
const file = join23(root, GITIGNORE_FILE);
|
|
10996
11192
|
try {
|
|
10997
11193
|
const text2 = existsSync17(file) ? readFileSync16(file, "utf8") : null;
|
|
10998
11194
|
withTrackingBlock(text2, flags, file);
|
|
@@ -11026,7 +11222,7 @@ function initialise(root, start, home, context) {
|
|
|
11026
11222
|
const config = resolvedConfig4(root.path, home, warn);
|
|
11027
11223
|
checkGitignore(root.path, config);
|
|
11028
11224
|
const configExisted = existsSync17(configFilePath(root.path));
|
|
11029
|
-
const digestExisted = existsSync17(
|
|
11225
|
+
const digestExisted = existsSync17(join23(root.path, TRACKING_DIGEST_FILE));
|
|
11030
11226
|
const project = writeProjectScope(root.path);
|
|
11031
11227
|
const tracking = applyTracking(root.path, config, warn);
|
|
11032
11228
|
const writes = [...project, ...trackingWrites(tracking, digestExisted), ...writeUserScope(home)];
|
|
@@ -11179,11 +11375,11 @@ function createInitCommand(seams = DEFAULT_INIT_SEAMS) {
|
|
|
11179
11375
|
var init_default = createInitCommand();
|
|
11180
11376
|
|
|
11181
11377
|
// src/commands/check-report.ts
|
|
11182
|
-
import { resolve as
|
|
11378
|
+
import { resolve as resolve15 } from "path";
|
|
11183
11379
|
|
|
11184
11380
|
// src/check/references.ts
|
|
11185
|
-
import { existsSync as existsSync18, statSync as
|
|
11186
|
-
import { delimiter as delimiter2, isAbsolute as isAbsolute8, relative as relative3, resolve as
|
|
11381
|
+
import { existsSync as existsSync18, statSync as statSync12 } from "fs";
|
|
11382
|
+
import { delimiter as delimiter2, isAbsolute as isAbsolute8, relative as relative3, resolve as resolve14 } from "path";
|
|
11187
11383
|
|
|
11188
11384
|
// src/schema/stack.ts
|
|
11189
11385
|
var LANGUAGE_EXTENSIONS = {
|
|
@@ -11626,12 +11822,12 @@ function isAbsoluteReference(token) {
|
|
|
11626
11822
|
return KNOWN_ROOTS.includes(`/${first}`) || EXTENSION.test(token);
|
|
11627
11823
|
}
|
|
11628
11824
|
function isInside(root, target) {
|
|
11629
|
-
const step = relative3(
|
|
11825
|
+
const step = relative3(resolve14(root), resolve14(target));
|
|
11630
11826
|
return step !== "" && !step.startsWith("..") && !isAbsolute8(step);
|
|
11631
11827
|
}
|
|
11632
11828
|
function isExecutableFile(path2) {
|
|
11633
11829
|
try {
|
|
11634
|
-
const stats =
|
|
11830
|
+
const stats = statSync12(path2);
|
|
11635
11831
|
return stats.isFile() && (stats.mode & 73) !== 0;
|
|
11636
11832
|
} catch {
|
|
11637
11833
|
return false;
|
|
@@ -11751,21 +11947,21 @@ function issueOf2(code, ref, sentence) {
|
|
|
11751
11947
|
};
|
|
11752
11948
|
}
|
|
11753
11949
|
function scriptIssue(ref, skillDir) {
|
|
11754
|
-
if (existsSync18(
|
|
11950
|
+
if (existsSync18(resolve14(skillDir, ref.text)))
|
|
11755
11951
|
return null;
|
|
11756
11952
|
return issueOf2("missing-script", ref, `${ref.text} names nothing under the skill directory`);
|
|
11757
11953
|
}
|
|
11758
11954
|
function isSkillPath(token, skillDir) {
|
|
11759
|
-
const target =
|
|
11955
|
+
const target = resolve14(skillDir, token);
|
|
11760
11956
|
if (!isInside(skillDir, target))
|
|
11761
11957
|
return false;
|
|
11762
11958
|
if (existsSync18(target))
|
|
11763
11959
|
return true;
|
|
11764
11960
|
const head = token.split("/")[0] ?? "";
|
|
11765
|
-
return head !== "" && head !== "." && head !== ".." && existsSync18(
|
|
11961
|
+
return head !== "" && head !== "." && head !== ".." && existsSync18(resolve14(skillDir, head));
|
|
11766
11962
|
}
|
|
11767
11963
|
function projectPathIssue(ref, projectRoot) {
|
|
11768
|
-
const target =
|
|
11964
|
+
const target = resolve14(projectRoot, ref.text);
|
|
11769
11965
|
if (!isInside(projectRoot, target)) {
|
|
11770
11966
|
return issueOf2("unresolved-path", ref, `${ref.text} points outside the project root`);
|
|
11771
11967
|
}
|
|
@@ -11809,7 +12005,7 @@ function isOffStack(stack) {
|
|
|
11809
12005
|
function toolIssue(ref, pathDirs, functions, offStack) {
|
|
11810
12006
|
if (functions.has(ref.text))
|
|
11811
12007
|
return null;
|
|
11812
|
-
if (pathDirs.some((dir) => isExecutableFile(
|
|
12008
|
+
if (pathDirs.some((dir) => isExecutableFile(resolve14(dir, ref.text))))
|
|
11813
12009
|
return null;
|
|
11814
12010
|
if (offStack) {
|
|
11815
12011
|
return issueOf2("missing-tool-off-stack", ref, `the command ${ref.text} is in no directory of the PATH this run was given,` + " and this file declares a stack this machine need not carry");
|
|
@@ -11843,7 +12039,7 @@ function checkReferences(body, seams) {
|
|
|
11843
12039
|
|
|
11844
12040
|
// src/check/run.ts
|
|
11845
12041
|
import { readFileSync as readFileSync17, writeFileSync as writeFileSync8 } from "fs";
|
|
11846
|
-
import { basename as
|
|
12042
|
+
import { basename as basename8, dirname as dirname9 } from "path";
|
|
11847
12043
|
|
|
11848
12044
|
// src/schema/instinct.ts
|
|
11849
12045
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -12370,8 +12566,8 @@ function checkSkillFrontmatter(data) {
|
|
|
12370
12566
|
}
|
|
12371
12567
|
|
|
12372
12568
|
// src/check/layout.ts
|
|
12373
|
-
import { readdirSync as
|
|
12374
|
-
import { join as
|
|
12569
|
+
import { readdirSync as readdirSync5, statSync as statSync13 } from "fs";
|
|
12570
|
+
import { join as join24, relative as relative4 } from "path";
|
|
12375
12571
|
var SKILL_FILE = "SKILL.md";
|
|
12376
12572
|
var MARKDOWN_EXTENSION = ".md";
|
|
12377
12573
|
var LAYOUT_SEVERITY = Object.freeze({
|
|
@@ -12389,7 +12585,7 @@ function layoutIssue(code, message) {
|
|
|
12389
12585
|
}
|
|
12390
12586
|
function entryKind(path2) {
|
|
12391
12587
|
try {
|
|
12392
|
-
const stats =
|
|
12588
|
+
const stats = statSync13(path2);
|
|
12393
12589
|
if (stats.isFile())
|
|
12394
12590
|
return "file";
|
|
12395
12591
|
if (stats.isDirectory())
|
|
@@ -12400,7 +12596,7 @@ function entryKind(path2) {
|
|
|
12400
12596
|
}
|
|
12401
12597
|
}
|
|
12402
12598
|
function visibleNames(dir) {
|
|
12403
|
-
return
|
|
12599
|
+
return readdirSync5(dir).filter((name2) => !name2.startsWith(".")).sort((left, right) => left.localeCompare(right));
|
|
12404
12600
|
}
|
|
12405
12601
|
function isMarkdown(name2) {
|
|
12406
12602
|
return name2.endsWith(MARKDOWN_EXTENSION);
|
|
@@ -12488,7 +12684,7 @@ function scanGroupMember(root, dir, name2) {
|
|
|
12488
12684
|
const children = visibleNames(dir);
|
|
12489
12685
|
if (children.length === 0)
|
|
12490
12686
|
return [dotfileOnlyEntry("skill", root, dir)];
|
|
12491
|
-
const skillPath =
|
|
12687
|
+
const skillPath = join24(dir, SKILL_FILE);
|
|
12492
12688
|
if (children.includes(SKILL_FILE) && entryKind(skillPath) === "file") {
|
|
12493
12689
|
return [nestedGroupEntry(root, skillPath, name2)];
|
|
12494
12690
|
}
|
|
@@ -12497,7 +12693,7 @@ function scanGroupMember(root, dir, name2) {
|
|
|
12497
12693
|
function scanSkillGroup(root, dir, children) {
|
|
12498
12694
|
const entries = [];
|
|
12499
12695
|
for (const child of children) {
|
|
12500
|
-
const path2 =
|
|
12696
|
+
const path2 = join24(dir, child);
|
|
12501
12697
|
const found = entryKind(path2);
|
|
12502
12698
|
if (found === null) {
|
|
12503
12699
|
entries.push(unreadableEntry("skill", root, path2));
|
|
@@ -12518,7 +12714,7 @@ function scanSkillDirectory(root, dir, name2) {
|
|
|
12518
12714
|
const children = visibleNames(dir);
|
|
12519
12715
|
if (children.length === 0)
|
|
12520
12716
|
return [dotfileOnlyEntry("skill", root, dir)];
|
|
12521
|
-
const skillPath =
|
|
12717
|
+
const skillPath = join24(dir, SKILL_FILE);
|
|
12522
12718
|
if (children.includes(SKILL_FILE) && entryKind(skillPath) === "file") {
|
|
12523
12719
|
return [skillFileEntry(skillPath, name2)];
|
|
12524
12720
|
}
|
|
@@ -12527,7 +12723,7 @@ function scanSkillDirectory(root, dir, name2) {
|
|
|
12527
12723
|
function scanSkillRoot(root) {
|
|
12528
12724
|
const entries = [];
|
|
12529
12725
|
for (const name2 of visibleNames(root)) {
|
|
12530
|
-
const path2 =
|
|
12726
|
+
const path2 = join24(root, name2);
|
|
12531
12727
|
const found = entryKind(path2);
|
|
12532
12728
|
if (found === null) {
|
|
12533
12729
|
entries.push(unreadableEntry("skill", root, path2));
|
|
@@ -12548,7 +12744,7 @@ function scanInstinctSubdirectory(root, dir) {
|
|
|
12548
12744
|
return [dotfileOnlyEntry("instinct", root, dir)];
|
|
12549
12745
|
const entries = [];
|
|
12550
12746
|
for (const child of children) {
|
|
12551
|
-
const path2 =
|
|
12747
|
+
const path2 = join24(dir, child);
|
|
12552
12748
|
const found = entryKind(path2);
|
|
12553
12749
|
if (found === null) {
|
|
12554
12750
|
entries.push(unreadableEntry("instinct", root, path2));
|
|
@@ -12566,7 +12762,7 @@ function scanInstinctSubdirectory(root, dir) {
|
|
|
12566
12762
|
function scanInstinctRoot(root) {
|
|
12567
12763
|
const entries = [];
|
|
12568
12764
|
for (const name2 of visibleNames(root)) {
|
|
12569
|
-
const path2 =
|
|
12765
|
+
const path2 = join24(root, name2);
|
|
12570
12766
|
const found = entryKind(path2);
|
|
12571
12767
|
if (found === null) {
|
|
12572
12768
|
entries.push(unreadableEntry("instinct", root, path2));
|
|
@@ -12672,7 +12868,7 @@ function isMarkdown2(name2) {
|
|
|
12672
12868
|
return name2.endsWith(MARKDOWN_EXTENSION);
|
|
12673
12869
|
}
|
|
12674
12870
|
function fileEntry(path2, kind) {
|
|
12675
|
-
const base =
|
|
12871
|
+
const base = basename8(path2);
|
|
12676
12872
|
const stem = isMarkdown2(base) ? base.slice(0, -MARKDOWN_EXTENSION.length) : base;
|
|
12677
12873
|
if (kind === "instinct") {
|
|
12678
12874
|
return {
|
|
@@ -12684,10 +12880,10 @@ function fileEntry(path2, kind) {
|
|
|
12684
12880
|
issues: []
|
|
12685
12881
|
};
|
|
12686
12882
|
}
|
|
12687
|
-
return base === SKILL_FILE ? { kind, path: path2, isFile: true, shape: "skill-file", name:
|
|
12883
|
+
return base === SKILL_FILE ? { kind, path: path2, isFile: true, shape: "skill-file", name: basename8(dirname9(path2)), issues: [] } : { kind, path: path2, isFile: true, shape: "loose-file", name: stem, issues: [] };
|
|
12688
12884
|
}
|
|
12689
12885
|
function unregisteredIssue(path2, kind) {
|
|
12690
|
-
const base =
|
|
12886
|
+
const base = basename8(path2);
|
|
12691
12887
|
if (kind === "instinct") {
|
|
12692
12888
|
return isMarkdown2(base) ? null : checkerIssue("layout", "unregistered-path", `${base} is not a ${MARKDOWN_EXTENSION} file, so it is no instinct record`);
|
|
12693
12889
|
}
|
|
@@ -12696,7 +12892,7 @@ function unregisteredIssue(path2, kind) {
|
|
|
12696
12892
|
function skillDirectoryOf(entry) {
|
|
12697
12893
|
if (entry.kind !== "skill" || !entry.isFile)
|
|
12698
12894
|
return null;
|
|
12699
|
-
return
|
|
12895
|
+
return basename8(entry.path) === SKILL_FILE ? dirname9(entry.path) : null;
|
|
12700
12896
|
}
|
|
12701
12897
|
function declaredStack(data) {
|
|
12702
12898
|
const value = data["stack"];
|
|
@@ -12857,7 +13053,7 @@ Usage: ${usage}`);
|
|
|
12857
13053
|
throw new CommandExit(1, `\u274C Expected one directory, and read "${extra}" as a second
|
|
12858
13054
|
Usage: ${usage}`);
|
|
12859
13055
|
}
|
|
12860
|
-
return
|
|
13056
|
+
return resolve15(cwd(), dir);
|
|
12861
13057
|
}
|
|
12862
13058
|
function readFix(value, usage) {
|
|
12863
13059
|
if (value === undefined || value === false || value === "false")
|
|
@@ -12874,7 +13070,7 @@ function readProjectRoot(value, usage, cwd) {
|
|
|
12874
13070
|
throw new CommandExit(1, `\u274C --project needs a path, and the line gave it none
|
|
12875
13071
|
Usage: ${usage}`);
|
|
12876
13072
|
}
|
|
12877
|
-
return
|
|
13073
|
+
return resolve15(cwd(), value);
|
|
12878
13074
|
}
|
|
12879
13075
|
function reportMarker(report2) {
|
|
12880
13076
|
if (report2.failed)
|
|
@@ -12987,16 +13183,16 @@ function createInstinctCheckCommand(seams = DEFAULT_CHECK_SEAMS) {
|
|
|
12987
13183
|
var check_default = createInstinctCheckCommand();
|
|
12988
13184
|
|
|
12989
13185
|
// src/commands/instinct/instinct-records.ts
|
|
12990
|
-
import { readdirSync as
|
|
12991
|
-
import { join as
|
|
13186
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync18, statSync as statSync14 } from "fs";
|
|
13187
|
+
import { join as join26 } from "path";
|
|
12992
13188
|
|
|
12993
13189
|
// src/schema/tiers.ts
|
|
12994
13190
|
import { existsSync as existsSync19, realpathSync as realpathSync3 } from "fs";
|
|
12995
|
-
import { dirname as dirname10, join as
|
|
13191
|
+
import { dirname as dirname10, join as join25 } from "path";
|
|
12996
13192
|
var SKILL_TIERS = ["project", "rafa", "user"];
|
|
12997
13193
|
var INSTINCT_SCOPES2 = ["project", "user"];
|
|
12998
|
-
var CLAUDE_SKILLS_PATH =
|
|
12999
|
-
var RAFA_INSTINCTS_PATH =
|
|
13194
|
+
var CLAUDE_SKILLS_PATH = join25(".claude", "skills");
|
|
13195
|
+
var RAFA_INSTINCTS_PATH = join25(".rafa", "instincts");
|
|
13000
13196
|
var BUNDLED_SKILLS_DIR = "skills";
|
|
13001
13197
|
function isSkillTier(value) {
|
|
13002
13198
|
return SKILL_TIERS.includes(value);
|
|
@@ -13009,14 +13205,14 @@ function realEntry(entry) {
|
|
|
13009
13205
|
}
|
|
13010
13206
|
}
|
|
13011
13207
|
function bundledSkillsDirectory(entry) {
|
|
13012
|
-
return
|
|
13208
|
+
return join25(dirname10(realEntry(entry ?? Bun.main)), BUNDLED_SKILLS_DIR);
|
|
13013
13209
|
}
|
|
13014
13210
|
function skillTierDirectory(tier, seams) {
|
|
13015
13211
|
if (tier === "user")
|
|
13016
|
-
return
|
|
13212
|
+
return join25(seams.home, CLAUDE_SKILLS_PATH);
|
|
13017
13213
|
if (tier === "rafa")
|
|
13018
13214
|
return bundledSkillsDirectory(seams.entry);
|
|
13019
|
-
return seams.projectRoot === null ? null :
|
|
13215
|
+
return seams.projectRoot === null ? null : join25(seams.projectRoot, CLAUDE_SKILLS_PATH);
|
|
13020
13216
|
}
|
|
13021
13217
|
function resolveSkillTiers(seams) {
|
|
13022
13218
|
const located = [];
|
|
@@ -13029,8 +13225,8 @@ function resolveSkillTiers(seams) {
|
|
|
13029
13225
|
}
|
|
13030
13226
|
function instinctScopeDirectory(scope, seams) {
|
|
13031
13227
|
if (scope === "user")
|
|
13032
|
-
return
|
|
13033
|
-
return seams.projectRoot === null ? null :
|
|
13228
|
+
return join25(seams.home, RAFA_INSTINCTS_PATH);
|
|
13229
|
+
return seams.projectRoot === null ? null : join25(seams.projectRoot, RAFA_INSTINCTS_PATH);
|
|
13034
13230
|
}
|
|
13035
13231
|
function resolveInstinctScopes(seams) {
|
|
13036
13232
|
const located = [];
|
|
@@ -13068,13 +13264,13 @@ function readRecord(path2, scope, id) {
|
|
|
13068
13264
|
}
|
|
13069
13265
|
function isFile2(path2) {
|
|
13070
13266
|
try {
|
|
13071
|
-
return
|
|
13267
|
+
return statSync14(path2).isFile();
|
|
13072
13268
|
} catch {
|
|
13073
13269
|
return false;
|
|
13074
13270
|
}
|
|
13075
13271
|
}
|
|
13076
13272
|
function recordFiles(dir) {
|
|
13077
|
-
return
|
|
13273
|
+
return readdirSync6(dir).filter((name2) => isRecordFile(name2) && isFile2(join26(dir, name2))).sort((left, right) => left.localeCompare(right));
|
|
13078
13274
|
}
|
|
13079
13275
|
function readScope(scope, dir) {
|
|
13080
13276
|
if (!tierExists(dir))
|
|
@@ -13083,7 +13279,7 @@ function readScope(scope, dir) {
|
|
|
13083
13279
|
scope,
|
|
13084
13280
|
dir,
|
|
13085
13281
|
exists: true,
|
|
13086
|
-
records: recordFiles(dir).map((name2) => readRecord(
|
|
13282
|
+
records: recordFiles(dir).map((name2) => readRecord(join26(dir, name2), scope, recordId(name2)))
|
|
13087
13283
|
};
|
|
13088
13284
|
}
|
|
13089
13285
|
function readScopes(seams) {
|
|
@@ -13310,7 +13506,7 @@ function createInstinctShowCommand() {
|
|
|
13310
13506
|
var show_default = createInstinctShowCommand();
|
|
13311
13507
|
|
|
13312
13508
|
// src/adapters/tracker/resolve.ts
|
|
13313
|
-
var
|
|
13509
|
+
var PREFIX9 = "tracker chain";
|
|
13314
13510
|
var FAILURE_SEPARATOR = "; ";
|
|
13315
13511
|
function warnThroughActiveOutput4(message) {
|
|
13316
13512
|
activeOutput().warn(message);
|
|
@@ -13351,9 +13547,9 @@ async function resolveTracker(options) {
|
|
|
13351
13547
|
}
|
|
13352
13548
|
attempts = [...attempts, Object.freeze({ kind, ok: false, reason: outcome.reason })];
|
|
13353
13549
|
failures = [...failures, `${kind}: ${outcome.reason}`];
|
|
13354
|
-
log(`${
|
|
13550
|
+
log(`${PREFIX9}: ${kind} unavailable: ${outcome.reason}`);
|
|
13355
13551
|
}
|
|
13356
|
-
throw new Error(`${
|
|
13552
|
+
throw new Error(`${PREFIX9}: no tracker available; tried ${failures.join(FAILURE_SEPARATOR)}`);
|
|
13357
13553
|
}
|
|
13358
13554
|
|
|
13359
13555
|
// src/commands/issue/issue-tracker.ts
|
|
@@ -13507,7 +13703,7 @@ function createIssueCommentCommand(seams = DEFAULT_ISSUE_SEAMS) {
|
|
|
13507
13703
|
var comment_default = createIssueCommentCommand();
|
|
13508
13704
|
|
|
13509
13705
|
// src/triage/triage.ts
|
|
13510
|
-
import { basename as
|
|
13706
|
+
import { basename as basename9, join as join27 } from "path";
|
|
13511
13707
|
|
|
13512
13708
|
// src/effort/store/tracker-refs.ts
|
|
13513
13709
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
@@ -13712,7 +13908,7 @@ function machineFaultSentence(fault) {
|
|
|
13712
13908
|
}
|
|
13713
13909
|
|
|
13714
13910
|
// src/triage/triage.ts
|
|
13715
|
-
var PRIVATE_TRIAGE_DIR =
|
|
13911
|
+
var PRIVATE_TRIAGE_DIR = join27(".rafa", "triage", "private");
|
|
13716
13912
|
var SECRET_ENV_NAMES = ["GITHUB_TOKEN", "ANTHROPIC_API_KEY", "LINEAR_API_KEY"];
|
|
13717
13913
|
var TRIAGE_MODULE = "unassigned";
|
|
13718
13914
|
var BLOCKER_SEPARATOR = "; ";
|
|
@@ -13723,7 +13919,7 @@ var MIN_FENCE_LENGTH = 3;
|
|
|
13723
13919
|
var ISSUE_OPENING = "An out-of-scope bug a rafa task session reported. The loop filed it and" + " dispatches no task for it: a plan that wants it fixed declares a task.";
|
|
13724
13920
|
var COMMENT_OPENING = "Reported again by a rafa task session.";
|
|
13725
13921
|
function privateTriageDir(repoRoot) {
|
|
13726
|
-
return
|
|
13922
|
+
return join27(repoRoot, PRIVATE_TRIAGE_DIR);
|
|
13727
13923
|
}
|
|
13728
13924
|
function createPrivateTriageTracker(repoRoot, options = {}) {
|
|
13729
13925
|
return createLocalTracker({
|
|
@@ -13786,7 +13982,7 @@ function oneLine3(text2) {
|
|
|
13786
13982
|
return text2.replace(/\s+/g, " ").trim();
|
|
13787
13983
|
}
|
|
13788
13984
|
function bugKeyOf(trackerPath, artifact) {
|
|
13789
|
-
return `${
|
|
13985
|
+
return `${basename9(trackerPath)}${KEY_SEPARATOR}${oneLine3(artifact)}`;
|
|
13790
13986
|
}
|
|
13791
13987
|
function fenced(value) {
|
|
13792
13988
|
const runs = Array.from(value.matchAll(/`+/g), (run) => run[0].length);
|
|
@@ -14225,7 +14421,7 @@ function createIssueMoveCommand(seams = DEFAULT_ISSUE_SEAMS) {
|
|
|
14225
14421
|
var move_default = createIssueMoveCommand();
|
|
14226
14422
|
|
|
14227
14423
|
// src/board/issue-board.ts
|
|
14228
|
-
var
|
|
14424
|
+
var PREFIX10 = "board issue";
|
|
14229
14425
|
var REPO_PATH2 = "{owner}/{repo}";
|
|
14230
14426
|
var COMMENT_ID = /^[1-9]\d*$/;
|
|
14231
14427
|
function detailOf7(result, command) {
|
|
@@ -14233,29 +14429,29 @@ function detailOf7(result, command) {
|
|
|
14233
14429
|
return written === "" ? `${command} failed and wrote nothing` : `${command} failed: ${written}`;
|
|
14234
14430
|
}
|
|
14235
14431
|
function failure2(command, result) {
|
|
14236
|
-
return new Error(`${
|
|
14432
|
+
return new Error(`${PREFIX10}: ${detailOf7(result, command)}`);
|
|
14237
14433
|
}
|
|
14238
14434
|
function issueNumber3(value, member) {
|
|
14239
14435
|
if (!Number.isSafeInteger(value) || value < 1) {
|
|
14240
|
-
throw new TypeError(`${
|
|
14436
|
+
throw new TypeError(`${PREFIX10}: ${member} refused issue number ${describeValue(value)}, expected a positive whole number`);
|
|
14241
14437
|
}
|
|
14242
14438
|
return String(value);
|
|
14243
14439
|
}
|
|
14244
14440
|
function commentId(value) {
|
|
14245
14441
|
if (typeof value !== "string" || !COMMENT_ID.test(value)) {
|
|
14246
|
-
throw new TypeError(`${
|
|
14442
|
+
throw new TypeError(`${PREFIX10}: editComment refused comment id ${describeValue(value)},` + " expected a positive whole number with no leading zero");
|
|
14247
14443
|
}
|
|
14248
14444
|
return value;
|
|
14249
14445
|
}
|
|
14250
14446
|
function commentBody2(value, member) {
|
|
14251
14447
|
if (typeof value !== "string") {
|
|
14252
|
-
throw new TypeError(`${
|
|
14448
|
+
throw new TypeError(`${PREFIX10}: ${member} refused a body ${describeValue(value)}, expected a string`);
|
|
14253
14449
|
}
|
|
14254
14450
|
return value;
|
|
14255
14451
|
}
|
|
14256
14452
|
function labelName(value, member, what) {
|
|
14257
14453
|
if (typeof value !== "string" || value === "" || value.startsWith("-")) {
|
|
14258
|
-
throw new TypeError(`${
|
|
14454
|
+
throw new TypeError(`${PREFIX10}: ${member} refused ${what} ${describeValue(value)},` + " expected a non-empty name that does not open with a hyphen");
|
|
14259
14455
|
}
|
|
14260
14456
|
return value;
|
|
14261
14457
|
}
|
|
@@ -14263,20 +14459,20 @@ function parseJson2(stdout, command) {
|
|
|
14263
14459
|
try {
|
|
14264
14460
|
return JSON.parse(stdout);
|
|
14265
14461
|
} catch (error) {
|
|
14266
|
-
throw new Error(`${
|
|
14462
|
+
throw new Error(`${PREFIX10}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
|
|
14267
14463
|
}
|
|
14268
14464
|
}
|
|
14269
14465
|
function readComment(value, command, where) {
|
|
14270
14466
|
if (!isMapping(value)) {
|
|
14271
|
-
throw new Error(`${
|
|
14467
|
+
throw new Error(`${PREFIX10}: ${command} answered ${where} as ${describeValue(value)}, expected a mapping`);
|
|
14272
14468
|
}
|
|
14273
14469
|
const id = value["id"];
|
|
14274
14470
|
if (typeof id !== "number" || !Number.isSafeInteger(id) || id < 1) {
|
|
14275
|
-
throw new Error(`${
|
|
14471
|
+
throw new Error(`${PREFIX10}: ${command} answered ${where}.id as ${describeValue(id)}, expected a positive whole number`);
|
|
14276
14472
|
}
|
|
14277
14473
|
const body = value["body"];
|
|
14278
14474
|
if (typeof body !== "string") {
|
|
14279
|
-
throw new Error(`${
|
|
14475
|
+
throw new Error(`${PREFIX10}: ${command} answered ${where}.body as ${describeValue(body)}, expected a string`);
|
|
14280
14476
|
}
|
|
14281
14477
|
const user = value["user"];
|
|
14282
14478
|
const login = isMapping(user) ? user["login"] : null;
|
|
@@ -14301,7 +14497,7 @@ function createGhIssueBoard(options) {
|
|
|
14301
14497
|
const command = `gh api ${path2}`;
|
|
14302
14498
|
const rows2 = parseJson2(await succeed(["api", path2], command), command);
|
|
14303
14499
|
if (!Array.isArray(rows2)) {
|
|
14304
|
-
throw new Error(`${
|
|
14500
|
+
throw new Error(`${PREFIX10}: ${command} answered ${describeValue(rows2)}, expected a list of comments`);
|
|
14305
14501
|
}
|
|
14306
14502
|
return rows2.map((row2, index) => readComment(row2, command, `comment ${String(index)}`));
|
|
14307
14503
|
},
|
|
@@ -14546,8 +14742,74 @@ function declinedMessage(alternative) {
|
|
|
14546
14742
|
return `#${String(alternative)} was not planned, and nothing was written`;
|
|
14547
14743
|
}
|
|
14548
14744
|
|
|
14745
|
+
// src/board/snapshot-settle.ts
|
|
14746
|
+
import { existsSync as existsSync21, readFileSync as readFileSync19, statSync as statSync15 } from "fs";
|
|
14747
|
+
import { resolve as resolve16 } from "path";
|
|
14748
|
+
var PREFIX11 = "board snapshot settle";
|
|
14749
|
+
var DATE_LENGTH = 10;
|
|
14750
|
+
function savedCopyDate(savedAt) {
|
|
14751
|
+
return savedAt.toISOString().slice(0, DATE_LENGTH);
|
|
14752
|
+
}
|
|
14753
|
+
function refreshQuestion(issue3, savedAt) {
|
|
14754
|
+
return `Issue #${String(issue3)} changed since the saved copy of ${savedCopyDate(savedAt)}.` + " Plan from it as it reads now? [y/N] ";
|
|
14755
|
+
}
|
|
14756
|
+
function notesRebuiltLine(path2, issue3, previous) {
|
|
14757
|
+
return `local notes changed; rebuilt ${path2} from issue #${String(issue3)} and the notes,` + ` the old copy is at ${previous}`;
|
|
14758
|
+
}
|
|
14759
|
+
function readSavedCopy(absolute, path2) {
|
|
14760
|
+
if (!existsSync21(absolute))
|
|
14761
|
+
return null;
|
|
14762
|
+
try {
|
|
14763
|
+
const stat = statSync15(absolute);
|
|
14764
|
+
if (!stat.isFile())
|
|
14765
|
+
return null;
|
|
14766
|
+
return { text: readFileSync19(absolute, "utf8"), savedAt: stat.mtime };
|
|
14767
|
+
} catch (error) {
|
|
14768
|
+
throw new Error(`${PREFIX11}: the saved copy ${path2} could not be read: ${messageOf(error)}`, { cause: error });
|
|
14769
|
+
}
|
|
14770
|
+
}
|
|
14771
|
+
function printChange(change, output) {
|
|
14772
|
+
for (const line of [change.bodyLine, change.notesLine]) {
|
|
14773
|
+
if (line !== null)
|
|
14774
|
+
output.info(line);
|
|
14775
|
+
}
|
|
14776
|
+
}
|
|
14777
|
+
async function settleSpecSnapshot(options) {
|
|
14778
|
+
const { repoRoot, specsDir, issue: issue3, refresh, output } = options;
|
|
14779
|
+
const write = (rewrite) => writeSpecSnapshot({ repoRoot, specsDir, issue: issue3, refresh: rewrite });
|
|
14780
|
+
const path2 = specPath(specsDir, issue3.number, issue3.title);
|
|
14781
|
+
const notes = notesPath(specsDir, issue3.number);
|
|
14782
|
+
if (path2 === notes)
|
|
14783
|
+
return write(refresh);
|
|
14784
|
+
const saved = readSavedCopy(resolve16(repoRoot, path2), path2);
|
|
14785
|
+
if (saved === null)
|
|
14786
|
+
return write(refresh);
|
|
14787
|
+
const change = readSnapshotChange({
|
|
14788
|
+
saved: saved.text,
|
|
14789
|
+
body: issue3.body,
|
|
14790
|
+
notes: readLocalNotes(repoRoot, specsDir, issue3.number),
|
|
14791
|
+
notesPath: notes
|
|
14792
|
+
});
|
|
14793
|
+
if (change.kind === "unchanged")
|
|
14794
|
+
return write(refresh);
|
|
14795
|
+
printChange(change, output);
|
|
14796
|
+
if (change.kind === "notes") {
|
|
14797
|
+
const rebuilt = write(refresh);
|
|
14798
|
+
if (rebuilt.previous !== null)
|
|
14799
|
+
output.info(notesRebuiltLine(rebuilt.path, issue3.number, rebuilt.previous));
|
|
14800
|
+
return rebuilt;
|
|
14801
|
+
}
|
|
14802
|
+
if (refresh)
|
|
14803
|
+
return write(true);
|
|
14804
|
+
const offer = options.offerRefresh ?? null;
|
|
14805
|
+
const accepted2 = offer !== null && await offer({ issue: issue3.number, path: path2, savedAt: saved.savedAt });
|
|
14806
|
+
if (!accepted2)
|
|
14807
|
+
throw new CommandExit(ISSUE_REFUSAL_EXIT, snapshotDiffersMessage(path2, issue3.number));
|
|
14808
|
+
return write(true);
|
|
14809
|
+
}
|
|
14810
|
+
|
|
14549
14811
|
// src/board/spec-source.ts
|
|
14550
|
-
var
|
|
14812
|
+
var PREFIX12 = "board spec source";
|
|
14551
14813
|
var SOURCE_FLAGS = [SPEC_FLAG, ISSUE_FLAG, NEXT_FLAG];
|
|
14552
14814
|
var SOURCE_REFUSAL_EXIT = 1;
|
|
14553
14815
|
function severalSourcesMessage(given) {
|
|
@@ -14666,7 +14928,7 @@ async function settleBlockedPick(options) {
|
|
|
14666
14928
|
async function pickRoadmapIssue(request, options, issues, output) {
|
|
14667
14929
|
const seams = options.roadmap;
|
|
14668
14930
|
if (seams === undefined) {
|
|
14669
|
-
throw new TypeError(`${
|
|
14931
|
+
throw new TypeError(`${PREFIX12}: ${NEXT_FLAG} was resolved with no roadmap seams`);
|
|
14670
14932
|
}
|
|
14671
14933
|
const roadmap = await resolveRoadmapIssue({
|
|
14672
14934
|
configured: request.roadmap ?? seams.configured,
|
|
@@ -14739,7 +15001,14 @@ async function resolveSpecSource(options) {
|
|
|
14739
15001
|
output.info(dryRunLine(describeIssue(read)));
|
|
14740
15002
|
return stopped("dry-run");
|
|
14741
15003
|
}
|
|
14742
|
-
const snapshot =
|
|
15004
|
+
const snapshot = await settleSpecSnapshot({
|
|
15005
|
+
repoRoot,
|
|
15006
|
+
specsDir,
|
|
15007
|
+
issue: read,
|
|
15008
|
+
refresh,
|
|
15009
|
+
offerRefresh: options.offerRefresh ?? null,
|
|
15010
|
+
output
|
|
15011
|
+
});
|
|
14743
15012
|
return Object.freeze({
|
|
14744
15013
|
outcome: "spec",
|
|
14745
15014
|
spec: Object.freeze({
|
|
@@ -14812,6 +15081,7 @@ async function resolvePlanSpec(options) {
|
|
|
14812
15081
|
const offer = offerReady === null || options.dryRun ? undefined : (issue3) => offerReady({ issue: issue3, gh, trust: trust(), output });
|
|
14813
15082
|
const offerAlternative = options.offerAlternative ?? null;
|
|
14814
15083
|
const alternative = offerAlternative === null || options.dryRun ? undefined : offerAlternative;
|
|
15084
|
+
const offerRefresh = options.dryRun ? null : options.offerRefresh ?? null;
|
|
14815
15085
|
const resolution = await resolveSpecSource({
|
|
14816
15086
|
request: options.request,
|
|
14817
15087
|
refresh: options.refresh,
|
|
@@ -14821,6 +15091,7 @@ async function resolvePlanSpec(options) {
|
|
|
14821
15091
|
findSpec: options.findSpec,
|
|
14822
15092
|
issues: createGhSpecIssueReader({ gh }),
|
|
14823
15093
|
inspect: (issue3) => inspectSpecIssue(issue3, trust(), offer),
|
|
15094
|
+
offerRefresh,
|
|
14824
15095
|
roadmap: {
|
|
14825
15096
|
configured: options.roadmapIssue,
|
|
14826
15097
|
search: createGhRoadmapSearch({ gh }),
|
|
@@ -14916,8 +15187,8 @@ function formatRows(rows2) {
|
|
|
14916
15187
|
`);
|
|
14917
15188
|
}
|
|
14918
15189
|
function defaultSleep(ms) {
|
|
14919
|
-
return new Promise((
|
|
14920
|
-
setTimeout(
|
|
15190
|
+
return new Promise((resolve17) => {
|
|
15191
|
+
setTimeout(resolve17, ms);
|
|
14921
15192
|
});
|
|
14922
15193
|
}
|
|
14923
15194
|
async function waitForChecks(options) {
|
|
@@ -15163,7 +15434,7 @@ function compareUrl(remote, branch) {
|
|
|
15163
15434
|
return `https://${segments[0] ?? host}/${path2}/compare/${encodeBranch(branch)}${COMPARE_QUERY}`;
|
|
15164
15435
|
}
|
|
15165
15436
|
// src/pr/gh.ts
|
|
15166
|
-
var
|
|
15437
|
+
var PREFIX13 = "gh pull requests";
|
|
15167
15438
|
var REPO_PATH3 = "{owner}/{repo}";
|
|
15168
15439
|
var SUMMARY_FIELDS = "author,baseRefName,headRefName,isCrossRepository,number,state,title,updatedAt,url";
|
|
15169
15440
|
var DETAIL_FIELDS = `${SUMMARY_FIELDS},body,headRefOid,labels,mergeStateStatus,mergeable`;
|
|
@@ -15192,16 +15463,16 @@ function detailOf8(result, command) {
|
|
|
15192
15463
|
return result.ok ? `${command} exited 0 and wrote nothing` : `${command} exited non-zero and wrote nothing`;
|
|
15193
15464
|
}
|
|
15194
15465
|
function failure3(command, result) {
|
|
15195
|
-
return new Error(`${
|
|
15466
|
+
return new Error(`${PREFIX13}: ${command} failed: ${detailOf8(result, command)}`);
|
|
15196
15467
|
}
|
|
15197
15468
|
function refuse3(command, problem2) {
|
|
15198
|
-
throw new Error(`${
|
|
15469
|
+
throw new Error(`${PREFIX13}: ${command} answered ${problem2}`);
|
|
15199
15470
|
}
|
|
15200
15471
|
function parseJson3(stdout, command) {
|
|
15201
15472
|
try {
|
|
15202
15473
|
return JSON.parse(stdout);
|
|
15203
15474
|
} catch (error) {
|
|
15204
|
-
throw new Error(`${
|
|
15475
|
+
throw new Error(`${PREFIX13}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
|
|
15205
15476
|
}
|
|
15206
15477
|
}
|
|
15207
15478
|
function readString2(value, command, where) {
|
|
@@ -15304,7 +15575,7 @@ function readWorkflowCount(stdout) {
|
|
|
15304
15575
|
}
|
|
15305
15576
|
function pullNumber(value, member) {
|
|
15306
15577
|
if (!Number.isSafeInteger(value) || value < 1) {
|
|
15307
|
-
throw new TypeError(`${
|
|
15578
|
+
throw new TypeError(`${PREFIX13}: ${member} refused pull request number ${describeValue(value)},` + " expected a positive whole number");
|
|
15308
15579
|
}
|
|
15309
15580
|
return String(value);
|
|
15310
15581
|
}
|
|
@@ -15312,13 +15583,13 @@ function textArgument(value, member, what, whole) {
|
|
|
15312
15583
|
const bad = typeof value !== "string" || value === "" || whole && !(WHOLE_NUMBER.test(value) && Number.isSafeInteger(Number(value)));
|
|
15313
15584
|
if (bad) {
|
|
15314
15585
|
const expected = whole ? "a positive whole number with no leading zero" : "a non-empty string";
|
|
15315
|
-
throw new TypeError(`${
|
|
15586
|
+
throw new TypeError(`${PREFIX13}: ${member} refused ${what} ${describeValue(value)}, expected ${expected}`);
|
|
15316
15587
|
}
|
|
15317
15588
|
return value;
|
|
15318
15589
|
}
|
|
15319
15590
|
function bodyText(value, member) {
|
|
15320
15591
|
if (typeof value !== "string") {
|
|
15321
|
-
throw new TypeError(`${
|
|
15592
|
+
throw new TypeError(`${PREFIX13}: ${member} refused a body ${describeValue(value)}, expected a string`);
|
|
15322
15593
|
}
|
|
15323
15594
|
return value;
|
|
15324
15595
|
}
|
|
@@ -15388,7 +15659,7 @@ function createGhPullRequests(options) {
|
|
|
15388
15659
|
merge: async (number, method) => {
|
|
15389
15660
|
const target = pullNumber(number, "merge");
|
|
15390
15661
|
if (!isMergeMethod(method)) {
|
|
15391
|
-
throw new TypeError(`${
|
|
15662
|
+
throw new TypeError(`${PREFIX13}: merge refused method ${describeValue(method)},` + ` expected one of: ${MERGE_METHODS.join(", ")}`);
|
|
15392
15663
|
}
|
|
15393
15664
|
const command = `gh pr merge ${target} --${method}`;
|
|
15394
15665
|
const result = await gh(["pr", "merge", target, `--${method}`]);
|
|
@@ -15623,11 +15894,11 @@ function readStepOutcome(step2, result, plan) {
|
|
|
15623
15894
|
|
|
15624
15895
|
// src/commands/loop/loop-sessions.ts
|
|
15625
15896
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
15626
|
-
import { readFileSync as
|
|
15627
|
-
import { join as
|
|
15897
|
+
import { readFileSync as readFileSync21 } from "fs";
|
|
15898
|
+
import { join as join29, relative as relative5 } from "path";
|
|
15628
15899
|
|
|
15629
15900
|
// src/effort/store/task-finishes.ts
|
|
15630
|
-
import { existsSync as
|
|
15901
|
+
import { existsSync as existsSync22 } from "fs";
|
|
15631
15902
|
var SELECT_FINISHES = `
|
|
15632
15903
|
SELECT collected_at FROM task_reports
|
|
15633
15904
|
WHERE plan_stub IS ? AND outcome = 'done' AND collected_at >= ?
|
|
@@ -15638,7 +15909,7 @@ var SELECT_FINISHES = `
|
|
|
15638
15909
|
`;
|
|
15639
15910
|
function readTaskFinishes(repoRoot, query) {
|
|
15640
15911
|
const path2 = sqliteStorePath(repoRoot);
|
|
15641
|
-
if (!
|
|
15912
|
+
if (!existsSync22(path2))
|
|
15642
15913
|
return [];
|
|
15643
15914
|
const bound = [query.planStub, query.since, query.planStub, query.since];
|
|
15644
15915
|
const rows2 = withSqliteStore(path2, false, (db) => db.query(SELECT_FINISHES).all(...bound));
|
|
@@ -15647,15 +15918,15 @@ function readTaskFinishes(repoRoot, query) {
|
|
|
15647
15918
|
|
|
15648
15919
|
// src/loop/sessions.ts
|
|
15649
15920
|
import {
|
|
15650
|
-
linkSync,
|
|
15651
|
-
mkdirSync as
|
|
15652
|
-
readdirSync as
|
|
15653
|
-
readFileSync as
|
|
15921
|
+
linkSync as linkSync2,
|
|
15922
|
+
mkdirSync as mkdirSync10,
|
|
15923
|
+
readdirSync as readdirSync7,
|
|
15924
|
+
readFileSync as readFileSync20,
|
|
15654
15925
|
renameSync as renameSync2,
|
|
15655
15926
|
rmSync,
|
|
15656
15927
|
writeFileSync as writeFileSync9
|
|
15657
15928
|
} from "fs";
|
|
15658
|
-
import { basename as
|
|
15929
|
+
import { basename as basename10, dirname as dirname11, join as join28 } from "path";
|
|
15659
15930
|
var SESSION_STATES = Object.freeze(["running", "paused", "stopped", "done"]);
|
|
15660
15931
|
var RUNS_DIR = "runs";
|
|
15661
15932
|
var RECORD_EXTENSION = ".json";
|
|
@@ -15695,13 +15966,13 @@ function isSessionId(value) {
|
|
|
15695
15966
|
return SESSION_ID_PATTERN.test(value);
|
|
15696
15967
|
}
|
|
15697
15968
|
function runsDir(root) {
|
|
15698
|
-
return
|
|
15969
|
+
return join28(scopeAt(root).dir, RUNS_DIR);
|
|
15699
15970
|
}
|
|
15700
15971
|
function sessionFilePath(root, sessionId) {
|
|
15701
15972
|
if (!isSessionId(sessionId)) {
|
|
15702
15973
|
throw new Error(`session record: unusable session id ${JSON.stringify(sessionId)}`);
|
|
15703
15974
|
}
|
|
15704
|
-
return
|
|
15975
|
+
return join28(runsDir(root), `${sessionId}${RECORD_EXTENSION}`);
|
|
15705
15976
|
}
|
|
15706
15977
|
function describeValue7(value) {
|
|
15707
15978
|
return value === undefined ? "missing" : JSON.stringify(value) ?? String(value);
|
|
@@ -15732,7 +16003,7 @@ function sessionIdProblem(sessionId, file) {
|
|
|
15732
16003
|
if (typeof sessionId !== "string" || !isSessionId(sessionId)) {
|
|
15733
16004
|
return `sessionId is ${describeValue7(sessionId)}, expected a plain file name`;
|
|
15734
16005
|
}
|
|
15735
|
-
return
|
|
16006
|
+
return basename10(file) === `${sessionId}${RECORD_EXTENSION}` ? null : `sessionId ${JSON.stringify(sessionId)} is not the file's name`;
|
|
15736
16007
|
}
|
|
15737
16008
|
function planStubProblem(planStub2) {
|
|
15738
16009
|
return planStub2 === null || typeof planStub2 === "string" && isStampableStub(planStub2) ? null : `planStub is ${describeValue7(planStub2)}, expected null or a plan stub`;
|
|
@@ -15803,7 +16074,7 @@ function readState2(record, isAlive = isPidAlive) {
|
|
|
15803
16074
|
function readRecordFile(file) {
|
|
15804
16075
|
let text2;
|
|
15805
16076
|
try {
|
|
15806
|
-
text2 =
|
|
16077
|
+
text2 = readFileSync20(file, "utf8");
|
|
15807
16078
|
} catch (error) {
|
|
15808
16079
|
throw new SessionRecordError(file, `cannot be read: ${messageOf(error)}`);
|
|
15809
16080
|
}
|
|
@@ -15811,7 +16082,7 @@ function readRecordFile(file) {
|
|
|
15811
16082
|
}
|
|
15812
16083
|
function recordNames(dir) {
|
|
15813
16084
|
try {
|
|
15814
|
-
return
|
|
16085
|
+
return readdirSync7(dir);
|
|
15815
16086
|
} catch (error) {
|
|
15816
16087
|
if (errorCode(error) === "ENOENT")
|
|
15817
16088
|
return [];
|
|
@@ -15824,7 +16095,7 @@ function byStart(a, b) {
|
|
|
15824
16095
|
function readSessions(root, seams = {}) {
|
|
15825
16096
|
const dir = runsDir(root);
|
|
15826
16097
|
const isAlive = seams.isAlive ?? isPidAlive;
|
|
15827
|
-
const records = recordNames(dir).filter((name2) => name2.endsWith(RECORD_EXTENSION)).map((name2) => readRecordFile(
|
|
16098
|
+
const records = recordNames(dir).filter((name2) => name2.endsWith(RECORD_EXTENSION)).map((name2) => readRecordFile(join28(dir, name2))).map((record) => Object.freeze({ ...record, state: readState2(record, isAlive) }));
|
|
15828
16099
|
return Object.freeze(records.sort(byStart));
|
|
15829
16100
|
}
|
|
15830
16101
|
function readSession(root, sessionId, seams = {}) {
|
|
@@ -15850,13 +16121,13 @@ function checkRecord(record, file) {
|
|
|
15850
16121
|
}
|
|
15851
16122
|
function writeRecordFile(file, record, isNew) {
|
|
15852
16123
|
checkRecord(record, file);
|
|
15853
|
-
|
|
16124
|
+
mkdirSync10(dirname11(file), { recursive: true });
|
|
15854
16125
|
const temporary = `${file}.${process.pid}.tmp`;
|
|
15855
16126
|
writeFileSync9(temporary, `${JSON.stringify(record, null, 2)}
|
|
15856
16127
|
`);
|
|
15857
16128
|
try {
|
|
15858
16129
|
if (isNew)
|
|
15859
|
-
|
|
16130
|
+
linkSync2(temporary, file);
|
|
15860
16131
|
else
|
|
15861
16132
|
renameSync2(temporary, file);
|
|
15862
16133
|
} finally {
|
|
@@ -16010,11 +16281,11 @@ function writeSession(root, record, change) {
|
|
|
16010
16281
|
}
|
|
16011
16282
|
}
|
|
16012
16283
|
function readSessionChecklist(root, record) {
|
|
16013
|
-
const plan =
|
|
16284
|
+
const plan = join29(root, record.plan);
|
|
16014
16285
|
const file = [trackerPathFor(plan), plan].find((path2) => isFile(path2));
|
|
16015
16286
|
if (file === undefined)
|
|
16016
16287
|
return null;
|
|
16017
|
-
const content =
|
|
16288
|
+
const content = readFileSync21(file, "utf8");
|
|
16018
16289
|
return { file, tasks: parsePlan(content).tasks, lines: content.split(`
|
|
16019
16290
|
`) };
|
|
16020
16291
|
}
|
|
@@ -16052,7 +16323,7 @@ function etaLine(eta) {
|
|
|
16052
16323
|
}
|
|
16053
16324
|
|
|
16054
16325
|
// src/next/sources.ts
|
|
16055
|
-
var
|
|
16326
|
+
var PREFIX14 = "rafa next";
|
|
16056
16327
|
var DEFAULT_BASE_BRANCH = "main";
|
|
16057
16328
|
var DEFAULT_NEXT_SOURCE_SEAMS = Object.freeze({});
|
|
16058
16329
|
function memoiseIssues2(issues) {
|
|
@@ -16090,7 +16361,7 @@ function ghNextBoard(options) {
|
|
|
16090
16361
|
}
|
|
16091
16362
|
function nextProject(context) {
|
|
16092
16363
|
if (context.project === null)
|
|
16093
|
-
throw new Error(`${
|
|
16364
|
+
throw new Error(`${PREFIX14} runs inside a project, and was handed none`);
|
|
16094
16365
|
return context.project;
|
|
16095
16366
|
}
|
|
16096
16367
|
function nextConfig(project, warn) {
|
|
@@ -16100,7 +16371,7 @@ function nextConfig(project, warn) {
|
|
|
16100
16371
|
if (!(error instanceof ConfigError))
|
|
16101
16372
|
throw error;
|
|
16102
16373
|
throw new CommandExit(1, [
|
|
16103
|
-
`\u274C ${
|
|
16374
|
+
`\u274C ${PREFIX14}: the config cannot be used:`,
|
|
16104
16375
|
...error.problems.map((problem2) => ` ${problem2}`)
|
|
16105
16376
|
].join(`
|
|
16106
16377
|
`));
|
|
@@ -16119,33 +16390,38 @@ function openNextSources(context, seams = DEFAULT_NEXT_SOURCE_SEAMS) {
|
|
|
16119
16390
|
const git = (seams.openGit ?? createGitRunner)(project.root);
|
|
16120
16391
|
const gh = (seams.openGh ?? ((root) => createGhRunner({ cwd: root })))(project.root);
|
|
16121
16392
|
const loop = resolveLoopSeams({ isAlive: seams.isAlive });
|
|
16122
|
-
|
|
16393
|
+
const board = () => ghNextBoard({ gh, git, configured: config.roadmapIssue });
|
|
16394
|
+
const held = {
|
|
16123
16395
|
base: config.prBase ?? DEFAULT_BASE_BRANCH,
|
|
16124
16396
|
plans: plansDirAt(project.root, config.planDir),
|
|
16125
16397
|
runs: () => readRecords(project.root, loop),
|
|
16126
16398
|
git,
|
|
16127
|
-
pulls: (seams.pullRequests ?? ghPullRequestsIn)(project.root)
|
|
16128
|
-
|
|
16399
|
+
pulls: (seams.pullRequests ?? ghPullRequestsIn)(project.root)
|
|
16400
|
+
};
|
|
16401
|
+
return Object.freeze({
|
|
16402
|
+
...held,
|
|
16403
|
+
board: board(),
|
|
16404
|
+
answer: () => Object.freeze({ ...held, board: board() })
|
|
16129
16405
|
});
|
|
16130
16406
|
}
|
|
16131
16407
|
|
|
16132
16408
|
// src/commands/plan/list.ts
|
|
16133
|
-
import { existsSync as
|
|
16134
|
-
import { join as
|
|
16409
|
+
import { existsSync as existsSync23, readdirSync as readdirSync8, readFileSync as readFileSync22 } from "fs";
|
|
16410
|
+
import { join as join30 } from "path";
|
|
16135
16411
|
var USAGE10 = "rafa plan list";
|
|
16136
16412
|
function listing(dir, stub) {
|
|
16137
|
-
const plan =
|
|
16138
|
-
const trackerPath =
|
|
16413
|
+
const plan = join30(dir, planFileName(stub, false));
|
|
16414
|
+
const trackerPath = join30(dir, planFileName(stub, true));
|
|
16139
16415
|
const tracker = isFile(trackerPath) ? trackerPath : null;
|
|
16140
|
-
const planModel = parsePlan(
|
|
16141
|
-
const counted3 = tracker === null ? planModel : parsePlan(
|
|
16416
|
+
const planModel = parsePlan(readFileSync22(plan, "utf8"));
|
|
16417
|
+
const counted3 = tracker === null ? planModel : parsePlan(readFileSync22(tracker, "utf8"));
|
|
16142
16418
|
return { stub, plan, tracker, tasks: countTasks(counted3.tasks), issues: planModel.issues.length };
|
|
16143
16419
|
}
|
|
16144
16420
|
function listPlans(plans) {
|
|
16145
16421
|
const dir = plans.path;
|
|
16146
|
-
if (!
|
|
16422
|
+
if (!existsSync23(dir))
|
|
16147
16423
|
return { dir, plans: [] };
|
|
16148
|
-
const stubs =
|
|
16424
|
+
const stubs = readdirSync8(dir).filter((name2) => isFile(join30(dir, name2))).map((name2) => stubOfPlanFile(name2)).filter((stub) => stub !== null).sort((a, b) => a.localeCompare(b));
|
|
16149
16425
|
return { dir, plans: stubs.map((stub) => listing(dir, stub)) };
|
|
16150
16426
|
}
|
|
16151
16427
|
function rowNotes(plan) {
|
|
@@ -16340,7 +16616,7 @@ function branchLabel(world) {
|
|
|
16340
16616
|
}
|
|
16341
16617
|
|
|
16342
16618
|
// src/next/state.ts
|
|
16343
|
-
var
|
|
16619
|
+
var PREFIX15 = "rafa next";
|
|
16344
16620
|
var MAX_NAMED_FILES = 5;
|
|
16345
16621
|
var NEXT_PRECONDITIONS = Object.freeze([
|
|
16346
16622
|
"tree-modified",
|
|
@@ -16618,7 +16894,7 @@ async function readNextState(sources) {
|
|
|
16618
16894
|
if (found !== null)
|
|
16619
16895
|
return answer(found, world.problems());
|
|
16620
16896
|
}
|
|
16621
|
-
throw new Error(`${
|
|
16897
|
+
throw new Error(`${PREFIX15}: no row of the table answered, and the last row answers for every reading`);
|
|
16622
16898
|
}
|
|
16623
16899
|
|
|
16624
16900
|
// src/next/hint.ts
|
|
@@ -16644,8 +16920,8 @@ function wantsHint(flags) {
|
|
|
16644
16920
|
}
|
|
16645
16921
|
var DEFAULT_HINT_SEAMS = Object.freeze({});
|
|
16646
16922
|
function afterDelay(ms) {
|
|
16647
|
-
return new Promise((
|
|
16648
|
-
setTimeout(
|
|
16923
|
+
return new Promise((resolve17) => {
|
|
16924
|
+
setTimeout(resolve17, ms).unref();
|
|
16649
16925
|
});
|
|
16650
16926
|
}
|
|
16651
16927
|
function silenced() {
|
|
@@ -16699,10 +16975,10 @@ async function nextStepHint(context, seams = DEFAULT_HINT_SEAMS) {
|
|
|
16699
16975
|
}
|
|
16700
16976
|
|
|
16701
16977
|
// src/next/actions.ts
|
|
16702
|
-
var
|
|
16978
|
+
var PREFIX16 = "rafa next";
|
|
16703
16979
|
function needed(state, value, what) {
|
|
16704
16980
|
if (value === null) {
|
|
16705
|
-
throw new Error(`${
|
|
16981
|
+
throw new Error(`${PREFIX16}: state "${state.id}" proposes "${state.action}" and names no ${what}`);
|
|
16706
16982
|
}
|
|
16707
16983
|
return String(value);
|
|
16708
16984
|
}
|
|
@@ -16752,12 +17028,12 @@ function actionContext(caller, command, argv) {
|
|
|
16752
17028
|
async function runAction(caller, state) {
|
|
16753
17029
|
const invocation = actionInvocation(state);
|
|
16754
17030
|
if (invocation === null) {
|
|
16755
|
-
throw new Error(`${
|
|
17031
|
+
throw new Error(`${PREFIX16}: the action "${state.action}" of state "${state.id}" runs no registered command`);
|
|
16756
17032
|
}
|
|
16757
17033
|
const spec = ACTION_COMMANDS[invocation.action];
|
|
16758
17034
|
const command = caller.registry.find(spec.subject, spec.action);
|
|
16759
17035
|
if (command === undefined) {
|
|
16760
|
-
throw new CommandExit(1, `\u274C ${
|
|
17036
|
+
throw new CommandExit(1, `\u274C ${PREFIX16}: the "${invocation.action}" step runs "rafa ${invocation.command}", which is registered by nothing`);
|
|
16761
17037
|
}
|
|
16762
17038
|
await command.run(actionContext(caller, command, invocation.argv));
|
|
16763
17039
|
}
|
|
@@ -17042,7 +17318,7 @@ var UNBLOCK_USAGE = "rafa issue unblock [<n>] [--all]";
|
|
|
17042
17318
|
var ALL_FLAG = "all";
|
|
17043
17319
|
var ISSUE_NUMBER4 = /^[1-9]\d*$/u;
|
|
17044
17320
|
var YES_ANSWERS3 = ["y", "yes"];
|
|
17045
|
-
var
|
|
17321
|
+
var PREFIX17 = "board unblock";
|
|
17046
17322
|
function nameIds2(ids) {
|
|
17047
17323
|
return ids.map((id) => `#${String(id)}`).join(" ");
|
|
17048
17324
|
}
|
|
@@ -17056,32 +17332,32 @@ function detailOf9(result, command) {
|
|
|
17056
17332
|
async function rowsOf2(gh, args, command) {
|
|
17057
17333
|
const result = await gh(args);
|
|
17058
17334
|
if (!result.ok)
|
|
17059
|
-
throw new Error(`${
|
|
17335
|
+
throw new Error(`${PREFIX17}: ${detailOf9(result, command)}`);
|
|
17060
17336
|
let parsed;
|
|
17061
17337
|
try {
|
|
17062
17338
|
parsed = JSON.parse(result.stdout);
|
|
17063
17339
|
} catch (error) {
|
|
17064
|
-
throw new Error(`${
|
|
17340
|
+
throw new Error(`${PREFIX17}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
|
|
17065
17341
|
}
|
|
17066
17342
|
if (!Array.isArray(parsed)) {
|
|
17067
|
-
throw new Error(`${
|
|
17343
|
+
throw new Error(`${PREFIX17}: ${command} answered ${describeValue(parsed)}, expected a list of issues`);
|
|
17068
17344
|
}
|
|
17069
17345
|
return parsed;
|
|
17070
17346
|
}
|
|
17071
17347
|
function rowNumber4(row2, command, where) {
|
|
17072
17348
|
if (!isMapping(row2)) {
|
|
17073
|
-
throw new Error(`${
|
|
17349
|
+
throw new Error(`${PREFIX17}: ${command} answered ${where} as ${describeValue(row2)}, expected a mapping`);
|
|
17074
17350
|
}
|
|
17075
17351
|
const number = row2["number"];
|
|
17076
17352
|
if (typeof number !== "number" || !Number.isSafeInteger(number) || number < 1) {
|
|
17077
|
-
throw new Error(`${
|
|
17353
|
+
throw new Error(`${PREFIX17}: ${command} answered ${where}.number as ${describeValue(number)}, expected a positive whole number`);
|
|
17078
17354
|
}
|
|
17079
17355
|
return number;
|
|
17080
17356
|
}
|
|
17081
17357
|
function rowBody2(row2, command, where) {
|
|
17082
17358
|
const body = isMapping(row2) ? row2["body"] : null;
|
|
17083
17359
|
if (typeof body !== "string") {
|
|
17084
|
-
throw new Error(`${
|
|
17360
|
+
throw new Error(`${PREFIX17}: ${command} answered ${where}.body as ${describeValue(body)}, expected a string`);
|
|
17085
17361
|
}
|
|
17086
17362
|
return body;
|
|
17087
17363
|
}
|
|
@@ -17723,9 +17999,9 @@ async function handleRedChecks(io, branch, prNumber, rows2) {
|
|
|
17723
17999
|
}
|
|
17724
18000
|
|
|
17725
18001
|
// src/start/runtime.ts
|
|
17726
|
-
import { existsSync as
|
|
18002
|
+
import { existsSync as existsSync24, readdirSync as readdirSync9, realpathSync as realpathSync4, statSync as statSync16 } from "fs";
|
|
17727
18003
|
import { homedir as homedir5 } from "os";
|
|
17728
|
-
import { isAbsolute as isAbsolute9, join as
|
|
18004
|
+
import { isAbsolute as isAbsolute9, join as join31, relative as relative7, resolve as resolve17, sep as sep4 } from "path";
|
|
17729
18005
|
|
|
17730
18006
|
// src/start/session.ts
|
|
17731
18007
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
@@ -17876,7 +18152,7 @@ function announcePlanIssues(planContent) {
|
|
|
17876
18152
|
|
|
17877
18153
|
// src/start/runtime.ts
|
|
17878
18154
|
var RUNTIME_FLAG = "--runtime";
|
|
17879
|
-
var RUNTIME_SUBDIR =
|
|
18155
|
+
var RUNTIME_SUBDIR = join31(".rafa", "runtime");
|
|
17880
18156
|
var RUNTIME_ENTRY = "cli.js";
|
|
17881
18157
|
var SOURCE_DIR = "src";
|
|
17882
18158
|
var RUNTIME_COMMAND = ["start"];
|
|
@@ -17903,7 +18179,7 @@ function resolveRuntime(value, places) {
|
|
|
17903
18179
|
throw refusal5([`\u274C Refusing to start: \`--runtime\` names no runtime. Spell it ${SPELLING}.`]);
|
|
17904
18180
|
}
|
|
17905
18181
|
const isVersion = VERSION_SHAPE.test(value);
|
|
17906
|
-
const named = isVersion ?
|
|
18182
|
+
const named = isVersion ? join31(places.home, RUNTIME_SUBDIR, value, RUNTIME_ENTRY) : resolve17(places.cwd, value);
|
|
17907
18183
|
refuseInsideSource(value, named, places);
|
|
17908
18184
|
const entry = entryOf(value, named, isVersion, places.home);
|
|
17909
18185
|
const real = realpathSync4(entry);
|
|
@@ -17914,7 +18190,7 @@ function entryOf(value, named, isVersion, home) {
|
|
|
17914
18190
|
if (isVersion) {
|
|
17915
18191
|
if (isFile3(named))
|
|
17916
18192
|
return named;
|
|
17917
|
-
const root =
|
|
18193
|
+
const root = join31(home, RUNTIME_SUBDIR);
|
|
17918
18194
|
const installed = installedVersions(root);
|
|
17919
18195
|
throw refusal5([
|
|
17920
18196
|
`\u274C Refusing to start: \`--runtime=${value}\` names no installed runtime: ${named} is no file.`,
|
|
@@ -17923,7 +18199,7 @@ function entryOf(value, named, isVersion, home) {
|
|
|
17923
18199
|
}
|
|
17924
18200
|
if (isFile3(named))
|
|
17925
18201
|
return named;
|
|
17926
|
-
const inside =
|
|
18202
|
+
const inside = join31(named, RUNTIME_ENTRY);
|
|
17927
18203
|
if (isDirectory2(named) && isFile3(inside))
|
|
17928
18204
|
return inside;
|
|
17929
18205
|
throw refusal5([
|
|
@@ -17941,8 +18217,8 @@ function refuseInsideSource(value, path2, places) {
|
|
|
17941
18217
|
]);
|
|
17942
18218
|
}
|
|
17943
18219
|
function sourceDirs({ cwd, root }) {
|
|
17944
|
-
const dirs = [
|
|
17945
|
-
const real = dirs.filter((dir) =>
|
|
18220
|
+
const dirs = [join31(cwd, SOURCE_DIR), join31(root, SOURCE_DIR)];
|
|
18221
|
+
const real = dirs.filter((dir) => existsSync24(dir)).map((dir) => realpathSync4(dir));
|
|
17946
18222
|
return [...new Set([...dirs, ...real])];
|
|
17947
18223
|
}
|
|
17948
18224
|
function isInside2(path2, dir) {
|
|
@@ -17951,21 +18227,21 @@ function isInside2(path2, dir) {
|
|
|
17951
18227
|
}
|
|
17952
18228
|
function installedVersions(root) {
|
|
17953
18229
|
try {
|
|
17954
|
-
return
|
|
18230
|
+
return readdirSync9(root).filter((name2) => isFile3(join31(root, name2, RUNTIME_ENTRY))).sort((a, b) => a.localeCompare(b));
|
|
17955
18231
|
} catch {
|
|
17956
18232
|
return [];
|
|
17957
18233
|
}
|
|
17958
18234
|
}
|
|
17959
18235
|
function isFile3(path2) {
|
|
17960
18236
|
try {
|
|
17961
|
-
return
|
|
18237
|
+
return statSync16(path2).isFile();
|
|
17962
18238
|
} catch {
|
|
17963
18239
|
return false;
|
|
17964
18240
|
}
|
|
17965
18241
|
}
|
|
17966
18242
|
function isDirectory2(path2) {
|
|
17967
18243
|
try {
|
|
17968
|
-
return
|
|
18244
|
+
return statSync16(path2).isDirectory();
|
|
17969
18245
|
} catch {
|
|
17970
18246
|
return false;
|
|
17971
18247
|
}
|
|
@@ -17996,7 +18272,7 @@ function realOrResolved(path2) {
|
|
|
17996
18272
|
try {
|
|
17997
18273
|
return realpathSync4(path2);
|
|
17998
18274
|
} catch {
|
|
17999
|
-
return
|
|
18275
|
+
return resolve17(path2);
|
|
18000
18276
|
}
|
|
18001
18277
|
}
|
|
18002
18278
|
async function runRuntime(entry, words, run) {
|
|
@@ -18091,13 +18367,13 @@ import { fileURLToPath as fileURLToPath2 } from "url";
|
|
|
18091
18367
|
import { homedir as homedir6 } from "os";
|
|
18092
18368
|
|
|
18093
18369
|
// src/notices/notices.ts
|
|
18094
|
-
import { mkdirSync as
|
|
18095
|
-
import { dirname as dirname12, join as
|
|
18370
|
+
import { mkdirSync as mkdirSync11, readFileSync as readFileSync23, writeFileSync as writeFileSync10 } from "fs";
|
|
18371
|
+
import { dirname as dirname12, join as join32 } from "path";
|
|
18096
18372
|
var NOTICE_IDS = Object.freeze(["alpha", "danger"]);
|
|
18097
18373
|
var FEEDBACK_URL = "https://github.com/open-tomato/rafa/issues/new/choose";
|
|
18098
18374
|
var ROADMAP_URL = "https://github.com/open-tomato/rafa/issues/31";
|
|
18099
18375
|
function noticesPath(home) {
|
|
18100
|
-
return
|
|
18376
|
+
return join32(home, ".rafa", "notices.json");
|
|
18101
18377
|
}
|
|
18102
18378
|
function isNoticeId(value) {
|
|
18103
18379
|
return typeof value === "string" && NOTICE_IDS.includes(value);
|
|
@@ -18105,7 +18381,7 @@ function isNoticeId(value) {
|
|
|
18105
18381
|
function readDismissed(home) {
|
|
18106
18382
|
let raw;
|
|
18107
18383
|
try {
|
|
18108
|
-
raw =
|
|
18384
|
+
raw = readFileSync23(noticesPath(home), "utf8");
|
|
18109
18385
|
} catch {
|
|
18110
18386
|
return [];
|
|
18111
18387
|
}
|
|
@@ -18124,7 +18400,7 @@ function readDismissed(home) {
|
|
|
18124
18400
|
function writeDismissed(home, ids) {
|
|
18125
18401
|
const all = NOTICE_IDS.filter((id) => ids.includes(id) || readDismissed(home).includes(id));
|
|
18126
18402
|
const path2 = noticesPath(home);
|
|
18127
|
-
|
|
18403
|
+
mkdirSync11(dirname12(path2), { recursive: true });
|
|
18128
18404
|
writeFileSync10(path2, `${JSON.stringify({ dismissed: all }, null, 2)}
|
|
18129
18405
|
`);
|
|
18130
18406
|
}
|
|
@@ -18667,7 +18943,7 @@ import { randomUUID as randomUUID9 } from "crypto";
|
|
|
18667
18943
|
|
|
18668
18944
|
// src/effort/store/absences.ts
|
|
18669
18945
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
18670
|
-
import { existsSync as
|
|
18946
|
+
import { existsSync as existsSync25 } from "fs";
|
|
18671
18947
|
var ABSENCE_REASONS = {
|
|
18672
18948
|
"no-block": true,
|
|
18673
18949
|
"unclosed-block": true,
|
|
@@ -18726,13 +19002,13 @@ function writeReportAbsence(repoRoot, write, seams = {}) {
|
|
|
18726
19002
|
write.outcome,
|
|
18727
19003
|
collectedAt
|
|
18728
19004
|
];
|
|
18729
|
-
const appended3 = withSqliteStore(path2, !
|
|
19005
|
+
const appended3 = withSqliteStore(path2, !existsSync25(path2), (db) => db.query(INSERT_ABSENCE).run(...values).changes);
|
|
18730
19006
|
return { path: path2, appended: appended3, skipped: 1 - appended3 };
|
|
18731
19007
|
}
|
|
18732
19008
|
|
|
18733
19009
|
// src/effort/store/changes.ts
|
|
18734
19010
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
18735
|
-
import { existsSync as
|
|
19011
|
+
import { existsSync as existsSync26 } from "fs";
|
|
18736
19012
|
var INSERT_CHANGE = `
|
|
18737
19013
|
INSERT INTO changes (
|
|
18738
19014
|
id, session_id, plan_stub, task_line,
|
|
@@ -18811,7 +19087,7 @@ var SELECT_PLAN_CHANGES = `
|
|
|
18811
19087
|
`;
|
|
18812
19088
|
function readPlanChanges(repoRoot, planStub2) {
|
|
18813
19089
|
const path2 = sqliteStorePath(repoRoot);
|
|
18814
|
-
if (!
|
|
19090
|
+
if (!existsSync26(path2))
|
|
18815
19091
|
return [];
|
|
18816
19092
|
const rows2 = withSqliteStore(path2, false, (db) => db.query(SELECT_PLAN_CHANGES).all(planStub2));
|
|
18817
19093
|
return rows2.map((row2) => ({
|
|
@@ -18980,8 +19256,8 @@ function describeTaskReportRecord(record) {
|
|
|
18980
19256
|
}
|
|
18981
19257
|
|
|
18982
19258
|
// src/utils/progress.ts
|
|
18983
|
-
import { existsSync as
|
|
18984
|
-
import { join as
|
|
19259
|
+
import { existsSync as existsSync27, writeFileSync as writeFileSync11 } from "fs";
|
|
19260
|
+
import { join as join33 } from "path";
|
|
18985
19261
|
var PROGRESS_FILE_NAME = "progress.txt";
|
|
18986
19262
|
var PROGRESS_CAP_BYTES = 16000;
|
|
18987
19263
|
var LISTED_FIELDS = [
|
|
@@ -19023,7 +19299,7 @@ var ESCAPED_CODE_POINTS = new Set([
|
|
|
19023
19299
|
8233
|
|
19024
19300
|
]);
|
|
19025
19301
|
function progressFilePath(repoRoot) {
|
|
19026
|
-
return
|
|
19302
|
+
return join33(repoRoot, PROGRESS_FILE_NAME);
|
|
19027
19303
|
}
|
|
19028
19304
|
function escapeValue(value) {
|
|
19029
19305
|
let escaped = "";
|
|
@@ -19084,7 +19360,7 @@ function renderProgressText(findings, capBytes = PROGRESS_CAP_BYTES) {
|
|
|
19084
19360
|
}
|
|
19085
19361
|
function readProgressFindings(repoRoot, planStub2) {
|
|
19086
19362
|
const path2 = sqliteStorePath(repoRoot);
|
|
19087
|
-
if (!
|
|
19363
|
+
if (!existsSync27(path2))
|
|
19088
19364
|
return [];
|
|
19089
19365
|
const rows2 = withSqliteStore(path2, false, (db) => {
|
|
19090
19366
|
const query = db.query(SELECT_FINDINGS);
|
|
@@ -19111,20 +19387,20 @@ function writeProgress(repoRoot, planStub2) {
|
|
|
19111
19387
|
|
|
19112
19388
|
// src/start/preflight.ts
|
|
19113
19389
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
19114
|
-
import { existsSync as
|
|
19390
|
+
import { existsSync as existsSync28, readFileSync as readFileSync24 } from "fs";
|
|
19115
19391
|
import { homedir as homedir7 } from "os";
|
|
19116
|
-
import { basename as
|
|
19392
|
+
import { basename as basename11 } from "path";
|
|
19117
19393
|
var KNOWN_MISSING_SENTENCE = "A known-missing item is neither a bug to fix nor a credential to patch" + " around: the preflight found it unavailable on this machine, so do the task without it.";
|
|
19118
19394
|
function knownMissingNotice(lines) {
|
|
19119
19395
|
return lines.length === 0 ? [] : [...lines, KNOWN_MISSING_SENTENCE];
|
|
19120
19396
|
}
|
|
19121
19397
|
function agentSourcePath(planPath) {
|
|
19122
19398
|
const tracker = trackerPathFor(planPath);
|
|
19123
|
-
return
|
|
19399
|
+
return existsSync28(tracker) ? tracker : planPath;
|
|
19124
19400
|
}
|
|
19125
19401
|
function readIfReadable2(path2) {
|
|
19126
19402
|
try {
|
|
19127
|
-
return
|
|
19403
|
+
return readFileSync24(path2, "utf8");
|
|
19128
19404
|
} catch {
|
|
19129
19405
|
return null;
|
|
19130
19406
|
}
|
|
@@ -19140,7 +19416,7 @@ function refuseUnresolvableAgents(options) {
|
|
|
19140
19416
|
if (missing2.length === 0)
|
|
19141
19417
|
return;
|
|
19142
19418
|
throw new CommandExit(1, [
|
|
19143
|
-
`\u274C Refusing to start: ${
|
|
19419
|
+
`\u274C Refusing to start: ${basename11(path2)} names ${missing2.length} agent(s) no loaded scope` + ` defines (loop.settingSources: ${settingSources.join(", ")}).`,
|
|
19144
19420
|
...missing2.map((agent) => ` ${missingAgentLine(agent)}`),
|
|
19145
19421
|
" Nothing was checked and nothing was dispatched."
|
|
19146
19422
|
].join(`
|
|
@@ -19173,7 +19449,7 @@ function skippedStartLine(item, tracker) {
|
|
|
19173
19449
|
return `\u23ED start-only item ${item.kind} ${JSON.stringify(item.name)} was not checked:` + " it is probed on a first dispatch alone," + ` and ${tracker} already holds a ticked task`;
|
|
19174
19450
|
}
|
|
19175
19451
|
function announceSkippedStart(planPath, skipped) {
|
|
19176
|
-
const tracker =
|
|
19452
|
+
const tracker = basename11(trackerPathFor(planPath));
|
|
19177
19453
|
const output = activeOutput();
|
|
19178
19454
|
for (const [index, item] of skipped.entries()) {
|
|
19179
19455
|
const lead = index === 0 ? `
|
|
@@ -19192,7 +19468,7 @@ function startTier(planPath, items) {
|
|
|
19192
19468
|
function announceReminders(planPath, reminders) {
|
|
19193
19469
|
if (reminders.length === 0)
|
|
19194
19470
|
return;
|
|
19195
|
-
const file =
|
|
19471
|
+
const file = basename11(prerequisitesPathForPlan(planPath) ?? planPath);
|
|
19196
19472
|
const output = activeOutput();
|
|
19197
19473
|
output.info(`
|
|
19198
19474
|
\uD83D\uDCCC ${file} names ${reminders.length} step(s) the preflight does not check:`);
|
|
@@ -19419,7 +19695,7 @@ async function holdWhilePaused(options) {
|
|
|
19419
19695
|
}
|
|
19420
19696
|
|
|
19421
19697
|
// src/release/prepare.ts
|
|
19422
|
-
import { readFileSync as
|
|
19698
|
+
import { readFileSync as readFileSync26, writeFileSync as writeFileSync13 } from "fs";
|
|
19423
19699
|
|
|
19424
19700
|
// src/release/changelog.ts
|
|
19425
19701
|
var RELEASE_HEADING_FIELDS = ["version", "date", "title"];
|
|
@@ -19623,7 +19899,7 @@ function resolveReleaseLevel(declared, notes) {
|
|
|
19623
19899
|
}
|
|
19624
19900
|
|
|
19625
19901
|
// src/release/version.ts
|
|
19626
|
-
import { readFileSync as
|
|
19902
|
+
import { readFileSync as readFileSync25, writeFileSync as writeFileSync12 } from "fs";
|
|
19627
19903
|
var RELEASE_REMOTE = "origin";
|
|
19628
19904
|
var RELEASE_BASE_BRANCH = "main";
|
|
19629
19905
|
var VERSION_SHAPE2 = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?(?:\+([0-9A-Za-z.-]+))?$/;
|
|
@@ -19715,7 +19991,7 @@ function writeManifestVersion(path2, next) {
|
|
|
19715
19991
|
const unwritten = { path: path2, written: false, previous: null, version: next };
|
|
19716
19992
|
let text2;
|
|
19717
19993
|
try {
|
|
19718
|
-
text2 =
|
|
19994
|
+
text2 = readFileSync25(path2, "utf8");
|
|
19719
19995
|
} catch (error) {
|
|
19720
19996
|
return { ...unwritten, problem: `${path2} could not be read: ${messageOf(error)}` };
|
|
19721
19997
|
}
|
|
@@ -19818,7 +20094,7 @@ function planVersion(input, versionFile, level) {
|
|
|
19818
20094
|
}
|
|
19819
20095
|
function textOf(path2) {
|
|
19820
20096
|
try {
|
|
19821
|
-
return
|
|
20097
|
+
return readFileSync26(path2, "utf8");
|
|
19822
20098
|
} catch {
|
|
19823
20099
|
return null;
|
|
19824
20100
|
}
|
|
@@ -19931,7 +20207,7 @@ function changelogFailure(failed2, versionEdit, restored) {
|
|
|
19931
20207
|
}
|
|
19932
20208
|
|
|
19933
20209
|
// src/release/verify.ts
|
|
19934
|
-
import { readFileSync as
|
|
20210
|
+
import { readFileSync as readFileSync27, writeFileSync as writeFileSync14 } from "fs";
|
|
19935
20211
|
function changelogInsertionSpan(before, after) {
|
|
19936
20212
|
const beforeLines = before.split(`
|
|
19937
20213
|
`);
|
|
@@ -19948,7 +20224,7 @@ function changelogInsertionSpan(before, after) {
|
|
|
19948
20224
|
}
|
|
19949
20225
|
function textOf2(path2) {
|
|
19950
20226
|
try {
|
|
19951
|
-
return
|
|
20227
|
+
return readFileSync27(path2, "utf8");
|
|
19952
20228
|
} catch {
|
|
19953
20229
|
return null;
|
|
19954
20230
|
}
|
|
@@ -20340,9 +20616,9 @@ function describeStartTriage(result) {
|
|
|
20340
20616
|
};
|
|
20341
20617
|
}
|
|
20342
20618
|
async function resolvePublicTracker(options, redact) {
|
|
20343
|
-
const
|
|
20619
|
+
const resolve18 = options.resolve ?? resolveTracker;
|
|
20344
20620
|
try {
|
|
20345
|
-
const { tracker, attempts } = await
|
|
20621
|
+
const { tracker, attempts } = await resolve18({
|
|
20346
20622
|
config: options.config,
|
|
20347
20623
|
context: { repoRoot: options.repoRoot }
|
|
20348
20624
|
});
|
|
@@ -20513,8 +20789,8 @@ function msUntil(startAt, now = new Date) {
|
|
|
20513
20789
|
return target.getTime() - now.getTime();
|
|
20514
20790
|
}
|
|
20515
20791
|
function sleepFor(ms) {
|
|
20516
|
-
return new Promise((
|
|
20517
|
-
setTimeout(
|
|
20792
|
+
return new Promise((resolve18) => {
|
|
20793
|
+
setTimeout(resolve18, ms);
|
|
20518
20794
|
});
|
|
20519
20795
|
}
|
|
20520
20796
|
async function deferUntil(startAt, now = new Date, sleep = sleepFor) {
|
|
@@ -21070,8 +21346,8 @@ Usage: ${USAGE17}`);
|
|
|
21070
21346
|
var exec_default = Object.freeze(execCommand);
|
|
21071
21347
|
|
|
21072
21348
|
// src/modules/load.ts
|
|
21073
|
-
import { readFileSync as
|
|
21074
|
-
import { isAbsolute as isAbsolute10, join as
|
|
21349
|
+
import { readFileSync as readFileSync28 } from "fs";
|
|
21350
|
+
import { isAbsolute as isAbsolute10, join as join34, relative as relative8, resolve as resolve18 } from "path";
|
|
21075
21351
|
|
|
21076
21352
|
// src/modules/manifest.ts
|
|
21077
21353
|
var MANIFEST_VERSION = 1;
|
|
@@ -21356,11 +21632,11 @@ function unread(name2, directory2, ...problems) {
|
|
|
21356
21632
|
return { name: name2, version: null, directory: directory2, manifest: null, problems };
|
|
21357
21633
|
}
|
|
21358
21634
|
function readPathSource(source, settings, seams) {
|
|
21359
|
-
const directory2 =
|
|
21360
|
-
const file =
|
|
21635
|
+
const directory2 = resolve18(settings.base, source.location);
|
|
21636
|
+
const file = join34(directory2, "package.json");
|
|
21361
21637
|
let parsed;
|
|
21362
21638
|
try {
|
|
21363
|
-
parsed = JSON.parse((seams.readText ?? ((path3) =>
|
|
21639
|
+
parsed = JSON.parse((seams.readText ?? ((path3) => readFileSync28(path3, "utf8")))(file));
|
|
21364
21640
|
} catch (error) {
|
|
21365
21641
|
return unread(null, directory2, `${file} cannot be read as JSON: ${firstLine2(error)}`);
|
|
21366
21642
|
}
|
|
@@ -21391,7 +21667,7 @@ function readSource(source, settings, seams) {
|
|
|
21391
21667
|
return unread(source.location, null, `${source.kind} source ${describeValue(source.location)} is refused: ${PHASE_7}`);
|
|
21392
21668
|
}
|
|
21393
21669
|
function resolveEntry(directory2, entry, label) {
|
|
21394
|
-
const path3 =
|
|
21670
|
+
const path3 = resolve18(directory2, entry);
|
|
21395
21671
|
const inside = relative8(directory2, path3);
|
|
21396
21672
|
return inside === "" || inside.startsWith("..") || isAbsolute10(inside) ? { problem: `${label} is ${describeValue(entry)}, which resolves outside the module directory ${directory2}` } : { path: path3 };
|
|
21397
21673
|
}
|
|
@@ -21859,7 +22135,7 @@ async function runNext(context, seams) {
|
|
|
21859
22135
|
const prompter = lazyPrompter(openPrompter);
|
|
21860
22136
|
try {
|
|
21861
22137
|
return await runNextChain({
|
|
21862
|
-
read: () => readNextState(sources),
|
|
22138
|
+
read: () => readNextState(sources.answer()),
|
|
21863
22139
|
run: (state) => runStateAction(context, sources, state),
|
|
21864
22140
|
ask: prompter.ask,
|
|
21865
22141
|
handOver: prompter.close,
|
|
@@ -22063,6 +22339,23 @@ function createPlanReadyOffer(seams = DEFAULT_OFFER_SEAMS) {
|
|
|
22063
22339
|
};
|
|
22064
22340
|
}
|
|
22065
22341
|
|
|
22342
|
+
// src/commands/plan/refresh-offer.ts
|
|
22343
|
+
var DEFAULT_REFRESH_OFFER_SEAMS = Object.freeze({});
|
|
22344
|
+
function createPlanRefreshOffer(seams = DEFAULT_REFRESH_OFFER_SEAMS) {
|
|
22345
|
+
const isTerminal = seams.isTerminal ?? (() => process.stdin.isTTY === true);
|
|
22346
|
+
if (!isTerminal())
|
|
22347
|
+
return null;
|
|
22348
|
+
const openPrompter = seams.openPrompter ?? (() => createLinePrompter(process.stdin, process.stderr));
|
|
22349
|
+
return async (request) => {
|
|
22350
|
+
const prompter = lazyPrompter(openPrompter);
|
|
22351
|
+
try {
|
|
22352
|
+
return await prompter.ask(refreshQuestion(request.issue, request.savedAt));
|
|
22353
|
+
} finally {
|
|
22354
|
+
prompter.close();
|
|
22355
|
+
}
|
|
22356
|
+
};
|
|
22357
|
+
}
|
|
22358
|
+
|
|
22066
22359
|
// src/commands/plan/spec-route.ts
|
|
22067
22360
|
function specCandidates(specArg, specsDir) {
|
|
22068
22361
|
if (path4.isAbsolute(specArg))
|
|
@@ -22092,10 +22385,11 @@ async function resolveCreateSpec(options, seams = DEFAULT_ROUTE_SEAMS) {
|
|
|
22092
22385
|
const source = readSpecSourceFlags(options.args);
|
|
22093
22386
|
if (source.request === null)
|
|
22094
22387
|
throw new CommandExit(SOURCE_REFUSAL_EXIT, usageRefusal(specsDir));
|
|
22095
|
-
const
|
|
22388
|
+
const resolve19 = seams.resolve ?? resolvePlanSpec;
|
|
22096
22389
|
const makeReadyOffer = seams.makeReadyOffer ?? createPlanReadyOffer;
|
|
22097
22390
|
const makeAlternativeOffer = seams.makeAlternativeOffer ?? createPlanBlockedOffer;
|
|
22098
|
-
|
|
22391
|
+
const makeRefreshOffer = seams.makeRefreshOffer ?? createPlanRefreshOffer;
|
|
22392
|
+
return await resolve19({
|
|
22099
22393
|
request: source.request,
|
|
22100
22394
|
refresh: source.refresh,
|
|
22101
22395
|
dryRun: source.dryRun,
|
|
@@ -22105,7 +22399,8 @@ async function resolveCreateSpec(options, seams = DEFAULT_ROUTE_SEAMS) {
|
|
|
22105
22399
|
trustedAuthors: options.trustedAuthors,
|
|
22106
22400
|
findSpec: (spec) => findSpec(repoRoot, spec, specsDir),
|
|
22107
22401
|
offerReady: makeReadyOffer(),
|
|
22108
|
-
offerAlternative: makeAlternativeOffer()
|
|
22402
|
+
offerAlternative: makeAlternativeOffer(),
|
|
22403
|
+
offerRefresh: makeRefreshOffer()
|
|
22109
22404
|
});
|
|
22110
22405
|
}
|
|
22111
22406
|
|
|
@@ -22277,7 +22572,7 @@ var wrapped2 = wrapPhaseZeroCommand({
|
|
|
22277
22572
|
},
|
|
22278
22573
|
{
|
|
22279
22574
|
name: "refresh",
|
|
22280
|
-
description: "
|
|
22575
|
+
description: "Rebuilds a snapshot that no longer matches the issue without asking; the old copy moves under" + " `previous/`.",
|
|
22281
22576
|
type: "boolean"
|
|
22282
22577
|
},
|
|
22283
22578
|
{
|
|
@@ -22337,8 +22632,8 @@ var wrapped2 = wrapPhaseZeroCommand({
|
|
|
22337
22632
|
var create_default2 = endingWith(wrapped2);
|
|
22338
22633
|
|
|
22339
22634
|
// src/commands/plan/show.ts
|
|
22340
|
-
import { readFileSync as
|
|
22341
|
-
import { join as
|
|
22635
|
+
import { readFileSync as readFileSync29 } from "fs";
|
|
22636
|
+
import { join as join35 } from "path";
|
|
22342
22637
|
var USAGE19 = "rafa plan show <stub> [--tracker]";
|
|
22343
22638
|
var COMMAND = "rafa plan show";
|
|
22344
22639
|
function readTrackerFlag(value) {
|
|
@@ -22356,10 +22651,10 @@ function showPlan(plans, stub, tracker) {
|
|
|
22356
22651
|
throw new CommandExit(1, `\u274C "${stub}" is no plan stub: a stub is letters, digits, ".", "_" and "-"`);
|
|
22357
22652
|
}
|
|
22358
22653
|
const name2 = planFileName(stub, tracker);
|
|
22359
|
-
const file =
|
|
22654
|
+
const file = join35(plans.path, name2);
|
|
22360
22655
|
if (!isFile(file))
|
|
22361
|
-
throw new CommandExit(1, missingText(stub, tracker,
|
|
22362
|
-
const model = parsePlan(
|
|
22656
|
+
throw new CommandExit(1, missingText(stub, tracker, join35(plans.label, name2)));
|
|
22657
|
+
const model = parsePlan(readFileSync29(file, "utf8"));
|
|
22363
22658
|
const { header } = model;
|
|
22364
22659
|
return {
|
|
22365
22660
|
stub,
|
|
@@ -22382,7 +22677,7 @@ function taskLine2(task) {
|
|
|
22382
22677
|
return ` ${checkbox(task.status)} ${task.text}`;
|
|
22383
22678
|
}
|
|
22384
22679
|
function renderShownPlan(shown2, dirLabel) {
|
|
22385
|
-
const relative9 =
|
|
22680
|
+
const relative9 = join35(dirLabel, planFileName(shown2.stub, shown2.tracker));
|
|
22386
22681
|
const fileLabel = shown2.tracker ? "Tracker" : "File";
|
|
22387
22682
|
const lines = [`Plan: ${shown2.stub}`, `${fileLabel}: ${relative9}`];
|
|
22388
22683
|
if (shown2.header.issue !== null)
|
|
@@ -22801,11 +23096,11 @@ function createPrListCommand(seams = DEFAULT_PR_SEAMS) {
|
|
|
22801
23096
|
var list_default7 = createPrListCommand();
|
|
22802
23097
|
|
|
22803
23098
|
// src/commands/pr/merge.ts
|
|
22804
|
-
import { existsSync as
|
|
22805
|
-
import { join as
|
|
23099
|
+
import { existsSync as existsSync30, readFileSync as readFileSync31 } from "fs";
|
|
23100
|
+
import { join as join37 } from "path";
|
|
22806
23101
|
|
|
22807
23102
|
// src/board/roadmap-tick.ts
|
|
22808
|
-
var
|
|
23103
|
+
var PREFIX18 = "board roadmap";
|
|
22809
23104
|
var REPO_PATH4 = "{owner}/{repo}";
|
|
22810
23105
|
var TICK_ATTEMPTS = 2;
|
|
22811
23106
|
var UNTICKED = /\[ \]/u;
|
|
@@ -22856,17 +23151,17 @@ function bodyOf(stdout, command) {
|
|
|
22856
23151
|
try {
|
|
22857
23152
|
payload = JSON.parse(stdout);
|
|
22858
23153
|
} catch (error) {
|
|
22859
|
-
throw new Error(`${
|
|
23154
|
+
throw new Error(`${PREFIX18}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
|
|
22860
23155
|
}
|
|
22861
23156
|
const body = isMapping(payload) ? payload["body"] : null;
|
|
22862
23157
|
if (typeof body !== "string") {
|
|
22863
|
-
throw new Error(`${
|
|
23158
|
+
throw new Error(`${PREFIX18}: ${command} answered body as ${describeValue(body)}, expected a string`);
|
|
22864
23159
|
}
|
|
22865
23160
|
return body;
|
|
22866
23161
|
}
|
|
22867
23162
|
function issueSegment(value, member) {
|
|
22868
23163
|
if (!Number.isSafeInteger(value) || value < 1) {
|
|
22869
|
-
throw new TypeError(`${
|
|
23164
|
+
throw new TypeError(`${PREFIX18}: ${member} refused issue number ${describeValue(value)}, expected a positive whole number`);
|
|
22870
23165
|
}
|
|
22871
23166
|
return String(value);
|
|
22872
23167
|
}
|
|
@@ -22876,23 +23171,23 @@ function createGhRoadmapBody(options) {
|
|
|
22876
23171
|
return Object.freeze({
|
|
22877
23172
|
read: async (issue3) => {
|
|
22878
23173
|
const path6 = pathOf(issue3, "read");
|
|
22879
|
-
const result = await gh([path6]);
|
|
23174
|
+
const result = await gh(["api", path6]);
|
|
22880
23175
|
if (!result.ok)
|
|
22881
|
-
throw new Error(`${
|
|
23176
|
+
throw new Error(`${PREFIX18}: ${detailOf10(result, `gh api ${path6}`)}`);
|
|
22882
23177
|
return bodyOf(result.stdout, `gh api ${path6}`);
|
|
22883
23178
|
},
|
|
22884
23179
|
write: async (issue3, body) => {
|
|
22885
23180
|
const path6 = pathOf(issue3, "write");
|
|
22886
|
-
const result = await gh([path6, "-X", "PATCH", "-f", `body=${body}`]);
|
|
23181
|
+
const result = await gh(["api", path6, "-X", "PATCH", "-f", `body=${body}`]);
|
|
22887
23182
|
const command = `gh api ${path6} -X PATCH -f body=<body>`;
|
|
22888
23183
|
if (!result.ok)
|
|
22889
|
-
throw new Error(`${
|
|
23184
|
+
throw new Error(`${PREFIX18}: ${detailOf10(result, command)}`);
|
|
22890
23185
|
return bodyOf(result.stdout, command);
|
|
22891
23186
|
}
|
|
22892
23187
|
});
|
|
22893
23188
|
}
|
|
22894
23189
|
function conflictProblem(issue3) {
|
|
22895
|
-
return `${
|
|
23190
|
+
return `${PREFIX18}: issue #${String(issue3)} answered a body other than the one that was written,` + " so somebody edited it in between";
|
|
22896
23191
|
}
|
|
22897
23192
|
async function attemptTick(roadmap, issues, board, attempt4) {
|
|
22898
23193
|
let edit;
|
|
@@ -22957,17 +23252,17 @@ function tickSentence(result) {
|
|
|
22957
23252
|
import {
|
|
22958
23253
|
chmodSync,
|
|
22959
23254
|
copyFileSync as copyFileSync2,
|
|
22960
|
-
existsSync as
|
|
22961
|
-
mkdirSync as
|
|
22962
|
-
readdirSync as
|
|
22963
|
-
readFileSync as
|
|
23255
|
+
existsSync as existsSync29,
|
|
23256
|
+
mkdirSync as mkdirSync12,
|
|
23257
|
+
readdirSync as readdirSync10,
|
|
23258
|
+
readFileSync as readFileSync30,
|
|
22964
23259
|
realpathSync as realpathSync5,
|
|
22965
23260
|
renameSync as renameSync3,
|
|
22966
23261
|
rmSync as rmSync2,
|
|
22967
|
-
statSync as
|
|
23262
|
+
statSync as statSync17,
|
|
22968
23263
|
symlinkSync
|
|
22969
23264
|
} from "fs";
|
|
22970
|
-
import { basename as
|
|
23265
|
+
import { basename as basename12, delimiter as delimiter3, dirname as dirname13, join as join36, relative as relative9, resolve as resolve19 } from "path";
|
|
22971
23266
|
var LINK_NAME = "rafa";
|
|
22972
23267
|
var FORCE_FLAG = "--force";
|
|
22973
23268
|
var EXIT_DONE = 0;
|
|
@@ -23028,20 +23323,20 @@ function runtimeRefusalLines(runtimeDir, version2) {
|
|
|
23028
23323
|
];
|
|
23029
23324
|
}
|
|
23030
23325
|
function listTrackers(repoRoot, planDir) {
|
|
23031
|
-
const dir =
|
|
23032
|
-
return entryNames(dir).filter((entry) => TRACKER_NAME.test(entry)).sort((a, b) => a.localeCompare(b)).map((entry) =>
|
|
23326
|
+
const dir = resolve19(repoRoot, planDir);
|
|
23327
|
+
return entryNames(dir).filter((entry) => TRACKER_NAME.test(entry)).sort((a, b) => a.localeCompare(b)).map((entry) => join36(dir, entry)).filter((path6) => statSync17(path6).isFile()).map((path6) => relative9(repoRoot, path6));
|
|
23033
23328
|
}
|
|
23034
23329
|
function openTrackers(repoRoot, planDir) {
|
|
23035
23330
|
return listTrackers(repoRoot, planDir).flatMap((path6) => {
|
|
23036
|
-
const task = findNextTask(
|
|
23331
|
+
const task = findNextTask(readFileSync30(join36(repoRoot, path6), "utf8"));
|
|
23037
23332
|
return task ? [{ path: path6, task }] : [];
|
|
23038
23333
|
});
|
|
23039
23334
|
}
|
|
23040
23335
|
function readManifestVersion2(repoRoot) {
|
|
23041
|
-
const manifest = JSON.parse(
|
|
23336
|
+
const manifest = JSON.parse(readFileSync30(join36(repoRoot, "package.json"), "utf8"));
|
|
23042
23337
|
const record = typeof manifest === "object" && manifest !== null ? manifest : {};
|
|
23043
23338
|
if (record["name"] !== name) {
|
|
23044
|
-
throw new Error(`${
|
|
23339
|
+
throw new Error(`${join36(repoRoot, "package.json")} names ${JSON.stringify(record["name"])}, not ${name}: this is no rafa checkout`);
|
|
23045
23340
|
}
|
|
23046
23341
|
const version2 = record["version"];
|
|
23047
23342
|
if (typeof version2 !== "string" || !VERSION_SHAPE.test(version2)) {
|
|
@@ -23050,12 +23345,12 @@ function readManifestVersion2(repoRoot) {
|
|
|
23050
23345
|
return version2;
|
|
23051
23346
|
}
|
|
23052
23347
|
function copyTree(from, to) {
|
|
23053
|
-
|
|
23348
|
+
mkdirSync12(to, { recursive: true });
|
|
23054
23349
|
let copied = 0;
|
|
23055
|
-
const entries =
|
|
23350
|
+
const entries = readdirSync10(from, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
23056
23351
|
for (const entry of entries) {
|
|
23057
|
-
const source =
|
|
23058
|
-
const target =
|
|
23352
|
+
const source = join36(from, entry.name);
|
|
23353
|
+
const target = join36(to, entry.name);
|
|
23059
23354
|
if (entry.isDirectory()) {
|
|
23060
23355
|
copied += copyTree(source, target);
|
|
23061
23356
|
} else if (entry.isFile()) {
|
|
@@ -23080,7 +23375,7 @@ function replaceTree(from, to) {
|
|
|
23080
23375
|
}
|
|
23081
23376
|
}
|
|
23082
23377
|
function swapIn(staging, to) {
|
|
23083
|
-
if (!
|
|
23378
|
+
if (!existsSync29(to)) {
|
|
23084
23379
|
renameSync3(staging, to);
|
|
23085
23380
|
return;
|
|
23086
23381
|
}
|
|
@@ -23096,7 +23391,7 @@ function swapIn(staging, to) {
|
|
|
23096
23391
|
rmSync2(outgoing, { recursive: true, force: true });
|
|
23097
23392
|
}
|
|
23098
23393
|
function relink(linkPath, target) {
|
|
23099
|
-
|
|
23394
|
+
mkdirSync12(dirname13(linkPath), { recursive: true });
|
|
23100
23395
|
const temporary = temporaryName(linkPath);
|
|
23101
23396
|
rmSync2(temporary, { force: true });
|
|
23102
23397
|
try {
|
|
@@ -23150,20 +23445,20 @@ function installRuntime(seams, options = {}) {
|
|
|
23150
23445
|
}
|
|
23151
23446
|
function runInstall(seams, options) {
|
|
23152
23447
|
const { repoRoot, home, log } = seams;
|
|
23153
|
-
const runtimeRoot =
|
|
23154
|
-
const linkPath =
|
|
23448
|
+
const runtimeRoot = join36(home, RUNTIME_SUBDIR);
|
|
23449
|
+
const linkPath = join36(home, RAFA_BIN_DIR, LINK_NAME);
|
|
23155
23450
|
log(`repo root: ${repoRoot}`);
|
|
23156
23451
|
log(`runtime root: ${runtimeRoot}`);
|
|
23157
23452
|
log(`link: ${linkPath}`);
|
|
23158
23453
|
const version2 = inStage("manifest", () => readManifestVersion2(repoRoot));
|
|
23159
|
-
const planDir = inStage("config", () =>
|
|
23454
|
+
const planDir = inStage("config", () => resolve19(repoRoot, loadConfig({ root: repoRoot, home }, {}, seams.warn).config.planDir));
|
|
23160
23455
|
log(`plan dir: ${planDir}`);
|
|
23161
23456
|
const open = inStage("trackers", () => openTrackers(repoRoot, planDir));
|
|
23162
23457
|
if (open.length > 0)
|
|
23163
23458
|
return { kind: "refused", reason: "trackers", planDir, trackers: open };
|
|
23164
|
-
const distDir =
|
|
23165
|
-
const runtimeDir =
|
|
23166
|
-
const installed =
|
|
23459
|
+
const distDir = join36(repoRoot, "dist");
|
|
23460
|
+
const runtimeDir = join36(runtimeRoot, version2);
|
|
23461
|
+
const installed = existsSync29(runtimeDir);
|
|
23167
23462
|
if (installed && options.force !== true) {
|
|
23168
23463
|
return { kind: "refused", reason: "runtime-exists", version: version2, runtimeDir };
|
|
23169
23464
|
}
|
|
@@ -23171,13 +23466,13 @@ function runInstall(seams, options) {
|
|
|
23171
23466
|
const code = inStage("build", () => seams.build(repoRoot));
|
|
23172
23467
|
if (code !== 0)
|
|
23173
23468
|
throw new StageFailure("build", `the build exited ${code}`);
|
|
23174
|
-
if (!
|
|
23175
|
-
throw new StageFailure("build", `the build exited 0 and left no ${
|
|
23469
|
+
if (!existsSync29(join36(distDir, RUNTIME_ENTRY))) {
|
|
23470
|
+
throw new StageFailure("build", `the build exited 0 and left no ${join36(distDir, RUNTIME_ENTRY)}`);
|
|
23176
23471
|
}
|
|
23177
23472
|
log(installed ? `copying ${distDir} over ${runtimeDir}, replacing it whole` : `copying ${distDir} into ${runtimeDir}`);
|
|
23178
23473
|
const copied = inStage("copy", () => replaceTree(distDir, runtimeDir));
|
|
23179
23474
|
log(`copied ${copied} file(s)`);
|
|
23180
|
-
const resolved = inStage("link", () => relink(linkPath,
|
|
23475
|
+
const resolved = inStage("link", () => relink(linkPath, join36(runtimeDir, RUNTIME_ENTRY)));
|
|
23181
23476
|
log(`${linkPath} resolves to ${resolved}`);
|
|
23182
23477
|
return { kind: "done", version: version2, runtimeDir, linkPath, resolved, copied };
|
|
23183
23478
|
}
|
|
@@ -23192,7 +23487,7 @@ function inStage(stage, run) {
|
|
|
23192
23487
|
}
|
|
23193
23488
|
function entryNames(dir) {
|
|
23194
23489
|
try {
|
|
23195
|
-
return
|
|
23490
|
+
return readdirSync10(dir);
|
|
23196
23491
|
} catch (err) {
|
|
23197
23492
|
if (typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT")
|
|
23198
23493
|
return [];
|
|
@@ -23203,7 +23498,7 @@ function replaceFile(source, target) {
|
|
|
23203
23498
|
const temporary = temporaryName(target);
|
|
23204
23499
|
try {
|
|
23205
23500
|
copyFileSync2(source, temporary);
|
|
23206
|
-
chmodSync(temporary,
|
|
23501
|
+
chmodSync(temporary, statSync17(source).mode & 4095);
|
|
23207
23502
|
renameSync3(temporary, target);
|
|
23208
23503
|
} catch (err) {
|
|
23209
23504
|
rmSync2(temporary, { force: true });
|
|
@@ -23211,7 +23506,7 @@ function replaceFile(source, target) {
|
|
|
23211
23506
|
}
|
|
23212
23507
|
}
|
|
23213
23508
|
function temporaryName(path6) {
|
|
23214
|
-
return
|
|
23509
|
+
return join36(dirname13(path6), `.${basename12(path6)}.snapshot-${process.pid}`);
|
|
23215
23510
|
}
|
|
23216
23511
|
|
|
23217
23512
|
// src/commands/pr/merge-followups.ts
|
|
@@ -23502,13 +23797,13 @@ function remoteHoldsBranch(git, branch, warn) {
|
|
|
23502
23797
|
}
|
|
23503
23798
|
function textOf3(path6) {
|
|
23504
23799
|
try {
|
|
23505
|
-
return
|
|
23800
|
+
return readFileSync31(path6, "utf8");
|
|
23506
23801
|
} catch {
|
|
23507
23802
|
return "";
|
|
23508
23803
|
}
|
|
23509
23804
|
}
|
|
23510
23805
|
function followUpsFor(pr, git) {
|
|
23511
|
-
const facts = readPackageFacts(textOf3(
|
|
23806
|
+
const facts = readPackageFacts(textOf3(join37(pr.project.root, "package.json")));
|
|
23512
23807
|
if (facts.version === null)
|
|
23513
23808
|
return [];
|
|
23514
23809
|
const tags = git(["tag", "--list", versionTag(facts.version)]);
|
|
@@ -23516,7 +23811,7 @@ function followUpsFor(pr, git) {
|
|
|
23516
23811
|
version: facts.version,
|
|
23517
23812
|
tagged: tags.ok && tags.stdout.trim() !== "",
|
|
23518
23813
|
rafaCheckout: facts.rafaCheckout,
|
|
23519
|
-
runtimeInstalled:
|
|
23814
|
+
runtimeInstalled: existsSync30(join37(pr.project.home, RUNTIME_SUBDIR, facts.version))
|
|
23520
23815
|
});
|
|
23521
23816
|
}
|
|
23522
23817
|
function openGh(pr, seams) {
|
|
@@ -23767,7 +24062,7 @@ var COLUMN = "\t";
|
|
|
23767
24062
|
var BOM = "\uFEFF";
|
|
23768
24063
|
var TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z /;
|
|
23769
24064
|
var COLOUR = new RegExp(`(?:${String.fromCharCode(27)}|\\^\\[)\\[[0-9;]*m`, "g");
|
|
23770
|
-
var
|
|
24065
|
+
var PREFIX19 = "pr triage evidence";
|
|
23771
24066
|
function named2(value) {
|
|
23772
24067
|
return value === "" ? undefined : value;
|
|
23773
24068
|
}
|
|
@@ -23833,7 +24128,7 @@ function readCap(value) {
|
|
|
23833
24128
|
if (value === undefined)
|
|
23834
24129
|
return FAILED_LOG_TAIL_LINES;
|
|
23835
24130
|
if (!Number.isSafeInteger(value) || value < 1) {
|
|
23836
|
-
throw new TypeError(`${
|
|
24131
|
+
throw new TypeError(`${PREFIX19}: readFailedLog refused a line cap ${describeValue(value)},` + " expected a positive whole number");
|
|
23837
24132
|
}
|
|
23838
24133
|
return value;
|
|
23839
24134
|
}
|
|
@@ -24720,8 +25015,8 @@ function describeAge(updatedAt, now) {
|
|
|
24720
25015
|
return `${plural4(hours, "hour", "hours")} ago`;
|
|
24721
25016
|
return `${plural4(Math.floor(age / DAY_MS2), "day", "days")} ago`;
|
|
24722
25017
|
}
|
|
24723
|
-
function triageCommand(number,
|
|
24724
|
-
return
|
|
25018
|
+
function triageCommand(number, resolve20 = false) {
|
|
25019
|
+
return resolve20 ? `rafa pr triage ${number} --resolve` : `rafa pr triage ${number}`;
|
|
24725
25020
|
}
|
|
24726
25021
|
function joinNumbers(pulls) {
|
|
24727
25022
|
const names = pulls.map((pull) => `#${pull.number}`);
|
|
@@ -24732,14 +25027,14 @@ function joinNumbers(pulls) {
|
|
|
24732
25027
|
function byNumber(pulls) {
|
|
24733
25028
|
return Object.freeze([...pulls].sort((left, right) => left.number - right.number));
|
|
24734
25029
|
}
|
|
24735
|
-
function skipped(pull, now,
|
|
25030
|
+
function skipped(pull, now, resolve20) {
|
|
24736
25031
|
return Object.freeze({
|
|
24737
25032
|
number: pull.number,
|
|
24738
25033
|
title: pull.title,
|
|
24739
25034
|
updatedAt: pull.updatedAt,
|
|
24740
25035
|
age: describeAge(pull.updatedAt, now),
|
|
24741
25036
|
fresh: isFreshCandidate(pull.updatedAt, now),
|
|
24742
|
-
command: triageCommand(pull.number,
|
|
25037
|
+
command: triageCommand(pull.number, resolve20)
|
|
24743
25038
|
});
|
|
24744
25039
|
}
|
|
24745
25040
|
function skippedLine(one) {
|
|
@@ -24783,15 +25078,15 @@ function listReading(rest, count) {
|
|
|
24783
25078
|
return listing2("list", Object.freeze([]), rest, headline);
|
|
24784
25079
|
}
|
|
24785
25080
|
function selectTriagePullRequests(input) {
|
|
24786
|
-
const
|
|
25081
|
+
const resolve20 = input.resolve === true;
|
|
24787
25082
|
const { current, now } = input;
|
|
24788
25083
|
if (current !== null) {
|
|
24789
25084
|
return plain("branch", Object.freeze([current]), `assessing #${current.number}, the pull request open on ${current.headRefName}`);
|
|
24790
25085
|
}
|
|
24791
25086
|
const candidates = byNumber(input.candidates);
|
|
24792
25087
|
const count = candidates.length;
|
|
24793
|
-
const lines = candidates.map((pull) => skipped(pull, now,
|
|
24794
|
-
if (
|
|
25088
|
+
const lines = candidates.map((pull) => skipped(pull, now, resolve20));
|
|
25089
|
+
if (resolve20 && count > 1)
|
|
24795
25090
|
return refusal9(lines, count);
|
|
24796
25091
|
if (count === 0) {
|
|
24797
25092
|
return plain("none", Object.freeze([]), "no red pull request to triage, and no pull request on the current branch");
|
|
@@ -25093,11 +25388,11 @@ function renderTriages(readings) {
|
|
|
25093
25388
|
var BUDGET_KEY = "budget";
|
|
25094
25389
|
var TASK_LINE2 = /^- \[[^\]]*\] /;
|
|
25095
25390
|
var TRAILING_BLOCK = /\{([^{}]*)\}$/;
|
|
25096
|
-
var
|
|
25391
|
+
var PREFIX20 = "pr triage --resolve budget";
|
|
25097
25392
|
function budgetEntry(usd) {
|
|
25098
25393
|
const spelled = String(usd);
|
|
25099
25394
|
if (parseBudgetUsd(spelled) === null) {
|
|
25100
|
-
throw new TypeError(`${
|
|
25395
|
+
throw new TypeError(`${PREFIX20}: refused an amount of ${JSON.stringify(usd)}, expected US dollars above zero` + " with at most six whole digits and six decimal places");
|
|
25101
25396
|
}
|
|
25102
25397
|
return `${BUDGET_KEY}=${spelled}`;
|
|
25103
25398
|
}
|
|
@@ -25126,8 +25421,8 @@ function withTaskBudget(plan2, usd) {
|
|
|
25126
25421
|
}
|
|
25127
25422
|
|
|
25128
25423
|
// src/pr/plans/load.ts
|
|
25129
|
-
import { existsSync as
|
|
25130
|
-
import { dirname as dirname14, join as
|
|
25424
|
+
import { existsSync as existsSync31, readFileSync as readFileSync32 } from "fs";
|
|
25425
|
+
import { dirname as dirname14, join as join38 } from "path";
|
|
25131
25426
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
25132
25427
|
var MODULE_DIR2 = dirname14(fileURLToPath4(import.meta.url));
|
|
25133
25428
|
var PINNED_PLANS_DIRNAME = "plans";
|
|
@@ -25144,8 +25439,8 @@ function hasPinnedPlan(triageClass) {
|
|
|
25144
25439
|
function pinnedPlanCandidates(moduleDir, triageClass) {
|
|
25145
25440
|
const name2 = pinnedPlanFileName(triageClass);
|
|
25146
25441
|
return [
|
|
25147
|
-
|
|
25148
|
-
|
|
25442
|
+
join38(moduleDir, PINNED_PLANS_DIRNAME, name2),
|
|
25443
|
+
join38(moduleDir, name2)
|
|
25149
25444
|
];
|
|
25150
25445
|
}
|
|
25151
25446
|
function readPinnedPlan(triageClass, lookup = {}) {
|
|
@@ -25153,11 +25448,11 @@ function readPinnedPlan(triageClass, lookup = {}) {
|
|
|
25153
25448
|
throw new Error(`No pinned resolve plan ships for ${triageClass}; ` + `one ships for ${PINNED_PLAN_CLASSES.join(", ")}`);
|
|
25154
25449
|
}
|
|
25155
25450
|
const candidates = pinnedPlanCandidates(lookup.moduleDir ?? MODULE_DIR2, triageClass);
|
|
25156
|
-
const found = candidates.find((candidate) =>
|
|
25451
|
+
const found = candidates.find((candidate) => existsSync31(candidate));
|
|
25157
25452
|
if (found === undefined) {
|
|
25158
25453
|
throw new Error(`The pinned resolve plan for ${triageClass} is missing: ` + `no file at ${candidates.join(" or ")}`);
|
|
25159
25454
|
}
|
|
25160
|
-
return
|
|
25455
|
+
return readFileSync32(found, "utf8");
|
|
25161
25456
|
}
|
|
25162
25457
|
var SLOT_PATTERN2 = /\{[A-Z][A-Z0-9_]*\}/g;
|
|
25163
25458
|
var CONFLICT_SENTENCE_SLOT = "CONFLICT_SENTENCE";
|
|
@@ -25220,12 +25515,12 @@ var ATTEMPT_STOPS = Object.freeze([
|
|
|
25220
25515
|
"cap",
|
|
25221
25516
|
"repeat"
|
|
25222
25517
|
]);
|
|
25223
|
-
var
|
|
25518
|
+
var PREFIX21 = "pr triage attempts";
|
|
25224
25519
|
var WHITESPACE2 = /\s+/g;
|
|
25225
25520
|
var NO_STEP_KEY = "";
|
|
25226
25521
|
function readCap2(maxAttempts) {
|
|
25227
25522
|
if (!Number.isSafeInteger(maxAttempts) || maxAttempts < 1) {
|
|
25228
|
-
throw new TypeError(`${
|
|
25523
|
+
throw new TypeError(`${PREFIX21}: refused a cap of ${JSON.stringify(maxAttempts)},` + " expected a positive whole number");
|
|
25229
25524
|
}
|
|
25230
25525
|
return maxAttempts;
|
|
25231
25526
|
}
|
|
@@ -25297,13 +25592,13 @@ function readAttemptRepeat(input) {
|
|
|
25297
25592
|
}
|
|
25298
25593
|
|
|
25299
25594
|
// src/pr/worktree.ts
|
|
25300
|
-
import { existsSync as
|
|
25301
|
-
import { join as
|
|
25302
|
-
var WORKTREES_SUBDIR =
|
|
25595
|
+
import { existsSync as existsSync32, realpathSync as realpathSync6 } from "fs";
|
|
25596
|
+
import { join as join39 } from "path";
|
|
25597
|
+
var WORKTREES_SUBDIR = join39(".rafa", "worktrees");
|
|
25303
25598
|
var WORKTREE_PREFIX = "pr-";
|
|
25304
25599
|
var REFUSAL_PREFIX3 = "rafa pr triage --resolve refuses";
|
|
25305
25600
|
function resolveWorktreePath(home, number) {
|
|
25306
|
-
return
|
|
25601
|
+
return join39(home, WORKTREES_SUBDIR, `${WORKTREE_PREFIX}${number}`);
|
|
25307
25602
|
}
|
|
25308
25603
|
function crossRepositoryRefusal(pr) {
|
|
25309
25604
|
if (!pr.isCrossRepository)
|
|
@@ -25346,7 +25641,7 @@ function removeResolveWorktree(git, path6) {
|
|
|
25346
25641
|
return runStep(git, removeWorktreeStep(path6));
|
|
25347
25642
|
}
|
|
25348
25643
|
function pathForms(path6) {
|
|
25349
|
-
if (!
|
|
25644
|
+
if (!existsSync32(path6))
|
|
25350
25645
|
return [path6];
|
|
25351
25646
|
return [path6, realpathSync6(path6)];
|
|
25352
25647
|
}
|
|
@@ -25359,20 +25654,20 @@ function worktreeAt(git, path6) {
|
|
|
25359
25654
|
}
|
|
25360
25655
|
|
|
25361
25656
|
// src/commands/pr/resolve-loop.ts
|
|
25362
|
-
import { mkdirSync as
|
|
25363
|
-
import { join as
|
|
25364
|
-
var RESOLVE_SUBDIR =
|
|
25657
|
+
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync15 } from "fs";
|
|
25658
|
+
import { join as join40 } from "path";
|
|
25659
|
+
var RESOLVE_SUBDIR = join40(".rafa", "resolve");
|
|
25365
25660
|
var RUN_PREFIX = "pr-";
|
|
25366
25661
|
var ATTEMPT_PREFIX = "attempt-";
|
|
25367
25662
|
var LOOP_WORDS = Object.freeze(["loop", "start"]);
|
|
25368
25663
|
var CHILD_OUTPUT = "text";
|
|
25369
25664
|
function resolveRunDir(home, number, attempt4) {
|
|
25370
|
-
return
|
|
25665
|
+
return join40(home, RESOLVE_SUBDIR, `${RUN_PREFIX}${number}`, `${ATTEMPT_PREFIX}${attempt4}`);
|
|
25371
25666
|
}
|
|
25372
25667
|
function writeResolvePlan(write) {
|
|
25373
25668
|
const dir = resolveRunDir(write.home, write.number, write.attempt);
|
|
25374
|
-
|
|
25375
|
-
const path6 =
|
|
25669
|
+
mkdirSync13(dir, { recursive: true });
|
|
25670
|
+
const path6 = join40(dir, pinnedPlanFileName(write.triageClass));
|
|
25376
25671
|
writeFileSync15(path6, write.plan, "utf8");
|
|
25377
25672
|
return path6;
|
|
25378
25673
|
}
|
|
@@ -25772,16 +26067,16 @@ async function redPullRequests(pr) {
|
|
|
25772
26067
|
}
|
|
25773
26068
|
return red;
|
|
25774
26069
|
}
|
|
25775
|
-
async function selectTargets(pr, warn, now,
|
|
26070
|
+
async function selectTargets(pr, warn, now, resolve20) {
|
|
25776
26071
|
const branch = readBranchOrNone(pr, warn);
|
|
25777
26072
|
const current = branch === null ? null : await onProvider(`read the open pull request for the branch "${branch}"`, () => pr.pulls.findOpen(branch));
|
|
25778
26073
|
if (current !== null)
|
|
25779
|
-
return selectTriagePullRequests({ current, candidates: [], now, resolve:
|
|
26074
|
+
return selectTriagePullRequests({ current, candidates: [], now, resolve: resolve20 });
|
|
25780
26075
|
return selectTriagePullRequests({
|
|
25781
26076
|
current: null,
|
|
25782
26077
|
candidates: await redPullRequests(pr),
|
|
25783
26078
|
now,
|
|
25784
|
-
resolve:
|
|
26079
|
+
resolve: resolve20
|
|
25785
26080
|
});
|
|
25786
26081
|
}
|
|
25787
26082
|
async function detailOf11(pr, number) {
|
|
@@ -25902,7 +26197,7 @@ async function runTriage(context, seams) {
|
|
|
25902
26197
|
}
|
|
25903
26198
|
const numbers = asked === null ? (selection?.assess ?? []).map((one) => one.number) : [asked];
|
|
25904
26199
|
const readings = [];
|
|
25905
|
-
let
|
|
26200
|
+
let resolve20 = null;
|
|
25906
26201
|
for (const number of numbers) {
|
|
25907
26202
|
const assess = () => assessOne({
|
|
25908
26203
|
pr,
|
|
@@ -25918,7 +26213,7 @@ async function runTriage(context, seams) {
|
|
|
25918
26213
|
readings.push(reading2);
|
|
25919
26214
|
continue;
|
|
25920
26215
|
}
|
|
25921
|
-
|
|
26216
|
+
resolve20 = await resolvePullRequest({
|
|
25922
26217
|
trust: {
|
|
25923
26218
|
permissions,
|
|
25924
26219
|
trustedAuthors: pr.trustedAuthors,
|
|
@@ -25939,16 +26234,16 @@ async function runTriage(context, seams) {
|
|
|
25939
26234
|
...seams.clock === undefined ? {} : { clock: seams.clock },
|
|
25940
26235
|
...seams.sleep === undefined ? {} : { sleep: seams.sleep }
|
|
25941
26236
|
});
|
|
25942
|
-
readings.push(
|
|
26237
|
+
readings.push(resolve20.reading);
|
|
25943
26238
|
}
|
|
25944
26239
|
const text2 = [
|
|
25945
26240
|
...selection === null ? [] : [selection.message],
|
|
25946
26241
|
...readings.length === 0 ? [] : [renderTriages(readings)],
|
|
25947
|
-
...
|
|
26242
|
+
...resolve20 === null ? [] : resolve20.lines
|
|
25948
26243
|
].join(`
|
|
25949
26244
|
|
|
25950
26245
|
`);
|
|
25951
|
-
return { selection, readings, resolve:
|
|
26246
|
+
return { selection, readings, resolve: resolve20, text: text2, exitCode: resolve20?.exitCode ?? 0 };
|
|
25952
26247
|
}
|
|
25953
26248
|
function createPrTriageCommand(seams = DEFAULT_TRIAGE_SEAMS) {
|
|
25954
26249
|
const command = {
|
|
@@ -26236,8 +26531,8 @@ function createPrWaitCommand(seams = DEFAULT_WAIT_SEAMS) {
|
|
|
26236
26531
|
var wait_default = createPrWaitCommand();
|
|
26237
26532
|
|
|
26238
26533
|
// src/commands/release/status.ts
|
|
26239
|
-
import { existsSync as
|
|
26240
|
-
import { join as
|
|
26534
|
+
import { existsSync as existsSync33, readdirSync as readdirSync11, readFileSync as readFileSync33 } from "fs";
|
|
26535
|
+
import { join as join41 } from "path";
|
|
26241
26536
|
var RELEASE_STATUS_USAGE = "rafa release status [--plan=<stub>]";
|
|
26242
26537
|
var UNREADABLE2 = "?";
|
|
26243
26538
|
var NOTHING = "none";
|
|
@@ -26338,13 +26633,13 @@ function untaggedVersions(released, tags) {
|
|
|
26338
26633
|
}
|
|
26339
26634
|
function textOf4(path6) {
|
|
26340
26635
|
try {
|
|
26341
|
-
return
|
|
26636
|
+
return readFileSync33(path6, "utf8");
|
|
26342
26637
|
} catch {
|
|
26343
26638
|
return null;
|
|
26344
26639
|
}
|
|
26345
26640
|
}
|
|
26346
26641
|
function readVersionFile(root, configured) {
|
|
26347
|
-
const resolved =
|
|
26642
|
+
const resolved = join41(root, configured);
|
|
26348
26643
|
const text2 = textOf4(resolved);
|
|
26349
26644
|
if (text2 === null) {
|
|
26350
26645
|
return { path: configured, version: null, problem: `the version file could not be read at ${resolved}` };
|
|
@@ -26361,7 +26656,7 @@ function readTags(git) {
|
|
|
26361
26656
|
return { tags, latest: tags[0] ?? null, problem: null };
|
|
26362
26657
|
}
|
|
26363
26658
|
function readUntagged(root, configured, tags) {
|
|
26364
|
-
const resolved =
|
|
26659
|
+
const resolved = join41(root, configured);
|
|
26365
26660
|
const text2 = textOf4(resolved);
|
|
26366
26661
|
if (text2 === null) {
|
|
26367
26662
|
return {
|
|
@@ -26380,11 +26675,11 @@ function readBranch2(git) {
|
|
|
26380
26675
|
return result.ok && branch !== "" && branch !== "HEAD" ? branch : null;
|
|
26381
26676
|
}
|
|
26382
26677
|
function planStubsUnder(root, planDir) {
|
|
26383
|
-
const resolved =
|
|
26384
|
-
if (!
|
|
26678
|
+
const resolved = join41(root, planDir);
|
|
26679
|
+
if (!existsSync33(resolved))
|
|
26385
26680
|
return [];
|
|
26386
26681
|
try {
|
|
26387
|
-
return planStubsFromFileNames(
|
|
26682
|
+
return planStubsFromFileNames(readdirSync11(resolved));
|
|
26388
26683
|
} catch {
|
|
26389
26684
|
return [];
|
|
26390
26685
|
}
|
|
@@ -26582,8 +26877,8 @@ function createReleaseStatusCommand(seams = DEFAULT_RELEASE_SEAMS) {
|
|
|
26582
26877
|
var status_default2 = createReleaseStatusCommand();
|
|
26583
26878
|
|
|
26584
26879
|
// src/commands/release/tag.ts
|
|
26585
|
-
import { readFileSync as
|
|
26586
|
-
import { join as
|
|
26880
|
+
import { readFileSync as readFileSync34 } from "fs";
|
|
26881
|
+
import { join as join42 } from "path";
|
|
26587
26882
|
var RELEASE_TAG_USAGE = "rafa release tag";
|
|
26588
26883
|
var DEFAULT_RELEASE_BRANCH = "main";
|
|
26589
26884
|
var DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
@@ -26602,13 +26897,13 @@ function readBranch3(git) {
|
|
|
26602
26897
|
}
|
|
26603
26898
|
function textOf5(path6) {
|
|
26604
26899
|
try {
|
|
26605
|
-
return
|
|
26900
|
+
return readFileSync34(path6, "utf8");
|
|
26606
26901
|
} catch {
|
|
26607
26902
|
return null;
|
|
26608
26903
|
}
|
|
26609
26904
|
}
|
|
26610
26905
|
function readVersion(root, configured) {
|
|
26611
|
-
const resolved =
|
|
26906
|
+
const resolved = join42(root, configured);
|
|
26612
26907
|
const text2 = textOf5(resolved);
|
|
26613
26908
|
if (text2 === null) {
|
|
26614
26909
|
return {
|
|
@@ -26622,7 +26917,7 @@ function readVersion(root, configured) {
|
|
|
26622
26917
|
return version2 === null ? { path: configured, text: text2, version: null, problem: `${resolved} declares no version this can read` } : { path: configured, text: text2, version: version2, problem: null };
|
|
26623
26918
|
}
|
|
26624
26919
|
function readNewestRelease(root, configured) {
|
|
26625
|
-
const resolved =
|
|
26920
|
+
const resolved = join42(root, configured);
|
|
26626
26921
|
const text2 = textOf5(resolved);
|
|
26627
26922
|
if (text2 === null) {
|
|
26628
26923
|
return { path: configured, version: null, problem: `the changelog could not be read at ${resolved}` };
|
|
@@ -26887,17 +27182,17 @@ function createSelfUpdateCommand(seams = DEFAULT_SELF_UPDATE_SEAMS) {
|
|
|
26887
27182
|
var self_update_default = createSelfUpdateCommand();
|
|
26888
27183
|
|
|
26889
27184
|
// src/commands/skill/backfill.ts
|
|
26890
|
-
import { statSync as
|
|
26891
|
-
import { join as
|
|
27185
|
+
import { statSync as statSync19 } from "fs";
|
|
27186
|
+
import { join as join47, resolve as resolve22 } from "path";
|
|
26892
27187
|
|
|
26893
27188
|
// src/backfill/backup.ts
|
|
26894
|
-
import { copyFileSync as copyFileSync3, existsSync as
|
|
26895
|
-
import { dirname as dirname15, isAbsolute as isAbsolute11, join as
|
|
27189
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync34, mkdirSync as mkdirSync14 } from "fs";
|
|
27190
|
+
import { dirname as dirname15, isAbsolute as isAbsolute11, join as join44, relative as relative10, resolve as resolve20, sep as sep5 } from "path";
|
|
26896
27191
|
|
|
26897
27192
|
// src/backfill/proposal-file.ts
|
|
26898
|
-
import { readdirSync as
|
|
26899
|
-
import { join as
|
|
26900
|
-
var BACKFILL_PATH =
|
|
27193
|
+
import { readdirSync as readdirSync12, readFileSync as readFileSync35 } from "fs";
|
|
27194
|
+
import { join as join43 } from "path";
|
|
27195
|
+
var BACKFILL_PATH = join43(".rafa", "backfill");
|
|
26901
27196
|
var PROPOSAL_FILE_PREFIX = "proposals-";
|
|
26902
27197
|
var PROPOSAL_FILE_EXTENSION = ".yaml";
|
|
26903
27198
|
var SHA256_PATTERN = /^[0-9a-f]{64}$/;
|
|
@@ -26923,7 +27218,7 @@ var PROPOSAL_FILE_HEADER = [
|
|
|
26923
27218
|
].join(`
|
|
26924
27219
|
`);
|
|
26925
27220
|
function proposalPath(dir, batch) {
|
|
26926
|
-
return
|
|
27221
|
+
return join43(dir, `${PROPOSAL_FILE_PREFIX}${String(batch).padStart(2, "0")}${PROPOSAL_FILE_EXTENSION}`);
|
|
26927
27222
|
}
|
|
26928
27223
|
function isProposalFileName(name2) {
|
|
26929
27224
|
return name2.startsWith(PROPOSAL_FILE_PREFIX) && name2.endsWith(PROPOSAL_FILE_EXTENSION);
|
|
@@ -27054,14 +27349,14 @@ function parseProposalFile(text2) {
|
|
|
27054
27349
|
function readProposalFiles(dir) {
|
|
27055
27350
|
let names;
|
|
27056
27351
|
try {
|
|
27057
|
-
names =
|
|
27352
|
+
names = readdirSync12(dir);
|
|
27058
27353
|
} catch {
|
|
27059
27354
|
return [];
|
|
27060
27355
|
}
|
|
27061
27356
|
return names.filter((name2) => isProposalFileName(name2)).sort((left, right) => left.localeCompare(right)).map((name2) => {
|
|
27062
|
-
const path6 =
|
|
27357
|
+
const path6 = join43(dir, name2);
|
|
27063
27358
|
try {
|
|
27064
|
-
return { path: path6, result: parseProposalFile(
|
|
27359
|
+
return { path: path6, result: parseProposalFile(readFileSync35(path6, "utf8")) };
|
|
27065
27360
|
} catch (error) {
|
|
27066
27361
|
return { path: path6, result: unreadable2(`the file cannot be read (${error instanceof Error ? error.message : String(error)})`) };
|
|
27067
27362
|
}
|
|
@@ -27069,19 +27364,19 @@ function readProposalFiles(dir) {
|
|
|
27069
27364
|
}
|
|
27070
27365
|
|
|
27071
27366
|
// src/backfill/backup.ts
|
|
27072
|
-
var BACKUP_PATH =
|
|
27367
|
+
var BACKUP_PATH = join44(BACKFILL_PATH, "backup");
|
|
27073
27368
|
var BACKUP_KINDS = ["copied", "kept", "tracked", "outside", "missing"];
|
|
27074
27369
|
function backupDirectory(base) {
|
|
27075
|
-
return
|
|
27370
|
+
return join44(base, BACKUP_PATH);
|
|
27076
27371
|
}
|
|
27077
27372
|
function isUnder(root, path6) {
|
|
27078
|
-
const step2 = relative10(
|
|
27373
|
+
const step2 = relative10(resolve20(root), resolve20(path6));
|
|
27079
27374
|
return step2 === "" || !step2.startsWith(`..${sep5}`) && step2 !== ".." && !isAbsolute11(step2);
|
|
27080
27375
|
}
|
|
27081
27376
|
function backupPathFor(path6, root, backupDir) {
|
|
27082
27377
|
if (!isUnder(root, path6))
|
|
27083
27378
|
return null;
|
|
27084
|
-
return
|
|
27379
|
+
return join44(backupDir, relative10(resolve20(root), resolve20(path6)));
|
|
27085
27380
|
}
|
|
27086
27381
|
function backupFile(path6, options) {
|
|
27087
27382
|
if (options.repoRoot !== null && isUnder(options.repoRoot, path6)) {
|
|
@@ -27090,11 +27385,11 @@ function backupFile(path6, options) {
|
|
|
27090
27385
|
const backup = backupPathFor(path6, options.root, options.backupDir);
|
|
27091
27386
|
if (backup === null)
|
|
27092
27387
|
return { path: path6, kind: "outside", backup: null };
|
|
27093
|
-
if (!
|
|
27388
|
+
if (!existsSync34(path6))
|
|
27094
27389
|
return { path: path6, kind: "missing", backup: null };
|
|
27095
|
-
if (
|
|
27390
|
+
if (existsSync34(backup))
|
|
27096
27391
|
return { path: path6, kind: "kept", backup };
|
|
27097
|
-
|
|
27392
|
+
mkdirSync14(dirname15(backup), { recursive: true });
|
|
27098
27393
|
copyFileSync3(path6, backup);
|
|
27099
27394
|
return { path: path6, kind: "copied", backup };
|
|
27100
27395
|
}
|
|
@@ -27117,8 +27412,8 @@ function countBackups(results) {
|
|
|
27117
27412
|
}
|
|
27118
27413
|
|
|
27119
27414
|
// src/backfill/derive.ts
|
|
27120
|
-
import { readFileSync as
|
|
27121
|
-
import { basename as
|
|
27415
|
+
import { readFileSync as readFileSync36, writeFileSync as writeFileSync16 } from "fs";
|
|
27416
|
+
import { basename as basename13 } from "path";
|
|
27122
27417
|
|
|
27123
27418
|
// src/demote/classify.ts
|
|
27124
27419
|
var LEARNED_DIRECTORY = "learned";
|
|
@@ -27575,15 +27870,15 @@ function fileChanges(name2, data, body, options) {
|
|
|
27575
27870
|
return { changes, notes };
|
|
27576
27871
|
}
|
|
27577
27872
|
function planFile(entry, options) {
|
|
27578
|
-
if (
|
|
27579
|
-
return inert(entry, "skipped", `${
|
|
27873
|
+
if (basename13(entry.path) !== SKILL_FILE) {
|
|
27874
|
+
return inert(entry, "skipped", `${basename13(entry.path)} is no ${SKILL_FILE}, so it registers no skill`);
|
|
27580
27875
|
}
|
|
27581
27876
|
if (entry.name === null) {
|
|
27582
27877
|
return inert(entry, "skipped", "its place implies no skill name, so no stack can be derived from it");
|
|
27583
27878
|
}
|
|
27584
27879
|
let original;
|
|
27585
27880
|
try {
|
|
27586
|
-
original =
|
|
27881
|
+
original = readFileSync36(entry.path, "utf8");
|
|
27587
27882
|
} catch (error) {
|
|
27588
27883
|
return inert(entry, "skipped", `the file cannot be read (${error instanceof Error ? error.message : String(error)})`);
|
|
27589
27884
|
}
|
|
@@ -27627,7 +27922,7 @@ function planDerivation(root, options) {
|
|
|
27627
27922
|
function applyFile(action, options) {
|
|
27628
27923
|
if (action.text === null || action.original === null)
|
|
27629
27924
|
return action;
|
|
27630
|
-
const current =
|
|
27925
|
+
const current = readFileSync36(action.path, "utf8");
|
|
27631
27926
|
if (current !== action.original) {
|
|
27632
27927
|
return {
|
|
27633
27928
|
...action,
|
|
@@ -27658,7 +27953,7 @@ function applyDerivation(plan2, options) {
|
|
|
27658
27953
|
}
|
|
27659
27954
|
|
|
27660
27955
|
// src/backfill/proposal-batch.ts
|
|
27661
|
-
import { readFileSync as
|
|
27956
|
+
import { readFileSync as readFileSync37 } from "fs";
|
|
27662
27957
|
import { relative as relative11 } from "path";
|
|
27663
27958
|
|
|
27664
27959
|
// src/demote/report.ts
|
|
@@ -27957,7 +28252,7 @@ function readCandidate(root, entry) {
|
|
|
27957
28252
|
return null;
|
|
27958
28253
|
let text2;
|
|
27959
28254
|
try {
|
|
27960
|
-
text2 =
|
|
28255
|
+
text2 = readFileSync37(entry.path, "utf8");
|
|
27961
28256
|
} catch {
|
|
27962
28257
|
return null;
|
|
27963
28258
|
}
|
|
@@ -28063,8 +28358,8 @@ function parseSessionAnswer(stdout) {
|
|
|
28063
28358
|
}
|
|
28064
28359
|
|
|
28065
28360
|
// src/backfill/propose.ts
|
|
28066
|
-
import { mkdirSync as
|
|
28067
|
-
import { join as
|
|
28361
|
+
import { mkdirSync as mkdirSync15, readFileSync as readFileSync38, writeFileSync as writeFileSync17 } from "fs";
|
|
28362
|
+
import { join as join45, resolve as resolve21 } from "path";
|
|
28068
28363
|
var PROPOSAL_ACTION_KINDS = [
|
|
28069
28364
|
"applied",
|
|
28070
28365
|
"unchanged",
|
|
@@ -28143,7 +28438,7 @@ async function runProposalPass(options) {
|
|
|
28143
28438
|
const batches = batchCandidates(candidates);
|
|
28144
28439
|
const written = [];
|
|
28145
28440
|
if (batches.length > 0)
|
|
28146
|
-
|
|
28441
|
+
mkdirSync15(options.backfillDir, { recursive: true });
|
|
28147
28442
|
for (const [index, batch] of batches.entries()) {
|
|
28148
28443
|
const number = index + 1;
|
|
28149
28444
|
const file = await runProposalBatch(number, batch, options);
|
|
@@ -28213,7 +28508,7 @@ function writeRow(path6, row2, original, options) {
|
|
|
28213
28508
|
};
|
|
28214
28509
|
}
|
|
28215
28510
|
function applyRow(root, row2, options) {
|
|
28216
|
-
const path6 =
|
|
28511
|
+
const path6 = join45(root, ...row2.path.split("/"));
|
|
28217
28512
|
if (row2.status === UNANSWERED_ROW) {
|
|
28218
28513
|
return inert2(path6, row2, "unanswered", row2.note ?? "the row carries no answer");
|
|
28219
28514
|
}
|
|
@@ -28222,7 +28517,7 @@ function applyRow(root, row2, options) {
|
|
|
28222
28517
|
}
|
|
28223
28518
|
let original;
|
|
28224
28519
|
try {
|
|
28225
|
-
original =
|
|
28520
|
+
original = readFileSync38(path6, "utf8");
|
|
28226
28521
|
} catch {
|
|
28227
28522
|
return inert2(path6, row2, "refused", "the file the row names is gone");
|
|
28228
28523
|
}
|
|
@@ -28236,7 +28531,7 @@ function fileRefusal(file, root) {
|
|
|
28236
28531
|
if (file.status !== REVIEWED_FILE) {
|
|
28237
28532
|
return `the proposal file is still marked ${file.status}: review it and mark it ${REVIEWED_FILE}`;
|
|
28238
28533
|
}
|
|
28239
|
-
return
|
|
28534
|
+
return resolve21(file.skills) === resolve21(root) ? null : `the proposal file names the skills directory ${file.skills}, not ${root}`;
|
|
28240
28535
|
}
|
|
28241
28536
|
function countProposalActions(actions) {
|
|
28242
28537
|
const counts = Object.fromEntries(PROPOSAL_ACTION_KINDS.map((kind) => [kind, 0]));
|
|
@@ -28250,7 +28545,7 @@ function applyProposals(files, root, options) {
|
|
|
28250
28545
|
for (const file of files) {
|
|
28251
28546
|
const refusal10 = fileRefusal(file, root);
|
|
28252
28547
|
for (const row2 of file.rows) {
|
|
28253
|
-
const action = refusal10 === null ? applyRow(root, row2, options) : inert2(
|
|
28548
|
+
const action = refusal10 === null ? applyRow(root, row2, options) : inert2(join45(root, ...row2.path.split("/")), row2, "refused", refusal10);
|
|
28254
28549
|
actions.push(action);
|
|
28255
28550
|
if (row2.trigger !== null && action.kind !== "refused" && action.kind !== "unanswered") {
|
|
28256
28551
|
triggers[row2.name] = row2.trigger;
|
|
@@ -28261,13 +28556,13 @@ function applyProposals(files, root, options) {
|
|
|
28261
28556
|
}
|
|
28262
28557
|
|
|
28263
28558
|
// src/demote/select.ts
|
|
28264
|
-
import { readdirSync as
|
|
28265
|
-
import { basename as
|
|
28559
|
+
import { readdirSync as readdirSync13, readFileSync as readFileSync39, realpathSync as realpathSync7, statSync as statSync18 } from "fs";
|
|
28560
|
+
import { basename as basename14, dirname as dirname16, join as join46 } from "path";
|
|
28266
28561
|
var SKILLS_DIRECTORY = "skills";
|
|
28267
28562
|
var CLAUDE_DIRECTORY = ".claude";
|
|
28268
28563
|
var AUTO_EXTRACTED_ORIGIN = "auto-extracted";
|
|
28269
|
-
var DEMOTED_PATH =
|
|
28270
|
-
var INSTINCTS_PATH =
|
|
28564
|
+
var DEMOTED_PATH = join46(".rafa", "demoted");
|
|
28565
|
+
var INSTINCTS_PATH = join46(".rafa", "instincts");
|
|
28271
28566
|
var REPORT_FILE = "report.md";
|
|
28272
28567
|
function realOrSelf(path6) {
|
|
28273
28568
|
try {
|
|
@@ -28284,25 +28579,25 @@ function samePath(left, right) {
|
|
|
28284
28579
|
return resolved === other || resolved.toLowerCase() === other.toLowerCase();
|
|
28285
28580
|
}
|
|
28286
28581
|
function resolveDemotionScope(dir, home) {
|
|
28287
|
-
if (
|
|
28582
|
+
if (basename14(dir) !== SKILLS_DIRECTORY)
|
|
28288
28583
|
return null;
|
|
28289
28584
|
const claude = dirname16(dir);
|
|
28290
|
-
if (
|
|
28585
|
+
if (basename14(claude) !== CLAUDE_DIRECTORY)
|
|
28291
28586
|
return null;
|
|
28292
28587
|
const base = dirname16(claude);
|
|
28293
|
-
const demotedDir =
|
|
28588
|
+
const demotedDir = join46(base, DEMOTED_PATH);
|
|
28294
28589
|
return {
|
|
28295
28590
|
scope: samePath(base, home) ? "user" : "project",
|
|
28296
28591
|
dir,
|
|
28297
28592
|
base,
|
|
28298
28593
|
demotedDir,
|
|
28299
|
-
instinctsDir:
|
|
28300
|
-
reportPath:
|
|
28594
|
+
instinctsDir: join46(base, INSTINCTS_PATH),
|
|
28595
|
+
reportPath: join46(demotedDir, REPORT_FILE)
|
|
28301
28596
|
};
|
|
28302
28597
|
}
|
|
28303
28598
|
function entryKind2(path6) {
|
|
28304
28599
|
try {
|
|
28305
|
-
const stats =
|
|
28600
|
+
const stats = statSync18(path6);
|
|
28306
28601
|
if (stats.isFile())
|
|
28307
28602
|
return "file";
|
|
28308
28603
|
if (stats.isDirectory())
|
|
@@ -28314,7 +28609,7 @@ function entryKind2(path6) {
|
|
|
28314
28609
|
}
|
|
28315
28610
|
function visibleNames2(dir) {
|
|
28316
28611
|
try {
|
|
28317
|
-
return
|
|
28612
|
+
return readdirSync13(dir).filter((name2) => !name2.startsWith(".")).sort((left, right) => left.localeCompare(right));
|
|
28318
28613
|
} catch {
|
|
28319
28614
|
return [];
|
|
28320
28615
|
}
|
|
@@ -28323,8 +28618,8 @@ function readSelected(absolute, path6, entries) {
|
|
|
28323
28618
|
let text2;
|
|
28324
28619
|
let mtime;
|
|
28325
28620
|
try {
|
|
28326
|
-
text2 =
|
|
28327
|
-
mtime =
|
|
28621
|
+
text2 = readFileSync39(absolute, "utf8");
|
|
28622
|
+
mtime = statSync18(absolute).mtime.toISOString();
|
|
28328
28623
|
} catch {
|
|
28329
28624
|
return null;
|
|
28330
28625
|
}
|
|
@@ -28339,14 +28634,14 @@ function selectsLearned(text2, scope) {
|
|
|
28339
28634
|
return origin === AUTO_EXTRACTED_ORIGIN;
|
|
28340
28635
|
}
|
|
28341
28636
|
function selectLearned(scope) {
|
|
28342
|
-
const dir =
|
|
28637
|
+
const dir = join46(scope.dir, LEARNED_DIRECTORY);
|
|
28343
28638
|
if (entryKind2(dir) !== "directory")
|
|
28344
28639
|
return [];
|
|
28345
28640
|
const selected = [];
|
|
28346
28641
|
for (const name2 of visibleNames2(dir)) {
|
|
28347
28642
|
if (!name2.endsWith(MARKDOWN_EXTENSION) || name2 === MARKDOWN_EXTENSION)
|
|
28348
28643
|
continue;
|
|
28349
|
-
const absolute =
|
|
28644
|
+
const absolute = join46(dir, name2);
|
|
28350
28645
|
if (entryKind2(absolute) !== "file")
|
|
28351
28646
|
continue;
|
|
28352
28647
|
const file = readSelected(absolute, `${LEARNED_DIRECTORY}/${name2}`, undefined);
|
|
@@ -28360,10 +28655,10 @@ function selectSkillFiles(scope) {
|
|
|
28360
28655
|
for (const name2 of visibleNames2(scope.dir)) {
|
|
28361
28656
|
if (name2 === LEARNED_DIRECTORY)
|
|
28362
28657
|
continue;
|
|
28363
|
-
const skillDir =
|
|
28658
|
+
const skillDir = join46(scope.dir, name2);
|
|
28364
28659
|
if (entryKind2(skillDir) !== "directory")
|
|
28365
28660
|
continue;
|
|
28366
|
-
const absolute =
|
|
28661
|
+
const absolute = join46(skillDir, SKILL_FILE);
|
|
28367
28662
|
if (entryKind2(absolute) !== "file")
|
|
28368
28663
|
continue;
|
|
28369
28664
|
const file = readSelected(absolute, `${name2}/${SKILL_FILE}`, visibleNames2(skillDir));
|
|
@@ -28411,7 +28706,7 @@ Usage: ${BACKFILL_USAGE}`);
|
|
|
28411
28706
|
throw new CommandExit(1, `\u274C Expected one directory, and read "${extra}" as a second
|
|
28412
28707
|
Usage: ${BACKFILL_USAGE}`);
|
|
28413
28708
|
}
|
|
28414
|
-
return
|
|
28709
|
+
return resolve22(cwd(), dir);
|
|
28415
28710
|
}
|
|
28416
28711
|
function readConsumerRoot(value, cwd) {
|
|
28417
28712
|
if (value === undefined || value === false)
|
|
@@ -28420,11 +28715,11 @@ function readConsumerRoot(value, cwd) {
|
|
|
28420
28715
|
throw new CommandExit(1, `\u274C --project needs a path, and the line gave it none
|
|
28421
28716
|
Usage: ${BACKFILL_USAGE}`);
|
|
28422
28717
|
}
|
|
28423
|
-
return
|
|
28718
|
+
return resolve22(cwd(), value);
|
|
28424
28719
|
}
|
|
28425
28720
|
function isDirectory3(path6) {
|
|
28426
28721
|
try {
|
|
28427
|
-
return
|
|
28722
|
+
return statSync19(path6).isDirectory();
|
|
28428
28723
|
} catch {
|
|
28429
28724
|
return false;
|
|
28430
28725
|
}
|
|
@@ -28548,7 +28843,7 @@ function reviewedProposals(dir) {
|
|
|
28548
28843
|
return read2.flatMap((entry) => entry.result.file === null ? [] : [entry.result.file]);
|
|
28549
28844
|
}
|
|
28550
28845
|
function rowPaths(files, root) {
|
|
28551
|
-
return files.flatMap((file) => file.status !== REVIEWED_FILE ? [] : file.rows.filter((row2) => row2.status === ANSWERED_ROW).map((row2) =>
|
|
28846
|
+
return files.flatMap((file) => file.status !== REVIEWED_FILE ? [] : file.rows.filter((row2) => row2.status === ANSWERED_ROW).map((row2) => join47(root, ...row2.path.split("/"))));
|
|
28552
28847
|
}
|
|
28553
28848
|
function runApply(context, scope, backfillDir, projectRoot3) {
|
|
28554
28849
|
const options = checkOptions(context, projectRoot3);
|
|
@@ -28594,7 +28889,7 @@ async function runBackfill(context, seams) {
|
|
|
28594
28889
|
const dir = readBackfillDirectory(context.args, seams.cwd);
|
|
28595
28890
|
const projectRoot3 = readConsumerRoot(context.flags["project"], seams.cwd);
|
|
28596
28891
|
const scope = backfillScopeOf(dir, projectOf8(context).home);
|
|
28597
|
-
const backfillDir =
|
|
28892
|
+
const backfillDir = join47(scope.base, BACKFILL_PATH);
|
|
28598
28893
|
if (mode === "propose") {
|
|
28599
28894
|
await runPropose(context, scope, backfillDir, seams);
|
|
28600
28895
|
return;
|
|
@@ -28707,13 +29002,13 @@ function createSkillCheckCommand(seams = DEFAULT_CHECK_SEAMS) {
|
|
|
28707
29002
|
var check_default2 = createSkillCheckCommand();
|
|
28708
29003
|
|
|
28709
29004
|
// src/commands/skill/demote.ts
|
|
28710
|
-
import { existsSync as
|
|
28711
|
-
import { dirname as dirname18, resolve as
|
|
29005
|
+
import { existsSync as existsSync36, mkdirSync as mkdirSync17, readFileSync as readFileSync41, statSync as statSync21, writeFileSync as writeFileSync19 } from "fs";
|
|
29006
|
+
import { dirname as dirname18, resolve as resolve23 } from "path";
|
|
28712
29007
|
|
|
28713
29008
|
// src/demote/apply.ts
|
|
28714
|
-
import { copyFileSync as copyFileSync4, existsSync as
|
|
29009
|
+
import { copyFileSync as copyFileSync4, existsSync as existsSync35, mkdirSync as mkdirSync16, mkdtempSync, readdirSync as readdirSync14, readFileSync as readFileSync40, renameSync as renameSync4, rmSync as rmSync3, statSync as statSync20, unlinkSync as unlinkSync2, writeFileSync as writeFileSync18 } from "fs";
|
|
28715
29010
|
import { tmpdir } from "os";
|
|
28716
|
-
import { dirname as dirname17, join as
|
|
29011
|
+
import { dirname as dirname17, join as join48 } from "path";
|
|
28717
29012
|
var DEMOTION_ACTION_KINDS = [
|
|
28718
29013
|
"demoted",
|
|
28719
29014
|
"kept",
|
|
@@ -28734,18 +29029,18 @@ function learnedSkillName(path6) {
|
|
|
28734
29029
|
return base.slice(0, -MARKDOWN_EXTENSION.length);
|
|
28735
29030
|
}
|
|
28736
29031
|
function sourcePath(scope, path6) {
|
|
28737
|
-
return
|
|
29032
|
+
return join48(scope.dir, ...path6.split("/"));
|
|
28738
29033
|
}
|
|
28739
29034
|
function demotedPath(scope, path6) {
|
|
28740
|
-
return
|
|
29035
|
+
return join48(scope.demotedDir, ...path6.split("/"));
|
|
28741
29036
|
}
|
|
28742
29037
|
function promotedPath(scope, path6) {
|
|
28743
|
-
return
|
|
29038
|
+
return join48(scope.dir, learnedSkillName(path6), SKILL_FILE);
|
|
28744
29039
|
}
|
|
28745
29040
|
function takenInstinctIds(dir) {
|
|
28746
29041
|
let names;
|
|
28747
29042
|
try {
|
|
28748
|
-
names =
|
|
29043
|
+
names = readdirSync14(dir);
|
|
28749
29044
|
} catch {
|
|
28750
29045
|
return [];
|
|
28751
29046
|
}
|
|
@@ -28753,7 +29048,7 @@ function takenInstinctIds(dir) {
|
|
|
28753
29048
|
}
|
|
28754
29049
|
function readText6(path6) {
|
|
28755
29050
|
try {
|
|
28756
|
-
return
|
|
29051
|
+
return statSync20(path6).isFile() ? readFileSync40(path6, "utf8") : null;
|
|
28757
29052
|
} catch {
|
|
28758
29053
|
return null;
|
|
28759
29054
|
}
|
|
@@ -28781,7 +29076,7 @@ function planKept(scope, row2, file) {
|
|
|
28781
29076
|
return inert3(row2.path, "procedure", "kept", "procedure-shaped: left where it is, for the backfill");
|
|
28782
29077
|
}
|
|
28783
29078
|
const to = promotedPath(scope, row2.path);
|
|
28784
|
-
if (
|
|
29079
|
+
if (existsSync35(to) || existsSync35(dirname17(to))) {
|
|
28785
29080
|
return inert3(row2.path, "procedure", "refused", `kept as a skill, but ${to} is already there`);
|
|
28786
29081
|
}
|
|
28787
29082
|
return {
|
|
@@ -28795,7 +29090,7 @@ function planKept(scope, row2, file) {
|
|
|
28795
29090
|
};
|
|
28796
29091
|
}
|
|
28797
29092
|
function checkCandidate(dir, id, text2, pathDirs) {
|
|
28798
|
-
const path6 =
|
|
29093
|
+
const path6 = join48(dir, `${id}${MARKDOWN_EXTENSION}`);
|
|
28799
29094
|
writeFileSync18(path6, text2, "utf8");
|
|
28800
29095
|
const report3 = checkFile(path6, "instinct", { projectRoot: null, pathDirs });
|
|
28801
29096
|
return failureLines2(report3.issues);
|
|
@@ -28816,11 +29111,11 @@ function planDemoted(scope, row2, file, context) {
|
|
|
28816
29111
|
if (failures.length > 0) {
|
|
28817
29112
|
return inert3(row2.path, "observation", "refused", `the record ${id} fails the checker \u2014 ${failures.join("; ")}`);
|
|
28818
29113
|
}
|
|
28819
|
-
const destination =
|
|
29114
|
+
const destination = join48(scope.instinctsDir, `${id}${MARKDOWN_EXTENSION}`);
|
|
28820
29115
|
const kept = demotedPath(scope, row2.path);
|
|
28821
|
-
if (
|
|
29116
|
+
if (existsSync35(destination))
|
|
28822
29117
|
return inert3(row2.path, "observation", "refused", `${destination} is already there`);
|
|
28823
|
-
if (
|
|
29118
|
+
if (existsSync35(kept))
|
|
28824
29119
|
return inert3(row2.path, "observation", "refused", `${kept} is already there`);
|
|
28825
29120
|
context.taken.push(id);
|
|
28826
29121
|
return {
|
|
@@ -28852,7 +29147,7 @@ function planRow(scope, row2, context) {
|
|
|
28852
29147
|
entries: undefined,
|
|
28853
29148
|
absolute,
|
|
28854
29149
|
hash,
|
|
28855
|
-
mtime:
|
|
29150
|
+
mtime: statSync20(absolute).mtime.toISOString()
|
|
28856
29151
|
};
|
|
28857
29152
|
return verdict === "procedure" ? planKept(scope, row2, file) : planDemoted(scope, row2, file, context);
|
|
28858
29153
|
}
|
|
@@ -28863,7 +29158,7 @@ function countActions(actions) {
|
|
|
28863
29158
|
return counts;
|
|
28864
29159
|
}
|
|
28865
29160
|
function planDemotion(report3, scope, seams = DEFAULT_APPLY_SEAMS) {
|
|
28866
|
-
const temp = mkdtempSync(
|
|
29161
|
+
const temp = mkdtempSync(join48(tmpdir(), "rafa-demote-"));
|
|
28867
29162
|
const taken = [...takenInstinctIds(scope.instinctsDir)];
|
|
28868
29163
|
try {
|
|
28869
29164
|
const actions = report3.rows.map((row2) => planRow(scope, row2, { temp, taken, seams }));
|
|
@@ -28873,18 +29168,18 @@ function planDemotion(report3, scope, seams = DEFAULT_APPLY_SEAMS) {
|
|
|
28873
29168
|
}
|
|
28874
29169
|
}
|
|
28875
29170
|
function moveFile(move) {
|
|
28876
|
-
|
|
29171
|
+
mkdirSync16(dirname17(move.to), { recursive: true });
|
|
28877
29172
|
try {
|
|
28878
29173
|
renameSync4(move.from, move.to);
|
|
28879
29174
|
} catch {
|
|
28880
29175
|
copyFileSync4(move.from, move.to);
|
|
28881
|
-
|
|
29176
|
+
unlinkSync2(move.from);
|
|
28882
29177
|
}
|
|
28883
29178
|
}
|
|
28884
29179
|
function applyDemotion(plan2) {
|
|
28885
29180
|
for (const action of plan2.actions) {
|
|
28886
29181
|
if (action.write !== null) {
|
|
28887
|
-
|
|
29182
|
+
mkdirSync16(dirname17(action.write.path), { recursive: true });
|
|
28888
29183
|
writeFileSync18(action.write.path, action.write.text, "utf8");
|
|
28889
29184
|
}
|
|
28890
29185
|
if (action.move !== null)
|
|
@@ -28950,11 +29245,11 @@ Usage: ${DEMOTE_USAGE}`);
|
|
|
28950
29245
|
throw new CommandExit(1, `\u274C Expected one directory, and read "${extra}" as a second
|
|
28951
29246
|
Usage: ${DEMOTE_USAGE}`);
|
|
28952
29247
|
}
|
|
28953
|
-
return
|
|
29248
|
+
return resolve23(cwd(), dir);
|
|
28954
29249
|
}
|
|
28955
29250
|
function isDirectory4(path6) {
|
|
28956
29251
|
try {
|
|
28957
|
-
return
|
|
29252
|
+
return statSync21(path6).isDirectory();
|
|
28958
29253
|
} catch {
|
|
28959
29254
|
return false;
|
|
28960
29255
|
}
|
|
@@ -28982,9 +29277,9 @@ function verdictLine(report3) {
|
|
|
28982
29277
|
return `${String(report3.rows.length)} file(s): ${parts.join(", ")}`;
|
|
28983
29278
|
}
|
|
28984
29279
|
function previousReport(path6, context) {
|
|
28985
|
-
if (!
|
|
29280
|
+
if (!existsSync36(path6))
|
|
28986
29281
|
return null;
|
|
28987
|
-
const parsed = parseDemotionReport(
|
|
29282
|
+
const parsed = parseDemotionReport(readFileSync41(path6, "utf8"));
|
|
28988
29283
|
if (parsed.report !== null)
|
|
28989
29284
|
return parsed.report;
|
|
28990
29285
|
context.output.warn(`the report already at ${path6} does not parse, so no override in it is carried forward:` + ` ${parsed.issues.map((issue4) => `line ${String(issue4.line)} ${issue4.code}`).join(", ")}`);
|
|
@@ -28993,7 +29288,7 @@ function previousReport(path6, context) {
|
|
|
28993
29288
|
function runWrite(context, scope) {
|
|
28994
29289
|
const files = selectSources(scope);
|
|
28995
29290
|
const report3 = buildDemotionReport(files, previousReport(scope.reportPath, context));
|
|
28996
|
-
|
|
29291
|
+
mkdirSync17(dirname18(scope.reportPath), { recursive: true });
|
|
28997
29292
|
writeFileSync19(scope.reportPath, writeDemotionReport(report3), "utf8");
|
|
28998
29293
|
const result = {
|
|
28999
29294
|
applied: false,
|
|
@@ -29017,10 +29312,10 @@ function runWrite(context, scope) {
|
|
|
29017
29312
|
context.output.info(line);
|
|
29018
29313
|
}
|
|
29019
29314
|
function reviewedReport(scope) {
|
|
29020
|
-
if (!
|
|
29315
|
+
if (!existsSync36(scope.reportPath)) {
|
|
29021
29316
|
throw new CommandExit(1, `\u274C No report at ${scope.reportPath}; run "rafa skill demote ${scope.dir}" first, then review it`);
|
|
29022
29317
|
}
|
|
29023
|
-
const parsed = parseDemotionReport(
|
|
29318
|
+
const parsed = parseDemotionReport(readFileSync41(scope.reportPath, "utf8"));
|
|
29024
29319
|
if (parsed.report === null) {
|
|
29025
29320
|
const lines = parsed.issues.map((issue4) => ` line ${String(issue4.line)} ${issue4.code}: ${issue4.message}`);
|
|
29026
29321
|
throw new CommandExit(1, [`\u274C ${scope.reportPath} does not parse:`, ...lines].join(`
|
|
@@ -29125,7 +29420,7 @@ function createSkillDemoteCommand(seams = DEFAULT_DEMOTE_SEAMS) {
|
|
|
29125
29420
|
var demote_default = createSkillDemoteCommand();
|
|
29126
29421
|
|
|
29127
29422
|
// src/commands/skill/list.ts
|
|
29128
|
-
import { readFileSync as
|
|
29423
|
+
import { readFileSync as readFileSync42 } from "fs";
|
|
29129
29424
|
var DEFAULT_SKILL_LIST_SEAMS = Object.freeze({
|
|
29130
29425
|
entry: () => Bun.main
|
|
29131
29426
|
});
|
|
@@ -29145,7 +29440,7 @@ Usage: ${USAGE28}`);
|
|
|
29145
29440
|
function skillStack(path6) {
|
|
29146
29441
|
let text2;
|
|
29147
29442
|
try {
|
|
29148
|
-
text2 =
|
|
29443
|
+
text2 = readFileSync42(path6, "utf8");
|
|
29149
29444
|
} catch {
|
|
29150
29445
|
return [];
|
|
29151
29446
|
}
|