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/dist/mason-mcp.js CHANGED
@@ -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/audit/commands.ts
35
61
  import path from "path";
36
62
  function splitCommands(line) {
@@ -255,14 +281,14 @@ function extractClaims(content2) {
255
281
  const ignored = computeIgnoredLines(lines);
256
282
  const paths = /* @__PURE__ */ new Map();
257
283
  const counts = [];
258
- const commands = /* @__PURE__ */ new Map();
284
+ const commands2 = /* @__PURE__ */ new Map();
259
285
  const addPath = (claim) => {
260
286
  const key = [claim.relativeTo ?? "scope", claim.path].join(":");
261
287
  if (!paths.has(key)) paths.set(key, claim);
262
288
  };
263
289
  const addCommand = (claim) => {
264
290
  const key = JSON.stringify([claim.scriptName, claim.directory, claim.scopeUnknown]);
265
- if (!commands.has(key)) commands.set(key, claim);
291
+ if (!commands2.has(key)) commands2.set(key, claim);
266
292
  };
267
293
  const parsed = markdown.parse(lines.map((line, i) => ignored[i] ? "" : line).join("\n"), {});
268
294
  for (const token of parsed) {
@@ -364,7 +390,7 @@ function extractClaims(content2) {
364
390
  return {
365
391
  paths: [...paths.values()],
366
392
  counts,
367
- commands: [...commands.values()]
393
+ commands: [...commands2.values()]
368
394
  };
369
395
  }
370
396
  var ROOT_FILE_NAMES, SHELL_FENCE_INFOS, markdown, COUNT_RE, COUNT_DENYLIST_RE, IGNORE_LINE, IGNORE_START, IGNORE_END;
@@ -414,97 +440,6 @@ var init_claims = __esm({
414
440
  }
415
441
  });
416
442
 
417
- // src/audit/git.ts
418
- import { execFile } from "child_process";
419
- import { promisify } from "util";
420
- function parseCommitLine(line) {
421
- const parts = line.split(" ");
422
- if (parts.length < 3 || !parts[0]) return null;
423
- return { hash: parts[0], date: parts[1], subject: parts.slice(2).join(" ") };
424
- }
425
- async function lastCommitOf(resolvedRoot, relPath) {
426
- try {
427
- const { stdout } = await exec(
428
- "git",
429
- ["log", "-1", `--format=${COMMIT_FORMAT}`, "--", `:(literal)${relPath}`],
430
- { cwd: resolvedRoot }
431
- );
432
- const line = stdout.trim().split("\n")[0];
433
- return line ? parseCommitLine(line) : null;
434
- } catch {
435
- return null;
436
- }
437
- }
438
- async function deletingCommitOf(resolvedRoot, relPath) {
439
- try {
440
- const { stdout } = await exec(
441
- "git",
442
- [
443
- "log",
444
- "-1",
445
- "--diff-filter=D",
446
- `--format=${COMMIT_FORMAT}`,
447
- "--",
448
- `:(literal)${relPath}`
449
- ],
450
- { cwd: resolvedRoot }
451
- );
452
- const line = stdout.trim().split("\n")[0];
453
- return line ? parseCommitLine(line) : null;
454
- } catch {
455
- return null;
456
- }
457
- }
458
- async function firstCommitOf(resolvedRoot, relPath) {
459
- try {
460
- const { stdout } = await exec(
461
- "git",
462
- ["log", "--reverse", `--format=${COMMIT_FORMAT}`, "--", `:(literal)${relPath}`],
463
- { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 }
464
- );
465
- const line = stdout.trim().split("\n")[0];
466
- return line ? parseCommitLine(line) : null;
467
- } catch {
468
- return null;
469
- }
470
- }
471
- async function commitsTouchingSince(resolvedRoot, fromHash, pathspecs) {
472
- if (!fromHash || fromHash === "unknown") return null;
473
- try {
474
- const { stdout } = await exec(
475
- "git",
476
- [
477
- "log",
478
- `${fromHash}..HEAD`,
479
- `--format=%x01${COMMIT_FORMAT}`,
480
- "--name-only",
481
- "--",
482
- ...pathspecs
483
- ],
484
- { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 }
485
- );
486
- const commits = [];
487
- for (const block of stdout.split("")) {
488
- if (!block.trim()) continue;
489
- const lines = block.split("\n").filter((l) => l.trim().length > 0);
490
- const ref = parseCommitLine(lines[0]);
491
- if (!ref) continue;
492
- commits.push({ ...ref, files: lines.slice(1).map((l) => l.trim()) });
493
- }
494
- return { commits, total: commits.length };
495
- } catch {
496
- return null;
497
- }
498
- }
499
- var exec, COMMIT_FORMAT;
500
- var init_git = __esm({
501
- "src/audit/git.ts"() {
502
- "use strict";
503
- exec = promisify(execFile);
504
- COMMIT_FORMAT = "%H%x09%cI%x09%s";
505
- }
506
- });
507
-
508
443
  // src/utils/paths.ts
509
444
  import path2 from "path";
510
445
  function normalizeRepoPath(value) {
@@ -539,8 +474,6 @@ var init_paths = __esm({
539
474
  import fs from "fs/promises";
540
475
  import { constants } from "fs";
541
476
  import path3 from "path";
542
- import { execFile as execFile2 } from "child_process";
543
- import { promisify as promisify2 } from "util";
544
477
  import fg from "fast-glob";
545
478
  function isSensitiveFile(file) {
546
479
  return file.split(/[\\/]/).some(
@@ -594,12 +527,12 @@ async function createFileAccess(rootDir) {
594
527
  const ignore = [...SOURCE_IGNORE, ...config.ignore ?? []];
595
528
  let gitFiles = null;
596
529
  try {
597
- const { stdout } = await exec2("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], { cwd: root, maxBuffer: 50 * 1024 * 1024 });
530
+ const { stdout } = await execGit(["ls-files", "-z", "--cached", "--others", "--exclude-standard"], { cwd: root, maxBuffer: 50 * 1024 * 1024 });
598
531
  gitFiles = new Set(stdout.split("\0").filter(Boolean));
599
532
  } catch {
600
533
  let inGit = false;
601
534
  try {
602
- await exec2("git", ["rev-parse", "--git-dir"], { cwd: root });
535
+ await execGit(["rev-parse", "--git-dir"], { cwd: root });
603
536
  inGit = true;
604
537
  } catch {
605
538
  }
@@ -642,12 +575,12 @@ async function createFileAccess(rootDir) {
642
575
  }
643
576
  return { root, config, list, read };
644
577
  }
645
- var exec2, SOURCE_EXTENSIONS, SOURCE_GLOB, SOURCE_IGNORE, MAX_SOURCE_BYTES;
578
+ var SOURCE_EXTENSIONS, SOURCE_GLOB, SOURCE_IGNORE, MAX_SOURCE_BYTES;
646
579
  var init_files = __esm({
647
580
  "src/utils/files.ts"() {
648
581
  "use strict";
582
+ init_git_read();
649
583
  init_paths();
650
- exec2 = promisify2(execFile2);
651
584
  SOURCE_EXTENSIONS = ["ts", "tsx", "js", "jsx", "mts", "cts", "mjs", "cjs", "vue", "svelte", "kt", "kts", "java", "py", "go", "rs", "swift", "rb", "cs", "cpp", "c", "h", "hpp", "dart", "php"];
652
585
  SOURCE_GLOB = `**/*.{${SOURCE_EXTENSIONS.join(",")}}`;
653
586
  SOURCE_IGNORE = [
@@ -677,9 +610,140 @@ var init_files = __esm({
677
610
  }
678
611
  });
679
612
 
613
+ // src/audit/inspection.ts
614
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
615
+ import path4 from "path";
616
+ function withRepositoryInspection(root, run) {
617
+ return inspections.run({ root: path4.resolve(root), reads: /* @__PURE__ */ new Map() }, run);
618
+ }
619
+ function inspectionRead(root, key, read) {
620
+ const inspection = inspections.getStore();
621
+ if (!inspection || inspection.root !== path4.resolve(root)) return read();
622
+ if (!inspection.reads.has(key)) {
623
+ const pending = read();
624
+ inspection.reads.set(key, pending);
625
+ void pending.catch(() => {
626
+ if (inspection.reads.get(key) === pending) inspection.reads.delete(key);
627
+ });
628
+ }
629
+ return inspection.reads.get(key);
630
+ }
631
+ function inspectionGit(args, options) {
632
+ return inspectionRead(options.cwd, "git:" + JSON.stringify([args, options]), () => execGit(args, options));
633
+ }
634
+ async function auditGitPaths(root, kind) {
635
+ const shared = inspections.getStore()?.root === path4.resolve(root);
636
+ const specs = shared ? [...docSpecs, ...sourceSpecs] : kind === "documents" ? docSpecs : sourceSpecs;
637
+ const limit = kind === "documents" ? 16 * 1024 * 1024 : 32 * 1024 * 1024;
638
+ const { stdout } = await inspectionGit(
639
+ ["ls-files", "-z", "--cached", "--others", "--exclude-standard", "--", ...specs],
640
+ { cwd: root, maxBuffer: shared ? 48 * 1024 * 1024 : limit, timeout: 1e4 }
641
+ );
642
+ const paths = [...new Set(stdout.split("\0").filter(Boolean))].filter((file) => kind === "documents" ? /^(readme|agents|claude)\.md$/i.test(path4.posix.basename(file)) : sourceSuffixes.has(file.slice(file.lastIndexOf("."))));
643
+ if (Buffer.byteLength(paths.join("\0")) > limit) throw new Error("Git " + kind + " inventory exceeds its bounded read limit.");
644
+ return paths.sort();
645
+ }
646
+ var inspections, sourceSpecs, docSpecs, sourceSuffixes;
647
+ var init_inspection = __esm({
648
+ "src/audit/inspection.ts"() {
649
+ "use strict";
650
+ init_git_read();
651
+ init_files();
652
+ inspections = new AsyncLocalStorage2();
653
+ sourceSpecs = SOURCE_EXTENSIONS.map((extension) => `:(glob)**/*.${extension}`);
654
+ docSpecs = [":(icase,glob)**/readme.md", ":(icase,glob)**/agents.md", ":(icase,glob)**/claude.md"];
655
+ sourceSuffixes = new Set(SOURCE_EXTENSIONS.map((extension) => "." + extension));
656
+ }
657
+ });
658
+
659
+ // src/audit/git.ts
660
+ function parseCommitLine(line) {
661
+ const parts = line.split(" ");
662
+ if (parts.length < 3 || !parts[0]) return null;
663
+ return { hash: parts[0], date: parts[1], subject: parts.slice(2).join(" ") };
664
+ }
665
+ async function lastCommitOf(resolvedRoot, relPath) {
666
+ try {
667
+ const { stdout } = await inspectionGit(
668
+ ["log", "-1", `--format=${COMMIT_FORMAT}`, "--", `:(literal)${relPath}`],
669
+ { cwd: resolvedRoot }
670
+ );
671
+ const line = stdout.trim().split("\n")[0];
672
+ return line ? parseCommitLine(line) : null;
673
+ } catch {
674
+ return null;
675
+ }
676
+ }
677
+ async function deletingCommitOf(resolvedRoot, relPath) {
678
+ try {
679
+ const { stdout } = await inspectionGit(
680
+ [
681
+ "log",
682
+ "-1",
683
+ "--diff-filter=D",
684
+ `--format=${COMMIT_FORMAT}`,
685
+ "--",
686
+ `:(literal)${relPath}`
687
+ ],
688
+ { cwd: resolvedRoot }
689
+ );
690
+ const line = stdout.trim().split("\n")[0];
691
+ return line ? parseCommitLine(line) : null;
692
+ } catch {
693
+ return null;
694
+ }
695
+ }
696
+ async function firstCommitOf(resolvedRoot, relPath) {
697
+ try {
698
+ const { stdout } = await inspectionGit(
699
+ ["log", "--reverse", `--format=${COMMIT_FORMAT}`, "--", `:(literal)${relPath}`],
700
+ { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 }
701
+ );
702
+ const line = stdout.trim().split("\n")[0];
703
+ return line ? parseCommitLine(line) : null;
704
+ } catch {
705
+ return null;
706
+ }
707
+ }
708
+ async function commitsTouchingSince(resolvedRoot, fromHash, pathspecs) {
709
+ if (!fromHash || fromHash === "unknown") return null;
710
+ try {
711
+ const { stdout } = await inspectionGit(
712
+ [
713
+ "log",
714
+ `${fromHash}..HEAD`,
715
+ `--format=%x01${COMMIT_FORMAT}`,
716
+ "--name-only",
717
+ "--",
718
+ ...pathspecs
719
+ ],
720
+ { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 }
721
+ );
722
+ const commits = [];
723
+ for (const block of stdout.split("")) {
724
+ if (!block.trim()) continue;
725
+ const lines = block.split("\n").filter((l) => l.trim().length > 0);
726
+ const ref = parseCommitLine(lines[0]);
727
+ if (!ref) continue;
728
+ commits.push({ ...ref, files: lines.slice(1).map((l) => l.trim()) });
729
+ }
730
+ return { commits, total: commits.length };
731
+ } catch {
732
+ return null;
733
+ }
734
+ }
735
+ var COMMIT_FORMAT;
736
+ var init_git = __esm({
737
+ "src/audit/git.ts"() {
738
+ "use strict";
739
+ init_inspection();
740
+ COMMIT_FORMAT = "%H%x09%cI%x09%s";
741
+ }
742
+ });
743
+
680
744
  // src/utils/storage.ts
681
745
  import fs2 from "fs/promises";
682
- import path4 from "path";
746
+ import path5 from "path";
683
747
  import { randomUUID } from "crypto";
684
748
  async function storePath(root, relative, createParents = false) {
685
749
  const normalized = normalizeRepoPath(relative);
@@ -687,7 +751,7 @@ async function storePath(root, relative, createParents = false) {
687
751
  let current = await fs2.realpath(root);
688
752
  const parts = normalized.split("/");
689
753
  for (let i = 0; i < parts.length; i++) {
690
- current = path4.join(current, parts[i]);
754
+ current = path5.join(current, parts[i]);
691
755
  let stat;
692
756
  try {
693
757
  stat = await fs2.lstat(current);
@@ -725,7 +789,7 @@ async function writeStoreJson(root, relative, value) {
725
789
  throw new Error(`Mason store ${relative} exceeds 10 MiB`);
726
790
  }
727
791
  const file = await storePath(root, relative, true);
728
- const temporary = path4.join(path4.dirname(file), `.${path4.basename(file)}.${randomUUID()}.tmp`);
792
+ const temporary = path5.join(path5.dirname(file), `.${path5.basename(file)}.${randomUUID()}.tmp`);
729
793
  try {
730
794
  const handle = await fs2.open(temporary, "wx", 384);
731
795
  try {
@@ -750,9 +814,7 @@ var init_storage = __esm({
750
814
  // src/audit/inputs.ts
751
815
  import fs3 from "fs/promises";
752
816
  import * as nativeFs from "fs";
753
- import path5 from "path";
754
- import { execFile as execFile3 } from "child_process";
755
- import { promisify as promisify3 } from "util";
817
+ import path6 from "path";
756
818
  import fg2 from "fast-glob";
757
819
  async function auditInputPath(root, file) {
758
820
  if (file === ".") return fs3.realpath(root);
@@ -779,7 +841,7 @@ async function auditGlob(root, patterns, options) {
779
841
  if (Array.isArray(patterns) && !patterns.length) return [];
780
842
  const directories = /* @__PURE__ */ new Set();
781
843
  const readdir2 = ((directory, opts, callback) => {
782
- const relative = path5.relative(root, directory).split(path5.sep).join("/") || ".";
844
+ const relative = path6.relative(root, directory).split(path6.sep).join("/") || ".";
783
845
  directories.add(relative);
784
846
  if (directories.size > MAX_INPUTS) {
785
847
  callback(new Error(`${options.label} exceeds ${MAX_INPUTS.toLocaleString("en-US")} directories while inspecting ${relative}. Narrow this check's inputs.`));
@@ -790,7 +852,7 @@ async function auditGlob(root, patterns, options) {
790
852
  }, callback);
791
853
  });
792
854
  const lstat2 = ((file, callback) => {
793
- const relative = path5.relative(root, file).split(path5.sep).join("/") || ".";
855
+ const relative = path6.relative(root, file).split(path6.sep).join("/") || ".";
794
856
  auditInputPath(root, relative).then(() => nativeFs.lstat(file, callback), callback);
795
857
  });
796
858
  const stream = fg2.stream(patterns, {
@@ -827,62 +889,48 @@ async function auditGlob(root, patterns, options) {
827
889
  return matches.sort();
828
890
  }
829
891
  async function gitSourcePaths(root) {
830
- const { stdout } = await execute(
831
- "git",
832
- [
833
- "ls-files",
834
- "-z",
835
- "--cached",
836
- "--others",
837
- "--exclude-standard",
838
- "--",
839
- ...SOURCE_EXTENSIONS.map((extension) => `:(glob)**/*.${extension}`)
840
- ],
841
- { cwd: root, maxBuffer: 32 * 1024 * 1024, timeout: 1e4 }
842
- );
843
- return [...new Set(stdout.split("\0").filter(Boolean))].sort();
892
+ return auditGitPaths(root, "sources");
844
893
  }
845
894
  async function gitIgnoredPaths(root, files) {
846
895
  if (!files.length) return /* @__PURE__ */ new Set();
847
- const operation = execute(
848
- "git",
849
- ["check-ignore", "-z", "--stdin"],
850
- { cwd: root, maxBuffer: 4 * 1024 * 1024, timeout: 1e4 }
851
- );
852
- operation.child.stdin.on("error", () => {
853
- });
854
- operation.child.stdin.end(files.join("\0") + "\0");
855
- try {
856
- return new Set((await operation).stdout.split("\0").filter(Boolean));
857
- } catch (error) {
858
- if (error.code === 1) return /* @__PURE__ */ new Set();
859
- throw error;
896
+ const ignored = /* @__PURE__ */ new Set();
897
+ for (let offset = 0; offset < files.length; offset += 1e3) {
898
+ try {
899
+ const { stdout } = await execGit(["check-ignore", "-z", "--stdin"], {
900
+ cwd: root,
901
+ maxBuffer: 4 * 1024 * 1024,
902
+ timeout: 1e4,
903
+ input: files.slice(offset, offset + 1e3).join("\0") + "\0"
904
+ });
905
+ for (const file of stdout.split("\0").filter(Boolean)) ignored.add(file);
906
+ } catch (error) {
907
+ if (error.code !== 1) throw error;
908
+ }
860
909
  }
910
+ return ignored;
861
911
  }
862
- var execute, MAX_INPUTS;
912
+ var MAX_INPUTS;
863
913
  var init_inputs = __esm({
864
914
  "src/audit/inputs.ts"() {
865
915
  "use strict";
866
916
  init_files();
867
917
  init_storage();
868
- execute = promisify3(execFile3);
918
+ init_git_read();
919
+ init_inspection();
869
920
  MAX_INPUTS = 1e5;
870
921
  }
871
922
  });
872
923
 
873
924
  // src/audit/docs.ts
874
925
  import fs4 from "fs/promises";
875
- import path6 from "path";
876
- import { execFile as execFile4 } from "child_process";
877
- import { promisify as promisify4 } from "util";
926
+ import path7 from "path";
878
927
  import fg3 from "fast-glob";
879
928
  async function dirtyDocs(resolvedRoot, files) {
880
929
  if (!files.length) return /* @__PURE__ */ new Set();
881
930
  try {
882
931
  const dirty = /* @__PURE__ */ new Set();
883
932
  for (let offset = 0; offset < files.length; offset += 256) {
884
- const { stdout } = await exec3(
885
- "git",
933
+ const { stdout } = await execGit(
886
934
  ["status", "--porcelain=v1", "-z", "--untracked-files=all", "--", ...files.slice(offset, offset + 256).map((file) => `:(literal)${file}`)],
887
935
  { cwd: resolvedRoot, maxBuffer: 16 * 1024 * 1024, timeout: 1e4 }
888
936
  );
@@ -902,25 +950,10 @@ async function dirtyDocs(resolvedRoot, files) {
902
950
  async function discoverDocPaths(root) {
903
951
  let candidates = [];
904
952
  try {
905
- const { stdout } = await exec3(
906
- "git",
907
- [
908
- "ls-files",
909
- "-z",
910
- "--cached",
911
- "--others",
912
- "--exclude-standard",
913
- "--",
914
- ":(icase,glob)**/readme.md",
915
- ":(icase,glob)**/agents.md",
916
- ":(icase,glob)**/claude.md"
917
- ],
918
- { cwd: root, maxBuffer: 16 * 1024 * 1024, timeout: 1e4 }
919
- );
920
- candidates = stdout.split("\0").filter(Boolean);
953
+ candidates = await auditGitPaths(root, "documents");
921
954
  } catch (error) {
922
955
  try {
923
- await exec3("git", ["rev-parse", "--git-dir"], { cwd: root });
956
+ await execGit(["rev-parse", "--git-dir"], { cwd: root });
924
957
  } catch {
925
958
  return localDocPaths(root);
926
959
  }
@@ -967,14 +1000,14 @@ async function localDocPaths(root) {
967
1000
  throw error;
968
1001
  }
969
1002
  for (const entry of entries) if (/^(?:agents|claude|readme)\.md$/i.test(entry.name)) {
970
- paths.push(path6.posix.join(dir, entry.name));
1003
+ paths.push(path7.posix.join(dir, entry.name));
971
1004
  }
972
1005
  }
973
1006
  return paths;
974
1007
  }
975
1008
  function documentScope(file) {
976
- const parent = path6.posix.dirname(file);
977
- return path6.posix.basename(parent).toLowerCase() === ".claude" ? path6.posix.dirname(parent) : parent;
1009
+ const parent = path7.posix.dirname(file);
1010
+ return path7.posix.basename(parent).toLowerCase() === ".claude" ? path7.posix.dirname(parent) : parent;
978
1011
  }
979
1012
  async function discoverDocs(resolvedRoot) {
980
1013
  return profilePhase("audit.documents", () => readDocs(resolvedRoot));
@@ -994,7 +1027,7 @@ async function readDocs(resolvedRoot) {
994
1027
  path: candidate,
995
1028
  content: content2,
996
1029
  scope: documentScope(candidate),
997
- kind: /^readme\.md$/i.test(path6.posix.basename(candidate)) ? "readme" : "instructions",
1030
+ kind: /^readme\.md$/i.test(path7.posix.basename(candidate)) ? "readme" : "instructions",
998
1031
  lineCount: content2.split("\n").length,
999
1032
  lastCommit,
1000
1033
  dirty: dirty.has(candidate),
@@ -1004,16 +1037,17 @@ async function readDocs(resolvedRoot) {
1004
1037
  }
1005
1038
  return docs;
1006
1039
  }
1007
- var exec3, DOC_CANDIDATES;
1040
+ var DOC_CANDIDATES;
1008
1041
  var init_docs = __esm({
1009
1042
  "src/audit/docs.ts"() {
1010
1043
  "use strict";
1044
+ init_git_read();
1011
1045
  init_profile();
1012
1046
  init_claims();
1013
1047
  init_git();
1014
1048
  init_inputs();
1015
1049
  init_files();
1016
- exec3 = promisify4(execFile4);
1050
+ init_inspection();
1017
1051
  DOC_CANDIDATES = [
1018
1052
  "AGENTS.md",
1019
1053
  "CLAUDE.md",
@@ -1024,14 +1058,14 @@ var init_docs = __esm({
1024
1058
 
1025
1059
  // src/audit/scope.ts
1026
1060
  import fs5 from "fs/promises";
1027
- import path7 from "path";
1061
+ import path8 from "path";
1028
1062
  function scopedPath(directory, value) {
1029
- if (path7.posix.isAbsolute(value) || value.includes("\\") || /[\x00-\x1f]/.test(value)) return null;
1030
- const resolved = path7.posix.normalize(path7.posix.join(directory, value));
1063
+ if (path8.posix.isAbsolute(value) || value.includes("\\") || /[\x00-\x1f]/.test(value)) return null;
1064
+ const resolved = path8.posix.normalize(path8.posix.join(directory, value));
1031
1065
  return resolved === ".." || resolved.startsWith("../") ? null : resolved;
1032
1066
  }
1033
1067
  function pathClaimScope(doc, claim) {
1034
- const directory = claim.relativeTo === "document" ? path7.posix.dirname(doc) : documentScope(doc);
1068
+ const directory = claim.relativeTo === "document" ? path8.posix.dirname(doc) : documentScope(doc);
1035
1069
  const local = scopedPath(directory, claim.path);
1036
1070
  if (!local) return null;
1037
1071
  const candidates = claim.relativeTo === "document" || directory === "." ? [local] : [...new Set([local, scopedPath(".", claim.path)].filter((p) => p !== null))];
@@ -1069,9 +1103,9 @@ var test_map_exports = {};
1069
1103
  __export(test_map_exports, {
1070
1104
  buildTestMap: () => buildTestMap
1071
1105
  });
1072
- import path8 from "path";
1106
+ import path9 from "path";
1073
1107
  async function buildTestMap(dir) {
1074
- const rootDir = path8.resolve(dir);
1108
+ const rootDir = path9.resolve(dir);
1075
1109
  const access = await createFileAccess(rootDir);
1076
1110
  const testPatterns = [
1077
1111
  "**/*.test.*",
@@ -1092,7 +1126,7 @@ async function buildTestMap(dir) {
1092
1126
  const sourceByBaseName = /* @__PURE__ */ new Map();
1093
1127
  for (const file of sourceFiles) {
1094
1128
  if (testFiles.includes(file)) continue;
1095
- const baseName = path8.basename(file).replace(/\.[^.]+$/, "");
1129
+ const baseName = path9.basename(file).replace(/\.[^.]+$/, "");
1096
1130
  const existing = sourceByBaseName.get(baseName) ?? [];
1097
1131
  existing.push(file);
1098
1132
  sourceByBaseName.set(baseName, existing);
@@ -1100,7 +1134,7 @@ async function buildTestMap(dir) {
1100
1134
  const paired = [];
1101
1135
  const unmatched = [];
1102
1136
  for (const testFile of testFiles) {
1103
- const testBaseName = path8.basename(testFile).replace(/\.[^.]+$/, "");
1137
+ const testBaseName = path9.basename(testFile).replace(/\.[^.]+$/, "");
1104
1138
  const sourceName = testBaseName.replace(/Test$|Tests$|Spec$|\.test$|\.spec$/, "").replace(/^test_|_test$/, "");
1105
1139
  if (!sourceName) {
1106
1140
  unmatched.push(testFile);
@@ -1108,10 +1142,10 @@ async function buildTestMap(dir) {
1108
1142
  }
1109
1143
  const candidates = sourceByBaseName.get(sourceName);
1110
1144
  if (candidates && candidates.length > 0) {
1111
- const testDir = path8.dirname(testFile);
1145
+ const testDir = path9.dirname(testFile);
1112
1146
  const bestMatch = candidates.reduce((best, candidate) => {
1113
- const candidateDir = path8.dirname(candidate);
1114
- const bestDir = path8.dirname(best);
1147
+ const candidateDir = path9.dirname(candidate);
1148
+ const bestDir = path9.dirname(best);
1115
1149
  const candidateOverlap = commonSegments(testDir, candidateDir);
1116
1150
  const bestOverlap = commonSegments(testDir, bestDir);
1117
1151
  return candidateOverlap > bestOverlap ? candidate : best;
@@ -1145,9 +1179,7 @@ var init_test_map = __esm({
1145
1179
  });
1146
1180
 
1147
1181
  // src/snapshot/snapshot.ts
1148
- import path9 from "path";
1149
- import { execFile as execFile5 } from "child_process";
1150
- import { promisify as promisify5 } from "util";
1182
+ import path10 from "path";
1151
1183
  import { z } from "zod";
1152
1184
  function normalizeFeatureType(value) {
1153
1185
  return value === "infrastructure" ? "infrastructure" : "capability";
@@ -1176,7 +1208,7 @@ async function saveSnapshot(rootDir, snapshot) {
1176
1208
  }
1177
1209
  async function getCurrentGitHash(rootDir) {
1178
1210
  try {
1179
- const { stdout } = await exec4("git", ["rev-parse", "HEAD"], {
1211
+ const { stdout } = await execGit(["rev-parse", "HEAD"], {
1180
1212
  cwd: rootDir
1181
1213
  });
1182
1214
  return stdout.trim();
@@ -1188,7 +1220,7 @@ async function listSourceFiles(resolvedRoot) {
1188
1220
  return (await createFileAccess(resolvedRoot)).list();
1189
1221
  }
1190
1222
  async function prepareSnapshotBatch(rootDir, offset, batchSize = DEFAULT_BATCH_SIZE, scopeFiles) {
1191
- const resolvedRoot = path9.resolve(rootDir);
1223
+ const resolvedRoot = path10.resolve(rootDir);
1192
1224
  const access = await createFileAccess(resolvedRoot);
1193
1225
  let allFiles = await access.list();
1194
1226
  if (scopeFiles) {
@@ -1237,16 +1269,16 @@ async function prepareSnapshotBatch(rootDir, offset, batchSize = DEFAULT_BATCH_S
1237
1269
  testPairs
1238
1270
  };
1239
1271
  }
1240
- var exec4, repoPath, verificationFields, featureSchema, flowSchema, snapshotSchema, DEFAULT_BATCH_SIZE, SKELETON_CHARS, DEEP_SAMPLE_CHARS, DEEP_SAMPLES_PER_BATCH;
1272
+ var repoPath, verificationFields, featureSchema, flowSchema, snapshotSchema, DEFAULT_BATCH_SIZE, SKELETON_CHARS, DEEP_SAMPLE_CHARS, DEEP_SAMPLES_PER_BATCH;
1241
1273
  var init_snapshot = __esm({
1242
1274
  "src/snapshot/snapshot.ts"() {
1243
1275
  "use strict";
1276
+ init_git_read();
1244
1277
  init_files();
1245
1278
  init_files();
1246
1279
  init_storage();
1247
1280
  init_paths();
1248
1281
  init_test_map();
1249
- exec4 = promisify5(execFile5);
1250
1282
  repoPath = z.string().refine((value) => normalizeRepoPath(value) !== null, "Expected a relative repository path");
1251
1283
  verificationFields = {
1252
1284
  refreshedHash: z.string().optional(),
@@ -1280,9 +1312,7 @@ var init_snapshot = __esm({
1280
1312
 
1281
1313
  // src/drift/drift.ts
1282
1314
  import fs6 from "fs/promises";
1283
- import path10 from "path";
1284
- import { execFile as execFile6 } from "child_process";
1285
- import { promisify as promisify6 } from "util";
1315
+ import path11 from "path";
1286
1316
  function parseChanges(output) {
1287
1317
  const fields = output.split("\0");
1288
1318
  const changes = [];
@@ -1303,7 +1333,7 @@ function touchedPaths(changes) {
1303
1333
  async function getChangesWithStatus(resolvedRoot, fromHash, toHash = "HEAD") {
1304
1334
  if (!fromHash || fromHash === "unknown" || fromHash.startsWith("-") || !toHash || toHash === "unknown" || toHash.startsWith("-")) return null;
1305
1335
  try {
1306
- const { stdout } = await exec5("git", ["diff", "--name-status", "-z", "-M", fromHash, toHash, "--"], { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 });
1336
+ const { stdout } = await execGit(["diff", "--name-status", "-z", "-M", fromHash, toHash, "--"], { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 });
1307
1337
  return parseChanges(stdout);
1308
1338
  } catch {
1309
1339
  return null;
@@ -1312,8 +1342,8 @@ async function getChangesWithStatus(resolvedRoot, fromHash, toHash = "HEAD") {
1312
1342
  async function getWorkingTree(resolvedRoot) {
1313
1343
  try {
1314
1344
  const [diff, untracked] = await Promise.all([
1315
- exec5("git", ["diff", "--name-status", "-z", "-M", "HEAD", "--"], { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 }),
1316
- exec5("git", ["ls-files", "-z", "--others", "--exclude-standard"], { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 })
1345
+ execGit(["diff", "--name-status", "-z", "-M", "HEAD", "--"], { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 }),
1346
+ execGit(["ls-files", "-z", "--others", "--exclude-standard"], { cwd: resolvedRoot, maxBuffer: 10 * 1024 * 1024 })
1317
1347
  ]);
1318
1348
  const untrackedFiles = untracked.stdout.split("\0").filter((f) => f && !f.startsWith(".mason/"));
1319
1349
  return { available: true, changedFiles: [.../* @__PURE__ */ new Set([...touchedPaths(parseChanges(diff.stdout)), ...untrackedFiles])].sort(), untrackedFiles };
@@ -1324,8 +1354,7 @@ async function getWorkingTree(resolvedRoot) {
1324
1354
  async function countCommitsBehind(resolvedRoot, fromHash) {
1325
1355
  if (!fromHash || fromHash === "unknown" || fromHash.startsWith("-")) return null;
1326
1356
  try {
1327
- const { stdout } = await exec5(
1328
- "git",
1357
+ const { stdout } = await execGit(
1329
1358
  ["rev-list", "--count", `${fromHash}..HEAD`],
1330
1359
  { cwd: resolvedRoot }
1331
1360
  );
@@ -1350,7 +1379,7 @@ async function findGhostFiles(resolvedRoot, mappedFiles) {
1350
1379
  const ghosts = [];
1351
1380
  for (const file of mappedFiles) {
1352
1381
  try {
1353
- await fs6.access(path10.join(resolvedRoot, file));
1382
+ await fs6.access(path11.join(resolvedRoot, file));
1354
1383
  } catch {
1355
1384
  ghosts.push(file);
1356
1385
  }
@@ -1358,7 +1387,7 @@ async function findGhostFiles(resolvedRoot, mappedFiles) {
1358
1387
  return ghosts.sort();
1359
1388
  }
1360
1389
  async function computeDrift(rootDir) {
1361
- const root = path10.resolve(rootDir);
1390
+ const root = path11.resolve(rootDir);
1362
1391
  const snapshot = await loadSnapshot(root);
1363
1392
  if (!snapshot) return null;
1364
1393
  const [headHash, workingTree] = await Promise.all([getCurrentGitHash(root), getWorkingTree(root)]);
@@ -1415,7 +1444,7 @@ async function computeDrift(rootDir) {
1415
1444
  const sourceFiles = new Set(await listSourceFiles(root));
1416
1445
  let committedFiles = /* @__PURE__ */ new Set();
1417
1446
  try {
1418
- const { stdout } = await exec5("git", ["ls-tree", "-r", "--name-only", "-z", "HEAD"], { cwd: root, maxBuffer: 50 * 1024 * 1024 });
1447
+ const { stdout } = await execGit(["ls-tree", "-r", "--name-only", "-z", "HEAD"], { cwd: root, maxBuffer: 50 * 1024 * 1024 });
1419
1448
  committedFiles = new Set(stdout.split("\0").filter(Boolean));
1420
1449
  } catch {
1421
1450
  report.historyAvailable = false;
@@ -1435,13 +1464,13 @@ async function computeDrift(rootDir) {
1435
1464
  else report.recommendation = changedMapped.size >= FULL_REBUILD_MIN_CHANGED_MAPPED_FILES && changedMapped.size / Math.max(1, mappedFiles.size) > FULL_REBUILD_FRACTION ? "full-rebuild" : "incremental";
1436
1465
  return report;
1437
1466
  }
1438
- var exec5, FULL_REBUILD_FRACTION, FULL_REBUILD_MIN_CHANGED_MAPPED_FILES;
1467
+ var FULL_REBUILD_FRACTION, FULL_REBUILD_MIN_CHANGED_MAPPED_FILES;
1439
1468
  var init_drift = __esm({
1440
1469
  "src/drift/drift.ts"() {
1441
1470
  "use strict";
1471
+ init_git_read();
1442
1472
  init_snapshot();
1443
1473
  init_paths();
1444
- exec5 = promisify6(execFile6);
1445
1474
  FULL_REBUILD_FRACTION = 0.4;
1446
1475
  FULL_REBUILD_MIN_CHANGED_MAPPED_FILES = 10;
1447
1476
  }
@@ -1464,7 +1493,7 @@ var init_types = __esm({
1464
1493
  });
1465
1494
 
1466
1495
  // src/audit/checks/deleted-reference.ts
1467
- import path11 from "path";
1496
+ import path12 from "path";
1468
1497
  async function checkDeletedReferences(ctx) {
1469
1498
  const result = emptyResult();
1470
1499
  for (const doc of ctx.docs) {
@@ -1493,7 +1522,7 @@ async function checkDeletedReferences(ctx) {
1493
1522
  const candidates = await Promise.all(scope.candidates.map(async (file) => ({
1494
1523
  file,
1495
1524
  tracked: await lastCommitOf(ctx.root, file),
1496
- parent: await pathExists(ctx.root, path11.posix.dirname(file))
1525
+ parent: await pathExists(ctx.root, path12.posix.dirname(file))
1497
1526
  })));
1498
1527
  const candidate = candidates.find((c) => c.tracked || renames.has(c.file)) ?? candidates.find((c) => c.parent);
1499
1528
  if (!candidate && claim.relativeTo !== "document") continue;
@@ -1533,7 +1562,7 @@ var init_deleted_reference = __esm({
1533
1562
  // src/audit/checks/new-module.ts
1534
1563
  import fg4 from "fast-glob";
1535
1564
  import fs7 from "fs/promises";
1536
- import path12 from "path";
1565
+ import path13 from "path";
1537
1566
  function escapeRegExp(text2) {
1538
1567
  return text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1539
1568
  }
@@ -1571,23 +1600,36 @@ async function checkNewModules(ctx) {
1571
1600
  return result;
1572
1601
  }
1573
1602
  function moduleDocumentation(docs) {
1574
- return docs.map((doc) => doc.content + "\n" + (path12.posix.dirname(doc.path) === "." ? "" : path12.posix.dirname(doc.path) + "/")).join("\n");
1603
+ return docs.map((doc) => doc.content + "\n" + (path13.posix.dirname(doc.path) === "." ? "" : path13.posix.dirname(doc.path) + "/")).join("\n");
1604
+ }
1605
+ function moduleCandidates(root, combinedDocs) {
1606
+ return inspectionRead(root, "modules:" + combinedDocs, () => collectModuleCandidates(root, combinedDocs));
1575
1607
  }
1576
- async function moduleCandidates(root, combinedDocs) {
1608
+ async function collectModuleCandidates(root, combinedDocs) {
1577
1609
  const [config, sourcePaths] = await Promise.all([loadProjectConfig(root), gitSourcePaths(root)]);
1578
1610
  const ignore = [...SOURCE_IGNORE, ...config.ignore ?? []];
1579
1611
  const sources = sourcePaths.filter((file) => !file.split("/").some((part) => part.startsWith(".")));
1580
- const listSubdirs = async (dir) => {
1581
- const entries = await fs7.readdir(await auditInputPath(root, dir), { withFileTypes: true });
1582
- 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));
1583
- const ignored = await gitIgnoredPaths(root, dirs);
1584
- const visible = new Set(dirs.filter((dir2) => !ignored.has(dir2) || sources.some((file) => file.startsWith(dir2 + "/"))));
1585
- return (await auditGlob(root, dir === "." ? "*" : `${fg4.escapePath(dir)}/*`, {
1586
- ignore,
1587
- onlyDirectories: true,
1588
- label: "Module directory discovery",
1589
- select: (file) => visible.has(file)
1590
- })).map((file) => path12.posix.basename(file));
1612
+ const listSubdirs = async (parents) => {
1613
+ const byParent = /* @__PURE__ */ new Map();
1614
+ for (let offset = 0; offset < parents.length; offset += 16) {
1615
+ await Promise.all(parents.slice(offset, offset + 16).map(async (dir) => {
1616
+ const entries = await fs7.readdir(await auditInputPath(root, dir), { withFileTypes: true });
1617
+ const dirs = entries.filter((entry) => (entry.isDirectory() || entry.isSymbolicLink() && sources.some((file) => file.startsWith(path13.posix.join(dir, entry.name) + "/"))) && !entry.name.startsWith(".") && !DIR_DENYLIST.has(entry.name)).map((entry) => path13.posix.join(dir, entry.name));
1618
+ byParent.set(dir, dirs);
1619
+ }));
1620
+ }
1621
+ const ignored = await gitIgnoredPaths(root, [...byParent.values()].flat());
1622
+ const result = /* @__PURE__ */ new Map();
1623
+ for (const dir of parents) {
1624
+ const visible = new Set(byParent.get(dir).filter((candidate) => !ignored.has(candidate) || sources.some((file) => file.startsWith(candidate + "/"))));
1625
+ result.set(dir, (await auditGlob(root, dir === "." ? "*" : `${fg4.escapePath(dir)}/*`, {
1626
+ ignore,
1627
+ onlyDirectories: true,
1628
+ label: "Module directory discovery",
1629
+ select: (file) => visible.has(file)
1630
+ })).map((file) => path13.posix.basename(file)));
1631
+ }
1632
+ return result;
1591
1633
  };
1592
1634
  const countSourceFiles = async (dir) => {
1593
1635
  const files = sources.filter((file) => file.startsWith(dir + "/"));
@@ -1603,14 +1645,16 @@ async function moduleCandidates(root, combinedDocs) {
1603
1645
  return count4;
1604
1646
  };
1605
1647
  const candidates = [];
1606
- for (const topDir of await listSubdirs(".")) {
1648
+ const topDirs = (await listSubdirs(["."])).get(".");
1649
+ const nested = await listSubdirs(topDirs.filter((dir) => isMentioned(combinedDocs, dir)));
1650
+ for (const topDir of topDirs) {
1607
1651
  const topMentioned = isMentioned(combinedDocs, topDir);
1608
1652
  if (!topMentioned) {
1609
1653
  const count4 = await countSourceFiles(topDir);
1610
1654
  if (count4 >= 1) candidates.push({ dir: topDir, sourceFileCount: count4 });
1611
1655
  continue;
1612
1656
  }
1613
- const subdirs = await listSubdirs(topDir);
1657
+ const subdirs = nested.get(topDir);
1614
1658
  const mentioned = subdirs.filter((s) => isMentioned(combinedDocs, s));
1615
1659
  if (mentioned.length < ENUMERATION_THRESHOLD) continue;
1616
1660
  for (const sub of subdirs) {
@@ -1627,6 +1671,7 @@ var DIR_DENYLIST, SECOND_LEVEL_MIN_SOURCE_FILES, ENUMERATION_THRESHOLD;
1627
1671
  var init_new_module = __esm({
1628
1672
  "src/audit/checks/new-module.ts"() {
1629
1673
  "use strict";
1674
+ init_inspection();
1630
1675
  init_files();
1631
1676
  init_inputs();
1632
1677
  init_git();
@@ -1656,7 +1701,7 @@ var init_new_module = __esm({
1656
1701
  });
1657
1702
 
1658
1703
  // src/audit/checks/stale-count.ts
1659
- import path13 from "path";
1704
+ import path14 from "path";
1660
1705
  async function countGradleModules(root) {
1661
1706
  for (const name of ["settings.gradle.kts", "settings.gradle"]) {
1662
1707
  const content2 = await readAuditInput(root, name);
@@ -1690,7 +1735,7 @@ async function countNpmWorkspaces(root) {
1690
1735
  return {
1691
1736
  actual: matched.length,
1692
1737
  countedFrom: "package.json workspaces",
1693
- members: matched.map((m) => path13.dirname(m)).sort()
1738
+ members: matched.map((m) => path14.dirname(m)).sort()
1694
1739
  };
1695
1740
  }
1696
1741
  }
@@ -1721,7 +1766,7 @@ async function countNpmWorkspaces(root) {
1721
1766
  return {
1722
1767
  actual: matched.length,
1723
1768
  countedFrom: "pnpm-workspace.yaml",
1724
- members: matched.map((m) => path13.dirname(m)).sort()
1769
+ members: matched.map((m) => path14.dirname(m)).sort()
1725
1770
  };
1726
1771
  }
1727
1772
  }
@@ -1743,8 +1788,8 @@ async function countCargoCrates(root) {
1743
1788
  ignore: ["**/target/**"],
1744
1789
  label: "Cargo workspace discovery"
1745
1790
  });
1746
- for (const m of matched) members.add(path13.dirname(m));
1747
- } else if (await readAuditInput(root, path13.posix.join(entry, "Cargo.toml")) !== null) {
1791
+ for (const m of matched) members.add(path14.dirname(m));
1792
+ } else if (await readAuditInput(root, path14.posix.join(entry, "Cargo.toml")) !== null) {
1748
1793
  members.add(entry);
1749
1794
  }
1750
1795
  }
@@ -1764,7 +1809,7 @@ async function resolveCountSource(root, claim) {
1764
1809
  }
1765
1810
  async function resolveDocCountSource(root, doc, claim) {
1766
1811
  const scope = documentScope(doc);
1767
- const source = await resolveCountSource(path13.join(root, scope), claim);
1812
+ const source = await resolveCountSource(path14.join(root, scope), claim);
1768
1813
  if (!source || scope === ".") return source;
1769
1814
  return {
1770
1815
  ...source,
@@ -1816,7 +1861,7 @@ var init_stale_count = __esm({
1816
1861
  });
1817
1862
 
1818
1863
  // src/audit/checks/dead-command.ts
1819
- import path14 from "path";
1864
+ import path15 from "path";
1820
1865
  function scriptsOf(raw) {
1821
1866
  try {
1822
1867
  const pkg = JSON.parse(raw);
@@ -1852,7 +1897,7 @@ async function commandInputs(root, docs) {
1852
1897
  }
1853
1898
  let cursor = directory;
1854
1899
  while (true) {
1855
- const manifest = path14.posix.join(cursor, "package.json");
1900
+ const manifest = path15.posix.join(cursor, "package.json");
1856
1901
  scope.candidates.push(manifest);
1857
1902
  const raw = await read(manifest);
1858
1903
  if (raw !== null) {
@@ -1863,7 +1908,7 @@ async function commandInputs(root, docs) {
1863
1908
  break;
1864
1909
  }
1865
1910
  if (claim.directory !== void 0 || cursor === ".") break;
1866
- cursor = path14.posix.dirname(cursor);
1911
+ cursor = path15.posix.dirname(cursor);
1867
1912
  }
1868
1913
  if (!resolution.manifest && !resolution.problem) {
1869
1914
  resolution.problem = `No package.json for the command's scope (${directory}).`;
@@ -1926,10 +1971,8 @@ var init_dead_command = __esm({
1926
1971
  });
1927
1972
 
1928
1973
  // src/audit/release-metadata.ts
1929
- import { execFile as execFile7 } from "child_process";
1930
- import { promisify as promisify7 } from "util";
1931
1974
  async function git(root, args) {
1932
- return (await exec6("git", args, { cwd: root, timeout: 1e4, maxBuffer: 2 * 1024 * 1024 })).stdout;
1975
+ return (await execGit(args, { cwd: root, timeout: 1e4, maxBuffer: 2 * 1024 * 1024 })).stdout;
1933
1976
  }
1934
1977
  function withoutAndroidReleaseValues(text2) {
1935
1978
  if (/\/\*|"""|'''/.test(text2) || !/id\s*\(?\s*["']com\.android\.(application|library)["']/.test(text2)) return null;
@@ -1975,11 +2018,10 @@ async function releaseMetadataOnly(root, commit) {
1975
2018
  return false;
1976
2019
  }
1977
2020
  }
1978
- var exec6;
1979
2021
  var init_release_metadata = __esm({
1980
2022
  "src/audit/release-metadata.ts"() {
1981
2023
  "use strict";
1982
- exec6 = promisify7(execFile7);
2024
+ init_git_read();
1983
2025
  }
1984
2026
  });
1985
2027
 
@@ -2075,7 +2117,7 @@ var init_deps_changed = __esm({
2075
2117
  });
2076
2118
 
2077
2119
  // src/context/lexical.ts
2078
- import path15 from "path";
2120
+ import path16 from "path";
2079
2121
  function tokenize(text2) {
2080
2122
  return text2.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 2 && !STOPWORDS.has(t));
2081
2123
  }
@@ -2088,7 +2130,7 @@ function tokenSet(text2) {
2088
2130
  function scoreEntry(taskTokens, entry) {
2089
2131
  const nameTokens = tokenSet(entry.name);
2090
2132
  const descTokens = tokenSet(entry.description);
2091
- const fileTokens = tokenSet(entry.files.map((f) => path15.basename(f)).join(" "));
2133
+ const fileTokens = tokenSet(entry.files.map((f) => path16.basename(f)).join(" "));
2092
2134
  let score = 0;
2093
2135
  for (const token of taskTokens) {
2094
2136
  if (nameTokens.has(token)) score += 3;
@@ -2420,7 +2462,7 @@ __export(decisions_exports, {
2420
2462
  withDecisionWrite: () => withDecisionWrite
2421
2463
  });
2422
2464
  import fs8 from "fs/promises";
2423
- import path16 from "path";
2465
+ import path17 from "path";
2424
2466
  import { createHash } from "crypto";
2425
2467
  import { z as z3 } from "zod";
2426
2468
  async function loadDecisionStore(rootDir) {
@@ -2518,7 +2560,7 @@ async function upsertDecision(rootDir, input) {
2518
2560
  if (input.files && files.length < input.files.length) warnings.push("some anchor paths were outside the repo or duplicated and were dropped");
2519
2561
  for (const file of files) {
2520
2562
  try {
2521
- await fs8.access(path16.join(rootDir, file));
2563
+ await fs8.access(path17.join(rootDir, file));
2522
2564
  } catch {
2523
2565
  warnings.push(`anchor file does not exist on disk: ${file}`);
2524
2566
  }
@@ -2643,9 +2685,9 @@ var init_decisions = __esm({
2643
2685
  });
2644
2686
 
2645
2687
  // src/decisions/drift.ts
2646
- import path17 from "path";
2688
+ import path18 from "path";
2647
2689
  async function computeDecisionDrift(rootDir, decisions) {
2648
- const resolvedRoot = path17.resolve(rootDir);
2690
+ const resolvedRoot = path18.resolve(rootDir);
2649
2691
  const store = decisions ? { records: decisions, diagnostics: [] } : await loadDecisionStore(resolvedRoot);
2650
2692
  const report = { historyAvailable: true, totalDecisions: store.records.length, staleDecisions: {}, freshness: {}, diagnostics: store.diagnostics };
2651
2693
  const [head, workingTree] = await Promise.all([getCurrentGitHash(resolvedRoot), getWorkingTree(resolvedRoot)]);
@@ -2967,8 +3009,6 @@ var init_store = __esm({
2967
3009
 
2968
3010
  // src/audit/advisory-review.ts
2969
3011
  import fs10 from "fs/promises";
2970
- import { execFile as execFile8 } from "child_process";
2971
- import { promisify as promisify8 } from "util";
2972
3012
  import { z as z6 } from "zod";
2973
3013
  async function readRecord(root, id) {
2974
3014
  const raw = await readStoreJson(root, recordPath(id));
@@ -3163,10 +3203,12 @@ async function reviewAdvisory(rootDir, input) {
3163
3203
  };
3164
3204
  });
3165
3205
  }
3166
- var execute2, git2, hashSchema, commitSchema2, evidenceSchema2, eventSchema2, recordSchema, reviewSummarySchema, advisoryReviewRequest, recordPath;
3206
+ var git2, hashSchema, commitSchema2, evidenceSchema2, eventSchema2, recordSchema, reviewSummarySchema, advisoryReviewRequest, recordPath;
3167
3207
  var init_advisory_review = __esm({
3168
3208
  "src/audit/advisory-review.ts"() {
3169
3209
  "use strict";
3210
+ init_inspection();
3211
+ init_git_read();
3170
3212
  init_findings();
3171
3213
  init_deps_changed();
3172
3214
  init_inputs();
@@ -3176,8 +3218,7 @@ var init_advisory_review = __esm({
3176
3218
  init_store();
3177
3219
  init_decisions();
3178
3220
  init_drift2();
3179
- execute2 = promisify8(execFile8);
3180
- git2 = async (root, ...args) => (await execute2("git", args, { cwd: root, timeout: 1e4, maxBuffer: 8 * 1024 * 1024 })).stdout;
3221
+ git2 = async (root, ...args) => (await (["log", "ls-tree", "ls-files", "merge-base"].includes(args[0]) ? inspectionGit : execGit)(args, { cwd: root, timeout: 1e4, maxBuffer: 8 * 1024 * 1024 })).stdout;
3181
3222
  hashSchema = z6.string().regex(/^[a-f0-9]{64}$/);
3182
3223
  commitSchema2 = z6.string().regex(/^[a-f0-9]{40,64}$/);
3183
3224
  evidenceSchema2 = z6.object({
@@ -3221,12 +3262,12 @@ var init_advisory_review = __esm({
3221
3262
 
3222
3263
  // src/audit/audit.ts
3223
3264
  import fs11 from "fs/promises";
3224
- import path18 from "path";
3265
+ import path19 from "path";
3225
3266
  async function computeAudit(rootDir, options = {}) {
3226
3267
  return profilePhase("audit.current", () => auditCurrent(rootDir, options));
3227
3268
  }
3228
3269
  async function auditCurrent(rootDir, options) {
3229
- const resolvedRoot = path18.resolve(rootDir);
3270
+ const resolvedRoot = path19.resolve(rootDir);
3230
3271
  const [docs, headHash] = await Promise.all([discoverDocs(resolvedRoot), getCurrentGitHash(resolvedRoot)]);
3231
3272
  if (docs.length === 0) return null;
3232
3273
  const report = {
@@ -3260,7 +3301,7 @@ async function auditCurrent(rootDir, options) {
3260
3301
  }
3261
3302
  let decisionsPresent = false;
3262
3303
  try {
3263
- await fs11.access(path18.join(resolvedRoot, ".mason", "decisions"));
3304
+ await fs11.access(path19.join(resolvedRoot, ".mason", "decisions"));
3264
3305
  decisionsPresent = true;
3265
3306
  } catch {
3266
3307
  }
@@ -3315,7 +3356,7 @@ __export(repair_exports, {
3315
3356
  withRepairSession: () => withRepairSession
3316
3357
  });
3317
3358
  import fs12 from "fs/promises";
3318
- import path19 from "path";
3359
+ import path20 from "path";
3319
3360
  import { randomUUID as randomUUID2 } from "crypto";
3320
3361
  import { z as z7 } from "zod";
3321
3362
  function recheckable(finding) {
@@ -3353,8 +3394,8 @@ async function readStableAudit(root, checks, options) {
3353
3394
  async function prepareRepair(rootDir, checks = ALL_CHECKS, options = {}) {
3354
3395
  return prepareOriginal(rootDir, checks, options);
3355
3396
  }
3356
- async function withRepairSession(root, options, run, cachedAudit) {
3357
- const before = await Promise.all([getCurrentGitHash(root), docState(root), advisoryReviewInventory(root).then(digest)]);
3397
+ async function withRepairSession(root, options, run, validate, cachedAudit) {
3398
+ const head = await getCurrentGitHash(root);
3358
3399
  const audits = /* @__PURE__ */ new Map();
3359
3400
  const documents = /* @__PURE__ */ new Map();
3360
3401
  const history = /* @__PURE__ */ new Map();
@@ -3365,9 +3406,9 @@ async function withRepairSession(root, options, run, cachedAudit) {
3365
3406
  };
3366
3407
  const session = {
3367
3408
  audit: (checks) => memo(audits, [...checks].sort().join(","), async () => {
3368
- if (cachedAudit && cachedAudit.headHash === before[0] && cachedAudit.root === root && digest([...checks].sort()) === digest([...cachedAudit.checksRun ?? []].sort())) {
3409
+ if (cachedAudit && cachedAudit.headHash === head && cachedAudit.root === root && digest([...checks].sort()) === digest([...cachedAudit.checksRun ?? []].sort())) {
3369
3410
  const current = structuredClone(cachedAudit);
3370
- const reviews = (await Promise.all(allFindings(current).map((f) => assessAdvisory(root, f, before[0])))).filter((r) => r !== null);
3411
+ const reviews = (await Promise.all(allFindings(current).map((f) => assessAdvisory(root, f, head)))).filter((r) => r !== null);
3371
3412
  delete current.advisoryReviews;
3372
3413
  if (reviews.length) current.advisoryReviews = reviews;
3373
3414
  return current;
@@ -3375,16 +3416,15 @@ async function withRepairSession(root, options, run, cachedAudit) {
3375
3416
  return computeAudit(root, { ...options, checks });
3376
3417
  }),
3377
3418
  document: (file) => memo(documents, file, async () => readBoundedFile(await storePath(root, file), 10 * 1024 * 1024)),
3378
- history: (head) => memo(history, head, () => getChangesWithStatus(root, head)),
3379
- assessment: (dir, finding, head) => memo(assessments, digest([dir, finding, head]), () => assessAdvisory(dir, finding, head))
3419
+ history: (head2) => memo(history, head2, () => getChangesWithStatus(root, head2)),
3420
+ assessment: (dir, finding, head2) => memo(assessments, digest([dir, finding, head2]), () => assessAdvisory(dir, finding, head2))
3380
3421
  };
3381
3422
  const result = await run({
3382
3423
  prepare: () => prepareOriginal(root, ALL_CHECKS, options, session),
3383
3424
  verify: (baseline) => profilePhase("repair.verify", () => verifyOriginal(root, baseline, options, session)),
3384
3425
  currentAudit: () => session.audit(ALL_CHECKS)
3385
3426
  });
3386
- const after = await Promise.all([getCurrentGitHash(root), docState(root), advisoryReviewInventory(root).then(digest)]);
3387
- if (digest(before) !== digest(after)) throw new Error("HEAD, context files or advisory review records changed during verification; retry against stable evidence.");
3427
+ await validate();
3388
3428
  return result;
3389
3429
  }
3390
3430
  async function prepareOriginal(rootDir, checks, options, session) {
@@ -3406,8 +3446,8 @@ async function prepareOriginal(rootDir, checks, options, session) {
3406
3446
  }
3407
3447
  async function loadRepairBaseline(rootDir, baselinePath) {
3408
3448
  const root = await fs12.realpath(rootDir);
3409
- const declaredRoot = path19.resolve(rootDir);
3410
- const relative = path19.isAbsolute(baselinePath) ? path19.relative(isWithinRoot(declaredRoot, baselinePath) ? declaredRoot : root, baselinePath) : baselinePath;
3449
+ const declaredRoot = path20.resolve(rootDir);
3450
+ const relative = path20.isAbsolute(baselinePath) ? path20.relative(isWithinRoot(declaredRoot, baselinePath) ? declaredRoot : root, baselinePath) : baselinePath;
3411
3451
  const stored = baselineSchema.parse(await readStoreJson(root, relative));
3412
3452
  const { digest: savedDigest, ...payload } = stored;
3413
3453
  if (digest(payload) !== savedDigest) throw new Error("Repair baseline was modified; use the original baseline.");
@@ -3582,14 +3622,12 @@ var init_repair = __esm({
3582
3622
 
3583
3623
  // src/automation/evidence.ts
3584
3624
  import fs13 from "fs/promises";
3585
- import path20 from "path";
3625
+ import path21 from "path";
3586
3626
  import { createHash as createHash3 } from "crypto";
3587
- import { execFile as execFile9 } from "child_process";
3588
- import { promisify as promisify9 } from "util";
3589
3627
  import fg5 from "fast-glob";
3590
3628
  import { z as z8 } from "zod";
3591
3629
  async function git3(root, ...args) {
3592
- return profilePhase("automation.git", async () => (await exec7("git", args, { cwd: root, maxBuffer: 16 * 1024 * 1024, timeout: 1e4 })).stdout);
3630
+ return profilePhase("automation.git", async () => (await execGit(args, { cwd: root, maxBuffer: 16 * 1024 * 1024, timeout: 1e4 })).stdout);
3593
3631
  }
3594
3632
  async function workspace(dir) {
3595
3633
  const root = await fs13.realpath((await git3(dir, "rev-parse", "--show-toplevel")).trim());
@@ -3613,7 +3651,7 @@ async function collectInputs(root) {
3613
3651
  const head = headText.trim();
3614
3652
  let shallow = null;
3615
3653
  try {
3616
- shallow = await fs13.readFile(path20.resolve(root, shallowPath.trim()), "utf8");
3654
+ shallow = await fs13.readFile(path21.resolve(root, shallowPath.trim()), "utf8");
3617
3655
  } catch (error) {
3618
3656
  if (error.code !== "ENOENT") throw error;
3619
3657
  }
@@ -3637,7 +3675,7 @@ async function collectInputs(root) {
3637
3675
  if (scope?.candidates.some(gitMetadataPath)) continue;
3638
3676
  for (const candidate of scope?.candidates ?? []) {
3639
3677
  if (optionalMasonPath(candidate)) continue;
3640
- const [filePresent, parentPresent] = await Promise.all([exists(candidate), exists(path20.posix.dirname(candidate))]);
3678
+ const [filePresent, parentPresent] = await Promise.all([exists(candidate), exists(path21.posix.dirname(candidate))]);
3641
3679
  claims.push([candidate, filePresent, parentPresent]);
3642
3680
  }
3643
3681
  }
@@ -3649,7 +3687,7 @@ async function collectInputs(root) {
3649
3687
  if (error.code === "ENOENT") return "absent";
3650
3688
  throw error;
3651
3689
  });
3652
- const [modules, counts, commands, decisionFiles] = await Promise.all([
3690
+ const [modules, counts, commands2, decisionFiles] = await Promise.all([
3653
3691
  combinedDocs ? moduleCandidates(root, combinedDocs) : [],
3654
3692
  Promise.all(countClaims.map(({ doc, claim }) => resolveDocCountSource(root, doc, claim))),
3655
3693
  commandInputs(root, parsedDocs),
@@ -3668,7 +3706,7 @@ async function collectInputs(root) {
3668
3706
  "deleted-reference": hash([common, claims, docStatus]),
3669
3707
  "new-module": hash([common, modules]),
3670
3708
  "stale-count": hash([common, counts]),
3671
- "dead-command": hash([common, commands]),
3709
+ "dead-command": hash([common, commands2]),
3672
3710
  "deps-changed": hash([common, docStatus]),
3673
3711
  "decision-anchor-drift": hash([common, decisionPresence, decisions, status, index2])
3674
3712
  };
@@ -3706,10 +3744,11 @@ function checkCache(raw, inputs) {
3706
3744
  }
3707
3745
  };
3708
3746
  }
3709
- var exec7, engineVersion, hash, content, cacheSchema;
3747
+ var engineVersion, hash, content, cacheSchema;
3710
3748
  var init_evidence = __esm({
3711
3749
  "src/automation/evidence.ts"() {
3712
3750
  "use strict";
3751
+ init_git_read();
3713
3752
  init_profile();
3714
3753
  init_docs();
3715
3754
  init_scope();
@@ -3722,8 +3761,7 @@ var init_evidence = __esm({
3722
3761
  init_dead_command();
3723
3762
  init_storage();
3724
3763
  init_advisory_review();
3725
- exec7 = promisify9(execFile9);
3726
- engineVersion = true ? "0.17.3" : "development";
3764
+ engineVersion = true ? "0.17.4" : "development";
3727
3765
  hash = (value) => createHash3("sha256").update(JSON.stringify(value)).digest("hex");
3728
3766
  content = readAuditInput;
3729
3767
  cacheSchema = z8.object({ version: z8.literal(1), entries: z8.record(z8.object({ key: z8.string(), result: checkResultSchema })), digest: z8.string() });
@@ -3783,8 +3821,8 @@ var init_model = __esm({
3783
3821
  });
3784
3822
 
3785
3823
  // src/setup/launcher.ts
3786
- import { execFile as execFile10 } from "child_process";
3787
- import { promisify as promisify10 } from "util";
3824
+ import { execFile as execFile2 } from "child_process";
3825
+ import { promisify as promisify2 } from "util";
3788
3826
  import fs14 from "fs/promises";
3789
3827
  function globalCommand(args, platform = process.platform) {
3790
3828
  return platform === "win32" ? { command: "cmd.exe", args: ["/d", "/s", "/c", "mason", ...args] } : { command: "mason", args };
@@ -3792,7 +3830,7 @@ function globalCommand(args, platform = process.platform) {
3792
3830
  async function installedCommand() {
3793
3831
  const invocation = globalCommand(["internal-integration-version"]);
3794
3832
  try {
3795
- const { stdout } = await promisify10(execFile10)(
3833
+ const { stdout } = await promisify2(execFile2)(
3796
3834
  invocation.command,
3797
3835
  invocation.args,
3798
3836
  { timeout: 5e3, maxBuffer: 8192, windowsHide: true }
@@ -3815,7 +3853,7 @@ var init_launcher = __esm({
3815
3853
  "use strict";
3816
3854
  init_evidence();
3817
3855
  init_model();
3818
- executingVersion = () => true ? "0.17.3" : "development";
3856
+ executingVersion = () => true ? "0.17.4" : "development";
3819
3857
  mcpCommand = (host) => globalCommand(["--setup-host", host, "mcp"]);
3820
3858
  hookCommand = (host) => `mason --setup-host ${host} auto`;
3821
3859
  }
@@ -3829,7 +3867,7 @@ __export(files_exports, {
3829
3867
  readText: () => readText
3830
3868
  });
3831
3869
  import fs17 from "fs/promises";
3832
- import path24 from "path";
3870
+ import path25 from "path";
3833
3871
  import { randomUUID as randomUUID3 } from "crypto";
3834
3872
  async function readText(root, file) {
3835
3873
  try {
@@ -3862,7 +3900,7 @@ async function applyEdit(root, edit) {
3862
3900
  return true;
3863
3901
  }
3864
3902
  const file = await storePath(root, edit.path, true);
3865
- const temporary = path24.join(path24.dirname(file), ".mason-setup-" + randomUUID3() + ".tmp");
3903
+ const temporary = path25.join(path25.dirname(file), ".mason-setup-" + randomUUID3() + ".tmp");
3866
3904
  try {
3867
3905
  const mode = await fs17.stat(file).then((s) => s.mode & 511, () => 384);
3868
3906
  const handle = await fs17.open(temporary, "wx", mode);
@@ -4164,12 +4202,12 @@ async function matchingAnalysis(ws, state, inputs) {
4164
4202
  return { analysis: a };
4165
4203
  }
4166
4204
  async function verifyInputs(ws, inputs) {
4167
- return profilePhase("automation.revalidate", async () => {
4205
+ return profilePhase("automation.revalidate", () => withRepositoryInspection(ws.root, async () => {
4168
4206
  const [after, currentWs] = await Promise.all([readInputs(ws.root), workspace(ws.root)]);
4169
4207
  if (after.fingerprint !== inputs.fingerprint || currentWs.directory !== ws.directory) {
4170
4208
  throw new Error("Repository inputs or branch changed during automation; no current verification was recorded. Retry on a stable checkout.");
4171
4209
  }
4172
- });
4210
+ }));
4173
4211
  }
4174
4212
  async function computeAnalysis(ws, state, inputs, event, previousAudit, cacheDiagnostic) {
4175
4213
  const baselines = [...state.baselines];
@@ -4189,7 +4227,10 @@ async function computeAnalysis(ws, state, inputs, event, previousAudit, cacheDia
4189
4227
  capture: "unknown",
4190
4228
  scope: SCOPE
4191
4229
  };
4192
- if (!baselines.length && Object.values(inputs.docs).every((value) => value === null)) return { baselines, report: empty, cache: null };
4230
+ if (!baselines.length && Object.values(inputs.docs).every((value) => value === null)) {
4231
+ await verifyInputs(ws, inputs);
4232
+ return { baselines, report: empty, cache: null };
4233
+ }
4193
4234
  if (ws.branch === "detached" && state.latest) {
4194
4235
  const previous = await readStoreJson(ws.root, state.latest);
4195
4236
  if (!previous?.head || !/^[a-f0-9]{40,64}$/.test(previous.head)) throw new Error("The previous detached checkout evidence is unavailable.");
@@ -4227,7 +4268,7 @@ async function computeAnalysis(ws, state, inputs, event, previousAudit, cacheDia
4227
4268
  }
4228
4269
  currentAudit = await repairs.currentAudit();
4229
4270
  return verifications2;
4230
- }, cachedAudit);
4271
+ }, () => verifyInputs(ws, inputs), cachedAudit);
4231
4272
  const merged = /* @__PURE__ */ new Map();
4232
4273
  for (const verification of verifications) {
4233
4274
  for (const finding of verification.findings) {
@@ -4262,7 +4303,6 @@ async function analyze(ws, inputs, event, startedAt) {
4262
4303
  if (previous && previous.completedAt >= startedAt) return { analysis: previous, shared: true };
4263
4304
  const originalDigest = await baselineDigest(ws, before);
4264
4305
  const computed = await computeAnalysis(ws, before, inputs, event, previous?.currentAudit, diagnostic);
4265
- await verifyInputs(ws, inputs);
4266
4306
  if (originalDigest !== await baselineDigest(ws, before)) throw new Error("Repair baseline was modified during automation; original evidence must be inspected.");
4267
4307
  const id = randomUUID5();
4268
4308
  const analysis = analysisSchema.parse({
@@ -4291,7 +4331,7 @@ async function analyze(ws, inputs, event, startedAt) {
4291
4331
  async function automate(dir, event) {
4292
4332
  const startedAt = Date.now();
4293
4333
  const ws = await profilePhase("automation.workspace", () => workspace(dir));
4294
- return recordExecution(ws.root, ws.directory, event.event, async () => {
4334
+ return recordExecution(ws.root, ws.directory, event.event, () => withRepositoryInspection(ws.root, async () => {
4295
4335
  const inputs = await readInputs(ws.root);
4296
4336
  const result = await profilePhase("automation.analysis", () => analyze(ws, inputs, event, startedAt));
4297
4337
  if (result.shared) {
@@ -4364,7 +4404,7 @@ async function automate(dir, event) {
4364
4404
  await writeStoreJson(ws.root, ws.directory + "/state.json", state);
4365
4405
  return { report, message: notify ? summarize(report) : null, continueOnce };
4366
4406
  }));
4367
- });
4407
+ }));
4368
4408
  }
4369
4409
  async function automationStatus(dir) {
4370
4410
  const ws = await profilePhase("automation.workspace", () => workspace(dir));
@@ -4402,6 +4442,7 @@ var init_runtime = __esm({
4402
4442
  "src/automation/runtime.ts"() {
4403
4443
  "use strict";
4404
4444
  init_profile();
4445
+ init_inspection();
4405
4446
  init_execution();
4406
4447
  init_repair();
4407
4448
  init_types();
@@ -4447,7 +4488,7 @@ var init_runtime = __esm({
4447
4488
 
4448
4489
  // src/automation/hook-root.ts
4449
4490
  import fs19 from "fs/promises";
4450
- import path25 from "path";
4491
+ import path26 from "path";
4451
4492
  var init_hook_root = __esm({
4452
4493
  "src/automation/hook-root.ts"() {
4453
4494
  "use strict";
@@ -4916,13 +4957,13 @@ If a build is interrupted, its partials remain in \`.mason/partial-snapshots/\`.
4916
4957
  });
4917
4958
 
4918
4959
  // src/review/cochange.ts
4919
- import { execFile as execFile15 } from "child_process";
4920
- import { promisify as promisify15 } from "util";
4960
+ import { execFile as execFile6 } from "child_process";
4961
+ import { promisify as promisify6 } from "util";
4921
4962
  async function buildMatrix(resolvedRoot) {
4922
4963
  try {
4923
- const { stdout: shallow } = await exec12("git", ["rev-parse", "--is-shallow-repository"], { cwd: resolvedRoot });
4964
+ const { stdout: shallow } = await exec4("git", ["rev-parse", "--is-shallow-repository"], { cwd: resolvedRoot });
4924
4965
  if (shallow.trim() === "true") return null;
4925
- const { stdout } = await exec12(
4966
+ const { stdout } = await exec4(
4926
4967
  "git",
4927
4968
  ["log", `-n${HISTORY_COMMITS}`, "--format=%x01", "--name-only", "-M"],
4928
4969
  { cwd: resolvedRoot, maxBuffer: 100 * 1024 * 1024 }
@@ -4978,11 +5019,11 @@ async function findMissingPartners(resolvedRoot, changedFiles, existsOnDisk, all
4978
5019
  findings.sort((a, b) => b.rate - a.rate || b.sharedCommits - a.sharedCommits);
4979
5020
  return findings;
4980
5021
  }
4981
- var exec12, HISTORY_COMMITS, MIN_FILE_COMMITS, MIN_SHARED_COMMITS, MIN_COCHANGE_RATE, MAX_COMMIT_FILES;
5022
+ var exec4, HISTORY_COMMITS, MIN_FILE_COMMITS, MIN_SHARED_COMMITS, MIN_COCHANGE_RATE, MAX_COMMIT_FILES;
4982
5023
  var init_cochange = __esm({
4983
5024
  "src/review/cochange.ts"() {
4984
5025
  "use strict";
4985
- exec12 = promisify15(execFile15);
5026
+ exec4 = promisify6(execFile6);
4986
5027
  HISTORY_COMMITS = 1500;
4987
5028
  MIN_FILE_COMMITS = 5;
4988
5029
  MIN_SHARED_COMMITS = 4;
@@ -5290,10 +5331,10 @@ var init_check = __esm({
5290
5331
  });
5291
5332
 
5292
5333
  // src/review/evidence.ts
5293
- import path26 from "path";
5334
+ import path27 from "path";
5294
5335
  import { z as z22 } from "zod";
5295
5336
  function relativeArtifact(root, file) {
5296
- const relative = path26.isAbsolute(file) ? path26.relative(root, file) : file;
5337
+ const relative = path27.isAbsolute(file) ? path27.relative(root, file) : file;
5297
5338
  const normalized = normalizeRepoPath(relative);
5298
5339
  if (!normalized) throw new Error(`Evidence artifact must be inside the repository: ${file}`);
5299
5340
  return normalized;
@@ -5390,7 +5431,7 @@ async function collectReviewEvidence(root, manifests, changedFiles, decisions, d
5390
5431
  const report = await readStoreJson(root, file);
5391
5432
  if (report === null) throw new Error(`Report artifact is missing: ${file}`);
5392
5433
  const sourceRoot = check.sourceRoot ?? root;
5393
- if (!path26.posix.isAbsolute(sourceRoot) && !/^[A-Za-z]:[\\/]/.test(sourceRoot)) throw new Error("sourceRoot must identify the absolute checkout root on the check runner.");
5434
+ if (!path27.posix.isAbsolute(sourceRoot) && !/^[A-Za-z]:[\\/]/.test(sourceRoot)) throw new Error("sourceRoot must identify the absolute checkout root on the check runner.");
5394
5435
  const parsed = check.report.format === "vitest-json" ? parseVitest(report, sourceRoot) : check.report.format === "mason-check-json" ? parseCheck(report, sourceRoot) : parseSarif(report, sourceRoot);
5395
5436
  result.outcome = parsed.outcome;
5396
5437
  result.counts = parsed.counts;
@@ -5512,12 +5553,12 @@ var init_evidence2 = __esm({
5512
5553
 
5513
5554
  // src/review/review.ts
5514
5555
  import fs20 from "fs/promises";
5515
- import path27 from "path";
5516
- import { execFile as execFile16 } from "child_process";
5517
- import { promisify as promisify16 } from "util";
5556
+ import path28 from "path";
5557
+ import { execFile as execFile7 } from "child_process";
5558
+ import { promisify as promisify7 } from "util";
5518
5559
  async function resolveMergeBase(resolvedRoot, base, head) {
5519
5560
  try {
5520
- const { stdout } = await exec13("git", ["merge-base", base, head], {
5561
+ const { stdout } = await exec5("git", ["merge-base", base, head], {
5521
5562
  cwd: resolvedRoot
5522
5563
  });
5523
5564
  return stdout.trim() || null;
@@ -5528,7 +5569,7 @@ async function resolveMergeBase(resolvedRoot, base, head) {
5528
5569
  async function defaultBase(resolvedRoot) {
5529
5570
  for (const ref of ["origin/HEAD", "origin/main", "origin/master", "main"]) {
5530
5571
  try {
5531
- await exec13("git", ["rev-parse", "--verify", "--quiet", ref], {
5572
+ await exec5("git", ["rev-parse", "--verify", "--quiet", ref], {
5532
5573
  cwd: resolvedRoot
5533
5574
  });
5534
5575
  return ref;
@@ -5541,7 +5582,7 @@ function anchorsTouched(record, changedFiles) {
5541
5582
  return matchingPaths(record.files, changedFiles);
5542
5583
  }
5543
5584
  async function computeReview(rootDir, base, options = {}) {
5544
- const resolvedRoot = path27.resolve(rootDir);
5585
+ const resolvedRoot = path28.resolve(rootDir);
5545
5586
  const head = await getCurrentGitHash(resolvedRoot);
5546
5587
  if (head === "unknown") return null;
5547
5588
  const mergeBase = await resolveMergeBase(resolvedRoot, base, head);
@@ -5591,7 +5632,7 @@ async function computeReview(rootDir, base, options = {}) {
5591
5632
  analyzed,
5592
5633
  async (relPath) => {
5593
5634
  try {
5594
- await fs20.access(path27.join(resolvedRoot, relPath));
5635
+ await fs20.access(path28.join(resolvedRoot, relPath));
5595
5636
  return true;
5596
5637
  } catch {
5597
5638
  return false;
@@ -5624,7 +5665,7 @@ async function computeReview(rootDir, base, options = {}) {
5624
5665
  }
5625
5666
  return finalize();
5626
5667
  }
5627
- var exec13, MAX_ANALYZED_FILES;
5668
+ var exec5, MAX_ANALYZED_FILES;
5628
5669
  var init_review = __esm({
5629
5670
  "src/review/review.ts"() {
5630
5671
  "use strict";
@@ -5636,7 +5677,7 @@ var init_review = __esm({
5636
5677
  init_cochange();
5637
5678
  init_evidence2();
5638
5679
  init_snapshot();
5639
- exec13 = promisify16(execFile16);
5680
+ exec5 = promisify7(execFile7);
5640
5681
  MAX_ANALYZED_FILES = 50;
5641
5682
  }
5642
5683
  });
@@ -5726,7 +5767,7 @@ var init_onboarding = __esm({
5726
5767
  });
5727
5768
 
5728
5769
  // src/impact/references.ts
5729
- import path28 from "path";
5770
+ import path29 from "path";
5730
5771
  function templateEnd(content2, start) {
5731
5772
  let i = start + 1, interpolation = 0;
5732
5773
  while (i < content2.length) {
@@ -5852,10 +5893,10 @@ function jvmImports(content2) {
5852
5893
  }
5853
5894
  function resolveImport(file, specifier, available) {
5854
5895
  if (!specifier.startsWith("./") && !specifier.startsWith("../")) return null;
5855
- const relative = path28.posix.normalize(path28.posix.join(path28.posix.dirname(file), specifier));
5896
+ const relative = path29.posix.normalize(path29.posix.join(path29.posix.dirname(file), specifier));
5856
5897
  if (relative.startsWith("../")) return null;
5857
5898
  if (available.has(relative)) return relative;
5858
- const ext = path28.posix.extname(relative);
5899
+ const ext = path29.posix.extname(relative);
5859
5900
  const replacements = { ".js": [".ts", ".tsx"], ".mjs": [".mts"], ".cjs": [".cts"] };
5860
5901
  const candidates = ext ? (replacements[ext] ?? []).map((e) => relative.slice(0, -ext.length) + e) : jsExtensions.flatMap((e) => [relative + e, relative + "/index" + e]);
5861
5902
  const found = candidates.filter((f) => available.has(f));
@@ -5867,13 +5908,13 @@ function mentionsPath(content2, target) {
5867
5908
  function createReferenceMatcher(targets, available, targetContents = /* @__PURE__ */ new Map()) {
5868
5909
  const counts = /* @__PURE__ */ new Map();
5869
5910
  for (const file of available) {
5870
- const name = path28.posix.basename(file, path28.posix.extname(file));
5911
+ const name = path29.posix.basename(file, path29.posix.extname(file));
5871
5912
  counts.set(name, (counts.get(name) ?? 0) + 1);
5872
5913
  }
5873
5914
  const jvmTargets = new Map([...targetContents].filter(([file]) => /\.(?:kt|kts|java)$/.test(file)).map(([file, content2]) => [file, declaredSymbols(content2)]));
5874
5915
  return (source) => {
5875
5916
  if (targets.includes(source.path)) return [];
5876
- const resolved = new Set(jsExtensions.includes(path28.posix.extname(source.path)) ? imports(source.content).map((s) => resolveImport(source.path, s, available)).filter(Boolean) : []);
5917
+ const resolved = new Set(jsExtensions.includes(path29.posix.extname(source.path)) ? imports(source.content).map((s) => resolveImport(source.path, s, available)).filter(Boolean) : []);
5877
5918
  if (/\.(?:kt|kts|java)$/.test(source.path)) {
5878
5919
  const imported = jvmImports(source.content);
5879
5920
  for (const [target, symbols] of jvmTargets) if (symbols.some((symbol) => imported.some((name) => name === symbol || name.startsWith(symbol + ".")))) resolved.add(target);
@@ -5889,7 +5930,7 @@ function createReferenceMatcher(targets, available, targetContents = /* @__PURE_
5889
5930
  add("explicit-path", target);
5890
5931
  continue;
5891
5932
  }
5892
- const ext = path28.posix.extname(target), name = path28.posix.basename(target, ext);
5933
+ const ext = path29.posix.extname(target), name = path29.posix.basename(target, ext);
5893
5934
  if (!/\.(?:[cm]?[jt]sx?|py|kt|java|go|rs|swift|rb|cs|cpp|c|h|hpp|dart|php)$/.test(ext) || name.startsWith(".") || name.length < 3 || genericNames.has(name.toLowerCase()) || counts.get(name) !== 1) continue;
5894
5935
  if (word(name).test(source.content)) {
5895
5936
  add("name-candidate", name);
@@ -5918,11 +5959,11 @@ var impact_exports = {};
5918
5959
  __export(impact_exports, {
5919
5960
  analyzeImpact: () => analyzeImpact
5920
5961
  });
5921
- import path29 from "path";
5922
- import { execFile as execFile17 } from "child_process";
5923
- import { promisify as promisify17 } from "util";
5962
+ import path30 from "path";
5963
+ import { execFile as execFile8 } from "child_process";
5964
+ import { promisify as promisify8 } from "util";
5924
5965
  async function analyzeImpact(rootDir, targetFiles) {
5925
- const resolvedRoot = path29.resolve(rootDir);
5966
+ const resolvedRoot = path30.resolve(rootDir);
5926
5967
  const resolvedTargets = await resolveTargetFiles(resolvedRoot, targetFiles);
5927
5968
  const [cochange, references, tests] = await Promise.all([
5928
5969
  getCochangeFiles(resolvedRoot, resolvedTargets),
@@ -5965,7 +6006,7 @@ async function getCochangeFiles(rootDir, targetFiles) {
5965
6006
  let totalTargetCommits = 0;
5966
6007
  for (const targetFile of targetFiles) {
5967
6008
  try {
5968
- const { stdout: commitLog } = await exec14(
6009
+ const { stdout: commitLog } = await exec6(
5969
6010
  "git",
5970
6011
  ["log", "--format=%H", "-n", "500", "--", targetFile],
5971
6012
  { cwd: rootDir, maxBuffer: 5e6 }
@@ -5975,7 +6016,7 @@ async function getCochangeFiles(rootDir, targetFiles) {
5975
6016
  if (commits.length === 0) continue;
5976
6017
  for (const commit of commits) {
5977
6018
  try {
5978
- const { stdout: filesInCommit } = await exec14(
6019
+ const { stdout: filesInCommit } = await exec6(
5979
6020
  "git",
5980
6021
  ["diff-tree", "--no-commit-id", "--name-only", "-r", commit],
5981
6022
  { cwd: rootDir }
@@ -6036,9 +6077,9 @@ async function getRelatedTests(rootDir, targetFiles) {
6036
6077
  const testFiles = await (await createFileAccess(rootDir)).list(testPatterns);
6037
6078
  const results = [];
6038
6079
  for (const target of targetFiles) {
6039
- const targetBaseName = path29.basename(target).replace(/\.[^.]+$/, "");
6080
+ const targetBaseName = path30.basename(target).replace(/\.[^.]+$/, "");
6040
6081
  for (const testFile of testFiles) {
6041
- const testBaseName = path29.basename(testFile).replace(/\.[^.]+$/, "");
6082
+ const testBaseName = path30.basename(testFile).replace(/\.[^.]+$/, "");
6042
6083
  const sourceName = testBaseName.replace(/Test$|Tests$|Spec$|\.test$|\.spec$/, "").replace(/^test_|_test$/, "");
6043
6084
  if (sourceName === targetBaseName) {
6044
6085
  results.push({
@@ -6050,14 +6091,14 @@ async function getRelatedTests(rootDir, targetFiles) {
6050
6091
  }
6051
6092
  return results;
6052
6093
  }
6053
- var exec14;
6094
+ var exec6;
6054
6095
  var init_impact = __esm({
6055
6096
  "src/impact/impact.ts"() {
6056
6097
  "use strict";
6057
6098
  init_files();
6058
6099
  init_paths();
6059
6100
  init_references();
6060
- exec14 = promisify17(execFile17);
6101
+ exec6 = promisify8(execFile8);
6061
6102
  }
6062
6103
  });
6063
6104
 
@@ -6066,13 +6107,13 @@ var assemble_exports = {};
6066
6107
  __export(assemble_exports, {
6067
6108
  assembleContext: () => assembleContext
6068
6109
  });
6069
- import path30 from "path";
6110
+ import path31 from "path";
6070
6111
  import fs21 from "fs/promises";
6071
6112
  async function collectImpact(root, candidates) {
6072
6113
  const targets = /* @__PURE__ */ new Set();
6073
6114
  let sourceFiles;
6074
6115
  for (const candidate of sanitizeRepoPaths(candidates)) {
6075
- const stat = await fs21.stat(path30.join(root, candidate)).catch(() => null);
6116
+ const stat = await fs21.stat(path31.join(root, candidate)).catch(() => null);
6076
6117
  if (stat?.isDirectory()) {
6077
6118
  sourceFiles ??= await (await createFileAccess(root)).list();
6078
6119
  for (const file of sourceFiles) {
@@ -6098,7 +6139,7 @@ async function collectImpact(root, candidates) {
6098
6139
  };
6099
6140
  }
6100
6141
  async function assembleContext(rootDir, task, files) {
6101
- const resolvedRoot = path30.resolve(rootDir);
6142
+ const resolvedRoot = path31.resolve(rootDir);
6102
6143
  const mapState = await inspectSnapshot(resolvedRoot);
6103
6144
  const snapshot = mapState.snapshot;
6104
6145
  const store = await loadDecisionStore(resolvedRoot);
@@ -6641,8 +6682,8 @@ __export(client_exports, {
6641
6682
  function createConfluenceClient(config, fetchFn = fetch) {
6642
6683
  const baseUrl = config.baseUrl.replace(/\/+$/, "");
6643
6684
  const auth = "Basic " + Buffer.from(`${config.email}:${config.apiToken}`).toString("base64");
6644
- async function call(method, path34, body) {
6645
- const res = await fetchFn(`${baseUrl}${path34}`, {
6685
+ async function call(method, path35, body) {
6686
+ const res = await fetchFn(`${baseUrl}${path35}`, {
6646
6687
  method,
6647
6688
  headers: {
6648
6689
  Authorization: auth,
@@ -6654,7 +6695,7 @@ function createConfluenceClient(config, fetchFn = fetch) {
6654
6695
  if (!res.ok) {
6655
6696
  const text2 = await res.text();
6656
6697
  throw new Error(
6657
- `Confluence ${method} ${path34} failed: ${res.status} ${res.statusText} \u2014 ${text2}`
6698
+ `Confluence ${method} ${path35} failed: ${res.status} ${res.statusText} \u2014 ${text2}`
6658
6699
  );
6659
6700
  }
6660
6701
  if (res.status === 204) return null;
@@ -6756,15 +6797,15 @@ __export(config_exports2, {
6756
6797
  validateProvider: () => validateProvider
6757
6798
  });
6758
6799
  import fs23 from "fs/promises";
6759
- import path31 from "path";
6800
+ import path32 from "path";
6760
6801
  import os3 from "os";
6761
- import { execFile as execFile18 } from "child_process";
6762
- import { promisify as promisify18 } from "util";
6802
+ import { execFile as execFile9 } from "child_process";
6803
+ import { promisify as promisify9 } from "util";
6763
6804
  function configDir() {
6764
- return path31.join(os3.homedir(), ".mason");
6805
+ return path32.join(os3.homedir(), ".mason");
6765
6806
  }
6766
6807
  function configFile() {
6767
- return path31.join(configDir(), "config.json");
6808
+ return path32.join(configDir(), "config.json");
6768
6809
  }
6769
6810
  async function loadConfig() {
6770
6811
  try {
@@ -6794,7 +6835,7 @@ async function detectCLI(provider) {
6794
6835
  const cliName = provider === "claude" ? "claude" : provider === "gemini" ? "gemini" : provider === "ollama" ? "ollama" : null;
6795
6836
  if (!cliName) return { available: false };
6796
6837
  try {
6797
- const { stdout } = await exec15(cliName, ["--version"]);
6838
+ const { stdout } = await exec7(cliName, ["--version"]);
6798
6839
  return { available: true, version: stdout.trim() };
6799
6840
  } catch {
6800
6841
  return { available: false };
@@ -6811,11 +6852,11 @@ async function loadConfluenceConfig() {
6811
6852
  const config = await loadConfig();
6812
6853
  return config?.confluence ?? null;
6813
6854
  }
6814
- var exec15, DEFAULT_MODELS;
6855
+ var exec7, DEFAULT_MODELS;
6815
6856
  var init_config2 = __esm({
6816
6857
  "src/llm/config.ts"() {
6817
6858
  "use strict";
6818
- exec15 = promisify18(execFile18);
6859
+ exec7 = promisify9(execFile9);
6819
6860
  DEFAULT_MODELS = {
6820
6861
  claude: "claude-sonnet-4-20250514",
6821
6862
  gemini: "gemini-2.5-flash",
@@ -6937,16 +6978,16 @@ var init_renderer = __esm({
6937
6978
 
6938
6979
  // src/confluence/diff.ts
6939
6980
  import fs24 from "fs/promises";
6940
- import path32 from "path";
6981
+ import path33 from "path";
6941
6982
  import { createHash as createHash5 } from "crypto";
6942
6983
  function hashDescription(description) {
6943
6984
  return createHash5("sha256").update(description, "utf8").digest("hex");
6944
6985
  }
6945
6986
  function syncStateDir(rootDir) {
6946
- return path32.join(rootDir, ".mason");
6987
+ return path33.join(rootDir, ".mason");
6947
6988
  }
6948
6989
  function syncStatePath(rootDir) {
6949
- return path32.join(syncStateDir(rootDir), "confluence-sync.json");
6990
+ return path33.join(syncStateDir(rootDir), "confluence-sync.json");
6950
6991
  }
6951
6992
  async function loadSyncState(rootDir) {
6952
6993
  try {
@@ -7014,8 +7055,8 @@ var init_diff = __esm({
7014
7055
  });
7015
7056
 
7016
7057
  // src/llm/providers.ts
7017
- import { execFile as execFile19, spawn } from "child_process";
7018
- import { promisify as promisify19 } from "util";
7058
+ import { execFile as execFile10, spawn } from "child_process";
7059
+ import { promisify as promisify10 } from "util";
7019
7060
  async function callLLM(config, userMessage, systemPrompt) {
7020
7061
  const model = config.model ?? getDefaultModel(config.provider);
7021
7062
  const system = systemPrompt ?? CLAUDE_MD_SYSTEM_PROMPT;
@@ -7172,12 +7213,12 @@ async function callOpenAIAPI(apiKey, model, system, userMessage) {
7172
7213
  });
7173
7214
  return response.choices[0]?.message?.content ?? "";
7174
7215
  }
7175
- var exec16, CLAUDE_MD_SYSTEM_PROMPT;
7216
+ var exec8, CLAUDE_MD_SYSTEM_PROMPT;
7176
7217
  var init_providers = __esm({
7177
7218
  "src/llm/providers.ts"() {
7178
7219
  "use strict";
7179
7220
  init_config2();
7180
- exec16 = promisify19(execFile19);
7221
+ exec8 = promisify10(execFile10);
7181
7222
  CLAUDE_MD_SYSTEM_PROMPT = `You are Mason, a context engineering tool. You've been given a comprehensive analysis of a codebase including:
7182
7223
  - Git history stats (commit patterns, frequently changed files, stale directories)
7183
7224
  - Project structure (directory layout, file counts by type)
@@ -7519,20 +7560,20 @@ import { z as z24 } from "zod";
7519
7560
  init_repair();
7520
7561
  init_advisory_review();
7521
7562
  import fs25 from "fs/promises";
7522
- import path33 from "path";
7523
- import { execFile as execFile20 } from "child_process";
7524
- import { promisify as promisify20 } from "util";
7563
+ import path34 from "path";
7564
+ import { execFile as execFile11 } from "child_process";
7565
+ import { promisify as promisify11 } from "util";
7525
7566
 
7526
7567
  // src/audit/cli.ts
7527
7568
  init_audit();
7528
7569
  init_types();
7529
7570
  init_repair();
7530
7571
  init_findings();
7531
- import path22 from "path";
7572
+ import path23 from "path";
7532
7573
 
7533
7574
  // src/audit/advisory-cli.ts
7534
7575
  init_advisory_review();
7535
- import path21 from "path";
7576
+ import path22 from "path";
7536
7577
  import { stripVTControlCharacters } from "util";
7537
7578
 
7538
7579
  // src/audit/cli.ts
@@ -7625,8 +7666,8 @@ function formatFixPrompt(report, baselinePath) {
7625
7666
  }
7626
7667
 
7627
7668
  // src/analyzers/git-history.ts
7628
- import { execFile as execFile11 } from "child_process";
7629
- import { promisify as promisify11 } from "util";
7669
+ import { execFile as execFile3 } from "child_process";
7670
+ import { promisify as promisify3 } from "util";
7630
7671
 
7631
7672
  // src/analyzers/base.ts
7632
7673
  import fs15 from "fs/promises";
@@ -7663,7 +7704,7 @@ var BaseAnalyzer = class {
7663
7704
  };
7664
7705
 
7665
7706
  // src/analyzers/git-history.ts
7666
- var exec8 = promisify11(execFile11);
7707
+ var exec = promisify3(execFile3);
7667
7708
  var GitHistoryAnalyzer = class extends BaseAnalyzer {
7668
7709
  name = "git-history";
7669
7710
  async analyze(context) {
@@ -7684,7 +7725,7 @@ var GitHistoryAnalyzer = class extends BaseAnalyzer {
7684
7725
  }
7685
7726
  async git(args, cwd) {
7686
7727
  try {
7687
- const { stdout } = await exec8("git", args, { cwd, maxBuffer: 1e7 });
7728
+ const { stdout } = await exec("git", args, { cwd, maxBuffer: 1e7 });
7688
7729
  return stdout.trim();
7689
7730
  } catch {
7690
7731
  return "";
@@ -7833,12 +7874,10 @@ async function runAll(context) {
7833
7874
  }
7834
7875
 
7835
7876
  // src/utils/git.ts
7836
- import { execFile as execFile12 } from "child_process";
7837
- import { promisify as promisify12 } from "util";
7838
- var exec9 = promisify12(execFile12);
7877
+ init_git_read();
7839
7878
  async function isGitRepo(dir) {
7840
7879
  try {
7841
- await exec9("git", ["rev-parse", "--git-dir"], { cwd: dir });
7880
+ await execGit(["rev-parse", "--git-dir"], { cwd: dir });
7842
7881
  return true;
7843
7882
  } catch {
7844
7883
  return false;
@@ -7847,10 +7886,10 @@ async function isGitRepo(dir) {
7847
7886
 
7848
7887
  // src/mcp/sampler.ts
7849
7888
  init_files();
7850
- import path23 from "path";
7851
- import { execFile as execFile13 } from "child_process";
7852
- import { promisify as promisify13 } from "util";
7853
- var exec10 = promisify13(execFile13);
7889
+ import path24 from "path";
7890
+ import { execFile as execFile4 } from "child_process";
7891
+ import { promisify as promisify4 } from "util";
7892
+ var exec2 = promisify4(execFile4);
7854
7893
  var CONFIG_FILES = [
7855
7894
  // Build & project config
7856
7895
  "package.json",
@@ -7955,8 +7994,8 @@ async function sampleFiles(rootDir, maxFiles = 25) {
7955
7994
  const projectConfig = access.config;
7956
7995
  for (const filePath of projectConfig.alwaysInclude ?? []) {
7957
7996
  if (selected.size >= maxFiles) break;
7958
- const resolvedPath = path23.resolve(rootDir, filePath);
7959
- if (!resolvedPath.startsWith(path23.resolve(rootDir))) continue;
7997
+ const resolvedPath = path24.resolve(rootDir, filePath);
7998
+ if (!resolvedPath.startsWith(path24.resolve(rootDir))) continue;
7960
7999
  selected.set(filePath, "always-include (project config)");
7961
8000
  }
7962
8001
  let configCount = 0;
@@ -8012,7 +8051,7 @@ async function sampleFiles(rootDir, maxFiles = 25) {
8012
8051
  }
8013
8052
  }
8014
8053
  try {
8015
- const { stdout } = await exec10(
8054
+ const { stdout } = await exec2(
8016
8055
  "git",
8017
8056
  ["log", "--since=3 months ago", "--format=", "--name-only"],
8018
8057
  { cwd: rootDir, maxBuffer: 5e6 }
@@ -8022,7 +8061,7 @@ async function sampleFiles(rootDir, maxFiles = 25) {
8022
8061
  if (!line) continue;
8023
8062
  if (line.includes("node_modules") || line.includes("/build/") || line.includes(".gradle") || line.includes("/generated/"))
8024
8063
  continue;
8025
- const ext = path23.extname(line).slice(1);
8064
+ const ext = path24.extname(line).slice(1);
8026
8065
  if (!SOURCE_EXTENSIONS.includes(ext)) continue;
8027
8066
  fileCounts.set(line, (fileCounts.get(line) ?? 0) + 1);
8028
8067
  }
@@ -8142,10 +8181,10 @@ init_drift();
8142
8181
  init_paths();
8143
8182
  init_files();
8144
8183
  import { createHash as createHash4 } from "crypto";
8145
- import { execFile as execFile14 } from "child_process";
8146
- import { promisify as promisify14 } from "util";
8184
+ import { execFile as execFile5 } from "child_process";
8185
+ import { promisify as promisify5 } from "util";
8147
8186
  import { z as z10 } from "zod";
8148
- var exec11 = promisify14(execFile14);
8187
+ var exec3 = promisify5(execFile5);
8149
8188
  var requestSchema = z10.object({
8150
8189
  id: z10.string().regex(/^[a-zA-Z0-9_-]+$/),
8151
8190
  action: z10.enum(["prepare", "accept", "reaffirm", "retire"]).default("prepare"),
@@ -8200,7 +8239,7 @@ async function previews(root, record, state) {
8200
8239
  let diffUnavailable;
8201
8240
  if (state.evidence.historyAvailable && diffPaths.length) {
8202
8241
  try {
8203
- const { stdout } = await exec11("git", ["--literal-pathspecs", "diff", "--no-ext-diff", "--no-textconv", "--no-color", "--no-renames", "-U3", record.refreshedHash, state.evidence.headHash, "--", ...diffPaths], { cwd: root, maxBuffer: 1024 * 1024 });
8242
+ const { stdout } = await exec3("git", ["--literal-pathspecs", "diff", "--no-ext-diff", "--no-textconv", "--no-color", "--no-renames", "-U3", record.refreshedHash, state.evidence.headHash, "--", ...diffPaths], { cwd: root, maxBuffer: 1024 * 1024 });
8204
8243
  diff = stdout.slice(0, 16e3);
8205
8244
  if (stdout.length > diff.length) diffUnavailable = "Diff preview was truncated; inspect the full diff before reviewing.";
8206
8245
  } catch {
@@ -8426,7 +8465,7 @@ function batchIdFor(offset) {
8426
8465
  // src/mcp/tools.ts
8427
8466
  init_init();
8428
8467
  init_onboarding();
8429
- var exec17 = promisify20(execFile20);
8468
+ var exec9 = promisify11(execFile11);
8430
8469
  async function buildContext(dir) {
8431
8470
  return {
8432
8471
  rootDir: dir,
@@ -8434,7 +8473,7 @@ async function buildContext(dir) {
8434
8473
  };
8435
8474
  }
8436
8475
  async function analyzeProject(dir) {
8437
- const rootDir = path33.resolve(dir);
8476
+ const rootDir = path34.resolve(dir);
8438
8477
  const context = await buildContext(rootDir);
8439
8478
  const results = await runAll(context);
8440
8479
  const projectSnapshot = await detectProjectSnapshot(rootDir);
@@ -8489,7 +8528,7 @@ async function detectProjectSnapshot(rootDir) {
8489
8528
  const present = [];
8490
8529
  for (const file of buildFiles) {
8491
8530
  try {
8492
- await fs25.access(path33.join(rootDir, file));
8531
+ await fs25.access(path34.join(rootDir, file));
8493
8532
  present.push(file);
8494
8533
  } catch {
8495
8534
  }
@@ -8531,7 +8570,7 @@ async function detectProjectSnapshot(rootDir) {
8531
8570
  const sourceFiles = await access.list();
8532
8571
  const fileCounts = {};
8533
8572
  for (const file of sourceFiles) {
8534
- const ext = path33.extname(file).slice(1);
8573
+ const ext = path34.extname(file).slice(1);
8535
8574
  fileCounts[ext] = (fileCounts[ext] ?? 0) + 1;
8536
8575
  }
8537
8576
  return {
@@ -8542,7 +8581,7 @@ async function detectProjectSnapshot(rootDir) {
8542
8581
  };
8543
8582
  }
8544
8583
  async function getCodeSamples(dir, count4 = 15) {
8545
- const rootDir = path33.resolve(dir);
8584
+ const rootDir = path34.resolve(dir);
8546
8585
  const samples = await sampleFiles(rootDir, count4);
8547
8586
  const output = {
8548
8587
  note: "These are previews (first ~60 lines). Read the file directly with your own tools to see it in full.",
@@ -8585,7 +8624,7 @@ async function unmappedContextResponse(rootDir) {
8585
8624
  });
8586
8625
  }
8587
8626
  async function getProjectStructure(dir) {
8588
- const rootDir = path33.resolve(dir);
8627
+ const rootDir = path34.resolve(dir);
8589
8628
  const allFiles = await (await createFileAccess(rootDir)).list("**/*");
8590
8629
  const dirInfo = /* @__PURE__ */ new Map();
8591
8630
  for (const file of allFiles) {
@@ -8597,7 +8636,7 @@ async function getProjectStructure(dir) {
8597
8636
  }
8598
8637
  const info = dirInfo.get(dirPath);
8599
8638
  info.fileCount++;
8600
- const ext = path33.extname(file).slice(1);
8639
+ const ext = path34.extname(file).slice(1);
8601
8640
  if (ext) {
8602
8641
  info.extensions.set(ext, (info.extensions.get(ext) ?? 0) + 1);
8603
8642
  }
@@ -8642,7 +8681,7 @@ async function buildChangedFilePreviews(rootDir, changedFiles) {
8642
8681
  return previews2;
8643
8682
  }
8644
8683
  async function getSnapshot(dir) {
8645
- const rootDir = path33.resolve(dir);
8684
+ const rootDir = path34.resolve(dir);
8646
8685
  const snapshot = await loadSnapshot(rootDir);
8647
8686
  if (!snapshot) {
8648
8687
  return unmappedContextResponse(rootDir);
@@ -8743,7 +8782,7 @@ function driftHint(report) {
8743
8782
  return "Read the changed files under staleFeatures/staleFlows, update those entries (fold unmappedFiles into the right features), and call save_snapshot with only the affected entries \u2014 unchanged entries are preserved. Drop ghostFiles from any entries that reference them, and delete features/flows that no longer exist via save_snapshot's removeFeatures/removeFlows.";
8744
8783
  }
8745
8784
  async function checkDrift(dir) {
8746
- const rootDir = path33.resolve(dir);
8785
+ const rootDir = path34.resolve(dir);
8747
8786
  const report = await computeDrift(rootDir);
8748
8787
  if (!report) {
8749
8788
  return JSON.stringify({
@@ -8774,7 +8813,7 @@ async function checkDrift(dir) {
8774
8813
  return JSON.stringify({ exists: true, ...report, verification, hint });
8775
8814
  }
8776
8815
  async function generateSnapshotBatch(dir, offset = 0, batchSize = DEFAULT_BATCH_SIZE, files) {
8777
- const rootDir = path33.resolve(dir);
8816
+ const rootDir = path34.resolve(dir);
8778
8817
  const scoped = files !== void 0 && files.length > 0;
8779
8818
  const scopeFiles = scoped ? sanitizePaths(rootDir, files) : void 0;
8780
8819
  const batch = await prepareSnapshotBatch(dir, offset, batchSize, scopeFiles);
@@ -8821,7 +8860,7 @@ async function generateSnapshotBatch(dir, offset = 0, batchSize = DEFAULT_BATCH_
8821
8860
  );
8822
8861
  }
8823
8862
  async function saveSnapshotPartial(dir, batchId, offset, features, flows) {
8824
- const rootDir = path33.resolve(dir);
8863
+ const rootDir = path34.resolve(dir);
8825
8864
  for (const feat of Object.values(features)) {
8826
8865
  feat.files = sanitizePaths(rootDir, feat.files);
8827
8866
  if (feat.tests) feat.tests = sanitizePaths(rootDir, feat.tests);
@@ -8850,7 +8889,7 @@ async function saveSnapshotPartial(dir, batchId, offset, features, flows) {
8850
8889
  );
8851
8890
  }
8852
8891
  async function reduceSnapshot(dir) {
8853
- const rootDir = path33.resolve(dir);
8892
+ const rootDir = path34.resolve(dir);
8854
8893
  const partials = await loadAllPartials(rootDir);
8855
8894
  if (partials.length === 0) {
8856
8895
  return JSON.stringify(
@@ -8912,7 +8951,7 @@ async function reduceSnapshot(dir) {
8912
8951
  );
8913
8952
  }
8914
8953
  async function fullAnalysis(dir) {
8915
- const rootDir = path33.resolve(dir);
8954
+ const rootDir = path34.resolve(dir);
8916
8955
  const [analysis, structure, samples, testMap, snapshot] = await Promise.all([
8917
8956
  analyzeProject(dir),
8918
8957
  getProjectStructure(dir),
@@ -8941,7 +8980,7 @@ function sanitizePaths(rootDir, files) {
8941
8980
  return sanitizeRepoPaths(files);
8942
8981
  }
8943
8982
  async function saveSnapshotData(dir, features, flows, removeFeatures = [], removeFlows = []) {
8944
- const rootDir = path33.resolve(dir);
8983
+ const rootDir = path34.resolve(dir);
8945
8984
  const gitHash = await getCurrentGitHash(rootDir);
8946
8985
  const now = (/* @__PURE__ */ new Date()).toISOString();
8947
8986
  for (const feat of Object.values(features)) {
@@ -9025,7 +9064,7 @@ async function saveSnapshotData(dir, features, flows, removeFeatures = [], remov
9025
9064
  });
9026
9065
  }
9027
9066
  async function getImpact(dir, files) {
9028
- const rootDir = path33.resolve(dir);
9067
+ const rootDir = path34.resolve(dir);
9029
9068
  const { analyzeImpact: analyzeImpact2 } = await Promise.resolve().then(() => (init_impact(), impact_exports));
9030
9069
  const result = await analyzeImpact2(rootDir, files);
9031
9070
  return JSON.stringify(result, null, 2);
@@ -9034,7 +9073,7 @@ var VERIFY_DEFAULT_SAMPLE = 5;
9034
9073
  var VERIFY_MAX_FILES_PER_ENTRY = 8;
9035
9074
  var VERIFY_SKELETON_CHARS = 500;
9036
9075
  async function verifySnapshot(dir, sample = VERIFY_DEFAULT_SAMPLE) {
9037
- const rootDir = path33.resolve(dir);
9076
+ const rootDir = path34.resolve(dir);
9038
9077
  const snapshot = await loadSnapshot(rootDir);
9039
9078
  if (!snapshot) {
9040
9079
  return JSON.stringify({
@@ -9099,7 +9138,7 @@ async function verifySnapshot(dir, sample = VERIFY_DEFAULT_SAMPLE) {
9099
9138
  });
9100
9139
  }
9101
9140
  async function saveVerification(dir, verdicts) {
9102
- const rootDir = path33.resolve(dir);
9141
+ const rootDir = path34.resolve(dir);
9103
9142
  const snapshot = await loadSnapshot(rootDir);
9104
9143
  if (!snapshot) {
9105
9144
  return JSON.stringify({ exists: false, hint: "No concept map exists." });
@@ -9137,16 +9176,16 @@ async function saveVerification(dir, verdicts) {
9137
9176
  });
9138
9177
  }
9139
9178
  async function saveDecision(dir, input) {
9140
- const rootDir = path33.resolve(dir);
9179
+ const rootDir = path34.resolve(dir);
9141
9180
  const { upsertDecision: upsertDecision2 } = await Promise.resolve().then(() => (init_decisions(), decisions_exports));
9142
9181
  const result = await upsertDecision2(rootDir, input);
9143
9182
  return JSON.stringify(result);
9144
9183
  }
9145
9184
  async function reviewDecision2(dir, input) {
9146
- return JSON.stringify(await reviewDecision(path33.resolve(dir), input));
9185
+ return JSON.stringify(await reviewDecision(path34.resolve(dir), input));
9147
9186
  }
9148
9187
  async function getContext(dir, task, files) {
9149
- const rootDir = path33.resolve(dir);
9188
+ const rootDir = path34.resolve(dir);
9150
9189
  const { assembleContext: assembleContext2 } = await Promise.resolve().then(() => (init_assemble(), assemble_exports));
9151
9190
  const bundle = await assembleContext2(rootDir, task, files);
9152
9191
  return JSON.stringify(bundle);
@@ -9195,7 +9234,7 @@ async function masonInit(dir, options = {}) {
9195
9234
  return JSON.stringify(await setupProject2(dir, options), null, 2);
9196
9235
  }
9197
9236
  if (options.host) throw new Error("host applies only to mode: setup.");
9198
- const rootDir = path33.resolve(dir);
9237
+ const rootDir = path34.resolve(dir);
9199
9238
  const marker = await loadProjectMarker(rootDir);
9200
9239
  const { loadSetup: loadSetup2 } = await Promise.resolve().then(() => (init_model(), model_exports));
9201
9240
  const setup = await loadSetup2(rootDir);
@@ -9215,7 +9254,7 @@ async function masonInit(dir, options = {}) {
9215
9254
  );
9216
9255
  }
9217
9256
  async function masonCompleteInit(dir, options = {}) {
9218
- const rootDir = path33.resolve(dir);
9257
+ const rootDir = path34.resolve(dir);
9219
9258
  const existing = await loadProjectMarker(rootDir);
9220
9259
  const features = await loadProjectFeatures(rootDir);
9221
9260
  const marker = {
@@ -9324,7 +9363,7 @@ async function masonSetConfluence(input) {
9324
9363
  );
9325
9364
  }
9326
9365
  async function exportToConfluenceTool(dir, overrides) {
9327
- const rootDir = path33.resolve(dir);
9366
+ const rootDir = path34.resolve(dir);
9328
9367
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config2(), config_exports2));
9329
9368
  const { exportToConfluence: exportToConfluence2 } = await Promise.resolve().then(() => (init_sync(), sync_exports));
9330
9369
  const config = await loadConfig2();
@@ -9363,7 +9402,7 @@ function createMcpServer() {
9363
9402
  const server = new McpServer(
9364
9403
  {
9365
9404
  name: "mason",
9366
- version: "0.17.3"
9405
+ version: "0.17.4"
9367
9406
  },
9368
9407
  {
9369
9408
  instructions: (setupNotice ? setupNotice + "\n\n" : "") + 'Mason retrieves recorded decisions, file impact, and optional feature/flow maps. Use get_context with the task and known files, and get_impact before editing. Save learned rationale and constraints with save_decision; review and commit the local records through the project workflow. These tools work without initialization or a concept map. Consult trust and diagnostics: changed or unknown freshness needs source inspection, failed verification needs correction, and proposals are suggestions and legacy records are unreviewed. Accepted decisions are recorded team constraints, still subject to freshness checks. Use review_decision to prepare code evidence and record only authorized acceptance, reaffirmation, or retirement. Never invent a reviewer or treat these recorded identities as authenticated approval. mason_init returns documentation audit and committed-diff review findings with a quickstart guide. Use mode: "map" only when a full architecture map is requested. A missing map is not a setup failure; use decisions and source evidence. get_snapshot provides architecture navigation when a map is available. The mason-audit and mason-review CLIs also work without setup.'