mason-context 0.16.2 → 0.17.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 +25 -0
- package/README.md +14 -10
- package/dist/mason-audit.js +1719 -751
- package/dist/mason-audit.js.map +1 -1
- package/dist/mason-auto.js +1963 -1244
- package/dist/mason-auto.js.map +1 -1
- package/dist/mason-drift.js +13 -0
- package/dist/mason-drift.js.map +1 -1
- package/dist/mason-hook.js +13 -0
- package/dist/mason-hook.js.map +1 -1
- package/dist/mason-mcp.js +2777 -1934
- package/dist/mason-mcp.js.map +1 -1
- package/dist/mason-review.js +127 -53
- package/dist/mason-review.js.map +1 -1
- package/dist/mason.js +2115 -1396
- package/dist/mason.js.map +1 -1
- package/package.json +5 -1
package/dist/mason-review.js
CHANGED
|
@@ -542,11 +542,24 @@ function decisionKnowledge(record, freshness = "unknown", proposalFreshness = "u
|
|
|
542
542
|
import fs4 from "fs/promises";
|
|
543
543
|
import path8 from "path";
|
|
544
544
|
import { createHash } from "crypto";
|
|
545
|
+
import { z as z3 } from "zod";
|
|
545
546
|
|
|
546
547
|
// src/context/lexical.ts
|
|
547
548
|
import path7 from "path";
|
|
548
549
|
|
|
549
550
|
// src/decisions/decisions.ts
|
|
551
|
+
var TITLE_MAX_CHARS = 80;
|
|
552
|
+
var BODY_RECOMMENDED_CHARS = 1500;
|
|
553
|
+
var BODY_MAX_CHARS = 2500;
|
|
554
|
+
var boundedText = (field, max, hint) => z3.string({
|
|
555
|
+
errorMap: (issue, ctx) => ({ message: issue.code === "too_big" ? `${field} has ${ctx.data.length} characters; maximum is ${max} (${ctx.data.length - max} over). ${hint}` : ctx.defaultError })
|
|
556
|
+
}).trim().min(1, `${field} must be non-empty`).max(max);
|
|
557
|
+
var decisionTitleSchema = boundedText("title", TITLE_MAX_CHARS, "Tighten it to a specific headline.");
|
|
558
|
+
var decisionBodySchema = boundedText(
|
|
559
|
+
"body",
|
|
560
|
+
BODY_MAX_CHARS,
|
|
561
|
+
`Aim for ${BODY_RECOMMENDED_CHARS} characters or fewer. Preserve the rule, reason, and exceptions; put supporting references in sources.`
|
|
562
|
+
);
|
|
550
563
|
async function loadDecisionStore(rootDir) {
|
|
551
564
|
const records = [];
|
|
552
565
|
const diagnostics = [];
|
|
@@ -675,10 +688,10 @@ async function findMissingPartners(resolvedRoot, changedFiles, existsOnDisk, all
|
|
|
675
688
|
|
|
676
689
|
// src/review/evidence.ts
|
|
677
690
|
import path10 from "path";
|
|
678
|
-
import { z as
|
|
691
|
+
import { z as z7 } from "zod";
|
|
679
692
|
|
|
680
693
|
// src/review/evidence/vitest.ts
|
|
681
|
-
import { z as
|
|
694
|
+
import { z as z4 } from "zod";
|
|
682
695
|
|
|
683
696
|
// src/review/evidence/paths.ts
|
|
684
697
|
function evidencePath(value, sourceRoot, uri = false) {
|
|
@@ -714,24 +727,24 @@ function messagePreview(message2) {
|
|
|
714
727
|
}
|
|
715
728
|
|
|
716
729
|
// src/review/evidence/vitest.ts
|
|
717
|
-
var count =
|
|
718
|
-
var schema =
|
|
719
|
-
success:
|
|
730
|
+
var count = z4.number().int().nonnegative();
|
|
731
|
+
var schema = z4.object({
|
|
732
|
+
success: z4.boolean(),
|
|
720
733
|
numTotalTests: count,
|
|
721
734
|
numPassedTests: count,
|
|
722
735
|
numFailedTests: count,
|
|
723
736
|
numPendingTests: count,
|
|
724
737
|
numTodoTests: count,
|
|
725
738
|
numFailedTestSuites: count,
|
|
726
|
-
testResults:
|
|
727
|
-
name:
|
|
728
|
-
status:
|
|
729
|
-
message:
|
|
730
|
-
assertionResults:
|
|
731
|
-
fullName:
|
|
732
|
-
status:
|
|
733
|
-
failureMessages:
|
|
734
|
-
location:
|
|
739
|
+
testResults: z4.array(z4.object({
|
|
740
|
+
name: z4.string().min(1),
|
|
741
|
+
status: z4.enum(["passed", "failed"]),
|
|
742
|
+
message: z4.string().optional(),
|
|
743
|
+
assertionResults: z4.array(z4.object({
|
|
744
|
+
fullName: z4.string(),
|
|
745
|
+
status: z4.enum(["passed", "failed", "pending", "skipped", "todo", "disabled"]),
|
|
746
|
+
failureMessages: z4.array(z4.string()).nullable().optional(),
|
|
747
|
+
location: z4.object({ line: count, column: count }).nullable().optional()
|
|
735
748
|
}))
|
|
736
749
|
}))
|
|
737
750
|
});
|
|
@@ -779,30 +792,30 @@ function parseVitest(raw, sourceRoot) {
|
|
|
779
792
|
}
|
|
780
793
|
|
|
781
794
|
// src/review/evidence/sarif.ts
|
|
782
|
-
import { z as
|
|
783
|
-
var index =
|
|
784
|
-
var artifact =
|
|
785
|
-
var message =
|
|
786
|
-
var level =
|
|
787
|
-
var location =
|
|
788
|
-
var schema2 =
|
|
789
|
-
version:
|
|
790
|
-
runs:
|
|
791
|
-
tool:
|
|
792
|
-
invocations:
|
|
793
|
-
versionControlProvenance:
|
|
794
|
-
originalUriBaseIds:
|
|
795
|
-
artifacts:
|
|
796
|
-
results:
|
|
797
|
-
ruleId:
|
|
795
|
+
import { z as z5 } from "zod";
|
|
796
|
+
var index = z5.number().int().nonnegative();
|
|
797
|
+
var artifact = z5.object({ uri: z5.string().optional(), uriBaseId: z5.string().optional(), index: index.optional() });
|
|
798
|
+
var message = z5.object({ text: z5.string().optional(), markdown: z5.string().optional(), id: z5.string().optional(), arguments: z5.array(z5.string()).optional() });
|
|
799
|
+
var level = z5.enum(["error", "warning", "note", "none"]);
|
|
800
|
+
var location = z5.object({ physicalLocation: z5.object({ artifactLocation: artifact.optional(), region: z5.object({ startLine: index.optional(), startColumn: index.optional() }).optional() }).optional() });
|
|
801
|
+
var schema2 = z5.object({
|
|
802
|
+
version: z5.literal("2.1.0"),
|
|
803
|
+
runs: z5.array(z5.object({
|
|
804
|
+
tool: z5.object({ driver: z5.object({ name: z5.string().min(1), rules: z5.array(z5.object({ id: z5.string(), defaultConfiguration: z5.object({ level: level.optional() }).optional(), messageStrings: z5.record(message).optional() })).optional(), globalMessageStrings: z5.record(message).optional() }) }),
|
|
805
|
+
invocations: z5.array(z5.object({ executionSuccessful: z5.boolean(), commandLine: z5.string().optional(), toolExecutionNotifications: z5.array(z5.object({ level: level.optional(), message })).optional() })).optional(),
|
|
806
|
+
versionControlProvenance: z5.array(z5.object({ revisionId: z5.string().optional() })).optional(),
|
|
807
|
+
originalUriBaseIds: z5.record(artifact).optional(),
|
|
808
|
+
artifacts: z5.array(z5.object({ location: artifact.optional() })).optional(),
|
|
809
|
+
results: z5.array(z5.object({
|
|
810
|
+
ruleId: z5.string().optional(),
|
|
798
811
|
ruleIndex: index.optional(),
|
|
799
812
|
message,
|
|
800
813
|
level: level.optional(),
|
|
801
|
-
kind:
|
|
802
|
-
baselineState:
|
|
803
|
-
suppressions:
|
|
804
|
-
locations:
|
|
805
|
-
relatedLocations:
|
|
814
|
+
kind: z5.enum(["fail", "pass", "open", "informational", "notApplicable", "review"]).optional(),
|
|
815
|
+
baselineState: z5.enum(["new", "unchanged", "updated", "absent"]).optional(),
|
|
816
|
+
suppressions: z5.array(z5.object({ kind: z5.enum(["inSource", "external"]), status: z5.enum(["accepted", "underReview", "rejected"]).optional() })).nullable().optional(),
|
|
817
|
+
locations: z5.array(location).optional(),
|
|
818
|
+
relatedLocations: z5.array(location).optional()
|
|
806
819
|
})).optional()
|
|
807
820
|
}))
|
|
808
821
|
});
|
|
@@ -891,20 +904,76 @@ function parseSarif(raw, sourceRoot) {
|
|
|
891
904
|
};
|
|
892
905
|
}
|
|
893
906
|
|
|
907
|
+
// src/review/evidence/check.ts
|
|
908
|
+
import { z as z6 } from "zod";
|
|
909
|
+
var schema3 = z6.object({
|
|
910
|
+
version: z6.literal(1),
|
|
911
|
+
commit: z6.string().regex(/^(?:[a-fA-F0-9]{40}|[a-fA-F0-9]{64})$/).optional(),
|
|
912
|
+
results: z6.array(z6.object({
|
|
913
|
+
name: z6.string().min(1).max(1e3),
|
|
914
|
+
status: z6.enum(["passed", "failed", "skipped"]),
|
|
915
|
+
message: z6.string().max(1e5).optional(),
|
|
916
|
+
locations: z6.array(z6.object({
|
|
917
|
+
file: z6.string().min(1).max(4e3),
|
|
918
|
+
line: z6.number().int().positive().optional(),
|
|
919
|
+
column: z6.number().int().positive().optional()
|
|
920
|
+
})).max(100).default([])
|
|
921
|
+
})).max(1e5)
|
|
922
|
+
});
|
|
923
|
+
function parseCheck(raw, sourceRoot) {
|
|
924
|
+
const report = schema3.parse(raw);
|
|
925
|
+
const diagnostics = [], findings = [];
|
|
926
|
+
const counts = { total: report.results.length, passed: 0, failed: 0, skipped: 0 };
|
|
927
|
+
for (const [index2, result] of report.results.entries()) {
|
|
928
|
+
counts[result.status]++;
|
|
929
|
+
const locations = result.locations.flatMap((location2) => {
|
|
930
|
+
const file = evidencePath(location2.file, sourceRoot);
|
|
931
|
+
if (!file) {
|
|
932
|
+
diagnostics.push(`Check location is outside the declared checkout or invalid: ${location2.file}`);
|
|
933
|
+
return [];
|
|
934
|
+
}
|
|
935
|
+
return [{ ...location2, file }];
|
|
936
|
+
});
|
|
937
|
+
if (result.status === "failed") findings.push({
|
|
938
|
+
id: String(index2),
|
|
939
|
+
...messagePreview([result.name, result.message].filter(Boolean).join("\n")),
|
|
940
|
+
severity: "error",
|
|
941
|
+
state: "active",
|
|
942
|
+
locations
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
if (!counts.total) diagnostics.push("No check results were recorded; an empty report is not passing evidence.");
|
|
946
|
+
if (counts.skipped) diagnostics.push(`${counts.skipped} checks were skipped; verification is incomplete.`);
|
|
947
|
+
return {
|
|
948
|
+
outcome: counts.failed ? "failed" : !counts.total ? "unavailable" : !counts.passed ? "skipped" : "passed",
|
|
949
|
+
counts,
|
|
950
|
+
findings,
|
|
951
|
+
incomplete: diagnostics.length > 0,
|
|
952
|
+
diagnostics,
|
|
953
|
+
...report.commit ? { reportedCommits: [report.commit] } : {}
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
|
|
894
957
|
// src/review/evidence.ts
|
|
895
|
-
var checkSchema =
|
|
896
|
-
id:
|
|
897
|
-
kind:
|
|
898
|
-
tool:
|
|
899
|
-
command:
|
|
900
|
-
commit:
|
|
901
|
-
workingTreeClean:
|
|
902
|
-
source:
|
|
903
|
-
sourceRoot:
|
|
904
|
-
status:
|
|
905
|
-
reason:
|
|
906
|
-
exitCode:
|
|
907
|
-
|
|
958
|
+
var checkSchema = z7.object({
|
|
959
|
+
id: z7.string().min(1).max(100),
|
|
960
|
+
kind: z7.enum(["tests", "static-analysis", "security", "complexity", "duplication", "documentation"]),
|
|
961
|
+
tool: z7.string().min(1).max(200),
|
|
962
|
+
command: z7.string().min(1).max(2e3),
|
|
963
|
+
commit: z7.string().regex(/^(?:[a-fA-F0-9]{40}|[a-fA-F0-9]{64})$/).nullable().optional(),
|
|
964
|
+
workingTreeClean: z7.boolean().optional(),
|
|
965
|
+
source: z7.string().max(2e3).optional(),
|
|
966
|
+
sourceRoot: z7.string().min(1).max(4e3).optional(),
|
|
967
|
+
status: z7.enum(["completed", "skipped", "unavailable"]).default("completed"),
|
|
968
|
+
reason: z7.string().min(1).max(2e3).optional(),
|
|
969
|
+
exitCode: z7.number().int().nullable().optional(),
|
|
970
|
+
environment: z7.object({
|
|
971
|
+
toolVersion: z7.string().min(1).max(200),
|
|
972
|
+
runtime: z7.string().min(1).max(200).optional(),
|
|
973
|
+
platform: z7.string().min(1).max(200).optional(),
|
|
974
|
+
configuration: z7.array(z7.string().min(1).max(4e3)).max(100).optional()
|
|
975
|
+
}).optional(),
|
|
976
|
+
report: z7.object({ format: z7.enum(["vitest-json", "sarif", "mason-check-json"]), path: z7.string().min(1).max(4e3) }).optional()
|
|
908
977
|
});
|
|
909
978
|
function relativeArtifact(root, file) {
|
|
910
979
|
const relative = path10.isAbsolute(file) ? path10.relative(root, file) : file;
|
|
@@ -946,7 +1015,7 @@ async function collectReviewEvidence(root, manifests, changedFiles, decisions, d
|
|
|
946
1015
|
let manifest, raw;
|
|
947
1016
|
try {
|
|
948
1017
|
manifest = relativeArtifact(root, supplied);
|
|
949
|
-
raw =
|
|
1018
|
+
raw = z7.object({ version: z7.literal(1), checks: z7.array(z7.unknown()) }).parse(await readStoreJson(root, manifest));
|
|
950
1019
|
} catch (error) {
|
|
951
1020
|
output.diagnostics.push(`${supplied}: ${String(error)}`);
|
|
952
1021
|
continue;
|
|
@@ -985,7 +1054,8 @@ async function collectReviewEvidence(root, manifests, changedFiles, decisions, d
|
|
|
985
1054
|
counts: {},
|
|
986
1055
|
incomplete: false,
|
|
987
1056
|
diagnostics: [],
|
|
988
|
-
truncated: false
|
|
1057
|
+
truncated: false,
|
|
1058
|
+
...check.environment ? { environment: check.environment } : {}
|
|
989
1059
|
};
|
|
990
1060
|
output.checks.push(result);
|
|
991
1061
|
if (check.workingTreeClean !== true) result.diagnostics.push("The check's working tree was dirty or not recorded; its results cannot be attributed to the claimed commit alone.");
|
|
@@ -998,13 +1068,13 @@ async function collectReviewEvidence(root, manifests, changedFiles, decisions, d
|
|
|
998
1068
|
}
|
|
999
1069
|
try {
|
|
1000
1070
|
if (!check.report) throw new Error("Completed check has no report artifact.");
|
|
1001
|
-
if (check.kind === "tests" !== (check.report.format === "vitest-json")) throw new Error("Test checks require vitest-json; analysis checks require sarif.");
|
|
1071
|
+
if (check.report.format !== "mason-check-json" && check.kind === "tests" !== (check.report.format === "vitest-json")) throw new Error("Test checks require vitest-json or mason-check-json; analysis checks require sarif or mason-check-json.");
|
|
1002
1072
|
const file = relativeArtifact(root, check.report.path);
|
|
1003
1073
|
const report = await readStoreJson(root, file);
|
|
1004
1074
|
if (report === null) throw new Error(`Report artifact is missing: ${file}`);
|
|
1005
1075
|
const sourceRoot = check.sourceRoot ?? root;
|
|
1006
1076
|
if (!path10.posix.isAbsolute(sourceRoot) && !/^[A-Za-z]:[\\/]/.test(sourceRoot)) throw new Error("sourceRoot must identify the absolute checkout root on the check runner.");
|
|
1007
|
-
const parsed = check.report.format === "vitest-json" ? parseVitest(report, sourceRoot) : parseSarif(report, sourceRoot);
|
|
1077
|
+
const parsed = check.report.format === "vitest-json" ? parseVitest(report, sourceRoot) : check.report.format === "mason-check-json" ? parseCheck(report, sourceRoot) : parseSarif(report, sourceRoot);
|
|
1008
1078
|
result.outcome = parsed.outcome;
|
|
1009
1079
|
result.counts = parsed.counts;
|
|
1010
1080
|
result.incomplete = parsed.incomplete;
|
|
@@ -1024,7 +1094,7 @@ async function collectReviewEvidence(root, manifests, changedFiles, decisions, d
|
|
|
1024
1094
|
result.outcome = "failed";
|
|
1025
1095
|
result.diagnostics.push(`Check command exited ${check.exitCode} despite a report with no active failures.`);
|
|
1026
1096
|
}
|
|
1027
|
-
if (check.
|
|
1097
|
+
if (check.kind === "tests" && pairs === void 0) {
|
|
1028
1098
|
try {
|
|
1029
1099
|
pairs = (await buildTestMap(root)).paired;
|
|
1030
1100
|
} catch (error) {
|
|
@@ -1246,6 +1316,10 @@ function evidenceSummary(report) {
|
|
|
1246
1316
|
for (const check of evidence.checks) {
|
|
1247
1317
|
lines.push(` [${check.kind}] ${display(check.id)}: ${check.outcome}, ${check.freshness} (${display(check.tool)})`);
|
|
1248
1318
|
lines.push(` command: ${display(check.command)}; tested commit: ${check.commit ?? "unknown"}`);
|
|
1319
|
+
if (check.environment) {
|
|
1320
|
+
lines.push(` environment: ${display(check.tool)} ${display(check.environment.toolVersion)}` + (check.environment.runtime ? `; ${display(check.environment.runtime)}` : "") + (check.environment.platform ? `; ${display(check.environment.platform)}` : ""));
|
|
1321
|
+
if (check.environment.configuration?.length) lines.push(" configuration: " + check.environment.configuration.map((value) => display(value)).join(", "));
|
|
1322
|
+
}
|
|
1249
1323
|
lines.push(` source: ${display(check.source ?? check.manifest)}; report: ${display(check.report?.path ?? "none")}`);
|
|
1250
1324
|
if (Object.keys(check.counts).length) lines.push(` counts: ${JSON.stringify(check.counts)}`);
|
|
1251
1325
|
for (const diagnostic of check.diagnostics.slice(0, 5)) lines.push(` note: ${display(diagnostic)}`);
|