mason-context 0.21.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/README.md +10 -5
- package/dist/mason-audit.js +159 -95
- package/dist/mason-audit.js.map +1 -1
- package/dist/mason-drift.js +90 -53
- package/dist/mason-drift.js.map +1 -1
- package/dist/mason-hook.js +97 -45
- package/dist/mason-hook.js.map +1 -1
- package/dist/mason-mcp.js +3425 -5255
- package/dist/mason-mcp.js.map +1 -1
- package/dist/mason-review.js +180 -125
- package/dist/mason-review.js.map +1 -1
- package/dist/mason-runtime.js +13 -6
- package/dist/mason-runtime.js.map +1 -1
- package/dist/mason.js +1808 -1882
- package/dist/mason.js.map +1 -1
- package/package.json +1 -4
package/dist/mason-audit.js
CHANGED
|
@@ -405,11 +405,12 @@ async function storePath(root, relative, createParents = false) {
|
|
|
405
405
|
}
|
|
406
406
|
return current;
|
|
407
407
|
}
|
|
408
|
-
async function readStoreJson(root, relative) {
|
|
408
|
+
async function readStoreJson(root, relative, maxBytes = STORE_MAX_BYTES) {
|
|
409
409
|
try {
|
|
410
410
|
const file = await storePath(root, relative);
|
|
411
|
-
const raw = await readBoundedFile(file,
|
|
412
|
-
if (raw === null)
|
|
411
|
+
const raw = await readBoundedFile(file, maxBytes);
|
|
412
|
+
if (raw === null)
|
|
413
|
+
throw new Error(`file is not regular or exceeds ${maxBytes / 1024 / 1024} MiB`);
|
|
413
414
|
const parsed = JSON.parse(raw);
|
|
414
415
|
if (parsed === null) throw new Error("expected a JSON object, received null");
|
|
415
416
|
return parsed;
|
|
@@ -421,9 +422,9 @@ async function readStoreJson(root, relative) {
|
|
|
421
422
|
);
|
|
422
423
|
}
|
|
423
424
|
}
|
|
424
|
-
async function writeStoreJson(root, relative, value) {
|
|
425
|
-
const payload = JSON.stringify(value, null, 2) + "\n";
|
|
426
|
-
if (Buffer.byteLength(payload) >
|
|
425
|
+
async function writeStoreJson(root, relative, value, options = {}) {
|
|
426
|
+
const payload = JSON.stringify(value, null, options.compact ? void 0 : 2) + "\n";
|
|
427
|
+
if (Buffer.byteLength(payload) > STORE_MAX_BYTES) {
|
|
427
428
|
throw new Error(`Mason store ${relative} exceeds 10 MiB`);
|
|
428
429
|
}
|
|
429
430
|
const file = await storePath(root, relative, true);
|
|
@@ -441,11 +442,13 @@ async function writeStoreJson(root, relative, value) {
|
|
|
441
442
|
await fs2.rm(temporary, { force: true });
|
|
442
443
|
}
|
|
443
444
|
}
|
|
445
|
+
var STORE_MAX_BYTES;
|
|
444
446
|
var init_storage = __esm({
|
|
445
447
|
"src/utils/storage.ts"() {
|
|
446
448
|
"use strict";
|
|
447
449
|
init_paths();
|
|
448
450
|
init_files();
|
|
451
|
+
STORE_MAX_BYTES = 10 * 1024 * 1024;
|
|
449
452
|
}
|
|
450
453
|
});
|
|
451
454
|
|
|
@@ -775,17 +778,7 @@ var init_lock = __esm({
|
|
|
775
778
|
}
|
|
776
779
|
});
|
|
777
780
|
|
|
778
|
-
// src/test-map.ts
|
|
779
|
-
import path6 from "path";
|
|
780
|
-
var init_test_map = __esm({
|
|
781
|
-
"src/test-map.ts"() {
|
|
782
|
-
"use strict";
|
|
783
|
-
init_files();
|
|
784
|
-
}
|
|
785
|
-
});
|
|
786
|
-
|
|
787
781
|
// src/snapshot/snapshot.ts
|
|
788
|
-
import path7 from "path";
|
|
789
782
|
import { z as z2 } from "zod";
|
|
790
783
|
async function getCurrentGitHash(rootDir) {
|
|
791
784
|
try {
|
|
@@ -803,11 +796,9 @@ var init_snapshot = __esm({
|
|
|
803
796
|
"use strict";
|
|
804
797
|
init_git_read();
|
|
805
798
|
init_files();
|
|
806
|
-
init_files();
|
|
807
799
|
init_storage();
|
|
808
800
|
init_lock();
|
|
809
801
|
init_paths();
|
|
810
|
-
init_test_map();
|
|
811
802
|
repoPath = z2.string().refine((value) => normalizeRepoPath(value) !== null, "Expected a relative repository path");
|
|
812
803
|
verificationFields = {
|
|
813
804
|
refreshedHash: z2.string().optional(),
|
|
@@ -838,7 +829,7 @@ var init_snapshot = __esm({
|
|
|
838
829
|
|
|
839
830
|
// src/drift/drift.ts
|
|
840
831
|
import fs5 from "fs/promises";
|
|
841
|
-
import
|
|
832
|
+
import path6 from "path";
|
|
842
833
|
function parseChanges(output) {
|
|
843
834
|
const fields = output.split("\0");
|
|
844
835
|
const changes = [];
|
|
@@ -906,7 +897,7 @@ var init_drift = __esm({
|
|
|
906
897
|
});
|
|
907
898
|
|
|
908
899
|
// src/audit/commands.ts
|
|
909
|
-
import
|
|
900
|
+
import path7 from "path";
|
|
910
901
|
function splitCommands(line) {
|
|
911
902
|
const parts = [];
|
|
912
903
|
let quote = "", start = 0;
|
|
@@ -942,7 +933,7 @@ function commandClaims(line, lineNo, cwd, shell = false) {
|
|
|
942
933
|
const cd = text2.match(/^cd\s+(.+)$/);
|
|
943
934
|
if (cd) {
|
|
944
935
|
const directory = literal(cd[1]);
|
|
945
|
-
cwd = directory === null || cwd === null || /\s/.test(directory) && !/^["']/.test(cd[1]) ? null :
|
|
936
|
+
cwd = directory === null || cwd === null || /\s/.test(directory) && !/^["']/.test(cd[1]) ? null : path7.posix.join(cwd ?? ".", directory);
|
|
946
937
|
continue;
|
|
947
938
|
}
|
|
948
939
|
for (const match of text2.matchAll(COMMAND)) {
|
|
@@ -954,7 +945,7 @@ function commandClaims(line, lineNo, cwd, shell = false) {
|
|
|
954
945
|
scopeUnknown = "Unsupported shell wrapper or options before the script.";
|
|
955
946
|
if (selector) {
|
|
956
947
|
const selected = literal(selector[1]);
|
|
957
|
-
directory = selected === null || cwd === null ? null :
|
|
948
|
+
directory = selected === null || cwd === null ? null : path7.posix.join(cwd ?? ".", selected);
|
|
958
949
|
} else if (flags) scopeUnknown = "Unsupported package-manager options before run: " + flags;
|
|
959
950
|
const tail = text2.slice(match.index + match[0].length).split(/\s--(?:\s|$)/)[0];
|
|
960
951
|
if (/(?:^|\s)(?:--workspace(?:s)?|-w|--filter|-F|--prefix|--dir|-C|--if-present)(?:\s|=|$)/.test(
|
|
@@ -989,7 +980,7 @@ var init_commands = __esm({
|
|
|
989
980
|
);
|
|
990
981
|
literal = (value) => {
|
|
991
982
|
const unquoted = value.replace(/^(["'])(.*)\1$/, "$2");
|
|
992
|
-
return /[$`*?[\]{}~\\\x00-\x1f]/.test(unquoted) ||
|
|
983
|
+
return /[$`*?[\]{}~\\\x00-\x1f]/.test(unquoted) || path7.posix.isAbsolute(unquoted) ? null : unquoted;
|
|
993
984
|
};
|
|
994
985
|
}
|
|
995
986
|
});
|
|
@@ -1372,7 +1363,7 @@ var init_git = __esm({
|
|
|
1372
1363
|
|
|
1373
1364
|
// src/audit/docs.ts
|
|
1374
1365
|
import fs6 from "fs/promises";
|
|
1375
|
-
import
|
|
1366
|
+
import path8 from "path";
|
|
1376
1367
|
import fg5 from "fast-glob";
|
|
1377
1368
|
async function dirtyDocs(resolvedRoot, files) {
|
|
1378
1369
|
if (!files.length) return /* @__PURE__ */ new Set();
|
|
@@ -1468,15 +1459,15 @@ async function localDocPaths(root) {
|
|
|
1468
1459
|
}
|
|
1469
1460
|
for (const entry of entries)
|
|
1470
1461
|
if (/^(?:agents|claude|readme)\.md$/i.test(entry.name)) {
|
|
1471
|
-
paths.push(
|
|
1462
|
+
paths.push(path8.posix.join(dir, entry.name));
|
|
1472
1463
|
}
|
|
1473
1464
|
}
|
|
1474
1465
|
return paths;
|
|
1475
1466
|
}
|
|
1476
1467
|
function documentScope(file) {
|
|
1477
|
-
if (!/^(readme|agents|claude)\.md$/i.test(
|
|
1478
|
-
const parent =
|
|
1479
|
-
return
|
|
1468
|
+
if (!/^(readme|agents|claude)\.md$/i.test(path8.posix.basename(file))) return ".";
|
|
1469
|
+
const parent = path8.posix.dirname(file);
|
|
1470
|
+
return path8.posix.basename(parent).toLowerCase() === ".claude" ? path8.posix.dirname(parent) : parent;
|
|
1480
1471
|
}
|
|
1481
1472
|
async function discoverDocs(resolvedRoot) {
|
|
1482
1473
|
return profilePhase("audit.documents", () => readDocs(resolvedRoot));
|
|
@@ -1499,7 +1490,7 @@ async function readDocs(resolvedRoot) {
|
|
|
1499
1490
|
path: candidate,
|
|
1500
1491
|
content,
|
|
1501
1492
|
scope: documentScope(candidate),
|
|
1502
|
-
kind: /^readme\.md$/i.test(
|
|
1493
|
+
kind: /^readme\.md$/i.test(path8.posix.basename(candidate)) ? "readme" : /^(agents|claude)\.md$/i.test(path8.posix.basename(candidate)) ? "instructions" : "guide",
|
|
1503
1494
|
lineCount: content.split("\n").length,
|
|
1504
1495
|
lastCommit,
|
|
1505
1496
|
dirty: dirty.has(candidate),
|
|
@@ -1528,15 +1519,15 @@ var init_docs = __esm({
|
|
|
1528
1519
|
|
|
1529
1520
|
// src/audit/scope.ts
|
|
1530
1521
|
import fs7 from "fs/promises";
|
|
1531
|
-
import
|
|
1522
|
+
import path9 from "path";
|
|
1532
1523
|
function scopedPath(directory, value) {
|
|
1533
|
-
if (
|
|
1524
|
+
if (path9.posix.isAbsolute(value) || value.includes("\\") || /[\x00-\x1f]/.test(value))
|
|
1534
1525
|
return null;
|
|
1535
|
-
const resolved =
|
|
1526
|
+
const resolved = path9.posix.normalize(path9.posix.join(directory, value));
|
|
1536
1527
|
return resolved === ".." || resolved.startsWith("../") ? null : resolved;
|
|
1537
1528
|
}
|
|
1538
1529
|
function pathClaimScope(doc, claim) {
|
|
1539
|
-
const directory = claim.relativeTo === "document" ?
|
|
1530
|
+
const directory = claim.relativeTo === "document" ? path9.posix.dirname(doc) : documentScope(doc);
|
|
1540
1531
|
const local = scopedPath(directory, claim.path);
|
|
1541
1532
|
if (!local) return null;
|
|
1542
1533
|
const candidates = claim.relativeTo === "document" || directory === "." ? [local] : [...new Set([local, scopedPath(".", claim.path)].filter((p) => p !== null))];
|
|
@@ -1570,7 +1561,7 @@ var init_scope = __esm({
|
|
|
1570
1561
|
});
|
|
1571
1562
|
|
|
1572
1563
|
// src/audit/checks/deleted-reference.ts
|
|
1573
|
-
import
|
|
1564
|
+
import path10 from "path";
|
|
1574
1565
|
async function checkDeletedReferences(ctx) {
|
|
1575
1566
|
const result = emptyResult();
|
|
1576
1567
|
for (const doc of ctx.docs) {
|
|
@@ -1605,7 +1596,7 @@ async function checkDeletedReferences(ctx) {
|
|
|
1605
1596
|
scope.candidates.map(async (file) => ({
|
|
1606
1597
|
file,
|
|
1607
1598
|
tracked: await lastCommitOf(ctx.root, file),
|
|
1608
|
-
parent: await pathExists(ctx.root,
|
|
1599
|
+
parent: await pathExists(ctx.root, path10.posix.dirname(file))
|
|
1609
1600
|
}))
|
|
1610
1601
|
);
|
|
1611
1602
|
const candidate = candidates.find((c) => c.tracked || renames.has(c.file)) ?? candidates.find((c) => c.parent);
|
|
@@ -1658,7 +1649,7 @@ var init_deleted_reference = __esm({
|
|
|
1658
1649
|
// src/audit/checks/new-module.ts
|
|
1659
1650
|
import fg6 from "fast-glob";
|
|
1660
1651
|
import fs8 from "fs/promises";
|
|
1661
|
-
import
|
|
1652
|
+
import path11 from "path";
|
|
1662
1653
|
function escapeRegExp(text2) {
|
|
1663
1654
|
return text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1664
1655
|
}
|
|
@@ -1694,7 +1685,7 @@ async function checkNewModules(ctx) {
|
|
|
1694
1685
|
}
|
|
1695
1686
|
function moduleDocumentation(docs) {
|
|
1696
1687
|
return docs.map(
|
|
1697
|
-
(doc) => doc.content + "\n" + (
|
|
1688
|
+
(doc) => doc.content + "\n" + (path11.posix.dirname(doc.path) === "." ? "" : path11.posix.dirname(doc.path) + "/")
|
|
1698
1689
|
).join("\n");
|
|
1699
1690
|
}
|
|
1700
1691
|
function moduleCandidates(root, combinedDocs) {
|
|
@@ -1720,9 +1711,9 @@ async function collectModuleCandidates(root, combinedDocs) {
|
|
|
1720
1711
|
});
|
|
1721
1712
|
const dirs = entries.filter(
|
|
1722
1713
|
(entry) => (entry.isDirectory() || entry.isSymbolicLink() && sources.some(
|
|
1723
|
-
(file) => file.startsWith(
|
|
1714
|
+
(file) => file.startsWith(path11.posix.join(dir, entry.name) + "/")
|
|
1724
1715
|
)) && !entry.name.startsWith(".") && !DIR_DENYLIST.has(entry.name)
|
|
1725
|
-
).map((entry) =>
|
|
1716
|
+
).map((entry) => path11.posix.join(dir, entry.name));
|
|
1726
1717
|
byParent.set(dir, dirs);
|
|
1727
1718
|
})
|
|
1728
1719
|
);
|
|
@@ -1742,7 +1733,7 @@ async function collectModuleCandidates(root, combinedDocs) {
|
|
|
1742
1733
|
onlyDirectories: true,
|
|
1743
1734
|
label: "Module directory discovery",
|
|
1744
1735
|
select: (file) => visible.has(file)
|
|
1745
|
-
})).map((file) =>
|
|
1736
|
+
})).map((file) => path11.posix.basename(file))
|
|
1746
1737
|
);
|
|
1747
1738
|
}
|
|
1748
1739
|
return result;
|
|
@@ -1824,7 +1815,7 @@ var init_new_module = __esm({
|
|
|
1824
1815
|
});
|
|
1825
1816
|
|
|
1826
1817
|
// src/audit/checks/stale-count.ts
|
|
1827
|
-
import
|
|
1818
|
+
import path12 from "path";
|
|
1828
1819
|
async function countGradleModules(root) {
|
|
1829
1820
|
for (const name of ["settings.gradle.kts", "settings.gradle"]) {
|
|
1830
1821
|
const content = await readAuditInput(root, name);
|
|
@@ -1861,7 +1852,7 @@ async function countNpmWorkspaces(root) {
|
|
|
1861
1852
|
return {
|
|
1862
1853
|
actual: matched.length,
|
|
1863
1854
|
countedFrom: "package.json workspaces",
|
|
1864
|
-
members: matched.map((m) =>
|
|
1855
|
+
members: matched.map((m) => path12.dirname(m)).sort()
|
|
1865
1856
|
};
|
|
1866
1857
|
}
|
|
1867
1858
|
}
|
|
@@ -1892,7 +1883,7 @@ async function countNpmWorkspaces(root) {
|
|
|
1892
1883
|
return {
|
|
1893
1884
|
actual: matched.length,
|
|
1894
1885
|
countedFrom: "pnpm-workspace.yaml",
|
|
1895
|
-
members: matched.map((m) =>
|
|
1886
|
+
members: matched.map((m) => path12.dirname(m)).sort()
|
|
1896
1887
|
};
|
|
1897
1888
|
}
|
|
1898
1889
|
}
|
|
@@ -1912,8 +1903,8 @@ async function countCargoCrates(root) {
|
|
|
1912
1903
|
ignore: ["**/target/**"],
|
|
1913
1904
|
label: "Cargo workspace discovery"
|
|
1914
1905
|
});
|
|
1915
|
-
for (const m of matched) members.add(
|
|
1916
|
-
} else if (await readAuditInput(root,
|
|
1906
|
+
for (const m of matched) members.add(path12.dirname(m));
|
|
1907
|
+
} else if (await readAuditInput(root, path12.posix.join(entry, "Cargo.toml")) !== null) {
|
|
1917
1908
|
members.add(entry);
|
|
1918
1909
|
}
|
|
1919
1910
|
}
|
|
@@ -1933,7 +1924,7 @@ async function resolveCountSource(root, claim) {
|
|
|
1933
1924
|
}
|
|
1934
1925
|
async function resolveDocCountSource(root, doc, claim) {
|
|
1935
1926
|
const scope = documentScope(doc);
|
|
1936
|
-
const source = await resolveCountSource(
|
|
1927
|
+
const source = await resolveCountSource(path12.join(root, scope), claim);
|
|
1937
1928
|
if (!source || scope === ".") return source;
|
|
1938
1929
|
return {
|
|
1939
1930
|
...source,
|
|
@@ -1986,7 +1977,7 @@ var init_stale_count = __esm({
|
|
|
1986
1977
|
});
|
|
1987
1978
|
|
|
1988
1979
|
// src/audit/checks/dead-command.ts
|
|
1989
|
-
import
|
|
1980
|
+
import path13 from "path";
|
|
1990
1981
|
function scriptsOf(raw) {
|
|
1991
1982
|
try {
|
|
1992
1983
|
const pkg = JSON.parse(raw);
|
|
@@ -2030,7 +2021,7 @@ async function commandInputs(root, docs) {
|
|
|
2030
2021
|
}
|
|
2031
2022
|
let cursor = directory;
|
|
2032
2023
|
while (true) {
|
|
2033
|
-
const manifest =
|
|
2024
|
+
const manifest = path13.posix.join(cursor, "package.json");
|
|
2034
2025
|
scope.candidates.push(manifest);
|
|
2035
2026
|
const raw = await read(manifest);
|
|
2036
2027
|
if (raw !== null) {
|
|
@@ -2041,7 +2032,7 @@ async function commandInputs(root, docs) {
|
|
|
2041
2032
|
break;
|
|
2042
2033
|
}
|
|
2043
2034
|
if (claim.directory !== void 0 || cursor === ".") break;
|
|
2044
|
-
cursor =
|
|
2035
|
+
cursor = path13.posix.dirname(cursor);
|
|
2045
2036
|
}
|
|
2046
2037
|
if (!resolution.manifest && !resolution.problem) {
|
|
2047
2038
|
resolution.problem = `No package.json for the command's scope (${directory}).`;
|
|
@@ -2127,7 +2118,7 @@ var init_dead_command = __esm({
|
|
|
2127
2118
|
});
|
|
2128
2119
|
|
|
2129
2120
|
// src/audit/dependency-claims.ts
|
|
2130
|
-
import
|
|
2121
|
+
import path14 from "path";
|
|
2131
2122
|
import { parse as parseToml } from "smol-toml";
|
|
2132
2123
|
function dependencyText(content) {
|
|
2133
2124
|
return content.replace(
|
|
@@ -2138,7 +2129,7 @@ function dependencyText(content) {
|
|
|
2138
2129
|
function declarations(file, source) {
|
|
2139
2130
|
const result = /* @__PURE__ */ new Map();
|
|
2140
2131
|
if (!source) return result;
|
|
2141
|
-
if (
|
|
2132
|
+
if (path14.posix.basename(file) === "package.json") {
|
|
2142
2133
|
const json = JSON.parse(source);
|
|
2143
2134
|
if (!isRecord(json)) throw new Error("Invalid package manifest");
|
|
2144
2135
|
for (const section of [
|
|
@@ -2470,7 +2461,12 @@ async function captureAnchorScopes(root, scopes2) {
|
|
|
2470
2461
|
);
|
|
2471
2462
|
const inventory = access.inventory();
|
|
2472
2463
|
if (!inventory) throw new Error("Git anchor inventory unavailable");
|
|
2473
|
-
const
|
|
2464
|
+
const visible = indexMatchingPaths(
|
|
2465
|
+
union.map((anchor) => [anchor]),
|
|
2466
|
+
inventory
|
|
2467
|
+
);
|
|
2468
|
+
const hidden = new Set(union.filter((_, index) => !visible[index].length));
|
|
2469
|
+
const ignored = hidden.size ? (await inspectionGit(
|
|
2474
2470
|
[
|
|
2475
2471
|
"ls-files",
|
|
2476
2472
|
"-z",
|
|
@@ -2478,13 +2474,11 @@ async function captureAnchorScopes(root, scopes2) {
|
|
|
2478
2474
|
"--ignored",
|
|
2479
2475
|
"--exclude-standard",
|
|
2480
2476
|
"--",
|
|
2481
|
-
...
|
|
2477
|
+
...[...hidden].map((anchor) => `:(literal)${anchor}`)
|
|
2482
2478
|
],
|
|
2483
2479
|
{ cwd: root, maxBuffer: 8 * 1024 * 1024 }
|
|
2484
|
-
);
|
|
2485
|
-
const files = [
|
|
2486
|
-
.../* @__PURE__ */ new Set([...inventory, ...ignored.stdout.split("\0").filter(Boolean)])
|
|
2487
|
-
].sort();
|
|
2480
|
+
)).stdout.split("\0").filter(Boolean) : [];
|
|
2481
|
+
const files = [.../* @__PURE__ */ new Set([...inventory, ...ignored])].sort();
|
|
2488
2482
|
const patterns = await Promise.all(
|
|
2489
2483
|
union.map(async (anchor) => {
|
|
2490
2484
|
const literal2 = fg7.escapePath(anchor);
|
|
@@ -2500,7 +2494,7 @@ async function captureAnchorScopes(root, scopes2) {
|
|
|
2500
2494
|
const filesByScope = new Map(
|
|
2501
2495
|
scopes2.map((scope, index) => [JSON.stringify([...new Set(scope)].sort()), indexed[index]])
|
|
2502
2496
|
);
|
|
2503
|
-
return { filesByScope, eligible };
|
|
2497
|
+
return { filesByScope, eligible, hidden, ignored: new Set(ignored) };
|
|
2504
2498
|
};
|
|
2505
2499
|
return Promise.all(
|
|
2506
2500
|
scopes2.map((scope) => {
|
|
@@ -2512,8 +2506,10 @@ async function captureAnchorScopes(root, scopes2) {
|
|
|
2512
2506
|
const capture = { anchors, files: {}, complete: true };
|
|
2513
2507
|
if (!anchors.length) return capture;
|
|
2514
2508
|
try {
|
|
2515
|
-
const { eligible, filesByScope } = await (context ??= prepareContext());
|
|
2516
|
-
const files = filesByScope.get(JSON.stringify(anchors))
|
|
2509
|
+
const { eligible, filesByScope, hidden, ignored } = await (context ??= prepareContext());
|
|
2510
|
+
const files = filesByScope.get(JSON.stringify(anchors)).filter(
|
|
2511
|
+
(file) => !ignored.has(file) || anchors.some((anchor) => hidden.has(anchor) && anchorMatches(anchor, file))
|
|
2512
|
+
);
|
|
2517
2513
|
if (files.length > 2e3) return { ...capture, complete: false };
|
|
2518
2514
|
for (const file of files) {
|
|
2519
2515
|
const evidence = await inspectionRead(
|
|
@@ -2591,7 +2587,7 @@ var init_anchors = __esm({
|
|
|
2591
2587
|
});
|
|
2592
2588
|
|
|
2593
2589
|
// src/context/lexical.ts
|
|
2594
|
-
import
|
|
2590
|
+
import path15 from "path";
|
|
2595
2591
|
var init_lexical = __esm({
|
|
2596
2592
|
"src/context/lexical.ts"() {
|
|
2597
2593
|
"use strict";
|
|
@@ -2660,8 +2656,19 @@ function decisionProvenance(record, freshness = "unknown") {
|
|
|
2660
2656
|
} : null
|
|
2661
2657
|
};
|
|
2662
2658
|
}
|
|
2663
|
-
function decisionInspectionDigest(record) {
|
|
2659
|
+
function decisionInspectionDigest(record, scope = "all") {
|
|
2664
2660
|
const effective = effectiveDecision(record);
|
|
2661
|
+
if (scope === "operative")
|
|
2662
|
+
return createHash2("sha256").update(
|
|
2663
|
+
JSON.stringify([
|
|
2664
|
+
"operative",
|
|
2665
|
+
record.status,
|
|
2666
|
+
decisionApproval(effective),
|
|
2667
|
+
effective.version === 2 ? effective.revision : 0,
|
|
2668
|
+
decisionContent(effective),
|
|
2669
|
+
effective.refreshedHash
|
|
2670
|
+
])
|
|
2671
|
+
).digest("hex");
|
|
2665
2672
|
return createHash2("sha256").update(
|
|
2666
2673
|
JSON.stringify([
|
|
2667
2674
|
record.status,
|
|
@@ -2673,8 +2680,10 @@ function decisionInspectionDigest(record) {
|
|
|
2673
2680
|
])
|
|
2674
2681
|
).digest("hex");
|
|
2675
2682
|
}
|
|
2676
|
-
function latestDecisionInspection(record) {
|
|
2677
|
-
return record.version === 2 ? record.inspections?.filter(
|
|
2683
|
+
function latestDecisionInspection(record, scope = "all") {
|
|
2684
|
+
return record.version === 2 ? record.inspections?.filter(
|
|
2685
|
+
(i) => (scope === "operative" || i.scope !== "operative") && i.decisionDigest === decisionInspectionDigest(record, i.scope ?? "all")
|
|
2686
|
+
).at(-1) : void 0;
|
|
2678
2687
|
}
|
|
2679
2688
|
var text, decisionSourceSchema, attributionSchema, contentSchema, approvalSchema, statusSchema, reviewEvidenceSchema, eventSchema, legacySchema, currentSchema, decisionSchema;
|
|
2680
2689
|
var init_provenance = __esm({
|
|
@@ -2760,6 +2769,7 @@ var init_provenance = __esm({
|
|
|
2760
2769
|
inspector: text(200),
|
|
2761
2770
|
note: text(1500),
|
|
2762
2771
|
outcome: z4.literal("no_contradiction_found"),
|
|
2772
|
+
scope: z4.literal("operative").optional(),
|
|
2763
2773
|
decisionDigest: z4.string().regex(/^[a-f0-9]{64}$/),
|
|
2764
2774
|
headHash: z4.string(),
|
|
2765
2775
|
capture: anchorCaptureSchema
|
|
@@ -2816,7 +2826,7 @@ var init_provenance = __esm({
|
|
|
2816
2826
|
|
|
2817
2827
|
// src/decisions/decisions.ts
|
|
2818
2828
|
import fs10 from "fs/promises";
|
|
2819
|
-
import
|
|
2829
|
+
import path16 from "path";
|
|
2820
2830
|
import { createHash as createHash3 } from "crypto";
|
|
2821
2831
|
import { z as z5 } from "zod";
|
|
2822
2832
|
async function readDecisionInputs(rootDir) {
|
|
@@ -2908,10 +2918,39 @@ var init_decisions = __esm({
|
|
|
2908
2918
|
}
|
|
2909
2919
|
});
|
|
2910
2920
|
|
|
2921
|
+
// src/decisions/excerpts.ts
|
|
2922
|
+
import { createHash as createHash4 } from "crypto";
|
|
2923
|
+
import { execFile as execFile2 } from "child_process";
|
|
2924
|
+
import { promisify as promisify2 } from "util";
|
|
2925
|
+
function driftBaseline(record) {
|
|
2926
|
+
const effective = effectiveDecision(record);
|
|
2927
|
+
const inspection = latestDecisionInspection(
|
|
2928
|
+
record,
|
|
2929
|
+
decisionApproval(effective) === "accepted" ? "operative" : "all"
|
|
2930
|
+
);
|
|
2931
|
+
return {
|
|
2932
|
+
record: effective,
|
|
2933
|
+
capture: inspection?.capture ?? (effective.version === 2 ? effective.capture : void 0),
|
|
2934
|
+
commits: [inspection?.headHash, effective.refreshedHash].filter(
|
|
2935
|
+
(hash) => !!hash && /^[a-f0-9]{40,64}$/.test(hash)
|
|
2936
|
+
)
|
|
2937
|
+
};
|
|
2938
|
+
}
|
|
2939
|
+
var exec;
|
|
2940
|
+
var init_excerpts = __esm({
|
|
2941
|
+
"src/decisions/excerpts.ts"() {
|
|
2942
|
+
"use strict";
|
|
2943
|
+
init_files();
|
|
2944
|
+
init_paths();
|
|
2945
|
+
init_provenance();
|
|
2946
|
+
exec = promisify2(execFile2);
|
|
2947
|
+
}
|
|
2948
|
+
});
|
|
2949
|
+
|
|
2911
2950
|
// src/decisions/drift.ts
|
|
2912
|
-
import
|
|
2951
|
+
import path17 from "path";
|
|
2913
2952
|
async function computeDecisionDrift(rootDir, decisions) {
|
|
2914
|
-
const resolvedRoot =
|
|
2953
|
+
const resolvedRoot = path17.resolve(rootDir);
|
|
2915
2954
|
const store = decisions ? { records: decisions, diagnostics: [] } : await loadDecisionStore(resolvedRoot);
|
|
2916
2955
|
const report = {
|
|
2917
2956
|
historyAvailable: true,
|
|
@@ -2926,11 +2965,15 @@ async function computeDecisionDrift(rootDir, decisions) {
|
|
|
2926
2965
|
]);
|
|
2927
2966
|
const changesByHash = /* @__PURE__ */ new Map();
|
|
2928
2967
|
const baselines = store.records.filter((record) => record.status === "active").flatMap((record) => {
|
|
2929
|
-
const
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2968
|
+
const effective = effectiveDecision(record);
|
|
2969
|
+
return [.../* @__PURE__ */ new Set([effective, record])].flatMap((candidate) => {
|
|
2970
|
+
const inspection = latestDecisionInspection(
|
|
2971
|
+
record,
|
|
2972
|
+
candidate === effective && decisionApproval(effective) === "accepted" ? "operative" : "all"
|
|
2973
|
+
);
|
|
2974
|
+
const capture = inspection?.capture ?? (candidate.version === 2 ? candidate.capture : void 0);
|
|
2975
|
+
return capture ? [capture] : [];
|
|
2976
|
+
});
|
|
2934
2977
|
});
|
|
2935
2978
|
const captures = await captureAnchorScopes(
|
|
2936
2979
|
resolvedRoot,
|
|
@@ -2968,13 +3011,15 @@ async function computeDecisionDrift(rootDir, decisions) {
|
|
|
2968
3011
|
};
|
|
2969
3012
|
for (const record of store.records) {
|
|
2970
3013
|
if (record.status !== "active") continue;
|
|
2971
|
-
const
|
|
2972
|
-
const
|
|
2973
|
-
const state = await inspect(effective, inspection?.capture);
|
|
3014
|
+
const baseline = driftBaseline(record);
|
|
3015
|
+
const state = await inspect(baseline.record, baseline.capture);
|
|
2974
3016
|
report.freshness[record.id] = state.freshness;
|
|
2975
3017
|
if (state.changedFiles.length) report.staleDecisions[record.id] = state.changedFiles;
|
|
2976
|
-
if (
|
|
2977
|
-
(report.pendingProposals ??= {})[record.id] = await inspect(
|
|
3018
|
+
if (baseline.record !== record)
|
|
3019
|
+
(report.pendingProposals ??= {})[record.id] = await inspect(
|
|
3020
|
+
record,
|
|
3021
|
+
latestDecisionInspection(record)?.capture
|
|
3022
|
+
);
|
|
2978
3023
|
}
|
|
2979
3024
|
return report;
|
|
2980
3025
|
}
|
|
@@ -2986,13 +3031,14 @@ var init_drift2 = __esm({
|
|
|
2986
3031
|
init_paths();
|
|
2987
3032
|
init_snapshot();
|
|
2988
3033
|
init_decisions();
|
|
3034
|
+
init_excerpts();
|
|
2989
3035
|
init_provenance();
|
|
2990
3036
|
}
|
|
2991
3037
|
});
|
|
2992
3038
|
|
|
2993
3039
|
// src/audit/checks/decision-anchor.ts
|
|
2994
3040
|
async function checkDecisionAnchors(ctx) {
|
|
2995
|
-
const result = emptyResult();
|
|
3041
|
+
const result = { ...emptyResult(), incompleteDecisions: [] };
|
|
2996
3042
|
if (!ctx.decisionsPresent) return result;
|
|
2997
3043
|
const store = await loadDecisionStore(ctx.root);
|
|
2998
3044
|
const records = store.records;
|
|
@@ -3024,6 +3070,7 @@ async function checkDecisionAnchors(ctx) {
|
|
|
3024
3070
|
check: "decision-anchor-drift",
|
|
3025
3071
|
reason: `Anchor evidence for decision ${record.id} is incomplete or unreadable`
|
|
3026
3072
|
});
|
|
3073
|
+
result.incompleteDecisions.push(record.id);
|
|
3027
3074
|
continue;
|
|
3028
3075
|
}
|
|
3029
3076
|
if (!changedFiles.length) continue;
|
|
@@ -3085,7 +3132,7 @@ var init_checks = __esm({
|
|
|
3085
3132
|
});
|
|
3086
3133
|
|
|
3087
3134
|
// src/audit/findings.ts
|
|
3088
|
-
import { createHash as
|
|
3135
|
+
import { createHash as createHash5 } from "crypto";
|
|
3089
3136
|
import { z as z6 } from "zod";
|
|
3090
3137
|
function findingId(finding) {
|
|
3091
3138
|
const e = finding.evidence;
|
|
@@ -3116,7 +3163,7 @@ var digest, checkSchema, commitSchema, anchorSchema, count, scopeSchema, evidenc
|
|
|
3116
3163
|
var init_findings = __esm({
|
|
3117
3164
|
"src/audit/findings.ts"() {
|
|
3118
3165
|
"use strict";
|
|
3119
|
-
digest = (value) =>
|
|
3166
|
+
digest = (value) => createHash5("sha256").update(JSON.stringify(value)).digest("hex");
|
|
3120
3167
|
checkSchema = z6.enum([
|
|
3121
3168
|
"deleted-reference",
|
|
3122
3169
|
"new-module",
|
|
@@ -3223,6 +3270,13 @@ var init_findings = __esm({
|
|
|
3223
3270
|
}
|
|
3224
3271
|
});
|
|
3225
3272
|
|
|
3273
|
+
// src/automation/pending-tools.ts
|
|
3274
|
+
var init_pending_tools = __esm({
|
|
3275
|
+
"src/automation/pending-tools.ts"() {
|
|
3276
|
+
"use strict";
|
|
3277
|
+
}
|
|
3278
|
+
});
|
|
3279
|
+
|
|
3226
3280
|
// src/automation/stop-review.ts
|
|
3227
3281
|
import { z as z7 } from "zod";
|
|
3228
3282
|
var evidenceSchema2, stopReviewSchema;
|
|
@@ -3231,6 +3285,7 @@ var init_stop_review = __esm({
|
|
|
3231
3285
|
"use strict";
|
|
3232
3286
|
init_anchors();
|
|
3233
3287
|
init_findings();
|
|
3288
|
+
init_pending_tools();
|
|
3234
3289
|
evidenceSchema2 = z7.record(z7.object({ digest: z7.string(), capture: anchorCaptureSchema }));
|
|
3235
3290
|
stopReviewSchema = z7.object({
|
|
3236
3291
|
pending: z7.record(z7.object({ head: z7.string(), evidence: evidenceSchema2 })),
|
|
@@ -3267,13 +3322,14 @@ import { z as z9 } from "zod";
|
|
|
3267
3322
|
function withLock(root, directory, run, waitMs = 5e3) {
|
|
3268
3323
|
return withStoreLock(root, directory, run, waitMs, "Automation");
|
|
3269
3324
|
}
|
|
3270
|
-
var hostSchema, stateSchema;
|
|
3325
|
+
var hostSchema, stateSchema, IDLE_PENDING_MS, STATE_READ_MAX_BYTES;
|
|
3271
3326
|
var init_store = __esm({
|
|
3272
3327
|
"src/automation/store.ts"() {
|
|
3273
3328
|
"use strict";
|
|
3274
3329
|
init_store_lock();
|
|
3275
3330
|
init_stop_review();
|
|
3276
3331
|
init_notifications();
|
|
3332
|
+
init_storage();
|
|
3277
3333
|
hostSchema = z9.enum(["claude", "codex"]);
|
|
3278
3334
|
stateSchema = z9.object({
|
|
3279
3335
|
version: z9.literal(1),
|
|
@@ -3288,6 +3344,8 @@ var init_store = __esm({
|
|
|
3288
3344
|
host: hostSchema,
|
|
3289
3345
|
lastUsed: z9.string(),
|
|
3290
3346
|
notifiedDiagnostics: z9.array(z9.string()).default([]),
|
|
3347
|
+
/** Digest of the upstream drift notice already shown to this session. */
|
|
3348
|
+
upstreamNotice: z9.string().optional(),
|
|
3291
3349
|
pending: z9.record(z9.string()),
|
|
3292
3350
|
stopReview: stopReviewSchema.optional(),
|
|
3293
3351
|
coverageGaps: z9.array(z9.string()),
|
|
@@ -3297,10 +3355,13 @@ var init_store = __esm({
|
|
|
3297
3355
|
updatedAt: z9.string(),
|
|
3298
3356
|
fingerprint: z9.string().nullable(),
|
|
3299
3357
|
latest: z9.string().nullable(),
|
|
3358
|
+
guidance: z9.object({ fingerprint: z9.string(), path: z9.string() }).optional(),
|
|
3300
3359
|
notifications: notificationStateSchema.optional(),
|
|
3301
3360
|
agentNotifications: notificationStateSchema.optional(),
|
|
3302
3361
|
analysis: z9.object({ id: z9.string(), fingerprint: z9.string(), completedAt: z9.number(), path: z9.string() }).optional()
|
|
3303
3362
|
});
|
|
3363
|
+
IDLE_PENDING_MS = 24 * 60 * 60 * 1e3;
|
|
3364
|
+
STATE_READ_MAX_BYTES = 64 * 1024 * 1024;
|
|
3304
3365
|
}
|
|
3305
3366
|
});
|
|
3306
3367
|
|
|
@@ -3319,7 +3380,7 @@ __export(repair_exports, {
|
|
|
3319
3380
|
withRepairSession: () => withRepairSession
|
|
3320
3381
|
});
|
|
3321
3382
|
import fs11 from "fs/promises";
|
|
3322
|
-
import
|
|
3383
|
+
import path18 from "path";
|
|
3323
3384
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3324
3385
|
import { z as z10 } from "zod";
|
|
3325
3386
|
function recheckable(finding) {
|
|
@@ -3417,8 +3478,8 @@ async function prepareOriginal(rootDir, checks, options, session) {
|
|
|
3417
3478
|
}
|
|
3418
3479
|
async function loadRepairBaseline(rootDir, baselinePath) {
|
|
3419
3480
|
const root = await fs11.realpath(rootDir);
|
|
3420
|
-
const declaredRoot =
|
|
3421
|
-
const relative =
|
|
3481
|
+
const declaredRoot = path18.resolve(rootDir);
|
|
3482
|
+
const relative = path18.isAbsolute(baselinePath) ? path18.relative(isWithinRoot(declaredRoot, baselinePath) ? declaredRoot : root, baselinePath) : baselinePath;
|
|
3422
3483
|
const stored = baselineSchema.parse(await readStoreJson(root, relative));
|
|
3423
3484
|
const { digest: savedDigest, ...payload } = stored;
|
|
3424
3485
|
if (digest(payload) !== savedDigest)
|
|
@@ -3779,7 +3840,10 @@ async function decisionAssessment(root, finding, baselineHead) {
|
|
|
3779
3840
|
);
|
|
3780
3841
|
const record = store.records.find((record2) => record2.id === evidence.decisionId);
|
|
3781
3842
|
if (!record || record.version !== 2) return null;
|
|
3782
|
-
const inspection = latestDecisionInspection(
|
|
3843
|
+
const inspection = latestDecisionInspection(
|
|
3844
|
+
record,
|
|
3845
|
+
evidence.provenance?.approval === "accepted" ? "operative" : "all"
|
|
3846
|
+
);
|
|
3783
3847
|
if (inspection && record.status === "active") {
|
|
3784
3848
|
const capture = await captureAnchors(root, inspection.capture.anchors);
|
|
3785
3849
|
let ancestor = true;
|
|
@@ -4074,12 +4138,12 @@ var init_advisory_review = __esm({
|
|
|
4074
4138
|
|
|
4075
4139
|
// src/audit/audit.ts
|
|
4076
4140
|
import fs13 from "fs/promises";
|
|
4077
|
-
import
|
|
4141
|
+
import path19 from "path";
|
|
4078
4142
|
async function computeAudit(rootDir, options = {}) {
|
|
4079
4143
|
return profilePhase("audit.current", () => auditCurrent(rootDir, options));
|
|
4080
4144
|
}
|
|
4081
4145
|
async function auditCurrent(rootDir, options) {
|
|
4082
|
-
const resolvedRoot =
|
|
4146
|
+
const resolvedRoot = path19.resolve(rootDir);
|
|
4083
4147
|
const [docs, headHash] = await Promise.all([
|
|
4084
4148
|
discoverDocs(resolvedRoot),
|
|
4085
4149
|
getCurrentGitHash(resolvedRoot)
|
|
@@ -4117,7 +4181,7 @@ async function auditCurrent(rootDir, options) {
|
|
|
4117
4181
|
}
|
|
4118
4182
|
let decisionsPresent = false;
|
|
4119
4183
|
try {
|
|
4120
|
-
await fs13.access(
|
|
4184
|
+
await fs13.access(path19.join(resolvedRoot, ".mason", "decisions"));
|
|
4121
4185
|
decisionsPresent = true;
|
|
4122
4186
|
} catch {
|
|
4123
4187
|
}
|
|
@@ -4186,11 +4250,11 @@ init_audit();
|
|
|
4186
4250
|
init_types();
|
|
4187
4251
|
init_repair();
|
|
4188
4252
|
init_findings();
|
|
4189
|
-
import
|
|
4253
|
+
import path21 from "path";
|
|
4190
4254
|
|
|
4191
4255
|
// src/audit/advisory-cli.ts
|
|
4192
4256
|
init_advisory_review();
|
|
4193
|
-
import
|
|
4257
|
+
import path20 from "path";
|
|
4194
4258
|
import { stripVTControlCharacters } from "util";
|
|
4195
4259
|
var USAGE = `Usage: mason audit review --baseline <path> --finding <id> [options]
|
|
4196
4260
|
|
|
@@ -4250,7 +4314,7 @@ async function runAdvisoryCli(args, io = { out: (text2) => console.log(text2), e
|
|
|
4250
4314
|
reviewToken: flags["--token"],
|
|
4251
4315
|
reasonCode: flags["--reason"]
|
|
4252
4316
|
};
|
|
4253
|
-
const result = await reviewAdvisory(
|
|
4317
|
+
const result = await reviewAdvisory(path20.resolve(flags["--dir"] ?? process.cwd()), input);
|
|
4254
4318
|
if (json) io.out(JSON.stringify(result, null, 2));
|
|
4255
4319
|
else {
|
|
4256
4320
|
const lines = [`Advisory review: ${result.status}.`];
|
|
@@ -4438,7 +4502,7 @@ function formatFixPrompt(report, baselinePath) {
|
|
|
4438
4502
|
lines.push("");
|
|
4439
4503
|
lines.push("RULES:");
|
|
4440
4504
|
lines.push(
|
|
4441
|
-
baselinePath ? `- Preserve the original repair baseline: ${JSON.stringify(baselinePath)}. Do not replace it after editing.` : "- Before the first edit,
|
|
4505
|
+
baselinePath ? `- Preserve the original repair baseline: ${JSON.stringify(baselinePath)}. Do not replace it after editing.` : "- Before the first edit, run mason audit --prepare-repair --json with the same --dir and --checks. Keep the returned baselinePath through verification."
|
|
4442
4506
|
);
|
|
4443
4507
|
lines.push(
|
|
4444
4508
|
`- Edit ONLY these files: ${flaggedDocs.join(", ") || "none (advisory review only)"}. Bring the docs into agreement with verified source evidence; do not change source code or configs to silence findings.`
|
|
@@ -4461,7 +4525,7 @@ function formatFixPrompt(report, baselinePath) {
|
|
|
4461
4525
|
"- new-module: review whether the directory needs documenting; an omission alone does not establish a defect. Preserve scope and describe only what you verified."
|
|
4462
4526
|
);
|
|
4463
4527
|
lines.push(
|
|
4464
|
-
"- Specific historical ADVISORIES require a separate authorized assessment using
|
|
4528
|
+
"- Specific historical ADVISORIES require a separate authorized assessment using mason audit review with --baseline and --finding from verification. Prepare and inspect evidence first; record addressed, inapplicable or deferred with the actual reviewer, note and reviewToken. Dependency notices marked resolution: informational require no review. Dependency advisories may be dismissed with --outcome dismiss, --reason (no-dependency-claims or unrelated-manifest-change), and --note, without a reviewer or token. Decision findings use review_decision. Candidates marked resolution: recheck can be rerun to see whether the condition remains; this does not establish semantic approval. Report what remains unknown."
|
|
4465
4529
|
);
|
|
4466
4530
|
lines.push("");
|
|
4467
4531
|
lines.push(
|
|
@@ -4483,7 +4547,7 @@ function formatFixPrompt(report, baselinePath) {
|
|
|
4483
4547
|
);
|
|
4484
4548
|
lines.push("");
|
|
4485
4549
|
lines.push(
|
|
4486
|
-
"After edits,
|
|
4550
|
+
"After edits, run mason audit --verify-repair <baselinePath> --dir <project>. Repeat against the same baseline after any final documentation commit. Summarize resolved, unresolved, review-required, unverified, and new findings with their evidence. Do not report a suppressed or unavailable check as fixed. This audit covers the listed documents and supported claims; application behavior and code examples need their own validation."
|
|
4487
4551
|
);
|
|
4488
4552
|
return lines.join("\n");
|
|
4489
4553
|
}
|
|
@@ -4514,7 +4578,7 @@ async function runAuditCli(argv, io = {
|
|
|
4514
4578
|
io.out(USAGE2);
|
|
4515
4579
|
return 0;
|
|
4516
4580
|
}
|
|
4517
|
-
const rootDir =
|
|
4581
|
+
const rootDir = path21.resolve(args.dir);
|
|
4518
4582
|
if (args.baseline || args.prepareRepair) {
|
|
4519
4583
|
try {
|
|
4520
4584
|
if (args.baseline) {
|