@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/index.js
CHANGED
|
@@ -1914,7 +1914,7 @@ async function report(args, repoRoot) {
|
|
|
1914
1914
|
}
|
|
1915
1915
|
}
|
|
1916
1916
|
// package.json
|
|
1917
|
-
var version = "0.9.
|
|
1917
|
+
var version = "0.9.2";
|
|
1918
1918
|
|
|
1919
1919
|
// src/modules/manifest.ts
|
|
1920
1920
|
var MANIFEST_VERSION = 1;
|
|
@@ -3503,8 +3503,8 @@ function createGhIssueBoard(options) {
|
|
|
3503
3503
|
}
|
|
3504
3504
|
|
|
3505
3505
|
// src/board/issue.ts
|
|
3506
|
-
import { existsSync as existsSync7, mkdirSync as
|
|
3507
|
-
import { dirname as dirname2, resolve as
|
|
3506
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync6, statSync as statSync5, writeFileSync as writeFileSync2 } from "fs";
|
|
3507
|
+
import { dirname as dirname2, resolve as resolve6 } from "path";
|
|
3508
3508
|
|
|
3509
3509
|
// src/board/naming.ts
|
|
3510
3510
|
import path2 from "path";
|
|
@@ -3584,8 +3584,72 @@ function notesPath(specsDir, issue) {
|
|
|
3584
3584
|
return path2.join(specsDir, notesFileName(issue));
|
|
3585
3585
|
}
|
|
3586
3586
|
|
|
3587
|
+
// src/board/previous-copy.ts
|
|
3588
|
+
import { linkSync, mkdirSync as mkdirSync2, readdirSync as readdirSync3, statSync as statSync4, unlinkSync } from "fs";
|
|
3589
|
+
import { basename as basename3, extname, join as join5, resolve as resolve5 } from "path";
|
|
3590
|
+
var PREFIX2 = "board previous copy";
|
|
3591
|
+
var PREVIOUS_DIR_NAME = "previous";
|
|
3592
|
+
var COPY_EXTENSION = ".md";
|
|
3593
|
+
var FIRST_SUFFIX = 2;
|
|
3594
|
+
function previousDir(specsDir) {
|
|
3595
|
+
return join5(specsDir, PREVIOUS_DIR_NAME);
|
|
3596
|
+
}
|
|
3597
|
+
function two(value) {
|
|
3598
|
+
return String(value).padStart(2, "0");
|
|
3599
|
+
}
|
|
3600
|
+
function previousTimestamp(mtime) {
|
|
3601
|
+
const date = `${String(mtime.getUTCFullYear()).padStart(4, "0")}${two(mtime.getUTCMonth() + 1)}${two(mtime.getUTCDate())}`;
|
|
3602
|
+
const time = `${two(mtime.getUTCHours())}${two(mtime.getUTCMinutes())}${two(mtime.getUTCSeconds())}`;
|
|
3603
|
+
return `${date}T${time}Z`;
|
|
3604
|
+
}
|
|
3605
|
+
function previousCopyName(savedName, timestamp, attempt) {
|
|
3606
|
+
const stem = basename3(savedName, extname(savedName));
|
|
3607
|
+
const suffix = attempt < FIRST_SUFFIX ? "" : `-${String(attempt)}`;
|
|
3608
|
+
return `${stem}.${timestamp}${suffix}${COPY_EXTENSION}`;
|
|
3609
|
+
}
|
|
3610
|
+
function isTaken(error) {
|
|
3611
|
+
return error instanceof Error && "code" in error && error.code === "EEXIST";
|
|
3612
|
+
}
|
|
3613
|
+
function linkUnderFreeName(source, directory, savedName, timestamp) {
|
|
3614
|
+
for (let attempt = 1;; attempt += 1) {
|
|
3615
|
+
const name = previousCopyName(savedName, timestamp, attempt);
|
|
3616
|
+
try {
|
|
3617
|
+
linkSync(source, join5(directory, name));
|
|
3618
|
+
return name;
|
|
3619
|
+
} catch (error) {
|
|
3620
|
+
if (!isTaken(error))
|
|
3621
|
+
throw error;
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
}
|
|
3625
|
+
function movePreviousCopy(options) {
|
|
3626
|
+
const { repoRoot, specsDir, path: path3 } = options;
|
|
3627
|
+
const source = resolve5(repoRoot, path3);
|
|
3628
|
+
const directory = previousDir(specsDir);
|
|
3629
|
+
const absoluteDirectory = resolve5(repoRoot, directory);
|
|
3630
|
+
const savedName = basename3(path3);
|
|
3631
|
+
let name;
|
|
3632
|
+
try {
|
|
3633
|
+
const timestamp = previousTimestamp(statSync4(source).mtime);
|
|
3634
|
+
mkdirSync2(absoluteDirectory, { recursive: true });
|
|
3635
|
+
name = linkUnderFreeName(source, absoluteDirectory, savedName, timestamp);
|
|
3636
|
+
} catch (error) {
|
|
3637
|
+
throw new Error(`${PREFIX2}: ${path3} could not be moved to ${directory}: ${messageOf(error)}`, { cause: error });
|
|
3638
|
+
}
|
|
3639
|
+
const moved = {
|
|
3640
|
+
path: join5(directory, name),
|
|
3641
|
+
absolute: join5(absoluteDirectory, name)
|
|
3642
|
+
};
|
|
3643
|
+
try {
|
|
3644
|
+
unlinkSync(source);
|
|
3645
|
+
} catch (error) {
|
|
3646
|
+
throw new Error(`${PREFIX2}: ${path3} was copied to ${moved.path} but could not be removed: ${messageOf(error)}`, { cause: error });
|
|
3647
|
+
}
|
|
3648
|
+
return moved;
|
|
3649
|
+
}
|
|
3650
|
+
|
|
3587
3651
|
// src/board/issue.ts
|
|
3588
|
-
var
|
|
3652
|
+
var PREFIX3 = "board issue";
|
|
3589
3653
|
var ISSUE_VIEW_FIELDS = "number,title,body,state,labels,author";
|
|
3590
3654
|
var SPEC_LABEL = "type:spec";
|
|
3591
3655
|
var ISSUE_REFUSAL_EXIT = 2;
|
|
@@ -3596,7 +3660,7 @@ function detailOf3(result, command) {
|
|
|
3596
3660
|
}
|
|
3597
3661
|
function issueNumber2(value) {
|
|
3598
3662
|
if (!Number.isSafeInteger(value) || value < 1) {
|
|
3599
|
-
throw new TypeError(`${
|
|
3663
|
+
throw new TypeError(`${PREFIX3}: the read refused issue number ${describeValue(value)}, expected a positive whole number`);
|
|
3600
3664
|
}
|
|
3601
3665
|
return String(value);
|
|
3602
3666
|
}
|
|
@@ -3631,11 +3695,11 @@ function readViewedIssue(stdout, number, command) {
|
|
|
3631
3695
|
try {
|
|
3632
3696
|
payload = JSON.parse(stdout);
|
|
3633
3697
|
} catch (error) {
|
|
3634
|
-
throw new Error(`${
|
|
3698
|
+
throw new Error(`${PREFIX3}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
|
|
3635
3699
|
}
|
|
3636
3700
|
const problem = viewProblem(payload, number);
|
|
3637
3701
|
if (problem !== null)
|
|
3638
|
-
throw new Error(`${
|
|
3702
|
+
throw new Error(`${PREFIX3}: ${command} answered ${problem}`);
|
|
3639
3703
|
const checked = payload;
|
|
3640
3704
|
return {
|
|
3641
3705
|
number,
|
|
@@ -3653,7 +3717,7 @@ function createGhSpecIssueReader(options) {
|
|
|
3653
3717
|
const command = `gh issue view ${number} --json ${ISSUE_VIEW_FIELDS}`;
|
|
3654
3718
|
const result = await gh(["issue", "view", number, "--json", ISSUE_VIEW_FIELDS]);
|
|
3655
3719
|
if (!result.ok)
|
|
3656
|
-
throw new Error(`${
|
|
3720
|
+
throw new Error(`${PREFIX3}: ${detailOf3(result, command)}`);
|
|
3657
3721
|
return readViewedIssue(result.stdout, issue, command);
|
|
3658
3722
|
};
|
|
3659
3723
|
}
|
|
@@ -3691,10 +3755,91 @@ ${local}`
|
|
|
3691
3755
|
`)}
|
|
3692
3756
|
`;
|
|
3693
3757
|
}
|
|
3758
|
+
var NOTES_SEPARATOR = `
|
|
3759
|
+
|
|
3760
|
+
${LOCAL_NOTES_HEADING}
|
|
3761
|
+
|
|
3762
|
+
`;
|
|
3763
|
+
var HEADING = /^#{1,6}(?:[ \t]|$)/u;
|
|
3764
|
+
var FENCE = /^ {0,3}(?:`{3,}|~{3,})/u;
|
|
3765
|
+
function commonLines(before, after) {
|
|
3766
|
+
let previous = new Uint32Array(after.length + 1);
|
|
3767
|
+
for (const line of before) {
|
|
3768
|
+
const row = new Uint32Array(after.length + 1);
|
|
3769
|
+
after.forEach((other, index) => {
|
|
3770
|
+
row[index + 1] = line === other ? (previous[index] ?? 0) + 1 : Math.max(previous[index + 1] ?? 0, row[index] ?? 0);
|
|
3771
|
+
});
|
|
3772
|
+
previous = row;
|
|
3773
|
+
}
|
|
3774
|
+
return previous[after.length] ?? 0;
|
|
3775
|
+
}
|
|
3776
|
+
function headingName(line) {
|
|
3777
|
+
const text = line.replace(/^#+/u, "").replace(/[ \t]+#+[ \t]*$/u, "").trim();
|
|
3778
|
+
return text === "" ? line.trim() : text;
|
|
3779
|
+
}
|
|
3780
|
+
function headingSections(lines) {
|
|
3781
|
+
const sections = new Map;
|
|
3782
|
+
let fenced = false;
|
|
3783
|
+
let current = null;
|
|
3784
|
+
const close = () => {
|
|
3785
|
+
if (current === null)
|
|
3786
|
+
return;
|
|
3787
|
+
sections.set(current.key, [...sections.get(current.key) ?? [], current.lines.join(`
|
|
3788
|
+
`)]);
|
|
3789
|
+
};
|
|
3790
|
+
for (const line of lines) {
|
|
3791
|
+
if (FENCE.test(line))
|
|
3792
|
+
fenced = !fenced;
|
|
3793
|
+
if (fenced || !HEADING.test(line)) {
|
|
3794
|
+
current?.lines.push(line);
|
|
3795
|
+
continue;
|
|
3796
|
+
}
|
|
3797
|
+
close();
|
|
3798
|
+
current = { key: line.trim(), lines: [] };
|
|
3799
|
+
}
|
|
3800
|
+
close();
|
|
3801
|
+
return sections;
|
|
3802
|
+
}
|
|
3803
|
+
function changedHeadings(before, after) {
|
|
3804
|
+
const old = headingSections(before);
|
|
3805
|
+
const now = headingSections(after);
|
|
3806
|
+
const keys = [...now.keys(), ...[...old.keys()].filter((key) => !now.has(key))];
|
|
3807
|
+
const changed = keys.filter((key) => JSON.stringify(old.get(key) ?? []) !== JSON.stringify(now.get(key) ?? []));
|
|
3808
|
+
return [...new Set(changed.map(headingName))];
|
|
3809
|
+
}
|
|
3810
|
+
function bodyChangeLine(before, after) {
|
|
3811
|
+
const old = before.split(`
|
|
3812
|
+
`);
|
|
3813
|
+
const now = after.split(`
|
|
3814
|
+
`);
|
|
3815
|
+
const common = commonLines(old, now);
|
|
3816
|
+
const headings = changedHeadings(old, now);
|
|
3817
|
+
const clause = headings.length === 0 ? "no heading changed" : `headings changed: ${headings.join(", ")}`;
|
|
3818
|
+
return `issue body: +${String(now.length - common)} -${String(old.length - common)} lines; ${clause}`;
|
|
3819
|
+
}
|
|
3820
|
+
function notesChangeLine(path3) {
|
|
3821
|
+
return `local notes: ${path3} changed since the saved copy`;
|
|
3822
|
+
}
|
|
3823
|
+
function readSnapshotChange(options) {
|
|
3824
|
+
const { saved, body, notes, notesPath: path3 } = options;
|
|
3825
|
+
if (saved === snapshotText(body, notes))
|
|
3826
|
+
return { kind: "unchanged", bodyLine: null, notesLine: null };
|
|
3827
|
+
const now = normalise(body);
|
|
3828
|
+
if (saved === `${now}
|
|
3829
|
+
` || saved.startsWith(`${now}${NOTES_SEPARATOR}`)) {
|
|
3830
|
+
return { kind: "notes", bodyLine: null, notesLine: notesChangeLine(path3) };
|
|
3831
|
+
}
|
|
3832
|
+
const split = saved.lastIndexOf(NOTES_SEPARATOR);
|
|
3833
|
+
const oldBody = split === -1 ? saved : saved.slice(0, split);
|
|
3834
|
+
const oldNotes = split === -1 ? "" : normalise(saved.slice(split + NOTES_SEPARATOR.length)).trim();
|
|
3835
|
+
const newNotes = notes === null ? "" : normalise(notes).trim();
|
|
3836
|
+
const bodyLine = bodyChangeLine(normalise(oldBody), now);
|
|
3837
|
+
return oldNotes === newNotes ? { kind: "body", bodyLine, notesLine: null } : { kind: "both", bodyLine, notesLine: notesChangeLine(path3) };
|
|
3838
|
+
}
|
|
3694
3839
|
function readIfFile(file, what) {
|
|
3695
3840
|
if (!existsSync7(file))
|
|
3696
3841
|
return null;
|
|
3697
|
-
if (!
|
|
3842
|
+
if (!statSync5(file).isFile()) {
|
|
3698
3843
|
throw new CommandExit(ISSUE_REFUSAL_EXIT, `${what} ${file} is not a file, and a spec is one`);
|
|
3699
3844
|
}
|
|
3700
3845
|
try {
|
|
@@ -3704,7 +3849,7 @@ function readIfFile(file, what) {
|
|
|
3704
3849
|
}
|
|
3705
3850
|
}
|
|
3706
3851
|
function readLocalNotes(repoRoot, specsDir, issue) {
|
|
3707
|
-
return readIfFile(
|
|
3852
|
+
return readIfFile(resolve6(repoRoot, notesPath(specsDir, issue)), "the local notes");
|
|
3708
3853
|
}
|
|
3709
3854
|
function snapshotDiffersMessage(path3, issue) {
|
|
3710
3855
|
return `${path3} 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=${path3} to plan from` + " the snapshot as it stands";
|
|
@@ -3721,27 +3866,38 @@ function writeSpecSnapshot(options) {
|
|
|
3721
3866
|
}
|
|
3722
3867
|
const local = readLocalNotes(repoRoot, specsDir, issue.number);
|
|
3723
3868
|
const text = snapshotText(issue.body, local);
|
|
3724
|
-
const absolute =
|
|
3869
|
+
const absolute = resolve6(repoRoot, path3);
|
|
3725
3870
|
const existing = readIfFile(absolute, "the spec snapshot");
|
|
3726
3871
|
const written = {
|
|
3727
3872
|
path: path3,
|
|
3728
3873
|
absolute,
|
|
3729
3874
|
action: "created",
|
|
3730
3875
|
notes: local === null ? null : notes,
|
|
3876
|
+
previous: null,
|
|
3731
3877
|
text
|
|
3732
3878
|
};
|
|
3733
3879
|
if (existing === text)
|
|
3734
3880
|
return { ...written, action: "unchanged" };
|
|
3735
|
-
|
|
3881
|
+
const action = rewriteAction(existing, issue.body, local, notes, refresh);
|
|
3882
|
+
if (action === null) {
|
|
3736
3883
|
throw new CommandExit(ISSUE_REFUSAL_EXIT, snapshotDiffersMessage(path3, issue.number));
|
|
3737
3884
|
}
|
|
3885
|
+
const previous = action === "created" ? null : movePreviousCopy({ repoRoot, specsDir, path: path3 }).path;
|
|
3738
3886
|
try {
|
|
3739
|
-
|
|
3887
|
+
mkdirSync3(dirname2(absolute), { recursive: true });
|
|
3740
3888
|
writeFileSync2(absolute, text);
|
|
3741
3889
|
} catch (error) {
|
|
3742
|
-
throw new Error(`${
|
|
3890
|
+
throw new Error(`${PREFIX3}: the snapshot ${path3} could not be written: ${messageOf(error)}`, { cause: error });
|
|
3743
3891
|
}
|
|
3744
|
-
return
|
|
3892
|
+
return { ...written, action, previous };
|
|
3893
|
+
}
|
|
3894
|
+
function rewriteAction(existing, body, notes, notesFile, refresh) {
|
|
3895
|
+
if (existing === null)
|
|
3896
|
+
return "created";
|
|
3897
|
+
const { kind } = readSnapshotChange({ saved: existing, body, notes, notesPath: notesFile });
|
|
3898
|
+
if (kind === "notes")
|
|
3899
|
+
return "notes-rebuilt";
|
|
3900
|
+
return refresh ? "refreshed" : null;
|
|
3745
3901
|
}
|
|
3746
3902
|
|
|
3747
3903
|
// src/board/leak.ts
|
|
@@ -3843,7 +3999,7 @@ function requireNoLeak(source, body) {
|
|
|
3843
3999
|
}
|
|
3844
4000
|
|
|
3845
4001
|
// src/board/roadmap.ts
|
|
3846
|
-
var
|
|
4002
|
+
var PREFIX4 = "board roadmap";
|
|
3847
4003
|
var ROADMAP_TITLE = "Roadmap";
|
|
3848
4004
|
var ROADMAP_SETTING = "roadmap.issue";
|
|
3849
4005
|
var ROADMAP_REFUSAL_EXIT = 2;
|
|
@@ -3852,7 +4008,7 @@ var OPEN_PULL_REQUEST_LIMIT = 100;
|
|
|
3852
4008
|
var PR_LIST_FIELDS = "number,headRefName,body";
|
|
3853
4009
|
var ISSUE_LIST_FIELDS = "number,title";
|
|
3854
4010
|
var TASK_LINE = /^\s*[-*+]\s+\[([ xX])\]\s+#(\d+)\s*(.*)$/u;
|
|
3855
|
-
var
|
|
4011
|
+
var FENCE2 = /^\s*(?:`{3,}|~{3,})/u;
|
|
3856
4012
|
var WHY_SEPARATOR = /^[-:,.\u2013\u2014]\s*/u;
|
|
3857
4013
|
var CLOSING = /\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\b\s*:?\s*#(\d+)\b/giu;
|
|
3858
4014
|
function whyOf(rest) {
|
|
@@ -3862,7 +4018,7 @@ function parseRoadmapBody(body) {
|
|
|
3862
4018
|
let lines = [];
|
|
3863
4019
|
let fenced = false;
|
|
3864
4020
|
body.split(/\r\n?|\n/u).forEach((text, index) => {
|
|
3865
|
-
if (
|
|
4021
|
+
if (FENCE2.test(text)) {
|
|
3866
4022
|
fenced = !fenced;
|
|
3867
4023
|
return;
|
|
3868
4024
|
}
|
|
@@ -3893,24 +4049,24 @@ function parseRows(stdout, command) {
|
|
|
3893
4049
|
try {
|
|
3894
4050
|
payload = JSON.parse(stdout);
|
|
3895
4051
|
} catch (error) {
|
|
3896
|
-
throw new Error(`${
|
|
4052
|
+
throw new Error(`${PREFIX4}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
|
|
3897
4053
|
}
|
|
3898
4054
|
if (!Array.isArray(payload)) {
|
|
3899
|
-
throw new Error(`${
|
|
4055
|
+
throw new Error(`${PREFIX4}: ${command} answered ${describeValue(payload)}, expected a list`);
|
|
3900
4056
|
}
|
|
3901
4057
|
return payload;
|
|
3902
4058
|
}
|
|
3903
4059
|
function rowNumber(row, key, command, where) {
|
|
3904
4060
|
const value = isMapping(row) ? row[key] : null;
|
|
3905
4061
|
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 1) {
|
|
3906
|
-
throw new Error(`${
|
|
4062
|
+
throw new Error(`${PREFIX4}: ${command} answered ${where}.${key} as ${describeValue(value)}, expected a positive whole number`);
|
|
3907
4063
|
}
|
|
3908
4064
|
return value;
|
|
3909
4065
|
}
|
|
3910
4066
|
function rowText(row, key, command, where) {
|
|
3911
4067
|
const value = isMapping(row) ? row[key] : null;
|
|
3912
4068
|
if (typeof value !== "string") {
|
|
3913
|
-
throw new Error(`${
|
|
4069
|
+
throw new Error(`${PREFIX4}: ${command} answered ${where}.${key} as ${describeValue(value)}, expected a string`);
|
|
3914
4070
|
}
|
|
3915
4071
|
return value;
|
|
3916
4072
|
}
|
|
@@ -3922,7 +4078,7 @@ function createGhRoadmapSearch(options) {
|
|
|
3922
4078
|
return async () => {
|
|
3923
4079
|
const result = await gh(args);
|
|
3924
4080
|
if (!result.ok)
|
|
3925
|
-
throw new Error(`${
|
|
4081
|
+
throw new Error(`${PREFIX4}: ${detailOf4(result, command)}`);
|
|
3926
4082
|
return Object.freeze(parseRows(result.stdout, command).map((row, index) => ({
|
|
3927
4083
|
number: rowNumber(row, "number", command, `issue ${String(index)}`),
|
|
3928
4084
|
title: rowText(row, "title", command, `issue ${String(index)}`)
|
|
@@ -3957,7 +4113,7 @@ function createGhOpenPullRequests(options) {
|
|
|
3957
4113
|
return async () => {
|
|
3958
4114
|
const result = await gh(args);
|
|
3959
4115
|
if (!result.ok)
|
|
3960
|
-
throw new Error(`${
|
|
4116
|
+
throw new Error(`${PREFIX4}: ${detailOf4(result, command)}`);
|
|
3961
4117
|
return Object.freeze(parseRows(result.stdout, command).map((row, index) => ({
|
|
3962
4118
|
number: rowNumber(row, "number", command, `pull request ${String(index)}`),
|
|
3963
4119
|
headRefName: rowText(row, "headRefName", command, `pull request ${String(index)}`),
|
|
@@ -4216,8 +4372,74 @@ function declinedMessage(alternative) {
|
|
|
4216
4372
|
return `#${String(alternative)} was not planned, and nothing was written`;
|
|
4217
4373
|
}
|
|
4218
4374
|
|
|
4375
|
+
// src/board/snapshot-settle.ts
|
|
4376
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, statSync as statSync6 } from "fs";
|
|
4377
|
+
import { resolve as resolve7 } from "path";
|
|
4378
|
+
var PREFIX5 = "board snapshot settle";
|
|
4379
|
+
var DATE_LENGTH = 10;
|
|
4380
|
+
function savedCopyDate(savedAt) {
|
|
4381
|
+
return savedAt.toISOString().slice(0, DATE_LENGTH);
|
|
4382
|
+
}
|
|
4383
|
+
function refreshQuestion(issue, savedAt) {
|
|
4384
|
+
return `Issue #${String(issue)} changed since the saved copy of ${savedCopyDate(savedAt)}.` + " Plan from it as it reads now? [y/N] ";
|
|
4385
|
+
}
|
|
4386
|
+
function notesRebuiltLine(path3, issue, previous) {
|
|
4387
|
+
return `local notes changed; rebuilt ${path3} from issue #${String(issue)} and the notes,` + ` the old copy is at ${previous}`;
|
|
4388
|
+
}
|
|
4389
|
+
function readSavedCopy(absolute, path3) {
|
|
4390
|
+
if (!existsSync8(absolute))
|
|
4391
|
+
return null;
|
|
4392
|
+
try {
|
|
4393
|
+
const stat = statSync6(absolute);
|
|
4394
|
+
if (!stat.isFile())
|
|
4395
|
+
return null;
|
|
4396
|
+
return { text: readFileSync7(absolute, "utf8"), savedAt: stat.mtime };
|
|
4397
|
+
} catch (error) {
|
|
4398
|
+
throw new Error(`${PREFIX5}: the saved copy ${path3} could not be read: ${messageOf(error)}`, { cause: error });
|
|
4399
|
+
}
|
|
4400
|
+
}
|
|
4401
|
+
function printChange(change, output) {
|
|
4402
|
+
for (const line of [change.bodyLine, change.notesLine]) {
|
|
4403
|
+
if (line !== null)
|
|
4404
|
+
output.info(line);
|
|
4405
|
+
}
|
|
4406
|
+
}
|
|
4407
|
+
async function settleSpecSnapshot(options) {
|
|
4408
|
+
const { repoRoot, specsDir, issue, refresh, output } = options;
|
|
4409
|
+
const write = (rewrite) => writeSpecSnapshot({ repoRoot, specsDir, issue, refresh: rewrite });
|
|
4410
|
+
const path3 = specPath(specsDir, issue.number, issue.title);
|
|
4411
|
+
const notes = notesPath(specsDir, issue.number);
|
|
4412
|
+
if (path3 === notes)
|
|
4413
|
+
return write(refresh);
|
|
4414
|
+
const saved = readSavedCopy(resolve7(repoRoot, path3), path3);
|
|
4415
|
+
if (saved === null)
|
|
4416
|
+
return write(refresh);
|
|
4417
|
+
const change = readSnapshotChange({
|
|
4418
|
+
saved: saved.text,
|
|
4419
|
+
body: issue.body,
|
|
4420
|
+
notes: readLocalNotes(repoRoot, specsDir, issue.number),
|
|
4421
|
+
notesPath: notes
|
|
4422
|
+
});
|
|
4423
|
+
if (change.kind === "unchanged")
|
|
4424
|
+
return write(refresh);
|
|
4425
|
+
printChange(change, output);
|
|
4426
|
+
if (change.kind === "notes") {
|
|
4427
|
+
const rebuilt = write(refresh);
|
|
4428
|
+
if (rebuilt.previous !== null)
|
|
4429
|
+
output.info(notesRebuiltLine(rebuilt.path, issue.number, rebuilt.previous));
|
|
4430
|
+
return rebuilt;
|
|
4431
|
+
}
|
|
4432
|
+
if (refresh)
|
|
4433
|
+
return write(true);
|
|
4434
|
+
const offer = options.offerRefresh ?? null;
|
|
4435
|
+
const accepted = offer !== null && await offer({ issue: issue.number, path: path3, savedAt: saved.savedAt });
|
|
4436
|
+
if (!accepted)
|
|
4437
|
+
throw new CommandExit(ISSUE_REFUSAL_EXIT, snapshotDiffersMessage(path3, issue.number));
|
|
4438
|
+
return write(true);
|
|
4439
|
+
}
|
|
4440
|
+
|
|
4219
4441
|
// src/board/spec-source.ts
|
|
4220
|
-
var
|
|
4442
|
+
var PREFIX6 = "board spec source";
|
|
4221
4443
|
var SOURCE_FLAGS = [SPEC_FLAG, ISSUE_FLAG, NEXT_FLAG];
|
|
4222
4444
|
var SOURCE_REFUSAL_EXIT = 1;
|
|
4223
4445
|
function severalSourcesMessage(given) {
|
|
@@ -4336,7 +4558,7 @@ async function settleBlockedPick(options) {
|
|
|
4336
4558
|
async function pickRoadmapIssue(request, options, issues, output) {
|
|
4337
4559
|
const seams = options.roadmap;
|
|
4338
4560
|
if (seams === undefined) {
|
|
4339
|
-
throw new TypeError(`${
|
|
4561
|
+
throw new TypeError(`${PREFIX6}: ${NEXT_FLAG} was resolved with no roadmap seams`);
|
|
4340
4562
|
}
|
|
4341
4563
|
const roadmap = await resolveRoadmapIssue({
|
|
4342
4564
|
configured: request.roadmap ?? seams.configured,
|
|
@@ -4409,7 +4631,14 @@ async function resolveSpecSource(options) {
|
|
|
4409
4631
|
output.info(dryRunLine(describeIssue(read)));
|
|
4410
4632
|
return stopped("dry-run");
|
|
4411
4633
|
}
|
|
4412
|
-
const snapshot =
|
|
4634
|
+
const snapshot = await settleSpecSnapshot({
|
|
4635
|
+
repoRoot,
|
|
4636
|
+
specsDir,
|
|
4637
|
+
issue: read,
|
|
4638
|
+
refresh,
|
|
4639
|
+
offerRefresh: options.offerRefresh ?? null,
|
|
4640
|
+
output
|
|
4641
|
+
});
|
|
4413
4642
|
return Object.freeze({
|
|
4414
4643
|
outcome: "spec",
|
|
4415
4644
|
spec: Object.freeze({
|
|
@@ -4482,6 +4711,7 @@ async function resolvePlanSpec(options) {
|
|
|
4482
4711
|
const offer = offerReady === null || options.dryRun ? undefined : (issue) => offerReady({ issue, gh, trust: trust(), output });
|
|
4483
4712
|
const offerAlternative = options.offerAlternative ?? null;
|
|
4484
4713
|
const alternative = offerAlternative === null || options.dryRun ? undefined : offerAlternative;
|
|
4714
|
+
const offerRefresh = options.dryRun ? null : options.offerRefresh ?? null;
|
|
4485
4715
|
const resolution = await resolveSpecSource({
|
|
4486
4716
|
request: options.request,
|
|
4487
4717
|
refresh: options.refresh,
|
|
@@ -4491,6 +4721,7 @@ async function resolvePlanSpec(options) {
|
|
|
4491
4721
|
findSpec: options.findSpec,
|
|
4492
4722
|
issues: createGhSpecIssueReader({ gh }),
|
|
4493
4723
|
inspect: (issue) => inspectSpecIssue(issue, trust(), offer),
|
|
4724
|
+
offerRefresh,
|
|
4494
4725
|
roadmap: {
|
|
4495
4726
|
configured: options.roadmapIssue,
|
|
4496
4727
|
search: createGhRoadmapSearch({ gh }),
|
|
@@ -4516,15 +4747,15 @@ async function resolvePlanSpec(options) {
|
|
|
4516
4747
|
import { createInterface as createInterface2 } from "readline";
|
|
4517
4748
|
|
|
4518
4749
|
// src/project/roots.ts
|
|
4519
|
-
import { accessSync, constants, readFileSync as
|
|
4750
|
+
import { accessSync, constants, readFileSync as readFileSync8, statSync as statSync7 } from "fs";
|
|
4520
4751
|
|
|
4521
4752
|
// src/project/scope.ts
|
|
4522
|
-
import { existsSync as
|
|
4523
|
-
import { dirname as dirname3, isAbsolute as isAbsolute2, join as
|
|
4753
|
+
import { existsSync as existsSync9, realpathSync as realpathSync2 } from "fs";
|
|
4754
|
+
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join6 } from "path";
|
|
4524
4755
|
var SCOPE_DIR = dirname3(CONFIG_FILE);
|
|
4525
4756
|
var INIT_COMMAND = "rafa init";
|
|
4526
4757
|
var DISK_FILE_SYSTEM = Object.freeze({
|
|
4527
|
-
exists: (path3) =>
|
|
4758
|
+
exists: (path3) => existsSync9(path3),
|
|
4528
4759
|
realpath: (path3) => realpathSync2(path3)
|
|
4529
4760
|
});
|
|
4530
4761
|
|
|
@@ -4535,7 +4766,7 @@ class ScopeError extends Error {
|
|
|
4535
4766
|
}
|
|
4536
4767
|
}
|
|
4537
4768
|
function scopeAt(base) {
|
|
4538
|
-
return { dir:
|
|
4769
|
+
return { dir: join6(base, SCOPE_DIR), configFile: configFilePath(base) };
|
|
4539
4770
|
}
|
|
4540
4771
|
function initHint(start, passedUserConfig = null) {
|
|
4541
4772
|
const passed2 = passedUserConfig === null ? [] : [`${passedUserConfig} is the user scope's config and marks no project`];
|
|
@@ -4612,8 +4843,8 @@ var MONOREPO_MARKER_FILES = Object.freeze([
|
|
|
4612
4843
|
]);
|
|
4613
4844
|
var DISK_ROOTS_FILE_SYSTEM = Object.freeze({
|
|
4614
4845
|
...DISK_FILE_SYSTEM,
|
|
4615
|
-
readFile: (path3) =>
|
|
4616
|
-
isDirectory: (path3) =>
|
|
4846
|
+
readFile: (path3) => readFileSync8(path3, "utf8"),
|
|
4847
|
+
isDirectory: (path3) => statSync7(path3).isDirectory(),
|
|
4617
4848
|
assertWritable: (path3) => accessSync(path3, constants.W_OK)
|
|
4618
4849
|
});
|
|
4619
4850
|
|
|
@@ -4731,8 +4962,8 @@ function formatRows(rows) {
|
|
|
4731
4962
|
`);
|
|
4732
4963
|
}
|
|
4733
4964
|
function defaultSleep(ms) {
|
|
4734
|
-
return new Promise((
|
|
4735
|
-
setTimeout(
|
|
4965
|
+
return new Promise((resolve8) => {
|
|
4966
|
+
setTimeout(resolve8, ms);
|
|
4736
4967
|
});
|
|
4737
4968
|
}
|
|
4738
4969
|
async function waitForChecks(options) {
|
|
@@ -4877,7 +5108,7 @@ function compareUrl(remote, branch) {
|
|
|
4877
5108
|
return `https://${segments[0] ?? host}/${path3}/compare/${encodeBranch(branch)}${COMPARE_QUERY}`;
|
|
4878
5109
|
}
|
|
4879
5110
|
// src/pr/gh.ts
|
|
4880
|
-
var
|
|
5111
|
+
var PREFIX7 = "gh pull requests";
|
|
4881
5112
|
var REPO_PATH3 = "{owner}/{repo}";
|
|
4882
5113
|
var SUMMARY_FIELDS = "author,baseRefName,headRefName,isCrossRepository,number,state,title,updatedAt,url";
|
|
4883
5114
|
var DETAIL_FIELDS = `${SUMMARY_FIELDS},body,headRefOid,labels,mergeStateStatus,mergeable`;
|
|
@@ -4906,16 +5137,16 @@ function detailOf5(result, command) {
|
|
|
4906
5137
|
return result.ok ? `${command} exited 0 and wrote nothing` : `${command} exited non-zero and wrote nothing`;
|
|
4907
5138
|
}
|
|
4908
5139
|
function failure2(command, result) {
|
|
4909
|
-
return new Error(`${
|
|
5140
|
+
return new Error(`${PREFIX7}: ${command} failed: ${detailOf5(result, command)}`);
|
|
4910
5141
|
}
|
|
4911
5142
|
function refuse3(command, problem) {
|
|
4912
|
-
throw new Error(`${
|
|
5143
|
+
throw new Error(`${PREFIX7}: ${command} answered ${problem}`);
|
|
4913
5144
|
}
|
|
4914
5145
|
function parseJson2(stdout, command) {
|
|
4915
5146
|
try {
|
|
4916
5147
|
return JSON.parse(stdout);
|
|
4917
5148
|
} catch (error) {
|
|
4918
|
-
throw new Error(`${
|
|
5149
|
+
throw new Error(`${PREFIX7}: ${command} wrote output that is not JSON: ${messageOf(error)}`, { cause: error });
|
|
4919
5150
|
}
|
|
4920
5151
|
}
|
|
4921
5152
|
function readString2(value, command, where) {
|
|
@@ -5018,7 +5249,7 @@ function readWorkflowCount(stdout) {
|
|
|
5018
5249
|
}
|
|
5019
5250
|
function pullNumber(value, member) {
|
|
5020
5251
|
if (!Number.isSafeInteger(value) || value < 1) {
|
|
5021
|
-
throw new TypeError(`${
|
|
5252
|
+
throw new TypeError(`${PREFIX7}: ${member} refused pull request number ${describeValue(value)},` + " expected a positive whole number");
|
|
5022
5253
|
}
|
|
5023
5254
|
return String(value);
|
|
5024
5255
|
}
|
|
@@ -5026,13 +5257,13 @@ function textArgument(value, member, what, whole) {
|
|
|
5026
5257
|
const bad = typeof value !== "string" || value === "" || whole && !(WHOLE_NUMBER.test(value) && Number.isSafeInteger(Number(value)));
|
|
5027
5258
|
if (bad) {
|
|
5028
5259
|
const expected2 = whole ? "a positive whole number with no leading zero" : "a non-empty string";
|
|
5029
|
-
throw new TypeError(`${
|
|
5260
|
+
throw new TypeError(`${PREFIX7}: ${member} refused ${what} ${describeValue(value)}, expected ${expected2}`);
|
|
5030
5261
|
}
|
|
5031
5262
|
return value;
|
|
5032
5263
|
}
|
|
5033
5264
|
function bodyText(value, member) {
|
|
5034
5265
|
if (typeof value !== "string") {
|
|
5035
|
-
throw new TypeError(`${
|
|
5266
|
+
throw new TypeError(`${PREFIX7}: ${member} refused a body ${describeValue(value)}, expected a string`);
|
|
5036
5267
|
}
|
|
5037
5268
|
return value;
|
|
5038
5269
|
}
|
|
@@ -5102,7 +5333,7 @@ function createGhPullRequests(options) {
|
|
|
5102
5333
|
merge: async (number, method) => {
|
|
5103
5334
|
const target = pullNumber(number, "merge");
|
|
5104
5335
|
if (!isMergeMethod(method)) {
|
|
5105
|
-
throw new TypeError(`${
|
|
5336
|
+
throw new TypeError(`${PREFIX7}: merge refused method ${describeValue(method)},` + ` expected one of: ${MERGE_METHODS.join(", ")}`);
|
|
5106
5337
|
}
|
|
5107
5338
|
const command = `gh pr merge ${target} --${method}`;
|
|
5108
5339
|
const result = await gh(["pr", "merge", target, `--${method}`]);
|
|
@@ -5418,19 +5649,19 @@ function parseArgs(argv, spec = {}) {
|
|
|
5418
5649
|
|
|
5419
5650
|
// src/commands/loop/loop-sessions.ts
|
|
5420
5651
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
5421
|
-
import { join as
|
|
5652
|
+
import { join as join8, relative } from "path";
|
|
5422
5653
|
|
|
5423
5654
|
// src/loop/sessions.ts
|
|
5424
5655
|
import {
|
|
5425
|
-
linkSync,
|
|
5426
|
-
mkdirSync as
|
|
5427
|
-
readdirSync as
|
|
5428
|
-
readFileSync as
|
|
5656
|
+
linkSync as linkSync2,
|
|
5657
|
+
mkdirSync as mkdirSync4,
|
|
5658
|
+
readdirSync as readdirSync4,
|
|
5659
|
+
readFileSync as readFileSync9,
|
|
5429
5660
|
renameSync as renameSync2,
|
|
5430
5661
|
rmSync,
|
|
5431
5662
|
writeFileSync as writeFileSync3
|
|
5432
5663
|
} from "fs";
|
|
5433
|
-
import { basename as
|
|
5664
|
+
import { basename as basename4, dirname as dirname4, join as join7 } from "path";
|
|
5434
5665
|
var SESSION_STATES = Object.freeze(["running", "paused", "stopped", "done"]);
|
|
5435
5666
|
var RUNS_DIR = "runs";
|
|
5436
5667
|
var RECORD_EXTENSION = ".json";
|
|
@@ -5470,13 +5701,13 @@ function isSessionId(value) {
|
|
|
5470
5701
|
return SESSION_ID_PATTERN.test(value);
|
|
5471
5702
|
}
|
|
5472
5703
|
function runsDir(root) {
|
|
5473
|
-
return
|
|
5704
|
+
return join7(scopeAt(root).dir, RUNS_DIR);
|
|
5474
5705
|
}
|
|
5475
5706
|
function sessionFilePath(root, sessionId) {
|
|
5476
5707
|
if (!isSessionId(sessionId)) {
|
|
5477
5708
|
throw new Error(`session record: unusable session id ${JSON.stringify(sessionId)}`);
|
|
5478
5709
|
}
|
|
5479
|
-
return
|
|
5710
|
+
return join7(runsDir(root), `${sessionId}${RECORD_EXTENSION}`);
|
|
5480
5711
|
}
|
|
5481
5712
|
function describeValue3(value) {
|
|
5482
5713
|
return value === undefined ? "missing" : JSON.stringify(value) ?? String(value);
|
|
@@ -5507,7 +5738,7 @@ function sessionIdProblem(sessionId, file) {
|
|
|
5507
5738
|
if (typeof sessionId !== "string" || !isSessionId(sessionId)) {
|
|
5508
5739
|
return `sessionId is ${describeValue3(sessionId)}, expected a plain file name`;
|
|
5509
5740
|
}
|
|
5510
|
-
return
|
|
5741
|
+
return basename4(file) === `${sessionId}${RECORD_EXTENSION}` ? null : `sessionId ${JSON.stringify(sessionId)} is not the file's name`;
|
|
5511
5742
|
}
|
|
5512
5743
|
function planStubProblem(planStub2) {
|
|
5513
5744
|
return planStub2 === null || typeof planStub2 === "string" && isStampableStub(planStub2) ? null : `planStub is ${describeValue3(planStub2)}, expected null or a plan stub`;
|
|
@@ -5578,7 +5809,7 @@ function readState2(record, isAlive = isPidAlive) {
|
|
|
5578
5809
|
function readRecordFile(file) {
|
|
5579
5810
|
let text;
|
|
5580
5811
|
try {
|
|
5581
|
-
text =
|
|
5812
|
+
text = readFileSync9(file, "utf8");
|
|
5582
5813
|
} catch (error) {
|
|
5583
5814
|
throw new SessionRecordError(file, `cannot be read: ${messageOf(error)}`);
|
|
5584
5815
|
}
|
|
@@ -5586,7 +5817,7 @@ function readRecordFile(file) {
|
|
|
5586
5817
|
}
|
|
5587
5818
|
function recordNames(dir) {
|
|
5588
5819
|
try {
|
|
5589
|
-
return
|
|
5820
|
+
return readdirSync4(dir);
|
|
5590
5821
|
} catch (error) {
|
|
5591
5822
|
if (errorCode(error) === "ENOENT")
|
|
5592
5823
|
return [];
|
|
@@ -5599,7 +5830,7 @@ function byStart(a, b) {
|
|
|
5599
5830
|
function readSessions(root, seams = {}) {
|
|
5600
5831
|
const dir = runsDir(root);
|
|
5601
5832
|
const isAlive = seams.isAlive ?? isPidAlive;
|
|
5602
|
-
const records = recordNames(dir).filter((name) => name.endsWith(RECORD_EXTENSION)).map((name) => readRecordFile(
|
|
5833
|
+
const records = recordNames(dir).filter((name) => name.endsWith(RECORD_EXTENSION)).map((name) => readRecordFile(join7(dir, name))).map((record) => Object.freeze({ ...record, state: readState2(record, isAlive) }));
|
|
5603
5834
|
return Object.freeze(records.sort(byStart));
|
|
5604
5835
|
}
|
|
5605
5836
|
function readSession(root, sessionId, seams = {}) {
|
|
@@ -5625,13 +5856,13 @@ function checkRecord(record, file) {
|
|
|
5625
5856
|
}
|
|
5626
5857
|
function writeRecordFile(file, record, isNew) {
|
|
5627
5858
|
checkRecord(record, file);
|
|
5628
|
-
|
|
5859
|
+
mkdirSync4(dirname4(file), { recursive: true });
|
|
5629
5860
|
const temporary = `${file}.${process.pid}.tmp`;
|
|
5630
5861
|
writeFileSync3(temporary, `${JSON.stringify(record, null, 2)}
|
|
5631
5862
|
`);
|
|
5632
5863
|
try {
|
|
5633
5864
|
if (isNew)
|
|
5634
|
-
|
|
5865
|
+
linkSync2(temporary, file);
|
|
5635
5866
|
else
|
|
5636
5867
|
renameSync2(temporary, file);
|
|
5637
5868
|
} finally {
|
|
@@ -5708,7 +5939,7 @@ function readRecords(root, seams) {
|
|
|
5708
5939
|
}
|
|
5709
5940
|
|
|
5710
5941
|
// src/next/sources.ts
|
|
5711
|
-
var
|
|
5942
|
+
var PREFIX8 = "rafa next";
|
|
5712
5943
|
var DEFAULT_BASE_BRANCH = "main";
|
|
5713
5944
|
var DEFAULT_NEXT_SOURCE_SEAMS = Object.freeze({});
|
|
5714
5945
|
function memoiseIssues2(issues) {
|
|
@@ -5746,7 +5977,7 @@ function ghNextBoard(options) {
|
|
|
5746
5977
|
}
|
|
5747
5978
|
function nextProject(context) {
|
|
5748
5979
|
if (context.project === null)
|
|
5749
|
-
throw new Error(`${
|
|
5980
|
+
throw new Error(`${PREFIX8} runs inside a project, and was handed none`);
|
|
5750
5981
|
return context.project;
|
|
5751
5982
|
}
|
|
5752
5983
|
function nextConfig(project, warn) {
|
|
@@ -5756,7 +5987,7 @@ function nextConfig(project, warn) {
|
|
|
5756
5987
|
if (!(error instanceof ConfigError))
|
|
5757
5988
|
throw error;
|
|
5758
5989
|
throw new CommandExit(1, [
|
|
5759
|
-
`\u274C ${
|
|
5990
|
+
`\u274C ${PREFIX8}: the config cannot be used:`,
|
|
5760
5991
|
...error.problems.map((problem) => ` ${problem}`)
|
|
5761
5992
|
].join(`
|
|
5762
5993
|
`));
|
|
@@ -5775,33 +6006,38 @@ function openNextSources(context, seams = DEFAULT_NEXT_SOURCE_SEAMS) {
|
|
|
5775
6006
|
const git = (seams.openGit ?? createGitRunner)(project.root);
|
|
5776
6007
|
const gh = (seams.openGh ?? ((root) => createGhRunner({ cwd: root })))(project.root);
|
|
5777
6008
|
const loop = resolveLoopSeams({ isAlive: seams.isAlive });
|
|
5778
|
-
|
|
6009
|
+
const board = () => ghNextBoard({ gh, git, configured: config.roadmapIssue });
|
|
6010
|
+
const held = {
|
|
5779
6011
|
base: config.prBase ?? DEFAULT_BASE_BRANCH,
|
|
5780
6012
|
plans: plansDirAt(project.root, config.planDir),
|
|
5781
6013
|
runs: () => readRecords(project.root, loop),
|
|
5782
6014
|
git,
|
|
5783
|
-
pulls: (seams.pullRequests ?? ghPullRequestsIn)(project.root)
|
|
5784
|
-
|
|
6015
|
+
pulls: (seams.pullRequests ?? ghPullRequestsIn)(project.root)
|
|
6016
|
+
};
|
|
6017
|
+
return Object.freeze({
|
|
6018
|
+
...held,
|
|
6019
|
+
board: board(),
|
|
6020
|
+
answer: () => Object.freeze({ ...held, board: board() })
|
|
5785
6021
|
});
|
|
5786
6022
|
}
|
|
5787
6023
|
|
|
5788
6024
|
// src/commands/plan/list.ts
|
|
5789
|
-
import { existsSync as
|
|
5790
|
-
import { join as
|
|
6025
|
+
import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync10 } from "fs";
|
|
6026
|
+
import { join as join9 } from "path";
|
|
5791
6027
|
var USAGE2 = "rafa plan list";
|
|
5792
6028
|
function listing(dir, stub) {
|
|
5793
|
-
const plan =
|
|
5794
|
-
const trackerPath =
|
|
6029
|
+
const plan = join9(dir, planFileName(stub, false));
|
|
6030
|
+
const trackerPath = join9(dir, planFileName(stub, true));
|
|
5795
6031
|
const tracker = isFile(trackerPath) ? trackerPath : null;
|
|
5796
|
-
const planModel = parsePlan(
|
|
5797
|
-
const counted2 = tracker === null ? planModel : parsePlan(
|
|
6032
|
+
const planModel = parsePlan(readFileSync10(plan, "utf8"));
|
|
6033
|
+
const counted2 = tracker === null ? planModel : parsePlan(readFileSync10(tracker, "utf8"));
|
|
5798
6034
|
return { stub, plan, tracker, tasks: countTasks(counted2.tasks), issues: planModel.issues.length };
|
|
5799
6035
|
}
|
|
5800
6036
|
function listPlans(plans) {
|
|
5801
6037
|
const dir = plans.path;
|
|
5802
|
-
if (!
|
|
6038
|
+
if (!existsSync10(dir))
|
|
5803
6039
|
return { dir, plans: [] };
|
|
5804
|
-
const stubs =
|
|
6040
|
+
const stubs = readdirSync5(dir).filter((name) => isFile(join9(dir, name))).map((name) => stubOfPlanFile(name)).filter((stub) => stub !== null).sort((a, b) => a.localeCompare(b));
|
|
5805
6041
|
return { dir, plans: stubs.map((stub) => listing(dir, stub)) };
|
|
5806
6042
|
}
|
|
5807
6043
|
function rowNotes(plan) {
|
|
@@ -5996,7 +6232,7 @@ function branchLabel(world) {
|
|
|
5996
6232
|
}
|
|
5997
6233
|
|
|
5998
6234
|
// src/next/state.ts
|
|
5999
|
-
var
|
|
6235
|
+
var PREFIX9 = "rafa next";
|
|
6000
6236
|
var MAX_NAMED_FILES = 5;
|
|
6001
6237
|
var NEXT_PRECONDITIONS = Object.freeze([
|
|
6002
6238
|
"tree-modified",
|
|
@@ -6274,7 +6510,7 @@ async function readNextState(sources) {
|
|
|
6274
6510
|
if (found !== null)
|
|
6275
6511
|
return answer(found, world.problems());
|
|
6276
6512
|
}
|
|
6277
|
-
throw new Error(`${
|
|
6513
|
+
throw new Error(`${PREFIX9}: no row of the table answered, and the last row answers for every reading`);
|
|
6278
6514
|
}
|
|
6279
6515
|
|
|
6280
6516
|
// src/next/hint.ts
|
|
@@ -6300,8 +6536,8 @@ function wantsHint(flags) {
|
|
|
6300
6536
|
}
|
|
6301
6537
|
var DEFAULT_HINT_SEAMS = Object.freeze({});
|
|
6302
6538
|
function afterDelay(ms) {
|
|
6303
|
-
return new Promise((
|
|
6304
|
-
setTimeout(
|
|
6539
|
+
return new Promise((resolve8) => {
|
|
6540
|
+
setTimeout(resolve8, ms).unref();
|
|
6305
6541
|
});
|
|
6306
6542
|
}
|
|
6307
6543
|
function silenced() {
|
|
@@ -6355,10 +6591,10 @@ async function nextStepHint(context, seams = DEFAULT_HINT_SEAMS) {
|
|
|
6355
6591
|
}
|
|
6356
6592
|
|
|
6357
6593
|
// src/next/actions.ts
|
|
6358
|
-
var
|
|
6594
|
+
var PREFIX10 = "rafa next";
|
|
6359
6595
|
function needed(state, value, what) {
|
|
6360
6596
|
if (value === null) {
|
|
6361
|
-
throw new Error(`${
|
|
6597
|
+
throw new Error(`${PREFIX10}: state "${state.id}" proposes "${state.action}" and names no ${what}`);
|
|
6362
6598
|
}
|
|
6363
6599
|
return String(value);
|
|
6364
6600
|
}
|
|
@@ -6408,12 +6644,12 @@ function actionContext(caller, command, argv) {
|
|
|
6408
6644
|
async function runAction(caller, state) {
|
|
6409
6645
|
const invocation = actionInvocation(state);
|
|
6410
6646
|
if (invocation === null) {
|
|
6411
|
-
throw new Error(`${
|
|
6647
|
+
throw new Error(`${PREFIX10}: the action "${state.action}" of state "${state.id}" runs no registered command`);
|
|
6412
6648
|
}
|
|
6413
6649
|
const spec = ACTION_COMMANDS[invocation.action];
|
|
6414
6650
|
const command = caller.registry.find(spec.subject, spec.action);
|
|
6415
6651
|
if (command === undefined) {
|
|
6416
|
-
throw new CommandExit(1, `\u274C ${
|
|
6652
|
+
throw new CommandExit(1, `\u274C ${PREFIX10}: the "${invocation.action}" step runs "rafa ${invocation.command}", which is registered by nothing`);
|
|
6417
6653
|
}
|
|
6418
6654
|
await command.run(actionContext(caller, command, invocation.argv));
|
|
6419
6655
|
}
|
|
@@ -6452,7 +6688,7 @@ async function endWithNextStep(context, seams = DEFAULT_ENDING_SEAMS) {
|
|
|
6452
6688
|
}
|
|
6453
6689
|
|
|
6454
6690
|
// src/adapters/tracker/resolve.ts
|
|
6455
|
-
var
|
|
6691
|
+
var PREFIX11 = "tracker chain";
|
|
6456
6692
|
var FAILURE_SEPARATOR = "; ";
|
|
6457
6693
|
function warnThroughActiveOutput(message) {
|
|
6458
6694
|
activeOutput().warn(message);
|
|
@@ -6493,9 +6729,9 @@ async function resolveTracker(options) {
|
|
|
6493
6729
|
}
|
|
6494
6730
|
attempts = [...attempts, Object.freeze({ kind, ok: false, reason: outcome.reason })];
|
|
6495
6731
|
failures = [...failures, `${kind}: ${outcome.reason}`];
|
|
6496
|
-
log(`${
|
|
6732
|
+
log(`${PREFIX11}: ${kind} unavailable: ${outcome.reason}`);
|
|
6497
6733
|
}
|
|
6498
|
-
throw new Error(`${
|
|
6734
|
+
throw new Error(`${PREFIX11}: no tracker available; tried ${failures.join(FAILURE_SEPARATOR)}`);
|
|
6499
6735
|
}
|
|
6500
6736
|
|
|
6501
6737
|
// src/commands/issue/issue-tracker.ts
|
|
@@ -6738,6 +6974,23 @@ function createPlanReadyOffer(seams = DEFAULT_OFFER_SEAMS) {
|
|
|
6738
6974
|
};
|
|
6739
6975
|
}
|
|
6740
6976
|
|
|
6977
|
+
// src/commands/plan/refresh-offer.ts
|
|
6978
|
+
var DEFAULT_REFRESH_OFFER_SEAMS = Object.freeze({});
|
|
6979
|
+
function createPlanRefreshOffer(seams = DEFAULT_REFRESH_OFFER_SEAMS) {
|
|
6980
|
+
const isTerminal = seams.isTerminal ?? (() => process.stdin.isTTY === true);
|
|
6981
|
+
if (!isTerminal())
|
|
6982
|
+
return null;
|
|
6983
|
+
const openPrompter = seams.openPrompter ?? (() => createLinePrompter(process.stdin, process.stderr));
|
|
6984
|
+
return async (request) => {
|
|
6985
|
+
const prompter = lazyPrompter(openPrompter);
|
|
6986
|
+
try {
|
|
6987
|
+
return await prompter.ask(refreshQuestion(request.issue, request.savedAt));
|
|
6988
|
+
} finally {
|
|
6989
|
+
prompter.close();
|
|
6990
|
+
}
|
|
6991
|
+
};
|
|
6992
|
+
}
|
|
6993
|
+
|
|
6741
6994
|
// src/commands/plan/spec-route.ts
|
|
6742
6995
|
function specCandidates(specArg, specsDir) {
|
|
6743
6996
|
if (path3.isAbsolute(specArg))
|
|
@@ -6767,10 +7020,11 @@ async function resolveCreateSpec(options, seams = DEFAULT_ROUTE_SEAMS) {
|
|
|
6767
7020
|
const source = readSpecSourceFlags(options.args);
|
|
6768
7021
|
if (source.request === null)
|
|
6769
7022
|
throw new CommandExit(SOURCE_REFUSAL_EXIT, usageRefusal(specsDir));
|
|
6770
|
-
const
|
|
7023
|
+
const resolve8 = seams.resolve ?? resolvePlanSpec;
|
|
6771
7024
|
const makeReadyOffer = seams.makeReadyOffer ?? createPlanReadyOffer;
|
|
6772
7025
|
const makeAlternativeOffer = seams.makeAlternativeOffer ?? createPlanBlockedOffer;
|
|
6773
|
-
|
|
7026
|
+
const makeRefreshOffer = seams.makeRefreshOffer ?? createPlanRefreshOffer;
|
|
7027
|
+
return await resolve8({
|
|
6774
7028
|
request: source.request,
|
|
6775
7029
|
refresh: source.refresh,
|
|
6776
7030
|
dryRun: source.dryRun,
|
|
@@ -6780,7 +7034,8 @@ async function resolveCreateSpec(options, seams = DEFAULT_ROUTE_SEAMS) {
|
|
|
6780
7034
|
trustedAuthors: options.trustedAuthors,
|
|
6781
7035
|
findSpec: (spec) => findSpec(repoRoot, spec, specsDir),
|
|
6782
7036
|
offerReady: makeReadyOffer(),
|
|
6783
|
-
offerAlternative: makeAlternativeOffer()
|
|
7037
|
+
offerAlternative: makeAlternativeOffer(),
|
|
7038
|
+
offerRefresh: makeRefreshOffer()
|
|
6784
7039
|
});
|
|
6785
7040
|
}
|
|
6786
7041
|
|
|
@@ -6791,13 +7046,13 @@ import { homedir as homedir3 } from "os";
|
|
|
6791
7046
|
var RAFA_VERSION = version;
|
|
6792
7047
|
|
|
6793
7048
|
// src/notices/notices.ts
|
|
6794
|
-
import { mkdirSync as
|
|
6795
|
-
import { dirname as dirname5, join as
|
|
7049
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "fs";
|
|
7050
|
+
import { dirname as dirname5, join as join10 } from "path";
|
|
6796
7051
|
var NOTICE_IDS = Object.freeze(["alpha", "danger"]);
|
|
6797
7052
|
var FEEDBACK_URL = "https://github.com/open-tomato/rafa/issues/new/choose";
|
|
6798
7053
|
var ROADMAP_URL = "https://github.com/open-tomato/rafa/issues/31";
|
|
6799
7054
|
function noticesPath(home) {
|
|
6800
|
-
return
|
|
7055
|
+
return join10(home, ".rafa", "notices.json");
|
|
6801
7056
|
}
|
|
6802
7057
|
function isNoticeId(value) {
|
|
6803
7058
|
return typeof value === "string" && NOTICE_IDS.includes(value);
|
|
@@ -6805,7 +7060,7 @@ function isNoticeId(value) {
|
|
|
6805
7060
|
function readDismissed(home) {
|
|
6806
7061
|
let raw;
|
|
6807
7062
|
try {
|
|
6808
|
-
raw =
|
|
7063
|
+
raw = readFileSync11(noticesPath(home), "utf8");
|
|
6809
7064
|
} catch {
|
|
6810
7065
|
return [];
|
|
6811
7066
|
}
|
|
@@ -6824,7 +7079,7 @@ function readDismissed(home) {
|
|
|
6824
7079
|
function writeDismissed(home, ids) {
|
|
6825
7080
|
const all = NOTICE_IDS.filter((id) => ids.includes(id) || readDismissed(home).includes(id));
|
|
6826
7081
|
const path4 = noticesPath(home);
|
|
6827
|
-
|
|
7082
|
+
mkdirSync5(dirname5(path4), { recursive: true });
|
|
6828
7083
|
writeFileSync4(path4, `${JSON.stringify({ dismissed: all }, null, 2)}
|
|
6829
7084
|
`);
|
|
6830
7085
|
}
|
|
@@ -7514,7 +7769,7 @@ import { randomUUID as randomUUID7 } from "crypto";
|
|
|
7514
7769
|
|
|
7515
7770
|
// src/effort/store/absences.ts
|
|
7516
7771
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
7517
|
-
import { existsSync as
|
|
7772
|
+
import { existsSync as existsSync11 } from "fs";
|
|
7518
7773
|
var ABSENCE_REASONS = {
|
|
7519
7774
|
"no-block": true,
|
|
7520
7775
|
"unclosed-block": true,
|
|
@@ -7573,13 +7828,13 @@ function writeReportAbsence(repoRoot, write, seams = {}) {
|
|
|
7573
7828
|
write.outcome,
|
|
7574
7829
|
collectedAt
|
|
7575
7830
|
];
|
|
7576
|
-
const appended = withSqliteStore(path5, !
|
|
7831
|
+
const appended = withSqliteStore(path5, !existsSync11(path5), (db) => db.query(INSERT_ABSENCE).run(...values).changes);
|
|
7577
7832
|
return { path: path5, appended, skipped: 1 - appended };
|
|
7578
7833
|
}
|
|
7579
7834
|
|
|
7580
7835
|
// src/effort/store/changes.ts
|
|
7581
7836
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
7582
|
-
import { existsSync as
|
|
7837
|
+
import { existsSync as existsSync12 } from "fs";
|
|
7583
7838
|
var INSERT_CHANGE = `
|
|
7584
7839
|
INSERT INTO changes (
|
|
7585
7840
|
id, session_id, plan_stub, task_line,
|
|
@@ -7658,7 +7913,7 @@ var SELECT_PLAN_CHANGES = `
|
|
|
7658
7913
|
`;
|
|
7659
7914
|
function readPlanChanges(repoRoot, planStub2) {
|
|
7660
7915
|
const path5 = sqliteStorePath(repoRoot);
|
|
7661
|
-
if (!
|
|
7916
|
+
if (!existsSync12(path5))
|
|
7662
7917
|
return [];
|
|
7663
7918
|
const rows = withSqliteStore(path5, false, (db) => db.query(SELECT_PLAN_CHANGES).all(planStub2));
|
|
7664
7919
|
return rows.map((row) => ({
|
|
@@ -7891,8 +8146,8 @@ function describeTaskReportRecord(record) {
|
|
|
7891
8146
|
}
|
|
7892
8147
|
|
|
7893
8148
|
// src/utils/progress.ts
|
|
7894
|
-
import { existsSync as
|
|
7895
|
-
import { join as
|
|
8149
|
+
import { existsSync as existsSync13, writeFileSync as writeFileSync5 } from "fs";
|
|
8150
|
+
import { join as join11 } from "path";
|
|
7896
8151
|
var PROGRESS_FILE_NAME = "progress.txt";
|
|
7897
8152
|
var PROGRESS_CAP_BYTES = 16000;
|
|
7898
8153
|
var LISTED_FIELDS = [
|
|
@@ -7934,7 +8189,7 @@ var ESCAPED_CODE_POINTS = new Set([
|
|
|
7934
8189
|
8233
|
|
7935
8190
|
]);
|
|
7936
8191
|
function progressFilePath(repoRoot) {
|
|
7937
|
-
return
|
|
8192
|
+
return join11(repoRoot, PROGRESS_FILE_NAME);
|
|
7938
8193
|
}
|
|
7939
8194
|
function escapeValue(value) {
|
|
7940
8195
|
let escaped = "";
|
|
@@ -7995,7 +8250,7 @@ function renderProgressText(findings, capBytes = PROGRESS_CAP_BYTES) {
|
|
|
7995
8250
|
}
|
|
7996
8251
|
function readProgressFindings(repoRoot, planStub2) {
|
|
7997
8252
|
const path5 = sqliteStorePath(repoRoot);
|
|
7998
|
-
if (!
|
|
8253
|
+
if (!existsSync13(path5))
|
|
7999
8254
|
return [];
|
|
8000
8255
|
const rows = withSqliteStore(path5, false, (db) => {
|
|
8001
8256
|
const query = db.query(SELECT_FINDINGS);
|
|
@@ -8022,16 +8277,16 @@ function writeProgress(repoRoot, planStub2) {
|
|
|
8022
8277
|
|
|
8023
8278
|
// src/start/preflight.ts
|
|
8024
8279
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
8025
|
-
import { existsSync as
|
|
8280
|
+
import { existsSync as existsSync14, readFileSync as readFileSync13 } from "fs";
|
|
8026
8281
|
import { homedir as homedir5 } from "os";
|
|
8027
|
-
import { basename as
|
|
8282
|
+
import { basename as basename5 } from "path";
|
|
8028
8283
|
|
|
8029
8284
|
// src/preflight/first-dispatch.ts
|
|
8030
|
-
import { readFileSync as
|
|
8285
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
8031
8286
|
var TICKED_TASK = /^- \[[xX]\] (.+)/;
|
|
8032
8287
|
function readIfReadable(path5) {
|
|
8033
8288
|
try {
|
|
8034
|
-
return
|
|
8289
|
+
return readFileSync12(path5, "utf8");
|
|
8035
8290
|
} catch {
|
|
8036
8291
|
return null;
|
|
8037
8292
|
}
|
|
@@ -8064,11 +8319,11 @@ function knownMissingNotice(lines) {
|
|
|
8064
8319
|
}
|
|
8065
8320
|
function agentSourcePath(planPath) {
|
|
8066
8321
|
const tracker = trackerPathFor(planPath);
|
|
8067
|
-
return
|
|
8322
|
+
return existsSync14(tracker) ? tracker : planPath;
|
|
8068
8323
|
}
|
|
8069
8324
|
function readIfReadable2(path5) {
|
|
8070
8325
|
try {
|
|
8071
|
-
return
|
|
8326
|
+
return readFileSync13(path5, "utf8");
|
|
8072
8327
|
} catch {
|
|
8073
8328
|
return null;
|
|
8074
8329
|
}
|
|
@@ -8084,7 +8339,7 @@ function refuseUnresolvableAgents(options) {
|
|
|
8084
8339
|
if (missing.length === 0)
|
|
8085
8340
|
return;
|
|
8086
8341
|
throw new CommandExit(1, [
|
|
8087
|
-
`\u274C Refusing to start: ${
|
|
8342
|
+
`\u274C Refusing to start: ${basename5(path5)} names ${missing.length} agent(s) no loaded scope` + ` defines (loop.settingSources: ${settingSources.join(", ")}).`,
|
|
8088
8343
|
...missing.map((agent) => ` ${missingAgentLine(agent)}`),
|
|
8089
8344
|
" Nothing was checked and nothing was dispatched."
|
|
8090
8345
|
].join(`
|
|
@@ -8117,7 +8372,7 @@ function skippedStartLine(item, tracker) {
|
|
|
8117
8372
|
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`;
|
|
8118
8373
|
}
|
|
8119
8374
|
function announceSkippedStart(planPath, skipped) {
|
|
8120
|
-
const tracker =
|
|
8375
|
+
const tracker = basename5(trackerPathFor(planPath));
|
|
8121
8376
|
const output = activeOutput();
|
|
8122
8377
|
for (const [index, item] of skipped.entries()) {
|
|
8123
8378
|
const lead = index === 0 ? `
|
|
@@ -8136,7 +8391,7 @@ function startTier(planPath, items) {
|
|
|
8136
8391
|
function announceReminders(planPath, reminders) {
|
|
8137
8392
|
if (reminders.length === 0)
|
|
8138
8393
|
return;
|
|
8139
|
-
const file =
|
|
8394
|
+
const file = basename5(prerequisitesPathForPlan(planPath) ?? planPath);
|
|
8140
8395
|
const output = activeOutput();
|
|
8141
8396
|
output.info(`
|
|
8142
8397
|
\uD83D\uDCCC ${file} names ${reminders.length} step(s) the preflight does not check:`);
|
|
@@ -8372,14 +8627,14 @@ async function holdWhilePaused(options) {
|
|
|
8372
8627
|
}
|
|
8373
8628
|
|
|
8374
8629
|
// src/start/plan-path.ts
|
|
8375
|
-
import { existsSync as
|
|
8376
|
-
import { resolve as
|
|
8630
|
+
import { existsSync as existsSync15 } from "fs";
|
|
8631
|
+
import { resolve as resolve8 } from "path";
|
|
8377
8632
|
var DEFAULT_PLAN_FILE = "PLAN.md";
|
|
8378
8633
|
function resolvePlanPath(repoRoot, planDir, planArg) {
|
|
8379
8634
|
if (planArg)
|
|
8380
|
-
return
|
|
8381
|
-
const inPlanDir =
|
|
8382
|
-
return
|
|
8635
|
+
return resolve8(repoRoot, planArg);
|
|
8636
|
+
const inPlanDir = resolve8(repoRoot, planDir, DEFAULT_PLAN_FILE);
|
|
8637
|
+
return existsSync15(inPlanDir) ? inPlanDir : resolve8(repoRoot, DEFAULT_PLAN_FILE);
|
|
8383
8638
|
}
|
|
8384
8639
|
|
|
8385
8640
|
// src/utils/git.ts
|
|
@@ -8562,7 +8817,7 @@ async function handleRedChecks(io, branch, prNumber, rows) {
|
|
|
8562
8817
|
}
|
|
8563
8818
|
|
|
8564
8819
|
// src/release/prepare.ts
|
|
8565
|
-
import { readFileSync as
|
|
8820
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync7 } from "fs";
|
|
8566
8821
|
|
|
8567
8822
|
// src/release/changelog.ts
|
|
8568
8823
|
var RELEASE_HEADING_FIELDS = ["version", "date", "title"];
|
|
@@ -8747,17 +9002,17 @@ function changelogDate(now) {
|
|
|
8747
9002
|
}
|
|
8748
9003
|
|
|
8749
9004
|
// src/release/enabled.ts
|
|
8750
|
-
import { statSync as
|
|
8751
|
-
import { resolve as
|
|
9005
|
+
import { statSync as statSync8 } from "fs";
|
|
9006
|
+
import { resolve as resolve9 } from "path";
|
|
8752
9007
|
function isRegularFile(full) {
|
|
8753
9008
|
try {
|
|
8754
|
-
return
|
|
9009
|
+
return statSync8(full).isFile();
|
|
8755
9010
|
} catch {
|
|
8756
9011
|
return false;
|
|
8757
9012
|
}
|
|
8758
9013
|
}
|
|
8759
9014
|
function readFile(repoRoot, path5) {
|
|
8760
|
-
const resolved =
|
|
9015
|
+
const resolved = resolve9(repoRoot, path5);
|
|
8761
9016
|
return { path: path5, resolved, present: isRegularFile(resolved) };
|
|
8762
9017
|
}
|
|
8763
9018
|
function resolveReleaseEnabled(settings, repoRoot) {
|
|
@@ -8794,7 +9049,7 @@ function resolveReleaseLevel(declared, notes) {
|
|
|
8794
9049
|
}
|
|
8795
9050
|
|
|
8796
9051
|
// src/release/version.ts
|
|
8797
|
-
import { readFileSync as
|
|
9052
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync6 } from "fs";
|
|
8798
9053
|
var RELEASE_REMOTE = "origin";
|
|
8799
9054
|
var RELEASE_BASE_BRANCH = "main";
|
|
8800
9055
|
var VERSION_SHAPE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?(?:\+([0-9A-Za-z.-]+))?$/;
|
|
@@ -8886,7 +9141,7 @@ function writeManifestVersion(path5, next) {
|
|
|
8886
9141
|
const unwritten = { path: path5, written: false, previous: null, version: next };
|
|
8887
9142
|
let text;
|
|
8888
9143
|
try {
|
|
8889
|
-
text =
|
|
9144
|
+
text = readFileSync14(path5, "utf8");
|
|
8890
9145
|
} catch (error) {
|
|
8891
9146
|
return { ...unwritten, problem: `${path5} could not be read: ${messageOf(error)}` };
|
|
8892
9147
|
}
|
|
@@ -8989,7 +9244,7 @@ function planVersion(input, versionFile, level) {
|
|
|
8989
9244
|
}
|
|
8990
9245
|
function textOf(path5) {
|
|
8991
9246
|
try {
|
|
8992
|
-
return
|
|
9247
|
+
return readFileSync15(path5, "utf8");
|
|
8993
9248
|
} catch {
|
|
8994
9249
|
return null;
|
|
8995
9250
|
}
|
|
@@ -9102,7 +9357,7 @@ function changelogFailure(failed3, versionEdit, restored) {
|
|
|
9102
9357
|
}
|
|
9103
9358
|
|
|
9104
9359
|
// src/release/verify.ts
|
|
9105
|
-
import { readFileSync as
|
|
9360
|
+
import { readFileSync as readFileSync16, writeFileSync as writeFileSync8 } from "fs";
|
|
9106
9361
|
function changelogInsertionSpan(before, after) {
|
|
9107
9362
|
const beforeLines = before.split(`
|
|
9108
9363
|
`);
|
|
@@ -9119,7 +9374,7 @@ function changelogInsertionSpan(before, after) {
|
|
|
9119
9374
|
}
|
|
9120
9375
|
function textOf2(path5) {
|
|
9121
9376
|
try {
|
|
9122
|
-
return
|
|
9377
|
+
return readFileSync16(path5, "utf8");
|
|
9123
9378
|
} catch {
|
|
9124
9379
|
return null;
|
|
9125
9380
|
}
|
|
@@ -9601,11 +9856,11 @@ function announcePlanIssues(planContent) {
|
|
|
9601
9856
|
}
|
|
9602
9857
|
|
|
9603
9858
|
// src/start/runtime.ts
|
|
9604
|
-
import { existsSync as
|
|
9859
|
+
import { existsSync as existsSync16, readdirSync as readdirSync6, realpathSync as realpathSync3, statSync as statSync9 } from "fs";
|
|
9605
9860
|
import { homedir as homedir6 } from "os";
|
|
9606
|
-
import { isAbsolute as isAbsolute3, join as
|
|
9861
|
+
import { isAbsolute as isAbsolute3, join as join12, relative as relative3, resolve as resolve10, sep } from "path";
|
|
9607
9862
|
var RUNTIME_FLAG = "--runtime";
|
|
9608
|
-
var RUNTIME_SUBDIR =
|
|
9863
|
+
var RUNTIME_SUBDIR = join12(".rafa", "runtime");
|
|
9609
9864
|
var RUNTIME_ENTRY = "cli.js";
|
|
9610
9865
|
var SOURCE_DIR = "src";
|
|
9611
9866
|
var RUNTIME_COMMAND = ["start"];
|
|
@@ -9622,7 +9877,7 @@ function resolveRuntime(value, places) {
|
|
|
9622
9877
|
throw refusal3([`\u274C Refusing to start: \`--runtime\` names no runtime. Spell it ${SPELLING}.`]);
|
|
9623
9878
|
}
|
|
9624
9879
|
const isVersion = VERSION_SHAPE2.test(value);
|
|
9625
|
-
const named = isVersion ?
|
|
9880
|
+
const named = isVersion ? join12(places.home, RUNTIME_SUBDIR, value, RUNTIME_ENTRY) : resolve10(places.cwd, value);
|
|
9626
9881
|
refuseInsideSource(value, named, places);
|
|
9627
9882
|
const entry = entryOf(value, named, isVersion, places.home);
|
|
9628
9883
|
const real = realpathSync3(entry);
|
|
@@ -9633,7 +9888,7 @@ function entryOf(value, named, isVersion, home) {
|
|
|
9633
9888
|
if (isVersion) {
|
|
9634
9889
|
if (isFile2(named))
|
|
9635
9890
|
return named;
|
|
9636
|
-
const root =
|
|
9891
|
+
const root = join12(home, RUNTIME_SUBDIR);
|
|
9637
9892
|
const installed = installedVersions(root);
|
|
9638
9893
|
throw refusal3([
|
|
9639
9894
|
`\u274C Refusing to start: \`--runtime=${value}\` names no installed runtime: ${named} is no file.`,
|
|
@@ -9642,7 +9897,7 @@ function entryOf(value, named, isVersion, home) {
|
|
|
9642
9897
|
}
|
|
9643
9898
|
if (isFile2(named))
|
|
9644
9899
|
return named;
|
|
9645
|
-
const inside =
|
|
9900
|
+
const inside = join12(named, RUNTIME_ENTRY);
|
|
9646
9901
|
if (isDirectory(named) && isFile2(inside))
|
|
9647
9902
|
return inside;
|
|
9648
9903
|
throw refusal3([
|
|
@@ -9660,8 +9915,8 @@ function refuseInsideSource(value, path5, places) {
|
|
|
9660
9915
|
]);
|
|
9661
9916
|
}
|
|
9662
9917
|
function sourceDirs({ cwd, root }) {
|
|
9663
|
-
const dirs = [
|
|
9664
|
-
const real = dirs.filter((dir) =>
|
|
9918
|
+
const dirs = [join12(cwd, SOURCE_DIR), join12(root, SOURCE_DIR)];
|
|
9919
|
+
const real = dirs.filter((dir) => existsSync16(dir)).map((dir) => realpathSync3(dir));
|
|
9665
9920
|
return [...new Set([...dirs, ...real])];
|
|
9666
9921
|
}
|
|
9667
9922
|
function isInside(path5, dir) {
|
|
@@ -9670,21 +9925,21 @@ function isInside(path5, dir) {
|
|
|
9670
9925
|
}
|
|
9671
9926
|
function installedVersions(root) {
|
|
9672
9927
|
try {
|
|
9673
|
-
return
|
|
9928
|
+
return readdirSync6(root).filter((name) => isFile2(join12(root, name, RUNTIME_ENTRY))).sort((a, b) => a.localeCompare(b));
|
|
9674
9929
|
} catch {
|
|
9675
9930
|
return [];
|
|
9676
9931
|
}
|
|
9677
9932
|
}
|
|
9678
9933
|
function isFile2(path5) {
|
|
9679
9934
|
try {
|
|
9680
|
-
return
|
|
9935
|
+
return statSync9(path5).isFile();
|
|
9681
9936
|
} catch {
|
|
9682
9937
|
return false;
|
|
9683
9938
|
}
|
|
9684
9939
|
}
|
|
9685
9940
|
function isDirectory(path5) {
|
|
9686
9941
|
try {
|
|
9687
|
-
return
|
|
9942
|
+
return statSync9(path5).isDirectory();
|
|
9688
9943
|
} catch {
|
|
9689
9944
|
return false;
|
|
9690
9945
|
}
|
|
@@ -9715,7 +9970,7 @@ function realOrResolved(path5) {
|
|
|
9715
9970
|
try {
|
|
9716
9971
|
return realpathSync3(path5);
|
|
9717
9972
|
} catch {
|
|
9718
|
-
return
|
|
9973
|
+
return resolve10(path5);
|
|
9719
9974
|
}
|
|
9720
9975
|
}
|
|
9721
9976
|
async function runRuntime(entry, words, run) {
|
|
@@ -9801,11 +10056,11 @@ function eventOf(line) {
|
|
|
9801
10056
|
}
|
|
9802
10057
|
|
|
9803
10058
|
// src/triage/triage.ts
|
|
9804
|
-
import { basename as
|
|
10059
|
+
import { basename as basename6, join as join13 } from "path";
|
|
9805
10060
|
|
|
9806
10061
|
// src/effort/store/tracker-refs.ts
|
|
9807
10062
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
9808
|
-
import { existsSync as
|
|
10063
|
+
import { existsSync as existsSync17 } from "fs";
|
|
9809
10064
|
var SELECT_HELD = `
|
|
9810
10065
|
SELECT seq, tracker_ref FROM findings
|
|
9811
10066
|
WHERE session_id = ? AND artifact = ?
|
|
@@ -9928,7 +10183,7 @@ function writeTrackerRef(repoRoot, write, seams = {}) {
|
|
|
9928
10183
|
wanted.json,
|
|
9929
10184
|
collectedAt
|
|
9930
10185
|
];
|
|
9931
|
-
const [action, stored] = withSqliteStore(path5, !
|
|
10186
|
+
const [action, stored] = withSqliteStore(path5, !existsSync17(path5), (db) => {
|
|
9932
10187
|
const place = db.transaction(() => placeRef(db, path5, write, wanted, insertValues));
|
|
9933
10188
|
return place.immediate();
|
|
9934
10189
|
});
|
|
@@ -9937,14 +10192,14 @@ function writeTrackerRef(repoRoot, write, seams = {}) {
|
|
|
9937
10192
|
function readTrackerRef(repoRoot, artifact) {
|
|
9938
10193
|
checkArtifact("read", artifact);
|
|
9939
10194
|
const path5 = sqliteStorePath(repoRoot);
|
|
9940
|
-
if (!
|
|
10195
|
+
if (!existsSync17(path5))
|
|
9941
10196
|
return null;
|
|
9942
10197
|
const oldest = withSqliteStore(path5, false, (db) => db.query(SELECT_OLDEST).get(artifact));
|
|
9943
10198
|
return oldest === null ? null : heldRefOf(path5, oldest.seq, oldest.tracker_ref).ref;
|
|
9944
10199
|
}
|
|
9945
10200
|
|
|
9946
10201
|
// src/triage/triage.ts
|
|
9947
|
-
var PRIVATE_TRIAGE_DIR =
|
|
10202
|
+
var PRIVATE_TRIAGE_DIR = join13(".rafa", "triage", "private");
|
|
9948
10203
|
var SECRET_ENV_NAMES = ["GITHUB_TOKEN", "ANTHROPIC_API_KEY", "LINEAR_API_KEY"];
|
|
9949
10204
|
var TRIAGE_MODULE = "unassigned";
|
|
9950
10205
|
var BLOCKER_SEPARATOR = "; ";
|
|
@@ -9955,7 +10210,7 @@ var MIN_FENCE_LENGTH = 3;
|
|
|
9955
10210
|
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.";
|
|
9956
10211
|
var COMMENT_OPENING = "Reported again by a rafa task session.";
|
|
9957
10212
|
function privateTriageDir(repoRoot) {
|
|
9958
|
-
return
|
|
10213
|
+
return join13(repoRoot, PRIVATE_TRIAGE_DIR);
|
|
9959
10214
|
}
|
|
9960
10215
|
function createPrivateTriageTracker(repoRoot, options = {}) {
|
|
9961
10216
|
return createLocalTracker({
|
|
@@ -10018,7 +10273,7 @@ function oneLine3(text) {
|
|
|
10018
10273
|
return text.replace(/\s+/g, " ").trim();
|
|
10019
10274
|
}
|
|
10020
10275
|
function bugKeyOf(trackerPath, artifact) {
|
|
10021
|
-
return `${
|
|
10276
|
+
return `${basename6(trackerPath)}${KEY_SEPARATOR}${oneLine3(artifact)}`;
|
|
10022
10277
|
}
|
|
10023
10278
|
function fenced(value) {
|
|
10024
10279
|
const runs = Array.from(value.matchAll(/`+/g), (run) => run[0].length);
|
|
@@ -10252,9 +10507,9 @@ function describeStartTriage(result) {
|
|
|
10252
10507
|
};
|
|
10253
10508
|
}
|
|
10254
10509
|
async function resolvePublicTracker(options, redact) {
|
|
10255
|
-
const
|
|
10510
|
+
const resolve11 = options.resolve ?? resolveTracker;
|
|
10256
10511
|
try {
|
|
10257
|
-
const { tracker, attempts } = await
|
|
10512
|
+
const { tracker, attempts } = await resolve11({
|
|
10258
10513
|
config: options.config,
|
|
10259
10514
|
context: { repoRoot: options.repoRoot }
|
|
10260
10515
|
});
|
|
@@ -10425,8 +10680,8 @@ function msUntil(startAt, now = new Date) {
|
|
|
10425
10680
|
return target.getTime() - now.getTime();
|
|
10426
10681
|
}
|
|
10427
10682
|
function sleepFor(ms) {
|
|
10428
|
-
return new Promise((
|
|
10429
|
-
setTimeout(
|
|
10683
|
+
return new Promise((resolve11) => {
|
|
10684
|
+
setTimeout(resolve11, ms);
|
|
10430
10685
|
});
|
|
10431
10686
|
}
|
|
10432
10687
|
async function deferUntil(startAt, now = new Date, sleep = sleepFor) {
|