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-drift.js
CHANGED
|
@@ -31,7 +31,7 @@ var init_types = __esm({
|
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
// src/drift/cli.ts
|
|
34
|
-
import
|
|
34
|
+
import path9 from "path";
|
|
35
35
|
|
|
36
36
|
// src/utils/git-read.ts
|
|
37
37
|
import { execFile } from "child_process";
|
|
@@ -84,10 +84,7 @@ function execGit(args, options) {
|
|
|
84
84
|
|
|
85
85
|
// src/drift/drift.ts
|
|
86
86
|
import fs4 from "fs/promises";
|
|
87
|
-
import
|
|
88
|
-
|
|
89
|
-
// src/snapshot/snapshot.ts
|
|
90
|
-
import path5 from "path";
|
|
87
|
+
import path4 from "path";
|
|
91
88
|
|
|
92
89
|
// src/utils/validation.ts
|
|
93
90
|
function isRecord(value) {
|
|
@@ -387,11 +384,13 @@ async function storePath(root, relative, createParents = false) {
|
|
|
387
384
|
}
|
|
388
385
|
return current;
|
|
389
386
|
}
|
|
390
|
-
|
|
387
|
+
var STORE_MAX_BYTES = 10 * 1024 * 1024;
|
|
388
|
+
async function readStoreJson(root, relative, maxBytes = STORE_MAX_BYTES) {
|
|
391
389
|
try {
|
|
392
390
|
const file = await storePath(root, relative);
|
|
393
|
-
const raw = await readBoundedFile(file,
|
|
394
|
-
if (raw === null)
|
|
391
|
+
const raw = await readBoundedFile(file, maxBytes);
|
|
392
|
+
if (raw === null)
|
|
393
|
+
throw new Error(`file is not regular or exceeds ${maxBytes / 1024 / 1024} MiB`);
|
|
395
394
|
const parsed = JSON.parse(raw);
|
|
396
395
|
if (parsed === null) throw new Error("expected a JSON object, received null");
|
|
397
396
|
return parsed;
|
|
@@ -413,9 +412,6 @@ import fs3 from "fs/promises";
|
|
|
413
412
|
import os from "os";
|
|
414
413
|
var lockOwnerSchema = z.object({ host: z.string(), pid: z.number().int().positive() });
|
|
415
414
|
|
|
416
|
-
// src/test-map.ts
|
|
417
|
-
import path4 from "path";
|
|
418
|
-
|
|
419
415
|
// src/snapshot/snapshot.ts
|
|
420
416
|
var repoPath = z2.string().refine((value) => normalizeRepoPath(value) !== null, "Expected a relative repository path");
|
|
421
417
|
var verificationFields = {
|
|
@@ -550,7 +546,7 @@ async function findGhostFiles(resolvedRoot, mappedFiles) {
|
|
|
550
546
|
const ghosts = [];
|
|
551
547
|
for (const file of mappedFiles) {
|
|
552
548
|
try {
|
|
553
|
-
await fs4.access(
|
|
549
|
+
await fs4.access(path4.join(resolvedRoot, file));
|
|
554
550
|
} catch {
|
|
555
551
|
ghosts.push(file);
|
|
556
552
|
}
|
|
@@ -558,7 +554,7 @@ async function findGhostFiles(resolvedRoot, mappedFiles) {
|
|
|
558
554
|
return ghosts.sort();
|
|
559
555
|
}
|
|
560
556
|
async function computeDrift(rootDir) {
|
|
561
|
-
const root =
|
|
557
|
+
const root = path4.resolve(rootDir);
|
|
562
558
|
const snapshot = await loadSnapshot(root);
|
|
563
559
|
if (!snapshot) return null;
|
|
564
560
|
const [headHash, workingTree] = await Promise.all([
|
|
@@ -673,11 +669,11 @@ import fg3 from "fast-glob";
|
|
|
673
669
|
// src/audit/inspection.ts
|
|
674
670
|
import fg2 from "fast-glob";
|
|
675
671
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
676
|
-
import
|
|
672
|
+
import path5 from "path";
|
|
677
673
|
var inspections = new AsyncLocalStorage2();
|
|
678
674
|
function inspectionRead(root, key, read) {
|
|
679
675
|
const inspection = inspections.getStore();
|
|
680
|
-
if (!inspection || inspection.root !==
|
|
676
|
+
if (!inspection || inspection.root !== path5.resolve(root)) return read();
|
|
681
677
|
if (!inspection.reads.has(key)) {
|
|
682
678
|
const pending = read();
|
|
683
679
|
inspection.reads.set(key, pending);
|
|
@@ -718,7 +714,12 @@ async function captureAnchorScopes(root, scopes) {
|
|
|
718
714
|
);
|
|
719
715
|
const inventory = access.inventory();
|
|
720
716
|
if (!inventory) throw new Error("Git anchor inventory unavailable");
|
|
721
|
-
const
|
|
717
|
+
const visible = indexMatchingPaths(
|
|
718
|
+
union.map((anchor) => [anchor]),
|
|
719
|
+
inventory
|
|
720
|
+
);
|
|
721
|
+
const hidden = new Set(union.filter((_, index) => !visible[index].length));
|
|
722
|
+
const ignored = hidden.size ? (await inspectionGit(
|
|
722
723
|
[
|
|
723
724
|
"ls-files",
|
|
724
725
|
"-z",
|
|
@@ -726,13 +727,11 @@ async function captureAnchorScopes(root, scopes) {
|
|
|
726
727
|
"--ignored",
|
|
727
728
|
"--exclude-standard",
|
|
728
729
|
"--",
|
|
729
|
-
...
|
|
730
|
+
...[...hidden].map((anchor) => `:(literal)${anchor}`)
|
|
730
731
|
],
|
|
731
732
|
{ cwd: root, maxBuffer: 8 * 1024 * 1024 }
|
|
732
|
-
);
|
|
733
|
-
const files = [
|
|
734
|
-
.../* @__PURE__ */ new Set([...inventory, ...ignored.stdout.split("\0").filter(Boolean)])
|
|
735
|
-
].sort();
|
|
733
|
+
)).stdout.split("\0").filter(Boolean) : [];
|
|
734
|
+
const files = [.../* @__PURE__ */ new Set([...inventory, ...ignored])].sort();
|
|
736
735
|
const patterns = await Promise.all(
|
|
737
736
|
union.map(async (anchor) => {
|
|
738
737
|
const literal = fg3.escapePath(anchor);
|
|
@@ -748,7 +747,7 @@ async function captureAnchorScopes(root, scopes) {
|
|
|
748
747
|
const filesByScope = new Map(
|
|
749
748
|
scopes.map((scope, index) => [JSON.stringify([...new Set(scope)].sort()), indexed[index]])
|
|
750
749
|
);
|
|
751
|
-
return { filesByScope, eligible };
|
|
750
|
+
return { filesByScope, eligible, hidden, ignored: new Set(ignored) };
|
|
752
751
|
};
|
|
753
752
|
return Promise.all(
|
|
754
753
|
scopes.map((scope) => {
|
|
@@ -760,8 +759,10 @@ async function captureAnchorScopes(root, scopes) {
|
|
|
760
759
|
const capture = { anchors, files: {}, complete: true };
|
|
761
760
|
if (!anchors.length) return capture;
|
|
762
761
|
try {
|
|
763
|
-
const { eligible, filesByScope } = await (context ??= prepareContext());
|
|
764
|
-
const files = filesByScope.get(JSON.stringify(anchors))
|
|
762
|
+
const { eligible, filesByScope, hidden, ignored } = await (context ??= prepareContext());
|
|
763
|
+
const files = filesByScope.get(JSON.stringify(anchors)).filter(
|
|
764
|
+
(file) => !ignored.has(file) || anchors.some((anchor) => hidden.has(anchor) && anchorMatches(anchor, file))
|
|
765
|
+
);
|
|
765
766
|
if (files.length > 2e3) return { ...capture, complete: false };
|
|
766
767
|
for (const file of files) {
|
|
767
768
|
const evidence = await inspectionRead(
|
|
@@ -817,16 +818,16 @@ function changedCaptureFiles(before, after) {
|
|
|
817
818
|
}
|
|
818
819
|
|
|
819
820
|
// src/decisions/drift.ts
|
|
820
|
-
import
|
|
821
|
+
import path8 from "path";
|
|
821
822
|
|
|
822
823
|
// src/decisions/decisions.ts
|
|
823
824
|
import fs6 from "fs/promises";
|
|
824
|
-
import
|
|
825
|
+
import path7 from "path";
|
|
825
826
|
import { createHash as createHash3 } from "crypto";
|
|
826
827
|
import { z as z5 } from "zod";
|
|
827
828
|
|
|
828
829
|
// src/context/lexical.ts
|
|
829
|
-
import
|
|
830
|
+
import path6 from "path";
|
|
830
831
|
|
|
831
832
|
// src/decisions/provenance.ts
|
|
832
833
|
import { z as z4 } from "zod";
|
|
@@ -908,6 +909,7 @@ var currentSchema = legacySchema.extend({
|
|
|
908
909
|
inspector: text(200),
|
|
909
910
|
note: text(1500),
|
|
910
911
|
outcome: z4.literal("no_contradiction_found"),
|
|
912
|
+
scope: z4.literal("operative").optional(),
|
|
911
913
|
decisionDigest: z4.string().regex(/^[a-f0-9]{64}$/),
|
|
912
914
|
headHash: z4.string(),
|
|
913
915
|
capture: anchorCaptureSchema
|
|
@@ -991,8 +993,19 @@ function effectiveDecision(record) {
|
|
|
991
993
|
history: record.history.slice(0, index + 1)
|
|
992
994
|
};
|
|
993
995
|
}
|
|
994
|
-
function decisionInspectionDigest(record) {
|
|
996
|
+
function decisionInspectionDigest(record, scope = "all") {
|
|
995
997
|
const effective = effectiveDecision(record);
|
|
998
|
+
if (scope === "operative")
|
|
999
|
+
return createHash2("sha256").update(
|
|
1000
|
+
JSON.stringify([
|
|
1001
|
+
"operative",
|
|
1002
|
+
record.status,
|
|
1003
|
+
decisionApproval(effective),
|
|
1004
|
+
effective.version === 2 ? effective.revision : 0,
|
|
1005
|
+
decisionContent(effective),
|
|
1006
|
+
effective.refreshedHash
|
|
1007
|
+
])
|
|
1008
|
+
).digest("hex");
|
|
996
1009
|
return createHash2("sha256").update(
|
|
997
1010
|
JSON.stringify([
|
|
998
1011
|
record.status,
|
|
@@ -1004,8 +1017,10 @@ function decisionInspectionDigest(record) {
|
|
|
1004
1017
|
])
|
|
1005
1018
|
).digest("hex");
|
|
1006
1019
|
}
|
|
1007
|
-
function latestDecisionInspection(record) {
|
|
1008
|
-
return record.version === 2 ? record.inspections?.filter(
|
|
1020
|
+
function latestDecisionInspection(record, scope = "all") {
|
|
1021
|
+
return record.version === 2 ? record.inspections?.filter(
|
|
1022
|
+
(i) => (scope === "operative" || i.scope !== "operative") && i.decisionDigest === decisionInspectionDigest(record, i.scope ?? "all")
|
|
1023
|
+
).at(-1) : void 0;
|
|
1009
1024
|
}
|
|
1010
1025
|
|
|
1011
1026
|
// src/decisions/decisions.ts
|
|
@@ -1084,9 +1099,29 @@ async function loadDecisionStore(rootDir) {
|
|
|
1084
1099
|
);
|
|
1085
1100
|
}
|
|
1086
1101
|
|
|
1102
|
+
// src/decisions/excerpts.ts
|
|
1103
|
+
import { createHash as createHash4 } from "crypto";
|
|
1104
|
+
import { execFile as execFile2 } from "child_process";
|
|
1105
|
+
import { promisify as promisify2 } from "util";
|
|
1106
|
+
var exec = promisify2(execFile2);
|
|
1107
|
+
function driftBaseline(record) {
|
|
1108
|
+
const effective = effectiveDecision(record);
|
|
1109
|
+
const inspection = latestDecisionInspection(
|
|
1110
|
+
record,
|
|
1111
|
+
decisionApproval(effective) === "accepted" ? "operative" : "all"
|
|
1112
|
+
);
|
|
1113
|
+
return {
|
|
1114
|
+
record: effective,
|
|
1115
|
+
capture: inspection?.capture ?? (effective.version === 2 ? effective.capture : void 0),
|
|
1116
|
+
commits: [inspection?.headHash, effective.refreshedHash].filter(
|
|
1117
|
+
(hash) => !!hash && /^[a-f0-9]{40,64}$/.test(hash)
|
|
1118
|
+
)
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1087
1122
|
// src/decisions/drift.ts
|
|
1088
1123
|
async function computeDecisionDrift(rootDir, decisions) {
|
|
1089
|
-
const resolvedRoot =
|
|
1124
|
+
const resolvedRoot = path8.resolve(rootDir);
|
|
1090
1125
|
const store = decisions ? { records: decisions, diagnostics: [] } : await loadDecisionStore(resolvedRoot);
|
|
1091
1126
|
const report = {
|
|
1092
1127
|
historyAvailable: true,
|
|
@@ -1101,11 +1136,15 @@ async function computeDecisionDrift(rootDir, decisions) {
|
|
|
1101
1136
|
]);
|
|
1102
1137
|
const changesByHash = /* @__PURE__ */ new Map();
|
|
1103
1138
|
const baselines = store.records.filter((record) => record.status === "active").flatMap((record) => {
|
|
1104
|
-
const
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1139
|
+
const effective = effectiveDecision(record);
|
|
1140
|
+
return [.../* @__PURE__ */ new Set([effective, record])].flatMap((candidate) => {
|
|
1141
|
+
const inspection = latestDecisionInspection(
|
|
1142
|
+
record,
|
|
1143
|
+
candidate === effective && decisionApproval(effective) === "accepted" ? "operative" : "all"
|
|
1144
|
+
);
|
|
1145
|
+
const capture = inspection?.capture ?? (candidate.version === 2 ? candidate.capture : void 0);
|
|
1146
|
+
return capture ? [capture] : [];
|
|
1147
|
+
});
|
|
1109
1148
|
});
|
|
1110
1149
|
const captures = await captureAnchorScopes(
|
|
1111
1150
|
resolvedRoot,
|
|
@@ -1143,13 +1182,15 @@ async function computeDecisionDrift(rootDir, decisions) {
|
|
|
1143
1182
|
};
|
|
1144
1183
|
for (const record of store.records) {
|
|
1145
1184
|
if (record.status !== "active") continue;
|
|
1146
|
-
const
|
|
1147
|
-
const
|
|
1148
|
-
const state = await inspect(effective, inspection?.capture);
|
|
1185
|
+
const baseline = driftBaseline(record);
|
|
1186
|
+
const state = await inspect(baseline.record, baseline.capture);
|
|
1149
1187
|
report.freshness[record.id] = state.freshness;
|
|
1150
1188
|
if (state.changedFiles.length) report.staleDecisions[record.id] = state.changedFiles;
|
|
1151
|
-
if (
|
|
1152
|
-
(report.pendingProposals ??= {})[record.id] = await inspect(
|
|
1189
|
+
if (baseline.record !== record)
|
|
1190
|
+
(report.pendingProposals ??= {})[record.id] = await inspect(
|
|
1191
|
+
record,
|
|
1192
|
+
latestDecisionInspection(record)?.capture
|
|
1193
|
+
);
|
|
1153
1194
|
}
|
|
1154
1195
|
return report;
|
|
1155
1196
|
}
|
|
@@ -1270,18 +1311,14 @@ function formatRefreshPrompt(report, decisionDrift) {
|
|
|
1270
1311
|
)
|
|
1271
1312
|
);
|
|
1272
1313
|
lines.push("");
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
);
|
|
1278
|
-
} else {
|
|
1314
|
+
lines.push(
|
|
1315
|
+
"PROCEDURE (scoped refresh): inspect the changed and unmapped files with your own source tools. Use get_snapshot to read the existing map, then save_snapshot with only evidence-supported corrected features/flows. Remove deleted entries with removeFeatures/removeFlows. Managed batch creation and reduction have been removed."
|
|
1316
|
+
);
|
|
1317
|
+
if (!report.historyAvailable || report.recommendation === "full-rebuild")
|
|
1279
1318
|
lines.push(
|
|
1280
|
-
"
|
|
1319
|
+
"History or coverage is insufficient for a bounded refresh. Inspect all affected entries directly; do not claim freshness for entries you have not checked."
|
|
1281
1320
|
);
|
|
1282
|
-
|
|
1283
|
-
lines.push(`files: ${JSON.stringify(scopedFiles)}`);
|
|
1284
|
-
}
|
|
1321
|
+
lines.push(`files: ${JSON.stringify([...report.changedFiles, ...report.unmappedFiles])}`);
|
|
1285
1322
|
const staleDecisionIds = Object.keys(decisionDrift.staleDecisions);
|
|
1286
1323
|
if (staleDecisionIds.length > 0) {
|
|
1287
1324
|
lines.push("");
|
|
@@ -1319,7 +1356,7 @@ async function runDriftCli(argv, io = {
|
|
|
1319
1356
|
io.out(USAGE);
|
|
1320
1357
|
return 0;
|
|
1321
1358
|
}
|
|
1322
|
-
const rootDir =
|
|
1359
|
+
const rootDir = path9.resolve(args.dir);
|
|
1323
1360
|
let report;
|
|
1324
1361
|
try {
|
|
1325
1362
|
report = await computeDrift(rootDir);
|
|
@@ -1329,7 +1366,7 @@ async function runDriftCli(argv, io = {
|
|
|
1329
1366
|
}
|
|
1330
1367
|
if (!report) {
|
|
1331
1368
|
io.err(
|
|
1332
|
-
`No Mason snapshot found at ${
|
|
1369
|
+
`No Mason snapshot found at ${path9.join(rootDir, ".mason", "snapshot.json")}. Managed map creation has been removed; decisions and context work without a map.`
|
|
1333
1370
|
);
|
|
1334
1371
|
return 2;
|
|
1335
1372
|
}
|