mason-context 0.22.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 +10 -0
- package/dist/mason-audit.js +74 -26
- package/dist/mason-audit.js.map +1 -1
- package/dist/mason-drift.js +42 -19
- package/dist/mason-drift.js.map +1 -1
- package/dist/mason-hook.js +41 -19
- package/dist/mason-hook.js.map +1 -1
- package/dist/mason-mcp.js +699 -173
- package/dist/mason-mcp.js.map +1 -1
- package/dist/mason-review.js +54 -29
- package/dist/mason-review.js.map +1 -1
- package/dist/mason-runtime.js +12 -6
- package/dist/mason-runtime.js.map +1 -1
- package/dist/mason.js +722 -148
- package/dist/mason.js.map +1 -1
- package/package.json +1 -1
package/dist/mason-review.js
CHANGED
|
@@ -37,8 +37,8 @@ import { stripVTControlCharacters } from "util";
|
|
|
37
37
|
// src/review/review.ts
|
|
38
38
|
import fs7 from "fs/promises";
|
|
39
39
|
import path11 from "path";
|
|
40
|
-
import { execFile as
|
|
41
|
-
import { promisify as
|
|
40
|
+
import { execFile as execFile4 } from "child_process";
|
|
41
|
+
import { promisify as promisify4 } from "util";
|
|
42
42
|
|
|
43
43
|
// src/utils/git-read.ts
|
|
44
44
|
import { execFile } from "child_process";
|
|
@@ -391,11 +391,13 @@ async function storePath(root, relative, createParents = false) {
|
|
|
391
391
|
}
|
|
392
392
|
return current;
|
|
393
393
|
}
|
|
394
|
-
|
|
394
|
+
var STORE_MAX_BYTES = 10 * 1024 * 1024;
|
|
395
|
+
async function readStoreJson(root, relative, maxBytes = STORE_MAX_BYTES) {
|
|
395
396
|
try {
|
|
396
397
|
const file = await storePath(root, relative);
|
|
397
|
-
const raw = await readBoundedFile(file,
|
|
398
|
-
if (raw === null)
|
|
398
|
+
const raw = await readBoundedFile(file, maxBytes);
|
|
399
|
+
if (raw === null)
|
|
400
|
+
throw new Error(`file is not regular or exceeds ${maxBytes / 1024 / 1024} MiB`);
|
|
399
401
|
const parsed = JSON.parse(raw);
|
|
400
402
|
if (parsed === null) throw new Error("expected a JSON object, received null");
|
|
401
403
|
return parsed;
|
|
@@ -572,7 +574,12 @@ async function captureAnchorScopes(root, scopes) {
|
|
|
572
574
|
);
|
|
573
575
|
const inventory = access.inventory();
|
|
574
576
|
if (!inventory) throw new Error("Git anchor inventory unavailable");
|
|
575
|
-
const
|
|
577
|
+
const visible = indexMatchingPaths(
|
|
578
|
+
union.map((anchor) => [anchor]),
|
|
579
|
+
inventory
|
|
580
|
+
);
|
|
581
|
+
const hidden = new Set(union.filter((_, index2) => !visible[index2].length));
|
|
582
|
+
const ignored = hidden.size ? (await inspectionGit(
|
|
576
583
|
[
|
|
577
584
|
"ls-files",
|
|
578
585
|
"-z",
|
|
@@ -580,13 +587,11 @@ async function captureAnchorScopes(root, scopes) {
|
|
|
580
587
|
"--ignored",
|
|
581
588
|
"--exclude-standard",
|
|
582
589
|
"--",
|
|
583
|
-
...
|
|
590
|
+
...[...hidden].map((anchor) => `:(literal)${anchor}`)
|
|
584
591
|
],
|
|
585
592
|
{ cwd: root, maxBuffer: 8 * 1024 * 1024 }
|
|
586
|
-
);
|
|
587
|
-
const files = [
|
|
588
|
-
.../* @__PURE__ */ new Set([...inventory, ...ignored.stdout.split("\0").filter(Boolean)])
|
|
589
|
-
].sort();
|
|
593
|
+
)).stdout.split("\0").filter(Boolean) : [];
|
|
594
|
+
const files = [.../* @__PURE__ */ new Set([...inventory, ...ignored])].sort();
|
|
590
595
|
const patterns = await Promise.all(
|
|
591
596
|
union.map(async (anchor) => {
|
|
592
597
|
const literal = fg3.escapePath(anchor);
|
|
@@ -602,7 +607,7 @@ async function captureAnchorScopes(root, scopes) {
|
|
|
602
607
|
const filesByScope = new Map(
|
|
603
608
|
scopes.map((scope, index2) => [JSON.stringify([...new Set(scope)].sort()), indexed[index2]])
|
|
604
609
|
);
|
|
605
|
-
return { filesByScope, eligible };
|
|
610
|
+
return { filesByScope, eligible, hidden, ignored: new Set(ignored) };
|
|
606
611
|
};
|
|
607
612
|
return Promise.all(
|
|
608
613
|
scopes.map((scope) => {
|
|
@@ -614,8 +619,10 @@ async function captureAnchorScopes(root, scopes) {
|
|
|
614
619
|
const capture = { anchors, files: {}, complete: true };
|
|
615
620
|
if (!anchors.length) return capture;
|
|
616
621
|
try {
|
|
617
|
-
const { eligible, filesByScope } = await (context ??= prepareContext());
|
|
618
|
-
const files = filesByScope.get(JSON.stringify(anchors))
|
|
622
|
+
const { eligible, filesByScope, hidden, ignored } = await (context ??= prepareContext());
|
|
623
|
+
const files = filesByScope.get(JSON.stringify(anchors)).filter(
|
|
624
|
+
(file) => !ignored.has(file) || anchors.some((anchor) => hidden.has(anchor) && anchorMatches(anchor, file))
|
|
625
|
+
);
|
|
619
626
|
if (files.length > 2e3) return { ...capture, complete: false };
|
|
620
627
|
for (const file of files) {
|
|
621
628
|
const evidence = await inspectionRead(
|
|
@@ -1036,6 +1043,28 @@ async function loadDecisionStore(rootDir) {
|
|
|
1036
1043
|
|
|
1037
1044
|
// src/decisions/drift.ts
|
|
1038
1045
|
import path8 from "path";
|
|
1046
|
+
|
|
1047
|
+
// src/decisions/excerpts.ts
|
|
1048
|
+
import { createHash as createHash4 } from "crypto";
|
|
1049
|
+
import { execFile as execFile2 } from "child_process";
|
|
1050
|
+
import { promisify as promisify2 } from "util";
|
|
1051
|
+
var exec = promisify2(execFile2);
|
|
1052
|
+
function driftBaseline(record) {
|
|
1053
|
+
const effective = effectiveDecision(record);
|
|
1054
|
+
const inspection = latestDecisionInspection(
|
|
1055
|
+
record,
|
|
1056
|
+
decisionApproval(effective) === "accepted" ? "operative" : "all"
|
|
1057
|
+
);
|
|
1058
|
+
return {
|
|
1059
|
+
record: effective,
|
|
1060
|
+
capture: inspection?.capture ?? (effective.version === 2 ? effective.capture : void 0),
|
|
1061
|
+
commits: [inspection?.headHash, effective.refreshedHash].filter(
|
|
1062
|
+
(hash) => !!hash && /^[a-f0-9]{40,64}$/.test(hash)
|
|
1063
|
+
)
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// src/decisions/drift.ts
|
|
1039
1068
|
async function computeDecisionDrift(rootDir, decisions) {
|
|
1040
1069
|
const resolvedRoot = path8.resolve(rootDir);
|
|
1041
1070
|
const store = decisions ? { records: decisions, diagnostics: [] } : await loadDecisionStore(resolvedRoot);
|
|
@@ -1098,15 +1127,11 @@ async function computeDecisionDrift(rootDir, decisions) {
|
|
|
1098
1127
|
};
|
|
1099
1128
|
for (const record of store.records) {
|
|
1100
1129
|
if (record.status !== "active") continue;
|
|
1101
|
-
const
|
|
1102
|
-
const
|
|
1103
|
-
record,
|
|
1104
|
-
decisionApproval(effective) === "accepted" ? "operative" : "all"
|
|
1105
|
-
);
|
|
1106
|
-
const state = await inspect(effective, inspection?.capture);
|
|
1130
|
+
const baseline = driftBaseline(record);
|
|
1131
|
+
const state = await inspect(baseline.record, baseline.capture);
|
|
1107
1132
|
report.freshness[record.id] = state.freshness;
|
|
1108
1133
|
if (state.changedFiles.length) report.staleDecisions[record.id] = state.changedFiles;
|
|
1109
|
-
if (
|
|
1134
|
+
if (baseline.record !== record)
|
|
1110
1135
|
(report.pendingProposals ??= {})[record.id] = await inspect(
|
|
1111
1136
|
record,
|
|
1112
1137
|
latestDecisionInspection(record)?.capture
|
|
@@ -1116,9 +1141,9 @@ async function computeDecisionDrift(rootDir, decisions) {
|
|
|
1116
1141
|
}
|
|
1117
1142
|
|
|
1118
1143
|
// src/review/cochange.ts
|
|
1119
|
-
import { execFile as
|
|
1120
|
-
import { promisify as
|
|
1121
|
-
var
|
|
1144
|
+
import { execFile as execFile3 } from "child_process";
|
|
1145
|
+
import { promisify as promisify3 } from "util";
|
|
1146
|
+
var exec2 = promisify3(execFile3);
|
|
1122
1147
|
var HISTORY_COMMITS = 1500;
|
|
1123
1148
|
var MIN_FILE_COMMITS = 5;
|
|
1124
1149
|
var MIN_SHARED_COMMITS = 4;
|
|
@@ -1126,11 +1151,11 @@ var MIN_COCHANGE_RATE = 0.6;
|
|
|
1126
1151
|
var MAX_COMMIT_FILES = 30;
|
|
1127
1152
|
async function buildMatrix(resolvedRoot) {
|
|
1128
1153
|
try {
|
|
1129
|
-
const { stdout: shallow } = await
|
|
1154
|
+
const { stdout: shallow } = await exec2("git", ["rev-parse", "--is-shallow-repository"], {
|
|
1130
1155
|
cwd: resolvedRoot
|
|
1131
1156
|
});
|
|
1132
1157
|
if (shallow.trim() === "true") return null;
|
|
1133
|
-
const { stdout } = await
|
|
1158
|
+
const { stdout } = await exec2(
|
|
1134
1159
|
"git",
|
|
1135
1160
|
["log", `-n${HISTORY_COMMITS}`, "--format=%x01", "--name-only", "-M"],
|
|
1136
1161
|
{ cwd: resolvedRoot, maxBuffer: 100 * 1024 * 1024 }
|
|
@@ -1858,11 +1883,11 @@ async function collectReviewEvidence(root, manifests, changedFiles, decisions, d
|
|
|
1858
1883
|
}
|
|
1859
1884
|
|
|
1860
1885
|
// src/review/review.ts
|
|
1861
|
-
var
|
|
1886
|
+
var exec3 = promisify4(execFile4);
|
|
1862
1887
|
var MAX_ANALYZED_FILES = 50;
|
|
1863
1888
|
async function resolveMergeBase(resolvedRoot, base, head) {
|
|
1864
1889
|
try {
|
|
1865
|
-
const { stdout } = await
|
|
1890
|
+
const { stdout } = await exec3("git", ["merge-base", base, head], {
|
|
1866
1891
|
cwd: resolvedRoot
|
|
1867
1892
|
});
|
|
1868
1893
|
return stdout.trim() || null;
|
|
@@ -1873,7 +1898,7 @@ async function resolveMergeBase(resolvedRoot, base, head) {
|
|
|
1873
1898
|
async function defaultBase(resolvedRoot) {
|
|
1874
1899
|
for (const ref of ["origin/HEAD", "origin/main", "origin/master", "main"]) {
|
|
1875
1900
|
try {
|
|
1876
|
-
await
|
|
1901
|
+
await exec3("git", ["rev-parse", "--verify", "--quiet", ref], {
|
|
1877
1902
|
cwd: resolvedRoot
|
|
1878
1903
|
});
|
|
1879
1904
|
return ref;
|