mason-context 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +1 -1
- package/dist/mason-auto.js +443 -91
- package/dist/mason-auto.js.map +1 -1
- package/dist/mason-mcp.js +295 -200
- package/dist/mason-mcp.js.map +1 -1
- package/dist/mason.js +471 -118
- package/dist/mason.js.map +1 -1
- package/package.json +1 -1
package/dist/mason-mcp.js
CHANGED
|
@@ -2832,7 +2832,13 @@ function managedBlock(text2, start, end, body) {
|
|
|
2832
2832
|
async function applyEdit(root, edit) {
|
|
2833
2833
|
const current = await readText(root, edit.path);
|
|
2834
2834
|
if (current === edit.after) return false;
|
|
2835
|
-
if (current !== edit.before) throw new Error("
|
|
2835
|
+
if (current !== edit.before) throw new Error("Integration input changed during update: " + edit.path + ". Rerun the requested command to resume.");
|
|
2836
|
+
if (edit.after === null) {
|
|
2837
|
+
const file2 = await storePath(root, edit.path);
|
|
2838
|
+
if (await readText(root, edit.path) !== edit.before) throw new Error("Input changed during removal: " + edit.path);
|
|
2839
|
+
await fs13.unlink(file2);
|
|
2840
|
+
return true;
|
|
2841
|
+
}
|
|
2836
2842
|
const file = await storePath(root, edit.path, true);
|
|
2837
2843
|
const temporary = path19.join(path19.dirname(file), ".mason-setup-" + randomUUID3() + ".tmp");
|
|
2838
2844
|
try {
|
|
@@ -2844,7 +2850,7 @@ async function applyEdit(root, edit) {
|
|
|
2844
2850
|
} finally {
|
|
2845
2851
|
await handle.close();
|
|
2846
2852
|
}
|
|
2847
|
-
if (await readText(root, edit.path) !== edit.before) throw new Error("
|
|
2853
|
+
if (await readText(root, edit.path) !== edit.before) throw new Error("Integration input changed during update: " + edit.path);
|
|
2848
2854
|
await fs13.rename(temporary, file);
|
|
2849
2855
|
return true;
|
|
2850
2856
|
} finally {
|
|
@@ -3130,7 +3136,7 @@ var init_evidence = __esm({
|
|
|
3130
3136
|
init_dead_command();
|
|
3131
3137
|
init_storage();
|
|
3132
3138
|
exec11 = promisify12(execFile12);
|
|
3133
|
-
engineVersion = true ? "0.
|
|
3139
|
+
engineVersion = true ? "0.16.0" : "development";
|
|
3134
3140
|
hash = (value) => createHash4("sha256").update(JSON.stringify(value)).digest("hex");
|
|
3135
3141
|
content = readAuditInput;
|
|
3136
3142
|
internal = (file) => file === ".mason" || file === ".mason/reports" || file.startsWith(".mason/reports/");
|
|
@@ -3494,6 +3500,79 @@ var init_adapters = __esm({
|
|
|
3494
3500
|
}
|
|
3495
3501
|
});
|
|
3496
3502
|
|
|
3503
|
+
// src/setup/ownership.ts
|
|
3504
|
+
var ownership_exports = {};
|
|
3505
|
+
__export(ownership_exports, {
|
|
3506
|
+
BLOCKS: () => BLOCKS,
|
|
3507
|
+
OWNERSHIP_PATH: () => OWNERSHIP_PATH,
|
|
3508
|
+
blockSpan: () => blockSpan,
|
|
3509
|
+
completedHookOwnership: () => completedHookOwnership,
|
|
3510
|
+
ownershipEdit: () => ownershipEdit,
|
|
3511
|
+
ownershipSchema: () => ownershipSchema
|
|
3512
|
+
});
|
|
3513
|
+
import { z as z10 } from "zod";
|
|
3514
|
+
import { parse } from "smol-toml";
|
|
3515
|
+
function blockSpan(text2, start, end) {
|
|
3516
|
+
const starts = text2.split(start).length - 1, ends = text2.split(end).length - 1;
|
|
3517
|
+
if (!starts && !ends) return null;
|
|
3518
|
+
const from = text2.indexOf(start), to = text2.indexOf(end) + end.length;
|
|
3519
|
+
if (starts !== 1 || ends !== 1 || to <= from) throw new Error("Ambiguous Mason markers; inspect the marked block manually.");
|
|
3520
|
+
return { from, to, text: text2.slice(from, to) };
|
|
3521
|
+
}
|
|
3522
|
+
async function ownershipEdit(root, edits, hookCommands = {}) {
|
|
3523
|
+
const before = await readText(root, OWNERSHIP_PATH);
|
|
3524
|
+
const record = before === null ? { version: 1, files: {} } : ownershipSchema.parse(JSON.parse(before));
|
|
3525
|
+
for (const edit of edits) {
|
|
3526
|
+
const previous = record.files[edit.path];
|
|
3527
|
+
const entry = {
|
|
3528
|
+
created: edit.before === null || (previous?.created ?? false),
|
|
3529
|
+
initialHash: edit.before === null || previous?.initialHash === hash(edit.before) ? hash(edit.after) : previous?.initialHash ?? hash(edit.after),
|
|
3530
|
+
blocks: Object.fromEntries(BLOCKS.flatMap(([start, end]) => {
|
|
3531
|
+
const block = blockSpan(edit.after, start, end);
|
|
3532
|
+
return block ? [[start, hash(block.text)]] : [];
|
|
3533
|
+
}))
|
|
3534
|
+
};
|
|
3535
|
+
const mcp = edit.path === ".codex/config.toml" ? parse(edit.after).mcp_servers?.mason : edit.path === ".mcp.json" ? JSON.parse(edit.after).mcpServers?.mason : void 0;
|
|
3536
|
+
const hooks = edit.path === ".codex/hooks.json" || edit.path === ".claude/settings.json" ? JSON.parse(edit.after).hooks : void 0;
|
|
3537
|
+
record.files[edit.path] = {
|
|
3538
|
+
...entry,
|
|
3539
|
+
...mcp === void 0 ? {} : { mcpHash: hash(mcp) },
|
|
3540
|
+
...hookCommands[edit.path] ? { hookCommands: [.../* @__PURE__ */ new Set([...previous?.hookCommands ?? [], hookCommands[edit.path]])] } : {},
|
|
3541
|
+
...hooks === void 0 ? {} : { hooks: Object.fromEntries(Object.entries(hooks).map(([event, groups]) => [event, groups.flatMap((group) => group.hooks.map(hash))])) }
|
|
3542
|
+
};
|
|
3543
|
+
}
|
|
3544
|
+
return { path: OWNERSHIP_PATH, before, after: JSON.stringify(record, null, 2) + "\n" };
|
|
3545
|
+
}
|
|
3546
|
+
async function completedHookOwnership(root, file, command) {
|
|
3547
|
+
const before = await readText(root, OWNERSHIP_PATH);
|
|
3548
|
+
const record = ownershipSchema.parse(JSON.parse(before ?? "null"));
|
|
3549
|
+
record.files[file].hookCommands = [command];
|
|
3550
|
+
return { path: OWNERSHIP_PATH, before, after: JSON.stringify(record, null, 2) + "\n" };
|
|
3551
|
+
}
|
|
3552
|
+
var OWNERSHIP_PATH, BLOCKS, fileSchema, ownershipSchema;
|
|
3553
|
+
var init_ownership = __esm({
|
|
3554
|
+
"src/setup/ownership.ts"() {
|
|
3555
|
+
"use strict";
|
|
3556
|
+
init_evidence();
|
|
3557
|
+
init_files2();
|
|
3558
|
+
OWNERSHIP_PATH = ".mason/local/integration.json";
|
|
3559
|
+
BLOCKS = [
|
|
3560
|
+
["<!-- mason:start -->", "<!-- mason:end -->"],
|
|
3561
|
+
["<!-- mason:agents:start -->", "<!-- mason:agents:end -->"],
|
|
3562
|
+
["# mason:mcp:start", "# mason:mcp:end"]
|
|
3563
|
+
];
|
|
3564
|
+
fileSchema = z10.object({
|
|
3565
|
+
created: z10.boolean(),
|
|
3566
|
+
initialHash: z10.string(),
|
|
3567
|
+
blocks: z10.record(z10.string()),
|
|
3568
|
+
mcpHash: z10.string().optional(),
|
|
3569
|
+
hookCommands: z10.array(z10.string()).optional(),
|
|
3570
|
+
hooks: z10.record(z10.array(z10.string())).optional()
|
|
3571
|
+
});
|
|
3572
|
+
ownershipSchema = z10.object({ version: z10.literal(1), files: z10.record(fileSchema) });
|
|
3573
|
+
}
|
|
3574
|
+
});
|
|
3575
|
+
|
|
3497
3576
|
// src/automation/install.ts
|
|
3498
3577
|
var install_exports = {};
|
|
3499
3578
|
__export(install_exports, {
|
|
@@ -3502,24 +3581,28 @@ __export(install_exports, {
|
|
|
3502
3581
|
configPath: () => configPath,
|
|
3503
3582
|
installAutomation: () => installAutomation,
|
|
3504
3583
|
installedAutomation: () => installedAutomation,
|
|
3505
|
-
planAutomationInstall: () => planAutomationInstall
|
|
3584
|
+
planAutomationInstall: () => planAutomationInstall,
|
|
3585
|
+
recordSchema: () => recordSchema
|
|
3506
3586
|
});
|
|
3507
|
-
import { z as
|
|
3587
|
+
import { z as z11 } from "zod";
|
|
3508
3588
|
async function installationRecord(root) {
|
|
3509
3589
|
return recordSchema.parse(await readStoreJson(root, AUTOMATION_PATH) ?? { version: 1, hosts: {} });
|
|
3510
3590
|
}
|
|
3511
3591
|
async function installAutomation(dir, host, command) {
|
|
3512
3592
|
const ws = await workspace(dir);
|
|
3513
|
-
return withLock(ws.root, ".mason/reports/
|
|
3593
|
+
return withLock(ws.root, ".mason/reports/setup-lock", () => installLocked(ws.root, host, command));
|
|
3514
3594
|
}
|
|
3515
3595
|
async function planAutomationInstall(root, host, command) {
|
|
3516
3596
|
const file = configPath(host);
|
|
3517
|
-
const
|
|
3597
|
+
const before = await readText(root, file);
|
|
3598
|
+
const existing = automationConfigSchema.parse(JSON.parse(before ?? "{}"));
|
|
3518
3599
|
const record = await installationRecord(root);
|
|
3519
3600
|
const desired = hookConfig(host, command);
|
|
3520
3601
|
const newCommand = desired.hooks.SessionStart[0].hooks[0].command;
|
|
3521
3602
|
const previous = record.hosts[host]?.command;
|
|
3522
|
-
const
|
|
3603
|
+
const ownershipText = await readText(root, OWNERSHIP_PATH);
|
|
3604
|
+
const pending = ownershipText === null ? [] : ownershipSchema.parse(JSON.parse(ownershipText)).files[file]?.hookCommands ?? [];
|
|
3605
|
+
const owned = /* @__PURE__ */ new Set([previous, ...pending, newCommand]);
|
|
3523
3606
|
const hooks = existing.hooks ?? {};
|
|
3524
3607
|
for (const event of HOOK_EVENTS) {
|
|
3525
3608
|
hooks[event] = (hooks[event] ?? []).map((group) => ({
|
|
@@ -3529,15 +3612,20 @@ async function planAutomationInstall(root, host, command) {
|
|
|
3529
3612
|
hooks[event].push(...desired.hooks[event]);
|
|
3530
3613
|
}
|
|
3531
3614
|
record.hosts[host] = { command: newCommand };
|
|
3532
|
-
return { file, config: { ...existing, hooks }, record, newCommand };
|
|
3615
|
+
return { file, before, config: { ...existing, hooks }, record, newCommand };
|
|
3533
3616
|
}
|
|
3534
3617
|
async function installLocked(root, host, command) {
|
|
3535
|
-
const { file, config, record, newCommand } = await planAutomationInstall(root, host, command);
|
|
3618
|
+
const { file, before, config, record, newCommand } = await planAutomationInstall(root, host, command);
|
|
3536
3619
|
const { ancillaryEdits: ancillaryEdits2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
3537
3620
|
const { applyEdit: applyEdit2 } = await Promise.resolve().then(() => (init_files2(), files_exports));
|
|
3538
|
-
|
|
3539
|
-
|
|
3621
|
+
const { ownershipEdit: ownershipEdit2, completedHookOwnership: completedHookOwnership2 } = await Promise.resolve().then(() => (init_ownership(), ownership_exports));
|
|
3622
|
+
const edit = { path: file, before, after: JSON.stringify(config, null, 2) + "\n" };
|
|
3623
|
+
const ancillary = await ancillaryEdits2(root);
|
|
3624
|
+
await applyEdit2(root, await ownershipEdit2(root, [edit], { [file]: newCommand }));
|
|
3625
|
+
for (const edit2 of ancillary) await applyEdit2(root, edit2);
|
|
3626
|
+
await applyEdit2(root, edit);
|
|
3540
3627
|
await writeStoreJson(root, AUTOMATION_PATH, record);
|
|
3628
|
+
await applyEdit2(root, await completedHookOwnership2(root, file, newCommand));
|
|
3541
3629
|
return {
|
|
3542
3630
|
version: 1,
|
|
3543
3631
|
host,
|
|
@@ -3575,9 +3663,11 @@ var init_install = __esm({
|
|
|
3575
3663
|
init_evidence();
|
|
3576
3664
|
init_adapters();
|
|
3577
3665
|
init_store();
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3666
|
+
init_files2();
|
|
3667
|
+
init_ownership();
|
|
3668
|
+
groupSchema = z11.object({ hooks: z11.array(z11.object({ type: z11.string(), command: z11.string().optional() }).passthrough()) }).passthrough();
|
|
3669
|
+
automationConfigSchema = z11.object({ hooks: z11.record(z11.array(groupSchema)).optional() }).passthrough();
|
|
3670
|
+
recordSchema = z11.object({ version: z11.literal(1), hosts: z11.record(z11.object({ command: z11.string() })) });
|
|
3581
3671
|
AUTOMATION_PATH = ".mason/local/automation.json";
|
|
3582
3672
|
configPath = (host) => host === "claude" ? ".claude/settings.json" : ".codex/hooks.json";
|
|
3583
3673
|
}
|
|
@@ -3592,7 +3682,7 @@ __export(model_exports, {
|
|
|
3592
3682
|
setupHostSchema: () => setupHostSchema,
|
|
3593
3683
|
setupSchema: () => setupSchema
|
|
3594
3684
|
});
|
|
3595
|
-
import { z as
|
|
3685
|
+
import { z as z12 } from "zod";
|
|
3596
3686
|
async function loadSetup(root) {
|
|
3597
3687
|
const raw = await readStoreJson(root, SETUP_PATH);
|
|
3598
3688
|
if (raw === null) return null;
|
|
@@ -3610,27 +3700,27 @@ var init_model = __esm({
|
|
|
3610
3700
|
"src/setup/model.ts"() {
|
|
3611
3701
|
"use strict";
|
|
3612
3702
|
init_storage();
|
|
3613
|
-
setupHostSchema =
|
|
3614
|
-
configuredVersion:
|
|
3615
|
-
revision:
|
|
3616
|
-
fingerprint:
|
|
3617
|
-
mcpFingerprint:
|
|
3618
|
-
instructions:
|
|
3703
|
+
setupHostSchema = z12.object({
|
|
3704
|
+
configuredVersion: z12.string(),
|
|
3705
|
+
revision: z12.string().uuid(),
|
|
3706
|
+
fingerprint: z12.string(),
|
|
3707
|
+
mcpFingerprint: z12.string(),
|
|
3708
|
+
instructions: z12.array(z12.string())
|
|
3619
3709
|
});
|
|
3620
|
-
setupSchema =
|
|
3710
|
+
setupSchema = z12.object({ version: z12.literal(2), hosts: z12.object({
|
|
3621
3711
|
codex: setupHostSchema.optional(),
|
|
3622
3712
|
claude: setupHostSchema.optional()
|
|
3623
3713
|
}) });
|
|
3624
3714
|
SETUP_PATH = ".mason/local/setup.json";
|
|
3625
|
-
receiptSchema =
|
|
3626
|
-
version:
|
|
3627
|
-
host:
|
|
3628
|
-
status:
|
|
3629
|
-
initialReportPath:
|
|
3630
|
-
initialBaselinePaths:
|
|
3631
|
-
root:
|
|
3632
|
-
revision:
|
|
3633
|
-
configuredAt:
|
|
3715
|
+
receiptSchema = z12.object({
|
|
3716
|
+
version: z12.literal(1),
|
|
3717
|
+
host: z12.enum(["codex", "claude"]),
|
|
3718
|
+
status: z12.enum(["installing", "configured"]),
|
|
3719
|
+
initialReportPath: z12.string(),
|
|
3720
|
+
initialBaselinePaths: z12.array(z12.string()),
|
|
3721
|
+
root: z12.string(),
|
|
3722
|
+
revision: z12.string().uuid(),
|
|
3723
|
+
configuredAt: z12.string().optional()
|
|
3634
3724
|
});
|
|
3635
3725
|
}
|
|
3636
3726
|
});
|
|
@@ -3662,7 +3752,7 @@ var init_launcher = __esm({
|
|
|
3662
3752
|
"use strict";
|
|
3663
3753
|
init_evidence();
|
|
3664
3754
|
init_model();
|
|
3665
|
-
executingVersion = () => true ? "0.
|
|
3755
|
+
executingVersion = () => true ? "0.16.0" : "development";
|
|
3666
3756
|
mcpCommand = (host) => globalCommand(["--setup-host", host, "mcp"]);
|
|
3667
3757
|
hookCommand = (host) => `mason --setup-host ${host} auto`;
|
|
3668
3758
|
}
|
|
@@ -3679,8 +3769,8 @@ __export(config_exports, {
|
|
|
3679
3769
|
mcpPath: () => mcpPath
|
|
3680
3770
|
});
|
|
3681
3771
|
import { isDeepStrictEqual } from "util";
|
|
3682
|
-
import { parse, stringify } from "smol-toml";
|
|
3683
|
-
import { z as
|
|
3772
|
+
import { parse as parse2, stringify } from "smol-toml";
|
|
3773
|
+
import { z as z13 } from "zod";
|
|
3684
3774
|
async function instructionEdits(root, host) {
|
|
3685
3775
|
const found = await Promise.all(DOC_CANDIDATES.map(async (file) => ({ path: file, text: await readText(root, file) })));
|
|
3686
3776
|
const existing = found.find((f) => f.text !== null);
|
|
@@ -3721,7 +3811,7 @@ async function mcpEdit(root, host) {
|
|
|
3721
3811
|
const servers2 = object.parse(config2.mcpServers ?? {});
|
|
3722
3812
|
return { path: file, before, after: JSON.stringify({ ...config2, mcpServers: { ...servers2, mason: managedMcp(servers2.mason, host) } }, null, 2) + "\n" };
|
|
3723
3813
|
}
|
|
3724
|
-
const config =
|
|
3814
|
+
const config = parse2(before ?? "");
|
|
3725
3815
|
const managed = (before ?? "").includes(TOML_START);
|
|
3726
3816
|
const servers = config.mcp_servers;
|
|
3727
3817
|
const desired = managedMcp(servers?.mason, host);
|
|
@@ -3733,7 +3823,7 @@ async function mcpEdit(root, host) {
|
|
|
3733
3823
|
for (const span of spans.reverse()) base = base.slice(0, span.start) + base.slice(span.end);
|
|
3734
3824
|
}
|
|
3735
3825
|
const after = managedBlock(base, TOML_START, TOML_END, stringify({ mcp_servers: { mason: desired } }).trimEnd());
|
|
3736
|
-
const parsed =
|
|
3826
|
+
const parsed = parse2(after);
|
|
3737
3827
|
const expected = { ...config, mcp_servers: { ...servers ?? {}, mason: desired } };
|
|
3738
3828
|
if (!isDeepStrictEqual(parsed, expected)) throw new Error("Could not safely configure the Mason MCP entry without changing other settings.");
|
|
3739
3829
|
return { path: file, before, after };
|
|
@@ -3771,7 +3861,7 @@ async function ancillaryEdits(root) {
|
|
|
3771
3861
|
}
|
|
3772
3862
|
async function inspectHostConfig(root, host, plannedMcp) {
|
|
3773
3863
|
const text2 = plannedMcp ?? await readText(root, mcpPath(host));
|
|
3774
|
-
const config = host === "codex" ?
|
|
3864
|
+
const config = host === "codex" ? parse2(text2 ?? "") : object.parse(JSON.parse(text2 ?? "{}"));
|
|
3775
3865
|
const servers = host === "codex" ? config.mcp_servers : config.mcpServers;
|
|
3776
3866
|
const hooks = automationConfigSchema.parse(JSON.parse(await readText(root, configPath(host)) ?? "{}"));
|
|
3777
3867
|
const expected = hookConfig(host, hookCommand(host));
|
|
@@ -3796,7 +3886,7 @@ var init_config = __esm({
|
|
|
3796
3886
|
init_install();
|
|
3797
3887
|
init_files2();
|
|
3798
3888
|
init_launcher();
|
|
3799
|
-
object =
|
|
3889
|
+
object = z13.record(z13.unknown());
|
|
3800
3890
|
mcpPath = (host) => host === "codex" ? ".codex/config.toml" : ".mcp.json";
|
|
3801
3891
|
TOML_START = "# mason:mcp:start";
|
|
3802
3892
|
TOML_END = "# mason:mcp:end";
|
|
@@ -3806,7 +3896,7 @@ var init_config = __esm({
|
|
|
3806
3896
|
});
|
|
3807
3897
|
|
|
3808
3898
|
// src/mcp/init.ts
|
|
3809
|
-
import { z as
|
|
3899
|
+
import { z as z14 } from "zod";
|
|
3810
3900
|
async function loadProjectMarker(rootDir) {
|
|
3811
3901
|
const raw = await readStoreJson(rootDir, ".mason/local/project.json");
|
|
3812
3902
|
if (raw === null) return null;
|
|
@@ -3814,8 +3904,8 @@ async function loadProjectMarker(rootDir) {
|
|
|
3814
3904
|
return { ...marker, features: await loadProjectFeatures(rootDir) };
|
|
3815
3905
|
}
|
|
3816
3906
|
async function loadProjectFeatures(rootDir) {
|
|
3817
|
-
const config =
|
|
3818
|
-
return
|
|
3907
|
+
const config = z14.record(z14.unknown()).parse(await readStoreJson(rootDir, ".mason/config.json") ?? {});
|
|
3908
|
+
return z14.object({ confluence: z14.boolean().optional() }).passthrough().parse(config.features ?? {});
|
|
3819
3909
|
}
|
|
3820
3910
|
async function saveProjectMarker(rootDir, marker) {
|
|
3821
3911
|
const { ancillaryEdits: ancillaryEdits2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
@@ -3827,8 +3917,8 @@ async function projectMarkerEdits(rootDir, marker) {
|
|
|
3827
3917
|
const edits = [{ path: file, before: await readText(rootDir, file), after: JSON.stringify(local, null, 2) + "\n" }];
|
|
3828
3918
|
if (features && Object.keys(features).length) {
|
|
3829
3919
|
const configPath2 = ".mason/config.json", before = await readText(rootDir, configPath2);
|
|
3830
|
-
const config =
|
|
3831
|
-
config.features = { ...
|
|
3920
|
+
const config = z14.record(z14.unknown()).parse(JSON.parse(before ?? "{}"));
|
|
3921
|
+
config.features = { ...z14.record(z14.unknown()).parse(config.features ?? {}), ...features };
|
|
3832
3922
|
edits.push({ path: configPath2, before, after: JSON.stringify(config, null, 2) + "\n" });
|
|
3833
3923
|
}
|
|
3834
3924
|
return edits;
|
|
@@ -3842,10 +3932,10 @@ var init_init = __esm({
|
|
|
3842
3932
|
"use strict";
|
|
3843
3933
|
init_storage();
|
|
3844
3934
|
init_files2();
|
|
3845
|
-
markerSchema =
|
|
3846
|
-
version:
|
|
3847
|
-
initializedAt:
|
|
3848
|
-
features:
|
|
3935
|
+
markerSchema = z14.object({
|
|
3936
|
+
version: z14.literal(1),
|
|
3937
|
+
initializedAt: z14.string(),
|
|
3938
|
+
features: z14.object({ confluence: z14.boolean().optional() }).passthrough().optional()
|
|
3849
3939
|
}).passthrough();
|
|
3850
3940
|
CLAUDE_MD_SECTION = `<!-- mason:start -->
|
|
3851
3941
|
## Mason project knowledge
|
|
@@ -4039,7 +4129,7 @@ var init_types2 = __esm({
|
|
|
4039
4129
|
});
|
|
4040
4130
|
|
|
4041
4131
|
// src/review/evidence/vitest.ts
|
|
4042
|
-
import { z as
|
|
4132
|
+
import { z as z15 } from "zod";
|
|
4043
4133
|
function parseVitest(raw, sourceRoot) {
|
|
4044
4134
|
const report = schema.parse(raw);
|
|
4045
4135
|
const findings = [], diagnostics = [];
|
|
@@ -4088,24 +4178,24 @@ var init_vitest = __esm({
|
|
|
4088
4178
|
"use strict";
|
|
4089
4179
|
init_paths2();
|
|
4090
4180
|
init_types2();
|
|
4091
|
-
count2 =
|
|
4092
|
-
schema =
|
|
4093
|
-
success:
|
|
4181
|
+
count2 = z15.number().int().nonnegative();
|
|
4182
|
+
schema = z15.object({
|
|
4183
|
+
success: z15.boolean(),
|
|
4094
4184
|
numTotalTests: count2,
|
|
4095
4185
|
numPassedTests: count2,
|
|
4096
4186
|
numFailedTests: count2,
|
|
4097
4187
|
numPendingTests: count2,
|
|
4098
4188
|
numTodoTests: count2,
|
|
4099
4189
|
numFailedTestSuites: count2,
|
|
4100
|
-
testResults:
|
|
4101
|
-
name:
|
|
4102
|
-
status:
|
|
4103
|
-
message:
|
|
4104
|
-
assertionResults:
|
|
4105
|
-
fullName:
|
|
4106
|
-
status:
|
|
4107
|
-
failureMessages:
|
|
4108
|
-
location:
|
|
4190
|
+
testResults: z15.array(z15.object({
|
|
4191
|
+
name: z15.string().min(1),
|
|
4192
|
+
status: z15.enum(["passed", "failed"]),
|
|
4193
|
+
message: z15.string().optional(),
|
|
4194
|
+
assertionResults: z15.array(z15.object({
|
|
4195
|
+
fullName: z15.string(),
|
|
4196
|
+
status: z15.enum(["passed", "failed", "pending", "skipped", "todo", "disabled"]),
|
|
4197
|
+
failureMessages: z15.array(z15.string()).nullable().optional(),
|
|
4198
|
+
location: z15.object({ line: count2, column: count2 }).nullable().optional()
|
|
4109
4199
|
}))
|
|
4110
4200
|
}))
|
|
4111
4201
|
});
|
|
@@ -4113,7 +4203,7 @@ var init_vitest = __esm({
|
|
|
4113
4203
|
});
|
|
4114
4204
|
|
|
4115
4205
|
// src/review/evidence/sarif.ts
|
|
4116
|
-
import { z as
|
|
4206
|
+
import { z as z16 } from "zod";
|
|
4117
4207
|
function parseSarif(raw, sourceRoot) {
|
|
4118
4208
|
const report = schema2.parse(raw), findings = [], diagnostics = [];
|
|
4119
4209
|
let active = 0, suppressed = 0, absent = 0, unresolved = 0;
|
|
@@ -4204,29 +4294,29 @@ var init_sarif = __esm({
|
|
|
4204
4294
|
"use strict";
|
|
4205
4295
|
init_paths2();
|
|
4206
4296
|
init_types2();
|
|
4207
|
-
index =
|
|
4208
|
-
artifact =
|
|
4209
|
-
message =
|
|
4210
|
-
level =
|
|
4211
|
-
location =
|
|
4212
|
-
schema2 =
|
|
4213
|
-
version:
|
|
4214
|
-
runs:
|
|
4215
|
-
tool:
|
|
4216
|
-
invocations:
|
|
4217
|
-
versionControlProvenance:
|
|
4218
|
-
originalUriBaseIds:
|
|
4219
|
-
artifacts:
|
|
4220
|
-
results:
|
|
4221
|
-
ruleId:
|
|
4297
|
+
index = z16.number().int().nonnegative();
|
|
4298
|
+
artifact = z16.object({ uri: z16.string().optional(), uriBaseId: z16.string().optional(), index: index.optional() });
|
|
4299
|
+
message = z16.object({ text: z16.string().optional(), markdown: z16.string().optional(), id: z16.string().optional(), arguments: z16.array(z16.string()).optional() });
|
|
4300
|
+
level = z16.enum(["error", "warning", "note", "none"]);
|
|
4301
|
+
location = z16.object({ physicalLocation: z16.object({ artifactLocation: artifact.optional(), region: z16.object({ startLine: index.optional(), startColumn: index.optional() }).optional() }).optional() });
|
|
4302
|
+
schema2 = z16.object({
|
|
4303
|
+
version: z16.literal("2.1.0"),
|
|
4304
|
+
runs: z16.array(z16.object({
|
|
4305
|
+
tool: z16.object({ driver: z16.object({ name: z16.string().min(1), rules: z16.array(z16.object({ id: z16.string(), defaultConfiguration: z16.object({ level: level.optional() }).optional(), messageStrings: z16.record(message).optional() })).optional(), globalMessageStrings: z16.record(message).optional() }) }),
|
|
4306
|
+
invocations: z16.array(z16.object({ executionSuccessful: z16.boolean(), commandLine: z16.string().optional(), toolExecutionNotifications: z16.array(z16.object({ level: level.optional(), message })).optional() })).optional(),
|
|
4307
|
+
versionControlProvenance: z16.array(z16.object({ revisionId: z16.string().optional() })).optional(),
|
|
4308
|
+
originalUriBaseIds: z16.record(artifact).optional(),
|
|
4309
|
+
artifacts: z16.array(z16.object({ location: artifact.optional() })).optional(),
|
|
4310
|
+
results: z16.array(z16.object({
|
|
4311
|
+
ruleId: z16.string().optional(),
|
|
4222
4312
|
ruleIndex: index.optional(),
|
|
4223
4313
|
message,
|
|
4224
4314
|
level: level.optional(),
|
|
4225
|
-
kind:
|
|
4226
|
-
baselineState:
|
|
4227
|
-
suppressions:
|
|
4228
|
-
locations:
|
|
4229
|
-
relatedLocations:
|
|
4315
|
+
kind: z16.enum(["fail", "pass", "open", "informational", "notApplicable", "review"]).optional(),
|
|
4316
|
+
baselineState: z16.enum(["new", "unchanged", "updated", "absent"]).optional(),
|
|
4317
|
+
suppressions: z16.array(z16.object({ kind: z16.enum(["inSource", "external"]), status: z16.enum(["accepted", "underReview", "rejected"]).optional() })).nullable().optional(),
|
|
4318
|
+
locations: z16.array(location).optional(),
|
|
4319
|
+
relatedLocations: z16.array(location).optional()
|
|
4230
4320
|
})).optional()
|
|
4231
4321
|
}))
|
|
4232
4322
|
});
|
|
@@ -4235,7 +4325,7 @@ var init_sarif = __esm({
|
|
|
4235
4325
|
|
|
4236
4326
|
// src/review/evidence.ts
|
|
4237
4327
|
import path21 from "path";
|
|
4238
|
-
import { z as
|
|
4328
|
+
import { z as z17 } from "zod";
|
|
4239
4329
|
function relativeArtifact(root, file) {
|
|
4240
4330
|
const relative = path21.isAbsolute(file) ? path21.relative(root, file) : file;
|
|
4241
4331
|
const normalized = normalizeRepoPath(relative);
|
|
@@ -4276,7 +4366,7 @@ async function collectReviewEvidence(root, manifests, changedFiles, decisions, d
|
|
|
4276
4366
|
let manifest, raw;
|
|
4277
4367
|
try {
|
|
4278
4368
|
manifest = relativeArtifact(root, supplied);
|
|
4279
|
-
raw =
|
|
4369
|
+
raw = z17.object({ version: z17.literal(1), checks: z17.array(z17.unknown()) }).parse(await readStoreJson(root, manifest));
|
|
4280
4370
|
} catch (error) {
|
|
4281
4371
|
output.diagnostics.push(`${supplied}: ${String(error)}`);
|
|
4282
4372
|
continue;
|
|
@@ -4429,19 +4519,19 @@ var init_evidence2 = __esm({
|
|
|
4429
4519
|
init_vitest();
|
|
4430
4520
|
init_sarif();
|
|
4431
4521
|
init_types2();
|
|
4432
|
-
checkSchema2 =
|
|
4433
|
-
id:
|
|
4434
|
-
kind:
|
|
4435
|
-
tool:
|
|
4436
|
-
command:
|
|
4437
|
-
commit:
|
|
4438
|
-
workingTreeClean:
|
|
4439
|
-
source:
|
|
4440
|
-
sourceRoot:
|
|
4441
|
-
status:
|
|
4442
|
-
reason:
|
|
4443
|
-
exitCode:
|
|
4444
|
-
report:
|
|
4522
|
+
checkSchema2 = z17.object({
|
|
4523
|
+
id: z17.string().min(1).max(100),
|
|
4524
|
+
kind: z17.enum(["tests", "static-analysis", "security", "complexity", "duplication"]),
|
|
4525
|
+
tool: z17.string().min(1).max(200),
|
|
4526
|
+
command: z17.string().min(1).max(2e3),
|
|
4527
|
+
commit: z17.string().regex(/^(?:[a-fA-F0-9]{40}|[a-fA-F0-9]{64})$/).nullable().optional(),
|
|
4528
|
+
workingTreeClean: z17.boolean().optional(),
|
|
4529
|
+
source: z17.string().max(2e3).optional(),
|
|
4530
|
+
sourceRoot: z17.string().min(1).max(4e3).optional(),
|
|
4531
|
+
status: z17.enum(["completed", "skipped", "unavailable"]).default("completed"),
|
|
4532
|
+
reason: z17.string().min(1).max(2e3).optional(),
|
|
4533
|
+
exitCode: z17.number().int().nullable().optional(),
|
|
4534
|
+
report: z17.object({ format: z17.enum(["vitest-json", "sarif"]), path: z17.string().min(1).max(4e3) }).optional()
|
|
4445
4535
|
});
|
|
4446
4536
|
}
|
|
4447
4537
|
});
|
|
@@ -5072,7 +5162,7 @@ __export(observations_exports, {
|
|
|
5072
5162
|
readObservation: () => readObservation
|
|
5073
5163
|
});
|
|
5074
5164
|
import fs18 from "fs/promises";
|
|
5075
|
-
import { z as
|
|
5165
|
+
import { z as z18 } from "zod";
|
|
5076
5166
|
function observationPath(directory, host, revision, version = executingVersion()) {
|
|
5077
5167
|
return `${directory}/activation/${host}-${revision}-${hash(version).slice(0, 12)}.json`;
|
|
5078
5168
|
}
|
|
@@ -5137,19 +5227,19 @@ var init_observations = __esm({
|
|
|
5137
5227
|
init_storage();
|
|
5138
5228
|
init_model();
|
|
5139
5229
|
init_launcher();
|
|
5140
|
-
observationSchema =
|
|
5141
|
-
version:
|
|
5142
|
-
masonVersion:
|
|
5143
|
-
root:
|
|
5144
|
-
revision:
|
|
5145
|
-
host:
|
|
5146
|
-
contextCalls:
|
|
5147
|
-
lastContextAt:
|
|
5148
|
-
sessions:
|
|
5149
|
-
events:
|
|
5150
|
-
at:
|
|
5151
|
-
verificationStatus:
|
|
5152
|
-
reportPath:
|
|
5230
|
+
observationSchema = z18.object({
|
|
5231
|
+
version: z18.literal(1),
|
|
5232
|
+
masonVersion: z18.string(),
|
|
5233
|
+
root: z18.string(),
|
|
5234
|
+
revision: z18.string(),
|
|
5235
|
+
host: z18.enum(["codex", "claude"]),
|
|
5236
|
+
contextCalls: z18.number().int().nonnegative(),
|
|
5237
|
+
lastContextAt: z18.string().optional(),
|
|
5238
|
+
sessions: z18.record(z18.object({
|
|
5239
|
+
events: z18.array(z18.enum(events)),
|
|
5240
|
+
at: z18.string(),
|
|
5241
|
+
verificationStatus: z18.string().optional(),
|
|
5242
|
+
reportPath: z18.string().optional()
|
|
5153
5243
|
}))
|
|
5154
5244
|
});
|
|
5155
5245
|
}
|
|
@@ -5289,6 +5379,8 @@ async function setupProject(dir, options = {}) {
|
|
|
5289
5379
|
const setupBefore = await readText(ws.root, SETUP_PATH);
|
|
5290
5380
|
const mcpFingerprint = hash((await inspectHostConfig(ws.root, host, mcp.after)).mcp);
|
|
5291
5381
|
const desiredHooks = hookConfig(host, hookCommand(host)).hooks;
|
|
5382
|
+
const configuredHook = desiredHooks.SessionStart[0].hooks[0].command;
|
|
5383
|
+
const ownership = await ownershipEdit(ws.root, [...instructions, mcp, hooks[0]], { [hooks[0].path]: configuredHook });
|
|
5292
5384
|
const fingerprint = hash({
|
|
5293
5385
|
command: "mason",
|
|
5294
5386
|
mcp: mcpFingerprint,
|
|
@@ -5315,8 +5407,10 @@ async function setupProject(dir, options = {}) {
|
|
|
5315
5407
|
revision
|
|
5316
5408
|
});
|
|
5317
5409
|
const changedFiles = [];
|
|
5410
|
+
if (await applyEdit(ws.root, ownership)) changedFiles.push(ownership.path);
|
|
5318
5411
|
for (const edit of edits) if (await applyEdit(ws.root, edit)) changedFiles.push(edit.path);
|
|
5319
5412
|
if (await applyEdit(ws.root, { path: SETUP_PATH, before: setupBefore, after: JSON.stringify(setup, null, 2) + "\n" })) changedFiles.push(SETUP_PATH);
|
|
5413
|
+
if (await applyEdit(ws.root, await completedHookOwnership(ws.root, hooks[0].path, configuredHook)) && !changedFiles.includes(ownership.path)) changedFiles.push(ownership.path);
|
|
5320
5414
|
const checked = await automate(ws.root, { event: "turn_start" });
|
|
5321
5415
|
const configured = await inspectHostConfig(ws.root, host, void 0);
|
|
5322
5416
|
await writeStoreJson(ws.root, receiptPath, {
|
|
@@ -5375,6 +5469,7 @@ var init_setup = __esm({
|
|
|
5375
5469
|
init_adapters();
|
|
5376
5470
|
init_model();
|
|
5377
5471
|
init_status();
|
|
5472
|
+
init_ownership();
|
|
5378
5473
|
}
|
|
5379
5474
|
});
|
|
5380
5475
|
|
|
@@ -6255,7 +6350,7 @@ var init_sync = __esm({
|
|
|
6255
6350
|
init_provenance();
|
|
6256
6351
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6257
6352
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6258
|
-
import { z as
|
|
6353
|
+
import { z as z19 } from "zod";
|
|
6259
6354
|
|
|
6260
6355
|
// src/mcp/tools.ts
|
|
6261
6356
|
init_repair();
|
|
@@ -8076,7 +8171,7 @@ function createMcpServer() {
|
|
|
8076
8171
|
const server = new McpServer(
|
|
8077
8172
|
{
|
|
8078
8173
|
name: "mason",
|
|
8079
|
-
version: "0.
|
|
8174
|
+
version: "0.16.0"
|
|
8080
8175
|
},
|
|
8081
8176
|
{
|
|
8082
8177
|
instructions: 'Mason retrieves recorded decisions, file impact, and optional feature/flow maps. Use get_context with the task and known files, and get_impact before editing. Save learned rationale and constraints with save_decision; review and commit the local records through the project workflow. These tools work without initialization or a concept map. Consult trust and diagnostics: changed or unknown freshness needs source inspection, failed verification needs correction, and proposals are suggestions and legacy records are unreviewed. Accepted decisions are recorded team constraints, still subject to freshness checks. Use review_decision to prepare code evidence and record only authorized acceptance, reaffirmation, or retirement. Never invent a reviewer or treat these recorded identities as authenticated approval. mason_init returns documentation audit and committed-diff review findings with a quickstart guide. Use mode: "map" only when a full architecture map is requested. A missing map is not a setup failure; use decisions and source evidence. get_snapshot provides architecture navigation when a map is available. The mason-audit and mason-review CLIs also work without setup.'
|
|
@@ -8086,11 +8181,11 @@ function createMcpServer() {
|
|
|
8086
8181
|
"mason_init",
|
|
8087
8182
|
"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.",
|
|
8088
8183
|
{
|
|
8089
|
-
dir:
|
|
8090
|
-
mode:
|
|
8091
|
-
host:
|
|
8092
|
-
base:
|
|
8093
|
-
evidence:
|
|
8184
|
+
dir: z19.string().describe("Absolute path to the project root directory"),
|
|
8185
|
+
mode: z19.enum(["quickstart", "map", "setup"]).optional().default("quickstart").describe("Quickstart inspects without edits; map requests an architecture build; setup installs the shared onboarding flow."),
|
|
8186
|
+
host: z19.enum(["codex", "claude"]).optional().describe("Assistant to configure in setup mode; inferred only when unambiguous."),
|
|
8187
|
+
base: z19.string().optional().describe("Git ref for committed-diff review. Defaults to the first available main branch ref."),
|
|
8188
|
+
evidence: z19.array(z19.string()).max(10).optional().describe("Repository-local CI evidence manifests to include in the review. Imports Vitest JSON and SARIF without executing check commands.")
|
|
8094
8189
|
},
|
|
8095
8190
|
async ({ dir, mode, host, base, evidence }) => {
|
|
8096
8191
|
const result = await masonInit(dir, { mode, host, base, evidence });
|
|
@@ -8101,8 +8196,8 @@ function createMcpServer() {
|
|
|
8101
8196
|
"mason_automation",
|
|
8102
8197
|
"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.",
|
|
8103
8198
|
{
|
|
8104
|
-
dir:
|
|
8105
|
-
action:
|
|
8199
|
+
dir: z19.string().describe("Absolute path to the project directory"),
|
|
8200
|
+
action: z19.enum(["status", "check"]).describe("Inspect configuration and receipts, or capture/resume and verify original audit evidence.")
|
|
8106
8201
|
},
|
|
8107
8202
|
async ({ dir, action }) => ({ content: [{ type: "text", text: await masonAutomation(dir, action) }] })
|
|
8108
8203
|
);
|
|
@@ -8110,10 +8205,10 @@ function createMcpServer() {
|
|
|
8110
8205
|
"mason_repair",
|
|
8111
8206
|
"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.",
|
|
8112
8207
|
{
|
|
8113
|
-
dir:
|
|
8114
|
-
action:
|
|
8115
|
-
baselinePath:
|
|
8116
|
-
checks:
|
|
8208
|
+
dir: z19.string().describe("Absolute path to the project root directory"),
|
|
8209
|
+
action: z19.enum(["prepare", "verify"]),
|
|
8210
|
+
baselinePath: z19.string().optional().describe("Original baseline path returned by prepare; required for verify."),
|
|
8211
|
+
checks: z19.array(z19.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.")
|
|
8117
8212
|
},
|
|
8118
8213
|
async ({ dir, action, baselinePath, checks }) => {
|
|
8119
8214
|
const result = await masonRepair(dir, { action, baselinePath, checks });
|
|
@@ -8124,8 +8219,8 @@ function createMcpServer() {
|
|
|
8124
8219
|
"mason_complete_init",
|
|
8125
8220
|
"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.",
|
|
8126
8221
|
{
|
|
8127
|
-
dir:
|
|
8128
|
-
confluenceConfigured:
|
|
8222
|
+
dir: z19.string().describe("Absolute path to the project root directory"),
|
|
8223
|
+
confluenceConfigured: z19.boolean().optional().describe("Set the Confluence setup status; omit to preserve the existing value")
|
|
8129
8224
|
},
|
|
8130
8225
|
async ({ dir, confluenceConfigured }) => {
|
|
8131
8226
|
const result = await masonCompleteInit(dir, { confluenceConfigured });
|
|
@@ -8136,11 +8231,11 @@ function createMcpServer() {
|
|
|
8136
8231
|
"mason_set_confluence",
|
|
8137
8232
|
"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.",
|
|
8138
8233
|
{
|
|
8139
|
-
baseUrl:
|
|
8140
|
-
email:
|
|
8141
|
-
apiToken:
|
|
8142
|
-
spaceKey:
|
|
8143
|
-
parentPageId:
|
|
8234
|
+
baseUrl: z19.string().describe("Confluence base URL. Accepts `acme`, `acme.atlassian.net`, or `https://acme.atlassian.net` (normalized automatically)."),
|
|
8235
|
+
email: z19.string().describe("User's Atlassian account email"),
|
|
8236
|
+
apiToken: z19.string().describe("API token from id.atlassian.com/manage-profile/security/api-tokens"),
|
|
8237
|
+
spaceKey: z19.string().optional().describe("Confluence space key. Omit on the first call to list available spaces."),
|
|
8238
|
+
parentPageId: z19.string().optional().describe("Optional parent page ID under which Mason's index page is created")
|
|
8144
8239
|
},
|
|
8145
8240
|
async ({ baseUrl, email, apiToken, spaceKey, parentPageId }) => {
|
|
8146
8241
|
const result = await masonSetConfluence({
|
|
@@ -8157,7 +8252,7 @@ function createMcpServer() {
|
|
|
8157
8252
|
"full_analysis",
|
|
8158
8253
|
"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.",
|
|
8159
8254
|
{
|
|
8160
|
-
dir:
|
|
8255
|
+
dir: z19.string().describe("Absolute path to the project root directory")
|
|
8161
8256
|
},
|
|
8162
8257
|
async ({ dir }) => {
|
|
8163
8258
|
const result = await fullAnalysis(dir);
|
|
@@ -8170,7 +8265,7 @@ function createMcpServer() {
|
|
|
8170
8265
|
"analyze_project",
|
|
8171
8266
|
"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.",
|
|
8172
8267
|
{
|
|
8173
|
-
dir:
|
|
8268
|
+
dir: z19.string().describe("Absolute path to the project root directory")
|
|
8174
8269
|
},
|
|
8175
8270
|
async ({ dir }) => {
|
|
8176
8271
|
const result = await analyzeProject(dir);
|
|
@@ -8183,8 +8278,8 @@ function createMcpServer() {
|
|
|
8183
8278
|
"get_code_samples",
|
|
8184
8279
|
"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.",
|
|
8185
8280
|
{
|
|
8186
|
-
dir:
|
|
8187
|
-
count:
|
|
8281
|
+
dir: z19.string().describe("Absolute path to the project root directory"),
|
|
8282
|
+
count: z19.number().optional().default(15).describe("Maximum number of files to sample (default: 15)")
|
|
8188
8283
|
},
|
|
8189
8284
|
async ({ dir, count: count3 }) => {
|
|
8190
8285
|
const result = await getCodeSamples(dir, count3);
|
|
@@ -8197,7 +8292,7 @@ function createMcpServer() {
|
|
|
8197
8292
|
"get_snapshot",
|
|
8198
8293
|
"Return the optional feature-to-file architecture map with drift and trust evidence. 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.",
|
|
8199
8294
|
{
|
|
8200
|
-
dir:
|
|
8295
|
+
dir: z19.string().describe("Absolute path to the project root directory")
|
|
8201
8296
|
},
|
|
8202
8297
|
async ({ dir }) => {
|
|
8203
8298
|
const result = await getSnapshot(dir);
|
|
@@ -8210,9 +8305,9 @@ function createMcpServer() {
|
|
|
8210
8305
|
"get_context",
|
|
8211
8306
|
"Assemble task context: matching decisions with rationale, approval, owner, sources, last review, and freshness, plus related tests, file impact, and any available map entries. 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.",
|
|
8212
8307
|
{
|
|
8213
|
-
dir:
|
|
8214
|
-
task:
|
|
8215
|
-
files:
|
|
8308
|
+
dir: z19.string().describe("Absolute path to the project root directory"),
|
|
8309
|
+
task: z19.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"),
|
|
8310
|
+
files: z19.array(z19.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.")
|
|
8216
8311
|
},
|
|
8217
8312
|
async ({ dir, task, files }) => {
|
|
8218
8313
|
const result = await getContext(dir, task, files);
|
|
@@ -8228,10 +8323,10 @@ function createMcpServer() {
|
|
|
8228
8323
|
"generate_snapshot_batch",
|
|
8229
8324
|
"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.",
|
|
8230
8325
|
{
|
|
8231
|
-
dir:
|
|
8232
|
-
offset:
|
|
8233
|
-
batchSize:
|
|
8234
|
-
files:
|
|
8326
|
+
dir: z19.string().describe("Absolute path to the project root directory"),
|
|
8327
|
+
offset: z19.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."),
|
|
8328
|
+
batchSize: z19.number().int().optional().describe("Files per batch. Defaults to 50."),
|
|
8329
|
+
files: z19.array(z19.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.")
|
|
8235
8330
|
},
|
|
8236
8331
|
async ({ dir, offset, batchSize, files }) => {
|
|
8237
8332
|
const result = await generateSnapshotBatch(dir, offset, batchSize, files);
|
|
@@ -8244,23 +8339,23 @@ function createMcpServer() {
|
|
|
8244
8339
|
"save_partial_snapshot",
|
|
8245
8340
|
"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.",
|
|
8246
8341
|
{
|
|
8247
|
-
dir:
|
|
8248
|
-
batchId:
|
|
8249
|
-
offset:
|
|
8250
|
-
features:
|
|
8251
|
-
|
|
8252
|
-
description:
|
|
8253
|
-
files:
|
|
8254
|
-
tests:
|
|
8255
|
-
type:
|
|
8342
|
+
dir: z19.string().describe("Absolute path to the project root directory"),
|
|
8343
|
+
batchId: z19.string().describe("The `batchId` returned by `generate_snapshot_batch`."),
|
|
8344
|
+
offset: z19.number().int().describe("The `offset` returned by `generate_snapshot_batch`. Used to order partials in the reduce step."),
|
|
8345
|
+
features: z19.record(
|
|
8346
|
+
z19.object({
|
|
8347
|
+
description: z19.string(),
|
|
8348
|
+
files: z19.array(z19.string()),
|
|
8349
|
+
tests: z19.array(z19.string()).optional(),
|
|
8350
|
+
type: z19.enum(["capability", "infrastructure"]).optional().describe(
|
|
8256
8351
|
'"capability" (user-facing functionality) or "infrastructure" (internal plumbing with no end user \u2014 DI/service wiring, config, logging, adapters). Defaults to "capability".'
|
|
8257
8352
|
)
|
|
8258
8353
|
})
|
|
8259
8354
|
).describe("Partial features for this batch only \u2014 files outside the batch will be added by other partials."),
|
|
8260
|
-
flows:
|
|
8261
|
-
|
|
8262
|
-
description:
|
|
8263
|
-
chain:
|
|
8355
|
+
flows: z19.record(
|
|
8356
|
+
z19.object({
|
|
8357
|
+
description: z19.string(),
|
|
8358
|
+
chain: z19.array(z19.string())
|
|
8264
8359
|
})
|
|
8265
8360
|
).describe("Partial flows whose entire chain is in this batch. Cross-batch flows are reconstructed in reduce.")
|
|
8266
8361
|
},
|
|
@@ -8275,7 +8370,7 @@ function createMcpServer() {
|
|
|
8275
8370
|
"reduce_snapshot",
|
|
8276
8371
|
"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).",
|
|
8277
8372
|
{
|
|
8278
|
-
dir:
|
|
8373
|
+
dir: z19.string().describe("Absolute path to the project root directory")
|
|
8279
8374
|
},
|
|
8280
8375
|
async ({ dir }) => {
|
|
8281
8376
|
const result = await reduceSnapshot(dir);
|
|
@@ -8288,25 +8383,25 @@ function createMcpServer() {
|
|
|
8288
8383
|
"save_snapshot",
|
|
8289
8384
|
"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.",
|
|
8290
8385
|
{
|
|
8291
|
-
dir:
|
|
8292
|
-
features:
|
|
8293
|
-
|
|
8294
|
-
description:
|
|
8295
|
-
files:
|
|
8296
|
-
tests:
|
|
8297
|
-
type:
|
|
8386
|
+
dir: z19.string().describe("Absolute path to the project root directory"),
|
|
8387
|
+
features: z19.record(
|
|
8388
|
+
z19.object({
|
|
8389
|
+
description: z19.string().describe("One-line description of the feature"),
|
|
8390
|
+
files: z19.array(z19.string()).describe("File paths that implement this feature"),
|
|
8391
|
+
tests: z19.array(z19.string()).optional().describe("Test file paths for this feature"),
|
|
8392
|
+
type: z19.enum(["capability", "infrastructure"]).optional().describe(
|
|
8298
8393
|
'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".'
|
|
8299
8394
|
)
|
|
8300
8395
|
})
|
|
8301
8396
|
).describe("Map of feature names to their implementing files"),
|
|
8302
|
-
flows:
|
|
8303
|
-
|
|
8304
|
-
description:
|
|
8305
|
-
chain:
|
|
8397
|
+
flows: z19.record(
|
|
8398
|
+
z19.object({
|
|
8399
|
+
description: z19.string().describe("One-line description of the flow"),
|
|
8400
|
+
chain: z19.array(z19.string()).describe("Ordered list of file paths showing data/call flow")
|
|
8306
8401
|
})
|
|
8307
8402
|
).describe("Map of flow names to ordered file chains"),
|
|
8308
|
-
removeFeatures:
|
|
8309
|
-
removeFlows:
|
|
8403
|
+
removeFeatures: z19.array(z19.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."),
|
|
8404
|
+
removeFlows: z19.array(z19.string()).optional().describe("Flow names to delete from the existing map. Applied before merging; only meaningful on incremental saves.")
|
|
8310
8405
|
},
|
|
8311
8406
|
async ({ dir, features, flows, removeFeatures, removeFlows }) => {
|
|
8312
8407
|
const result = await saveSnapshotData(
|
|
@@ -8325,17 +8420,17 @@ function createMcpServer() {
|
|
|
8325
8420
|
"save_decision",
|
|
8326
8421
|
"Capture or revise a decision proposal with rationale, anchors, optional owner, sources, and a known actor. 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.",
|
|
8327
8422
|
{
|
|
8328
|
-
dir:
|
|
8329
|
-
title:
|
|
8330
|
-
body:
|
|
8331
|
-
category:
|
|
8332
|
-
files:
|
|
8333
|
-
id:
|
|
8334
|
-
supersedes:
|
|
8423
|
+
dir: z19.string().describe("Absolute path to the project root directory"),
|
|
8424
|
+
title: z19.string().max(80).describe("Short, specific headline \u2014 becomes the stable record id"),
|
|
8425
|
+
body: z19.string().max(1500).describe("The knowledge itself: what was tried/decided, why, and what to avoid. Must contain information NOT derivable by reading the code."),
|
|
8426
|
+
category: z19.enum(["decision", "gotcha", "deprecation", "convention"]),
|
|
8427
|
+
files: z19.array(z19.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."),
|
|
8428
|
+
id: z19.string().optional().describe("Existing id to revise. Changed content becomes a proposal; unchanged content leaves the review and freshness untouched."),
|
|
8429
|
+
supersedes: z19.string().optional().describe("Id of an unreviewed record or proposal with no accepted revision to replace. Operative accepted decisions require separate review and retirement."),
|
|
8335
8430
|
owner: attributionSchema.shape.owner.describe("Responsible person or team, when known. Null clears it. Required for acceptance."),
|
|
8336
8431
|
sources: attributionSchema.shape.sources.describe("Known PR, issue, incident, discussion, or document references. Omit to preserve; [] clears. At least one is required for acceptance."),
|
|
8337
8432
|
actor: attributionSchema.shape.actor.describe("Known person or agent recording this revision. Omit if unknown; do not infer from Git identity."),
|
|
8338
|
-
force:
|
|
8433
|
+
force: z19.boolean().optional().describe("Save even when a near-duplicate was detected")
|
|
8339
8434
|
},
|
|
8340
8435
|
async ({ dir, title, body, category, files, id, supersedes, force, owner, sources, actor }) => {
|
|
8341
8436
|
const result = await saveDecision(dir, {
|
|
@@ -8357,12 +8452,12 @@ function createMcpServer() {
|
|
|
8357
8452
|
"review_decision",
|
|
8358
8453
|
"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.",
|
|
8359
8454
|
{
|
|
8360
|
-
dir:
|
|
8361
|
-
id:
|
|
8362
|
-
action:
|
|
8363
|
-
reviewer:
|
|
8364
|
-
note:
|
|
8365
|
-
reviewToken:
|
|
8455
|
+
dir: z19.string().describe("Absolute path to the project root directory"),
|
|
8456
|
+
id: z19.string().regex(/^[a-zA-Z0-9_-]+$/).describe("Decision id from get_context or save_decision"),
|
|
8457
|
+
action: z19.enum(["prepare", "accept", "reaffirm", "retire"]).optional().default("prepare").describe("Prepare is read-only. Other actions record an explicitly authorized review."),
|
|
8458
|
+
reviewer: z19.string().trim().min(1).max(200).optional().describe("Identity of the actual reviewer; required for a verdict. Never invent one."),
|
|
8459
|
+
note: z19.string().trim().min(1).max(1500).optional().describe("Review rationale; required for a verdict. Cite evidence for the decision."),
|
|
8460
|
+
reviewToken: z19.string().regex(/^[a-f0-9]{64}$/).optional().describe("Token from the prepared review; rejects stale record or code revisions")
|
|
8366
8461
|
},
|
|
8367
8462
|
async ({ dir, ...input }) => {
|
|
8368
8463
|
const result = await reviewDecision2(dir, input);
|
|
@@ -8373,7 +8468,7 @@ function createMcpServer() {
|
|
|
8373
8468
|
"mason_check_drift",
|
|
8374
8469
|
"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.",
|
|
8375
8470
|
{
|
|
8376
|
-
dir:
|
|
8471
|
+
dir: z19.string().describe("Absolute path to the project root directory")
|
|
8377
8472
|
},
|
|
8378
8473
|
async ({ dir }) => {
|
|
8379
8474
|
const result = await checkDrift(dir);
|
|
@@ -8386,8 +8481,8 @@ function createMcpServer() {
|
|
|
8386
8481
|
"verify_snapshot",
|
|
8387
8482
|
"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. Run periodically, or after an automated refresh wrote entries no human reviewed.",
|
|
8388
8483
|
{
|
|
8389
|
-
dir:
|
|
8390
|
-
sample:
|
|
8484
|
+
dir: z19.string().describe("Absolute path to the project root directory"),
|
|
8485
|
+
sample: z19.number().int().optional().describe("Entries to sample (default 5)")
|
|
8391
8486
|
},
|
|
8392
8487
|
async ({ dir, sample }) => {
|
|
8393
8488
|
const result = await verifySnapshot(dir, sample);
|
|
@@ -8398,11 +8493,11 @@ function createMcpServer() {
|
|
|
8398
8493
|
"save_verification",
|
|
8399
8494
|
"Record verify_snapshot verdicts. 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.",
|
|
8400
8495
|
{
|
|
8401
|
-
dir:
|
|
8402
|
-
verdicts:
|
|
8403
|
-
|
|
8404
|
-
ok:
|
|
8405
|
-
note:
|
|
8496
|
+
dir: z19.string().describe("Absolute path to the project root directory"),
|
|
8497
|
+
verdicts: z19.record(
|
|
8498
|
+
z19.object({
|
|
8499
|
+
ok: z19.boolean(),
|
|
8500
|
+
note: z19.string().optional().describe("One line on what's wrong \u2014 required when ok is false")
|
|
8406
8501
|
})
|
|
8407
8502
|
).describe("Entry name \u2192 verdict, exactly as returned by verify_snapshot")
|
|
8408
8503
|
},
|
|
@@ -8415,8 +8510,8 @@ function createMcpServer() {
|
|
|
8415
8510
|
"get_impact",
|
|
8416
8511
|
"Trace the impact of changing files: historical co-change partners, references, and related tests. Deterministic, read-only, and usable without initialization, saved decisions, or a concept map.",
|
|
8417
8512
|
{
|
|
8418
|
-
dir:
|
|
8419
|
-
files:
|
|
8513
|
+
dir: z19.string().describe("Absolute path to the project root directory"),
|
|
8514
|
+
files: z19.array(z19.string()).describe("File paths or names to analyze (e.g., ['WeatherRepository.kt'] or ['src/services/auth.ts'])")
|
|
8420
8515
|
},
|
|
8421
8516
|
async ({ dir, files }) => {
|
|
8422
8517
|
const result = await getImpact(dir, files);
|
|
@@ -8429,12 +8524,12 @@ function createMcpServer() {
|
|
|
8429
8524
|
"export_to_confluence",
|
|
8430
8525
|
"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.",
|
|
8431
8526
|
{
|
|
8432
|
-
dir:
|
|
8433
|
-
spaceKey:
|
|
8434
|
-
parentPageId:
|
|
8435
|
-
indexPageTitle:
|
|
8436
|
-
changelogPageTitle:
|
|
8437
|
-
featurePagePrefix:
|
|
8527
|
+
dir: z19.string().describe("Absolute path to the project root directory"),
|
|
8528
|
+
spaceKey: z19.string().optional().describe("Override the configured space key"),
|
|
8529
|
+
parentPageId: z19.string().optional().describe("Override the configured parent page ID"),
|
|
8530
|
+
indexPageTitle: z19.string().optional().describe("Title of the index page (default: 'Mason \u2014 System Map')"),
|
|
8531
|
+
changelogPageTitle: z19.string().optional().describe("Title of the changelog page (default: 'Mason \u2014 Changelog')"),
|
|
8532
|
+
featurePagePrefix: z19.string().optional().describe("Prefix for each feature page title (default: 'Feature: ')")
|
|
8438
8533
|
},
|
|
8439
8534
|
async ({ dir, spaceKey, parentPageId, indexPageTitle, changelogPageTitle, featurePagePrefix }) => {
|
|
8440
8535
|
const result = await exportToConfluenceTool(dir, {
|