@productbrain/mcp 0.0.1-beta.61 → 0.0.1-beta.63

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.
@@ -5,7 +5,7 @@ import {
5
5
  trackCaptureClassifierFallback,
6
6
  trackQualityVerdict,
7
7
  trackToolCall
8
- } from "./chunk-MRIO53BY.js";
8
+ } from "./chunk-RQXM3TCI.js";
9
9
 
10
10
  // src/tools/smart-capture.ts
11
11
  import { z as z2 } from "zod";
@@ -1780,7 +1780,9 @@ var captureSchema = z2.object({
1780
1780
  to: z2.string().describe("Target entry ID (e.g. 'BR-64', 'ARCH-8')"),
1781
1781
  type: z2.string().describe("Relation type (e.g. 'governs', 'related_to', 'informs')")
1782
1782
  })).optional().describe("Relations to create after capture. Skips auto-link discovery when provided."),
1783
- autoCommit: z2.boolean().optional().describe("If true, commits the entry immediately after capture + linking. Use for ungoverned collections or when you're certain.")
1783
+ autoCommit: z2.boolean().optional().describe(
1784
+ "If true, commits the entry immediately after capture + linking. If omitted, Open mode workspaces auto-commit by default and consensus/role modes stay draft-first."
1785
+ )
1784
1786
  });
1785
1787
  var batchCaptureSchema = z2.object({
1786
1788
  entries: z2.array(z2.object({
@@ -1788,7 +1790,10 @@ var batchCaptureSchema = z2.object({
1788
1790
  name: z2.string().describe("Display name"),
1789
1791
  description: z2.string().describe("Full context / definition"),
1790
1792
  entryId: z2.string().optional().describe("Optional custom entry ID")
1791
- })).min(1).max(50).describe("Array of entries to capture")
1793
+ })).min(1).max(50).describe("Array of entries to capture"),
1794
+ autoCommit: z2.boolean().optional().describe(
1795
+ "If true, commits created entries immediately after linking. If omitted, Open mode workspaces commit by default and consensus/role modes stay draft-first."
1796
+ )
1792
1797
  });
1793
1798
  var captureClassifierSchema = z2.object({
1794
1799
  enabled: z2.boolean(),
@@ -2073,10 +2078,15 @@ var batchCaptureOutputSchema = z2.object({
2073
2078
  captured: z2.array(z2.object({
2074
2079
  entryId: z2.string(),
2075
2080
  collection: z2.string(),
2076
- name: z2.string()
2081
+ name: z2.string(),
2082
+ status: z2.enum(["draft", "committed", "proposed"])
2077
2083
  })),
2078
2084
  total: z2.number(),
2079
2085
  failed: z2.number(),
2086
+ committed: z2.number(),
2087
+ proposed: z2.number(),
2088
+ drafts: z2.number(),
2089
+ autoCommitApplied: z2.boolean(),
2080
2090
  failedEntries: z2.array(z2.object({
2081
2091
  index: z2.number(),
2082
2092
  collection: z2.string(),
@@ -2084,6 +2094,9 @@ var batchCaptureOutputSchema = z2.object({
2084
2094
  error: z2.string()
2085
2095
  })).optional()
2086
2096
  });
2097
+ function shouldAutoCommitCapture(autoCommit, governanceMode) {
2098
+ return autoCommit === true || autoCommit === void 0 && governanceMode === "open";
2099
+ }
2087
2100
  function registerSmartCaptureTools(server) {
2088
2101
  const supportedCollections = new Set(
2089
2102
  CLASSIFIABLE_COLLECTIONS.filter((slug) => PROFILES.has(slug))
@@ -2092,7 +2105,7 @@ function registerSmartCaptureTools(server) {
2092
2105
  "capture",
2093
2106
  {
2094
2107
  title: "Capture",
2095
- description: "The single tool for creating knowledge entries. Creates an entry, auto-links related entries, and returns a quality scorecard \u2014 all in one call. Provide a name and description; `collection` is optional when `capture-without-thinking` is enabled.\n\nSupported collections with smart profiles: tensions, business-rules, glossary, decisions, features, audiences, strategy, standards, maps, bets, insights, assumptions, principles, tracking-events.\nAll other collections get an ENT-{random} ID and sensible defaults.\n\n**Explicit data:** When you know the schema, pass `data: { field: value }` to set fields directly. Top-level `name` and `description` always win for those fields. `data` wins over inference for all other fields.\n\n**Compound capture:** Pass `links` to create relations in the same call (skips auto-link discovery). Pass `autoCommit: true` to promote the entry from draft to SSOT immediately after linking. Governed collections (glossary, business-rules, principles, standards, strategy, features, architecture) will warn but still commit \u2014 use only when you're certain.\n\nAlways creates as 'draft' unless `autoCommit` is true. Use `update-entry` for post-creation adjustments.",
2108
+ description: "The single tool for creating knowledge entries. Creates an entry, auto-links related entries, and returns a quality scorecard \u2014 all in one call. Provide a name and description; `collection` is optional when `capture-without-thinking` is enabled.\n\nSupported collections with smart profiles: tensions, business-rules, glossary, decisions, features, audiences, strategy, standards, maps, bets, insights, assumptions, principles, tracking-events.\nAll other collections get an ENT-{random} ID and sensible defaults.\n\n**Explicit data:** When you know the schema, pass `data: { field: value }` to set fields directly. Top-level `name` and `description` always win for those fields. `data` wins over inference for all other fields.\n\n**Compound capture:** Pass `links` to create relations in the same call (skips auto-link discovery). In Open mode, entries commit by default when `autoCommit` is omitted. Pass `autoCommit: true` to promote the entry from draft to SSOT immediately after linking when you want to be explicit. Governed collections (glossary, business-rules, principles, standards, strategy, features, architecture) will warn but still commit \u2014 use only when you're certain.\n\nEntries are created as `draft` first, then may publish immediately depending on governance and `autoCommit`. Use `update-entry` for post-creation adjustments.",
2096
2109
  inputSchema: captureSchema.shape,
2097
2110
  annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
2098
2111
  },
@@ -2377,7 +2390,7 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
2377
2390
  } catch {
2378
2391
  }
2379
2392
  }
2380
- const shouldAutoCommit = autoCommit === true || autoCommit === void 0 && wsCtx.governanceMode === "open";
2393
+ const shouldAutoCommit = shouldAutoCommitCapture(autoCommit, wsCtx.governanceMode);
2381
2394
  let finalStatus = "draft";
2382
2395
  let commitError = null;
2383
2396
  if (shouldAutoCommit && finalEntryId) {
@@ -2576,14 +2589,16 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
2576
2589
  "batch-capture",
2577
2590
  {
2578
2591
  title: "Batch Capture",
2579
- description: "Create multiple knowledge entries in one call. Ideal for workspace setup, document ingestion, or any scenario where you need to capture many entries at once.\n\nEach entry is created independently \u2014 if one fails, the others still succeed. Returns a compact summary instead of per-entry quality scorecards.\n\nAuto-linking runs per entry but contradiction checks and readiness hints are skipped for speed. Use `quality action=check` on individual entries afterward if needed.",
2592
+ description: "Create multiple knowledge entries in one call. Ideal for workspace setup, document ingestion, or any scenario where you need to capture many entries at once.\n\nEach entry is created independently \u2014 if one fails, the others still succeed. Returns a compact summary instead of per-entry quality scorecards.\n\nAuto-linking runs per entry but contradiction checks and readiness hints are skipped for speed. Use `quality action=check` on individual entries afterward if needed.\n\nPass `autoCommit: false` to keep the whole batch draft-first. If omitted, Open mode workspaces commit by default and consensus/role modes keep drafts.",
2580
2593
  inputSchema: batchCaptureSchema.shape,
2581
2594
  annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
2582
2595
  },
2583
- withEnvelope(async ({ entries }) => {
2596
+ withEnvelope(async ({ entries, autoCommit }) => {
2584
2597
  requireWriteAccess();
2585
2598
  const agentId = getAgentSessionId();
2586
2599
  const createdBy = agentId ? `agent:${agentId}` : "capture";
2600
+ const wsCtx = await getWorkspaceContext();
2601
+ const autoCommitApplied = shouldAutoCommitCapture(autoCommit, wsCtx.governanceMode);
2587
2602
  const results = [];
2588
2603
  await server.sendLoggingMessage({
2589
2604
  level: "info",
@@ -2607,7 +2622,15 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
2607
2622
  const profile = PROFILES.get(entry.collection) ?? FALLBACK_PROFILE;
2608
2623
  const col = collCache.get(entry.collection);
2609
2624
  if (!col) {
2610
- results.push({ name: entry.name, collection: entry.collection, entryId: "", ok: false, autoLinks: 0, error: `Collection "${entry.collection}" not found` });
2625
+ results.push({
2626
+ name: entry.name,
2627
+ collection: entry.collection,
2628
+ entryId: "",
2629
+ ok: false,
2630
+ autoLinks: 0,
2631
+ status: "draft",
2632
+ error: `Collection "${entry.collection}" not found`
2633
+ });
2611
2634
  continue;
2612
2635
  }
2613
2636
  const data = {};
@@ -2657,6 +2680,8 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
2657
2680
  });
2658
2681
  const internalId = result.docId;
2659
2682
  const finalEntryId = result.entryId;
2683
+ let finalStatus = "draft";
2684
+ let commitError;
2660
2685
  let autoLinkCount = 0;
2661
2686
  const searchQuery = extractSearchTerms(entry.name, entry.description);
2662
2687
  if (searchQuery) {
@@ -2689,14 +2714,49 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
2689
2714
  } catch {
2690
2715
  }
2691
2716
  }
2692
- results.push({ name: entry.name, collection: entry.collection, entryId: finalEntryId, ok: true, autoLinks: autoLinkCount });
2717
+ if (autoCommitApplied) {
2718
+ try {
2719
+ const commitResult = await mcpMutation("chain.commitEntry", {
2720
+ entryId: finalEntryId,
2721
+ author: agentId ? `agent:${agentId}` : void 0,
2722
+ sessionId: agentId ?? void 0
2723
+ });
2724
+ finalStatus = commitResult?.status === "proposal_created" ? "proposed" : "committed";
2725
+ if (finalStatus === "committed") {
2726
+ await recordSessionActivity({ entryModified: internalId });
2727
+ }
2728
+ } catch (error) {
2729
+ commitError = error instanceof Error ? error.message : String(error);
2730
+ }
2731
+ }
2732
+ results.push({
2733
+ name: entry.name,
2734
+ collection: entry.collection,
2735
+ entryId: finalEntryId,
2736
+ ok: true,
2737
+ autoLinks: autoLinkCount,
2738
+ status: finalStatus,
2739
+ ...commitError ? { commitError } : {}
2740
+ });
2693
2741
  } catch (error) {
2694
2742
  const msg = error instanceof Error ? error.message : String(error);
2695
- results.push({ name: entry.name, collection: entry.collection, entryId: "", ok: false, autoLinks: 0, error: msg });
2743
+ results.push({
2744
+ name: entry.name,
2745
+ collection: entry.collection,
2746
+ entryId: "",
2747
+ ok: false,
2748
+ autoLinks: 0,
2749
+ status: "draft",
2750
+ error: msg
2751
+ });
2696
2752
  }
2697
2753
  }
2698
2754
  const created = results.filter((r) => r.ok);
2699
2755
  const failed = results.filter((r) => !r.ok);
2756
+ const committed = created.filter((r) => r.status === "committed");
2757
+ const proposed = created.filter((r) => r.status === "proposed");
2758
+ const drafts = created.filter((r) => r.status === "draft");
2759
+ const commitFailures = created.filter((r) => r.commitError);
2700
2760
  await server.sendLoggingMessage({
2701
2761
  level: "info",
2702
2762
  data: `Batch complete. ${created.length} succeeded, ${failed.length} failed.`,
@@ -2713,6 +2773,12 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
2713
2773
  `**Auto-links created:** ${totalAutoLinks}`,
2714
2774
  ""
2715
2775
  ];
2776
+ if (created.length > 0) {
2777
+ lines.push(
2778
+ `**Statuses:** ${committed.length} committed, ${proposed.length} proposed, ${drafts.length} draft.`
2779
+ );
2780
+ lines.push("");
2781
+ }
2716
2782
  if (byCollection.size > 0) {
2717
2783
  lines.push("## By Collection");
2718
2784
  for (const [col, count] of byCollection) {
@@ -2723,8 +2789,15 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
2723
2789
  if (created.length > 0) {
2724
2790
  lines.push("## Created");
2725
2791
  for (const r of created) {
2726
- const linkNote = r.autoLinks > 0 ? ` (${r.autoLinks} auto-links)` : "";
2727
- lines.push(`- **${r.entryId}**: ${r.name} [${r.collection}]${linkNote}`);
2792
+ const linkNote = r.autoLinks > 0 ? `, ${r.autoLinks} auto-links` : "";
2793
+ lines.push(`- **${r.entryId}**: ${r.name} [${r.collection}] \u2014 \`${r.status}\`${linkNote}`);
2794
+ }
2795
+ }
2796
+ if (commitFailures.length > 0) {
2797
+ lines.push("");
2798
+ lines.push("## Saved as draft");
2799
+ for (const r of commitFailures) {
2800
+ lines.push(`- **${r.entryId}**: ${r.name} [${r.collection}] \u2014 ${r.commitError}`);
2728
2801
  }
2729
2802
  }
2730
2803
  if (failed.length > 0) {
@@ -2741,22 +2814,34 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
2741
2814
  lines.push("");
2742
2815
  lines.push("## Next Steps");
2743
2816
  lines.push(`- **Connect:** Run \`graph action=suggest\` on key entries to build the knowledge graph`);
2744
- lines.push(`- **Commit:** Use \`commit-entry\` to promote drafts to SSOT`);
2817
+ if (drafts.length > 0) {
2818
+ lines.push(`- **Commit:** Use \`commit-entry\` to promote remaining drafts to SSOT`);
2819
+ }
2745
2820
  lines.push(`- **Quality:** Run \`quality action=check\` on individual entries to assess completeness`);
2746
2821
  }
2747
- const summary = failed.length > 0 ? `Batch captured ${created.length}/${entries.length} entries (${failed.length} failed).` : `Batch captured ${created.length} entries successfully.`;
2822
+ const summary = failed.length > 0 ? `Batch captured ${created.length}/${entries.length} entries (${failed.length} failed, ${committed.length} committed, ${proposed.length} proposed, ${drafts.length} draft).` : `Batch captured ${created.length} entries successfully (${committed.length} committed, ${proposed.length} proposed, ${drafts.length} draft).`;
2823
+ const firstDraft = drafts[0];
2748
2824
  const next = created.length > 0 ? [
2749
2825
  { tool: "graph", description: "Discover connections", parameters: { action: "suggest", entryId: created[0].entryId } },
2750
- { tool: "commit-entry", description: "Commit first entry", parameters: { entryId: created[0].entryId } }
2826
+ ...firstDraft ? [{ tool: "commit-entry", description: "Commit first draft", parameters: { entryId: firstDraft.entryId } }] : []
2751
2827
  ] : [];
2752
2828
  return {
2753
2829
  content: [{ type: "text", text: lines.join("\n") }],
2754
2830
  structuredContent: success(
2755
2831
  summary,
2756
2832
  {
2757
- captured: created.map((r) => ({ entryId: r.entryId, collection: r.collection, name: r.name })),
2833
+ captured: created.map((r) => ({
2834
+ entryId: r.entryId,
2835
+ collection: r.collection,
2836
+ name: r.name,
2837
+ status: r.status
2838
+ })),
2758
2839
  total: created.length,
2759
2840
  failed: failed.length,
2841
+ committed: committed.length,
2842
+ proposed: proposed.length,
2843
+ drafts: drafts.length,
2844
+ autoCommitApplied,
2760
2845
  ...failed.length > 0 && {
2761
2846
  failedEntries: failed.map((r) => ({
2762
2847
  index: results.indexOf(r),
@@ -3026,4 +3111,4 @@ export {
3026
3111
  formatRubricCoaching,
3027
3112
  formatRubricVerdictSection
3028
3113
  };
3029
- //# sourceMappingURL=chunk-4M3SUZHX.js.map
3114
+ //# sourceMappingURL=chunk-DM5DZC7B.js.map