mason-context 0.14.1 → 0.15.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 +13 -0
- package/README.md +2 -2
- package/dist/mason-audit.js +214 -69
- package/dist/mason-audit.js.map +1 -1
- package/dist/mason-auto.js +702 -766
- package/dist/mason-auto.js.map +1 -1
- package/dist/mason-mcp.js +2505 -2542
- package/dist/mason-mcp.js.map +1 -1
- package/dist/mason.js +876 -867
- package/dist/mason.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.15.0 — 2026-09-08
|
|
4
|
+
|
|
5
|
+
- Simplify project setup to use the installed `mason` command on PATH. Remove generated project launchers, copied runtimes, and per-project version pins. Global upgrades apply on the next launch; restart running assistants.
|
|
6
|
+
- Keep setup and hook ownership records in ignored `.mason/local/`, with shared options in `.mason/config.json`. Decisions and optional maps remain shareable; original repair evidence stays local and durable.
|
|
7
|
+
- Check command availability before configuration, retain evidence across interrupted writes, and require fresh activation observations after a Mason upgrade. Earlier generated setup layouts have no automatic migration; use a fresh project setup.
|
|
8
|
+
|
|
9
|
+
## 0.14.2 — 2026-09-07
|
|
10
|
+
|
|
11
|
+
- Remove the repository-wide automation inventory that blocked setup above 100,000 files and directories. Discover module source through Git and Mason exclusions, and search workspace command manifests only when needed.
|
|
12
|
+
- Preserve checks of explicitly documented ignored paths and workspace members. Validate symlinks at the inputs actually read, bound scoped discovery, and identify the operation responsible for a limit failure.
|
|
13
|
+
- Cover setup with 100,001 ignored generated files, retained evidence, and detection of a later source module.
|
|
14
|
+
- Request a new terminal after installation only when the installation directory is absent from the inherited PATH.
|
|
15
|
+
|
|
3
16
|
## 0.14.1 — 2026-09-07
|
|
4
17
|
|
|
5
18
|
- Configure PATH automatically during standalone installation for Bash, Zsh, POSIX sh, and Windows. Supported installations finish with a simple instruction to open a new terminal and run setup.
|
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ irm https://github.com/adrianczuczka/mason/releases/latest/download/install.ps1
|
|
|
28
28
|
|
|
29
29
|
No Node or npm required. Git is required. The installer configures PATH for Bash, Zsh, and Windows. [Platform details](docs/distribution.md) · [npm installation](docs/setup.md#unified-project-setup)
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
Run this in your Git repository (open a new terminal if the installer asks):
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
34
|
mason setup --host codex
|
|
@@ -43,7 +43,7 @@ Check that Mason is being used:
|
|
|
43
43
|
mason status
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
Status distinguishes
|
|
46
|
+
Setup uses your installed `mason` command—no project launch scripts or runtime copies. Status distinguishes configuration from observed use. [Setup and upgrades](docs/setup.md#unified-project-setup) · [Other MCP clients](docs/setup.md#other-clients)
|
|
47
47
|
|
|
48
48
|
### Try a check without setup
|
|
49
49
|
|
package/dist/mason-audit.js
CHANGED
|
@@ -93,6 +93,29 @@ async function readBoundedFile(file, maxBytes) {
|
|
|
93
93
|
await handle.close();
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
+
async function loadProjectConfig(root) {
|
|
97
|
+
try {
|
|
98
|
+
const canonicalRoot = await fs.realpath(root);
|
|
99
|
+
const configPath = await fs.realpath(path2.join(root, ".mason/config.json"));
|
|
100
|
+
if (!isWithinRoot(canonicalRoot, configPath)) throw new Error("Project configuration resolves outside the repository");
|
|
101
|
+
const raw = await readBoundedFile(configPath, 64 * 1024);
|
|
102
|
+
if (raw === null) throw new Error("Project configuration is not a regular file or exceeds 64 KiB");
|
|
103
|
+
const value = JSON.parse(raw);
|
|
104
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Expected a configuration object");
|
|
105
|
+
const config = {};
|
|
106
|
+
for (const key of ["patterns", "alwaysInclude", "ignore"]) {
|
|
107
|
+
if (value[key] === void 0) continue;
|
|
108
|
+
if (!Array.isArray(value[key]) || !value[key].every((s) => typeof s === "string")) {
|
|
109
|
+
throw new Error(`Configuration ${key} must be an array of strings`);
|
|
110
|
+
}
|
|
111
|
+
config[key] = value[key];
|
|
112
|
+
}
|
|
113
|
+
return config;
|
|
114
|
+
} catch (error) {
|
|
115
|
+
if (error.code === "ENOENT") return {};
|
|
116
|
+
throw new Error(`Cannot apply project file policy: ${error instanceof Error ? error.message : String(error)}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
96
119
|
|
|
97
120
|
// src/utils/storage.ts
|
|
98
121
|
import fs2 from "fs/promises";
|
|
@@ -744,8 +767,126 @@ async function checkDeletedReferences(ctx) {
|
|
|
744
767
|
}
|
|
745
768
|
|
|
746
769
|
// src/audit/checks/new-module.ts
|
|
747
|
-
import
|
|
770
|
+
import fg3 from "fast-glob";
|
|
771
|
+
import fs7 from "fs/promises";
|
|
772
|
+
import path10 from "path";
|
|
773
|
+
|
|
774
|
+
// src/audit/inputs.ts
|
|
775
|
+
import fs6 from "fs/promises";
|
|
776
|
+
import * as nativeFs from "fs";
|
|
748
777
|
import path9 from "path";
|
|
778
|
+
import { execFile as execFile6 } from "child_process";
|
|
779
|
+
import { promisify as promisify6 } from "util";
|
|
780
|
+
import fg2 from "fast-glob";
|
|
781
|
+
var execute = promisify6(execFile6);
|
|
782
|
+
var MAX_INPUTS = 1e5;
|
|
783
|
+
async function auditInputPath(root, file) {
|
|
784
|
+
if (file === ".") return fs6.realpath(root);
|
|
785
|
+
try {
|
|
786
|
+
return await storePath(root, file);
|
|
787
|
+
} catch (error) {
|
|
788
|
+
if (error instanceof Error && error.message.startsWith("Symlink")) {
|
|
789
|
+
throw new Error(`Audit input contains a symbolic link: ${file}. Its evidence could not be verified.`, { cause: error });
|
|
790
|
+
}
|
|
791
|
+
throw error;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
async function readAuditInput(root, file) {
|
|
795
|
+
try {
|
|
796
|
+
const value = await readBoundedFile(await auditInputPath(root, file), 10 * 1024 * 1024);
|
|
797
|
+
if (value === null) throw new Error(`Unreadable or oversized audit input: ${file}`);
|
|
798
|
+
return value;
|
|
799
|
+
} catch (error) {
|
|
800
|
+
if (error.code === "ENOENT") return null;
|
|
801
|
+
throw error;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
async function auditGlob(root, patterns, options) {
|
|
805
|
+
if (Array.isArray(patterns) && !patterns.length) return [];
|
|
806
|
+
const directories = /* @__PURE__ */ new Set();
|
|
807
|
+
const readdir2 = ((directory, opts, callback) => {
|
|
808
|
+
const relative = path9.relative(root, directory).split(path9.sep).join("/") || ".";
|
|
809
|
+
directories.add(relative);
|
|
810
|
+
if (directories.size > MAX_INPUTS) {
|
|
811
|
+
callback(new Error(`${options.label} exceeds ${MAX_INPUTS.toLocaleString("en-US")} directories while inspecting ${relative}. Narrow this check's inputs.`));
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
auditInputPath(root, relative).then(() => {
|
|
815
|
+
nativeFs.readdir(directory, opts, callback);
|
|
816
|
+
}, callback);
|
|
817
|
+
});
|
|
818
|
+
const lstat2 = ((file, callback) => {
|
|
819
|
+
const relative = path9.relative(root, file).split(path9.sep).join("/") || ".";
|
|
820
|
+
auditInputPath(root, relative).then(() => nativeFs.lstat(file, callback), callback);
|
|
821
|
+
});
|
|
822
|
+
const stream = fg2.stream(patterns, {
|
|
823
|
+
cwd: root,
|
|
824
|
+
ignore: options.ignore,
|
|
825
|
+
onlyFiles: false,
|
|
826
|
+
onlyDirectories: options.onlyDirectories,
|
|
827
|
+
objectMode: true,
|
|
828
|
+
followSymbolicLinks: options.followSymbolicLinks ?? true,
|
|
829
|
+
fs: { readdir: readdir2, lstat: lstat2 },
|
|
830
|
+
concurrency: 16
|
|
831
|
+
});
|
|
832
|
+
const matches = [];
|
|
833
|
+
try {
|
|
834
|
+
for await (const value of stream) {
|
|
835
|
+
const entry = value;
|
|
836
|
+
const file = entry.path;
|
|
837
|
+
if (options.select && !options.select(file)) continue;
|
|
838
|
+
await auditInputPath(root, file);
|
|
839
|
+
if (!options.onlyDirectories && !entry.dirent.isFile()) continue;
|
|
840
|
+
matches.push(file);
|
|
841
|
+
if (matches.length > MAX_INPUTS) {
|
|
842
|
+
const largest = [...matches.reduce((counts, item) => {
|
|
843
|
+
const dir = item.split("/")[0];
|
|
844
|
+
counts.set(dir, (counts.get(dir) ?? 0) + 1);
|
|
845
|
+
return counts;
|
|
846
|
+
}, /* @__PURE__ */ new Map())].sort((a, b) => b[1] - a[1]).slice(0, 3);
|
|
847
|
+
throw new Error(`${options.label} exceeds ${MAX_INPUTS.toLocaleString("en-US")} relevant paths. Largest directories: ${largest.map(([dir, count2]) => `${dir} (${count2})`).join(", ")}. Narrow this check's inputs.`);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
} finally {
|
|
851
|
+
stream.destroy();
|
|
852
|
+
}
|
|
853
|
+
return matches.sort();
|
|
854
|
+
}
|
|
855
|
+
async function gitSourcePaths(root) {
|
|
856
|
+
const { stdout } = await execute(
|
|
857
|
+
"git",
|
|
858
|
+
[
|
|
859
|
+
"ls-files",
|
|
860
|
+
"-z",
|
|
861
|
+
"--cached",
|
|
862
|
+
"--others",
|
|
863
|
+
"--exclude-standard",
|
|
864
|
+
"--",
|
|
865
|
+
...SOURCE_EXTENSIONS.map((extension) => `:(glob)**/*.${extension}`)
|
|
866
|
+
],
|
|
867
|
+
{ cwd: root, maxBuffer: 32 * 1024 * 1024, timeout: 1e4 }
|
|
868
|
+
);
|
|
869
|
+
return [...new Set(stdout.split("\0").filter(Boolean))].sort();
|
|
870
|
+
}
|
|
871
|
+
async function gitIgnoredPaths(root, files) {
|
|
872
|
+
if (!files.length) return /* @__PURE__ */ new Set();
|
|
873
|
+
const operation = execute(
|
|
874
|
+
"git",
|
|
875
|
+
["check-ignore", "-z", "--stdin"],
|
|
876
|
+
{ cwd: root, maxBuffer: 4 * 1024 * 1024, timeout: 1e4 }
|
|
877
|
+
);
|
|
878
|
+
operation.child.stdin.on("error", () => {
|
|
879
|
+
});
|
|
880
|
+
operation.child.stdin.end(files.join("\0") + "\0");
|
|
881
|
+
try {
|
|
882
|
+
return new Set((await operation).stdout.split("\0").filter(Boolean));
|
|
883
|
+
} catch (error) {
|
|
884
|
+
if (error.code === 1) return /* @__PURE__ */ new Set();
|
|
885
|
+
throw error;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// src/audit/checks/new-module.ts
|
|
749
890
|
var DIR_DENYLIST = /* @__PURE__ */ new Set([
|
|
750
891
|
"node_modules",
|
|
751
892
|
"dist",
|
|
@@ -777,22 +918,6 @@ function isMentioned(combinedDocs, name) {
|
|
|
777
918
|
);
|
|
778
919
|
return re.test(combinedDocs);
|
|
779
920
|
}
|
|
780
|
-
async function listSubdirs(absDir) {
|
|
781
|
-
const dirs = await fg2("*", {
|
|
782
|
-
cwd: absDir,
|
|
783
|
-
onlyDirectories: true,
|
|
784
|
-
suppressErrors: true
|
|
785
|
-
});
|
|
786
|
-
return dirs.filter((d) => !DIR_DENYLIST.has(d)).sort();
|
|
787
|
-
}
|
|
788
|
-
async function countSourceFiles(absDir) {
|
|
789
|
-
const files = await fg2(SOURCE_GLOB, {
|
|
790
|
-
cwd: absDir,
|
|
791
|
-
ignore: SOURCE_IGNORE,
|
|
792
|
-
suppressErrors: true
|
|
793
|
-
});
|
|
794
|
-
return files.length;
|
|
795
|
-
}
|
|
796
921
|
async function checkNewModules(ctx) {
|
|
797
922
|
const result = emptyResult();
|
|
798
923
|
if (ctx.docs.length === 0) return result;
|
|
@@ -820,21 +945,48 @@ async function checkNewModules(ctx) {
|
|
|
820
945
|
return result;
|
|
821
946
|
}
|
|
822
947
|
async function moduleCandidates(root, combinedDocs) {
|
|
948
|
+
const [config, sourcePaths] = await Promise.all([loadProjectConfig(root), gitSourcePaths(root)]);
|
|
949
|
+
const ignore = [...SOURCE_IGNORE, ...config.ignore ?? []];
|
|
950
|
+
const sources = sourcePaths.filter((file) => !file.split("/").some((part) => part.startsWith(".")));
|
|
951
|
+
const listSubdirs = async (dir) => {
|
|
952
|
+
const entries = await fs7.readdir(await auditInputPath(root, dir), { withFileTypes: true });
|
|
953
|
+
const dirs = entries.filter((entry) => (entry.isDirectory() || entry.isSymbolicLink()) && !entry.name.startsWith(".") && !DIR_DENYLIST.has(entry.name)).map((entry) => path10.posix.join(dir, entry.name));
|
|
954
|
+
const ignored = await gitIgnoredPaths(root, dirs);
|
|
955
|
+
const visible = new Set(dirs.filter((dir2) => !ignored.has(dir2) || sources.some((file) => file.startsWith(dir2 + "/"))));
|
|
956
|
+
return (await auditGlob(root, dir === "." ? "*" : `${fg3.escapePath(dir)}/*`, {
|
|
957
|
+
ignore,
|
|
958
|
+
onlyDirectories: true,
|
|
959
|
+
label: "Module directory discovery",
|
|
960
|
+
select: (file) => visible.has(file)
|
|
961
|
+
})).map((file) => path10.posix.basename(file));
|
|
962
|
+
};
|
|
963
|
+
const countSourceFiles = async (dir) => {
|
|
964
|
+
const files = sources.filter((file) => file.startsWith(dir + "/"));
|
|
965
|
+
let count2 = 0;
|
|
966
|
+
for (let offset = 0; offset < files.length; offset += 256) {
|
|
967
|
+
count2 += (await auditGlob(root, files.slice(offset, offset + 256).map((file) => fg3.escapePath(file)), {
|
|
968
|
+
ignore,
|
|
969
|
+
followSymbolicLinks: false,
|
|
970
|
+
label: `Source discovery in ${dir}`
|
|
971
|
+
})).length;
|
|
972
|
+
if (count2 > 1e5) throw new Error(`Source discovery in ${dir} exceeds 100,000 relevant files. Exclude generated source with .mason/config.json ignore patterns or narrow this audit's checks.`);
|
|
973
|
+
}
|
|
974
|
+
return count2;
|
|
975
|
+
};
|
|
823
976
|
const candidates = [];
|
|
824
|
-
for (const topDir of await listSubdirs(
|
|
825
|
-
const absTop = path9.join(root, topDir);
|
|
977
|
+
for (const topDir of await listSubdirs(".")) {
|
|
826
978
|
const topMentioned = isMentioned(combinedDocs, topDir);
|
|
827
979
|
if (!topMentioned) {
|
|
828
|
-
const count2 = await countSourceFiles(
|
|
980
|
+
const count2 = await countSourceFiles(topDir);
|
|
829
981
|
if (count2 >= 1) candidates.push({ dir: topDir, sourceFileCount: count2 });
|
|
830
982
|
continue;
|
|
831
983
|
}
|
|
832
|
-
const subdirs = await listSubdirs(
|
|
984
|
+
const subdirs = await listSubdirs(topDir);
|
|
833
985
|
const mentioned = subdirs.filter((s) => isMentioned(combinedDocs, s));
|
|
834
986
|
if (mentioned.length < ENUMERATION_THRESHOLD) continue;
|
|
835
987
|
for (const sub of subdirs) {
|
|
836
988
|
if (isMentioned(combinedDocs, sub)) continue;
|
|
837
|
-
const count2 = await countSourceFiles(
|
|
989
|
+
const count2 = await countSourceFiles(`${topDir}/${sub}`);
|
|
838
990
|
if (count2 >= SECOND_LEVEL_MIN_SOURCE_FILES) {
|
|
839
991
|
candidates.push({ dir: `${topDir}/${sub}`, sourceFileCount: count2 });
|
|
840
992
|
}
|
|
@@ -844,20 +996,11 @@ async function moduleCandidates(root, combinedDocs) {
|
|
|
844
996
|
}
|
|
845
997
|
|
|
846
998
|
// src/audit/checks/stale-count.ts
|
|
847
|
-
import
|
|
848
|
-
import path10 from "path";
|
|
849
|
-
import fg3 from "fast-glob";
|
|
999
|
+
import path11 from "path";
|
|
850
1000
|
var MEMBERS_CAP = 50;
|
|
851
|
-
async function readIfExists(absPath) {
|
|
852
|
-
try {
|
|
853
|
-
return await fs6.readFile(absPath, "utf-8");
|
|
854
|
-
} catch {
|
|
855
|
-
return null;
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
1001
|
async function countGradleModules(root) {
|
|
859
1002
|
for (const name of ["settings.gradle.kts", "settings.gradle"]) {
|
|
860
|
-
const content = await
|
|
1003
|
+
const content = await readAuditInput(root, name);
|
|
861
1004
|
if (content === null) continue;
|
|
862
1005
|
const members = [];
|
|
863
1006
|
for (const call of content.matchAll(/include\s*\(([^)]*)\)/g)) {
|
|
@@ -871,26 +1014,28 @@ async function countGradleModules(root) {
|
|
|
871
1014
|
return null;
|
|
872
1015
|
}
|
|
873
1016
|
async function countNpmWorkspaces(root) {
|
|
874
|
-
const pkgRaw = await
|
|
1017
|
+
const pkgRaw = await readAuditInput(root, "package.json");
|
|
875
1018
|
if (pkgRaw !== null) {
|
|
1019
|
+
let globs = [];
|
|
876
1020
|
try {
|
|
877
1021
|
const pkg = JSON.parse(pkgRaw);
|
|
878
|
-
|
|
879
|
-
if (globs.length > 0) {
|
|
880
|
-
const matched = await fg3(
|
|
881
|
-
globs.map((g) => `${g.replace(/\/+$/, "")}/package.json`),
|
|
882
|
-
{ cwd: root, ignore: ["**/node_modules/**"] }
|
|
883
|
-
);
|
|
884
|
-
return {
|
|
885
|
-
actual: matched.length,
|
|
886
|
-
countedFrom: "package.json workspaces",
|
|
887
|
-
members: matched.map((m) => path10.dirname(m)).sort()
|
|
888
|
-
};
|
|
889
|
-
}
|
|
1022
|
+
globs = Array.isArray(pkg.workspaces) ? pkg.workspaces : Array.isArray(pkg.workspaces?.packages) ? pkg.workspaces.packages : [];
|
|
890
1023
|
} catch {
|
|
891
1024
|
}
|
|
1025
|
+
if (globs.length > 0) {
|
|
1026
|
+
const matched = await auditGlob(
|
|
1027
|
+
root,
|
|
1028
|
+
globs.map((g) => `${g.replace(/\/+$/, "")}/package.json`),
|
|
1029
|
+
{ ignore: ["**/node_modules/**"], label: "npm workspace discovery" }
|
|
1030
|
+
);
|
|
1031
|
+
return {
|
|
1032
|
+
actual: matched.length,
|
|
1033
|
+
countedFrom: "package.json workspaces",
|
|
1034
|
+
members: matched.map((m) => path11.dirname(m)).sort()
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
892
1037
|
}
|
|
893
|
-
const pnpmRaw = await
|
|
1038
|
+
const pnpmRaw = await readAuditInput(root, "pnpm-workspace.yaml");
|
|
894
1039
|
if (pnpmRaw !== null) {
|
|
895
1040
|
const globs = [];
|
|
896
1041
|
let inPackages = false;
|
|
@@ -909,21 +1054,22 @@ async function countNpmWorkspaces(root) {
|
|
|
909
1054
|
}
|
|
910
1055
|
}
|
|
911
1056
|
if (globs.length > 0) {
|
|
912
|
-
const matched = await
|
|
1057
|
+
const matched = await auditGlob(
|
|
1058
|
+
root,
|
|
913
1059
|
globs.map((g) => `${g.replace(/\/+$/, "")}/package.json`),
|
|
914
|
-
{
|
|
1060
|
+
{ ignore: ["**/node_modules/**"], label: "pnpm workspace discovery" }
|
|
915
1061
|
);
|
|
916
1062
|
return {
|
|
917
1063
|
actual: matched.length,
|
|
918
1064
|
countedFrom: "pnpm-workspace.yaml",
|
|
919
|
-
members: matched.map((m) =>
|
|
1065
|
+
members: matched.map((m) => path11.dirname(m)).sort()
|
|
920
1066
|
};
|
|
921
1067
|
}
|
|
922
1068
|
}
|
|
923
1069
|
return null;
|
|
924
1070
|
}
|
|
925
1071
|
async function countCargoCrates(root) {
|
|
926
|
-
const content = await
|
|
1072
|
+
const content = await readAuditInput(root, "Cargo.toml");
|
|
927
1073
|
if (content === null) return null;
|
|
928
1074
|
const membersBlock = content.match(/members\s*=\s*\[([\s\S]*?)\]/);
|
|
929
1075
|
if (!membersBlock) return null;
|
|
@@ -934,12 +1080,12 @@ async function countCargoCrates(root) {
|
|
|
934
1080
|
const members = /* @__PURE__ */ new Set();
|
|
935
1081
|
for (const entry of entries) {
|
|
936
1082
|
if (/[*?[\]{}]/.test(entry)) {
|
|
937
|
-
const matched = await
|
|
938
|
-
|
|
939
|
-
|
|
1083
|
+
const matched = await auditGlob(root, `${entry.replace(/\/+$/, "")}/Cargo.toml`, {
|
|
1084
|
+
ignore: ["**/target/**"],
|
|
1085
|
+
label: "Cargo workspace discovery"
|
|
940
1086
|
});
|
|
941
|
-
for (const m of matched) members.add(
|
|
942
|
-
} else if (await
|
|
1087
|
+
for (const m of matched) members.add(path11.dirname(m));
|
|
1088
|
+
} else if (await readAuditInput(root, path11.posix.join(entry, "Cargo.toml")) !== null) {
|
|
943
1089
|
members.add(entry);
|
|
944
1090
|
}
|
|
945
1091
|
}
|
|
@@ -991,13 +1137,12 @@ async function checkStaleCounts(ctx) {
|
|
|
991
1137
|
}
|
|
992
1138
|
|
|
993
1139
|
// src/audit/checks/dead-command.ts
|
|
994
|
-
import fs7 from "fs/promises";
|
|
995
|
-
import path11 from "path";
|
|
996
|
-
import fg4 from "fast-glob";
|
|
997
1140
|
var AVAILABLE_SCRIPTS_CAP = 30;
|
|
998
|
-
async function scriptsOf(
|
|
1141
|
+
async function scriptsOf(root, manifest) {
|
|
1142
|
+
const raw = await readAuditInput(root, manifest);
|
|
1143
|
+
if (raw === null) return null;
|
|
999
1144
|
try {
|
|
1000
|
-
const pkg = JSON.parse(
|
|
1145
|
+
const pkg = JSON.parse(raw);
|
|
1001
1146
|
return pkg && typeof pkg.scripts === "object" && pkg.scripts !== null ? Object.keys(pkg.scripts) : [];
|
|
1002
1147
|
} catch {
|
|
1003
1148
|
return null;
|
|
@@ -1009,7 +1154,7 @@ async function checkDeadCommands(ctx) {
|
|
|
1009
1154
|
(doc) => doc.claims.commands.map((claim) => ({ doc, claim }))
|
|
1010
1155
|
);
|
|
1011
1156
|
if (commandClaims.length === 0) return result;
|
|
1012
|
-
const rootScripts = await scriptsOf(
|
|
1157
|
+
const rootScripts = await scriptsOf(ctx.root, "package.json");
|
|
1013
1158
|
if (rootScripts === null) {
|
|
1014
1159
|
result.skipped.push({
|
|
1015
1160
|
check: "dead-command",
|
|
@@ -1026,7 +1171,7 @@ async function checkDeadCommands(ctx) {
|
|
|
1026
1171
|
const manifests = await commandManifests(ctx.root);
|
|
1027
1172
|
manifestsChecked = ["package.json", ...manifests.sort()];
|
|
1028
1173
|
for (const manifest of manifests) {
|
|
1029
|
-
const scripts = await scriptsOf(
|
|
1174
|
+
const scripts = await scriptsOf(ctx.root, manifest);
|
|
1030
1175
|
for (const name of scripts ?? []) workspaceScripts.add(name);
|
|
1031
1176
|
}
|
|
1032
1177
|
return workspaceScripts;
|
|
@@ -1052,16 +1197,16 @@ async function checkDeadCommands(ctx) {
|
|
|
1052
1197
|
return result;
|
|
1053
1198
|
}
|
|
1054
1199
|
function commandManifests(root) {
|
|
1055
|
-
return
|
|
1056
|
-
|
|
1057
|
-
|
|
1200
|
+
return auditGlob(root, "**/package.json", {
|
|
1201
|
+
ignore: ["**/node_modules/**", "**/dist/**", "**/build/**", ".mason/reports/**", "package.json"],
|
|
1202
|
+
label: "Command manifest discovery"
|
|
1058
1203
|
});
|
|
1059
1204
|
}
|
|
1060
1205
|
|
|
1061
1206
|
// src/audit/release-metadata.ts
|
|
1062
|
-
import { execFile as
|
|
1063
|
-
import { promisify as
|
|
1064
|
-
var exec6 =
|
|
1207
|
+
import { execFile as execFile7 } from "child_process";
|
|
1208
|
+
import { promisify as promisify7 } from "util";
|
|
1209
|
+
var exec6 = promisify7(execFile7);
|
|
1065
1210
|
async function git(root, args) {
|
|
1066
1211
|
return (await exec6("git", args, { cwd: root, timeout: 1e4, maxBuffer: 2 * 1024 * 1024 })).stdout;
|
|
1067
1212
|
}
|