mason-context 0.16.0 → 0.16.1
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 +6 -0
- package/dist/mason-auto.js +4 -4
- package/dist/mason-auto.js.map +1 -1
- package/dist/mason-mcp.js +268 -104
- package/dist/mason-mcp.js.map +1 -1
- package/dist/mason.js +7 -7
- package/dist/mason.js.map +1 -1
- package/package.json +2 -1
package/dist/mason-mcp.js
CHANGED
|
@@ -3136,7 +3136,7 @@ var init_evidence = __esm({
|
|
|
3136
3136
|
init_dead_command();
|
|
3137
3137
|
init_storage();
|
|
3138
3138
|
exec11 = promisify12(execFile12);
|
|
3139
|
-
engineVersion = true ? "0.16.
|
|
3139
|
+
engineVersion = true ? "0.16.1" : "development";
|
|
3140
3140
|
hash = (value) => createHash4("sha256").update(JSON.stringify(value)).digest("hex");
|
|
3141
3141
|
content = readAuditInput;
|
|
3142
3142
|
internal = (file) => file === ".mason" || file === ".mason/reports" || file.startsWith(".mason/reports/");
|
|
@@ -3752,7 +3752,7 @@ var init_launcher = __esm({
|
|
|
3752
3752
|
"use strict";
|
|
3753
3753
|
init_evidence();
|
|
3754
3754
|
init_model();
|
|
3755
|
-
executingVersion = () => true ? "0.16.
|
|
3755
|
+
executingVersion = () => true ? "0.16.1" : "development";
|
|
3756
3756
|
mcpCommand = (host) => globalCommand(["--setup-host", host, "mcp"]);
|
|
3757
3757
|
hookCommand = (host) => `mason --setup-host ${host} auto`;
|
|
3758
3758
|
}
|
|
@@ -3942,9 +3942,9 @@ var init_init = __esm({
|
|
|
3942
3942
|
|
|
3943
3943
|
Mason provides recorded decisions and file impact over MCP. A concept map is optional.
|
|
3944
3944
|
|
|
3945
|
-
- Task, bug,
|
|
3945
|
+
- Task, bug, change request, or investigation of why the project works this way \u2192 \`get_context\` with the task text and known files: matching decisions, related tests, impact, and any available map entries.
|
|
3946
3946
|
- Before editing a file \u2192 \`get_impact\` to check references, tests, and historical change partners.
|
|
3947
|
-
-
|
|
3947
|
+
- Investigation or correction reveals a reusable project lesson \u2192 \`save_decision\` before finishing. Capture only the lasting constraint and reason, with anchors and known sources, owner, and recorder. This creates a proposal; never infer approval. Preserve uncertain causes and unknown attribution. Route project lessons here; keep personal preferences in agent memory. Skip code summaries, current checkout status, temporary task permissions, session trivia, and secrets. No qualifying lesson means no record.
|
|
3948
3948
|
- Consult trust metadata before relying on entries: unknown or changed freshness requires inspection, and failed verification means the description must be corrected. Check approval too: proposals are suggestions, legacy records are unreviewed, and accepted decisions are recorded constraints subject to freshness checks. An accepted revision remains operative while a pending proposal is reviewed; keep both versions and their freshness distinct.
|
|
3949
3949
|
- Asked to review or re-verify a decision \u2192 \`review_decision\` first to inspect content, sources, history, and code changes. Record acceptance, reaffirmation, or retirement only when authorized by the user or a cited team review, with the actual reviewer and reason. Never infer approval from unchanged code. Review and commit the local record through the normal project workflow.
|
|
3950
3950
|
- For an architectural overview, use \`get_snapshot\` if a map is available. If \`map.status\` is missing or invalid, use available decisions and source evidence; do not start building a map unless requested.
|
|
@@ -4751,16 +4751,204 @@ var init_onboarding = __esm({
|
|
|
4751
4751
|
}
|
|
4752
4752
|
});
|
|
4753
4753
|
|
|
4754
|
+
// src/impact/references.ts
|
|
4755
|
+
import path23 from "path";
|
|
4756
|
+
function templateEnd(content2, start) {
|
|
4757
|
+
let i = start + 1, interpolation = 0;
|
|
4758
|
+
while (i < content2.length) {
|
|
4759
|
+
const c = content2[i];
|
|
4760
|
+
if (c === "\\") {
|
|
4761
|
+
i += 2;
|
|
4762
|
+
continue;
|
|
4763
|
+
}
|
|
4764
|
+
if (!interpolation) {
|
|
4765
|
+
if (c === "`") return i + 1;
|
|
4766
|
+
if (c === "$" && content2[i + 1] === "{") {
|
|
4767
|
+
interpolation = 1;
|
|
4768
|
+
i += 2;
|
|
4769
|
+
continue;
|
|
4770
|
+
}
|
|
4771
|
+
} else {
|
|
4772
|
+
if (c === "`") {
|
|
4773
|
+
i = templateEnd(content2, i);
|
|
4774
|
+
continue;
|
|
4775
|
+
}
|
|
4776
|
+
if (c === '"' || c === "'") {
|
|
4777
|
+
const quote = c;
|
|
4778
|
+
for (i++; i < content2.length; i++) {
|
|
4779
|
+
if (content2[i] === "\\") i++;
|
|
4780
|
+
else if (content2[i] === quote) {
|
|
4781
|
+
i++;
|
|
4782
|
+
break;
|
|
4783
|
+
}
|
|
4784
|
+
}
|
|
4785
|
+
continue;
|
|
4786
|
+
}
|
|
4787
|
+
if (content2.startsWith("//", i)) {
|
|
4788
|
+
const end = content2.indexOf("\n", i + 2);
|
|
4789
|
+
i = end < 0 ? content2.length : end;
|
|
4790
|
+
continue;
|
|
4791
|
+
}
|
|
4792
|
+
if (content2.startsWith("/*", i)) {
|
|
4793
|
+
const end = content2.indexOf("*/", i + 2);
|
|
4794
|
+
i = end < 0 ? content2.length : end + 2;
|
|
4795
|
+
continue;
|
|
4796
|
+
}
|
|
4797
|
+
if (c === "{") interpolation++;
|
|
4798
|
+
if (c === "}") interpolation--;
|
|
4799
|
+
}
|
|
4800
|
+
i++;
|
|
4801
|
+
}
|
|
4802
|
+
return content2.length;
|
|
4803
|
+
}
|
|
4804
|
+
function tokens(content2) {
|
|
4805
|
+
const result = [];
|
|
4806
|
+
const re = /\/\/[^\n]*|\/\*[\s\S]*?(?:\*\/|$)|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`|[\w$]+|[^\s]/g;
|
|
4807
|
+
let match;
|
|
4808
|
+
while (match = re.exec(content2)) {
|
|
4809
|
+
const value = match[0];
|
|
4810
|
+
if (value.startsWith("//") || value.startsWith("/*")) continue;
|
|
4811
|
+
if (/^["'`]/.test(value)) {
|
|
4812
|
+
if (value[0] === "`") {
|
|
4813
|
+
re.lastIndex = templateEnd(content2, match.index);
|
|
4814
|
+
result.push({ value: "<opaque>" });
|
|
4815
|
+
continue;
|
|
4816
|
+
}
|
|
4817
|
+
result.push({ value: value.slice(1, -1), string: !value.includes("\\") && value[0] !== "`" });
|
|
4818
|
+
if (!result.at(-1).string) result[result.length - 1] = { value: "<opaque>" };
|
|
4819
|
+
} else result.push({ value });
|
|
4820
|
+
}
|
|
4821
|
+
return result;
|
|
4822
|
+
}
|
|
4823
|
+
function imports(content2) {
|
|
4824
|
+
const ts = tokens(content2), found = [];
|
|
4825
|
+
for (let i = 0; i < ts.length; i++) {
|
|
4826
|
+
const t = ts[i];
|
|
4827
|
+
if (t.string || ts[i - 1]?.value === ".") continue;
|
|
4828
|
+
if (["require", "import"].includes(t.value) && ts[i + 1]?.value === "(" && ts[i + 2]?.string && ts[i + 3]?.value === ")") {
|
|
4829
|
+
found.push(ts[i + 2].value);
|
|
4830
|
+
}
|
|
4831
|
+
if (!["import", "export"].includes(t.value) || ts[i + 1]?.value === "(") continue;
|
|
4832
|
+
if (t.value === "import" && ts[i + 1]?.string) {
|
|
4833
|
+
found.push(ts[i + 1].value);
|
|
4834
|
+
continue;
|
|
4835
|
+
}
|
|
4836
|
+
for (let j = i + 1; j < Math.min(ts.length, i + 150); j++) {
|
|
4837
|
+
if ([";", "=", "import", "export"].includes(ts[j].value) && !ts[j].string) break;
|
|
4838
|
+
if (ts[j].value === "from" && !ts[j].string && ts[j + 1]?.string) {
|
|
4839
|
+
found.push(ts[j + 1].value);
|
|
4840
|
+
break;
|
|
4841
|
+
}
|
|
4842
|
+
}
|
|
4843
|
+
}
|
|
4844
|
+
return found;
|
|
4845
|
+
}
|
|
4846
|
+
function qualifiedName(ts, start) {
|
|
4847
|
+
let name = "";
|
|
4848
|
+
for (let i = start; i < ts.length; i++) {
|
|
4849
|
+
if (ts[i].string || !/^[A-Za-z_$][\w$]*$/.test(ts[i].value)) break;
|
|
4850
|
+
name += ts[i].value;
|
|
4851
|
+
if (ts[i + 1]?.value !== "." || !/^[A-Za-z_$][\w$]*$/.test(ts[i + 2]?.value ?? "")) break;
|
|
4852
|
+
name += ".";
|
|
4853
|
+
i++;
|
|
4854
|
+
}
|
|
4855
|
+
return name;
|
|
4856
|
+
}
|
|
4857
|
+
function declaredSymbols(content2) {
|
|
4858
|
+
const ts = tokens(content2);
|
|
4859
|
+
const packageAt = ts.findIndex((t) => !t.string && t.value === "package");
|
|
4860
|
+
if (packageAt < 0) return [];
|
|
4861
|
+
const pkg = qualifiedName(ts, packageAt + 1), names = [];
|
|
4862
|
+
let depth = 0;
|
|
4863
|
+
for (let i = 0; i < ts.length; i++) {
|
|
4864
|
+
if (ts[i].string) continue;
|
|
4865
|
+
if (ts[i].value === "{") depth++;
|
|
4866
|
+
if (ts[i].value === "}") depth--;
|
|
4867
|
+
if (depth === 0 && ["class", "interface", "object", "enum", "typealias", "fun"].includes(ts[i].value) && /^[A-Za-z_$][\w$]*$/.test(ts[i + 1]?.value ?? "") && ts[i + 1].value !== "class") names.push(pkg + "." + ts[i + 1].value);
|
|
4868
|
+
}
|
|
4869
|
+
return names;
|
|
4870
|
+
}
|
|
4871
|
+
function jvmImports(content2) {
|
|
4872
|
+
const ts = tokens(content2), result = [];
|
|
4873
|
+
for (let i = 0; i < ts.length; i++) if (!ts[i].string && ts[i].value === "import") {
|
|
4874
|
+
const name = qualifiedName(ts, i + (ts[i + 1]?.value === "static" ? 2 : 1));
|
|
4875
|
+
if (name) result.push(name);
|
|
4876
|
+
}
|
|
4877
|
+
return result;
|
|
4878
|
+
}
|
|
4879
|
+
function resolveImport(file, specifier, available) {
|
|
4880
|
+
if (!specifier.startsWith("./") && !specifier.startsWith("../")) return null;
|
|
4881
|
+
const relative = path23.posix.normalize(path23.posix.join(path23.posix.dirname(file), specifier));
|
|
4882
|
+
if (relative.startsWith("../")) return null;
|
|
4883
|
+
if (available.has(relative)) return relative;
|
|
4884
|
+
const ext = path23.posix.extname(relative);
|
|
4885
|
+
const replacements = { ".js": [".ts", ".tsx"], ".mjs": [".mts"], ".cjs": [".cts"] };
|
|
4886
|
+
const candidates = ext ? (replacements[ext] ?? []).map((e) => relative.slice(0, -ext.length) + e) : jsExtensions.flatMap((e) => [relative + e, relative + "/index" + e]);
|
|
4887
|
+
const found = candidates.filter((f) => available.has(f));
|
|
4888
|
+
return found.length === 1 ? found[0] : null;
|
|
4889
|
+
}
|
|
4890
|
+
function mentionsPath(content2, target) {
|
|
4891
|
+
return new RegExp(`(?<![\\w./\\\\-])(?:\\./)?${escape(target)}(?![\\w./\\\\-])`).test(content2);
|
|
4892
|
+
}
|
|
4893
|
+
function createReferenceMatcher(targets, available, targetContents = /* @__PURE__ */ new Map()) {
|
|
4894
|
+
const counts = /* @__PURE__ */ new Map();
|
|
4895
|
+
for (const file of available) {
|
|
4896
|
+
const name = path23.posix.basename(file, path23.posix.extname(file));
|
|
4897
|
+
counts.set(name, (counts.get(name) ?? 0) + 1);
|
|
4898
|
+
}
|
|
4899
|
+
const jvmTargets = new Map([...targetContents].filter(([file]) => /\.(?:kt|kts|java)$/.test(file)).map(([file, content2]) => [file, declaredSymbols(content2)]));
|
|
4900
|
+
return (source) => {
|
|
4901
|
+
if (targets.includes(source.path)) return [];
|
|
4902
|
+
const resolved = new Set(jsExtensions.includes(path23.posix.extname(source.path)) ? imports(source.content).map((s) => resolveImport(source.path, s, available)).filter(Boolean) : []);
|
|
4903
|
+
if (/\.(?:kt|kts|java)$/.test(source.path)) {
|
|
4904
|
+
const imported = jvmImports(source.content);
|
|
4905
|
+
for (const [target, symbols] of jvmTargets) if (symbols.some((symbol) => imported.some((name) => name === symbol || name.startsWith(symbol + ".")))) resolved.add(target);
|
|
4906
|
+
}
|
|
4907
|
+
const groups = /* @__PURE__ */ new Map();
|
|
4908
|
+
const add = (evidence, match) => groups.set(evidence, [...groups.get(evidence) ?? [], match]);
|
|
4909
|
+
for (const target of targets) {
|
|
4910
|
+
if (resolved.has(target)) {
|
|
4911
|
+
add("resolved-import", target);
|
|
4912
|
+
continue;
|
|
4913
|
+
}
|
|
4914
|
+
if (mentionsPath(source.content, target)) {
|
|
4915
|
+
add("explicit-path", target);
|
|
4916
|
+
continue;
|
|
4917
|
+
}
|
|
4918
|
+
const ext = path23.posix.extname(target), name = path23.posix.basename(target, ext);
|
|
4919
|
+
if (!/\.(?:[cm]?[jt]sx?|py|kt|java|go|rs|swift|rb|cs|cpp|c|h|hpp|dart|php)$/.test(ext) || name.startsWith(".") || name.length < 3 || genericNames.has(name.toLowerCase()) || counts.get(name) !== 1) continue;
|
|
4920
|
+
if (word(name).test(source.content)) {
|
|
4921
|
+
add("name-candidate", name);
|
|
4922
|
+
}
|
|
4923
|
+
}
|
|
4924
|
+
return [...groups].map(([evidence, matches]) => ({ file: source.path, matches, kind: evidence === "resolved-import" ? "import" : "mention", evidence }));
|
|
4925
|
+
};
|
|
4926
|
+
}
|
|
4927
|
+
function sortReferences(result) {
|
|
4928
|
+
const rank = { "resolved-import": 0, "explicit-path": 1, "name-candidate": 2 };
|
|
4929
|
+
return result.sort((a, b) => rank[a.evidence] - rank[b.evidence] || b.matches.length - a.matches.length || a.file.localeCompare(b.file));
|
|
4930
|
+
}
|
|
4931
|
+
var jsExtensions, genericNames, escape, word;
|
|
4932
|
+
var init_references = __esm({
|
|
4933
|
+
"src/impact/references.ts"() {
|
|
4934
|
+
"use strict";
|
|
4935
|
+
jsExtensions = [".ts", ".tsx", ".js", ".jsx", ".mts", ".cts", ".mjs", ".cjs"];
|
|
4936
|
+
genericNames = /* @__PURE__ */ new Set(["index", "main", "app", "init", "config", "settings", "types", "utils", "test", "tests"]);
|
|
4937
|
+
escape = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4938
|
+
word = (s) => new RegExp(`(?<![\\w$])${escape(s)}(?![\\w$])`);
|
|
4939
|
+
}
|
|
4940
|
+
});
|
|
4941
|
+
|
|
4754
4942
|
// src/impact/impact.ts
|
|
4755
4943
|
var impact_exports = {};
|
|
4756
4944
|
__export(impact_exports, {
|
|
4757
4945
|
analyzeImpact: () => analyzeImpact
|
|
4758
4946
|
});
|
|
4759
|
-
import
|
|
4947
|
+
import path24 from "path";
|
|
4760
4948
|
import { execFile as execFile16 } from "child_process";
|
|
4761
4949
|
import { promisify as promisify16 } from "util";
|
|
4762
4950
|
async function analyzeImpact(rootDir, targetFiles) {
|
|
4763
|
-
const resolvedRoot =
|
|
4951
|
+
const resolvedRoot = path24.resolve(rootDir);
|
|
4764
4952
|
const resolvedTargets = await resolveTargetFiles(resolvedRoot, targetFiles);
|
|
4765
4953
|
const [cochange, references, tests] = await Promise.all([
|
|
4766
4954
|
getCochangeFiles(resolvedRoot, resolvedTargets),
|
|
@@ -4837,52 +5025,24 @@ async function getCochangeFiles(rootDir, targetFiles) {
|
|
|
4837
5025
|
})).filter((e) => e.cochangeRate >= 0.3 || e.sharedCommits >= 3).sort((a, b) => b.cochangeRate - a.cochangeRate).slice(0, 20);
|
|
4838
5026
|
}
|
|
4839
5027
|
async function getReferences(rootDir, targetFiles) {
|
|
4840
|
-
const searchNames = /* @__PURE__ */ new Set();
|
|
4841
|
-
for (const target of targetFiles) {
|
|
4842
|
-
const basename = path23.basename(target).replace(/\.[^.]+$/, "");
|
|
4843
|
-
searchNames.add(basename);
|
|
4844
|
-
}
|
|
4845
5028
|
const access = await createFileAccess(rootDir);
|
|
4846
5029
|
const allSourceFiles = await access.list(SOURCE_GLOB);
|
|
4847
|
-
const
|
|
4848
|
-
const
|
|
4849
|
-
const
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
batch.map(async (file) => {
|
|
4856
|
-
try {
|
|
4857
|
-
const full = await access.read(file);
|
|
4858
|
-
if (!full) return;
|
|
4859
|
-
const content2 = full.content;
|
|
4860
|
-
const lines = content2.split("\n");
|
|
4861
|
-
for (const name of searchNames) {
|
|
4862
|
-
const regex = new RegExp(`\\b${escapeRegex(name)}\\b`);
|
|
4863
|
-
if (!regex.test(content2)) continue;
|
|
4864
|
-
if (!results.has(file)) {
|
|
4865
|
-
results.set(file, { matches: /* @__PURE__ */ new Set(), isImport: false });
|
|
4866
|
-
}
|
|
4867
|
-
const entry = results.get(file);
|
|
4868
|
-
entry.matches.add(name);
|
|
4869
|
-
if (!entry.isImport && lines.some((l) => regex.test(l) && importLine.test(l))) {
|
|
4870
|
-
entry.isImport = true;
|
|
4871
|
-
}
|
|
4872
|
-
}
|
|
4873
|
-
} catch {
|
|
4874
|
-
}
|
|
4875
|
-
})
|
|
4876
|
-
);
|
|
5030
|
+
const available = new Set(allSourceFiles);
|
|
5031
|
+
const targetContents = /* @__PURE__ */ new Map();
|
|
5032
|
+
for (const target of targetFiles) {
|
|
5033
|
+
const source = await access.read(target);
|
|
5034
|
+
if (source) {
|
|
5035
|
+
available.add(target);
|
|
5036
|
+
targetContents.set(target, source.content);
|
|
5037
|
+
}
|
|
4877
5038
|
}
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
});
|
|
5039
|
+
const match = createReferenceMatcher(targetFiles, available, targetContents);
|
|
5040
|
+
const references = [];
|
|
5041
|
+
for (let i = 0; i < allSourceFiles.length; i += 50) {
|
|
5042
|
+
const batch = await Promise.all(allSourceFiles.slice(i, i + 50).map((file) => access.read(file)));
|
|
5043
|
+
for (const source of batch) if (source) references.push(...match(source));
|
|
5044
|
+
}
|
|
5045
|
+
return sortReferences(references);
|
|
4886
5046
|
}
|
|
4887
5047
|
async function getRelatedTests(rootDir, targetFiles) {
|
|
4888
5048
|
const testPatterns = [
|
|
@@ -4902,9 +5062,9 @@ async function getRelatedTests(rootDir, targetFiles) {
|
|
|
4902
5062
|
const testFiles = await (await createFileAccess(rootDir)).list(testPatterns);
|
|
4903
5063
|
const results = [];
|
|
4904
5064
|
for (const target of targetFiles) {
|
|
4905
|
-
const targetBaseName =
|
|
5065
|
+
const targetBaseName = path24.basename(target).replace(/\.[^.]+$/, "");
|
|
4906
5066
|
for (const testFile of testFiles) {
|
|
4907
|
-
const testBaseName =
|
|
5067
|
+
const testBaseName = path24.basename(testFile).replace(/\.[^.]+$/, "");
|
|
4908
5068
|
const sourceName = testBaseName.replace(/Test$|Tests$|Spec$|\.test$|\.spec$/, "").replace(/^test_|_test$/, "");
|
|
4909
5069
|
if (sourceName === targetBaseName) {
|
|
4910
5070
|
results.push({
|
|
@@ -4916,15 +5076,13 @@ async function getRelatedTests(rootDir, targetFiles) {
|
|
|
4916
5076
|
}
|
|
4917
5077
|
return results;
|
|
4918
5078
|
}
|
|
4919
|
-
function escapeRegex(str) {
|
|
4920
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4921
|
-
}
|
|
4922
5079
|
var exec14;
|
|
4923
5080
|
var init_impact = __esm({
|
|
4924
5081
|
"src/impact/impact.ts"() {
|
|
4925
5082
|
"use strict";
|
|
4926
5083
|
init_files();
|
|
4927
5084
|
init_paths();
|
|
5085
|
+
init_references();
|
|
4928
5086
|
exec14 = promisify16(execFile16);
|
|
4929
5087
|
}
|
|
4930
5088
|
});
|
|
@@ -4934,13 +5092,13 @@ var assemble_exports = {};
|
|
|
4934
5092
|
__export(assemble_exports, {
|
|
4935
5093
|
assembleContext: () => assembleContext
|
|
4936
5094
|
});
|
|
4937
|
-
import
|
|
5095
|
+
import path25 from "path";
|
|
4938
5096
|
import fs17 from "fs/promises";
|
|
4939
5097
|
async function collectImpact(root, candidates) {
|
|
4940
5098
|
const targets = /* @__PURE__ */ new Set();
|
|
4941
5099
|
let sourceFiles;
|
|
4942
5100
|
for (const candidate of sanitizeRepoPaths(candidates)) {
|
|
4943
|
-
const stat = await fs17.stat(
|
|
5101
|
+
const stat = await fs17.stat(path25.join(root, candidate)).catch(() => null);
|
|
4944
5102
|
if (stat?.isDirectory()) {
|
|
4945
5103
|
sourceFiles ??= await (await createFileAccess(root)).list();
|
|
4946
5104
|
for (const file of sourceFiles) {
|
|
@@ -4954,13 +5112,19 @@ async function collectImpact(root, candidates) {
|
|
|
4954
5112
|
}
|
|
4955
5113
|
if (!targets.size) return { impact: null, relatedTests: [] };
|
|
4956
5114
|
const result = await analyzeImpact(root, [...targets]);
|
|
5115
|
+
const referenceCandidatesOmitted = result.references.filter((r) => r.evidence === "name-candidate").length;
|
|
4957
5116
|
return {
|
|
4958
|
-
impact: {
|
|
5117
|
+
impact: {
|
|
5118
|
+
targets: result.targetFiles,
|
|
5119
|
+
cochange: result.cochange,
|
|
5120
|
+
references: result.references.filter((r) => r.evidence !== "name-candidate").slice(0, 10),
|
|
5121
|
+
...referenceCandidatesOmitted ? { referenceCandidatesOmitted } : {}
|
|
5122
|
+
},
|
|
4959
5123
|
relatedTests: [...new Set(result.tests.map((t) => t.file))]
|
|
4960
5124
|
};
|
|
4961
5125
|
}
|
|
4962
5126
|
async function assembleContext(rootDir, task, files) {
|
|
4963
|
-
const resolvedRoot =
|
|
5127
|
+
const resolvedRoot = path25.resolve(rootDir);
|
|
4964
5128
|
const mapState = await inspectSnapshot(resolvedRoot);
|
|
4965
5129
|
const snapshot = mapState.snapshot;
|
|
4966
5130
|
const store = await loadDecisionStore(resolvedRoot);
|
|
@@ -5481,8 +5645,8 @@ __export(client_exports, {
|
|
|
5481
5645
|
function createConfluenceClient(config, fetchFn = fetch) {
|
|
5482
5646
|
const baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
5483
5647
|
const auth = "Basic " + Buffer.from(`${config.email}:${config.apiToken}`).toString("base64");
|
|
5484
|
-
async function call(method,
|
|
5485
|
-
const res = await fetchFn(`${baseUrl}${
|
|
5648
|
+
async function call(method, path29, body) {
|
|
5649
|
+
const res = await fetchFn(`${baseUrl}${path29}`, {
|
|
5486
5650
|
method,
|
|
5487
5651
|
headers: {
|
|
5488
5652
|
Authorization: auth,
|
|
@@ -5494,7 +5658,7 @@ function createConfluenceClient(config, fetchFn = fetch) {
|
|
|
5494
5658
|
if (!res.ok) {
|
|
5495
5659
|
const text2 = await res.text();
|
|
5496
5660
|
throw new Error(
|
|
5497
|
-
`Confluence ${method} ${
|
|
5661
|
+
`Confluence ${method} ${path29} failed: ${res.status} ${res.statusText} \u2014 ${text2}`
|
|
5498
5662
|
);
|
|
5499
5663
|
}
|
|
5500
5664
|
if (res.status === 204) return null;
|
|
@@ -5596,15 +5760,15 @@ __export(config_exports2, {
|
|
|
5596
5760
|
validateProvider: () => validateProvider
|
|
5597
5761
|
});
|
|
5598
5762
|
import fs19 from "fs/promises";
|
|
5599
|
-
import
|
|
5763
|
+
import path26 from "path";
|
|
5600
5764
|
import os3 from "os";
|
|
5601
5765
|
import { execFile as execFile17 } from "child_process";
|
|
5602
5766
|
import { promisify as promisify17 } from "util";
|
|
5603
5767
|
function configDir() {
|
|
5604
|
-
return
|
|
5768
|
+
return path26.join(os3.homedir(), ".mason");
|
|
5605
5769
|
}
|
|
5606
5770
|
function configFile() {
|
|
5607
|
-
return
|
|
5771
|
+
return path26.join(configDir(), "config.json");
|
|
5608
5772
|
}
|
|
5609
5773
|
async function loadConfig() {
|
|
5610
5774
|
try {
|
|
@@ -5684,21 +5848,21 @@ var init_url = __esm({
|
|
|
5684
5848
|
});
|
|
5685
5849
|
|
|
5686
5850
|
// src/confluence/renderer.ts
|
|
5687
|
-
function
|
|
5851
|
+
function escape2(value) {
|
|
5688
5852
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
5689
5853
|
}
|
|
5690
5854
|
function infoPanel(text2) {
|
|
5691
|
-
return `<ac:structured-macro ac:name="info"><ac:rich-text-body><p>${
|
|
5855
|
+
return `<ac:structured-macro ac:name="info"><ac:rich-text-body><p>${escape2(text2)}</p></ac:rich-text-body></ac:structured-macro>`;
|
|
5692
5856
|
}
|
|
5693
5857
|
function featurePageTitle(prefix, name) {
|
|
5694
5858
|
return `${prefix}${name}`;
|
|
5695
5859
|
}
|
|
5696
5860
|
function renderFeaturePage(options) {
|
|
5697
|
-
const overviewBody = `<h2>What it does</h2><p>${
|
|
5861
|
+
const overviewBody = `<h2>What it does</h2><p>${escape2(options.productDescription)}</p>`;
|
|
5698
5862
|
const flowsBody = options.flowDescriptions.length ? `<h2>How it fits in</h2><ul>` + options.flowDescriptions.map(
|
|
5699
|
-
(f) => `<li><strong>${
|
|
5863
|
+
(f) => `<li><strong>${escape2(f.name)}</strong> \u2014 ${escape2(f.description)}</li>`
|
|
5700
5864
|
).join("") + `</ul>` : "";
|
|
5701
|
-
const navBody = `<p><ac:link><ri:page ri:content-title="${
|
|
5865
|
+
const navBody = `<p><ac:link><ri:page ri:content-title="${escape2(options.indexPageTitle)}"/><ac:plain-text-link-body><![CDATA[Back to ${options.indexPageTitle}]]></ac:plain-text-link-body></ac:link></p>`;
|
|
5702
5866
|
const footer = infoPanel(
|
|
5703
5867
|
`Generated from code by Mason. This page is overwritten on each sync \u2014 edit the code, not the page.`
|
|
5704
5868
|
);
|
|
@@ -5714,7 +5878,7 @@ function renderIndexPage(options) {
|
|
|
5714
5878
|
}
|
|
5715
5879
|
const list = `<h2>Features</h2><ul>` + options.featureTitles.map((name) => {
|
|
5716
5880
|
const pageTitle = featurePageTitle(options.featurePrefix, name);
|
|
5717
|
-
return `<li><ac:link><ri:page ri:content-title="${
|
|
5881
|
+
return `<li><ac:link><ri:page ri:content-title="${escape2(pageTitle)}"/><ac:plain-text-link-body><![CDATA[${name}]]></ac:plain-text-link-body></ac:link></li>`;
|
|
5718
5882
|
}).join("") + `</ul>`;
|
|
5719
5883
|
const banner = infoPanel(
|
|
5720
5884
|
`Generated from code by Mason. Maintained automatically \u2014 edit the code, not this page.`
|
|
@@ -5725,33 +5889,33 @@ function renderChangelogSection(section) {
|
|
|
5725
5889
|
const segments = [];
|
|
5726
5890
|
if (section.addedFeatures.length) {
|
|
5727
5891
|
segments.push(
|
|
5728
|
-
`<p><strong>Added features:</strong> ${section.addedFeatures.map(
|
|
5892
|
+
`<p><strong>Added features:</strong> ${section.addedFeatures.map(escape2).join(", ")}</p>`
|
|
5729
5893
|
);
|
|
5730
5894
|
}
|
|
5731
5895
|
if (section.removedFeatures.length) {
|
|
5732
5896
|
segments.push(
|
|
5733
|
-
`<p><strong>Removed features:</strong> ${section.removedFeatures.map(
|
|
5897
|
+
`<p><strong>Removed features:</strong> ${section.removedFeatures.map(escape2).join(", ")}</p>`
|
|
5734
5898
|
);
|
|
5735
5899
|
}
|
|
5736
5900
|
if (section.changedFeatures.length) {
|
|
5737
5901
|
segments.push(
|
|
5738
|
-
`<p><strong>Updated features:</strong> ${section.changedFeatures.map(
|
|
5902
|
+
`<p><strong>Updated features:</strong> ${section.changedFeatures.map(escape2).join(", ")}</p>`
|
|
5739
5903
|
);
|
|
5740
5904
|
}
|
|
5741
5905
|
if (section.addedFlows.length) {
|
|
5742
5906
|
segments.push(
|
|
5743
|
-
`<p><strong>Added flows:</strong> ${section.addedFlows.map(
|
|
5907
|
+
`<p><strong>Added flows:</strong> ${section.addedFlows.map(escape2).join(", ")}</p>`
|
|
5744
5908
|
);
|
|
5745
5909
|
}
|
|
5746
5910
|
if (section.removedFlows.length) {
|
|
5747
5911
|
segments.push(
|
|
5748
|
-
`<p><strong>Removed flows:</strong> ${section.removedFlows.map(
|
|
5912
|
+
`<p><strong>Removed flows:</strong> ${section.removedFlows.map(escape2).join(", ")}</p>`
|
|
5749
5913
|
);
|
|
5750
5914
|
}
|
|
5751
5915
|
if (segments.length === 0) {
|
|
5752
5916
|
segments.push(`<p><em>No meaningful changes detected.</em></p>`);
|
|
5753
5917
|
}
|
|
5754
|
-
return `<h3>${
|
|
5918
|
+
return `<h3>${escape2(section.syncedAt)}</h3>` + segments.join("");
|
|
5755
5919
|
}
|
|
5756
5920
|
function renderChangelogPage(sections) {
|
|
5757
5921
|
if (sections.length === 0) {
|
|
@@ -5777,16 +5941,16 @@ var init_renderer = __esm({
|
|
|
5777
5941
|
|
|
5778
5942
|
// src/confluence/diff.ts
|
|
5779
5943
|
import fs20 from "fs/promises";
|
|
5780
|
-
import
|
|
5944
|
+
import path27 from "path";
|
|
5781
5945
|
import { createHash as createHash5 } from "crypto";
|
|
5782
5946
|
function hashDescription(description) {
|
|
5783
5947
|
return createHash5("sha256").update(description, "utf8").digest("hex");
|
|
5784
5948
|
}
|
|
5785
5949
|
function syncStateDir(rootDir) {
|
|
5786
|
-
return
|
|
5950
|
+
return path27.join(rootDir, ".mason");
|
|
5787
5951
|
}
|
|
5788
5952
|
function syncStatePath(rootDir) {
|
|
5789
|
-
return
|
|
5953
|
+
return path27.join(syncStateDir(rootDir), "confluence-sync.json");
|
|
5790
5954
|
}
|
|
5791
5955
|
async function loadSyncState(rootDir) {
|
|
5792
5956
|
try {
|
|
@@ -6355,7 +6519,7 @@ import { z as z19 } from "zod";
|
|
|
6355
6519
|
// src/mcp/tools.ts
|
|
6356
6520
|
init_repair();
|
|
6357
6521
|
import fs21 from "fs/promises";
|
|
6358
|
-
import
|
|
6522
|
+
import path28 from "path";
|
|
6359
6523
|
import { execFile as execFile19 } from "child_process";
|
|
6360
6524
|
import { promisify as promisify19 } from "util";
|
|
6361
6525
|
|
|
@@ -7250,7 +7414,7 @@ async function buildContext(dir) {
|
|
|
7250
7414
|
};
|
|
7251
7415
|
}
|
|
7252
7416
|
async function analyzeProject(dir) {
|
|
7253
|
-
const rootDir =
|
|
7417
|
+
const rootDir = path28.resolve(dir);
|
|
7254
7418
|
const context = await buildContext(rootDir);
|
|
7255
7419
|
const results = await runAll(context);
|
|
7256
7420
|
const projectSnapshot = await detectProjectSnapshot(rootDir);
|
|
@@ -7305,7 +7469,7 @@ async function detectProjectSnapshot(rootDir) {
|
|
|
7305
7469
|
const present = [];
|
|
7306
7470
|
for (const file of buildFiles) {
|
|
7307
7471
|
try {
|
|
7308
|
-
await fs21.access(
|
|
7472
|
+
await fs21.access(path28.join(rootDir, file));
|
|
7309
7473
|
present.push(file);
|
|
7310
7474
|
} catch {
|
|
7311
7475
|
}
|
|
@@ -7347,7 +7511,7 @@ async function detectProjectSnapshot(rootDir) {
|
|
|
7347
7511
|
const sourceFiles = await access.list();
|
|
7348
7512
|
const fileCounts = {};
|
|
7349
7513
|
for (const file of sourceFiles) {
|
|
7350
|
-
const ext =
|
|
7514
|
+
const ext = path28.extname(file).slice(1);
|
|
7351
7515
|
fileCounts[ext] = (fileCounts[ext] ?? 0) + 1;
|
|
7352
7516
|
}
|
|
7353
7517
|
return {
|
|
@@ -7358,7 +7522,7 @@ async function detectProjectSnapshot(rootDir) {
|
|
|
7358
7522
|
};
|
|
7359
7523
|
}
|
|
7360
7524
|
async function getCodeSamples(dir, count3 = 15) {
|
|
7361
|
-
const rootDir =
|
|
7525
|
+
const rootDir = path28.resolve(dir);
|
|
7362
7526
|
const samples = await sampleFiles(rootDir, count3);
|
|
7363
7527
|
const output = {
|
|
7364
7528
|
note: "These are previews (first ~60 lines). Read the file directly with your own tools to see it in full.",
|
|
@@ -7401,7 +7565,7 @@ async function unmappedContextResponse(rootDir) {
|
|
|
7401
7565
|
});
|
|
7402
7566
|
}
|
|
7403
7567
|
async function getProjectStructure(dir) {
|
|
7404
|
-
const rootDir =
|
|
7568
|
+
const rootDir = path28.resolve(dir);
|
|
7405
7569
|
const allFiles = await (await createFileAccess(rootDir)).list("**/*");
|
|
7406
7570
|
const dirInfo = /* @__PURE__ */ new Map();
|
|
7407
7571
|
for (const file of allFiles) {
|
|
@@ -7413,7 +7577,7 @@ async function getProjectStructure(dir) {
|
|
|
7413
7577
|
}
|
|
7414
7578
|
const info = dirInfo.get(dirPath);
|
|
7415
7579
|
info.fileCount++;
|
|
7416
|
-
const ext =
|
|
7580
|
+
const ext = path28.extname(file).slice(1);
|
|
7417
7581
|
if (ext) {
|
|
7418
7582
|
info.extensions.set(ext, (info.extensions.get(ext) ?? 0) + 1);
|
|
7419
7583
|
}
|
|
@@ -7458,7 +7622,7 @@ async function buildChangedFilePreviews(rootDir, changedFiles) {
|
|
|
7458
7622
|
return previews2;
|
|
7459
7623
|
}
|
|
7460
7624
|
async function getSnapshot(dir) {
|
|
7461
|
-
const rootDir =
|
|
7625
|
+
const rootDir = path28.resolve(dir);
|
|
7462
7626
|
const snapshot = await loadSnapshot(rootDir);
|
|
7463
7627
|
if (!snapshot) {
|
|
7464
7628
|
return unmappedContextResponse(rootDir);
|
|
@@ -7559,7 +7723,7 @@ function driftHint(report) {
|
|
|
7559
7723
|
return "Read the changed files under staleFeatures/staleFlows, update those entries (fold unmappedFiles into the right features), and call save_snapshot with only the affected entries \u2014 unchanged entries are preserved. Drop ghostFiles from any entries that reference them, and delete features/flows that no longer exist via save_snapshot's removeFeatures/removeFlows.";
|
|
7560
7724
|
}
|
|
7561
7725
|
async function checkDrift(dir) {
|
|
7562
|
-
const rootDir =
|
|
7726
|
+
const rootDir = path28.resolve(dir);
|
|
7563
7727
|
const report = await computeDrift(rootDir);
|
|
7564
7728
|
if (!report) {
|
|
7565
7729
|
return JSON.stringify({
|
|
@@ -7590,7 +7754,7 @@ async function checkDrift(dir) {
|
|
|
7590
7754
|
return JSON.stringify({ exists: true, ...report, verification, hint });
|
|
7591
7755
|
}
|
|
7592
7756
|
async function generateSnapshotBatch(dir, offset = 0, batchSize = DEFAULT_BATCH_SIZE, files) {
|
|
7593
|
-
const rootDir =
|
|
7757
|
+
const rootDir = path28.resolve(dir);
|
|
7594
7758
|
const scoped = files !== void 0 && files.length > 0;
|
|
7595
7759
|
const scopeFiles = scoped ? sanitizePaths(rootDir, files) : void 0;
|
|
7596
7760
|
const batch = await prepareSnapshotBatch(dir, offset, batchSize, scopeFiles);
|
|
@@ -7637,7 +7801,7 @@ async function generateSnapshotBatch(dir, offset = 0, batchSize = DEFAULT_BATCH_
|
|
|
7637
7801
|
);
|
|
7638
7802
|
}
|
|
7639
7803
|
async function saveSnapshotPartial(dir, batchId, offset, features, flows) {
|
|
7640
|
-
const rootDir =
|
|
7804
|
+
const rootDir = path28.resolve(dir);
|
|
7641
7805
|
for (const feat of Object.values(features)) {
|
|
7642
7806
|
feat.files = sanitizePaths(rootDir, feat.files);
|
|
7643
7807
|
if (feat.tests) feat.tests = sanitizePaths(rootDir, feat.tests);
|
|
@@ -7666,7 +7830,7 @@ async function saveSnapshotPartial(dir, batchId, offset, features, flows) {
|
|
|
7666
7830
|
);
|
|
7667
7831
|
}
|
|
7668
7832
|
async function reduceSnapshot(dir) {
|
|
7669
|
-
const rootDir =
|
|
7833
|
+
const rootDir = path28.resolve(dir);
|
|
7670
7834
|
const partials = await loadAllPartials(rootDir);
|
|
7671
7835
|
if (partials.length === 0) {
|
|
7672
7836
|
return JSON.stringify(
|
|
@@ -7728,7 +7892,7 @@ async function reduceSnapshot(dir) {
|
|
|
7728
7892
|
);
|
|
7729
7893
|
}
|
|
7730
7894
|
async function fullAnalysis(dir) {
|
|
7731
|
-
const rootDir =
|
|
7895
|
+
const rootDir = path28.resolve(dir);
|
|
7732
7896
|
const [analysis, structure, samples, testMap, snapshot] = await Promise.all([
|
|
7733
7897
|
analyzeProject(dir),
|
|
7734
7898
|
getProjectStructure(dir),
|
|
@@ -7757,7 +7921,7 @@ function sanitizePaths(rootDir, files) {
|
|
|
7757
7921
|
return sanitizeRepoPaths(files);
|
|
7758
7922
|
}
|
|
7759
7923
|
async function saveSnapshotData(dir, features, flows, removeFeatures = [], removeFlows = []) {
|
|
7760
|
-
const rootDir =
|
|
7924
|
+
const rootDir = path28.resolve(dir);
|
|
7761
7925
|
const gitHash = await getCurrentGitHash(rootDir);
|
|
7762
7926
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7763
7927
|
for (const feat of Object.values(features)) {
|
|
@@ -7841,7 +8005,7 @@ async function saveSnapshotData(dir, features, flows, removeFeatures = [], remov
|
|
|
7841
8005
|
});
|
|
7842
8006
|
}
|
|
7843
8007
|
async function getImpact(dir, files) {
|
|
7844
|
-
const rootDir =
|
|
8008
|
+
const rootDir = path28.resolve(dir);
|
|
7845
8009
|
const { analyzeImpact: analyzeImpact2 } = await Promise.resolve().then(() => (init_impact(), impact_exports));
|
|
7846
8010
|
const result = await analyzeImpact2(rootDir, files);
|
|
7847
8011
|
return JSON.stringify(result, null, 2);
|
|
@@ -7850,7 +8014,7 @@ var VERIFY_DEFAULT_SAMPLE = 5;
|
|
|
7850
8014
|
var VERIFY_MAX_FILES_PER_ENTRY = 8;
|
|
7851
8015
|
var VERIFY_SKELETON_CHARS = 500;
|
|
7852
8016
|
async function verifySnapshot(dir, sample = VERIFY_DEFAULT_SAMPLE) {
|
|
7853
|
-
const rootDir =
|
|
8017
|
+
const rootDir = path28.resolve(dir);
|
|
7854
8018
|
const snapshot = await loadSnapshot(rootDir);
|
|
7855
8019
|
if (!snapshot) {
|
|
7856
8020
|
return JSON.stringify({
|
|
@@ -7915,7 +8079,7 @@ async function verifySnapshot(dir, sample = VERIFY_DEFAULT_SAMPLE) {
|
|
|
7915
8079
|
});
|
|
7916
8080
|
}
|
|
7917
8081
|
async function saveVerification(dir, verdicts) {
|
|
7918
|
-
const rootDir =
|
|
8082
|
+
const rootDir = path28.resolve(dir);
|
|
7919
8083
|
const snapshot = await loadSnapshot(rootDir);
|
|
7920
8084
|
if (!snapshot) {
|
|
7921
8085
|
return JSON.stringify({ exists: false, hint: "No concept map exists." });
|
|
@@ -7953,16 +8117,16 @@ async function saveVerification(dir, verdicts) {
|
|
|
7953
8117
|
});
|
|
7954
8118
|
}
|
|
7955
8119
|
async function saveDecision(dir, input) {
|
|
7956
|
-
const rootDir =
|
|
8120
|
+
const rootDir = path28.resolve(dir);
|
|
7957
8121
|
const { upsertDecision: upsertDecision2 } = await Promise.resolve().then(() => (init_decisions(), decisions_exports));
|
|
7958
8122
|
const result = await upsertDecision2(rootDir, input);
|
|
7959
8123
|
return JSON.stringify(result);
|
|
7960
8124
|
}
|
|
7961
8125
|
async function reviewDecision2(dir, input) {
|
|
7962
|
-
return JSON.stringify(await reviewDecision(
|
|
8126
|
+
return JSON.stringify(await reviewDecision(path28.resolve(dir), input));
|
|
7963
8127
|
}
|
|
7964
8128
|
async function getContext(dir, task, files) {
|
|
7965
|
-
const rootDir =
|
|
8129
|
+
const rootDir = path28.resolve(dir);
|
|
7966
8130
|
const { assembleContext: assembleContext2 } = await Promise.resolve().then(() => (init_assemble(), assemble_exports));
|
|
7967
8131
|
const bundle = await assembleContext2(rootDir, task, files);
|
|
7968
8132
|
return JSON.stringify(bundle);
|
|
@@ -8004,7 +8168,7 @@ async function masonInit(dir, options = {}) {
|
|
|
8004
8168
|
return JSON.stringify(await setupProject2(dir, options), null, 2);
|
|
8005
8169
|
}
|
|
8006
8170
|
if (options.host) throw new Error("host applies only to mode: setup.");
|
|
8007
|
-
const rootDir =
|
|
8171
|
+
const rootDir = path28.resolve(dir);
|
|
8008
8172
|
const marker = await loadProjectMarker(rootDir);
|
|
8009
8173
|
const { loadSetup: loadSetup2 } = await Promise.resolve().then(() => (init_model(), model_exports));
|
|
8010
8174
|
const setup = await loadSetup2(rootDir);
|
|
@@ -8024,7 +8188,7 @@ async function masonInit(dir, options = {}) {
|
|
|
8024
8188
|
);
|
|
8025
8189
|
}
|
|
8026
8190
|
async function masonCompleteInit(dir, options = {}) {
|
|
8027
|
-
const rootDir =
|
|
8191
|
+
const rootDir = path28.resolve(dir);
|
|
8028
8192
|
const existing = await loadProjectMarker(rootDir);
|
|
8029
8193
|
const features = await loadProjectFeatures(rootDir);
|
|
8030
8194
|
const marker = {
|
|
@@ -8133,7 +8297,7 @@ async function masonSetConfluence(input) {
|
|
|
8133
8297
|
);
|
|
8134
8298
|
}
|
|
8135
8299
|
async function exportToConfluenceTool(dir, overrides) {
|
|
8136
|
-
const rootDir =
|
|
8300
|
+
const rootDir = path28.resolve(dir);
|
|
8137
8301
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config2(), config_exports2));
|
|
8138
8302
|
const { exportToConfluence: exportToConfluence2 } = await Promise.resolve().then(() => (init_sync(), sync_exports));
|
|
8139
8303
|
const config = await loadConfig2();
|
|
@@ -8171,7 +8335,7 @@ function createMcpServer() {
|
|
|
8171
8335
|
const server = new McpServer(
|
|
8172
8336
|
{
|
|
8173
8337
|
name: "mason",
|
|
8174
|
-
version: "0.16.
|
|
8338
|
+
version: "0.16.1"
|
|
8175
8339
|
},
|
|
8176
8340
|
{
|
|
8177
8341
|
instructions: 'Mason retrieves recorded decisions, file impact, and optional feature/flow maps. Use get_context with the task and known files, and get_impact before editing. Save learned rationale and constraints with save_decision; review and commit the local records through the project workflow. These tools work without initialization or a concept map. Consult trust and diagnostics: changed or unknown freshness needs source inspection, failed verification needs correction, and proposals are suggestions and legacy records are unreviewed. Accepted decisions are recorded team constraints, still subject to freshness checks. Use review_decision to prepare code evidence and record only authorized acceptance, reaffirmation, or retirement. Never invent a reviewer or treat these recorded identities as authenticated approval. mason_init returns documentation audit and committed-diff review findings with a quickstart guide. Use mode: "map" only when a full architecture map is requested. A missing map is not a setup failure; use decisions and source evidence. get_snapshot provides architecture navigation when a map is available. The mason-audit and mason-review CLIs also work without setup.'
|
|
@@ -8508,7 +8672,7 @@ function createMcpServer() {
|
|
|
8508
8672
|
);
|
|
8509
8673
|
server.tool(
|
|
8510
8674
|
"get_impact",
|
|
8511
|
-
"Trace
|
|
8675
|
+
"Trace historical co-change partners, related tests, and references with evidence: resolved imports, explicit paths, or uncertain name candidates. Candidates are not proven dependencies. Deterministic and read-only; no initialization or concept map required.",
|
|
8512
8676
|
{
|
|
8513
8677
|
dir: z19.string().describe("Absolute path to the project root directory"),
|
|
8514
8678
|
files: z19.array(z19.string()).describe("File paths or names to analyze (e.g., ['WeatherRepository.kt'] or ['src/services/auth.ts'])")
|