@productbrain/mcp 0.0.1-beta.6507 → 0.0.1-beta.6513

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.
@@ -41,7 +41,7 @@ import {
41
41
  trackSessionCaptureRate,
42
42
  trackWriteBackHintServed,
43
43
  trackZeroCaptureAuditFired
44
- } from "./chunk-GLLZURWD.js";
44
+ } from "./chunk-KL74MUS5.js";
45
45
 
46
46
  // src/server.ts
47
47
  import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -292,10 +292,6 @@ function parseOrFail(schema, args) {
292
292
  result: { content: textContent(msg), structuredContent: validationError(msg) }
293
293
  };
294
294
  }
295
- function unknownAction(action, valid) {
296
- const msg = `Unknown action '${action}'. Valid actions: ${valid.join(", ")}.`;
297
- return { content: textContent(msg), structuredContent: validationError(msg) };
298
- }
299
295
  function successResult(text, summary, data, next) {
300
296
  return { content: textContent(text), structuredContent: success(summary, data, next) };
301
297
  }
@@ -565,8 +561,12 @@ var TOOL_NAME_MIGRATIONS = /* @__PURE__ */ new Map([
565
561
  ["get-usage-summary", 'workspace action="usage"'],
566
562
  ["audit", 'quality action="audit"'],
567
563
  ["governance-proposals", 'workspace action="proposals-list|proposals-respond|proposals-count" (list/respond/count \u2192 proposals-<same>)'],
568
- ["chain-version", 'chain action="version.*" (commit/list/diff/revert/history \u2192 version.<same>)'],
569
- ["chain-branch", 'chain-review action="branch.*" (create/list/merge/conflicts \u2192 branch.<same>)']
564
+ // WP-757 TASK-224 F2 (DEC-1722): the `chain` and `chain-review` MCP tools these two rows
565
+ // targeted are themselves deleted along with gitchain — retargeted from executable
566
+ // replacements to NOTICE rows (no equivalent command exists), same shape as
567
+ // `create-collection`/`map` above, so the coach never points an agent at a second dead name.
568
+ ["chain-version", "closed \u2014 the `chain` MCP tool has been retired along with gitchain. There is no supported way to commit/list/diff/revert/history a chain version through MCP."],
569
+ ["chain-branch", "closed \u2014 the `chain-review` MCP tool has been retired along with gitchain. There is no supported way to create/list/merge/check conflicts on a chain branch through MCP."]
570
570
  ]);
571
571
  function isExecutableReference(value) {
572
572
  return /^[\w./-]+(\s+action="[^"]*")?$/.test(value);
@@ -9617,7 +9617,7 @@ var IMPLEMENTATION_REVIEW_WORKFLOW_DESCRIPTOR = {
9617
9617
 
9618
9618
  ## Your Behavior
9619
9619
 
9620
- 1. **Tool-heavy, not chat-heavy.** Each round calls specific tools: orient, quality, verify, chain-review, context action=gather, review-against-rules. Use Context7 **CLI** (\`npx ctx7 library\` then \`npx ctx7 docs\` \u2014 project-local after \`npm install\`; avoid cold \`npx ctx7@latest\` in short-timeout shells) for relevant testing and framework docs \u2014 not for Chain truth. Do not use Context7 MCP.
9620
+ 1. **Tool-heavy, not chat-heavy.** Each round calls specific tools: orient, quality, verify, context action=gather, review-against-rules. Use Context7 **CLI** (\`npx ctx7 library\` then \`npx ctx7 docs\` \u2014 project-local after \`npm install\`; avoid cold \`npx ctx7@latest\` in short-timeout shells) for relevant testing and framework docs \u2014 not for Chain truth. Do not use Context7 MCP.
9621
9621
  2. **Spawn sub-agents** for parallel review: (a) code/architecture review, (b) test honesty (did we edit tests to pass or do they validate real behavior?). Max 2 agents in parallel, 30s budget each. **Constrain each sub-agent to the stated review scope only.**
9622
9622
  3. **Fix-as-you-go (FEAT-172).** When you find a HIGH or MEDIUM issue: fix it immediately, verify (lints, types), then proceed. The user sees clean work, not a list of issues. Only defer if estimated >5 min \u2014 capture deferred items as tensions. If user says "skip", capture as tension and continue.
9623
9623
  4. **Chain is SSOT.** Call orient or start first. Use entries action=search and context action=gather to load relevant BETs, DECs, BRs. If findings conflict with Chain entries, report the conflict and cite the entry ID.
@@ -9629,8 +9629,7 @@ var IMPLEMENTATION_REVIEW_WORKFLOW_DESCRIPTOR = {
9629
9629
  ## Tool Usage
9630
9630
 
9631
9631
  - \`orient\` or \`start\` \u2014 governance, active work packages, stale entries
9632
- - \`quality action=check entryId="<BET-ID>"\` \u2014 score against collection criteria
9633
- - \`chain-review action=gate chainEntryId="<id>"\` \u2014 coherence gate for chains
9632
+ - \`quality action=check entryId="<BET-ID>"\` \u2014 score against collection criteria (chain-type entries included; the chain-review tool this row named is retired, DEC-1722)
9634
9633
  - \`quality action=verify-chain\` \u2014 glossary code mappings, cross-references vs codebase
9635
9634
  - \`review-against-rules\` (domain) \u2014 business rule compliance
9636
9635
  - \`context action=gather task="implementation review for [BET-ID]"\` \u2014 related knowledge
@@ -9671,8 +9670,8 @@ This block is the final deliverable of the review. Everything else is supporting
9671
9670
  num: "02",
9672
9671
  label: "Standards & Chain Coherence",
9673
9672
  type: "open",
9674
- instruction: "Run quality check, chain-review gate (if applicable), review-against-rules, and codebase coherence gate. Is the implementation coherent with the Chain SSOT? Did it introduce or fix registry drift?",
9675
- facilitatorGuidance: "For the BET/entry: quality action=check entryId='<ID>'. If the work produced a chain artifact: chain-review action=gate. Call review-against-rules with the relevant domain (e.g. 'Governance & Decision-Making'). **Codebase Coherence Gate (BET-99/FEAT-183):** Compare the Codebase Coherence section from Round 01's orient output against the current state. If orient showed coherence gaps and this implementation touched registry files (collection colors, routes, classification, renderers, etc.), check whether gaps increased or decreased. Gaps increased \u2192 flag as 'Coherence: WARN \u2014 N new gaps introduced' and list them. Gaps decreased \u2192 celebrate as 'Coherence: PASS \u2014 N gaps fixed.' No change \u2192 note as 'Coherence: PASS \u2014 no change.' If Round 01 did not show a coherence section, skip the gate \u2014 do not block on missing baseline. Present: quality score, gate result, rule compliance, coherence gate verdict.",
9673
+ instruction: "Run quality check (chain-type entries included \u2014 the chain-review tool is retired, DEC-1722), review-against-rules, and codebase coherence gate. Is the implementation coherent with the Chain SSOT? Did it introduce or fix registry drift?",
9674
+ facilitatorGuidance: "For the BET/entry: quality action=check entryId='<ID>' \u2014 this covers a chain artifact too now. Call review-against-rules with the relevant domain (e.g. 'Governance & Decision-Making'). **Codebase Coherence Gate (BET-99/FEAT-183):** Compare the Codebase Coherence section from Round 01's orient output against the current state. If orient showed coherence gaps and this implementation touched registry files (collection colors, routes, classification, renderers, etc.), check whether gaps increased or decreased. Gaps increased \u2192 flag as 'Coherence: WARN \u2014 N new gaps introduced' and list them. Gaps decreased \u2192 celebrate as 'Coherence: PASS \u2014 N gaps fixed.' No change \u2192 note as 'Coherence: PASS \u2014 no change.' If Round 01 did not show a coherence section, skip the gate \u2014 do not block on missing baseline. Present: quality score, gate result, rule compliance, coherence gate verdict.",
9676
9675
  outputSchema: {
9677
9676
  field: "standards",
9678
9677
  description: "Quality score, gate result, rule compliance, coherence gate verdict (improved/degraded/unchanged)",
@@ -12440,735 +12439,11 @@ async function handleStatus() {
12440
12439
  };
12441
12440
  }
12442
12441
 
12443
- // src/tools/gitchain.ts
12444
- import { z as z27 } from "zod/v3";
12445
-
12446
- // src/lib/versionDisplay.ts
12447
- function toVersionDisplay(version) {
12448
- if (version == null || version === 0) return "Draft";
12449
- return `v${version}`;
12450
- }
12451
-
12452
- // src/tools/gitchain.ts
12453
- var chainSchema = z27.object({
12454
- action: z27.enum(["create", "get", "list", "edit"]).describe("Action: create a process, get process details, list all processes, or edit a process link"),
12455
- chainEntryId: z27.string().max(200).optional().describe("Chain entry ID (required for get/edit)"),
12456
- title: z27.string().max(500).optional().describe("Process title (required for create)"),
12457
- chainTypeId: z27.string().max(200).optional().default("strategy-coherence").describe("Process template slug for create: 'strategy-coherence', 'idm-proposal', or any custom template slug"),
12458
- description: z27.string().max(2e4).optional().describe("Description (for create)"),
12459
- linkId: z27.string().max(200).optional().describe("Link to edit (for edit action): problem, insight, choice, action, outcome"),
12460
- content: z27.string().max(5e4).optional().describe("New content for the link (for edit action)"),
12461
- status: z27.string().max(200).optional().describe("Filter by status for list: 'draft' or 'active'"),
12462
- author: z27.string().max(200).optional().describe("Who is performing the action. Defaults to 'mcp'."),
12463
- // WP-513 review round 3 (P1): team+role to create AS OWNER of (rung 2 only) — entry ID or entryId (e.g. "TEAM-1").
12464
- ownerTeamEntryId: z27.string().max(200).optional().describe("For 'create': owning team (rung-2 workspaces)."),
12465
- ownerRoleEntryId: z27.string().max(200).optional().describe("For 'create': owning role (rung-2 workspaces).")
12466
- });
12467
- var chainVersionSchema = z27.object({
12468
- action: z27.enum(["commit", "list", "diff", "revert", "history"]).describe("Action: commit a snapshot, list commits, diff two versions, revert to a version, or view history"),
12469
- chainEntryId: z27.string().max(200).describe("The chain's entry ID"),
12470
- commitMessage: z27.string().max(2e3).optional().describe("Commit message (required for commit). Convention: type(link): description"),
12471
- versionA: z27.number().optional().describe("Earlier version for diff"),
12472
- versionB: z27.number().optional().describe("Later version for diff"),
12473
- toVersion: z27.number().optional().describe("Version number to revert to"),
12474
- author: z27.string().max(200).optional().describe("Who is performing the action. Defaults to 'mcp'.")
12475
- });
12476
- var chainBranchSchema = z27.object({
12477
- action: z27.enum(["create", "list", "merge", "conflicts"]).describe("Action: create a branch, list branches, merge a branch, or check for conflicts"),
12478
- chainEntryId: z27.string().max(200).describe("The chain's entry ID"),
12479
- branchName: z27.string().max(200).optional().describe("Branch name (required for merge/conflicts, optional for create)"),
12480
- strategy: z27.enum(["merge_commit", "squash"]).optional().describe("Merge strategy: 'merge_commit' (default) or 'squash'"),
12481
- author: z27.string().max(200).optional().describe("Who is performing the action. Defaults to 'mcp'.")
12482
- });
12483
- var chainReviewSchema = z27.object({
12484
- action: z27.enum(["gate", "comment", "resolve-comment", "list-comments"]).describe("Action: run coherence gate, add a comment, resolve a comment, or list comments"),
12485
- // Finding #12: optional at the base (mirrors chainSchema's chainEntryId pattern at
12486
- // line ~690) — resolve-comment resolves purely by commentId (handleChainReview never
12487
- // reads chainEntryId in that branch) and the compound-tool's advertised schema
12488
- // (chainReviewCompoundSchema below) already documents it as optional for that action.
12489
- // Per-action variants that DO need it (gate/comment/list-comments) re-require it below,
12490
- // same pattern as chainGetVariant/chainEditVariant re-requiring over chainSchema's base.
12491
- chainEntryId: z27.string().max(200).optional().describe("The chain's entry ID. Required for every action except 'resolve-comment'."),
12492
- commitMessage: z27.string().max(2e3).optional().describe("Commit message to lint (for gate action)"),
12493
- versionNumber: z27.number().optional().describe("Version to comment on or list comments for"),
12494
- linkId: z27.string().max(200).optional().describe("Link this comment targets (optional for comment)"),
12495
- body: z27.string().max(2e4).optional().describe("Comment text (required for comment action)"),
12496
- commentId: z27.string().max(200).optional().describe("Comment ID (required for resolve-comment)"),
12497
- author: z27.string().max(200).optional().describe("Who is performing the action. Defaults to 'mcp'.")
12498
- });
12499
- function linkSummary(links) {
12500
- return Object.entries(links).map(([id, content]) => {
12501
- const filled = typeof content === "string" && content.length > 0;
12502
- const preview = filled ? content.substring(0, 80) + (content.length > 80 ? "..." : "") : "(empty)";
12503
- return ` - **${id}**: ${preview}`;
12504
- }).join("\n");
12505
- }
12506
- async function handleChainCrud({ action, chainEntryId, title, chainTypeId, description, linkId, content, status: status2, author, ownerTeamEntryId, ownerRoleEntryId }) {
12507
- if (action === "create") {
12508
- if (!title) {
12509
- return validationResult("A `title` is required to create a process.");
12510
- }
12511
- const result2 = await kernelMutation(
12512
- "gitchain.createChain",
12513
- { title, chainTypeId, description, author, ownerTeamEntryId, ownerRoleEntryId }
12514
- );
12515
- return {
12516
- content: [{
12517
- type: "text",
12518
- text: `# Process Created
12519
-
12520
- - **Entry ID:** \`${result2.entryId}\`
12521
- - **Title:** ${title}
12522
- - **Type:** ${chainTypeId}
12523
- - **Status:** draft
12524
-
12525
- Use \`chain action=edit chainEntryId="${result2.entryId}" linkId="problem" content="..."\` to start filling in links.`
12526
- }],
12527
- structuredContent: success(
12528
- `Created process '${title}' (${result2.entryId}), type: ${chainTypeId}.`,
12529
- { entryId: result2.entryId, title, chainTypeId, status: "draft" },
12530
- [{ tool: "chain", description: "Edit a link", parameters: { action: "edit", chainEntryId: result2.entryId, linkId: "problem" } }]
12531
- )
12532
- };
12533
- }
12534
- if (action === "get") {
12535
- if (!chainEntryId) {
12536
- return validationResult("A `chainEntryId` is required.");
12537
- }
12538
- const chain = await kernelQuery("gitchain.getChain", { chainEntryId });
12539
- if (!chain) {
12540
- return {
12541
- content: [{ type: "text", text: `Process "${chainEntryId}" not found.` }],
12542
- structuredContent: failure(
12543
- "NOT_FOUND",
12544
- `Process "${chainEntryId}" not found.`,
12545
- "Use chain action=list to find available processes.",
12546
- [{ tool: "chain", description: "List processes", parameters: { action: "list" } }]
12547
- )
12548
- };
12549
- }
12550
- const scoreSection = chain.scores ? `
12551
- ## Coherence: ${chain.coherenceScore}%
12552
-
12553
- ` + chain.scores.sections.map((s) => `- **${s.key}**: ${s.power}/100 (${"\u2605".repeat(Math.min(s.stars ?? 0, 5))}${"\u2606".repeat(Math.max(0, 5 - (s.stars ?? 0)))})`).join("\n") : "";
12554
- return {
12555
- content: [{
12556
- type: "text",
12557
- text: `# ${chain.name}
12558
-
12559
- - **Entry ID:** \`${chain.entryId}\`
12560
- - **Type:** ${chain.chainTypeName}
12561
- - **Status:** ${chain.status}
12562
- - **Links filled:** ${chain.filledCount}/${chain.totalCount}
12563
- - **Version:** ${toVersionDisplay(chain.currentVersion)}
12564
- - **Created by:** ${chain.createdBy ?? "unknown"}
12565
- - **History events:** ${chain.historyCount}
12566
- ` + scoreSection + `
12567
-
12568
- ## Links
12569
-
12570
- ` + linkSummary(chain.links)
12571
- }],
12572
- structuredContent: success(
12573
- `${chain.name}: ${chain.filledCount}/${chain.totalCount} links, coherence ${chain.coherenceScore}%, ${toVersionDisplay(chain.currentVersion)}.`,
12574
- { entryId: chain.entryId, name: chain.name, status: chain.status, filledCount: chain.filledCount, totalCount: chain.totalCount, coherenceScore: chain.coherenceScore, version: chain.currentVersion ?? null, versionDisplay: toVersionDisplay(chain.currentVersion) }
12575
- )
12576
- };
12577
- }
12578
- if (action === "list") {
12579
- const chains = await kernelQuery("gitchain.listChains", { chainTypeId, status: status2 });
12580
- if (chains.length === 0) {
12581
- return {
12582
- content: [{ type: "text", text: "No processes found. Use `chain action=create` to create one." }],
12583
- structuredContent: success(
12584
- "No processes found.",
12585
- { chains: [], total: 0 },
12586
- [{ tool: "chain", description: "Create a process", parameters: { action: "create" } }]
12587
- )
12588
- };
12589
- }
12590
- const formatted = chains.map(
12591
- (c) => `- **\`${c.entryId}\`** ${c.name} \u2014 ${c.chainTypeId} \xB7 ${c.filledCount}/${c.totalCount} links \xB7 coherence: ${c.coherenceScore}% \xB7 status: ${c.status}`
12592
- ).join("\n");
12593
- return {
12594
- content: [{ type: "text", text: `# Chains (${chains.length})
12595
-
12596
- ${formatted}` }],
12597
- structuredContent: success(
12598
- `Found ${chains.length} process(es).`,
12599
- { total: chains.length }
12600
- )
12601
- };
12602
- }
12603
- if (action === "edit") {
12604
- if (!chainEntryId) {
12605
- return validationResult("A `chainEntryId` is required.");
12606
- }
12607
- if (!linkId) {
12608
- return validationResult("A `linkId` is required (e.g. problem, insight, choice, action, outcome).");
12609
- }
12610
- if (!content) {
12611
- return validationResult("The `content` for the link is required.");
12612
- }
12613
- const result2 = await kernelMutation("gitchain.editLink", { chainEntryId, linkId, content, author });
12614
- const remedyBlock = result2.strategyLinkRemedy ? `
12615
-
12616
- **Still draft:** ${result2.strategyLinkRemedy}` : "";
12617
- return {
12618
- content: [{
12619
- type: "text",
12620
- text: `# Link Updated
12621
-
12622
- - **Chain:** \`${result2.entryId}\`
12623
- - **Link:** ${result2.linkId}
12624
- - **Chain status:** ${result2.status}
12625
- - **Content length:** ${content.length} chars${remedyBlock}
12626
-
12627
- Use \`chain action=get\` to see the full chain with updated scores.`
12628
- }],
12629
- structuredContent: success(
12630
- `Updated link "${result2.linkId}" on ${result2.entryId}. Status: ${result2.status}.${result2.strategyLinkRemedy ? ` ${result2.strategyLinkRemedy}` : ""}`,
12631
- { entryId: result2.entryId, linkId: result2.linkId, status: result2.status, contentLength: content.length, ...result2.strategyLinkRemedy ? { strategyLinkRemedy: result2.strategyLinkRemedy } : {} },
12632
- [{ tool: "chain", description: "View full chain", parameters: { action: "get", chainEntryId } }]
12633
- )
12634
- };
12635
- }
12636
- return unknownAction(action, ["create", "get", "list", "edit"]);
12637
- }
12638
- async function handleChainVersion({ action, chainEntryId, commitMessage, versionA, versionB, toVersion, author }) {
12639
- if (action === "commit") {
12640
- if (!commitMessage) {
12641
- return validationResult("A `commitMessage` is required.");
12642
- }
12643
- const result2 = await kernelMutation("gitchain.commitChain", { chainEntryId, commitMessage, author });
12644
- const warning = result2.commitLintWarning ? `
12645
-
12646
- > **Lint warning:** ${result2.commitLintWarning}` : "";
12647
- return {
12648
- content: [{
12649
- type: "text",
12650
- text: `# Committed v${result2.version}
12651
-
12652
- - **Chain:** \`${result2.entryId}\`
12653
- - **Version:** ${result2.version}
12654
- - **Message:** ${commitMessage}
12655
- - **Coherence:** ${result2.coherenceScore}%
12656
- - **Links modified:** ${result2.linksModified.length > 0 ? result2.linksModified.join(", ") : "none"}
12657
- ` + warning
12658
- }],
12659
- structuredContent: success(
12660
- `Committed v${result2.version} on ${result2.entryId}. Coherence: ${result2.coherenceScore}%.`,
12661
- { entryId: result2.entryId, version: result2.version, versionDisplay: toVersionDisplay(result2.version), coherenceScore: result2.coherenceScore, linksModified: result2.linksModified, lintWarning: result2.commitLintWarning }
12662
- )
12663
- };
12664
- }
12665
- if (action === "list") {
12666
- const commits = await kernelQuery("gitchain.listCommits", { chainEntryId });
12667
- if (commits.length === 0) {
12668
- return {
12669
- content: [{ type: "text", text: `No commits found for chain "${chainEntryId}". Use \`chain action=version.commit\` to create the first snapshot.` }],
12670
- structuredContent: success(
12671
- `No commits for ${chainEntryId}.`,
12672
- { chainEntryId, commits: [], total: 0 },
12673
- [{ tool: "chain", description: "Commit chain", parameters: { action: "version.commit", chainEntryId } }]
12674
- )
12675
- };
12676
- }
12677
- const formatted = commits.map((c) => {
12678
- const date = new Date(c.createdAt).toISOString().replace("T", " ").substring(0, 19);
12679
- const msg = c.commitMessage ?? c.changeNote ?? "(no message)";
12680
- const links = c.linksModified?.length > 0 ? ` [${c.linksModified.join(", ")}]` : "";
12681
- return `- **v${c.version}** ${date} by ${c.author} \u2014 ${msg}${links} (${c.versionStatus})`;
12682
- }).join("\n");
12683
- return {
12684
- content: [{ type: "text", text: `# Commits for ${chainEntryId} (${commits.length})
12685
-
12686
- ${formatted}` }],
12687
- structuredContent: success(
12688
- `${commits.length} commit(s) for ${chainEntryId}.`,
12689
- { chainEntryId, total: commits.length }
12690
- )
12691
- };
12692
- }
12693
- if (action === "diff") {
12694
- if (versionA == null || versionB == null) {
12695
- return validationResult("Both `versionA` and `versionB` are required for diff.");
12696
- }
12697
- const diff = await kernelMutation("gitchain.diffVersions", { chainEntryId, versionA, versionB });
12698
- let text = `# Diff: v${versionA} \u2192 v${versionB}
12699
-
12700
- - **Chain:** \`${diff.chainEntryId}\`
12701
- - **Coherence:** ${diff.coherenceBefore}% \u2192 ${diff.coherenceAfter}% (${diff.coherenceDelta >= 0 ? "+" : ""}${diff.coherenceDelta})
12702
- - **Links changed:** ${diff.linksChanged.length > 0 ? diff.linksChanged.join(", ") : "none"}
12703
- `;
12704
- for (const ld of diff.linkDiffs) {
12705
- if (ld.status === "unchanged") continue;
12706
- text += `
12707
- ## ${ld.linkId} (${ld.status})
12708
-
12709
- `;
12710
- const wordDiff = diff.wordDiffs[ld.linkId];
12711
- if (wordDiff?.length > 0) {
12712
- for (const w of wordDiff) {
12713
- if (w.type === "delete") text += `~~${w.value.substring(0, 200)}~~`;
12714
- else if (w.type === "insert") text += `**${w.value.substring(0, 200)}**`;
12715
- else text += w.value.substring(0, 200);
12716
- }
12717
- text += "\n";
12718
- }
12719
- }
12720
- return {
12721
- content: [{ type: "text", text }],
12722
- structuredContent: success(
12723
- `Diff v${versionA} \u2192 v${versionB}: coherence ${diff.coherenceBefore}% \u2192 ${diff.coherenceAfter}%, ${diff.linksChanged.length} link(s) changed.`,
12724
- { chainEntryId: diff.chainEntryId, versionA, versionB, coherenceBefore: diff.coherenceBefore, coherenceAfter: diff.coherenceAfter, coherenceDelta: diff.coherenceDelta, linksChanged: diff.linksChanged }
12725
- )
12726
- };
12727
- }
12728
- if (action === "revert") {
12729
- if (toVersion == null) {
12730
- return validationResult("A `toVersion` is required for revert.");
12731
- }
12732
- const result2 = await kernelMutation("gitchain.revertChain", { chainEntryId, toVersion, author });
12733
- return {
12734
- content: [{
12735
- type: "text",
12736
- text: `# Reverted
12737
-
12738
- - **Chain:** \`${result2.entryId}\`
12739
- - **Reverted to:** v${result2.revertedTo}
12740
- - **New version:** v${result2.newVersion}
12741
- - **Links affected:** ${result2.linksModified.length > 0 ? result2.linksModified.join(", ") : "none"}
12742
-
12743
- History is preserved \u2014 this created a new version, not a destructive reset.`
12744
- }],
12745
- structuredContent: success(
12746
- `Reverted ${result2.entryId} to v${result2.revertedTo}. New version: v${result2.newVersion}.`,
12747
- { entryId: result2.entryId, revertedTo: result2.revertedTo, revertedToDisplay: toVersionDisplay(result2.revertedTo), newVersion: result2.newVersion, newVersionDisplay: toVersionDisplay(result2.newVersion), linksModified: result2.linksModified }
12748
- )
12749
- };
12750
- }
12751
- if (action === "history") {
12752
- const history = await kernelQuery("gitchain.getHistory", { chainEntryId });
12753
- if (history.length === 0) {
12754
- return {
12755
- content: [{ type: "text", text: `No history found for chain "${chainEntryId}".` }],
12756
- structuredContent: success(
12757
- `No history for ${chainEntryId}.`,
12758
- { chainEntryId, events: [], total: 0 }
12759
- )
12760
- };
12761
- }
12762
- const formatted = history.sort((a, b) => b.timestamp - a.timestamp).map((h) => {
12763
- const date = new Date(h.timestamp).toISOString().replace("T", " ").substring(0, 19);
12764
- return `- **${date}** [${h.event}] by ${h.changedBy ?? "unknown"} \u2014 ${h.note ?? ""}`;
12765
- }).join("\n");
12766
- return {
12767
- content: [{ type: "text", text: `# History for ${chainEntryId} (${history.length} events)
12768
-
12769
- ${formatted}` }],
12770
- structuredContent: success(
12771
- `${history.length} history event(s) for ${chainEntryId}.`,
12772
- { chainEntryId, total: history.length }
12773
- )
12774
- };
12775
- }
12776
- return unknownAction(action, ["commit", "list", "diff", "revert", "history"]);
12777
- }
12778
- async function handleChainBranch({ action, chainEntryId, branchName, strategy, author }) {
12779
- if (action === "create") {
12780
- const result2 = await kernelMutation("gitchain.createBranch", { chainEntryId, name: branchName, author });
12781
- return {
12782
- content: [{
12783
- type: "text",
12784
- text: `# Branch Created
12785
-
12786
- - **Name:** ${result2.name}
12787
- - **Based on:** v${result2.baseVersion}
12788
- - **Chain:** \`${chainEntryId}\`
12789
-
12790
- Edit links and commit on this branch, then use \`chain-review action=branch.merge\` to land changes.`
12791
- }],
12792
- structuredContent: success(
12793
- `Created branch "${result2.name}" based on v${result2.baseVersion}.`,
12794
- { branchId: result2.branchId, name: result2.name, baseVersion: result2.baseVersion, chainEntryId },
12795
- [{ tool: "chain-review", description: "Merge branch", parameters: { action: "branch.merge", chainEntryId, branchName: result2.name } }]
12796
- )
12797
- };
12798
- }
12799
- if (action === "list") {
12800
- const branches = await kernelMutation("gitchain.listBranches", { chainEntryId });
12801
- if (branches.length === 0) {
12802
- return {
12803
- content: [{ type: "text", text: `No branches found for chain "${chainEntryId}".` }],
12804
- structuredContent: success(
12805
- `No branches for ${chainEntryId}.`,
12806
- { chainEntryId, branches: [], total: 0 },
12807
- [{ tool: "chain-review", description: "Create a branch", parameters: { action: "branch.create", chainEntryId } }]
12808
- )
12809
- };
12810
- }
12811
- const formatted = branches.map((b) => `- **${b.name}** (${b.status}) \u2014 based on v${b.baseVersion}, by ${b.createdBy}`).join("\n");
12812
- return {
12813
- content: [{ type: "text", text: `# Branches for ${chainEntryId} (${branches.length})
12814
-
12815
- ${formatted}` }],
12816
- structuredContent: success(
12817
- `${branches.length} branch(es) for ${chainEntryId}.`,
12818
- { chainEntryId, total: branches.length }
12819
- )
12820
- };
12821
- }
12822
- if (action === "merge") {
12823
- if (!branchName) {
12824
- return validationResult("A `branchName` is required for merge.");
12825
- }
12826
- const result2 = await kernelMutation("gitchain.mergeBranch", { chainEntryId, branchName, strategy, author });
12827
- return {
12828
- content: [{
12829
- type: "text",
12830
- text: `# Branch Merged
12831
-
12832
- - **Chain:** \`${result2.entryId}\`
12833
- - **Branch:** ${result2.branchName} (now closed)
12834
- - **Version:** v${result2.version}
12835
- - **Strategy:** ${result2.strategy}
12836
-
12837
- Main is now at v${result2.version}. The branch has been closed.`
12838
- }],
12839
- structuredContent: success(
12840
- `Merged branch "${result2.branchName}" into ${result2.entryId} at v${result2.version}.`,
12841
- { entryId: result2.entryId, branchName: result2.branchName, version: result2.version, versionDisplay: toVersionDisplay(result2.version), strategy: result2.strategy }
12842
- )
12843
- };
12844
- }
12845
- if (action === "conflicts") {
12846
- if (!branchName) {
12847
- return validationResult("A `branchName` is required for conflict check.");
12848
- }
12849
- const result2 = await kernelMutation("gitchain.checkConflicts", { chainEntryId, branchName });
12850
- if (!result2.hasConflicts) {
12851
- return {
12852
- content: [{
12853
- type: "text",
12854
- text: `# No Conflicts
12855
-
12856
- Branch "${branchName}" on \`${chainEntryId}\` has no conflicts. Safe to merge.`
12857
- }],
12858
- structuredContent: success(
12859
- `No conflicts on branch "${branchName}". Safe to merge.`,
12860
- { chainEntryId, branchName, hasConflicts: false },
12861
- [{ tool: "chain-review", description: "Merge branch", parameters: { action: "branch.merge", chainEntryId, branchName } }]
12862
- )
12863
- };
12864
- }
12865
- const conflictLines = result2.conflicts.map(
12866
- (c) => `- **${c.linkId}** \u2014 modified by: ${c.branches.map((b) => `${b.branchName} (${b.author})`).join(", ")}`
12867
- ).join("\n");
12868
- return {
12869
- content: [{
12870
- type: "text",
12871
- text: `# Conflicts Detected
12872
-
12873
- Branch "${branchName}" conflicts with other branches on these links:
12874
-
12875
- ` + conflictLines + `
12876
-
12877
- Resolve conflicts before merging.`
12878
- }],
12879
- structuredContent: failure(
12880
- "CONFLICT",
12881
- `Branch "${branchName}" has ${result2.conflicts.length} conflict(s).`,
12882
- "Resolve conflicts on the conflicting links before merging."
12883
- )
12884
- };
12885
- }
12886
- return unknownAction(action, ["create", "list", "merge", "conflicts"]);
12887
- }
12888
- async function handleChainReview({ action, chainEntryId, commitMessage, versionNumber, linkId, body, commentId, author }) {
12889
- if (action === "gate") {
12890
- const gate = await kernelQuery("gitchain.runGate", { chainEntryId, commitMessage });
12891
- const checkLines = gate.checks.map((c) => `- ${c.pass ? "PASS" : "FAIL"} **${c.name}**: ${c.detail}`).join("\n");
12892
- const icon = gate.pass ? "PASS" : "BLOCKED";
12893
- return {
12894
- content: [{
12895
- type: "text",
12896
- text: `# Gate: ${icon}
12897
-
12898
- - **Score:** ${gate.score}%
12899
- - **Threshold:** ${gate.threshold}%
12900
-
12901
- ## Checks
12902
-
12903
- ${checkLines}`
12904
- }],
12905
- structuredContent: success(
12906
- `Gate ${icon}: ${gate.score}% (threshold ${gate.threshold}%).`,
12907
- { pass: gate.pass, score: gate.score, threshold: gate.threshold, checks: gate.checks }
12908
- )
12909
- };
12910
- }
12911
- if (action === "comment") {
12912
- if (!versionNumber) {
12913
- return validationResult("A `versionNumber` is required.");
12914
- }
12915
- if (!body) {
12916
- return validationResult("A `body` is required.");
12917
- }
12918
- const result2 = await kernelMutation("gitchain.addComment", {
12919
- chainEntryId,
12920
- versionNumber,
12921
- linkId,
12922
- body,
12923
- author
12924
- });
12925
- return {
12926
- content: [{
12927
- type: "text",
12928
- text: `# Comment Added
12929
-
12930
- - **Chain:** \`${result2.chainEntryId}\`
12931
- - **Version:** v${result2.versionNumber}
12932
- ` + (result2.linkId ? `- **Link:** ${result2.linkId}
12933
- ` : "") + `- **Body:** ${body.substring(0, 200)}`
12934
- }],
12935
- structuredContent: success(
12936
- `Comment added on ${result2.chainEntryId} v${result2.versionNumber}${result2.linkId ? ` [${result2.linkId}]` : ""}.`,
12937
- { chainEntryId: result2.chainEntryId, versionNumber: result2.versionNumber, linkId: result2.linkId }
12938
- )
12939
- };
12940
- }
12941
- if (action === "resolve-comment") {
12942
- if (!commentId) {
12943
- return validationResult("A `commentId` is required.");
12944
- }
12945
- await kernelMutation("gitchain.resolveComment", { commentId });
12946
- return successResult("Comment resolved.", `Comment ${commentId} resolved.`, { commentId, resolved: true });
12947
- }
12948
- if (action === "list-comments") {
12949
- const comments = await kernelMutation("gitchain.listComments", {
12950
- chainEntryId,
12951
- versionNumber
12952
- });
12953
- if (comments.length === 0) {
12954
- return {
12955
- content: [{ type: "text", text: `No comments found for chain "${chainEntryId}"${versionNumber ? ` v${versionNumber}` : ""}.` }],
12956
- structuredContent: success(
12957
- `No comments for ${chainEntryId}${versionNumber ? ` v${versionNumber}` : ""}.`,
12958
- { chainEntryId, comments: [], total: 0, unresolved: 0 }
12959
- )
12960
- };
12961
- }
12962
- const formatted = comments.map((c) => {
12963
- const date = new Date(c.createdAt).toISOString().replace("T", " ").substring(0, 19);
12964
- const resolved = c.resolved ? " (RESOLVED)" : "";
12965
- const link = c.linkId ? ` [${c.linkId}]` : "";
12966
- return `- **v${c.version}${link}** ${date} by ${c.author}${resolved}: ${c.body.substring(0, 150)}`;
12967
- }).join("\n");
12968
- const unresolvedCount = comments.filter((c) => !c.resolved).length;
12969
- return {
12970
- content: [{
12971
- type: "text",
12972
- text: `# Comments for ${chainEntryId} (${comments.length}, ${unresolvedCount} unresolved)
12973
-
12974
- ${formatted}`
12975
- }],
12976
- structuredContent: success(
12977
- `${comments.length} comment(s) for ${chainEntryId}, ${unresolvedCount} unresolved.`,
12978
- { chainEntryId, total: comments.length, unresolved: unresolvedCount }
12979
- )
12980
- };
12981
- }
12982
- return unknownAction(action, ["gate", "comment", "resolve-comment", "list-comments"]);
12983
- }
12984
- var CHAIN_ACTIONS = [
12985
- "create",
12986
- "get",
12987
- "list",
12988
- "edit",
12989
- "version.commit",
12990
- "version.list",
12991
- "version.diff",
12992
- "version.revert",
12993
- "version.history"
12994
- ];
12995
- var CHAIN_REVIEW_ACTIONS = [
12996
- "gate",
12997
- "comment",
12998
- "resolve-comment",
12999
- "list-comments",
13000
- "branch.create",
13001
- "branch.list",
13002
- "branch.merge",
13003
- "branch.conflicts"
13004
- ];
13005
- var chainCompoundSchema = z27.object({
13006
- action: z27.enum(CHAIN_ACTIONS).describe(
13007
- "Unnamespaced: 'create'/'get'/'list'/'edit' \u2014 process CRUD (the original `chain` tool). 'version.*' (commit/list/diff/revert/history) \u2014 versioning, absorbs `chain-version`. Branching and review live on the sibling `chain-review` tool."
13008
- ),
13009
- chainEntryId: z27.string().max(200).optional().describe("Chain entry ID. Required for get/edit and all version.* actions."),
13010
- title: z27.string().max(500).optional().describe("For 'create': process title (required)."),
13011
- chainTypeId: z27.string().max(200).optional().default("strategy-coherence").describe("For 'create'/'list': process template slug."),
13012
- description: z27.string().max(2e4).optional().describe("For 'create': description."),
13013
- linkId: z27.string().max(200).optional().describe("For 'edit': link to edit (required)."),
13014
- content: z27.string().max(5e4).optional().describe("For 'edit': new content for the link (required)."),
13015
- status: z27.string().max(200).optional().describe("For 'list': filter by status."),
13016
- author: z27.string().max(200).optional().describe("Who is performing the action. Defaults to 'mcp'."),
13017
- commitMessage: z27.string().max(2e3).optional().describe("For 'version.commit': commit message (required)."),
13018
- versionA: z27.number().optional().describe("For 'version.diff': earlier version (required)."),
13019
- versionB: z27.number().optional().describe("For 'version.diff': later version (required)."),
13020
- toVersion: z27.number().optional().describe("For 'version.revert': version number to revert to (required)."),
13021
- // WP-513 review round 3 (P1): team+role to create AS OWNER of (rung 2 only).
13022
- ownerTeamEntryId: z27.string().max(200).optional().describe("For 'create': owning team (rung-2 workspaces)."),
13023
- ownerRoleEntryId: z27.string().max(200).optional().describe("For 'create': owning role (rung-2 workspaces).")
13024
- });
13025
- var chainReviewCompoundSchema = z27.object({
13026
- action: z27.enum(CHAIN_REVIEW_ACTIONS).describe(
13027
- "'gate'/'comment'/'resolve-comment'/'list-comments' \u2014 quality gate + comments (the original `chain-review` tool, unchanged call shape). 'branch.*' (create/list/merge/conflicts) \u2014 branching, absorbs `chain-branch`. Process CRUD and versioning live on the sibling `chain` tool."
13028
- ),
13029
- chainEntryId: z27.string().max(200).optional().describe("Chain entry ID. Required for every action except 'resolve-comment'."),
13030
- commitMessage: z27.string().max(2e3).optional().describe("For 'gate': commit message to lint."),
13031
- versionNumber: z27.number().optional().describe("For 'comment'/'list-comments': version to comment on or list comments for."),
13032
- linkId: z27.string().max(200).optional().describe("For 'comment': optional link this comment targets."),
13033
- body: z27.string().max(2e4).optional().describe("For 'comment': comment text (required)."),
13034
- commentId: z27.string().max(200).optional().describe("For 'resolve-comment': comment ID (required)."),
13035
- branchName: z27.string().max(200).optional().describe("For 'branch.merge'/'branch.conflicts': required. For 'branch.create': optional."),
13036
- strategy: z27.enum(["merge_commit", "squash"]).optional().describe("For 'branch.merge': merge strategy. Default 'merge_commit'."),
13037
- author: z27.string().max(200).optional().describe("Who is performing the action. Defaults to 'mcp'.")
13038
- });
13039
- var chainCreateVariant = chainSchema.omit({ action: true }).extend({ action: z27.literal("create") });
13040
- var chainGetVariant = z27.object({ action: z27.literal("get"), chainEntryId: z27.string().max(200) });
13041
- var chainListVariant = z27.object({ action: z27.literal("list"), chainTypeId: z27.string().max(200).optional(), status: z27.string().max(200).optional() });
13042
- var chainEditVariant = z27.object({
13043
- action: z27.literal("edit"),
13044
- chainEntryId: z27.string().max(200),
13045
- linkId: z27.string().max(200),
13046
- content: z27.string().max(5e4),
13047
- author: z27.string().max(200).optional()
13048
- });
13049
- var versionBase = chainVersionSchema.omit({ action: true });
13050
- var chainVersionCommitVariant = versionBase.extend({ action: z27.literal("version.commit"), commitMessage: z27.string().max(2e3) });
13051
- var chainVersionListVariant = versionBase.extend({ action: z27.literal("version.list") });
13052
- var chainVersionDiffVariant = versionBase.extend({ action: z27.literal("version.diff"), versionA: z27.number(), versionB: z27.number() });
13053
- var chainVersionRevertVariant = versionBase.extend({ action: z27.literal("version.revert"), toVersion: z27.number() });
13054
- var chainVersionHistoryVariant = versionBase.extend({ action: z27.literal("version.history") });
13055
- var chainActionUnion = z27.discriminatedUnion("action", [
13056
- chainCreateVariant,
13057
- chainGetVariant,
13058
- chainListVariant,
13059
- chainEditVariant,
13060
- chainVersionCommitVariant,
13061
- chainVersionListVariant,
13062
- chainVersionDiffVariant,
13063
- chainVersionRevertVariant,
13064
- chainVersionHistoryVariant
13065
- ]);
13066
- var reviewBase = chainReviewSchema.omit({ action: true });
13067
- var chainReviewGateVariant = reviewBase.extend({ action: z27.literal("gate"), chainEntryId: z27.string().max(200) });
13068
- var chainReviewCommentVariant = reviewBase.extend({ action: z27.literal("comment"), chainEntryId: z27.string().max(200), versionNumber: z27.number(), body: z27.string().max(2e4) });
13069
- var chainReviewResolveCommentVariant = reviewBase.extend({ action: z27.literal("resolve-comment"), commentId: z27.string().max(200) });
13070
- var chainReviewListCommentsVariant = reviewBase.extend({ action: z27.literal("list-comments"), chainEntryId: z27.string().max(200) });
13071
- var branchBase = chainBranchSchema.omit({ action: true });
13072
- var chainBranchCreateVariant = branchBase.extend({ action: z27.literal("branch.create") });
13073
- var chainBranchListVariant = branchBase.extend({ action: z27.literal("branch.list") });
13074
- var chainBranchMergeVariant = branchBase.extend({ action: z27.literal("branch.merge"), branchName: z27.string().max(200) });
13075
- var chainBranchConflictsVariant = branchBase.extend({ action: z27.literal("branch.conflicts"), branchName: z27.string().max(200) });
13076
- var chainReviewActionUnion = z27.discriminatedUnion("action", [
13077
- chainReviewGateVariant,
13078
- chainReviewCommentVariant,
13079
- chainReviewResolveCommentVariant,
13080
- chainReviewListCommentsVariant,
13081
- chainBranchCreateVariant,
13082
- chainBranchListVariant,
13083
- chainBranchMergeVariant,
13084
- chainBranchConflictsVariant
13085
- ]);
13086
- var CHAIN_ACTION_SPECS = {
13087
- create: { params: ["title", "chainTypeId", "description", "author", "ownerTeamEntryId", "ownerRoleEntryId"], description: "title is required." },
13088
- get: { params: ["chainEntryId"], description: "chainEntryId is required." },
13089
- list: { params: ["chainTypeId", "status"], description: "All params optional." },
13090
- edit: { params: ["chainEntryId", "linkId", "content", "author"], description: "chainEntryId, linkId, and content are required." },
13091
- "version.commit": { params: ["chainEntryId", "commitMessage", "author"], description: "chainEntryId and commitMessage are required." },
13092
- "version.list": { params: ["chainEntryId"], description: "chainEntryId is required." },
13093
- "version.diff": { params: ["chainEntryId", "versionA", "versionB"], description: "chainEntryId, versionA, and versionB are required." },
13094
- "version.revert": { params: ["chainEntryId", "toVersion", "author"], description: "chainEntryId and toVersion are required." },
13095
- "version.history": { params: ["chainEntryId"], description: "chainEntryId is required." }
13096
- };
13097
- var CHAIN_REVIEW_ACTION_SPECS = {
13098
- gate: { params: ["chainEntryId", "commitMessage"], description: "chainEntryId is required." },
13099
- comment: { params: ["chainEntryId", "versionNumber", "linkId", "body", "author"], description: "chainEntryId, versionNumber, and body are required." },
13100
- "resolve-comment": { params: ["commentId"], description: "commentId is required." },
13101
- "list-comments": { params: ["chainEntryId", "versionNumber"], description: "chainEntryId is required." },
13102
- "branch.create": { params: ["chainEntryId", "branchName", "author"], description: "chainEntryId is required." },
13103
- "branch.list": { params: ["chainEntryId"], description: "chainEntryId is required." },
13104
- "branch.merge": { params: ["chainEntryId", "branchName", "strategy", "author"], description: "chainEntryId and branchName are required." },
13105
- "branch.conflicts": { params: ["chainEntryId", "branchName"], description: "chainEntryId and branchName are required." }
13106
- };
13107
- function registerGitChainTools(server) {
13108
- const chainHandlers = {
13109
- create: (data) => handleChainCrud({ ...data, action: "create" }),
13110
- get: (data) => handleChainCrud({ ...data, action: "get" }),
13111
- list: (data) => handleChainCrud({ ...data, action: "list" }),
13112
- edit: (data) => handleChainCrud({ ...data, action: "edit" }),
13113
- "version.commit": (data) => handleChainVersion({ ...data, action: "commit" }),
13114
- "version.list": (data) => handleChainVersion({ ...data, action: "list" }),
13115
- "version.diff": (data) => handleChainVersion({ ...data, action: "diff" }),
13116
- "version.revert": (data) => handleChainVersion({ ...data, action: "revert" }),
13117
- "version.history": (data) => handleChainVersion({ ...data, action: "history" })
13118
- };
13119
- const chainReviewHandlers = {
13120
- gate: (data) => handleChainReview({ ...data, action: "gate" }),
13121
- comment: (data) => handleChainReview({ ...data, action: "comment" }),
13122
- "resolve-comment": (data) => handleChainReview({ ...data, action: "resolve-comment" }),
13123
- "list-comments": (data) => handleChainReview({ ...data, action: "list-comments" }),
13124
- "branch.create": (data) => handleChainBranch({ ...data, action: "create" }),
13125
- "branch.list": (data) => handleChainBranch({ ...data, action: "list" }),
13126
- "branch.merge": (data) => handleChainBranch({ ...data, action: "merge" }),
13127
- "branch.conflicts": (data) => handleChainBranch({ ...data, action: "conflicts" })
13128
- };
13129
- server.registerTool(
13130
- "chain",
13131
- {
13132
- title: "Chain",
13133
- description: "Manage processes \u2014 CRUD and versioning. Processes are versioned knowledge artifacts that follow a process template (e.g. Strategy Coherence: Problem \u2192 Insight \u2192 Choice \u2192 Action \u2192 Outcome). Module-gated (PB_MODULES=gitchain).\n\n- **create/get/list/edit**: process CRUD.\n- **version.commit/version.list/version.diff/version.revert/version.history**: versioning (absorbs `chain-version`).\n\nBranching and review live on the sibling `chain-review` tool.",
13134
- inputSchema: chainCompoundSchema,
13135
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
13136
- },
13137
- thinWrapper(async (args) => {
13138
- const parsed = parseOrFail(chainCompoundSchema, args);
13139
- if (!parsed.ok) return parsed.result;
13140
- const { action } = parsed.data;
13141
- return runWithToolContext(
13142
- { tool: "chain", action },
13143
- () => dispatchDiscriminated("chain", chainActionUnion, parsed.data, CHAIN_ACTION_SPECS, chainHandlers)
13144
- );
13145
- })
13146
- );
13147
- server.registerTool(
13148
- "chain-review",
13149
- {
13150
- title: "Chain Review",
13151
- description: "Review process quality and manage branches. Module-gated (PB_MODULES=gitchain).\n\n- **gate**: run the coherence gate \u2014 checks coherence score >= 70%, all links filled, commit message convention.\n- **comment/resolve-comment/list-comments**: manage comments on process versions.\n- **branch.create/branch.list/branch.merge/branch.conflicts**: branching (absorbs `chain-branch`).\n\nProcess CRUD and versioning live on the sibling `chain` tool.",
13152
- inputSchema: chainReviewCompoundSchema,
13153
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
13154
- },
13155
- thinWrapper(async (args) => {
13156
- const parsed = parseOrFail(chainReviewCompoundSchema, args);
13157
- if (!parsed.ok) return parsed.result;
13158
- const { action } = parsed.data;
13159
- return runWithToolContext(
13160
- { tool: "chain-review", action },
13161
- () => dispatchDiscriminated("chain-review", chainReviewActionUnion, parsed.data, CHAIN_REVIEW_ACTION_SPECS, chainReviewHandlers)
13162
- );
13163
- })
13164
- );
13165
- }
13166
-
13167
12442
  // src/tools/workspace.ts
13168
- import { z as z30 } from "zod/v3";
12443
+ import { z as z29 } from "zod/v3";
13169
12444
 
13170
12445
  // src/tools/health.ts
13171
- import { z as z28 } from "zod/v3";
12446
+ import { z as z27 } from "zod/v3";
13172
12447
 
13173
12448
  // src/lib/auditView.ts
13174
12449
  async function buildTenantAuditView(limit) {
@@ -13528,61 +12803,61 @@ ${logLines.join("\n")}${seamSummaryText}` }],
13528
12803
  };
13529
12804
  }
13530
12805
  var HEALTH_ACTIONS = ["check", "whoami", "status", "audit", "self-test"];
13531
- var healthSchema = z28.object({
13532
- action: z28.enum(HEALTH_ACTIONS).describe(
12806
+ var healthSchema = z27.object({
12807
+ action: z27.enum(HEALTH_ACTIONS).describe(
13533
12808
  "'check': connectivity and workspace stats. 'whoami': session identity. 'status': workspace readiness. 'audit': session audit log. 'self-test': validate all tool schemas."
13534
12809
  ),
13535
- limit: z28.number().min(1).max(50).default(20).optional().describe("For audit: how many recent calls to show (max 50)")
12810
+ limit: z27.number().min(1).max(50).default(20).optional().describe("For audit: how many recent calls to show (max 50)")
13536
12811
  });
13537
- var healthCheckOutputSchema = z28.object({
13538
- healthy: z28.boolean(),
13539
- collections: z28.number(),
13540
- entries: z28.number(),
13541
- latencyMs: z28.number(),
13542
- workspace: z28.string()
12812
+ var healthCheckOutputSchema = z27.object({
12813
+ healthy: z27.boolean(),
12814
+ collections: z27.number(),
12815
+ entries: z27.number(),
12816
+ latencyMs: z27.number(),
12817
+ workspace: z27.string()
13543
12818
  });
13544
- var organisationHealthSchema = z28.object({
13545
- reviewed: z28.number(),
13546
- agreements: z28.number(),
13547
- disagreements: z28.number(),
13548
- abstentions: z28.number(),
13549
- agreementRate: z28.number(),
13550
- flags: z28.array(z28.object({ collection: z28.string(), count: z28.number(), suggestedCollection: z28.string() }))
12819
+ var organisationHealthSchema = z27.object({
12820
+ reviewed: z27.number(),
12821
+ agreements: z27.number(),
12822
+ disagreements: z27.number(),
12823
+ abstentions: z27.number(),
12824
+ agreementRate: z27.number(),
12825
+ flags: z27.array(z27.object({ collection: z27.string(), count: z27.number(), suggestedCollection: z27.string() }))
13551
12826
  });
13552
- var healthStatusOutputSchema = z28.object({
13553
- stage: z28.enum(["blank", "seeded", "grounded", "connected"]).optional().default("seeded"),
13554
- sampleTruncated: z28.boolean().nullable().optional(),
13555
- activeEntries: z28.number(),
13556
- totalRelations: z28.number(),
13557
- orphanedEntries: z28.number(),
13558
- checks: z28.array(z28.object({ label: z28.string(), passed: z28.boolean(), current: z28.number(), required: z28.number() })),
13559
- gaps: z28.array(z28.object({ id: z28.string(), label: z28.string(), guidance: z28.string() })),
12827
+ var healthStatusOutputSchema = z27.object({
12828
+ stage: z27.enum(["blank", "seeded", "grounded", "connected"]).optional().default("seeded"),
12829
+ sampleTruncated: z27.boolean().nullable().optional(),
12830
+ activeEntries: z27.number(),
12831
+ totalRelations: z27.number(),
12832
+ orphanedEntries: z27.number(),
12833
+ checks: z27.array(z27.object({ label: z27.string(), passed: z27.boolean(), current: z27.number(), required: z27.number() })),
12834
+ gaps: z27.array(z27.object({ id: z27.string(), label: z27.string(), guidance: z27.string() })),
13560
12835
  organisationHealth: organisationHealthSchema.optional()
13561
12836
  });
13562
- var healthAuditOutputSchema = z28.object({
13563
- totalCalls: z28.number(),
13564
- calls: z28.array(z28.object({
13565
- tool: z28.string(),
13566
- action: z28.string().optional(),
13567
- timestamp: z28.string(),
13568
- durationMs: z28.number().optional()
12837
+ var healthAuditOutputSchema = z27.object({
12838
+ totalCalls: z27.number(),
12839
+ calls: z27.array(z27.object({
12840
+ tool: z27.string(),
12841
+ action: z27.string().optional(),
12842
+ timestamp: z27.string(),
12843
+ durationMs: z27.number().optional()
13569
12844
  }))
13570
12845
  });
13571
- var healthWhoamiOutputSchema = z28.object({
13572
- workspaceId: z28.string(),
13573
- workspaceName: z28.string(),
13574
- scope: z28.string(),
13575
- sessionId: z28.union([z28.string(), z28.null()]),
13576
- oriented: z28.boolean()
12846
+ var healthWhoamiOutputSchema = z27.object({
12847
+ workspaceId: z27.string(),
12848
+ workspaceName: z27.string(),
12849
+ scope: z27.string(),
12850
+ sessionId: z27.union([z27.string(), z27.null()]),
12851
+ oriented: z27.boolean()
13577
12852
  });
13578
- var selfTestOutputSchema = z28.object({
13579
- passed: z28.number(),
13580
- failed: z28.number(),
13581
- total: z28.number(),
13582
- results: z28.array(z28.object({
13583
- tool: z28.string(),
13584
- valid: z28.boolean(),
13585
- error: z28.string().optional()
12853
+ var selfTestOutputSchema = z27.object({
12854
+ passed: z27.number(),
12855
+ failed: z27.number(),
12856
+ total: z27.number(),
12857
+ results: z27.array(z27.object({
12858
+ tool: z27.string(),
12859
+ valid: z27.boolean(),
12860
+ error: z27.string().optional()
13586
12861
  }))
13587
12862
  });
13588
12863
  function handleSelfTest(server) {
@@ -13633,9 +12908,9 @@ function handleSelfTest(server) {
13633
12908
  }
13634
12909
 
13635
12910
  // src/tools/usage.ts
13636
- import { z as z29 } from "zod/v3";
13637
- var usageSummarySchema = z29.object({
13638
- periodDays: z29.number().min(1).max(90).optional().describe("Number of days to look back (default 30, max 90)")
12911
+ import { z as z28 } from "zod/v3";
12912
+ var usageSummarySchema = z28.object({
12913
+ periodDays: z28.number().min(1).max(90).optional().describe("Number of days to look back (default 30, max 90)")
13639
12914
  });
13640
12915
  async function handleUsageSummary(periodDays) {
13641
12916
  const ws = await getWorkspaceContext();
@@ -13807,35 +13082,35 @@ var WORKSPACE_ACTIONS = [
13807
13082
  "proposals-respond",
13808
13083
  "proposals-count"
13809
13084
  ];
13810
- var workspaceSchema = z30.object({
13811
- action: z30.enum(WORKSPACE_ACTIONS).describe(
13085
+ var workspaceSchema = z29.object({
13086
+ action: z29.enum(WORKSPACE_ACTIONS).describe(
13812
13087
  "'check': connectivity and workspace stats (absorbs health action=check). 'whoami': session identity (absorbs health action=whoami). 'status': workspace readiness (absorbs health action=status). 'audit': session audit log (absorbs health action=audit). 'self-test': validate all tool schemas (absorbs health action=self-test). 'usage': LLM usage and cost summary (absorbs get-usage-summary). 'proposals-list': list open consent proposals (absorbs governance-proposals action=list). 'proposals-respond': approve/reject a consent proposal (absorbs governance-proposals action=respond). 'proposals-count': count open consent proposals (absorbs governance-proposals action=count)."
13813
13088
  ),
13814
- limit: z30.number().min(1).max(50).optional().describe("For 'audit': how many recent calls to show (max 50, default 20)."),
13815
- periodDays: z30.number().min(1).max(90).optional().describe("For 'usage': number of days to look back (default 30, max 90)."),
13816
- status: z30.enum(["open", "approved", "objected", "expired"]).optional().describe("For 'proposals-list': filter by status (default: open)."),
13817
- proposalId: z30.string().max(200).optional().describe("For 'proposals-respond': proposal ID."),
13818
- verdict: z30.enum(["approve", "reject"]).optional().describe("For 'proposals-respond': approve or reject."),
13819
- reason: z30.string().max(2e3).optional().describe("For 'proposals-respond': reason for the verdict (required when rejecting).")
13089
+ limit: z29.number().min(1).max(50).optional().describe("For 'audit': how many recent calls to show (max 50, default 20)."),
13090
+ periodDays: z29.number().min(1).max(90).optional().describe("For 'usage': number of days to look back (default 30, max 90)."),
13091
+ status: z29.enum(["open", "approved", "objected", "expired"]).optional().describe("For 'proposals-list': filter by status (default: open)."),
13092
+ proposalId: z29.string().max(200).optional().describe("For 'proposals-respond': proposal ID."),
13093
+ verdict: z29.enum(["approve", "reject"]).optional().describe("For 'proposals-respond': approve or reject."),
13094
+ reason: z29.string().max(2e3).optional().describe("For 'proposals-respond': reason for the verdict (required when rejecting).")
13820
13095
  });
13821
- var workspaceCheckVariant = z30.object({ action: z30.literal("check") });
13822
- var workspaceWhoamiVariant = z30.object({ action: z30.literal("whoami") });
13823
- var workspaceStatusVariant = z30.object({ action: z30.literal("status") });
13824
- var workspaceAuditVariant = z30.object({ action: z30.literal("audit"), limit: z30.number().min(1).max(50).optional().default(20) });
13825
- var workspaceSelfTestVariant = z30.object({ action: z30.literal("self-test") });
13826
- var workspaceUsageVariant = z30.object({ action: z30.literal("usage"), periodDays: z30.number().min(1).max(90).optional() });
13827
- var workspaceProposalsListVariant = z30.object({
13828
- action: z30.literal("proposals-list"),
13829
- status: z30.enum(["open", "approved", "objected", "expired"]).optional()
13096
+ var workspaceCheckVariant = z29.object({ action: z29.literal("check") });
13097
+ var workspaceWhoamiVariant = z29.object({ action: z29.literal("whoami") });
13098
+ var workspaceStatusVariant = z29.object({ action: z29.literal("status") });
13099
+ var workspaceAuditVariant = z29.object({ action: z29.literal("audit"), limit: z29.number().min(1).max(50).optional().default(20) });
13100
+ var workspaceSelfTestVariant = z29.object({ action: z29.literal("self-test") });
13101
+ var workspaceUsageVariant = z29.object({ action: z29.literal("usage"), periodDays: z29.number().min(1).max(90).optional() });
13102
+ var workspaceProposalsListVariant = z29.object({
13103
+ action: z29.literal("proposals-list"),
13104
+ status: z29.enum(["open", "approved", "objected", "expired"]).optional()
13830
13105
  });
13831
- var workspaceProposalsRespondVariant = z30.object({
13832
- action: z30.literal("proposals-respond"),
13833
- proposalId: z30.string().max(200),
13834
- verdict: z30.enum(["approve", "reject"]),
13835
- reason: z30.string().max(2e3).optional()
13106
+ var workspaceProposalsRespondVariant = z29.object({
13107
+ action: z29.literal("proposals-respond"),
13108
+ proposalId: z29.string().max(200),
13109
+ verdict: z29.enum(["approve", "reject"]),
13110
+ reason: z29.string().max(2e3).optional()
13836
13111
  });
13837
- var workspaceProposalsCountVariant = z30.object({ action: z30.literal("proposals-count") });
13838
- var workspaceActionUnion = z30.discriminatedUnion("action", [
13112
+ var workspaceProposalsCountVariant = z29.object({ action: z29.literal("proposals-count") });
13113
+ var workspaceActionUnion = z29.discriminatedUnion("action", [
13839
13114
  workspaceCheckVariant,
13840
13115
  workspaceWhoamiVariant,
13841
13116
  workspaceStatusVariant,
@@ -13891,7 +13166,7 @@ function registerWorkspaceTools(server) {
13891
13166
  }
13892
13167
 
13893
13168
  // src/tools/feedback.ts
13894
- import { z as z31 } from "zod/v3";
13169
+ import { z as z30 } from "zod/v3";
13895
13170
 
13896
13171
  // src/lib/productFeedbackConstants.ts
13897
13172
  var PRODUCT_FEEDBACK_CATEGORIES = ["bug", "friction", "idea", "praise", "other"];
@@ -13902,9 +13177,9 @@ var VENDOR_SETTABLE_STATUSES = PRODUCT_FEEDBACK_STATUSES.filter(
13902
13177
 
13903
13178
  // src/tools/feedback.ts
13904
13179
  var actions = ["submit", "list", "queue", "note", "group", "status"];
13905
- var category = z31.enum(PRODUCT_FEEDBACK_CATEGORIES);
13906
- var status = z31.enum(PRODUCT_FEEDBACK_STATUSES);
13907
- var vendorStatus = z31.enum(VENDOR_SETTABLE_STATUSES);
13180
+ var category = z30.enum(PRODUCT_FEEDBACK_CATEGORIES);
13181
+ var status = z30.enum(PRODUCT_FEEDBACK_STATUSES);
13182
+ var vendorStatus = z30.enum(VENDOR_SETTABLE_STATUSES);
13908
13183
  var GATEWAY_MAX_STRING_BYTES = 10240;
13909
13184
  var utf8Bytes = (value) => new TextEncoder().encode(value).length;
13910
13185
  var fitsGatewayBytes = (value) => utf8Bytes(value) <= GATEWAY_MAX_STRING_BYTES;
@@ -13913,32 +13188,32 @@ var byteLimitMessage = (field) => ({
13913
13188
  });
13914
13189
  var MESSAGE_DISPLAY_LIMIT = 1e3;
13915
13190
  var FULL_MESSAGE_MAX_LIMIT = 5;
13916
- var feedbackSchema = z31.object({
13917
- action: z31.enum(actions).describe("submit requires message; list returns your own workspace's feedback (all statuses); queue is the vendor triage queue (system admins only) and accepts filters; note requires feedbackId+note; group requires feedbackIds+groupId (null clears); status requires feedbackId+status."),
13918
- message: z31.string().max(1e4).optional().describe("Required for submit: the product feedback text (max 10,000 chars and 10,240 UTF-8 bytes)."),
13191
+ var feedbackSchema = z30.object({
13192
+ action: z30.enum(actions).describe("submit requires message; list returns your own workspace's feedback (all statuses); queue is the vendor triage queue (system admins only) and accepts filters; note requires feedbackId+note; group requires feedbackIds+groupId (null clears); status requires feedbackId+status."),
13193
+ message: z30.string().max(1e4).optional().describe("Required for submit: the product feedback text (max 10,000 chars and 10,240 UTF-8 bytes)."),
13919
13194
  category: category.optional().describe("Submit/queue category; submit defaults to other."),
13920
- command: z31.string().max(1e3).optional().describe("Optional submit command context; argument values are scrubbed server-side."),
13921
- route: z31.string().max(500).optional().describe("Optional submit route context."),
13922
- client: z31.string().max(100).optional().describe("Optional submit client label."),
13923
- feedbackId: z31.string().max(200).optional().describe("Required for note/status: target feedback ID."),
13924
- feedbackIds: z31.array(z31.string().max(200)).max(100).optional().describe("Required for group: 1\u2013100 target feedback IDs."),
13925
- note: z31.string().max(4e3).optional().describe("Required for note: replacement triage note (max 10,240 UTF-8 bytes)."),
13195
+ command: z30.string().max(1e3).optional().describe("Optional submit command context; argument values are scrubbed server-side."),
13196
+ route: z30.string().max(500).optional().describe("Optional submit route context."),
13197
+ client: z30.string().max(100).optional().describe("Optional submit client label."),
13198
+ feedbackId: z30.string().max(200).optional().describe("Required for note/status: target feedback ID."),
13199
+ feedbackIds: z30.array(z30.string().max(200)).max(100).optional().describe("Required for group: 1\u2013100 target feedback IDs."),
13200
+ note: z30.string().max(4e3).optional().describe("Required for note: replacement triage note (max 10,240 UTF-8 bytes)."),
13926
13201
  status: status.optional().describe("list/queue filter (any status, including 'screening'); the status action's required target value excludes 'screening' (system-only)."),
13927
- groupId: z31.string().min(1).max(200).nullable().optional().describe("Group filter, or required group destination; null explicitly clears grouping."),
13928
- workspaceId: z31.string().max(200).optional().describe("Queue-only filter (vendor cross-workspace triage); omitted means all workspaces. Has no effect on list \u2014 your own workspace is always injected server-side."),
13929
- since: z31.number().optional().describe("Queue-only lower createdAt window bound (inclusive) in epoch milliseconds."),
13930
- before: z31.number().optional().describe("Queue-only upper createdAt window bound (exclusive) in epoch milliseconds. A time-window FILTER, not the pager \u2014 use cursor to page."),
13931
- limit: z31.number().int().min(1).max(100).optional().describe("list/queue row limit, default 50, maximum 100."),
13932
- cursor: z31.string().max(2e3).optional().describe("Opaque pagination cursor from a previous list/queue response (continueCursor); omit for the first page."),
13933
- full: z31.boolean().optional().describe(`list/queue only: return untruncated messages; allowed only when limit <= ${FULL_MESSAGE_MAX_LIMIT}.`)
13202
+ groupId: z30.string().min(1).max(200).nullable().optional().describe("Group filter, or required group destination; null explicitly clears grouping."),
13203
+ workspaceId: z30.string().max(200).optional().describe("Queue-only filter (vendor cross-workspace triage); omitted means all workspaces. Has no effect on list \u2014 your own workspace is always injected server-side."),
13204
+ since: z30.number().optional().describe("Queue-only lower createdAt window bound (inclusive) in epoch milliseconds."),
13205
+ before: z30.number().optional().describe("Queue-only upper createdAt window bound (exclusive) in epoch milliseconds. A time-window FILTER, not the pager \u2014 use cursor to page."),
13206
+ limit: z30.number().int().min(1).max(100).optional().describe("list/queue row limit, default 50, maximum 100."),
13207
+ cursor: z30.string().max(2e3).optional().describe("Opaque pagination cursor from a previous list/queue response (continueCursor); omit for the first page."),
13208
+ full: z30.boolean().optional().describe(`list/queue only: return untruncated messages; allowed only when limit <= ${FULL_MESSAGE_MAX_LIMIT}.`)
13934
13209
  });
13935
- var union = z31.discriminatedUnion("action", [
13936
- z31.object({ action: z31.literal("submit"), message: z31.string().min(1).max(1e4).refine(fitsGatewayBytes, byteLimitMessage("message")), category: category.optional(), command: z31.string().max(1e3).optional(), route: z31.string().max(500).optional(), client: z31.string().max(100).optional() }),
13937
- z31.object({ action: z31.literal("list"), status: status.optional(), limit: z31.number().int().min(1).max(100).optional(), cursor: z31.string().max(2e3).optional(), full: z31.boolean().optional() }),
13938
- z31.object({ action: z31.literal("queue"), status: status.optional(), category: category.optional(), groupId: z31.string().min(1).max(200).optional(), workspaceId: z31.string().max(200).optional(), since: z31.number().optional(), before: z31.number().optional(), limit: z31.number().int().min(1).max(100).optional(), cursor: z31.string().max(2e3).optional(), full: z31.boolean().optional() }),
13939
- z31.object({ action: z31.literal("note"), feedbackId: z31.string().min(1).max(200), note: z31.string().max(4e3).refine(fitsGatewayBytes, byteLimitMessage("note")) }),
13940
- z31.object({ action: z31.literal("group"), feedbackIds: z31.array(z31.string().min(1).max(200)).min(1).max(100), groupId: z31.string().min(1).max(200).nullable() }),
13941
- z31.object({ action: z31.literal("status"), feedbackId: z31.string().min(1).max(200), status: vendorStatus })
13210
+ var union = z30.discriminatedUnion("action", [
13211
+ z30.object({ action: z30.literal("submit"), message: z30.string().min(1).max(1e4).refine(fitsGatewayBytes, byteLimitMessage("message")), category: category.optional(), command: z30.string().max(1e3).optional(), route: z30.string().max(500).optional(), client: z30.string().max(100).optional() }),
13212
+ z30.object({ action: z30.literal("list"), status: status.optional(), limit: z30.number().int().min(1).max(100).optional(), cursor: z30.string().max(2e3).optional(), full: z30.boolean().optional() }),
13213
+ z30.object({ action: z30.literal("queue"), status: status.optional(), category: category.optional(), groupId: z30.string().min(1).max(200).optional(), workspaceId: z30.string().max(200).optional(), since: z30.number().optional(), before: z30.number().optional(), limit: z30.number().int().min(1).max(100).optional(), cursor: z30.string().max(2e3).optional(), full: z30.boolean().optional() }),
13214
+ z30.object({ action: z30.literal("note"), feedbackId: z30.string().min(1).max(200), note: z30.string().max(4e3).refine(fitsGatewayBytes, byteLimitMessage("note")) }),
13215
+ z30.object({ action: z30.literal("group"), feedbackIds: z30.array(z30.string().min(1).max(200)).min(1).max(100), groupId: z30.string().min(1).max(200).nullable() }),
13216
+ z30.object({ action: z30.literal("status"), feedbackId: z30.string().min(1).max(200), status: vendorStatus })
13942
13217
  ]);
13943
13218
  var specs = {
13944
13219
  submit: { params: ["message", "category", "command", "route", "client"], description: "message is required; category defaults to other." },
@@ -14065,34 +13340,34 @@ function registerFeedbackTool(server) {
14065
13340
  }
14066
13341
 
14067
13342
  // src/tools/shape.ts
14068
- import { z as z32 } from "zod/v3";
13343
+ import { z as z31 } from "zod/v3";
14069
13344
  var SHAPE_ACTIONS = ["list", "show", "agree", "dismiss"];
14070
13345
  var LIST_DISPOSITIONS = ["pending", "agreed", "dismissed", "expired"];
14071
13346
  var LIST_OUTCOMES = ["not_candidate", "atomic", "compound", "unavailable"];
14072
- var shapeSchema = z32.object({
14073
- action: z32.enum(SHAPE_ACTIONS).describe(
13347
+ var shapeSchema = z31.object({
13348
+ action: z31.enum(SHAPE_ACTIONS).describe(
14074
13349
  "'list': list shape advisories for this workspace, latest per subject. 'show': show one shape advisory by id. 'agree': agree with a compound advisory's split verdict. 'dismiss': dismiss a compound advisory's split verdict."
14075
13350
  ),
14076
- disposition: z32.enum(LIST_DISPOSITIONS).optional().describe(
13351
+ disposition: z31.enum(LIST_DISPOSITIONS).optional().describe(
14077
13352
  "For 'list': filter by disposition. Omitting both disposition and outcome defaults to the actionable set (disposition:pending, outcome:compound)."
14078
13353
  ),
14079
- outcome: z32.enum(LIST_OUTCOMES).optional().describe(
13354
+ outcome: z31.enum(LIST_OUTCOMES).optional().describe(
14080
13355
  "For 'list': filter by outcome. Omitting both disposition and outcome defaults to the actionable set (disposition:pending, outcome:compound)."
14081
13356
  ),
14082
- limit: z32.number().min(1).max(200).optional().describe("For 'list': max rows (default 50, max 200)."),
14083
- rowId: z32.string().max(200).optional().describe("For 'show'/'agree'/'dismiss': the advisory row id."),
14084
- reason: z32.string().max(1e3).optional().describe("For 'agree'/'dismiss': optional reason (capped at 1000 chars).")
13357
+ limit: z31.number().min(1).max(200).optional().describe("For 'list': max rows (default 50, max 200)."),
13358
+ rowId: z31.string().max(200).optional().describe("For 'show'/'agree'/'dismiss': the advisory row id."),
13359
+ reason: z31.string().max(1e3).optional().describe("For 'agree'/'dismiss': optional reason (capped at 1000 chars).")
14085
13360
  });
14086
- var shapeListVariant = z32.object({
14087
- action: z32.literal("list"),
14088
- disposition: z32.enum(LIST_DISPOSITIONS).optional(),
14089
- outcome: z32.enum(LIST_OUTCOMES).optional(),
14090
- limit: z32.number().min(1).max(200).optional()
13361
+ var shapeListVariant = z31.object({
13362
+ action: z31.literal("list"),
13363
+ disposition: z31.enum(LIST_DISPOSITIONS).optional(),
13364
+ outcome: z31.enum(LIST_OUTCOMES).optional(),
13365
+ limit: z31.number().min(1).max(200).optional()
14091
13366
  });
14092
- var shapeShowVariant = z32.object({ action: z32.literal("show"), rowId: z32.string().max(200) });
14093
- var shapeAgreeVariant = z32.object({ action: z32.literal("agree"), rowId: z32.string().max(200), reason: z32.string().max(1e3).optional() });
14094
- var shapeDismissVariant = z32.object({ action: z32.literal("dismiss"), rowId: z32.string().max(200), reason: z32.string().max(1e3).optional() });
14095
- var shapeActionUnion = z32.discriminatedUnion("action", [
13367
+ var shapeShowVariant = z31.object({ action: z31.literal("show"), rowId: z31.string().max(200) });
13368
+ var shapeAgreeVariant = z31.object({ action: z31.literal("agree"), rowId: z31.string().max(200), reason: z31.string().max(1e3).optional() });
13369
+ var shapeDismissVariant = z31.object({ action: z31.literal("dismiss"), rowId: z31.string().max(200), reason: z31.string().max(1e3).optional() });
13370
+ var shapeActionUnion = z31.discriminatedUnion("action", [
14096
13371
  shapeListVariant,
14097
13372
  shapeShowVariant,
14098
13373
  shapeAgreeVariant,
@@ -14239,31 +13514,31 @@ function registerShapeTools(server) {
14239
13514
  }
14240
13515
 
14241
13516
  // src/tools/question.ts
14242
- import { z as z33 } from "zod/v3";
13517
+ import { z as z32 } from "zod/v3";
14243
13518
  var QUESTION_ACTIONS = ["create", "adopt", "assign", "snooze", "decline", "answer", "force-close", "list"];
14244
- var questionSchema = z33.object({
14245
- action: z33.enum(QUESTION_ACTIONS).describe(
13519
+ var questionSchema = z32.object({
13520
+ action: z32.enum(QUESTION_ACTIONS).describe(
14246
13521
  "'create': create a new routed question from text (domains come from `pb question --help` / this tool's own refusal \u2014 never hardcode a slug). 'adopt': adopt attaches routing to an existing tension or repairs a facet-less question \u2014 create is the birth path. Without adopt, a tension captured via `pb capture \"TEN: ...\"` stays invisible to routing forever. 'assign': confirm ('take it', omit toRole/toPerson) or reassign (supply toRole and/or toPerson) ownership. 'snooze': defer a routed/escalated question (max 2, then it escalates). 'decline': pass on a suggested question, or (as owner/lead) decline an owned one \u2014 reason required. 'answer': close the loop \u2014 creates the answering entry itself unless --entry is given. 'force-close': Team-Lead-only escape hatch \u2014 reason required. 'list': the ack-gate read model \u2014 what you owe (or, with mine:true, every question you asked, including closed ones)."
14247
13522
  ),
14248
- entryId: z33.string().max(200).optional().describe("Target question's id (human TEN-nnn form or a raw entry id). Required for every action except 'create'/'list'."),
14249
- text: z33.string().max(5e3).optional().describe("'create': the question text. 'answer': the answer text (mutually exclusive with answerEntryId, max 2000 chars)."),
14250
- domain: z33.string().max(200).optional().describe("'create'/'adopt': the domain slug (see this tool's refusal for the live list)."),
14251
- blocks: z33.array(z33.string().max(200)).max(50).optional().describe("'create'/'adopt': entry ids this question is blocking (repeatable)."),
14252
- toRole: z33.string().max(200).optional().describe("'assign': reassign to this role (human ROL-nnn form or a raw entry id)."),
14253
- toPerson: z33.string().max(200).optional().describe("'assign': reassign to this person (a raw person id)."),
14254
- reason: z33.string().max(2e3).optional().describe("'decline'/'force-close': required reason."),
14255
- answerEntryId: z33.string().max(200).optional().describe("'answer': point at an already-written entry instead of supplying text (mutually exclusive with text)."),
14256
- mine: z33.boolean().optional().describe("'list': true for arm C (every question YOU asked, every status). Omit/false for the ack-gate view (what you owe).")
13523
+ entryId: z32.string().max(200).optional().describe("Target question's id (human TEN-nnn form or a raw entry id). Required for every action except 'create'/'list'."),
13524
+ text: z32.string().max(5e3).optional().describe("'create': the question text. 'answer': the answer text (mutually exclusive with answerEntryId, max 2000 chars)."),
13525
+ domain: z32.string().max(200).optional().describe("'create'/'adopt': the domain slug (see this tool's refusal for the live list)."),
13526
+ blocks: z32.array(z32.string().max(200)).max(50).optional().describe("'create'/'adopt': entry ids this question is blocking (repeatable)."),
13527
+ toRole: z32.string().max(200).optional().describe("'assign': reassign to this role (human ROL-nnn form or a raw entry id)."),
13528
+ toPerson: z32.string().max(200).optional().describe("'assign': reassign to this person (a raw person id)."),
13529
+ reason: z32.string().max(2e3).optional().describe("'decline'/'force-close': required reason."),
13530
+ answerEntryId: z32.string().max(200).optional().describe("'answer': point at an already-written entry instead of supplying text (mutually exclusive with text)."),
13531
+ mine: z32.boolean().optional().describe("'list': true for arm C (every question YOU asked, every status). Omit/false for the ack-gate view (what you owe).")
14257
13532
  });
14258
- var createVariant = z33.object({ action: z33.literal("create"), text: z33.string().min(1).max(5e3), domain: z33.string().max(200).optional(), blocks: z33.array(z33.string().max(200)).max(50).optional() });
14259
- var adoptVariant = z33.object({ action: z33.literal("adopt"), entryId: z33.string().max(200), domain: z33.string().max(200).optional(), blocks: z33.array(z33.string().max(200)).max(50).optional() });
14260
- var assignVariant = z33.object({ action: z33.literal("assign"), entryId: z33.string().max(200), toRole: z33.string().max(200).optional(), toPerson: z33.string().max(200).optional() });
14261
- var snoozeVariant = z33.object({ action: z33.literal("snooze"), entryId: z33.string().max(200) });
14262
- var declineVariant = z33.object({ action: z33.literal("decline"), entryId: z33.string().max(200), reason: z33.string().min(1).max(2e3) });
14263
- var answerVariant = z33.object({ action: z33.literal("answer"), entryId: z33.string().max(200), text: z33.string().max(2e3).optional(), answerEntryId: z33.string().max(200).optional() });
14264
- var forceCloseVariant = z33.object({ action: z33.literal("force-close"), entryId: z33.string().max(200), reason: z33.string().min(1).max(2e3) });
14265
- var listVariant = z33.object({ action: z33.literal("list"), mine: z33.boolean().optional() });
14266
- var questionActionUnion = z33.discriminatedUnion("action", [
13533
+ var createVariant = z32.object({ action: z32.literal("create"), text: z32.string().min(1).max(5e3), domain: z32.string().max(200).optional(), blocks: z32.array(z32.string().max(200)).max(50).optional() });
13534
+ var adoptVariant = z32.object({ action: z32.literal("adopt"), entryId: z32.string().max(200), domain: z32.string().max(200).optional(), blocks: z32.array(z32.string().max(200)).max(50).optional() });
13535
+ var assignVariant = z32.object({ action: z32.literal("assign"), entryId: z32.string().max(200), toRole: z32.string().max(200).optional(), toPerson: z32.string().max(200).optional() });
13536
+ var snoozeVariant = z32.object({ action: z32.literal("snooze"), entryId: z32.string().max(200) });
13537
+ var declineVariant = z32.object({ action: z32.literal("decline"), entryId: z32.string().max(200), reason: z32.string().min(1).max(2e3) });
13538
+ var answerVariant = z32.object({ action: z32.literal("answer"), entryId: z32.string().max(200), text: z32.string().max(2e3).optional(), answerEntryId: z32.string().max(200).optional() });
13539
+ var forceCloseVariant = z32.object({ action: z32.literal("force-close"), entryId: z32.string().max(200), reason: z32.string().min(1).max(2e3) });
13540
+ var listVariant = z32.object({ action: z32.literal("list"), mine: z32.boolean().optional() });
13541
+ var questionActionUnion = z32.discriminatedUnion("action", [
14267
13542
  createVariant,
14268
13543
  adoptVariant,
14269
13544
  assignVariant,
@@ -14506,23 +13781,23 @@ function registerQuestionTools(server) {
14506
13781
  }
14507
13782
 
14508
13783
  // src/tools/role.ts
14509
- import { z as z34 } from "zod/v3";
13784
+ import { z as z33 } from "zod/v3";
14510
13785
  var ROLE_ACTIONS = ["accountable"];
14511
- var roleSchema = z34.object({
14512
- action: z34.enum(ROLE_ACTIONS).describe("'accountable': what this role owns (explicit), reports on (reporting), and \u2014 not yet computed \u2014 inherits via Team-Lead vacancy coverage."),
14513
- roleId: z34.string().max(200).optional().describe("The role, by the human chain id `chain.getEntry` or `pb get` would print for it, or a raw entry id. Required for 'accountable'."),
14514
- canonicalKey: z34.string().max(200).optional().describe("'accountable': narrow to one kind (e.g. objective, key_result, tension)."),
14515
- limit: z34.number().int().positive().max(200).optional().describe("'accountable': max explicit-list rows per page (default and maximum 200)."),
14516
- cursor: z34.number().optional().describe("'accountable': continue the explicit list from an earlier truncated page's nextCursor.")
13786
+ var roleSchema = z33.object({
13787
+ action: z33.enum(ROLE_ACTIONS).describe("'accountable': what this role owns (explicit), reports on (reporting), and \u2014 not yet computed \u2014 inherits via Team-Lead vacancy coverage."),
13788
+ roleId: z33.string().max(200).optional().describe("The role, by the human chain id `chain.getEntry` or `pb get` would print for it, or a raw entry id. Required for 'accountable'."),
13789
+ canonicalKey: z33.string().max(200).optional().describe("'accountable': narrow to one kind (e.g. objective, key_result, tension)."),
13790
+ limit: z33.number().int().positive().max(200).optional().describe("'accountable': max explicit-list rows per page (default and maximum 200)."),
13791
+ cursor: z33.number().optional().describe("'accountable': continue the explicit list from an earlier truncated page's nextCursor.")
14517
13792
  });
14518
- var accountableVariant = z34.object({
14519
- action: z34.literal("accountable"),
14520
- roleId: z34.string().max(200),
14521
- canonicalKey: z34.string().max(200).optional(),
14522
- limit: z34.number().int().positive().max(200).optional(),
14523
- cursor: z34.number().optional()
13793
+ var accountableVariant = z33.object({
13794
+ action: z33.literal("accountable"),
13795
+ roleId: z33.string().max(200),
13796
+ canonicalKey: z33.string().max(200).optional(),
13797
+ limit: z33.number().int().positive().max(200).optional(),
13798
+ cursor: z33.number().optional()
14524
13799
  });
14525
- var roleActionUnion = z34.discriminatedUnion("action", [accountableVariant]);
13800
+ var roleActionUnion = z33.discriminatedUnion("action", [accountableVariant]);
14526
13801
  var ROLE_ACTION_SPECS = {
14527
13802
  accountable: { params: ["roleId", "canonicalKey", "limit", "cursor"], description: "roleId is required; canonicalKey/limit/cursor are optional." }
14528
13803
  };
@@ -14595,49 +13870,49 @@ function registerRoleTools(server) {
14595
13870
  }
14596
13871
 
14597
13872
  // src/tools/outcome.ts
14598
- import { z as z35 } from "zod/v3";
13873
+ import { z as z34 } from "zod/v3";
14599
13874
  var OUTCOME_ACTIONS = ["record", "list", "show"];
14600
13875
  var READING_KINDS = ["measurement", "estimate", "correction"];
14601
- var outcomeSchema = z35.object({
14602
- action: z35.enum(OUTCOME_ACTIONS).describe(
13876
+ var outcomeSchema = z34.object({
13877
+ action: z34.enum(OUTCOME_ACTIONS).describe(
14603
13878
  "'record': record a reading (evidence, not a verdict). 'list': list readings, newest first. 'show': the derived view \u2014 current, progress, pace, staleness, recorded completion."
14604
13879
  ),
14605
- entryId: z35.string().max(200).optional().describe("The key result (or other outcome-bearing) entry, by its chain id. Required for all three actions."),
13880
+ entryId: z34.string().max(200).optional().describe("The key result (or other outcome-bearing) entry, by its chain id. Required for all three actions."),
14606
13881
  // PR #858 review round 3 (Codex P2, PRRT_kwDORS4ils6gXHj9): a JSON number that overflows to
14607
13882
  // `Infinity` (e.g. `1e400`) parses fine in JS and passes a plain `z.number()`, but
14608
13883
  // `callGateway()` serializes it with `JSON.stringify`, which turns `Infinity` into `null` —
14609
13884
  // the backend's `v.number()` validator then rejects a call this tool's own schema accepted.
14610
13885
  // `.finite()` rejects it here, matching the explicit finite-value checks the CLI/other outcome
14611
13886
  // entry surfaces already use.
14612
- value: z35.number().finite().optional().describe("'record': the measured value. Required for 'record'."),
14613
- valueText: z35.string().max(500).optional().describe("'record': verbatim as-reported form when the source was not clean, e.g. '~62%'. Display only."),
14614
- asOf: z35.string().max(64).optional().describe("'record': when the world was in this state, as a parseable date (default: now). Back-dating a backfill is legitimate \u2014 it shows up as asOf earlier than recordedAt."),
14615
- kind: z35.enum(READING_KINDS).optional().describe("'record': measurement | estimate | correction (default: measurement)."),
14616
- correctsReadingId: z35.string().max(200).optional().describe("'record': required with kind='correction' \u2014 the reading id this replaces. Refused if that row is already superseded."),
14617
- recordedByRoleId: z35.string().max(200).optional().describe("'record': the accountable role (default: the entry's reporterRoleId, then its owning role)."),
14618
- note: z35.string().max(2e3).optional().describe("'record': free-text note recorded with the reading."),
14619
- source: z35.string().max(500).optional().describe("'record': where the number came from \u2014 a dashboard name, a query, a URL."),
14620
- limit: z35.number().int().positive().max(200).optional().describe("'list': max rows to return (default and maximum 200).")
13887
+ value: z34.number().finite().optional().describe("'record': the measured value. Required for 'record'."),
13888
+ valueText: z34.string().max(500).optional().describe("'record': verbatim as-reported form when the source was not clean, e.g. '~62%'. Display only."),
13889
+ asOf: z34.string().max(64).optional().describe("'record': when the world was in this state, as a parseable date (default: now). Back-dating a backfill is legitimate \u2014 it shows up as asOf earlier than recordedAt."),
13890
+ kind: z34.enum(READING_KINDS).optional().describe("'record': measurement | estimate | correction (default: measurement)."),
13891
+ correctsReadingId: z34.string().max(200).optional().describe("'record': required with kind='correction' \u2014 the reading id this replaces. Refused if that row is already superseded."),
13892
+ recordedByRoleId: z34.string().max(200).optional().describe("'record': the accountable role (default: the entry's reporterRoleId, then its owning role)."),
13893
+ note: z34.string().max(2e3).optional().describe("'record': free-text note recorded with the reading."),
13894
+ source: z34.string().max(500).optional().describe("'record': where the number came from \u2014 a dashboard name, a query, a URL."),
13895
+ limit: z34.number().int().positive().max(200).optional().describe("'list': max rows to return (default and maximum 200).")
14621
13896
  });
14622
- var recordVariant = z35.object({
14623
- action: z35.literal("record"),
14624
- entryId: z35.string().max(200),
14625
- value: z35.number().finite(),
14626
- valueText: z35.string().max(500).optional(),
14627
- asOf: z35.string().max(64).optional(),
14628
- kind: z35.enum(READING_KINDS).optional(),
14629
- correctsReadingId: z35.string().max(200).optional(),
14630
- recordedByRoleId: z35.string().max(200).optional(),
14631
- note: z35.string().max(2e3).optional(),
14632
- source: z35.string().max(500).optional()
13897
+ var recordVariant = z34.object({
13898
+ action: z34.literal("record"),
13899
+ entryId: z34.string().max(200),
13900
+ value: z34.number().finite(),
13901
+ valueText: z34.string().max(500).optional(),
13902
+ asOf: z34.string().max(64).optional(),
13903
+ kind: z34.enum(READING_KINDS).optional(),
13904
+ correctsReadingId: z34.string().max(200).optional(),
13905
+ recordedByRoleId: z34.string().max(200).optional(),
13906
+ note: z34.string().max(2e3).optional(),
13907
+ source: z34.string().max(500).optional()
14633
13908
  });
14634
- var listVariant2 = z35.object({
14635
- action: z35.literal("list"),
14636
- entryId: z35.string().max(200),
14637
- limit: z35.number().int().positive().max(200).optional()
13909
+ var listVariant2 = z34.object({
13910
+ action: z34.literal("list"),
13911
+ entryId: z34.string().max(200),
13912
+ limit: z34.number().int().positive().max(200).optional()
14638
13913
  });
14639
- var showVariant = z35.object({ action: z35.literal("show"), entryId: z35.string().max(200) });
14640
- var outcomeActionUnion = z35.discriminatedUnion("action", [recordVariant, listVariant2, showVariant]);
13914
+ var showVariant = z34.object({ action: z34.literal("show"), entryId: z34.string().max(200) });
13915
+ var outcomeActionUnion = z34.discriminatedUnion("action", [recordVariant, listVariant2, showVariant]);
14641
13916
  var OUTCOME_ACTION_SPECS = {
14642
13917
  record: {
14643
13918
  params: ["entryId", "value", "valueText", "asOf", "kind", "correctsReadingId", "recordedByRoleId", "note", "source"],
@@ -14843,7 +14118,7 @@ import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
14843
14118
  var DATA_HANDLING_SECTION = "### How PB Handles Your Data\n- **The reply is the source of truth**: every reply carries `ok`; on failure it carries `code` and guidance. Read the reply, never an expectation.\n- **Empty-workspace safe**: the orientation resource and this cheatsheet render on a workspace with no entries and no collections.\n- **Workspace-agnostic**: PB is one product used by many teams. Nothing in these instructions is specific to your workspace; your own principles, standards and rules arrive through `productbrain://orientation`.\n- **Served, not installed**: these instructions, `productbrain://orientation` and `productbrain://agent-cheatsheet` are served over MCP.";
14844
14119
  var AGENT_CHEATSHEET = `# Product Brain \u2014 Agent Cheatsheet
14845
14120
 
14846
- ## Core Tools (Serves 15 default compound tools; 2 more (chain, chain-review) behind PB_MODULES=gitchain)
14121
+ ## Core Tools (Serves 15 compound tools, always)
14847
14122
  | Tool | Purpose | Actions |
14848
14123
  |---|---|---|
14849
14124
  | \`orient\` | Workspace context, governance, active work packages, session start | \`action\`: start, task, record-activation |
@@ -15349,12 +14624,12 @@ ${entry.labels.map((l) => `- ${l.name ?? l.slug}`).join("\n")}`);
15349
14624
  }
15350
14625
 
15351
14626
  // src/prompts/index.ts
15352
- import { z as z36 } from "zod/v3";
14627
+ import { z as z35 } from "zod/v3";
15353
14628
  function registerPrompts(server) {
15354
14629
  server.prompt(
15355
14630
  "review-against-rules",
15356
14631
  "Review code or a design decision against all business rules for a given domain. Fetches the rules and asks you to do a structured compliance review.",
15357
- { domain: z36.string().describe("Business rule domain (e.g. 'Identity & Access', 'Governance & Decision-Making')") },
14632
+ { domain: z35.string().describe("Business rule domain (e.g. 'Identity & Access', 'Governance & Decision-Making')") },
15358
14633
  async ({ domain }) => {
15359
14634
  const entries = await kernelQuery("chain.listEntries", { collectionSlug: "business-rules" });
15360
14635
  const rules = entries.filter((e) => e.data?.domain === domain);
@@ -15407,7 +14682,7 @@ Provide a structured review with a compliance status for each rule (COMPLIANT /
15407
14682
  server.prompt(
15408
14683
  "name-check",
15409
14684
  "Check variable names, field names, or API names against the glossary for terminology alignment. Flags drift from canonical terms.",
15410
- { names: z36.string().describe("Comma-separated list of names to check (e.g. 'vendor_id, compliance_level, formulator_type')") },
14685
+ { names: z35.string().describe("Comma-separated list of names to check (e.g. 'vendor_id, compliance_level, formulator_type')") },
15411
14686
  async ({ names }) => {
15412
14687
  const terms = await kernelQuery("chain.listEntries", { collectionSlug: "glossary" });
15413
14688
  const glossaryContext = terms.map(
@@ -15443,7 +14718,7 @@ Format as a table: Name | Status | Canonical Form | Action Needed`
15443
14718
  server.prompt(
15444
14719
  "draft-decision-record",
15445
14720
  "Draft a structured decision record from a description of what was decided. Includes context from recent decisions and relevant rules.",
15446
- { context: z36.string().describe("Description of the decision (e.g. 'We decided to use MRSL v3.1 as the conformance baseline because...')") },
14721
+ { context: z35.string().describe("Description of the decision (e.g. 'We decided to use MRSL v3.1 as the conformance baseline because...')") },
15447
14722
  async ({ context }) => {
15448
14723
  const recentDecisions = await kernelQuery("chain.listEntries", { collectionSlug: "decisions" });
15449
14724
  const sorted = [...recentDecisions].sort((a, b) => (b.data?.date ?? "") > (a.data?.date ?? "") ? 1 : -1).slice(0, 5);
@@ -15481,8 +14756,8 @@ After drafting, I can log it using the capture tool with collection "decisions".
15481
14756
  "draft-rule-from-context",
15482
14757
  "Draft a new business rule from an observation or discovery made while coding. Fetches existing rules for the domain to ensure consistency.",
15483
14758
  {
15484
- observation: z36.string().describe("What you observed or discovered (e.g. 'Suppliers can have multiple org types in Gateway')"),
15485
- domain: z36.string().describe("Which domain this rule belongs to (e.g. 'Governance & Decision-Making')")
14759
+ observation: z35.string().describe("What you observed or discovered (e.g. 'Suppliers can have multiple org types in Gateway')"),
14760
+ domain: z35.string().describe("Which domain this rule belongs to (e.g. 'Governance & Decision-Making')")
15486
14761
  },
15487
14762
  async ({ observation, domain }) => {
15488
14763
  const allRules = await kernelQuery("chain.listEntries", { collectionSlug: "business-rules" });
@@ -15591,9 +14866,6 @@ function createProductBrainServer() {
15591
14866
  { name: "Product Brain", version: SERVER_VERSION },
15592
14867
  { capabilities: { logging: {} }, instructions: INSTRUCTIONS }
15593
14868
  );
15594
- const enabledModules = new Set(
15595
- (process.env.PB_MODULES ?? "core").split(",").map((m) => m.trim().toLowerCase())
15596
- );
15597
14869
  registerSessionTools(server);
15598
14870
  registerEntriesTools(server);
15599
14871
  registerRelationsTools(server);
@@ -15609,7 +14881,6 @@ function createProductBrainServer() {
15609
14881
  registerQuestionTools(server);
15610
14882
  registerRoleTools(server);
15611
14883
  registerOutcomeTools(server);
15612
- if (enabledModules.has("gitchain")) registerGitChainTools(server);
15613
14884
  registerResources(server);
15614
14885
  registerPrompts(server);
15615
14886
  return server;
@@ -15645,4 +14916,4 @@ export {
15645
14916
  createProductBrainServer,
15646
14917
  initFeatureFlags
15647
14918
  };
15648
- //# sourceMappingURL=chunk-GBEBTAN6.js.map
14919
+ //# sourceMappingURL=chunk-NKO44SHW.js.map