mason-context 0.17.2 → 0.17.4
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/CHANGELOG.md +20 -0
- package/dist/mason-audit.js +309 -179
- package/dist/mason-audit.js.map +1 -1
- package/dist/mason-auto.js +475 -289
- package/dist/mason-auto.js.map +1 -1
- package/dist/mason-drift.js +45 -18
- package/dist/mason-drift.js.map +1 -1
- package/dist/mason-hook.js +41 -13
- package/dist/mason-hook.js.map +1 -1
- package/dist/mason-mcp.js +632 -456
- package/dist/mason-mcp.js.map +1 -1
- package/dist/mason-review.js +53 -25
- package/dist/mason-review.js.map +1 -1
- package/dist/mason.js +510 -324
- package/dist/mason.js.map +1 -1
- package/package.json +1 -1
package/dist/mason-mcp.js
CHANGED
|
@@ -9,6 +9,54 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
// src/utils/profile.ts
|
|
13
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
14
|
+
async function profilePhase(label, run) {
|
|
15
|
+
const phases = profiles.getStore();
|
|
16
|
+
if (!phases) return run();
|
|
17
|
+
const start = performance.now();
|
|
18
|
+
try {
|
|
19
|
+
return await run();
|
|
20
|
+
} finally {
|
|
21
|
+
const timing = phases[label] ??= { calls: 0, totalMs: 0 };
|
|
22
|
+
timing.calls++;
|
|
23
|
+
timing.totalMs += performance.now() - start;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
var profiles;
|
|
27
|
+
var init_profile = __esm({
|
|
28
|
+
"src/utils/profile.ts"() {
|
|
29
|
+
"use strict";
|
|
30
|
+
profiles = new AsyncLocalStorage();
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// src/utils/git-read.ts
|
|
35
|
+
import { execFile } from "child_process";
|
|
36
|
+
import { promisify } from "util";
|
|
37
|
+
function execGit(args, options) {
|
|
38
|
+
const command = args.find((arg) => !arg.startsWith("-")) ?? "other";
|
|
39
|
+
return profilePhase("git." + (commands.has(command) ? command : "other"), async () => {
|
|
40
|
+
const { input, ...processOptions } = options;
|
|
41
|
+
const operation = execute("git", args, processOptions);
|
|
42
|
+
if (input !== void 0) {
|
|
43
|
+
operation.child.stdin.on("error", () => {
|
|
44
|
+
});
|
|
45
|
+
operation.child.stdin.end(input);
|
|
46
|
+
}
|
|
47
|
+
return operation;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
var execute, commands;
|
|
51
|
+
var init_git_read = __esm({
|
|
52
|
+
"src/utils/git-read.ts"() {
|
|
53
|
+
"use strict";
|
|
54
|
+
init_profile();
|
|
55
|
+
execute = promisify(execFile);
|
|
56
|
+
commands = /* @__PURE__ */ new Set(["rev-parse", "symbolic-ref", "for-each-ref", "ls-files", "check-ignore", "status", "log", "diff", "ls-tree", "show", "merge-base"]);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
12
60
|
// src/audit/commands.ts
|
|
13
61
|
import path from "path";
|
|
14
62
|
function splitCommands(line) {
|
|
@@ -233,14 +281,14 @@ function extractClaims(content2) {
|
|
|
233
281
|
const ignored = computeIgnoredLines(lines);
|
|
234
282
|
const paths = /* @__PURE__ */ new Map();
|
|
235
283
|
const counts = [];
|
|
236
|
-
const
|
|
284
|
+
const commands2 = /* @__PURE__ */ new Map();
|
|
237
285
|
const addPath = (claim) => {
|
|
238
286
|
const key = [claim.relativeTo ?? "scope", claim.path].join(":");
|
|
239
287
|
if (!paths.has(key)) paths.set(key, claim);
|
|
240
288
|
};
|
|
241
289
|
const addCommand = (claim) => {
|
|
242
290
|
const key = JSON.stringify([claim.scriptName, claim.directory, claim.scopeUnknown]);
|
|
243
|
-
if (!
|
|
291
|
+
if (!commands2.has(key)) commands2.set(key, claim);
|
|
244
292
|
};
|
|
245
293
|
const parsed = markdown.parse(lines.map((line, i) => ignored[i] ? "" : line).join("\n"), {});
|
|
246
294
|
for (const token of parsed) {
|
|
@@ -342,7 +390,7 @@ function extractClaims(content2) {
|
|
|
342
390
|
return {
|
|
343
391
|
paths: [...paths.values()],
|
|
344
392
|
counts,
|
|
345
|
-
commands: [...
|
|
393
|
+
commands: [...commands2.values()]
|
|
346
394
|
};
|
|
347
395
|
}
|
|
348
396
|
var ROOT_FILE_NAMES, SHELL_FENCE_INFOS, markdown, COUNT_RE, COUNT_DENYLIST_RE, IGNORE_LINE, IGNORE_START, IGNORE_END;
|
|
@@ -392,97 +440,6 @@ var init_claims = __esm({
|
|
|
392
440
|
}
|
|
393
441
|
});
|
|
394
442
|
|
|
395
|
-
// src/audit/git.ts
|
|
396
|
-
import { execFile } from "child_process";
|
|
397
|
-
import { promisify } from "util";
|
|
398
|
-
function parseCommitLine(line) {
|
|
399
|
-
const parts = line.split(" ");
|
|
400
|
-
if (parts.length < 3 || !parts[0]) return null;
|
|
401
|
-
return { hash: parts[0], date: parts[1], subject: parts.slice(2).join(" ") };
|
|
402
|
-
}
|
|
403
|
-
async function lastCommitOf(resolvedRoot, relPath) {
|
|
404
|
-
try {
|
|
405
|
-
const { stdout } = await exec(
|
|
406
|
-
"git",
|
|
407
|
-
["log", "-1", `--format=${COMMIT_FORMAT}`, "--", `:(literal)${relPath}`],
|
|
408
|
-
{ cwd: resolvedRoot }
|
|
409
|
-
);
|
|
410
|
-
const line = stdout.trim().split("\n")[0];
|
|
411
|
-
return line ? parseCommitLine(line) : null;
|
|
412
|
-
} catch {
|
|
413
|
-
return null;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
async function deletingCommitOf(resolvedRoot, relPath) {
|
|
417
|
-
try {
|
|
418
|
-
const { stdout } = await exec(
|
|
419
|
-
"git",
|
|
420
|
-
[
|
|
421
|
-
"log",
|
|
422
|
-
"-1",
|
|
423
|
-
"--diff-filter=D",
|
|
424
|
-
`--format=${COMMIT_FORMAT}`,
|
|
425
|
-
"--",
|
|
426
|
-
`:(literal)${relPath}`
|
|
427
|
-
],
|
|
428
|
-
{ cwd: resolvedRoot }
|
|
429
|
-
);
|
|
430
|
-
const line = stdout.trim().split("\n")[0];
|
|
431
|
-
return line ? parseCommitLine(line) : null;
|
|
432
|
-
} catch {
|
|
433
|
-
return null;
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
async function firstCommitOf(resolvedRoot, relPath) {
|
|
437
|
-
try {
|
|
438
|
-
const { stdout } = await exec(
|
|
439
|
-
"git",
|
|
440
|
-
["log", "--reverse", `--format=${COMMIT_FORMAT}`, "--", `:(literal)${relPath}`],
|
|
441
|
-
{ cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 }
|
|
442
|
-
);
|
|
443
|
-
const line = stdout.trim().split("\n")[0];
|
|
444
|
-
return line ? parseCommitLine(line) : null;
|
|
445
|
-
} catch {
|
|
446
|
-
return null;
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
async function commitsTouchingSince(resolvedRoot, fromHash, pathspecs) {
|
|
450
|
-
if (!fromHash || fromHash === "unknown") return null;
|
|
451
|
-
try {
|
|
452
|
-
const { stdout } = await exec(
|
|
453
|
-
"git",
|
|
454
|
-
[
|
|
455
|
-
"log",
|
|
456
|
-
`${fromHash}..HEAD`,
|
|
457
|
-
`--format=%x01${COMMIT_FORMAT}`,
|
|
458
|
-
"--name-only",
|
|
459
|
-
"--",
|
|
460
|
-
...pathspecs
|
|
461
|
-
],
|
|
462
|
-
{ cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 }
|
|
463
|
-
);
|
|
464
|
-
const commits = [];
|
|
465
|
-
for (const block of stdout.split("")) {
|
|
466
|
-
if (!block.trim()) continue;
|
|
467
|
-
const lines = block.split("\n").filter((l) => l.trim().length > 0);
|
|
468
|
-
const ref = parseCommitLine(lines[0]);
|
|
469
|
-
if (!ref) continue;
|
|
470
|
-
commits.push({ ...ref, files: lines.slice(1).map((l) => l.trim()) });
|
|
471
|
-
}
|
|
472
|
-
return { commits, total: commits.length };
|
|
473
|
-
} catch {
|
|
474
|
-
return null;
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
var exec, COMMIT_FORMAT;
|
|
478
|
-
var init_git = __esm({
|
|
479
|
-
"src/audit/git.ts"() {
|
|
480
|
-
"use strict";
|
|
481
|
-
exec = promisify(execFile);
|
|
482
|
-
COMMIT_FORMAT = "%H%x09%cI%x09%s";
|
|
483
|
-
}
|
|
484
|
-
});
|
|
485
|
-
|
|
486
443
|
// src/utils/paths.ts
|
|
487
444
|
import path2 from "path";
|
|
488
445
|
function normalizeRepoPath(value) {
|
|
@@ -517,8 +474,6 @@ var init_paths = __esm({
|
|
|
517
474
|
import fs from "fs/promises";
|
|
518
475
|
import { constants } from "fs";
|
|
519
476
|
import path3 from "path";
|
|
520
|
-
import { execFile as execFile2 } from "child_process";
|
|
521
|
-
import { promisify as promisify2 } from "util";
|
|
522
477
|
import fg from "fast-glob";
|
|
523
478
|
function isSensitiveFile(file) {
|
|
524
479
|
return file.split(/[\\/]/).some(
|
|
@@ -572,12 +527,12 @@ async function createFileAccess(rootDir) {
|
|
|
572
527
|
const ignore = [...SOURCE_IGNORE, ...config.ignore ?? []];
|
|
573
528
|
let gitFiles = null;
|
|
574
529
|
try {
|
|
575
|
-
const { stdout } = await
|
|
530
|
+
const { stdout } = await execGit(["ls-files", "-z", "--cached", "--others", "--exclude-standard"], { cwd: root, maxBuffer: 50 * 1024 * 1024 });
|
|
576
531
|
gitFiles = new Set(stdout.split("\0").filter(Boolean));
|
|
577
532
|
} catch {
|
|
578
533
|
let inGit = false;
|
|
579
534
|
try {
|
|
580
|
-
await
|
|
535
|
+
await execGit(["rev-parse", "--git-dir"], { cwd: root });
|
|
581
536
|
inGit = true;
|
|
582
537
|
} catch {
|
|
583
538
|
}
|
|
@@ -620,12 +575,12 @@ async function createFileAccess(rootDir) {
|
|
|
620
575
|
}
|
|
621
576
|
return { root, config, list, read };
|
|
622
577
|
}
|
|
623
|
-
var
|
|
578
|
+
var SOURCE_EXTENSIONS, SOURCE_GLOB, SOURCE_IGNORE, MAX_SOURCE_BYTES;
|
|
624
579
|
var init_files = __esm({
|
|
625
580
|
"src/utils/files.ts"() {
|
|
626
581
|
"use strict";
|
|
582
|
+
init_git_read();
|
|
627
583
|
init_paths();
|
|
628
|
-
exec2 = promisify2(execFile2);
|
|
629
584
|
SOURCE_EXTENSIONS = ["ts", "tsx", "js", "jsx", "mts", "cts", "mjs", "cjs", "vue", "svelte", "kt", "kts", "java", "py", "go", "rs", "swift", "rb", "cs", "cpp", "c", "h", "hpp", "dart", "php"];
|
|
630
585
|
SOURCE_GLOB = `**/*.{${SOURCE_EXTENSIONS.join(",")}}`;
|
|
631
586
|
SOURCE_IGNORE = [
|
|
@@ -655,9 +610,140 @@ var init_files = __esm({
|
|
|
655
610
|
}
|
|
656
611
|
});
|
|
657
612
|
|
|
613
|
+
// src/audit/inspection.ts
|
|
614
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
615
|
+
import path4 from "path";
|
|
616
|
+
function withRepositoryInspection(root, run) {
|
|
617
|
+
return inspections.run({ root: path4.resolve(root), reads: /* @__PURE__ */ new Map() }, run);
|
|
618
|
+
}
|
|
619
|
+
function inspectionRead(root, key, read) {
|
|
620
|
+
const inspection = inspections.getStore();
|
|
621
|
+
if (!inspection || inspection.root !== path4.resolve(root)) return read();
|
|
622
|
+
if (!inspection.reads.has(key)) {
|
|
623
|
+
const pending = read();
|
|
624
|
+
inspection.reads.set(key, pending);
|
|
625
|
+
void pending.catch(() => {
|
|
626
|
+
if (inspection.reads.get(key) === pending) inspection.reads.delete(key);
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
return inspection.reads.get(key);
|
|
630
|
+
}
|
|
631
|
+
function inspectionGit(args, options) {
|
|
632
|
+
return inspectionRead(options.cwd, "git:" + JSON.stringify([args, options]), () => execGit(args, options));
|
|
633
|
+
}
|
|
634
|
+
async function auditGitPaths(root, kind) {
|
|
635
|
+
const shared = inspections.getStore()?.root === path4.resolve(root);
|
|
636
|
+
const specs = shared ? [...docSpecs, ...sourceSpecs] : kind === "documents" ? docSpecs : sourceSpecs;
|
|
637
|
+
const limit = kind === "documents" ? 16 * 1024 * 1024 : 32 * 1024 * 1024;
|
|
638
|
+
const { stdout } = await inspectionGit(
|
|
639
|
+
["ls-files", "-z", "--cached", "--others", "--exclude-standard", "--", ...specs],
|
|
640
|
+
{ cwd: root, maxBuffer: shared ? 48 * 1024 * 1024 : limit, timeout: 1e4 }
|
|
641
|
+
);
|
|
642
|
+
const paths = [...new Set(stdout.split("\0").filter(Boolean))].filter((file) => kind === "documents" ? /^(readme|agents|claude)\.md$/i.test(path4.posix.basename(file)) : sourceSuffixes.has(file.slice(file.lastIndexOf("."))));
|
|
643
|
+
if (Buffer.byteLength(paths.join("\0")) > limit) throw new Error("Git " + kind + " inventory exceeds its bounded read limit.");
|
|
644
|
+
return paths.sort();
|
|
645
|
+
}
|
|
646
|
+
var inspections, sourceSpecs, docSpecs, sourceSuffixes;
|
|
647
|
+
var init_inspection = __esm({
|
|
648
|
+
"src/audit/inspection.ts"() {
|
|
649
|
+
"use strict";
|
|
650
|
+
init_git_read();
|
|
651
|
+
init_files();
|
|
652
|
+
inspections = new AsyncLocalStorage2();
|
|
653
|
+
sourceSpecs = SOURCE_EXTENSIONS.map((extension) => `:(glob)**/*.${extension}`);
|
|
654
|
+
docSpecs = [":(icase,glob)**/readme.md", ":(icase,glob)**/agents.md", ":(icase,glob)**/claude.md"];
|
|
655
|
+
sourceSuffixes = new Set(SOURCE_EXTENSIONS.map((extension) => "." + extension));
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
// src/audit/git.ts
|
|
660
|
+
function parseCommitLine(line) {
|
|
661
|
+
const parts = line.split(" ");
|
|
662
|
+
if (parts.length < 3 || !parts[0]) return null;
|
|
663
|
+
return { hash: parts[0], date: parts[1], subject: parts.slice(2).join(" ") };
|
|
664
|
+
}
|
|
665
|
+
async function lastCommitOf(resolvedRoot, relPath) {
|
|
666
|
+
try {
|
|
667
|
+
const { stdout } = await inspectionGit(
|
|
668
|
+
["log", "-1", `--format=${COMMIT_FORMAT}`, "--", `:(literal)${relPath}`],
|
|
669
|
+
{ cwd: resolvedRoot }
|
|
670
|
+
);
|
|
671
|
+
const line = stdout.trim().split("\n")[0];
|
|
672
|
+
return line ? parseCommitLine(line) : null;
|
|
673
|
+
} catch {
|
|
674
|
+
return null;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
async function deletingCommitOf(resolvedRoot, relPath) {
|
|
678
|
+
try {
|
|
679
|
+
const { stdout } = await inspectionGit(
|
|
680
|
+
[
|
|
681
|
+
"log",
|
|
682
|
+
"-1",
|
|
683
|
+
"--diff-filter=D",
|
|
684
|
+
`--format=${COMMIT_FORMAT}`,
|
|
685
|
+
"--",
|
|
686
|
+
`:(literal)${relPath}`
|
|
687
|
+
],
|
|
688
|
+
{ cwd: resolvedRoot }
|
|
689
|
+
);
|
|
690
|
+
const line = stdout.trim().split("\n")[0];
|
|
691
|
+
return line ? parseCommitLine(line) : null;
|
|
692
|
+
} catch {
|
|
693
|
+
return null;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
async function firstCommitOf(resolvedRoot, relPath) {
|
|
697
|
+
try {
|
|
698
|
+
const { stdout } = await inspectionGit(
|
|
699
|
+
["log", "--reverse", `--format=${COMMIT_FORMAT}`, "--", `:(literal)${relPath}`],
|
|
700
|
+
{ cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 }
|
|
701
|
+
);
|
|
702
|
+
const line = stdout.trim().split("\n")[0];
|
|
703
|
+
return line ? parseCommitLine(line) : null;
|
|
704
|
+
} catch {
|
|
705
|
+
return null;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
async function commitsTouchingSince(resolvedRoot, fromHash, pathspecs) {
|
|
709
|
+
if (!fromHash || fromHash === "unknown") return null;
|
|
710
|
+
try {
|
|
711
|
+
const { stdout } = await inspectionGit(
|
|
712
|
+
[
|
|
713
|
+
"log",
|
|
714
|
+
`${fromHash}..HEAD`,
|
|
715
|
+
`--format=%x01${COMMIT_FORMAT}`,
|
|
716
|
+
"--name-only",
|
|
717
|
+
"--",
|
|
718
|
+
...pathspecs
|
|
719
|
+
],
|
|
720
|
+
{ cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 }
|
|
721
|
+
);
|
|
722
|
+
const commits = [];
|
|
723
|
+
for (const block of stdout.split("")) {
|
|
724
|
+
if (!block.trim()) continue;
|
|
725
|
+
const lines = block.split("\n").filter((l) => l.trim().length > 0);
|
|
726
|
+
const ref = parseCommitLine(lines[0]);
|
|
727
|
+
if (!ref) continue;
|
|
728
|
+
commits.push({ ...ref, files: lines.slice(1).map((l) => l.trim()) });
|
|
729
|
+
}
|
|
730
|
+
return { commits, total: commits.length };
|
|
731
|
+
} catch {
|
|
732
|
+
return null;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
var COMMIT_FORMAT;
|
|
736
|
+
var init_git = __esm({
|
|
737
|
+
"src/audit/git.ts"() {
|
|
738
|
+
"use strict";
|
|
739
|
+
init_inspection();
|
|
740
|
+
COMMIT_FORMAT = "%H%x09%cI%x09%s";
|
|
741
|
+
}
|
|
742
|
+
});
|
|
743
|
+
|
|
658
744
|
// src/utils/storage.ts
|
|
659
745
|
import fs2 from "fs/promises";
|
|
660
|
-
import
|
|
746
|
+
import path5 from "path";
|
|
661
747
|
import { randomUUID } from "crypto";
|
|
662
748
|
async function storePath(root, relative, createParents = false) {
|
|
663
749
|
const normalized = normalizeRepoPath(relative);
|
|
@@ -665,7 +751,7 @@ async function storePath(root, relative, createParents = false) {
|
|
|
665
751
|
let current = await fs2.realpath(root);
|
|
666
752
|
const parts = normalized.split("/");
|
|
667
753
|
for (let i = 0; i < parts.length; i++) {
|
|
668
|
-
current =
|
|
754
|
+
current = path5.join(current, parts[i]);
|
|
669
755
|
let stat;
|
|
670
756
|
try {
|
|
671
757
|
stat = await fs2.lstat(current);
|
|
@@ -703,7 +789,7 @@ async function writeStoreJson(root, relative, value) {
|
|
|
703
789
|
throw new Error(`Mason store ${relative} exceeds 10 MiB`);
|
|
704
790
|
}
|
|
705
791
|
const file = await storePath(root, relative, true);
|
|
706
|
-
const temporary =
|
|
792
|
+
const temporary = path5.join(path5.dirname(file), `.${path5.basename(file)}.${randomUUID()}.tmp`);
|
|
707
793
|
try {
|
|
708
794
|
const handle = await fs2.open(temporary, "wx", 384);
|
|
709
795
|
try {
|
|
@@ -728,9 +814,7 @@ var init_storage = __esm({
|
|
|
728
814
|
// src/audit/inputs.ts
|
|
729
815
|
import fs3 from "fs/promises";
|
|
730
816
|
import * as nativeFs from "fs";
|
|
731
|
-
import
|
|
732
|
-
import { execFile as execFile3 } from "child_process";
|
|
733
|
-
import { promisify as promisify3 } from "util";
|
|
817
|
+
import path6 from "path";
|
|
734
818
|
import fg2 from "fast-glob";
|
|
735
819
|
async function auditInputPath(root, file) {
|
|
736
820
|
if (file === ".") return fs3.realpath(root);
|
|
@@ -757,7 +841,7 @@ async function auditGlob(root, patterns, options) {
|
|
|
757
841
|
if (Array.isArray(patterns) && !patterns.length) return [];
|
|
758
842
|
const directories = /* @__PURE__ */ new Set();
|
|
759
843
|
const readdir2 = ((directory, opts, callback) => {
|
|
760
|
-
const relative =
|
|
844
|
+
const relative = path6.relative(root, directory).split(path6.sep).join("/") || ".";
|
|
761
845
|
directories.add(relative);
|
|
762
846
|
if (directories.size > MAX_INPUTS) {
|
|
763
847
|
callback(new Error(`${options.label} exceeds ${MAX_INPUTS.toLocaleString("en-US")} directories while inspecting ${relative}. Narrow this check's inputs.`));
|
|
@@ -768,7 +852,7 @@ async function auditGlob(root, patterns, options) {
|
|
|
768
852
|
}, callback);
|
|
769
853
|
});
|
|
770
854
|
const lstat2 = ((file, callback) => {
|
|
771
|
-
const relative =
|
|
855
|
+
const relative = path6.relative(root, file).split(path6.sep).join("/") || ".";
|
|
772
856
|
auditInputPath(root, relative).then(() => nativeFs.lstat(file, callback), callback);
|
|
773
857
|
});
|
|
774
858
|
const stream = fg2.stream(patterns, {
|
|
@@ -805,89 +889,71 @@ async function auditGlob(root, patterns, options) {
|
|
|
805
889
|
return matches.sort();
|
|
806
890
|
}
|
|
807
891
|
async function gitSourcePaths(root) {
|
|
808
|
-
|
|
809
|
-
"git",
|
|
810
|
-
[
|
|
811
|
-
"ls-files",
|
|
812
|
-
"-z",
|
|
813
|
-
"--cached",
|
|
814
|
-
"--others",
|
|
815
|
-
"--exclude-standard",
|
|
816
|
-
"--",
|
|
817
|
-
...SOURCE_EXTENSIONS.map((extension) => `:(glob)**/*.${extension}`)
|
|
818
|
-
],
|
|
819
|
-
{ cwd: root, maxBuffer: 32 * 1024 * 1024, timeout: 1e4 }
|
|
820
|
-
);
|
|
821
|
-
return [...new Set(stdout.split("\0").filter(Boolean))].sort();
|
|
892
|
+
return auditGitPaths(root, "sources");
|
|
822
893
|
}
|
|
823
894
|
async function gitIgnoredPaths(root, files) {
|
|
824
895
|
if (!files.length) return /* @__PURE__ */ new Set();
|
|
825
|
-
const
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
896
|
+
const ignored = /* @__PURE__ */ new Set();
|
|
897
|
+
for (let offset = 0; offset < files.length; offset += 1e3) {
|
|
898
|
+
try {
|
|
899
|
+
const { stdout } = await execGit(["check-ignore", "-z", "--stdin"], {
|
|
900
|
+
cwd: root,
|
|
901
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
902
|
+
timeout: 1e4,
|
|
903
|
+
input: files.slice(offset, offset + 1e3).join("\0") + "\0"
|
|
904
|
+
});
|
|
905
|
+
for (const file of stdout.split("\0").filter(Boolean)) ignored.add(file);
|
|
906
|
+
} catch (error) {
|
|
907
|
+
if (error.code !== 1) throw error;
|
|
908
|
+
}
|
|
838
909
|
}
|
|
910
|
+
return ignored;
|
|
839
911
|
}
|
|
840
|
-
var
|
|
912
|
+
var MAX_INPUTS;
|
|
841
913
|
var init_inputs = __esm({
|
|
842
914
|
"src/audit/inputs.ts"() {
|
|
843
915
|
"use strict";
|
|
844
916
|
init_files();
|
|
845
917
|
init_storage();
|
|
846
|
-
|
|
918
|
+
init_git_read();
|
|
919
|
+
init_inspection();
|
|
847
920
|
MAX_INPUTS = 1e5;
|
|
848
921
|
}
|
|
849
922
|
});
|
|
850
923
|
|
|
851
924
|
// src/audit/docs.ts
|
|
852
925
|
import fs4 from "fs/promises";
|
|
853
|
-
import
|
|
854
|
-
import { execFile as execFile4 } from "child_process";
|
|
855
|
-
import { promisify as promisify4 } from "util";
|
|
926
|
+
import path7 from "path";
|
|
856
927
|
import fg3 from "fast-glob";
|
|
857
|
-
async function
|
|
928
|
+
async function dirtyDocs(resolvedRoot, files) {
|
|
929
|
+
if (!files.length) return /* @__PURE__ */ new Set();
|
|
858
930
|
try {
|
|
859
|
-
const
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
931
|
+
const dirty = /* @__PURE__ */ new Set();
|
|
932
|
+
for (let offset = 0; offset < files.length; offset += 256) {
|
|
933
|
+
const { stdout } = await execGit(
|
|
934
|
+
["status", "--porcelain=v1", "-z", "--untracked-files=all", "--", ...files.slice(offset, offset + 256).map((file) => `:(literal)${file}`)],
|
|
935
|
+
{ cwd: resolvedRoot, maxBuffer: 16 * 1024 * 1024, timeout: 1e4 }
|
|
936
|
+
);
|
|
937
|
+
const entries = stdout.split("\0");
|
|
938
|
+
for (let i = 0; i < entries.length; i++) {
|
|
939
|
+
if (!entries[i]) continue;
|
|
940
|
+
const status = entries[i].slice(0, 2);
|
|
941
|
+
dirty.add(entries[i].slice(3));
|
|
942
|
+
if (/[RC]/.test(status) && entries[i + 1]) dirty.add(entries[++i]);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
return dirty;
|
|
865
946
|
} catch {
|
|
866
|
-
return
|
|
947
|
+
return new Set(files);
|
|
867
948
|
}
|
|
868
949
|
}
|
|
869
950
|
async function discoverDocPaths(root) {
|
|
870
951
|
let candidates = [];
|
|
871
952
|
try {
|
|
872
|
-
|
|
873
|
-
"git",
|
|
874
|
-
[
|
|
875
|
-
"ls-files",
|
|
876
|
-
"-z",
|
|
877
|
-
"--cached",
|
|
878
|
-
"--others",
|
|
879
|
-
"--exclude-standard",
|
|
880
|
-
"--",
|
|
881
|
-
":(icase,glob)**/readme.md",
|
|
882
|
-
":(icase,glob)**/agents.md",
|
|
883
|
-
":(icase,glob)**/claude.md"
|
|
884
|
-
],
|
|
885
|
-
{ cwd: root, maxBuffer: 16 * 1024 * 1024, timeout: 1e4 }
|
|
886
|
-
);
|
|
887
|
-
candidates = stdout.split("\0").filter(Boolean);
|
|
953
|
+
candidates = await auditGitPaths(root, "documents");
|
|
888
954
|
} catch (error) {
|
|
889
955
|
try {
|
|
890
|
-
await
|
|
956
|
+
await execGit(["rev-parse", "--git-dir"], { cwd: root });
|
|
891
957
|
} catch {
|
|
892
958
|
return localDocPaths(root);
|
|
893
959
|
}
|
|
@@ -934,43 +1000,54 @@ async function localDocPaths(root) {
|
|
|
934
1000
|
throw error;
|
|
935
1001
|
}
|
|
936
1002
|
for (const entry of entries) if (/^(?:agents|claude|readme)\.md$/i.test(entry.name)) {
|
|
937
|
-
paths.push(
|
|
1003
|
+
paths.push(path7.posix.join(dir, entry.name));
|
|
938
1004
|
}
|
|
939
1005
|
}
|
|
940
1006
|
return paths;
|
|
941
1007
|
}
|
|
942
1008
|
function documentScope(file) {
|
|
943
|
-
const parent =
|
|
944
|
-
return
|
|
1009
|
+
const parent = path7.posix.dirname(file);
|
|
1010
|
+
return path7.posix.basename(parent).toLowerCase() === ".claude" ? path7.posix.dirname(parent) : parent;
|
|
945
1011
|
}
|
|
946
1012
|
async function discoverDocs(resolvedRoot) {
|
|
1013
|
+
return profilePhase("audit.documents", () => readDocs(resolvedRoot));
|
|
1014
|
+
}
|
|
1015
|
+
async function readDocs(resolvedRoot) {
|
|
947
1016
|
const docs = [];
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1017
|
+
const files = await discoverDocPaths(resolvedRoot);
|
|
1018
|
+
const dirty = await dirtyDocs(resolvedRoot, files);
|
|
1019
|
+
for (let offset = 0; offset < files.length; offset += 4) {
|
|
1020
|
+
docs.push(...await Promise.all(files.slice(offset, offset + 4).map(async (candidate) => {
|
|
1021
|
+
const [content2, lastCommit] = await Promise.all([
|
|
1022
|
+
readAuditInput(resolvedRoot, candidate),
|
|
1023
|
+
profilePhase("audit.document-history", () => lastCommitOf(resolvedRoot, candidate))
|
|
1024
|
+
]);
|
|
1025
|
+
if (content2 === null) throw new Error("Document disappeared during discovery: " + candidate);
|
|
1026
|
+
return {
|
|
1027
|
+
path: candidate,
|
|
1028
|
+
content: content2,
|
|
1029
|
+
scope: documentScope(candidate),
|
|
1030
|
+
kind: /^readme\.md$/i.test(path7.posix.basename(candidate)) ? "readme" : "instructions",
|
|
1031
|
+
lineCount: content2.split("\n").length,
|
|
1032
|
+
lastCommit,
|
|
1033
|
+
dirty: dirty.has(candidate),
|
|
1034
|
+
claims: extractClaims(content2)
|
|
1035
|
+
};
|
|
1036
|
+
})));
|
|
962
1037
|
}
|
|
963
1038
|
return docs;
|
|
964
1039
|
}
|
|
965
|
-
var
|
|
1040
|
+
var DOC_CANDIDATES;
|
|
966
1041
|
var init_docs = __esm({
|
|
967
1042
|
"src/audit/docs.ts"() {
|
|
968
1043
|
"use strict";
|
|
1044
|
+
init_git_read();
|
|
1045
|
+
init_profile();
|
|
969
1046
|
init_claims();
|
|
970
1047
|
init_git();
|
|
971
1048
|
init_inputs();
|
|
972
1049
|
init_files();
|
|
973
|
-
|
|
1050
|
+
init_inspection();
|
|
974
1051
|
DOC_CANDIDATES = [
|
|
975
1052
|
"AGENTS.md",
|
|
976
1053
|
"CLAUDE.md",
|
|
@@ -981,14 +1058,14 @@ var init_docs = __esm({
|
|
|
981
1058
|
|
|
982
1059
|
// src/audit/scope.ts
|
|
983
1060
|
import fs5 from "fs/promises";
|
|
984
|
-
import
|
|
1061
|
+
import path8 from "path";
|
|
985
1062
|
function scopedPath(directory, value) {
|
|
986
|
-
if (
|
|
987
|
-
const resolved =
|
|
1063
|
+
if (path8.posix.isAbsolute(value) || value.includes("\\") || /[\x00-\x1f]/.test(value)) return null;
|
|
1064
|
+
const resolved = path8.posix.normalize(path8.posix.join(directory, value));
|
|
988
1065
|
return resolved === ".." || resolved.startsWith("../") ? null : resolved;
|
|
989
1066
|
}
|
|
990
1067
|
function pathClaimScope(doc, claim) {
|
|
991
|
-
const directory = claim.relativeTo === "document" ?
|
|
1068
|
+
const directory = claim.relativeTo === "document" ? path8.posix.dirname(doc) : documentScope(doc);
|
|
992
1069
|
const local = scopedPath(directory, claim.path);
|
|
993
1070
|
if (!local) return null;
|
|
994
1071
|
const candidates = claim.relativeTo === "document" || directory === "." ? [local] : [...new Set([local, scopedPath(".", claim.path)].filter((p) => p !== null))];
|
|
@@ -1026,9 +1103,9 @@ var test_map_exports = {};
|
|
|
1026
1103
|
__export(test_map_exports, {
|
|
1027
1104
|
buildTestMap: () => buildTestMap
|
|
1028
1105
|
});
|
|
1029
|
-
import
|
|
1106
|
+
import path9 from "path";
|
|
1030
1107
|
async function buildTestMap(dir) {
|
|
1031
|
-
const rootDir =
|
|
1108
|
+
const rootDir = path9.resolve(dir);
|
|
1032
1109
|
const access = await createFileAccess(rootDir);
|
|
1033
1110
|
const testPatterns = [
|
|
1034
1111
|
"**/*.test.*",
|
|
@@ -1049,7 +1126,7 @@ async function buildTestMap(dir) {
|
|
|
1049
1126
|
const sourceByBaseName = /* @__PURE__ */ new Map();
|
|
1050
1127
|
for (const file of sourceFiles) {
|
|
1051
1128
|
if (testFiles.includes(file)) continue;
|
|
1052
|
-
const baseName =
|
|
1129
|
+
const baseName = path9.basename(file).replace(/\.[^.]+$/, "");
|
|
1053
1130
|
const existing = sourceByBaseName.get(baseName) ?? [];
|
|
1054
1131
|
existing.push(file);
|
|
1055
1132
|
sourceByBaseName.set(baseName, existing);
|
|
@@ -1057,7 +1134,7 @@ async function buildTestMap(dir) {
|
|
|
1057
1134
|
const paired = [];
|
|
1058
1135
|
const unmatched = [];
|
|
1059
1136
|
for (const testFile of testFiles) {
|
|
1060
|
-
const testBaseName =
|
|
1137
|
+
const testBaseName = path9.basename(testFile).replace(/\.[^.]+$/, "");
|
|
1061
1138
|
const sourceName = testBaseName.replace(/Test$|Tests$|Spec$|\.test$|\.spec$/, "").replace(/^test_|_test$/, "");
|
|
1062
1139
|
if (!sourceName) {
|
|
1063
1140
|
unmatched.push(testFile);
|
|
@@ -1065,10 +1142,10 @@ async function buildTestMap(dir) {
|
|
|
1065
1142
|
}
|
|
1066
1143
|
const candidates = sourceByBaseName.get(sourceName);
|
|
1067
1144
|
if (candidates && candidates.length > 0) {
|
|
1068
|
-
const testDir =
|
|
1145
|
+
const testDir = path9.dirname(testFile);
|
|
1069
1146
|
const bestMatch = candidates.reduce((best, candidate) => {
|
|
1070
|
-
const candidateDir =
|
|
1071
|
-
const bestDir =
|
|
1147
|
+
const candidateDir = path9.dirname(candidate);
|
|
1148
|
+
const bestDir = path9.dirname(best);
|
|
1072
1149
|
const candidateOverlap = commonSegments(testDir, candidateDir);
|
|
1073
1150
|
const bestOverlap = commonSegments(testDir, bestDir);
|
|
1074
1151
|
return candidateOverlap > bestOverlap ? candidate : best;
|
|
@@ -1102,9 +1179,7 @@ var init_test_map = __esm({
|
|
|
1102
1179
|
});
|
|
1103
1180
|
|
|
1104
1181
|
// src/snapshot/snapshot.ts
|
|
1105
|
-
import
|
|
1106
|
-
import { execFile as execFile5 } from "child_process";
|
|
1107
|
-
import { promisify as promisify5 } from "util";
|
|
1182
|
+
import path10 from "path";
|
|
1108
1183
|
import { z } from "zod";
|
|
1109
1184
|
function normalizeFeatureType(value) {
|
|
1110
1185
|
return value === "infrastructure" ? "infrastructure" : "capability";
|
|
@@ -1133,7 +1208,7 @@ async function saveSnapshot(rootDir, snapshot) {
|
|
|
1133
1208
|
}
|
|
1134
1209
|
async function getCurrentGitHash(rootDir) {
|
|
1135
1210
|
try {
|
|
1136
|
-
const { stdout } = await
|
|
1211
|
+
const { stdout } = await execGit(["rev-parse", "HEAD"], {
|
|
1137
1212
|
cwd: rootDir
|
|
1138
1213
|
});
|
|
1139
1214
|
return stdout.trim();
|
|
@@ -1145,7 +1220,7 @@ async function listSourceFiles(resolvedRoot) {
|
|
|
1145
1220
|
return (await createFileAccess(resolvedRoot)).list();
|
|
1146
1221
|
}
|
|
1147
1222
|
async function prepareSnapshotBatch(rootDir, offset, batchSize = DEFAULT_BATCH_SIZE, scopeFiles) {
|
|
1148
|
-
const resolvedRoot =
|
|
1223
|
+
const resolvedRoot = path10.resolve(rootDir);
|
|
1149
1224
|
const access = await createFileAccess(resolvedRoot);
|
|
1150
1225
|
let allFiles = await access.list();
|
|
1151
1226
|
if (scopeFiles) {
|
|
@@ -1194,16 +1269,16 @@ async function prepareSnapshotBatch(rootDir, offset, batchSize = DEFAULT_BATCH_S
|
|
|
1194
1269
|
testPairs
|
|
1195
1270
|
};
|
|
1196
1271
|
}
|
|
1197
|
-
var
|
|
1272
|
+
var repoPath, verificationFields, featureSchema, flowSchema, snapshotSchema, DEFAULT_BATCH_SIZE, SKELETON_CHARS, DEEP_SAMPLE_CHARS, DEEP_SAMPLES_PER_BATCH;
|
|
1198
1273
|
var init_snapshot = __esm({
|
|
1199
1274
|
"src/snapshot/snapshot.ts"() {
|
|
1200
1275
|
"use strict";
|
|
1276
|
+
init_git_read();
|
|
1201
1277
|
init_files();
|
|
1202
1278
|
init_files();
|
|
1203
1279
|
init_storage();
|
|
1204
1280
|
init_paths();
|
|
1205
1281
|
init_test_map();
|
|
1206
|
-
exec4 = promisify5(execFile5);
|
|
1207
1282
|
repoPath = z.string().refine((value) => normalizeRepoPath(value) !== null, "Expected a relative repository path");
|
|
1208
1283
|
verificationFields = {
|
|
1209
1284
|
refreshedHash: z.string().optional(),
|
|
@@ -1237,9 +1312,7 @@ var init_snapshot = __esm({
|
|
|
1237
1312
|
|
|
1238
1313
|
// src/drift/drift.ts
|
|
1239
1314
|
import fs6 from "fs/promises";
|
|
1240
|
-
import
|
|
1241
|
-
import { execFile as execFile6 } from "child_process";
|
|
1242
|
-
import { promisify as promisify6 } from "util";
|
|
1315
|
+
import path11 from "path";
|
|
1243
1316
|
function parseChanges(output) {
|
|
1244
1317
|
const fields = output.split("\0");
|
|
1245
1318
|
const changes = [];
|
|
@@ -1260,7 +1333,7 @@ function touchedPaths(changes) {
|
|
|
1260
1333
|
async function getChangesWithStatus(resolvedRoot, fromHash, toHash = "HEAD") {
|
|
1261
1334
|
if (!fromHash || fromHash === "unknown" || fromHash.startsWith("-") || !toHash || toHash === "unknown" || toHash.startsWith("-")) return null;
|
|
1262
1335
|
try {
|
|
1263
|
-
const { stdout } = await
|
|
1336
|
+
const { stdout } = await execGit(["diff", "--name-status", "-z", "-M", fromHash, toHash, "--"], { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 });
|
|
1264
1337
|
return parseChanges(stdout);
|
|
1265
1338
|
} catch {
|
|
1266
1339
|
return null;
|
|
@@ -1269,8 +1342,8 @@ async function getChangesWithStatus(resolvedRoot, fromHash, toHash = "HEAD") {
|
|
|
1269
1342
|
async function getWorkingTree(resolvedRoot) {
|
|
1270
1343
|
try {
|
|
1271
1344
|
const [diff, untracked] = await Promise.all([
|
|
1272
|
-
|
|
1273
|
-
|
|
1345
|
+
execGit(["diff", "--name-status", "-z", "-M", "HEAD", "--"], { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 }),
|
|
1346
|
+
execGit(["ls-files", "-z", "--others", "--exclude-standard"], { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 })
|
|
1274
1347
|
]);
|
|
1275
1348
|
const untrackedFiles = untracked.stdout.split("\0").filter((f) => f && !f.startsWith(".mason/"));
|
|
1276
1349
|
return { available: true, changedFiles: [.../* @__PURE__ */ new Set([...touchedPaths(parseChanges(diff.stdout)), ...untrackedFiles])].sort(), untrackedFiles };
|
|
@@ -1281,8 +1354,7 @@ async function getWorkingTree(resolvedRoot) {
|
|
|
1281
1354
|
async function countCommitsBehind(resolvedRoot, fromHash) {
|
|
1282
1355
|
if (!fromHash || fromHash === "unknown" || fromHash.startsWith("-")) return null;
|
|
1283
1356
|
try {
|
|
1284
|
-
const { stdout } = await
|
|
1285
|
-
"git",
|
|
1357
|
+
const { stdout } = await execGit(
|
|
1286
1358
|
["rev-list", "--count", `${fromHash}..HEAD`],
|
|
1287
1359
|
{ cwd: resolvedRoot }
|
|
1288
1360
|
);
|
|
@@ -1307,7 +1379,7 @@ async function findGhostFiles(resolvedRoot, mappedFiles) {
|
|
|
1307
1379
|
const ghosts = [];
|
|
1308
1380
|
for (const file of mappedFiles) {
|
|
1309
1381
|
try {
|
|
1310
|
-
await fs6.access(
|
|
1382
|
+
await fs6.access(path11.join(resolvedRoot, file));
|
|
1311
1383
|
} catch {
|
|
1312
1384
|
ghosts.push(file);
|
|
1313
1385
|
}
|
|
@@ -1315,7 +1387,7 @@ async function findGhostFiles(resolvedRoot, mappedFiles) {
|
|
|
1315
1387
|
return ghosts.sort();
|
|
1316
1388
|
}
|
|
1317
1389
|
async function computeDrift(rootDir) {
|
|
1318
|
-
const root =
|
|
1390
|
+
const root = path11.resolve(rootDir);
|
|
1319
1391
|
const snapshot = await loadSnapshot(root);
|
|
1320
1392
|
if (!snapshot) return null;
|
|
1321
1393
|
const [headHash, workingTree] = await Promise.all([getCurrentGitHash(root), getWorkingTree(root)]);
|
|
@@ -1372,7 +1444,7 @@ async function computeDrift(rootDir) {
|
|
|
1372
1444
|
const sourceFiles = new Set(await listSourceFiles(root));
|
|
1373
1445
|
let committedFiles = /* @__PURE__ */ new Set();
|
|
1374
1446
|
try {
|
|
1375
|
-
const { stdout } = await
|
|
1447
|
+
const { stdout } = await execGit(["ls-tree", "-r", "--name-only", "-z", "HEAD"], { cwd: root, maxBuffer: 50 * 1024 * 1024 });
|
|
1376
1448
|
committedFiles = new Set(stdout.split("\0").filter(Boolean));
|
|
1377
1449
|
} catch {
|
|
1378
1450
|
report.historyAvailable = false;
|
|
@@ -1392,13 +1464,13 @@ async function computeDrift(rootDir) {
|
|
|
1392
1464
|
else report.recommendation = changedMapped.size >= FULL_REBUILD_MIN_CHANGED_MAPPED_FILES && changedMapped.size / Math.max(1, mappedFiles.size) > FULL_REBUILD_FRACTION ? "full-rebuild" : "incremental";
|
|
1393
1465
|
return report;
|
|
1394
1466
|
}
|
|
1395
|
-
var
|
|
1467
|
+
var FULL_REBUILD_FRACTION, FULL_REBUILD_MIN_CHANGED_MAPPED_FILES;
|
|
1396
1468
|
var init_drift = __esm({
|
|
1397
1469
|
"src/drift/drift.ts"() {
|
|
1398
1470
|
"use strict";
|
|
1471
|
+
init_git_read();
|
|
1399
1472
|
init_snapshot();
|
|
1400
1473
|
init_paths();
|
|
1401
|
-
exec5 = promisify6(execFile6);
|
|
1402
1474
|
FULL_REBUILD_FRACTION = 0.4;
|
|
1403
1475
|
FULL_REBUILD_MIN_CHANGED_MAPPED_FILES = 10;
|
|
1404
1476
|
}
|
|
@@ -1421,7 +1493,7 @@ var init_types = __esm({
|
|
|
1421
1493
|
});
|
|
1422
1494
|
|
|
1423
1495
|
// src/audit/checks/deleted-reference.ts
|
|
1424
|
-
import
|
|
1496
|
+
import path12 from "path";
|
|
1425
1497
|
async function checkDeletedReferences(ctx) {
|
|
1426
1498
|
const result = emptyResult();
|
|
1427
1499
|
for (const doc of ctx.docs) {
|
|
@@ -1450,7 +1522,7 @@ async function checkDeletedReferences(ctx) {
|
|
|
1450
1522
|
const candidates = await Promise.all(scope.candidates.map(async (file) => ({
|
|
1451
1523
|
file,
|
|
1452
1524
|
tracked: await lastCommitOf(ctx.root, file),
|
|
1453
|
-
parent: await pathExists(ctx.root,
|
|
1525
|
+
parent: await pathExists(ctx.root, path12.posix.dirname(file))
|
|
1454
1526
|
})));
|
|
1455
1527
|
const candidate = candidates.find((c) => c.tracked || renames.has(c.file)) ?? candidates.find((c) => c.parent);
|
|
1456
1528
|
if (!candidate && claim.relativeTo !== "document") continue;
|
|
@@ -1490,7 +1562,7 @@ var init_deleted_reference = __esm({
|
|
|
1490
1562
|
// src/audit/checks/new-module.ts
|
|
1491
1563
|
import fg4 from "fast-glob";
|
|
1492
1564
|
import fs7 from "fs/promises";
|
|
1493
|
-
import
|
|
1565
|
+
import path13 from "path";
|
|
1494
1566
|
function escapeRegExp(text2) {
|
|
1495
1567
|
return text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1496
1568
|
}
|
|
@@ -1528,23 +1600,36 @@ async function checkNewModules(ctx) {
|
|
|
1528
1600
|
return result;
|
|
1529
1601
|
}
|
|
1530
1602
|
function moduleDocumentation(docs) {
|
|
1531
|
-
return docs.map((doc) => doc.content + "\n" + (
|
|
1603
|
+
return docs.map((doc) => doc.content + "\n" + (path13.posix.dirname(doc.path) === "." ? "" : path13.posix.dirname(doc.path) + "/")).join("\n");
|
|
1604
|
+
}
|
|
1605
|
+
function moduleCandidates(root, combinedDocs) {
|
|
1606
|
+
return inspectionRead(root, "modules:" + combinedDocs, () => collectModuleCandidates(root, combinedDocs));
|
|
1532
1607
|
}
|
|
1533
|
-
async function
|
|
1608
|
+
async function collectModuleCandidates(root, combinedDocs) {
|
|
1534
1609
|
const [config, sourcePaths] = await Promise.all([loadProjectConfig(root), gitSourcePaths(root)]);
|
|
1535
1610
|
const ignore = [...SOURCE_IGNORE, ...config.ignore ?? []];
|
|
1536
1611
|
const sources = sourcePaths.filter((file) => !file.split("/").some((part) => part.startsWith(".")));
|
|
1537
|
-
const listSubdirs = async (
|
|
1538
|
-
const
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1612
|
+
const listSubdirs = async (parents) => {
|
|
1613
|
+
const byParent = /* @__PURE__ */ new Map();
|
|
1614
|
+
for (let offset = 0; offset < parents.length; offset += 16) {
|
|
1615
|
+
await Promise.all(parents.slice(offset, offset + 16).map(async (dir) => {
|
|
1616
|
+
const entries = await fs7.readdir(await auditInputPath(root, dir), { withFileTypes: true });
|
|
1617
|
+
const dirs = entries.filter((entry) => (entry.isDirectory() || entry.isSymbolicLink() && sources.some((file) => file.startsWith(path13.posix.join(dir, entry.name) + "/"))) && !entry.name.startsWith(".") && !DIR_DENYLIST.has(entry.name)).map((entry) => path13.posix.join(dir, entry.name));
|
|
1618
|
+
byParent.set(dir, dirs);
|
|
1619
|
+
}));
|
|
1620
|
+
}
|
|
1621
|
+
const ignored = await gitIgnoredPaths(root, [...byParent.values()].flat());
|
|
1622
|
+
const result = /* @__PURE__ */ new Map();
|
|
1623
|
+
for (const dir of parents) {
|
|
1624
|
+
const visible = new Set(byParent.get(dir).filter((candidate) => !ignored.has(candidate) || sources.some((file) => file.startsWith(candidate + "/"))));
|
|
1625
|
+
result.set(dir, (await auditGlob(root, dir === "." ? "*" : `${fg4.escapePath(dir)}/*`, {
|
|
1626
|
+
ignore,
|
|
1627
|
+
onlyDirectories: true,
|
|
1628
|
+
label: "Module directory discovery",
|
|
1629
|
+
select: (file) => visible.has(file)
|
|
1630
|
+
})).map((file) => path13.posix.basename(file)));
|
|
1631
|
+
}
|
|
1632
|
+
return result;
|
|
1548
1633
|
};
|
|
1549
1634
|
const countSourceFiles = async (dir) => {
|
|
1550
1635
|
const files = sources.filter((file) => file.startsWith(dir + "/"));
|
|
@@ -1560,14 +1645,16 @@ async function moduleCandidates(root, combinedDocs) {
|
|
|
1560
1645
|
return count4;
|
|
1561
1646
|
};
|
|
1562
1647
|
const candidates = [];
|
|
1563
|
-
|
|
1648
|
+
const topDirs = (await listSubdirs(["."])).get(".");
|
|
1649
|
+
const nested = await listSubdirs(topDirs.filter((dir) => isMentioned(combinedDocs, dir)));
|
|
1650
|
+
for (const topDir of topDirs) {
|
|
1564
1651
|
const topMentioned = isMentioned(combinedDocs, topDir);
|
|
1565
1652
|
if (!topMentioned) {
|
|
1566
1653
|
const count4 = await countSourceFiles(topDir);
|
|
1567
1654
|
if (count4 >= 1) candidates.push({ dir: topDir, sourceFileCount: count4 });
|
|
1568
1655
|
continue;
|
|
1569
1656
|
}
|
|
1570
|
-
const subdirs =
|
|
1657
|
+
const subdirs = nested.get(topDir);
|
|
1571
1658
|
const mentioned = subdirs.filter((s) => isMentioned(combinedDocs, s));
|
|
1572
1659
|
if (mentioned.length < ENUMERATION_THRESHOLD) continue;
|
|
1573
1660
|
for (const sub of subdirs) {
|
|
@@ -1584,6 +1671,7 @@ var DIR_DENYLIST, SECOND_LEVEL_MIN_SOURCE_FILES, ENUMERATION_THRESHOLD;
|
|
|
1584
1671
|
var init_new_module = __esm({
|
|
1585
1672
|
"src/audit/checks/new-module.ts"() {
|
|
1586
1673
|
"use strict";
|
|
1674
|
+
init_inspection();
|
|
1587
1675
|
init_files();
|
|
1588
1676
|
init_inputs();
|
|
1589
1677
|
init_git();
|
|
@@ -1613,7 +1701,7 @@ var init_new_module = __esm({
|
|
|
1613
1701
|
});
|
|
1614
1702
|
|
|
1615
1703
|
// src/audit/checks/stale-count.ts
|
|
1616
|
-
import
|
|
1704
|
+
import path14 from "path";
|
|
1617
1705
|
async function countGradleModules(root) {
|
|
1618
1706
|
for (const name of ["settings.gradle.kts", "settings.gradle"]) {
|
|
1619
1707
|
const content2 = await readAuditInput(root, name);
|
|
@@ -1647,7 +1735,7 @@ async function countNpmWorkspaces(root) {
|
|
|
1647
1735
|
return {
|
|
1648
1736
|
actual: matched.length,
|
|
1649
1737
|
countedFrom: "package.json workspaces",
|
|
1650
|
-
members: matched.map((m) =>
|
|
1738
|
+
members: matched.map((m) => path14.dirname(m)).sort()
|
|
1651
1739
|
};
|
|
1652
1740
|
}
|
|
1653
1741
|
}
|
|
@@ -1678,7 +1766,7 @@ async function countNpmWorkspaces(root) {
|
|
|
1678
1766
|
return {
|
|
1679
1767
|
actual: matched.length,
|
|
1680
1768
|
countedFrom: "pnpm-workspace.yaml",
|
|
1681
|
-
members: matched.map((m) =>
|
|
1769
|
+
members: matched.map((m) => path14.dirname(m)).sort()
|
|
1682
1770
|
};
|
|
1683
1771
|
}
|
|
1684
1772
|
}
|
|
@@ -1700,8 +1788,8 @@ async function countCargoCrates(root) {
|
|
|
1700
1788
|
ignore: ["**/target/**"],
|
|
1701
1789
|
label: "Cargo workspace discovery"
|
|
1702
1790
|
});
|
|
1703
|
-
for (const m of matched) members.add(
|
|
1704
|
-
} else if (await readAuditInput(root,
|
|
1791
|
+
for (const m of matched) members.add(path14.dirname(m));
|
|
1792
|
+
} else if (await readAuditInput(root, path14.posix.join(entry, "Cargo.toml")) !== null) {
|
|
1705
1793
|
members.add(entry);
|
|
1706
1794
|
}
|
|
1707
1795
|
}
|
|
@@ -1721,7 +1809,7 @@ async function resolveCountSource(root, claim) {
|
|
|
1721
1809
|
}
|
|
1722
1810
|
async function resolveDocCountSource(root, doc, claim) {
|
|
1723
1811
|
const scope = documentScope(doc);
|
|
1724
|
-
const source = await resolveCountSource(
|
|
1812
|
+
const source = await resolveCountSource(path14.join(root, scope), claim);
|
|
1725
1813
|
if (!source || scope === ".") return source;
|
|
1726
1814
|
return {
|
|
1727
1815
|
...source,
|
|
@@ -1773,7 +1861,7 @@ var init_stale_count = __esm({
|
|
|
1773
1861
|
});
|
|
1774
1862
|
|
|
1775
1863
|
// src/audit/checks/dead-command.ts
|
|
1776
|
-
import
|
|
1864
|
+
import path15 from "path";
|
|
1777
1865
|
function scriptsOf(raw) {
|
|
1778
1866
|
try {
|
|
1779
1867
|
const pkg = JSON.parse(raw);
|
|
@@ -1809,7 +1897,7 @@ async function commandInputs(root, docs) {
|
|
|
1809
1897
|
}
|
|
1810
1898
|
let cursor = directory;
|
|
1811
1899
|
while (true) {
|
|
1812
|
-
const manifest =
|
|
1900
|
+
const manifest = path15.posix.join(cursor, "package.json");
|
|
1813
1901
|
scope.candidates.push(manifest);
|
|
1814
1902
|
const raw = await read(manifest);
|
|
1815
1903
|
if (raw !== null) {
|
|
@@ -1820,7 +1908,7 @@ async function commandInputs(root, docs) {
|
|
|
1820
1908
|
break;
|
|
1821
1909
|
}
|
|
1822
1910
|
if (claim.directory !== void 0 || cursor === ".") break;
|
|
1823
|
-
cursor =
|
|
1911
|
+
cursor = path15.posix.dirname(cursor);
|
|
1824
1912
|
}
|
|
1825
1913
|
if (!resolution.manifest && !resolution.problem) {
|
|
1826
1914
|
resolution.problem = `No package.json for the command's scope (${directory}).`;
|
|
@@ -1883,10 +1971,8 @@ var init_dead_command = __esm({
|
|
|
1883
1971
|
});
|
|
1884
1972
|
|
|
1885
1973
|
// src/audit/release-metadata.ts
|
|
1886
|
-
import { execFile as execFile7 } from "child_process";
|
|
1887
|
-
import { promisify as promisify7 } from "util";
|
|
1888
1974
|
async function git(root, args) {
|
|
1889
|
-
return (await
|
|
1975
|
+
return (await execGit(args, { cwd: root, timeout: 1e4, maxBuffer: 2 * 1024 * 1024 })).stdout;
|
|
1890
1976
|
}
|
|
1891
1977
|
function withoutAndroidReleaseValues(text2) {
|
|
1892
1978
|
if (/\/\*|"""|'''/.test(text2) || !/id\s*\(?\s*["']com\.android\.(application|library)["']/.test(text2)) return null;
|
|
@@ -1932,11 +2018,10 @@ async function releaseMetadataOnly(root, commit) {
|
|
|
1932
2018
|
return false;
|
|
1933
2019
|
}
|
|
1934
2020
|
}
|
|
1935
|
-
var exec6;
|
|
1936
2021
|
var init_release_metadata = __esm({
|
|
1937
2022
|
"src/audit/release-metadata.ts"() {
|
|
1938
2023
|
"use strict";
|
|
1939
|
-
|
|
2024
|
+
init_git_read();
|
|
1940
2025
|
}
|
|
1941
2026
|
});
|
|
1942
2027
|
|
|
@@ -2032,7 +2117,7 @@ var init_deps_changed = __esm({
|
|
|
2032
2117
|
});
|
|
2033
2118
|
|
|
2034
2119
|
// src/context/lexical.ts
|
|
2035
|
-
import
|
|
2120
|
+
import path16 from "path";
|
|
2036
2121
|
function tokenize(text2) {
|
|
2037
2122
|
return text2.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 2 && !STOPWORDS.has(t));
|
|
2038
2123
|
}
|
|
@@ -2045,7 +2130,7 @@ function tokenSet(text2) {
|
|
|
2045
2130
|
function scoreEntry(taskTokens, entry) {
|
|
2046
2131
|
const nameTokens = tokenSet(entry.name);
|
|
2047
2132
|
const descTokens = tokenSet(entry.description);
|
|
2048
|
-
const fileTokens = tokenSet(entry.files.map((f) =>
|
|
2133
|
+
const fileTokens = tokenSet(entry.files.map((f) => path16.basename(f)).join(" "));
|
|
2049
2134
|
let score = 0;
|
|
2050
2135
|
for (const token of taskTokens) {
|
|
2051
2136
|
if (nameTokens.has(token)) score += 3;
|
|
@@ -2377,7 +2462,7 @@ __export(decisions_exports, {
|
|
|
2377
2462
|
withDecisionWrite: () => withDecisionWrite
|
|
2378
2463
|
});
|
|
2379
2464
|
import fs8 from "fs/promises";
|
|
2380
|
-
import
|
|
2465
|
+
import path17 from "path";
|
|
2381
2466
|
import { createHash } from "crypto";
|
|
2382
2467
|
import { z as z3 } from "zod";
|
|
2383
2468
|
async function loadDecisionStore(rootDir) {
|
|
@@ -2475,7 +2560,7 @@ async function upsertDecision(rootDir, input) {
|
|
|
2475
2560
|
if (input.files && files.length < input.files.length) warnings.push("some anchor paths were outside the repo or duplicated and were dropped");
|
|
2476
2561
|
for (const file of files) {
|
|
2477
2562
|
try {
|
|
2478
|
-
await fs8.access(
|
|
2563
|
+
await fs8.access(path17.join(rootDir, file));
|
|
2479
2564
|
} catch {
|
|
2480
2565
|
warnings.push(`anchor file does not exist on disk: ${file}`);
|
|
2481
2566
|
}
|
|
@@ -2600,9 +2685,9 @@ var init_decisions = __esm({
|
|
|
2600
2685
|
});
|
|
2601
2686
|
|
|
2602
2687
|
// src/decisions/drift.ts
|
|
2603
|
-
import
|
|
2688
|
+
import path18 from "path";
|
|
2604
2689
|
async function computeDecisionDrift(rootDir, decisions) {
|
|
2605
|
-
const resolvedRoot =
|
|
2690
|
+
const resolvedRoot = path18.resolve(rootDir);
|
|
2606
2691
|
const store = decisions ? { records: decisions, diagnostics: [] } : await loadDecisionStore(resolvedRoot);
|
|
2607
2692
|
const report = { historyAvailable: true, totalDecisions: store.records.length, staleDecisions: {}, freshness: {}, diagnostics: store.diagnostics };
|
|
2608
2693
|
const [head, workingTree] = await Promise.all([getCurrentGitHash(resolvedRoot), getWorkingTree(resolvedRoot)]);
|
|
@@ -2924,8 +3009,6 @@ var init_store = __esm({
|
|
|
2924
3009
|
|
|
2925
3010
|
// src/audit/advisory-review.ts
|
|
2926
3011
|
import fs10 from "fs/promises";
|
|
2927
|
-
import { execFile as execFile8 } from "child_process";
|
|
2928
|
-
import { promisify as promisify8 } from "util";
|
|
2929
3012
|
import { z as z6 } from "zod";
|
|
2930
3013
|
async function readRecord(root, id) {
|
|
2931
3014
|
const raw = await readStoreJson(root, recordPath(id));
|
|
@@ -3120,10 +3203,12 @@ async function reviewAdvisory(rootDir, input) {
|
|
|
3120
3203
|
};
|
|
3121
3204
|
});
|
|
3122
3205
|
}
|
|
3123
|
-
var
|
|
3206
|
+
var git2, hashSchema, commitSchema2, evidenceSchema2, eventSchema2, recordSchema, reviewSummarySchema, advisoryReviewRequest, recordPath;
|
|
3124
3207
|
var init_advisory_review = __esm({
|
|
3125
3208
|
"src/audit/advisory-review.ts"() {
|
|
3126
3209
|
"use strict";
|
|
3210
|
+
init_inspection();
|
|
3211
|
+
init_git_read();
|
|
3127
3212
|
init_findings();
|
|
3128
3213
|
init_deps_changed();
|
|
3129
3214
|
init_inputs();
|
|
@@ -3133,8 +3218,7 @@ var init_advisory_review = __esm({
|
|
|
3133
3218
|
init_store();
|
|
3134
3219
|
init_decisions();
|
|
3135
3220
|
init_drift2();
|
|
3136
|
-
|
|
3137
|
-
git2 = async (root, ...args) => (await execute2("git", args, { cwd: root, timeout: 1e4, maxBuffer: 8 * 1024 * 1024 })).stdout;
|
|
3221
|
+
git2 = async (root, ...args) => (await (["log", "ls-tree", "ls-files", "merge-base"].includes(args[0]) ? inspectionGit : execGit)(args, { cwd: root, timeout: 1e4, maxBuffer: 8 * 1024 * 1024 })).stdout;
|
|
3138
3222
|
hashSchema = z6.string().regex(/^[a-f0-9]{64}$/);
|
|
3139
3223
|
commitSchema2 = z6.string().regex(/^[a-f0-9]{40,64}$/);
|
|
3140
3224
|
evidenceSchema2 = z6.object({
|
|
@@ -3178,9 +3262,12 @@ var init_advisory_review = __esm({
|
|
|
3178
3262
|
|
|
3179
3263
|
// src/audit/audit.ts
|
|
3180
3264
|
import fs11 from "fs/promises";
|
|
3181
|
-
import
|
|
3265
|
+
import path19 from "path";
|
|
3182
3266
|
async function computeAudit(rootDir, options = {}) {
|
|
3183
|
-
|
|
3267
|
+
return profilePhase("audit.current", () => auditCurrent(rootDir, options));
|
|
3268
|
+
}
|
|
3269
|
+
async function auditCurrent(rootDir, options) {
|
|
3270
|
+
const resolvedRoot = path19.resolve(rootDir);
|
|
3184
3271
|
const [docs, headHash] = await Promise.all([discoverDocs(resolvedRoot), getCurrentGitHash(resolvedRoot)]);
|
|
3185
3272
|
if (docs.length === 0) return null;
|
|
3186
3273
|
const report = {
|
|
@@ -3206,15 +3293,15 @@ async function computeAudit(rootDir, options = {}) {
|
|
|
3206
3293
|
};
|
|
3207
3294
|
if (!report.gitAvailable) return report;
|
|
3208
3295
|
const changesSinceDoc = /* @__PURE__ */ new Map();
|
|
3296
|
+
const changesByCommit = /* @__PURE__ */ new Map();
|
|
3209
3297
|
for (const doc of docs) {
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
);
|
|
3298
|
+
const hash2 = doc.lastCommit?.hash;
|
|
3299
|
+
if (hash2 && !changesByCommit.has(hash2)) changesByCommit.set(hash2, await getChangesWithStatus(resolvedRoot, hash2));
|
|
3300
|
+
changesSinceDoc.set(doc.path, hash2 ? changesByCommit.get(hash2) : null);
|
|
3214
3301
|
}
|
|
3215
3302
|
let decisionsPresent = false;
|
|
3216
3303
|
try {
|
|
3217
|
-
await fs11.access(
|
|
3304
|
+
await fs11.access(path19.join(resolvedRoot, ".mason", "decisions"));
|
|
3218
3305
|
decisionsPresent = true;
|
|
3219
3306
|
} catch {
|
|
3220
3307
|
}
|
|
@@ -3244,6 +3331,7 @@ async function computeAudit(rootDir, options = {}) {
|
|
|
3244
3331
|
var init_audit = __esm({
|
|
3245
3332
|
"src/audit/audit.ts"() {
|
|
3246
3333
|
"use strict";
|
|
3334
|
+
init_profile();
|
|
3247
3335
|
init_drift();
|
|
3248
3336
|
init_snapshot();
|
|
3249
3337
|
init_docs();
|
|
@@ -3263,10 +3351,12 @@ __export(repair_exports, {
|
|
|
3263
3351
|
prepareRepair: () => prepareRepair,
|
|
3264
3352
|
repairExitCode: () => repairExitCode,
|
|
3265
3353
|
repairFindingSchema: () => repairFindingSchema,
|
|
3266
|
-
|
|
3354
|
+
reportSchema: () => reportSchema,
|
|
3355
|
+
verifyRepair: () => verifyRepair,
|
|
3356
|
+
withRepairSession: () => withRepairSession
|
|
3267
3357
|
});
|
|
3268
3358
|
import fs12 from "fs/promises";
|
|
3269
|
-
import
|
|
3359
|
+
import path20 from "path";
|
|
3270
3360
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3271
3361
|
import { z as z7 } from "zod";
|
|
3272
3362
|
function recheckable(finding) {
|
|
@@ -3290,6 +3380,9 @@ async function docState(root) {
|
|
|
3290
3380
|
return digest(docs);
|
|
3291
3381
|
}
|
|
3292
3382
|
async function stableAudit(root, checks, options = {}) {
|
|
3383
|
+
return profilePhase("repair.audit", () => readStableAudit(root, checks, options));
|
|
3384
|
+
}
|
|
3385
|
+
async function readStableAudit(root, checks, options) {
|
|
3293
3386
|
const head = await getCurrentGitHash(root);
|
|
3294
3387
|
const before = await docState(root);
|
|
3295
3388
|
const report = await computeAudit(root, { ...options, checks });
|
|
@@ -3299,9 +3392,45 @@ async function stableAudit(root, checks, options = {}) {
|
|
|
3299
3392
|
return report;
|
|
3300
3393
|
}
|
|
3301
3394
|
async function prepareRepair(rootDir, checks = ALL_CHECKS, options = {}) {
|
|
3395
|
+
return prepareOriginal(rootDir, checks, options);
|
|
3396
|
+
}
|
|
3397
|
+
async function withRepairSession(root, options, run, validate, cachedAudit) {
|
|
3398
|
+
const head = await getCurrentGitHash(root);
|
|
3399
|
+
const audits = /* @__PURE__ */ new Map();
|
|
3400
|
+
const documents = /* @__PURE__ */ new Map();
|
|
3401
|
+
const history = /* @__PURE__ */ new Map();
|
|
3402
|
+
const assessments = /* @__PURE__ */ new Map();
|
|
3403
|
+
const memo = (cache, key, read) => {
|
|
3404
|
+
if (!cache.has(key)) cache.set(key, read());
|
|
3405
|
+
return cache.get(key);
|
|
3406
|
+
};
|
|
3407
|
+
const session = {
|
|
3408
|
+
audit: (checks) => memo(audits, [...checks].sort().join(","), async () => {
|
|
3409
|
+
if (cachedAudit && cachedAudit.headHash === head && cachedAudit.root === root && digest([...checks].sort()) === digest([...cachedAudit.checksRun ?? []].sort())) {
|
|
3410
|
+
const current = structuredClone(cachedAudit);
|
|
3411
|
+
const reviews = (await Promise.all(allFindings(current).map((f) => assessAdvisory(root, f, head)))).filter((r) => r !== null);
|
|
3412
|
+
delete current.advisoryReviews;
|
|
3413
|
+
if (reviews.length) current.advisoryReviews = reviews;
|
|
3414
|
+
return current;
|
|
3415
|
+
}
|
|
3416
|
+
return computeAudit(root, { ...options, checks });
|
|
3417
|
+
}),
|
|
3418
|
+
document: (file) => memo(documents, file, async () => readBoundedFile(await storePath(root, file), 10 * 1024 * 1024)),
|
|
3419
|
+
history: (head2) => memo(history, head2, () => getChangesWithStatus(root, head2)),
|
|
3420
|
+
assessment: (dir, finding, head2) => memo(assessments, digest([dir, finding, head2]), () => assessAdvisory(dir, finding, head2))
|
|
3421
|
+
};
|
|
3422
|
+
const result = await run({
|
|
3423
|
+
prepare: () => prepareOriginal(root, ALL_CHECKS, options, session),
|
|
3424
|
+
verify: (baseline) => profilePhase("repair.verify", () => verifyOriginal(root, baseline, options, session)),
|
|
3425
|
+
currentAudit: () => session.audit(ALL_CHECKS)
|
|
3426
|
+
});
|
|
3427
|
+
await validate();
|
|
3428
|
+
return result;
|
|
3429
|
+
}
|
|
3430
|
+
async function prepareOriginal(rootDir, checks, options, session) {
|
|
3302
3431
|
const root = await fs12.realpath(rootDir);
|
|
3303
3432
|
const selected = z7.array(checkSchema).nonempty().parse(checks);
|
|
3304
|
-
const report = await stableAudit(root, selected, options);
|
|
3433
|
+
const report = await (session ? session.audit(selected) : stableAudit(root, selected, options));
|
|
3305
3434
|
if (!report) throw new Error("No context files found to prepare a repair.");
|
|
3306
3435
|
if (!report.gitAvailable) throw new Error("Readable Git history is required to prepare a repair.");
|
|
3307
3436
|
const storedReport = reportSchema.parse(report);
|
|
@@ -3317,8 +3446,8 @@ async function prepareRepair(rootDir, checks = ALL_CHECKS, options = {}) {
|
|
|
3317
3446
|
}
|
|
3318
3447
|
async function loadRepairBaseline(rootDir, baselinePath) {
|
|
3319
3448
|
const root = await fs12.realpath(rootDir);
|
|
3320
|
-
const declaredRoot =
|
|
3321
|
-
const relative =
|
|
3449
|
+
const declaredRoot = path20.resolve(rootDir);
|
|
3450
|
+
const relative = path20.isAbsolute(baselinePath) ? path20.relative(isWithinRoot(declaredRoot, baselinePath) ? declaredRoot : root, baselinePath) : baselinePath;
|
|
3322
3451
|
const stored = baselineSchema.parse(await readStoreJson(root, relative));
|
|
3323
3452
|
const { digest: savedDigest, ...payload } = stored;
|
|
3324
3453
|
if (digest(payload) !== savedDigest) throw new Error("Repair baseline was modified; use the original baseline.");
|
|
@@ -3327,23 +3456,26 @@ async function loadRepairBaseline(rootDir, baselinePath) {
|
|
|
3327
3456
|
return { root, relative, original };
|
|
3328
3457
|
}
|
|
3329
3458
|
async function verifyRepair(rootDir, baselinePath, options = {}) {
|
|
3459
|
+
return profilePhase("repair.verify", () => verifyOriginal(rootDir, baselinePath, options));
|
|
3460
|
+
}
|
|
3461
|
+
async function verifyOriginal(rootDir, baselinePath, options, session) {
|
|
3330
3462
|
const { root, relative, original } = await loadRepairBaseline(rootDir, baselinePath);
|
|
3331
3463
|
const diagnostics = [];
|
|
3332
3464
|
let reviewDigest;
|
|
3333
3465
|
let current = null;
|
|
3334
3466
|
try {
|
|
3335
|
-
reviewDigest = digest(await advisoryReviewInventory(root));
|
|
3336
|
-
current = await stableAudit(root, original.checksRun, options);
|
|
3467
|
+
if (!session) reviewDigest = digest(await advisoryReviewInventory(root));
|
|
3468
|
+
current = await (session ? session.audit(original.checksRun) : stableAudit(root, original.checksRun, options));
|
|
3337
3469
|
if (!current) diagnostics.push("No context files remain available to audit.");
|
|
3338
3470
|
else if (!current.gitAvailable) diagnostics.push("Git history is unavailable.");
|
|
3339
3471
|
for (const doc of original.docs) {
|
|
3340
3472
|
if (!allFindings(original).some((f) => f.anchor.doc === doc.path) || !current?.docs.some((d) => d.path === doc.path)) continue;
|
|
3341
|
-
const content2 = await readBoundedFile(await storePath(root, doc.path), 10 * 1024 * 1024);
|
|
3473
|
+
const content2 = await (session ? session.document(doc.path) : readBoundedFile(await storePath(root, doc.path), 10 * 1024 * 1024));
|
|
3342
3474
|
if (content2 === null || !content2.trim()) {
|
|
3343
3475
|
diagnostics.push("Original context file " + doc.path + " is empty or unreadable; losing its claims does not verify a repair.");
|
|
3344
3476
|
}
|
|
3345
3477
|
}
|
|
3346
|
-
if (await getChangesWithStatus(root, original.headHash) === null) {
|
|
3478
|
+
if (await (session ? session.history(original.headHash) : getChangesWithStatus(root, original.headHash)) === null) {
|
|
3347
3479
|
diagnostics.push("The original audit commit is unavailable; repair history cannot be verified.");
|
|
3348
3480
|
}
|
|
3349
3481
|
} catch (error) {
|
|
@@ -3368,7 +3500,7 @@ async function verifyRepair(rootDir, baselinePath, options = {}) {
|
|
|
3368
3500
|
if (!current.checksRun?.includes(finding.type) || skipped.length) {
|
|
3369
3501
|
return { ...base, status: "unverified", reason: skipped.map((s) => s.reason).join("; ") || "The original check did not run." };
|
|
3370
3502
|
}
|
|
3371
|
-
const review = await assessAdvisory(root, now ?? finding, original.headHash);
|
|
3503
|
+
const review = await (session?.assessment ?? assessAdvisory)(root, now ?? finding, original.headHash);
|
|
3372
3504
|
if (review) {
|
|
3373
3505
|
if (review.status === "unverified") return { ...base, review, status: "unverified", reason: review.reason };
|
|
3374
3506
|
if (review.status === "current") return { ...base, review, status: "resolved", reason: review.reason };
|
|
@@ -3383,7 +3515,7 @@ async function verifyRepair(rootDir, baselinePath, options = {}) {
|
|
|
3383
3515
|
}
|
|
3384
3516
|
return { ...base, status: "resolved", reason: "The original check ran and no longer reports this claim. Inspect the edit for semantic correctness." };
|
|
3385
3517
|
}));
|
|
3386
|
-
try {
|
|
3518
|
+
if (!session) try {
|
|
3387
3519
|
if (reviewDigest !== digest(await advisoryReviewInventory(root)) || current?.headHash !== await getCurrentGitHash(root)) {
|
|
3388
3520
|
diagnostics.push("HEAD or advisory review records changed during verification; repeat against stable evidence.");
|
|
3389
3521
|
for (const finding of findings) {
|
|
@@ -3436,6 +3568,7 @@ var count2, reportSchema, baselineSchema, repairFindingSchema;
|
|
|
3436
3568
|
var init_repair = __esm({
|
|
3437
3569
|
"src/audit/repair.ts"() {
|
|
3438
3570
|
"use strict";
|
|
3571
|
+
init_profile();
|
|
3439
3572
|
init_audit();
|
|
3440
3573
|
init_docs();
|
|
3441
3574
|
init_snapshot();
|
|
@@ -3489,27 +3622,25 @@ var init_repair = __esm({
|
|
|
3489
3622
|
|
|
3490
3623
|
// src/automation/evidence.ts
|
|
3491
3624
|
import fs13 from "fs/promises";
|
|
3492
|
-
import
|
|
3625
|
+
import path21 from "path";
|
|
3493
3626
|
import { createHash as createHash3 } from "crypto";
|
|
3494
|
-
import { execFile as execFile9 } from "child_process";
|
|
3495
|
-
import { promisify as promisify9 } from "util";
|
|
3496
3627
|
import fg5 from "fast-glob";
|
|
3497
3628
|
import { z as z8 } from "zod";
|
|
3498
3629
|
async function git3(root, ...args) {
|
|
3499
|
-
return (
|
|
3630
|
+
return profilePhase("automation.git", async () => (await execGit(args, { cwd: root, maxBuffer: 16 * 1024 * 1024, timeout: 1e4 })).stdout);
|
|
3500
3631
|
}
|
|
3501
3632
|
async function workspace(dir) {
|
|
3502
3633
|
const root = await fs13.realpath((await git3(dir, "rev-parse", "--show-toplevel")).trim());
|
|
3503
|
-
const gitDir = await
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
} catch {
|
|
3508
|
-
branch = "detached";
|
|
3509
|
-
}
|
|
3634
|
+
const [gitDir, branch] = await Promise.all([
|
|
3635
|
+
git3(root, "rev-parse", "--absolute-git-dir").then((value) => fs13.realpath(value.trim())),
|
|
3636
|
+
git3(root, "symbolic-ref", "--quiet", "HEAD").then((value) => value.trim(), () => "detached")
|
|
3637
|
+
]);
|
|
3510
3638
|
return { root, gitDir, branch, directory: ".mason/reports/automation/" + hash([root, gitDir, branch]).slice(0, 24) };
|
|
3511
3639
|
}
|
|
3512
3640
|
async function readInputs(root) {
|
|
3641
|
+
return profilePhase("automation.inputs", () => collectInputs(root));
|
|
3642
|
+
}
|
|
3643
|
+
async function collectInputs(root) {
|
|
3513
3644
|
const docPaths = await discoverDocPaths(root);
|
|
3514
3645
|
const [headText, docStatus, shallowPath, replacements] = await Promise.all([
|
|
3515
3646
|
git3(root, "rev-parse", "HEAD"),
|
|
@@ -3520,27 +3651,35 @@ async function readInputs(root) {
|
|
|
3520
3651
|
const head = headText.trim();
|
|
3521
3652
|
let shallow = null;
|
|
3522
3653
|
try {
|
|
3523
|
-
shallow = await fs13.readFile(
|
|
3654
|
+
shallow = await fs13.readFile(path21.resolve(root, shallowPath.trim()), "utf8");
|
|
3524
3655
|
} catch (error) {
|
|
3525
3656
|
if (error.code !== "ENOENT") throw error;
|
|
3526
3657
|
}
|
|
3527
3658
|
const docs = {};
|
|
3528
3659
|
const docContents = [];
|
|
3660
|
+
const parsedDocs = [];
|
|
3529
3661
|
const claims = [];
|
|
3662
|
+
const presence = /* @__PURE__ */ new Map();
|
|
3663
|
+
const exists = (file) => {
|
|
3664
|
+
if (!presence.has(file)) presence.set(file, pathExists(root, file));
|
|
3665
|
+
return presence.get(file);
|
|
3666
|
+
};
|
|
3530
3667
|
for (const file of docPaths) {
|
|
3531
3668
|
const text2 = await content(root, file);
|
|
3532
3669
|
docs[file] = text2 === null ? null : hash(text2);
|
|
3533
3670
|
docContents.push([file, text2]);
|
|
3534
|
-
|
|
3671
|
+
const parsed = { path: file, content: text2 ?? "", claims: extractClaims(text2 ?? "") };
|
|
3672
|
+
parsedDocs.push(parsed);
|
|
3673
|
+
for (const claim of parsed.claims.paths) {
|
|
3535
3674
|
const scope = pathClaimScope(file, claim);
|
|
3536
3675
|
if (scope?.candidates.some(gitMetadataPath)) continue;
|
|
3537
3676
|
for (const candidate of scope?.candidates ?? []) {
|
|
3538
3677
|
if (optionalMasonPath(candidate)) continue;
|
|
3539
|
-
|
|
3678
|
+
const [filePresent, parentPresent] = await Promise.all([exists(candidate), exists(path21.posix.dirname(candidate))]);
|
|
3679
|
+
claims.push([candidate, filePresent, parentPresent]);
|
|
3540
3680
|
}
|
|
3541
3681
|
}
|
|
3542
3682
|
}
|
|
3543
|
-
const parsedDocs = docContents.map(([file, text2]) => ({ path: file, content: text2 ?? "", claims: extractClaims(text2 ?? "") }));
|
|
3544
3683
|
const combinedDocs = moduleDocumentation(parsedDocs);
|
|
3545
3684
|
const countClaims = parsedDocs.flatMap((doc) => doc.claims.counts.map((claim) => ({ doc: doc.path, claim })));
|
|
3546
3685
|
const decisionDirectory = await storePath(root, ".mason/decisions");
|
|
@@ -3548,7 +3687,7 @@ async function readInputs(root) {
|
|
|
3548
3687
|
if (error.code === "ENOENT") return "absent";
|
|
3549
3688
|
throw error;
|
|
3550
3689
|
});
|
|
3551
|
-
const [modules, counts,
|
|
3690
|
+
const [modules, counts, commands2, decisionFiles] = await Promise.all([
|
|
3552
3691
|
combinedDocs ? moduleCandidates(root, combinedDocs) : [],
|
|
3553
3692
|
Promise.all(countClaims.map(({ doc, claim }) => resolveDocCountSource(root, doc, claim))),
|
|
3554
3693
|
commandInputs(root, parsedDocs),
|
|
@@ -3562,12 +3701,12 @@ async function readInputs(root) {
|
|
|
3562
3701
|
git3(root, "status", "--porcelain=v1", "-z", "--untracked-files=all", "--", ".", ":(exclude).mason/reports"),
|
|
3563
3702
|
git3(root, "ls-files", "--stage", "-z", "--", ".", ":(exclude).mason/reports")
|
|
3564
3703
|
]) : ["", ""];
|
|
3565
|
-
const common = [
|
|
3704
|
+
const common = [5, engineVersion, head, shallow, replacements, docContents];
|
|
3566
3705
|
const keys = {
|
|
3567
3706
|
"deleted-reference": hash([common, claims, docStatus]),
|
|
3568
3707
|
"new-module": hash([common, modules]),
|
|
3569
3708
|
"stale-count": hash([common, counts]),
|
|
3570
|
-
"dead-command": hash([common,
|
|
3709
|
+
"dead-command": hash([common, commands2]),
|
|
3571
3710
|
"deps-changed": hash([common, docStatus]),
|
|
3572
3711
|
"decision-anchor-drift": hash([common, decisionPresence, decisions, status, index2])
|
|
3573
3712
|
};
|
|
@@ -3587,21 +3726,30 @@ function checkCache(raw, inputs) {
|
|
|
3587
3726
|
reused.add(name);
|
|
3588
3727
|
return structuredClone(entries[name].result);
|
|
3589
3728
|
}
|
|
3590
|
-
const result = await CHECKS[name](ctx);
|
|
3729
|
+
const result = await profilePhase("check." + name, () => CHECKS[name](ctx));
|
|
3591
3730
|
ran.add(name);
|
|
3592
3731
|
if (!result.skipped.length) entries[name] = { key: inputs.keys[name], result };
|
|
3593
3732
|
else delete entries[name];
|
|
3594
3733
|
return result;
|
|
3595
3734
|
} };
|
|
3596
|
-
return {
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3735
|
+
return {
|
|
3736
|
+
options,
|
|
3737
|
+
ran,
|
|
3738
|
+
reused,
|
|
3739
|
+
diagnostic,
|
|
3740
|
+
has: (name) => entries[name]?.key === inputs.keys[name] && !entries[name].result.skipped.length,
|
|
3741
|
+
serialize: () => {
|
|
3742
|
+
const canonical = cacheSchema.shape.entries.parse(entries);
|
|
3743
|
+
return { version: 1, entries: canonical, digest: hash(canonical) };
|
|
3744
|
+
}
|
|
3745
|
+
};
|
|
3600
3746
|
}
|
|
3601
|
-
var
|
|
3747
|
+
var engineVersion, hash, content, cacheSchema;
|
|
3602
3748
|
var init_evidence = __esm({
|
|
3603
3749
|
"src/automation/evidence.ts"() {
|
|
3604
3750
|
"use strict";
|
|
3751
|
+
init_git_read();
|
|
3752
|
+
init_profile();
|
|
3605
3753
|
init_docs();
|
|
3606
3754
|
init_scope();
|
|
3607
3755
|
init_claims();
|
|
@@ -3613,8 +3761,7 @@ var init_evidence = __esm({
|
|
|
3613
3761
|
init_dead_command();
|
|
3614
3762
|
init_storage();
|
|
3615
3763
|
init_advisory_review();
|
|
3616
|
-
|
|
3617
|
-
engineVersion = true ? "0.17.2" : "development";
|
|
3764
|
+
engineVersion = true ? "0.17.4" : "development";
|
|
3618
3765
|
hash = (value) => createHash3("sha256").update(JSON.stringify(value)).digest("hex");
|
|
3619
3766
|
content = readAuditInput;
|
|
3620
3767
|
cacheSchema = z8.object({ version: z8.literal(1), entries: z8.record(z8.object({ key: z8.string(), result: checkResultSchema })), digest: z8.string() });
|
|
@@ -3674,8 +3821,8 @@ var init_model = __esm({
|
|
|
3674
3821
|
});
|
|
3675
3822
|
|
|
3676
3823
|
// src/setup/launcher.ts
|
|
3677
|
-
import { execFile as
|
|
3678
|
-
import { promisify as
|
|
3824
|
+
import { execFile as execFile2 } from "child_process";
|
|
3825
|
+
import { promisify as promisify2 } from "util";
|
|
3679
3826
|
import fs14 from "fs/promises";
|
|
3680
3827
|
function globalCommand(args, platform = process.platform) {
|
|
3681
3828
|
return platform === "win32" ? { command: "cmd.exe", args: ["/d", "/s", "/c", "mason", ...args] } : { command: "mason", args };
|
|
@@ -3683,7 +3830,7 @@ function globalCommand(args, platform = process.platform) {
|
|
|
3683
3830
|
async function installedCommand() {
|
|
3684
3831
|
const invocation = globalCommand(["internal-integration-version"]);
|
|
3685
3832
|
try {
|
|
3686
|
-
const { stdout } = await
|
|
3833
|
+
const { stdout } = await promisify2(execFile2)(
|
|
3687
3834
|
invocation.command,
|
|
3688
3835
|
invocation.args,
|
|
3689
3836
|
{ timeout: 5e3, maxBuffer: 8192, windowsHide: true }
|
|
@@ -3706,7 +3853,7 @@ var init_launcher = __esm({
|
|
|
3706
3853
|
"use strict";
|
|
3707
3854
|
init_evidence();
|
|
3708
3855
|
init_model();
|
|
3709
|
-
executingVersion = () => true ? "0.17.
|
|
3856
|
+
executingVersion = () => true ? "0.17.4" : "development";
|
|
3710
3857
|
mcpCommand = (host) => globalCommand(["--setup-host", host, "mcp"]);
|
|
3711
3858
|
hookCommand = (host) => `mason --setup-host ${host} auto`;
|
|
3712
3859
|
}
|
|
@@ -3720,7 +3867,7 @@ __export(files_exports, {
|
|
|
3720
3867
|
readText: () => readText
|
|
3721
3868
|
});
|
|
3722
3869
|
import fs17 from "fs/promises";
|
|
3723
|
-
import
|
|
3870
|
+
import path25 from "path";
|
|
3724
3871
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3725
3872
|
async function readText(root, file) {
|
|
3726
3873
|
try {
|
|
@@ -3753,7 +3900,7 @@ async function applyEdit(root, edit) {
|
|
|
3753
3900
|
return true;
|
|
3754
3901
|
}
|
|
3755
3902
|
const file = await storePath(root, edit.path, true);
|
|
3756
|
-
const temporary =
|
|
3903
|
+
const temporary = path25.join(path25.dirname(file), ".mason-setup-" + randomUUID3() + ".tmp");
|
|
3757
3904
|
try {
|
|
3758
3905
|
const mode = await fs17.stat(file).then((s) => s.mode & 511, () => 384);
|
|
3759
3906
|
const handle = await fs17.open(temporary, "wx", mode);
|
|
@@ -3981,7 +4128,7 @@ __export(runtime_exports, {
|
|
|
3981
4128
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
3982
4129
|
import { z as z13 } from "zod";
|
|
3983
4130
|
async function observeReadOnlyTool(dir, event) {
|
|
3984
|
-
const ws = await workspace(dir);
|
|
4131
|
+
const ws = await profilePhase("automation.workspace", () => workspace(dir));
|
|
3985
4132
|
await withLock(ws.root, ws.directory, async () => {
|
|
3986
4133
|
const statePath = ws.directory + "/state.json";
|
|
3987
4134
|
const raw = await readStoreJson(ws.root, statePath);
|
|
@@ -4028,24 +4175,41 @@ async function loadState(ws) {
|
|
|
4028
4175
|
}
|
|
4029
4176
|
return state;
|
|
4030
4177
|
}
|
|
4031
|
-
async function
|
|
4178
|
+
async function matchingAnalysis(ws, state, inputs) {
|
|
4032
4179
|
const pointer = state.analysis;
|
|
4033
|
-
if (!pointer || pointer.fingerprint !== inputs.fingerprint
|
|
4034
|
-
|
|
4180
|
+
if (!pointer || pointer.fingerprint !== inputs.fingerprint) return { analysis: null };
|
|
4181
|
+
if (pointer.path !== ws.directory + "/checks/analysis-" + inputs.fingerprint + ".json") {
|
|
4182
|
+
throw new Error("Invalid automation analysis cache path; inspect the retained state.");
|
|
4183
|
+
}
|
|
4184
|
+
const invalid = { analysis: null, diagnostic: "Discarded an invalid current-audit cache; audit preparation and original verification were repeated." };
|
|
4185
|
+
let raw;
|
|
4186
|
+
try {
|
|
4187
|
+
raw = await readStoreJson(ws.root, pointer.path);
|
|
4188
|
+
} catch (error) {
|
|
4189
|
+
const cause = error.cause;
|
|
4190
|
+
if (cause instanceof SyntaxError || cause instanceof Error && cause.message === "expected a JSON object, received null") return invalid;
|
|
4191
|
+
throw error;
|
|
4192
|
+
}
|
|
4193
|
+
if (raw === null) return { analysis: null };
|
|
4194
|
+
const parsed = sharedSchema.safeParse(raw);
|
|
4195
|
+
if (!parsed.success) return invalid;
|
|
4196
|
+
const stored = parsed.data;
|
|
4035
4197
|
const a = stored.analysis;
|
|
4036
4198
|
if (stored.digest !== hash(a) || a.id !== pointer.id || a.fingerprint !== inputs.fingerprint || a.completedAt !== pointer.completedAt || a.report.root !== ws.root || a.report.branch !== ws.branch || a.report.head !== inputs.head || hash(a.report.baselinePaths) !== hash(state.baselines.map((b) => b.path))) {
|
|
4037
|
-
|
|
4199
|
+
return invalid;
|
|
4038
4200
|
}
|
|
4039
4201
|
if (a.baselineDigest !== await baselineDigest(ws, state)) throw new Error("Repair baseline was modified; use the original baseline.");
|
|
4040
|
-
return a;
|
|
4202
|
+
return { analysis: a };
|
|
4041
4203
|
}
|
|
4042
4204
|
async function verifyInputs(ws, inputs) {
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4205
|
+
return profilePhase("automation.revalidate", () => withRepositoryInspection(ws.root, async () => {
|
|
4206
|
+
const [after, currentWs] = await Promise.all([readInputs(ws.root), workspace(ws.root)]);
|
|
4207
|
+
if (after.fingerprint !== inputs.fingerprint || currentWs.directory !== ws.directory) {
|
|
4208
|
+
throw new Error("Repository inputs or branch changed during automation; no current verification was recorded. Retry on a stable checkout.");
|
|
4209
|
+
}
|
|
4210
|
+
}));
|
|
4047
4211
|
}
|
|
4048
|
-
async function computeAnalysis(ws, state, inputs, event) {
|
|
4212
|
+
async function computeAnalysis(ws, state, inputs, event, previousAudit, cacheDiagnostic) {
|
|
4049
4213
|
const baselines = [...state.baselines];
|
|
4050
4214
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4051
4215
|
const empty = {
|
|
@@ -4063,7 +4227,10 @@ async function computeAnalysis(ws, state, inputs, event) {
|
|
|
4063
4227
|
capture: "unknown",
|
|
4064
4228
|
scope: SCOPE
|
|
4065
4229
|
};
|
|
4066
|
-
if (!baselines.length && Object.values(inputs.docs).every((value) => value === null))
|
|
4230
|
+
if (!baselines.length && Object.values(inputs.docs).every((value) => value === null)) {
|
|
4231
|
+
await verifyInputs(ws, inputs);
|
|
4232
|
+
return { baselines, report: empty, cache: null };
|
|
4233
|
+
}
|
|
4067
4234
|
if (ws.branch === "detached" && state.latest) {
|
|
4068
4235
|
const previous = await readStoreJson(ws.root, state.latest);
|
|
4069
4236
|
if (!previous?.head || !/^[a-f0-9]{40,64}$/.test(previous.head)) throw new Error("The previous detached checkout evidence is unavailable.");
|
|
@@ -4074,7 +4241,7 @@ async function computeAnalysis(ws, state, inputs, event) {
|
|
|
4074
4241
|
}
|
|
4075
4242
|
}
|
|
4076
4243
|
let cached = null;
|
|
4077
|
-
const diagnostics = [];
|
|
4244
|
+
const diagnostics = cacheDiagnostic ? [cacheDiagnostic] : [];
|
|
4078
4245
|
try {
|
|
4079
4246
|
cached = await readStoreJson(ws.root, ws.directory + "/cache.json");
|
|
4080
4247
|
} catch {
|
|
@@ -4082,19 +4249,26 @@ async function computeAnalysis(ws, state, inputs, event) {
|
|
|
4082
4249
|
}
|
|
4083
4250
|
const cache = checkCache(cached, inputs);
|
|
4084
4251
|
if (cache.diagnostic) diagnostics.push(cache.diagnostic);
|
|
4085
|
-
const
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4252
|
+
const cachedAudit = previousAudit?.gitAvailable && previousAudit.headHash === inputs.head && previousAudit.root === ws.root && !previousAudit.skippedChecks.length && ALL_CHECKS.every((name) => previousAudit.checksRun?.includes(name) && cache.has(name)) ? previousAudit : void 0;
|
|
4253
|
+
if (cachedAudit) for (const name of ALL_CHECKS) cache.reused.add(name);
|
|
4254
|
+
let currentAudit = null;
|
|
4255
|
+
const verifications = await withRepairSession(ws.root, cache.options, async (repairs) => {
|
|
4256
|
+
const saveBaseline = async () => {
|
|
4257
|
+
if (baselines.length >= 128) throw new Error("128 retained baselines need review; automatic capture stopped without discarding original evidence.");
|
|
4258
|
+
const prepared = await repairs.prepare();
|
|
4259
|
+
baselines.push({ path: prepared.baselinePath, at: now, event: event.event, fingerprint: inputs.fingerprint });
|
|
4260
|
+
};
|
|
4261
|
+
if (!baselines.length) await saveBaseline();
|
|
4262
|
+
const verifications2 = [];
|
|
4263
|
+
for (const baseline of baselines) verifications2.push(await repairs.verify(baseline.path));
|
|
4264
|
+
const known = new Set(verifications2.flatMap((v) => v.findings.map((f) => f.id)));
|
|
4265
|
+
if (verifications2.some((v) => v.newFindings.some((f) => !known.has(findingId(f))))) {
|
|
4266
|
+
await saveBaseline();
|
|
4267
|
+
verifications2.push(await repairs.verify(baselines.at(-1).path));
|
|
4268
|
+
}
|
|
4269
|
+
currentAudit = await repairs.currentAudit();
|
|
4270
|
+
return verifications2;
|
|
4271
|
+
}, () => verifyInputs(ws, inputs), cachedAudit);
|
|
4098
4272
|
const merged = /* @__PURE__ */ new Map();
|
|
4099
4273
|
for (const verification of verifications) {
|
|
4100
4274
|
for (const finding of verification.findings) {
|
|
@@ -4115,21 +4289,20 @@ async function computeAnalysis(ws, state, inputs, event) {
|
|
|
4115
4289
|
reportPath: "",
|
|
4116
4290
|
findings: [...merged.values()],
|
|
4117
4291
|
diagnostics: [...new Set(diagnostics)],
|
|
4118
|
-
checks: { ran: [...cache.ran], reused: [...cache.reused].filter((name) => !cache.ran.has(name)), skipped: verifications.at(-1).currentAudit?.skippedChecks ?? [] },
|
|
4292
|
+
checks: { ran: [...cache.ran], reused: [...cache.reused].filter((name) => !cache.ran.has(name)), skipped: verifications.at(-1).currentAudit?.skippedChecks ?? [], ...cachedAudit ? { auditReused: true } : {} },
|
|
4119
4293
|
counts,
|
|
4120
4294
|
capture: "unknown",
|
|
4121
4295
|
scope: SCOPE
|
|
4122
4296
|
};
|
|
4123
|
-
return { baselines, report, cache: cache.ran.size || cached === null || cache.diagnostic ? cache.serialize() : null };
|
|
4297
|
+
return { baselines, report, currentAudit, cache: cache.ran.size || cached === null || cache.diagnostic ? cache.serialize() : null };
|
|
4124
4298
|
}
|
|
4125
4299
|
async function analyze(ws, inputs, event, startedAt) {
|
|
4126
4300
|
return withLock(ws.root, ws.directory + "/analysis/" + inputs.fingerprint, async () => {
|
|
4127
4301
|
const before = await loadState(ws);
|
|
4128
|
-
const
|
|
4129
|
-
if (
|
|
4302
|
+
const { analysis: previous, diagnostic } = await matchingAnalysis(ws, before, inputs);
|
|
4303
|
+
if (previous && previous.completedAt >= startedAt) return { analysis: previous, shared: true };
|
|
4130
4304
|
const originalDigest = await baselineDigest(ws, before);
|
|
4131
|
-
const computed = await computeAnalysis(ws, before, inputs, event);
|
|
4132
|
-
await verifyInputs(ws, inputs);
|
|
4305
|
+
const computed = await computeAnalysis(ws, before, inputs, event, previous?.currentAudit, diagnostic);
|
|
4133
4306
|
if (originalDigest !== await baselineDigest(ws, before)) throw new Error("Repair baseline was modified during automation; original evidence must be inspected.");
|
|
4134
4307
|
const id = randomUUID5();
|
|
4135
4308
|
const analysis = analysisSchema.parse({
|
|
@@ -4137,7 +4310,8 @@ async function analyze(ws, inputs, event, startedAt) {
|
|
|
4137
4310
|
fingerprint: inputs.fingerprint,
|
|
4138
4311
|
completedAt: Date.now(),
|
|
4139
4312
|
baselineDigest: await baselineDigest(ws, { ...before, baselines: computed.baselines }),
|
|
4140
|
-
report: computed.report
|
|
4313
|
+
report: computed.report,
|
|
4314
|
+
currentAudit: computed.currentAudit
|
|
4141
4315
|
});
|
|
4142
4316
|
const analysisPath = ws.directory + "/checks/analysis-" + inputs.fingerprint + ".json";
|
|
4143
4317
|
await withLock(ws.root, ws.directory, async () => {
|
|
@@ -4156,17 +4330,17 @@ async function analyze(ws, inputs, event, startedAt) {
|
|
|
4156
4330
|
}
|
|
4157
4331
|
async function automate(dir, event) {
|
|
4158
4332
|
const startedAt = Date.now();
|
|
4159
|
-
const ws = await workspace(dir);
|
|
4160
|
-
return recordExecution(ws.root, ws.directory, event.event, async () => {
|
|
4333
|
+
const ws = await profilePhase("automation.workspace", () => workspace(dir));
|
|
4334
|
+
return recordExecution(ws.root, ws.directory, event.event, () => withRepositoryInspection(ws.root, async () => {
|
|
4161
4335
|
const inputs = await readInputs(ws.root);
|
|
4162
|
-
const result = await analyze(ws, inputs, event, startedAt);
|
|
4336
|
+
const result = await profilePhase("automation.analysis", () => analyze(ws, inputs, event, startedAt));
|
|
4163
4337
|
if (result.shared) {
|
|
4164
4338
|
await verifyInputs(ws, inputs);
|
|
4165
4339
|
if (result.analysis.baselineDigest !== await baselineDigest(ws, await loadState(ws))) {
|
|
4166
4340
|
throw new Error("Repair baseline was modified during shared automation; original evidence must be inspected.");
|
|
4167
4341
|
}
|
|
4168
4342
|
}
|
|
4169
|
-
return withLock(ws.root, ws.directory, async () => {
|
|
4343
|
+
return profilePhase("automation.publish", () => withLock(ws.root, ws.directory, async () => {
|
|
4170
4344
|
const state = await loadState(ws);
|
|
4171
4345
|
if (state.analysis?.id !== result.analysis.id) throw new Error("Automation evidence changed during publication; retry against the current inputs.");
|
|
4172
4346
|
const report = structuredClone(result.analysis.report);
|
|
@@ -4229,11 +4403,11 @@ async function automate(dir, event) {
|
|
|
4229
4403
|
if (persistReport) await writeStoreJson(ws.root, report.reportPath, report);
|
|
4230
4404
|
await writeStoreJson(ws.root, ws.directory + "/state.json", state);
|
|
4231
4405
|
return { report, message: notify ? summarize(report) : null, continueOnce };
|
|
4232
|
-
});
|
|
4233
|
-
});
|
|
4406
|
+
}));
|
|
4407
|
+
}));
|
|
4234
4408
|
}
|
|
4235
4409
|
async function automationStatus(dir) {
|
|
4236
|
-
const ws = await workspace(dir);
|
|
4410
|
+
const ws = await profilePhase("automation.workspace", () => workspace(dir));
|
|
4237
4411
|
const execution = await executionStatus(ws.root, ws.directory);
|
|
4238
4412
|
const notifications = await failureNotifications(ws.root, ws.directory);
|
|
4239
4413
|
const raw = await readStoreJson(ws.root, ws.directory + "/state.json");
|
|
@@ -4267,6 +4441,8 @@ var SCOPE, priority, cleanText, analysisSchema, sharedSchema, baselineDigest;
|
|
|
4267
4441
|
var init_runtime = __esm({
|
|
4268
4442
|
"src/automation/runtime.ts"() {
|
|
4269
4443
|
"use strict";
|
|
4444
|
+
init_profile();
|
|
4445
|
+
init_inspection();
|
|
4270
4446
|
init_execution();
|
|
4271
4447
|
init_repair();
|
|
4272
4448
|
init_types();
|
|
@@ -4281,6 +4457,7 @@ var init_runtime = __esm({
|
|
|
4281
4457
|
fingerprint: z13.string(),
|
|
4282
4458
|
completedAt: z13.number(),
|
|
4283
4459
|
baselineDigest: z13.string(),
|
|
4460
|
+
currentAudit: reportSchema.nullable().optional(),
|
|
4284
4461
|
report: z13.object({
|
|
4285
4462
|
version: z13.literal(1),
|
|
4286
4463
|
status: z13.enum(["verified", "issues-remain", "incomplete", "unavailable"]),
|
|
@@ -4294,7 +4471,8 @@ var init_runtime = __esm({
|
|
|
4294
4471
|
checks: z13.object({
|
|
4295
4472
|
ran: z13.array(z13.string()),
|
|
4296
4473
|
reused: z13.array(z13.string()),
|
|
4297
|
-
skipped: z13.array(z13.object({ check: z13.string(), reason: z13.string(), doc: z13.string().optional() }))
|
|
4474
|
+
skipped: z13.array(z13.object({ check: z13.string(), reason: z13.string(), doc: z13.string().optional() })),
|
|
4475
|
+
auditReused: z13.boolean().optional()
|
|
4298
4476
|
}),
|
|
4299
4477
|
counts: z13.object({ resolved: z13.number(), unresolved: z13.number(), "review-required": z13.number(), unverified: z13.number() }),
|
|
4300
4478
|
capture: z13.enum(["observed", "unknown"]),
|
|
@@ -4302,15 +4480,15 @@ var init_runtime = __esm({
|
|
|
4302
4480
|
})
|
|
4303
4481
|
});
|
|
4304
4482
|
sharedSchema = z13.object({ analysis: analysisSchema, digest: z13.string() });
|
|
4305
|
-
baselineDigest = async (ws, state) => hash(await Promise.all(
|
|
4483
|
+
baselineDigest = async (ws, state) => profilePhase("automation.baselines", async () => hash(await Promise.all(
|
|
4306
4484
|
state.baselines.map(async (b) => [b.path, await readStoreJson(ws.root, b.path)])
|
|
4307
|
-
));
|
|
4485
|
+
)));
|
|
4308
4486
|
}
|
|
4309
4487
|
});
|
|
4310
4488
|
|
|
4311
4489
|
// src/automation/hook-root.ts
|
|
4312
4490
|
import fs19 from "fs/promises";
|
|
4313
|
-
import
|
|
4491
|
+
import path26 from "path";
|
|
4314
4492
|
var init_hook_root = __esm({
|
|
4315
4493
|
"src/automation/hook-root.ts"() {
|
|
4316
4494
|
"use strict";
|
|
@@ -4779,13 +4957,13 @@ If a build is interrupted, its partials remain in \`.mason/partial-snapshots/\`.
|
|
|
4779
4957
|
});
|
|
4780
4958
|
|
|
4781
4959
|
// src/review/cochange.ts
|
|
4782
|
-
import { execFile as
|
|
4783
|
-
import { promisify as
|
|
4960
|
+
import { execFile as execFile6 } from "child_process";
|
|
4961
|
+
import { promisify as promisify6 } from "util";
|
|
4784
4962
|
async function buildMatrix(resolvedRoot) {
|
|
4785
4963
|
try {
|
|
4786
|
-
const { stdout: shallow } = await
|
|
4964
|
+
const { stdout: shallow } = await exec4("git", ["rev-parse", "--is-shallow-repository"], { cwd: resolvedRoot });
|
|
4787
4965
|
if (shallow.trim() === "true") return null;
|
|
4788
|
-
const { stdout } = await
|
|
4966
|
+
const { stdout } = await exec4(
|
|
4789
4967
|
"git",
|
|
4790
4968
|
["log", `-n${HISTORY_COMMITS}`, "--format=%x01", "--name-only", "-M"],
|
|
4791
4969
|
{ cwd: resolvedRoot, maxBuffer: 100 * 1024 * 1024 }
|
|
@@ -4841,11 +5019,11 @@ async function findMissingPartners(resolvedRoot, changedFiles, existsOnDisk, all
|
|
|
4841
5019
|
findings.sort((a, b) => b.rate - a.rate || b.sharedCommits - a.sharedCommits);
|
|
4842
5020
|
return findings;
|
|
4843
5021
|
}
|
|
4844
|
-
var
|
|
5022
|
+
var exec4, HISTORY_COMMITS, MIN_FILE_COMMITS, MIN_SHARED_COMMITS, MIN_COCHANGE_RATE, MAX_COMMIT_FILES;
|
|
4845
5023
|
var init_cochange = __esm({
|
|
4846
5024
|
"src/review/cochange.ts"() {
|
|
4847
5025
|
"use strict";
|
|
4848
|
-
|
|
5026
|
+
exec4 = promisify6(execFile6);
|
|
4849
5027
|
HISTORY_COMMITS = 1500;
|
|
4850
5028
|
MIN_FILE_COMMITS = 5;
|
|
4851
5029
|
MIN_SHARED_COMMITS = 4;
|
|
@@ -5153,10 +5331,10 @@ var init_check = __esm({
|
|
|
5153
5331
|
});
|
|
5154
5332
|
|
|
5155
5333
|
// src/review/evidence.ts
|
|
5156
|
-
import
|
|
5334
|
+
import path27 from "path";
|
|
5157
5335
|
import { z as z22 } from "zod";
|
|
5158
5336
|
function relativeArtifact(root, file) {
|
|
5159
|
-
const relative =
|
|
5337
|
+
const relative = path27.isAbsolute(file) ? path27.relative(root, file) : file;
|
|
5160
5338
|
const normalized = normalizeRepoPath(relative);
|
|
5161
5339
|
if (!normalized) throw new Error(`Evidence artifact must be inside the repository: ${file}`);
|
|
5162
5340
|
return normalized;
|
|
@@ -5253,7 +5431,7 @@ async function collectReviewEvidence(root, manifests, changedFiles, decisions, d
|
|
|
5253
5431
|
const report = await readStoreJson(root, file);
|
|
5254
5432
|
if (report === null) throw new Error(`Report artifact is missing: ${file}`);
|
|
5255
5433
|
const sourceRoot = check.sourceRoot ?? root;
|
|
5256
|
-
if (!
|
|
5434
|
+
if (!path27.posix.isAbsolute(sourceRoot) && !/^[A-Za-z]:[\\/]/.test(sourceRoot)) throw new Error("sourceRoot must identify the absolute checkout root on the check runner.");
|
|
5257
5435
|
const parsed = check.report.format === "vitest-json" ? parseVitest(report, sourceRoot) : check.report.format === "mason-check-json" ? parseCheck(report, sourceRoot) : parseSarif(report, sourceRoot);
|
|
5258
5436
|
result.outcome = parsed.outcome;
|
|
5259
5437
|
result.counts = parsed.counts;
|
|
@@ -5375,12 +5553,12 @@ var init_evidence2 = __esm({
|
|
|
5375
5553
|
|
|
5376
5554
|
// src/review/review.ts
|
|
5377
5555
|
import fs20 from "fs/promises";
|
|
5378
|
-
import
|
|
5379
|
-
import { execFile as
|
|
5380
|
-
import { promisify as
|
|
5556
|
+
import path28 from "path";
|
|
5557
|
+
import { execFile as execFile7 } from "child_process";
|
|
5558
|
+
import { promisify as promisify7 } from "util";
|
|
5381
5559
|
async function resolveMergeBase(resolvedRoot, base, head) {
|
|
5382
5560
|
try {
|
|
5383
|
-
const { stdout } = await
|
|
5561
|
+
const { stdout } = await exec5("git", ["merge-base", base, head], {
|
|
5384
5562
|
cwd: resolvedRoot
|
|
5385
5563
|
});
|
|
5386
5564
|
return stdout.trim() || null;
|
|
@@ -5391,7 +5569,7 @@ async function resolveMergeBase(resolvedRoot, base, head) {
|
|
|
5391
5569
|
async function defaultBase(resolvedRoot) {
|
|
5392
5570
|
for (const ref of ["origin/HEAD", "origin/main", "origin/master", "main"]) {
|
|
5393
5571
|
try {
|
|
5394
|
-
await
|
|
5572
|
+
await exec5("git", ["rev-parse", "--verify", "--quiet", ref], {
|
|
5395
5573
|
cwd: resolvedRoot
|
|
5396
5574
|
});
|
|
5397
5575
|
return ref;
|
|
@@ -5404,7 +5582,7 @@ function anchorsTouched(record, changedFiles) {
|
|
|
5404
5582
|
return matchingPaths(record.files, changedFiles);
|
|
5405
5583
|
}
|
|
5406
5584
|
async function computeReview(rootDir, base, options = {}) {
|
|
5407
|
-
const resolvedRoot =
|
|
5585
|
+
const resolvedRoot = path28.resolve(rootDir);
|
|
5408
5586
|
const head = await getCurrentGitHash(resolvedRoot);
|
|
5409
5587
|
if (head === "unknown") return null;
|
|
5410
5588
|
const mergeBase = await resolveMergeBase(resolvedRoot, base, head);
|
|
@@ -5454,7 +5632,7 @@ async function computeReview(rootDir, base, options = {}) {
|
|
|
5454
5632
|
analyzed,
|
|
5455
5633
|
async (relPath) => {
|
|
5456
5634
|
try {
|
|
5457
|
-
await fs20.access(
|
|
5635
|
+
await fs20.access(path28.join(resolvedRoot, relPath));
|
|
5458
5636
|
return true;
|
|
5459
5637
|
} catch {
|
|
5460
5638
|
return false;
|
|
@@ -5487,7 +5665,7 @@ async function computeReview(rootDir, base, options = {}) {
|
|
|
5487
5665
|
}
|
|
5488
5666
|
return finalize();
|
|
5489
5667
|
}
|
|
5490
|
-
var
|
|
5668
|
+
var exec5, MAX_ANALYZED_FILES;
|
|
5491
5669
|
var init_review = __esm({
|
|
5492
5670
|
"src/review/review.ts"() {
|
|
5493
5671
|
"use strict";
|
|
@@ -5499,7 +5677,7 @@ var init_review = __esm({
|
|
|
5499
5677
|
init_cochange();
|
|
5500
5678
|
init_evidence2();
|
|
5501
5679
|
init_snapshot();
|
|
5502
|
-
|
|
5680
|
+
exec5 = promisify7(execFile7);
|
|
5503
5681
|
MAX_ANALYZED_FILES = 50;
|
|
5504
5682
|
}
|
|
5505
5683
|
});
|
|
@@ -5589,7 +5767,7 @@ var init_onboarding = __esm({
|
|
|
5589
5767
|
});
|
|
5590
5768
|
|
|
5591
5769
|
// src/impact/references.ts
|
|
5592
|
-
import
|
|
5770
|
+
import path29 from "path";
|
|
5593
5771
|
function templateEnd(content2, start) {
|
|
5594
5772
|
let i = start + 1, interpolation = 0;
|
|
5595
5773
|
while (i < content2.length) {
|
|
@@ -5715,10 +5893,10 @@ function jvmImports(content2) {
|
|
|
5715
5893
|
}
|
|
5716
5894
|
function resolveImport(file, specifier, available) {
|
|
5717
5895
|
if (!specifier.startsWith("./") && !specifier.startsWith("../")) return null;
|
|
5718
|
-
const relative =
|
|
5896
|
+
const relative = path29.posix.normalize(path29.posix.join(path29.posix.dirname(file), specifier));
|
|
5719
5897
|
if (relative.startsWith("../")) return null;
|
|
5720
5898
|
if (available.has(relative)) return relative;
|
|
5721
|
-
const ext =
|
|
5899
|
+
const ext = path29.posix.extname(relative);
|
|
5722
5900
|
const replacements = { ".js": [".ts", ".tsx"], ".mjs": [".mts"], ".cjs": [".cts"] };
|
|
5723
5901
|
const candidates = ext ? (replacements[ext] ?? []).map((e) => relative.slice(0, -ext.length) + e) : jsExtensions.flatMap((e) => [relative + e, relative + "/index" + e]);
|
|
5724
5902
|
const found = candidates.filter((f) => available.has(f));
|
|
@@ -5730,13 +5908,13 @@ function mentionsPath(content2, target) {
|
|
|
5730
5908
|
function createReferenceMatcher(targets, available, targetContents = /* @__PURE__ */ new Map()) {
|
|
5731
5909
|
const counts = /* @__PURE__ */ new Map();
|
|
5732
5910
|
for (const file of available) {
|
|
5733
|
-
const name =
|
|
5911
|
+
const name = path29.posix.basename(file, path29.posix.extname(file));
|
|
5734
5912
|
counts.set(name, (counts.get(name) ?? 0) + 1);
|
|
5735
5913
|
}
|
|
5736
5914
|
const jvmTargets = new Map([...targetContents].filter(([file]) => /\.(?:kt|kts|java)$/.test(file)).map(([file, content2]) => [file, declaredSymbols(content2)]));
|
|
5737
5915
|
return (source) => {
|
|
5738
5916
|
if (targets.includes(source.path)) return [];
|
|
5739
|
-
const resolved = new Set(jsExtensions.includes(
|
|
5917
|
+
const resolved = new Set(jsExtensions.includes(path29.posix.extname(source.path)) ? imports(source.content).map((s) => resolveImport(source.path, s, available)).filter(Boolean) : []);
|
|
5740
5918
|
if (/\.(?:kt|kts|java)$/.test(source.path)) {
|
|
5741
5919
|
const imported = jvmImports(source.content);
|
|
5742
5920
|
for (const [target, symbols] of jvmTargets) if (symbols.some((symbol) => imported.some((name) => name === symbol || name.startsWith(symbol + ".")))) resolved.add(target);
|
|
@@ -5752,7 +5930,7 @@ function createReferenceMatcher(targets, available, targetContents = /* @__PURE_
|
|
|
5752
5930
|
add("explicit-path", target);
|
|
5753
5931
|
continue;
|
|
5754
5932
|
}
|
|
5755
|
-
const ext =
|
|
5933
|
+
const ext = path29.posix.extname(target), name = path29.posix.basename(target, ext);
|
|
5756
5934
|
if (!/\.(?:[cm]?[jt]sx?|py|kt|java|go|rs|swift|rb|cs|cpp|c|h|hpp|dart|php)$/.test(ext) || name.startsWith(".") || name.length < 3 || genericNames.has(name.toLowerCase()) || counts.get(name) !== 1) continue;
|
|
5757
5935
|
if (word(name).test(source.content)) {
|
|
5758
5936
|
add("name-candidate", name);
|
|
@@ -5781,11 +5959,11 @@ var impact_exports = {};
|
|
|
5781
5959
|
__export(impact_exports, {
|
|
5782
5960
|
analyzeImpact: () => analyzeImpact
|
|
5783
5961
|
});
|
|
5784
|
-
import
|
|
5785
|
-
import { execFile as
|
|
5786
|
-
import { promisify as
|
|
5962
|
+
import path30 from "path";
|
|
5963
|
+
import { execFile as execFile8 } from "child_process";
|
|
5964
|
+
import { promisify as promisify8 } from "util";
|
|
5787
5965
|
async function analyzeImpact(rootDir, targetFiles) {
|
|
5788
|
-
const resolvedRoot =
|
|
5966
|
+
const resolvedRoot = path30.resolve(rootDir);
|
|
5789
5967
|
const resolvedTargets = await resolveTargetFiles(resolvedRoot, targetFiles);
|
|
5790
5968
|
const [cochange, references, tests] = await Promise.all([
|
|
5791
5969
|
getCochangeFiles(resolvedRoot, resolvedTargets),
|
|
@@ -5828,7 +6006,7 @@ async function getCochangeFiles(rootDir, targetFiles) {
|
|
|
5828
6006
|
let totalTargetCommits = 0;
|
|
5829
6007
|
for (const targetFile of targetFiles) {
|
|
5830
6008
|
try {
|
|
5831
|
-
const { stdout: commitLog } = await
|
|
6009
|
+
const { stdout: commitLog } = await exec6(
|
|
5832
6010
|
"git",
|
|
5833
6011
|
["log", "--format=%H", "-n", "500", "--", targetFile],
|
|
5834
6012
|
{ cwd: rootDir, maxBuffer: 5e6 }
|
|
@@ -5838,7 +6016,7 @@ async function getCochangeFiles(rootDir, targetFiles) {
|
|
|
5838
6016
|
if (commits.length === 0) continue;
|
|
5839
6017
|
for (const commit of commits) {
|
|
5840
6018
|
try {
|
|
5841
|
-
const { stdout: filesInCommit } = await
|
|
6019
|
+
const { stdout: filesInCommit } = await exec6(
|
|
5842
6020
|
"git",
|
|
5843
6021
|
["diff-tree", "--no-commit-id", "--name-only", "-r", commit],
|
|
5844
6022
|
{ cwd: rootDir }
|
|
@@ -5899,9 +6077,9 @@ async function getRelatedTests(rootDir, targetFiles) {
|
|
|
5899
6077
|
const testFiles = await (await createFileAccess(rootDir)).list(testPatterns);
|
|
5900
6078
|
const results = [];
|
|
5901
6079
|
for (const target of targetFiles) {
|
|
5902
|
-
const targetBaseName =
|
|
6080
|
+
const targetBaseName = path30.basename(target).replace(/\.[^.]+$/, "");
|
|
5903
6081
|
for (const testFile of testFiles) {
|
|
5904
|
-
const testBaseName =
|
|
6082
|
+
const testBaseName = path30.basename(testFile).replace(/\.[^.]+$/, "");
|
|
5905
6083
|
const sourceName = testBaseName.replace(/Test$|Tests$|Spec$|\.test$|\.spec$/, "").replace(/^test_|_test$/, "");
|
|
5906
6084
|
if (sourceName === targetBaseName) {
|
|
5907
6085
|
results.push({
|
|
@@ -5913,14 +6091,14 @@ async function getRelatedTests(rootDir, targetFiles) {
|
|
|
5913
6091
|
}
|
|
5914
6092
|
return results;
|
|
5915
6093
|
}
|
|
5916
|
-
var
|
|
6094
|
+
var exec6;
|
|
5917
6095
|
var init_impact = __esm({
|
|
5918
6096
|
"src/impact/impact.ts"() {
|
|
5919
6097
|
"use strict";
|
|
5920
6098
|
init_files();
|
|
5921
6099
|
init_paths();
|
|
5922
6100
|
init_references();
|
|
5923
|
-
|
|
6101
|
+
exec6 = promisify8(execFile8);
|
|
5924
6102
|
}
|
|
5925
6103
|
});
|
|
5926
6104
|
|
|
@@ -5929,13 +6107,13 @@ var assemble_exports = {};
|
|
|
5929
6107
|
__export(assemble_exports, {
|
|
5930
6108
|
assembleContext: () => assembleContext
|
|
5931
6109
|
});
|
|
5932
|
-
import
|
|
6110
|
+
import path31 from "path";
|
|
5933
6111
|
import fs21 from "fs/promises";
|
|
5934
6112
|
async function collectImpact(root, candidates) {
|
|
5935
6113
|
const targets = /* @__PURE__ */ new Set();
|
|
5936
6114
|
let sourceFiles;
|
|
5937
6115
|
for (const candidate of sanitizeRepoPaths(candidates)) {
|
|
5938
|
-
const stat = await fs21.stat(
|
|
6116
|
+
const stat = await fs21.stat(path31.join(root, candidate)).catch(() => null);
|
|
5939
6117
|
if (stat?.isDirectory()) {
|
|
5940
6118
|
sourceFiles ??= await (await createFileAccess(root)).list();
|
|
5941
6119
|
for (const file of sourceFiles) {
|
|
@@ -5961,7 +6139,7 @@ async function collectImpact(root, candidates) {
|
|
|
5961
6139
|
};
|
|
5962
6140
|
}
|
|
5963
6141
|
async function assembleContext(rootDir, task, files) {
|
|
5964
|
-
const resolvedRoot =
|
|
6142
|
+
const resolvedRoot = path31.resolve(rootDir);
|
|
5965
6143
|
const mapState = await inspectSnapshot(resolvedRoot);
|
|
5966
6144
|
const snapshot = mapState.snapshot;
|
|
5967
6145
|
const store = await loadDecisionStore(resolvedRoot);
|
|
@@ -6504,8 +6682,8 @@ __export(client_exports, {
|
|
|
6504
6682
|
function createConfluenceClient(config, fetchFn = fetch) {
|
|
6505
6683
|
const baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
6506
6684
|
const auth = "Basic " + Buffer.from(`${config.email}:${config.apiToken}`).toString("base64");
|
|
6507
|
-
async function call(method,
|
|
6508
|
-
const res = await fetchFn(`${baseUrl}${
|
|
6685
|
+
async function call(method, path35, body) {
|
|
6686
|
+
const res = await fetchFn(`${baseUrl}${path35}`, {
|
|
6509
6687
|
method,
|
|
6510
6688
|
headers: {
|
|
6511
6689
|
Authorization: auth,
|
|
@@ -6517,7 +6695,7 @@ function createConfluenceClient(config, fetchFn = fetch) {
|
|
|
6517
6695
|
if (!res.ok) {
|
|
6518
6696
|
const text2 = await res.text();
|
|
6519
6697
|
throw new Error(
|
|
6520
|
-
`Confluence ${method} ${
|
|
6698
|
+
`Confluence ${method} ${path35} failed: ${res.status} ${res.statusText} \u2014 ${text2}`
|
|
6521
6699
|
);
|
|
6522
6700
|
}
|
|
6523
6701
|
if (res.status === 204) return null;
|
|
@@ -6619,15 +6797,15 @@ __export(config_exports2, {
|
|
|
6619
6797
|
validateProvider: () => validateProvider
|
|
6620
6798
|
});
|
|
6621
6799
|
import fs23 from "fs/promises";
|
|
6622
|
-
import
|
|
6800
|
+
import path32 from "path";
|
|
6623
6801
|
import os3 from "os";
|
|
6624
|
-
import { execFile as
|
|
6625
|
-
import { promisify as
|
|
6802
|
+
import { execFile as execFile9 } from "child_process";
|
|
6803
|
+
import { promisify as promisify9 } from "util";
|
|
6626
6804
|
function configDir() {
|
|
6627
|
-
return
|
|
6805
|
+
return path32.join(os3.homedir(), ".mason");
|
|
6628
6806
|
}
|
|
6629
6807
|
function configFile() {
|
|
6630
|
-
return
|
|
6808
|
+
return path32.join(configDir(), "config.json");
|
|
6631
6809
|
}
|
|
6632
6810
|
async function loadConfig() {
|
|
6633
6811
|
try {
|
|
@@ -6657,7 +6835,7 @@ async function detectCLI(provider) {
|
|
|
6657
6835
|
const cliName = provider === "claude" ? "claude" : provider === "gemini" ? "gemini" : provider === "ollama" ? "ollama" : null;
|
|
6658
6836
|
if (!cliName) return { available: false };
|
|
6659
6837
|
try {
|
|
6660
|
-
const { stdout } = await
|
|
6838
|
+
const { stdout } = await exec7(cliName, ["--version"]);
|
|
6661
6839
|
return { available: true, version: stdout.trim() };
|
|
6662
6840
|
} catch {
|
|
6663
6841
|
return { available: false };
|
|
@@ -6674,11 +6852,11 @@ async function loadConfluenceConfig() {
|
|
|
6674
6852
|
const config = await loadConfig();
|
|
6675
6853
|
return config?.confluence ?? null;
|
|
6676
6854
|
}
|
|
6677
|
-
var
|
|
6855
|
+
var exec7, DEFAULT_MODELS;
|
|
6678
6856
|
var init_config2 = __esm({
|
|
6679
6857
|
"src/llm/config.ts"() {
|
|
6680
6858
|
"use strict";
|
|
6681
|
-
|
|
6859
|
+
exec7 = promisify9(execFile9);
|
|
6682
6860
|
DEFAULT_MODELS = {
|
|
6683
6861
|
claude: "claude-sonnet-4-20250514",
|
|
6684
6862
|
gemini: "gemini-2.5-flash",
|
|
@@ -6800,16 +6978,16 @@ var init_renderer = __esm({
|
|
|
6800
6978
|
|
|
6801
6979
|
// src/confluence/diff.ts
|
|
6802
6980
|
import fs24 from "fs/promises";
|
|
6803
|
-
import
|
|
6981
|
+
import path33 from "path";
|
|
6804
6982
|
import { createHash as createHash5 } from "crypto";
|
|
6805
6983
|
function hashDescription(description) {
|
|
6806
6984
|
return createHash5("sha256").update(description, "utf8").digest("hex");
|
|
6807
6985
|
}
|
|
6808
6986
|
function syncStateDir(rootDir) {
|
|
6809
|
-
return
|
|
6987
|
+
return path33.join(rootDir, ".mason");
|
|
6810
6988
|
}
|
|
6811
6989
|
function syncStatePath(rootDir) {
|
|
6812
|
-
return
|
|
6990
|
+
return path33.join(syncStateDir(rootDir), "confluence-sync.json");
|
|
6813
6991
|
}
|
|
6814
6992
|
async function loadSyncState(rootDir) {
|
|
6815
6993
|
try {
|
|
@@ -6877,8 +7055,8 @@ var init_diff = __esm({
|
|
|
6877
7055
|
});
|
|
6878
7056
|
|
|
6879
7057
|
// src/llm/providers.ts
|
|
6880
|
-
import { execFile as
|
|
6881
|
-
import { promisify as
|
|
7058
|
+
import { execFile as execFile10, spawn } from "child_process";
|
|
7059
|
+
import { promisify as promisify10 } from "util";
|
|
6882
7060
|
async function callLLM(config, userMessage, systemPrompt) {
|
|
6883
7061
|
const model = config.model ?? getDefaultModel(config.provider);
|
|
6884
7062
|
const system = systemPrompt ?? CLAUDE_MD_SYSTEM_PROMPT;
|
|
@@ -7035,12 +7213,12 @@ async function callOpenAIAPI(apiKey, model, system, userMessage) {
|
|
|
7035
7213
|
});
|
|
7036
7214
|
return response.choices[0]?.message?.content ?? "";
|
|
7037
7215
|
}
|
|
7038
|
-
var
|
|
7216
|
+
var exec8, CLAUDE_MD_SYSTEM_PROMPT;
|
|
7039
7217
|
var init_providers = __esm({
|
|
7040
7218
|
"src/llm/providers.ts"() {
|
|
7041
7219
|
"use strict";
|
|
7042
7220
|
init_config2();
|
|
7043
|
-
|
|
7221
|
+
exec8 = promisify10(execFile10);
|
|
7044
7222
|
CLAUDE_MD_SYSTEM_PROMPT = `You are Mason, a context engineering tool. You've been given a comprehensive analysis of a codebase including:
|
|
7045
7223
|
- Git history stats (commit patterns, frequently changed files, stale directories)
|
|
7046
7224
|
- Project structure (directory layout, file counts by type)
|
|
@@ -7382,20 +7560,20 @@ import { z as z24 } from "zod";
|
|
|
7382
7560
|
init_repair();
|
|
7383
7561
|
init_advisory_review();
|
|
7384
7562
|
import fs25 from "fs/promises";
|
|
7385
|
-
import
|
|
7386
|
-
import { execFile as
|
|
7387
|
-
import { promisify as
|
|
7563
|
+
import path34 from "path";
|
|
7564
|
+
import { execFile as execFile11 } from "child_process";
|
|
7565
|
+
import { promisify as promisify11 } from "util";
|
|
7388
7566
|
|
|
7389
7567
|
// src/audit/cli.ts
|
|
7390
7568
|
init_audit();
|
|
7391
7569
|
init_types();
|
|
7392
7570
|
init_repair();
|
|
7393
7571
|
init_findings();
|
|
7394
|
-
import
|
|
7572
|
+
import path23 from "path";
|
|
7395
7573
|
|
|
7396
7574
|
// src/audit/advisory-cli.ts
|
|
7397
7575
|
init_advisory_review();
|
|
7398
|
-
import
|
|
7576
|
+
import path22 from "path";
|
|
7399
7577
|
import { stripVTControlCharacters } from "util";
|
|
7400
7578
|
|
|
7401
7579
|
// src/audit/cli.ts
|
|
@@ -7488,8 +7666,8 @@ function formatFixPrompt(report, baselinePath) {
|
|
|
7488
7666
|
}
|
|
7489
7667
|
|
|
7490
7668
|
// src/analyzers/git-history.ts
|
|
7491
|
-
import { execFile as
|
|
7492
|
-
import { promisify as
|
|
7669
|
+
import { execFile as execFile3 } from "child_process";
|
|
7670
|
+
import { promisify as promisify3 } from "util";
|
|
7493
7671
|
|
|
7494
7672
|
// src/analyzers/base.ts
|
|
7495
7673
|
import fs15 from "fs/promises";
|
|
@@ -7526,7 +7704,7 @@ var BaseAnalyzer = class {
|
|
|
7526
7704
|
};
|
|
7527
7705
|
|
|
7528
7706
|
// src/analyzers/git-history.ts
|
|
7529
|
-
var
|
|
7707
|
+
var exec = promisify3(execFile3);
|
|
7530
7708
|
var GitHistoryAnalyzer = class extends BaseAnalyzer {
|
|
7531
7709
|
name = "git-history";
|
|
7532
7710
|
async analyze(context) {
|
|
@@ -7547,7 +7725,7 @@ var GitHistoryAnalyzer = class extends BaseAnalyzer {
|
|
|
7547
7725
|
}
|
|
7548
7726
|
async git(args, cwd) {
|
|
7549
7727
|
try {
|
|
7550
|
-
const { stdout } = await
|
|
7728
|
+
const { stdout } = await exec("git", args, { cwd, maxBuffer: 1e7 });
|
|
7551
7729
|
return stdout.trim();
|
|
7552
7730
|
} catch {
|
|
7553
7731
|
return "";
|
|
@@ -7696,12 +7874,10 @@ async function runAll(context) {
|
|
|
7696
7874
|
}
|
|
7697
7875
|
|
|
7698
7876
|
// src/utils/git.ts
|
|
7699
|
-
|
|
7700
|
-
import { promisify as promisify12 } from "util";
|
|
7701
|
-
var exec9 = promisify12(execFile12);
|
|
7877
|
+
init_git_read();
|
|
7702
7878
|
async function isGitRepo(dir) {
|
|
7703
7879
|
try {
|
|
7704
|
-
await
|
|
7880
|
+
await execGit(["rev-parse", "--git-dir"], { cwd: dir });
|
|
7705
7881
|
return true;
|
|
7706
7882
|
} catch {
|
|
7707
7883
|
return false;
|
|
@@ -7710,10 +7886,10 @@ async function isGitRepo(dir) {
|
|
|
7710
7886
|
|
|
7711
7887
|
// src/mcp/sampler.ts
|
|
7712
7888
|
init_files();
|
|
7713
|
-
import
|
|
7714
|
-
import { execFile as
|
|
7715
|
-
import { promisify as
|
|
7716
|
-
var
|
|
7889
|
+
import path24 from "path";
|
|
7890
|
+
import { execFile as execFile4 } from "child_process";
|
|
7891
|
+
import { promisify as promisify4 } from "util";
|
|
7892
|
+
var exec2 = promisify4(execFile4);
|
|
7717
7893
|
var CONFIG_FILES = [
|
|
7718
7894
|
// Build & project config
|
|
7719
7895
|
"package.json",
|
|
@@ -7818,8 +7994,8 @@ async function sampleFiles(rootDir, maxFiles = 25) {
|
|
|
7818
7994
|
const projectConfig = access.config;
|
|
7819
7995
|
for (const filePath of projectConfig.alwaysInclude ?? []) {
|
|
7820
7996
|
if (selected.size >= maxFiles) break;
|
|
7821
|
-
const resolvedPath =
|
|
7822
|
-
if (!resolvedPath.startsWith(
|
|
7997
|
+
const resolvedPath = path24.resolve(rootDir, filePath);
|
|
7998
|
+
if (!resolvedPath.startsWith(path24.resolve(rootDir))) continue;
|
|
7823
7999
|
selected.set(filePath, "always-include (project config)");
|
|
7824
8000
|
}
|
|
7825
8001
|
let configCount = 0;
|
|
@@ -7875,7 +8051,7 @@ async function sampleFiles(rootDir, maxFiles = 25) {
|
|
|
7875
8051
|
}
|
|
7876
8052
|
}
|
|
7877
8053
|
try {
|
|
7878
|
-
const { stdout } = await
|
|
8054
|
+
const { stdout } = await exec2(
|
|
7879
8055
|
"git",
|
|
7880
8056
|
["log", "--since=3 months ago", "--format=", "--name-only"],
|
|
7881
8057
|
{ cwd: rootDir, maxBuffer: 5e6 }
|
|
@@ -7885,7 +8061,7 @@ async function sampleFiles(rootDir, maxFiles = 25) {
|
|
|
7885
8061
|
if (!line) continue;
|
|
7886
8062
|
if (line.includes("node_modules") || line.includes("/build/") || line.includes(".gradle") || line.includes("/generated/"))
|
|
7887
8063
|
continue;
|
|
7888
|
-
const ext =
|
|
8064
|
+
const ext = path24.extname(line).slice(1);
|
|
7889
8065
|
if (!SOURCE_EXTENSIONS.includes(ext)) continue;
|
|
7890
8066
|
fileCounts.set(line, (fileCounts.get(line) ?? 0) + 1);
|
|
7891
8067
|
}
|
|
@@ -8005,10 +8181,10 @@ init_drift();
|
|
|
8005
8181
|
init_paths();
|
|
8006
8182
|
init_files();
|
|
8007
8183
|
import { createHash as createHash4 } from "crypto";
|
|
8008
|
-
import { execFile as
|
|
8009
|
-
import { promisify as
|
|
8184
|
+
import { execFile as execFile5 } from "child_process";
|
|
8185
|
+
import { promisify as promisify5 } from "util";
|
|
8010
8186
|
import { z as z10 } from "zod";
|
|
8011
|
-
var
|
|
8187
|
+
var exec3 = promisify5(execFile5);
|
|
8012
8188
|
var requestSchema = z10.object({
|
|
8013
8189
|
id: z10.string().regex(/^[a-zA-Z0-9_-]+$/),
|
|
8014
8190
|
action: z10.enum(["prepare", "accept", "reaffirm", "retire"]).default("prepare"),
|
|
@@ -8063,7 +8239,7 @@ async function previews(root, record, state) {
|
|
|
8063
8239
|
let diffUnavailable;
|
|
8064
8240
|
if (state.evidence.historyAvailable && diffPaths.length) {
|
|
8065
8241
|
try {
|
|
8066
|
-
const { stdout } = await
|
|
8242
|
+
const { stdout } = await exec3("git", ["--literal-pathspecs", "diff", "--no-ext-diff", "--no-textconv", "--no-color", "--no-renames", "-U3", record.refreshedHash, state.evidence.headHash, "--", ...diffPaths], { cwd: root, maxBuffer: 1024 * 1024 });
|
|
8067
8243
|
diff = stdout.slice(0, 16e3);
|
|
8068
8244
|
if (stdout.length > diff.length) diffUnavailable = "Diff preview was truncated; inspect the full diff before reviewing.";
|
|
8069
8245
|
} catch {
|
|
@@ -8289,7 +8465,7 @@ function batchIdFor(offset) {
|
|
|
8289
8465
|
// src/mcp/tools.ts
|
|
8290
8466
|
init_init();
|
|
8291
8467
|
init_onboarding();
|
|
8292
|
-
var
|
|
8468
|
+
var exec9 = promisify11(execFile11);
|
|
8293
8469
|
async function buildContext(dir) {
|
|
8294
8470
|
return {
|
|
8295
8471
|
rootDir: dir,
|
|
@@ -8297,7 +8473,7 @@ async function buildContext(dir) {
|
|
|
8297
8473
|
};
|
|
8298
8474
|
}
|
|
8299
8475
|
async function analyzeProject(dir) {
|
|
8300
|
-
const rootDir =
|
|
8476
|
+
const rootDir = path34.resolve(dir);
|
|
8301
8477
|
const context = await buildContext(rootDir);
|
|
8302
8478
|
const results = await runAll(context);
|
|
8303
8479
|
const projectSnapshot = await detectProjectSnapshot(rootDir);
|
|
@@ -8352,7 +8528,7 @@ async function detectProjectSnapshot(rootDir) {
|
|
|
8352
8528
|
const present = [];
|
|
8353
8529
|
for (const file of buildFiles) {
|
|
8354
8530
|
try {
|
|
8355
|
-
await fs25.access(
|
|
8531
|
+
await fs25.access(path34.join(rootDir, file));
|
|
8356
8532
|
present.push(file);
|
|
8357
8533
|
} catch {
|
|
8358
8534
|
}
|
|
@@ -8394,7 +8570,7 @@ async function detectProjectSnapshot(rootDir) {
|
|
|
8394
8570
|
const sourceFiles = await access.list();
|
|
8395
8571
|
const fileCounts = {};
|
|
8396
8572
|
for (const file of sourceFiles) {
|
|
8397
|
-
const ext =
|
|
8573
|
+
const ext = path34.extname(file).slice(1);
|
|
8398
8574
|
fileCounts[ext] = (fileCounts[ext] ?? 0) + 1;
|
|
8399
8575
|
}
|
|
8400
8576
|
return {
|
|
@@ -8405,7 +8581,7 @@ async function detectProjectSnapshot(rootDir) {
|
|
|
8405
8581
|
};
|
|
8406
8582
|
}
|
|
8407
8583
|
async function getCodeSamples(dir, count4 = 15) {
|
|
8408
|
-
const rootDir =
|
|
8584
|
+
const rootDir = path34.resolve(dir);
|
|
8409
8585
|
const samples = await sampleFiles(rootDir, count4);
|
|
8410
8586
|
const output = {
|
|
8411
8587
|
note: "These are previews (first ~60 lines). Read the file directly with your own tools to see it in full.",
|
|
@@ -8448,7 +8624,7 @@ async function unmappedContextResponse(rootDir) {
|
|
|
8448
8624
|
});
|
|
8449
8625
|
}
|
|
8450
8626
|
async function getProjectStructure(dir) {
|
|
8451
|
-
const rootDir =
|
|
8627
|
+
const rootDir = path34.resolve(dir);
|
|
8452
8628
|
const allFiles = await (await createFileAccess(rootDir)).list("**/*");
|
|
8453
8629
|
const dirInfo = /* @__PURE__ */ new Map();
|
|
8454
8630
|
for (const file of allFiles) {
|
|
@@ -8460,7 +8636,7 @@ async function getProjectStructure(dir) {
|
|
|
8460
8636
|
}
|
|
8461
8637
|
const info = dirInfo.get(dirPath);
|
|
8462
8638
|
info.fileCount++;
|
|
8463
|
-
const ext =
|
|
8639
|
+
const ext = path34.extname(file).slice(1);
|
|
8464
8640
|
if (ext) {
|
|
8465
8641
|
info.extensions.set(ext, (info.extensions.get(ext) ?? 0) + 1);
|
|
8466
8642
|
}
|
|
@@ -8505,7 +8681,7 @@ async function buildChangedFilePreviews(rootDir, changedFiles) {
|
|
|
8505
8681
|
return previews2;
|
|
8506
8682
|
}
|
|
8507
8683
|
async function getSnapshot(dir) {
|
|
8508
|
-
const rootDir =
|
|
8684
|
+
const rootDir = path34.resolve(dir);
|
|
8509
8685
|
const snapshot = await loadSnapshot(rootDir);
|
|
8510
8686
|
if (!snapshot) {
|
|
8511
8687
|
return unmappedContextResponse(rootDir);
|
|
@@ -8606,7 +8782,7 @@ function driftHint(report) {
|
|
|
8606
8782
|
return "Read the changed files under staleFeatures/staleFlows, update those entries (fold unmappedFiles into the right features), and call save_snapshot with only the affected entries \u2014 unchanged entries are preserved. Drop ghostFiles from any entries that reference them, and delete features/flows that no longer exist via save_snapshot's removeFeatures/removeFlows.";
|
|
8607
8783
|
}
|
|
8608
8784
|
async function checkDrift(dir) {
|
|
8609
|
-
const rootDir =
|
|
8785
|
+
const rootDir = path34.resolve(dir);
|
|
8610
8786
|
const report = await computeDrift(rootDir);
|
|
8611
8787
|
if (!report) {
|
|
8612
8788
|
return JSON.stringify({
|
|
@@ -8637,7 +8813,7 @@ async function checkDrift(dir) {
|
|
|
8637
8813
|
return JSON.stringify({ exists: true, ...report, verification, hint });
|
|
8638
8814
|
}
|
|
8639
8815
|
async function generateSnapshotBatch(dir, offset = 0, batchSize = DEFAULT_BATCH_SIZE, files) {
|
|
8640
|
-
const rootDir =
|
|
8816
|
+
const rootDir = path34.resolve(dir);
|
|
8641
8817
|
const scoped = files !== void 0 && files.length > 0;
|
|
8642
8818
|
const scopeFiles = scoped ? sanitizePaths(rootDir, files) : void 0;
|
|
8643
8819
|
const batch = await prepareSnapshotBatch(dir, offset, batchSize, scopeFiles);
|
|
@@ -8684,7 +8860,7 @@ async function generateSnapshotBatch(dir, offset = 0, batchSize = DEFAULT_BATCH_
|
|
|
8684
8860
|
);
|
|
8685
8861
|
}
|
|
8686
8862
|
async function saveSnapshotPartial(dir, batchId, offset, features, flows) {
|
|
8687
|
-
const rootDir =
|
|
8863
|
+
const rootDir = path34.resolve(dir);
|
|
8688
8864
|
for (const feat of Object.values(features)) {
|
|
8689
8865
|
feat.files = sanitizePaths(rootDir, feat.files);
|
|
8690
8866
|
if (feat.tests) feat.tests = sanitizePaths(rootDir, feat.tests);
|
|
@@ -8713,7 +8889,7 @@ async function saveSnapshotPartial(dir, batchId, offset, features, flows) {
|
|
|
8713
8889
|
);
|
|
8714
8890
|
}
|
|
8715
8891
|
async function reduceSnapshot(dir) {
|
|
8716
|
-
const rootDir =
|
|
8892
|
+
const rootDir = path34.resolve(dir);
|
|
8717
8893
|
const partials = await loadAllPartials(rootDir);
|
|
8718
8894
|
if (partials.length === 0) {
|
|
8719
8895
|
return JSON.stringify(
|
|
@@ -8775,7 +8951,7 @@ async function reduceSnapshot(dir) {
|
|
|
8775
8951
|
);
|
|
8776
8952
|
}
|
|
8777
8953
|
async function fullAnalysis(dir) {
|
|
8778
|
-
const rootDir =
|
|
8954
|
+
const rootDir = path34.resolve(dir);
|
|
8779
8955
|
const [analysis, structure, samples, testMap, snapshot] = await Promise.all([
|
|
8780
8956
|
analyzeProject(dir),
|
|
8781
8957
|
getProjectStructure(dir),
|
|
@@ -8804,7 +8980,7 @@ function sanitizePaths(rootDir, files) {
|
|
|
8804
8980
|
return sanitizeRepoPaths(files);
|
|
8805
8981
|
}
|
|
8806
8982
|
async function saveSnapshotData(dir, features, flows, removeFeatures = [], removeFlows = []) {
|
|
8807
|
-
const rootDir =
|
|
8983
|
+
const rootDir = path34.resolve(dir);
|
|
8808
8984
|
const gitHash = await getCurrentGitHash(rootDir);
|
|
8809
8985
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8810
8986
|
for (const feat of Object.values(features)) {
|
|
@@ -8888,7 +9064,7 @@ async function saveSnapshotData(dir, features, flows, removeFeatures = [], remov
|
|
|
8888
9064
|
});
|
|
8889
9065
|
}
|
|
8890
9066
|
async function getImpact(dir, files) {
|
|
8891
|
-
const rootDir =
|
|
9067
|
+
const rootDir = path34.resolve(dir);
|
|
8892
9068
|
const { analyzeImpact: analyzeImpact2 } = await Promise.resolve().then(() => (init_impact(), impact_exports));
|
|
8893
9069
|
const result = await analyzeImpact2(rootDir, files);
|
|
8894
9070
|
return JSON.stringify(result, null, 2);
|
|
@@ -8897,7 +9073,7 @@ var VERIFY_DEFAULT_SAMPLE = 5;
|
|
|
8897
9073
|
var VERIFY_MAX_FILES_PER_ENTRY = 8;
|
|
8898
9074
|
var VERIFY_SKELETON_CHARS = 500;
|
|
8899
9075
|
async function verifySnapshot(dir, sample = VERIFY_DEFAULT_SAMPLE) {
|
|
8900
|
-
const rootDir =
|
|
9076
|
+
const rootDir = path34.resolve(dir);
|
|
8901
9077
|
const snapshot = await loadSnapshot(rootDir);
|
|
8902
9078
|
if (!snapshot) {
|
|
8903
9079
|
return JSON.stringify({
|
|
@@ -8962,7 +9138,7 @@ async function verifySnapshot(dir, sample = VERIFY_DEFAULT_SAMPLE) {
|
|
|
8962
9138
|
});
|
|
8963
9139
|
}
|
|
8964
9140
|
async function saveVerification(dir, verdicts) {
|
|
8965
|
-
const rootDir =
|
|
9141
|
+
const rootDir = path34.resolve(dir);
|
|
8966
9142
|
const snapshot = await loadSnapshot(rootDir);
|
|
8967
9143
|
if (!snapshot) {
|
|
8968
9144
|
return JSON.stringify({ exists: false, hint: "No concept map exists." });
|
|
@@ -9000,16 +9176,16 @@ async function saveVerification(dir, verdicts) {
|
|
|
9000
9176
|
});
|
|
9001
9177
|
}
|
|
9002
9178
|
async function saveDecision(dir, input) {
|
|
9003
|
-
const rootDir =
|
|
9179
|
+
const rootDir = path34.resolve(dir);
|
|
9004
9180
|
const { upsertDecision: upsertDecision2 } = await Promise.resolve().then(() => (init_decisions(), decisions_exports));
|
|
9005
9181
|
const result = await upsertDecision2(rootDir, input);
|
|
9006
9182
|
return JSON.stringify(result);
|
|
9007
9183
|
}
|
|
9008
9184
|
async function reviewDecision2(dir, input) {
|
|
9009
|
-
return JSON.stringify(await reviewDecision(
|
|
9185
|
+
return JSON.stringify(await reviewDecision(path34.resolve(dir), input));
|
|
9010
9186
|
}
|
|
9011
9187
|
async function getContext(dir, task, files) {
|
|
9012
|
-
const rootDir =
|
|
9188
|
+
const rootDir = path34.resolve(dir);
|
|
9013
9189
|
const { assembleContext: assembleContext2 } = await Promise.resolve().then(() => (init_assemble(), assemble_exports));
|
|
9014
9190
|
const bundle = await assembleContext2(rootDir, task, files);
|
|
9015
9191
|
return JSON.stringify(bundle);
|
|
@@ -9058,7 +9234,7 @@ async function masonInit(dir, options = {}) {
|
|
|
9058
9234
|
return JSON.stringify(await setupProject2(dir, options), null, 2);
|
|
9059
9235
|
}
|
|
9060
9236
|
if (options.host) throw new Error("host applies only to mode: setup.");
|
|
9061
|
-
const rootDir =
|
|
9237
|
+
const rootDir = path34.resolve(dir);
|
|
9062
9238
|
const marker = await loadProjectMarker(rootDir);
|
|
9063
9239
|
const { loadSetup: loadSetup2 } = await Promise.resolve().then(() => (init_model(), model_exports));
|
|
9064
9240
|
const setup = await loadSetup2(rootDir);
|
|
@@ -9078,7 +9254,7 @@ async function masonInit(dir, options = {}) {
|
|
|
9078
9254
|
);
|
|
9079
9255
|
}
|
|
9080
9256
|
async function masonCompleteInit(dir, options = {}) {
|
|
9081
|
-
const rootDir =
|
|
9257
|
+
const rootDir = path34.resolve(dir);
|
|
9082
9258
|
const existing = await loadProjectMarker(rootDir);
|
|
9083
9259
|
const features = await loadProjectFeatures(rootDir);
|
|
9084
9260
|
const marker = {
|
|
@@ -9187,7 +9363,7 @@ async function masonSetConfluence(input) {
|
|
|
9187
9363
|
);
|
|
9188
9364
|
}
|
|
9189
9365
|
async function exportToConfluenceTool(dir, overrides) {
|
|
9190
|
-
const rootDir =
|
|
9366
|
+
const rootDir = path34.resolve(dir);
|
|
9191
9367
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config2(), config_exports2));
|
|
9192
9368
|
const { exportToConfluence: exportToConfluence2 } = await Promise.resolve().then(() => (init_sync(), sync_exports));
|
|
9193
9369
|
const config = await loadConfig2();
|
|
@@ -9226,7 +9402,7 @@ function createMcpServer() {
|
|
|
9226
9402
|
const server = new McpServer(
|
|
9227
9403
|
{
|
|
9228
9404
|
name: "mason",
|
|
9229
|
-
version: "0.17.
|
|
9405
|
+
version: "0.17.4"
|
|
9230
9406
|
},
|
|
9231
9407
|
{
|
|
9232
9408
|
instructions: (setupNotice ? setupNotice + "\n\n" : "") + 'Mason retrieves recorded decisions, file impact, and optional feature/flow maps. Use get_context with the task and known files, and get_impact before editing. Save learned rationale and constraints with save_decision; review and commit the local records through the project workflow. These tools work without initialization or a concept map. Consult trust and diagnostics: changed or unknown freshness needs source inspection, failed verification needs correction, and proposals are suggestions and legacy records are unreviewed. Accepted decisions are recorded team constraints, still subject to freshness checks. Use review_decision to prepare code evidence and record only authorized acceptance, reaffirmation, or retirement. Never invent a reviewer or treat these recorded identities as authenticated approval. mason_init returns documentation audit and committed-diff review findings with a quickstart guide. Use mode: "map" only when a full architecture map is requested. A missing map is not a setup failure; use decisions and source evidence. get_snapshot provides architecture navigation when a map is available. The mason-audit and mason-review CLIs also work without setup.'
|