mason-context 0.3.1 → 0.3.3
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/dist/bin/mason-mcp.js +12 -11
- package/dist/bin/mason-mcp.js.map +1 -1
- package/dist/bin/mason.js +54 -24
- package/dist/bin/mason.js.map +1 -1
- package/dist/src/cli.js +54 -24
- package/dist/src/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/mason.js
CHANGED
|
@@ -378,6 +378,8 @@ function spawnWithStdin(command, args, input) {
|
|
|
378
378
|
stdio: ["pipe", "pipe", "pipe"],
|
|
379
379
|
timeout: 3e5
|
|
380
380
|
});
|
|
381
|
+
const onSigint = () => proc.kill("SIGINT");
|
|
382
|
+
process.on("SIGINT", onSigint);
|
|
381
383
|
let stdout = "";
|
|
382
384
|
let stderr = "";
|
|
383
385
|
proc.stdout.on("data", (data) => {
|
|
@@ -387,25 +389,28 @@ function spawnWithStdin(command, args, input) {
|
|
|
387
389
|
stderr += data.toString();
|
|
388
390
|
});
|
|
389
391
|
proc.on("close", (code) => {
|
|
392
|
+
process.off("SIGINT", onSigint);
|
|
390
393
|
if (code === 0) {
|
|
391
394
|
resolve(stdout.trim());
|
|
392
395
|
} else {
|
|
393
396
|
reject(new Error(`${command} exited with code ${code}: ${stderr}`));
|
|
394
397
|
}
|
|
395
398
|
});
|
|
396
|
-
proc.on("error",
|
|
399
|
+
proc.on("error", (err) => {
|
|
400
|
+
process.off("SIGINT", onSigint);
|
|
401
|
+
reject(err);
|
|
402
|
+
});
|
|
397
403
|
proc.stdin.write(input);
|
|
398
404
|
proc.stdin.end();
|
|
399
405
|
});
|
|
400
406
|
}
|
|
401
407
|
async function callClaudeCLI(system, userMessage) {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
${userMessage}`;
|
|
405
|
-
return spawnWithStdin("claude", ["-p"], prompt);
|
|
408
|
+
return spawnWithStdin("claude", ["-p", "--system-prompt", system], userMessage);
|
|
406
409
|
}
|
|
407
410
|
async function callGeminiCLI(system, userMessage) {
|
|
408
|
-
const prompt =
|
|
411
|
+
const prompt = `<system>
|
|
412
|
+
${system}
|
|
413
|
+
</system>
|
|
409
414
|
|
|
410
415
|
${userMessage}`;
|
|
411
416
|
return spawnWithStdin("gemini", ["-p", ""], prompt);
|
|
@@ -1088,6 +1093,7 @@ import fs4 from "fs/promises";
|
|
|
1088
1093
|
import path4 from "path";
|
|
1089
1094
|
import { execFile as execFile6 } from "child_process";
|
|
1090
1095
|
import { promisify as promisify6 } from "util";
|
|
1096
|
+
import fg4 from "fast-glob";
|
|
1091
1097
|
function snapshotDir(rootDir) {
|
|
1092
1098
|
return path4.join(rootDir, ".mason");
|
|
1093
1099
|
}
|
|
@@ -1151,7 +1157,31 @@ function parseSnapshotResponse(raw) {
|
|
|
1151
1157
|
}
|
|
1152
1158
|
async function createSnapshot(rootDir, config) {
|
|
1153
1159
|
const resolvedRoot = path4.resolve(rootDir);
|
|
1154
|
-
const
|
|
1160
|
+
const allFiles = await fg4(
|
|
1161
|
+
"**/*.{ts,tsx,js,jsx,kt,kts,java,py,go,rs,swift,rb,cs,cpp,c,dart}",
|
|
1162
|
+
{
|
|
1163
|
+
cwd: resolvedRoot,
|
|
1164
|
+
ignore: [
|
|
1165
|
+
"**/node_modules/**",
|
|
1166
|
+
"**/dist/**",
|
|
1167
|
+
"**/build/**",
|
|
1168
|
+
"**/.gradle/**",
|
|
1169
|
+
"**/target/**",
|
|
1170
|
+
"**/.git/**",
|
|
1171
|
+
"**/vendor/**",
|
|
1172
|
+
"**/__pycache__/**",
|
|
1173
|
+
"**/venv/**",
|
|
1174
|
+
"**/.venv/**",
|
|
1175
|
+
"**/*.min.*",
|
|
1176
|
+
"**/*.map",
|
|
1177
|
+
"**/generated/**",
|
|
1178
|
+
"**/R.java",
|
|
1179
|
+
"**/BuildConfig.java"
|
|
1180
|
+
]
|
|
1181
|
+
}
|
|
1182
|
+
);
|
|
1183
|
+
const sampleCount = Math.min(80, Math.max(20, Math.round(allFiles.length * 0.15)));
|
|
1184
|
+
const sampled = await sampleFiles(resolvedRoot, sampleCount);
|
|
1155
1185
|
const filesWithContent = [];
|
|
1156
1186
|
for (const sample of sampled) {
|
|
1157
1187
|
const full = await readFullFile(resolvedRoot, sample.path);
|
|
@@ -1318,7 +1348,7 @@ import fs5 from "fs/promises";
|
|
|
1318
1348
|
import path5 from "path";
|
|
1319
1349
|
import { execFile as execFile7 } from "child_process";
|
|
1320
1350
|
import { promisify as promisify7 } from "util";
|
|
1321
|
-
import
|
|
1351
|
+
import fg5 from "fast-glob";
|
|
1322
1352
|
async function analyzeImpact(rootDir, targetFiles) {
|
|
1323
1353
|
const resolvedRoot = path5.resolve(rootDir);
|
|
1324
1354
|
const resolvedTargets = await resolveTargetFiles(resolvedRoot, targetFiles);
|
|
@@ -1341,7 +1371,7 @@ async function resolveTargetFiles(rootDir, targets) {
|
|
|
1341
1371
|
resolved.push(target);
|
|
1342
1372
|
continue;
|
|
1343
1373
|
}
|
|
1344
|
-
const matches = await
|
|
1374
|
+
const matches = await fg5(`**/${target}`, {
|
|
1345
1375
|
cwd: rootDir,
|
|
1346
1376
|
ignore: IGNORE2
|
|
1347
1377
|
});
|
|
@@ -1349,7 +1379,7 @@ async function resolveTargetFiles(rootDir, targets) {
|
|
|
1349
1379
|
resolved.push(matches[0]);
|
|
1350
1380
|
} else {
|
|
1351
1381
|
const noExt = target.replace(/\.[^.]+$/, "");
|
|
1352
|
-
const extMatches = await
|
|
1382
|
+
const extMatches = await fg5(`**/${noExt}.*`, {
|
|
1353
1383
|
cwd: rootDir,
|
|
1354
1384
|
ignore: IGNORE2
|
|
1355
1385
|
});
|
|
@@ -1406,7 +1436,7 @@ async function getReferences(rootDir, targetFiles) {
|
|
|
1406
1436
|
const basename = path5.basename(target).replace(/\.[^.]+$/, "");
|
|
1407
1437
|
searchNames.add(basename);
|
|
1408
1438
|
}
|
|
1409
|
-
const allSourceFiles = await
|
|
1439
|
+
const allSourceFiles = await fg5(`**/${SOURCE_EXTENSIONS2}`, {
|
|
1410
1440
|
cwd: rootDir,
|
|
1411
1441
|
ignore: IGNORE2
|
|
1412
1442
|
});
|
|
@@ -1455,7 +1485,7 @@ async function getRelatedTests(rootDir, targetFiles) {
|
|
|
1455
1485
|
"**/*Test.swift",
|
|
1456
1486
|
"**/*_test.rs"
|
|
1457
1487
|
];
|
|
1458
|
-
const testFiles = await
|
|
1488
|
+
const testFiles = await fg5(testPatterns, { cwd: rootDir, ignore: IGNORE2 });
|
|
1459
1489
|
const results = [];
|
|
1460
1490
|
for (const target of targetFiles) {
|
|
1461
1491
|
const targetBaseName = path5.basename(target).replace(/\.[^.]+$/, "");
|
|
@@ -1502,7 +1532,7 @@ import fs6 from "fs/promises";
|
|
|
1502
1532
|
import path6 from "path";
|
|
1503
1533
|
import { execFile as execFile8 } from "child_process";
|
|
1504
1534
|
import { promisify as promisify8 } from "util";
|
|
1505
|
-
import
|
|
1535
|
+
import fg6 from "fast-glob";
|
|
1506
1536
|
async function buildContext(dir) {
|
|
1507
1537
|
return {
|
|
1508
1538
|
rootDir: dir,
|
|
@@ -1582,7 +1612,7 @@ async function detectProjectSnapshot(rootDir) {
|
|
|
1582
1612
|
];
|
|
1583
1613
|
const testInfo = {};
|
|
1584
1614
|
for (const pattern of testDirs) {
|
|
1585
|
-
const files = await
|
|
1615
|
+
const files = await fg6(`${pattern}/**/*`, {
|
|
1586
1616
|
cwd: rootDir,
|
|
1587
1617
|
ignore: IGNORE3,
|
|
1588
1618
|
onlyFiles: true
|
|
@@ -1602,12 +1632,12 @@ async function detectProjectSnapshot(rootDir) {
|
|
|
1602
1632
|
{ pattern: "**/*_test.rs", label: "*_test.rs" }
|
|
1603
1633
|
];
|
|
1604
1634
|
for (const { pattern, label } of testFilePatterns) {
|
|
1605
|
-
const files = await
|
|
1635
|
+
const files = await fg6(pattern, { cwd: rootDir, ignore: IGNORE3 });
|
|
1606
1636
|
if (files.length > 0) {
|
|
1607
1637
|
testInfo[label] = files.length;
|
|
1608
1638
|
}
|
|
1609
1639
|
}
|
|
1610
|
-
const sourceFiles = await
|
|
1640
|
+
const sourceFiles = await fg6("**/*.{ts,tsx,js,jsx,kt,kts,java,py,go,rs,swift,rb,cs,cpp,c,dart}", {
|
|
1611
1641
|
cwd: rootDir,
|
|
1612
1642
|
ignore: IGNORE3
|
|
1613
1643
|
});
|
|
@@ -1640,7 +1670,7 @@ async function getCodeSamples(dir, count = 15) {
|
|
|
1640
1670
|
}
|
|
1641
1671
|
async function getProjectStructure(dir) {
|
|
1642
1672
|
const rootDir = path6.resolve(dir);
|
|
1643
|
-
const allFiles = await
|
|
1673
|
+
const allFiles = await fg6("**/*", {
|
|
1644
1674
|
cwd: rootDir,
|
|
1645
1675
|
ignore: IGNORE3,
|
|
1646
1676
|
onlyFiles: true
|
|
@@ -1823,7 +1853,7 @@ function createMcpServer() {
|
|
|
1823
1853
|
const server = new McpServer(
|
|
1824
1854
|
{
|
|
1825
1855
|
name: "mason",
|
|
1826
|
-
version: "0.3.
|
|
1856
|
+
version: "0.3.3"
|
|
1827
1857
|
},
|
|
1828
1858
|
{
|
|
1829
1859
|
instructions: "Mason is a context engineering tool. Always call get_snapshot before using Explore agents, Glob, or Grep to understand the codebase. The snapshot is a concept map that maps features and flows to their implementing files \u2014 it eliminates the need to search. This applies to ANY question about architecture, features, flows, how things work, cross-feature interactions, or bug investigation. Workflow: 1) Call get_snapshot first. 2) If no snapshot, call full_analysis and then save_snapshot to create one. 3) If the snapshot is stale, tell the user and offer to update it. 4) Use your native file reading tool to read files the snapshot points to. 5) Before modifying a file, call get_impact to check what else might be affected. 6) After making significant changes (new features, refactors, architecture changes), call save_snapshot to update the concept map."
|
|
@@ -2008,7 +2038,7 @@ function createCLI() {
|
|
|
2008
2038
|
const program2 = new Command();
|
|
2009
2039
|
program2.name("mason").description(
|
|
2010
2040
|
"Context engineering CLI & MCP server \u2014 generates intelligent CLAUDE.md files"
|
|
2011
|
-
).version("0.3.
|
|
2041
|
+
).version("0.3.3");
|
|
2012
2042
|
program2.command("setup").description("Register Mason as an MCP server with Claude Code").option("--scope <scope>", "Config scope: user or project", "user").action(async (opts) => {
|
|
2013
2043
|
const { execFile: execFile9 } = await import("child_process");
|
|
2014
2044
|
const { promisify: promisify9 } = await import("util");
|
|
@@ -2090,7 +2120,7 @@ function createCLI() {
|
|
|
2090
2120
|
}
|
|
2091
2121
|
const rootDir = path7.resolve(dir);
|
|
2092
2122
|
const runConfig = opts.model ? { ...config, model: opts.model } : config;
|
|
2093
|
-
const spinner = ora("Analyzing codebase...").start();
|
|
2123
|
+
const spinner = ora({ discardStdin: false, text: "Analyzing codebase..." }).start();
|
|
2094
2124
|
const analysisData = await fullAnalysis(rootDir);
|
|
2095
2125
|
spinner.text = `Generating CLAUDE.md with ${runConfig.provider}...`;
|
|
2096
2126
|
try {
|
|
@@ -2125,7 +2155,7 @@ ${analysisData}`
|
|
|
2125
2155
|
success(`Generated ${outPath}`);
|
|
2126
2156
|
try {
|
|
2127
2157
|
const { createSnapshot: createSnapshot2 } = await Promise.resolve().then(() => (init_snapshot(), snapshot_exports));
|
|
2128
|
-
const spinner2 = ora("Building concept map...").start();
|
|
2158
|
+
const spinner2 = ora({ discardStdin: false, text: "Building concept map..." }).start();
|
|
2129
2159
|
const snapshot = await createSnapshot2(rootDir, runConfig);
|
|
2130
2160
|
spinner2.stop();
|
|
2131
2161
|
const featureCount = Object.keys(snapshot.features).length;
|
|
@@ -2184,7 +2214,7 @@ ${analysisData}`
|
|
|
2184
2214
|
);
|
|
2185
2215
|
process.exit(1);
|
|
2186
2216
|
}
|
|
2187
|
-
const spinner = ora("Building project snapshot...").start();
|
|
2217
|
+
const spinner = ora({ discardStdin: false, text: "Building project snapshot..." }).start();
|
|
2188
2218
|
try {
|
|
2189
2219
|
const snapshot = await createSnapshot2(rootDir, config);
|
|
2190
2220
|
spinner.stop();
|
|
@@ -2218,7 +2248,7 @@ ${analysisData}`
|
|
|
2218
2248
|
program2.command("impact").description("Show files affected by changes to a given file").argument("<files...>", "File paths or names to analyze").option("-d, --dir <dir>", "Project directory", ".").action(async (files, opts) => {
|
|
2219
2249
|
const { analyzeImpact: analyzeImpact2 } = await Promise.resolve().then(() => (init_impact(), impact_exports));
|
|
2220
2250
|
const rootDir = path7.resolve(opts.dir);
|
|
2221
|
-
const spinner = ora("Analyzing impact...").start();
|
|
2251
|
+
const spinner = ora({ discardStdin: false, text: "Analyzing impact..." }).start();
|
|
2222
2252
|
const result = await analyzeImpact2(rootDir, files);
|
|
2223
2253
|
spinner.stop();
|
|
2224
2254
|
console.log(chalk2.bold(`
|
|
@@ -2255,7 +2285,7 @@ Impact analysis for: ${result.targetFiles.join(", ")}
|
|
|
2255
2285
|
});
|
|
2256
2286
|
program2.command("analyze").description("Analyze the codebase and print findings").argument("[dir]", "Directory to analyze", ".").action(async (dir) => {
|
|
2257
2287
|
const rootDir = path7.resolve(dir);
|
|
2258
|
-
const spinner = ora("Analyzing codebase...").start();
|
|
2288
|
+
const spinner = ora({ discardStdin: false, text: "Analyzing codebase..." }).start();
|
|
2259
2289
|
const context = await buildContext2(rootDir);
|
|
2260
2290
|
const results = await runAll(context);
|
|
2261
2291
|
spinner.stop();
|