mason-context 0.19.1 → 0.19.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
@@ -3182,8 +3182,121 @@ var init_findings = __esm({
3182
3182
  }
3183
3183
  });
3184
3184
 
3185
- // src/automation/store.ts
3185
+ // src/automation/notifications.ts
3186
3186
  import { z as z5 } from "zod";
3187
+ function condition(finding) {
3188
+ const source = finding.current ?? finding.original;
3189
+ const e = source.evidence;
3190
+ const id = e.kind === "decision-anchor" ? digest([source.type, source.anchor.doc, e.decisionId]) : finding.id;
3191
+ const result = { level: "confidence" in source ? 2 : 1, facts: [] };
3192
+ switch (e.kind) {
3193
+ case "count-mismatch":
3194
+ result.magnitude = Math.abs(e.actual - e.claimed);
3195
+ break;
3196
+ case "unmentioned-dir":
3197
+ result.magnitude = e.sourceFileCount;
3198
+ break;
3199
+ case "decision-anchor":
3200
+ result.facts = [...e.changedFiles].sort();
3201
+ break;
3202
+ case "doc-behind-manifests":
3203
+ result.facts = (e.matches ?? []).map((m) => JSON.stringify([m.manifest, m.dependency, m.after])).sort();
3204
+ break;
3205
+ }
3206
+ return { id, condition: result };
3207
+ }
3208
+ function notificationConditions(findings) {
3209
+ const active = findings.filter((f) => f.status !== "resolved");
3210
+ const currentIds = new Set(active.filter((f) => f.current).map((f) => condition(f).id));
3211
+ const result = {};
3212
+ for (const f of active) {
3213
+ const value = condition(f);
3214
+ if (!f.current && currentIds.has(value.id)) continue;
3215
+ const previous = result[value.id];
3216
+ result[value.id] = previous ? {
3217
+ level: Math.max(previous.level, value.condition.level),
3218
+ facts: [.../* @__PURE__ */ new Set([...previous.facts, ...value.condition.facts])].sort(),
3219
+ ...previous.magnitude !== void 0 || value.condition.magnitude !== void 0 ? { magnitude: Math.max(previous.magnitude ?? 0, value.condition.magnitude ?? 0) } : {}
3220
+ } : value.condition;
3221
+ }
3222
+ return result;
3223
+ }
3224
+ function createNotificationState(findings, head) {
3225
+ return { version: 1, baselineHead: head, baseline: notificationConditions(findings), delivered: {}, cleared: [] };
3226
+ }
3227
+ function worsened(current, previous) {
3228
+ return !previous || current.level > previous.level || (current.magnitude ?? 0) > (previous.magnitude ?? 0) || current.facts.some((fact) => !previous.facts.includes(fact));
3229
+ }
3230
+ function completionFindings(state, findings) {
3231
+ const current = notificationConditions(findings);
3232
+ const cleared = new Set(state.cleared);
3233
+ const resolvedIds = new Set(findings.filter((f) => f.status === "resolved").map((f) => condition(f).id));
3234
+ for (const id of Object.keys(state.baseline)) if (resolvedIds.has(id) && !current[id]) cleared.add(id);
3235
+ state.cleared = [...cleared];
3236
+ const selected = [];
3237
+ const selectedIds = /* @__PURE__ */ new Set();
3238
+ const delivered = { ...state.delivered };
3239
+ for (const finding of findings) {
3240
+ const value = condition(finding);
3241
+ const previous = state.delivered[value.id];
3242
+ value.condition = current[value.id] ?? value.condition;
3243
+ if (finding.status === "unverified" || !finding.current && finding.status !== "resolved") {
3244
+ continue;
3245
+ }
3246
+ if (finding.status === "resolved") {
3247
+ if (!current[value.id]) delete delivered[value.id];
3248
+ continue;
3249
+ }
3250
+ const eligible = cleared.has(value.id) || worsened(value.condition, state.baseline[value.id]);
3251
+ if (!eligible) continue;
3252
+ if (!selectedIds.has(value.id) && worsened(value.condition, previous)) {
3253
+ selectedIds.add(value.id);
3254
+ const source = finding.current;
3255
+ selected.push(source.evidence.kind === "decision-anchor" ? { ...finding, current: { ...source, evidence: { ...source.evidence, changedFiles: value.condition.facts } } } : finding);
3256
+ if (selected.length <= MAX_COMPLETION_FINDINGS) delivered[value.id] = value.condition;
3257
+ }
3258
+ }
3259
+ state.delivered = delivered;
3260
+ return selected;
3261
+ }
3262
+ function completionSummary(findings, reportPath) {
3263
+ if (!findings.length) return null;
3264
+ const safe = (value) => value.replace(/[\u0000-\u001f\u007f-\u009f]/g, " ").slice(0, 250);
3265
+ return [
3266
+ `Mason: ${findings.length} new or worsened finding(s) since the retained baseline.`,
3267
+ ...findings.slice(0, MAX_COMPLETION_FINDINGS).map((f) => {
3268
+ const source = f.current ?? f.original;
3269
+ const detail = source.evidence.kind === "decision-anchor" ? `Changes to ${source.evidence.changedFiles.join(", ")} may affect decision "${source.evidence.title}". Inspect the diff if relevant; acceptance is not required.` : source.message;
3270
+ return `[${f.status === "unresolved" ? "issue" : "advisory"}] ${safe(source.anchor.doc)}: ${safe(detail)}`;
3271
+ }),
3272
+ ...findings.length > MAX_COMPLETION_FINDINGS ? [`${findings.length - MAX_COMPLETION_FINDINGS} more in the report; pending later feedback.`] : [],
3273
+ `Evidence: ${reportPath}. Explicit audit: mason_automation(action: "check").`,
3274
+ "Inspect findings within the authorized work. Concurrent changes may also contribute; this summary does not establish who introduced them."
3275
+ ].join("\n");
3276
+ }
3277
+ var conditionSchema, MAX_COMPLETION_FINDINGS, notificationStateSchema;
3278
+ var init_notifications = __esm({
3279
+ "src/automation/notifications.ts"() {
3280
+ "use strict";
3281
+ init_findings();
3282
+ conditionSchema = z5.object({
3283
+ level: z5.number(),
3284
+ facts: z5.array(z5.string()),
3285
+ magnitude: z5.number().optional()
3286
+ });
3287
+ MAX_COMPLETION_FINDINGS = 4;
3288
+ notificationStateSchema = z5.object({
3289
+ version: z5.literal(1),
3290
+ baselineHead: z5.string(),
3291
+ baseline: z5.record(conditionSchema),
3292
+ delivered: z5.record(conditionSchema),
3293
+ cleared: z5.array(z5.string())
3294
+ });
3295
+ }
3296
+ });
3297
+
3298
+ // src/automation/store.ts
3299
+ import { z as z6 } from "zod";
3187
3300
  function parseState(raw) {
3188
3301
  try {
3189
3302
  return stateSchema.parse(raw);
@@ -3199,37 +3312,36 @@ var init_store = __esm({
3199
3312
  "src/automation/store.ts"() {
3200
3313
  "use strict";
3201
3314
  init_store_lock();
3202
- hostSchema = z5.enum(["claude", "codex"]);
3315
+ init_notifications();
3316
+ hostSchema = z6.enum(["claude", "codex"]);
3203
3317
  events = ["session_start", "turn_start", "before_tool", "after_tool", "task_end"];
3204
- stateSchema = z5.object({
3205
- version: z5.literal(1),
3206
- root: z5.string(),
3207
- gitDir: z5.string(),
3208
- branch: z5.string(),
3209
- baselines: z5.array(z5.object({ path: z5.string(), at: z5.string(), event: z5.string(), fingerprint: z5.string() })).max(128),
3210
- sessions: z5.record(z5.object({
3318
+ stateSchema = z6.object({
3319
+ version: z6.literal(1),
3320
+ root: z6.string(),
3321
+ gitDir: z6.string(),
3322
+ branch: z6.string(),
3323
+ baselines: z6.array(z6.object({ path: z6.string(), at: z6.string(), event: z6.string(), fingerprint: z6.string() })).max(128),
3324
+ sessions: z6.record(z6.object({
3211
3325
  host: hostSchema,
3212
- seen: z5.string().nullable(),
3213
- continued: z5.boolean(),
3214
- initialIssues: z5.array(z5.string()),
3215
- initialDocs: z5.record(z5.string().nullable()),
3216
- lastUsed: z5.string(),
3217
- mutationObserved: z5.boolean(),
3218
- pending: z5.record(z5.string()),
3219
- coverageGaps: z5.array(z5.string()),
3220
- events: z5.record(z5.object({ at: z5.string(), count: z5.number().int().positive() }))
3326
+ lastUsed: z6.string(),
3327
+ notifiedDiagnostics: z6.array(z6.string()).default([]),
3328
+ pending: z6.record(z6.string()),
3329
+ coverageGaps: z6.array(z6.string()),
3330
+ events: z6.record(z6.object({ at: z6.string(), count: z6.number().int().positive() }))
3221
3331
  })),
3222
- updatedAt: z5.string(),
3223
- fingerprint: z5.string().nullable(),
3224
- latest: z5.string().nullable(),
3225
- analysis: z5.object({ id: z5.string(), fingerprint: z5.string(), completedAt: z5.number(), path: z5.string() }).optional()
3332
+ updatedAt: z6.string(),
3333
+ fingerprint: z6.string().nullable(),
3334
+ latest: z6.string().nullable(),
3335
+ notifications: notificationStateSchema.optional(),
3336
+ agentNotifications: notificationStateSchema.optional(),
3337
+ analysis: z6.object({ id: z6.string(), fingerprint: z6.string(), completedAt: z6.number(), path: z6.string() }).optional()
3226
3338
  });
3227
3339
  }
3228
3340
  });
3229
3341
 
3230
3342
  // src/audit/advisory-review.ts
3231
3343
  import fs10 from "fs/promises";
3232
- import { z as z6 } from "zod";
3344
+ import { z as z7 } from "zod";
3233
3345
  async function readRecord(root, id) {
3234
3346
  const raw = await readStoreJson(root, recordPath(id));
3235
3347
  if (raw === null) return null;
@@ -3448,41 +3560,41 @@ var init_advisory_review = __esm({
3448
3560
  init_decisions();
3449
3561
  init_drift2();
3450
3562
  git2 = async (root, ...args) => (await (["log", "ls-tree", "ls-files", "merge-base"].includes(args[0]) ? inspectionGit : execGit)(args, { cwd: root, timeout: 1e4, maxBuffer: 8 * 1024 * 1024 })).stdout;
3451
- hashSchema = z6.string().regex(/^[a-f0-9]{64}$/);
3452
- commitSchema2 = z6.string().regex(/^[a-f0-9]{40,64}$/);
3453
- evidenceSchema2 = z6.object({
3563
+ hashSchema = z7.string().regex(/^[a-f0-9]{64}$/);
3564
+ commitSchema2 = z7.string().regex(/^[a-f0-9]{40,64}$/);
3565
+ evidenceSchema2 = z7.object({
3454
3566
  head: commitSchema2,
3455
3567
  fingerprint: hashSchema,
3456
- scope: z6.array(z6.string()).min(1).max(1e3),
3457
- tree: z6.string().max(2 * 1024 * 1024),
3568
+ scope: z7.array(z7.string()).min(1).max(1e3),
3569
+ tree: z7.string().max(2 * 1024 * 1024),
3458
3570
  lastScopeCommit: commitSchema2,
3459
- paths: z6.array(z6.object({ path: z6.string(), exists: z6.boolean() })).max(1e3)
3571
+ paths: z7.array(z7.object({ path: z7.string(), exists: z7.boolean() })).max(1e3)
3460
3572
  });
3461
- dismissalReasonSchema = z6.enum(["no-dependency-claims", "unrelated-manifest-change"]);
3462
- eventSchema2 = z6.object({
3463
- at: z6.string().datetime(),
3464
- reviewer: z6.string().trim().min(1).max(200).optional(),
3465
- note: z6.string().trim().min(1).max(2500),
3466
- outcome: z6.enum(["addressed", "inapplicable", "deferred", "dismissed"]),
3573
+ dismissalReasonSchema = z7.enum(["no-dependency-claims", "unrelated-manifest-change"]);
3574
+ eventSchema2 = z7.object({
3575
+ at: z7.string().datetime(),
3576
+ reviewer: z7.string().trim().min(1).max(200).optional(),
3577
+ note: z7.string().trim().min(1).max(2500),
3578
+ outcome: z7.enum(["addressed", "inapplicable", "deferred", "dismissed"]),
3467
3579
  reasonCode: dismissalReasonSchema.optional(),
3468
3580
  finding: advisorySchema.refine((finding) => finding.evidence.kind !== "decision-anchor", "Decision findings use decision reviews"),
3469
3581
  evidence: evidenceSchema2
3470
3582
  });
3471
- recordSchema = z6.object({ version: z6.literal(1), id: hashSchema, events: z6.array(eventSchema2).min(1).max(200), digest: hashSchema });
3472
- reviewSummarySchema = z6.object({
3583
+ recordSchema = z7.object({ version: z7.literal(1), id: hashSchema, events: z7.array(eventSchema2).min(1).max(200), digest: hashSchema });
3584
+ reviewSummarySchema = z7.object({
3473
3585
  id: hashSchema,
3474
- status: z6.enum(["current", "reopened", "deferred", "unverified"]),
3475
- reason: z6.string(),
3476
- recordPath: z6.string().optional(),
3477
- reviewer: z6.string().optional(),
3478
- note: z6.string().optional(),
3479
- outcome: z6.enum(["addressed", "inapplicable", "deferred", "accepted", "reaffirmed", "retired", "dismissed"]).optional(),
3480
- reviewedHead: z6.string().optional()
3586
+ status: z7.enum(["current", "reopened", "deferred", "unverified"]),
3587
+ reason: z7.string(),
3588
+ recordPath: z7.string().optional(),
3589
+ reviewer: z7.string().optional(),
3590
+ note: z7.string().optional(),
3591
+ outcome: z7.enum(["addressed", "inapplicable", "deferred", "accepted", "reaffirmed", "retired", "dismissed"]).optional(),
3592
+ reviewedHead: z7.string().optional()
3481
3593
  });
3482
- advisoryReviewRequest = z6.object({
3483
- baselinePath: z6.string().min(1),
3594
+ advisoryReviewRequest = z7.object({
3595
+ baselinePath: z7.string().min(1),
3484
3596
  findingId: hashSchema,
3485
- action: z6.enum(["prepare", "addressed", "inapplicable", "deferred", "dismiss"]).default("prepare"),
3597
+ action: z7.enum(["prepare", "addressed", "inapplicable", "deferred", "dismiss"]).default("prepare"),
3486
3598
  reasonCode: dismissalReasonSchema.optional(),
3487
3599
  reviewer: eventSchema2.shape.reviewer.optional(),
3488
3600
  note: eventSchema2.shape.note.optional(),
@@ -3600,7 +3712,7 @@ __export(repair_exports, {
3600
3712
  import fs12 from "fs/promises";
3601
3713
  import path21 from "path";
3602
3714
  import { randomUUID as randomUUID2 } from "crypto";
3603
- import { z as z7 } from "zod";
3715
+ import { z as z8 } from "zod";
3604
3716
  function recheckable(finding) {
3605
3717
  return !("confidence" in finding) && finding.resolution === "recheck" && ["deleted-reference", "dead-command", "new-module"].includes(finding.type) && ["missing-path", "missing-script", "unmentioned-dir"].includes(finding.evidence.kind);
3606
3718
  }
@@ -3671,7 +3783,7 @@ async function withRepairSession(root, options, run, validate, cachedAudit) {
3671
3783
  }
3672
3784
  async function prepareOriginal(rootDir, checks, options, session) {
3673
3785
  const root = await fs12.realpath(rootDir);
3674
- const selected = z7.array(checkSchema).nonempty().parse(checks);
3786
+ const selected = z8.array(checkSchema).nonempty().parse(checks);
3675
3787
  const report = await (session ? session.audit(selected) : stableAudit(root, selected, options));
3676
3788
  if (!report) throw new Error("No context files found to prepare a repair.");
3677
3789
  if (!report.gitAvailable) throw new Error("Readable Git history is required to prepare a repair.");
@@ -3828,42 +3940,42 @@ var init_repair = __esm({
3828
3940
  init_findings();
3829
3941
  init_advisory_review();
3830
3942
  init_findings();
3831
- count2 = z7.number().int().nonnegative();
3832
- reportSchema = z7.object({
3833
- version: z7.literal(1),
3834
- root: z7.string(),
3835
- gitAvailable: z7.literal(true),
3943
+ count2 = z8.number().int().nonnegative();
3944
+ reportSchema = z8.object({
3945
+ version: z8.literal(1),
3946
+ root: z8.string(),
3947
+ gitAvailable: z8.literal(true),
3836
3948
  headHash: commitSchema.shape.hash,
3837
- checksRun: z7.array(checkSchema).nonempty(),
3838
- docs: z7.array(z7.object({
3839
- path: z7.string().refine((value) => normalizeRepoPath(value) === value),
3840
- scope: z7.string().optional(),
3841
- kind: z7.enum(["instructions", "readme", "guide"]).optional(),
3949
+ checksRun: z8.array(checkSchema).nonempty(),
3950
+ docs: z8.array(z8.object({
3951
+ path: z8.string().refine((value) => normalizeRepoPath(value) === value),
3952
+ scope: z8.string().optional(),
3953
+ kind: z8.enum(["instructions", "readme", "guide"]).optional(),
3842
3954
  lastCommit: commitSchema.nullable(),
3843
- dirty: z7.boolean(),
3955
+ dirty: z8.boolean(),
3844
3956
  lineCount: count2
3845
3957
  })).nonempty(),
3846
- decisionsChecked: z7.boolean(),
3847
- clean: z7.boolean(),
3848
- excludedChecks: z7.array(z7.object({ check: checkSchema, doc: z7.string() })).optional(),
3849
- issues: z7.array(issueSchema),
3850
- advisories: z7.array(advisorySchema),
3851
- suppressedAdvisories: z7.array(advisorySchema).optional(),
3852
- skippedChecks: z7.array(z7.object({ check: z7.string(), reason: z7.string(), doc: z7.string().optional() }))
3958
+ decisionsChecked: z8.boolean(),
3959
+ clean: z8.boolean(),
3960
+ excludedChecks: z8.array(z8.object({ check: checkSchema, doc: z8.string() })).optional(),
3961
+ issues: z8.array(issueSchema),
3962
+ advisories: z8.array(advisorySchema),
3963
+ suppressedAdvisories: z8.array(advisorySchema).optional(),
3964
+ skippedChecks: z8.array(z8.object({ check: z8.string(), reason: z8.string(), doc: z8.string().optional() }))
3853
3965
  });
3854
- baselineSchema = z7.object({
3855
- kind: z7.literal("mason-audit-repair"),
3856
- version: z7.literal(1),
3857
- createdAt: z7.string().datetime(),
3966
+ baselineSchema = z8.object({
3967
+ kind: z8.literal("mason-audit-repair"),
3968
+ version: z8.literal(1),
3969
+ createdAt: z8.string().datetime(),
3858
3970
  report: reportSchema,
3859
- digest: z7.string().regex(/^[a-f0-9]{64}$/)
3971
+ digest: z8.string().regex(/^[a-f0-9]{64}$/)
3860
3972
  });
3861
- repairFindingSchema = z7.object({
3862
- id: z7.string(),
3863
- original: z7.union([issueSchema, advisorySchema]),
3864
- status: z7.enum(["resolved", "unresolved", "review-required", "unverified"]),
3865
- reason: z7.string(),
3866
- current: z7.union([issueSchema, advisorySchema]).optional(),
3973
+ repairFindingSchema = z8.object({
3974
+ id: z8.string(),
3975
+ original: z8.union([issueSchema, advisorySchema]),
3976
+ status: z8.enum(["resolved", "unresolved", "review-required", "unverified"]),
3977
+ reason: z8.string(),
3978
+ current: z8.union([issueSchema, advisorySchema]).optional(),
3867
3979
  review: reviewSummarySchema.optional()
3868
3980
  });
3869
3981
  }
@@ -3874,7 +3986,7 @@ import fs13 from "fs/promises";
3874
3986
  import path22 from "path";
3875
3987
  import { createHash as createHash3 } from "crypto";
3876
3988
  import fg7 from "fast-glob";
3877
- import { z as z8 } from "zod";
3989
+ import { z as z9 } from "zod";
3878
3990
  async function git3(root, ...args) {
3879
3991
  return profilePhase("automation.git", async () => (await execGit(args, { cwd: root, maxBuffer: 16 * 1024 * 1024, timeout: 1e4 })).stdout);
3880
3992
  }
@@ -4017,10 +4129,10 @@ var init_evidence = __esm({
4017
4129
  init_dead_command();
4018
4130
  init_storage();
4019
4131
  init_advisory_review();
4020
- engineVersion = true ? "0.19.1" : "development";
4132
+ engineVersion = true ? "0.19.2" : "development";
4021
4133
  hash = (value) => createHash3("sha256").update(JSON.stringify(value)).digest("hex");
4022
4134
  content = readAuditInput;
4023
- cacheSchema = z8.object({ version: z8.literal(1), entries: z8.record(z8.object({ key: z8.string(), result: checkResultSchema })), digest: z8.string() });
4135
+ cacheSchema = z9.object({ version: z9.literal(1), entries: z9.record(z9.object({ key: z9.string(), result: checkResultSchema })), digest: z9.string() });
4024
4136
  }
4025
4137
  });
4026
4138
 
@@ -4036,7 +4148,7 @@ __export(model_exports, {
4036
4148
  setupHostSchema: () => setupHostSchema,
4037
4149
  setupSchema: () => setupSchema
4038
4150
  });
4039
- import { z as z9 } from "zod";
4151
+ import { z as z10 } from "zod";
4040
4152
  import fs14 from "fs/promises";
4041
4153
  import path23 from "path";
4042
4154
  async function loadSetup(root) {
@@ -4083,29 +4195,29 @@ var init_model = __esm({
4083
4195
  "use strict";
4084
4196
  init_evidence();
4085
4197
  init_storage();
4086
- setupHostSchema = z9.object({
4087
- configuredVersion: z9.string(),
4088
- revision: z9.string().uuid(),
4089
- fingerprint: z9.string(),
4090
- mcpFingerprint: z9.string(),
4091
- instructions: z9.array(z9.string())
4198
+ setupHostSchema = z10.object({
4199
+ configuredVersion: z10.string(),
4200
+ revision: z10.string().uuid(),
4201
+ fingerprint: z10.string(),
4202
+ mcpFingerprint: z10.string(),
4203
+ instructions: z10.array(z10.string())
4092
4204
  });
4093
- setupSchema = z9.object({ version: z9.literal(2), hosts: z9.object({
4205
+ setupSchema = z10.object({ version: z10.literal(2), hosts: z10.object({
4094
4206
  codex: setupHostSchema.optional(),
4095
4207
  claude: setupHostSchema.optional()
4096
4208
  }) });
4097
4209
  SETUP_PATH = ".mason/local/setup.json";
4098
4210
  INACTIVE_HOSTS_PATH = ".mason/local/inactive-hosts.json";
4099
- inactiveHostsSchema = z9.object({ version: z9.literal(1), hosts: z9.array(z9.enum(["claude", "codex"])) });
4100
- receiptSchema = z9.object({
4101
- version: z9.literal(1),
4102
- host: z9.enum(["codex", "claude"]),
4103
- status: z9.enum(["installing", "configured"]),
4104
- initialReportPath: z9.string(),
4105
- initialBaselinePaths: z9.array(z9.string()),
4106
- root: z9.string(),
4107
- revision: z9.string().uuid(),
4108
- configuredAt: z9.string().optional()
4211
+ inactiveHostsSchema = z10.object({ version: z10.literal(1), hosts: z10.array(z10.enum(["claude", "codex"])) });
4212
+ receiptSchema = z10.object({
4213
+ version: z10.literal(1),
4214
+ host: z10.enum(["codex", "claude"]),
4215
+ status: z10.enum(["installing", "configured"]),
4216
+ initialReportPath: z10.string(),
4217
+ initialBaselinePaths: z10.array(z10.string()),
4218
+ root: z10.string(),
4219
+ revision: z10.string().uuid(),
4220
+ configuredAt: z10.string().optional()
4109
4221
  });
4110
4222
  }
4111
4223
  });
@@ -4143,7 +4255,7 @@ var init_launcher = __esm({
4143
4255
  "use strict";
4144
4256
  init_evidence();
4145
4257
  init_model();
4146
- executingVersion = () => true ? "0.19.1" : "development";
4258
+ executingVersion = () => true ? "0.19.2" : "development";
4147
4259
  mcpCommand = (host) => globalCommand(["--setup-host", host, "mcp"]);
4148
4260
  hookCommand = (host) => `mason --setup-host ${host} auto`;
4149
4261
  }
@@ -4340,7 +4452,7 @@ __export(execution_exports, {
4340
4452
  import os2 from "os";
4341
4453
  import fs19 from "fs/promises";
4342
4454
  import { randomUUID as randomUUID4 } from "crypto";
4343
- import { z as z12 } from "zod";
4455
+ import { z as z13 } from "zod";
4344
4456
  function parseExecution(raw) {
4345
4457
  try {
4346
4458
  return executionSchema.parse(raw);
@@ -4358,7 +4470,7 @@ function automationFailure(error) {
4358
4470
  codes.add(String(cause.code));
4359
4471
  cause = cause.cause;
4360
4472
  }
4361
- const code = codes.has("ENOSPC") || codes.has("EDQUOT") ? "storage-full" : /changed during|changed while|changed between/.test(message2) ? "inputs-changed" : /Automation is busy/.test(message2) ? "busy" : /not a git repository|unknown revision|bad revision|different history|unreachable/.test(message2) ? "history-unavailable" : error instanceof z12.ZodError || error instanceof SyntaxError || /Hook input|Expected one command|Unknown automation command|--host/.test(message2) ? "invalid-input" : /store|baseline|modified|symbolic link|Symlink|automation state|state belongs/.test(message2) ? "invalid-evidence" : [...codes].some((c) => /^E[A-Z]+$/.test(c)) ? "io-error" : "internal";
4473
+ const code = codes.has("ENOSPC") || codes.has("EDQUOT") ? "storage-full" : /changed during|changed while|changed between/.test(message2) ? "inputs-changed" : /Automation is busy/.test(message2) ? "busy" : /not a git repository|unknown revision|bad revision|different history|unreachable/.test(message2) ? "history-unavailable" : error instanceof z13.ZodError || error instanceof SyntaxError || /Hook input|Expected one command|Unknown automation command|--host/.test(message2) ? "invalid-input" : /store|baseline|modified|symbolic link|Symlink|automation state|state belongs/.test(message2) ? "invalid-evidence" : [...codes].some((c) => /^E[A-Z]+$/.test(c)) ? "io-error" : "internal";
4362
4474
  return { code, message: message2, retryable: ["inputs-changed", "storage-full", "busy", "io-error"].includes(code), receiptRecorded: false };
4363
4475
  }
4364
4476
  function failureMessage(error) {
@@ -4487,38 +4599,100 @@ var init_execution = __esm({
4487
4599
  init_store();
4488
4600
  init_evidence();
4489
4601
  init_storage();
4490
- failureSchema = z12.object({
4491
- code: z12.enum(["inputs-changed", "storage-full", "busy", "invalid-input", "history-unavailable", "invalid-evidence", "io-error", "internal"]),
4492
- message: z12.string(),
4493
- retryable: z12.boolean(),
4494
- receiptRecorded: z12.boolean()
4602
+ failureSchema = z13.object({
4603
+ code: z13.enum(["inputs-changed", "storage-full", "busy", "invalid-input", "history-unavailable", "invalid-evidence", "io-error", "internal"]),
4604
+ message: z13.string(),
4605
+ retryable: z13.boolean(),
4606
+ receiptRecorded: z13.boolean()
4495
4607
  });
4496
- attemptSchema = z12.object({
4497
- id: z12.string().regex(/^[a-zA-Z0-9_-]+$/),
4498
- lease: z12.literal(true).optional(),
4499
- event: z12.string(),
4500
- startedAt: z12.string(),
4501
- finishedAt: z12.string().optional(),
4502
- durationMs: z12.number().nonnegative().optional(),
4503
- pid: z12.number().int().positive(),
4504
- host: z12.string(),
4505
- status: z12.enum(["running", "completed", "failed", "unknown"]),
4506
- verificationStatus: z12.string().optional(),
4507
- reportPath: z12.string().optional(),
4608
+ attemptSchema = z13.object({
4609
+ id: z13.string().regex(/^[a-zA-Z0-9_-]+$/),
4610
+ lease: z13.literal(true).optional(),
4611
+ event: z13.string(),
4612
+ startedAt: z13.string(),
4613
+ finishedAt: z13.string().optional(),
4614
+ durationMs: z13.number().nonnegative().optional(),
4615
+ pid: z13.number().int().positive(),
4616
+ host: z13.string(),
4617
+ status: z13.enum(["running", "completed", "failed", "unknown"]),
4618
+ verificationStatus: z13.string().optional(),
4619
+ reportPath: z13.string().optional(),
4508
4620
  failure: failureSchema.optional()
4509
4621
  });
4510
- executionSchema = z12.object({ version: z12.literal(1), attempts: z12.array(attemptSchema).max(160), discardedAttempts: z12.number().int().nonnegative().default(0) });
4511
- notificationsSchema = z12.object({ version: z12.literal(1), sessions: z12.record(z12.object({
4512
- signature: z12.string(),
4622
+ executionSchema = z13.object({ version: z13.literal(1), attempts: z13.array(attemptSchema).max(160), discardedAttempts: z13.number().int().nonnegative().default(0) });
4623
+ notificationsSchema = z13.object({ version: z13.literal(1), sessions: z13.record(z13.object({
4624
+ signature: z13.string(),
4513
4625
  code: failureSchema.shape.code,
4514
- message: z12.string(),
4515
- count: z12.number().int().positive(),
4516
- lastAt: z12.string()
4626
+ message: z13.string(),
4627
+ count: z13.number().int().positive(),
4628
+ lastAt: z13.string()
4517
4629
  })) });
4518
4630
  leasePath = (directory, id) => `${directory}/executions/${id}.json`;
4519
4631
  }
4520
4632
  });
4521
4633
 
4634
+ // src/decisions/pending.ts
4635
+ var pending_exports = {};
4636
+ __export(pending_exports, {
4637
+ pendingKnowledge: () => pendingKnowledge
4638
+ });
4639
+ async function pendingKnowledge(root, now = Date.now()) {
4640
+ try {
4641
+ const [store, status] = await Promise.all([
4642
+ loadDecisionStore(root),
4643
+ execGit(
4644
+ ["status", "--porcelain=v1", "-z", "--untracked-files=all", "--", ".mason/decisions"],
4645
+ { cwd: root, timeout: 1e4, maxBuffer: 1024 * 1024 }
4646
+ )
4647
+ ]);
4648
+ const changed = /* @__PURE__ */ new Set();
4649
+ const entries = status.stdout.split("\0");
4650
+ for (let i = 0; i < entries.length; i++) {
4651
+ const entry = entries[i];
4652
+ if (!entry) continue;
4653
+ const file = entry.slice(3);
4654
+ if (/^\.mason\/decisions\/[^/]+\.json$/.test(file)) changed.add(file);
4655
+ if (/[RC]/.test(entry.slice(0, 2))) i++;
4656
+ }
4657
+ const records = new Map(store.records.map((record) => [`.mason/decisions/${record.id}.json`, record]));
4658
+ const age = (at) => {
4659
+ const time = at ? Date.parse(at) : NaN;
4660
+ return Number.isFinite(time) ? `${Math.max(0, Math.floor((now - time) / 864e5))} days` : "unknown age";
4661
+ };
4662
+ const safe = (value) => value.replace(/[\u0000-\u001f\u007f-\u009f]/g, " ");
4663
+ const list = (items) => items.slice(0, 5).map(safe).join("; ") + (items.length > 5 ? `; ${items.length - 5} more` : "");
4664
+ const proposals = store.records.filter((record) => record.status === "active" && decisionApproval(record) === "proposed").map((record) => {
4665
+ const history = record.version === 2 ? record.history : [];
4666
+ let lastReview = -1;
4667
+ history.forEach((event, index2) => {
4668
+ if (event.kind === "accepted" || event.kind === "reaffirmed") lastReview = index2;
4669
+ });
4670
+ const since = history.slice(lastReview + 1).find((event) => event.kind === "created" || event.kind === "revised")?.at ?? record.createdAt;
4671
+ return { record, since };
4672
+ }).sort((a, b) => {
4673
+ const timestamp = (value) => Number.isFinite(Date.parse(value)) ? Date.parse(value) : Infinity;
4674
+ return timestamp(a.since) - timestamp(b.since) || a.record.id.localeCompare(b.record.id);
4675
+ });
4676
+ const lines = [];
4677
+ if (changed.size) lines.push(`${changed.size} uncommitted decision record(s): ${list([...changed].sort().map((file) => `${file} (saved-record age: ${age(records.get(file)?.updatedAt)})`))}.`);
4678
+ if (proposals.length) lines.push(`${proposals.length} proposal(s) awaiting review (oldest first): ${list(proposals.map(({ record, since }) => `.mason/decisions/${record.id}.json (${age(since)})`))}.`);
4679
+ if (store.diagnostics.length) lines.push(`${store.diagnostics.length} decision record(s) could not be read; pending knowledge summary is incomplete.`);
4680
+ return lines.length ? `Mason pending knowledge:
4681
+ ${lines.join("\n")}
4682
+ Informational only; review and commit through the normal project workflow. No approval is implied.` : null;
4683
+ } catch {
4684
+ return "Mason pending knowledge: unavailable; decision records or Git status could not be read.";
4685
+ }
4686
+ }
4687
+ var init_pending = __esm({
4688
+ "src/decisions/pending.ts"() {
4689
+ "use strict";
4690
+ init_git_read();
4691
+ init_decisions();
4692
+ init_provenance();
4693
+ }
4694
+ });
4695
+
4522
4696
  // src/automation/runtime.ts
4523
4697
  var runtime_exports = {};
4524
4698
  __export(runtime_exports, {
@@ -4528,7 +4702,7 @@ __export(runtime_exports, {
4528
4702
  summarize: () => summarize
4529
4703
  });
4530
4704
  import { randomUUID as randomUUID5 } from "crypto";
4531
- import { z as z13 } from "zod";
4705
+ import { z as z14 } from "zod";
4532
4706
  async function observeReadOnlyTool(dir, event) {
4533
4707
  const ws = await profilePhase("automation.workspace", () => workspace(dir));
4534
4708
  await withLock(ws.root, ws.directory, async () => {
@@ -4750,6 +4924,19 @@ async function automate(dir, event) {
4750
4924
  const state = await loadState(ws);
4751
4925
  if (state.analysis?.id !== result.analysis.id) throw new Error("Automation evidence changed during publication; retry against the current inputs.");
4752
4926
  const report = structuredClone(result.analysis.report);
4927
+ if (!state.notifications) {
4928
+ let baseline = { findings: report.findings, head: report.head };
4929
+ if (state.latest) {
4930
+ try {
4931
+ const previous = z14.object({ findings: z14.array(repairFindingSchema), head: z14.string() }).safeParse(await readStoreJson(ws.root, state.latest));
4932
+ if (!previous.success) throw new Error("Unsupported saved report");
4933
+ baseline = previous.data;
4934
+ } catch {
4935
+ report.diagnostics.push("The saved notification baseline could not be read; initialized from the current audit. Original repair evidence is retained. Run an explicit check for all outstanding findings.");
4936
+ }
4937
+ }
4938
+ state.notifications = createNotificationState(baseline.findings, baseline.head);
4939
+ }
4753
4940
  if (result.shared) report.checks = { ...report.checks, ran: [], reused: [.../* @__PURE__ */ new Set([...report.checks.ran, ...report.checks.reused])], shared: true };
4754
4941
  report.reportPath = ws.directory + "/checks/" + randomUUID5() + ".json";
4755
4942
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -4762,12 +4949,8 @@ async function automate(dir, event) {
4762
4949
  if (Object.keys(state.sessions).length >= 128) throw new Error("Too many sessions with unfinished tool calls; original evidence was retained.");
4763
4950
  state.sessions[key] = {
4764
4951
  host: event.host,
4765
- seen: null,
4766
- continued: false,
4767
- initialIssues: report.findings.filter((f) => f.status === "unresolved").map((f) => f.id),
4768
- initialDocs: inputs.docs,
4952
+ notifiedDiagnostics: [],
4769
4953
  lastUsed: now,
4770
- mutationObserved: false,
4771
4954
  pending: {},
4772
4955
  coverageGaps: [],
4773
4956
  events: {}
@@ -4782,7 +4965,6 @@ async function automate(dir, event) {
4782
4965
  session.pending[event.toolId] = inputs.fingerprint;
4783
4966
  }
4784
4967
  if (event.event === "after_tool" && event.mutating) {
4785
- session.mutationObserved = true;
4786
4968
  if (!event.toolId || !session.pending[event.toolId]) {
4787
4969
  const gap = "A tool completed without an observed matching pre-tool capture in this checkout; pre-edit coverage is unknown. The tool may have changed checkouts; captures are kept separately for each worktree.";
4788
4970
  if (!session.coverageGaps.includes(gap)) session.coverageGaps.push(gap);
@@ -4793,31 +4975,39 @@ async function automate(dir, event) {
4793
4975
  if (session.coverageGaps.length && report.status !== "unavailable") report.status = "incomplete";
4794
4976
  report.capture = report.status !== "unavailable" && !session.coverageGaps.length && (session.events.session_start || session.events.before_tool) ? "observed" : "unknown";
4795
4977
  }
4796
- const signature = hash([report.status, report.findings, report.diagnostics, report.checks.skipped]);
4797
- const relevant = report.findings.some((f) => f.status === "unresolved" && session && (!session.initialIssues.includes(f.id) || session.initialDocs[f.original.anchor.doc] !== inputs.docs[f.original.anchor.doc]));
4798
- const continueOnce = event.event === "task_end" && !!session?.mutationObserved && relevant && !session.continued && !event.stopHookActive;
4799
- const notify = !session || newSession || signature !== session.seen || continueOnce;
4800
- if (session) {
4801
- session.seen = signature;
4802
- if (continueOnce) session.continued = true;
4803
- }
4804
- const persistReport = !state.latest || state.fingerprint !== inputs.fingerprint || notify || event.event === "task_end";
4978
+ const selected = session && event.event === "task_end" ? completionFindings(state.notifications, report.findings) : [];
4979
+ state.agentNotifications ??= { ...structuredClone(state.notifications), delivered: {} };
4980
+ const feedback = session && ["turn_start", "after_tool"].includes(event.event) ? completionFindings(state.agentNotifications, report.findings) : [];
4981
+ const diagnostics = session ? report.diagnostics.filter((d) => !session.notifiedDiagnostics.includes(d)).slice(0, 2) : [];
4982
+ if (session) session.notifiedDiagnostics = [.../* @__PURE__ */ new Set([
4983
+ ...session.notifiedDiagnostics.filter((d) => report.diagnostics.includes(d)),
4984
+ ...diagnostics
4985
+ ])];
4986
+ const message2 = !session ? summarize(report) : [
4987
+ completionSummary(selected, report.reportPath),
4988
+ completionSummary(feedback, report.reportPath),
4989
+ ...diagnostics.length ? [`Mason: verification limited. ${diagnostics.slice(0, 2).map(cleanText).join(" ")} Evidence: ${report.reportPath}.`] : []
4990
+ ].filter(Boolean).join("\n") || null;
4991
+ const notify = message2 !== null;
4992
+ const persistReport = !state.latest || state.fingerprint !== inputs.fingerprint || notify || newSession || event.event === "task_end";
4805
4993
  if (!persistReport) report.reportPath = state.latest;
4806
4994
  state.updatedAt = now;
4807
4995
  state.fingerprint = inputs.fingerprint;
4808
4996
  state.latest = report.reportPath;
4809
4997
  if (persistReport) await writeStoreJson(ws.root, report.reportPath, report);
4810
4998
  await writeStoreJson(ws.root, ws.directory + "/state.json", state);
4811
- return { report, message: notify ? summarize(report) : null, continueOnce };
4999
+ return { report, message: message2 };
4812
5000
  }));
4813
5001
  }));
4814
5002
  }
4815
5003
  async function automationStatus(dir) {
5004
+ const { pendingKnowledge: pendingKnowledge2 } = await Promise.resolve().then(() => (init_pending(), pending_exports));
4816
5005
  const ws = await profilePhase("automation.workspace", () => workspace(dir));
5006
+ const knowledge = await pendingKnowledge2(ws.root);
4817
5007
  const execution = await executionStatus(ws.root, ws.directory);
4818
5008
  const notifications = await failureNotifications(ws.root, ws.directory);
4819
5009
  const raw = await readStoreJson(ws.root, ws.directory + "/state.json");
4820
- if (raw === null) return { version: 1, status: execution.status === "not-observed" ? "not-observed" : "unavailable", root: ws.root, branch: ws.branch, baselinePaths: [], hosts: {}, execution, notifications };
5010
+ if (raw === null) return { version: 1, status: execution.status === "not-observed" ? "not-observed" : "unavailable", root: ws.root, branch: ws.branch, baselinePaths: [], hosts: {}, execution, notifications, knowledge };
4821
5011
  const state = parseState(raw);
4822
5012
  if (state.root !== ws.root || state.gitDir !== ws.gitDir || state.branch !== ws.branch) throw new Error("Automation state belongs to another workspace.");
4823
5013
  const inputs = await readInputs(ws.root);
@@ -4840,6 +5030,8 @@ async function automationStatus(dir) {
4840
5030
  hosts,
4841
5031
  execution,
4842
5032
  notifications,
5033
+ knowledge,
5034
+ notificationBaselineHead: state.notifications?.baselineHead ?? null,
4843
5035
  note: "Observed events do not prove all tool paths are intercepted. Run check to verify the retained evidence."
4844
5036
  };
4845
5037
  }
@@ -4847,6 +5039,7 @@ var SCOPE, priority, cleanText, analysisSchema, sharedSchema, baselineDigest;
4847
5039
  var init_runtime = __esm({
4848
5040
  "src/automation/runtime.ts"() {
4849
5041
  "use strict";
5042
+ init_notifications();
4850
5043
  init_profile();
4851
5044
  init_inspection();
4852
5045
  init_execution();
@@ -4858,34 +5051,34 @@ var init_runtime = __esm({
4858
5051
  SCOPE = "Documentation audit evidence only. Hook receipts show observed events, not complete interception. Resolved findings no longer fail their checks or have a recorded assessment covering current evidence. Repair and review only within the user's task authorization.";
4859
5052
  priority = { resolved: 0, "review-required": 1, unresolved: 2, unverified: 3 };
4860
5053
  cleanText = (text2) => text2.replace(/[\u0000-\u001f\u007f-\u009f]/g, " ").slice(0, 250);
4861
- analysisSchema = z13.object({
4862
- id: z13.string(),
4863
- fingerprint: z13.string(),
4864
- completedAt: z13.number(),
4865
- baselineDigest: z13.string(),
5054
+ analysisSchema = z14.object({
5055
+ id: z14.string(),
5056
+ fingerprint: z14.string(),
5057
+ completedAt: z14.number(),
5058
+ baselineDigest: z14.string(),
4866
5059
  currentAudit: reportSchema.nullable().optional(),
4867
- report: z13.object({
4868
- version: z13.literal(1),
4869
- status: z13.enum(["verified", "issues-remain", "incomplete", "unavailable"]),
4870
- root: z13.string(),
4871
- branch: z13.string(),
4872
- head: z13.string(),
4873
- baselinePaths: z13.array(z13.string()),
4874
- reportPath: z13.string(),
4875
- findings: z13.array(repairFindingSchema),
4876
- diagnostics: z13.array(z13.string()),
4877
- checks: z13.object({
4878
- ran: z13.array(z13.string()),
4879
- reused: z13.array(z13.string()),
4880
- skipped: z13.array(z13.object({ check: z13.string(), reason: z13.string(), doc: z13.string().optional() })),
4881
- auditReused: z13.boolean().optional()
5060
+ report: z14.object({
5061
+ version: z14.literal(1),
5062
+ status: z14.enum(["verified", "issues-remain", "incomplete", "unavailable"]),
5063
+ root: z14.string(),
5064
+ branch: z14.string(),
5065
+ head: z14.string(),
5066
+ baselinePaths: z14.array(z14.string()),
5067
+ reportPath: z14.string(),
5068
+ findings: z14.array(repairFindingSchema),
5069
+ diagnostics: z14.array(z14.string()),
5070
+ checks: z14.object({
5071
+ ran: z14.array(z14.string()),
5072
+ reused: z14.array(z14.string()),
5073
+ skipped: z14.array(z14.object({ check: z14.string(), reason: z14.string(), doc: z14.string().optional() })),
5074
+ auditReused: z14.boolean().optional()
4882
5075
  }),
4883
- counts: z13.object({ resolved: z13.number(), unresolved: z13.number(), "review-required": z13.number(), unverified: z13.number() }),
4884
- capture: z13.enum(["observed", "unknown"]),
4885
- scope: z13.string()
5076
+ counts: z14.object({ resolved: z14.number(), unresolved: z14.number(), "review-required": z14.number(), unverified: z14.number() }),
5077
+ capture: z14.enum(["observed", "unknown"]),
5078
+ scope: z14.string()
4886
5079
  })
4887
5080
  });
4888
- sharedSchema = z13.object({ analysis: analysisSchema, digest: z13.string() });
5081
+ sharedSchema = z14.object({ analysis: analysisSchema, digest: z14.string() });
4889
5082
  baselineDigest = async (ws, state) => profilePhase("automation.baselines", async () => hash(await Promise.all(
4890
5083
  state.baselines.map(async (b) => [b.path, await readStoreJson(ws.root, b.path)])
4891
5084
  )));
@@ -4903,7 +5096,7 @@ var init_hook_root = __esm({
4903
5096
  });
4904
5097
 
4905
5098
  // src/automation/adapters.ts
4906
- import { z as z14 } from "zod";
5099
+ import { z as z15 } from "zod";
4907
5100
  function managedHookCommand(host, platform = process.platform) {
4908
5101
  const command = `mason --setup-host ${host} auto hook --host ${host}`;
4909
5102
  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`;
@@ -4929,15 +5122,15 @@ var init_adapters = __esm({
4929
5122
  init_runtime();
4930
5123
  init_store();
4931
5124
  init_hook_root();
4932
- inputSchema = z14.object({
4933
- cwd: z14.string().min(1),
4934
- session_id: z14.string().min(1).max(500),
4935
- hook_event_name: z14.enum(["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "Stop"]),
4936
- tool_name: z14.string().optional(),
4937
- tool_use_id: z14.string().max(500).optional(),
4938
- tool_input: z14.unknown().optional(),
4939
- stop_hook_active: z14.boolean().optional(),
4940
- permission_mode: z14.string().optional()
5125
+ inputSchema = z15.object({
5126
+ cwd: z15.string().min(1),
5127
+ session_id: z15.string().min(1).max(500),
5128
+ hook_event_name: z15.enum(["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "Stop"]),
5129
+ tool_name: z15.string().optional(),
5130
+ tool_use_id: z15.string().max(500).optional(),
5131
+ tool_input: z15.unknown().optional(),
5132
+ stop_hook_active: z15.boolean().optional(),
5133
+ permission_mode: z15.string().optional()
4941
5134
  });
4942
5135
  HOOK_EVENTS = ["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "Stop"];
4943
5136
  }
@@ -4953,7 +5146,7 @@ __export(ownership_exports, {
4953
5146
  ownershipEdit: () => ownershipEdit,
4954
5147
  ownershipSchema: () => ownershipSchema
4955
5148
  });
4956
- import { z as z15 } from "zod";
5149
+ import { z as z16 } from "zod";
4957
5150
  import { parse } from "smol-toml";
4958
5151
  function blockSpan(text2, start, end) {
4959
5152
  const starts = text2.split(start).length - 1, ends = text2.split(end).length - 1;
@@ -5004,15 +5197,15 @@ var init_ownership = __esm({
5004
5197
  ["<!-- mason:agents:start -->", "<!-- mason:agents:end -->"],
5005
5198
  ["# mason:mcp:start", "# mason:mcp:end"]
5006
5199
  ];
5007
- fileSchema = z15.object({
5008
- created: z15.boolean(),
5009
- initialHash: z15.string(),
5010
- blocks: z15.record(z15.string()),
5011
- mcpHash: z15.string().optional(),
5012
- hookCommands: z15.array(z15.string()).optional(),
5013
- hooks: z15.record(z15.array(z15.string())).optional()
5200
+ fileSchema = z16.object({
5201
+ created: z16.boolean(),
5202
+ initialHash: z16.string(),
5203
+ blocks: z16.record(z16.string()),
5204
+ mcpHash: z16.string().optional(),
5205
+ hookCommands: z16.array(z16.string()).optional(),
5206
+ hooks: z16.record(z16.array(z16.string())).optional()
5014
5207
  });
5015
- ownershipSchema = z15.object({ version: z15.literal(1), files: z15.record(fileSchema) });
5208
+ ownershipSchema = z16.object({ version: z16.literal(1), files: z16.record(fileSchema) });
5016
5209
  }
5017
5210
  });
5018
5211
 
@@ -5027,7 +5220,7 @@ __export(install_exports, {
5027
5220
  planAutomationInstall: () => planAutomationInstall,
5028
5221
  recordSchema: () => recordSchema2
5029
5222
  });
5030
- import { z as z16 } from "zod";
5223
+ import { z as z17 } from "zod";
5031
5224
  async function installationRecord(root) {
5032
5225
  return recordSchema2.parse(await readStoreJson(root, AUTOMATION_PATH) ?? { version: 1, hosts: {} });
5033
5226
  }
@@ -5108,9 +5301,9 @@ var init_install = __esm({
5108
5301
  init_store();
5109
5302
  init_files2();
5110
5303
  init_ownership();
5111
- groupSchema = z16.object({ hooks: z16.array(z16.object({ type: z16.string(), command: z16.string().optional() }).passthrough()) }).passthrough();
5112
- automationConfigSchema = z16.object({ hooks: z16.record(z16.array(groupSchema)).optional() }).passthrough();
5113
- recordSchema2 = z16.object({ version: z16.literal(1), hosts: z16.record(z16.object({ command: z16.string() })) });
5304
+ groupSchema = z17.object({ hooks: z17.array(z17.object({ type: z17.string(), command: z17.string().optional() }).passthrough()) }).passthrough();
5305
+ automationConfigSchema = z17.object({ hooks: z17.record(z17.array(groupSchema)).optional() }).passthrough();
5306
+ recordSchema2 = z17.object({ version: z17.literal(1), hosts: z17.record(z17.object({ command: z17.string() })) });
5114
5307
  AUTOMATION_PATH = ".mason/local/automation.json";
5115
5308
  configPath = (host) => host === "claude" ? ".claude/settings.json" : ".codex/hooks.json";
5116
5309
  }
@@ -5128,7 +5321,7 @@ __export(config_exports, {
5128
5321
  });
5129
5322
  import { isDeepStrictEqual } from "util";
5130
5323
  import { parse as parse2, stringify } from "smol-toml";
5131
- import { z as z17 } from "zod";
5324
+ import { z as z18 } from "zod";
5132
5325
  async function instructionEdits(root, host) {
5133
5326
  const found = await Promise.all(DOC_CANDIDATES.map(async (file) => ({ path: file, text: await readText(root, file) })));
5134
5327
  const existing = found.find((f) => f.text !== null);
@@ -5239,7 +5432,7 @@ var init_config = __esm({
5239
5432
  init_install();
5240
5433
  init_files2();
5241
5434
  init_launcher();
5242
- object = z17.record(z17.unknown());
5435
+ object = z18.record(z18.unknown());
5243
5436
  mcpPath = (host) => host === "codex" ? ".codex/config.toml" : ".mcp.json";
5244
5437
  TOML_START = "# mason:mcp:start";
5245
5438
  TOML_END = "# mason:mcp:end";
@@ -5249,7 +5442,7 @@ var init_config = __esm({
5249
5442
  });
5250
5443
 
5251
5444
  // src/mcp/init.ts
5252
- import { z as z18 } from "zod";
5445
+ import { z as z19 } from "zod";
5253
5446
  async function loadProjectMarker(rootDir) {
5254
5447
  const raw = await readStoreJson(rootDir, ".mason/local/project.json");
5255
5448
  if (raw === null) return null;
@@ -5257,8 +5450,8 @@ async function loadProjectMarker(rootDir) {
5257
5450
  return { ...marker, features: await loadProjectFeatures(rootDir) };
5258
5451
  }
5259
5452
  async function loadProjectFeatures(rootDir) {
5260
- const config = z18.record(z18.unknown()).parse(await readStoreJson(rootDir, ".mason/config.json") ?? {});
5261
- return z18.object({ confluence: z18.boolean().optional() }).passthrough().parse(config.features ?? {});
5453
+ const config = z19.record(z19.unknown()).parse(await readStoreJson(rootDir, ".mason/config.json") ?? {});
5454
+ return z19.object({ confluence: z19.boolean().optional() }).passthrough().parse(config.features ?? {});
5262
5455
  }
5263
5456
  async function saveProjectMarker(rootDir, marker) {
5264
5457
  const { ancillaryEdits: ancillaryEdits2 } = await Promise.resolve().then(() => (init_config(), config_exports));
@@ -5270,8 +5463,8 @@ async function projectMarkerEdits(rootDir, marker) {
5270
5463
  const edits = [{ path: file, before: await readText(rootDir, file), after: JSON.stringify(local, null, 2) + "\n" }];
5271
5464
  if (features && Object.keys(features).length) {
5272
5465
  const configPath2 = ".mason/config.json", before = await readText(rootDir, configPath2);
5273
- const config = z18.record(z18.unknown()).parse(JSON.parse(before ?? "{}"));
5274
- config.features = { ...z18.record(z18.unknown()).parse(config.features ?? {}), ...features };
5466
+ const config = z19.record(z19.unknown()).parse(JSON.parse(before ?? "{}"));
5467
+ config.features = { ...z19.record(z19.unknown()).parse(config.features ?? {}), ...features };
5275
5468
  edits.push({ path: configPath2, before, after: JSON.stringify(config, null, 2) + "\n" });
5276
5469
  }
5277
5470
  return edits;
@@ -5285,10 +5478,10 @@ var init_init = __esm({
5285
5478
  "use strict";
5286
5479
  init_storage();
5287
5480
  init_files2();
5288
- markerSchema = z18.object({
5289
- version: z18.literal(1),
5290
- initializedAt: z18.string(),
5291
- features: z18.object({ confluence: z18.boolean().optional() }).passthrough().optional()
5481
+ markerSchema = z19.object({
5482
+ version: z19.literal(1),
5483
+ initializedAt: z19.string(),
5484
+ features: z19.object({ confluence: z19.boolean().optional() }).passthrough().optional()
5292
5485
  }).passthrough();
5293
5486
  CLAUDE_MD_SECTION = `<!-- mason:start -->
5294
5487
  ## Mason project knowledge
@@ -5297,9 +5490,10 @@ Mason provides recorded decisions and file impact over MCP. A concept map is opt
5297
5490
 
5298
5491
  - 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.
5299
5492
  - Before editing a file \u2192 \`get_impact\` to check references, tests, and historical change partners.
5300
- - 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.
5493
+ - Investigation or correction reveals a reusable project lesson \u2192 \`save_decision\` as soon as the lesson is supported by evidence; do not wait for implementation, task completion, or merge. Record the established observation now and identify unimplemented remedies as tentative. 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.
5301
5494
  - Before saving, compare the lesson with matching \`get_context\` records and any pending proposal. If new evidence changes the same lesson's assumptions, scope, or recommended action, revise it with \`save_decision(id: existingId)\`, including when it is accepted. Preserve supported rationale and sources, replace obsolete instructions, and add the new evidence. Create a separate record for a genuinely distinct lesson; skip unchanged restatements. Revision preserves history and leaves the accepted version operative until authorized review.
5302
5495
  - Consult trust metadata before relying on entries: unknown or changed freshness requires inspection, stale or unknown map verification requires a fresh evidence review, and a recorded failed verdict 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.
5496
+ - Asked to review pending project knowledge \u2192 \`mason_automation(action: "status")\` lists pending proposals and uncommitted records. For ordinary tasks, inspect matching proposals through \`get_context\`; do not request acceptance merely because a proposal exists.
5303
5497
  - 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.
5304
5498
  - 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.
5305
5499
  - \`mason_init\` returns documentation audit and committed-diff review results, plus a short setup guide. Pass \`evidence\` with local CI manifest paths to include test and analysis results; the CLI equivalent is \`mason-review --evidence <manifest>\`. State skipped, unavailable, stale, or unknown checks explicitly. Related accepted decisions identify review context, not proven violations.
@@ -5323,7 +5517,7 @@ ${CLAUDE_MD_SECTION}`;
5323
5517
 
5324
5518
  1. Explain the actionable findings with their source evidence. Separate audit issues, advisories, and skipped checks. The review covers committed changes from the merge base to HEAD; workingTree paths are not included in that review. An unavailable or empty check is not proof that the project is correct. Use the CLI for full output if a summary is truncated.
5325
5519
  2. Address findings within the user's requested scope. Setup alone authorizes Mason host configuration, hooks, instructions, and ignore rules; rewriting existing project claims requires repair scope. If repair is authorized, call \`mason_repair(dir, action: "prepare")\` before editing; it saves the full original findings even when this summary is truncated. Inspect relevant source, make grounded edits, then call \`mason_repair(dir, action: "verify", baselinePath)\` with that same baseline, including after any final doc commit. Report resolved, unresolved, review-required, unverified, and new findings. Keep suppressed advisories visible even when setup has already dirtied a doc. Do not invent a decision just to populate the store.
5326
- 3. When the task reveals a real lesson or constraint, compare matching \`get_context\` records first. If its assumptions, scope, or recommended action changed, revise that record using \`save_decision(id: existingId)\`; create a separate record for a genuinely distinct lesson and skip unchanged restatements. Include title, body, category, anchors, and known owner/source/actor information. Preserve supported rationale and sources, replace obsolete instructions, and add new evidence. Missing attribution can be added later. The tool writes a local proposal; editing it preserves revision history and requires a new acceptance, while any earlier accepted revision remains operative. An unchanged save never refreshes its evidence. When decision review is requested, \`review_decision\` prepares the record and code evidence before any authorized verdict. Review and commit records through the normal workflow. Retrieve it on the next relevant task with \`get_context(dir, task, files)\`.
5520
+ 3. Capture a supported lesson as soon as it is established; do not wait for implementation, task completion, or merge. Keep unimplemented remedies tentative. When the task reveals a real lesson or constraint, compare matching \`get_context\` records first. If its assumptions, scope, or recommended action changed, revise that record using \`save_decision(id: existingId)\`; create a separate record for a genuinely distinct lesson and skip unchanged restatements. Include title, body, category, anchors, and known owner/source/actor information. Preserve supported rationale and sources, replace obsolete instructions, and add new evidence. Missing attribution can be added later. The tool writes a local proposal; editing it preserves revision history and requires a new acceptance, while any earlier accepted revision remains operative. An unchanged save never refreshes its evidence. When decision review is requested, \`review_decision\` prepares the record and code evidence before any authorized verdict. Review and commit records through the normal workflow. Retrieve it on the next relevant task with \`get_context(dir, task, files)\`.
5327
5521
 
5328
5522
  ${ASSISTANT_SETUP}
5329
5523
 
@@ -5486,7 +5680,7 @@ var init_types2 = __esm({
5486
5680
  });
5487
5681
 
5488
5682
  // src/review/evidence/vitest.ts
5489
- import { z as z19 } from "zod";
5683
+ import { z as z20 } from "zod";
5490
5684
  function parseVitest(raw, sourceRoot) {
5491
5685
  const report = schema.parse(raw);
5492
5686
  const findings = [], diagnostics = [];
@@ -5535,24 +5729,24 @@ var init_vitest = __esm({
5535
5729
  "use strict";
5536
5730
  init_paths2();
5537
5731
  init_types2();
5538
- count3 = z19.number().int().nonnegative();
5539
- schema = z19.object({
5540
- success: z19.boolean(),
5732
+ count3 = z20.number().int().nonnegative();
5733
+ schema = z20.object({
5734
+ success: z20.boolean(),
5541
5735
  numTotalTests: count3,
5542
5736
  numPassedTests: count3,
5543
5737
  numFailedTests: count3,
5544
5738
  numPendingTests: count3,
5545
5739
  numTodoTests: count3,
5546
5740
  numFailedTestSuites: count3,
5547
- testResults: z19.array(z19.object({
5548
- name: z19.string().min(1),
5549
- status: z19.enum(["passed", "failed"]),
5550
- message: z19.string().optional(),
5551
- assertionResults: z19.array(z19.object({
5552
- fullName: z19.string(),
5553
- status: z19.enum(["passed", "failed", "pending", "skipped", "todo", "disabled"]),
5554
- failureMessages: z19.array(z19.string()).nullable().optional(),
5555
- location: z19.object({ line: count3, column: count3 }).nullable().optional()
5741
+ testResults: z20.array(z20.object({
5742
+ name: z20.string().min(1),
5743
+ status: z20.enum(["passed", "failed"]),
5744
+ message: z20.string().optional(),
5745
+ assertionResults: z20.array(z20.object({
5746
+ fullName: z20.string(),
5747
+ status: z20.enum(["passed", "failed", "pending", "skipped", "todo", "disabled"]),
5748
+ failureMessages: z20.array(z20.string()).nullable().optional(),
5749
+ location: z20.object({ line: count3, column: count3 }).nullable().optional()
5556
5750
  }))
5557
5751
  }))
5558
5752
  });
@@ -5560,7 +5754,7 @@ var init_vitest = __esm({
5560
5754
  });
5561
5755
 
5562
5756
  // src/review/evidence/sarif.ts
5563
- import { z as z20 } from "zod";
5757
+ import { z as z21 } from "zod";
5564
5758
  function parseSarif(raw, sourceRoot) {
5565
5759
  const report = schema2.parse(raw), findings = [], diagnostics = [];
5566
5760
  let active = 0, suppressed = 0, absent = 0, unresolved = 0;
@@ -5651,29 +5845,29 @@ var init_sarif = __esm({
5651
5845
  "use strict";
5652
5846
  init_paths2();
5653
5847
  init_types2();
5654
- index = z20.number().int().nonnegative();
5655
- artifact = z20.object({ uri: z20.string().optional(), uriBaseId: z20.string().optional(), index: index.optional() });
5656
- message = z20.object({ text: z20.string().optional(), markdown: z20.string().optional(), id: z20.string().optional(), arguments: z20.array(z20.string()).optional() });
5657
- level = z20.enum(["error", "warning", "note", "none"]);
5658
- location = z20.object({ physicalLocation: z20.object({ artifactLocation: artifact.optional(), region: z20.object({ startLine: index.optional(), startColumn: index.optional() }).optional() }).optional() });
5659
- schema2 = z20.object({
5660
- version: z20.literal("2.1.0"),
5661
- runs: z20.array(z20.object({
5662
- tool: z20.object({ driver: z20.object({ name: z20.string().min(1), rules: z20.array(z20.object({ id: z20.string(), defaultConfiguration: z20.object({ level: level.optional() }).optional(), messageStrings: z20.record(message).optional() })).optional(), globalMessageStrings: z20.record(message).optional() }) }),
5663
- invocations: z20.array(z20.object({ executionSuccessful: z20.boolean(), commandLine: z20.string().optional(), toolExecutionNotifications: z20.array(z20.object({ level: level.optional(), message })).optional() })).optional(),
5664
- versionControlProvenance: z20.array(z20.object({ revisionId: z20.string().optional() })).optional(),
5665
- originalUriBaseIds: z20.record(artifact).optional(),
5666
- artifacts: z20.array(z20.object({ location: artifact.optional() })).optional(),
5667
- results: z20.array(z20.object({
5668
- ruleId: z20.string().optional(),
5848
+ index = z21.number().int().nonnegative();
5849
+ artifact = z21.object({ uri: z21.string().optional(), uriBaseId: z21.string().optional(), index: index.optional() });
5850
+ message = z21.object({ text: z21.string().optional(), markdown: z21.string().optional(), id: z21.string().optional(), arguments: z21.array(z21.string()).optional() });
5851
+ level = z21.enum(["error", "warning", "note", "none"]);
5852
+ location = z21.object({ physicalLocation: z21.object({ artifactLocation: artifact.optional(), region: z21.object({ startLine: index.optional(), startColumn: index.optional() }).optional() }).optional() });
5853
+ schema2 = z21.object({
5854
+ version: z21.literal("2.1.0"),
5855
+ runs: z21.array(z21.object({
5856
+ tool: z21.object({ driver: z21.object({ name: z21.string().min(1), rules: z21.array(z21.object({ id: z21.string(), defaultConfiguration: z21.object({ level: level.optional() }).optional(), messageStrings: z21.record(message).optional() })).optional(), globalMessageStrings: z21.record(message).optional() }) }),
5857
+ invocations: z21.array(z21.object({ executionSuccessful: z21.boolean(), commandLine: z21.string().optional(), toolExecutionNotifications: z21.array(z21.object({ level: level.optional(), message })).optional() })).optional(),
5858
+ versionControlProvenance: z21.array(z21.object({ revisionId: z21.string().optional() })).optional(),
5859
+ originalUriBaseIds: z21.record(artifact).optional(),
5860
+ artifacts: z21.array(z21.object({ location: artifact.optional() })).optional(),
5861
+ results: z21.array(z21.object({
5862
+ ruleId: z21.string().optional(),
5669
5863
  ruleIndex: index.optional(),
5670
5864
  message,
5671
5865
  level: level.optional(),
5672
- kind: z20.enum(["fail", "pass", "open", "informational", "notApplicable", "review"]).optional(),
5673
- baselineState: z20.enum(["new", "unchanged", "updated", "absent"]).optional(),
5674
- suppressions: z20.array(z20.object({ kind: z20.enum(["inSource", "external"]), status: z20.enum(["accepted", "underReview", "rejected"]).optional() })).nullable().optional(),
5675
- locations: z20.array(location).optional(),
5676
- relatedLocations: z20.array(location).optional()
5866
+ kind: z21.enum(["fail", "pass", "open", "informational", "notApplicable", "review"]).optional(),
5867
+ baselineState: z21.enum(["new", "unchanged", "updated", "absent"]).optional(),
5868
+ suppressions: z21.array(z21.object({ kind: z21.enum(["inSource", "external"]), status: z21.enum(["accepted", "underReview", "rejected"]).optional() })).nullable().optional(),
5869
+ locations: z21.array(location).optional(),
5870
+ relatedLocations: z21.array(location).optional()
5677
5871
  })).optional()
5678
5872
  }))
5679
5873
  });
@@ -5681,7 +5875,7 @@ var init_sarif = __esm({
5681
5875
  });
5682
5876
 
5683
5877
  // src/review/evidence/check.ts
5684
- import { z as z21 } from "zod";
5878
+ import { z as z22 } from "zod";
5685
5879
  function parseCheck(raw, sourceRoot) {
5686
5880
  const report = schema3.parse(raw);
5687
5881
  const diagnostics = [], findings = [];
@@ -5721,17 +5915,17 @@ var init_check = __esm({
5721
5915
  "use strict";
5722
5916
  init_paths2();
5723
5917
  init_types2();
5724
- schema3 = z21.object({
5725
- version: z21.literal(1),
5726
- commit: z21.string().regex(/^(?:[a-fA-F0-9]{40}|[a-fA-F0-9]{64})$/).optional(),
5727
- results: z21.array(z21.object({
5728
- name: z21.string().min(1).max(1e3),
5729
- status: z21.enum(["passed", "failed", "skipped"]),
5730
- message: z21.string().max(1e5).optional(),
5731
- locations: z21.array(z21.object({
5732
- file: z21.string().min(1).max(4e3),
5733
- line: z21.number().int().positive().optional(),
5734
- column: z21.number().int().positive().optional()
5918
+ schema3 = z22.object({
5919
+ version: z22.literal(1),
5920
+ commit: z22.string().regex(/^(?:[a-fA-F0-9]{40}|[a-fA-F0-9]{64})$/).optional(),
5921
+ results: z22.array(z22.object({
5922
+ name: z22.string().min(1).max(1e3),
5923
+ status: z22.enum(["passed", "failed", "skipped"]),
5924
+ message: z22.string().max(1e5).optional(),
5925
+ locations: z22.array(z22.object({
5926
+ file: z22.string().min(1).max(4e3),
5927
+ line: z22.number().int().positive().optional(),
5928
+ column: z22.number().int().positive().optional()
5735
5929
  })).max(100).default([])
5736
5930
  })).max(1e5)
5737
5931
  });
@@ -5740,7 +5934,7 @@ var init_check = __esm({
5740
5934
 
5741
5935
  // src/review/evidence.ts
5742
5936
  import path29 from "path";
5743
- import { z as z22 } from "zod";
5937
+ import { z as z23 } from "zod";
5744
5938
  function relativeArtifact(root, file) {
5745
5939
  const relative = path29.isAbsolute(file) ? path29.relative(root, file) : file;
5746
5940
  const normalized = normalizeRepoPath(relative);
@@ -5781,7 +5975,7 @@ async function collectReviewEvidence(root, manifests, changedFiles, decisions, d
5781
5975
  let manifest, raw;
5782
5976
  try {
5783
5977
  manifest = relativeArtifact(root, supplied);
5784
- raw = z22.object({ version: z22.literal(1), checks: z22.array(z22.unknown()) }).parse(await readStoreJson(root, manifest));
5978
+ raw = z23.object({ version: z23.literal(1), checks: z23.array(z23.unknown()) }).parse(await readStoreJson(root, manifest));
5785
5979
  } catch (error) {
5786
5980
  output.diagnostics.push(`${supplied}: ${String(error)}`);
5787
5981
  continue;
@@ -5936,25 +6130,25 @@ var init_evidence2 = __esm({
5936
6130
  init_sarif();
5937
6131
  init_check();
5938
6132
  init_types2();
5939
- checkSchema2 = z22.object({
5940
- id: z22.string().min(1).max(100),
5941
- kind: z22.enum(["tests", "static-analysis", "security", "complexity", "duplication", "documentation"]),
5942
- tool: z22.string().min(1).max(200),
5943
- command: z22.string().min(1).max(2e3),
5944
- commit: z22.string().regex(/^(?:[a-fA-F0-9]{40}|[a-fA-F0-9]{64})$/).nullable().optional(),
5945
- workingTreeClean: z22.boolean().optional(),
5946
- source: z22.string().max(2e3).optional(),
5947
- sourceRoot: z22.string().min(1).max(4e3).optional(),
5948
- status: z22.enum(["completed", "skipped", "unavailable"]).default("completed"),
5949
- reason: z22.string().min(1).max(2e3).optional(),
5950
- exitCode: z22.number().int().nullable().optional(),
5951
- environment: z22.object({
5952
- toolVersion: z22.string().min(1).max(200),
5953
- runtime: z22.string().min(1).max(200).optional(),
5954
- platform: z22.string().min(1).max(200).optional(),
5955
- configuration: z22.array(z22.string().min(1).max(4e3)).max(100).optional()
6133
+ checkSchema2 = z23.object({
6134
+ id: z23.string().min(1).max(100),
6135
+ kind: z23.enum(["tests", "static-analysis", "security", "complexity", "duplication", "documentation"]),
6136
+ tool: z23.string().min(1).max(200),
6137
+ command: z23.string().min(1).max(2e3),
6138
+ commit: z23.string().regex(/^(?:[a-fA-F0-9]{40}|[a-fA-F0-9]{64})$/).nullable().optional(),
6139
+ workingTreeClean: z23.boolean().optional(),
6140
+ source: z23.string().max(2e3).optional(),
6141
+ sourceRoot: z23.string().min(1).max(4e3).optional(),
6142
+ status: z23.enum(["completed", "skipped", "unavailable"]).default("completed"),
6143
+ reason: z23.string().min(1).max(2e3).optional(),
6144
+ exitCode: z23.number().int().nullable().optional(),
6145
+ environment: z23.object({
6146
+ toolVersion: z23.string().min(1).max(200),
6147
+ runtime: z23.string().min(1).max(200).optional(),
6148
+ platform: z23.string().min(1).max(200).optional(),
6149
+ configuration: z23.array(z23.string().min(1).max(4e3)).max(100).optional()
5956
6150
  }).optional(),
5957
- report: z22.object({ format: z22.enum(["vitest-json", "sarif", "mason-check-json"]), path: z22.string().min(1).max(4e3) }).optional()
6151
+ report: z23.object({ format: z23.enum(["vitest-json", "sarif", "mason-check-json"]), path: z23.string().min(1).max(4e3) }).optional()
5958
6152
  });
5959
6153
  }
5960
6154
  });
@@ -6744,7 +6938,7 @@ __export(observations_exports, {
6744
6938
  readObservation: () => readObservation
6745
6939
  });
6746
6940
  import fs23 from "fs/promises";
6747
- import { z as z23 } from "zod";
6941
+ import { z as z24 } from "zod";
6748
6942
  function observationPath(directory, host, revision, version = executingVersion()) {
6749
6943
  return `${directory}/activation/${host}-${revision}-${hash(version).slice(0, 12)}.json`;
6750
6944
  }
@@ -6815,19 +7009,19 @@ var init_observations = __esm({
6815
7009
  init_storage();
6816
7010
  init_model();
6817
7011
  init_launcher();
6818
- observationSchema = z23.object({
6819
- version: z23.literal(1),
6820
- masonVersion: z23.string(),
6821
- root: z23.string(),
6822
- revision: z23.string(),
6823
- host: z23.enum(["codex", "claude"]),
6824
- contextCalls: z23.number().int().nonnegative(),
6825
- lastContextAt: z23.string().optional(),
6826
- sessions: z23.record(z23.object({
6827
- events: z23.array(z23.enum(events)),
6828
- at: z23.string(),
6829
- verificationStatus: z23.string().optional(),
6830
- reportPath: z23.string().optional()
7012
+ observationSchema = z24.object({
7013
+ version: z24.literal(1),
7014
+ masonVersion: z24.string(),
7015
+ root: z24.string(),
7016
+ revision: z24.string(),
7017
+ host: z24.enum(["codex", "claude"]),
7018
+ contextCalls: z24.number().int().nonnegative(),
7019
+ lastContextAt: z24.string().optional(),
7020
+ sessions: z24.record(z24.object({
7021
+ events: z24.array(z24.enum(events)),
7022
+ at: z24.string(),
7023
+ verificationStatus: z24.string().optional(),
7024
+ reportPath: z24.string().optional()
6831
7025
  }))
6832
7026
  });
6833
7027
  }
@@ -7962,7 +8156,7 @@ init_advisory_review();
7962
8156
  init_decisions();
7963
8157
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7964
8158
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7965
- import { z as z24 } from "zod";
8159
+ import { z as z25 } from "zod";
7966
8160
 
7967
8161
  // src/mcp/tools.ts
7968
8162
  init_repair();
@@ -8591,14 +8785,14 @@ init_files();
8591
8785
  import { createHash as createHash4 } from "crypto";
8592
8786
  import { execFile as execFile5 } from "child_process";
8593
8787
  import { promisify as promisify5 } from "util";
8594
- import { z as z10 } from "zod";
8788
+ import { z as z11 } from "zod";
8595
8789
  var exec3 = promisify5(execFile5);
8596
- var requestSchema = z10.object({
8597
- id: z10.string().regex(/^[a-zA-Z0-9_-]+$/),
8598
- action: z10.enum(["prepare", "accept", "reaffirm", "retire"]).default("prepare"),
8599
- reviewer: z10.string().trim().min(1).max(200).optional(),
8600
- note: z10.string().trim().min(1).max(1500).optional(),
8601
- reviewToken: z10.string().regex(/^[a-f0-9]{64}$/).optional()
8790
+ var requestSchema = z11.object({
8791
+ id: z11.string().regex(/^[a-zA-Z0-9_-]+$/),
8792
+ action: z11.enum(["prepare", "accept", "reaffirm", "retire"]).default("prepare"),
8793
+ reviewer: z11.string().trim().min(1).max(200).optional(),
8794
+ note: z11.string().trim().min(1).max(1500).optional(),
8795
+ reviewToken: z11.string().regex(/^[a-f0-9]{64}$/).optional()
8602
8796
  });
8603
8797
  async function reviewState(root, record) {
8604
8798
  const [headHash, workingTree, changes] = await Promise.all([
@@ -8825,16 +9019,16 @@ init_lock();
8825
9019
  init_storage();
8826
9020
  init_snapshot();
8827
9021
  import fs17 from "fs/promises";
8828
- import { z as z11 } from "zod";
9022
+ import { z as z12 } from "zod";
8829
9023
  var DIRECTORY = ".mason/partial-snapshots";
8830
- var partialSchema = z11.object({
8831
- batchId: z11.string().regex(/^[a-zA-Z0-9_-]+$/),
8832
- offset: z11.number().int().nonnegative(),
8833
- features: z11.record(featureSchema),
8834
- flows: z11.record(flowSchema),
8835
- savedAt: z11.string()
9024
+ var partialSchema = z12.object({
9025
+ batchId: z12.string().regex(/^[a-zA-Z0-9_-]+$/),
9026
+ offset: z12.number().int().nonnegative(),
9027
+ features: z12.record(featureSchema),
9028
+ flows: z12.record(flowSchema),
9029
+ savedAt: z12.string()
8836
9030
  });
8837
- var scopeSchema2 = z11.object({ files: z11.array(z11.string()), savedAt: z11.string() });
9031
+ var scopeSchema2 = z12.object({ files: z12.array(z12.string()), savedAt: z12.string() });
8838
9032
  async function savePartial(rootDir, partial) {
8839
9033
  if (!/^[a-zA-Z0-9_-]+$/.test(partial.batchId)) throw new Error(`Invalid batchId: ${partial.batchId}`);
8840
9034
  await withSnapshotWrite(rootDir, () => writeStoreJson(rootDir, `${DIRECTORY}/${partial.batchId}.json`, partialSchema.parse(partial)));
@@ -9833,21 +10027,21 @@ function createMcpServer() {
9833
10027
  const server = new McpServer(
9834
10028
  {
9835
10029
  name: "mason",
9836
- version: "0.19.1"
10030
+ version: "0.19.2"
9837
10031
  },
9838
10032
  {
9839
- instructions: (setupNotice ? setupNotice + "\n\n" : "") + '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, stale or unknown map verification needs a fresh review, recorded failed verdicts need 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.'
10033
+ instructions: (setupNotice ? setupNotice + "\n\n" : "") + '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 as soon as evidence supports them, without waiting for implementation or merge; 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, stale or unknown map verification needs a fresh review, recorded failed verdicts need 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.'
9840
10034
  }
9841
10035
  );
9842
10036
  server.tool(
9843
10037
  "mason_init",
9844
10038
  "Inspect this project now: returns documentation audit findings, committed-diff review findings, decision/map status, and a quickstart playbook. Quickstart and map modes are read-only and deterministic. Explicit mode: setup configures MCP, instructions and lifecycle hooks to use mason on PATH while retaining original audit evidence; use it only when the user requests setup. Optional host selects codex or claude. Optional base selects the review comparison; evidence imports CI manifests with check outcomes, commit freshness, and links to changed files and accepted decisions. mode: map returns the full Map-Reduce build workflow. Repeat calls refresh findings even after setup.",
9845
10039
  {
9846
- dir: z24.string().describe("Absolute path to the project root directory"),
9847
- mode: z24.enum(["quickstart", "map", "setup"]).optional().default("quickstart").describe("Quickstart inspects without edits; map requests an architecture build; setup installs the shared onboarding flow."),
9848
- host: z24.enum(["codex", "claude"]).optional().describe("Assistant to configure in setup mode; inferred only when unambiguous."),
9849
- base: z24.string().optional().describe("Git ref for committed-diff review. Defaults to the first available main branch ref."),
9850
- evidence: z24.array(z24.string()).max(10).optional().describe("Repository-local CI evidence manifests to include in the review. Imports Vitest JSON, SARIF, and native-validator mason-check-json results without executing check commands.")
10040
+ dir: z25.string().describe("Absolute path to the project root directory"),
10041
+ mode: z25.enum(["quickstart", "map", "setup"]).optional().default("quickstart").describe("Quickstart inspects without edits; map requests an architecture build; setup installs the shared onboarding flow."),
10042
+ host: z25.enum(["codex", "claude"]).optional().describe("Assistant to configure in setup mode; inferred only when unambiguous."),
10043
+ base: z25.string().optional().describe("Git ref for committed-diff review. Defaults to the first available main branch ref."),
10044
+ evidence: z25.array(z25.string()).max(10).optional().describe("Repository-local CI evidence manifests to include in the review. Imports Vitest JSON, SARIF, and native-validator mason-check-json results without executing check commands.")
9851
10045
  },
9852
10046
  async ({ dir, mode, host, base, evidence }) => {
9853
10047
  const result = await masonInit(dir, { mode, host, base, evidence });
@@ -9856,10 +10050,10 @@ function createMcpServer() {
9856
10050
  );
9857
10051
  server.tool(
9858
10052
  "mason_automation",
9859
- "Inspect installed automation and observed host events, or resume/check retained documentation repair evidence across sessions. status is read-only; check saves local baselines and verification reports without editing source or approving advisories. Returns concise results with a full report path. Works without a map.",
10053
+ "Inspect installed automation, observed host events, pending decision records and the notification baseline, or check all retained documentation findings across sessions. status is read-only; check saves local baselines and verification reports without editing source, approving advisories or consuming pending completion notices. Returns concise results with a full report path. Works without a map.",
9860
10054
  {
9861
- dir: z24.string().describe("Absolute path to the project directory"),
9862
- action: z24.enum(["status", "check"]).describe("Inspect configuration and receipts, or capture/resume and verify original audit evidence.")
10055
+ dir: z25.string().describe("Absolute path to the project directory"),
10056
+ action: z25.enum(["status", "check"]).describe("Inspect configuration and receipts, or capture/resume and verify original audit evidence.")
9863
10057
  },
9864
10058
  async ({ dir, action }) => ({ content: [{ type: "text", text: await masonAutomation(dir, action) }] })
9865
10059
  );
@@ -9867,10 +10061,10 @@ function createMcpServer() {
9867
10061
  "mason_repair",
9868
10062
  "Track documentation repairs against original audit evidence. prepare saves a local baseline and returns a scoped work order; verify reads that baseline and reports resolved, unresolved, review-required, unverified, and new findings. Suppressed advisories remain unresolved. Does not edit documentation or approve decisions. No map required.",
9869
10063
  {
9870
- dir: z24.string().describe("Absolute path to the project root directory"),
9871
- action: z24.enum(["prepare", "verify"]),
9872
- baselinePath: z24.string().optional().describe("Original baseline path returned by prepare; required for verify."),
9873
- checks: z24.array(z24.enum(["deleted-reference", "new-module", "stale-count", "dead-command", "deps-changed", "decision-anchor-drift"])).min(1).optional().describe("Optional audit check subset for prepare. Verification always uses the original checks.")
10064
+ dir: z25.string().describe("Absolute path to the project root directory"),
10065
+ action: z25.enum(["prepare", "verify"]),
10066
+ baselinePath: z25.string().optional().describe("Original baseline path returned by prepare; required for verify."),
10067
+ checks: z25.array(z25.enum(["deleted-reference", "new-module", "stale-count", "dead-command", "deps-changed", "decision-anchor-drift"])).min(1).optional().describe("Optional audit check subset for prepare. Verification always uses the original checks.")
9874
10068
  },
9875
10069
  async ({ dir, action, baselinePath, checks }) => {
9876
10070
  const result = await masonRepair(dir, { action, baselinePath, checks });
@@ -9880,15 +10074,15 @@ function createMcpServer() {
9880
10074
  server.tool(
9881
10075
  "review_advisory",
9882
10076
  "Prepare an assessment of an original repair advisory and its exact repository scope, then record authorized addressed, inapplicable or deferred outcomes with reviewToken, reviewer and note. Relevant changes reopen reviews; unrelated commits preserve them. Records belong in Git. Decision findings route to review_decision; this tool never approves decisions. Recorded identities are assertions, not authenticated approvals. For dependency advisories, action dismiss with reasonCode and note records a one-step dismissal without a token or reviewer. no-dependency-claims reopens only on document content changes; unrelated-manifest-change also tracks scoped manifests.",
9883
- { dir: z24.string().describe("Absolute path to the project root"), ...advisoryReviewRequest.shape },
10077
+ { dir: z25.string().describe("Absolute path to the project root"), ...advisoryReviewRequest.shape },
9884
10078
  async ({ dir, ...request }) => ({ content: [{ type: "text", text: await masonReviewAdvisory(dir, request) }] })
9885
10079
  );
9886
10080
  server.tool(
9887
10081
  "mason_complete_init",
9888
10082
  "Record assistant instruction setup locally in ignored .mason/local/project.json, with feature settings in shared .mason/config.json. Other tools work without this marker. Repeated calls preserve the original setup time and existing settings; pass confluenceConfigured only to change that setting.",
9889
10083
  {
9890
- dir: z24.string().describe("Absolute path to the project root directory"),
9891
- confluenceConfigured: z24.boolean().optional().describe("Set the Confluence setup status; omit to preserve the existing value")
10084
+ dir: z25.string().describe("Absolute path to the project root directory"),
10085
+ confluenceConfigured: z25.boolean().optional().describe("Set the Confluence setup status; omit to preserve the existing value")
9892
10086
  },
9893
10087
  async ({ dir, confluenceConfigured }) => {
9894
10088
  const result = await masonCompleteInit(dir, { confluenceConfigured });
@@ -9899,11 +10093,11 @@ function createMcpServer() {
9899
10093
  "mason_set_confluence",
9900
10094
  "Configure Confluence credentials. Two-step flow: (1) call without `spaceKey` to validate the credentials and receive a list of available spaces \u2014 relay them to the user. (2) call again with the same `baseUrl`/`email`/`apiToken` plus the chosen `spaceKey` to persist. Credentials are stored in `~/.mason/config.json`. Warn the user that the API token will be visible in chat history before they paste it.",
9901
10095
  {
9902
- baseUrl: z24.string().describe("Confluence base URL. Accepts `acme`, `acme.atlassian.net`, or `https://acme.atlassian.net` (normalized automatically)."),
9903
- email: z24.string().describe("User's Atlassian account email"),
9904
- apiToken: z24.string().describe("API token from id.atlassian.com/manage-profile/security/api-tokens"),
9905
- spaceKey: z24.string().optional().describe("Confluence space key. Omit on the first call to list available spaces."),
9906
- parentPageId: z24.string().optional().describe("Optional parent page ID under which Mason's index page is created")
10096
+ baseUrl: z25.string().describe("Confluence base URL. Accepts `acme`, `acme.atlassian.net`, or `https://acme.atlassian.net` (normalized automatically)."),
10097
+ email: z25.string().describe("User's Atlassian account email"),
10098
+ apiToken: z25.string().describe("API token from id.atlassian.com/manage-profile/security/api-tokens"),
10099
+ spaceKey: z25.string().optional().describe("Confluence space key. Omit on the first call to list available spaces."),
10100
+ parentPageId: z25.string().optional().describe("Optional parent page ID under which Mason's index page is created")
9907
10101
  },
9908
10102
  async ({ baseUrl, email, apiToken, spaceKey, parentPageId }) => {
9909
10103
  const result = await masonSetConfluence({
@@ -9920,7 +10114,7 @@ function createMcpServer() {
9920
10114
  "full_analysis",
9921
10115
  "One-shot orientation for a project WITHOUT a concept map (get_snapshot returned exists:false). Returns git history stats, project structure with file counts, curated code sample previews (~60 lines each), and test-to-source mapping. On a mapped project, prefer get_snapshot \u2014 it is cheaper and answers feature/architecture questions directly.",
9922
10116
  {
9923
- dir: z24.string().describe("Absolute path to the project root directory")
10117
+ dir: z25.string().describe("Absolute path to the project root directory")
9924
10118
  },
9925
10119
  async ({ dir }) => {
9926
10120
  const result = await fullAnalysis(dir);
@@ -9933,7 +10127,7 @@ function createMcpServer() {
9933
10127
  "analyze_project",
9934
10128
  "Run git history analysis on a codebase. Returns commit convention patterns, stale directories, and frequently changed files. These are aggregate stats across hundreds of commits that would be expensive to compute manually.",
9935
10129
  {
9936
- dir: z24.string().describe("Absolute path to the project root directory")
10130
+ dir: z25.string().describe("Absolute path to the project root directory")
9937
10131
  },
9938
10132
  async ({ dir }) => {
9939
10133
  const result = await analyzeProject(dir);
@@ -9946,8 +10140,8 @@ function createMcpServer() {
9946
10140
  "get_code_samples",
9947
10141
  "Get previews (first ~60 lines) of representative source files from the codebase. Includes entry points, config files, hot files (frequently changed), test examples, and one file per directory for breadth. Read files natively for full content.",
9948
10142
  {
9949
- dir: z24.string().describe("Absolute path to the project root directory"),
9950
- count: z24.number().optional().default(15).describe("Maximum number of files to sample (default: 15)")
10143
+ dir: z25.string().describe("Absolute path to the project root directory"),
10144
+ count: z25.number().optional().default(15).describe("Maximum number of files to sample (default: 15)")
9951
10145
  },
9952
10146
  async ({ dir, count: count4 }) => {
9953
10147
  const result = await getCodeSamples(dir, count4);
@@ -9960,7 +10154,7 @@ function createMcpServer() {
9960
10154
  "get_snapshot",
9961
10155
  "Return the optional feature-to-file architecture map with drift and trust evidence. Map verification is rechecked against sampled source contents; stale or unknown verification requires a fresh review, and recordedVerdict preserves the historical verdict. If no map exists, returns exists:false plus project structure, Git signals, and test pairs. Decision capture, get_context, and get_impact still work. No initialization required.",
9962
10156
  {
9963
- dir: z24.string().describe("Absolute path to the project root directory")
10157
+ dir: z25.string().describe("Absolute path to the project root directory")
9964
10158
  },
9965
10159
  async ({ dir }) => {
9966
10160
  const result = await getSnapshot(dir);
@@ -9973,9 +10167,9 @@ function createMcpServer() {
9973
10167
  "get_context",
9974
10168
  "Assemble task context: matching decisions with rationale, approval, owner, sources, last review, and freshness, plus related tests, file impact, and any available map entries. Map verification is rechecked against sampled source contents; stale or unknown verification requires a fresh review, and recordedVerdict preserves the historical verdict. Proposals are suggestions; legacy records are unreviewed; accepted decisions are constraints subject to freshness. No initialization or map required. Pass task and optional files. map.status and diagnostics preserve missing or invalid knowledge. Impact covers up to three unique files, expanding directory anchors.",
9975
10169
  {
9976
- dir: z24.string().describe("Absolute path to the project root directory"),
9977
- task: z24.string().describe("The task, bug, or change request in natural language \u2014 e.g. 'add rate limiting to the API client' or a ticket description"),
9978
- files: z24.array(z24.string()).optional().describe("Optional file paths already known to be involved (e.g. from a diff or stack trace). Entries containing them are boosted above pure text matches.")
10170
+ dir: z25.string().describe("Absolute path to the project root directory"),
10171
+ task: z25.string().describe("The task, bug, or change request in natural language \u2014 e.g. 'add rate limiting to the API client' or a ticket description"),
10172
+ files: z25.array(z25.string()).optional().describe("Optional file paths already known to be involved (e.g. from a diff or stack trace). Entries containing them are boosted above pure text matches.")
9979
10173
  },
9980
10174
  async ({ dir, task, files }) => {
9981
10175
  const result = await getContext(dir, task, files);
@@ -9991,10 +10185,10 @@ function createMcpServer() {
9991
10185
  "generate_snapshot_batch",
9992
10186
  "Map step of the concept-map build. Returns one batch of source files (skeletons of every file in the batch plus a few deeper-read bodies for grounding), along with a system prompt instructing you to derive features and flows for ONLY this batch. Call repeatedly with the returned `nextOffset` until it is null, calling `save_partial_snapshot` between each call. Use product-natural feature names so partials merge cleanly in the reduce step.",
9993
10187
  {
9994
- dir: z24.string().describe("Absolute path to the project root directory"),
9995
- offset: z24.number().int().optional().describe("0-indexed file offset to start the batch at. Omit on the first call; pass the `nextOffset` from the previous response for subsequent calls."),
9996
- batchSize: z24.number().int().optional().describe("Files per batch. Defaults to 50."),
9997
- files: z24.array(z24.string()).optional().describe("Scope the batch walk to this explicit file list \u2014 e.g. the drift set from mason_check_drift (changedFiles + unmappedFiles). Pass the SAME list on every batch call of one refresh run. Triggers refresh mode: reduce_snapshot will merge the partials into the existing map instead of rebuilding it.")
10188
+ dir: z25.string().describe("Absolute path to the project root directory"),
10189
+ offset: z25.number().int().optional().describe("0-indexed file offset to start the batch at. Omit on the first call; pass the `nextOffset` from the previous response for subsequent calls."),
10190
+ batchSize: z25.number().int().optional().describe("Files per batch. Defaults to 50."),
10191
+ files: z25.array(z25.string()).optional().describe("Scope the batch walk to this explicit file list \u2014 e.g. the drift set from mason_check_drift (changedFiles + unmappedFiles). Pass the SAME list on every batch call of one refresh run. Triggers refresh mode: reduce_snapshot will merge the partials into the existing map instead of rebuilding it.")
9998
10192
  },
9999
10193
  async ({ dir, offset, batchSize, files }) => {
10000
10194
  const result = await generateSnapshotBatch(dir, offset, batchSize, files);
@@ -10007,23 +10201,23 @@ function createMcpServer() {
10007
10201
  "save_partial_snapshot",
10008
10202
  "Persist the partial concept map you derived for one batch. Call this once per batch, with the `batchId` from the `generate_snapshot_batch` response. Partials accumulate in `.mason/partial-snapshots/` and are merged in the reduce step.",
10009
10203
  {
10010
- dir: z24.string().describe("Absolute path to the project root directory"),
10011
- batchId: z24.string().describe("The `batchId` returned by `generate_snapshot_batch`."),
10012
- offset: z24.number().int().describe("The `offset` returned by `generate_snapshot_batch`. Used to order partials in the reduce step."),
10013
- features: z24.record(
10014
- z24.object({
10015
- description: z24.string(),
10016
- files: z24.array(z24.string()),
10017
- tests: z24.array(z24.string()).optional(),
10018
- type: z24.enum(["capability", "infrastructure"]).optional().describe(
10204
+ dir: z25.string().describe("Absolute path to the project root directory"),
10205
+ batchId: z25.string().describe("The `batchId` returned by `generate_snapshot_batch`."),
10206
+ offset: z25.number().int().describe("The `offset` returned by `generate_snapshot_batch`. Used to order partials in the reduce step."),
10207
+ features: z25.record(
10208
+ z25.object({
10209
+ description: z25.string(),
10210
+ files: z25.array(z25.string()),
10211
+ tests: z25.array(z25.string()).optional(),
10212
+ type: z25.enum(["capability", "infrastructure"]).optional().describe(
10019
10213
  '"capability" (user-facing functionality) or "infrastructure" (internal plumbing with no end user \u2014 DI/service wiring, config, logging, adapters). Defaults to "capability".'
10020
10214
  )
10021
10215
  })
10022
10216
  ).describe("Partial features for this batch only \u2014 files outside the batch will be added by other partials."),
10023
- flows: z24.record(
10024
- z24.object({
10025
- description: z24.string(),
10026
- chain: z24.array(z24.string())
10217
+ flows: z25.record(
10218
+ z25.object({
10219
+ description: z25.string(),
10220
+ chain: z25.array(z25.string())
10027
10221
  })
10028
10222
  ).describe("Partial flows whose entire chain is in this batch. Cross-batch flows are reconstructed in reduce.")
10029
10223
  },
@@ -10038,7 +10232,7 @@ function createMcpServer() {
10038
10232
  "reduce_snapshot",
10039
10233
  "Reduce step of the concept-map build. Returns every partial snapshot plus a system prompt asking you to merge them into one coherent project-wide map. Resolve platform variants into single product features, dedupe near-duplicates, and ensure no file is dropped. After producing the unified map, call `save_snapshot` to persist it (this also clears the partials).",
10040
10234
  {
10041
- dir: z24.string().describe("Absolute path to the project root directory")
10235
+ dir: z25.string().describe("Absolute path to the project root directory")
10042
10236
  },
10043
10237
  async ({ dir }) => {
10044
10238
  const result = await reduceSnapshot(dir);
@@ -10051,25 +10245,25 @@ function createMcpServer() {
10051
10245
  "save_snapshot",
10052
10246
  "Save a concept-to-files map as a persistent project snapshot. Maps feature names and data flows to the files that implement them. Persists across conversations \u2014 future sessions can call get_snapshot to instantly find relevant files. No API key needed \u2014 you are the LLM generating the map.",
10053
10247
  {
10054
- dir: z24.string().describe("Absolute path to the project root directory"),
10055
- features: z24.record(
10056
- z24.object({
10057
- description: z24.string().describe("One-line description of the feature"),
10058
- files: z24.array(z24.string()).describe("File paths that implement this feature"),
10059
- tests: z24.array(z24.string()).optional().describe("Test file paths for this feature"),
10060
- type: z24.enum(["capability", "infrastructure"]).optional().describe(
10248
+ dir: z25.string().describe("Absolute path to the project root directory"),
10249
+ features: z25.record(
10250
+ z25.object({
10251
+ description: z25.string().describe("One-line description of the feature"),
10252
+ files: z25.array(z25.string()).describe("File paths that implement this feature"),
10253
+ tests: z25.array(z25.string()).optional().describe("Test file paths for this feature"),
10254
+ type: z25.enum(["capability", "infrastructure"]).optional().describe(
10061
10255
  'Classification: "capability" for user-facing functionality, "infrastructure" for internal plumbing with no end user (DI/service wiring, config, logging, adapters). Capabilities are published to Confluence; infrastructure stays in the AI concept map only. Defaults to "capability".'
10062
10256
  )
10063
10257
  })
10064
10258
  ).describe("Map of feature names to their implementing files"),
10065
- flows: z24.record(
10066
- z24.object({
10067
- description: z24.string().describe("One-line description of the flow"),
10068
- chain: z24.array(z24.string()).describe("Ordered list of file paths showing data/call flow")
10259
+ flows: z25.record(
10260
+ z25.object({
10261
+ description: z25.string().describe("One-line description of the flow"),
10262
+ chain: z25.array(z25.string()).describe("Ordered list of file paths showing data/call flow")
10069
10263
  })
10070
10264
  ).describe("Map of flow names to ordered file chains"),
10071
- removeFeatures: z24.array(z24.string()).optional().describe("Feature names to delete from the existing map \u2014 for features that were renamed or no longer exist. Applied before merging; only meaningful on incremental saves."),
10072
- removeFlows: z24.array(z24.string()).optional().describe("Flow names to delete from the existing map. Applied before merging; only meaningful on incremental saves.")
10265
+ removeFeatures: z25.array(z25.string()).optional().describe("Feature names to delete from the existing map \u2014 for features that were renamed or no longer exist. Applied before merging; only meaningful on incremental saves."),
10266
+ removeFlows: z25.array(z25.string()).optional().describe("Flow names to delete from the existing map. Applied before merging; only meaningful on incremental saves.")
10073
10267
  },
10074
10268
  async ({ dir, features, flows, removeFeatures, removeFlows }) => {
10075
10269
  const result = await saveSnapshotData(
@@ -10086,19 +10280,19 @@ function createMcpServer() {
10086
10280
  );
10087
10281
  server.tool(
10088
10282
  "save_decision",
10089
- "Capture or revise a decision proposal with rationale, anchors, optional owner, sources, and a known actor. Compare matching get_context records and pending proposals before saving. When new evidence changes the same lesson's assumptions, scope, or recommended action, pass its existing id, including for accepted records. Preserve supported rationale and sources and replace obsolete instructions. Create a new record for a genuinely distinct lesson; skip unchanged restatements. No setup or map required. Writes a local record and preserves content history. Changes create a pending proposal while the last accepted revision remains operative; unchanged content does not re-verify or refresh it. Use review_decision for authorized acceptance or reaffirmation. A proposal cannot supersede a record with an operative accepted revision; review its replacement and retire the original separately.",
10283
+ "Capture or revise a decision proposal as soon as the lesson is supported by evidence, without waiting for implementation or merge. Keep unimplemented remedies tentative. Include rationale, anchors, optional owner, sources, and a known actor. Compare matching get_context records and pending proposals before saving. When new evidence changes the same lesson's assumptions, scope, or recommended action, pass its existing id, including for accepted records. Preserve supported rationale and sources and replace obsolete instructions. Create a new record for a genuinely distinct lesson; skip unchanged restatements. No setup or map required. Writes a local record and preserves content history. Changes create a pending proposal while the last accepted revision remains operative; unchanged content does not re-verify or refresh it. Use review_decision for authorized acceptance or reaffirmation. A proposal cannot supersede a record with an operative accepted revision; review its replacement and retire the original separately.",
10090
10284
  {
10091
- dir: z24.string().describe("Absolute path to the project root directory"),
10285
+ dir: z25.string().describe("Absolute path to the project root directory"),
10092
10286
  title: decisionTitleSchema.describe(`Short, specific headline (max ${TITLE_MAX_CHARS} characters). Generates a stable id on creation; revising the title keeps the existing id.`),
10093
10287
  body: decisionBodySchema.describe(`The knowledge itself: what was tried/decided, why, and what to avoid. Must contain information NOT derivable by reading the code. Aim for ${BODY_RECOMMENDED_CHARS} characters or fewer; up to ${BODY_MAX_CHARS} is accepted with a warning above the target. Matching context and hooks include the full body. Preserve exceptions; use sources for supporting references.`),
10094
- category: z24.enum(["decision", "gotcha", "deprecation", "convention"]),
10095
- files: z24.array(z24.string()).optional().describe("Repo-relative files or directory prefixes this applies to. Matching is shared by retrieval, hooks, review, and drift checking; changes flag the decision for re-verification."),
10096
- id: z24.string().optional().describe("Pass the matching record's existing id when evidence changes that lesson's assumptions, scope, or recommended action, including for accepted records. Changed content becomes a proposal while the accepted version remains operative; unchanged content leaves review and freshness untouched."),
10097
- supersedes: z24.string().optional().describe("Id of an unreviewed record or proposal with no accepted revision to replace. Operative accepted decisions require separate review and retirement."),
10288
+ category: z25.enum(["decision", "gotcha", "deprecation", "convention"]),
10289
+ files: z25.array(z25.string()).optional().describe("Repo-relative files or directory prefixes this applies to. Matching is shared by retrieval, hooks, review, and drift checking; changes flag the decision for re-verification."),
10290
+ id: z25.string().optional().describe("Pass the matching record's existing id when evidence changes that lesson's assumptions, scope, or recommended action, including for accepted records. Changed content becomes a proposal while the accepted version remains operative; unchanged content leaves review and freshness untouched."),
10291
+ supersedes: z25.string().optional().describe("Id of an unreviewed record or proposal with no accepted revision to replace. Operative accepted decisions require separate review and retirement."),
10098
10292
  owner: attributionSchema.shape.owner.describe("Responsible person or team, when known. Null clears it. Required for acceptance."),
10099
10293
  sources: attributionSchema.shape.sources.describe("Known PR, issue, incident, discussion, or document references. Omit to preserve; [] clears. At least one is required for acceptance."),
10100
10294
  actor: attributionSchema.shape.actor.describe("Known person or agent recording this revision. Omit if unknown; do not infer from Git identity."),
10101
- force: z24.boolean().optional().describe("Save even when a near-duplicate was detected")
10295
+ force: z25.boolean().optional().describe("Save even when a near-duplicate was detected")
10102
10296
  },
10103
10297
  async ({ dir, title, body, category, files, id, supersedes, force, owner, sources, actor }) => {
10104
10298
  const result = await saveDecision(dir, {
@@ -10120,12 +10314,12 @@ function createMcpServer() {
10120
10314
  "review_decision",
10121
10315
  "Prepare a decision review: returns the full record and history, any operative accepted revision, provenance, changes and previews for both sets of anchors, and a reviewToken. Then record accept, reaffirm, or retire with that token, the authorized reviewer, and a reason. Acceptance replaces the operative revision; retirement withdraws the entire record including its proposal. Acceptance requires owner, source, readable Git HEAD, and committed anchor changes. Changed records or code invalidate the token. Identities and approvals are recorded assertions for normal PR review, not authenticated proof.",
10122
10316
  {
10123
- dir: z24.string().describe("Absolute path to the project root directory"),
10124
- id: z24.string().regex(/^[a-zA-Z0-9_-]+$/).describe("Decision id from get_context or save_decision"),
10125
- action: z24.enum(["prepare", "accept", "reaffirm", "retire"]).optional().default("prepare").describe("Prepare is read-only. Other actions record an explicitly authorized review."),
10126
- reviewer: z24.string().trim().min(1).max(200).optional().describe("Identity of the actual reviewer; required for a verdict. Never invent one."),
10127
- note: z24.string().trim().min(1).max(1500).optional().describe("Review rationale; required for a verdict. Cite evidence for the decision."),
10128
- reviewToken: z24.string().regex(/^[a-f0-9]{64}$/).optional().describe("Token from the prepared review; rejects stale record or code revisions")
10317
+ dir: z25.string().describe("Absolute path to the project root directory"),
10318
+ id: z25.string().regex(/^[a-zA-Z0-9_-]+$/).describe("Decision id from get_context or save_decision"),
10319
+ action: z25.enum(["prepare", "accept", "reaffirm", "retire"]).optional().default("prepare").describe("Prepare is read-only. Other actions record an explicitly authorized review."),
10320
+ reviewer: z25.string().trim().min(1).max(200).optional().describe("Identity of the actual reviewer; required for a verdict. Never invent one."),
10321
+ note: z25.string().trim().min(1).max(1500).optional().describe("Review rationale; required for a verdict. Cite evidence for the decision."),
10322
+ reviewToken: z25.string().regex(/^[a-f0-9]{64}$/).optional().describe("Token from the prepared review; rejects stale record or code revisions")
10129
10323
  },
10130
10324
  async ({ dir, ...input }) => {
10131
10325
  const result = await reviewDecision2(dir, input);
@@ -10136,7 +10330,7 @@ function createMcpServer() {
10136
10330
  "mason_check_drift",
10137
10331
  "Check how far the concept map has drifted from HEAD. Deterministic (git + filesystem, no LLM). Returns which features/flows are stale and the changed files behind them, new source files not yet mapped, ghost files (mapped but deleted), renames, and a `recommendation`: `up-to-date` (nothing to do), `incremental` (update just the stale entries via save_snapshot), or `full-rebuild` (re-run the Map-Reduce build). Call this before trusting the map in a long session, or periodically to keep the map and any synced wikis fresh.",
10138
10332
  {
10139
- dir: z24.string().describe("Absolute path to the project root directory")
10333
+ dir: z25.string().describe("Absolute path to the project root directory")
10140
10334
  },
10141
10335
  async ({ dir }) => {
10142
10336
  const result = await checkDrift(dir);
@@ -10149,8 +10343,8 @@ function createMcpServer() {
10149
10343
  "verify_snapshot",
10150
10344
  "Spot-check the concept map's CORRECTNESS (drift checks freshness; this checks entries were right to begin with). Returns a sample of entries \u2014 always the never-verified and least-recently-verified first \u2014 with skeletons of their claimed files, for you to judge whether the files actually implement what the entry claims. Report verdicts back via save_verification with each entry\u2019s kind and reviewToken. Tokens bind the entry and bounded source evidence; changed evidence requires a fresh review. Run periodically, or after an automated refresh wrote entries no human reviewed.",
10151
10345
  {
10152
- dir: z24.string().describe("Absolute path to the project root directory"),
10153
- sample: z24.number().int().optional().describe("Entries to sample (default 5)")
10346
+ dir: z25.string().describe("Absolute path to the project root directory"),
10347
+ sample: z25.number().int().optional().describe("Entries to sample (default 5)")
10154
10348
  },
10155
10349
  async ({ dir, sample }) => {
10156
10350
  const result = await verifySnapshot(dir, sample);
@@ -10161,13 +10355,13 @@ function createMcpServer() {
10161
10355
  "save_verification",
10162
10356
  "Record verify_snapshot verdicts using each entry\u2019s kind and reviewToken. Missing tokens request a fresh review; changed or deleted entries return conflicts without being stamped. Entries judged ok are stamped verifiedAt; failures are flagged verificationFailed with your note and surface in get_context, get_snapshot, and mason_check_drift until corrected. Verdict notes are required for failures.",
10163
10357
  {
10164
- dir: z24.string().describe("Absolute path to the project root directory"),
10165
- verdicts: z24.record(
10166
- z24.object({
10167
- kind: z24.enum(["feature", "flow"]).optional().describe("Entry kind returned by verify_snapshot; required to record a verdict"),
10168
- reviewToken: z24.string().optional().describe("Copy the reviewToken from verify_snapshot after inspecting its evidence; required to record a verdict"),
10169
- ok: z24.boolean(),
10170
- note: z24.string().optional().describe("One line on what's wrong \u2014 required when ok is false")
10358
+ dir: z25.string().describe("Absolute path to the project root directory"),
10359
+ verdicts: z25.record(
10360
+ z25.object({
10361
+ kind: z25.enum(["feature", "flow"]).optional().describe("Entry kind returned by verify_snapshot; required to record a verdict"),
10362
+ reviewToken: z25.string().optional().describe("Copy the reviewToken from verify_snapshot after inspecting its evidence; required to record a verdict"),
10363
+ ok: z25.boolean(),
10364
+ note: z25.string().optional().describe("One line on what's wrong \u2014 required when ok is false")
10171
10365
  })
10172
10366
  ).describe("Entry name \u2192 verdict, exactly as returned by verify_snapshot")
10173
10367
  },
@@ -10180,8 +10374,8 @@ function createMcpServer() {
10180
10374
  "get_impact",
10181
10375
  "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.",
10182
10376
  {
10183
- dir: z24.string().describe("Absolute path to the project root directory"),
10184
- files: z24.array(z24.string()).describe("File paths or names to analyze (e.g., ['WeatherRepository.kt'] or ['src/services/auth.ts'])")
10377
+ dir: z25.string().describe("Absolute path to the project root directory"),
10378
+ files: z25.array(z25.string()).describe("File paths or names to analyze (e.g., ['WeatherRepository.kt'] or ['src/services/auth.ts'])")
10185
10379
  },
10186
10380
  async ({ dir, files }) => {
10187
10381
  const result = await getImpact(dir, files);
@@ -10194,12 +10388,12 @@ function createMcpServer() {
10194
10388
  "export_to_confluence",
10195
10389
  "Sync the project's concept map to Confluence as product-readable wiki pages: an index page, one page per feature (PM-language descriptions, no file paths), and a changelog page. Mason replaces managed page bodies; manual edits to those bodies are overwritten. Requires `mason_set_confluence` to have been called first.",
10196
10390
  {
10197
- dir: z24.string().describe("Absolute path to the project root directory"),
10198
- spaceKey: z24.string().optional().describe("Override the configured space key"),
10199
- parentPageId: z24.string().optional().describe("Override the configured parent page ID"),
10200
- indexPageTitle: z24.string().optional().describe("Title of the index page (default: 'Mason \u2014 System Map')"),
10201
- changelogPageTitle: z24.string().optional().describe("Title of the changelog page (default: 'Mason \u2014 Changelog')"),
10202
- featurePagePrefix: z24.string().optional().describe("Prefix for each feature page title (default: 'Feature: ')")
10391
+ dir: z25.string().describe("Absolute path to the project root directory"),
10392
+ spaceKey: z25.string().optional().describe("Override the configured space key"),
10393
+ parentPageId: z25.string().optional().describe("Override the configured parent page ID"),
10394
+ indexPageTitle: z25.string().optional().describe("Title of the index page (default: 'Mason \u2014 System Map')"),
10395
+ changelogPageTitle: z25.string().optional().describe("Title of the changelog page (default: 'Mason \u2014 Changelog')"),
10396
+ featurePagePrefix: z25.string().optional().describe("Prefix for each feature page title (default: 'Feature: ')")
10203
10397
  },
10204
10398
  async ({ dir, spaceKey, parentPageId, indexPageTitle, changelogPageTitle, featurePagePrefix }) => {
10205
10399
  const result = await exportToConfluenceTool(dir, {
@@ -10228,7 +10422,7 @@ import fs33 from "fs/promises";
10228
10422
  import path42 from "path";
10229
10423
  import { spawn as spawn3 } from "child_process";
10230
10424
  import { randomUUID as randomUUID11 } from "crypto";
10231
- import { z as z30 } from "zod";
10425
+ import { z as z31 } from "zod";
10232
10426
 
10233
10427
  // src/distribution/state.ts
10234
10428
  init_storage();
@@ -10236,7 +10430,7 @@ import os5 from "os";
10236
10430
  import path38 from "path";
10237
10431
  import { fileURLToPath } from "url";
10238
10432
  import fs28 from "fs/promises";
10239
- import { z as z26 } from "zod";
10433
+ import { z as z27 } from "zod";
10240
10434
 
10241
10435
  // src/distribution/path.ts
10242
10436
  import fs27 from "fs/promises";
@@ -10245,11 +10439,11 @@ import path37 from "path";
10245
10439
  import { execFile as execFile12 } from "child_process";
10246
10440
  import { promisify as promisify12 } from "util";
10247
10441
  import { createHash as createHash7, randomUUID as randomUUID7 } from "crypto";
10248
- import { z as z25 } from "zod";
10442
+ import { z as z26 } from "zod";
10249
10443
  var execute2 = promisify12(execFile12);
10250
- var pathChangeSchema = z25.discriminatedUnion("kind", [
10251
- z25.object({ kind: z25.literal("profile"), file: z25.string(), block: z25.string(), created: z25.boolean() }),
10252
- z25.object({ kind: z25.literal("windows"), entry: z25.string(), created: z25.boolean() })
10444
+ var pathChangeSchema = z26.discriminatedUnion("kind", [
10445
+ z26.object({ kind: z26.literal("profile"), file: z26.string(), block: z26.string(), created: z26.boolean() }),
10446
+ z26.object({ kind: z26.literal("windows"), entry: z26.string(), created: z26.boolean() })
10253
10447
  ]);
10254
10448
  var quote = (s) => "'" + s.replace(/'/g, "'\\''") + "'";
10255
10449
  async function configurePath(bin, previous, save, options = {}) {
@@ -10369,26 +10563,26 @@ try {
10369
10563
  }
10370
10564
 
10371
10565
  // src/distribution/state.ts
10372
- var releaseVersion = z26.string().regex(/^\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/);
10373
- var bundleId = z26.string().regex(/^[a-f0-9]{24}$/);
10374
- var updatePolicySchema = z26.object({
10375
- enabled: z26.boolean(),
10376
- revision: z26.string(),
10566
+ var releaseVersion = z27.string().regex(/^\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/);
10567
+ var bundleId = z27.string().regex(/^[a-f0-9]{24}$/);
10568
+ var updatePolicySchema = z27.object({
10569
+ enabled: z27.boolean(),
10570
+ revision: z27.string(),
10377
10571
  pinnedVersion: releaseVersion.optional(),
10378
- mirror: z26.string().optional()
10572
+ mirror: z27.string().optional()
10379
10573
  });
10380
- var recordSchema3 = z26.object({
10381
- format: z26.literal(1),
10382
- home: z26.string(),
10383
- bin: z26.string(),
10574
+ var recordSchema3 = z27.object({
10575
+ format: z27.literal(1),
10576
+ home: z27.string(),
10577
+ bin: z27.string(),
10384
10578
  current: bundleId,
10385
10579
  version: releaseVersion,
10386
- launchers: z26.record(z26.string()),
10387
- previousLaunchers: z26.record(z26.string()).optional(),
10388
- pathChanges: z26.array(pathChangeSchema).optional(),
10580
+ launchers: z27.record(z27.string()),
10581
+ previousLaunchers: z27.record(z27.string()).optional(),
10582
+ pathChanges: z27.array(pathChangeSchema).optional(),
10389
10583
  updates: updatePolicySchema.optional(),
10390
- previous: z26.object({ id: bundleId, version: releaseVersion }).optional(),
10391
- pending: z26.object({ id: bundleId, version: releaseVersion, policyRevision: z26.string() }).optional()
10584
+ previous: z27.object({ id: bundleId, version: releaseVersion }).optional(),
10585
+ pending: z27.object({ id: bundleId, version: releaseVersion, policyRevision: z27.string() }).optional()
10392
10586
  });
10393
10587
  async function readInstallation(home) {
10394
10588
  const raw = await readStoreJson(home, "install.json");
@@ -10429,15 +10623,15 @@ init_storage();
10429
10623
  import fs29 from "fs/promises";
10430
10624
  import path39 from "path";
10431
10625
  import { createHash as createHash8 } from "crypto";
10432
- import { z as z27 } from "zod";
10626
+ import { z as z28 } from "zod";
10433
10627
  var targets = ["darwin-arm64", "darwin-x64", "linux-arm64", "linux-x64", "win32-arm64", "win32-x64"];
10434
- var digest2 = z27.string().regex(/^[a-f0-9]{64}$/);
10435
- var bundleSchema = z27.object({
10436
- format: z27.literal(1),
10437
- version: z27.string().regex(/^\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/),
10438
- target: z27.enum(targets),
10439
- nodeVersion: z27.string(),
10440
- files: z27.record(digest2)
10628
+ var digest2 = z28.string().regex(/^[a-f0-9]{64}$/);
10629
+ var bundleSchema = z28.object({
10630
+ format: z28.literal(1),
10631
+ version: z28.string().regex(/^\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/),
10632
+ target: z28.enum(targets),
10633
+ nodeVersion: z28.string(),
10634
+ files: z28.record(digest2)
10441
10635
  }).strict();
10442
10636
  var sha256 = (bytes) => createHash8("sha256").update(bytes).digest("hex");
10443
10637
  async function readBundle(root) {
@@ -10473,8 +10667,13 @@ import { unlinkSync } from "fs";
10473
10667
  import os6 from "os";
10474
10668
  import path40 from "path";
10475
10669
  import { randomUUID as randomUUID8 } from "crypto";
10476
- import { z as z28 } from "zod";
10477
- var leaseSchema = z28.object({ pid: z28.number().int().positive(), host: z28.string(), bundle: z28.string().regex(/^[a-f0-9]{24}$/) });
10670
+ import { z as z29 } from "zod";
10671
+
10672
+ // src/distribution/trust.ts
10673
+ import tls from "tls";
10674
+
10675
+ // src/distribution/runtime.ts
10676
+ var leaseSchema = z29.object({ pid: z29.number().int().positive(), host: z29.string(), bundle: z29.string().regex(/^[a-f0-9]{24}$/) });
10478
10677
 
10479
10678
  // src/distribution/install.ts
10480
10679
  import fs32 from "fs/promises";
@@ -10491,8 +10690,8 @@ init_storage();
10491
10690
  import fs31 from "fs/promises";
10492
10691
  import os7 from "os";
10493
10692
  import { randomUUID as randomUUID9 } from "crypto";
10494
- import { z as z29 } from "zod";
10495
- var leaseSchema2 = z29.object({ pid: z29.number().int().positive(), host: z29.string() });
10693
+ import { z as z30 } from "zod";
10694
+ var leaseSchema2 = z30.object({ pid: z30.number().int().positive(), host: z30.string() });
10496
10695
  async function hasRunningMcp(home) {
10497
10696
  const directory = await storePath(home, "sessions");
10498
10697
  const files = await fs31.readdir(directory).catch((error) => {
@@ -10722,14 +10921,14 @@ async function prepareStandaloneMcp(home) {
10722
10921
  // src/distribution/updates.ts
10723
10922
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
10724
10923
  var RELEASE_AGE_MS = 24 * 60 * 60 * 1e3;
10725
- var releaseListSchema = z30.array(z30.object({ tag_name: z30.string(), draft: z30.boolean(), prerelease: z30.boolean(), published_at: z30.string().nullable() }));
10726
- var updateManifestSchema = z30.object({
10727
- format: z30.literal(1),
10728
- version: z30.string().regex(/^\d+\.\d+\.\d+$/),
10729
- publishedAt: z30.string().datetime(),
10730
- assets: z30.record(z30.string().regex(/^[a-f0-9]{64}$/))
10924
+ var releaseListSchema = z31.array(z31.object({ tag_name: z31.string(), draft: z31.boolean(), prerelease: z31.boolean(), published_at: z31.string().nullable() }));
10925
+ var updateManifestSchema = z31.object({
10926
+ format: z31.literal(1),
10927
+ version: z31.string().regex(/^\d+\.\d+\.\d+$/),
10928
+ publishedAt: z31.string().datetime(),
10929
+ assets: z31.record(z31.string().regex(/^[a-f0-9]{64}$/))
10731
10930
  }).strict();
10732
- var statusSchema2 = z30.object({ lastAttempt: z30.number(), availableVersion: z30.string().optional(), error: z30.string().optional() });
10931
+ var statusSchema2 = z31.object({ lastAttempt: z31.number(), availableVersion: z31.string().optional(), error: z31.string().optional() });
10733
10932
  async function scheduleAutomaticUpdate() {
10734
10933
  try {
10735
10934
  const { home, record } = await currentInstallation();