@open-tomato/rafa 0.33.0 → 0.34.1
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 +39 -0
- package/dist/bundled/operators/agents/rafa-stretch-analyst.md +53 -0
- package/dist/bundled/operators/agents/rafa-stretch-engineer.md +15 -5
- package/dist/bundled/operators/skills/rafa-stretch-pit-stop/SKILL.md +8 -2
- package/dist/bundled/skills/dev-planner/SKILL.md +3 -3
- package/dist/bundled/skills/git-workflow/SKILL.md +1 -1
- package/dist/cli.js +1935 -605
- package/dist/effort/store/index.js +2 -2
- package/dist/{index-kz7e4gtt.js → index-bebv92ha.js} +112 -58
- package/dist/{index-13caphhp.js → index-nr4hdb35.js} +672 -23
- package/dist/{index-w3vyb08t.js → index-s8ff86xt.js} +689 -198
- package/dist/{index-zn08ywsv.js → index-v8d7c6sw.js} +1 -1
- package/dist/{index-dv0ycmp8.js → index-vp0xghk2.js} +40 -0
- package/dist/index.js +4 -4
- package/dist/plan/index.js +2 -2
- package/package.json +4 -2
package/dist/cli.js
CHANGED
|
@@ -850,6 +850,34 @@ var init_config_schema_tests = __esm(() => {
|
|
|
850
850
|
};
|
|
851
851
|
});
|
|
852
852
|
|
|
853
|
+
// src/config-schema-triage.ts
|
|
854
|
+
function isThreshold(raw) {
|
|
855
|
+
return typeof raw === "number" && Number.isFinite(raw) && raw > 0 && raw <= SIMILARITY_THRESHOLD_MAX;
|
|
856
|
+
}
|
|
857
|
+
function isCandidateCount(raw) {
|
|
858
|
+
return typeof raw === "number" && Number.isSafeInteger(raw) && raw >= SIMILARITY_CANDIDATES_MIN && raw <= SIMILARITY_CANDIDATES_MAX;
|
|
859
|
+
}
|
|
860
|
+
var SIMILARITY_THRESHOLD_MAX = 1, SIMILARITY_CANDIDATES_MIN = 1, SIMILARITY_CANDIDATES_MAX = 10, similarityThreshold = (raw, at) => raw === false || isThreshold(raw) ? { value: raw, problems: [], extras: [] } : refused(at, raw, `false or a number above 0 and at most ${String(SIMILARITY_THRESHOLD_MAX)}`), similarityCandidates = (raw, at) => isCandidateCount(raw) ? { value: raw, problems: [], extras: [] } : refused(at, raw, `a whole number from ${String(SIMILARITY_CANDIDATES_MIN)} to ${String(SIMILARITY_CANDIDATES_MAX)}`), TRIAGE_DEFAULTS, TRIAGE_SETTINGS;
|
|
861
|
+
var init_config_schema_triage = __esm(() => {
|
|
862
|
+
init_config_sections();
|
|
863
|
+
TRIAGE_DEFAULTS = Object.freeze({
|
|
864
|
+
triageSimilarityThreshold: 0.3,
|
|
865
|
+
triageSimilarityCandidates: 3
|
|
866
|
+
});
|
|
867
|
+
TRIAGE_SETTINGS = {
|
|
868
|
+
triageSimilarityThreshold: {
|
|
869
|
+
key: "triage.similarity.threshold",
|
|
870
|
+
read: similarityThreshold,
|
|
871
|
+
cli: false
|
|
872
|
+
},
|
|
873
|
+
triageSimilarityCandidates: {
|
|
874
|
+
key: "triage.similarity.candidates",
|
|
875
|
+
read: similarityCandidates,
|
|
876
|
+
cli: false
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
});
|
|
880
|
+
|
|
853
881
|
// src/config-schema-wrap-up.ts
|
|
854
882
|
function isRetryCount(raw) {
|
|
855
883
|
return typeof raw === "number" && Number.isSafeInteger(raw) && raw >= WRAP_UP_RETRIES_MIN && raw <= WRAP_UP_RETRIES_MAX;
|
|
@@ -1065,6 +1093,7 @@ var init_config_schema = __esm(() => {
|
|
|
1065
1093
|
init_config_schema_hub();
|
|
1066
1094
|
init_config_schema_release();
|
|
1067
1095
|
init_config_schema_tests();
|
|
1096
|
+
init_config_schema_triage();
|
|
1068
1097
|
init_config_schema_wrap_up();
|
|
1069
1098
|
init_config_sections();
|
|
1070
1099
|
init_routing();
|
|
@@ -1101,6 +1130,7 @@ var init_config_schema = __esm(() => {
|
|
|
1101
1130
|
roadmapIssue: null,
|
|
1102
1131
|
claimsStaleAfter: "3d",
|
|
1103
1132
|
claimsAhead: "off",
|
|
1133
|
+
...TRIAGE_DEFAULTS,
|
|
1104
1134
|
...RELEASE_DEFAULTS,
|
|
1105
1135
|
cleanupStaleDays: 30,
|
|
1106
1136
|
cleanupWorktreeIdleDays: 7,
|
|
@@ -1188,6 +1218,7 @@ var init_config_schema = __esm(() => {
|
|
|
1188
1218
|
roadmapIssue: { key: "roadmap.issue", read: issueNumber, cli: false },
|
|
1189
1219
|
claimsStaleAfter: { key: "claims.staleAfter", read: claimsStaleAfter, cli: false },
|
|
1190
1220
|
claimsAhead: { key: "claims.ahead", read: claimsAhead, cli: false },
|
|
1221
|
+
...TRIAGE_SETTINGS,
|
|
1191
1222
|
...RELEASE_SETTINGS,
|
|
1192
1223
|
cleanupStaleDays: { key: "cleanup.staleDays", read: dayCount, cli: false },
|
|
1193
1224
|
cleanupWorktreeIdleDays: {
|
|
@@ -1297,6 +1328,8 @@ function readLayer(rawOf, atOf) {
|
|
|
1297
1328
|
roadmapIssue: read("roadmapIssue"),
|
|
1298
1329
|
claimsStaleAfter: read("claimsStaleAfter"),
|
|
1299
1330
|
claimsAhead: read("claimsAhead"),
|
|
1331
|
+
triageSimilarityThreshold: read("triageSimilarityThreshold"),
|
|
1332
|
+
triageSimilarityCandidates: read("triageSimilarityCandidates"),
|
|
1300
1333
|
releaseEnabled: read("releaseEnabled"),
|
|
1301
1334
|
releaseVersionFile: read("releaseVersionFile"),
|
|
1302
1335
|
releaseChangelog: read("releaseChangelog"),
|
|
@@ -1360,7 +1393,13 @@ function sortKeys(document, path) {
|
|
|
1360
1393
|
}
|
|
1361
1394
|
return { named, extras, problems };
|
|
1362
1395
|
}
|
|
1396
|
+
function tabIndentProblems(text2, path) {
|
|
1397
|
+
return text2.split(/\r?\n/).flatMap((line, index) => TAB_INDENTED_LINE.test(line) ? [`${path}: line ${index + 1} is indented with a tab; YAML indents with spaces only`] : []);
|
|
1398
|
+
}
|
|
1363
1399
|
function parseYaml(text2, path) {
|
|
1400
|
+
const tabProblems = tabIndentProblems(text2, path);
|
|
1401
|
+
if (tabProblems.length > 0)
|
|
1402
|
+
throw new ConfigError(tabProblems);
|
|
1364
1403
|
try {
|
|
1365
1404
|
return Bun.YAML.parse(text2);
|
|
1366
1405
|
} catch (error) {
|
|
@@ -1461,7 +1500,7 @@ function resolveConfig(layers = {}) {
|
|
|
1461
1500
|
warnings
|
|
1462
1501
|
};
|
|
1463
1502
|
}
|
|
1464
|
-
var ConfigError, MAP_SETTINGS;
|
|
1503
|
+
var ConfigError, TAB_INDENTED_LINE, MAP_SETTINGS;
|
|
1465
1504
|
var init_config = __esm(() => {
|
|
1466
1505
|
init_config_schema_hub();
|
|
1467
1506
|
init_config_schema();
|
|
@@ -1477,6 +1516,7 @@ var init_config = __esm(() => {
|
|
|
1477
1516
|
this.problems = problems;
|
|
1478
1517
|
}
|
|
1479
1518
|
};
|
|
1519
|
+
TAB_INDENTED_LINE = /^[ \t]*\t[ \t]*[^\s#]/;
|
|
1480
1520
|
MAP_SETTINGS = new Set([
|
|
1481
1521
|
"tiersSkills",
|
|
1482
1522
|
"tiersAgents",
|
|
@@ -2193,7 +2233,7 @@ function restoreRunningCommand(previous) {
|
|
|
2193
2233
|
var running = null;
|
|
2194
2234
|
|
|
2195
2235
|
// package.json
|
|
2196
|
-
var name = "@open-tomato/rafa", version = "0.
|
|
2236
|
+
var name = "@open-tomato/rafa", version = "0.34.1";
|
|
2197
2237
|
var init_package = () => {};
|
|
2198
2238
|
|
|
2199
2239
|
// src/cli/version.ts
|
|
@@ -6522,11 +6562,18 @@ var init_store = __esm(() => {
|
|
|
6522
6562
|
// src/effort/store/location.ts
|
|
6523
6563
|
import { existsSync as existsSync6, realpathSync as realpathSync4 } from "fs";
|
|
6524
6564
|
import { tmpdir } from "os";
|
|
6525
|
-
import { isAbsolute as isAbsolute9, join as join15, relative as relative4, resolve as resolve2, sep } from "path";
|
|
6565
|
+
import { basename as basename4, dirname as dirname10, isAbsolute as isAbsolute9, join as join15, relative as relative4, resolve as resolve2, sep } from "path";
|
|
6526
6566
|
function canonical(path) {
|
|
6527
6567
|
const resolved = resolve2(path);
|
|
6528
6568
|
return existsSync6(resolved) ? realpathSync4(resolved) : resolved;
|
|
6529
6569
|
}
|
|
6570
|
+
function realPathOf(path) {
|
|
6571
|
+
const resolved = resolve2(path);
|
|
6572
|
+
if (existsSync6(resolved))
|
|
6573
|
+
return realpathSync4(resolved);
|
|
6574
|
+
const parent = dirname10(resolved);
|
|
6575
|
+
return parent === resolved ? resolved : join15(realPathOf(parent), basename4(resolved));
|
|
6576
|
+
}
|
|
6530
6577
|
function isWithin(path, dir) {
|
|
6531
6578
|
const between = relative4(resolve2(dir), resolve2(path));
|
|
6532
6579
|
const climbsOut = between === ".." || between.startsWith(`..${sep}`);
|
|
@@ -6551,7 +6598,7 @@ function isTestProcess(main = Bun.main, env = process.env) {
|
|
|
6551
6598
|
return main.endsWith(TEST_ENTRY_SUFFIX) || env[TEST_PROCESS_VARIABLE] === "1";
|
|
6552
6599
|
}
|
|
6553
6600
|
function isUnderTempDir(path, tempDir = tmpdir()) {
|
|
6554
|
-
return isWithin(path, tempDir) || isWithin(path,
|
|
6601
|
+
return isWithin(path, tempDir) || isWithin(realPathOf(path), realPathOf(tempDir));
|
|
6555
6602
|
}
|
|
6556
6603
|
function currentProbe() {
|
|
6557
6604
|
return { main: Bun.main, env: process.env, tempDir: tmpdir() };
|
|
@@ -6618,7 +6665,7 @@ var init_ndjson = __esm(() => {
|
|
|
6618
6665
|
|
|
6619
6666
|
// src/runtime/identity.ts
|
|
6620
6667
|
import { existsSync as existsSync7, readFileSync as readFileSync13, realpathSync as realpathSync5 } from "fs";
|
|
6621
|
-
import { dirname as
|
|
6668
|
+
import { dirname as dirname11, join as join17, resolve as resolve3 } from "path";
|
|
6622
6669
|
function realOrResolved(path) {
|
|
6623
6670
|
const resolved = resolve3(path);
|
|
6624
6671
|
return existsSync7(resolved) ? realpathSync5(resolved) : resolved;
|
|
@@ -6641,7 +6688,7 @@ function nearestRafaPackage(start) {
|
|
|
6641
6688
|
const manifest = join17(dir, MANIFEST);
|
|
6642
6689
|
if (existsSync7(manifest) && manifestName(manifest) === name)
|
|
6643
6690
|
return dir;
|
|
6644
|
-
const parent =
|
|
6691
|
+
const parent = dirname11(dir);
|
|
6645
6692
|
if (parent === dir)
|
|
6646
6693
|
return null;
|
|
6647
6694
|
dir = parent;
|
|
@@ -6649,7 +6696,7 @@ function nearestRafaPackage(start) {
|
|
|
6649
6696
|
}
|
|
6650
6697
|
function readRuntimeIdentity(entry = Bun.main) {
|
|
6651
6698
|
const real = realOrResolved(entry);
|
|
6652
|
-
const packageDir = nearestRafaPackage(
|
|
6699
|
+
const packageDir = nearestRafaPackage(dirname11(real));
|
|
6653
6700
|
if (packageDir !== null && existsSync7(join17(packageDir, CHECKOUT_ENTRY))) {
|
|
6654
6701
|
return { kind: "development", entry: real, checkout: packageDir };
|
|
6655
6702
|
}
|
|
@@ -6663,16 +6710,16 @@ var init_identity2 = __esm(() => {
|
|
|
6663
6710
|
|
|
6664
6711
|
// src/effort/store/development-build.ts
|
|
6665
6712
|
import { tmpdir as tmpdir2 } from "os";
|
|
6666
|
-
import { basename as
|
|
6713
|
+
import { basename as basename5, dirname as dirname12, isAbsolute as isAbsolute10 } from "path";
|
|
6667
6714
|
function appliedByName(identity) {
|
|
6668
6715
|
return identity.kind === "development" ? `${RAFA_VERSION}${DEVELOPMENT_MARK}${identity.checkout}` : RAFA_VERSION;
|
|
6669
6716
|
}
|
|
6670
6717
|
function storeProjectRoot(path) {
|
|
6671
|
-
const storeDir =
|
|
6672
|
-
const scopeDir =
|
|
6673
|
-
if (
|
|
6718
|
+
const storeDir = dirname12(path);
|
|
6719
|
+
const scopeDir = dirname12(storeDir);
|
|
6720
|
+
if (basename5(storeDir) !== EFFORT_DIR_NAME || basename5(scopeDir) !== SCOPE_DIR)
|
|
6674
6721
|
return null;
|
|
6675
|
-
return
|
|
6722
|
+
return dirname12(scopeDir);
|
|
6676
6723
|
}
|
|
6677
6724
|
function isOwnedStore(path, env, tempDir) {
|
|
6678
6725
|
if (isUnderTempDir(path, tempDir))
|
|
@@ -7401,7 +7448,7 @@ var init_store_identity = __esm(() => {
|
|
|
7401
7448
|
|
|
7402
7449
|
// src/effort/store/store-meta.ts
|
|
7403
7450
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
7404
|
-
import { dirname as
|
|
7451
|
+
import { dirname as dirname13 } from "path";
|
|
7405
7452
|
function toStoredInteger(value) {
|
|
7406
7453
|
return BigInt.asIntN(STORED_INTEGER_BITS, value);
|
|
7407
7454
|
}
|
|
@@ -7424,7 +7471,7 @@ function readStoreMeta(db) {
|
|
|
7424
7471
|
};
|
|
7425
7472
|
}
|
|
7426
7473
|
function projectFor(storePath, recorded, seams) {
|
|
7427
|
-
const read = (seams.readProject ?? readProjectIdentity)(
|
|
7474
|
+
const read = (seams.readProject ?? readProjectIdentity)(dirname13(storePath));
|
|
7428
7475
|
if (read.rootCommit !== null)
|
|
7429
7476
|
return { rootCommit: read.rootCommit, remote: read.remote };
|
|
7430
7477
|
if (recorded === null)
|
|
@@ -7467,7 +7514,7 @@ var init_store_meta = __esm(() => {
|
|
|
7467
7514
|
|
|
7468
7515
|
// src/effort/store/sqlite.ts
|
|
7469
7516
|
import { existsSync as existsSync8, mkdirSync as mkdirSync6 } from "fs";
|
|
7470
|
-
import { dirname as
|
|
7517
|
+
import { dirname as dirname14, join as join18 } from "path";
|
|
7471
7518
|
import { Database } from "bun:sqlite";
|
|
7472
7519
|
function sqliteStorePath(repoRoot) {
|
|
7473
7520
|
return join18(effortStoreDir(repoRoot), SQLITE_STORE_FILE_NAME);
|
|
@@ -7496,7 +7543,7 @@ function batchEntries(kind, rows2) {
|
|
|
7496
7543
|
function withSqliteStore(path, access, create, use, identity = {}) {
|
|
7497
7544
|
guardTestProcess(path);
|
|
7498
7545
|
if (create)
|
|
7499
|
-
mkdirSync6(
|
|
7546
|
+
mkdirSync6(dirname14(path), { recursive: true });
|
|
7500
7547
|
const db = new Database(path, { readwrite: true, create });
|
|
7501
7548
|
try {
|
|
7502
7549
|
db.run(`PRAGMA busy_timeout = ${String(activeStoreSettings().busyTimeoutMs)}`);
|
|
@@ -8667,7 +8714,7 @@ var init_migrate = __esm(() => {
|
|
|
8667
8714
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
8668
8715
|
import { existsSync as existsSync13, mkdtempSync, rmSync as rmSync7 } from "fs";
|
|
8669
8716
|
import { tmpdir as tmpdir3 } from "os";
|
|
8670
|
-
import { dirname as
|
|
8717
|
+
import { dirname as dirname15, join as join20 } from "path";
|
|
8671
8718
|
import { Database as Database5, SQLiteError as SQLiteError2 } from "bun:sqlite";
|
|
8672
8719
|
function refuseNdjson(backend) {
|
|
8673
8720
|
if (backend !== "ndjson")
|
|
@@ -8714,7 +8761,7 @@ function recordedMeta(path) {
|
|
|
8714
8761
|
}
|
|
8715
8762
|
function refuseOtherProject(options) {
|
|
8716
8763
|
const other = recordedMeta(options.otherPath);
|
|
8717
|
-
const here = recordedMeta(options.path)?.projectRootCommit ?? (options.readProject ?? readProjectIdentity)(
|
|
8764
|
+
const here = recordedMeta(options.path)?.projectRootCommit ?? (options.readProject ?? readProjectIdentity)(dirname15(options.path)).rootCommit;
|
|
8718
8765
|
const there = other?.projectRootCommit ?? null;
|
|
8719
8766
|
if (here !== null && there !== null && here !== there) {
|
|
8720
8767
|
throw new MergeRefusal("other-project", `REFUSED \u2014 ${options.otherPath} is the effort store of another project (root commit ${there});` + ` this store's project has root commit ${here}. ${NOTHING_MERGED}`);
|
|
@@ -10255,28 +10302,49 @@ function viewedIssue(stdout, number) {
|
|
|
10255
10302
|
const checked = payload;
|
|
10256
10303
|
return { ...checked, labels: labelNames(checked.labels) ?? [] };
|
|
10257
10304
|
}
|
|
10258
|
-
function
|
|
10259
|
-
const
|
|
10260
|
-
const payload = parseJson(stdout, command);
|
|
10305
|
+
function listedRows(stdout) {
|
|
10306
|
+
const payload = parseJson(stdout, LIST_COMMAND);
|
|
10261
10307
|
if (!Array.isArray(payload)) {
|
|
10262
|
-
throw new Error(`${PREFIX2}: ${
|
|
10308
|
+
throw new Error(`${PREFIX2}: ${LIST_COMMAND} answered ${describeValue(payload)}, expected a list`);
|
|
10263
10309
|
}
|
|
10264
|
-
return payload
|
|
10265
|
-
|
|
10266
|
-
|
|
10267
|
-
|
|
10268
|
-
|
|
10269
|
-
|
|
10270
|
-
|
|
10271
|
-
|
|
10272
|
-
|
|
10273
|
-
|
|
10274
|
-
|
|
10275
|
-
|
|
10276
|
-
|
|
10277
|
-
|
|
10310
|
+
return payload;
|
|
10311
|
+
}
|
|
10312
|
+
function rowRefusal(index, problem) {
|
|
10313
|
+
return new Error(`${PREFIX2}: ${LIST_COMMAND} answered row ${index} ${problem}`);
|
|
10314
|
+
}
|
|
10315
|
+
function listedRow(row2, index) {
|
|
10316
|
+
if (!isMapping(row2))
|
|
10317
|
+
throw rowRefusal(index, `${describeValue(row2)}, expected a mapping`);
|
|
10318
|
+
const { number, url } = row2;
|
|
10319
|
+
const labels = labelNames(row2["labels"]);
|
|
10320
|
+
if (typeof number !== "number" || !isIssueNumber3(String(number))) {
|
|
10321
|
+
throw rowRefusal(index, `with number ${describeValue(number)}, expected an issue number`);
|
|
10322
|
+
}
|
|
10323
|
+
if (typeof url !== "string")
|
|
10324
|
+
throw rowRefusal(index, `with url ${describeValue(url)}, expected a string`);
|
|
10325
|
+
if (labels === null)
|
|
10326
|
+
throw rowRefusal(index, "with labels that are not a list of named labels");
|
|
10327
|
+
return { number, url, labels };
|
|
10328
|
+
}
|
|
10329
|
+
function listedIssues(stdout) {
|
|
10330
|
+
return listedRows(stdout).map(listedRow);
|
|
10331
|
+
}
|
|
10332
|
+
function listedOpenIssues(stdout) {
|
|
10333
|
+
return listedRows(stdout).map((row2, index) => {
|
|
10334
|
+
const listed3 = listedRow(row2, index);
|
|
10335
|
+
const { title: title2, body } = row2;
|
|
10336
|
+
if (typeof title2 !== "string")
|
|
10337
|
+
throw rowRefusal(index, `with title ${describeValue(title2)}, expected a string`);
|
|
10338
|
+
if (typeof body !== "string")
|
|
10339
|
+
throw rowRefusal(index, `with body ${describeValue(body)}, expected a string`);
|
|
10340
|
+
return { ...listed3, title: title2, body };
|
|
10278
10341
|
});
|
|
10279
10342
|
}
|
|
10343
|
+
function refOfRow(row2) {
|
|
10344
|
+
const module = labelValue(row2.labels, GITHUB_LABELS.modulePrefix);
|
|
10345
|
+
const ref = { opt: 0, kind: "github", externalId: String(row2.number), url: row2.url };
|
|
10346
|
+
return module === undefined ? ref : { ...ref, module };
|
|
10347
|
+
}
|
|
10280
10348
|
function labelValue(labels, prefix) {
|
|
10281
10349
|
return labels.find((label) => label.startsWith(prefix))?.slice(prefix.length);
|
|
10282
10350
|
}
|
|
@@ -10290,7 +10358,7 @@ function moduleOfLabels(labels) {
|
|
|
10290
10358
|
function stateOf(viewed) {
|
|
10291
10359
|
if (viewed.state === "OPEN")
|
|
10292
10360
|
return "todo";
|
|
10293
|
-
return viewed.stateReason
|
|
10361
|
+
return viewed.stateReason !== null && CANCELLED_REASONS.has(viewed.stateReason) ? "cancelled" : "done";
|
|
10294
10362
|
}
|
|
10295
10363
|
function issueOf2(ref, viewed) {
|
|
10296
10364
|
const priority = labelValue(viewed.labels, GITHUB_LABELS.priorityPrefix);
|
|
@@ -10401,12 +10469,26 @@ function createGithubTracker(options) {
|
|
|
10401
10469
|
...optionalFlag("--label", moduleLabel),
|
|
10402
10470
|
...optionalFlag("--label", typeLabel),
|
|
10403
10471
|
...optionalFlag("--search", query.text)
|
|
10404
|
-
],
|
|
10405
|
-
return listedIssues(stdout).map(
|
|
10406
|
-
|
|
10407
|
-
|
|
10408
|
-
|
|
10409
|
-
|
|
10472
|
+
], LIST_COMMAND);
|
|
10473
|
+
return listedIssues(stdout).map(refOfRow);
|
|
10474
|
+
},
|
|
10475
|
+
openIssues: async (type) => {
|
|
10476
|
+
if (!isOneOf2(ISSUE_TYPES, type)) {
|
|
10477
|
+
throw new TypeError(`${PREFIX2}: openIssues refused type ${describeValue(type)}, expected one of: ${ISSUE_TYPES.join(", ")}`);
|
|
10478
|
+
}
|
|
10479
|
+
const stdout = await run([
|
|
10480
|
+
"issue",
|
|
10481
|
+
"list",
|
|
10482
|
+
"--state",
|
|
10483
|
+
"open",
|
|
10484
|
+
"--json",
|
|
10485
|
+
OPEN_LIST_FIELDS,
|
|
10486
|
+
"--limit",
|
|
10487
|
+
String(OPEN_ISSUES_LIMIT),
|
|
10488
|
+
"--label",
|
|
10489
|
+
`${GITHUB_LABELS.typePrefix}${type}`
|
|
10490
|
+
], LIST_COMMAND);
|
|
10491
|
+
return listedOpenIssues(stdout).map((row2) => ({ ref: refOfRow(row2), title: row2.title, body: row2.body }));
|
|
10410
10492
|
},
|
|
10411
10493
|
comment: async (ref, body) => {
|
|
10412
10494
|
const number = issueNumberOfRef(ref);
|
|
@@ -10437,7 +10519,7 @@ function createGithubTracker(options) {
|
|
|
10437
10519
|
};
|
|
10438
10520
|
return Object.freeze(tracker);
|
|
10439
10521
|
}
|
|
10440
|
-
var PREFIX2 = "github tracker", GITHUB_LABELS, VIEW_FIELDS = "number,title,body,state,stateReason,labels,url", LIST_FIELDS = "number,url,labels",
|
|
10522
|
+
var PREFIX2 = "github tracker", GITHUB_LABELS, VIEW_FIELDS = "number,title,body,state,stateReason,labels,url", LIST_COMMAND = "gh issue list", LIST_FIELDS = "number,url,labels", OPEN_LIST_FIELDS = "number,url,labels,title,body", OPEN_ISSUES_LIMIT = 1000, DEFAULT_FIND_LIMIT = 30, CANCELLED_REASONS, ISSUE_NUMBER, REPO_PART, CREATED_ISSUE_URL, REST_STATES, TIMED_OUT, DRAFT_CHECKS;
|
|
10441
10523
|
var init_github = __esm(() => {
|
|
10442
10524
|
init_config_sections();
|
|
10443
10525
|
init_issue_values();
|
|
@@ -10448,6 +10530,7 @@ var init_github = __esm(() => {
|
|
|
10448
10530
|
needsTriage: "needs-triage",
|
|
10449
10531
|
blockedByPrefix: "blocked-by:OPT-"
|
|
10450
10532
|
});
|
|
10533
|
+
CANCELLED_REASONS = new Set(["NOT_PLANNED", "DUPLICATE"]);
|
|
10451
10534
|
ISSUE_NUMBER = /^[1-9]\d*$/;
|
|
10452
10535
|
REPO_PART = /^[\w.-]+$/;
|
|
10453
10536
|
CREATED_ISSUE_URL = /^https:\/\/[^\s/]+\/[\w.-]+\/[\w.-]+\/issues\/([1-9]\d*)$/;
|
|
@@ -10601,7 +10684,7 @@ function parseLocalIssue(contents, sourcePath) {
|
|
|
10601
10684
|
function warnThroughActiveOutput2(message) {
|
|
10602
10685
|
activeOutput().warn(message);
|
|
10603
10686
|
}
|
|
10604
|
-
async function listLocalIssues(issuesDir, warn = warnThroughActiveOutput2) {
|
|
10687
|
+
async function listLocalIssues(issuesDir, warn = warnThroughActiveOutput2, reader = "find") {
|
|
10605
10688
|
const numbers = await issueNumbers(issuesDir);
|
|
10606
10689
|
const settled = await Promise.allSettled(numbers.map(async (number) => {
|
|
10607
10690
|
const path = issuePath(issuesDir, number);
|
|
@@ -10616,7 +10699,7 @@ async function listLocalIssues(issuesDir, warn = warnThroughActiveOutput2) {
|
|
|
10616
10699
|
return settled.flatMap((result) => {
|
|
10617
10700
|
if (result.status === "fulfilled")
|
|
10618
10701
|
return [result.value];
|
|
10619
|
-
warn(`${messageOf(result.reason)};
|
|
10702
|
+
warn(`${messageOf(result.reason)}; ${reader} skipped it`);
|
|
10620
10703
|
return [];
|
|
10621
10704
|
});
|
|
10622
10705
|
}
|
|
@@ -10702,6 +10785,14 @@ function createLocalTracker(options) {
|
|
|
10702
10785
|
const matched = (await listLocalIssues(issuesDir, warn)).filter(({ record }) => matchesQuery(record, query));
|
|
10703
10786
|
return matched.slice(0, query.limit ?? matched.length).map(({ number, record }) => refFor(number, record.draft.opt));
|
|
10704
10787
|
},
|
|
10788
|
+
openIssues: async (type) => {
|
|
10789
|
+
const open = (await listLocalIssues(issuesDir, warn, "openIssues")).filter(({ record }) => record.draft.type === type && !CLOSED_STATES.has(record.state));
|
|
10790
|
+
return open.map(({ number, record }) => ({
|
|
10791
|
+
ref: refFor(number, record.draft.opt),
|
|
10792
|
+
title: record.draft.title,
|
|
10793
|
+
body: record.draft.body
|
|
10794
|
+
}));
|
|
10795
|
+
},
|
|
10705
10796
|
comment: async (ref, body) => {
|
|
10706
10797
|
const { number, record } = await read(ref);
|
|
10707
10798
|
await rewrite(number, { ...record, comments: [...record.comments, `${now()}: ${body}`] });
|
|
@@ -10714,13 +10805,14 @@ function createLocalTracker(options) {
|
|
|
10714
10805
|
};
|
|
10715
10806
|
return Object.freeze(tracker);
|
|
10716
10807
|
}
|
|
10717
|
-
var PREFIX3 = "local tracker", FRONTMATTER, ISSUE_NUMBER2, ISSUE_EXTENSION = ".md", FIELD_CHECKS;
|
|
10808
|
+
var PREFIX3 = "local tracker", FRONTMATTER, ISSUE_NUMBER2, CLOSED_STATES, ISSUE_EXTENSION = ".md", FIELD_CHECKS;
|
|
10718
10809
|
var init_local2 = __esm(() => {
|
|
10719
10810
|
init_config_sections();
|
|
10720
10811
|
init_active();
|
|
10721
10812
|
init_issue_values();
|
|
10722
10813
|
FRONTMATTER = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/;
|
|
10723
10814
|
ISSUE_NUMBER2 = /^[1-9]\d*$/;
|
|
10815
|
+
CLOSED_STATES = new Set(["done", "released", "cancelled"]);
|
|
10724
10816
|
FIELD_CHECKS = {
|
|
10725
10817
|
opt: [isFiniteNumber, "a number"],
|
|
10726
10818
|
type: [oneOf3(ISSUE_TYPES), `one of: ${ISSUE_TYPES.join(", ")}`],
|
|
@@ -13037,7 +13129,7 @@ var init_store2 = __esm(() => {
|
|
|
13037
13129
|
|
|
13038
13130
|
// src/effort/session-log.ts
|
|
13039
13131
|
import { createReadStream } from "fs";
|
|
13040
|
-
import { basename as
|
|
13132
|
+
import { basename as basename6 } from "path";
|
|
13041
13133
|
import { createInterface } from "readline";
|
|
13042
13134
|
function emptyUsageTotals() {
|
|
13043
13135
|
return {
|
|
@@ -13065,7 +13157,7 @@ function bump(counts, key) {
|
|
|
13065
13157
|
counts[key] = (counts[key] ?? 0) + 1;
|
|
13066
13158
|
}
|
|
13067
13159
|
function sessionIdFromPath(filePath) {
|
|
13068
|
-
return
|
|
13160
|
+
return basename6(filePath).replace(/\.jsonl$/i, "");
|
|
13069
13161
|
}
|
|
13070
13162
|
async function* readLines(filePath) {
|
|
13071
13163
|
const stream = createReadStream(filePath, { encoding: "utf8" });
|
|
@@ -13786,7 +13878,7 @@ var init_skill_invocations = __esm(() => {
|
|
|
13786
13878
|
|
|
13787
13879
|
// src/effort/skill-use.ts
|
|
13788
13880
|
import { existsSync as existsSync17, readdirSync as readdirSync6 } from "fs";
|
|
13789
|
-
import { dirname as
|
|
13881
|
+
import { dirname as dirname16, join as join27 } from "path";
|
|
13790
13882
|
function asObject4(value) {
|
|
13791
13883
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
13792
13884
|
}
|
|
@@ -13798,7 +13890,7 @@ function parseRecord4(line) {
|
|
|
13798
13890
|
}
|
|
13799
13891
|
}
|
|
13800
13892
|
function subagentLogPaths(sessionLogPath) {
|
|
13801
|
-
const dir = join27(
|
|
13893
|
+
const dir = join27(dirname16(sessionLogPath), sessionIdFromPath(sessionLogPath), "subagents");
|
|
13802
13894
|
if (!existsSync17(dir))
|
|
13803
13895
|
return [];
|
|
13804
13896
|
return readdirSync6(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && SUBAGENT_LOG_NAME.test(entry.name)).map((entry) => join27(dir, entry.name)).sort((a, b) => a.localeCompare(b));
|
|
@@ -14425,7 +14517,7 @@ var init_dispatches = __esm(() => {
|
|
|
14425
14517
|
});
|
|
14426
14518
|
|
|
14427
14519
|
// src/preflight/prerequisites-md.ts
|
|
14428
|
-
import { basename as
|
|
14520
|
+
import { basename as basename7, dirname as dirname17, join as join29 } from "path";
|
|
14429
14521
|
function inferSectionTag(header) {
|
|
14430
14522
|
if (SECTION_AUTO_RE.test(header))
|
|
14431
14523
|
return "auto";
|
|
@@ -14563,10 +14655,10 @@ function mergePlanPrerequisites(settings, content) {
|
|
|
14563
14655
|
});
|
|
14564
14656
|
}
|
|
14565
14657
|
function prerequisitesPathForPlan(planPath) {
|
|
14566
|
-
const stub = PLAN_FILE_RE.exec(
|
|
14658
|
+
const stub = PLAN_FILE_RE.exec(basename7(planPath))?.[1];
|
|
14567
14659
|
if (stub === undefined)
|
|
14568
14660
|
return null;
|
|
14569
|
-
return join29(
|
|
14661
|
+
return join29(dirname17(planPath), `PREREQUISITES-${stub}.md`);
|
|
14570
14662
|
}
|
|
14571
14663
|
async function readIfPresent2(path) {
|
|
14572
14664
|
try {
|
|
@@ -16363,6 +16455,27 @@ var init_handout = __esm(() => {
|
|
|
16363
16455
|
EMPTY_RESOLUTION = { loadedTiers: [], items: [], collisions: [] };
|
|
16364
16456
|
});
|
|
16365
16457
|
|
|
16458
|
+
// src/start/inherited-notice.ts
|
|
16459
|
+
function oneLine5(text2) {
|
|
16460
|
+
return text2.replace(/\s+/g, " ").trim();
|
|
16461
|
+
}
|
|
16462
|
+
function failureLine(failure2) {
|
|
16463
|
+
return `- \`${oneLine5(failure2.file)}\`: ${oneLine5(failure2.name)}`;
|
|
16464
|
+
}
|
|
16465
|
+
function renderInheritedSection(failures) {
|
|
16466
|
+
if (failures.length === 0)
|
|
16467
|
+
return "";
|
|
16468
|
+
const listed3 = failures.slice(0, INHERITED_LIST_CAP).map(failureLine);
|
|
16469
|
+
const rest = failures.length - listed3.length;
|
|
16470
|
+
const more = rest > 0 ? [`- ...and ${rest} more.`] : [];
|
|
16471
|
+
return [INHERITED_HEADING, INHERITED_SENTENCE, ...listed3, ...more].join(`
|
|
16472
|
+
`);
|
|
16473
|
+
}
|
|
16474
|
+
var INHERITED_HEADING = "## Failures this run inherited", INHERITED_SENTENCE, INHERITED_LIST_CAP = 20;
|
|
16475
|
+
var init_inherited_notice = __esm(() => {
|
|
16476
|
+
INHERITED_SENTENCE = "These tests were already failing when this run started. " + "They need no proof that they predate your task: do not run `git stash` or check out a clean worktree of `main` to show it. " + "A bug you report for one of them, by its file and case, is recognised as inherited and files nothing.";
|
|
16477
|
+
});
|
|
16478
|
+
|
|
16366
16479
|
// src/effort/store/preflight.ts
|
|
16367
16480
|
import { existsSync as existsSync24 } from "fs";
|
|
16368
16481
|
function refusedWrite6(reason) {
|
|
@@ -17227,7 +17340,7 @@ var init_blocked = __esm(() => {
|
|
|
17227
17340
|
|
|
17228
17341
|
// src/commands/plan/validate.ts
|
|
17229
17342
|
import { readFileSync as readFileSync21 } from "fs";
|
|
17230
|
-
import { basename as
|
|
17343
|
+
import { basename as basename8, resolve as resolve11 } from "path";
|
|
17231
17344
|
function readFileOrNull(path) {
|
|
17232
17345
|
return path !== null && isFile(path) ? readFileSync21(path, "utf8") : null;
|
|
17233
17346
|
}
|
|
@@ -17236,7 +17349,7 @@ function checkStoreRules(file, markdown) {
|
|
|
17236
17349
|
return findStoreRuleProblems({
|
|
17237
17350
|
plan: markdown,
|
|
17238
17351
|
prerequisites: readFileOrNull(prerequisitesPath),
|
|
17239
|
-
prerequisitesName:
|
|
17352
|
+
prerequisitesName: basename8(prerequisitesPath ?? "PREREQUISITES-<stub>.md")
|
|
17240
17353
|
});
|
|
17241
17354
|
}
|
|
17242
17355
|
function validatePlan(file) {
|
|
@@ -17552,7 +17665,7 @@ var init_review_stamp = __esm(() => {
|
|
|
17552
17665
|
|
|
17553
17666
|
// src/board/gate.ts
|
|
17554
17667
|
import { existsSync as existsSync26, mkdirSync as mkdirSync8, readFileSync as readFileSync22, renameSync as renameSync5, statSync as statSync10, writeFileSync as writeFileSync7 } from "fs";
|
|
17555
|
-
import { basename as
|
|
17668
|
+
import { basename as basename9, dirname as dirname18, join as join31, resolve as resolve12 } from "path";
|
|
17556
17669
|
function readGateFlags(args) {
|
|
17557
17670
|
return {
|
|
17558
17671
|
skipReview: args.includes(SKIP_REVIEW_FLAG),
|
|
@@ -17592,8 +17705,8 @@ function isWrittenFile(repoRoot, path) {
|
|
|
17592
17705
|
return existsSync26(full) && statSync10(full).isFile();
|
|
17593
17706
|
}
|
|
17594
17707
|
function rejectedPath(path) {
|
|
17595
|
-
const parent =
|
|
17596
|
-
return parent === "." ? join31(REJECTED_DIR,
|
|
17708
|
+
const parent = dirname18(path);
|
|
17709
|
+
return parent === "." ? join31(REJECTED_DIR, basename9(path)) : join31(parent, REJECTED_DIR, basename9(path));
|
|
17597
17710
|
}
|
|
17598
17711
|
function moveWritten(options, output, reason) {
|
|
17599
17712
|
for (const path of [options.planPath, options.prerequisitesPath]) {
|
|
@@ -17601,7 +17714,7 @@ function moveWritten(options, output, reason) {
|
|
|
17601
17714
|
continue;
|
|
17602
17715
|
const destination = rejectedPath(path);
|
|
17603
17716
|
try {
|
|
17604
|
-
mkdirSync8(resolve12(options.repoRoot,
|
|
17717
|
+
mkdirSync8(resolve12(options.repoRoot, dirname18(destination)), { recursive: true });
|
|
17605
17718
|
renameSync5(resolve12(options.repoRoot, path), resolve12(options.repoRoot, destination));
|
|
17606
17719
|
output.info(`\uD83D\uDDC3 Moved ${path} to ${destination}: ${reason}`);
|
|
17607
17720
|
} catch (error) {
|
|
@@ -18064,7 +18177,7 @@ var init_naming = __esm(() => {
|
|
|
18064
18177
|
|
|
18065
18178
|
// src/board/previous-copy.ts
|
|
18066
18179
|
import { linkSync as linkSync2, mkdirSync as mkdirSync9, readdirSync as readdirSync8, statSync as statSync11, unlinkSync } from "fs";
|
|
18067
|
-
import { basename as
|
|
18180
|
+
import { basename as basename10, extname, join as join32, resolve as resolve13 } from "path";
|
|
18068
18181
|
function previousDir(specsDir) {
|
|
18069
18182
|
return join32(specsDir, PREVIOUS_DIR_NAME);
|
|
18070
18183
|
}
|
|
@@ -18077,7 +18190,7 @@ function previousTimestamp(mtime) {
|
|
|
18077
18190
|
return `${date}T${time}Z`;
|
|
18078
18191
|
}
|
|
18079
18192
|
function previousCopyName(savedName, timestamp, attempt) {
|
|
18080
|
-
const stem =
|
|
18193
|
+
const stem = basename10(savedName, extname(savedName));
|
|
18081
18194
|
const suffix = attempt < FIRST_SUFFIX ? "" : `-${String(attempt)}`;
|
|
18082
18195
|
return `${stem}.${timestamp}${suffix}${COPY_EXTENSION}`;
|
|
18083
18196
|
}
|
|
@@ -18101,7 +18214,7 @@ function movePreviousCopy(options) {
|
|
|
18101
18214
|
const source = resolve13(repoRoot, path2);
|
|
18102
18215
|
const directory2 = previousDir(specsDir);
|
|
18103
18216
|
const absoluteDirectory = resolve13(repoRoot, directory2);
|
|
18104
|
-
const savedName =
|
|
18217
|
+
const savedName = basename10(path2);
|
|
18105
18218
|
let name2;
|
|
18106
18219
|
try {
|
|
18107
18220
|
const timestamp = previousTimestamp(statSync11(source).mtime);
|
|
@@ -18141,7 +18254,7 @@ var init_previous_copy = __esm(() => {
|
|
|
18141
18254
|
|
|
18142
18255
|
// src/board/issue.ts
|
|
18143
18256
|
import { existsSync as existsSync27, mkdirSync as mkdirSync10, readFileSync as readFileSync23, statSync as statSync12, writeFileSync as writeFileSync8 } from "fs";
|
|
18144
|
-
import { dirname as
|
|
18257
|
+
import { dirname as dirname19, resolve as resolve14 } from "path";
|
|
18145
18258
|
function detailOf3(result, command) {
|
|
18146
18259
|
const written = result.stderr.trim() || result.stdout.trim();
|
|
18147
18260
|
return written === "" ? `${command} failed and wrote nothing` : `${command} failed: ${written}`;
|
|
@@ -18366,7 +18479,7 @@ function writeSpecSnapshot(options) {
|
|
|
18366
18479
|
}
|
|
18367
18480
|
const previous = action === "created" ? null : movePreviousCopy({ repoRoot, specsDir, path: path2 }).path;
|
|
18368
18481
|
try {
|
|
18369
|
-
mkdirSync10(
|
|
18482
|
+
mkdirSync10(dirname19(absolute), { recursive: true });
|
|
18370
18483
|
writeFileSync8(absolute, text2);
|
|
18371
18484
|
} catch (error) {
|
|
18372
18485
|
throw new Error(`${PREFIX5}: the snapshot ${path2} could not be written: ${messageOf(error)}`, { cause: error });
|
|
@@ -19021,6 +19134,11 @@ function createGhPullRequests(options) {
|
|
|
19021
19134
|
const text2 = textArgument(title2, "editTitle", "title", false);
|
|
19022
19135
|
await succeed(["pr", "edit", target, "--title", text2], `gh pr edit ${target} --title <title>`);
|
|
19023
19136
|
},
|
|
19137
|
+
editBase: async (number, base) => {
|
|
19138
|
+
const target = pullNumber(number, "editBase");
|
|
19139
|
+
const branch = textArgument(base, "editBase", "base", false);
|
|
19140
|
+
await succeed(["pr", "edit", target, "--base", branch], `gh pr edit ${target} --base ${branch}`);
|
|
19141
|
+
},
|
|
19024
19142
|
editBody: async (number, body) => {
|
|
19025
19143
|
const target = pullNumber(number, "editBody");
|
|
19026
19144
|
const text2 = bodyText(body, "editBody");
|
|
@@ -19918,7 +20036,7 @@ var init_setup_config = __esm(() => {
|
|
|
19918
20036
|
|
|
19919
20037
|
// src/board/setup.ts
|
|
19920
20038
|
import { existsSync as existsSync28, lstatSync, mkdirSync as mkdirSync11, readFileSync as readFileSync25, statSync as statSync13, writeFileSync as writeFileSync9 } from "fs";
|
|
19921
|
-
import { dirname as
|
|
20039
|
+
import { dirname as dirname20, join as join33 } from "path";
|
|
19922
20040
|
import { fileURLToPath } from "url";
|
|
19923
20041
|
function boardChanged(report) {
|
|
19924
20042
|
return report.parts.some((part) => part.outcome === "created");
|
|
@@ -20030,7 +20148,7 @@ function writeShippedFile(root, file) {
|
|
|
20030
20148
|
}
|
|
20031
20149
|
try {
|
|
20032
20150
|
const text2 = file.read();
|
|
20033
|
-
mkdirSync11(
|
|
20151
|
+
mkdirSync11(dirname20(path2), { recursive: true });
|
|
20034
20152
|
writeFileSync9(path2, text2, { encoding: "utf8", flag: "wx" });
|
|
20035
20153
|
return partOf("template", name2, "created", `written from ${file.source}`);
|
|
20036
20154
|
} catch (error) {
|
|
@@ -20226,7 +20344,7 @@ var init_setup = __esm(() => {
|
|
|
20226
20344
|
init_readiness();
|
|
20227
20345
|
init_roadmap();
|
|
20228
20346
|
init_setup_config();
|
|
20229
|
-
MODULE_DIR =
|
|
20347
|
+
MODULE_DIR = dirname20(fileURLToPath(import.meta.url));
|
|
20230
20348
|
ROADMAP_LABEL = `${GITHUB_LABELS.typePrefix}roadmap`;
|
|
20231
20349
|
BOARD_LABELS = Object.freeze([
|
|
20232
20350
|
{
|
|
@@ -20975,7 +21093,7 @@ var init_roadmap_board = __esm(() => {
|
|
|
20975
21093
|
|
|
20976
21094
|
// src/board/board-cache.ts
|
|
20977
21095
|
import { mkdirSync as mkdirSync12, readFileSync as readFileSync26, renameSync as renameSync6, writeFileSync as writeFileSync10 } from "fs";
|
|
20978
|
-
import { dirname as
|
|
21096
|
+
import { dirname as dirname21, join as join34 } from "path";
|
|
20979
21097
|
function changedArgs(watermark) {
|
|
20980
21098
|
return Object.freeze([
|
|
20981
21099
|
"api",
|
|
@@ -21028,7 +21146,7 @@ function fileOf(cache) {
|
|
|
21028
21146
|
function writeCache(path2, cache) {
|
|
21029
21147
|
const temporary = `${path2}.${String(process.pid)}.tmp`;
|
|
21030
21148
|
try {
|
|
21031
|
-
mkdirSync12(
|
|
21149
|
+
mkdirSync12(dirname21(path2), { recursive: true });
|
|
21032
21150
|
writeFileSync10(temporary, `${JSON.stringify(fileOf(cache))}
|
|
21033
21151
|
`);
|
|
21034
21152
|
renameSync6(temporary, path2);
|
|
@@ -21960,7 +22078,7 @@ var init_preflight_sync = __esm(() => {
|
|
|
21960
22078
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
21961
22079
|
import { existsSync as existsSync30, readFileSync as readFileSync27 } from "fs";
|
|
21962
22080
|
import { homedir as homedir4 } from "os";
|
|
21963
|
-
import { basename as
|
|
22081
|
+
import { basename as basename11, join as join35 } from "path";
|
|
21964
22082
|
function knownMissingNotice(lines) {
|
|
21965
22083
|
return lines.length === 0 ? [] : [...lines, KNOWN_MISSING_SENTENCE];
|
|
21966
22084
|
}
|
|
@@ -22007,7 +22125,7 @@ function refuseUnresolvableAgents(options) {
|
|
|
22007
22125
|
if (missing2.length === 0 && colliding.length === 0 && unresolved.length === 0)
|
|
22008
22126
|
return;
|
|
22009
22127
|
throw new CommandExit(1, [
|
|
22010
|
-
`\u274C Refusing to start: ${
|
|
22128
|
+
`\u274C Refusing to start: ${basename11(path2)} ${refusedNames(missing2.length, colliding.length, unresolved.length)}` + ` (loop.settingSources: ${settings.settingSources.join(", ")}; tiers.rafa: ${settings.tiersRafa}).`,
|
|
22011
22129
|
...missing2.map((agent) => ` ${missingAgentLine(agent)}`),
|
|
22012
22130
|
...colliding.map((skill) => ` ${skillCollisionLine(skill)}`),
|
|
22013
22131
|
...unresolved.map((skill) => ` ${unresolvedSkillLine(skill)}`),
|
|
@@ -22025,11 +22143,11 @@ function refuseStoreRuleBreaks(planPath) {
|
|
|
22025
22143
|
const problems = findStoreRuleProblems({
|
|
22026
22144
|
plan: markdown,
|
|
22027
22145
|
prerequisites,
|
|
22028
|
-
prerequisitesName:
|
|
22146
|
+
prerequisitesName: basename11(prerequisitesPath ?? "PREREQUISITES-<stub>.md")
|
|
22029
22147
|
});
|
|
22030
22148
|
if (problems.length === 0)
|
|
22031
22149
|
return;
|
|
22032
|
-
const file =
|
|
22150
|
+
const file = basename11(path2);
|
|
22033
22151
|
throw new CommandExit(1, [
|
|
22034
22152
|
`\u274C Refusing to start: ${file} breaks ${problems.length} effort-store rule(s) that \`rafa plan validate\` checks.`,
|
|
22035
22153
|
...problems.map((problem) => ` ${storeRuleLine(file, problem)}`),
|
|
@@ -22062,7 +22180,7 @@ async function loadItems(options) {
|
|
|
22062
22180
|
function refuseMalformedItems(planPath, items) {
|
|
22063
22181
|
if (items.malformed.length === 0)
|
|
22064
22182
|
return;
|
|
22065
|
-
const file =
|
|
22183
|
+
const file = basename11(prerequisitesPathForPlan(planPath) ?? planPath);
|
|
22066
22184
|
const [head = "", ...rest] = malformedPrerequisiteLines(file, items.malformed);
|
|
22067
22185
|
throw new CommandExit(1, [
|
|
22068
22186
|
`\u274C Refusing to start: ${head}`,
|
|
@@ -22075,7 +22193,7 @@ function skippedStartLine(item, tracker) {
|
|
|
22075
22193
|
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`;
|
|
22076
22194
|
}
|
|
22077
22195
|
function announceSkippedStart(planPath, skipped2) {
|
|
22078
|
-
const tracker =
|
|
22196
|
+
const tracker = basename11(trackerPathFor(planPath));
|
|
22079
22197
|
const output = activeOutput();
|
|
22080
22198
|
for (const [index, item] of skipped2.entries()) {
|
|
22081
22199
|
const lead = index === 0 ? `
|
|
@@ -22094,7 +22212,7 @@ function startTier(planPath, items) {
|
|
|
22094
22212
|
function announceReminders(planPath, reminders) {
|
|
22095
22213
|
if (reminders.length === 0)
|
|
22096
22214
|
return;
|
|
22097
|
-
const file =
|
|
22215
|
+
const file = basename11(prerequisitesPathForPlan(planPath) ?? planPath);
|
|
22098
22216
|
const output = activeOutput();
|
|
22099
22217
|
output.info(`
|
|
22100
22218
|
\uD83D\uDCCC ${file} names ${reminders.length} step(s) the preflight does not check:`);
|
|
@@ -22245,16 +22363,16 @@ function baseLines(base) {
|
|
|
22245
22363
|
return [`${BASE_PROMPT_PREFIX}${base}: run \`bun test --changed=${base}\` for the tests your changes reach.`];
|
|
22246
22364
|
}
|
|
22247
22365
|
function sectionLines(sections2) {
|
|
22248
|
-
return [sections2.skills, sections2.lessons].filter((section2) => section2.trim().length > 0).flatMap((section2) => [section2, ""]);
|
|
22366
|
+
return [sections2.skills, sections2.lessons, sections2.inherited].filter((section2) => section2.trim().length > 0).flatMap((section2) => [section2, ""]);
|
|
22249
22367
|
}
|
|
22250
|
-
function buildTaskPrompt(taskText, promptContent, planText, knownMissing = [], blocker = null, sections2 = NO_TASK_SECTIONS, base = null) {
|
|
22368
|
+
function buildTaskPrompt(taskText, promptContent, planText, knownMissing = [], blocker = null, sections2 = NO_TASK_SECTIONS, base = null, inherited = []) {
|
|
22251
22369
|
return [
|
|
22252
22370
|
`Your scoped task is: ${taskText}`,
|
|
22253
22371
|
"Consider tasks listed above this one in the plan checklist as completed. Do not re-evaluate or re-do them. Focus only on the scoped task.",
|
|
22254
22372
|
...blockerLines(blocker),
|
|
22255
22373
|
...baseLines(base),
|
|
22256
22374
|
"",
|
|
22257
|
-
...sectionLines(sections2),
|
|
22375
|
+
...sectionLines({ ...sections2, inherited: renderInheritedSection(inherited) }),
|
|
22258
22376
|
promptContent,
|
|
22259
22377
|
planText,
|
|
22260
22378
|
...knownMissingNotice(knownMissing)
|
|
@@ -22296,7 +22414,7 @@ async function dispatchTask(options) {
|
|
|
22296
22414
|
const resolution = options.serving === null ? EMPTY_RESOLUTION : resolveSessionTiers(options.serving);
|
|
22297
22415
|
const task = taskInputFor(taskInfo, taskText, declaration, options.planContent);
|
|
22298
22416
|
const handed = await handOut(options.handout, task, resolution, options.repoRoot);
|
|
22299
|
-
const prompt = withStamp(buildTaskPrompt(taskText, options.promptContent, injection.text, options.knownMissing, taskInfo.blocker ?? null, { skills: renderSkillsSection(handed.skills), lessons: renderLessonsSection(handed.lessons) }, options.base));
|
|
22417
|
+
const prompt = withStamp(buildTaskPrompt(taskText, options.promptContent, injection.text, options.knownMissing, taskInfo.blocker ?? null, { skills: renderSkillsSection(handed.skills), lessons: renderLessonsSection(handed.lessons) }, options.base, options.inherited));
|
|
22300
22418
|
const served2 = serveForSession(options.serving, resolution);
|
|
22301
22419
|
const sessionId = (options.newSessionId ?? randomUUID11)();
|
|
22302
22420
|
const session = await run(prompt, flags, sessionId, options.settingSources, served2, options.checkout);
|
|
@@ -22415,6 +22533,7 @@ var init_dispatch = __esm(() => {
|
|
|
22415
22533
|
init_progress();
|
|
22416
22534
|
init_tracker();
|
|
22417
22535
|
init_handout();
|
|
22536
|
+
init_inherited_notice();
|
|
22418
22537
|
init_preflight2();
|
|
22419
22538
|
init_serving();
|
|
22420
22539
|
init_stamp2();
|
|
@@ -22636,7 +22755,7 @@ var QUOTE_WINDOW = 3;
|
|
|
22636
22755
|
// src/inventory/search/scratch.ts
|
|
22637
22756
|
import { copyFileSync as copyFileSync2, mkdirSync as mkdirSync13, mkdtempSync as mkdtempSync2, rmSync as rmSync8 } from "fs";
|
|
22638
22757
|
import { tmpdir as tmpdir4 } from "os";
|
|
22639
|
-
import { basename as
|
|
22758
|
+
import { basename as basename12, join as join36 } from "path";
|
|
22640
22759
|
function safeName(name2) {
|
|
22641
22760
|
return name2 !== "" && name2 !== "." && name2 !== ".." && !/[\\/]/.test(name2);
|
|
22642
22761
|
}
|
|
@@ -22661,7 +22780,7 @@ function createScratchCopy(candidates, root = tmpdir4()) {
|
|
|
22661
22780
|
for (const { name: name2, path: path2 } of candidates) {
|
|
22662
22781
|
const target = join36(dir, name2);
|
|
22663
22782
|
mkdirSync13(target);
|
|
22664
|
-
const copy = join36(target,
|
|
22783
|
+
const copy = join36(target, basename12(path2));
|
|
22665
22784
|
copyFileSync2(path2, copy);
|
|
22666
22785
|
paths.set(name2, copy);
|
|
22667
22786
|
}
|
|
@@ -23086,7 +23205,7 @@ var init_show2 = __esm(() => {
|
|
|
23086
23205
|
|
|
23087
23206
|
// src/commands/agent/vendor.ts
|
|
23088
23207
|
import { copyFileSync as copyFileSync3, existsSync as existsSync32, mkdirSync as mkdirSync14, readFileSync as readFileSync28, writeFileSync as writeFileSync11 } from "fs";
|
|
23089
|
-
import { basename as
|
|
23208
|
+
import { basename as basename13, join as join38 } from "path";
|
|
23090
23209
|
function readForce(value) {
|
|
23091
23210
|
if (value === undefined || value === false || value === "false")
|
|
23092
23211
|
return false;
|
|
@@ -23152,7 +23271,7 @@ function planCopies(names, project, force, entry) {
|
|
|
23152
23271
|
problems.push(` agent "${name2}": no definition under ${searched} carries that name`);
|
|
23153
23272
|
continue;
|
|
23154
23273
|
}
|
|
23155
|
-
const to = join38(dir,
|
|
23274
|
+
const to = join38(dir, basename13(holder.definition.path));
|
|
23156
23275
|
const replaced = existsSync32(to);
|
|
23157
23276
|
if (replaced && !force) {
|
|
23158
23277
|
problems.push(` agent "${name2}": ${to} is already there; pass --force to replace it`);
|
|
@@ -33135,7 +33254,7 @@ import {
|
|
|
33135
33254
|
statSync as statSync20,
|
|
33136
33255
|
symlinkSync
|
|
33137
33256
|
} from "fs";
|
|
33138
|
-
import { basename as
|
|
33257
|
+
import { basename as basename14, delimiter as delimiter4, dirname as dirname22, join as join49, relative as relative11, resolve as resolve21 } from "path";
|
|
33139
33258
|
function exitCodeFor(outcome2) {
|
|
33140
33259
|
switch (outcome2.kind) {
|
|
33141
33260
|
case "done":
|
|
@@ -33241,7 +33360,7 @@ function swapIn2(staging, to) {
|
|
|
33241
33360
|
rmSync9(outgoing, { recursive: true, force: true });
|
|
33242
33361
|
}
|
|
33243
33362
|
function relink(linkPath, target) {
|
|
33244
|
-
mkdirSync16(
|
|
33363
|
+
mkdirSync16(dirname22(linkPath), { recursive: true });
|
|
33245
33364
|
const temporary = temporaryName(linkPath);
|
|
33246
33365
|
rmSync9(temporary, { force: true });
|
|
33247
33366
|
try {
|
|
@@ -33264,7 +33383,7 @@ function runBuild(repoRoot, lines) {
|
|
|
33264
33383
|
cwd: repoRoot,
|
|
33265
33384
|
env: {
|
|
33266
33385
|
...process.env,
|
|
33267
|
-
PATH: [
|
|
33386
|
+
PATH: [dirname22(process.execPath), process.env["PATH"] ?? ""].join(delimiter4)
|
|
33268
33387
|
},
|
|
33269
33388
|
stdin: "ignore",
|
|
33270
33389
|
stdout: stdio,
|
|
@@ -33356,7 +33475,7 @@ function replaceFile(source, target) {
|
|
|
33356
33475
|
}
|
|
33357
33476
|
}
|
|
33358
33477
|
function temporaryName(path3) {
|
|
33359
|
-
return join49(
|
|
33478
|
+
return join49(dirname22(path3), `.${basename14(path3)}.snapshot-${process.pid}`);
|
|
33360
33479
|
}
|
|
33361
33480
|
var LINK_NAME = "rafa", FORCE_FLAG = "--force", EXIT_DONE = 0, EXIT_REFUSED = 1, EXIT_COULD_NOT_RUN = 2, TRACKER_NAME, NOTHING_DONE = "nothing was built, copied or linked", AFTERMATH, StageFailure;
|
|
33362
33481
|
var init_install = __esm(() => {
|
|
@@ -35691,6 +35810,47 @@ function hasBranch(refs, stub) {
|
|
|
35691
35810
|
const branch = `${BRANCH_PREFIX2}${stub}`;
|
|
35692
35811
|
return refs.some((ref7) => ref7 === branch || ref7.endsWith(`/${branch}`));
|
|
35693
35812
|
}
|
|
35813
|
+
function readBranchTips(sources, remote, stub, note) {
|
|
35814
|
+
const branch = `${BRANCH_PREFIX2}${stub}`;
|
|
35815
|
+
const tips = [];
|
|
35816
|
+
for (const ref7 of [`refs/heads/${branch}`, `refs/remotes/${remote}/${branch}`]) {
|
|
35817
|
+
const resolved = sources.git(["rev-parse", "--verify", "--quiet", `${ref7}^{commit}`]);
|
|
35818
|
+
const said = gitSaid(resolved);
|
|
35819
|
+
if (resolved.ok && resolved.stdout.trim() !== "") {
|
|
35820
|
+
tips.push(ref7);
|
|
35821
|
+
} else if (said !== "") {
|
|
35822
|
+
note(`\`${ref7}\` could not be read, so its plan reads as started: ${said}`);
|
|
35823
|
+
return null;
|
|
35824
|
+
}
|
|
35825
|
+
}
|
|
35826
|
+
return tips;
|
|
35827
|
+
}
|
|
35828
|
+
function readBranchMessages(sources, ref7, note) {
|
|
35829
|
+
const result = sources.git(["log", "-z", "--format=%B", `${sources.base}..${ref7}`]);
|
|
35830
|
+
if (!result.ok) {
|
|
35831
|
+
note(`the commits of \`${ref7}\` past \`${sources.base}\` could not be read, so its plan reads as started: ${gitSaid(result)}`);
|
|
35832
|
+
return null;
|
|
35833
|
+
}
|
|
35834
|
+
return result.stdout.split("\x00").map((message) => message.trim()).filter((message) => message !== "");
|
|
35835
|
+
}
|
|
35836
|
+
function claimsOnly(messages) {
|
|
35837
|
+
return messages.length > 0 && messages.every((message) => parseClaimMessage(message).kind === "ownership");
|
|
35838
|
+
}
|
|
35839
|
+
function hasStartedBranch(sources, remote, refs, stub, note) {
|
|
35840
|
+
if (!hasBranch(refs, stub))
|
|
35841
|
+
return false;
|
|
35842
|
+
const tips = readBranchTips(sources, remote, stub, note);
|
|
35843
|
+
if (tips === null)
|
|
35844
|
+
return true;
|
|
35845
|
+
if (tips.length === 0) {
|
|
35846
|
+
note(`\`${BRANCH_PREFIX2}${stub}\` is on ${remote} but not fetched here, so its commits were not read and its plan reads as started`);
|
|
35847
|
+
return true;
|
|
35848
|
+
}
|
|
35849
|
+
return tips.some((ref7) => {
|
|
35850
|
+
const messages = readBranchMessages(sources, ref7, note);
|
|
35851
|
+
return messages === null || !claimsOnly(messages);
|
|
35852
|
+
});
|
|
35853
|
+
}
|
|
35694
35854
|
function openWorld(sources) {
|
|
35695
35855
|
let problems = [];
|
|
35696
35856
|
const note = (problem) => {
|
|
@@ -35715,7 +35875,7 @@ function openWorld(sources) {
|
|
|
35715
35875
|
tracked: once2(() => readTracked(sources, note)),
|
|
35716
35876
|
checkout: once2(() => readCheckout(sources, note)),
|
|
35717
35877
|
branchPlan: once2(() => readBranchPlan(branch(), plans())),
|
|
35718
|
-
unstartedPlan: once2(() => plans().find((plan) => !hasRun(runs(), plan.stub) && !
|
|
35878
|
+
unstartedPlan: once2(() => plans().find((plan) => !hasRun(runs(), plan.stub) && !hasStartedBranch(sources, remote, refs(), plan.stub, note)) ?? null),
|
|
35719
35879
|
openPull: once2(() => readOpenPull(sources, branch(), note)),
|
|
35720
35880
|
roadmap,
|
|
35721
35881
|
picked: once2(async () => {
|
|
@@ -35743,6 +35903,7 @@ function branchLabel(world) {
|
|
|
35743
35903
|
var DETACHED = "HEAD", PORCELAIN_PATH_AT = 3;
|
|
35744
35904
|
var init_readings = __esm(() => {
|
|
35745
35905
|
init_roadmap();
|
|
35906
|
+
init_record3();
|
|
35746
35907
|
init_loop_sessions();
|
|
35747
35908
|
init_list5();
|
|
35748
35909
|
init_plan_files();
|
|
@@ -35752,7 +35913,7 @@ var init_readings = __esm(() => {
|
|
|
35752
35913
|
});
|
|
35753
35914
|
|
|
35754
35915
|
// src/next/hop-rows.ts
|
|
35755
|
-
function
|
|
35916
|
+
function oneLine6(text2) {
|
|
35756
35917
|
return text2.replace(/\s+/g, " ").trim();
|
|
35757
35918
|
}
|
|
35758
35919
|
function ref7(issue4) {
|
|
@@ -35793,7 +35954,7 @@ async function readGate(roadmap, pullRequest) {
|
|
|
35793
35954
|
} catch (error) {
|
|
35794
35955
|
return {
|
|
35795
35956
|
state: "unknown",
|
|
35796
|
-
reason: `could not read the owner gate of ${ref7(pullRequest)}: ${
|
|
35957
|
+
reason: `could not read the owner gate of ${ref7(pullRequest)}: ${oneLine6(messageOf(error))}`,
|
|
35797
35958
|
owners: []
|
|
35798
35959
|
};
|
|
35799
35960
|
}
|
|
@@ -35829,7 +35990,7 @@ async function readPrOwnerReview(world) {
|
|
|
35829
35990
|
id: "pr-owner-review",
|
|
35830
35991
|
action: "none",
|
|
35831
35992
|
reading: `waiting on ${ref7(issue4 ?? pullRequest)} (owner review)`,
|
|
35832
|
-
proposal: `leave ${ref7(pullRequest)} to its owner's review, which rafa does not merge past: ${
|
|
35993
|
+
proposal: `leave ${ref7(pullRequest)} to its owner's review, which rafa does not merge past: ${oneLine6(gate.reason)}`,
|
|
35833
35994
|
pullRequest,
|
|
35834
35995
|
...issue4 === null ? {} : { issue: issue4 }
|
|
35835
35996
|
};
|
|
@@ -35979,7 +36140,7 @@ function nameFiles(paths) {
|
|
|
35979
36140
|
const hidden = paths.length - shown2.length;
|
|
35980
36141
|
return hidden > 0 ? `${shown2.join(", ")} and ${hidden} more` : shown2.join(", ");
|
|
35981
36142
|
}
|
|
35982
|
-
function
|
|
36143
|
+
function oneLine7(text2) {
|
|
35983
36144
|
return text2.replace(/\s+/g, " ").trim();
|
|
35984
36145
|
}
|
|
35985
36146
|
function treeLabel(checkout) {
|
|
@@ -36005,7 +36166,7 @@ async function readPullsUnusable(world) {
|
|
|
36005
36166
|
return {
|
|
36006
36167
|
id: "pulls-unusable",
|
|
36007
36168
|
action: "none",
|
|
36008
|
-
reading: `the \`${world.sources.pulls.kind}\` pull request provider could not be asked: ${
|
|
36169
|
+
reading: `the \`${world.sources.pulls.kind}\` pull request provider could not be asked: ${oneLine7(messageOf(error))}`,
|
|
36009
36170
|
proposal: "run `rafa doctor` to see what the provider needs, then read the state again"
|
|
36010
36171
|
};
|
|
36011
36172
|
}
|
|
@@ -38083,7 +38244,7 @@ var init_doctor_epics = __esm(() => {
|
|
|
38083
38244
|
|
|
38084
38245
|
// src/board/epic-guard.ts
|
|
38085
38246
|
import { existsSync as existsSync43, readFileSync as readFileSync46 } from "fs";
|
|
38086
|
-
import { dirname as
|
|
38247
|
+
import { dirname as dirname23, join as join56 } from "path";
|
|
38087
38248
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
38088
38249
|
function epicGuardSource(moduleDir = MODULE_DIR2) {
|
|
38089
38250
|
return join56(moduleDir, TEMPLATES_DIRNAME, EPIC_GUARD_FILE);
|
|
@@ -38106,7 +38267,7 @@ function writeEpicGuard(root, moduleDir = MODULE_DIR2) {
|
|
|
38106
38267
|
var MODULE_DIR2, EPIC_GUARD_PATH, EPIC_GUARD_FILE = "epic-guard.yml";
|
|
38107
38268
|
var init_epic_guard = __esm(() => {
|
|
38108
38269
|
init_setup();
|
|
38109
|
-
MODULE_DIR2 =
|
|
38270
|
+
MODULE_DIR2 = dirname23(fileURLToPath2(import.meta.url));
|
|
38110
38271
|
EPIC_GUARD_PATH = join56(".github", "workflows", "epic-guard.yml");
|
|
38111
38272
|
});
|
|
38112
38273
|
|
|
@@ -38862,7 +39023,7 @@ var init_doctor_relations = __esm(() => {
|
|
|
38862
39023
|
});
|
|
38863
39024
|
|
|
38864
39025
|
// src/commands/doctor-render.ts
|
|
38865
|
-
import { basename as
|
|
39026
|
+
import { basename as basename15, relative as relative13, sep as sep4 } from "path";
|
|
38866
39027
|
function shownPath(path3, root) {
|
|
38867
39028
|
return path3.startsWith(`${root}${sep4}`) ? relative13(root, path3) : path3;
|
|
38868
39029
|
}
|
|
@@ -38881,7 +39042,7 @@ function headLine2(preflight) {
|
|
|
38881
39042
|
const checked2 = checkedPhrase(count2, automatic, "from the config ");
|
|
38882
39043
|
return `Preflight with no plan, none being at ${where}: ${checked2}, no run started.`;
|
|
38883
39044
|
}
|
|
38884
|
-
const merged = prerequisitesFile === null ? "" : `, with ${
|
|
39045
|
+
const merged = prerequisitesFile === null ? "" : `, with ${basename15(prerequisitesFile)} merged in`;
|
|
38885
39046
|
const checked = checkedPhrase(count2, automatic, "");
|
|
38886
39047
|
return `Preflight for ${shownPath(plan, root)}${merged}: ${checked}, no run started.`;
|
|
38887
39048
|
}
|
|
@@ -38903,7 +39064,7 @@ function reminderLines(preflight) {
|
|
|
38903
39064
|
if (reminders.length === 0 || prerequisitesFile === null)
|
|
38904
39065
|
return [];
|
|
38905
39066
|
return [
|
|
38906
|
-
`${
|
|
39067
|
+
`${basename15(prerequisitesFile)} names ${plural(reminders.length, "step")} the preflight does not check:`,
|
|
38907
39068
|
...reminders.map((reminder) => ` line ${String(reminder.line)}: ${reminder.description}`)
|
|
38908
39069
|
];
|
|
38909
39070
|
}
|
|
@@ -40926,7 +41087,7 @@ var init_doctor_release = __esm(() => {
|
|
|
40926
41087
|
|
|
40927
41088
|
// src/commands/doctor-tiers.ts
|
|
40928
41089
|
import { readFileSync as readFileSync48, realpathSync as realpathSync12 } from "fs";
|
|
40929
|
-
import { basename as
|
|
41090
|
+
import { basename as basename16, dirname as dirname24 } from "path";
|
|
40930
41091
|
function readTextFile(path4) {
|
|
40931
41092
|
try {
|
|
40932
41093
|
return readFileSync48(path4, "utf8");
|
|
@@ -40934,7 +41095,7 @@ function readTextFile(path4) {
|
|
|
40934
41095
|
return null;
|
|
40935
41096
|
}
|
|
40936
41097
|
}
|
|
40937
|
-
function
|
|
41098
|
+
function realPathOf2(path4) {
|
|
40938
41099
|
try {
|
|
40939
41100
|
return realpathSync12(path4);
|
|
40940
41101
|
} catch {
|
|
@@ -40984,9 +41145,9 @@ function keptHolder(item) {
|
|
|
40984
41145
|
return [item.winner, ...item.copies].find((holder) => holder.source === "rafa") ?? item.winner;
|
|
40985
41146
|
}
|
|
40986
41147
|
function deletion(holder) {
|
|
40987
|
-
return holder.kind === "skill" &&
|
|
41148
|
+
return holder.kind === "skill" && basename16(holder.path) === SKILL_FILE2 ? `delete ${dirname24(holder.path)} (only its ${SKILL_FILE2} was compared)` : `delete ${holder.path}`;
|
|
40988
41149
|
}
|
|
40989
|
-
function copyRows(items, realPath =
|
|
41150
|
+
function copyRows(items, realPath = realPathOf2) {
|
|
40990
41151
|
return items.flatMap((item) => {
|
|
40991
41152
|
if (item.state !== "served" || item.copies.length === 0)
|
|
40992
41153
|
return [];
|
|
@@ -41028,7 +41189,7 @@ function doctorTierRows(inventory, installed, readers = {}) {
|
|
|
41028
41189
|
...unreviewedRows(inventory.records, readText7),
|
|
41029
41190
|
...cliVersionRows(installed),
|
|
41030
41191
|
...skillUseVersionRows(installed),
|
|
41031
|
-
...copyRows(inventory.resolution.items, readers.realPath ??
|
|
41192
|
+
...copyRows(inventory.resolution.items, readers.realPath ?? realPathOf2),
|
|
41032
41193
|
...noProvenanceRows(inventory, readText7)
|
|
41033
41194
|
];
|
|
41034
41195
|
}
|
|
@@ -41076,7 +41237,7 @@ var init_doctor_tiers = __esm(() => {
|
|
|
41076
41237
|
});
|
|
41077
41238
|
|
|
41078
41239
|
// src/commands/doctor.ts
|
|
41079
|
-
import { basename as
|
|
41240
|
+
import { basename as basename17, resolve as resolve24 } from "path";
|
|
41080
41241
|
function refusal6(lines2) {
|
|
41081
41242
|
return new CommandExit(1, [...lines2, NOTHING_CHECKED].join(`
|
|
41082
41243
|
`));
|
|
@@ -41140,7 +41301,7 @@ async function loadItems2(plan, config) {
|
|
|
41140
41301
|
function refuseMalformedItems2(plan, items) {
|
|
41141
41302
|
if (plan === null || items.malformed.length === 0)
|
|
41142
41303
|
return;
|
|
41143
|
-
const file =
|
|
41304
|
+
const file = basename17(prerequisitesPathForPlan(plan) ?? plan);
|
|
41144
41305
|
const [head = "", ...rest] = malformedPrerequisiteLines(file, items.malformed);
|
|
41145
41306
|
throw refusal6([
|
|
41146
41307
|
`rafa doctor: ${head}`,
|
|
@@ -41165,7 +41326,7 @@ function readStartTier(plan, items) {
|
|
|
41165
41326
|
return { tier: NO_START_TIER, items: NO_ITEMS3 };
|
|
41166
41327
|
if (isFirstDispatch(plan))
|
|
41167
41328
|
return { tier: { checked: start.length, skipped: 0, tracker: null }, items: start };
|
|
41168
|
-
const tracker =
|
|
41329
|
+
const tracker = basename17(trackerPathFor(plan));
|
|
41169
41330
|
return { tier: { checked: 0, skipped: start.length, tracker }, items: NO_ITEMS3 };
|
|
41170
41331
|
}
|
|
41171
41332
|
async function checkPreflight(context, project, seams) {
|
|
@@ -41655,7 +41816,7 @@ function formatTaskReports(report) {
|
|
|
41655
41816
|
const rows2 = tallies.map(tallyCells);
|
|
41656
41817
|
return [...lines2, ...alignRows(TASK_REPORT_COLUMNS, rows2, TASK_REPORT_RIGHT_ALIGNED)];
|
|
41657
41818
|
}
|
|
41658
|
-
function
|
|
41819
|
+
function oneLine8(text2) {
|
|
41659
41820
|
return text2.replace(/\s+/g, " ").trim();
|
|
41660
41821
|
}
|
|
41661
41822
|
function haltCells(halt2, check) {
|
|
@@ -41665,7 +41826,7 @@ function haltCells(halt2, check) {
|
|
|
41665
41826
|
`${check.kind} ${JSON.stringify(check.item)}`,
|
|
41666
41827
|
check.outcome,
|
|
41667
41828
|
formatCount(check.durationMs),
|
|
41668
|
-
|
|
41829
|
+
oneLine8(check.failure)
|
|
41669
41830
|
];
|
|
41670
41831
|
}
|
|
41671
41832
|
function formatPreflightHalts(report) {
|
|
@@ -41836,7 +41997,7 @@ function outlierLines(outliers) {
|
|
|
41836
41997
|
`outliers: ${plural(outliers.sessions.length, "recent session")} over ${formatMinutes(outliers.limitMinutes)} min` + " (baseline median + 3 robust SD)"
|
|
41837
41998
|
];
|
|
41838
41999
|
for (const session of outliers.sessions.slice(0, OUTLIERS_LISTED)) {
|
|
41839
|
-
lines2.push(` ${formatMinutes(session.minutes)} min ${formatTokens(session.outputTokens)} out` + ` ${session.outcome ?? "no report"} ${session.loop}`, ` ${
|
|
42000
|
+
lines2.push(` ${formatMinutes(session.minutes)} min ${formatTokens(session.outputTokens)} out` + ` ${session.outcome ?? "no report"} ${session.loop}`, ` ${oneLine8(session.taskText ?? "").slice(0, TASK_TEXT_CHARS)}`);
|
|
41840
42001
|
}
|
|
41841
42002
|
if (outliers.sessions.length > OUTLIERS_LISTED) {
|
|
41842
42003
|
lines2.push(` and ${outliers.sessions.length - OUTLIERS_LISTED} more in --output=json`);
|
|
@@ -42303,7 +42464,7 @@ function budgetedSessions(rows2, budgets) {
|
|
|
42303
42464
|
|
|
42304
42465
|
// src/effort/report-skills-format.ts
|
|
42305
42466
|
function matchedCell(matches) {
|
|
42306
|
-
const texts = [...new Set(matches.map(({ text: text2 }) =>
|
|
42467
|
+
const texts = [...new Set(matches.map(({ text: text2 }) => oneLine8(text2)))];
|
|
42307
42468
|
return texts.length === 0 ? "-" : texts.join(" / ");
|
|
42308
42469
|
}
|
|
42309
42470
|
function skillSignalCell(row3) {
|
|
@@ -42327,7 +42488,7 @@ function skillCells(row3) {
|
|
|
42327
42488
|
function lessonCells(row3) {
|
|
42328
42489
|
return [
|
|
42329
42490
|
row3.id,
|
|
42330
|
-
row3.artifact === null ? "unknown" :
|
|
42491
|
+
row3.artifact === null ? "unknown" : oneLine8(row3.artifact),
|
|
42331
42492
|
formatCount(row3.injected),
|
|
42332
42493
|
formatCount(row3.recurred),
|
|
42333
42494
|
matchedCell(row3.matches),
|
|
@@ -42347,7 +42508,7 @@ function formatMetric2(value) {
|
|
|
42347
42508
|
function formatPlanCi(reading2) {
|
|
42348
42509
|
if (reading2 === null)
|
|
42349
42510
|
return `${PLAN_CI_LABEL}: not read`;
|
|
42350
|
-
const failing = reading2.failing.length === 0 ? "" : `, failing: ${reading2.failing.map(
|
|
42511
|
+
const failing = reading2.failing.length === 0 ? "" : `, failing: ${reading2.failing.map(oneLine8).join(", ")}`;
|
|
42351
42512
|
const sha = reading2.headSha.slice(0, SHORT_SHA_LENGTH);
|
|
42352
42513
|
return `${PLAN_CI_LABEL}: ${reading2.verdict} on #${reading2.pr} at ${sha}, read ${reading2.readAt}${failing}`;
|
|
42353
42514
|
}
|
|
@@ -45640,7 +45801,7 @@ var init_cancel = __esm(() => {
|
|
|
45640
45801
|
|
|
45641
45802
|
// src/board/epic-template.ts
|
|
45642
45803
|
import { existsSync as existsSync46, readFileSync as readFileSync51 } from "fs";
|
|
45643
|
-
import { dirname as
|
|
45804
|
+
import { dirname as dirname25, join as join68 } from "path";
|
|
45644
45805
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
45645
45806
|
function epicTemplateSource(moduleDir = MODULE_DIR3) {
|
|
45646
45807
|
return join68(moduleDir, TEMPLATES_DIRNAME, EPIC_TEMPLATE_FILE);
|
|
@@ -45676,7 +45837,7 @@ var MODULE_DIR3, EPIC_TEMPLATE_FILE = "epic.md", CRITERIA_PLACEHOLDERS, PLACEHOL
|
|
|
45676
45837
|
var init_epic_template = __esm(() => {
|
|
45677
45838
|
init_epic_body();
|
|
45678
45839
|
init_setup();
|
|
45679
|
-
MODULE_DIR3 =
|
|
45840
|
+
MODULE_DIR3 = dirname25(fileURLToPath3(import.meta.url));
|
|
45680
45841
|
CRITERIA_PLACEHOLDERS = Object.freeze([
|
|
45681
45842
|
"- Placeholder: one line per thing that makes this epic finished," + " written so a check against main can tell whether it holds."
|
|
45682
45843
|
]);
|
|
@@ -45757,7 +45918,7 @@ var init_epic_cost = __esm(() => {
|
|
|
45757
45918
|
|
|
45758
45919
|
// src/epic/verify-plan.ts
|
|
45759
45920
|
import { existsSync as existsSync47, readFileSync as readFileSync52 } from "fs";
|
|
45760
|
-
import { dirname as
|
|
45921
|
+
import { dirname as dirname26, join as join69 } from "path";
|
|
45761
45922
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
45762
45923
|
function opensCriterion(text2, state) {
|
|
45763
45924
|
const item = LIST_ITEM2.exec(text2);
|
|
@@ -45802,7 +45963,7 @@ function criteriaToAsk(criteria) {
|
|
|
45802
45963
|
return Object.freeze(criteria.filter((criterion) => !criterion.placeholder));
|
|
45803
45964
|
}
|
|
45804
45965
|
function verifyPromptCandidates(moduleDir = MODULE_DIR4) {
|
|
45805
|
-
return [join69(moduleDir, VERIFY_PROMPT_FILE), join69(
|
|
45966
|
+
return [join69(moduleDir, VERIFY_PROMPT_FILE), join69(dirname26(moduleDir), VERIFY_PROMPT_FILE)];
|
|
45806
45967
|
}
|
|
45807
45968
|
function readVerifyPrompt(moduleDir = MODULE_DIR4) {
|
|
45808
45969
|
const candidates = verifyPromptCandidates(moduleDir);
|
|
@@ -45948,7 +46109,7 @@ var MODULE_DIR4, VERIFY_PROMPT_FILE = "epic-verify-prompt.md", VERIFY_BLOCK_FENC
|
|
|
45948
46109
|
var init_verify_plan = __esm(() => {
|
|
45949
46110
|
init_epic_template();
|
|
45950
46111
|
init_blocks();
|
|
45951
|
-
MODULE_DIR4 =
|
|
46112
|
+
MODULE_DIR4 = dirname26(fileURLToPath4(import.meta.url));
|
|
45952
46113
|
VERIFY_PROMPT_SLOTS = ["EPIC_NUMBER", "EPIC_TITLE", "CRITERIA"];
|
|
45953
46114
|
FENCE5 = /^\s*(?:`{3,}|~{3,})/u;
|
|
45954
46115
|
LIST_ITEM2 = /^( {0,3})(?:[-*+]|\d{1,9}[.)])(?:\s|$)/u;
|
|
@@ -46153,9 +46314,371 @@ var init_verify_run = __esm(() => {
|
|
|
46153
46314
|
VERDICT_KIND = VERDICT_BLOCK_FENCE.slice("rafa:".length);
|
|
46154
46315
|
});
|
|
46155
46316
|
|
|
46317
|
+
// src/suite/baseline.ts
|
|
46318
|
+
import { existsSync as existsSync48, mkdirSync as mkdirSync17, readFileSync as readFileSync53, renameSync as renameSync9, writeFileSync as writeFileSync17 } from "fs";
|
|
46319
|
+
import { basename as basename18, dirname as dirname27, join as join71 } from "path";
|
|
46320
|
+
function baselinePathFor(trackerPath) {
|
|
46321
|
+
const match = PLAN_FILE_NAME2.exec(basename18(trackerPath));
|
|
46322
|
+
if (match === null) {
|
|
46323
|
+
throw new RangeError(`baselinePathFor: ${trackerPath} is not a PLAN or PLAN_TRACKER file`);
|
|
46324
|
+
}
|
|
46325
|
+
return join71(dirname27(trackerPath), `${BASELINE_FILE_PREFIX}${match[1] ?? ""}.json`);
|
|
46326
|
+
}
|
|
46327
|
+
function copyFailure(failure6) {
|
|
46328
|
+
return failure6.message === undefined ? { file: failure6.file, name: failure6.name } : { file: failure6.file, name: failure6.name, message: failure6.message };
|
|
46329
|
+
}
|
|
46330
|
+
function baselineOf(result2, recordedAt, commit) {
|
|
46331
|
+
return {
|
|
46332
|
+
version: BASELINE_VERSION,
|
|
46333
|
+
recordedAt: recordedAt.toISOString(),
|
|
46334
|
+
commit,
|
|
46335
|
+
command: [...result2.command],
|
|
46336
|
+
exitCode: result2.exitCode,
|
|
46337
|
+
summary: result2.summary,
|
|
46338
|
+
failures: result2.failures.map(copyFailure),
|
|
46339
|
+
errors: result2.errors,
|
|
46340
|
+
junit: result2.junit
|
|
46341
|
+
};
|
|
46342
|
+
}
|
|
46343
|
+
function writeBaseline(path4, baseline) {
|
|
46344
|
+
mkdirSync17(dirname27(path4), { recursive: true });
|
|
46345
|
+
const temporary = `${path4}.${process.pid}.tmp`;
|
|
46346
|
+
writeFileSync17(temporary, `${JSON.stringify(baseline, null, 2)}
|
|
46347
|
+
`, "utf8");
|
|
46348
|
+
renameSync9(temporary, path4);
|
|
46349
|
+
}
|
|
46350
|
+
function isStringOrNull(value) {
|
|
46351
|
+
return value === null || typeof value === "string";
|
|
46352
|
+
}
|
|
46353
|
+
function isFailure2(value) {
|
|
46354
|
+
if (typeof value !== "object" || value === null)
|
|
46355
|
+
return false;
|
|
46356
|
+
const record = value;
|
|
46357
|
+
const message = record["message"];
|
|
46358
|
+
return typeof record["file"] === "string" && typeof record["name"] === "string" && (message === undefined || typeof message === "string");
|
|
46359
|
+
}
|
|
46360
|
+
function badField(record) {
|
|
46361
|
+
const checks = [
|
|
46362
|
+
["recordedAt", (value) => typeof value === "string"],
|
|
46363
|
+
["commit", isStringOrNull],
|
|
46364
|
+
["command", (value) => Array.isArray(value) && value.every((part) => typeof part === "string")],
|
|
46365
|
+
["exitCode", (value) => Number.isInteger(value)],
|
|
46366
|
+
["summary", isStringOrNull],
|
|
46367
|
+
["failures", (value) => Array.isArray(value) && value.every(isFailure2)],
|
|
46368
|
+
["errors", (value) => value === null || Number.isInteger(value)],
|
|
46369
|
+
["junit", (value) => JUNIT_READINGS.includes(value)]
|
|
46370
|
+
];
|
|
46371
|
+
const failed3 = checks.find(([key, check]) => !check(record[key]));
|
|
46372
|
+
return failed3?.[0] ?? null;
|
|
46373
|
+
}
|
|
46374
|
+
function asBaseline(parsed) {
|
|
46375
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
46376
|
+
return { state: "unreadable", reason: "not a JSON object" };
|
|
46377
|
+
}
|
|
46378
|
+
const record = parsed;
|
|
46379
|
+
if (record["version"] !== BASELINE_VERSION) {
|
|
46380
|
+
return { state: "unreadable", reason: `version ${JSON.stringify(record["version"])}, expected ${BASELINE_VERSION}` };
|
|
46381
|
+
}
|
|
46382
|
+
const bad = badField(record);
|
|
46383
|
+
if (bad !== null)
|
|
46384
|
+
return { state: "unreadable", reason: `field ${bad} is missing or malformed` };
|
|
46385
|
+
return { state: "read", baseline: parsed };
|
|
46386
|
+
}
|
|
46387
|
+
function readBaseline(path4) {
|
|
46388
|
+
if (!existsSync48(path4))
|
|
46389
|
+
return { state: "missing" };
|
|
46390
|
+
let text2;
|
|
46391
|
+
try {
|
|
46392
|
+
text2 = readFileSync53(path4, "utf8");
|
|
46393
|
+
} catch (error) {
|
|
46394
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
46395
|
+
return { state: "unreadable", reason };
|
|
46396
|
+
}
|
|
46397
|
+
try {
|
|
46398
|
+
return asBaseline(JSON.parse(text2));
|
|
46399
|
+
} catch {
|
|
46400
|
+
return { state: "unreadable", reason: "not JSON" };
|
|
46401
|
+
}
|
|
46402
|
+
}
|
|
46403
|
+
function failureKey2(failure6) {
|
|
46404
|
+
return JSON.stringify([failure6.file, failure6.name]);
|
|
46405
|
+
}
|
|
46406
|
+
function splitFailures(failures, baseline) {
|
|
46407
|
+
const held = new Set((baseline?.failures ?? []).map(failureKey2));
|
|
46408
|
+
return {
|
|
46409
|
+
fresh: failures.filter((failure6) => !held.has(failureKey2(failure6))),
|
|
46410
|
+
known: failures.filter((failure6) => held.has(failureKey2(failure6)))
|
|
46411
|
+
};
|
|
46412
|
+
}
|
|
46413
|
+
var BASELINE_VERSION = 1, BASELINE_FILE_PREFIX = "SUITE_BASELINE", PLAN_FILE_NAME2, JUNIT_READINGS;
|
|
46414
|
+
var init_baseline = __esm(() => {
|
|
46415
|
+
PLAN_FILE_NAME2 = /^PLAN(?:_TRACKER)?(-.+)?\.md$/;
|
|
46416
|
+
JUNIT_READINGS = ["read", "missing", "unreadable"];
|
|
46417
|
+
});
|
|
46418
|
+
|
|
46419
|
+
// src/triage/test-failure.ts
|
|
46420
|
+
function caseLineOf(line, index) {
|
|
46421
|
+
const fail = FAIL_LINE.exec(line);
|
|
46422
|
+
if (fail !== null)
|
|
46423
|
+
return { index, name: fail[1], file: null };
|
|
46424
|
+
const fileCase = FILE_CASE_LINE.exec(line);
|
|
46425
|
+
return fileCase === null ? null : { index, name: fileCase[2], file: fileCase[1] };
|
|
46426
|
+
}
|
|
46427
|
+
function linesOf3(text2) {
|
|
46428
|
+
return text2.split(/\r?\n/);
|
|
46429
|
+
}
|
|
46430
|
+
function caseLinesOf(lines2) {
|
|
46431
|
+
return lines2.map((line, index) => caseLineOf(line, index)).filter((found) => found !== null);
|
|
46432
|
+
}
|
|
46433
|
+
function testFilesIn(text2) {
|
|
46434
|
+
return text2.match(TEST_FILE_ANYWHERE) ?? [];
|
|
46435
|
+
}
|
|
46436
|
+
function firstEvidence(lines2) {
|
|
46437
|
+
const found = lines2.find((line) => caseLineOf(line, 0) === null && !NOISE_LINES.some((noise) => noise.test(line)));
|
|
46438
|
+
return found === undefined ? null : found.trim().replace(ERROR_PREFIX, "");
|
|
46439
|
+
}
|
|
46440
|
+
function testFileOf(found, artifactLines, what) {
|
|
46441
|
+
if (found.file !== null)
|
|
46442
|
+
return found.file;
|
|
46443
|
+
const above = artifactLines.slice(0, found.index);
|
|
46444
|
+
const header = above.map((line) => FILE_HEADER.exec(line)?.[1]).filter((file) => file !== undefined).at(-1);
|
|
46445
|
+
const before = testFilesIn(above.join(`
|
|
46446
|
+
`));
|
|
46447
|
+
const after = testFilesIn(artifactLines.slice(found.index).join(`
|
|
46448
|
+
`));
|
|
46449
|
+
return header ?? before.at(-1) ?? after[0] ?? testFilesIn(what)[0] ?? null;
|
|
46450
|
+
}
|
|
46451
|
+
function evidenceOf(found, artifactLines) {
|
|
46452
|
+
if (found === null)
|
|
46453
|
+
return firstEvidence(artifactLines);
|
|
46454
|
+
const cases = caseLinesOf(artifactLines);
|
|
46455
|
+
const previous = cases.filter((other) => other.index < found.index).at(-1);
|
|
46456
|
+
const next = cases.find((other) => other.index > found.index);
|
|
46457
|
+
const before = artifactLines.slice(previous === undefined ? 0 : previous.index + 1, found.index);
|
|
46458
|
+
const after = artifactLines.slice(found.index + 1, next === undefined ? artifactLines.length : next.index);
|
|
46459
|
+
return firstEvidence(before) ?? firstEvidence(after);
|
|
46460
|
+
}
|
|
46461
|
+
function testFailureOf(bug) {
|
|
46462
|
+
const artifactLines = linesOf3(bug.artifact ?? "");
|
|
46463
|
+
const what = bug.what ?? "";
|
|
46464
|
+
const inArtifact = caseLinesOf(artifactLines)[0];
|
|
46465
|
+
if (inArtifact !== undefined) {
|
|
46466
|
+
return {
|
|
46467
|
+
file: testFileOf(inArtifact, artifactLines, what),
|
|
46468
|
+
name: inArtifact.name,
|
|
46469
|
+
evidence: evidenceOf(inArtifact, artifactLines)
|
|
46470
|
+
};
|
|
46471
|
+
}
|
|
46472
|
+
const inWhat = caseLinesOf(linesOf3(what))[0];
|
|
46473
|
+
if (inWhat === undefined)
|
|
46474
|
+
return null;
|
|
46475
|
+
return {
|
|
46476
|
+
file: inWhat.file ?? testFilesIn(bug.artifact ?? "")[0] ?? testFilesIn(what)[0] ?? null,
|
|
46477
|
+
name: inWhat.name,
|
|
46478
|
+
evidence: evidenceOf(null, artifactLines)
|
|
46479
|
+
};
|
|
46480
|
+
}
|
|
46481
|
+
var TEST_FILE = "(?:/?(?:[\\w.@+-]+/)*)[\\w.@+-]+\\.(?:test|spec)\\.[cm]?[jt]sx?", TEST_FILE_ANYWHERE, FAIL_LINE, FILE_CASE_LINE, FILE_HEADER, NOISE_LINES, ERROR_PREFIX;
|
|
46482
|
+
var init_test_failure = __esm(() => {
|
|
46483
|
+
TEST_FILE_ANYWHERE = new RegExp(`(?<![\\w.@+/-])${TEST_FILE}(?![\\w])`, "g");
|
|
46484
|
+
FAIL_LINE = /^\s*\(fail\)\s+(.+?)(?:\s+\[\d+(?:\.\d+)?m?s\])?\s*$/;
|
|
46485
|
+
FILE_CASE_LINE = new RegExp(`^\\s*(${TEST_FILE})\\s+>\\s+(.+?)\\s*$`);
|
|
46486
|
+
FILE_HEADER = new RegExp(`^\\s*(${TEST_FILE}):\\s*$`);
|
|
46487
|
+
NOISE_LINES = [
|
|
46488
|
+
/^\s*$/,
|
|
46489
|
+
FILE_HEADER,
|
|
46490
|
+
/^\s*\d+\s+\|/,
|
|
46491
|
+
/^\s*\^+\s*$/,
|
|
46492
|
+
/^\s*at\s/,
|
|
46493
|
+
/^\s*bun test v/
|
|
46494
|
+
];
|
|
46495
|
+
ERROR_PREFIX = /^error:\s+/;
|
|
46496
|
+
});
|
|
46497
|
+
|
|
46498
|
+
// src/triage/bug-key.ts
|
|
46499
|
+
import { basename as basename19 } from "path";
|
|
46500
|
+
function hasText2(value) {
|
|
46501
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
46502
|
+
}
|
|
46503
|
+
function artifactOf(bug) {
|
|
46504
|
+
return hasText2(bug.artifact) && textProblem(bug.artifact) === null ? bug.artifact : null;
|
|
46505
|
+
}
|
|
46506
|
+
function oneLine9(text2) {
|
|
46507
|
+
return text2.replace(/\s+/g, " ").trim();
|
|
46508
|
+
}
|
|
46509
|
+
function strippedText(text2) {
|
|
46510
|
+
const taken = text2.replace(COMMIT_HASH, "").replace(FOLDER_PREFIX, "").replace(NUMBER, "");
|
|
46511
|
+
return oneLine9(taken);
|
|
46512
|
+
}
|
|
46513
|
+
function keyText(text2) {
|
|
46514
|
+
const stripped = strippedText(text2);
|
|
46515
|
+
return stripped.length > 0 ? stripped : oneLine9(text2);
|
|
46516
|
+
}
|
|
46517
|
+
function testFailureKeyOf(failure6) {
|
|
46518
|
+
const test = failure6.file === null ? oneLine9(failure6.name) : `${basename19(failure6.file)}${CASE_SEPARATOR}${oneLine9(failure6.name)}`;
|
|
46519
|
+
return failure6.evidence === null ? test : `${test}${KEY_SEPARATOR}${keyText(failure6.evidence)}`;
|
|
46520
|
+
}
|
|
46521
|
+
function bugKeyOf(trackerPath, artifact, what = null) {
|
|
46522
|
+
const failure6 = testFailureOf({ what, artifact });
|
|
46523
|
+
return failure6 === null ? `${basename19(trackerPath)}${KEY_SEPARATOR}${keyText(artifact)}` : testFailureKeyOf(failure6);
|
|
46524
|
+
}
|
|
46525
|
+
function legacyBugKeyOf(trackerPath, artifact) {
|
|
46526
|
+
return `${basename19(trackerPath)}${KEY_SEPARATOR}${oneLine9(artifact)}`;
|
|
46527
|
+
}
|
|
46528
|
+
var KEY_SEPARATOR = ": ", CASE_SEPARATOR = " > ", COMMIT_HASH, FOLDER_PREFIX, NUMBER;
|
|
46529
|
+
var init_bug_key = __esm(() => {
|
|
46530
|
+
init_findings();
|
|
46531
|
+
init_test_failure();
|
|
46532
|
+
COMMIT_HASH = /\b[0-9a-f]{7,40}\b/gi;
|
|
46533
|
+
FOLDER_PREFIX = /[^\s'"`()[\]{}<>,;|]*\//g;
|
|
46534
|
+
NUMBER = /\d+/g;
|
|
46535
|
+
});
|
|
46536
|
+
|
|
46537
|
+
// src/triage/inherited.ts
|
|
46538
|
+
import { basename as basename20 } from "path";
|
|
46539
|
+
function isOpenIssueState(state) {
|
|
46540
|
+
return !CLOSED_ISSUE_STATES.includes(state);
|
|
46541
|
+
}
|
|
46542
|
+
function sameFailure(failure6, baseline) {
|
|
46543
|
+
const { file, evidence } = failure6;
|
|
46544
|
+
if (file === null)
|
|
46545
|
+
return false;
|
|
46546
|
+
const sameTest = basename20(file) === basename20(baseline.file) && oneLine9(failure6.name) === oneLine9(baseline.name);
|
|
46547
|
+
const message = baseline.message ?? null;
|
|
46548
|
+
if (!sameTest || !hasText2(message))
|
|
46549
|
+
return sameTest;
|
|
46550
|
+
return evidence !== null && keyText(evidence).includes(keyText(message));
|
|
46551
|
+
}
|
|
46552
|
+
function inheritedFailureOf(bug, failures) {
|
|
46553
|
+
const failure6 = testFailureOf(bug);
|
|
46554
|
+
if (failure6 === null)
|
|
46555
|
+
return null;
|
|
46556
|
+
return failures.find((baseline) => sameFailure(failure6, baseline)) ?? null;
|
|
46557
|
+
}
|
|
46558
|
+
var CLOSED_ISSUE_STATES;
|
|
46559
|
+
var init_inherited = __esm(() => {
|
|
46560
|
+
init_bug_key();
|
|
46561
|
+
init_test_failure();
|
|
46562
|
+
CLOSED_ISSUE_STATES = ["done", "released", "cancelled"];
|
|
46563
|
+
});
|
|
46564
|
+
|
|
46565
|
+
// src/triage/issue-text.ts
|
|
46566
|
+
function fenced(value) {
|
|
46567
|
+
const runs = Array.from(value.matchAll(/`+/g), (run) => run[0].length);
|
|
46568
|
+
const fence = "`".repeat(Math.max(MIN_FENCE_LENGTH3, ...runs.map((length) => length + 1)));
|
|
46569
|
+
const body = value.endsWith(`
|
|
46570
|
+
`) ? value : `${value}
|
|
46571
|
+
`;
|
|
46572
|
+
return `${fence}
|
|
46573
|
+
${body}${fence}`;
|
|
46574
|
+
}
|
|
46575
|
+
function issueTitle2(redactedWhat) {
|
|
46576
|
+
const line = oneLine9(redactedWhat);
|
|
46577
|
+
const points = Array.from(line);
|
|
46578
|
+
if (points.length <= TITLE_MAX_LENGTH)
|
|
46579
|
+
return line;
|
|
46580
|
+
const kept = points.slice(0, TITLE_MAX_LENGTH - TITLE_CUT.length).join("");
|
|
46581
|
+
return `${kept.trimEnd()}${TITLE_CUT}`;
|
|
46582
|
+
}
|
|
46583
|
+
function scoreText(score) {
|
|
46584
|
+
return score.toFixed(2);
|
|
46585
|
+
}
|
|
46586
|
+
function similarCommentOpening(score, threshold) {
|
|
46587
|
+
return `${COMMENT_OPENING} No key found this issue: the report's words match its first report` + ` at a Jaccard similarity of ${scoreText(score)}, at or above the threshold of` + ` ${String(threshold)}, so it is taken for the same bug.`;
|
|
46588
|
+
}
|
|
46589
|
+
function possibleDuplicatesSection(duplicates2, redact) {
|
|
46590
|
+
const intro = `No open bug scored at or above the similarity threshold of ${String(duplicates2.threshold)}.`;
|
|
46591
|
+
const listed4 = duplicates2.nearest.length === 0 ? "No open bug shares a word with this one." : duplicates2.nearest.map(({ issue: issue4, score }) => `- ${issueNameOf(issue4.ref)} (score ${scoreText(score)})`).join(`
|
|
46592
|
+
`);
|
|
46593
|
+
return `## Possible duplicates
|
|
46594
|
+
|
|
46595
|
+
${redact(`${intro}
|
|
46596
|
+
|
|
46597
|
+
${listed4}`)}`;
|
|
46598
|
+
}
|
|
46599
|
+
function issueNameOf(ref14) {
|
|
46600
|
+
return ref14.url ?? `${ref14.kind} issue ${ref14.externalId}`;
|
|
46601
|
+
}
|
|
46602
|
+
function supersedesSection(closed, redact) {
|
|
46603
|
+
const sentence = `This bug came back after ${issueNameOf(closed)} was closed as completed;` + " this issue is filed in its place.";
|
|
46604
|
+
return `## Supersedes
|
|
46605
|
+
|
|
46606
|
+
${redact(sentence)}`;
|
|
46607
|
+
}
|
|
46608
|
+
function section2(heading, value, absent6, redact) {
|
|
46609
|
+
const shown2 = value === null ? absent6 : fenced(redact(value));
|
|
46610
|
+
return `## ${heading}
|
|
46611
|
+
|
|
46612
|
+
${shown2}`;
|
|
46613
|
+
}
|
|
46614
|
+
function issueText(opening, values, redact) {
|
|
46615
|
+
return [
|
|
46616
|
+
opening,
|
|
46617
|
+
...values.supersedes === undefined ? [] : [supersedesSection(values.supersedes, redact)],
|
|
46618
|
+
section2("What", values.what, "", redact),
|
|
46619
|
+
section2("Artifact", values.artifact, "The report gave no artifact, so a recurrence files again.", redact),
|
|
46620
|
+
...values.refs === null ? [] : [values.refs],
|
|
46621
|
+
section2("Recurrence key", values.key, "The report gave no artifact, so this bug has no key.", redact),
|
|
46622
|
+
section2("Plan", values.planStub, "The dispatch resolved no plan stub.", redact),
|
|
46623
|
+
section2("Task", values.taskText, "", redact),
|
|
46624
|
+
section2("Feedback", values.feedback, "The report gave no feedback.", redact),
|
|
46625
|
+
...values.duplicates === undefined ? [] : [possibleDuplicatesSection(values.duplicates, redact)]
|
|
46626
|
+
].join(`
|
|
46627
|
+
|
|
46628
|
+
`) + `
|
|
46629
|
+
`;
|
|
46630
|
+
}
|
|
46631
|
+
var TITLE_MAX_LENGTH = 120, TITLE_CUT = "...", MIN_FENCE_LENGTH3 = 3, ISSUE_OPENING, COMMENT_OPENING = "Reported again by a rafa task session.";
|
|
46632
|
+
var init_issue_text = __esm(() => {
|
|
46633
|
+
init_bug_key();
|
|
46634
|
+
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.";
|
|
46635
|
+
});
|
|
46636
|
+
|
|
46637
|
+
// src/triage/local-paths.ts
|
|
46638
|
+
import { realpathSync as realpathSync13 } from "fs";
|
|
46639
|
+
import { resolve as resolve29 } from "path";
|
|
46640
|
+
function literalPattern(text2) {
|
|
46641
|
+
return text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
46642
|
+
}
|
|
46643
|
+
function forms(path4) {
|
|
46644
|
+
const given = resolve29(path4);
|
|
46645
|
+
try {
|
|
46646
|
+
const real = realpathSync13(given);
|
|
46647
|
+
return real === given ? [given] : [given, real];
|
|
46648
|
+
} catch {
|
|
46649
|
+
return [given];
|
|
46650
|
+
}
|
|
46651
|
+
}
|
|
46652
|
+
function isRoot(path4) {
|
|
46653
|
+
return path4.length > 1;
|
|
46654
|
+
}
|
|
46655
|
+
function localPathRedactor(repoRoot, home) {
|
|
46656
|
+
const asHome = (pattern) => ({ pattern, under: `${HOME_MARKER}/`, bare: HOME_MARKER });
|
|
46657
|
+
const asRepo = (pattern) => ({ pattern, under: "", bare: "." });
|
|
46658
|
+
const literals = [
|
|
46659
|
+
...forms(repoRoot).filter(isRoot).map((root) => asRepo(literalPattern(root))),
|
|
46660
|
+
...forms(home).filter(isRoot).map((root) => asHome(literalPattern(root)))
|
|
46661
|
+
];
|
|
46662
|
+
const roots = [
|
|
46663
|
+
...[...literals].sort((a, b) => b.pattern.length - a.pattern.length),
|
|
46664
|
+
asHome(HOME_PATTERN)
|
|
46665
|
+
];
|
|
46666
|
+
const alternatives = roots.map(({ pattern }) => `(${pattern})(?:${UNDER}|${PATH_END})`);
|
|
46667
|
+
const matcher = new RegExp(`${PATH_START}(?:${alternatives.join("|")})`, "g");
|
|
46668
|
+
return (text2) => text2.replace(matcher, (...groups) => {
|
|
46669
|
+
const index = roots.findIndex((_, at) => groups[1 + at * 2] !== undefined);
|
|
46670
|
+
const root = roots[index];
|
|
46671
|
+
return groups[2 + index * 2] === undefined ? root.bare : root.under;
|
|
46672
|
+
});
|
|
46673
|
+
}
|
|
46674
|
+
var HOME_MARKER = "[redacted: HOME]", SEGMENT = "[^/\\s'\"`()<>:,;]+", HOME_PATTERN, PATH_START = "(?:(?<![\\w.\\-])|(?<=(?:^|\\s)-[A-Za-z]))", UNDER = "(/)(?=[^\\s'\"`()<>])", PATH_END = "(?![\\w\\-]|\\.[\\w.\\-/])";
|
|
46675
|
+
var init_local_paths = __esm(() => {
|
|
46676
|
+
HOME_PATTERN = `(?:/${SEGMENT})*?/(?:Users|home)/${SEGMENT}`;
|
|
46677
|
+
});
|
|
46678
|
+
|
|
46156
46679
|
// src/effort/store/tracker-refs.ts
|
|
46157
46680
|
import { randomUUID as randomUUID15 } from "crypto";
|
|
46158
|
-
import { existsSync as
|
|
46681
|
+
import { existsSync as existsSync49 } from "fs";
|
|
46159
46682
|
function refused3(call, reason) {
|
|
46160
46683
|
return new Error(`effort store: tracker ref ${call} ${reason}; ${REFUSAL_TAILS[call]}`);
|
|
46161
46684
|
}
|
|
@@ -46217,7 +46740,7 @@ function heldRefOf(path4, seq, text2) {
|
|
|
46217
46740
|
return stored;
|
|
46218
46741
|
throw new Error(`effort store: ${path4}: findings row ${seq} holds a tracker_ref ${stored};` + " no writer stores one");
|
|
46219
46742
|
}
|
|
46220
|
-
function placeRef(db, path4, write, wanted, insertValues) {
|
|
46743
|
+
function placeRef(db, path4, write, wanted, superseded, insertValues) {
|
|
46221
46744
|
const held = db.query(SELECT_HELD2).get(write.dispatch.sessionId, write.artifact);
|
|
46222
46745
|
if (held === null) {
|
|
46223
46746
|
db.query(INSERT_REF).run(...insertValues());
|
|
@@ -46228,14 +46751,27 @@ function placeRef(db, path4, write, wanted, insertValues) {
|
|
|
46228
46751
|
return ["attached", wanted.ref];
|
|
46229
46752
|
}
|
|
46230
46753
|
const kept = heldRefOf(path4, held.seq, held.tracker_ref);
|
|
46754
|
+
if (kept.json === superseded?.json)
|
|
46755
|
+
throw new SupersedeInSessionRefusal(path4, held.seq);
|
|
46231
46756
|
return kept.json === wanted.json ? ["held", kept.ref] : ["conflict", kept.ref];
|
|
46232
46757
|
}
|
|
46758
|
+
function supersededOf(write, wanted) {
|
|
46759
|
+
if (write.supersedes === undefined)
|
|
46760
|
+
return null;
|
|
46761
|
+
const superseded = storedRefOf(write.supersedes);
|
|
46762
|
+
if (typeof superseded === "string")
|
|
46763
|
+
throw refused3("write", `supersedes a reference ${superseded}`);
|
|
46764
|
+
if (superseded.json === wanted.json)
|
|
46765
|
+
throw refused3("write", "supersedes the reference it writes");
|
|
46766
|
+
return superseded;
|
|
46767
|
+
}
|
|
46233
46768
|
function writeTrackerRef(repoRoot, write, seams = {}) {
|
|
46234
46769
|
checkDispatch("tracker ref", write.dispatch, write.outcome);
|
|
46235
46770
|
checkArtifact2("write", write.artifact);
|
|
46236
46771
|
const wanted = storedRefOf(write.ref);
|
|
46237
46772
|
if (typeof wanted === "string")
|
|
46238
46773
|
throw refused3("write", `has a reference ${wanted}`);
|
|
46774
|
+
const superseded = supersededOf(write, wanted);
|
|
46239
46775
|
const path4 = sqliteStorePath(repoRoot);
|
|
46240
46776
|
const { sessionId, planStub: planStub2, taskLine: taskLine2 } = write.dispatch;
|
|
46241
46777
|
const collectedAt = (seams.now ?? (() => new Date))().toISOString();
|
|
@@ -46250,8 +46786,8 @@ function writeTrackerRef(repoRoot, write, seams = {}) {
|
|
|
46250
46786
|
wanted.json,
|
|
46251
46787
|
collectedAt
|
|
46252
46788
|
];
|
|
46253
|
-
const [action, stored] = withSqliteStore(path4, "write", !
|
|
46254
|
-
const place2 = db.transaction(() => placeRef(db, path4, write, wanted, insertValues));
|
|
46789
|
+
const [action, stored] = withSqliteStore(path4, "write", !existsSync49(path4), (db) => {
|
|
46790
|
+
const place2 = db.transaction(() => placeRef(db, path4, write, wanted, superseded, insertValues));
|
|
46255
46791
|
return place2.immediate();
|
|
46256
46792
|
});
|
|
46257
46793
|
return { path: path4, action, stored };
|
|
@@ -46259,20 +46795,15 @@ function writeTrackerRef(repoRoot, write, seams = {}) {
|
|
|
46259
46795
|
function readTrackerRef(repoRoot, artifact) {
|
|
46260
46796
|
checkArtifact2("read", artifact);
|
|
46261
46797
|
const path4 = sqliteStorePath(repoRoot);
|
|
46262
|
-
if (!
|
|
46798
|
+
if (!existsSync49(path4))
|
|
46263
46799
|
return null;
|
|
46264
|
-
const
|
|
46265
|
-
return
|
|
46800
|
+
const newest = withSqliteStore(path4, "read", false, (db) => db.query(SELECT_NEWEST).get(artifact));
|
|
46801
|
+
return newest === null ? null : heldRefOf(path4, newest.seq, newest.tracker_ref).ref;
|
|
46266
46802
|
}
|
|
46267
46803
|
var SELECT_HELD2 = `
|
|
46268
46804
|
SELECT seq, tracker_ref FROM findings
|
|
46269
46805
|
WHERE session_id = ? AND artifact = ?
|
|
46270
|
-
`, INSERT_REF, ATTACH_REF = "UPDATE findings SET tracker_ref = ? WHERE seq = ? AND tracker_ref IS NULL",
|
|
46271
|
-
SELECT seq, tracker_ref FROM findings
|
|
46272
|
-
WHERE artifact = ? AND tracker_ref IS NOT NULL
|
|
46273
|
-
ORDER BY seq
|
|
46274
|
-
LIMIT 1
|
|
46275
|
-
`, REFUSAL_TAILS, REF_FIELDS;
|
|
46806
|
+
`, INSERT_REF, ATTACH_REF = "UPDATE findings SET tracker_ref = ? WHERE seq = ? AND tracker_ref IS NULL", NEWEST_FIRST, SELECT_NEWEST, REFUSAL_TAILS, REF_FIELDS, SupersedeInSessionRefusal;
|
|
46276
46807
|
var init_tracker_refs = __esm(() => {
|
|
46277
46808
|
init_findings();
|
|
46278
46809
|
init_origins();
|
|
@@ -46285,6 +46816,13 @@ var init_tracker_refs = __esm(() => {
|
|
|
46285
46816
|
${STAMPED_COLUMNS}
|
|
46286
46817
|
)
|
|
46287
46818
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ${stampedValues("findings")})
|
|
46819
|
+
`;
|
|
46820
|
+
NEWEST_FIRST = ACROSS_STORES_ORDER.split(",").map((term) => `${term.trim()} DESC`).join(", ");
|
|
46821
|
+
SELECT_NEWEST = `
|
|
46822
|
+
SELECT seq, tracker_ref FROM findings
|
|
46823
|
+
WHERE artifact = ? AND tracker_ref IS NOT NULL
|
|
46824
|
+
ORDER BY ${NEWEST_FIRST}
|
|
46825
|
+
LIMIT 1
|
|
46288
46826
|
`;
|
|
46289
46827
|
REFUSAL_TAILS = { write: "nothing written", read: "nothing opened" };
|
|
46290
46828
|
REF_FIELDS = [
|
|
@@ -46295,48 +46833,16 @@ var init_tracker_refs = __esm(() => {
|
|
|
46295
46833
|
["module", optionalText3],
|
|
46296
46834
|
["repo", optionalText3]
|
|
46297
46835
|
];
|
|
46298
|
-
|
|
46299
|
-
|
|
46300
|
-
|
|
46301
|
-
|
|
46302
|
-
|
|
46303
|
-
|
|
46304
|
-
|
|
46305
|
-
|
|
46306
|
-
|
|
46307
|
-
|
|
46308
|
-
try {
|
|
46309
|
-
const real = realpathSync13(given);
|
|
46310
|
-
return real === given ? [given] : [given, real];
|
|
46311
|
-
} catch {
|
|
46312
|
-
return [given];
|
|
46313
|
-
}
|
|
46314
|
-
}
|
|
46315
|
-
function isRoot(path4) {
|
|
46316
|
-
return path4.length > 1;
|
|
46317
|
-
}
|
|
46318
|
-
function localPathRedactor(repoRoot, home) {
|
|
46319
|
-
const asHome = (pattern) => ({ pattern, under: `${HOME_MARKER}/`, bare: HOME_MARKER });
|
|
46320
|
-
const asRepo = (pattern) => ({ pattern, under: "", bare: "." });
|
|
46321
|
-
const literals = [
|
|
46322
|
-
...forms(repoRoot).filter(isRoot).map((root) => asRepo(literalPattern(root))),
|
|
46323
|
-
...forms(home).filter(isRoot).map((root) => asHome(literalPattern(root)))
|
|
46324
|
-
];
|
|
46325
|
-
const roots = [
|
|
46326
|
-
...[...literals].sort((a, b) => b.pattern.length - a.pattern.length),
|
|
46327
|
-
asHome(HOME_PATTERN)
|
|
46328
|
-
];
|
|
46329
|
-
const alternatives = roots.map(({ pattern }) => `(${pattern})(?:${UNDER}|${PATH_END})`);
|
|
46330
|
-
const matcher = new RegExp(`${PATH_START}(?:${alternatives.join("|")})`, "g");
|
|
46331
|
-
return (text2) => text2.replace(matcher, (...groups) => {
|
|
46332
|
-
const index = roots.findIndex((_, at) => groups[1 + at * 2] !== undefined);
|
|
46333
|
-
const root = roots[index];
|
|
46334
|
-
return groups[2 + index * 2] === undefined ? root.bare : root.under;
|
|
46335
|
-
});
|
|
46336
|
-
}
|
|
46337
|
-
var HOME_MARKER = "[redacted: HOME]", SEGMENT = "[^/\\s'\"`()<>:,;]+", HOME_PATTERN, PATH_START = "(?:(?<![\\w.\\-])|(?<=(?:^|\\s)-[A-Za-z]))", UNDER = "(/)(?=[^\\s'\"`()<>])", PATH_END = "(?![\\w\\-]|\\.[\\w.\\-/])";
|
|
46338
|
-
var init_local_paths = __esm(() => {
|
|
46339
|
-
HOME_PATTERN = `(?:/${SEGMENT})*?/(?:Users|home)/${SEGMENT}`;
|
|
46836
|
+
SupersedeInSessionRefusal = class SupersedeInSessionRefusal extends Error {
|
|
46837
|
+
path;
|
|
46838
|
+
seq;
|
|
46839
|
+
constructor(path4, seq) {
|
|
46840
|
+
super(`effort store: ${path4}: tracker ref write supersedes the reference findings row ${seq} holds` + " for this session and artifact, and findings_by_artifact holds one row per session and" + " artifact; nothing written");
|
|
46841
|
+
this.name = "SupersedeInSessionRefusal";
|
|
46842
|
+
this.path = path4;
|
|
46843
|
+
this.seq = seq;
|
|
46844
|
+
}
|
|
46845
|
+
};
|
|
46340
46846
|
});
|
|
46341
46847
|
|
|
46342
46848
|
// src/triage/refs-section.ts
|
|
@@ -46406,11 +46912,77 @@ var init_refs_section = __esm(() => {
|
|
|
46406
46912
|
};
|
|
46407
46913
|
});
|
|
46408
46914
|
|
|
46915
|
+
// src/triage/similarity.ts
|
|
46916
|
+
import { basename as basename21 } from "path";
|
|
46917
|
+
function wordSetOf(text2) {
|
|
46918
|
+
const words = strippedText(text2).toLowerCase().split(NON_WORD2).filter((word) => word.length > 0);
|
|
46919
|
+
return new Set(words);
|
|
46920
|
+
}
|
|
46921
|
+
function reportWordsOf(report2) {
|
|
46922
|
+
return wordSetOf(`${report2.what ?? ""}
|
|
46923
|
+
${report2.artifact ?? ""}`);
|
|
46924
|
+
}
|
|
46925
|
+
function jaccard(a, b) {
|
|
46926
|
+
const shared = [...a].filter((word) => b.has(word)).length;
|
|
46927
|
+
const all = a.size + b.size - shared;
|
|
46928
|
+
return all === 0 ? 0 : shared / all;
|
|
46929
|
+
}
|
|
46930
|
+
function fencedValueAt(lines2, start) {
|
|
46931
|
+
const open = lines2.slice(start).findIndex((line) => line.trim().length > 0);
|
|
46932
|
+
if (open === -1)
|
|
46933
|
+
return null;
|
|
46934
|
+
const fence = lines2[start + open];
|
|
46935
|
+
if (!FENCE_LINE6.test(fence))
|
|
46936
|
+
return null;
|
|
46937
|
+
const body = lines2.slice(start + open + 1);
|
|
46938
|
+
const close = body.indexOf(fence);
|
|
46939
|
+
return close === -1 ? null : body.slice(0, close).join(`
|
|
46940
|
+
`);
|
|
46941
|
+
}
|
|
46942
|
+
function sectionValueOf(body, heading) {
|
|
46943
|
+
const lines2 = body.split(/\r?\n/);
|
|
46944
|
+
const at = lines2.findIndex((line) => HEADING_LINE3.exec(line)?.[1]?.trim() === heading);
|
|
46945
|
+
return at === -1 ? null : fencedValueAt(lines2, at + 1);
|
|
46946
|
+
}
|
|
46947
|
+
function issueTextOf(body) {
|
|
46948
|
+
return {
|
|
46949
|
+
what: sectionValueOf(body, "What"),
|
|
46950
|
+
artifact: sectionValueOf(body, "Artifact")
|
|
46951
|
+
};
|
|
46952
|
+
}
|
|
46953
|
+
function testFileOf2(text2) {
|
|
46954
|
+
const file = testFailureOf(text2)?.file ?? testFilesIn(text2.artifact ?? "")[0] ?? testFilesIn(text2.what ?? "")[0] ?? null;
|
|
46955
|
+
return file === null ? null : basename21(file);
|
|
46956
|
+
}
|
|
46957
|
+
function namesOtherTestFile(reportFile, issueFile) {
|
|
46958
|
+
return reportFile !== null && issueFile !== null && reportFile !== issueFile;
|
|
46959
|
+
}
|
|
46960
|
+
function nearestOpenBugs(report2, openBugs) {
|
|
46961
|
+
const words = reportWordsOf(report2);
|
|
46962
|
+
const reportFile = testFileOf2(report2);
|
|
46963
|
+
const scored = openBugs.flatMap((issue4) => {
|
|
46964
|
+
const text2 = issueTextOf(issue4.body);
|
|
46965
|
+
if (namesOtherTestFile(reportFile, testFileOf2(text2)))
|
|
46966
|
+
return [];
|
|
46967
|
+
const score = jaccard(words, reportWordsOf(text2));
|
|
46968
|
+
return score > 0 ? [{ issue: issue4, score }] : [];
|
|
46969
|
+
});
|
|
46970
|
+
return [...scored].sort((a, b) => b.score - a.score);
|
|
46971
|
+
}
|
|
46972
|
+
var NON_WORD2, FENCE_LINE6, HEADING_LINE3;
|
|
46973
|
+
var init_similarity = __esm(() => {
|
|
46974
|
+
init_bug_key();
|
|
46975
|
+
init_test_failure();
|
|
46976
|
+
NON_WORD2 = /\W+/;
|
|
46977
|
+
FENCE_LINE6 = /^`{3,}$/;
|
|
46978
|
+
HEADING_LINE3 = /^## (.+)$/;
|
|
46979
|
+
});
|
|
46980
|
+
|
|
46409
46981
|
// src/triage/triage.ts
|
|
46410
46982
|
import { homedir as homedir9 } from "os";
|
|
46411
|
-
import {
|
|
46983
|
+
import { join as join72 } from "path";
|
|
46412
46984
|
function privateTriageDir(repoRoot) {
|
|
46413
|
-
return
|
|
46985
|
+
return join72(repoRoot, PRIVATE_TRIAGE_DIR);
|
|
46414
46986
|
}
|
|
46415
46987
|
function createPrivateTriageTracker(repoRoot, options = {}) {
|
|
46416
46988
|
return createLocalTracker({
|
|
@@ -46445,9 +47017,6 @@ function redactSecrets(text2, secrets) {
|
|
|
46445
47017
|
const pattern = new RegExp(values.map(literalPattern2).join("|"), "g");
|
|
46446
47018
|
return text2.replace(pattern, (value) => `[redacted: ${nameByValue.get(value)}]`);
|
|
46447
47019
|
}
|
|
46448
|
-
function hasText2(value) {
|
|
46449
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
46450
|
-
}
|
|
46451
47020
|
function blockerTextOf(blockers) {
|
|
46452
47021
|
const texts = blockers.map((blocker) => blocker.what).filter(hasText2);
|
|
46453
47022
|
return texts.length === 0 ? null : texts.join(BLOCKER_SEPARATOR);
|
|
@@ -46466,57 +47035,11 @@ function triageBlockers(options) {
|
|
|
46466
47035
|
return { text: text2, written: false, problem: `the blocker was not written: ${messageOf(error)}` };
|
|
46467
47036
|
}
|
|
46468
47037
|
}
|
|
46469
|
-
function artifactOf(bug) {
|
|
46470
|
-
return hasText2(bug.artifact) && textProblem(bug.artifact) === null ? bug.artifact : null;
|
|
46471
|
-
}
|
|
46472
|
-
function oneLine8(text2) {
|
|
46473
|
-
return text2.replace(/\s+/g, " ").trim();
|
|
46474
|
-
}
|
|
46475
|
-
function bugKeyOf(trackerPath, artifact) {
|
|
46476
|
-
return `${basename17(trackerPath)}${KEY_SEPARATOR}${oneLine8(artifact)}`;
|
|
46477
|
-
}
|
|
46478
|
-
function fenced(value) {
|
|
46479
|
-
const runs = Array.from(value.matchAll(/`+/g), (run) => run[0].length);
|
|
46480
|
-
const fence = "`".repeat(Math.max(MIN_FENCE_LENGTH3, ...runs.map((length) => length + 1)));
|
|
46481
|
-
const body = value.endsWith(`
|
|
46482
|
-
`) ? value : `${value}
|
|
46483
|
-
`;
|
|
46484
|
-
return `${fence}
|
|
46485
|
-
${body}${fence}`;
|
|
46486
|
-
}
|
|
46487
|
-
function issueTitle2(redactedWhat) {
|
|
46488
|
-
const line = oneLine8(redactedWhat);
|
|
46489
|
-
const points = Array.from(line);
|
|
46490
|
-
if (points.length <= TITLE_MAX_LENGTH)
|
|
46491
|
-
return line;
|
|
46492
|
-
const kept = points.slice(0, TITLE_MAX_LENGTH - TITLE_CUT.length).join("");
|
|
46493
|
-
return `${kept.trimEnd()}${TITLE_CUT}`;
|
|
46494
|
-
}
|
|
46495
|
-
function section2(heading, value, absent6, redact) {
|
|
46496
|
-
const shown2 = value === null ? absent6 : fenced(redact(value));
|
|
46497
|
-
return `## ${heading}
|
|
46498
|
-
|
|
46499
|
-
${shown2}`;
|
|
46500
|
-
}
|
|
46501
|
-
function issueText(opening, values, redact) {
|
|
46502
|
-
return [
|
|
46503
|
-
opening,
|
|
46504
|
-
section2("What", values.what, "", redact),
|
|
46505
|
-
section2("Artifact", values.artifact, "The report gave no artifact, so a recurrence files again.", redact),
|
|
46506
|
-
...values.refs === null ? [] : [values.refs],
|
|
46507
|
-
section2("Recurrence key", values.key, "The report gave no artifact, so this bug has no key.", redact),
|
|
46508
|
-
section2("Plan", values.planStub, "The dispatch resolved no plan stub.", redact),
|
|
46509
|
-
section2("Task", values.taskText, "", redact),
|
|
46510
|
-
section2("Feedback", values.feedback, "The report gave no feedback.", redact)
|
|
46511
|
-
].join(`
|
|
46512
|
-
|
|
46513
|
-
`) + `
|
|
46514
|
-
`;
|
|
46515
|
-
}
|
|
46516
47038
|
async function filingFor(what, bug, options, local, redact, verifyRefs) {
|
|
46517
47039
|
const reported2 = artifactOf(bug);
|
|
46518
47040
|
const artifact = reported2 === null ? null : local(reported2);
|
|
46519
|
-
const searchText = artifact === null ? null : bugKeyOf(options.trackerPath, redact(artifact));
|
|
47041
|
+
const searchText = artifact === null ? null : bugKeyOf(options.trackerPath, redact(artifact), redact(what));
|
|
47042
|
+
const key = artifact === null ? null : bugKeyOf(options.trackerPath, artifact, local(what));
|
|
46520
47043
|
const refs2 = await buildRefsSection(artifact === null ? null : redact(artifact), verifyRefs);
|
|
46521
47044
|
const values = {
|
|
46522
47045
|
what,
|
|
@@ -46540,10 +47063,23 @@ async function filingFor(what, bug, options, local, redact, verifyRefs) {
|
|
|
46540
47063
|
},
|
|
46541
47064
|
comment: issueText(COMMENT_OPENING, values, redact),
|
|
46542
47065
|
searchText,
|
|
46543
|
-
key
|
|
46544
|
-
|
|
47066
|
+
key,
|
|
47067
|
+
legacyKeys: legacyKeysOf(options.trackerPath, key, reported2, artifact),
|
|
47068
|
+
values,
|
|
47069
|
+
compared: { what: local(what), artifact }
|
|
46545
47070
|
};
|
|
46546
47071
|
}
|
|
47072
|
+
function legacyKeysOf(trackerPath, key, reported2, artifact) {
|
|
47073
|
+
if (key === null || reported2 === null || artifact === null)
|
|
47074
|
+
return [];
|
|
47075
|
+
const keys = [legacyBugKeyOf(trackerPath, artifact), legacyBugKeyOf(trackerPath, reported2)];
|
|
47076
|
+
return [...new Set(keys)].filter((legacy) => legacy !== key);
|
|
47077
|
+
}
|
|
47078
|
+
function similarityStepOf(options) {
|
|
47079
|
+
const threshold = options.similarity?.triageSimilarityThreshold;
|
|
47080
|
+
const candidates = options.similarity?.triageSimilarityCandidates;
|
|
47081
|
+
return typeof threshold === "number" && typeof candidates === "number" ? { threshold, candidates } : null;
|
|
47082
|
+
}
|
|
46547
47083
|
async function step(run, name2, action) {
|
|
46548
47084
|
try {
|
|
46549
47085
|
return { ok: true, value: await action() };
|
|
@@ -46563,47 +47099,109 @@ function resultOf5(run, action, fields = {}) {
|
|
|
46563
47099
|
...fields
|
|
46564
47100
|
};
|
|
46565
47101
|
}
|
|
46566
|
-
async function settle2(run, action, ref14, foundBy) {
|
|
47102
|
+
async function settle2(run, action, ref14, foundBy, supersedes) {
|
|
46567
47103
|
const { store } = run.route;
|
|
46568
47104
|
const { key } = run.filing;
|
|
46569
47105
|
if (store === null || key === null)
|
|
46570
47106
|
return resultOf5(run, action, { ref: ref14, foundBy });
|
|
46571
|
-
const stored = await step(run, "storing the reference", () => store(key, ref14));
|
|
47107
|
+
const stored = await step(run, "storing the reference", () => store(key, ref14, supersedes));
|
|
46572
47108
|
return stored.ok ? resultOf5(run, action, { ref: ref14, foundBy, stored: stored.value }) : resultOf5(run, action, { ref: ref14, foundBy, problem: stored.problem });
|
|
46573
47109
|
}
|
|
46574
|
-
async function fileNew(run) {
|
|
46575
|
-
const
|
|
46576
|
-
|
|
47110
|
+
async function fileNew(run, extras = {}) {
|
|
47111
|
+
const { filing } = run;
|
|
47112
|
+
const draft = extras.supersedes === undefined && extras.duplicates === undefined ? filing.draft : { ...filing.draft, body: issueText(ISSUE_OPENING, { ...filing.values, ...extras }, run.redact) };
|
|
47113
|
+
const created = await step(run, `the ${run.route.channel} tracker create`, () => run.route.tracker.create(draft));
|
|
47114
|
+
return created.ok ? settle2(run, "filed", created.value, null, extras.supersedes) : resultOf5(run, "failed", { problem: created.problem });
|
|
46577
47115
|
}
|
|
46578
|
-
async function commentOn2(run, ref14, foundBy) {
|
|
46579
|
-
const commented = await step(run, `the ${run.route.channel} tracker comment`, () => run.route.tracker.comment(ref14,
|
|
47116
|
+
async function commentOn2(run, ref14, foundBy, action = "commented", comment = run.filing.comment) {
|
|
47117
|
+
const commented = await step(run, `the ${run.route.channel} tracker comment`, () => run.route.tracker.comment(ref14, comment));
|
|
46580
47118
|
if (!commented.ok)
|
|
46581
47119
|
return resultOf5(run, "failed", { ref: ref14, foundBy, problem: commented.problem });
|
|
46582
|
-
return foundBy === "store" ? resultOf5(run,
|
|
47120
|
+
return foundBy === "store" ? resultOf5(run, action, { ref: ref14, foundBy }) : settle2(run, action, ref14, foundBy);
|
|
46583
47121
|
}
|
|
46584
|
-
async function
|
|
47122
|
+
async function lookUp2(run, key, searchText) {
|
|
46585
47123
|
const { route, filing } = run;
|
|
46586
|
-
const { key, searchText } = filing;
|
|
46587
|
-
if (key === null || searchText === null)
|
|
46588
|
-
return fileNew(run);
|
|
46589
47124
|
const { readStored } = route;
|
|
46590
47125
|
if (readStored !== null) {
|
|
46591
|
-
for (const storedKey of [key, filing.
|
|
46592
|
-
if (storedKey === null)
|
|
46593
|
-
continue;
|
|
47126
|
+
for (const storedKey of [key, ...filing.legacyKeys]) {
|
|
46594
47127
|
const stored = await step(run, "reading the stored reference", () => readStored(storedKey));
|
|
46595
47128
|
if (!stored.ok)
|
|
46596
|
-
return
|
|
47129
|
+
return stored;
|
|
46597
47130
|
if (stored.value !== null && stored.value.kind === route.tracker.kind) {
|
|
46598
|
-
return
|
|
47131
|
+
return { ok: true, value: { ref: stored.value, foundBy: "store" } };
|
|
46599
47132
|
}
|
|
46600
47133
|
}
|
|
46601
47134
|
}
|
|
46602
47135
|
const found = await step(run, `the ${route.channel} tracker find`, () => route.tracker.find({ text: searchText, type: "bug" }));
|
|
46603
47136
|
if (!found.ok)
|
|
46604
|
-
return
|
|
47137
|
+
return found;
|
|
46605
47138
|
const [match] = found.value;
|
|
46606
|
-
return
|
|
47139
|
+
return {
|
|
47140
|
+
ok: true,
|
|
47141
|
+
value: match === undefined ? null : { ref: match, foundBy: "find" }
|
|
47142
|
+
};
|
|
47143
|
+
}
|
|
47144
|
+
async function answerRecurrence(run, { ref: ref14, foundBy }) {
|
|
47145
|
+
if (!run.route.readsState)
|
|
47146
|
+
return commentOn2(run, ref14, foundBy);
|
|
47147
|
+
const issue4 = await step(run, `the ${run.route.channel} tracker get`, () => run.route.tracker.get(ref14));
|
|
47148
|
+
if (!issue4.ok)
|
|
47149
|
+
return resultOf5(run, "failed", { ref: ref14, foundBy, problem: issue4.problem });
|
|
47150
|
+
return COMPLETED_ISSUE_STATES.includes(issue4.value.state) ? fileNew(run, { supersedes: ref14 }) : commentOn2(run, ref14, foundBy);
|
|
47151
|
+
}
|
|
47152
|
+
async function commentOnNearest(run, nearest, threshold) {
|
|
47153
|
+
const opening = similarCommentOpening(nearest.score, threshold);
|
|
47154
|
+
const comment = issueText(opening, run.filing.values, run.redact);
|
|
47155
|
+
const result2 = await commentOn2(run, nearest.issue.ref, "similarity", "commented", comment);
|
|
47156
|
+
return { ...result2, score: nearest.score };
|
|
47157
|
+
}
|
|
47158
|
+
async function answerMiss(run) {
|
|
47159
|
+
const { tracker, similarity } = run.route;
|
|
47160
|
+
const list = tracker.openIssues;
|
|
47161
|
+
if (similarity === null || list === undefined)
|
|
47162
|
+
return fileNew(run);
|
|
47163
|
+
const listed4 = await step(run, `the ${run.route.channel} tracker openIssues`, () => list.call(tracker, "bug"));
|
|
47164
|
+
if (!listed4.ok)
|
|
47165
|
+
return resultOf5(run, "failed", { problem: listed4.problem });
|
|
47166
|
+
const nearest = nearestOpenBugs(run.filing.compared, listed4.value);
|
|
47167
|
+
const [first] = nearest;
|
|
47168
|
+
return first !== undefined && first.score >= similarity.threshold ? commentOnNearest(run, first, similarity.threshold) : fileNew(run, { duplicates: { threshold: similarity.threshold, nearest: nearest.slice(0, similarity.candidates) } });
|
|
47169
|
+
}
|
|
47170
|
+
async function triageBug(run) {
|
|
47171
|
+
const { key, searchText } = run.filing;
|
|
47172
|
+
if (key === null || searchText === null)
|
|
47173
|
+
return fileNew(run);
|
|
47174
|
+
const found = await lookUp2(run, key, searchText);
|
|
47175
|
+
if (!found.ok)
|
|
47176
|
+
return resultOf5(run, "failed", { problem: found.problem });
|
|
47177
|
+
return found.value === null ? answerMiss(run) : answerRecurrence(run, found.value);
|
|
47178
|
+
}
|
|
47179
|
+
async function triageInherited(run, commented) {
|
|
47180
|
+
const { key, searchText } = run.filing;
|
|
47181
|
+
if (key === null || searchText === null || commented.has(key))
|
|
47182
|
+
return resultOf5(run, "inherited");
|
|
47183
|
+
const found = await lookUp2(run, key, searchText);
|
|
47184
|
+
if (!found.ok)
|
|
47185
|
+
return resultOf5(run, "failed", { problem: found.problem });
|
|
47186
|
+
if (found.value === null)
|
|
47187
|
+
return resultOf5(run, "inherited");
|
|
47188
|
+
const { ref: ref14, foundBy } = found.value;
|
|
47189
|
+
const issue4 = await step(run, `the ${run.route.channel} tracker get`, () => run.route.tracker.get(ref14));
|
|
47190
|
+
if (!issue4.ok)
|
|
47191
|
+
return resultOf5(run, "failed", { ref: ref14, foundBy, problem: issue4.problem });
|
|
47192
|
+
if (!isOpenIssueState(issue4.value.state))
|
|
47193
|
+
return resultOf5(run, "inherited");
|
|
47194
|
+
const result2 = await commentOn2(run, ref14, foundBy, "inherited");
|
|
47195
|
+
if (result2.action === "inherited")
|
|
47196
|
+
commented.add(key);
|
|
47197
|
+
return result2;
|
|
47198
|
+
}
|
|
47199
|
+
function localBug(bug, what, local) {
|
|
47200
|
+
const artifact = artifactOf(bug);
|
|
47201
|
+
return {
|
|
47202
|
+
what: local(what),
|
|
47203
|
+
artifact: artifact === null ? null : local(artifact)
|
|
47204
|
+
};
|
|
46607
47205
|
}
|
|
46608
47206
|
function skippedRow(index, channel, problem) {
|
|
46609
47207
|
return { index, channel, action: "skipped", ref: null, foundBy: null, stored: null, problem };
|
|
@@ -46632,14 +47230,24 @@ async function triageReport(options) {
|
|
|
46632
47230
|
channel: "public",
|
|
46633
47231
|
tracker: options.tracker,
|
|
46634
47232
|
readStored: (key) => readTrackerRef(repoRoot, key),
|
|
46635
|
-
store: (key, ref14) => writeTrackerRef(repoRoot, {
|
|
47233
|
+
store: (key, ref14, supersedes) => writeTrackerRef(repoRoot, {
|
|
46636
47234
|
dispatch: options.dispatch,
|
|
46637
47235
|
outcome: options.outcome,
|
|
46638
47236
|
artifact: key,
|
|
46639
|
-
ref: ref14
|
|
46640
|
-
|
|
47237
|
+
ref: ref14,
|
|
47238
|
+
...supersedes === undefined ? {} : { supersedes }
|
|
47239
|
+
}, options.seams).action,
|
|
47240
|
+
readsState: true,
|
|
47241
|
+
similarity: similarityStepOf(options)
|
|
46641
47242
|
},
|
|
46642
|
-
private: {
|
|
47243
|
+
private: {
|
|
47244
|
+
channel: "private",
|
|
47245
|
+
tracker: privateTracker,
|
|
47246
|
+
readStored: null,
|
|
47247
|
+
store: null,
|
|
47248
|
+
readsState: false,
|
|
47249
|
+
similarity: null
|
|
47250
|
+
}
|
|
46643
47251
|
};
|
|
46644
47252
|
const bugs = [];
|
|
46645
47253
|
for (const [index, bug] of options.report.outOfScopeBugs.entries()) {
|
|
@@ -46655,26 +47263,97 @@ async function triageReport(options) {
|
|
|
46655
47263
|
continue;
|
|
46656
47264
|
}
|
|
46657
47265
|
const filing = await filingFor(what, bug, options, local, redact, verifyRefs);
|
|
46658
|
-
|
|
47266
|
+
const run = { route, index, filing, redact };
|
|
47267
|
+
const { inherited } = options;
|
|
47268
|
+
const isInherited = route.channel === "public" && inherited !== undefined && inheritedFailureOf(localBug(bug, what, local), inherited.failures) !== null;
|
|
47269
|
+
bugs.push(isInherited ? await triageInherited(run, inherited.commented) : await triageBug(run));
|
|
46659
47270
|
}
|
|
46660
47271
|
return { blocker, bugs };
|
|
46661
47272
|
}
|
|
46662
|
-
var PRIVATE_TRIAGE_DIR, SECRET_ENV_NAMES, TRIAGE_MODULE = "unassigned", BLOCKER_SEPARATOR = "; ",
|
|
47273
|
+
var PRIVATE_TRIAGE_DIR, SECRET_ENV_NAMES, TRIAGE_MODULE = "unassigned", BLOCKER_SEPARATOR = "; ", COMPLETED_ISSUE_STATES;
|
|
46663
47274
|
var init_triage2 = __esm(() => {
|
|
46664
47275
|
init_local2();
|
|
46665
47276
|
init_config_sections();
|
|
46666
|
-
init_findings();
|
|
46667
47277
|
init_tracker_refs();
|
|
46668
47278
|
init_tracker();
|
|
47279
|
+
init_bug_key();
|
|
47280
|
+
init_inherited();
|
|
47281
|
+
init_issue_text();
|
|
46669
47282
|
init_local_paths();
|
|
46670
47283
|
init_machine_fault();
|
|
46671
47284
|
init_refs_section();
|
|
46672
|
-
|
|
47285
|
+
init_similarity();
|
|
47286
|
+
init_bug_key();
|
|
47287
|
+
init_issue_text();
|
|
47288
|
+
PRIVATE_TRIAGE_DIR = join72(".rafa", "triage", "private");
|
|
46673
47289
|
SECRET_ENV_NAMES = ["GITHUB_TOKEN", "ANTHROPIC_API_KEY", "LINEAR_API_KEY"];
|
|
46674
|
-
|
|
47290
|
+
COMPLETED_ISSUE_STATES = ["done", "released"];
|
|
47291
|
+
});
|
|
47292
|
+
|
|
47293
|
+
// src/start/loop-events.ts
|
|
47294
|
+
import { join as join73 } from "path";
|
|
47295
|
+
function quoted3(text2) {
|
|
47296
|
+
return `"${oneLine(text2).replaceAll('"', "\\\"")}"`;
|
|
47297
|
+
}
|
|
47298
|
+
function taskKind(position, verb) {
|
|
47299
|
+
return padKind(`task ${position.index}/${position.total} ${verb}`);
|
|
47300
|
+
}
|
|
47301
|
+
function minutes(durationMs) {
|
|
47302
|
+
return `${Math.max(1, Math.round(durationMs / MINUTE_MS))}m`;
|
|
47303
|
+
}
|
|
47304
|
+
function summaryOf3(event) {
|
|
47305
|
+
switch (event.kind) {
|
|
47306
|
+
case "task-start":
|
|
47307
|
+
return `${taskKind(event.position, "start")}${quoted3(event.text)}`;
|
|
47308
|
+
case "task-done":
|
|
47309
|
+
return event.tokens === null ? `${taskKind(event.position, "done")}${minutes(event.durationMs)}` : `${taskKind(event.position, "done")}${minutes(event.durationMs)} ${Math.round(event.tokens / TOKENS_PER_K)}k tokens`;
|
|
47310
|
+
case "task-blocked":
|
|
47311
|
+
return `${taskKind(event.position, "blocked")}${oneLine(event.reason)}`;
|
|
47312
|
+
case "wrap-up":
|
|
47313
|
+
return `${padKind("wrap-up")}${event.phase}`;
|
|
47314
|
+
case "pr":
|
|
47315
|
+
return `pr #${event.number} opened`;
|
|
47316
|
+
case "no-pr":
|
|
47317
|
+
return `${padKind("no pr")}${oneLine(event.reason)}`;
|
|
47318
|
+
case "halt":
|
|
47319
|
+
return `${padKind("halt")}${oneLine(event.reason)}`;
|
|
47320
|
+
case "inherited":
|
|
47321
|
+
return `${padKind("inherited")}${oneLine(`${event.file} > ${event.name}`)}`;
|
|
47322
|
+
}
|
|
47323
|
+
}
|
|
47324
|
+
function emitLoopEvent(event, now = () => new Date) {
|
|
47325
|
+
const { kind, ...data } = event;
|
|
47326
|
+
activeOutput().emit({ type: "event", name: kind, summary: summaryOf3(event), data, ts: now().toISOString() });
|
|
47327
|
+
}
|
|
47328
|
+
async function unlessText(read) {
|
|
47329
|
+
return activeOutputMode() === "text" ? null : read();
|
|
47330
|
+
}
|
|
47331
|
+
function taskPosition(trackerContent, lineNum) {
|
|
47332
|
+
const { tasks } = parsePlan(trackerContent);
|
|
47333
|
+
return { index: tasks.findIndex((task) => task.lineNum === lineNum) + 1, total: tasks.length };
|
|
47334
|
+
}
|
|
47335
|
+
async function taskTokens(checkout, sessionId, home) {
|
|
47336
|
+
try {
|
|
47337
|
+
const { usage } = await readSessionLog(join73(sessionLogDir(checkout, home), `${sessionId}.jsonl`));
|
|
47338
|
+
return usage.inputTokens + usage.cacheCreationInputTokens + usage.outputTokens;
|
|
47339
|
+
} catch {
|
|
47340
|
+
return null;
|
|
47341
|
+
}
|
|
47342
|
+
}
|
|
47343
|
+
var MINUTE_MS = 60000, TOKENS_PER_K = 1000;
|
|
47344
|
+
var init_loop_events = __esm(() => {
|
|
47345
|
+
init_active();
|
|
47346
|
+
init_collect();
|
|
47347
|
+
init_session_log();
|
|
47348
|
+
init_plan();
|
|
46675
47349
|
});
|
|
46676
47350
|
|
|
46677
47351
|
// src/start/triage.ts
|
|
47352
|
+
import { homedir as homedir10 } from "os";
|
|
47353
|
+
function foundByText(bug) {
|
|
47354
|
+
const found = FOUND_BY[bug.foundBy ?? "find"];
|
|
47355
|
+
return bug.score === undefined ? found : `${found} (score ${scoreText(bug.score)})`;
|
|
47356
|
+
}
|
|
46678
47357
|
function unresolvedTracker(kind, reason) {
|
|
46679
47358
|
const refuse13 = () => Promise.reject(new Error(reason));
|
|
46680
47359
|
return Object.freeze({
|
|
@@ -46697,7 +47376,15 @@ function hasTriage(report2) {
|
|
|
46697
47376
|
function issueName2(ref14) {
|
|
46698
47377
|
return ref14.url ?? `${ref14.kind} issue ${ref14.externalId}`;
|
|
46699
47378
|
}
|
|
46700
|
-
function
|
|
47379
|
+
function failureName(failure6) {
|
|
47380
|
+
return `${failure6.file} > ${failure6.name}`;
|
|
47381
|
+
}
|
|
47382
|
+
function inheritedNote(bug, name2, where, failure6) {
|
|
47383
|
+
const test = failure6 === undefined ? "a red test the run started with" : `the run-start failure ${failureName(failure6)}`;
|
|
47384
|
+
const comment = bug.ref === null ? "" : `, and ${issueName2(bug.ref)} on ${where}, found by ${foundByText(bug)}, was commented on`;
|
|
47385
|
+
return `${name2}: inherited: ${test}; nothing was filed${comment}.`;
|
|
47386
|
+
}
|
|
47387
|
+
function bugLines(bug, inherited) {
|
|
46701
47388
|
const name2 = `out_of_scope_bugs[${bug.index}]`;
|
|
46702
47389
|
const where = `the ${bug.channel} tracker`;
|
|
46703
47390
|
if (bug.action === "skipped") {
|
|
@@ -46708,16 +47395,17 @@ function bugLines(bug) {
|
|
|
46708
47395
|
return { notes: [], warnings: [warning] };
|
|
46709
47396
|
}
|
|
46710
47397
|
const issue4 = bug.ref === null ? "an issue" : issueName2(bug.ref);
|
|
46711
|
-
const
|
|
47398
|
+
const reached = bug.action === "filed" ? `${name2}: filed on ${where} as ${issue4}.` : `${name2}: recurs in ${issue4} on ${where}, found by ${foundByText(bug)}; commented on it.`;
|
|
47399
|
+
const note = bug.action === "inherited" ? inheritedNote(bug, name2, where, inherited) : reached;
|
|
46712
47400
|
const problem = bug.problem === null ? [] : [`${name2}: ${bug.problem}`];
|
|
46713
47401
|
const conflict = bug.stored === "conflict" ? [`${name2}: the store keeps another reference for its artifact, in place of ${issue4}.`] : [];
|
|
46714
47402
|
return { notes: [note], warnings: [...problem, ...conflict] };
|
|
46715
47403
|
}
|
|
46716
|
-
function describeStartTriage(result2) {
|
|
47404
|
+
function describeStartTriage(result2, inherited = new Map) {
|
|
46717
47405
|
const { blocker } = result2;
|
|
46718
47406
|
const blockerNotes = blocker.written ? ["blockers: written onto the task line, for the next dispatch of this task."] : [];
|
|
46719
47407
|
const blockerWarnings = blocker.problem === null ? [] : [`blockers: ${blocker.problem}`];
|
|
46720
|
-
const bugs = result2.bugs.map(bugLines);
|
|
47408
|
+
const bugs = result2.bugs.map((bug) => bugLines(bug, inherited.get(bug.index)));
|
|
46721
47409
|
const prefixed = (lines2) => lines2.map((line) => `${LINE_PREFIX}${line}`);
|
|
46722
47410
|
return {
|
|
46723
47411
|
notes: prefixed([...blockerNotes, ...bugs.flatMap((lines2) => lines2.notes)]),
|
|
@@ -46741,10 +47429,42 @@ async function resolvePublicTracker(options, redact) {
|
|
|
46741
47429
|
return unresolvedTracker(options.config.trackerDefault, reason);
|
|
46742
47430
|
}
|
|
46743
47431
|
}
|
|
47432
|
+
function baselinePathOrNull(trackerPath) {
|
|
47433
|
+
try {
|
|
47434
|
+
return baselinePathFor(trackerPath);
|
|
47435
|
+
} catch {
|
|
47436
|
+
return null;
|
|
47437
|
+
}
|
|
47438
|
+
}
|
|
47439
|
+
function runStartFailures(trackerPath) {
|
|
47440
|
+
const path4 = baselinePathOrNull(trackerPath);
|
|
47441
|
+
const reading2 = path4 === null ? null : readBaseline(path4);
|
|
47442
|
+
return reading2?.state === "read" && reading2.baseline.junit === "read" ? reading2.baseline.failures : [];
|
|
47443
|
+
}
|
|
47444
|
+
function inheritedByIndex(result2, report2, failures, local, redact) {
|
|
47445
|
+
const named3 = result2.bugs.flatMap((bug) => {
|
|
47446
|
+
const reported2 = report2.outOfScopeBugs[bug.index];
|
|
47447
|
+
if (bug.action !== "inherited" || reported2 === undefined || !hasText2(reported2.what))
|
|
47448
|
+
return [];
|
|
47449
|
+
const artifact = artifactOf(reported2);
|
|
47450
|
+
const failure6 = inheritedFailureOf({
|
|
47451
|
+
what: local(reported2.what),
|
|
47452
|
+
artifact: artifact === null ? null : local(artifact)
|
|
47453
|
+
}, failures);
|
|
47454
|
+
return failure6 === null ? [] : [[bug.index, { file: redact(failure6.file), name: redact(failure6.name) }]];
|
|
47455
|
+
});
|
|
47456
|
+
return new Map(named3);
|
|
47457
|
+
}
|
|
46744
47458
|
function createStartTriage(options) {
|
|
46745
47459
|
const secrets = namedSecrets(options.config, options.env ?? process.env);
|
|
46746
47460
|
const redact = (text2) => redactSecrets(text2, secrets);
|
|
47461
|
+
const local = localPathRedactor(options.repoRoot, homedir10());
|
|
46747
47462
|
let publicTracker = null;
|
|
47463
|
+
let inherited = null;
|
|
47464
|
+
const inheritedFor = (trackerPath) => {
|
|
47465
|
+
inherited ??= { failures: runStartFailures(trackerPath), commented: new Set };
|
|
47466
|
+
return inherited;
|
|
47467
|
+
};
|
|
46748
47468
|
const trackerFor = (report2) => {
|
|
46749
47469
|
if (!report2.outOfScopeBugs.some(filesPublicly)) {
|
|
46750
47470
|
return Promise.resolve(unresolvedTracker(options.config.trackerDefault, NOT_RESOLVED_REASON));
|
|
@@ -46757,6 +47477,7 @@ function createStartTriage(options) {
|
|
|
46757
47477
|
if (!reading2.present || !hasTriage(reading2.report))
|
|
46758
47478
|
return null;
|
|
46759
47479
|
const { report: report2 } = reading2;
|
|
47480
|
+
const run = inheritedFor(input.trackerPath);
|
|
46760
47481
|
const result2 = await triageReport({
|
|
46761
47482
|
repoRoot: options.repoRoot,
|
|
46762
47483
|
trackerPath: input.trackerPath,
|
|
@@ -46771,13 +47492,18 @@ function createStartTriage(options) {
|
|
|
46771
47492
|
tracker: await trackerFor(report2),
|
|
46772
47493
|
privateTracker: options.privateTracker,
|
|
46773
47494
|
secrets,
|
|
46774
|
-
seams: options.seams
|
|
47495
|
+
seams: options.seams,
|
|
47496
|
+
inherited: run,
|
|
47497
|
+
similarity: options.config
|
|
46775
47498
|
});
|
|
46776
|
-
const
|
|
47499
|
+
const named3 = inheritedByIndex(result2, report2, run.failures, local, redact);
|
|
47500
|
+
const { notes, warnings } = describeStartTriage(result2, named3);
|
|
46777
47501
|
for (const note of notes)
|
|
46778
47502
|
activeOutput().info(`${INDENT6}${note}`);
|
|
46779
47503
|
for (const warning of warnings)
|
|
46780
47504
|
activeOutput().warn(`${INDENT6}${warning}`);
|
|
47505
|
+
for (const failure6 of named3.values())
|
|
47506
|
+
emitLoopEvent({ kind: "inherited", file: failure6.file, name: failure6.name });
|
|
46781
47507
|
return result2;
|
|
46782
47508
|
};
|
|
46783
47509
|
return async (input) => {
|
|
@@ -46795,24 +47521,31 @@ var init_triage3 = __esm(() => {
|
|
|
46795
47521
|
init_resolve2();
|
|
46796
47522
|
init_config_sections();
|
|
46797
47523
|
init_parse();
|
|
47524
|
+
init_baseline();
|
|
47525
|
+
init_bug_key();
|
|
47526
|
+
init_inherited();
|
|
47527
|
+
init_issue_text();
|
|
47528
|
+
init_local_paths();
|
|
46798
47529
|
init_triage2();
|
|
47530
|
+
init_loop_events();
|
|
46799
47531
|
FOUND_BY = {
|
|
46800
47532
|
store: "its stored reference",
|
|
46801
|
-
find: "a tracker find"
|
|
47533
|
+
find: "a tracker find",
|
|
47534
|
+
similarity: "word similarity"
|
|
46802
47535
|
};
|
|
46803
47536
|
});
|
|
46804
47537
|
|
|
46805
47538
|
// src/commands/epic/close.ts
|
|
46806
47539
|
import { randomUUID as randomUUID16 } from "crypto";
|
|
46807
|
-
import { join as
|
|
47540
|
+
import { join as join74 } from "path";
|
|
46808
47541
|
function ref14(issue4) {
|
|
46809
47542
|
return `#${String(issue4)}`;
|
|
46810
47543
|
}
|
|
46811
|
-
function
|
|
47544
|
+
function oneLine10(text2) {
|
|
46812
47545
|
return text2.replace(/\s+/gu, " ").trim();
|
|
46813
47546
|
}
|
|
46814
47547
|
function criterionName(criterion) {
|
|
46815
|
-
return `criterion ${String(criterion.number)}: ${
|
|
47548
|
+
return `criterion ${String(criterion.number)}: ${oneLine10(criterion.text)}`;
|
|
46816
47549
|
}
|
|
46817
47550
|
function criteria(count2) {
|
|
46818
47551
|
return count2 === 1 ? "1 criterion" : `${String(count2)} criteria`;
|
|
@@ -46836,7 +47569,7 @@ function refuseOpenMembers(number, members) {
|
|
|
46836
47569
|
const open = members.filter((member) => member.state === "OPEN");
|
|
46837
47570
|
if (open.length === 0)
|
|
46838
47571
|
return;
|
|
46839
|
-
const named3 = open.map((member) => `${ref14(member.number)} ${
|
|
47572
|
+
const named3 = open.map((member) => `${ref14(member.number)} ${oneLine10(member.title)}`).join(", ");
|
|
46840
47573
|
throw refusal10(`Epic ${ref14(number)} has ${String(open.length)} open ${open.length === 1 ? "member" : "members"}: ${named3}. The gate checks an epic only once every member is closed: close them, or move them` + " out with rafa epic move <issue> --to=<epic>. No session was started");
|
|
46841
47574
|
}
|
|
46842
47575
|
function labelsEpicToClose(issues, epic) {
|
|
@@ -46905,10 +47638,10 @@ async function planVerification(input) {
|
|
|
46905
47638
|
return Object.freeze({ status: "planned", sessionId, verdicts: reading2.verdicts, issues: reading2.issues });
|
|
46906
47639
|
}
|
|
46907
47640
|
function closeTriageFile(root, epic) {
|
|
46908
|
-
return
|
|
47641
|
+
return join74(root, ".rafa", `epic-${String(epic)}-close`);
|
|
46909
47642
|
}
|
|
46910
47643
|
function failedCheckArtifact(epic, criterion) {
|
|
46911
|
-
return `epic ${ref14(epic)} acceptance criterion: ${
|
|
47644
|
+
return `epic ${ref14(epic)} acceptance criterion: ${oneLine10(criterion.text)}`;
|
|
46912
47645
|
}
|
|
46913
47646
|
function failedCheckReport(epic, failure6, commit) {
|
|
46914
47647
|
const { criterion } = failure6.check;
|
|
@@ -46932,7 +47665,7 @@ function failedCheckReport(epic, failure6, commit) {
|
|
|
46932
47665
|
skillsUsed: [],
|
|
46933
47666
|
blockers: [],
|
|
46934
47667
|
outOfScopeBugs: [Object.freeze({
|
|
46935
|
-
what: `Epic ${ref14(epic)} acceptance criterion ${String(criterion.number)} fails against main: ${
|
|
47668
|
+
what: `Epic ${ref14(epic)} acceptance criterion ${String(criterion.number)} fails against main: ${oneLine10(criterion.text)}`,
|
|
46936
47669
|
artifact: failedCheckArtifact(epic, criterion),
|
|
46937
47670
|
security: false,
|
|
46938
47671
|
extras: []
|
|
@@ -46953,6 +47686,9 @@ function bugLine(criterion, bug) {
|
|
|
46953
47686
|
case "commented": {
|
|
46954
47687
|
return { text: `${which}: recurs in ${bug.ref === null ? "an issue" : issueName3(bug.ref)} on the ${bug.channel} tracker; commented on it.`, warn: false };
|
|
46955
47688
|
}
|
|
47689
|
+
case "inherited": {
|
|
47690
|
+
return { text: `${which}: not filed: a test failure the run started with.`, warn: false };
|
|
47691
|
+
}
|
|
46956
47692
|
case "skipped": {
|
|
46957
47693
|
return { text: `${which}: not filed: ${bug.problem ?? "no reason given"}.`, warn: true };
|
|
46958
47694
|
}
|
|
@@ -46990,7 +47726,8 @@ async function fileFailures(run, epic, failures, commit) {
|
|
|
46990
47726
|
tracker,
|
|
46991
47727
|
privateTracker: run.seams.privateTracker,
|
|
46992
47728
|
secrets,
|
|
46993
|
-
seams: run.seams.findings
|
|
47729
|
+
seams: run.seams.findings,
|
|
47730
|
+
similarity: run.config
|
|
46994
47731
|
});
|
|
46995
47732
|
for (const bug of result2.bugs) {
|
|
46996
47733
|
const line = bugLine(criterion, bug);
|
|
@@ -47021,9 +47758,9 @@ async function weighResults(run, epic, ran) {
|
|
|
47021
47758
|
if (result2.kind === "unanswered")
|
|
47022
47759
|
warn3(run, `No answer for ${name2} \u2014 ${result2.reason}`);
|
|
47023
47760
|
else if (result2.result === "pass")
|
|
47024
|
-
say(run, `\u2713 Passed ${name2} \u2014 ${
|
|
47761
|
+
say(run, `\u2713 Passed ${name2} \u2014 ${oneLine10(result2.evidence)}`);
|
|
47025
47762
|
else
|
|
47026
|
-
warn3(run, `\u2717 Failed ${name2} \u2014 ${
|
|
47763
|
+
warn3(run, `\u2717 Failed ${name2} \u2014 ${oneLine10(result2.evidence)}`);
|
|
47027
47764
|
}
|
|
47028
47765
|
if (!ran.removal.ok) {
|
|
47029
47766
|
const said = ran.removal.said === "" ? "git said nothing" : ran.removal.said;
|
|
@@ -47557,8 +48294,8 @@ var init_promote = __esm(() => {
|
|
|
47557
48294
|
});
|
|
47558
48295
|
|
|
47559
48296
|
// src/agents/vendorable.ts
|
|
47560
|
-
import { readdirSync as readdirSync17, readFileSync as
|
|
47561
|
-
import { isAbsolute as isAbsolute17, join as
|
|
48297
|
+
import { readdirSync as readdirSync17, readFileSync as readFileSync54 } from "fs";
|
|
48298
|
+
import { isAbsolute as isAbsolute17, join as join75 } from "path";
|
|
47562
48299
|
function planFiles(dir) {
|
|
47563
48300
|
let entries;
|
|
47564
48301
|
try {
|
|
@@ -47570,7 +48307,7 @@ function planFiles(dir) {
|
|
|
47570
48307
|
}
|
|
47571
48308
|
function readText7(path4) {
|
|
47572
48309
|
try {
|
|
47573
|
-
return
|
|
48310
|
+
return readFileSync54(path4, "utf8");
|
|
47574
48311
|
} catch {
|
|
47575
48312
|
return null;
|
|
47576
48313
|
}
|
|
@@ -47580,7 +48317,7 @@ function rafaHolds(roster, name2) {
|
|
|
47580
48317
|
return item?.holders.some((holder) => holder.source === "rafa") === true;
|
|
47581
48318
|
}
|
|
47582
48319
|
function vendorableAgents(scan) {
|
|
47583
|
-
const dir = isAbsolute17(scan.planDir) ? scan.planDir :
|
|
48320
|
+
const dir = isAbsolute17(scan.planDir) ? scan.planDir : join75(scan.repoRoot, scan.planDir);
|
|
47584
48321
|
const roots = {
|
|
47585
48322
|
repoRoot: scan.repoRoot,
|
|
47586
48323
|
home: scan.home,
|
|
@@ -47589,7 +48326,7 @@ function vendorableAgents(scan) {
|
|
|
47589
48326
|
const roster = resolveAgentRoster(roots, scan.settings);
|
|
47590
48327
|
const found = [];
|
|
47591
48328
|
for (const file of planFiles(dir)) {
|
|
47592
|
-
const path4 =
|
|
48329
|
+
const path4 = join75(dir, file);
|
|
47593
48330
|
const markdown = readText7(path4);
|
|
47594
48331
|
if (markdown === null)
|
|
47595
48332
|
continue;
|
|
@@ -47619,8 +48356,8 @@ var init_vendorable = __esm(() => {
|
|
|
47619
48356
|
|
|
47620
48357
|
// src/project/gitignore.ts
|
|
47621
48358
|
import { createHash as createHash7 } from "crypto";
|
|
47622
|
-
import { existsSync as
|
|
47623
|
-
import { dirname as
|
|
48359
|
+
import { existsSync as existsSync50, mkdirSync as mkdirSync18, readFileSync as readFileSync55, writeFileSync as writeFileSync18 } from "fs";
|
|
48360
|
+
import { dirname as dirname28, isAbsolute as isAbsolute18, join as join76 } from "path";
|
|
47624
48361
|
function trackingEntry(flags) {
|
|
47625
48362
|
if (flags.trackingAll) {
|
|
47626
48363
|
return [
|
|
@@ -47696,19 +48433,19 @@ function attempt3(file, failure6, act) {
|
|
|
47696
48433
|
}
|
|
47697
48434
|
}
|
|
47698
48435
|
function readIfPresent4(file) {
|
|
47699
|
-
if (!
|
|
48436
|
+
if (!existsSync50(file))
|
|
47700
48437
|
return null;
|
|
47701
|
-
return attempt3(file, "cannot be read", () =>
|
|
48438
|
+
return attempt3(file, "cannot be read", () => readFileSync55(file, "utf8"));
|
|
47702
48439
|
}
|
|
47703
48440
|
function writeTrackingGitignore(root, flags) {
|
|
47704
48441
|
requireAbsolute2(root);
|
|
47705
|
-
const file =
|
|
48442
|
+
const file = join76(root, GITIGNORE_FILE);
|
|
47706
48443
|
const text2 = readIfPresent4(file);
|
|
47707
48444
|
const next = withTrackingBlock(text2, flags, file);
|
|
47708
48445
|
if (next === text2)
|
|
47709
48446
|
return { file, change: "unchanged" };
|
|
47710
48447
|
attempt3(file, "cannot be written", () => {
|
|
47711
|
-
|
|
48448
|
+
writeFileSync18(file, next);
|
|
47712
48449
|
});
|
|
47713
48450
|
const change = text2 === null ? "created" : "updated";
|
|
47714
48451
|
return { file, change };
|
|
@@ -47724,7 +48461,7 @@ function trackingDigest(flags) {
|
|
|
47724
48461
|
}
|
|
47725
48462
|
function exposureNotice(root) {
|
|
47726
48463
|
return [
|
|
47727
|
-
`tracking.all is true, so ${
|
|
48464
|
+
`tracking.all is true, so ${join76(root, GITIGNORE_FILE)} no longer ignores ${SCOPE_DIR}/` + " and a commit can carry what it holds, including:",
|
|
47728
48465
|
" - unpatched issues described in specs and plans",
|
|
47729
48466
|
" - effort rows with model names and token counts",
|
|
47730
48467
|
" - instincts that may quote error output",
|
|
@@ -47737,7 +48474,7 @@ function printWarning2(message) {
|
|
|
47737
48474
|
}
|
|
47738
48475
|
function noticeTrackingChange(root, flags, print2 = printWarning2) {
|
|
47739
48476
|
requireAbsolute2(root);
|
|
47740
|
-
const digestFile =
|
|
48477
|
+
const digestFile = join76(root, TRACKING_DIGEST_FILE);
|
|
47741
48478
|
const recorded = `${trackingDigest(flags)}
|
|
47742
48479
|
`;
|
|
47743
48480
|
if (readIfPresent4(digestFile) === recorded) {
|
|
@@ -47746,8 +48483,8 @@ function noticeTrackingChange(root, flags, print2 = printWarning2) {
|
|
|
47746
48483
|
if (flags.trackingAll)
|
|
47747
48484
|
print2(exposureNotice(root));
|
|
47748
48485
|
attempt3(digestFile, "cannot be written", () => {
|
|
47749
|
-
|
|
47750
|
-
|
|
48486
|
+
mkdirSync18(dirname28(digestFile), { recursive: true });
|
|
48487
|
+
writeFileSync18(digestFile, recorded);
|
|
47751
48488
|
});
|
|
47752
48489
|
return { digestFile, changed: true, printed: flags.trackingAll };
|
|
47753
48490
|
}
|
|
@@ -47762,7 +48499,7 @@ var init_gitignore = __esm(() => {
|
|
|
47762
48499
|
init_hop_record();
|
|
47763
48500
|
init_position();
|
|
47764
48501
|
init_scope();
|
|
47765
|
-
TRACKING_DIGEST_FILE =
|
|
48502
|
+
TRACKING_DIGEST_FILE = join76(SCOPE_DIR, DIGEST_NAME);
|
|
47766
48503
|
SUB_PATHS = [
|
|
47767
48504
|
{ flag: "trackingSpecs", dir: "specs" },
|
|
47768
48505
|
{ flag: "trackingPlans", dir: "plans" }
|
|
@@ -47777,8 +48514,8 @@ var init_gitignore = __esm(() => {
|
|
|
47777
48514
|
|
|
47778
48515
|
// src/project/roots.ts
|
|
47779
48516
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
47780
|
-
import { accessSync, constants as constants2, readFileSync as
|
|
47781
|
-
import { isAbsolute as isAbsolute19, join as
|
|
48517
|
+
import { accessSync, constants as constants2, readFileSync as readFileSync56, statSync as statSync25 } from "fs";
|
|
48518
|
+
import { isAbsolute as isAbsolute19, join as join77, resolve as resolve30 } from "path";
|
|
47782
48519
|
function gitToplevel(dir) {
|
|
47783
48520
|
const result2 = spawnSync5("git", ["rev-parse", "--show-toplevel"], {
|
|
47784
48521
|
cwd: dir,
|
|
@@ -47881,8 +48618,8 @@ function readManifest2(file, fs3) {
|
|
|
47881
48618
|
}
|
|
47882
48619
|
}
|
|
47883
48620
|
function readDir(dir, fs3) {
|
|
47884
|
-
const manifest = readManifest2(
|
|
47885
|
-
const files = MONOREPO_MARKER_FILES.filter((name2) => fs3.exists(
|
|
48621
|
+
const manifest = readManifest2(join77(dir, PACKAGE_JSON), fs3);
|
|
48622
|
+
const files = MONOREPO_MARKER_FILES.filter((name2) => fs3.exists(join77(dir, name2)));
|
|
47886
48623
|
const markers = manifest.marks ? [PACKAGE_JSON, ...files] : files;
|
|
47887
48624
|
return { ...manifest, dir, markers };
|
|
47888
48625
|
}
|
|
@@ -47935,7 +48672,7 @@ var init_roots = __esm(() => {
|
|
|
47935
48672
|
]);
|
|
47936
48673
|
DISK_ROOTS_FILE_SYSTEM = Object.freeze({
|
|
47937
48674
|
...DISK_FILE_SYSTEM,
|
|
47938
|
-
readFile: (path4) =>
|
|
48675
|
+
readFile: (path4) => readFileSync56(path4, "utf8"),
|
|
47939
48676
|
isDirectory: (path4) => statSync25(path4).isDirectory(),
|
|
47940
48677
|
assertWritable: (path4) => accessSync(path4, constants2.W_OK)
|
|
47941
48678
|
});
|
|
@@ -48031,7 +48768,7 @@ var init_root_choice = __esm(() => {
|
|
|
48031
48768
|
});
|
|
48032
48769
|
|
|
48033
48770
|
// src/release/setting.ts
|
|
48034
|
-
import { readFileSync as
|
|
48771
|
+
import { readFileSync as readFileSync57 } from "fs";
|
|
48035
48772
|
function releaseEnabledIn(text2, path4) {
|
|
48036
48773
|
return parseConfigText(text2, path4).values.releaseEnabled ?? null;
|
|
48037
48774
|
}
|
|
@@ -48039,7 +48776,7 @@ function readReleaseSetting(root) {
|
|
|
48039
48776
|
const path4 = configFilePath(root);
|
|
48040
48777
|
let text2;
|
|
48041
48778
|
try {
|
|
48042
|
-
text2 =
|
|
48779
|
+
text2 = readFileSync57(path4, "utf8");
|
|
48043
48780
|
} catch (error) {
|
|
48044
48781
|
return { enabled: null, text: "", problem: `${path4} could not be read: ${messageOf(error)}` };
|
|
48045
48782
|
}
|
|
@@ -48112,7 +48849,7 @@ var init_setting = __esm(() => {
|
|
|
48112
48849
|
});
|
|
48113
48850
|
|
|
48114
48851
|
// src/commands/init-release.ts
|
|
48115
|
-
import { writeFileSync as
|
|
48852
|
+
import { writeFileSync as writeFileSync19 } from "fs";
|
|
48116
48853
|
function releaseQuestion(versionFile, changelog) {
|
|
48117
48854
|
return `Bump ${versionFile} and add a ${changelog} entry with every pull request? [Y/n] `;
|
|
48118
48855
|
}
|
|
@@ -48164,7 +48901,7 @@ function writeSetting(root, text2, enabled) {
|
|
|
48164
48901
|
return `the file this run would have written reads ${RELEASE_ENABLED_SETTING} as ${describeValue(read)}`;
|
|
48165
48902
|
}
|
|
48166
48903
|
try {
|
|
48167
|
-
|
|
48904
|
+
writeFileSync19(path4, written3, "utf8");
|
|
48168
48905
|
} catch (error) {
|
|
48169
48906
|
return `it could not be written: ${messageOf(error)}`;
|
|
48170
48907
|
}
|
|
@@ -48227,8 +48964,8 @@ var init_init_release = __esm(() => {
|
|
|
48227
48964
|
});
|
|
48228
48965
|
|
|
48229
48966
|
// src/project/scaffold.ts
|
|
48230
|
-
import { existsSync as
|
|
48231
|
-
import { join as
|
|
48967
|
+
import { existsSync as existsSync51, lstatSync as lstatSync2, mkdirSync as mkdirSync19, statSync as statSync26, writeFileSync as writeFileSync20 } from "fs";
|
|
48968
|
+
import { join as join78 } from "path";
|
|
48232
48969
|
function globsLine(patterns) {
|
|
48233
48970
|
return `[${patterns.map((pattern) => JSON.stringify(pattern)).join(", ")}]`;
|
|
48234
48971
|
}
|
|
@@ -48244,7 +48981,7 @@ function userConfigText() {
|
|
|
48244
48981
|
}
|
|
48245
48982
|
function layoutOf2(base, tree) {
|
|
48246
48983
|
const scope = scopeAt(base);
|
|
48247
|
-
return { dirs: [scope.dir, ...tree.map((name2) =>
|
|
48984
|
+
return { dirs: [scope.dir, ...tree.map((name2) => join78(scope.dir, name2))], configFile: configFilePath(base) };
|
|
48248
48985
|
}
|
|
48249
48986
|
function occupied(path4) {
|
|
48250
48987
|
return lstatSync2(path4, { throwIfNoEntry: false }) !== undefined;
|
|
@@ -48255,7 +48992,7 @@ function isDirectory3(path4) {
|
|
|
48255
48992
|
function directoryConflict(dir) {
|
|
48256
48993
|
if (!occupied(dir) || isDirectory3(dir))
|
|
48257
48994
|
return null;
|
|
48258
|
-
return
|
|
48995
|
+
return existsSync51(dir) ? `${dir} is not a directory` : `${dir} is a link to nothing`;
|
|
48259
48996
|
}
|
|
48260
48997
|
function layoutConflicts(layout) {
|
|
48261
48998
|
const [scopeDir, ...tree] = layout.dirs;
|
|
@@ -48263,7 +49000,7 @@ function layoutConflicts(layout) {
|
|
|
48263
49000
|
if (opening !== null)
|
|
48264
49001
|
return [opening];
|
|
48265
49002
|
const dirs = tree.flatMap((dir) => directoryConflict(dir) ?? []);
|
|
48266
|
-
const file = occupied(layout.configFile) && !
|
|
49003
|
+
const file = occupied(layout.configFile) && !existsSync51(layout.configFile) ? [`${layout.configFile} is a link to nothing`] : [];
|
|
48267
49004
|
return [...dirs, ...file];
|
|
48268
49005
|
}
|
|
48269
49006
|
function scaffoldConflicts(root, home) {
|
|
@@ -48277,18 +49014,18 @@ function attempt4(path4, failure6, act) {
|
|
|
48277
49014
|
}
|
|
48278
49015
|
}
|
|
48279
49016
|
function ensureDirectory(path4) {
|
|
48280
|
-
if (
|
|
49017
|
+
if (existsSync51(path4))
|
|
48281
49018
|
return { path: path4, kind: "directory", change: "unchanged" };
|
|
48282
49019
|
attempt4(path4, "cannot be created", () => {
|
|
48283
|
-
|
|
49020
|
+
mkdirSync19(path4, { recursive: true });
|
|
48284
49021
|
});
|
|
48285
49022
|
return { path: path4, kind: "directory", change: "created" };
|
|
48286
49023
|
}
|
|
48287
49024
|
function ensureFile(path4, text2) {
|
|
48288
|
-
if (
|
|
49025
|
+
if (existsSync51(path4))
|
|
48289
49026
|
return { path: path4, kind: "file", change: "unchanged" };
|
|
48290
49027
|
attempt4(path4, "cannot be written", () => {
|
|
48291
|
-
|
|
49028
|
+
writeFileSync20(path4, text2, { encoding: "utf8", flag: "wx" });
|
|
48292
49029
|
});
|
|
48293
49030
|
return { path: path4, kind: "file", change: "created" };
|
|
48294
49031
|
}
|
|
@@ -48366,6 +49103,10 @@ var init_scaffold = __esm(() => {
|
|
|
48366
49103
|
"# claims:",
|
|
48367
49104
|
`# staleAfter: ${CONFIG_DEFAULTS.claimsStaleAfter} # 36h | 3d | disabled; how long a rafa:claimed claim stands before a takeover`,
|
|
48368
49105
|
`# ahead: ${CONFIG_DEFAULTS.claimsAhead} # off | allow; whether a claim may reach one issue ahead`,
|
|
49106
|
+
"# triage:",
|
|
49107
|
+
"# similarity:",
|
|
49108
|
+
`# threshold: ${String(CONFIG_DEFAULTS.triageSimilarityThreshold)} # above 0 and at most 1 | false; the least score at which triage comments on the nearest open bug`,
|
|
49109
|
+
`# candidates: ${String(CONFIG_DEFAULTS.triageSimilarityCandidates)} # 1..10, the nearest issues a new bug lists as Possible duplicates`,
|
|
48369
49110
|
"# release:",
|
|
48370
49111
|
`# enabled: ${String(CONFIG_DEFAULTS.releaseEnabled)} # true | false | auto, which is on when both files below exist`,
|
|
48371
49112
|
`# versionFile: ${CONFIG_DEFAULTS.releaseVersionFile}`,
|
|
@@ -48422,9 +49163,9 @@ var init_scaffold = __esm(() => {
|
|
|
48422
49163
|
});
|
|
48423
49164
|
|
|
48424
49165
|
// src/commands/init.ts
|
|
48425
|
-
import { existsSync as
|
|
48426
|
-
import { homedir as
|
|
48427
|
-
import { join as
|
|
49166
|
+
import { existsSync as existsSync52, readFileSync as readFileSync58 } from "fs";
|
|
49167
|
+
import { homedir as homedir11 } from "os";
|
|
49168
|
+
import { join as join79, relative as relative15, sep as sep6 } from "path";
|
|
48428
49169
|
function refusal12(lines2) {
|
|
48429
49170
|
return new CommandExit(1, [...lines2, NOTHING_WRITTEN2].join(`
|
|
48430
49171
|
`));
|
|
@@ -48522,9 +49263,9 @@ function resolvedConfig3(root, home, warn4) {
|
|
|
48522
49263
|
}
|
|
48523
49264
|
}
|
|
48524
49265
|
function checkGitignore(root, flags) {
|
|
48525
|
-
const file =
|
|
49266
|
+
const file = join79(root, GITIGNORE_FILE);
|
|
48526
49267
|
try {
|
|
48527
|
-
const text2 =
|
|
49268
|
+
const text2 = existsSync52(file) ? readFileSync58(file, "utf8") : null;
|
|
48528
49269
|
withTrackingBlock(text2, flags, file);
|
|
48529
49270
|
} catch (error) {
|
|
48530
49271
|
const message = error instanceof GitignoreError ? error.message : `rafa init: ${file} cannot be read (${messageOf(error)})`;
|
|
@@ -48556,8 +49297,8 @@ function initialise(root, start, home, context, entry) {
|
|
|
48556
49297
|
const resolved = resolvedConfig3(root.path, home, warn4);
|
|
48557
49298
|
const config = resolved.config;
|
|
48558
49299
|
checkGitignore(root.path, config);
|
|
48559
|
-
const configExisted =
|
|
48560
|
-
const digestExisted =
|
|
49300
|
+
const configExisted = existsSync52(configFilePath(root.path));
|
|
49301
|
+
const digestExisted = existsSync52(join79(root.path, TRACKING_DIGEST_FILE));
|
|
48561
49302
|
const project = writeProjectScope(root.path);
|
|
48562
49303
|
const tracking = applyTracking(root.path, config, warn4);
|
|
48563
49304
|
const writes = [...project, ...trackingWrites(tracking, digestExisted), ...writeUserScope(home)];
|
|
@@ -48766,7 +49507,7 @@ var init_init = __esm(() => {
|
|
|
48766
49507
|
init_init_release();
|
|
48767
49508
|
DEFAULT_INIT_SEAMS = Object.freeze({
|
|
48768
49509
|
cwd: () => process.cwd(),
|
|
48769
|
-
home: () =>
|
|
49510
|
+
home: () => homedir11(),
|
|
48770
49511
|
entry: () => Bun.main,
|
|
48771
49512
|
isTerminal: () => process.stdin.isTTY === true,
|
|
48772
49513
|
openPrompter: () => createLinePrompter(process.stdin, process.stderr),
|
|
@@ -49174,7 +49915,7 @@ Usage: ${USAGE24}`);
|
|
|
49174
49915
|
return "blessed";
|
|
49175
49916
|
return conflicts ? "conflicts" : "all";
|
|
49176
49917
|
}
|
|
49177
|
-
function
|
|
49918
|
+
function oneLine11(text2) {
|
|
49178
49919
|
return text2.trim().replace(/\s+/gu, " ");
|
|
49179
49920
|
}
|
|
49180
49921
|
function byConfidence2(left, right) {
|
|
@@ -49207,7 +49948,7 @@ function conflictLines(conflict) {
|
|
|
49207
49948
|
const width = Math.max(...conflict.records.map((record) => record.id.length));
|
|
49208
49949
|
return [
|
|
49209
49950
|
` ${conflict.scope} ${conflict.trigger}`,
|
|
49210
|
-
...conflict.records.map((record) => lessonLine2(" ", record, width,
|
|
49951
|
+
...conflict.records.map((record) => lessonLine2(" ", record, width, oneLine11(record.action)))
|
|
49211
49952
|
];
|
|
49212
49953
|
}
|
|
49213
49954
|
function renderConflicts(result2) {
|
|
@@ -49486,8 +50227,8 @@ var init_show4 = __esm(() => {
|
|
|
49486
50227
|
});
|
|
49487
50228
|
|
|
49488
50229
|
// src/commands/issue/check.ts
|
|
49489
|
-
import { readdirSync as readdirSync18, readFileSync as
|
|
49490
|
-
import { join as
|
|
50230
|
+
import { readdirSync as readdirSync18, readFileSync as readFileSync59, statSync as statSync27 } from "fs";
|
|
50231
|
+
import { join as join80, resolve as resolve33 } from "path";
|
|
49491
50232
|
function readCheckIssue(args) {
|
|
49492
50233
|
if (args.length > 1) {
|
|
49493
50234
|
throw lineRefusal(`Expected one issue number, got ${String(args.length)}: ${args.join(" ")}`, CHECK_USAGE);
|
|
@@ -49515,7 +50256,7 @@ function copyNames(dir, issue4) {
|
|
|
49515
50256
|
}
|
|
49516
50257
|
const prefix = `${boardId(issue4)}-`;
|
|
49517
50258
|
const notes = notesFileName(issue4);
|
|
49518
|
-
return names2.filter((name2) => name2.startsWith(prefix) && name2.endsWith(SPEC_EXTENSION) && name2 !== notes).filter((name2) => name2.length > prefix.length + SPEC_EXTENSION.length).filter((name2) => statSync27(
|
|
50259
|
+
return names2.filter((name2) => name2.startsWith(prefix) && name2.endsWith(SPEC_EXTENSION) && name2 !== notes).filter((name2) => name2.length > prefix.length + SPEC_EXTENSION.length).filter((name2) => statSync27(join80(dir, name2)).isFile()).sort();
|
|
49519
50260
|
}
|
|
49520
50261
|
function findSavedCopy(root, specsDir, issue4) {
|
|
49521
50262
|
const names2 = copyNames(resolve33(root, specsDir), issue4);
|
|
@@ -49525,11 +50266,11 @@ function findSavedCopy(root, specsDir, issue4) {
|
|
|
49525
50266
|
if (names2.length > 1) {
|
|
49526
50267
|
throw new CommandExit(1, [
|
|
49527
50268
|
`\u274C Issue #${String(issue4)} has ${String(names2.length)} saved copies under ${specsDir}; remove the stale one:`,
|
|
49528
|
-
...names2.map((name2) => ` ${
|
|
50269
|
+
...names2.map((name2) => ` ${join80(specsDir, name2)}`)
|
|
49529
50270
|
].join(`
|
|
49530
50271
|
`));
|
|
49531
50272
|
}
|
|
49532
|
-
const path4 =
|
|
50273
|
+
const path4 = join80(specsDir, only);
|
|
49533
50274
|
return { path: path4, absolute: resolve33(root, path4) };
|
|
49534
50275
|
}
|
|
49535
50276
|
function liveText(live) {
|
|
@@ -49578,7 +50319,7 @@ async function readReferences(absolute, issue4, verify, stamp2) {
|
|
|
49578
50319
|
return rows2.map((row3) => checkedRef(row3, row3.stamp));
|
|
49579
50320
|
}
|
|
49580
50321
|
const once4 = memoiseVerifier(verify);
|
|
49581
|
-
const before = await readRefsText({ copy:
|
|
50322
|
+
const before = await readRefsText({ copy: readFileSync59(absolute, "utf8"), issue: issue4, verify: once4 });
|
|
49582
50323
|
const after = await restampCopyRefs({ path: absolute, issue: issue4, verify: once4 });
|
|
49583
50324
|
return before.rows.map((row3, index) => checkedRef(row3, after.rows[index]?.stamp ?? null));
|
|
49584
50325
|
}
|
|
@@ -50503,10 +51244,10 @@ var init_pr_lifecycle = __esm(() => {
|
|
|
50503
51244
|
});
|
|
50504
51245
|
|
|
50505
51246
|
// src/notices/notices.ts
|
|
50506
|
-
import { mkdirSync as
|
|
50507
|
-
import { dirname as
|
|
51247
|
+
import { mkdirSync as mkdirSync20, readFileSync as readFileSync60, writeFileSync as writeFileSync21 } from "fs";
|
|
51248
|
+
import { dirname as dirname29, join as join81 } from "path";
|
|
50508
51249
|
function noticesPath(home) {
|
|
50509
|
-
return
|
|
51250
|
+
return join81(home, ".rafa", "notices.json");
|
|
50510
51251
|
}
|
|
50511
51252
|
function isNoticeId(value) {
|
|
50512
51253
|
return typeof value === "string" && NOTICE_IDS.includes(value);
|
|
@@ -50514,7 +51255,7 @@ function isNoticeId(value) {
|
|
|
50514
51255
|
function readDismissed(home) {
|
|
50515
51256
|
let raw;
|
|
50516
51257
|
try {
|
|
50517
|
-
raw =
|
|
51258
|
+
raw = readFileSync60(noticesPath(home), "utf8");
|
|
50518
51259
|
} catch {
|
|
50519
51260
|
return [];
|
|
50520
51261
|
}
|
|
@@ -50533,8 +51274,8 @@ function readDismissed(home) {
|
|
|
50533
51274
|
function writeDismissed(home, ids) {
|
|
50534
51275
|
const all = NOTICE_IDS.filter((id3) => ids.includes(id3) || readDismissed(home).includes(id3));
|
|
50535
51276
|
const path4 = noticesPath(home);
|
|
50536
|
-
|
|
50537
|
-
|
|
51277
|
+
mkdirSync20(dirname29(path4), { recursive: true });
|
|
51278
|
+
writeFileSync21(path4, `${JSON.stringify({ dismissed: all }, null, 2)}
|
|
50538
51279
|
`);
|
|
50539
51280
|
}
|
|
50540
51281
|
function pendingNotices(dismissed) {
|
|
@@ -50599,9 +51340,9 @@ var init_notices = __esm(() => {
|
|
|
50599
51340
|
});
|
|
50600
51341
|
|
|
50601
51342
|
// src/notices/run.ts
|
|
50602
|
-
import { homedir as
|
|
51343
|
+
import { homedir as homedir12 } from "os";
|
|
50603
51344
|
async function requireNoticesAnswered() {
|
|
50604
|
-
const outcome2 = await offerNotices({ home:
|
|
51345
|
+
const outcome2 = await offerNotices({ home: homedir12(), version: RAFA_VERSION }, {
|
|
50605
51346
|
isTerminal: () => process.stdin.isTTY === true,
|
|
50606
51347
|
openPrompter: () => createLinePrompter(process.stdin, process.stderr),
|
|
50607
51348
|
warn: (line) => {
|
|
@@ -50653,9 +51394,9 @@ var init_budget = __esm(() => {
|
|
|
50653
51394
|
});
|
|
50654
51395
|
|
|
50655
51396
|
// src/start/checkout-guard.ts
|
|
50656
|
-
import { existsSync as
|
|
51397
|
+
import { existsSync as existsSync53, realpathSync as realpathSync14 } from "fs";
|
|
50657
51398
|
function realDirOf(path4) {
|
|
50658
|
-
if (!
|
|
51399
|
+
if (!existsSync53(path4))
|
|
50659
51400
|
return null;
|
|
50660
51401
|
try {
|
|
50661
51402
|
return realpathSync14(path4);
|
|
@@ -51232,62 +51973,6 @@ var init_commit2 = __esm(() => {
|
|
|
51232
51973
|
DROPPED_BLOCKER_FIELD = /^blockers\[\d+\]$/;
|
|
51233
51974
|
});
|
|
51234
51975
|
|
|
51235
|
-
// src/start/loop-events.ts
|
|
51236
|
-
import { join as join80 } from "path";
|
|
51237
|
-
function quoted3(text2) {
|
|
51238
|
-
return `"${oneLine(text2).replaceAll('"', "\\\"")}"`;
|
|
51239
|
-
}
|
|
51240
|
-
function taskKind(position, verb) {
|
|
51241
|
-
return padKind(`task ${position.index}/${position.total} ${verb}`);
|
|
51242
|
-
}
|
|
51243
|
-
function minutes(durationMs) {
|
|
51244
|
-
return `${Math.max(1, Math.round(durationMs / MINUTE_MS))}m`;
|
|
51245
|
-
}
|
|
51246
|
-
function summaryOf3(event) {
|
|
51247
|
-
switch (event.kind) {
|
|
51248
|
-
case "task-start":
|
|
51249
|
-
return `${taskKind(event.position, "start")}${quoted3(event.text)}`;
|
|
51250
|
-
case "task-done":
|
|
51251
|
-
return event.tokens === null ? `${taskKind(event.position, "done")}${minutes(event.durationMs)}` : `${taskKind(event.position, "done")}${minutes(event.durationMs)} ${Math.round(event.tokens / TOKENS_PER_K)}k tokens`;
|
|
51252
|
-
case "task-blocked":
|
|
51253
|
-
return `${taskKind(event.position, "blocked")}${oneLine(event.reason)}`;
|
|
51254
|
-
case "wrap-up":
|
|
51255
|
-
return `${padKind("wrap-up")}${event.phase}`;
|
|
51256
|
-
case "pr":
|
|
51257
|
-
return `pr #${event.number} opened`;
|
|
51258
|
-
case "no-pr":
|
|
51259
|
-
return `${padKind("no pr")}${oneLine(event.reason)}`;
|
|
51260
|
-
case "halt":
|
|
51261
|
-
return `${padKind("halt")}${oneLine(event.reason)}`;
|
|
51262
|
-
}
|
|
51263
|
-
}
|
|
51264
|
-
function emitLoopEvent(event, now = () => new Date) {
|
|
51265
|
-
const { kind, ...data } = event;
|
|
51266
|
-
activeOutput().emit({ type: "event", name: kind, summary: summaryOf3(event), data, ts: now().toISOString() });
|
|
51267
|
-
}
|
|
51268
|
-
async function unlessText(read) {
|
|
51269
|
-
return activeOutputMode() === "text" ? null : read();
|
|
51270
|
-
}
|
|
51271
|
-
function taskPosition(trackerContent, lineNum) {
|
|
51272
|
-
const { tasks } = parsePlan(trackerContent);
|
|
51273
|
-
return { index: tasks.findIndex((task) => task.lineNum === lineNum) + 1, total: tasks.length };
|
|
51274
|
-
}
|
|
51275
|
-
async function taskTokens(checkout, sessionId, home) {
|
|
51276
|
-
try {
|
|
51277
|
-
const { usage } = await readSessionLog(join80(sessionLogDir(checkout, home), `${sessionId}.jsonl`));
|
|
51278
|
-
return usage.inputTokens + usage.cacheCreationInputTokens + usage.outputTokens;
|
|
51279
|
-
} catch {
|
|
51280
|
-
return null;
|
|
51281
|
-
}
|
|
51282
|
-
}
|
|
51283
|
-
var MINUTE_MS = 60000, TOKENS_PER_K = 1000;
|
|
51284
|
-
var init_loop_events = __esm(() => {
|
|
51285
|
-
init_active();
|
|
51286
|
-
init_collect();
|
|
51287
|
-
init_session_log();
|
|
51288
|
-
init_plan();
|
|
51289
|
-
});
|
|
51290
|
-
|
|
51291
51976
|
// src/start/pause.ts
|
|
51292
51977
|
function storedState2(repoRoot, sessionId) {
|
|
51293
51978
|
try {
|
|
@@ -51547,9 +52232,9 @@ var init_worktree_dir = () => {};
|
|
|
51547
52232
|
|
|
51548
52233
|
// src/start/worktree.ts
|
|
51549
52234
|
import { realpathSync as realpathSync15 } from "fs";
|
|
51550
|
-
import { join as
|
|
52235
|
+
import { join as join82, resolve as resolve35 } from "path";
|
|
51551
52236
|
function worktreePathFor(projectRoot3, worktreeDir, stub) {
|
|
51552
|
-
return
|
|
52237
|
+
return join82(worktreeDirAt(projectRoot3, worktreeDir), stub);
|
|
51553
52238
|
}
|
|
51554
52239
|
function addStep2(route, plan, path4) {
|
|
51555
52240
|
const label = `add the worktree for ${plan.branch} at ${path4}`;
|
|
@@ -51733,104 +52418,6 @@ var init_run_checkout = __esm(() => {
|
|
|
51733
52418
|
init_worktree2();
|
|
51734
52419
|
});
|
|
51735
52420
|
|
|
51736
|
-
// src/suite/baseline.ts
|
|
51737
|
-
import { existsSync as existsSync53, mkdirSync as mkdirSync20, readFileSync as readFileSync60, renameSync as renameSync9, writeFileSync as writeFileSync21 } from "fs";
|
|
51738
|
-
import { basename as basename18, dirname as dirname28, join as join82 } from "path";
|
|
51739
|
-
function baselinePathFor(trackerPath) {
|
|
51740
|
-
const match = PLAN_FILE_NAME2.exec(basename18(trackerPath));
|
|
51741
|
-
if (match === null) {
|
|
51742
|
-
throw new RangeError(`baselinePathFor: ${trackerPath} is not a PLAN or PLAN_TRACKER file`);
|
|
51743
|
-
}
|
|
51744
|
-
return join82(dirname28(trackerPath), `${BASELINE_FILE_PREFIX}${match[1] ?? ""}.json`);
|
|
51745
|
-
}
|
|
51746
|
-
function baselineOf(result2, recordedAt, commit) {
|
|
51747
|
-
return {
|
|
51748
|
-
version: BASELINE_VERSION,
|
|
51749
|
-
recordedAt: recordedAt.toISOString(),
|
|
51750
|
-
commit,
|
|
51751
|
-
command: [...result2.command],
|
|
51752
|
-
exitCode: result2.exitCode,
|
|
51753
|
-
summary: result2.summary,
|
|
51754
|
-
failures: result2.failures.map((failure6) => ({ file: failure6.file, name: failure6.name })),
|
|
51755
|
-
errors: result2.errors,
|
|
51756
|
-
junit: result2.junit
|
|
51757
|
-
};
|
|
51758
|
-
}
|
|
51759
|
-
function writeBaseline(path4, baseline) {
|
|
51760
|
-
mkdirSync20(dirname28(path4), { recursive: true });
|
|
51761
|
-
const temporary = `${path4}.${process.pid}.tmp`;
|
|
51762
|
-
writeFileSync21(temporary, `${JSON.stringify(baseline, null, 2)}
|
|
51763
|
-
`, "utf8");
|
|
51764
|
-
renameSync9(temporary, path4);
|
|
51765
|
-
}
|
|
51766
|
-
function isStringOrNull(value) {
|
|
51767
|
-
return value === null || typeof value === "string";
|
|
51768
|
-
}
|
|
51769
|
-
function isFailure2(value) {
|
|
51770
|
-
if (typeof value !== "object" || value === null)
|
|
51771
|
-
return false;
|
|
51772
|
-
const record = value;
|
|
51773
|
-
return typeof record["file"] === "string" && typeof record["name"] === "string";
|
|
51774
|
-
}
|
|
51775
|
-
function badField(record) {
|
|
51776
|
-
const checks = [
|
|
51777
|
-
["recordedAt", (value) => typeof value === "string"],
|
|
51778
|
-
["commit", isStringOrNull],
|
|
51779
|
-
["command", (value) => Array.isArray(value) && value.every((part) => typeof part === "string")],
|
|
51780
|
-
["exitCode", (value) => Number.isInteger(value)],
|
|
51781
|
-
["summary", isStringOrNull],
|
|
51782
|
-
["failures", (value) => Array.isArray(value) && value.every(isFailure2)],
|
|
51783
|
-
["errors", (value) => value === null || Number.isInteger(value)],
|
|
51784
|
-
["junit", (value) => JUNIT_READINGS.includes(value)]
|
|
51785
|
-
];
|
|
51786
|
-
const failed4 = checks.find(([key, check]) => !check(record[key]));
|
|
51787
|
-
return failed4?.[0] ?? null;
|
|
51788
|
-
}
|
|
51789
|
-
function asBaseline(parsed) {
|
|
51790
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
51791
|
-
return { state: "unreadable", reason: "not a JSON object" };
|
|
51792
|
-
}
|
|
51793
|
-
const record = parsed;
|
|
51794
|
-
if (record["version"] !== BASELINE_VERSION) {
|
|
51795
|
-
return { state: "unreadable", reason: `version ${JSON.stringify(record["version"])}, expected ${BASELINE_VERSION}` };
|
|
51796
|
-
}
|
|
51797
|
-
const bad = badField(record);
|
|
51798
|
-
if (bad !== null)
|
|
51799
|
-
return { state: "unreadable", reason: `field ${bad} is missing or malformed` };
|
|
51800
|
-
return { state: "read", baseline: parsed };
|
|
51801
|
-
}
|
|
51802
|
-
function readBaseline(path4) {
|
|
51803
|
-
if (!existsSync53(path4))
|
|
51804
|
-
return { state: "missing" };
|
|
51805
|
-
let text2;
|
|
51806
|
-
try {
|
|
51807
|
-
text2 = readFileSync60(path4, "utf8");
|
|
51808
|
-
} catch (error) {
|
|
51809
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
51810
|
-
return { state: "unreadable", reason };
|
|
51811
|
-
}
|
|
51812
|
-
try {
|
|
51813
|
-
return asBaseline(JSON.parse(text2));
|
|
51814
|
-
} catch {
|
|
51815
|
-
return { state: "unreadable", reason: "not JSON" };
|
|
51816
|
-
}
|
|
51817
|
-
}
|
|
51818
|
-
function failureKey2(failure6) {
|
|
51819
|
-
return JSON.stringify([failure6.file, failure6.name]);
|
|
51820
|
-
}
|
|
51821
|
-
function splitFailures(failures, baseline) {
|
|
51822
|
-
const held = new Set((baseline?.failures ?? []).map(failureKey2));
|
|
51823
|
-
return {
|
|
51824
|
-
fresh: failures.filter((failure6) => !held.has(failureKey2(failure6))),
|
|
51825
|
-
known: failures.filter((failure6) => held.has(failureKey2(failure6)))
|
|
51826
|
-
};
|
|
51827
|
-
}
|
|
51828
|
-
var BASELINE_VERSION = 1, BASELINE_FILE_PREFIX = "SUITE_BASELINE", PLAN_FILE_NAME2, JUNIT_READINGS;
|
|
51829
|
-
var init_baseline = __esm(() => {
|
|
51830
|
-
PLAN_FILE_NAME2 = /^PLAN(?:_TRACKER)?(-.+)?\.md$/;
|
|
51831
|
-
JUNIT_READINGS = ["read", "missing", "unreadable"];
|
|
51832
|
-
});
|
|
51833
|
-
|
|
51834
52421
|
// src/suite/owns.ts
|
|
51835
52422
|
function none(reason, detail) {
|
|
51836
52423
|
return Object.freeze({ owns: null, reason, detail });
|
|
@@ -51914,7 +52501,7 @@ var init_owns = __esm(() => {
|
|
|
51914
52501
|
|
|
51915
52502
|
// src/suite/run.ts
|
|
51916
52503
|
import { existsSync as existsSync54, mkdirSync as mkdirSync21, readFileSync as readFileSync61, rmSync as rmSync11 } from "fs";
|
|
51917
|
-
import { dirname as
|
|
52504
|
+
import { dirname as dirname30, isAbsolute as isAbsolute21 } from "path";
|
|
51918
52505
|
function readNoTestFiles(stderr) {
|
|
51919
52506
|
return NO_TEST_FILES_LINE.test(stderr);
|
|
51920
52507
|
}
|
|
@@ -51978,8 +52565,18 @@ function closeCase(walk) {
|
|
|
51978
52565
|
if (open === null || !open.failed)
|
|
51979
52566
|
return;
|
|
51980
52567
|
const held = walk.failures.some((failure6) => failure6.file === open.file && failure6.name === open.name);
|
|
51981
|
-
if (
|
|
51982
|
-
|
|
52568
|
+
if (held)
|
|
52569
|
+
return;
|
|
52570
|
+
walk.failures.push(open.message === undefined ? { file: open.file, name: open.name } : { file: open.file, name: open.name, message: open.message });
|
|
52571
|
+
}
|
|
52572
|
+
function firstMessageLine(message) {
|
|
52573
|
+
const line = message?.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
52574
|
+
return line === "" ? undefined : line;
|
|
52575
|
+
}
|
|
52576
|
+
function failCase(open, attributes) {
|
|
52577
|
+
open.failed = true;
|
|
52578
|
+
if (open.message === undefined)
|
|
52579
|
+
open.message = firstMessageLine(attributes["message"]);
|
|
51983
52580
|
}
|
|
51984
52581
|
function openCase(walk, attributes) {
|
|
51985
52582
|
const [fileSuite, ...describes] = walk.suites;
|
|
@@ -52006,7 +52603,7 @@ function takeTag(walk, match) {
|
|
|
52006
52603
|
return;
|
|
52007
52604
|
}
|
|
52008
52605
|
if ((element === "failure" || element === "error") && closing !== "/" && walk.open !== null)
|
|
52009
|
-
walk.open
|
|
52606
|
+
failCase(walk.open, attributes);
|
|
52010
52607
|
}
|
|
52011
52608
|
function parseJunitFailures(xml) {
|
|
52012
52609
|
if (!/<testsuites\b/.test(xml) || !xml.includes("</testsuites>"))
|
|
@@ -52046,7 +52643,7 @@ async function runSuite(options) {
|
|
|
52046
52643
|
throw new RangeError("runSuite: an empty path list would run the whole project; leave paths out for that");
|
|
52047
52644
|
}
|
|
52048
52645
|
const command = suiteCommand(options.junitFile, options.paths, options.changedSince);
|
|
52049
|
-
mkdirSync21(
|
|
52646
|
+
mkdirSync21(dirname30(options.junitFile), { recursive: true });
|
|
52050
52647
|
rmSync11(options.junitFile, { force: true });
|
|
52051
52648
|
const spawn = options.spawn ?? spawnSuite;
|
|
52052
52649
|
const env = suiteEnv(options.env ?? process.env);
|
|
@@ -52181,7 +52778,7 @@ var init_scope2 = __esm(() => {
|
|
|
52181
52778
|
// src/start/suite-step.ts
|
|
52182
52779
|
import { existsSync as existsSync56, mkdirSync as mkdirSync22, readFileSync as readFileSync63, renameSync as renameSync10, writeFileSync as writeFileSync22 } from "fs";
|
|
52183
52780
|
import { constants as constants3 } from "os";
|
|
52184
|
-
import { basename as
|
|
52781
|
+
import { basename as basename22, dirname as dirname31, join as join84 } from "path";
|
|
52185
52782
|
function seamsOf(context) {
|
|
52186
52783
|
const seams = context.seams ?? {};
|
|
52187
52784
|
return {
|
|
@@ -52200,8 +52797,8 @@ function junitFileFor(repoRoot, sessionId, kind) {
|
|
|
52200
52797
|
}
|
|
52201
52798
|
function stageLedgerPathFor(trackerPath) {
|
|
52202
52799
|
const baselinePath = baselinePathFor(trackerPath);
|
|
52203
|
-
const name2 =
|
|
52204
|
-
return join84(
|
|
52800
|
+
const name2 = basename22(baselinePath).replace(BASELINE_FILE_PREFIX, STAGE_LEDGER_PREFIX);
|
|
52801
|
+
return join84(dirname31(baselinePath), name2);
|
|
52205
52802
|
}
|
|
52206
52803
|
function isLedgerEntry(value) {
|
|
52207
52804
|
if (typeof value !== "object" || value === null)
|
|
@@ -52235,11 +52832,11 @@ function readStageLedger(path4) {
|
|
|
52235
52832
|
}
|
|
52236
52833
|
if (typeof parsed !== "string")
|
|
52237
52834
|
return parsed;
|
|
52238
|
-
activeOutput().warn(`\u26A0\uFE0F ${
|
|
52835
|
+
activeOutput().warn(`\u26A0\uFE0F ${basename22(path4)} does not read (${parsed}); every finished stage's step is taken again.`);
|
|
52239
52836
|
return [];
|
|
52240
52837
|
}
|
|
52241
52838
|
function writeStageLedger(path4, entries) {
|
|
52242
|
-
mkdirSync22(
|
|
52839
|
+
mkdirSync22(dirname31(path4), { recursive: true });
|
|
52243
52840
|
const temporary = `${path4}.${process.pid}.tmp`;
|
|
52244
52841
|
writeFileSync22(temporary, `${JSON.stringify({ version: STAGE_LEDGER_VERSION, stages: entries }, null, 2)}
|
|
52245
52842
|
`, "utf8");
|
|
@@ -52254,7 +52851,7 @@ function addToLedger(trackerPath, added) {
|
|
|
52254
52851
|
try {
|
|
52255
52852
|
writeStageLedger(path4, [...entries, ...fresh]);
|
|
52256
52853
|
} catch (error) {
|
|
52257
|
-
activeOutput().warn(`\u26A0\uFE0F Could not write ${
|
|
52854
|
+
activeOutput().warn(`\u26A0\uFE0F Could not write ${basename22(path4)} (${messageOf(error)}); the next run takes these stage steps again.`);
|
|
52258
52855
|
}
|
|
52259
52856
|
}
|
|
52260
52857
|
function sameStage(a, b) {
|
|
@@ -52389,11 +52986,11 @@ async function ensureBaseline(context) {
|
|
|
52389
52986
|
const reading2 = readBaseline(path4);
|
|
52390
52987
|
if (reading2.state === "read") {
|
|
52391
52988
|
const known = reading2.baseline.failures.length;
|
|
52392
|
-
activeOutput().info(`\uD83D\uDCCF Suite baseline reused from ${
|
|
52989
|
+
activeOutput().info(`\uD83D\uDCCF Suite baseline reused from ${basename22(path4)} (${reading2.baseline.recordedAt}): ${known} known failure(s).`);
|
|
52393
52990
|
return { baseline: reading2.baseline, step: null, interrupted: false };
|
|
52394
52991
|
}
|
|
52395
52992
|
if (reading2.state === "unreadable")
|
|
52396
|
-
activeOutput().warn(`\u26A0\uFE0F ${
|
|
52993
|
+
activeOutput().warn(`\u26A0\uFE0F ${basename22(path4)} does not read (${reading2.reason}); recording the baseline again.`);
|
|
52397
52994
|
const commit = readHead(seams.git);
|
|
52398
52995
|
const result2 = await runOne(context, seams, "baseline", {});
|
|
52399
52996
|
const baseline = baselineOf(result2, seams.now(), commit);
|
|
@@ -52403,7 +53000,7 @@ async function ensureBaseline(context) {
|
|
|
52403
53000
|
try {
|
|
52404
53001
|
writeBaseline(path4, baseline);
|
|
52405
53002
|
} catch (error) {
|
|
52406
|
-
activeOutput().warn(`\u26A0\uFE0F Could not write ${
|
|
53003
|
+
activeOutput().warn(`\u26A0\uFE0F Could not write ${basename22(path4)} (${messageOf(error)}); the next run records the baseline again.`);
|
|
52407
53004
|
}
|
|
52408
53005
|
const outcome2 = settleStep(context, seams, settling);
|
|
52409
53006
|
const covered = completeStages(readFileSync63(context.trackerPath, "utf8"));
|
|
@@ -52614,9 +53211,34 @@ var init_suite_steps_run = __esm(() => {
|
|
|
52614
53211
|
init_suite_step();
|
|
52615
53212
|
});
|
|
52616
53213
|
|
|
53214
|
+
// src/start/pr-retarget.ts
|
|
53215
|
+
function retargetedLine(number, from, to) {
|
|
53216
|
+
return `\u21AA Retargeted pull request #${String(number)} from ${from} to ${to} (pr.base).`;
|
|
53217
|
+
}
|
|
53218
|
+
function refusedRetargetLine(number, from, to, detail) {
|
|
53219
|
+
return `\u26A0\uFE0F Could not retarget pull request #${String(number)} from ${from} to ${to} (pr.base): ${detail}. Retarget it by hand; the run carries on to the CI wait.`;
|
|
53220
|
+
}
|
|
53221
|
+
async function retargetPullRequest(pull2, base, seams) {
|
|
53222
|
+
const from = pull2.baseRefName;
|
|
53223
|
+
if (from === base)
|
|
53224
|
+
return { kind: "unchanged" };
|
|
53225
|
+
try {
|
|
53226
|
+
await seams.pulls.editBase(pull2.number, base);
|
|
53227
|
+
} catch (error) {
|
|
53228
|
+
const detail = messageOf(error);
|
|
53229
|
+
seams.output.warn(refusedRetargetLine(pull2.number, from, base, detail));
|
|
53230
|
+
return { kind: "refused", from, to: base, detail };
|
|
53231
|
+
}
|
|
53232
|
+
seams.output.info(retargetedLine(pull2.number, from, base));
|
|
53233
|
+
return { kind: "retargeted", from, to: base };
|
|
53234
|
+
}
|
|
53235
|
+
var init_pr_retarget = __esm(() => {
|
|
53236
|
+
init_config_sections();
|
|
53237
|
+
});
|
|
53238
|
+
|
|
52617
53239
|
// src/release/prepare.ts
|
|
52618
53240
|
import { mkdirSync as mkdirSync23, readFileSync as readFileSync64, writeFileSync as writeFileSync23 } from "fs";
|
|
52619
|
-
import { dirname as
|
|
53241
|
+
import { dirname as dirname32, join as join85, posix as posix5 } from "path";
|
|
52620
53242
|
function levelFields(reading2) {
|
|
52621
53243
|
return { level: reading2.level, levelSource: reading2.source, notesLevel: reading2.notesLevel };
|
|
52622
53244
|
}
|
|
@@ -52682,7 +53304,7 @@ function nameOf2(path4) {
|
|
|
52682
53304
|
}
|
|
52683
53305
|
function writeFragment(path4, resolved, text2) {
|
|
52684
53306
|
try {
|
|
52685
|
-
mkdirSync23(
|
|
53307
|
+
mkdirSync23(dirname32(resolved), { recursive: true });
|
|
52686
53308
|
writeFileSync23(resolved, text2);
|
|
52687
53309
|
return null;
|
|
52688
53310
|
} catch (error) {
|
|
@@ -52999,18 +53621,18 @@ function announceBody(write, count2) {
|
|
|
52999
53621
|
}
|
|
53000
53622
|
out.info(` ${noun} ${verb} now in the body of pull request #${write.number}.`);
|
|
53001
53623
|
}
|
|
53002
|
-
async function writeBody(io,
|
|
53003
|
-
const reading2 = io.readProvider(repoRoot);
|
|
53624
|
+
async function writeBody(io, input, write) {
|
|
53625
|
+
const reading2 = io.readProvider(input.repoRoot);
|
|
53004
53626
|
if (reading2.provider !== "gh") {
|
|
53005
53627
|
const problem2 = noProviderProblem(reading2);
|
|
53006
53628
|
activeOutput().info(` No pull request body carries ${JSON.stringify(write.lines.join(" "))}: ${problem2}.`);
|
|
53007
53629
|
return unwritten(null, problem2);
|
|
53008
53630
|
}
|
|
53009
|
-
const body = await carryIntoBody(io.pulls(repoRoot),
|
|
53631
|
+
const body = await carryIntoBody(io.pulls(input.repoRoot), input.branch, write);
|
|
53010
53632
|
announceBody(body, write.lines.length);
|
|
53011
53633
|
return body;
|
|
53012
53634
|
}
|
|
53013
|
-
async function reportFailure(io,
|
|
53635
|
+
async function reportFailure(io, input, outcome2, sentence, facts) {
|
|
53014
53636
|
const out = activeOutput();
|
|
53015
53637
|
if (outcome2 === "skipped") {
|
|
53016
53638
|
out.info(`
|
|
@@ -53021,7 +53643,7 @@ async function reportFailure(io, repoRoot, outcome2, sentence, facts) {
|
|
|
53021
53643
|
}
|
|
53022
53644
|
const parts = { forecast: null, levelReport: facts.levelReport };
|
|
53023
53645
|
const block2 = releaseBodyBlock(parts);
|
|
53024
|
-
const body = await writeBody(io,
|
|
53646
|
+
const body = await writeBody(io, input, { sentence, block: block2, lines: [sentence, ...releaseBodyLines(parts)] });
|
|
53025
53647
|
return { outcome: outcome2, sentence, forecast: null, body, ...facts };
|
|
53026
53648
|
}
|
|
53027
53649
|
async function reportForecast(io, input, forecast, facts) {
|
|
@@ -53031,12 +53653,12 @@ async function reportForecast(io, input, forecast, facts) {
|
|
|
53031
53653
|
out.warn(` \u26A0\uFE0F ${problem2}`);
|
|
53032
53654
|
const parts = { forecast, levelReport: facts.levelReport };
|
|
53033
53655
|
const block2 = releaseBodyBlock(parts);
|
|
53034
|
-
const body = await writeBody(io, input
|
|
53656
|
+
const body = await writeBody(io, input, { sentence: null, block: block2, lines: releaseBodyLines(parts) });
|
|
53035
53657
|
return { outcome: "released", sentence: null, forecast, body, ...facts };
|
|
53036
53658
|
}
|
|
53037
53659
|
async function finishRelease(input, seams = {}) {
|
|
53038
53660
|
const io = { ...RELEASE_STAGE_SEAMS, ...seams };
|
|
53039
|
-
const { preparation, repoRoot, settings } = input;
|
|
53661
|
+
const { preparation, repoRoot, branch, settings } = input;
|
|
53040
53662
|
if (preparation === null)
|
|
53041
53663
|
return NO_RELEASE;
|
|
53042
53664
|
const levelReport = releaseLevelReport({
|
|
@@ -53046,31 +53668,30 @@ async function finishRelease(input, seams = {}) {
|
|
|
53046
53668
|
});
|
|
53047
53669
|
const none2 = { fragment: null, subject: null, sha: null, levelReport };
|
|
53048
53670
|
if (preparation.kind === "skipped") {
|
|
53049
|
-
return reportFailure(io,
|
|
53671
|
+
return reportFailure(io, input, "skipped", preparation.sentence, none2);
|
|
53050
53672
|
}
|
|
53051
53673
|
const git = io.git(repoRoot);
|
|
53052
53674
|
const verification = io.verify(preparation, { git, settings });
|
|
53053
53675
|
if (verification.kind === "refused") {
|
|
53054
|
-
return reportFailure(io,
|
|
53676
|
+
return reportFailure(io, input, "refused", verification.sentence, none2);
|
|
53055
53677
|
}
|
|
53056
53678
|
const fragment = verification.path;
|
|
53057
53679
|
const subject = releaseSubject(preparation);
|
|
53058
53680
|
const commit = commitFragment(git, fragment, subject);
|
|
53059
53681
|
if (!commit.committed) {
|
|
53060
|
-
return reportFailure(io,
|
|
53682
|
+
return reportFailure(io, input, "uncommitted", commit.sentence, { ...none2, fragment, subject });
|
|
53061
53683
|
}
|
|
53062
53684
|
const out = activeOutput();
|
|
53063
53685
|
const sha = commit.sha;
|
|
53064
53686
|
const facts = { fragment, subject, sha, levelReport };
|
|
53065
53687
|
out.info(`
|
|
53066
53688
|
\uD83D\uDCE6 Committed ${commitName(sha, subject)} over ${fragment}.`);
|
|
53067
|
-
const branch = io.currentBranch();
|
|
53068
53689
|
const pushed = io.push(repoRoot, branch);
|
|
53069
53690
|
if (!pushed.ok) {
|
|
53070
53691
|
const said = pushed.output.trim() === "" ? "" : `: ${pushed.output.trim().split(`
|
|
53071
53692
|
`)[0] ?? ""}`;
|
|
53072
53693
|
const sentence = `${commitName(sha, subject)} was made but could not be pushed to ${branch}${said}`;
|
|
53073
|
-
return reportFailure(io,
|
|
53694
|
+
return reportFailure(io, input, "unpushed", sentence, facts);
|
|
53074
53695
|
}
|
|
53075
53696
|
out.info(` Pushed it to ${branch}.`);
|
|
53076
53697
|
const forecast = io.forecast({ git, settings, prepared: preparation, verified: verification, now: io.now() });
|
|
@@ -53088,7 +53709,6 @@ var init_release_stage = __esm(() => {
|
|
|
53088
53709
|
init_prepare();
|
|
53089
53710
|
init_verify2();
|
|
53090
53711
|
init_version2();
|
|
53091
|
-
init_git3();
|
|
53092
53712
|
RELEASE_STAGE_SEAMS = {
|
|
53093
53713
|
prepare: prepareRelease,
|
|
53094
53714
|
verify: verifyRelease,
|
|
@@ -53098,7 +53718,6 @@ var init_release_stage = __esm(() => {
|
|
|
53098
53718
|
push: pushBranch,
|
|
53099
53719
|
pulls: ghPullRequestsIn,
|
|
53100
53720
|
readProvider: (repoRoot) => resolvePrProvider({ configured: null, dir: repoRoot }),
|
|
53101
|
-
currentBranch: getCurrentBranch,
|
|
53102
53721
|
now: () => new Date
|
|
53103
53722
|
};
|
|
53104
53723
|
PLAN_HEADING = /^# +(.+)$/;
|
|
@@ -53432,7 +54051,7 @@ var init_promoted_check = __esm(() => {
|
|
|
53432
54051
|
|
|
53433
54052
|
// src/start/wrap-up.ts
|
|
53434
54053
|
import { posix as posix8 } from "path";
|
|
53435
|
-
function buildWrapUpPrompt(branch, planContent, openPullRequest = null, release = null, lessons = []) {
|
|
54054
|
+
function buildWrapUpPrompt(branch, base, planContent, openPullRequest = null, release = null, lessons = []) {
|
|
53436
54055
|
return [
|
|
53437
54056
|
"* Read `@progress.txt` in full.",
|
|
53438
54057
|
"* `@progress.txt` above names the file `progress.txt` at the repo root; the `@` is a reference marker, never part of a path. Do not create or write a file whose name starts with `@`.",
|
|
@@ -53445,7 +54064,7 @@ function buildWrapUpPrompt(branch, planContent, openPullRequest = null, release
|
|
|
53445
54064
|
"* If the merge touched `bun.lock` or any `package.json`, run `bun install --frozen-lockfile` and require it to pass BEFORE pushing. It is the one-second local reproduction of the CI install step, and it catches a lockfile that no longer matches the merged manifests \u2014 the failure mode where every CI job dies at its first step and nothing downstream runs. When it fails, do NOT hand-edit the lockfile: restore the base's copy (`git checkout origin/main -- bun.lock`), run a plain `bun install` so this branch's own dependencies are re-added, and confirm the frozen run then passes.",
|
|
53446
54065
|
...releaseBullets(release),
|
|
53447
54066
|
`* Commit these changes and push them to the CURRENT branch (${branch}). Never create a branch here: the work under review is this branch's, and a second branch splits one plan across two reviews.`,
|
|
53448
|
-
pullRequestStep(branch, openPullRequest),
|
|
54067
|
+
pullRequestStep(branch, base, openPullRequest),
|
|
53449
54068
|
"* Do not include Claude attribution in the commit or PR message.",
|
|
53450
54069
|
...lessonsSection(lessons),
|
|
53451
54070
|
"",
|
|
@@ -53455,15 +54074,15 @@ function buildWrapUpPrompt(branch, planContent, openPullRequest = null, release
|
|
|
53455
54074
|
].join(`
|
|
53456
54075
|
`);
|
|
53457
54076
|
}
|
|
53458
|
-
function
|
|
54077
|
+
function oneLine12(text2) {
|
|
53459
54078
|
return text2.trim().replace(/\s+/g, " ");
|
|
53460
54079
|
}
|
|
53461
54080
|
function lessonLines(lesson) {
|
|
53462
|
-
const artifact = lesson.artifact === undefined ||
|
|
54081
|
+
const artifact = lesson.artifact === undefined || oneLine12(lesson.artifact) === "" ? "none" : oneLine12(lesson.artifact);
|
|
53463
54082
|
return [
|
|
53464
54083
|
`- \`${lesson.id}\``,
|
|
53465
|
-
` - trigger: ${
|
|
53466
|
-
` - action: ${
|
|
54084
|
+
` - trigger: ${oneLine12(lesson.trigger)}`,
|
|
54085
|
+
` - action: ${oneLine12(lesson.action)}`,
|
|
53467
54086
|
` - artifact: ${artifact}`
|
|
53468
54087
|
];
|
|
53469
54088
|
}
|
|
@@ -53488,8 +54107,8 @@ function lessonsSection(lessons) {
|
|
|
53488
54107
|
"```"
|
|
53489
54108
|
];
|
|
53490
54109
|
}
|
|
53491
|
-
function pullRequestStep(branch, openPullRequest) {
|
|
53492
|
-
return openPullRequest === null ? `* No open PR was found for ${branch}: open one with \`gh pr create\`. If it answers that a PR already exists, push to that PR and update its body with \`gh pr edit\`; never open a second PR from a new branch.` : `* PR #${openPullRequest} is already open for ${branch}, likely opened by the plan's close-out: push to it and update its body with \`gh pr edit ${openPullRequest}\` so the description covers the promotions this session committed.`;
|
|
54110
|
+
function pullRequestStep(branch, base, openPullRequest) {
|
|
54111
|
+
return openPullRequest === null ? `* No open PR was found for ${branch}: open one with \`gh pr create --base ${base}\`. If it answers that a PR already exists, push to that PR and update its body with \`gh pr edit\`; never open a second PR from a new branch.` : `* PR #${openPullRequest} is already open for ${branch}, likely opened by the plan's close-out: push to it and update its body with \`gh pr edit ${openPullRequest}\` so the description covers the promotions this session committed.`;
|
|
53493
54112
|
}
|
|
53494
54113
|
function releaseBullets(release) {
|
|
53495
54114
|
if (release === null)
|
|
@@ -53551,11 +54170,11 @@ async function lessonsToPromote(learning) {
|
|
|
53551
54170
|
return [];
|
|
53552
54171
|
}
|
|
53553
54172
|
}
|
|
53554
|
-
async function preserveProgress(planContent, settingSources, release, serving, learning, checkout) {
|
|
54173
|
+
async function preserveProgress(planContent, settingSources, release, serving, learning, base, checkout) {
|
|
53555
54174
|
const branch = getCurrentBranch(checkout);
|
|
53556
54175
|
const openPullRequest = await openPullRequestNumber(checkout, branch);
|
|
53557
54176
|
return runWrapUpSession({
|
|
53558
|
-
buildPrompt: (lessons) => buildWrapUpPrompt(branch, planContent, openPullRequest, release, lessons),
|
|
54177
|
+
buildPrompt: (lessons) => buildWrapUpPrompt(branch, base, planContent, openPullRequest, release, lessons),
|
|
53559
54178
|
settingSources,
|
|
53560
54179
|
serving,
|
|
53561
54180
|
learning,
|
|
@@ -53620,8 +54239,8 @@ function missingPullRequestBullet(branch, previousMessage) {
|
|
|
53620
54239
|
return [opening, " Its final message follows, quoted:", "", ...quoted4(previousMessage), ""];
|
|
53621
54240
|
}
|
|
53622
54241
|
function buildWrapUpRetryPrompt(retry) {
|
|
53623
|
-
const
|
|
53624
|
-
const [classifierKey, ...rest] =
|
|
54242
|
+
const first = buildWrapUpPrompt(retry.branch, retry.base, retry.planContent, null, retry.release, retry.lessons);
|
|
54243
|
+
const [classifierKey, ...rest] = first.split(`
|
|
53625
54244
|
`);
|
|
53626
54245
|
return [
|
|
53627
54246
|
classifierKey,
|
|
@@ -53631,9 +54250,9 @@ function buildWrapUpRetryPrompt(retry) {
|
|
|
53631
54250
|
`);
|
|
53632
54251
|
}
|
|
53633
54252
|
function retryWrapUp(run) {
|
|
53634
|
-
const { branch, planContent, previousMessage } = run;
|
|
54253
|
+
const { branch, base, planContent, previousMessage } = run;
|
|
53635
54254
|
return runWrapUpSession({
|
|
53636
|
-
buildPrompt: (lessons) => buildWrapUpRetryPrompt({ branch, planContent, release: null, lessons, previousMessage }),
|
|
54255
|
+
buildPrompt: (lessons) => buildWrapUpRetryPrompt({ branch, base, planContent, release: null, lessons, previousMessage }),
|
|
53637
54256
|
settingSources: run.settingSources,
|
|
53638
54257
|
serving: run.serving,
|
|
53639
54258
|
learning: run.learning,
|
|
@@ -53678,8 +54297,9 @@ async function runWrapUp(input) {
|
|
|
53678
54297
|
planStub: planStub2,
|
|
53679
54298
|
planContent
|
|
53680
54299
|
});
|
|
54300
|
+
const base = resolveBaseBranch(createGitRunner(checkout), settings.prBase);
|
|
53681
54301
|
emitLoopEvent({ kind: "wrap-up", phase: "session" });
|
|
53682
|
-
const finalMessage = await preserveProgress(planContent, settingSources, release, serving, wrapUpLearning, checkout);
|
|
54302
|
+
const finalMessage = await preserveProgress(planContent, settingSources, release, serving, wrapUpLearning, base, checkout);
|
|
53683
54303
|
if (activeOutputMode() !== "text") {
|
|
53684
54304
|
const pullRequest = await openPullRequestNumber(checkout, expected2.branch);
|
|
53685
54305
|
emitLoopEvent(pullRequest === null ? { kind: "no-pr", reason: `no open pull request for ${expected2.branch}` } : { kind: "pr", number: pullRequest });
|
|
@@ -53693,13 +54313,14 @@ async function runWrapUp(input) {
|
|
|
53693
54313
|
dir: checkout
|
|
53694
54314
|
});
|
|
53695
54315
|
emitLoopEvent({ kind: "wrap-up", phase: "release" });
|
|
53696
|
-
const finish2 = await finishRelease({ repoRoot: checkout, settings, preparation: release }, { readProvider: readProvider2 });
|
|
54316
|
+
const finish2 = await finishRelease({ repoRoot: checkout, branch: expected2.branch, settings, preparation: release }, { readProvider: readProvider2 });
|
|
53697
54317
|
if (readProvider2().provider !== "none") {
|
|
53698
|
-
const delivery = await deliverPullRequest({ branch: expected2.branch, retries: settings.loopWrapUpRetries, previousMessage: finalMessage }, deliverySeamsIn({ ...input, fragment: finish2.fragment }));
|
|
54318
|
+
const delivery = await deliverPullRequest({ branch: expected2.branch, retries: settings.loopWrapUpRetries, previousMessage: finalMessage }, deliverySeamsIn({ ...input, base, fragment: finish2.fragment }));
|
|
53699
54319
|
if (delivery.kind === "interrupted")
|
|
53700
54320
|
return;
|
|
53701
54321
|
if (delivery.kind === "blocked")
|
|
53702
54322
|
throw new CommandExit(1, delivery.message);
|
|
54323
|
+
await retargetPullRequest(delivery.pull, base, { pulls: ghPullRequestsIn(checkout), output: activeOutput() });
|
|
53703
54324
|
}
|
|
53704
54325
|
if (ciWait) {
|
|
53705
54326
|
emitLoopEvent({ kind: "wrap-up", phase: "ci" });
|
|
@@ -53792,6 +54413,7 @@ function deliverySeamsIn(context) {
|
|
|
53792
54413
|
retry: (previousMessage) => retryWrapUp({
|
|
53793
54414
|
previousMessage,
|
|
53794
54415
|
branch: expected2.branch,
|
|
54416
|
+
base: context.base,
|
|
53795
54417
|
planContent: context.planContent,
|
|
53796
54418
|
settingSources: context.settingSources,
|
|
53797
54419
|
serving: context.serving,
|
|
@@ -53801,7 +54423,7 @@ function deliverySeamsIn(context) {
|
|
|
53801
54423
|
openRunnerPullRequest: async () => {
|
|
53802
54424
|
const runnerInput = runnerPrInputFor({
|
|
53803
54425
|
branch: expected2.branch,
|
|
53804
|
-
base:
|
|
54426
|
+
base: context.base,
|
|
53805
54427
|
planContent: context.planContent,
|
|
53806
54428
|
planStub: context.planStub,
|
|
53807
54429
|
notes: fragmentNotesIn(checkout, context.fragment)
|
|
@@ -53826,6 +54448,7 @@ var init_wrap_up_run = __esm(() => {
|
|
|
53826
54448
|
init_checkout_watch();
|
|
53827
54449
|
init_loop_events();
|
|
53828
54450
|
init_pr_lifecycle();
|
|
54451
|
+
init_pr_retarget();
|
|
53829
54452
|
init_release_stage();
|
|
53830
54453
|
init_runner_pr();
|
|
53831
54454
|
init_wrap_up_retry();
|
|
@@ -53868,7 +54491,7 @@ var init_schedule = __esm(() => {
|
|
|
53868
54491
|
|
|
53869
54492
|
// src/start.ts
|
|
53870
54493
|
import fs3 from "fs";
|
|
53871
|
-
import { homedir as
|
|
54494
|
+
import { homedir as homedir13 } from "os";
|
|
53872
54495
|
import path5 from "path";
|
|
53873
54496
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
53874
54497
|
async function start(args, repoRoot) {
|
|
@@ -53879,7 +54502,7 @@ async function start(args, repoRoot) {
|
|
|
53879
54502
|
return;
|
|
53880
54503
|
let runConfig;
|
|
53881
54504
|
try {
|
|
53882
|
-
runConfig = loadRunConfig({ root: repoRoot, home:
|
|
54505
|
+
runConfig = loadRunConfig({ root: repoRoot, home: homedir13() }, args);
|
|
53883
54506
|
} catch (error) {
|
|
53884
54507
|
if (!(error instanceof ConfigError))
|
|
53885
54508
|
throw error;
|
|
@@ -53910,7 +54533,7 @@ async function start(args, repoRoot) {
|
|
|
53910
54533
|
let expected2 = openCheckoutExpectation({ projectRoot: repoRoot, checkout, branch });
|
|
53911
54534
|
await announceRiskTotal({
|
|
53912
54535
|
repoRoot,
|
|
53913
|
-
home:
|
|
54536
|
+
home: homedir13(),
|
|
53914
54537
|
planPath,
|
|
53915
54538
|
config: runConfig.config,
|
|
53916
54539
|
environment: process.env
|
|
@@ -53935,16 +54558,16 @@ async function start(args, repoRoot) {
|
|
|
53935
54558
|
tiersRafa: runConfig.config.tiersRafa,
|
|
53936
54559
|
tiersSkills: runConfig.config.tiersSkills,
|
|
53937
54560
|
tiersAgents: runConfig.config.tiersAgents,
|
|
53938
|
-
home:
|
|
54561
|
+
home: homedir13()
|
|
53939
54562
|
},
|
|
53940
|
-
sync: { resolved: runConfig, home:
|
|
54563
|
+
sync: { resolved: runConfig, home: homedir13() },
|
|
53941
54564
|
claim: createStartPreflightClaim(repoRoot, runConfig.config),
|
|
53942
54565
|
drift: createStartPreflightDrift(repoRoot, runConfig.config)
|
|
53943
54566
|
});
|
|
53944
|
-
const serving = { root: repoRoot, run: session.id, home:
|
|
54567
|
+
const serving = { root: repoRoot, run: session.id, home: homedir13(), settings: runConfig.config };
|
|
53945
54568
|
const learning = {
|
|
53946
54569
|
kind: runConfig.config.learningAdapter,
|
|
53947
|
-
home:
|
|
54570
|
+
home: homedir13(),
|
|
53948
54571
|
blessMinConfidence: runConfig.config.learningBlessMinConfidence
|
|
53949
54572
|
};
|
|
53950
54573
|
const handout = {
|
|
@@ -53960,7 +54583,7 @@ async function start(args, repoRoot) {
|
|
|
53960
54583
|
};
|
|
53961
54584
|
const hubContact = createHubContact({
|
|
53962
54585
|
root: repoRoot,
|
|
53963
|
-
home:
|
|
54586
|
+
home: homedir13(),
|
|
53964
54587
|
resolved: runConfig,
|
|
53965
54588
|
warn: (message) => activeOutput().warn(message),
|
|
53966
54589
|
sessionId: session.id
|
|
@@ -54039,9 +54662,10 @@ async function start(args, repoRoot) {
|
|
|
54039
54662
|
inject: injectMode,
|
|
54040
54663
|
repoRoot,
|
|
54041
54664
|
checkout,
|
|
54042
|
-
home:
|
|
54665
|
+
home: homedir13(),
|
|
54043
54666
|
settingSources,
|
|
54044
54667
|
knownMissing,
|
|
54668
|
+
inherited: runStartFailures(trackerPath),
|
|
54045
54669
|
serving,
|
|
54046
54670
|
handout,
|
|
54047
54671
|
base
|
|
@@ -55605,7 +56229,7 @@ var init_review_gate = __esm(() => {
|
|
|
55605
56229
|
|
|
55606
56230
|
// src/plan.ts
|
|
55607
56231
|
import fs5 from "fs";
|
|
55608
|
-
import { homedir as
|
|
56232
|
+
import { homedir as homedir14 } from "os";
|
|
55609
56233
|
import path8 from "path";
|
|
55610
56234
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
55611
56235
|
function argValue2(args, flag2) {
|
|
@@ -55767,7 +56391,7 @@ function resolvePlanConfig(repoRoot, home) {
|
|
|
55767
56391
|
}
|
|
55768
56392
|
}
|
|
55769
56393
|
async function plan(args, repoRoot, registry = CORE_ADAPTER_REGISTRY) {
|
|
55770
|
-
const home =
|
|
56394
|
+
const home = homedir14();
|
|
55771
56395
|
const config = resolvePlanConfig(repoRoot, home);
|
|
55772
56396
|
const {
|
|
55773
56397
|
settingSources,
|
|
@@ -56860,8 +57484,16 @@ var init_merge_guard = __esm(() => {
|
|
|
56860
57484
|
});
|
|
56861
57485
|
|
|
56862
57486
|
// src/pr/unchecked.ts
|
|
56863
|
-
function uncheckedCaseOf(workflowCount) {
|
|
56864
|
-
|
|
57487
|
+
function uncheckedCaseOf(workflowCount, baseWorkflows = null) {
|
|
57488
|
+
if (workflowCount === 0)
|
|
57489
|
+
return "no-workflow";
|
|
57490
|
+
if (workflowCount !== null && baseWorkflows !== null && !baseWorkflows.runsOnPullRequests) {
|
|
57491
|
+
return "no-pull-request-workflow";
|
|
57492
|
+
}
|
|
57493
|
+
return "workflows-exist";
|
|
57494
|
+
}
|
|
57495
|
+
function noPullRequestWorkflowLine(base) {
|
|
57496
|
+
return `no workflow runs on pull requests into ${base}`;
|
|
56865
57497
|
}
|
|
56866
57498
|
function workflowCountLine(workflowCount) {
|
|
56867
57499
|
if (workflowCount === null)
|
|
@@ -56875,24 +57507,177 @@ function skipChecksCommand(number) {
|
|
|
56875
57507
|
function uncheckedQuestion(number) {
|
|
56876
57508
|
return `Merge #${number} with no checks? [y/N]`;
|
|
56877
57509
|
}
|
|
56878
|
-
function uncheckedComment(workflowCount) {
|
|
56879
|
-
|
|
57510
|
+
function uncheckedComment(workflowCount, baseLine = null) {
|
|
57511
|
+
const countLine2 = workflowCountLine(workflowCount);
|
|
57512
|
+
const paragraphs = baseLine === null ? [UNCHECKED_MERGE_SENTENCE, countLine2] : [UNCHECKED_MERGE_SENTENCE, countLine2, baseLine];
|
|
57513
|
+
return paragraphs.join(`
|
|
56880
57514
|
|
|
56881
|
-
|
|
57515
|
+
`);
|
|
56882
57516
|
}
|
|
56883
|
-
function
|
|
56884
|
-
|
|
56885
|
-
|
|
57517
|
+
function warningOf2(which, baseWorkflows) {
|
|
57518
|
+
if (which === "no-workflow")
|
|
57519
|
+
return NO_WORKFLOW_WARNING;
|
|
57520
|
+
if (which === "no-pull-request-workflow" && baseWorkflows !== null)
|
|
57521
|
+
return noPullRequestWorkflowLine(baseWorkflows.base);
|
|
57522
|
+
return WORKFLOWS_EXIST_WARNING;
|
|
57523
|
+
}
|
|
57524
|
+
function readUnchecked(number, workflowCount, baseWorkflows = null) {
|
|
57525
|
+
const which = uncheckedCaseOf(workflowCount, baseWorkflows);
|
|
57526
|
+
const warning = warningOf2(which, baseWorkflows);
|
|
57527
|
+
const isBaseCase = which === "no-pull-request-workflow";
|
|
56886
57528
|
return Object.freeze({
|
|
56887
57529
|
case: which,
|
|
56888
57530
|
warning: Object.freeze([workflowCountLine(workflowCount), warning]),
|
|
56889
|
-
yesMayAnswer: which
|
|
57531
|
+
yesMayAnswer: which !== "workflows-exist",
|
|
56890
57532
|
question: uncheckedQuestion(number),
|
|
56891
|
-
comment: uncheckedComment(workflowCount)
|
|
57533
|
+
comment: uncheckedComment(workflowCount, isBaseCase ? warning : null)
|
|
56892
57534
|
});
|
|
56893
57535
|
}
|
|
56894
57536
|
var NO_WORKFLOW_WARNING = "nothing on GitHub has tested this branch; you are relying on the checks run locally", WORKFLOWS_EXIST_WARNING = "CI may not have started (a path filter, a draft, Actions disabled, or it has not registered yet); this is probably not what you want", UNCHECKED_MERGE_SENTENCE = "Merged with no checks reported, by rafa pr merge --skip-checks.";
|
|
56895
57537
|
|
|
57538
|
+
// src/pr/workflow-triggers.ts
|
|
57539
|
+
function workflowsRunOnPullRequestsInto(workflowTexts, base) {
|
|
57540
|
+
return workflowTexts.some((text2) => workflowNamesBase(text2, base));
|
|
57541
|
+
}
|
|
57542
|
+
function workflowNamesBase(workflowText, base) {
|
|
57543
|
+
let document;
|
|
57544
|
+
try {
|
|
57545
|
+
document = Bun.YAML.parse(workflowText);
|
|
57546
|
+
} catch {
|
|
57547
|
+
return true;
|
|
57548
|
+
}
|
|
57549
|
+
return readWorkflow(document, base) !== false;
|
|
57550
|
+
}
|
|
57551
|
+
function readWorkflow(document, base) {
|
|
57552
|
+
if (!isPlainObject(document) || !("on" in document))
|
|
57553
|
+
return UNKNOWN;
|
|
57554
|
+
const on = document.on;
|
|
57555
|
+
if (typeof on === "string")
|
|
57556
|
+
return on === PULL_REQUEST_EVENT;
|
|
57557
|
+
if (Array.isArray(on)) {
|
|
57558
|
+
if (!on.every((event) => typeof event === "string"))
|
|
57559
|
+
return UNKNOWN;
|
|
57560
|
+
return on.includes(PULL_REQUEST_EVENT);
|
|
57561
|
+
}
|
|
57562
|
+
if (!isPlainObject(on))
|
|
57563
|
+
return UNKNOWN;
|
|
57564
|
+
if (!(PULL_REQUEST_EVENT in on))
|
|
57565
|
+
return false;
|
|
57566
|
+
return readPullRequestTrigger(on[PULL_REQUEST_EVENT], base);
|
|
57567
|
+
}
|
|
57568
|
+
function readPullRequestTrigger(trigger, base) {
|
|
57569
|
+
if (trigger === null || trigger === undefined)
|
|
57570
|
+
return true;
|
|
57571
|
+
if (!isPlainObject(trigger))
|
|
57572
|
+
return UNKNOWN;
|
|
57573
|
+
const branches = trigger.branches;
|
|
57574
|
+
const ignored = trigger["branches-ignore"];
|
|
57575
|
+
if (branches !== undefined && ignored !== undefined)
|
|
57576
|
+
return UNKNOWN;
|
|
57577
|
+
if (branches !== undefined)
|
|
57578
|
+
return readBranches2(branches, base);
|
|
57579
|
+
if (ignored !== undefined)
|
|
57580
|
+
return readBranchesIgnore(ignored, base);
|
|
57581
|
+
return true;
|
|
57582
|
+
}
|
|
57583
|
+
function readBranches2(filter, base) {
|
|
57584
|
+
const patterns = patternListOf(filter);
|
|
57585
|
+
if (patterns === null)
|
|
57586
|
+
return UNKNOWN;
|
|
57587
|
+
let named4 = false;
|
|
57588
|
+
for (const pattern of patterns) {
|
|
57589
|
+
const negated = pattern.startsWith("!");
|
|
57590
|
+
const matches = branchPatternMatches(negated ? pattern.slice(1) : pattern, base);
|
|
57591
|
+
if (matches === null)
|
|
57592
|
+
return UNKNOWN;
|
|
57593
|
+
if (matches)
|
|
57594
|
+
named4 = !negated;
|
|
57595
|
+
}
|
|
57596
|
+
return named4;
|
|
57597
|
+
}
|
|
57598
|
+
function readBranchesIgnore(filter, base) {
|
|
57599
|
+
const patterns = patternListOf(filter);
|
|
57600
|
+
if (patterns === null || patterns.some((pattern) => pattern.startsWith("!")))
|
|
57601
|
+
return UNKNOWN;
|
|
57602
|
+
let anyMatch = false;
|
|
57603
|
+
for (const pattern of patterns) {
|
|
57604
|
+
const matches = branchPatternMatches(pattern, base);
|
|
57605
|
+
if (matches === null)
|
|
57606
|
+
return UNKNOWN;
|
|
57607
|
+
anyMatch = anyMatch || matches;
|
|
57608
|
+
}
|
|
57609
|
+
return !anyMatch;
|
|
57610
|
+
}
|
|
57611
|
+
function patternListOf(filter) {
|
|
57612
|
+
if (typeof filter === "string")
|
|
57613
|
+
return [filter];
|
|
57614
|
+
if (Array.isArray(filter) && filter.every((pattern) => typeof pattern === "string"))
|
|
57615
|
+
return filter;
|
|
57616
|
+
return null;
|
|
57617
|
+
}
|
|
57618
|
+
function branchPatternMatches(pattern, branch) {
|
|
57619
|
+
const source = regexSourceOf(pattern);
|
|
57620
|
+
if (source === null)
|
|
57621
|
+
return null;
|
|
57622
|
+
try {
|
|
57623
|
+
return new RegExp(`^${source}$`).test(branch);
|
|
57624
|
+
} catch {
|
|
57625
|
+
return null;
|
|
57626
|
+
}
|
|
57627
|
+
}
|
|
57628
|
+
function regexSourceOf(pattern) {
|
|
57629
|
+
let source = "";
|
|
57630
|
+
let quantifiable = false;
|
|
57631
|
+
let index = 0;
|
|
57632
|
+
while (index < pattern.length) {
|
|
57633
|
+
const char = pattern.charAt(index);
|
|
57634
|
+
if (char === "*") {
|
|
57635
|
+
const isDouble = pattern.charAt(index + 1) === "*";
|
|
57636
|
+
source += isDouble ? ".*" : "[^/]*";
|
|
57637
|
+
index += isDouble ? 2 : 1;
|
|
57638
|
+
quantifiable = false;
|
|
57639
|
+
} else if (char === "?" || char === "+") {
|
|
57640
|
+
if (!quantifiable)
|
|
57641
|
+
return null;
|
|
57642
|
+
source += char;
|
|
57643
|
+
index += 1;
|
|
57644
|
+
quantifiable = false;
|
|
57645
|
+
} else if (char === "[") {
|
|
57646
|
+
const close = pattern.indexOf("]", index + 1);
|
|
57647
|
+
if (close === -1)
|
|
57648
|
+
return null;
|
|
57649
|
+
const body = pattern.slice(index + 1, close);
|
|
57650
|
+
if (!BRACKET_BODY.test(body))
|
|
57651
|
+
return null;
|
|
57652
|
+
source += `[${body}]`;
|
|
57653
|
+
index = close + 1;
|
|
57654
|
+
quantifiable = true;
|
|
57655
|
+
} else if (char === "\\") {
|
|
57656
|
+
if (index + 1 >= pattern.length)
|
|
57657
|
+
return null;
|
|
57658
|
+
source += escapeRegex(pattern.charAt(index + 1));
|
|
57659
|
+
index += 2;
|
|
57660
|
+
quantifiable = true;
|
|
57661
|
+
} else {
|
|
57662
|
+
source += escapeRegex(char);
|
|
57663
|
+
index += 1;
|
|
57664
|
+
quantifiable = true;
|
|
57665
|
+
}
|
|
57666
|
+
}
|
|
57667
|
+
return source;
|
|
57668
|
+
}
|
|
57669
|
+
function escapeRegex(char) {
|
|
57670
|
+
return char.replace(REGEX_SPECIAL, "\\$&");
|
|
57671
|
+
}
|
|
57672
|
+
function isPlainObject(value) {
|
|
57673
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
57674
|
+
}
|
|
57675
|
+
var PULL_REQUEST_EVENT = "pull_request", UNKNOWN = "unknown", BRACKET_BODY, REGEX_SPECIAL;
|
|
57676
|
+
var init_workflow_triggers = __esm(() => {
|
|
57677
|
+
BRACKET_BODY = /^[A-Za-z0-9-]+$/;
|
|
57678
|
+
REGEX_SPECIAL = /[.*+?^${}()|[\]\\/]/g;
|
|
57679
|
+
});
|
|
57680
|
+
|
|
56896
57681
|
// src/commands/pr/merge-unchecked.ts
|
|
56897
57682
|
function refusal13(head, reading2, summary, tail) {
|
|
56898
57683
|
return new CommandExit(1, [
|
|
@@ -56910,10 +57695,37 @@ async function workflowCountOf(pulls) {
|
|
|
56910
57695
|
return null;
|
|
56911
57696
|
}
|
|
56912
57697
|
}
|
|
57698
|
+
function workflowBlobsOf(listing2) {
|
|
57699
|
+
return listing2.split("\x00").map((entry) => LS_TREE_ENTRY2.exec(entry)).filter((match) => match !== null).filter(([, type, , path9]) => type === "blob" && WORKFLOW_ENDINGS.some((ending) => path9?.endsWith(ending) === true)).map(([, , oid]) => oid ?? "");
|
|
57700
|
+
}
|
|
57701
|
+
function readBaseWorkflows(git, base) {
|
|
57702
|
+
if (base === "" || base.startsWith("-"))
|
|
57703
|
+
return null;
|
|
57704
|
+
if (!git(["fetch", "origin", base]).ok)
|
|
57705
|
+
return null;
|
|
57706
|
+
const listed4 = git(["ls-tree", "--full-tree", "-z", `origin/${base}`, WORKFLOWS_DIR]);
|
|
57707
|
+
if (!listed4.ok)
|
|
57708
|
+
return null;
|
|
57709
|
+
const blobs = workflowBlobsOf(listed4.stdout);
|
|
57710
|
+
if (blobs.length === 0)
|
|
57711
|
+
return null;
|
|
57712
|
+
const shown2 = blobs.map((oid) => git(["cat-file", "blob", oid]));
|
|
57713
|
+
if (shown2.some((result2) => !result2.ok))
|
|
57714
|
+
return null;
|
|
57715
|
+
const texts = shown2.map((result2) => result2.stdout);
|
|
57716
|
+
return Object.freeze({ base, runsOnPullRequests: workflowsRunOnPullRequestsInto(texts, base) });
|
|
57717
|
+
}
|
|
57718
|
+
function baseWorkflowsOf(options, workflowCount) {
|
|
57719
|
+
const { git, base } = options;
|
|
57720
|
+
if (workflowCount === null || workflowCount === 0 || git === undefined || base === undefined)
|
|
57721
|
+
return null;
|
|
57722
|
+
return readBaseWorkflows(git, base);
|
|
57723
|
+
}
|
|
56913
57724
|
async function readUncheckedMerge(options) {
|
|
56914
57725
|
const { pulls, number, yes, summary } = options;
|
|
56915
57726
|
const workflowCount = await workflowCountOf(pulls);
|
|
56916
|
-
const
|
|
57727
|
+
const baseWorkflows = baseWorkflowsOf(options, workflowCount);
|
|
57728
|
+
const reading2 = readUnchecked(number, workflowCount, baseWorkflows);
|
|
56917
57729
|
if (yes && !reading2.yesMayAnswer) {
|
|
56918
57730
|
throw refusal13(`rafa pr merge refuses --yes for #${number} with no checks: workflows exist, or their count could not be read.`, reading2, summary, "That is more likely a fault than a choice, so a person has to answer. Run it again without --yes on a terminal.");
|
|
56919
57731
|
}
|
|
@@ -56922,7 +57734,7 @@ async function readUncheckedMerge(options) {
|
|
|
56922
57734
|
const tail = reading2.yesMayAnswer ? "Merge it without the question with --yes." : "--yes is refused here, so run it on a terminal and answer it.";
|
|
56923
57735
|
throw refusal13(`rafa pr merge asks before merging #${number} with no checks, and standard input is no terminal.`, reading2, summary, tail);
|
|
56924
57736
|
}
|
|
56925
|
-
return Object.freeze({ workflowCount, reading: reading2, yes });
|
|
57737
|
+
return Object.freeze({ workflowCount, baseWorkflows, reading: reading2, yes });
|
|
56926
57738
|
}
|
|
56927
57739
|
function isYes2(answer5) {
|
|
56928
57740
|
return answer5 !== null && YES_ANSWERS5.includes(answer5.trim().toLowerCase());
|
|
@@ -56956,12 +57768,15 @@ async function postUncheckedComment(pulls, number, unchecked, warn4) {
|
|
|
56956
57768
|
return null;
|
|
56957
57769
|
}
|
|
56958
57770
|
}
|
|
56959
|
-
var INDENT12 = " ", YES_ANSWERS5;
|
|
57771
|
+
var INDENT12 = " ", YES_ANSWERS5, WORKFLOWS_DIR = ".github/workflows/", WORKFLOW_ENDINGS, LS_TREE_ENTRY2;
|
|
56960
57772
|
var init_merge_unchecked = __esm(() => {
|
|
56961
57773
|
init_command();
|
|
56962
57774
|
init_confirm();
|
|
56963
57775
|
init_config_sections();
|
|
57776
|
+
init_workflow_triggers();
|
|
56964
57777
|
YES_ANSWERS5 = ["y", "yes"];
|
|
57778
|
+
WORKFLOW_ENDINGS = [".yml", ".yaml"];
|
|
57779
|
+
LS_TREE_ENTRY2 = /^\d+ (\w+) ([0-9a-f]+)\t(.+)$/s;
|
|
56965
57780
|
});
|
|
56966
57781
|
|
|
56967
57782
|
// src/commands/pr/merge.ts
|
|
@@ -57108,8 +57923,8 @@ async function reportAfterCleanUp(context, pr, seams, detail) {
|
|
|
57108
57923
|
return { unblocked: null, freed };
|
|
57109
57924
|
}
|
|
57110
57925
|
async function askToMerge(context, pr, seams, ask) {
|
|
57111
|
-
const { number, summary, yes, skipChecks } = ask;
|
|
57112
|
-
const unchecked = skipChecks ? await readUncheckedMerge({ pulls: pr.pulls, number, yes, summary, isTerminal: seams.isTerminal }) : null;
|
|
57926
|
+
const { number, summary, yes, skipChecks, base, git } = ask;
|
|
57927
|
+
const unchecked = skipChecks ? await readUncheckedMerge({ pulls: pr.pulls, number, yes, summary, isTerminal: seams.isTerminal, base, git }) : null;
|
|
57113
57928
|
if (unchecked === null && !yes)
|
|
57114
57929
|
requireTerminal(seams, summary);
|
|
57115
57930
|
context.output.info(summary);
|
|
@@ -57174,7 +57989,7 @@ async function runMerge2(context, seams) {
|
|
|
57174
57989
|
openPrompter: prompterOf(seams)
|
|
57175
57990
|
});
|
|
57176
57991
|
const summary = summaryLine2(detail, method);
|
|
57177
|
-
const answer5 = guard.go ? await askToMerge(context, pr, seams, { number: detail.number, summary, yes, skipChecks }) : { go: false, unchecked: null };
|
|
57992
|
+
const answer5 = guard.go ? await askToMerge(context, pr, seams, { number: detail.number, summary, yes, skipChecks, base: detail.baseRefName, git }) : { go: false, unchecked: null };
|
|
57178
57993
|
const answered3 = {
|
|
57179
57994
|
number: detail.number,
|
|
57180
57995
|
source: pick.source,
|
|
@@ -57238,7 +58053,7 @@ function createPrMergeCommand(seams = DEFAULT_MERGE_SEAMS) {
|
|
|
57238
58053
|
subject: "pr",
|
|
57239
58054
|
action: "merge",
|
|
57240
58055
|
summary: "merge a pull request and clean up both branches",
|
|
57241
|
-
description: "Merges one pull request through the GitHub CLI and then, in code, switches to the base branch," + " pulls it fast-forward only, deletes the head branch locally and on the remote where it is still there," + " and prunes, reporting each step; a head branch this checkout has no local branch for is reported as a" + " skipped local delete, and the rest still run. Refuses before it asks anything on a dirty working tree, on a pull" + " request that is not green or does not merge, and on a head branch checked out in another worktree. It" + " shows the pull request, its branches and the method and asks `Merge? [y/N]`; `--yes` skips the question," + " and without a terminal and without `--yes` it refuses. A pull request that reports no checks at all is" + " refused unless `--skip-checks` is given, which is refused on any pull request that does report checks;" + " with it the command reads how many workflows the repository defines, prints a warning for that case, asks" + " `Merge #<n> with no checks? [y/N]` (which `--yes` answers only where the repository defines no workflow)," + " and after the merge posts one comment on the pull request saying so. Where the release runs, the release" + " guard reads the branch before the question: a `collision` is refused unless" + " `dangerous.acceptVersionCollision` is true, and `pr.versionCollision` sets whether a `missing` or" + " `stale` branch merges silently, with a warning, after its own question, or not at all. A step that fails never undoes the merge: it" + " prints what is left as commands to paste and exits 1. After the merge it ticks the `Closes #<n>` line of" + " every issue the pull request closes on every open board whose checklist lists it, or on the roadmap issue while no issue carries `type:roadmap`, warning rather than failing when that write" + ' does not land. It ends by reading every open issue whose "Blocked by:" line names an issue this pull' + " request closes, asking whether to remove `spec:blocked` from each one whose blockers have all closed;" + " `--yes` does not answer that question, and every failure of that reading is a warning. With" + " board.relationships set to native it ticks no epic checklist and, in place of that reading, prints the" + " open issues the merge freed, whose blockers GitHub clears by itself, asking nothing and writing nothing. With" + " `--output=json` the pull request, the method, the steps that ran, the follow-ups, the roadmap tick and" + " the unblock reading, or the freed issues in native mode, are the data of the terminal result event. Refuses with exit code 2 where" + " `pr.provider` is not `gh`.",
|
|
58056
|
+
description: "Merges one pull request through the GitHub CLI and then, in code, switches to the base branch," + " pulls it fast-forward only, deletes the head branch locally and on the remote where it is still there," + " and prunes, reporting each step; a head branch this checkout has no local branch for is reported as a" + " skipped local delete, and the rest still run. Refuses before it asks anything on a dirty working tree, on a pull" + " request that is not green or does not merge, and on a head branch checked out in another worktree. It" + " shows the pull request, its branches and the method and asks `Merge? [y/N]`; `--yes` skips the question," + " and without a terminal and without `--yes` it refuses. A pull request that reports no checks at all is" + " refused unless `--skip-checks` is given, which is refused on any pull request that does report checks;" + " with it the command reads how many workflows the repository defines, prints a warning for that case, asks" + " `Merge #<n> with no checks? [y/N]` (which `--yes` answers only where the repository defines no workflow, or" + " where no workflow file on the base has a `pull_request` trigger naming it)," + " and after the merge posts one comment on the pull request saying so. Where the release runs, the release" + " guard reads the branch before the question: a `collision` is refused unless" + " `dangerous.acceptVersionCollision` is true, and `pr.versionCollision` sets whether a `missing` or" + " `stale` branch merges silently, with a warning, after its own question, or not at all. A step that fails never undoes the merge: it" + " prints what is left as commands to paste and exits 1. After the merge it ticks the `Closes #<n>` line of" + " every issue the pull request closes on every open board whose checklist lists it, or on the roadmap issue while no issue carries `type:roadmap`, warning rather than failing when that write" + ' does not land. It ends by reading every open issue whose "Blocked by:" line names an issue this pull' + " request closes, asking whether to remove `spec:blocked` from each one whose blockers have all closed;" + " `--yes` does not answer that question, and every failure of that reading is a warning. With" + " board.relationships set to native it ticks no epic checklist and, in place of that reading, prints the" + " open issues the merge freed, whose blockers GitHub clears by itself, asking nothing and writing nothing. With" + " `--output=json` the pull request, the method, the steps that ran, the follow-ups, the roadmap tick and" + " the unblock reading, or the freed issues in native mode, are the data of the terminal result event. Refuses with exit code 2 where" + " `pr.provider` is not `gh`.",
|
|
57242
58057
|
args: [
|
|
57243
58058
|
{
|
|
57244
58059
|
name: "n",
|
|
@@ -57254,7 +58069,7 @@ function createPrMergeCommand(seams = DEFAULT_MERGE_SEAMS) {
|
|
|
57254
58069
|
},
|
|
57255
58070
|
{
|
|
57256
58071
|
name: "skip-checks",
|
|
57257
|
-
description: "Merge a pull request that reports no checks at all, after a warning and its own question." + " Refused where the pull request reports any check; `--yes` beside it is refused where the repository" + " defines a workflow or its workflow count could not be read.",
|
|
58072
|
+
description: "Merge a pull request that reports no checks at all, after a warning and its own question." + " Refused where the pull request reports any check; `--yes` beside it is refused where the repository" + " defines a workflow that may run on pull requests into the base, or its workflow count could not be read.",
|
|
57258
58073
|
type: "boolean"
|
|
57259
58074
|
},
|
|
57260
58075
|
{
|
|
@@ -57358,7 +58173,7 @@ function readFailedStep(lines2) {
|
|
|
57358
58173
|
const head = groupStep(line.text);
|
|
57359
58174
|
if (head !== undefined)
|
|
57360
58175
|
group = head;
|
|
57361
|
-
if (!line.text.startsWith(
|
|
58176
|
+
if (!line.text.startsWith(ERROR_PREFIX2))
|
|
57362
58177
|
continue;
|
|
57363
58178
|
if (line.step !== undefined)
|
|
57364
58179
|
return { name: line.step, source: "step-column" };
|
|
@@ -57398,7 +58213,7 @@ function readFailedLog(text2, options = {}) {
|
|
|
57398
58213
|
total: lines2.length
|
|
57399
58214
|
};
|
|
57400
58215
|
}
|
|
57401
|
-
var FAILED_LOG_TAIL_LINES = 40, UNKNOWN_STEP = "UNKNOWN STEP", GROUP_STEP_PREFIX = "##[group]Run ",
|
|
58216
|
+
var FAILED_LOG_TAIL_LINES = 40, UNKNOWN_STEP = "UNKNOWN STEP", GROUP_STEP_PREFIX = "##[group]Run ", ERROR_PREFIX2 = "##[error]", COLUMN = "\t", BOM = "\uFEFF", TIMESTAMP, COLOUR, PREFIX34 = "pr triage evidence";
|
|
57402
58217
|
var init_evidence = __esm(() => {
|
|
57403
58218
|
init_config_sections();
|
|
57404
58219
|
TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z /;
|
|
@@ -58595,7 +59410,7 @@ var init_triage_guard = __esm(() => {
|
|
|
58595
59410
|
function ignoredLines(ignored) {
|
|
58596
59411
|
return ignored.map((comment) => `${INDENT15}${comment.reason}`);
|
|
58597
59412
|
}
|
|
58598
|
-
function
|
|
59413
|
+
function evidenceOf2(reading2) {
|
|
58599
59414
|
return reading2.logs?.chosen?.evidence;
|
|
58600
59415
|
}
|
|
58601
59416
|
function workflowCountOf2(reading2) {
|
|
@@ -58632,7 +59447,7 @@ function noFiles(reading2) {
|
|
|
58632
59447
|
return "none; the head merges cleanly";
|
|
58633
59448
|
}
|
|
58634
59449
|
function excerptBlock(reading2) {
|
|
58635
|
-
const evidence =
|
|
59450
|
+
const evidence = evidenceOf2(reading2);
|
|
58636
59451
|
if (evidence === undefined)
|
|
58637
59452
|
return [];
|
|
58638
59453
|
const excerpt2 = excerptLines(evidence);
|
|
@@ -58775,7 +59590,7 @@ var init_budget2 = __esm(() => {
|
|
|
58775
59590
|
|
|
58776
59591
|
// src/pr/plans/load.ts
|
|
58777
59592
|
import { existsSync as existsSync57, readFileSync as readFileSync69 } from "fs";
|
|
58778
|
-
import { dirname as
|
|
59593
|
+
import { dirname as dirname33, join as join87 } from "path";
|
|
58779
59594
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
58780
59595
|
function pinnedPlanFileName(triageClass) {
|
|
58781
59596
|
return `resolve-${triageClass}.md`;
|
|
@@ -58853,7 +59668,7 @@ var MODULE_DIR5, PINNED_PLANS_DIRNAME = "plans", PINNED_PLAN_CLASSES, SLOT_PATTE
|
|
|
58853
59668
|
var init_load2 = __esm(() => {
|
|
58854
59669
|
init_classes();
|
|
58855
59670
|
init_follow_up();
|
|
58856
|
-
MODULE_DIR5 =
|
|
59671
|
+
MODULE_DIR5 = dirname33(fileURLToPath7(import.meta.url));
|
|
58857
59672
|
PINNED_PLAN_CLASSES = Object.freeze([
|
|
58858
59673
|
...SIMPLE_TRIAGE_CLASSES,
|
|
58859
59674
|
...DEPENDENCY_BUMP_SIMPLE_CLASSES
|
|
@@ -59003,7 +59818,7 @@ var init_resolve_loop = __esm(() => {
|
|
|
59003
59818
|
|
|
59004
59819
|
// src/pr/triage/version-convert.ts
|
|
59005
59820
|
import { mkdirSync as mkdirSync25, readFileSync as readFileSync70, writeFileSync as writeFileSync26 } from "fs";
|
|
59006
|
-
import { dirname as
|
|
59821
|
+
import { dirname as dirname34, join as join89, posix as posix9 } from "path";
|
|
59007
59822
|
function conversionLevel(from, to) {
|
|
59008
59823
|
if (from === null)
|
|
59009
59824
|
return null;
|
|
@@ -59139,7 +59954,7 @@ function writeConversion(input, plan2) {
|
|
|
59139
59954
|
for (const [path9, text2] of writes) {
|
|
59140
59955
|
const target = join89(input.worktree, path9);
|
|
59141
59956
|
try {
|
|
59142
|
-
mkdirSync25(
|
|
59957
|
+
mkdirSync25(dirname34(target), { recursive: true });
|
|
59143
59958
|
writeFileSync26(target, text2);
|
|
59144
59959
|
} catch (error) {
|
|
59145
59960
|
return `${path9} could not be written in the worktree, which may hold part of the conversion: ${messageOf(error)}`;
|
|
@@ -59345,7 +60160,7 @@ function resolveCommentBody(run, detail, assessment, attempts, resolved, workflo
|
|
|
59345
60160
|
at: run.now(),
|
|
59346
60161
|
attempts,
|
|
59347
60162
|
resolved,
|
|
59348
|
-
evidence:
|
|
60163
|
+
evidence: evidenceOf2(run.reading),
|
|
59349
60164
|
workflowCount
|
|
59350
60165
|
});
|
|
59351
60166
|
return isDependabotBranch(detail) ? `${body}
|
|
@@ -59423,7 +60238,7 @@ async function endOfAttempt(run, before, waited) {
|
|
|
59423
60238
|
reading: reading2,
|
|
59424
60239
|
outcome: { triageClass: assessment.triageClass, step: assessment.step },
|
|
59425
60240
|
assessment,
|
|
59426
|
-
evidence: fresh === null ? before.evidence :
|
|
60241
|
+
evidence: fresh === null ? before.evidence : evidenceOf2(reading2),
|
|
59427
60242
|
green,
|
|
59428
60243
|
unchanged: reading2.rerun.decision === "already-assessed" ? ATTEMPT_OUTCOME_UNCHANGED : null
|
|
59429
60244
|
};
|
|
@@ -59432,7 +60247,7 @@ function stopLines(run, reason, end, removal) {
|
|
|
59432
60247
|
const prompt = end.reading.prompt ?? buildFollowUpPrompt({
|
|
59433
60248
|
pr: end.reading.detail,
|
|
59434
60249
|
assessment: end.assessment,
|
|
59435
|
-
evidence:
|
|
60250
|
+
evidence: evidenceOf2(end.reading)
|
|
59436
60251
|
});
|
|
59437
60252
|
return [
|
|
59438
60253
|
`\u26D4 rafa pr triage --resolve gave up on #${run.number}: ${reason}.`,
|
|
@@ -59505,7 +60320,7 @@ async function runAttempts(run, detail, first) {
|
|
|
59505
60320
|
const runLoop = run.runLoop ?? runResolveLoop;
|
|
59506
60321
|
const state = {
|
|
59507
60322
|
assessment: first,
|
|
59508
|
-
evidence:
|
|
60323
|
+
evidence: evidenceOf2(run.reading),
|
|
59509
60324
|
reading: run.reading,
|
|
59510
60325
|
previous: null,
|
|
59511
60326
|
spent: spentAttempts(run.reading.attempts),
|
|
@@ -59696,7 +60511,7 @@ async function commentOn3(options, reading2, existing) {
|
|
|
59696
60511
|
at: options.at,
|
|
59697
60512
|
attempts: reading2.attempts,
|
|
59698
60513
|
resolved: RESOLVED,
|
|
59699
|
-
evidence:
|
|
60514
|
+
evidence: evidenceOf2(reading2),
|
|
59700
60515
|
workflowCount: workflowCountOf2(reading2)
|
|
59701
60516
|
});
|
|
59702
60517
|
try {
|
|
@@ -61260,7 +62075,7 @@ var init_proposal_file = __esm(() => {
|
|
|
61260
62075
|
|
|
61261
62076
|
// src/backfill/backup.ts
|
|
61262
62077
|
import { copyFileSync as copyFileSync5, existsSync as existsSync59, mkdirSync as mkdirSync26 } from "fs";
|
|
61263
|
-
import { dirname as
|
|
62078
|
+
import { dirname as dirname35, isAbsolute as isAbsolute24, join as join93, relative as relative19, resolve as resolve37, sep as sep8 } from "path";
|
|
61264
62079
|
function backupDirectory(base) {
|
|
61265
62080
|
return join93(base, BACKUP_PATH);
|
|
61266
62081
|
}
|
|
@@ -61284,7 +62099,7 @@ function backupFile(path9, options) {
|
|
|
61284
62099
|
return { path: path9, kind: "missing", backup: null };
|
|
61285
62100
|
if (existsSync59(backup))
|
|
61286
62101
|
return { path: path9, kind: "kept", backup };
|
|
61287
|
-
mkdirSync26(
|
|
62102
|
+
mkdirSync26(dirname35(backup), { recursive: true });
|
|
61288
62103
|
copyFileSync5(path9, backup);
|
|
61289
62104
|
return { path: path9, kind: "copied", backup };
|
|
61290
62105
|
}
|
|
@@ -61314,7 +62129,7 @@ var init_backup = __esm(() => {
|
|
|
61314
62129
|
|
|
61315
62130
|
// src/backfill/derive.ts
|
|
61316
62131
|
import { readFileSync as readFileSync73, writeFileSync as writeFileSync27 } from "fs";
|
|
61317
|
-
import { basename as
|
|
62132
|
+
import { basename as basename23 } from "path";
|
|
61318
62133
|
function renderWhenToUse(trigger, prevents) {
|
|
61319
62134
|
return `${sentenceEnded(trigger)} Prevents: ${prevents}`;
|
|
61320
62135
|
}
|
|
@@ -61424,8 +62239,8 @@ function fileChanges(name2, data, body, options) {
|
|
|
61424
62239
|
return { changes, notes };
|
|
61425
62240
|
}
|
|
61426
62241
|
function planFile(entry, options) {
|
|
61427
|
-
if (
|
|
61428
|
-
return inert(entry, "skipped", `${
|
|
62242
|
+
if (basename23(entry.path) !== SKILL_FILE) {
|
|
62243
|
+
return inert(entry, "skipped", `${basename23(entry.path)} is no ${SKILL_FILE}, so it registers no skill`);
|
|
61429
62244
|
}
|
|
61430
62245
|
if (entry.name === null) {
|
|
61431
62246
|
return inert(entry, "skipped", "its place implies no skill name, so no stack can be derived from it");
|
|
@@ -62138,7 +62953,7 @@ var init_propose = __esm(() => {
|
|
|
62138
62953
|
|
|
62139
62954
|
// src/demote/select.ts
|
|
62140
62955
|
import { readdirSync as readdirSync21, readFileSync as readFileSync76, realpathSync as realpathSync17, statSync as statSync28 } from "fs";
|
|
62141
|
-
import { basename as
|
|
62956
|
+
import { basename as basename24, dirname as dirname36, join as join95 } from "path";
|
|
62142
62957
|
function realOrSelf(path9) {
|
|
62143
62958
|
try {
|
|
62144
62959
|
return realpathSync17(path9);
|
|
@@ -62154,12 +62969,12 @@ function samePath(left, right) {
|
|
|
62154
62969
|
return resolved === other || resolved.toLowerCase() === other.toLowerCase();
|
|
62155
62970
|
}
|
|
62156
62971
|
function resolveDemotionScope(dir, home) {
|
|
62157
|
-
if (
|
|
62972
|
+
if (basename24(dir) !== SKILLS_DIRECTORY)
|
|
62158
62973
|
return null;
|
|
62159
|
-
const claude =
|
|
62160
|
-
if (
|
|
62974
|
+
const claude = dirname36(dir);
|
|
62975
|
+
if (basename24(claude) !== CLAUDE_DIRECTORY)
|
|
62161
62976
|
return null;
|
|
62162
|
-
const base =
|
|
62977
|
+
const base = dirname36(claude);
|
|
62163
62978
|
const demotedDir = join95(base, DEMOTED_PATH);
|
|
62164
62979
|
return {
|
|
62165
62980
|
scope: samePath(base, home) ? "user" : "project",
|
|
@@ -62604,7 +63419,7 @@ var init_check3 = __esm(() => {
|
|
|
62604
63419
|
// src/demote/apply.ts
|
|
62605
63420
|
import { copyFileSync as copyFileSync6, existsSync as existsSync60, mkdirSync as mkdirSync28, mkdtempSync as mkdtempSync4, readdirSync as readdirSync22, readFileSync as readFileSync77, renameSync as renameSync11, rmSync as rmSync13, statSync as statSync30, unlinkSync as unlinkSync2, writeFileSync as writeFileSync29 } from "fs";
|
|
62606
63421
|
import { tmpdir as tmpdir6 } from "os";
|
|
62607
|
-
import { dirname as
|
|
63422
|
+
import { dirname as dirname37, join as join97 } from "path";
|
|
62608
63423
|
function isLearnedPath(path9) {
|
|
62609
63424
|
const segments = path9.split("/");
|
|
62610
63425
|
return segments.length === 2 && segments[0] === LEARNED_DIRECTORY && (segments[1] ?? "").endsWith(MARKDOWN_EXTENSION);
|
|
@@ -62661,7 +63476,7 @@ function planKept(scope, row3, file) {
|
|
|
62661
63476
|
return inert3(row3.path, "procedure", "kept", "procedure-shaped: left where it is, for the backfill");
|
|
62662
63477
|
}
|
|
62663
63478
|
const to = promotedPath(scope, row3.path);
|
|
62664
|
-
if (existsSync60(to) || existsSync60(
|
|
63479
|
+
if (existsSync60(to) || existsSync60(dirname37(to))) {
|
|
62665
63480
|
return inert3(row3.path, "procedure", "refused", `kept as a skill, but ${to} is already there`);
|
|
62666
63481
|
}
|
|
62667
63482
|
return {
|
|
@@ -62753,7 +63568,7 @@ function planDemotion(report5, scope, seams = DEFAULT_APPLY_SEAMS) {
|
|
|
62753
63568
|
}
|
|
62754
63569
|
}
|
|
62755
63570
|
function moveFile(move3) {
|
|
62756
|
-
mkdirSync28(
|
|
63571
|
+
mkdirSync28(dirname37(move3.to), { recursive: true });
|
|
62757
63572
|
try {
|
|
62758
63573
|
renameSync11(move3.from, move3.to);
|
|
62759
63574
|
} catch {
|
|
@@ -62764,7 +63579,7 @@ function moveFile(move3) {
|
|
|
62764
63579
|
function applyDemotion(plan2) {
|
|
62765
63580
|
for (const action of plan2.actions) {
|
|
62766
63581
|
if (action.write !== null) {
|
|
62767
|
-
mkdirSync28(
|
|
63582
|
+
mkdirSync28(dirname37(action.write.path), { recursive: true });
|
|
62768
63583
|
writeFileSync29(action.write.path, action.write.text, "utf8");
|
|
62769
63584
|
}
|
|
62770
63585
|
if (action.move !== null)
|
|
@@ -62830,7 +63645,7 @@ var init_draft = __esm(() => {
|
|
|
62830
63645
|
|
|
62831
63646
|
// src/commands/skill/demote.ts
|
|
62832
63647
|
import { existsSync as existsSync61, mkdirSync as mkdirSync29, readFileSync as readFileSync78, statSync as statSync31, writeFileSync as writeFileSync30 } from "fs";
|
|
62833
|
-
import { dirname as
|
|
63648
|
+
import { dirname as dirname38, resolve as resolve40 } from "path";
|
|
62834
63649
|
function readApply(value) {
|
|
62835
63650
|
if (value === undefined || value === false || value === "false")
|
|
62836
63651
|
return false;
|
|
@@ -62892,7 +63707,7 @@ function previousReport(path9, context) {
|
|
|
62892
63707
|
function runWrite(context, scope) {
|
|
62893
63708
|
const files = selectSources(scope);
|
|
62894
63709
|
const report5 = buildDemotionReport(files, previousReport(scope.reportPath, context));
|
|
62895
|
-
mkdirSync29(
|
|
63710
|
+
mkdirSync29(dirname38(scope.reportPath), { recursive: true });
|
|
62896
63711
|
writeFileSync30(scope.reportPath, writeDemotionReport(report5), "utf8");
|
|
62897
63712
|
const result2 = {
|
|
62898
63713
|
applied: false,
|
|
@@ -63797,6 +64612,504 @@ var init_status4 = __esm(() => {
|
|
|
63797
64612
|
status_default3 = createStatusCommand();
|
|
63798
64613
|
});
|
|
63799
64614
|
|
|
64615
|
+
// src/commands/update/stub.ts
|
|
64616
|
+
function stubMessage(action, issue5) {
|
|
64617
|
+
return `rafa update ${action} is a feature in development, tracked in ${ISSUES_URL}/${String(issue5)}.` + " Nothing was changed.";
|
|
64618
|
+
}
|
|
64619
|
+
function createUpdateStub(spec) {
|
|
64620
|
+
const command = {
|
|
64621
|
+
name: `update ${spec.action}`,
|
|
64622
|
+
subject: "update",
|
|
64623
|
+
action: spec.action,
|
|
64624
|
+
summary: `${spec.summary} (in development, #${String(spec.issue)})`,
|
|
64625
|
+
description: `${spec.summary}. In development: it refuses with exit code 1, naming #${String(spec.issue)},` + " and changes nothing.",
|
|
64626
|
+
args: [],
|
|
64627
|
+
flags: [],
|
|
64628
|
+
examples: [{ cmd: `rafa update ${spec.action}`, note: `Refuses, naming #${String(spec.issue)}; changes nothing.` }],
|
|
64629
|
+
outputs: ["text", "json"],
|
|
64630
|
+
needsProject: false,
|
|
64631
|
+
...spec.hidden === true ? { hidden: true } : {},
|
|
64632
|
+
run: () => Promise.reject(new CommandExit(1, stubMessage(spec.action, spec.issue)))
|
|
64633
|
+
};
|
|
64634
|
+
return Object.freeze(command);
|
|
64635
|
+
}
|
|
64636
|
+
var ISSUES_URL = "https://github.com/open-tomato/rafa/issues";
|
|
64637
|
+
var init_stub = __esm(() => {
|
|
64638
|
+
init_command();
|
|
64639
|
+
});
|
|
64640
|
+
|
|
64641
|
+
// src/commands/update/board.ts
|
|
64642
|
+
var board_default;
|
|
64643
|
+
var init_board = __esm(() => {
|
|
64644
|
+
init_stub();
|
|
64645
|
+
board_default = createUpdateStub({ action: "board", summary: "apply the board's newest setup and its migrations", issue: 717 });
|
|
64646
|
+
});
|
|
64647
|
+
|
|
64648
|
+
// src/project/lock.ts
|
|
64649
|
+
import { existsSync as existsSync63, lstatSync as lstatSync3, readFileSync as readFileSync79, writeFileSync as writeFileSync31 } from "fs";
|
|
64650
|
+
import { join as join98 } from "path";
|
|
64651
|
+
function lockPath(root) {
|
|
64652
|
+
return join98(root, LOCK_FILE);
|
|
64653
|
+
}
|
|
64654
|
+
function projectLockText(version2) {
|
|
64655
|
+
const lock = { lockfileVersion: LOCKFILE_VERSION, rafa: version2 };
|
|
64656
|
+
return `${JSON.stringify(lock, null, 2)}
|
|
64657
|
+
`;
|
|
64658
|
+
}
|
|
64659
|
+
function isLink(path9) {
|
|
64660
|
+
try {
|
|
64661
|
+
return lstatSync3(path9).isSymbolicLink();
|
|
64662
|
+
} catch {
|
|
64663
|
+
return false;
|
|
64664
|
+
}
|
|
64665
|
+
}
|
|
64666
|
+
function refuse16(problem2) {
|
|
64667
|
+
throw new ProjectLockError(`${LOCK_FILE} ${problem2}`);
|
|
64668
|
+
}
|
|
64669
|
+
function lockOf(value) {
|
|
64670
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
64671
|
+
refuse16("holds no JSON object");
|
|
64672
|
+
const record = value;
|
|
64673
|
+
const format = record["lockfileVersion"];
|
|
64674
|
+
if (format !== LOCKFILE_VERSION) {
|
|
64675
|
+
refuse16(`is in lockfileVersion ${String(format)}, and this rafa reads ${String(LOCKFILE_VERSION)}: a newer rafa wrote it`);
|
|
64676
|
+
}
|
|
64677
|
+
const rafa = record["rafa"];
|
|
64678
|
+
if (typeof rafa !== "string" || parseSemanticVersion(rafa) === null) {
|
|
64679
|
+
refuse16(`names rafa ${JSON.stringify(rafa)}, which is no version`);
|
|
64680
|
+
}
|
|
64681
|
+
return { lockfileVersion: LOCKFILE_VERSION, rafa };
|
|
64682
|
+
}
|
|
64683
|
+
function readProjectLock(root) {
|
|
64684
|
+
const path9 = lockPath(root);
|
|
64685
|
+
if (!existsSync63(path9)) {
|
|
64686
|
+
if (isLink(path9))
|
|
64687
|
+
refuse16("is a link to nothing");
|
|
64688
|
+
return null;
|
|
64689
|
+
}
|
|
64690
|
+
let text2;
|
|
64691
|
+
try {
|
|
64692
|
+
text2 = readFileSync79(path9, "utf8");
|
|
64693
|
+
} catch (error) {
|
|
64694
|
+
refuse16(`could not be read: ${messageOf(error)}`);
|
|
64695
|
+
}
|
|
64696
|
+
let value;
|
|
64697
|
+
try {
|
|
64698
|
+
value = JSON.parse(text2);
|
|
64699
|
+
} catch {
|
|
64700
|
+
refuse16("is not JSON");
|
|
64701
|
+
}
|
|
64702
|
+
return lockOf(value);
|
|
64703
|
+
}
|
|
64704
|
+
function writeProjectLock(root, version2) {
|
|
64705
|
+
writeFileSync31(lockPath(root), projectLockText(version2), "utf8");
|
|
64706
|
+
}
|
|
64707
|
+
var LOCK_FILE = "rafa.lock", LOCKFILE_VERSION = 1, ProjectLockError;
|
|
64708
|
+
var init_lock = __esm(() => {
|
|
64709
|
+
init_config_sections();
|
|
64710
|
+
init_version2();
|
|
64711
|
+
ProjectLockError = class ProjectLockError extends Error {
|
|
64712
|
+
name = "ProjectLockError";
|
|
64713
|
+
};
|
|
64714
|
+
});
|
|
64715
|
+
|
|
64716
|
+
// src/project/update-current.ts
|
|
64717
|
+
import { existsSync as existsSync64 } from "fs";
|
|
64718
|
+
import { join as join99 } from "path";
|
|
64719
|
+
function missingFolders(root) {
|
|
64720
|
+
return PROJECT_TREE.map((name2) => join99(SCOPE_DIR, name2)).filter((relative21) => !existsSync64(join99(root, relative21)));
|
|
64721
|
+
}
|
|
64722
|
+
async function readBoard3(access) {
|
|
64723
|
+
if (access.gh === null)
|
|
64724
|
+
return { kind: "skipped", reason: access.reason };
|
|
64725
|
+
try {
|
|
64726
|
+
const held = await listBoardLabels(access.gh);
|
|
64727
|
+
return { kind: "labels", missing: missingBoardLabels(held).map((label) => label.name) };
|
|
64728
|
+
} catch (error) {
|
|
64729
|
+
return { kind: "skipped", reason: messageOf(error) };
|
|
64730
|
+
}
|
|
64731
|
+
}
|
|
64732
|
+
function lockChangeOf(range) {
|
|
64733
|
+
if (range.kind === "adopt")
|
|
64734
|
+
return "created";
|
|
64735
|
+
return range.kind === "patch" ? "updated" : "unchanged";
|
|
64736
|
+
}
|
|
64737
|
+
function fromOf(range) {
|
|
64738
|
+
if (range.kind === "patch")
|
|
64739
|
+
return range.from;
|
|
64740
|
+
if (range.kind === "same")
|
|
64741
|
+
return range.to;
|
|
64742
|
+
return null;
|
|
64743
|
+
}
|
|
64744
|
+
async function readUpdatePlan(roots, range, board) {
|
|
64745
|
+
const { root, lockRoot } = roots;
|
|
64746
|
+
return {
|
|
64747
|
+
root,
|
|
64748
|
+
lockRoot,
|
|
64749
|
+
from: fromOf(range),
|
|
64750
|
+
to: range.to,
|
|
64751
|
+
folders: missingFolders(root),
|
|
64752
|
+
board: await readBoard3(board),
|
|
64753
|
+
deprecations: DEPRECATIONS,
|
|
64754
|
+
lock: lockChangeOf(range)
|
|
64755
|
+
};
|
|
64756
|
+
}
|
|
64757
|
+
function planChanges(plan2) {
|
|
64758
|
+
const labels = plan2.board.kind === "labels" ? plan2.board.missing.length : 0;
|
|
64759
|
+
return plan2.folders.length > 0 || labels > 0 || plan2.deprecations.length > 0 || plan2.lock !== "unchanged";
|
|
64760
|
+
}
|
|
64761
|
+
function foldersNowThere(plan2) {
|
|
64762
|
+
return plan2.folders.filter((relative21) => existsSync64(join99(plan2.root, relative21)));
|
|
64763
|
+
}
|
|
64764
|
+
function writeFolders(plan2) {
|
|
64765
|
+
if (plan2.folders.length === 0)
|
|
64766
|
+
return [];
|
|
64767
|
+
try {
|
|
64768
|
+
return writeProjectScope(plan2.root).filter((write) => write.change === "created").map((write) => write.path);
|
|
64769
|
+
} catch (error) {
|
|
64770
|
+
throw new UpdateStepError("folders", messageOf(error), foldersNowThere(plan2));
|
|
64771
|
+
}
|
|
64772
|
+
}
|
|
64773
|
+
function createdLabels(parts) {
|
|
64774
|
+
return parts.filter((part) => part.outcome === "created").map((part) => `label ${part.name}`);
|
|
64775
|
+
}
|
|
64776
|
+
async function applyUpdatePlan(plan2, gh) {
|
|
64777
|
+
const folders = writeFolders(plan2);
|
|
64778
|
+
const labels = gh !== null && plan2.board.kind === "labels" && plan2.board.missing.length > 0 ? await setUpLabels(gh) : [];
|
|
64779
|
+
if (plan2.lock !== "unchanged") {
|
|
64780
|
+
try {
|
|
64781
|
+
writeProjectLock(plan2.lockRoot, plan2.to);
|
|
64782
|
+
} catch (error) {
|
|
64783
|
+
throw new UpdateStepError("lock", messageOf(error), [...folders, ...createdLabels(labels)]);
|
|
64784
|
+
}
|
|
64785
|
+
}
|
|
64786
|
+
return { folders, labels, lock: plan2.lock };
|
|
64787
|
+
}
|
|
64788
|
+
var DEPRECATIONS, UpdateStepError;
|
|
64789
|
+
var init_update_current = __esm(() => {
|
|
64790
|
+
init_setup();
|
|
64791
|
+
init_config_sections();
|
|
64792
|
+
init_lock();
|
|
64793
|
+
init_scaffold();
|
|
64794
|
+
init_scope();
|
|
64795
|
+
DEPRECATIONS = Object.freeze([]);
|
|
64796
|
+
UpdateStepError = class UpdateStepError extends Error {
|
|
64797
|
+
step;
|
|
64798
|
+
written;
|
|
64799
|
+
name = "UpdateStepError";
|
|
64800
|
+
constructor(step2, message, written3) {
|
|
64801
|
+
super(message);
|
|
64802
|
+
this.step = step2;
|
|
64803
|
+
this.written = written3;
|
|
64804
|
+
}
|
|
64805
|
+
};
|
|
64806
|
+
});
|
|
64807
|
+
|
|
64808
|
+
// src/project/update-range.ts
|
|
64809
|
+
function refused6(reason, message) {
|
|
64810
|
+
return { kind: "refused", reason, message };
|
|
64811
|
+
}
|
|
64812
|
+
function readCurrentRange(recorded, installed) {
|
|
64813
|
+
const to = parseSemanticVersion(installed);
|
|
64814
|
+
if (to === null)
|
|
64815
|
+
return refused6("unreadable", `the installed rafa answers "${installed}", which is no version`);
|
|
64816
|
+
if (recorded === null)
|
|
64817
|
+
return { kind: "adopt", to: installed };
|
|
64818
|
+
const from = parseSemanticVersion(recorded);
|
|
64819
|
+
if (from === null)
|
|
64820
|
+
return refused6("unreadable", `the project records rafa "${recorded}", which is no version`);
|
|
64821
|
+
const order = compareVersions(to, from);
|
|
64822
|
+
if (order < 0) {
|
|
64823
|
+
return refused6("older", `the installed rafa ${installed} is older than the project's ${recorded}, and no downgrade runs before 1.0.0`);
|
|
64824
|
+
}
|
|
64825
|
+
if (to.major !== from.major) {
|
|
64826
|
+
return refused6("newer-major", `the installed rafa ${installed} is a newer major than the project's ${recorded}; that move is rafa update latest`);
|
|
64827
|
+
}
|
|
64828
|
+
if (to.minor !== from.minor) {
|
|
64829
|
+
return refused6("newer-minor", `the installed rafa ${installed} is a newer minor than the project's ${recorded}; that move is rafa update next`);
|
|
64830
|
+
}
|
|
64831
|
+
return order === 0 ? { kind: "same", to: installed } : { kind: "patch", from: recorded, to: installed };
|
|
64832
|
+
}
|
|
64833
|
+
var init_update_range = __esm(() => {
|
|
64834
|
+
init_status();
|
|
64835
|
+
init_version2();
|
|
64836
|
+
});
|
|
64837
|
+
|
|
64838
|
+
// src/commands/update/current.ts
|
|
64839
|
+
function checkoutLockRoot(root, dir) {
|
|
64840
|
+
const run = Bun.spawnSync(["git", "rev-parse", "--show-toplevel"], { cwd: dir, stdout: "pipe", stderr: "pipe" });
|
|
64841
|
+
if (run.exitCode !== 0)
|
|
64842
|
+
return root;
|
|
64843
|
+
const top = run.stdout.toString().trim();
|
|
64844
|
+
return top !== "" && mainCheckoutOf(top) === root ? top : root;
|
|
64845
|
+
}
|
|
64846
|
+
function readSwitch4(context, name2) {
|
|
64847
|
+
const value = context.flags[name2];
|
|
64848
|
+
if (value === undefined || value === false || value === "false")
|
|
64849
|
+
return false;
|
|
64850
|
+
if (value === true || value === "true")
|
|
64851
|
+
return true;
|
|
64852
|
+
throw new CommandExit(1, `\u274C --${name2} takes no value, and read "${String(value)}" as one
|
|
64853
|
+
Usage: ${USAGE47}`);
|
|
64854
|
+
}
|
|
64855
|
+
function couldNotRun(problem2) {
|
|
64856
|
+
return new CommandExit(2, `FAIL \u2014 ${problem2}
|
|
64857
|
+
nothing was changed.`);
|
|
64858
|
+
}
|
|
64859
|
+
function projectOf17(context) {
|
|
64860
|
+
if (context.project === null)
|
|
64861
|
+
throw new Error("rafa update current runs inside a project, and was handed none");
|
|
64862
|
+
return context.project;
|
|
64863
|
+
}
|
|
64864
|
+
function boardAccess(project, seams) {
|
|
64865
|
+
let configured;
|
|
64866
|
+
try {
|
|
64867
|
+
configured = loadConfig({ root: project.root, home: project.home }, {}, () => {}).config.prProvider;
|
|
64868
|
+
} catch (error) {
|
|
64869
|
+
throw couldNotRun(`config: ${messageOf(error)}`);
|
|
64870
|
+
}
|
|
64871
|
+
const provider = resolvePrProvider({ configured, dir: project.root, readRemote: seams.readRemote }).provider;
|
|
64872
|
+
return provider === "gh" ? { gh: seams.openGh(project.root), reason: "" } : { gh: null, reason: `pr.provider is ${provider}, so there is no GitHub board` };
|
|
64873
|
+
}
|
|
64874
|
+
function foldersText(plan2) {
|
|
64875
|
+
if (plan2.folders.length === 0)
|
|
64876
|
+
return "nothing missing";
|
|
64877
|
+
return `create ${plan2.folders.join(", ")}`;
|
|
64878
|
+
}
|
|
64879
|
+
function boardText2(plan2) {
|
|
64880
|
+
if (plan2.board.kind === "skipped")
|
|
64881
|
+
return `skipped: ${plan2.board.reason}`;
|
|
64882
|
+
if (plan2.board.missing.length === 0)
|
|
64883
|
+
return "every label present";
|
|
64884
|
+
return `create labels ${plan2.board.missing.join(", ")}`;
|
|
64885
|
+
}
|
|
64886
|
+
function deprecationsText(plan2) {
|
|
64887
|
+
if (plan2.deprecations.length === 0)
|
|
64888
|
+
return "none registered yet (#718)";
|
|
64889
|
+
return `${String(plan2.deprecations.length)} to run`;
|
|
64890
|
+
}
|
|
64891
|
+
function lockText(plan2) {
|
|
64892
|
+
const where = plan2.lockRoot === plan2.root ? "" : ` in ${plan2.lockRoot}`;
|
|
64893
|
+
if (plan2.lock === "unchanged")
|
|
64894
|
+
return `unchanged at ${plan2.to}${where}`;
|
|
64895
|
+
if (plan2.lock === "created")
|
|
64896
|
+
return `create at ${plan2.to}${where}`;
|
|
64897
|
+
return `update ${String(plan2.from)} \u2192 ${plan2.to}${where}`;
|
|
64898
|
+
}
|
|
64899
|
+
function planLines2(plan2) {
|
|
64900
|
+
const from = plan2.from ?? `no ${LOCK_FILE}`;
|
|
64901
|
+
return [
|
|
64902
|
+
`rafa update current: ${plan2.root}, from ${from} to ${plan2.to}`,
|
|
64903
|
+
` folders ${foldersText(plan2)}`,
|
|
64904
|
+
` board ${boardText2(plan2)}`,
|
|
64905
|
+
` deprecations ${deprecationsText(plan2)}`,
|
|
64906
|
+
` ${LOCK_FILE} ${lockText(plan2)}`
|
|
64907
|
+
];
|
|
64908
|
+
}
|
|
64909
|
+
function appliedLines(applied, plan2, context) {
|
|
64910
|
+
for (const path9 of applied.folders)
|
|
64911
|
+
context.output.info(`created ${path9}`);
|
|
64912
|
+
for (const part of applied.labels) {
|
|
64913
|
+
if (part.outcome === "created")
|
|
64914
|
+
context.output.info(`created label ${part.name}`);
|
|
64915
|
+
if (part.outcome === "refused")
|
|
64916
|
+
context.output.warn(`label ${part.name} was not created: ${part.detail}`);
|
|
64917
|
+
}
|
|
64918
|
+
if (applied.lock !== "unchanged")
|
|
64919
|
+
context.output.info(`wrote ${LOCK_FILE} at ${plan2.to}`);
|
|
64920
|
+
}
|
|
64921
|
+
async function confirmed3(yes, seams) {
|
|
64922
|
+
if (yes)
|
|
64923
|
+
return true;
|
|
64924
|
+
if (!seams.isTerminal()) {
|
|
64925
|
+
throw new CommandExit(1, "REFUSED \u2014 rafa update current asks before it changes anything, and there is no" + " terminal to ask on. Run it with --dry-run to see the changes, then with --yes to apply them." + `
|
|
64926
|
+
nothing was changed.`);
|
|
64927
|
+
}
|
|
64928
|
+
const prompter = seams.openPrompter();
|
|
64929
|
+
try {
|
|
64930
|
+
const answer5 = await prompter.ask(APPLY_QUESTION);
|
|
64931
|
+
return answer5 !== null && YES_ANSWERS7.includes(answer5.trim().toLowerCase());
|
|
64932
|
+
} finally {
|
|
64933
|
+
prompter.close();
|
|
64934
|
+
}
|
|
64935
|
+
}
|
|
64936
|
+
function readRange2(root, installed) {
|
|
64937
|
+
let recorded;
|
|
64938
|
+
try {
|
|
64939
|
+
recorded = readProjectLock(root)?.rafa ?? null;
|
|
64940
|
+
} catch (error) {
|
|
64941
|
+
throw couldNotRun(messageOf(error));
|
|
64942
|
+
}
|
|
64943
|
+
const range = readCurrentRange(recorded, installed);
|
|
64944
|
+
if (range.kind === "refused")
|
|
64945
|
+
throw new CommandExit(1, `REFUSED \u2014 ${range.message}
|
|
64946
|
+
nothing was changed.`);
|
|
64947
|
+
return range;
|
|
64948
|
+
}
|
|
64949
|
+
function refuseConflicts(project) {
|
|
64950
|
+
const conflicts2 = scaffoldConflicts(project.root, project.home);
|
|
64951
|
+
if (conflicts2.length === 0)
|
|
64952
|
+
return;
|
|
64953
|
+
throw couldNotRun(`the project scope cannot be written:
|
|
64954
|
+
${conflicts2.map((conflict) => ` ${conflict}`).join(`
|
|
64955
|
+
`)}`);
|
|
64956
|
+
}
|
|
64957
|
+
function stoppedPartWay(error) {
|
|
64958
|
+
const kept = error.written.length === 0 ? "Nothing was written before it." : `Already written, and kept: ${error.written.join(", ")}.`;
|
|
64959
|
+
return new CommandExit(2, `FAIL \u2014 ${error.step}: ${error.message}
|
|
64960
|
+
stopped part-way. ${kept}` + `
|
|
64961
|
+
Fix the cause and run rafa update current again: each step writes only what is missing.`);
|
|
64962
|
+
}
|
|
64963
|
+
async function applyAndReport(plan2, gh, context) {
|
|
64964
|
+
let applied;
|
|
64965
|
+
try {
|
|
64966
|
+
applied = await applyUpdatePlan(plan2, gh);
|
|
64967
|
+
} catch (error) {
|
|
64968
|
+
if (error instanceof UpdateStepError)
|
|
64969
|
+
throw stoppedPartWay(error);
|
|
64970
|
+
throw error;
|
|
64971
|
+
}
|
|
64972
|
+
appliedLines(applied, plan2, context);
|
|
64973
|
+
return applied;
|
|
64974
|
+
}
|
|
64975
|
+
function refusedLabels(applied) {
|
|
64976
|
+
const refused7 = applied.labels.filter((part) => part.outcome === "refused");
|
|
64977
|
+
if (refused7.length === 0)
|
|
64978
|
+
return null;
|
|
64979
|
+
return new CommandExit(1, `${String(refused7.length)} board label(s) were not created; everything else was applied.` + " Run rafa update current again once gh can create them.");
|
|
64980
|
+
}
|
|
64981
|
+
function stopLine2(plan2, dryRun) {
|
|
64982
|
+
if (!planChanges(plan2))
|
|
64983
|
+
return "Nothing to change.";
|
|
64984
|
+
if (dryRun)
|
|
64985
|
+
return "Dry run: nothing was written. Run rafa update current to apply it.";
|
|
64986
|
+
return null;
|
|
64987
|
+
}
|
|
64988
|
+
async function runUpdateCurrent(context, seams) {
|
|
64989
|
+
expectNoArgument(context.args, USAGE47);
|
|
64990
|
+
const dryRun = readSwitch4(context, "dry-run");
|
|
64991
|
+
const yes = readSwitch4(context, "yes");
|
|
64992
|
+
const project = projectOf17(context);
|
|
64993
|
+
const lockRoot = seams.lockRootOf(project.root);
|
|
64994
|
+
const range = readRange2(lockRoot, seams.installed);
|
|
64995
|
+
refuseConflicts(project);
|
|
64996
|
+
const access = boardAccess(project, seams);
|
|
64997
|
+
const plan2 = await readUpdatePlan({ root: project.root, lockRoot }, range, access);
|
|
64998
|
+
for (const line of planLines2(plan2))
|
|
64999
|
+
context.output.info(line);
|
|
65000
|
+
const finish2 = (applied2) => {
|
|
65001
|
+
if (context.outputMode === "json")
|
|
65002
|
+
context.output.result({ plan: plan2, applied: applied2 });
|
|
65003
|
+
};
|
|
65004
|
+
const stop = stopLine2(plan2, dryRun);
|
|
65005
|
+
if (stop !== null) {
|
|
65006
|
+
context.output.info(stop);
|
|
65007
|
+
finish2(null);
|
|
65008
|
+
return;
|
|
65009
|
+
}
|
|
65010
|
+
context.output.warn("rafa cannot roll these changes back. Run it with --dry-run first to only see them.");
|
|
65011
|
+
if (!await confirmed3(yes, seams)) {
|
|
65012
|
+
context.output.info("Nothing was changed.");
|
|
65013
|
+
finish2(null);
|
|
65014
|
+
return;
|
|
65015
|
+
}
|
|
65016
|
+
const applied = await applyAndReport(plan2, access.gh, context);
|
|
65017
|
+
finish2(applied);
|
|
65018
|
+
const refusal16 = refusedLabels(applied);
|
|
65019
|
+
if (refusal16 !== null)
|
|
65020
|
+
throw refusal16;
|
|
65021
|
+
}
|
|
65022
|
+
function createUpdateCurrentCommand(seams = DEFAULT_UPDATE_CURRENT_SEAMS) {
|
|
65023
|
+
const command = {
|
|
65024
|
+
name: "update current",
|
|
65025
|
+
subject: "update",
|
|
65026
|
+
action: "current",
|
|
65027
|
+
summary: "bring this project to the installed rafa, within its patch range",
|
|
65028
|
+
description: "Brings the project to the installed rafa when the installed version shares the major and" + ` minor of the version \`${LOCK_FILE}\` records at the project root, and is the same patch or newer.` + " A project with no lock is adopted at the installed version. It creates the missing `.rafa/` folders" + " and the missing board labels on a GitHub board, runs the deprecations step (none registered yet)," + ` and writes \`${LOCK_FILE}\`. It prints every change first; \`--dry-run\` stops there. Otherwise it` + " warns that the changes cannot be rolled back and asks once, `--yes` answering; with no terminal and" + " no `--yes` it refuses with exit code 1. A newer minor or major, or an installed rafa older than the" + " lock, refuses with exit code 1, and so does a board label `gh` would not create, once everything" + " else is applied. A lock or config that cannot be read, or a `.rafa/` path holding something else," + " exits 2 before anything is asked; a folder or the lock that cannot be written exits 2 naming the" + " step and what was already written. In a linked worktree the lock is the worktree's, on its branch." + " With `--output=json` the plan and what was applied are the data of the terminal result event.",
|
|
65029
|
+
args: [],
|
|
65030
|
+
flags: [
|
|
65031
|
+
{ name: "dry-run", description: "Print every change and write nothing.", type: "boolean" },
|
|
65032
|
+
{ name: "yes", description: "Apply the changes without asking.", type: "boolean" }
|
|
65033
|
+
],
|
|
65034
|
+
examples: [
|
|
65035
|
+
{ cmd: "rafa update current --dry-run", note: "Lists the folders, labels and lock it would write, and writes nothing." },
|
|
65036
|
+
{ cmd: "rafa update current --yes", note: "Applies them without asking, as a script or a loop host runs it." }
|
|
65037
|
+
],
|
|
65038
|
+
outputs: ["text", "json"],
|
|
65039
|
+
run: async (context) => runUpdateCurrent(context, seams)
|
|
65040
|
+
};
|
|
65041
|
+
return Object.freeze(command);
|
|
65042
|
+
}
|
|
65043
|
+
var USAGE47 = "rafa update current [--dry-run] [--yes]", APPLY_QUESTION = "Apply these changes? [y/N] ", YES_ANSWERS7, DEFAULT_UPDATE_CURRENT_SEAMS, current_default2;
|
|
65044
|
+
var init_current2 = __esm(() => {
|
|
65045
|
+
init_github();
|
|
65046
|
+
init_command();
|
|
65047
|
+
init_confirm();
|
|
65048
|
+
init_version();
|
|
65049
|
+
init_config_load();
|
|
65050
|
+
init_config_sections();
|
|
65051
|
+
init_provider();
|
|
65052
|
+
init_lock();
|
|
65053
|
+
init_scaffold();
|
|
65054
|
+
init_update_current();
|
|
65055
|
+
init_update_range();
|
|
65056
|
+
init_worktree_root();
|
|
65057
|
+
init_project_id();
|
|
65058
|
+
init_plan_files();
|
|
65059
|
+
YES_ANSWERS7 = ["y", "yes"];
|
|
65060
|
+
DEFAULT_UPDATE_CURRENT_SEAMS = Object.freeze({
|
|
65061
|
+
installed: RAFA_VERSION,
|
|
65062
|
+
openGh: (root) => createGhRunner({ cwd: root }),
|
|
65063
|
+
readRemote: gitRemoteUrl,
|
|
65064
|
+
isTerminal: () => process.stdin.isTTY === true,
|
|
65065
|
+
openPrompter: () => createLinePrompter(process.stdin, process.stderr),
|
|
65066
|
+
lockRootOf: (root) => checkoutLockRoot(root, process.cwd())
|
|
65067
|
+
});
|
|
65068
|
+
current_default2 = createUpdateCurrentCommand();
|
|
65069
|
+
});
|
|
65070
|
+
|
|
65071
|
+
// src/commands/update/latest.ts
|
|
65072
|
+
var latest_default;
|
|
65073
|
+
var init_latest = __esm(() => {
|
|
65074
|
+
init_stub();
|
|
65075
|
+
latest_default = createUpdateStub({ action: "latest", summary: "move this project to the newest version", issue: 716 });
|
|
65076
|
+
});
|
|
65077
|
+
|
|
65078
|
+
// src/commands/update/next.ts
|
|
65079
|
+
var next_default2;
|
|
65080
|
+
var init_next2 = __esm(() => {
|
|
65081
|
+
init_stub();
|
|
65082
|
+
next_default2 = createUpdateStub({ action: "next", summary: "move this project to the newest minor of its major", issue: 716 });
|
|
65083
|
+
});
|
|
65084
|
+
|
|
65085
|
+
// src/commands/update/port.ts
|
|
65086
|
+
var port_default;
|
|
65087
|
+
var init_port = __esm(() => {
|
|
65088
|
+
init_stub();
|
|
65089
|
+
port_default = createUpdateStub({ action: "port", summary: "move this project to a version, as update project does", issue: 716, hidden: true });
|
|
65090
|
+
});
|
|
65091
|
+
|
|
65092
|
+
// src/commands/update/project.ts
|
|
65093
|
+
var project_default;
|
|
65094
|
+
var init_project = __esm(() => {
|
|
65095
|
+
init_stub();
|
|
65096
|
+
project_default = createUpdateStub({ action: "project", summary: "move this project to a version, with its migrations", issue: 716 });
|
|
65097
|
+
});
|
|
65098
|
+
|
|
65099
|
+
// src/commands/update/rafa.ts
|
|
65100
|
+
var rafa_default;
|
|
65101
|
+
var init_rafa = __esm(() => {
|
|
65102
|
+
init_stub();
|
|
65103
|
+
rafa_default = createUpdateStub({ action: "rafa", summary: "move this project to a version, as update project does", issue: 716, hidden: true });
|
|
65104
|
+
});
|
|
65105
|
+
|
|
65106
|
+
// src/commands/update/self.ts
|
|
65107
|
+
var self_default;
|
|
65108
|
+
var init_self = __esm(() => {
|
|
65109
|
+
init_stub();
|
|
65110
|
+
self_default = createUpdateStub({ action: "self", summary: "update rafa itself, from npm, a pinned version or another source", issue: 715 });
|
|
65111
|
+
});
|
|
65112
|
+
|
|
63800
65113
|
// src/usage.ts
|
|
63801
65114
|
async function usage(_args) {
|
|
63802
65115
|
const pct = await getClaudeUsagePercent();
|
|
@@ -63928,6 +65241,14 @@ var init_commands2 = __esm(() => {
|
|
|
63928
65241
|
init_show8();
|
|
63929
65242
|
init_status4();
|
|
63930
65243
|
init_switch();
|
|
65244
|
+
init_board();
|
|
65245
|
+
init_current2();
|
|
65246
|
+
init_latest();
|
|
65247
|
+
init_next2();
|
|
65248
|
+
init_port();
|
|
65249
|
+
init_project();
|
|
65250
|
+
init_rafa();
|
|
65251
|
+
init_self();
|
|
63931
65252
|
init_usage2();
|
|
63932
65253
|
CORE_SUBJECTS = Object.freeze([
|
|
63933
65254
|
{ name: "plan", summary: "create plans from specs; list, show and validate them; read their risk and needs" },
|
|
@@ -63942,7 +65263,8 @@ var init_commands2 = __esm(() => {
|
|
|
63942
65263
|
{ name: "release", summary: "read the release state of the project; settle the waiting fragments into a version; tag the commit that set it" },
|
|
63943
65264
|
{ name: "board", summary: "list the boards with their owner, epic count, and which is current and home" },
|
|
63944
65265
|
{ name: "epic", summary: "show one epic's issues as the Roadmap table; create an epic; defer or promote it; move an issue to it; close it through the gate or cancel it" },
|
|
63945
|
-
{ name: "claim", summary: "give up this device's claim on an issue; hand it to another store or withdraw the offer; accept a handover; take over a stale claim" }
|
|
65266
|
+
{ name: "claim", summary: "give up this device's claim on an issue; hand it to another store or withdraw the offer; accept a handover; take over a stale claim" },
|
|
65267
|
+
{ name: "update", summary: "bring this project to the installed rafa; the other updates are in development" }
|
|
63946
65268
|
]);
|
|
63947
65269
|
CORE_COMMANDS = Object.freeze([
|
|
63948
65270
|
create_default2,
|
|
@@ -64014,6 +65336,14 @@ var init_commands2 = __esm(() => {
|
|
|
64014
65336
|
hand_default,
|
|
64015
65337
|
accept_default,
|
|
64016
65338
|
take_default,
|
|
65339
|
+
current_default2,
|
|
65340
|
+
self_default,
|
|
65341
|
+
project_default,
|
|
65342
|
+
rafa_default,
|
|
65343
|
+
port_default,
|
|
65344
|
+
board_default,
|
|
65345
|
+
next_default2,
|
|
65346
|
+
latest_default,
|
|
64017
65347
|
status_default3,
|
|
64018
65348
|
next_default,
|
|
64019
65349
|
roadmap_default,
|
|
@@ -64029,7 +65359,7 @@ var init_commands2 = __esm(() => {
|
|
|
64029
65359
|
});
|
|
64030
65360
|
|
|
64031
65361
|
// src/rafa.ts
|
|
64032
|
-
import { homedir as
|
|
65362
|
+
import { homedir as homedir15 } from "os";
|
|
64033
65363
|
|
|
64034
65364
|
// src/cli/dispatch.ts
|
|
64035
65365
|
init_active();
|
|
@@ -64644,12 +65974,12 @@ init_config_sections();
|
|
|
64644
65974
|
init_sessions();
|
|
64645
65975
|
init_scope();
|
|
64646
65976
|
init_sections2();
|
|
64647
|
-
import { mkdirSync as mkdirSync30, readFileSync as
|
|
64648
|
-
import { dirname as
|
|
65977
|
+
import { mkdirSync as mkdirSync30, readFileSync as readFileSync80, renameSync as renameSync12, writeFileSync as writeFileSync32 } from "fs";
|
|
65978
|
+
import { dirname as dirname39, join as join100 } from "path";
|
|
64649
65979
|
var SEEN_FILE = "status-seen.json";
|
|
64650
65980
|
var SEEN_VERSION = 1;
|
|
64651
65981
|
function seenFilePath(root) {
|
|
64652
|
-
return
|
|
65982
|
+
return join100(scopeAt(root).dir, SEEN_FILE);
|
|
64653
65983
|
}
|
|
64654
65984
|
function readSeenSessions(root, isAlive) {
|
|
64655
65985
|
const sessions = {};
|
|
@@ -64704,7 +66034,7 @@ function asSnapshot(value) {
|
|
|
64704
66034
|
function readSeenFile(root) {
|
|
64705
66035
|
let text2;
|
|
64706
66036
|
try {
|
|
64707
|
-
text2 =
|
|
66037
|
+
text2 = readFileSync80(seenFilePath(root), "utf8");
|
|
64708
66038
|
} catch {
|
|
64709
66039
|
return null;
|
|
64710
66040
|
}
|
|
@@ -64716,9 +66046,9 @@ function readSeenFile(root) {
|
|
|
64716
66046
|
}
|
|
64717
66047
|
function writeSeenFile(root, snapshot) {
|
|
64718
66048
|
const file = seenFilePath(root);
|
|
64719
|
-
mkdirSync30(
|
|
66049
|
+
mkdirSync30(dirname39(file), { recursive: true });
|
|
64720
66050
|
const temporary = `${file}.${String(process.pid)}.tmp`;
|
|
64721
|
-
|
|
66051
|
+
writeFileSync32(temporary, `${JSON.stringify(snapshot, null, 2)}
|
|
64722
66052
|
`);
|
|
64723
66053
|
renameSync12(temporary, file);
|
|
64724
66054
|
}
|
|
@@ -64761,7 +66091,7 @@ function createStatusHook(seams = {}) {
|
|
|
64761
66091
|
}
|
|
64762
66092
|
|
|
64763
66093
|
// src/rafa.ts
|
|
64764
|
-
var modules = await loadInvocationModules({ cwd: process.cwd(), home:
|
|
66094
|
+
var modules = await loadInvocationModules({ cwd: process.cwd(), home: homedir15() });
|
|
64765
66095
|
var { exitCode } = await dispatch(process.argv.slice(2), {
|
|
64766
66096
|
registry: CORE_REGISTRY,
|
|
64767
66097
|
renderHelp,
|