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/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.0" : "development";
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.0" : "development";
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, or change request \u2192 \`get_context\` with the task text and known files: matching decisions, related tests, impact, and any available map entries.
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
- - Learned something the code cannot explain (a failed approach, an incident's cause, a workaround's reason, a review-settled convention) \u2192 \`save_decision\` with rationale, anchors, and any known owner, sources, and recorder. It creates a proposal immediately without setup or a map. Never invent attribution or record code-derivable facts, session trivia, or secrets.
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 fs16 from "fs/promises";
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 fs16.access(path22.join(resolvedRoot, relPath));
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 path23 from "path";
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 = path23.resolve(rootDir);
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 targetSet = new Set(targetFiles);
4848
- const filesToSearch = allSourceFiles.filter((f) => !targetSet.has(f));
4849
- const results = /* @__PURE__ */ new Map();
4850
- const importLine = /^\s*(import\b|from\b.*\bimport\b|const\b.*=\s*require\(|use\b|#include\b|require\s*\()/;
4851
- const batchSize = 50;
4852
- for (let i = 0; i < filesToSearch.length; i += batchSize) {
4853
- const batch = filesToSearch.slice(i, i + batchSize);
4854
- await Promise.all(
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
- return [...results.entries()].map(([file, { matches, isImport }]) => ({
4879
- file,
4880
- matches: [...matches],
4881
- kind: isImport ? "import" : "mention"
4882
- })).sort((a, b) => {
4883
- if (a.kind !== b.kind) return a.kind === "import" ? -1 : 1;
4884
- return b.matches.length - a.matches.length;
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 = path23.basename(target).replace(/\.[^.]+$/, "");
5093
+ const targetBaseName = path24.basename(target).replace(/\.[^.]+$/, "");
4906
5094
  for (const testFile of testFiles) {
4907
- const testBaseName = path23.basename(testFile).replace(/\.[^.]+$/, "");
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 path24 from "path";
4938
- import fs17 from "fs/promises";
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 fs17.stat(path24.join(root, candidate)).catch(() => null);
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: { targets: result.targetFiles, cochange: result.cochange, references: result.references.slice(0, 10) },
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 = path24.resolve(rootDir);
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 fs18 from "fs/promises";
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 fs18.realpath(dir), directory: capturedDirectory } : await workspace(dir);
5182
- if (ws.root !== await fs18.realpath(process.env.MASON_SETUP_ROOT)) return null;
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-auto setup --host codex or --host claude."
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, path28, body) {
5485
- const res = await fetchFn(`${baseUrl}${path28}`, {
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} ${path28} failed: ${res.status} ${res.statusText} \u2014 ${text2}`
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 fs19 from "fs/promises";
5599
- import path25 from "path";
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 path25.join(os3.homedir(), ".mason");
5796
+ return path26.join(os3.homedir(), ".mason");
5605
5797
  }
5606
5798
  function configFile() {
5607
- return path25.join(configDir(), "config.json");
5799
+ return path26.join(configDir(), "config.json");
5608
5800
  }
5609
5801
  async function loadConfig() {
5610
5802
  try {
5611
- const raw = await fs19.readFile(configFile(), "utf-8");
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 fs19.mkdir(configDir(), { recursive: true });
5619
- await fs19.writeFile(configFile(), JSON.stringify(config, null, 2), "utf-8");
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 escape(value) {
5879
+ function escape2(value) {
5688
5880
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5689
5881
  }
5690
5882
  function infoPanel(text2) {
5691
- return `<ac:structured-macro ac:name="info"><ac:rich-text-body><p>${escape(text2)}</p></ac:rich-text-body></ac:structured-macro>`;
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>${escape(options.productDescription)}</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>${escape(f.name)}</strong> \u2014 ${escape(f.description)}</li>`
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="${escape(options.indexPageTitle)}"/><ac:plain-text-link-body><![CDATA[Back to ${options.indexPageTitle}]]></ac:plain-text-link-body></ac:link></p>`;
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="${escape(pageTitle)}"/><ac:plain-text-link-body><![CDATA[${name}]]></ac:plain-text-link-body></ac:link></li>`;
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(escape).join(", ")}</p>`
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(escape).join(", ")}</p>`
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(escape).join(", ")}</p>`
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(escape).join(", ")}</p>`
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(escape).join(", ")}</p>`
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>${escape(section.syncedAt)}</h3>` + segments.join("");
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 fs20 from "fs/promises";
5780
- import path26 from "path";
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 path26.join(rootDir, ".mason");
5978
+ return path27.join(rootDir, ".mason");
5787
5979
  }
5788
5980
  function syncStatePath(rootDir) {
5789
- return path26.join(syncStateDir(rootDir), "confluence-sync.json");
5981
+ return path27.join(syncStateDir(rootDir), "confluence-sync.json");
5790
5982
  }
5791
5983
  async function loadSyncState(rootDir) {
5792
5984
  try {
5793
- const raw = await fs20.readFile(syncStatePath(rootDir), "utf-8");
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 fs20.mkdir(syncStateDir(rootDir), { recursive: true });
5803
- await fs20.writeFile(
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 fs21 from "fs/promises";
6358
- import path27 from "path";
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 = path27.resolve(dir);
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 fs21.access(path27.join(rootDir, file));
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 = path27.extname(file).slice(1);
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 = path27.resolve(dir);
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 = path27.resolve(dir);
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 = path27.extname(file).slice(1);
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 = path27.resolve(dir);
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 = path27.resolve(dir);
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 = path27.resolve(dir);
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 = path27.resolve(dir);
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 = path27.resolve(dir);
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 = path27.resolve(dir);
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 = path27.resolve(dir);
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 = path27.resolve(dir);
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 = path27.resolve(dir);
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 = path27.resolve(dir);
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 = path27.resolve(dir);
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(path27.resolve(dir), input));
8154
+ return JSON.stringify(await reviewDecision(path28.resolve(dir), input));
7963
8155
  }
7964
8156
  async function getContext(dir, task, files) {
7965
- const rootDir = path27.resolve(dir);
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 = path27.resolve(dir);
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 = path27.resolve(dir);
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 = path27.resolve(dir);
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.0"
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 the impact of changing files: historical co-change partners, references, and related tests. Deterministic, read-only, and usable without initialization, saved decisions, or a concept map.",
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'])")