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.
@@ -31,6 +31,32 @@ var init_profile = __esm({
31
31
  }
32
32
  });
33
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
+
34
60
  // src/utils/paths.ts
35
61
  import path from "path";
36
62
  function normalizeRepoPath(value) {
@@ -62,8 +88,6 @@ var init_paths = __esm({
62
88
  import fs from "fs/promises";
63
89
  import { constants } from "fs";
64
90
  import path2 from "path";
65
- import { execFile } from "child_process";
66
- import { promisify } from "util";
67
91
  import fg from "fast-glob";
68
92
  async function readBoundedFile(file, maxBytes) {
69
93
  const handle = await fs.open(file, constants.O_RDONLY | constants.O_NONBLOCK | constants.O_NOFOLLOW);
@@ -105,12 +129,12 @@ async function loadProjectConfig(root) {
105
129
  throw new Error(`Cannot apply project file policy: ${error instanceof Error ? error.message : String(error)}`);
106
130
  }
107
131
  }
108
- var exec, SOURCE_EXTENSIONS, SOURCE_GLOB, SOURCE_IGNORE, MAX_SOURCE_BYTES;
132
+ var SOURCE_EXTENSIONS, SOURCE_GLOB, SOURCE_IGNORE, MAX_SOURCE_BYTES;
109
133
  var init_files = __esm({
110
134
  "src/utils/files.ts"() {
111
135
  "use strict";
136
+ init_git_read();
112
137
  init_paths();
113
- exec = promisify(execFile);
114
138
  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"];
115
139
  SOURCE_GLOB = `**/*.{${SOURCE_EXTENSIONS.join(",")}}`;
116
140
  SOURCE_IGNORE = [
@@ -221,12 +245,10 @@ var init_test_map = __esm({
221
245
 
222
246
  // src/snapshot/snapshot.ts
223
247
  import path5 from "path";
224
- import { execFile as execFile2 } from "child_process";
225
- import { promisify as promisify2 } from "util";
226
248
  import { z } from "zod";
227
249
  async function getCurrentGitHash(rootDir) {
228
250
  try {
229
- const { stdout } = await exec2("git", ["rev-parse", "HEAD"], {
251
+ const { stdout } = await execGit(["rev-parse", "HEAD"], {
230
252
  cwd: rootDir
231
253
  });
232
254
  return stdout.trim();
@@ -234,16 +256,16 @@ async function getCurrentGitHash(rootDir) {
234
256
  return "unknown";
235
257
  }
236
258
  }
237
- var exec2, repoPath, verificationFields, featureSchema, flowSchema, snapshotSchema;
259
+ var repoPath, verificationFields, featureSchema, flowSchema, snapshotSchema;
238
260
  var init_snapshot = __esm({
239
261
  "src/snapshot/snapshot.ts"() {
240
262
  "use strict";
263
+ init_git_read();
241
264
  init_files();
242
265
  init_files();
243
266
  init_storage();
244
267
  init_paths();
245
268
  init_test_map();
246
- exec2 = promisify2(execFile2);
247
269
  repoPath = z.string().refine((value) => normalizeRepoPath(value) !== null, "Expected a relative repository path");
248
270
  verificationFields = {
249
271
  refreshedHash: z.string().optional(),
@@ -274,8 +296,6 @@ var init_snapshot = __esm({
274
296
  // src/drift/drift.ts
275
297
  import fs3 from "fs/promises";
276
298
  import path6 from "path";
277
- import { execFile as execFile3 } from "child_process";
278
- import { promisify as promisify3 } from "util";
279
299
  function parseChanges(output) {
280
300
  const fields = output.split("\0");
281
301
  const changes = [];
@@ -296,7 +316,7 @@ function touchedPaths(changes) {
296
316
  async function getChangesWithStatus(resolvedRoot, fromHash, toHash = "HEAD") {
297
317
  if (!fromHash || fromHash === "unknown" || fromHash.startsWith("-") || !toHash || toHash === "unknown" || toHash.startsWith("-")) return null;
298
318
  try {
299
- const { stdout } = await exec3("git", ["diff", "--name-status", "-z", "-M", fromHash, toHash, "--"], { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 });
319
+ const { stdout } = await execGit(["diff", "--name-status", "-z", "-M", fromHash, toHash, "--"], { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 });
300
320
  return parseChanges(stdout);
301
321
  } catch {
302
322
  return null;
@@ -305,8 +325,8 @@ async function getChangesWithStatus(resolvedRoot, fromHash, toHash = "HEAD") {
305
325
  async function getWorkingTree(resolvedRoot) {
306
326
  try {
307
327
  const [diff, untracked] = await Promise.all([
308
- exec3("git", ["diff", "--name-status", "-z", "-M", "HEAD", "--"], { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 }),
309
- exec3("git", ["ls-files", "-z", "--others", "--exclude-standard"], { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 })
328
+ execGit(["diff", "--name-status", "-z", "-M", "HEAD", "--"], { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 }),
329
+ execGit(["ls-files", "-z", "--others", "--exclude-standard"], { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 })
310
330
  ]);
311
331
  const untrackedFiles = untracked.stdout.split("\0").filter((f) => f && !f.startsWith(".mason/"));
312
332
  return { available: true, changedFiles: [.../* @__PURE__ */ new Set([...touchedPaths(parseChanges(diff.stdout)), ...untrackedFiles])].sort(), untrackedFiles };
@@ -314,13 +334,12 @@ async function getWorkingTree(resolvedRoot) {
314
334
  return { available: false, changedFiles: [], untrackedFiles: [] };
315
335
  }
316
336
  }
317
- var exec3;
318
337
  var init_drift = __esm({
319
338
  "src/drift/drift.ts"() {
320
339
  "use strict";
340
+ init_git_read();
321
341
  init_snapshot();
322
342
  init_paths();
323
- exec3 = promisify3(execFile3);
324
343
  }
325
344
  });
326
345
 
@@ -548,14 +567,14 @@ function extractClaims(content) {
548
567
  const ignored = computeIgnoredLines(lines);
549
568
  const paths = /* @__PURE__ */ new Map();
550
569
  const counts = [];
551
- const commands = /* @__PURE__ */ new Map();
570
+ const commands2 = /* @__PURE__ */ new Map();
552
571
  const addPath = (claim) => {
553
572
  const key = [claim.relativeTo ?? "scope", claim.path].join(":");
554
573
  if (!paths.has(key)) paths.set(key, claim);
555
574
  };
556
575
  const addCommand = (claim) => {
557
576
  const key = JSON.stringify([claim.scriptName, claim.directory, claim.scopeUnknown]);
558
- if (!commands.has(key)) commands.set(key, claim);
577
+ if (!commands2.has(key)) commands2.set(key, claim);
559
578
  };
560
579
  const parsed = markdown.parse(lines.map((line, i) => ignored[i] ? "" : line).join("\n"), {});
561
580
  for (const token of parsed) {
@@ -657,7 +676,7 @@ function extractClaims(content) {
657
676
  return {
658
677
  paths: [...paths.values()],
659
678
  counts,
660
- commands: [...commands.values()]
679
+ commands: [...commands2.values()]
661
680
  };
662
681
  }
663
682
  var ROOT_FILE_NAMES, SHELL_FENCE_INFOS, markdown, COUNT_RE, COUNT_DENYLIST_RE, IGNORE_LINE, IGNORE_START, IGNORE_END;
@@ -707,9 +726,50 @@ var init_claims = __esm({
707
726
  }
708
727
  });
709
728
 
729
+ // src/audit/inspection.ts
730
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
731
+ import path8 from "path";
732
+ function inspectionRead(root, key, read) {
733
+ const inspection = inspections.getStore();
734
+ if (!inspection || inspection.root !== path8.resolve(root)) return read();
735
+ if (!inspection.reads.has(key)) {
736
+ const pending = read();
737
+ inspection.reads.set(key, pending);
738
+ void pending.catch(() => {
739
+ if (inspection.reads.get(key) === pending) inspection.reads.delete(key);
740
+ });
741
+ }
742
+ return inspection.reads.get(key);
743
+ }
744
+ function inspectionGit(args, options) {
745
+ return inspectionRead(options.cwd, "git:" + JSON.stringify([args, options]), () => execGit(args, options));
746
+ }
747
+ async function auditGitPaths(root, kind) {
748
+ const shared = inspections.getStore()?.root === path8.resolve(root);
749
+ const specs = shared ? [...docSpecs, ...sourceSpecs] : kind === "documents" ? docSpecs : sourceSpecs;
750
+ const limit = kind === "documents" ? 16 * 1024 * 1024 : 32 * 1024 * 1024;
751
+ const { stdout } = await inspectionGit(
752
+ ["ls-files", "-z", "--cached", "--others", "--exclude-standard", "--", ...specs],
753
+ { cwd: root, maxBuffer: shared ? 48 * 1024 * 1024 : limit, timeout: 1e4 }
754
+ );
755
+ const paths = [...new Set(stdout.split("\0").filter(Boolean))].filter((file) => kind === "documents" ? /^(readme|agents|claude)\.md$/i.test(path8.posix.basename(file)) : sourceSuffixes.has(file.slice(file.lastIndexOf("."))));
756
+ if (Buffer.byteLength(paths.join("\0")) > limit) throw new Error("Git " + kind + " inventory exceeds its bounded read limit.");
757
+ return paths.sort();
758
+ }
759
+ var inspections, sourceSpecs, docSpecs, sourceSuffixes;
760
+ var init_inspection = __esm({
761
+ "src/audit/inspection.ts"() {
762
+ "use strict";
763
+ init_git_read();
764
+ init_files();
765
+ inspections = new AsyncLocalStorage2();
766
+ sourceSpecs = SOURCE_EXTENSIONS.map((extension) => `:(glob)**/*.${extension}`);
767
+ docSpecs = [":(icase,glob)**/readme.md", ":(icase,glob)**/agents.md", ":(icase,glob)**/claude.md"];
768
+ sourceSuffixes = new Set(SOURCE_EXTENSIONS.map((extension) => "." + extension));
769
+ }
770
+ });
771
+
710
772
  // src/audit/git.ts
711
- import { execFile as execFile4 } from "child_process";
712
- import { promisify as promisify4 } from "util";
713
773
  function parseCommitLine(line) {
714
774
  const parts = line.split(" ");
715
775
  if (parts.length < 3 || !parts[0]) return null;
@@ -717,8 +777,7 @@ function parseCommitLine(line) {
717
777
  }
718
778
  async function lastCommitOf(resolvedRoot, relPath) {
719
779
  try {
720
- const { stdout } = await exec4(
721
- "git",
780
+ const { stdout } = await inspectionGit(
722
781
  ["log", "-1", `--format=${COMMIT_FORMAT}`, "--", `:(literal)${relPath}`],
723
782
  { cwd: resolvedRoot }
724
783
  );
@@ -730,8 +789,7 @@ async function lastCommitOf(resolvedRoot, relPath) {
730
789
  }
731
790
  async function deletingCommitOf(resolvedRoot, relPath) {
732
791
  try {
733
- const { stdout } = await exec4(
734
- "git",
792
+ const { stdout } = await inspectionGit(
735
793
  [
736
794
  "log",
737
795
  "-1",
@@ -750,8 +808,7 @@ async function deletingCommitOf(resolvedRoot, relPath) {
750
808
  }
751
809
  async function firstCommitOf(resolvedRoot, relPath) {
752
810
  try {
753
- const { stdout } = await exec4(
754
- "git",
811
+ const { stdout } = await inspectionGit(
755
812
  ["log", "--reverse", `--format=${COMMIT_FORMAT}`, "--", `:(literal)${relPath}`],
756
813
  { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 }
757
814
  );
@@ -764,8 +821,7 @@ async function firstCommitOf(resolvedRoot, relPath) {
764
821
  async function commitsTouchingSince(resolvedRoot, fromHash, pathspecs) {
765
822
  if (!fromHash || fromHash === "unknown") return null;
766
823
  try {
767
- const { stdout } = await exec4(
768
- "git",
824
+ const { stdout } = await inspectionGit(
769
825
  [
770
826
  "log",
771
827
  `${fromHash}..HEAD`,
@@ -789,11 +845,11 @@ async function commitsTouchingSince(resolvedRoot, fromHash, pathspecs) {
789
845
  return null;
790
846
  }
791
847
  }
792
- var exec4, COMMIT_FORMAT;
848
+ var COMMIT_FORMAT;
793
849
  var init_git = __esm({
794
850
  "src/audit/git.ts"() {
795
851
  "use strict";
796
- exec4 = promisify4(execFile4);
852
+ init_inspection();
797
853
  COMMIT_FORMAT = "%H%x09%cI%x09%s";
798
854
  }
799
855
  });
@@ -801,9 +857,7 @@ var init_git = __esm({
801
857
  // src/audit/inputs.ts
802
858
  import fs4 from "fs/promises";
803
859
  import * as nativeFs from "fs";
804
- import path8 from "path";
805
- import { execFile as execFile5 } from "child_process";
806
- import { promisify as promisify5 } from "util";
860
+ import path9 from "path";
807
861
  import fg2 from "fast-glob";
808
862
  async function auditInputPath(root, file) {
809
863
  if (file === ".") return fs4.realpath(root);
@@ -830,7 +884,7 @@ async function auditGlob(root, patterns, options) {
830
884
  if (Array.isArray(patterns) && !patterns.length) return [];
831
885
  const directories = /* @__PURE__ */ new Set();
832
886
  const readdir2 = ((directory, opts, callback) => {
833
- const relative = path8.relative(root, directory).split(path8.sep).join("/") || ".";
887
+ const relative = path9.relative(root, directory).split(path9.sep).join("/") || ".";
834
888
  directories.add(relative);
835
889
  if (directories.size > MAX_INPUTS) {
836
890
  callback(new Error(`${options.label} exceeds ${MAX_INPUTS.toLocaleString("en-US")} directories while inspecting ${relative}. Narrow this check's inputs.`));
@@ -841,7 +895,7 @@ async function auditGlob(root, patterns, options) {
841
895
  }, callback);
842
896
  });
843
897
  const lstat2 = ((file, callback) => {
844
- const relative = path8.relative(root, file).split(path8.sep).join("/") || ".";
898
+ const relative = path9.relative(root, file).split(path9.sep).join("/") || ".";
845
899
  auditInputPath(root, relative).then(() => nativeFs.lstat(file, callback), callback);
846
900
  });
847
901
  const stream = fg2.stream(patterns, {
@@ -878,62 +932,48 @@ async function auditGlob(root, patterns, options) {
878
932
  return matches.sort();
879
933
  }
880
934
  async function gitSourcePaths(root) {
881
- const { stdout } = await execute(
882
- "git",
883
- [
884
- "ls-files",
885
- "-z",
886
- "--cached",
887
- "--others",
888
- "--exclude-standard",
889
- "--",
890
- ...SOURCE_EXTENSIONS.map((extension) => `:(glob)**/*.${extension}`)
891
- ],
892
- { cwd: root, maxBuffer: 32 * 1024 * 1024, timeout: 1e4 }
893
- );
894
- return [...new Set(stdout.split("\0").filter(Boolean))].sort();
935
+ return auditGitPaths(root, "sources");
895
936
  }
896
937
  async function gitIgnoredPaths(root, files) {
897
938
  if (!files.length) return /* @__PURE__ */ new Set();
898
- const operation = execute(
899
- "git",
900
- ["check-ignore", "-z", "--stdin"],
901
- { cwd: root, maxBuffer: 4 * 1024 * 1024, timeout: 1e4 }
902
- );
903
- operation.child.stdin.on("error", () => {
904
- });
905
- operation.child.stdin.end(files.join("\0") + "\0");
906
- try {
907
- return new Set((await operation).stdout.split("\0").filter(Boolean));
908
- } catch (error) {
909
- if (error.code === 1) return /* @__PURE__ */ new Set();
910
- throw error;
939
+ const ignored = /* @__PURE__ */ new Set();
940
+ for (let offset = 0; offset < files.length; offset += 1e3) {
941
+ try {
942
+ const { stdout } = await execGit(["check-ignore", "-z", "--stdin"], {
943
+ cwd: root,
944
+ maxBuffer: 4 * 1024 * 1024,
945
+ timeout: 1e4,
946
+ input: files.slice(offset, offset + 1e3).join("\0") + "\0"
947
+ });
948
+ for (const file of stdout.split("\0").filter(Boolean)) ignored.add(file);
949
+ } catch (error) {
950
+ if (error.code !== 1) throw error;
951
+ }
911
952
  }
953
+ return ignored;
912
954
  }
913
- var execute, MAX_INPUTS;
955
+ var MAX_INPUTS;
914
956
  var init_inputs = __esm({
915
957
  "src/audit/inputs.ts"() {
916
958
  "use strict";
917
959
  init_files();
918
960
  init_storage();
919
- execute = promisify5(execFile5);
961
+ init_git_read();
962
+ init_inspection();
920
963
  MAX_INPUTS = 1e5;
921
964
  }
922
965
  });
923
966
 
924
967
  // src/audit/docs.ts
925
968
  import fs5 from "fs/promises";
926
- import path9 from "path";
927
- import { execFile as execFile6 } from "child_process";
928
- import { promisify as promisify6 } from "util";
969
+ import path10 from "path";
929
970
  import fg3 from "fast-glob";
930
971
  async function dirtyDocs(resolvedRoot, files) {
931
972
  if (!files.length) return /* @__PURE__ */ new Set();
932
973
  try {
933
974
  const dirty = /* @__PURE__ */ new Set();
934
975
  for (let offset = 0; offset < files.length; offset += 256) {
935
- const { stdout } = await exec5(
936
- "git",
976
+ const { stdout } = await execGit(
937
977
  ["status", "--porcelain=v1", "-z", "--untracked-files=all", "--", ...files.slice(offset, offset + 256).map((file) => `:(literal)${file}`)],
938
978
  { cwd: resolvedRoot, maxBuffer: 16 * 1024 * 1024, timeout: 1e4 }
939
979
  );
@@ -953,25 +993,10 @@ async function dirtyDocs(resolvedRoot, files) {
953
993
  async function discoverDocPaths(root) {
954
994
  let candidates = [];
955
995
  try {
956
- const { stdout } = await exec5(
957
- "git",
958
- [
959
- "ls-files",
960
- "-z",
961
- "--cached",
962
- "--others",
963
- "--exclude-standard",
964
- "--",
965
- ":(icase,glob)**/readme.md",
966
- ":(icase,glob)**/agents.md",
967
- ":(icase,glob)**/claude.md"
968
- ],
969
- { cwd: root, maxBuffer: 16 * 1024 * 1024, timeout: 1e4 }
970
- );
971
- candidates = stdout.split("\0").filter(Boolean);
996
+ candidates = await auditGitPaths(root, "documents");
972
997
  } catch (error) {
973
998
  try {
974
- await exec5("git", ["rev-parse", "--git-dir"], { cwd: root });
999
+ await execGit(["rev-parse", "--git-dir"], { cwd: root });
975
1000
  } catch {
976
1001
  return localDocPaths(root);
977
1002
  }
@@ -1018,14 +1043,14 @@ async function localDocPaths(root) {
1018
1043
  throw error;
1019
1044
  }
1020
1045
  for (const entry of entries) if (/^(?:agents|claude|readme)\.md$/i.test(entry.name)) {
1021
- paths.push(path9.posix.join(dir, entry.name));
1046
+ paths.push(path10.posix.join(dir, entry.name));
1022
1047
  }
1023
1048
  }
1024
1049
  return paths;
1025
1050
  }
1026
1051
  function documentScope(file) {
1027
- const parent = path9.posix.dirname(file);
1028
- return path9.posix.basename(parent).toLowerCase() === ".claude" ? path9.posix.dirname(parent) : parent;
1052
+ const parent = path10.posix.dirname(file);
1053
+ return path10.posix.basename(parent).toLowerCase() === ".claude" ? path10.posix.dirname(parent) : parent;
1029
1054
  }
1030
1055
  async function discoverDocs(resolvedRoot) {
1031
1056
  return profilePhase("audit.documents", () => readDocs(resolvedRoot));
@@ -1045,7 +1070,7 @@ async function readDocs(resolvedRoot) {
1045
1070
  path: candidate,
1046
1071
  content,
1047
1072
  scope: documentScope(candidate),
1048
- kind: /^readme\.md$/i.test(path9.posix.basename(candidate)) ? "readme" : "instructions",
1073
+ kind: /^readme\.md$/i.test(path10.posix.basename(candidate)) ? "readme" : "instructions",
1049
1074
  lineCount: content.split("\n").length,
1050
1075
  lastCommit,
1051
1076
  dirty: dirty.has(candidate),
@@ -1055,16 +1080,17 @@ async function readDocs(resolvedRoot) {
1055
1080
  }
1056
1081
  return docs;
1057
1082
  }
1058
- var exec5, DOC_CANDIDATES;
1083
+ var DOC_CANDIDATES;
1059
1084
  var init_docs = __esm({
1060
1085
  "src/audit/docs.ts"() {
1061
1086
  "use strict";
1087
+ init_git_read();
1062
1088
  init_profile();
1063
1089
  init_claims();
1064
1090
  init_git();
1065
1091
  init_inputs();
1066
1092
  init_files();
1067
- exec5 = promisify6(execFile6);
1093
+ init_inspection();
1068
1094
  DOC_CANDIDATES = [
1069
1095
  "AGENTS.md",
1070
1096
  "CLAUDE.md",
@@ -1091,14 +1117,14 @@ var init_types = __esm({
1091
1117
 
1092
1118
  // src/audit/scope.ts
1093
1119
  import fs6 from "fs/promises";
1094
- import path10 from "path";
1120
+ import path11 from "path";
1095
1121
  function scopedPath(directory, value) {
1096
- if (path10.posix.isAbsolute(value) || value.includes("\\") || /[\x00-\x1f]/.test(value)) return null;
1097
- const resolved = path10.posix.normalize(path10.posix.join(directory, value));
1122
+ if (path11.posix.isAbsolute(value) || value.includes("\\") || /[\x00-\x1f]/.test(value)) return null;
1123
+ const resolved = path11.posix.normalize(path11.posix.join(directory, value));
1098
1124
  return resolved === ".." || resolved.startsWith("../") ? null : resolved;
1099
1125
  }
1100
1126
  function pathClaimScope(doc, claim) {
1101
- const directory = claim.relativeTo === "document" ? path10.posix.dirname(doc) : documentScope(doc);
1127
+ const directory = claim.relativeTo === "document" ? path11.posix.dirname(doc) : documentScope(doc);
1102
1128
  const local = scopedPath(directory, claim.path);
1103
1129
  if (!local) return null;
1104
1130
  const candidates = claim.relativeTo === "document" || directory === "." ? [local] : [...new Set([local, scopedPath(".", claim.path)].filter((p) => p !== null))];
@@ -1132,7 +1158,7 @@ var init_scope = __esm({
1132
1158
  });
1133
1159
 
1134
1160
  // src/audit/checks/deleted-reference.ts
1135
- import path11 from "path";
1161
+ import path12 from "path";
1136
1162
  async function checkDeletedReferences(ctx) {
1137
1163
  const result = emptyResult();
1138
1164
  for (const doc of ctx.docs) {
@@ -1161,7 +1187,7 @@ async function checkDeletedReferences(ctx) {
1161
1187
  const candidates = await Promise.all(scope.candidates.map(async (file) => ({
1162
1188
  file,
1163
1189
  tracked: await lastCommitOf(ctx.root, file),
1164
- parent: await pathExists(ctx.root, path11.posix.dirname(file))
1190
+ parent: await pathExists(ctx.root, path12.posix.dirname(file))
1165
1191
  })));
1166
1192
  const candidate = candidates.find((c) => c.tracked || renames.has(c.file)) ?? candidates.find((c) => c.parent);
1167
1193
  if (!candidate && claim.relativeTo !== "document") continue;
@@ -1201,7 +1227,7 @@ var init_deleted_reference = __esm({
1201
1227
  // src/audit/checks/new-module.ts
1202
1228
  import fg4 from "fast-glob";
1203
1229
  import fs7 from "fs/promises";
1204
- import path12 from "path";
1230
+ import path13 from "path";
1205
1231
  function escapeRegExp(text2) {
1206
1232
  return text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1207
1233
  }
@@ -1239,23 +1265,36 @@ async function checkNewModules(ctx) {
1239
1265
  return result;
1240
1266
  }
1241
1267
  function moduleDocumentation(docs) {
1242
- return docs.map((doc) => doc.content + "\n" + (path12.posix.dirname(doc.path) === "." ? "" : path12.posix.dirname(doc.path) + "/")).join("\n");
1268
+ return docs.map((doc) => doc.content + "\n" + (path13.posix.dirname(doc.path) === "." ? "" : path13.posix.dirname(doc.path) + "/")).join("\n");
1243
1269
  }
1244
- async function moduleCandidates(root, combinedDocs) {
1270
+ function moduleCandidates(root, combinedDocs) {
1271
+ return inspectionRead(root, "modules:" + combinedDocs, () => collectModuleCandidates(root, combinedDocs));
1272
+ }
1273
+ async function collectModuleCandidates(root, combinedDocs) {
1245
1274
  const [config, sourcePaths] = await Promise.all([loadProjectConfig(root), gitSourcePaths(root)]);
1246
1275
  const ignore = [...SOURCE_IGNORE, ...config.ignore ?? []];
1247
1276
  const sources = sourcePaths.filter((file) => !file.split("/").some((part) => part.startsWith(".")));
1248
- const listSubdirs = async (dir) => {
1249
- const entries = await fs7.readdir(await auditInputPath(root, dir), { withFileTypes: true });
1250
- const dirs = entries.filter((entry) => (entry.isDirectory() || entry.isSymbolicLink() && sources.some((file) => file.startsWith(path12.posix.join(dir, entry.name) + "/"))) && !entry.name.startsWith(".") && !DIR_DENYLIST.has(entry.name)).map((entry) => path12.posix.join(dir, entry.name));
1251
- const ignored = await gitIgnoredPaths(root, dirs);
1252
- const visible = new Set(dirs.filter((dir2) => !ignored.has(dir2) || sources.some((file) => file.startsWith(dir2 + "/"))));
1253
- return (await auditGlob(root, dir === "." ? "*" : `${fg4.escapePath(dir)}/*`, {
1254
- ignore,
1255
- onlyDirectories: true,
1256
- label: "Module directory discovery",
1257
- select: (file) => visible.has(file)
1258
- })).map((file) => path12.posix.basename(file));
1277
+ const listSubdirs = async (parents) => {
1278
+ const byParent = /* @__PURE__ */ new Map();
1279
+ for (let offset = 0; offset < parents.length; offset += 16) {
1280
+ await Promise.all(parents.slice(offset, offset + 16).map(async (dir) => {
1281
+ const entries = await fs7.readdir(await auditInputPath(root, dir), { withFileTypes: true });
1282
+ 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));
1283
+ byParent.set(dir, dirs);
1284
+ }));
1285
+ }
1286
+ const ignored = await gitIgnoredPaths(root, [...byParent.values()].flat());
1287
+ const result = /* @__PURE__ */ new Map();
1288
+ for (const dir of parents) {
1289
+ const visible = new Set(byParent.get(dir).filter((candidate) => !ignored.has(candidate) || sources.some((file) => file.startsWith(candidate + "/"))));
1290
+ result.set(dir, (await auditGlob(root, dir === "." ? "*" : `${fg4.escapePath(dir)}/*`, {
1291
+ ignore,
1292
+ onlyDirectories: true,
1293
+ label: "Module directory discovery",
1294
+ select: (file) => visible.has(file)
1295
+ })).map((file) => path13.posix.basename(file)));
1296
+ }
1297
+ return result;
1259
1298
  };
1260
1299
  const countSourceFiles = async (dir) => {
1261
1300
  const files = sources.filter((file) => file.startsWith(dir + "/"));
@@ -1271,14 +1310,16 @@ async function moduleCandidates(root, combinedDocs) {
1271
1310
  return count3;
1272
1311
  };
1273
1312
  const candidates = [];
1274
- for (const topDir of await listSubdirs(".")) {
1313
+ const topDirs = (await listSubdirs(["."])).get(".");
1314
+ const nested = await listSubdirs(topDirs.filter((dir) => isMentioned(combinedDocs, dir)));
1315
+ for (const topDir of topDirs) {
1275
1316
  const topMentioned = isMentioned(combinedDocs, topDir);
1276
1317
  if (!topMentioned) {
1277
1318
  const count3 = await countSourceFiles(topDir);
1278
1319
  if (count3 >= 1) candidates.push({ dir: topDir, sourceFileCount: count3 });
1279
1320
  continue;
1280
1321
  }
1281
- const subdirs = await listSubdirs(topDir);
1322
+ const subdirs = nested.get(topDir);
1282
1323
  const mentioned = subdirs.filter((s) => isMentioned(combinedDocs, s));
1283
1324
  if (mentioned.length < ENUMERATION_THRESHOLD) continue;
1284
1325
  for (const sub of subdirs) {
@@ -1295,6 +1336,7 @@ var DIR_DENYLIST, SECOND_LEVEL_MIN_SOURCE_FILES, ENUMERATION_THRESHOLD;
1295
1336
  var init_new_module = __esm({
1296
1337
  "src/audit/checks/new-module.ts"() {
1297
1338
  "use strict";
1339
+ init_inspection();
1298
1340
  init_files();
1299
1341
  init_inputs();
1300
1342
  init_git();
@@ -1324,7 +1366,7 @@ var init_new_module = __esm({
1324
1366
  });
1325
1367
 
1326
1368
  // src/audit/checks/stale-count.ts
1327
- import path13 from "path";
1369
+ import path14 from "path";
1328
1370
  async function countGradleModules(root) {
1329
1371
  for (const name of ["settings.gradle.kts", "settings.gradle"]) {
1330
1372
  const content = await readAuditInput(root, name);
@@ -1358,7 +1400,7 @@ async function countNpmWorkspaces(root) {
1358
1400
  return {
1359
1401
  actual: matched.length,
1360
1402
  countedFrom: "package.json workspaces",
1361
- members: matched.map((m) => path13.dirname(m)).sort()
1403
+ members: matched.map((m) => path14.dirname(m)).sort()
1362
1404
  };
1363
1405
  }
1364
1406
  }
@@ -1389,7 +1431,7 @@ async function countNpmWorkspaces(root) {
1389
1431
  return {
1390
1432
  actual: matched.length,
1391
1433
  countedFrom: "pnpm-workspace.yaml",
1392
- members: matched.map((m) => path13.dirname(m)).sort()
1434
+ members: matched.map((m) => path14.dirname(m)).sort()
1393
1435
  };
1394
1436
  }
1395
1437
  }
@@ -1411,8 +1453,8 @@ async function countCargoCrates(root) {
1411
1453
  ignore: ["**/target/**"],
1412
1454
  label: "Cargo workspace discovery"
1413
1455
  });
1414
- for (const m of matched) members.add(path13.dirname(m));
1415
- } else if (await readAuditInput(root, path13.posix.join(entry, "Cargo.toml")) !== null) {
1456
+ for (const m of matched) members.add(path14.dirname(m));
1457
+ } else if (await readAuditInput(root, path14.posix.join(entry, "Cargo.toml")) !== null) {
1416
1458
  members.add(entry);
1417
1459
  }
1418
1460
  }
@@ -1432,7 +1474,7 @@ async function resolveCountSource(root, claim) {
1432
1474
  }
1433
1475
  async function resolveDocCountSource(root, doc, claim) {
1434
1476
  const scope = documentScope(doc);
1435
- const source = await resolveCountSource(path13.join(root, scope), claim);
1477
+ const source = await resolveCountSource(path14.join(root, scope), claim);
1436
1478
  if (!source || scope === ".") return source;
1437
1479
  return {
1438
1480
  ...source,
@@ -1484,7 +1526,7 @@ var init_stale_count = __esm({
1484
1526
  });
1485
1527
 
1486
1528
  // src/audit/checks/dead-command.ts
1487
- import path14 from "path";
1529
+ import path15 from "path";
1488
1530
  function scriptsOf(raw) {
1489
1531
  try {
1490
1532
  const pkg = JSON.parse(raw);
@@ -1520,7 +1562,7 @@ async function commandInputs(root, docs) {
1520
1562
  }
1521
1563
  let cursor = directory;
1522
1564
  while (true) {
1523
- const manifest = path14.posix.join(cursor, "package.json");
1565
+ const manifest = path15.posix.join(cursor, "package.json");
1524
1566
  scope.candidates.push(manifest);
1525
1567
  const raw = await read(manifest);
1526
1568
  if (raw !== null) {
@@ -1531,7 +1573,7 @@ async function commandInputs(root, docs) {
1531
1573
  break;
1532
1574
  }
1533
1575
  if (claim.directory !== void 0 || cursor === ".") break;
1534
- cursor = path14.posix.dirname(cursor);
1576
+ cursor = path15.posix.dirname(cursor);
1535
1577
  }
1536
1578
  if (!resolution.manifest && !resolution.problem) {
1537
1579
  resolution.problem = `No package.json for the command's scope (${directory}).`;
@@ -1594,10 +1636,8 @@ var init_dead_command = __esm({
1594
1636
  });
1595
1637
 
1596
1638
  // src/audit/release-metadata.ts
1597
- import { execFile as execFile7 } from "child_process";
1598
- import { promisify as promisify7 } from "util";
1599
1639
  async function git(root, args) {
1600
- return (await exec6("git", args, { cwd: root, timeout: 1e4, maxBuffer: 2 * 1024 * 1024 })).stdout;
1640
+ return (await execGit(args, { cwd: root, timeout: 1e4, maxBuffer: 2 * 1024 * 1024 })).stdout;
1601
1641
  }
1602
1642
  function withoutAndroidReleaseValues(text2) {
1603
1643
  if (/\/\*|"""|'''/.test(text2) || !/id\s*\(?\s*["']com\.android\.(application|library)["']/.test(text2)) return null;
@@ -1643,11 +1683,10 @@ async function releaseMetadataOnly(root, commit) {
1643
1683
  return false;
1644
1684
  }
1645
1685
  }
1646
- var exec6;
1647
1686
  var init_release_metadata = __esm({
1648
1687
  "src/audit/release-metadata.ts"() {
1649
1688
  "use strict";
1650
- exec6 = promisify7(execFile7);
1689
+ init_git_read();
1651
1690
  }
1652
1691
  });
1653
1692
 
@@ -1743,7 +1782,7 @@ var init_deps_changed = __esm({
1743
1782
  });
1744
1783
 
1745
1784
  // src/context/lexical.ts
1746
- import path15 from "path";
1785
+ import path16 from "path";
1747
1786
  var init_lexical = __esm({
1748
1787
  "src/context/lexical.ts"() {
1749
1788
  "use strict";
@@ -1904,7 +1943,7 @@ var init_provenance = __esm({
1904
1943
 
1905
1944
  // src/decisions/decisions.ts
1906
1945
  import fs8 from "fs/promises";
1907
- import path16 from "path";
1946
+ import path17 from "path";
1908
1947
  import { createHash } from "crypto";
1909
1948
  import { z as z3 } from "zod";
1910
1949
  async function loadDecisionStore(rootDir) {
@@ -1955,9 +1994,9 @@ var init_decisions = __esm({
1955
1994
  });
1956
1995
 
1957
1996
  // src/decisions/drift.ts
1958
- import path17 from "path";
1997
+ import path18 from "path";
1959
1998
  async function computeDecisionDrift(rootDir, decisions) {
1960
- const resolvedRoot = path17.resolve(rootDir);
1999
+ const resolvedRoot = path18.resolve(rootDir);
1961
2000
  const store = decisions ? { records: decisions, diagnostics: [] } : await loadDecisionStore(resolvedRoot);
1962
2001
  const report = { historyAvailable: true, totalDecisions: store.records.length, staleDecisions: {}, freshness: {}, diagnostics: store.diagnostics };
1963
2002
  const [head, workingTree] = await Promise.all([getCurrentGitHash(resolvedRoot), getWorkingTree(resolvedRoot)]);
@@ -2284,7 +2323,7 @@ __export(repair_exports, {
2284
2323
  withRepairSession: () => withRepairSession
2285
2324
  });
2286
2325
  import fs10 from "fs/promises";
2287
- import path18 from "path";
2326
+ import path19 from "path";
2288
2327
  import { randomUUID as randomUUID2 } from "crypto";
2289
2328
  import { z as z6 } from "zod";
2290
2329
  function recheckable(finding) {
@@ -2322,8 +2361,8 @@ async function readStableAudit(root, checks, options) {
2322
2361
  async function prepareRepair(rootDir, checks = ALL_CHECKS, options = {}) {
2323
2362
  return prepareOriginal(rootDir, checks, options);
2324
2363
  }
2325
- async function withRepairSession(root, options, run, cachedAudit) {
2326
- const before = await Promise.all([getCurrentGitHash(root), docState(root), advisoryReviewInventory(root).then(digest)]);
2364
+ async function withRepairSession(root, options, run, validate, cachedAudit) {
2365
+ const head = await getCurrentGitHash(root);
2327
2366
  const audits = /* @__PURE__ */ new Map();
2328
2367
  const documents = /* @__PURE__ */ new Map();
2329
2368
  const history = /* @__PURE__ */ new Map();
@@ -2334,9 +2373,9 @@ async function withRepairSession(root, options, run, cachedAudit) {
2334
2373
  };
2335
2374
  const session = {
2336
2375
  audit: (checks) => memo(audits, [...checks].sort().join(","), async () => {
2337
- if (cachedAudit && cachedAudit.headHash === before[0] && cachedAudit.root === root && digest([...checks].sort()) === digest([...cachedAudit.checksRun ?? []].sort())) {
2376
+ if (cachedAudit && cachedAudit.headHash === head && cachedAudit.root === root && digest([...checks].sort()) === digest([...cachedAudit.checksRun ?? []].sort())) {
2338
2377
  const current = structuredClone(cachedAudit);
2339
- const reviews = (await Promise.all(allFindings(current).map((f) => assessAdvisory(root, f, before[0])))).filter((r) => r !== null);
2378
+ const reviews = (await Promise.all(allFindings(current).map((f) => assessAdvisory(root, f, head)))).filter((r) => r !== null);
2340
2379
  delete current.advisoryReviews;
2341
2380
  if (reviews.length) current.advisoryReviews = reviews;
2342
2381
  return current;
@@ -2344,16 +2383,15 @@ async function withRepairSession(root, options, run, cachedAudit) {
2344
2383
  return computeAudit(root, { ...options, checks });
2345
2384
  }),
2346
2385
  document: (file) => memo(documents, file, async () => readBoundedFile(await storePath(root, file), 10 * 1024 * 1024)),
2347
- history: (head) => memo(history, head, () => getChangesWithStatus(root, head)),
2348
- assessment: (dir, finding, head) => memo(assessments, digest([dir, finding, head]), () => assessAdvisory(dir, finding, head))
2386
+ history: (head2) => memo(history, head2, () => getChangesWithStatus(root, head2)),
2387
+ assessment: (dir, finding, head2) => memo(assessments, digest([dir, finding, head2]), () => assessAdvisory(dir, finding, head2))
2349
2388
  };
2350
2389
  const result = await run({
2351
2390
  prepare: () => prepareOriginal(root, ALL_CHECKS, options, session),
2352
2391
  verify: (baseline) => profilePhase("repair.verify", () => verifyOriginal(root, baseline, options, session)),
2353
2392
  currentAudit: () => session.audit(ALL_CHECKS)
2354
2393
  });
2355
- const after = await Promise.all([getCurrentGitHash(root), docState(root), advisoryReviewInventory(root).then(digest)]);
2356
- if (digest(before) !== digest(after)) throw new Error("HEAD, context files or advisory review records changed during verification; retry against stable evidence.");
2394
+ await validate();
2357
2395
  return result;
2358
2396
  }
2359
2397
  async function prepareOriginal(rootDir, checks, options, session) {
@@ -2375,8 +2413,8 @@ async function prepareOriginal(rootDir, checks, options, session) {
2375
2413
  }
2376
2414
  async function loadRepairBaseline(rootDir, baselinePath) {
2377
2415
  const root = await fs10.realpath(rootDir);
2378
- const declaredRoot = path18.resolve(rootDir);
2379
- const relative = path18.isAbsolute(baselinePath) ? path18.relative(isWithinRoot(declaredRoot, baselinePath) ? declaredRoot : root, baselinePath) : baselinePath;
2416
+ const declaredRoot = path19.resolve(rootDir);
2417
+ const relative = path19.isAbsolute(baselinePath) ? path19.relative(isWithinRoot(declaredRoot, baselinePath) ? declaredRoot : root, baselinePath) : baselinePath;
2380
2418
  const stored = baselineSchema.parse(await readStoreJson(root, relative));
2381
2419
  const { digest: savedDigest, ...payload } = stored;
2382
2420
  if (digest(payload) !== savedDigest) throw new Error("Repair baseline was modified; use the original baseline.");
@@ -2551,8 +2589,6 @@ var init_repair = __esm({
2551
2589
 
2552
2590
  // src/audit/advisory-review.ts
2553
2591
  import fs11 from "fs/promises";
2554
- import { execFile as execFile8 } from "child_process";
2555
- import { promisify as promisify8 } from "util";
2556
2592
  import { z as z7 } from "zod";
2557
2593
  async function readRecord(root, id) {
2558
2594
  const raw = await readStoreJson(root, recordPath(id));
@@ -2747,10 +2783,12 @@ async function reviewAdvisory(rootDir, input) {
2747
2783
  };
2748
2784
  });
2749
2785
  }
2750
- var execute2, git2, hashSchema, commitSchema2, evidenceSchema2, eventSchema2, recordSchema, reviewSummarySchema, advisoryReviewRequest, recordPath;
2786
+ var git2, hashSchema, commitSchema2, evidenceSchema2, eventSchema2, recordSchema, reviewSummarySchema, advisoryReviewRequest, recordPath;
2751
2787
  var init_advisory_review = __esm({
2752
2788
  "src/audit/advisory-review.ts"() {
2753
2789
  "use strict";
2790
+ init_inspection();
2791
+ init_git_read();
2754
2792
  init_findings();
2755
2793
  init_deps_changed();
2756
2794
  init_inputs();
@@ -2760,8 +2798,7 @@ var init_advisory_review = __esm({
2760
2798
  init_store();
2761
2799
  init_decisions();
2762
2800
  init_drift2();
2763
- execute2 = promisify8(execFile8);
2764
- git2 = async (root, ...args) => (await execute2("git", args, { cwd: root, timeout: 1e4, maxBuffer: 8 * 1024 * 1024 })).stdout;
2801
+ 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;
2765
2802
  hashSchema = z7.string().regex(/^[a-f0-9]{64}$/);
2766
2803
  commitSchema2 = z7.string().regex(/^[a-f0-9]{40,64}$/);
2767
2804
  evidenceSchema2 = z7.object({
@@ -2805,12 +2842,12 @@ var init_advisory_review = __esm({
2805
2842
 
2806
2843
  // src/audit/audit.ts
2807
2844
  import fs12 from "fs/promises";
2808
- import path19 from "path";
2845
+ import path20 from "path";
2809
2846
  async function computeAudit(rootDir, options = {}) {
2810
2847
  return profilePhase("audit.current", () => auditCurrent(rootDir, options));
2811
2848
  }
2812
2849
  async function auditCurrent(rootDir, options) {
2813
- const resolvedRoot = path19.resolve(rootDir);
2850
+ const resolvedRoot = path20.resolve(rootDir);
2814
2851
  const [docs, headHash] = await Promise.all([discoverDocs(resolvedRoot), getCurrentGitHash(resolvedRoot)]);
2815
2852
  if (docs.length === 0) return null;
2816
2853
  const report = {
@@ -2844,7 +2881,7 @@ async function auditCurrent(rootDir, options) {
2844
2881
  }
2845
2882
  let decisionsPresent = false;
2846
2883
  try {
2847
- await fs12.access(path19.join(resolvedRoot, ".mason", "decisions"));
2884
+ await fs12.access(path20.join(resolvedRoot, ".mason", "decisions"));
2848
2885
  decisionsPresent = true;
2849
2886
  } catch {
2850
2887
  }
@@ -2889,11 +2926,11 @@ init_audit();
2889
2926
  init_types();
2890
2927
  init_repair();
2891
2928
  init_findings();
2892
- import path21 from "path";
2929
+ import path22 from "path";
2893
2930
 
2894
2931
  // src/audit/advisory-cli.ts
2895
2932
  init_advisory_review();
2896
- import path20 from "path";
2933
+ import path21 from "path";
2897
2934
  import { stripVTControlCharacters } from "util";
2898
2935
  var USAGE = `Usage: mason audit review --baseline <path> --finding <id> [options]
2899
2936
 
@@ -2939,7 +2976,7 @@ async function runAdvisoryCli(args, io = { out: (text2) => console.log(text2), e
2939
2976
  note: flags["--note"],
2940
2977
  reviewToken: flags["--token"]
2941
2978
  };
2942
- const result = await reviewAdvisory(path20.resolve(flags["--dir"] ?? process.cwd()), input);
2979
+ const result = await reviewAdvisory(path21.resolve(flags["--dir"] ?? process.cwd()), input);
2943
2980
  if (json) io.out(JSON.stringify(result, null, 2));
2944
2981
  else {
2945
2982
  const lines = [`Advisory review: ${result.status}.`];
@@ -3172,7 +3209,7 @@ async function runAuditCli(argv, io = {
3172
3209
  io.out(USAGE2);
3173
3210
  return 0;
3174
3211
  }
3175
- const rootDir = path21.resolve(args.dir);
3212
+ const rootDir = path22.resolve(args.dir);
3176
3213
  if (args.baseline || args.prepareRepair) {
3177
3214
  try {
3178
3215
  if (args.baseline) {