mason-context 0.16.1 → 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.1" : "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.1" : "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
  }
@@ -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;
@@ -5093,12 +5121,12 @@ __export(assemble_exports, {
5093
5121
  assembleContext: () => assembleContext
5094
5122
  });
5095
5123
  import path25 from "path";
5096
- import fs17 from "fs/promises";
5124
+ import fs18 from "fs/promises";
5097
5125
  async function collectImpact(root, candidates) {
5098
5126
  const targets = /* @__PURE__ */ new Set();
5099
5127
  let sourceFiles;
5100
5128
  for (const candidate of sanitizeRepoPaths(candidates)) {
5101
- const stat = await fs17.stat(path25.join(root, candidate)).catch(() => null);
5129
+ const stat = await fs18.stat(path25.join(root, candidate)).catch(() => null);
5102
5130
  if (stat?.isDirectory()) {
5103
5131
  sourceFiles ??= await (await createFileAccess(root)).list();
5104
5132
  for (const file of sourceFiles) {
@@ -5325,7 +5353,7 @@ __export(observations_exports, {
5325
5353
  observeActivation: () => observeActivation,
5326
5354
  readObservation: () => readObservation
5327
5355
  });
5328
- import fs18 from "fs/promises";
5356
+ import fs19 from "fs/promises";
5329
5357
  import { z as z18 } from "zod";
5330
5358
  function observationPath(directory, host, revision, version = executingVersion()) {
5331
5359
  return `${directory}/activation/${host}-${revision}-${hash(version).slice(0, 12)}.json`;
@@ -5341,9 +5369,9 @@ async function observeActivation(dir, event, options = {}) {
5341
5369
  const host = process.env.MASON_SETUP_HOST, revision = process.env.MASON_SETUP_REVISION;
5342
5370
  if (host !== "codex" && host !== "claude" || !revision || !process.env.MASON_SETUP_ROOT) return null;
5343
5371
  try {
5344
- const capturedDirectory = options.reportPath?.match(/^(\.mason\/reports\/automation\/[a-f0-9]{24})\/checks\//)?.[1];
5345
- const ws = capturedDirectory ? { root: await fs18.realpath(dir), directory: capturedDirectory } : await workspace(dir);
5346
- 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;
5347
5375
  const setup = await loadSetup(ws.root);
5348
5376
  if (setup?.hosts[host]?.revision !== revision) return "Mason setup changed; restart the assistant to observe the current integration.";
5349
5377
  const directory = ws.directory + "/activation";
@@ -5429,7 +5457,7 @@ async function setupStatus(dir) {
5429
5457
  version: 1,
5430
5458
  status: pending.length ? "incomplete" : "not-configured",
5431
5459
  hosts: {},
5432
- 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."
5433
5461
  };
5434
5462
  }
5435
5463
  const hosts = {};
@@ -5759,7 +5787,7 @@ __export(config_exports2, {
5759
5787
  saveConfluenceConfig: () => saveConfluenceConfig,
5760
5788
  validateProvider: () => validateProvider
5761
5789
  });
5762
- import fs19 from "fs/promises";
5790
+ import fs20 from "fs/promises";
5763
5791
  import path26 from "path";
5764
5792
  import os3 from "os";
5765
5793
  import { execFile as execFile17 } from "child_process";
@@ -5772,15 +5800,15 @@ function configFile() {
5772
5800
  }
5773
5801
  async function loadConfig() {
5774
5802
  try {
5775
- const raw = await fs19.readFile(configFile(), "utf-8");
5803
+ const raw = await fs20.readFile(configFile(), "utf-8");
5776
5804
  return JSON.parse(raw);
5777
5805
  } catch {
5778
5806
  return null;
5779
5807
  }
5780
5808
  }
5781
5809
  async function saveConfig(config) {
5782
- await fs19.mkdir(configDir(), { recursive: true });
5783
- 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");
5784
5812
  }
5785
5813
  function getDefaultModel(provider) {
5786
5814
  return DEFAULT_MODELS[provider];
@@ -5940,7 +5968,7 @@ var init_renderer = __esm({
5940
5968
  });
5941
5969
 
5942
5970
  // src/confluence/diff.ts
5943
- import fs20 from "fs/promises";
5971
+ import fs21 from "fs/promises";
5944
5972
  import path27 from "path";
5945
5973
  import { createHash as createHash5 } from "crypto";
5946
5974
  function hashDescription(description) {
@@ -5954,7 +5982,7 @@ function syncStatePath(rootDir) {
5954
5982
  }
5955
5983
  async function loadSyncState(rootDir) {
5956
5984
  try {
5957
- const raw = await fs20.readFile(syncStatePath(rootDir), "utf-8");
5985
+ const raw = await fs21.readFile(syncStatePath(rootDir), "utf-8");
5958
5986
  const parsed = JSON.parse(raw);
5959
5987
  if (parsed.version !== 2) return null;
5960
5988
  return parsed;
@@ -5963,8 +5991,8 @@ async function loadSyncState(rootDir) {
5963
5991
  }
5964
5992
  }
5965
5993
  async function saveSyncState(rootDir, state) {
5966
- await fs20.mkdir(syncStateDir(rootDir), { recursive: true });
5967
- await fs20.writeFile(
5994
+ await fs21.mkdir(syncStateDir(rootDir), { recursive: true });
5995
+ await fs21.writeFile(
5968
5996
  syncStatePath(rootDir),
5969
5997
  JSON.stringify(state, null, 2),
5970
5998
  "utf-8"
@@ -6518,7 +6546,7 @@ import { z as z19 } from "zod";
6518
6546
 
6519
6547
  // src/mcp/tools.ts
6520
6548
  init_repair();
6521
- import fs21 from "fs/promises";
6549
+ import fs22 from "fs/promises";
6522
6550
  import path28 from "path";
6523
6551
  import { execFile as execFile19 } from "child_process";
6524
6552
  import { promisify as promisify19 } from "util";
@@ -7469,7 +7497,7 @@ async function detectProjectSnapshot(rootDir) {
7469
7497
  const present = [];
7470
7498
  for (const file of buildFiles) {
7471
7499
  try {
7472
- await fs21.access(path28.join(rootDir, file));
7500
+ await fs22.access(path28.join(rootDir, file));
7473
7501
  present.push(file);
7474
7502
  } catch {
7475
7503
  }
@@ -8335,7 +8363,7 @@ function createMcpServer() {
8335
8363
  const server = new McpServer(
8336
8364
  {
8337
8365
  name: "mason",
8338
- version: "0.16.1"
8366
+ version: "0.16.2"
8339
8367
  },
8340
8368
  {
8341
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.'