mason-context 0.16.0 → 0.16.2
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 +3 -0
- package/dist/mason-auto.js +63 -23
- package/dist/mason-auto.js.map +1 -1
- package/dist/mason-mcp.js +315 -123
- package/dist/mason-mcp.js.map +1 -1
- package/dist/mason.js +118 -73
- package/dist/mason.js.map +1 -1
- package/package.json +3 -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.2" : "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/");
|
|
@@ -3246,9 +3246,29 @@ var runtime_exports = {};
|
|
|
3246
3246
|
__export(runtime_exports, {
|
|
3247
3247
|
automate: () => automate,
|
|
3248
3248
|
automationStatus: () => automationStatus,
|
|
3249
|
+
observeReadOnlyTool: () => observeReadOnlyTool,
|
|
3249
3250
|
summarize: () => summarize
|
|
3250
3251
|
});
|
|
3251
3252
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
3253
|
+
async function observeReadOnlyTool(dir, event) {
|
|
3254
|
+
const ws = await workspace(dir);
|
|
3255
|
+
await withLock(ws.root, ws.directory, async () => {
|
|
3256
|
+
const statePath = ws.directory + "/state.json";
|
|
3257
|
+
const raw = await readStoreJson(ws.root, statePath);
|
|
3258
|
+
if (raw === null) return;
|
|
3259
|
+
const state = parseState(raw);
|
|
3260
|
+
if (state.root !== ws.root || state.gitDir !== ws.gitDir || state.branch !== ws.branch) {
|
|
3261
|
+
throw new Error("Automation state belongs to another branch or worktree; original evidence was retained.");
|
|
3262
|
+
}
|
|
3263
|
+
const session = event.host && event.sessionId ? state.sessions[hash([event.host, event.sessionId])] : null;
|
|
3264
|
+
if (!session) return;
|
|
3265
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3266
|
+
session.lastUsed = now;
|
|
3267
|
+
session.events[event.event] = { at: now, count: (session.events[event.event]?.count ?? 0) + 1 };
|
|
3268
|
+
await writeStoreJson(ws.root, statePath, state);
|
|
3269
|
+
});
|
|
3270
|
+
return { root: ws.root, directory: ws.directory };
|
|
3271
|
+
}
|
|
3252
3272
|
function summarize(report) {
|
|
3253
3273
|
const open = report.findings.filter((f) => f.status !== "resolved");
|
|
3254
3274
|
return [
|
|
@@ -3472,8 +3492,15 @@ var init_runtime = __esm({
|
|
|
3472
3492
|
|
|
3473
3493
|
// src/automation/adapters.ts
|
|
3474
3494
|
import { z as z9 } from "zod";
|
|
3495
|
+
function managedHookCommand(host, platform = process.platform) {
|
|
3496
|
+
const command = `mason --setup-host ${host} auto hook --host ${host}`;
|
|
3497
|
+
return platform === "win32" ? `cmd.exe /d /s /c "where mason >nul 2>nul & if errorlevel 1 (exit /b 0) else (${command})"` : `if command -v mason >/dev/null 2>&1; then ${command}; fi`;
|
|
3498
|
+
}
|
|
3499
|
+
function knownManagedHookCommands(host) {
|
|
3500
|
+
return [managedHookCommand(host, "linux"), managedHookCommand(host, "win32"), `mason --setup-host ${host} auto hook --host ${host}`];
|
|
3501
|
+
}
|
|
3475
3502
|
function hookConfig(host, command = "npx --no-install --package mason-context mason-auto") {
|
|
3476
|
-
const handler = { type: "command", command: command + " hook --host " + host, timeout: 30 };
|
|
3503
|
+
const handler = { type: "command", command: command === `mason --setup-host ${host} auto` ? managedHookCommand(host) : command + " hook --host " + host, timeout: 30 };
|
|
3477
3504
|
return { hooks: Object.fromEntries(HOOK_EVENTS.map((name) => [
|
|
3478
3505
|
name,
|
|
3479
3506
|
[{ ...["PreToolUse", "PostToolUse"].includes(name) ? { matcher: ".*" } : {}, hooks: [{ ...handler }] }]
|
|
@@ -3602,7 +3629,7 @@ async function planAutomationInstall(root, host, command) {
|
|
|
3602
3629
|
const previous = record.hosts[host]?.command;
|
|
3603
3630
|
const ownershipText = await readText(root, OWNERSHIP_PATH);
|
|
3604
3631
|
const pending = ownershipText === null ? [] : ownershipSchema.parse(JSON.parse(ownershipText)).files[file]?.hookCommands ?? [];
|
|
3605
|
-
const owned = /* @__PURE__ */ new Set([previous, ...pending, newCommand]);
|
|
3632
|
+
const owned = /* @__PURE__ */ new Set([previous, ...pending, newCommand, ...knownManagedHookCommands(host)]);
|
|
3606
3633
|
const hooks = existing.hooks ?? {};
|
|
3607
3634
|
for (const event of HOOK_EVENTS) {
|
|
3608
3635
|
hooks[event] = (hooks[event] ?? []).map((group) => ({
|
|
@@ -3728,6 +3755,7 @@ var init_model = __esm({
|
|
|
3728
3755
|
// src/setup/launcher.ts
|
|
3729
3756
|
import { execFile as execFile13 } from "child_process";
|
|
3730
3757
|
import { promisify as promisify13 } from "util";
|
|
3758
|
+
import fs16 from "fs/promises";
|
|
3731
3759
|
function globalCommand(args, platform = process.platform) {
|
|
3732
3760
|
return platform === "win32" ? { command: "cmd.exe", args: ["/d", "/s", "/c", "mason", ...args] } : { command: "mason", args };
|
|
3733
3761
|
}
|
|
@@ -3752,7 +3780,7 @@ var init_launcher = __esm({
|
|
|
3752
3780
|
"use strict";
|
|
3753
3781
|
init_evidence();
|
|
3754
3782
|
init_model();
|
|
3755
|
-
executingVersion = () => true ? "0.16.
|
|
3783
|
+
executingVersion = () => true ? "0.16.2" : "development";
|
|
3756
3784
|
mcpCommand = (host) => globalCommand(["--setup-host", host, "mcp"]);
|
|
3757
3785
|
hookCommand = (host) => `mason --setup-host ${host} auto`;
|
|
3758
3786
|
}
|
|
@@ -3942,9 +3970,9 @@ var init_init = __esm({
|
|
|
3942
3970
|
|
|
3943
3971
|
Mason provides recorded decisions and file impact over MCP. A concept map is optional.
|
|
3944
3972
|
|
|
3945
|
-
- Task, bug,
|
|
3973
|
+
- 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
3974
|
- Before editing a file \u2192 \`get_impact\` to check references, tests, and historical change partners.
|
|
3947
|
-
-
|
|
3975
|
+
- 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
3976
|
- 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
3977
|
- 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
3978
|
- 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.
|
|
@@ -4537,7 +4565,7 @@ var init_evidence2 = __esm({
|
|
|
4537
4565
|
});
|
|
4538
4566
|
|
|
4539
4567
|
// src/review/review.ts
|
|
4540
|
-
import
|
|
4568
|
+
import fs17 from "fs/promises";
|
|
4541
4569
|
import path22 from "path";
|
|
4542
4570
|
import { execFile as execFile15 } from "child_process";
|
|
4543
4571
|
import { promisify as promisify15 } from "util";
|
|
@@ -4617,7 +4645,7 @@ async function computeReview(rootDir, base, options = {}) {
|
|
|
4617
4645
|
analyzed,
|
|
4618
4646
|
async (relPath) => {
|
|
4619
4647
|
try {
|
|
4620
|
-
await
|
|
4648
|
+
await fs17.access(path22.join(resolvedRoot, relPath));
|
|
4621
4649
|
return true;
|
|
4622
4650
|
} catch {
|
|
4623
4651
|
return false;
|
|
@@ -4751,16 +4779,204 @@ var init_onboarding = __esm({
|
|
|
4751
4779
|
}
|
|
4752
4780
|
});
|
|
4753
4781
|
|
|
4782
|
+
// src/impact/references.ts
|
|
4783
|
+
import path23 from "path";
|
|
4784
|
+
function templateEnd(content2, start) {
|
|
4785
|
+
let i = start + 1, interpolation = 0;
|
|
4786
|
+
while (i < content2.length) {
|
|
4787
|
+
const c = content2[i];
|
|
4788
|
+
if (c === "\\") {
|
|
4789
|
+
i += 2;
|
|
4790
|
+
continue;
|
|
4791
|
+
}
|
|
4792
|
+
if (!interpolation) {
|
|
4793
|
+
if (c === "`") return i + 1;
|
|
4794
|
+
if (c === "$" && content2[i + 1] === "{") {
|
|
4795
|
+
interpolation = 1;
|
|
4796
|
+
i += 2;
|
|
4797
|
+
continue;
|
|
4798
|
+
}
|
|
4799
|
+
} else {
|
|
4800
|
+
if (c === "`") {
|
|
4801
|
+
i = templateEnd(content2, i);
|
|
4802
|
+
continue;
|
|
4803
|
+
}
|
|
4804
|
+
if (c === '"' || c === "'") {
|
|
4805
|
+
const quote = c;
|
|
4806
|
+
for (i++; i < content2.length; i++) {
|
|
4807
|
+
if (content2[i] === "\\") i++;
|
|
4808
|
+
else if (content2[i] === quote) {
|
|
4809
|
+
i++;
|
|
4810
|
+
break;
|
|
4811
|
+
}
|
|
4812
|
+
}
|
|
4813
|
+
continue;
|
|
4814
|
+
}
|
|
4815
|
+
if (content2.startsWith("//", i)) {
|
|
4816
|
+
const end = content2.indexOf("\n", i + 2);
|
|
4817
|
+
i = end < 0 ? content2.length : end;
|
|
4818
|
+
continue;
|
|
4819
|
+
}
|
|
4820
|
+
if (content2.startsWith("/*", i)) {
|
|
4821
|
+
const end = content2.indexOf("*/", i + 2);
|
|
4822
|
+
i = end < 0 ? content2.length : end + 2;
|
|
4823
|
+
continue;
|
|
4824
|
+
}
|
|
4825
|
+
if (c === "{") interpolation++;
|
|
4826
|
+
if (c === "}") interpolation--;
|
|
4827
|
+
}
|
|
4828
|
+
i++;
|
|
4829
|
+
}
|
|
4830
|
+
return content2.length;
|
|
4831
|
+
}
|
|
4832
|
+
function tokens(content2) {
|
|
4833
|
+
const result = [];
|
|
4834
|
+
const re = /\/\/[^\n]*|\/\*[\s\S]*?(?:\*\/|$)|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`|[\w$]+|[^\s]/g;
|
|
4835
|
+
let match;
|
|
4836
|
+
while (match = re.exec(content2)) {
|
|
4837
|
+
const value = match[0];
|
|
4838
|
+
if (value.startsWith("//") || value.startsWith("/*")) continue;
|
|
4839
|
+
if (/^["'`]/.test(value)) {
|
|
4840
|
+
if (value[0] === "`") {
|
|
4841
|
+
re.lastIndex = templateEnd(content2, match.index);
|
|
4842
|
+
result.push({ value: "<opaque>" });
|
|
4843
|
+
continue;
|
|
4844
|
+
}
|
|
4845
|
+
result.push({ value: value.slice(1, -1), string: !value.includes("\\") && value[0] !== "`" });
|
|
4846
|
+
if (!result.at(-1).string) result[result.length - 1] = { value: "<opaque>" };
|
|
4847
|
+
} else result.push({ value });
|
|
4848
|
+
}
|
|
4849
|
+
return result;
|
|
4850
|
+
}
|
|
4851
|
+
function imports(content2) {
|
|
4852
|
+
const ts = tokens(content2), found = [];
|
|
4853
|
+
for (let i = 0; i < ts.length; i++) {
|
|
4854
|
+
const t = ts[i];
|
|
4855
|
+
if (t.string || ts[i - 1]?.value === ".") continue;
|
|
4856
|
+
if (["require", "import"].includes(t.value) && ts[i + 1]?.value === "(" && ts[i + 2]?.string && ts[i + 3]?.value === ")") {
|
|
4857
|
+
found.push(ts[i + 2].value);
|
|
4858
|
+
}
|
|
4859
|
+
if (!["import", "export"].includes(t.value) || ts[i + 1]?.value === "(") continue;
|
|
4860
|
+
if (t.value === "import" && ts[i + 1]?.string) {
|
|
4861
|
+
found.push(ts[i + 1].value);
|
|
4862
|
+
continue;
|
|
4863
|
+
}
|
|
4864
|
+
for (let j = i + 1; j < Math.min(ts.length, i + 150); j++) {
|
|
4865
|
+
if ([";", "=", "import", "export"].includes(ts[j].value) && !ts[j].string) break;
|
|
4866
|
+
if (ts[j].value === "from" && !ts[j].string && ts[j + 1]?.string) {
|
|
4867
|
+
found.push(ts[j + 1].value);
|
|
4868
|
+
break;
|
|
4869
|
+
}
|
|
4870
|
+
}
|
|
4871
|
+
}
|
|
4872
|
+
return found;
|
|
4873
|
+
}
|
|
4874
|
+
function qualifiedName(ts, start) {
|
|
4875
|
+
let name = "";
|
|
4876
|
+
for (let i = start; i < ts.length; i++) {
|
|
4877
|
+
if (ts[i].string || !/^[A-Za-z_$][\w$]*$/.test(ts[i].value)) break;
|
|
4878
|
+
name += ts[i].value;
|
|
4879
|
+
if (ts[i + 1]?.value !== "." || !/^[A-Za-z_$][\w$]*$/.test(ts[i + 2]?.value ?? "")) break;
|
|
4880
|
+
name += ".";
|
|
4881
|
+
i++;
|
|
4882
|
+
}
|
|
4883
|
+
return name;
|
|
4884
|
+
}
|
|
4885
|
+
function declaredSymbols(content2) {
|
|
4886
|
+
const ts = tokens(content2);
|
|
4887
|
+
const packageAt = ts.findIndex((t) => !t.string && t.value === "package");
|
|
4888
|
+
if (packageAt < 0) return [];
|
|
4889
|
+
const pkg = qualifiedName(ts, packageAt + 1), names = [];
|
|
4890
|
+
let depth = 0;
|
|
4891
|
+
for (let i = 0; i < ts.length; i++) {
|
|
4892
|
+
if (ts[i].string) continue;
|
|
4893
|
+
if (ts[i].value === "{") depth++;
|
|
4894
|
+
if (ts[i].value === "}") depth--;
|
|
4895
|
+
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);
|
|
4896
|
+
}
|
|
4897
|
+
return names;
|
|
4898
|
+
}
|
|
4899
|
+
function jvmImports(content2) {
|
|
4900
|
+
const ts = tokens(content2), result = [];
|
|
4901
|
+
for (let i = 0; i < ts.length; i++) if (!ts[i].string && ts[i].value === "import") {
|
|
4902
|
+
const name = qualifiedName(ts, i + (ts[i + 1]?.value === "static" ? 2 : 1));
|
|
4903
|
+
if (name) result.push(name);
|
|
4904
|
+
}
|
|
4905
|
+
return result;
|
|
4906
|
+
}
|
|
4907
|
+
function resolveImport(file, specifier, available) {
|
|
4908
|
+
if (!specifier.startsWith("./") && !specifier.startsWith("../")) return null;
|
|
4909
|
+
const relative = path23.posix.normalize(path23.posix.join(path23.posix.dirname(file), specifier));
|
|
4910
|
+
if (relative.startsWith("../")) return null;
|
|
4911
|
+
if (available.has(relative)) return relative;
|
|
4912
|
+
const ext = path23.posix.extname(relative);
|
|
4913
|
+
const replacements = { ".js": [".ts", ".tsx"], ".mjs": [".mts"], ".cjs": [".cts"] };
|
|
4914
|
+
const candidates = ext ? (replacements[ext] ?? []).map((e) => relative.slice(0, -ext.length) + e) : jsExtensions.flatMap((e) => [relative + e, relative + "/index" + e]);
|
|
4915
|
+
const found = candidates.filter((f) => available.has(f));
|
|
4916
|
+
return found.length === 1 ? found[0] : null;
|
|
4917
|
+
}
|
|
4918
|
+
function mentionsPath(content2, target) {
|
|
4919
|
+
return new RegExp(`(?<![\\w./\\\\-])(?:\\./)?${escape(target)}(?![\\w./\\\\-])`).test(content2);
|
|
4920
|
+
}
|
|
4921
|
+
function createReferenceMatcher(targets, available, targetContents = /* @__PURE__ */ new Map()) {
|
|
4922
|
+
const counts = /* @__PURE__ */ new Map();
|
|
4923
|
+
for (const file of available) {
|
|
4924
|
+
const name = path23.posix.basename(file, path23.posix.extname(file));
|
|
4925
|
+
counts.set(name, (counts.get(name) ?? 0) + 1);
|
|
4926
|
+
}
|
|
4927
|
+
const jvmTargets = new Map([...targetContents].filter(([file]) => /\.(?:kt|kts|java)$/.test(file)).map(([file, content2]) => [file, declaredSymbols(content2)]));
|
|
4928
|
+
return (source) => {
|
|
4929
|
+
if (targets.includes(source.path)) return [];
|
|
4930
|
+
const resolved = new Set(jsExtensions.includes(path23.posix.extname(source.path)) ? imports(source.content).map((s) => resolveImport(source.path, s, available)).filter(Boolean) : []);
|
|
4931
|
+
if (/\.(?:kt|kts|java)$/.test(source.path)) {
|
|
4932
|
+
const imported = jvmImports(source.content);
|
|
4933
|
+
for (const [target, symbols] of jvmTargets) if (symbols.some((symbol) => imported.some((name) => name === symbol || name.startsWith(symbol + ".")))) resolved.add(target);
|
|
4934
|
+
}
|
|
4935
|
+
const groups = /* @__PURE__ */ new Map();
|
|
4936
|
+
const add = (evidence, match) => groups.set(evidence, [...groups.get(evidence) ?? [], match]);
|
|
4937
|
+
for (const target of targets) {
|
|
4938
|
+
if (resolved.has(target)) {
|
|
4939
|
+
add("resolved-import", target);
|
|
4940
|
+
continue;
|
|
4941
|
+
}
|
|
4942
|
+
if (mentionsPath(source.content, target)) {
|
|
4943
|
+
add("explicit-path", target);
|
|
4944
|
+
continue;
|
|
4945
|
+
}
|
|
4946
|
+
const ext = path23.posix.extname(target), name = path23.posix.basename(target, ext);
|
|
4947
|
+
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;
|
|
4948
|
+
if (word(name).test(source.content)) {
|
|
4949
|
+
add("name-candidate", name);
|
|
4950
|
+
}
|
|
4951
|
+
}
|
|
4952
|
+
return [...groups].map(([evidence, matches]) => ({ file: source.path, matches, kind: evidence === "resolved-import" ? "import" : "mention", evidence }));
|
|
4953
|
+
};
|
|
4954
|
+
}
|
|
4955
|
+
function sortReferences(result) {
|
|
4956
|
+
const rank = { "resolved-import": 0, "explicit-path": 1, "name-candidate": 2 };
|
|
4957
|
+
return result.sort((a, b) => rank[a.evidence] - rank[b.evidence] || b.matches.length - a.matches.length || a.file.localeCompare(b.file));
|
|
4958
|
+
}
|
|
4959
|
+
var jsExtensions, genericNames, escape, word;
|
|
4960
|
+
var init_references = __esm({
|
|
4961
|
+
"src/impact/references.ts"() {
|
|
4962
|
+
"use strict";
|
|
4963
|
+
jsExtensions = [".ts", ".tsx", ".js", ".jsx", ".mts", ".cts", ".mjs", ".cjs"];
|
|
4964
|
+
genericNames = /* @__PURE__ */ new Set(["index", "main", "app", "init", "config", "settings", "types", "utils", "test", "tests"]);
|
|
4965
|
+
escape = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4966
|
+
word = (s) => new RegExp(`(?<![\\w$])${escape(s)}(?![\\w$])`);
|
|
4967
|
+
}
|
|
4968
|
+
});
|
|
4969
|
+
|
|
4754
4970
|
// src/impact/impact.ts
|
|
4755
4971
|
var impact_exports = {};
|
|
4756
4972
|
__export(impact_exports, {
|
|
4757
4973
|
analyzeImpact: () => analyzeImpact
|
|
4758
4974
|
});
|
|
4759
|
-
import
|
|
4975
|
+
import path24 from "path";
|
|
4760
4976
|
import { execFile as execFile16 } from "child_process";
|
|
4761
4977
|
import { promisify as promisify16 } from "util";
|
|
4762
4978
|
async function analyzeImpact(rootDir, targetFiles) {
|
|
4763
|
-
const resolvedRoot =
|
|
4979
|
+
const resolvedRoot = path24.resolve(rootDir);
|
|
4764
4980
|
const resolvedTargets = await resolveTargetFiles(resolvedRoot, targetFiles);
|
|
4765
4981
|
const [cochange, references, tests] = await Promise.all([
|
|
4766
4982
|
getCochangeFiles(resolvedRoot, resolvedTargets),
|
|
@@ -4837,52 +5053,24 @@ async function getCochangeFiles(rootDir, targetFiles) {
|
|
|
4837
5053
|
})).filter((e) => e.cochangeRate >= 0.3 || e.sharedCommits >= 3).sort((a, b) => b.cochangeRate - a.cochangeRate).slice(0, 20);
|
|
4838
5054
|
}
|
|
4839
5055
|
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
5056
|
const access = await createFileAccess(rootDir);
|
|
4846
5057
|
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
|
-
);
|
|
5058
|
+
const available = new Set(allSourceFiles);
|
|
5059
|
+
const targetContents = /* @__PURE__ */ new Map();
|
|
5060
|
+
for (const target of targetFiles) {
|
|
5061
|
+
const source = await access.read(target);
|
|
5062
|
+
if (source) {
|
|
5063
|
+
available.add(target);
|
|
5064
|
+
targetContents.set(target, source.content);
|
|
5065
|
+
}
|
|
4877
5066
|
}
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
});
|
|
5067
|
+
const match = createReferenceMatcher(targetFiles, available, targetContents);
|
|
5068
|
+
const references = [];
|
|
5069
|
+
for (let i = 0; i < allSourceFiles.length; i += 50) {
|
|
5070
|
+
const batch = await Promise.all(allSourceFiles.slice(i, i + 50).map((file) => access.read(file)));
|
|
5071
|
+
for (const source of batch) if (source) references.push(...match(source));
|
|
5072
|
+
}
|
|
5073
|
+
return sortReferences(references);
|
|
4886
5074
|
}
|
|
4887
5075
|
async function getRelatedTests(rootDir, targetFiles) {
|
|
4888
5076
|
const testPatterns = [
|
|
@@ -4902,9 +5090,9 @@ async function getRelatedTests(rootDir, targetFiles) {
|
|
|
4902
5090
|
const testFiles = await (await createFileAccess(rootDir)).list(testPatterns);
|
|
4903
5091
|
const results = [];
|
|
4904
5092
|
for (const target of targetFiles) {
|
|
4905
|
-
const targetBaseName =
|
|
5093
|
+
const targetBaseName = path24.basename(target).replace(/\.[^.]+$/, "");
|
|
4906
5094
|
for (const testFile of testFiles) {
|
|
4907
|
-
const testBaseName =
|
|
5095
|
+
const testBaseName = path24.basename(testFile).replace(/\.[^.]+$/, "");
|
|
4908
5096
|
const sourceName = testBaseName.replace(/Test$|Tests$|Spec$|\.test$|\.spec$/, "").replace(/^test_|_test$/, "");
|
|
4909
5097
|
if (sourceName === targetBaseName) {
|
|
4910
5098
|
results.push({
|
|
@@ -4916,15 +5104,13 @@ async function getRelatedTests(rootDir, targetFiles) {
|
|
|
4916
5104
|
}
|
|
4917
5105
|
return results;
|
|
4918
5106
|
}
|
|
4919
|
-
function escapeRegex(str) {
|
|
4920
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4921
|
-
}
|
|
4922
5107
|
var exec14;
|
|
4923
5108
|
var init_impact = __esm({
|
|
4924
5109
|
"src/impact/impact.ts"() {
|
|
4925
5110
|
"use strict";
|
|
4926
5111
|
init_files();
|
|
4927
5112
|
init_paths();
|
|
5113
|
+
init_references();
|
|
4928
5114
|
exec14 = promisify16(execFile16);
|
|
4929
5115
|
}
|
|
4930
5116
|
});
|
|
@@ -4934,13 +5120,13 @@ var assemble_exports = {};
|
|
|
4934
5120
|
__export(assemble_exports, {
|
|
4935
5121
|
assembleContext: () => assembleContext
|
|
4936
5122
|
});
|
|
4937
|
-
import
|
|
4938
|
-
import
|
|
5123
|
+
import path25 from "path";
|
|
5124
|
+
import fs18 from "fs/promises";
|
|
4939
5125
|
async function collectImpact(root, candidates) {
|
|
4940
5126
|
const targets = /* @__PURE__ */ new Set();
|
|
4941
5127
|
let sourceFiles;
|
|
4942
5128
|
for (const candidate of sanitizeRepoPaths(candidates)) {
|
|
4943
|
-
const stat = await
|
|
5129
|
+
const stat = await fs18.stat(path25.join(root, candidate)).catch(() => null);
|
|
4944
5130
|
if (stat?.isDirectory()) {
|
|
4945
5131
|
sourceFiles ??= await (await createFileAccess(root)).list();
|
|
4946
5132
|
for (const file of sourceFiles) {
|
|
@@ -4954,13 +5140,19 @@ async function collectImpact(root, candidates) {
|
|
|
4954
5140
|
}
|
|
4955
5141
|
if (!targets.size) return { impact: null, relatedTests: [] };
|
|
4956
5142
|
const result = await analyzeImpact(root, [...targets]);
|
|
5143
|
+
const referenceCandidatesOmitted = result.references.filter((r) => r.evidence === "name-candidate").length;
|
|
4957
5144
|
return {
|
|
4958
|
-
impact: {
|
|
5145
|
+
impact: {
|
|
5146
|
+
targets: result.targetFiles,
|
|
5147
|
+
cochange: result.cochange,
|
|
5148
|
+
references: result.references.filter((r) => r.evidence !== "name-candidate").slice(0, 10),
|
|
5149
|
+
...referenceCandidatesOmitted ? { referenceCandidatesOmitted } : {}
|
|
5150
|
+
},
|
|
4959
5151
|
relatedTests: [...new Set(result.tests.map((t) => t.file))]
|
|
4960
5152
|
};
|
|
4961
5153
|
}
|
|
4962
5154
|
async function assembleContext(rootDir, task, files) {
|
|
4963
|
-
const resolvedRoot =
|
|
5155
|
+
const resolvedRoot = path25.resolve(rootDir);
|
|
4964
5156
|
const mapState = await inspectSnapshot(resolvedRoot);
|
|
4965
5157
|
const snapshot = mapState.snapshot;
|
|
4966
5158
|
const store = await loadDecisionStore(resolvedRoot);
|
|
@@ -5161,7 +5353,7 @@ __export(observations_exports, {
|
|
|
5161
5353
|
observeActivation: () => observeActivation,
|
|
5162
5354
|
readObservation: () => readObservation
|
|
5163
5355
|
});
|
|
5164
|
-
import
|
|
5356
|
+
import fs19 from "fs/promises";
|
|
5165
5357
|
import { z as z18 } from "zod";
|
|
5166
5358
|
function observationPath(directory, host, revision, version = executingVersion()) {
|
|
5167
5359
|
return `${directory}/activation/${host}-${revision}-${hash(version).slice(0, 12)}.json`;
|
|
@@ -5177,9 +5369,9 @@ async function observeActivation(dir, event, options = {}) {
|
|
|
5177
5369
|
const host = process.env.MASON_SETUP_HOST, revision = process.env.MASON_SETUP_REVISION;
|
|
5178
5370
|
if (host !== "codex" && host !== "claude" || !revision || !process.env.MASON_SETUP_ROOT) return null;
|
|
5179
5371
|
try {
|
|
5180
|
-
const capturedDirectory = options.reportPath?.match(/^(\.mason\/reports\/automation\/[a-f0-9]{24})\/checks\//)?.[1];
|
|
5181
|
-
const ws = capturedDirectory ? { root: await
|
|
5182
|
-
if (ws.root !== await
|
|
5372
|
+
const capturedDirectory = options.directory?.match(/^\.mason\/reports\/automation\/[a-f0-9]{24}$/)?.[0] ?? options.reportPath?.match(/^(\.mason\/reports\/automation\/[a-f0-9]{24})\/checks\//)?.[1];
|
|
5373
|
+
const ws = capturedDirectory ? { root: await fs19.realpath(dir), directory: capturedDirectory } : await workspace(dir);
|
|
5374
|
+
if (ws.root !== await fs19.realpath(process.env.MASON_SETUP_ROOT)) return null;
|
|
5183
5375
|
const setup = await loadSetup(ws.root);
|
|
5184
5376
|
if (setup?.hosts[host]?.revision !== revision) return "Mason setup changed; restart the assistant to observe the current integration.";
|
|
5185
5377
|
const directory = ws.directory + "/activation";
|
|
@@ -5265,7 +5457,7 @@ async function setupStatus(dir) {
|
|
|
5265
5457
|
version: 1,
|
|
5266
5458
|
status: pending.length ? "incomplete" : "not-configured",
|
|
5267
5459
|
hosts: {},
|
|
5268
|
-
next: pending.length ? "Setup did not finish. Rerun mason-auto setup --host " + pending[0] + " to resume using the retained original evidence." : "Run mason
|
|
5460
|
+
next: pending.length ? "Setup did not finish. Rerun mason-auto setup --host " + pending[0] + " to resume using the retained original evidence." : "Project hooks are inactive until local setup. Run mason setup --host codex or --host claude."
|
|
5269
5461
|
};
|
|
5270
5462
|
}
|
|
5271
5463
|
const hosts = {};
|
|
@@ -5481,8 +5673,8 @@ __export(client_exports, {
|
|
|
5481
5673
|
function createConfluenceClient(config, fetchFn = fetch) {
|
|
5482
5674
|
const baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
5483
5675
|
const auth = "Basic " + Buffer.from(`${config.email}:${config.apiToken}`).toString("base64");
|
|
5484
|
-
async function call(method,
|
|
5485
|
-
const res = await fetchFn(`${baseUrl}${
|
|
5676
|
+
async function call(method, path29, body) {
|
|
5677
|
+
const res = await fetchFn(`${baseUrl}${path29}`, {
|
|
5486
5678
|
method,
|
|
5487
5679
|
headers: {
|
|
5488
5680
|
Authorization: auth,
|
|
@@ -5494,7 +5686,7 @@ function createConfluenceClient(config, fetchFn = fetch) {
|
|
|
5494
5686
|
if (!res.ok) {
|
|
5495
5687
|
const text2 = await res.text();
|
|
5496
5688
|
throw new Error(
|
|
5497
|
-
`Confluence ${method} ${
|
|
5689
|
+
`Confluence ${method} ${path29} failed: ${res.status} ${res.statusText} \u2014 ${text2}`
|
|
5498
5690
|
);
|
|
5499
5691
|
}
|
|
5500
5692
|
if (res.status === 204) return null;
|
|
@@ -5595,28 +5787,28 @@ __export(config_exports2, {
|
|
|
5595
5787
|
saveConfluenceConfig: () => saveConfluenceConfig,
|
|
5596
5788
|
validateProvider: () => validateProvider
|
|
5597
5789
|
});
|
|
5598
|
-
import
|
|
5599
|
-
import
|
|
5790
|
+
import fs20 from "fs/promises";
|
|
5791
|
+
import path26 from "path";
|
|
5600
5792
|
import os3 from "os";
|
|
5601
5793
|
import { execFile as execFile17 } from "child_process";
|
|
5602
5794
|
import { promisify as promisify17 } from "util";
|
|
5603
5795
|
function configDir() {
|
|
5604
|
-
return
|
|
5796
|
+
return path26.join(os3.homedir(), ".mason");
|
|
5605
5797
|
}
|
|
5606
5798
|
function configFile() {
|
|
5607
|
-
return
|
|
5799
|
+
return path26.join(configDir(), "config.json");
|
|
5608
5800
|
}
|
|
5609
5801
|
async function loadConfig() {
|
|
5610
5802
|
try {
|
|
5611
|
-
const raw = await
|
|
5803
|
+
const raw = await fs20.readFile(configFile(), "utf-8");
|
|
5612
5804
|
return JSON.parse(raw);
|
|
5613
5805
|
} catch {
|
|
5614
5806
|
return null;
|
|
5615
5807
|
}
|
|
5616
5808
|
}
|
|
5617
5809
|
async function saveConfig(config) {
|
|
5618
|
-
await
|
|
5619
|
-
await
|
|
5810
|
+
await fs20.mkdir(configDir(), { recursive: true });
|
|
5811
|
+
await fs20.writeFile(configFile(), JSON.stringify(config, null, 2), "utf-8");
|
|
5620
5812
|
}
|
|
5621
5813
|
function getDefaultModel(provider) {
|
|
5622
5814
|
return DEFAULT_MODELS[provider];
|
|
@@ -5684,21 +5876,21 @@ var init_url = __esm({
|
|
|
5684
5876
|
});
|
|
5685
5877
|
|
|
5686
5878
|
// src/confluence/renderer.ts
|
|
5687
|
-
function
|
|
5879
|
+
function escape2(value) {
|
|
5688
5880
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
5689
5881
|
}
|
|
5690
5882
|
function infoPanel(text2) {
|
|
5691
|
-
return `<ac:structured-macro ac:name="info"><ac:rich-text-body><p>${
|
|
5883
|
+
return `<ac:structured-macro ac:name="info"><ac:rich-text-body><p>${escape2(text2)}</p></ac:rich-text-body></ac:structured-macro>`;
|
|
5692
5884
|
}
|
|
5693
5885
|
function featurePageTitle(prefix, name) {
|
|
5694
5886
|
return `${prefix}${name}`;
|
|
5695
5887
|
}
|
|
5696
5888
|
function renderFeaturePage(options) {
|
|
5697
|
-
const overviewBody = `<h2>What it does</h2><p>${
|
|
5889
|
+
const overviewBody = `<h2>What it does</h2><p>${escape2(options.productDescription)}</p>`;
|
|
5698
5890
|
const flowsBody = options.flowDescriptions.length ? `<h2>How it fits in</h2><ul>` + options.flowDescriptions.map(
|
|
5699
|
-
(f) => `<li><strong>${
|
|
5891
|
+
(f) => `<li><strong>${escape2(f.name)}</strong> \u2014 ${escape2(f.description)}</li>`
|
|
5700
5892
|
).join("") + `</ul>` : "";
|
|
5701
|
-
const navBody = `<p><ac:link><ri:page ri:content-title="${
|
|
5893
|
+
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
5894
|
const footer = infoPanel(
|
|
5703
5895
|
`Generated from code by Mason. This page is overwritten on each sync \u2014 edit the code, not the page.`
|
|
5704
5896
|
);
|
|
@@ -5714,7 +5906,7 @@ function renderIndexPage(options) {
|
|
|
5714
5906
|
}
|
|
5715
5907
|
const list = `<h2>Features</h2><ul>` + options.featureTitles.map((name) => {
|
|
5716
5908
|
const pageTitle = featurePageTitle(options.featurePrefix, name);
|
|
5717
|
-
return `<li><ac:link><ri:page ri:content-title="${
|
|
5909
|
+
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
5910
|
}).join("") + `</ul>`;
|
|
5719
5911
|
const banner = infoPanel(
|
|
5720
5912
|
`Generated from code by Mason. Maintained automatically \u2014 edit the code, not this page.`
|
|
@@ -5725,33 +5917,33 @@ function renderChangelogSection(section) {
|
|
|
5725
5917
|
const segments = [];
|
|
5726
5918
|
if (section.addedFeatures.length) {
|
|
5727
5919
|
segments.push(
|
|
5728
|
-
`<p><strong>Added features:</strong> ${section.addedFeatures.map(
|
|
5920
|
+
`<p><strong>Added features:</strong> ${section.addedFeatures.map(escape2).join(", ")}</p>`
|
|
5729
5921
|
);
|
|
5730
5922
|
}
|
|
5731
5923
|
if (section.removedFeatures.length) {
|
|
5732
5924
|
segments.push(
|
|
5733
|
-
`<p><strong>Removed features:</strong> ${section.removedFeatures.map(
|
|
5925
|
+
`<p><strong>Removed features:</strong> ${section.removedFeatures.map(escape2).join(", ")}</p>`
|
|
5734
5926
|
);
|
|
5735
5927
|
}
|
|
5736
5928
|
if (section.changedFeatures.length) {
|
|
5737
5929
|
segments.push(
|
|
5738
|
-
`<p><strong>Updated features:</strong> ${section.changedFeatures.map(
|
|
5930
|
+
`<p><strong>Updated features:</strong> ${section.changedFeatures.map(escape2).join(", ")}</p>`
|
|
5739
5931
|
);
|
|
5740
5932
|
}
|
|
5741
5933
|
if (section.addedFlows.length) {
|
|
5742
5934
|
segments.push(
|
|
5743
|
-
`<p><strong>Added flows:</strong> ${section.addedFlows.map(
|
|
5935
|
+
`<p><strong>Added flows:</strong> ${section.addedFlows.map(escape2).join(", ")}</p>`
|
|
5744
5936
|
);
|
|
5745
5937
|
}
|
|
5746
5938
|
if (section.removedFlows.length) {
|
|
5747
5939
|
segments.push(
|
|
5748
|
-
`<p><strong>Removed flows:</strong> ${section.removedFlows.map(
|
|
5940
|
+
`<p><strong>Removed flows:</strong> ${section.removedFlows.map(escape2).join(", ")}</p>`
|
|
5749
5941
|
);
|
|
5750
5942
|
}
|
|
5751
5943
|
if (segments.length === 0) {
|
|
5752
5944
|
segments.push(`<p><em>No meaningful changes detected.</em></p>`);
|
|
5753
5945
|
}
|
|
5754
|
-
return `<h3>${
|
|
5946
|
+
return `<h3>${escape2(section.syncedAt)}</h3>` + segments.join("");
|
|
5755
5947
|
}
|
|
5756
5948
|
function renderChangelogPage(sections) {
|
|
5757
5949
|
if (sections.length === 0) {
|
|
@@ -5776,21 +5968,21 @@ var init_renderer = __esm({
|
|
|
5776
5968
|
});
|
|
5777
5969
|
|
|
5778
5970
|
// src/confluence/diff.ts
|
|
5779
|
-
import
|
|
5780
|
-
import
|
|
5971
|
+
import fs21 from "fs/promises";
|
|
5972
|
+
import path27 from "path";
|
|
5781
5973
|
import { createHash as createHash5 } from "crypto";
|
|
5782
5974
|
function hashDescription(description) {
|
|
5783
5975
|
return createHash5("sha256").update(description, "utf8").digest("hex");
|
|
5784
5976
|
}
|
|
5785
5977
|
function syncStateDir(rootDir) {
|
|
5786
|
-
return
|
|
5978
|
+
return path27.join(rootDir, ".mason");
|
|
5787
5979
|
}
|
|
5788
5980
|
function syncStatePath(rootDir) {
|
|
5789
|
-
return
|
|
5981
|
+
return path27.join(syncStateDir(rootDir), "confluence-sync.json");
|
|
5790
5982
|
}
|
|
5791
5983
|
async function loadSyncState(rootDir) {
|
|
5792
5984
|
try {
|
|
5793
|
-
const raw = await
|
|
5985
|
+
const raw = await fs21.readFile(syncStatePath(rootDir), "utf-8");
|
|
5794
5986
|
const parsed = JSON.parse(raw);
|
|
5795
5987
|
if (parsed.version !== 2) return null;
|
|
5796
5988
|
return parsed;
|
|
@@ -5799,8 +5991,8 @@ async function loadSyncState(rootDir) {
|
|
|
5799
5991
|
}
|
|
5800
5992
|
}
|
|
5801
5993
|
async function saveSyncState(rootDir, state) {
|
|
5802
|
-
await
|
|
5803
|
-
await
|
|
5994
|
+
await fs21.mkdir(syncStateDir(rootDir), { recursive: true });
|
|
5995
|
+
await fs21.writeFile(
|
|
5804
5996
|
syncStatePath(rootDir),
|
|
5805
5997
|
JSON.stringify(state, null, 2),
|
|
5806
5998
|
"utf-8"
|
|
@@ -6354,8 +6546,8 @@ import { z as z19 } from "zod";
|
|
|
6354
6546
|
|
|
6355
6547
|
// src/mcp/tools.ts
|
|
6356
6548
|
init_repair();
|
|
6357
|
-
import
|
|
6358
|
-
import
|
|
6549
|
+
import fs22 from "fs/promises";
|
|
6550
|
+
import path28 from "path";
|
|
6359
6551
|
import { execFile as execFile19 } from "child_process";
|
|
6360
6552
|
import { promisify as promisify19 } from "util";
|
|
6361
6553
|
|
|
@@ -7250,7 +7442,7 @@ async function buildContext(dir) {
|
|
|
7250
7442
|
};
|
|
7251
7443
|
}
|
|
7252
7444
|
async function analyzeProject(dir) {
|
|
7253
|
-
const rootDir =
|
|
7445
|
+
const rootDir = path28.resolve(dir);
|
|
7254
7446
|
const context = await buildContext(rootDir);
|
|
7255
7447
|
const results = await runAll(context);
|
|
7256
7448
|
const projectSnapshot = await detectProjectSnapshot(rootDir);
|
|
@@ -7305,7 +7497,7 @@ async function detectProjectSnapshot(rootDir) {
|
|
|
7305
7497
|
const present = [];
|
|
7306
7498
|
for (const file of buildFiles) {
|
|
7307
7499
|
try {
|
|
7308
|
-
await
|
|
7500
|
+
await fs22.access(path28.join(rootDir, file));
|
|
7309
7501
|
present.push(file);
|
|
7310
7502
|
} catch {
|
|
7311
7503
|
}
|
|
@@ -7347,7 +7539,7 @@ async function detectProjectSnapshot(rootDir) {
|
|
|
7347
7539
|
const sourceFiles = await access.list();
|
|
7348
7540
|
const fileCounts = {};
|
|
7349
7541
|
for (const file of sourceFiles) {
|
|
7350
|
-
const ext =
|
|
7542
|
+
const ext = path28.extname(file).slice(1);
|
|
7351
7543
|
fileCounts[ext] = (fileCounts[ext] ?? 0) + 1;
|
|
7352
7544
|
}
|
|
7353
7545
|
return {
|
|
@@ -7358,7 +7550,7 @@ async function detectProjectSnapshot(rootDir) {
|
|
|
7358
7550
|
};
|
|
7359
7551
|
}
|
|
7360
7552
|
async function getCodeSamples(dir, count3 = 15) {
|
|
7361
|
-
const rootDir =
|
|
7553
|
+
const rootDir = path28.resolve(dir);
|
|
7362
7554
|
const samples = await sampleFiles(rootDir, count3);
|
|
7363
7555
|
const output = {
|
|
7364
7556
|
note: "These are previews (first ~60 lines). Read the file directly with your own tools to see it in full.",
|
|
@@ -7401,7 +7593,7 @@ async function unmappedContextResponse(rootDir) {
|
|
|
7401
7593
|
});
|
|
7402
7594
|
}
|
|
7403
7595
|
async function getProjectStructure(dir) {
|
|
7404
|
-
const rootDir =
|
|
7596
|
+
const rootDir = path28.resolve(dir);
|
|
7405
7597
|
const allFiles = await (await createFileAccess(rootDir)).list("**/*");
|
|
7406
7598
|
const dirInfo = /* @__PURE__ */ new Map();
|
|
7407
7599
|
for (const file of allFiles) {
|
|
@@ -7413,7 +7605,7 @@ async function getProjectStructure(dir) {
|
|
|
7413
7605
|
}
|
|
7414
7606
|
const info = dirInfo.get(dirPath);
|
|
7415
7607
|
info.fileCount++;
|
|
7416
|
-
const ext =
|
|
7608
|
+
const ext = path28.extname(file).slice(1);
|
|
7417
7609
|
if (ext) {
|
|
7418
7610
|
info.extensions.set(ext, (info.extensions.get(ext) ?? 0) + 1);
|
|
7419
7611
|
}
|
|
@@ -7458,7 +7650,7 @@ async function buildChangedFilePreviews(rootDir, changedFiles) {
|
|
|
7458
7650
|
return previews2;
|
|
7459
7651
|
}
|
|
7460
7652
|
async function getSnapshot(dir) {
|
|
7461
|
-
const rootDir =
|
|
7653
|
+
const rootDir = path28.resolve(dir);
|
|
7462
7654
|
const snapshot = await loadSnapshot(rootDir);
|
|
7463
7655
|
if (!snapshot) {
|
|
7464
7656
|
return unmappedContextResponse(rootDir);
|
|
@@ -7559,7 +7751,7 @@ function driftHint(report) {
|
|
|
7559
7751
|
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
7752
|
}
|
|
7561
7753
|
async function checkDrift(dir) {
|
|
7562
|
-
const rootDir =
|
|
7754
|
+
const rootDir = path28.resolve(dir);
|
|
7563
7755
|
const report = await computeDrift(rootDir);
|
|
7564
7756
|
if (!report) {
|
|
7565
7757
|
return JSON.stringify({
|
|
@@ -7590,7 +7782,7 @@ async function checkDrift(dir) {
|
|
|
7590
7782
|
return JSON.stringify({ exists: true, ...report, verification, hint });
|
|
7591
7783
|
}
|
|
7592
7784
|
async function generateSnapshotBatch(dir, offset = 0, batchSize = DEFAULT_BATCH_SIZE, files) {
|
|
7593
|
-
const rootDir =
|
|
7785
|
+
const rootDir = path28.resolve(dir);
|
|
7594
7786
|
const scoped = files !== void 0 && files.length > 0;
|
|
7595
7787
|
const scopeFiles = scoped ? sanitizePaths(rootDir, files) : void 0;
|
|
7596
7788
|
const batch = await prepareSnapshotBatch(dir, offset, batchSize, scopeFiles);
|
|
@@ -7637,7 +7829,7 @@ async function generateSnapshotBatch(dir, offset = 0, batchSize = DEFAULT_BATCH_
|
|
|
7637
7829
|
);
|
|
7638
7830
|
}
|
|
7639
7831
|
async function saveSnapshotPartial(dir, batchId, offset, features, flows) {
|
|
7640
|
-
const rootDir =
|
|
7832
|
+
const rootDir = path28.resolve(dir);
|
|
7641
7833
|
for (const feat of Object.values(features)) {
|
|
7642
7834
|
feat.files = sanitizePaths(rootDir, feat.files);
|
|
7643
7835
|
if (feat.tests) feat.tests = sanitizePaths(rootDir, feat.tests);
|
|
@@ -7666,7 +7858,7 @@ async function saveSnapshotPartial(dir, batchId, offset, features, flows) {
|
|
|
7666
7858
|
);
|
|
7667
7859
|
}
|
|
7668
7860
|
async function reduceSnapshot(dir) {
|
|
7669
|
-
const rootDir =
|
|
7861
|
+
const rootDir = path28.resolve(dir);
|
|
7670
7862
|
const partials = await loadAllPartials(rootDir);
|
|
7671
7863
|
if (partials.length === 0) {
|
|
7672
7864
|
return JSON.stringify(
|
|
@@ -7728,7 +7920,7 @@ async function reduceSnapshot(dir) {
|
|
|
7728
7920
|
);
|
|
7729
7921
|
}
|
|
7730
7922
|
async function fullAnalysis(dir) {
|
|
7731
|
-
const rootDir =
|
|
7923
|
+
const rootDir = path28.resolve(dir);
|
|
7732
7924
|
const [analysis, structure, samples, testMap, snapshot] = await Promise.all([
|
|
7733
7925
|
analyzeProject(dir),
|
|
7734
7926
|
getProjectStructure(dir),
|
|
@@ -7757,7 +7949,7 @@ function sanitizePaths(rootDir, files) {
|
|
|
7757
7949
|
return sanitizeRepoPaths(files);
|
|
7758
7950
|
}
|
|
7759
7951
|
async function saveSnapshotData(dir, features, flows, removeFeatures = [], removeFlows = []) {
|
|
7760
|
-
const rootDir =
|
|
7952
|
+
const rootDir = path28.resolve(dir);
|
|
7761
7953
|
const gitHash = await getCurrentGitHash(rootDir);
|
|
7762
7954
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7763
7955
|
for (const feat of Object.values(features)) {
|
|
@@ -7841,7 +8033,7 @@ async function saveSnapshotData(dir, features, flows, removeFeatures = [], remov
|
|
|
7841
8033
|
});
|
|
7842
8034
|
}
|
|
7843
8035
|
async function getImpact(dir, files) {
|
|
7844
|
-
const rootDir =
|
|
8036
|
+
const rootDir = path28.resolve(dir);
|
|
7845
8037
|
const { analyzeImpact: analyzeImpact2 } = await Promise.resolve().then(() => (init_impact(), impact_exports));
|
|
7846
8038
|
const result = await analyzeImpact2(rootDir, files);
|
|
7847
8039
|
return JSON.stringify(result, null, 2);
|
|
@@ -7850,7 +8042,7 @@ var VERIFY_DEFAULT_SAMPLE = 5;
|
|
|
7850
8042
|
var VERIFY_MAX_FILES_PER_ENTRY = 8;
|
|
7851
8043
|
var VERIFY_SKELETON_CHARS = 500;
|
|
7852
8044
|
async function verifySnapshot(dir, sample = VERIFY_DEFAULT_SAMPLE) {
|
|
7853
|
-
const rootDir =
|
|
8045
|
+
const rootDir = path28.resolve(dir);
|
|
7854
8046
|
const snapshot = await loadSnapshot(rootDir);
|
|
7855
8047
|
if (!snapshot) {
|
|
7856
8048
|
return JSON.stringify({
|
|
@@ -7915,7 +8107,7 @@ async function verifySnapshot(dir, sample = VERIFY_DEFAULT_SAMPLE) {
|
|
|
7915
8107
|
});
|
|
7916
8108
|
}
|
|
7917
8109
|
async function saveVerification(dir, verdicts) {
|
|
7918
|
-
const rootDir =
|
|
8110
|
+
const rootDir = path28.resolve(dir);
|
|
7919
8111
|
const snapshot = await loadSnapshot(rootDir);
|
|
7920
8112
|
if (!snapshot) {
|
|
7921
8113
|
return JSON.stringify({ exists: false, hint: "No concept map exists." });
|
|
@@ -7953,16 +8145,16 @@ async function saveVerification(dir, verdicts) {
|
|
|
7953
8145
|
});
|
|
7954
8146
|
}
|
|
7955
8147
|
async function saveDecision(dir, input) {
|
|
7956
|
-
const rootDir =
|
|
8148
|
+
const rootDir = path28.resolve(dir);
|
|
7957
8149
|
const { upsertDecision: upsertDecision2 } = await Promise.resolve().then(() => (init_decisions(), decisions_exports));
|
|
7958
8150
|
const result = await upsertDecision2(rootDir, input);
|
|
7959
8151
|
return JSON.stringify(result);
|
|
7960
8152
|
}
|
|
7961
8153
|
async function reviewDecision2(dir, input) {
|
|
7962
|
-
return JSON.stringify(await reviewDecision(
|
|
8154
|
+
return JSON.stringify(await reviewDecision(path28.resolve(dir), input));
|
|
7963
8155
|
}
|
|
7964
8156
|
async function getContext(dir, task, files) {
|
|
7965
|
-
const rootDir =
|
|
8157
|
+
const rootDir = path28.resolve(dir);
|
|
7966
8158
|
const { assembleContext: assembleContext2 } = await Promise.resolve().then(() => (init_assemble(), assemble_exports));
|
|
7967
8159
|
const bundle = await assembleContext2(rootDir, task, files);
|
|
7968
8160
|
return JSON.stringify(bundle);
|
|
@@ -8004,7 +8196,7 @@ async function masonInit(dir, options = {}) {
|
|
|
8004
8196
|
return JSON.stringify(await setupProject2(dir, options), null, 2);
|
|
8005
8197
|
}
|
|
8006
8198
|
if (options.host) throw new Error("host applies only to mode: setup.");
|
|
8007
|
-
const rootDir =
|
|
8199
|
+
const rootDir = path28.resolve(dir);
|
|
8008
8200
|
const marker = await loadProjectMarker(rootDir);
|
|
8009
8201
|
const { loadSetup: loadSetup2 } = await Promise.resolve().then(() => (init_model(), model_exports));
|
|
8010
8202
|
const setup = await loadSetup2(rootDir);
|
|
@@ -8024,7 +8216,7 @@ async function masonInit(dir, options = {}) {
|
|
|
8024
8216
|
);
|
|
8025
8217
|
}
|
|
8026
8218
|
async function masonCompleteInit(dir, options = {}) {
|
|
8027
|
-
const rootDir =
|
|
8219
|
+
const rootDir = path28.resolve(dir);
|
|
8028
8220
|
const existing = await loadProjectMarker(rootDir);
|
|
8029
8221
|
const features = await loadProjectFeatures(rootDir);
|
|
8030
8222
|
const marker = {
|
|
@@ -8133,7 +8325,7 @@ async function masonSetConfluence(input) {
|
|
|
8133
8325
|
);
|
|
8134
8326
|
}
|
|
8135
8327
|
async function exportToConfluenceTool(dir, overrides) {
|
|
8136
|
-
const rootDir =
|
|
8328
|
+
const rootDir = path28.resolve(dir);
|
|
8137
8329
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config2(), config_exports2));
|
|
8138
8330
|
const { exportToConfluence: exportToConfluence2 } = await Promise.resolve().then(() => (init_sync(), sync_exports));
|
|
8139
8331
|
const config = await loadConfig2();
|
|
@@ -8171,7 +8363,7 @@ function createMcpServer() {
|
|
|
8171
8363
|
const server = new McpServer(
|
|
8172
8364
|
{
|
|
8173
8365
|
name: "mason",
|
|
8174
|
-
version: "0.16.
|
|
8366
|
+
version: "0.16.2"
|
|
8175
8367
|
},
|
|
8176
8368
|
{
|
|
8177
8369
|
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 +8700,7 @@ function createMcpServer() {
|
|
|
8508
8700
|
);
|
|
8509
8701
|
server.tool(
|
|
8510
8702
|
"get_impact",
|
|
8511
|
-
"Trace
|
|
8703
|
+
"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
8704
|
{
|
|
8513
8705
|
dir: z19.string().describe("Absolute path to the project root directory"),
|
|
8514
8706
|
files: z19.array(z19.string()).describe("File paths or names to analyze (e.g., ['WeatherRepository.kt'] or ['src/services/auth.ts'])")
|