mason-context 0.17.3 → 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 +11 -0
- package/dist/mason-audit.js +193 -156
- package/dist/mason-audit.js.map +1 -1
- package/dist/mason-auto.js +285 -244
- 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 +421 -382
- 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 +318 -277
- package/dist/mason.js.map +1 -1
- package/package.json +1 -1
package/dist/mason-auto.js
CHANGED
|
@@ -36,12 +36,69 @@ var init_paths = __esm({
|
|
|
36
36
|
}
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
+
// src/utils/profile.ts
|
|
40
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
41
|
+
async function profilePhase(label, run) {
|
|
42
|
+
const phases = profiles.getStore();
|
|
43
|
+
if (!phases) return run();
|
|
44
|
+
const start = performance.now();
|
|
45
|
+
try {
|
|
46
|
+
return await run();
|
|
47
|
+
} finally {
|
|
48
|
+
const timing = phases[label] ??= { calls: 0, totalMs: 0 };
|
|
49
|
+
timing.calls++;
|
|
50
|
+
timing.totalMs += performance.now() - start;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function withProfile(run, emit) {
|
|
54
|
+
const phases = {};
|
|
55
|
+
const start = performance.now();
|
|
56
|
+
return profiles.run(phases, async () => {
|
|
57
|
+
try {
|
|
58
|
+
return await run();
|
|
59
|
+
} finally {
|
|
60
|
+
emit(JSON.stringify({ kind: "mason-profile", version: 1, totalMs: performance.now() - start, phases }));
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
var profiles;
|
|
65
|
+
var init_profile = __esm({
|
|
66
|
+
"src/utils/profile.ts"() {
|
|
67
|
+
"use strict";
|
|
68
|
+
profiles = new AsyncLocalStorage();
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// src/utils/git-read.ts
|
|
73
|
+
import { execFile } from "child_process";
|
|
74
|
+
import { promisify } from "util";
|
|
75
|
+
function execGit(args, options) {
|
|
76
|
+
const command = args.find((arg) => !arg.startsWith("-")) ?? "other";
|
|
77
|
+
return profilePhase("git." + (commands.has(command) ? command : "other"), async () => {
|
|
78
|
+
const { input: input2, ...processOptions } = options;
|
|
79
|
+
const operation = execute("git", args, processOptions);
|
|
80
|
+
if (input2 !== void 0) {
|
|
81
|
+
operation.child.stdin.on("error", () => {
|
|
82
|
+
});
|
|
83
|
+
operation.child.stdin.end(input2);
|
|
84
|
+
}
|
|
85
|
+
return operation;
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
var execute, commands;
|
|
89
|
+
var init_git_read = __esm({
|
|
90
|
+
"src/utils/git-read.ts"() {
|
|
91
|
+
"use strict";
|
|
92
|
+
init_profile();
|
|
93
|
+
execute = promisify(execFile);
|
|
94
|
+
commands = /* @__PURE__ */ new Set(["rev-parse", "symbolic-ref", "for-each-ref", "ls-files", "check-ignore", "status", "log", "diff", "ls-tree", "show", "merge-base"]);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
39
98
|
// src/utils/files.ts
|
|
40
99
|
import fs from "fs/promises";
|
|
41
100
|
import { constants } from "fs";
|
|
42
101
|
import path2 from "path";
|
|
43
|
-
import { execFile } from "child_process";
|
|
44
|
-
import { promisify } from "util";
|
|
45
102
|
import fg from "fast-glob";
|
|
46
103
|
function isSensitiveFile(file) {
|
|
47
104
|
return file.split(/[\\/]/).some(
|
|
@@ -95,12 +152,12 @@ async function createFileAccess(rootDir) {
|
|
|
95
152
|
const ignore = [...SOURCE_IGNORE, ...config.ignore ?? []];
|
|
96
153
|
let gitFiles = null;
|
|
97
154
|
try {
|
|
98
|
-
const { stdout } = await
|
|
155
|
+
const { stdout } = await execGit(["ls-files", "-z", "--cached", "--others", "--exclude-standard"], { cwd: root, maxBuffer: 50 * 1024 * 1024 });
|
|
99
156
|
gitFiles = new Set(stdout.split("\0").filter(Boolean));
|
|
100
157
|
} catch {
|
|
101
158
|
let inGit = false;
|
|
102
159
|
try {
|
|
103
|
-
await
|
|
160
|
+
await execGit(["rev-parse", "--git-dir"], { cwd: root });
|
|
104
161
|
inGit = true;
|
|
105
162
|
} catch {
|
|
106
163
|
}
|
|
@@ -143,12 +200,12 @@ async function createFileAccess(rootDir) {
|
|
|
143
200
|
}
|
|
144
201
|
return { root, config, list, read };
|
|
145
202
|
}
|
|
146
|
-
var
|
|
203
|
+
var SOURCE_EXTENSIONS, SOURCE_GLOB, SOURCE_IGNORE, MAX_SOURCE_BYTES;
|
|
147
204
|
var init_files = __esm({
|
|
148
205
|
"src/utils/files.ts"() {
|
|
149
206
|
"use strict";
|
|
207
|
+
init_git_read();
|
|
150
208
|
init_paths();
|
|
151
|
-
exec = promisify(execFile);
|
|
152
209
|
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"];
|
|
153
210
|
SOURCE_GLOB = `**/*.{${SOURCE_EXTENSIONS.join(",")}}`;
|
|
154
211
|
SOURCE_IGNORE = [
|
|
@@ -346,39 +403,6 @@ var init_store = __esm({
|
|
|
346
403
|
}
|
|
347
404
|
});
|
|
348
405
|
|
|
349
|
-
// src/utils/profile.ts
|
|
350
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
351
|
-
async function profilePhase(label, run) {
|
|
352
|
-
const phases = profiles.getStore();
|
|
353
|
-
if (!phases) return run();
|
|
354
|
-
const start = performance.now();
|
|
355
|
-
try {
|
|
356
|
-
return await run();
|
|
357
|
-
} finally {
|
|
358
|
-
const timing = phases[label] ??= { calls: 0, totalMs: 0 };
|
|
359
|
-
timing.calls++;
|
|
360
|
-
timing.totalMs += performance.now() - start;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
async function withProfile(run, emit) {
|
|
364
|
-
const phases = {};
|
|
365
|
-
const start = performance.now();
|
|
366
|
-
return profiles.run(phases, async () => {
|
|
367
|
-
try {
|
|
368
|
-
return await run();
|
|
369
|
-
} finally {
|
|
370
|
-
emit(JSON.stringify({ kind: "mason-profile", version: 1, totalMs: performance.now() - start, phases }));
|
|
371
|
-
}
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
var profiles;
|
|
375
|
-
var init_profile = __esm({
|
|
376
|
-
"src/utils/profile.ts"() {
|
|
377
|
-
"use strict";
|
|
378
|
-
profiles = new AsyncLocalStorage();
|
|
379
|
-
}
|
|
380
|
-
});
|
|
381
|
-
|
|
382
406
|
// src/audit/commands.ts
|
|
383
407
|
import path4 from "path";
|
|
384
408
|
function splitCommands(line) {
|
|
@@ -603,14 +627,14 @@ function extractClaims(content2) {
|
|
|
603
627
|
const ignored = computeIgnoredLines(lines);
|
|
604
628
|
const paths = /* @__PURE__ */ new Map();
|
|
605
629
|
const counts = [];
|
|
606
|
-
const
|
|
630
|
+
const commands2 = /* @__PURE__ */ new Map();
|
|
607
631
|
const addPath = (claim) => {
|
|
608
632
|
const key = [claim.relativeTo ?? "scope", claim.path].join(":");
|
|
609
633
|
if (!paths.has(key)) paths.set(key, claim);
|
|
610
634
|
};
|
|
611
635
|
const addCommand = (claim) => {
|
|
612
636
|
const key = JSON.stringify([claim.scriptName, claim.directory, claim.scopeUnknown]);
|
|
613
|
-
if (!
|
|
637
|
+
if (!commands2.has(key)) commands2.set(key, claim);
|
|
614
638
|
};
|
|
615
639
|
const parsed = markdown.parse(lines.map((line, i) => ignored[i] ? "" : line).join("\n"), {});
|
|
616
640
|
for (const token of parsed) {
|
|
@@ -712,7 +736,7 @@ function extractClaims(content2) {
|
|
|
712
736
|
return {
|
|
713
737
|
paths: [...paths.values()],
|
|
714
738
|
counts,
|
|
715
|
-
commands: [...
|
|
739
|
+
commands: [...commands2.values()]
|
|
716
740
|
};
|
|
717
741
|
}
|
|
718
742
|
var ROOT_FILE_NAMES, SHELL_FENCE_INFOS, markdown, COUNT_RE, COUNT_DENYLIST_RE, IGNORE_LINE, IGNORE_START, IGNORE_END;
|
|
@@ -762,9 +786,53 @@ var init_claims = __esm({
|
|
|
762
786
|
}
|
|
763
787
|
});
|
|
764
788
|
|
|
789
|
+
// src/audit/inspection.ts
|
|
790
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
791
|
+
import path5 from "path";
|
|
792
|
+
function withRepositoryInspection(root, run) {
|
|
793
|
+
return inspections.run({ root: path5.resolve(root), reads: /* @__PURE__ */ new Map() }, run);
|
|
794
|
+
}
|
|
795
|
+
function inspectionRead(root, key, read) {
|
|
796
|
+
const inspection = inspections.getStore();
|
|
797
|
+
if (!inspection || inspection.root !== path5.resolve(root)) return read();
|
|
798
|
+
if (!inspection.reads.has(key)) {
|
|
799
|
+
const pending = read();
|
|
800
|
+
inspection.reads.set(key, pending);
|
|
801
|
+
void pending.catch(() => {
|
|
802
|
+
if (inspection.reads.get(key) === pending) inspection.reads.delete(key);
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
return inspection.reads.get(key);
|
|
806
|
+
}
|
|
807
|
+
function inspectionGit(args, options) {
|
|
808
|
+
return inspectionRead(options.cwd, "git:" + JSON.stringify([args, options]), () => execGit(args, options));
|
|
809
|
+
}
|
|
810
|
+
async function auditGitPaths(root, kind) {
|
|
811
|
+
const shared = inspections.getStore()?.root === path5.resolve(root);
|
|
812
|
+
const specs = shared ? [...docSpecs, ...sourceSpecs] : kind === "documents" ? docSpecs : sourceSpecs;
|
|
813
|
+
const limit = kind === "documents" ? 16 * 1024 * 1024 : 32 * 1024 * 1024;
|
|
814
|
+
const { stdout } = await inspectionGit(
|
|
815
|
+
["ls-files", "-z", "--cached", "--others", "--exclude-standard", "--", ...specs],
|
|
816
|
+
{ cwd: root, maxBuffer: shared ? 48 * 1024 * 1024 : limit, timeout: 1e4 }
|
|
817
|
+
);
|
|
818
|
+
const paths = [...new Set(stdout.split("\0").filter(Boolean))].filter((file) => kind === "documents" ? /^(readme|agents|claude)\.md$/i.test(path5.posix.basename(file)) : sourceSuffixes.has(file.slice(file.lastIndexOf("."))));
|
|
819
|
+
if (Buffer.byteLength(paths.join("\0")) > limit) throw new Error("Git " + kind + " inventory exceeds its bounded read limit.");
|
|
820
|
+
return paths.sort();
|
|
821
|
+
}
|
|
822
|
+
var inspections, sourceSpecs, docSpecs, sourceSuffixes;
|
|
823
|
+
var init_inspection = __esm({
|
|
824
|
+
"src/audit/inspection.ts"() {
|
|
825
|
+
"use strict";
|
|
826
|
+
init_git_read();
|
|
827
|
+
init_files();
|
|
828
|
+
inspections = new AsyncLocalStorage2();
|
|
829
|
+
sourceSpecs = SOURCE_EXTENSIONS.map((extension) => `:(glob)**/*.${extension}`);
|
|
830
|
+
docSpecs = [":(icase,glob)**/readme.md", ":(icase,glob)**/agents.md", ":(icase,glob)**/claude.md"];
|
|
831
|
+
sourceSuffixes = new Set(SOURCE_EXTENSIONS.map((extension) => "." + extension));
|
|
832
|
+
}
|
|
833
|
+
});
|
|
834
|
+
|
|
765
835
|
// src/audit/git.ts
|
|
766
|
-
import { execFile as execFile2 } from "child_process";
|
|
767
|
-
import { promisify as promisify2 } from "util";
|
|
768
836
|
function parseCommitLine(line) {
|
|
769
837
|
const parts = line.split(" ");
|
|
770
838
|
if (parts.length < 3 || !parts[0]) return null;
|
|
@@ -772,8 +840,7 @@ function parseCommitLine(line) {
|
|
|
772
840
|
}
|
|
773
841
|
async function lastCommitOf(resolvedRoot, relPath) {
|
|
774
842
|
try {
|
|
775
|
-
const { stdout } = await
|
|
776
|
-
"git",
|
|
843
|
+
const { stdout } = await inspectionGit(
|
|
777
844
|
["log", "-1", `--format=${COMMIT_FORMAT}`, "--", `:(literal)${relPath}`],
|
|
778
845
|
{ cwd: resolvedRoot }
|
|
779
846
|
);
|
|
@@ -785,8 +852,7 @@ async function lastCommitOf(resolvedRoot, relPath) {
|
|
|
785
852
|
}
|
|
786
853
|
async function deletingCommitOf(resolvedRoot, relPath) {
|
|
787
854
|
try {
|
|
788
|
-
const { stdout } = await
|
|
789
|
-
"git",
|
|
855
|
+
const { stdout } = await inspectionGit(
|
|
790
856
|
[
|
|
791
857
|
"log",
|
|
792
858
|
"-1",
|
|
@@ -805,8 +871,7 @@ async function deletingCommitOf(resolvedRoot, relPath) {
|
|
|
805
871
|
}
|
|
806
872
|
async function firstCommitOf(resolvedRoot, relPath) {
|
|
807
873
|
try {
|
|
808
|
-
const { stdout } = await
|
|
809
|
-
"git",
|
|
874
|
+
const { stdout } = await inspectionGit(
|
|
810
875
|
["log", "--reverse", `--format=${COMMIT_FORMAT}`, "--", `:(literal)${relPath}`],
|
|
811
876
|
{ cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 }
|
|
812
877
|
);
|
|
@@ -819,8 +884,7 @@ async function firstCommitOf(resolvedRoot, relPath) {
|
|
|
819
884
|
async function commitsTouchingSince(resolvedRoot, fromHash, pathspecs) {
|
|
820
885
|
if (!fromHash || fromHash === "unknown") return null;
|
|
821
886
|
try {
|
|
822
|
-
const { stdout } = await
|
|
823
|
-
"git",
|
|
887
|
+
const { stdout } = await inspectionGit(
|
|
824
888
|
[
|
|
825
889
|
"log",
|
|
826
890
|
`${fromHash}..HEAD`,
|
|
@@ -844,11 +908,11 @@ async function commitsTouchingSince(resolvedRoot, fromHash, pathspecs) {
|
|
|
844
908
|
return null;
|
|
845
909
|
}
|
|
846
910
|
}
|
|
847
|
-
var
|
|
911
|
+
var COMMIT_FORMAT;
|
|
848
912
|
var init_git = __esm({
|
|
849
913
|
"src/audit/git.ts"() {
|
|
850
914
|
"use strict";
|
|
851
|
-
|
|
915
|
+
init_inspection();
|
|
852
916
|
COMMIT_FORMAT = "%H%x09%cI%x09%s";
|
|
853
917
|
}
|
|
854
918
|
});
|
|
@@ -856,9 +920,7 @@ var init_git = __esm({
|
|
|
856
920
|
// src/audit/inputs.ts
|
|
857
921
|
import fs4 from "fs/promises";
|
|
858
922
|
import * as nativeFs from "fs";
|
|
859
|
-
import
|
|
860
|
-
import { execFile as execFile3 } from "child_process";
|
|
861
|
-
import { promisify as promisify3 } from "util";
|
|
923
|
+
import path6 from "path";
|
|
862
924
|
import fg2 from "fast-glob";
|
|
863
925
|
async function auditInputPath(root, file) {
|
|
864
926
|
if (file === ".") return fs4.realpath(root);
|
|
@@ -885,7 +947,7 @@ async function auditGlob(root, patterns, options) {
|
|
|
885
947
|
if (Array.isArray(patterns) && !patterns.length) return [];
|
|
886
948
|
const directories = /* @__PURE__ */ new Set();
|
|
887
949
|
const readdir2 = ((directory, opts, callback) => {
|
|
888
|
-
const relative =
|
|
950
|
+
const relative = path6.relative(root, directory).split(path6.sep).join("/") || ".";
|
|
889
951
|
directories.add(relative);
|
|
890
952
|
if (directories.size > MAX_INPUTS) {
|
|
891
953
|
callback(new Error(`${options.label} exceeds ${MAX_INPUTS.toLocaleString("en-US")} directories while inspecting ${relative}. Narrow this check's inputs.`));
|
|
@@ -896,7 +958,7 @@ async function auditGlob(root, patterns, options) {
|
|
|
896
958
|
}, callback);
|
|
897
959
|
});
|
|
898
960
|
const lstat2 = ((file, callback) => {
|
|
899
|
-
const relative =
|
|
961
|
+
const relative = path6.relative(root, file).split(path6.sep).join("/") || ".";
|
|
900
962
|
auditInputPath(root, relative).then(() => nativeFs.lstat(file, callback), callback);
|
|
901
963
|
});
|
|
902
964
|
const stream = fg2.stream(patterns, {
|
|
@@ -933,62 +995,48 @@ async function auditGlob(root, patterns, options) {
|
|
|
933
995
|
return matches.sort();
|
|
934
996
|
}
|
|
935
997
|
async function gitSourcePaths(root) {
|
|
936
|
-
|
|
937
|
-
"git",
|
|
938
|
-
[
|
|
939
|
-
"ls-files",
|
|
940
|
-
"-z",
|
|
941
|
-
"--cached",
|
|
942
|
-
"--others",
|
|
943
|
-
"--exclude-standard",
|
|
944
|
-
"--",
|
|
945
|
-
...SOURCE_EXTENSIONS.map((extension) => `:(glob)**/*.${extension}`)
|
|
946
|
-
],
|
|
947
|
-
{ cwd: root, maxBuffer: 32 * 1024 * 1024, timeout: 1e4 }
|
|
948
|
-
);
|
|
949
|
-
return [...new Set(stdout.split("\0").filter(Boolean))].sort();
|
|
998
|
+
return auditGitPaths(root, "sources");
|
|
950
999
|
}
|
|
951
1000
|
async function gitIgnoredPaths(root, files) {
|
|
952
1001
|
if (!files.length) return /* @__PURE__ */ new Set();
|
|
953
|
-
const
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1002
|
+
const ignored = /* @__PURE__ */ new Set();
|
|
1003
|
+
for (let offset = 0; offset < files.length; offset += 1e3) {
|
|
1004
|
+
try {
|
|
1005
|
+
const { stdout } = await execGit(["check-ignore", "-z", "--stdin"], {
|
|
1006
|
+
cwd: root,
|
|
1007
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
1008
|
+
timeout: 1e4,
|
|
1009
|
+
input: files.slice(offset, offset + 1e3).join("\0") + "\0"
|
|
1010
|
+
});
|
|
1011
|
+
for (const file of stdout.split("\0").filter(Boolean)) ignored.add(file);
|
|
1012
|
+
} catch (error) {
|
|
1013
|
+
if (error.code !== 1) throw error;
|
|
1014
|
+
}
|
|
966
1015
|
}
|
|
1016
|
+
return ignored;
|
|
967
1017
|
}
|
|
968
|
-
var
|
|
1018
|
+
var MAX_INPUTS;
|
|
969
1019
|
var init_inputs = __esm({
|
|
970
1020
|
"src/audit/inputs.ts"() {
|
|
971
1021
|
"use strict";
|
|
972
1022
|
init_files();
|
|
973
1023
|
init_storage();
|
|
974
|
-
|
|
1024
|
+
init_git_read();
|
|
1025
|
+
init_inspection();
|
|
975
1026
|
MAX_INPUTS = 1e5;
|
|
976
1027
|
}
|
|
977
1028
|
});
|
|
978
1029
|
|
|
979
1030
|
// src/audit/docs.ts
|
|
980
1031
|
import fs5 from "fs/promises";
|
|
981
|
-
import
|
|
982
|
-
import { execFile as execFile4 } from "child_process";
|
|
983
|
-
import { promisify as promisify4 } from "util";
|
|
1032
|
+
import path7 from "path";
|
|
984
1033
|
import fg3 from "fast-glob";
|
|
985
1034
|
async function dirtyDocs(resolvedRoot, files) {
|
|
986
1035
|
if (!files.length) return /* @__PURE__ */ new Set();
|
|
987
1036
|
try {
|
|
988
1037
|
const dirty = /* @__PURE__ */ new Set();
|
|
989
1038
|
for (let offset = 0; offset < files.length; offset += 256) {
|
|
990
|
-
const { stdout } = await
|
|
991
|
-
"git",
|
|
1039
|
+
const { stdout } = await execGit(
|
|
992
1040
|
["status", "--porcelain=v1", "-z", "--untracked-files=all", "--", ...files.slice(offset, offset + 256).map((file) => `:(literal)${file}`)],
|
|
993
1041
|
{ cwd: resolvedRoot, maxBuffer: 16 * 1024 * 1024, timeout: 1e4 }
|
|
994
1042
|
);
|
|
@@ -1008,25 +1056,10 @@ async function dirtyDocs(resolvedRoot, files) {
|
|
|
1008
1056
|
async function discoverDocPaths(root) {
|
|
1009
1057
|
let candidates = [];
|
|
1010
1058
|
try {
|
|
1011
|
-
|
|
1012
|
-
"git",
|
|
1013
|
-
[
|
|
1014
|
-
"ls-files",
|
|
1015
|
-
"-z",
|
|
1016
|
-
"--cached",
|
|
1017
|
-
"--others",
|
|
1018
|
-
"--exclude-standard",
|
|
1019
|
-
"--",
|
|
1020
|
-
":(icase,glob)**/readme.md",
|
|
1021
|
-
":(icase,glob)**/agents.md",
|
|
1022
|
-
":(icase,glob)**/claude.md"
|
|
1023
|
-
],
|
|
1024
|
-
{ cwd: root, maxBuffer: 16 * 1024 * 1024, timeout: 1e4 }
|
|
1025
|
-
);
|
|
1026
|
-
candidates = stdout.split("\0").filter(Boolean);
|
|
1059
|
+
candidates = await auditGitPaths(root, "documents");
|
|
1027
1060
|
} catch (error) {
|
|
1028
1061
|
try {
|
|
1029
|
-
await
|
|
1062
|
+
await execGit(["rev-parse", "--git-dir"], { cwd: root });
|
|
1030
1063
|
} catch {
|
|
1031
1064
|
return localDocPaths(root);
|
|
1032
1065
|
}
|
|
@@ -1073,14 +1106,14 @@ async function localDocPaths(root) {
|
|
|
1073
1106
|
throw error;
|
|
1074
1107
|
}
|
|
1075
1108
|
for (const entry of entries) if (/^(?:agents|claude|readme)\.md$/i.test(entry.name)) {
|
|
1076
|
-
paths.push(
|
|
1109
|
+
paths.push(path7.posix.join(dir, entry.name));
|
|
1077
1110
|
}
|
|
1078
1111
|
}
|
|
1079
1112
|
return paths;
|
|
1080
1113
|
}
|
|
1081
1114
|
function documentScope(file) {
|
|
1082
|
-
const parent =
|
|
1083
|
-
return
|
|
1115
|
+
const parent = path7.posix.dirname(file);
|
|
1116
|
+
return path7.posix.basename(parent).toLowerCase() === ".claude" ? path7.posix.dirname(parent) : parent;
|
|
1084
1117
|
}
|
|
1085
1118
|
async function discoverDocs(resolvedRoot) {
|
|
1086
1119
|
return profilePhase("audit.documents", () => readDocs(resolvedRoot));
|
|
@@ -1100,7 +1133,7 @@ async function readDocs(resolvedRoot) {
|
|
|
1100
1133
|
path: candidate,
|
|
1101
1134
|
content: content2,
|
|
1102
1135
|
scope: documentScope(candidate),
|
|
1103
|
-
kind: /^readme\.md$/i.test(
|
|
1136
|
+
kind: /^readme\.md$/i.test(path7.posix.basename(candidate)) ? "readme" : "instructions",
|
|
1104
1137
|
lineCount: content2.split("\n").length,
|
|
1105
1138
|
lastCommit,
|
|
1106
1139
|
dirty: dirty.has(candidate),
|
|
@@ -1110,16 +1143,17 @@ async function readDocs(resolvedRoot) {
|
|
|
1110
1143
|
}
|
|
1111
1144
|
return docs;
|
|
1112
1145
|
}
|
|
1113
|
-
var
|
|
1146
|
+
var DOC_CANDIDATES;
|
|
1114
1147
|
var init_docs = __esm({
|
|
1115
1148
|
"src/audit/docs.ts"() {
|
|
1116
1149
|
"use strict";
|
|
1150
|
+
init_git_read();
|
|
1117
1151
|
init_profile();
|
|
1118
1152
|
init_claims();
|
|
1119
1153
|
init_git();
|
|
1120
1154
|
init_inputs();
|
|
1121
1155
|
init_files();
|
|
1122
|
-
|
|
1156
|
+
init_inspection();
|
|
1123
1157
|
DOC_CANDIDATES = [
|
|
1124
1158
|
"AGENTS.md",
|
|
1125
1159
|
"CLAUDE.md",
|
|
@@ -1130,14 +1164,14 @@ var init_docs = __esm({
|
|
|
1130
1164
|
|
|
1131
1165
|
// src/audit/scope.ts
|
|
1132
1166
|
import fs6 from "fs/promises";
|
|
1133
|
-
import
|
|
1167
|
+
import path8 from "path";
|
|
1134
1168
|
function scopedPath(directory, value) {
|
|
1135
|
-
if (
|
|
1136
|
-
const resolved =
|
|
1169
|
+
if (path8.posix.isAbsolute(value) || value.includes("\\") || /[\x00-\x1f]/.test(value)) return null;
|
|
1170
|
+
const resolved = path8.posix.normalize(path8.posix.join(directory, value));
|
|
1137
1171
|
return resolved === ".." || resolved.startsWith("../") ? null : resolved;
|
|
1138
1172
|
}
|
|
1139
1173
|
function pathClaimScope(doc, claim) {
|
|
1140
|
-
const directory = claim.relativeTo === "document" ?
|
|
1174
|
+
const directory = claim.relativeTo === "document" ? path8.posix.dirname(doc) : documentScope(doc);
|
|
1141
1175
|
const local = scopedPath(directory, claim.path);
|
|
1142
1176
|
if (!local) return null;
|
|
1143
1177
|
const candidates = claim.relativeTo === "document" || directory === "." ? [local] : [...new Set([local, scopedPath(".", claim.path)].filter((p) => p !== null))];
|
|
@@ -1171,9 +1205,9 @@ var init_scope = __esm({
|
|
|
1171
1205
|
});
|
|
1172
1206
|
|
|
1173
1207
|
// src/test-map.ts
|
|
1174
|
-
import
|
|
1208
|
+
import path9 from "path";
|
|
1175
1209
|
async function buildTestMap(dir) {
|
|
1176
|
-
const rootDir =
|
|
1210
|
+
const rootDir = path9.resolve(dir);
|
|
1177
1211
|
const access = await createFileAccess(rootDir);
|
|
1178
1212
|
const testPatterns = [
|
|
1179
1213
|
"**/*.test.*",
|
|
@@ -1194,7 +1228,7 @@ async function buildTestMap(dir) {
|
|
|
1194
1228
|
const sourceByBaseName = /* @__PURE__ */ new Map();
|
|
1195
1229
|
for (const file of sourceFiles) {
|
|
1196
1230
|
if (testFiles.includes(file)) continue;
|
|
1197
|
-
const baseName =
|
|
1231
|
+
const baseName = path9.basename(file).replace(/\.[^.]+$/, "");
|
|
1198
1232
|
const existing = sourceByBaseName.get(baseName) ?? [];
|
|
1199
1233
|
existing.push(file);
|
|
1200
1234
|
sourceByBaseName.set(baseName, existing);
|
|
@@ -1202,7 +1236,7 @@ async function buildTestMap(dir) {
|
|
|
1202
1236
|
const paired = [];
|
|
1203
1237
|
const unmatched = [];
|
|
1204
1238
|
for (const testFile of testFiles) {
|
|
1205
|
-
const testBaseName =
|
|
1239
|
+
const testBaseName = path9.basename(testFile).replace(/\.[^.]+$/, "");
|
|
1206
1240
|
const sourceName = testBaseName.replace(/Test$|Tests$|Spec$|\.test$|\.spec$/, "").replace(/^test_|_test$/, "");
|
|
1207
1241
|
if (!sourceName) {
|
|
1208
1242
|
unmatched.push(testFile);
|
|
@@ -1210,10 +1244,10 @@ async function buildTestMap(dir) {
|
|
|
1210
1244
|
}
|
|
1211
1245
|
const candidates = sourceByBaseName.get(sourceName);
|
|
1212
1246
|
if (candidates && candidates.length > 0) {
|
|
1213
|
-
const testDir =
|
|
1247
|
+
const testDir = path9.dirname(testFile);
|
|
1214
1248
|
const bestMatch = candidates.reduce((best, candidate) => {
|
|
1215
|
-
const candidateDir =
|
|
1216
|
-
const bestDir =
|
|
1249
|
+
const candidateDir = path9.dirname(candidate);
|
|
1250
|
+
const bestDir = path9.dirname(best);
|
|
1217
1251
|
const candidateOverlap = commonSegments(testDir, candidateDir);
|
|
1218
1252
|
const bestOverlap = commonSegments(testDir, bestDir);
|
|
1219
1253
|
return candidateOverlap > bestOverlap ? candidate : best;
|
|
@@ -1247,9 +1281,7 @@ var init_test_map = __esm({
|
|
|
1247
1281
|
});
|
|
1248
1282
|
|
|
1249
1283
|
// src/snapshot/snapshot.ts
|
|
1250
|
-
import
|
|
1251
|
-
import { execFile as execFile5 } from "child_process";
|
|
1252
|
-
import { promisify as promisify5 } from "util";
|
|
1284
|
+
import path10 from "path";
|
|
1253
1285
|
import { z as z2 } from "zod";
|
|
1254
1286
|
async function inspectSnapshot(rootDir) {
|
|
1255
1287
|
try {
|
|
@@ -1265,7 +1297,7 @@ async function inspectSnapshot(rootDir) {
|
|
|
1265
1297
|
}
|
|
1266
1298
|
async function getCurrentGitHash(rootDir) {
|
|
1267
1299
|
try {
|
|
1268
|
-
const { stdout } = await
|
|
1300
|
+
const { stdout } = await execGit(["rev-parse", "HEAD"], {
|
|
1269
1301
|
cwd: rootDir
|
|
1270
1302
|
});
|
|
1271
1303
|
return stdout.trim();
|
|
@@ -1273,16 +1305,16 @@ async function getCurrentGitHash(rootDir) {
|
|
|
1273
1305
|
return "unknown";
|
|
1274
1306
|
}
|
|
1275
1307
|
}
|
|
1276
|
-
var
|
|
1308
|
+
var repoPath, verificationFields, featureSchema, flowSchema, snapshotSchema;
|
|
1277
1309
|
var init_snapshot = __esm({
|
|
1278
1310
|
"src/snapshot/snapshot.ts"() {
|
|
1279
1311
|
"use strict";
|
|
1312
|
+
init_git_read();
|
|
1280
1313
|
init_files();
|
|
1281
1314
|
init_files();
|
|
1282
1315
|
init_storage();
|
|
1283
1316
|
init_paths();
|
|
1284
1317
|
init_test_map();
|
|
1285
|
-
exec4 = promisify5(execFile5);
|
|
1286
1318
|
repoPath = z2.string().refine((value) => normalizeRepoPath(value) !== null, "Expected a relative repository path");
|
|
1287
1319
|
verificationFields = {
|
|
1288
1320
|
refreshedHash: z2.string().optional(),
|
|
@@ -1312,9 +1344,7 @@ var init_snapshot = __esm({
|
|
|
1312
1344
|
|
|
1313
1345
|
// src/drift/drift.ts
|
|
1314
1346
|
import fs7 from "fs/promises";
|
|
1315
|
-
import
|
|
1316
|
-
import { execFile as execFile6 } from "child_process";
|
|
1317
|
-
import { promisify as promisify6 } from "util";
|
|
1347
|
+
import path11 from "path";
|
|
1318
1348
|
function parseChanges(output) {
|
|
1319
1349
|
const fields = output.split("\0");
|
|
1320
1350
|
const changes = [];
|
|
@@ -1335,7 +1365,7 @@ function touchedPaths(changes) {
|
|
|
1335
1365
|
async function getChangesWithStatus(resolvedRoot, fromHash, toHash = "HEAD") {
|
|
1336
1366
|
if (!fromHash || fromHash === "unknown" || fromHash.startsWith("-") || !toHash || toHash === "unknown" || toHash.startsWith("-")) return null;
|
|
1337
1367
|
try {
|
|
1338
|
-
const { stdout } = await
|
|
1368
|
+
const { stdout } = await execGit(["diff", "--name-status", "-z", "-M", fromHash, toHash, "--"], { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 });
|
|
1339
1369
|
return parseChanges(stdout);
|
|
1340
1370
|
} catch {
|
|
1341
1371
|
return null;
|
|
@@ -1344,8 +1374,8 @@ async function getChangesWithStatus(resolvedRoot, fromHash, toHash = "HEAD") {
|
|
|
1344
1374
|
async function getWorkingTree(resolvedRoot) {
|
|
1345
1375
|
try {
|
|
1346
1376
|
const [diff, untracked] = await Promise.all([
|
|
1347
|
-
|
|
1348
|
-
|
|
1377
|
+
execGit(["diff", "--name-status", "-z", "-M", "HEAD", "--"], { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 }),
|
|
1378
|
+
execGit(["ls-files", "-z", "--others", "--exclude-standard"], { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 })
|
|
1349
1379
|
]);
|
|
1350
1380
|
const untrackedFiles = untracked.stdout.split("\0").filter((f) => f && !f.startsWith(".mason/"));
|
|
1351
1381
|
return { available: true, changedFiles: [.../* @__PURE__ */ new Set([...touchedPaths(parseChanges(diff.stdout)), ...untrackedFiles])].sort(), untrackedFiles };
|
|
@@ -1353,13 +1383,12 @@ async function getWorkingTree(resolvedRoot) {
|
|
|
1353
1383
|
return { available: false, changedFiles: [], untrackedFiles: [] };
|
|
1354
1384
|
}
|
|
1355
1385
|
}
|
|
1356
|
-
var exec5;
|
|
1357
1386
|
var init_drift = __esm({
|
|
1358
1387
|
"src/drift/drift.ts"() {
|
|
1359
1388
|
"use strict";
|
|
1389
|
+
init_git_read();
|
|
1360
1390
|
init_snapshot();
|
|
1361
1391
|
init_paths();
|
|
1362
|
-
exec5 = promisify6(execFile6);
|
|
1363
1392
|
}
|
|
1364
1393
|
});
|
|
1365
1394
|
|
|
@@ -1380,7 +1409,7 @@ var init_types = __esm({
|
|
|
1380
1409
|
});
|
|
1381
1410
|
|
|
1382
1411
|
// src/audit/checks/deleted-reference.ts
|
|
1383
|
-
import
|
|
1412
|
+
import path12 from "path";
|
|
1384
1413
|
async function checkDeletedReferences(ctx) {
|
|
1385
1414
|
const result = emptyResult();
|
|
1386
1415
|
for (const doc of ctx.docs) {
|
|
@@ -1409,7 +1438,7 @@ async function checkDeletedReferences(ctx) {
|
|
|
1409
1438
|
const candidates = await Promise.all(scope.candidates.map(async (file) => ({
|
|
1410
1439
|
file,
|
|
1411
1440
|
tracked: await lastCommitOf(ctx.root, file),
|
|
1412
|
-
parent: await pathExists(ctx.root,
|
|
1441
|
+
parent: await pathExists(ctx.root, path12.posix.dirname(file))
|
|
1413
1442
|
})));
|
|
1414
1443
|
const candidate = candidates.find((c) => c.tracked || renames.has(c.file)) ?? candidates.find((c) => c.parent);
|
|
1415
1444
|
if (!candidate && claim.relativeTo !== "document") continue;
|
|
@@ -1449,7 +1478,7 @@ var init_deleted_reference = __esm({
|
|
|
1449
1478
|
// src/audit/checks/new-module.ts
|
|
1450
1479
|
import fg4 from "fast-glob";
|
|
1451
1480
|
import fs8 from "fs/promises";
|
|
1452
|
-
import
|
|
1481
|
+
import path13 from "path";
|
|
1453
1482
|
function escapeRegExp(text2) {
|
|
1454
1483
|
return text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1455
1484
|
}
|
|
@@ -1487,23 +1516,36 @@ async function checkNewModules(ctx) {
|
|
|
1487
1516
|
return result;
|
|
1488
1517
|
}
|
|
1489
1518
|
function moduleDocumentation(docs) {
|
|
1490
|
-
return docs.map((doc) => doc.content + "\n" + (
|
|
1519
|
+
return docs.map((doc) => doc.content + "\n" + (path13.posix.dirname(doc.path) === "." ? "" : path13.posix.dirname(doc.path) + "/")).join("\n");
|
|
1491
1520
|
}
|
|
1492
|
-
|
|
1521
|
+
function moduleCandidates(root, combinedDocs) {
|
|
1522
|
+
return inspectionRead(root, "modules:" + combinedDocs, () => collectModuleCandidates(root, combinedDocs));
|
|
1523
|
+
}
|
|
1524
|
+
async function collectModuleCandidates(root, combinedDocs) {
|
|
1493
1525
|
const [config, sourcePaths] = await Promise.all([loadProjectConfig(root), gitSourcePaths(root)]);
|
|
1494
1526
|
const ignore = [...SOURCE_IGNORE, ...config.ignore ?? []];
|
|
1495
1527
|
const sources = sourcePaths.filter((file) => !file.split("/").some((part) => part.startsWith(".")));
|
|
1496
|
-
const listSubdirs = async (
|
|
1497
|
-
const
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1528
|
+
const listSubdirs = async (parents) => {
|
|
1529
|
+
const byParent = /* @__PURE__ */ new Map();
|
|
1530
|
+
for (let offset = 0; offset < parents.length; offset += 16) {
|
|
1531
|
+
await Promise.all(parents.slice(offset, offset + 16).map(async (dir) => {
|
|
1532
|
+
const entries = await fs8.readdir(await auditInputPath(root, dir), { withFileTypes: true });
|
|
1533
|
+
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));
|
|
1534
|
+
byParent.set(dir, dirs);
|
|
1535
|
+
}));
|
|
1536
|
+
}
|
|
1537
|
+
const ignored = await gitIgnoredPaths(root, [...byParent.values()].flat());
|
|
1538
|
+
const result = /* @__PURE__ */ new Map();
|
|
1539
|
+
for (const dir of parents) {
|
|
1540
|
+
const visible = new Set(byParent.get(dir).filter((candidate) => !ignored.has(candidate) || sources.some((file) => file.startsWith(candidate + "/"))));
|
|
1541
|
+
result.set(dir, (await auditGlob(root, dir === "." ? "*" : `${fg4.escapePath(dir)}/*`, {
|
|
1542
|
+
ignore,
|
|
1543
|
+
onlyDirectories: true,
|
|
1544
|
+
label: "Module directory discovery",
|
|
1545
|
+
select: (file) => visible.has(file)
|
|
1546
|
+
})).map((file) => path13.posix.basename(file)));
|
|
1547
|
+
}
|
|
1548
|
+
return result;
|
|
1507
1549
|
};
|
|
1508
1550
|
const countSourceFiles = async (dir) => {
|
|
1509
1551
|
const files = sources.filter((file) => file.startsWith(dir + "/"));
|
|
@@ -1519,14 +1561,16 @@ async function moduleCandidates(root, combinedDocs) {
|
|
|
1519
1561
|
return count4;
|
|
1520
1562
|
};
|
|
1521
1563
|
const candidates = [];
|
|
1522
|
-
|
|
1564
|
+
const topDirs = (await listSubdirs(["."])).get(".");
|
|
1565
|
+
const nested = await listSubdirs(topDirs.filter((dir) => isMentioned(combinedDocs, dir)));
|
|
1566
|
+
for (const topDir of topDirs) {
|
|
1523
1567
|
const topMentioned = isMentioned(combinedDocs, topDir);
|
|
1524
1568
|
if (!topMentioned) {
|
|
1525
1569
|
const count4 = await countSourceFiles(topDir);
|
|
1526
1570
|
if (count4 >= 1) candidates.push({ dir: topDir, sourceFileCount: count4 });
|
|
1527
1571
|
continue;
|
|
1528
1572
|
}
|
|
1529
|
-
const subdirs =
|
|
1573
|
+
const subdirs = nested.get(topDir);
|
|
1530
1574
|
const mentioned = subdirs.filter((s) => isMentioned(combinedDocs, s));
|
|
1531
1575
|
if (mentioned.length < ENUMERATION_THRESHOLD) continue;
|
|
1532
1576
|
for (const sub of subdirs) {
|
|
@@ -1543,6 +1587,7 @@ var DIR_DENYLIST, SECOND_LEVEL_MIN_SOURCE_FILES, ENUMERATION_THRESHOLD;
|
|
|
1543
1587
|
var init_new_module = __esm({
|
|
1544
1588
|
"src/audit/checks/new-module.ts"() {
|
|
1545
1589
|
"use strict";
|
|
1590
|
+
init_inspection();
|
|
1546
1591
|
init_files();
|
|
1547
1592
|
init_inputs();
|
|
1548
1593
|
init_git();
|
|
@@ -1572,7 +1617,7 @@ var init_new_module = __esm({
|
|
|
1572
1617
|
});
|
|
1573
1618
|
|
|
1574
1619
|
// src/audit/checks/stale-count.ts
|
|
1575
|
-
import
|
|
1620
|
+
import path14 from "path";
|
|
1576
1621
|
async function countGradleModules(root) {
|
|
1577
1622
|
for (const name of ["settings.gradle.kts", "settings.gradle"]) {
|
|
1578
1623
|
const content2 = await readAuditInput(root, name);
|
|
@@ -1606,7 +1651,7 @@ async function countNpmWorkspaces(root) {
|
|
|
1606
1651
|
return {
|
|
1607
1652
|
actual: matched.length,
|
|
1608
1653
|
countedFrom: "package.json workspaces",
|
|
1609
|
-
members: matched.map((m) =>
|
|
1654
|
+
members: matched.map((m) => path14.dirname(m)).sort()
|
|
1610
1655
|
};
|
|
1611
1656
|
}
|
|
1612
1657
|
}
|
|
@@ -1637,7 +1682,7 @@ async function countNpmWorkspaces(root) {
|
|
|
1637
1682
|
return {
|
|
1638
1683
|
actual: matched.length,
|
|
1639
1684
|
countedFrom: "pnpm-workspace.yaml",
|
|
1640
|
-
members: matched.map((m) =>
|
|
1685
|
+
members: matched.map((m) => path14.dirname(m)).sort()
|
|
1641
1686
|
};
|
|
1642
1687
|
}
|
|
1643
1688
|
}
|
|
@@ -1659,8 +1704,8 @@ async function countCargoCrates(root) {
|
|
|
1659
1704
|
ignore: ["**/target/**"],
|
|
1660
1705
|
label: "Cargo workspace discovery"
|
|
1661
1706
|
});
|
|
1662
|
-
for (const m of matched) members.add(
|
|
1663
|
-
} else if (await readAuditInput(root,
|
|
1707
|
+
for (const m of matched) members.add(path14.dirname(m));
|
|
1708
|
+
} else if (await readAuditInput(root, path14.posix.join(entry, "Cargo.toml")) !== null) {
|
|
1664
1709
|
members.add(entry);
|
|
1665
1710
|
}
|
|
1666
1711
|
}
|
|
@@ -1680,7 +1725,7 @@ async function resolveCountSource(root, claim) {
|
|
|
1680
1725
|
}
|
|
1681
1726
|
async function resolveDocCountSource(root, doc, claim) {
|
|
1682
1727
|
const scope = documentScope(doc);
|
|
1683
|
-
const source = await resolveCountSource(
|
|
1728
|
+
const source = await resolveCountSource(path14.join(root, scope), claim);
|
|
1684
1729
|
if (!source || scope === ".") return source;
|
|
1685
1730
|
return {
|
|
1686
1731
|
...source,
|
|
@@ -1732,7 +1777,7 @@ var init_stale_count = __esm({
|
|
|
1732
1777
|
});
|
|
1733
1778
|
|
|
1734
1779
|
// src/audit/checks/dead-command.ts
|
|
1735
|
-
import
|
|
1780
|
+
import path15 from "path";
|
|
1736
1781
|
function scriptsOf(raw) {
|
|
1737
1782
|
try {
|
|
1738
1783
|
const pkg = JSON.parse(raw);
|
|
@@ -1768,7 +1813,7 @@ async function commandInputs(root, docs) {
|
|
|
1768
1813
|
}
|
|
1769
1814
|
let cursor = directory;
|
|
1770
1815
|
while (true) {
|
|
1771
|
-
const manifest =
|
|
1816
|
+
const manifest = path15.posix.join(cursor, "package.json");
|
|
1772
1817
|
scope.candidates.push(manifest);
|
|
1773
1818
|
const raw = await read(manifest);
|
|
1774
1819
|
if (raw !== null) {
|
|
@@ -1779,7 +1824,7 @@ async function commandInputs(root, docs) {
|
|
|
1779
1824
|
break;
|
|
1780
1825
|
}
|
|
1781
1826
|
if (claim.directory !== void 0 || cursor === ".") break;
|
|
1782
|
-
cursor =
|
|
1827
|
+
cursor = path15.posix.dirname(cursor);
|
|
1783
1828
|
}
|
|
1784
1829
|
if (!resolution.manifest && !resolution.problem) {
|
|
1785
1830
|
resolution.problem = `No package.json for the command's scope (${directory}).`;
|
|
@@ -1842,10 +1887,8 @@ var init_dead_command = __esm({
|
|
|
1842
1887
|
});
|
|
1843
1888
|
|
|
1844
1889
|
// src/audit/release-metadata.ts
|
|
1845
|
-
import { execFile as execFile7 } from "child_process";
|
|
1846
|
-
import { promisify as promisify7 } from "util";
|
|
1847
1890
|
async function git(root, args) {
|
|
1848
|
-
return (await
|
|
1891
|
+
return (await execGit(args, { cwd: root, timeout: 1e4, maxBuffer: 2 * 1024 * 1024 })).stdout;
|
|
1849
1892
|
}
|
|
1850
1893
|
function withoutAndroidReleaseValues(text2) {
|
|
1851
1894
|
if (/\/\*|"""|'''/.test(text2) || !/id\s*\(?\s*["']com\.android\.(application|library)["']/.test(text2)) return null;
|
|
@@ -1891,11 +1934,10 @@ async function releaseMetadataOnly(root, commit) {
|
|
|
1891
1934
|
return false;
|
|
1892
1935
|
}
|
|
1893
1936
|
}
|
|
1894
|
-
var exec6;
|
|
1895
1937
|
var init_release_metadata = __esm({
|
|
1896
1938
|
"src/audit/release-metadata.ts"() {
|
|
1897
1939
|
"use strict";
|
|
1898
|
-
|
|
1940
|
+
init_git_read();
|
|
1899
1941
|
}
|
|
1900
1942
|
});
|
|
1901
1943
|
|
|
@@ -1991,7 +2033,7 @@ var init_deps_changed = __esm({
|
|
|
1991
2033
|
});
|
|
1992
2034
|
|
|
1993
2035
|
// src/context/lexical.ts
|
|
1994
|
-
import
|
|
2036
|
+
import path16 from "path";
|
|
1995
2037
|
var init_lexical = __esm({
|
|
1996
2038
|
"src/context/lexical.ts"() {
|
|
1997
2039
|
"use strict";
|
|
@@ -2175,7 +2217,7 @@ var init_provenance = __esm({
|
|
|
2175
2217
|
|
|
2176
2218
|
// src/decisions/decisions.ts
|
|
2177
2219
|
import fs9 from "fs/promises";
|
|
2178
|
-
import
|
|
2220
|
+
import path17 from "path";
|
|
2179
2221
|
import { createHash } from "crypto";
|
|
2180
2222
|
import { z as z4 } from "zod";
|
|
2181
2223
|
async function loadDecisionStore(rootDir) {
|
|
@@ -2226,9 +2268,9 @@ var init_decisions = __esm({
|
|
|
2226
2268
|
});
|
|
2227
2269
|
|
|
2228
2270
|
// src/decisions/drift.ts
|
|
2229
|
-
import
|
|
2271
|
+
import path18 from "path";
|
|
2230
2272
|
async function computeDecisionDrift(rootDir, decisions) {
|
|
2231
|
-
const resolvedRoot =
|
|
2273
|
+
const resolvedRoot = path18.resolve(rootDir);
|
|
2232
2274
|
const store = decisions ? { records: decisions, diagnostics: [] } : await loadDecisionStore(resolvedRoot);
|
|
2233
2275
|
const report = { historyAvailable: true, totalDecisions: store.records.length, staleDecisions: {}, freshness: {}, diagnostics: store.diagnostics };
|
|
2234
2276
|
const [head, workingTree] = await Promise.all([getCurrentGitHash(resolvedRoot), getWorkingTree(resolvedRoot)]);
|
|
@@ -2452,8 +2494,6 @@ var init_findings = __esm({
|
|
|
2452
2494
|
|
|
2453
2495
|
// src/audit/advisory-review.ts
|
|
2454
2496
|
import fs10 from "fs/promises";
|
|
2455
|
-
import { execFile as execFile8 } from "child_process";
|
|
2456
|
-
import { promisify as promisify8 } from "util";
|
|
2457
2497
|
import { z as z6 } from "zod";
|
|
2458
2498
|
async function readRecord(root, id) {
|
|
2459
2499
|
const raw = await readStoreJson(root, recordPath(id));
|
|
@@ -2580,10 +2620,12 @@ async function advisoryReviewInventory(root) {
|
|
|
2580
2620
|
if (names.length > 5e3) throw new Error("Advisory review inventory exceeds 5,000 records.");
|
|
2581
2621
|
return Promise.all(names.filter((name) => name.endsWith(".json")).sort().map(async (name) => [name, await readAuditInput(root, ".mason/reviews/advisories/" + name)]));
|
|
2582
2622
|
}
|
|
2583
|
-
var
|
|
2623
|
+
var git2, hashSchema, commitSchema2, evidenceSchema2, eventSchema2, recordSchema, reviewSummarySchema, advisoryReviewRequest, recordPath;
|
|
2584
2624
|
var init_advisory_review = __esm({
|
|
2585
2625
|
"src/audit/advisory-review.ts"() {
|
|
2586
2626
|
"use strict";
|
|
2627
|
+
init_inspection();
|
|
2628
|
+
init_git_read();
|
|
2587
2629
|
init_findings();
|
|
2588
2630
|
init_deps_changed();
|
|
2589
2631
|
init_inputs();
|
|
@@ -2593,8 +2635,7 @@ var init_advisory_review = __esm({
|
|
|
2593
2635
|
init_store();
|
|
2594
2636
|
init_decisions();
|
|
2595
2637
|
init_drift2();
|
|
2596
|
-
|
|
2597
|
-
git2 = async (root, ...args) => (await execute2("git", args, { cwd: root, timeout: 1e4, maxBuffer: 8 * 1024 * 1024 })).stdout;
|
|
2638
|
+
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;
|
|
2598
2639
|
hashSchema = z6.string().regex(/^[a-f0-9]{64}$/);
|
|
2599
2640
|
commitSchema2 = z6.string().regex(/^[a-f0-9]{40,64}$/);
|
|
2600
2641
|
evidenceSchema2 = z6.object({
|
|
@@ -2638,12 +2679,12 @@ var init_advisory_review = __esm({
|
|
|
2638
2679
|
|
|
2639
2680
|
// src/audit/audit.ts
|
|
2640
2681
|
import fs11 from "fs/promises";
|
|
2641
|
-
import
|
|
2682
|
+
import path19 from "path";
|
|
2642
2683
|
async function computeAudit(rootDir, options = {}) {
|
|
2643
2684
|
return profilePhase("audit.current", () => auditCurrent(rootDir, options));
|
|
2644
2685
|
}
|
|
2645
2686
|
async function auditCurrent(rootDir, options) {
|
|
2646
|
-
const resolvedRoot =
|
|
2687
|
+
const resolvedRoot = path19.resolve(rootDir);
|
|
2647
2688
|
const [docs, headHash] = await Promise.all([discoverDocs(resolvedRoot), getCurrentGitHash(resolvedRoot)]);
|
|
2648
2689
|
if (docs.length === 0) return null;
|
|
2649
2690
|
const report = {
|
|
@@ -2677,7 +2718,7 @@ async function auditCurrent(rootDir, options) {
|
|
|
2677
2718
|
}
|
|
2678
2719
|
let decisionsPresent = false;
|
|
2679
2720
|
try {
|
|
2680
|
-
await fs11.access(
|
|
2721
|
+
await fs11.access(path19.join(resolvedRoot, ".mason", "decisions"));
|
|
2681
2722
|
decisionsPresent = true;
|
|
2682
2723
|
} catch {
|
|
2683
2724
|
}
|
|
@@ -2719,7 +2760,7 @@ var init_audit = __esm({
|
|
|
2719
2760
|
|
|
2720
2761
|
// src/audit/repair.ts
|
|
2721
2762
|
import fs12 from "fs/promises";
|
|
2722
|
-
import
|
|
2763
|
+
import path20 from "path";
|
|
2723
2764
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2724
2765
|
import { z as z7 } from "zod";
|
|
2725
2766
|
function recheckable(finding) {
|
|
@@ -2754,8 +2795,8 @@ async function readStableAudit(root, checks, options) {
|
|
|
2754
2795
|
}
|
|
2755
2796
|
return report;
|
|
2756
2797
|
}
|
|
2757
|
-
async function withRepairSession(root, options, run, cachedAudit) {
|
|
2758
|
-
const
|
|
2798
|
+
async function withRepairSession(root, options, run, validate, cachedAudit) {
|
|
2799
|
+
const head = await getCurrentGitHash(root);
|
|
2759
2800
|
const audits = /* @__PURE__ */ new Map();
|
|
2760
2801
|
const documents = /* @__PURE__ */ new Map();
|
|
2761
2802
|
const history = /* @__PURE__ */ new Map();
|
|
@@ -2766,9 +2807,9 @@ async function withRepairSession(root, options, run, cachedAudit) {
|
|
|
2766
2807
|
};
|
|
2767
2808
|
const session = {
|
|
2768
2809
|
audit: (checks) => memo(audits, [...checks].sort().join(","), async () => {
|
|
2769
|
-
if (cachedAudit && cachedAudit.headHash ===
|
|
2810
|
+
if (cachedAudit && cachedAudit.headHash === head && cachedAudit.root === root && digest([...checks].sort()) === digest([...cachedAudit.checksRun ?? []].sort())) {
|
|
2770
2811
|
const current = structuredClone(cachedAudit);
|
|
2771
|
-
const reviews = (await Promise.all(allFindings(current).map((f) => assessAdvisory(root, f,
|
|
2812
|
+
const reviews = (await Promise.all(allFindings(current).map((f) => assessAdvisory(root, f, head)))).filter((r) => r !== null);
|
|
2772
2813
|
delete current.advisoryReviews;
|
|
2773
2814
|
if (reviews.length) current.advisoryReviews = reviews;
|
|
2774
2815
|
return current;
|
|
@@ -2776,16 +2817,15 @@ async function withRepairSession(root, options, run, cachedAudit) {
|
|
|
2776
2817
|
return computeAudit(root, { ...options, checks });
|
|
2777
2818
|
}),
|
|
2778
2819
|
document: (file) => memo(documents, file, async () => readBoundedFile(await storePath(root, file), 10 * 1024 * 1024)),
|
|
2779
|
-
history: (
|
|
2780
|
-
assessment: (dir, finding,
|
|
2820
|
+
history: (head2) => memo(history, head2, () => getChangesWithStatus(root, head2)),
|
|
2821
|
+
assessment: (dir, finding, head2) => memo(assessments, digest([dir, finding, head2]), () => assessAdvisory(dir, finding, head2))
|
|
2781
2822
|
};
|
|
2782
2823
|
const result = await run({
|
|
2783
2824
|
prepare: () => prepareOriginal(root, ALL_CHECKS, options, session),
|
|
2784
2825
|
verify: (baseline) => profilePhase("repair.verify", () => verifyOriginal(root, baseline, options, session)),
|
|
2785
2826
|
currentAudit: () => session.audit(ALL_CHECKS)
|
|
2786
2827
|
});
|
|
2787
|
-
|
|
2788
|
-
if (digest(before) !== digest(after)) throw new Error("HEAD, context files or advisory review records changed during verification; retry against stable evidence.");
|
|
2828
|
+
await validate();
|
|
2789
2829
|
return result;
|
|
2790
2830
|
}
|
|
2791
2831
|
async function prepareOriginal(rootDir, checks, options, session) {
|
|
@@ -2807,8 +2847,8 @@ async function prepareOriginal(rootDir, checks, options, session) {
|
|
|
2807
2847
|
}
|
|
2808
2848
|
async function loadRepairBaseline(rootDir, baselinePath) {
|
|
2809
2849
|
const root = await fs12.realpath(rootDir);
|
|
2810
|
-
const declaredRoot =
|
|
2811
|
-
const relative =
|
|
2850
|
+
const declaredRoot = path20.resolve(rootDir);
|
|
2851
|
+
const relative = path20.isAbsolute(baselinePath) ? path20.relative(isWithinRoot(declaredRoot, baselinePath) ? declaredRoot : root, baselinePath) : baselinePath;
|
|
2812
2852
|
const stored = baselineSchema.parse(await readStoreJson(root, relative));
|
|
2813
2853
|
const { digest: savedDigest, ...payload } = stored;
|
|
2814
2854
|
if (digest(payload) !== savedDigest) throw new Error("Repair baseline was modified; use the original baseline.");
|
|
@@ -2967,14 +3007,12 @@ var init_repair = __esm({
|
|
|
2967
3007
|
|
|
2968
3008
|
// src/automation/evidence.ts
|
|
2969
3009
|
import fs13 from "fs/promises";
|
|
2970
|
-
import
|
|
3010
|
+
import path21 from "path";
|
|
2971
3011
|
import { createHash as createHash3 } from "crypto";
|
|
2972
|
-
import { execFile as execFile9 } from "child_process";
|
|
2973
|
-
import { promisify as promisify9 } from "util";
|
|
2974
3012
|
import fg5 from "fast-glob";
|
|
2975
3013
|
import { z as z8 } from "zod";
|
|
2976
3014
|
async function git3(root, ...args) {
|
|
2977
|
-
return profilePhase("automation.git", async () => (await
|
|
3015
|
+
return profilePhase("automation.git", async () => (await execGit(args, { cwd: root, maxBuffer: 16 * 1024 * 1024, timeout: 1e4 })).stdout);
|
|
2978
3016
|
}
|
|
2979
3017
|
async function workspace(dir) {
|
|
2980
3018
|
const root = await fs13.realpath((await git3(dir, "rev-parse", "--show-toplevel")).trim());
|
|
@@ -2998,7 +3036,7 @@ async function collectInputs(root) {
|
|
|
2998
3036
|
const head = headText.trim();
|
|
2999
3037
|
let shallow = null;
|
|
3000
3038
|
try {
|
|
3001
|
-
shallow = await fs13.readFile(
|
|
3039
|
+
shallow = await fs13.readFile(path21.resolve(root, shallowPath.trim()), "utf8");
|
|
3002
3040
|
} catch (error) {
|
|
3003
3041
|
if (error.code !== "ENOENT") throw error;
|
|
3004
3042
|
}
|
|
@@ -3022,7 +3060,7 @@ async function collectInputs(root) {
|
|
|
3022
3060
|
if (scope?.candidates.some(gitMetadataPath)) continue;
|
|
3023
3061
|
for (const candidate of scope?.candidates ?? []) {
|
|
3024
3062
|
if (optionalMasonPath(candidate)) continue;
|
|
3025
|
-
const [filePresent, parentPresent] = await Promise.all([exists(candidate), exists(
|
|
3063
|
+
const [filePresent, parentPresent] = await Promise.all([exists(candidate), exists(path21.posix.dirname(candidate))]);
|
|
3026
3064
|
claims.push([candidate, filePresent, parentPresent]);
|
|
3027
3065
|
}
|
|
3028
3066
|
}
|
|
@@ -3034,7 +3072,7 @@ async function collectInputs(root) {
|
|
|
3034
3072
|
if (error.code === "ENOENT") return "absent";
|
|
3035
3073
|
throw error;
|
|
3036
3074
|
});
|
|
3037
|
-
const [modules, counts,
|
|
3075
|
+
const [modules, counts, commands2, decisionFiles] = await Promise.all([
|
|
3038
3076
|
combinedDocs ? moduleCandidates(root, combinedDocs) : [],
|
|
3039
3077
|
Promise.all(countClaims.map(({ doc, claim }) => resolveDocCountSource(root, doc, claim))),
|
|
3040
3078
|
commandInputs(root, parsedDocs),
|
|
@@ -3053,7 +3091,7 @@ async function collectInputs(root) {
|
|
|
3053
3091
|
"deleted-reference": hash([common, claims, docStatus]),
|
|
3054
3092
|
"new-module": hash([common, modules]),
|
|
3055
3093
|
"stale-count": hash([common, counts]),
|
|
3056
|
-
"dead-command": hash([common,
|
|
3094
|
+
"dead-command": hash([common, commands2]),
|
|
3057
3095
|
"deps-changed": hash([common, docStatus]),
|
|
3058
3096
|
"decision-anchor-drift": hash([common, decisionPresence, decisions, status, index2])
|
|
3059
3097
|
};
|
|
@@ -3091,10 +3129,11 @@ function checkCache(raw, inputs) {
|
|
|
3091
3129
|
}
|
|
3092
3130
|
};
|
|
3093
3131
|
}
|
|
3094
|
-
var
|
|
3132
|
+
var engineVersion, hash, content, cacheSchema;
|
|
3095
3133
|
var init_evidence = __esm({
|
|
3096
3134
|
"src/automation/evidence.ts"() {
|
|
3097
3135
|
"use strict";
|
|
3136
|
+
init_git_read();
|
|
3098
3137
|
init_profile();
|
|
3099
3138
|
init_docs();
|
|
3100
3139
|
init_scope();
|
|
@@ -3107,8 +3146,7 @@ var init_evidence = __esm({
|
|
|
3107
3146
|
init_dead_command();
|
|
3108
3147
|
init_storage();
|
|
3109
3148
|
init_advisory_review();
|
|
3110
|
-
|
|
3111
|
-
engineVersion = true ? "0.17.3" : "development";
|
|
3149
|
+
engineVersion = true ? "0.17.4" : "development";
|
|
3112
3150
|
hash = (value) => createHash3("sha256").update(JSON.stringify(value)).digest("hex");
|
|
3113
3151
|
content = readAuditInput;
|
|
3114
3152
|
cacheSchema = z8.object({ version: z8.literal(1), entries: z8.record(z8.object({ key: z8.string(), result: checkResultSchema })), digest: z8.string() });
|
|
@@ -3376,12 +3414,12 @@ async function matchingAnalysis(ws, state, inputs) {
|
|
|
3376
3414
|
return { analysis: a };
|
|
3377
3415
|
}
|
|
3378
3416
|
async function verifyInputs(ws, inputs) {
|
|
3379
|
-
return profilePhase("automation.revalidate", async () => {
|
|
3417
|
+
return profilePhase("automation.revalidate", () => withRepositoryInspection(ws.root, async () => {
|
|
3380
3418
|
const [after, currentWs] = await Promise.all([readInputs(ws.root), workspace(ws.root)]);
|
|
3381
3419
|
if (after.fingerprint !== inputs.fingerprint || currentWs.directory !== ws.directory) {
|
|
3382
3420
|
throw new Error("Repository inputs or branch changed during automation; no current verification was recorded. Retry on a stable checkout.");
|
|
3383
3421
|
}
|
|
3384
|
-
});
|
|
3422
|
+
}));
|
|
3385
3423
|
}
|
|
3386
3424
|
async function computeAnalysis(ws, state, inputs, event, previousAudit, cacheDiagnostic) {
|
|
3387
3425
|
const baselines = [...state.baselines];
|
|
@@ -3401,7 +3439,10 @@ async function computeAnalysis(ws, state, inputs, event, previousAudit, cacheDia
|
|
|
3401
3439
|
capture: "unknown",
|
|
3402
3440
|
scope: SCOPE
|
|
3403
3441
|
};
|
|
3404
|
-
if (!baselines.length && Object.values(inputs.docs).every((value) => value === null))
|
|
3442
|
+
if (!baselines.length && Object.values(inputs.docs).every((value) => value === null)) {
|
|
3443
|
+
await verifyInputs(ws, inputs);
|
|
3444
|
+
return { baselines, report: empty, cache: null };
|
|
3445
|
+
}
|
|
3405
3446
|
if (ws.branch === "detached" && state.latest) {
|
|
3406
3447
|
const previous = await readStoreJson(ws.root, state.latest);
|
|
3407
3448
|
if (!previous?.head || !/^[a-f0-9]{40,64}$/.test(previous.head)) throw new Error("The previous detached checkout evidence is unavailable.");
|
|
@@ -3439,7 +3480,7 @@ async function computeAnalysis(ws, state, inputs, event, previousAudit, cacheDia
|
|
|
3439
3480
|
}
|
|
3440
3481
|
currentAudit = await repairs.currentAudit();
|
|
3441
3482
|
return verifications2;
|
|
3442
|
-
}, cachedAudit);
|
|
3483
|
+
}, () => verifyInputs(ws, inputs), cachedAudit);
|
|
3443
3484
|
const merged = /* @__PURE__ */ new Map();
|
|
3444
3485
|
for (const verification of verifications) {
|
|
3445
3486
|
for (const finding of verification.findings) {
|
|
@@ -3474,7 +3515,6 @@ async function analyze(ws, inputs, event, startedAt) {
|
|
|
3474
3515
|
if (previous && previous.completedAt >= startedAt) return { analysis: previous, shared: true };
|
|
3475
3516
|
const originalDigest = await baselineDigest(ws, before);
|
|
3476
3517
|
const computed = await computeAnalysis(ws, before, inputs, event, previous?.currentAudit, diagnostic);
|
|
3477
|
-
await verifyInputs(ws, inputs);
|
|
3478
3518
|
if (originalDigest !== await baselineDigest(ws, before)) throw new Error("Repair baseline was modified during automation; original evidence must be inspected.");
|
|
3479
3519
|
const id = randomUUID4();
|
|
3480
3520
|
const analysis = analysisSchema.parse({
|
|
@@ -3503,7 +3543,7 @@ async function analyze(ws, inputs, event, startedAt) {
|
|
|
3503
3543
|
async function automate(dir, event) {
|
|
3504
3544
|
const startedAt = Date.now();
|
|
3505
3545
|
const ws = await profilePhase("automation.workspace", () => workspace(dir));
|
|
3506
|
-
return recordExecution(ws.root, ws.directory, event.event, async () => {
|
|
3546
|
+
return recordExecution(ws.root, ws.directory, event.event, () => withRepositoryInspection(ws.root, async () => {
|
|
3507
3547
|
const inputs = await readInputs(ws.root);
|
|
3508
3548
|
const result = await profilePhase("automation.analysis", () => analyze(ws, inputs, event, startedAt));
|
|
3509
3549
|
if (result.shared) {
|
|
@@ -3576,7 +3616,7 @@ async function automate(dir, event) {
|
|
|
3576
3616
|
await writeStoreJson(ws.root, ws.directory + "/state.json", state);
|
|
3577
3617
|
return { report, message: notify ? summarize(report) : null, continueOnce };
|
|
3578
3618
|
}));
|
|
3579
|
-
});
|
|
3619
|
+
}));
|
|
3580
3620
|
}
|
|
3581
3621
|
async function automationStatus(dir) {
|
|
3582
3622
|
const ws = await profilePhase("automation.workspace", () => workspace(dir));
|
|
@@ -3614,6 +3654,7 @@ var init_runtime = __esm({
|
|
|
3614
3654
|
"src/automation/runtime.ts"() {
|
|
3615
3655
|
"use strict";
|
|
3616
3656
|
init_profile();
|
|
3657
|
+
init_inspection();
|
|
3617
3658
|
init_execution();
|
|
3618
3659
|
init_repair();
|
|
3619
3660
|
init_types();
|
|
@@ -3659,9 +3700,9 @@ var init_runtime = __esm({
|
|
|
3659
3700
|
|
|
3660
3701
|
// src/automation/hook-root.ts
|
|
3661
3702
|
import fs15 from "fs/promises";
|
|
3662
|
-
import
|
|
3703
|
+
import path22 from "path";
|
|
3663
3704
|
async function repositoryRoot(dir) {
|
|
3664
|
-
if (!
|
|
3705
|
+
if (!path22.isAbsolute(dir)) throw new Error("Hook input working directory must be absolute.");
|
|
3665
3706
|
try {
|
|
3666
3707
|
return await fs15.realpath((await git3(dir, "rev-parse", "--show-toplevel")).trim());
|
|
3667
3708
|
} catch (error) {
|
|
@@ -3745,8 +3786,8 @@ __export(launcher_exports, {
|
|
|
3745
3786
|
mcpCommand: () => mcpCommand,
|
|
3746
3787
|
prepareLaunch: () => prepareLaunch
|
|
3747
3788
|
});
|
|
3748
|
-
import { execFile as
|
|
3749
|
-
import { promisify as
|
|
3789
|
+
import { execFile as execFile2 } from "child_process";
|
|
3790
|
+
import { promisify as promisify2 } from "util";
|
|
3750
3791
|
import fs16 from "fs/promises";
|
|
3751
3792
|
function globalCommand(args, platform = process.platform) {
|
|
3752
3793
|
return platform === "win32" ? { command: "cmd.exe", args: ["/d", "/s", "/c", "mason", ...args] } : { command: "mason", args };
|
|
@@ -3754,7 +3795,7 @@ function globalCommand(args, platform = process.platform) {
|
|
|
3754
3795
|
async function installedCommand() {
|
|
3755
3796
|
const invocation = globalCommand(["internal-integration-version"]);
|
|
3756
3797
|
try {
|
|
3757
|
-
const { stdout } = await
|
|
3798
|
+
const { stdout } = await promisify2(execFile2)(
|
|
3758
3799
|
invocation.command,
|
|
3759
3800
|
invocation.args,
|
|
3760
3801
|
{ timeout: 5e3, maxBuffer: 8192, windowsHide: true }
|
|
@@ -3790,7 +3831,7 @@ var init_launcher = __esm({
|
|
|
3790
3831
|
"use strict";
|
|
3791
3832
|
init_evidence();
|
|
3792
3833
|
init_model();
|
|
3793
|
-
executingVersion = () => true ? "0.17.
|
|
3834
|
+
executingVersion = () => true ? "0.17.4" : "development";
|
|
3794
3835
|
mcpCommand = (host) => globalCommand(["--setup-host", host, "mcp"]);
|
|
3795
3836
|
hookCommand = (host) => `mason --setup-host ${host} auto`;
|
|
3796
3837
|
}
|
|
@@ -4006,7 +4047,7 @@ __export(files_exports, {
|
|
|
4006
4047
|
readText: () => readText
|
|
4007
4048
|
});
|
|
4008
4049
|
import fs18 from "fs/promises";
|
|
4009
|
-
import
|
|
4050
|
+
import path23 from "path";
|
|
4010
4051
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
4011
4052
|
async function readText(root, file) {
|
|
4012
4053
|
try {
|
|
@@ -4039,7 +4080,7 @@ async function applyEdit(root, edit) {
|
|
|
4039
4080
|
return true;
|
|
4040
4081
|
}
|
|
4041
4082
|
const file = await storePath(root, edit.path, true);
|
|
4042
|
-
const temporary =
|
|
4083
|
+
const temporary = path23.join(path23.dirname(file), ".mason-setup-" + randomUUID5() + ".tmp");
|
|
4043
4084
|
try {
|
|
4044
4085
|
const mode = await fs18.stat(file).then((s) => s.mode & 511, () => 384);
|
|
4045
4086
|
const handle = await fs18.open(temporary, "wx", mode);
|
|
@@ -4484,14 +4525,14 @@ async function planTeardown(root, selected) {
|
|
|
4484
4525
|
const setup = setupBefore === null ? null : setupSchema.parse(JSON.parse(setupBefore));
|
|
4485
4526
|
const automationBefore = await read(AUTOMATION_PATH);
|
|
4486
4527
|
const automation = automationBefore === null ? null : recordSchema2.parse(JSON.parse(automationBefore));
|
|
4487
|
-
const
|
|
4528
|
+
const commands2 = (host) => new Set([
|
|
4488
4529
|
automation?.hosts[host]?.command,
|
|
4489
4530
|
...ownership.files[configPath(host)]?.hookCommands ?? [],
|
|
4490
4531
|
...knownManagedHookCommands(host),
|
|
4491
4532
|
hookConfig(host, hookCommand(host)).hooks.SessionStart[0].hooks[0].command,
|
|
4492
4533
|
hookConfig(host).hooks.SessionStart[0].hooks[0].command
|
|
4493
4534
|
].filter((command) => !!command));
|
|
4494
|
-
const possibleHook = (command, host) =>
|
|
4535
|
+
const possibleHook = (command, host) => commands2(host).has(command) || /\bmason(?:-auto|-hook)?\b/.test(command);
|
|
4495
4536
|
const add = (file, before, after, empty) => {
|
|
4496
4537
|
edits.push({ path: file, before, after: ownership.files[file]?.created && (empty || ownership.files[file].initialHash === hash(before)) ? null : after });
|
|
4497
4538
|
};
|
|
@@ -4555,12 +4596,12 @@ async function planTeardown(root, selected) {
|
|
|
4555
4596
|
for (const [event, groups] of Object.entries(config.hooks ?? {})) {
|
|
4556
4597
|
const known = ownership.files[hookFile]?.hooks?.[event];
|
|
4557
4598
|
const handlers = groups.flatMap((group) => group.hooks);
|
|
4558
|
-
if (known && !handlers.some((handler) => handler.type === "command" && handler.command &&
|
|
4599
|
+
if (known && !handlers.some((handler) => handler.type === "command" && handler.command && commands2(host).has(handler.command)) && handlers.some((handler) => !known.includes(hash(handler)))) {
|
|
4559
4600
|
diagnostics.push({ path: hookFile, message: "A recorded Mason hook is missing beside a new or edited handler (" + event + "); inspect ownership manually." });
|
|
4560
4601
|
}
|
|
4561
4602
|
const kept = groups.flatMap((group) => {
|
|
4562
4603
|
const handlers2 = group.hooks.filter((handler) => {
|
|
4563
|
-
if (handler.type === "command" && handler.command &&
|
|
4604
|
+
if (handler.type === "command" && handler.command && commands2(host).has(handler.command)) {
|
|
4564
4605
|
changed = true;
|
|
4565
4606
|
return false;
|
|
4566
4607
|
}
|
|
@@ -4692,13 +4733,13 @@ var init_teardown = __esm({
|
|
|
4692
4733
|
});
|
|
4693
4734
|
|
|
4694
4735
|
// src/review/cochange.ts
|
|
4695
|
-
import { execFile as
|
|
4696
|
-
import { promisify as
|
|
4736
|
+
import { execFile as execFile3 } from "child_process";
|
|
4737
|
+
import { promisify as promisify3 } from "util";
|
|
4697
4738
|
async function buildMatrix(resolvedRoot) {
|
|
4698
4739
|
try {
|
|
4699
|
-
const { stdout: shallow } = await
|
|
4740
|
+
const { stdout: shallow } = await exec("git", ["rev-parse", "--is-shallow-repository"], { cwd: resolvedRoot });
|
|
4700
4741
|
if (shallow.trim() === "true") return null;
|
|
4701
|
-
const { stdout } = await
|
|
4742
|
+
const { stdout } = await exec(
|
|
4702
4743
|
"git",
|
|
4703
4744
|
["log", `-n${HISTORY_COMMITS}`, "--format=%x01", "--name-only", "-M"],
|
|
4704
4745
|
{ cwd: resolvedRoot, maxBuffer: 100 * 1024 * 1024 }
|
|
@@ -4754,11 +4795,11 @@ async function findMissingPartners(resolvedRoot, changedFiles, existsOnDisk, all
|
|
|
4754
4795
|
findings.sort((a, b) => b.rate - a.rate || b.sharedCommits - a.sharedCommits);
|
|
4755
4796
|
return findings;
|
|
4756
4797
|
}
|
|
4757
|
-
var
|
|
4798
|
+
var exec, HISTORY_COMMITS, MIN_FILE_COMMITS, MIN_SHARED_COMMITS, MIN_COCHANGE_RATE, MAX_COMMIT_FILES;
|
|
4758
4799
|
var init_cochange = __esm({
|
|
4759
4800
|
"src/review/cochange.ts"() {
|
|
4760
4801
|
"use strict";
|
|
4761
|
-
|
|
4802
|
+
exec = promisify3(execFile3);
|
|
4762
4803
|
HISTORY_COMMITS = 1500;
|
|
4763
4804
|
MIN_FILE_COMMITS = 5;
|
|
4764
4805
|
MIN_SHARED_COMMITS = 4;
|
|
@@ -5066,10 +5107,10 @@ var init_check = __esm({
|
|
|
5066
5107
|
});
|
|
5067
5108
|
|
|
5068
5109
|
// src/review/evidence.ts
|
|
5069
|
-
import
|
|
5110
|
+
import path24 from "path";
|
|
5070
5111
|
import { z as z22 } from "zod";
|
|
5071
5112
|
function relativeArtifact(root, file) {
|
|
5072
|
-
const relative =
|
|
5113
|
+
const relative = path24.isAbsolute(file) ? path24.relative(root, file) : file;
|
|
5073
5114
|
const normalized = normalizeRepoPath(relative);
|
|
5074
5115
|
if (!normalized) throw new Error(`Evidence artifact must be inside the repository: ${file}`);
|
|
5075
5116
|
return normalized;
|
|
@@ -5166,7 +5207,7 @@ async function collectReviewEvidence(root, manifests, changedFiles, decisions, d
|
|
|
5166
5207
|
const report = await readStoreJson(root, file);
|
|
5167
5208
|
if (report === null) throw new Error(`Report artifact is missing: ${file}`);
|
|
5168
5209
|
const sourceRoot = check.sourceRoot ?? root;
|
|
5169
|
-
if (!
|
|
5210
|
+
if (!path24.posix.isAbsolute(sourceRoot) && !/^[A-Za-z]:[\\/]/.test(sourceRoot)) throw new Error("sourceRoot must identify the absolute checkout root on the check runner.");
|
|
5170
5211
|
const parsed = check.report.format === "vitest-json" ? parseVitest(report, sourceRoot) : check.report.format === "mason-check-json" ? parseCheck(report, sourceRoot) : parseSarif(report, sourceRoot);
|
|
5171
5212
|
result.outcome = parsed.outcome;
|
|
5172
5213
|
result.counts = parsed.counts;
|
|
@@ -5288,12 +5329,12 @@ var init_evidence2 = __esm({
|
|
|
5288
5329
|
|
|
5289
5330
|
// src/review/review.ts
|
|
5290
5331
|
import fs19 from "fs/promises";
|
|
5291
|
-
import
|
|
5292
|
-
import { execFile as
|
|
5293
|
-
import { promisify as
|
|
5332
|
+
import path25 from "path";
|
|
5333
|
+
import { execFile as execFile4 } from "child_process";
|
|
5334
|
+
import { promisify as promisify4 } from "util";
|
|
5294
5335
|
async function resolveMergeBase(resolvedRoot, base, head) {
|
|
5295
5336
|
try {
|
|
5296
|
-
const { stdout } = await
|
|
5337
|
+
const { stdout } = await exec2("git", ["merge-base", base, head], {
|
|
5297
5338
|
cwd: resolvedRoot
|
|
5298
5339
|
});
|
|
5299
5340
|
return stdout.trim() || null;
|
|
@@ -5304,7 +5345,7 @@ async function resolveMergeBase(resolvedRoot, base, head) {
|
|
|
5304
5345
|
async function defaultBase(resolvedRoot) {
|
|
5305
5346
|
for (const ref of ["origin/HEAD", "origin/main", "origin/master", "main"]) {
|
|
5306
5347
|
try {
|
|
5307
|
-
await
|
|
5348
|
+
await exec2("git", ["rev-parse", "--verify", "--quiet", ref], {
|
|
5308
5349
|
cwd: resolvedRoot
|
|
5309
5350
|
});
|
|
5310
5351
|
return ref;
|
|
@@ -5317,7 +5358,7 @@ function anchorsTouched(record, changedFiles) {
|
|
|
5317
5358
|
return matchingPaths(record.files, changedFiles);
|
|
5318
5359
|
}
|
|
5319
5360
|
async function computeReview(rootDir, base, options = {}) {
|
|
5320
|
-
const resolvedRoot =
|
|
5361
|
+
const resolvedRoot = path25.resolve(rootDir);
|
|
5321
5362
|
const head = await getCurrentGitHash(resolvedRoot);
|
|
5322
5363
|
if (head === "unknown") return null;
|
|
5323
5364
|
const mergeBase = await resolveMergeBase(resolvedRoot, base, head);
|
|
@@ -5367,7 +5408,7 @@ async function computeReview(rootDir, base, options = {}) {
|
|
|
5367
5408
|
analyzed,
|
|
5368
5409
|
async (relPath) => {
|
|
5369
5410
|
try {
|
|
5370
|
-
await fs19.access(
|
|
5411
|
+
await fs19.access(path25.join(resolvedRoot, relPath));
|
|
5371
5412
|
return true;
|
|
5372
5413
|
} catch {
|
|
5373
5414
|
return false;
|
|
@@ -5400,7 +5441,7 @@ async function computeReview(rootDir, base, options = {}) {
|
|
|
5400
5441
|
}
|
|
5401
5442
|
return finalize();
|
|
5402
5443
|
}
|
|
5403
|
-
var
|
|
5444
|
+
var exec2, MAX_ANALYZED_FILES;
|
|
5404
5445
|
var init_review = __esm({
|
|
5405
5446
|
"src/review/review.ts"() {
|
|
5406
5447
|
"use strict";
|
|
@@ -5412,7 +5453,7 @@ var init_review = __esm({
|
|
|
5412
5453
|
init_cochange();
|
|
5413
5454
|
init_evidence2();
|
|
5414
5455
|
init_snapshot();
|
|
5415
|
-
|
|
5456
|
+
exec2 = promisify4(execFile4);
|
|
5416
5457
|
MAX_ANALYZED_FILES = 50;
|
|
5417
5458
|
}
|
|
5418
5459
|
});
|