@productbrain/mcp 0.0.1-beta.175 → 0.0.1-beta.176

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.
@@ -502,79 +502,6 @@ async function recoverSessionState() {
502
502
  }
503
503
  }
504
504
 
505
- // src/featureFlags.ts
506
- import { PostHog } from "posthog-node";
507
- var client = null;
508
- var SERVER_FLAGS = {
509
- "workspace-full-surface": { status: "active" },
510
- "active-intelligence-shaping": { status: "active" },
511
- "capture-without-thinking": { status: "active" },
512
- "chainwork-enabled": { status: "active" },
513
- "bet-203-roadmap-coordination": { status: "active" },
514
- "bet-221-governance-at-scale": { status: "active" },
515
- "bet-226-semantic-chunking": { status: "active" }
516
- };
517
- var REGISTERED_FLAGS_DEFAULT_ON = true;
518
- var DEV_DEFAULT_ALL_ON = process.env.NODE_ENV === "development" && process.env.FLAGS_DEV_DEFAULT_ON === "true";
519
- var LOCAL_OVERRIDES = {
520
- // Uncomment for local dev without PostHog:
521
- // "workspace-full-surface": false,
522
- // "chainwork-enabled": true,
523
- // "active-intelligence-shaping": true, // ENT-59: Opus-powered investigation during shaping
524
- // "capture-without-thinking": true, // BET-73: collection-optional capture classifier
525
- };
526
- function initFeatureFlags(posthogClient) {
527
- if (posthogClient) {
528
- client = posthogClient;
529
- return;
530
- }
531
- const apiKey = process.env.POSTHOG_MCP_KEY || process.env.PUBLIC_POSTHOG_KEY;
532
- if (!apiKey) {
533
- client = null;
534
- return;
535
- }
536
- client = new PostHog(apiKey, {
537
- host: process.env.PUBLIC_POSTHOG_HOST || "https://eu.i.posthog.com",
538
- flushAt: 1,
539
- flushInterval: 5e3,
540
- featureFlagsPollingInterval: 3e4
541
- });
542
- }
543
- async function isFeatureEnabled(flag, workspaceId, workspaceSlug) {
544
- if (process.env.FEATURE_KILL_SWITCH === "true") return false;
545
- if (flag in LOCAL_OVERRIDES) return LOCAL_OVERRIDES[flag];
546
- if (REGISTERED_FLAGS_DEFAULT_ON && flag in SERVER_FLAGS && SERVER_FLAGS[flag].status === "active") return true;
547
- if (DEV_DEFAULT_ALL_ON) return true;
548
- if (!client) return false;
549
- try {
550
- const primary = await client.isFeatureEnabled(flag, workspaceId, {
551
- groups: { workspace: workspaceId },
552
- groupProperties: {
553
- workspace: {
554
- workspace_id: workspaceId,
555
- slug: workspaceSlug ?? ""
556
- }
557
- }
558
- });
559
- if (primary) return true;
560
- if (workspaceSlug && workspaceSlug !== workspaceId) {
561
- const secondary = await client.isFeatureEnabled(flag, workspaceSlug, {
562
- groups: { workspace: workspaceSlug },
563
- groupProperties: {
564
- workspace: {
565
- workspace_id: workspaceId,
566
- slug: workspaceSlug
567
- }
568
- }
569
- });
570
- return secondary ?? false;
571
- }
572
- return primary ?? false;
573
- } catch {
574
- return false;
575
- }
576
- }
577
-
578
505
  // src/server.ts
579
506
  import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
580
507
 
@@ -1825,13 +1752,12 @@ async function checkEntryQuality(entryId) {
1825
1752
  var AUTO_LINK_CONFIDENCE_THRESHOLD = 35;
1826
1753
  var MAX_AUTO_LINKS = 5;
1827
1754
  var MAX_SUGGESTIONS = 5;
1828
- var CAPTURE_WITHOUT_THINKING_FLAG = "capture-without-thinking";
1829
1755
  var BR_STD_ENTRY_ID_REGEX = /^(BR|STD)-\d+$/;
1830
1756
  var entryIdSchema = z2.string().regex(BR_STD_ENTRY_ID_REGEX).optional().describe(
1831
1757
  "Only for business-rules and standards. Must match BR-NNN or STD-NNN. Omit for all other collections \u2014 IDs are auto-generated."
1832
1758
  );
1833
1759
  var captureSchema = z2.object({
1834
- collection: z2.string().optional().describe("Collection slug, e.g. 'tensions', 'business-rules', 'glossary', 'decisions'. Optional when `capture-without-thinking` is enabled."),
1760
+ collection: z2.string().optional().describe("Collection slug, e.g. 'tensions', 'business-rules', 'glossary', 'decisions'. Optional \u2014 classifier auto-routes when omitted."),
1835
1761
  name: z2.string().describe("Display name \u2014 be specific (e.g. 'Convex adjacency list won't scale for graph traversal')"),
1836
1762
  description: z2.string().describe("Full context \u2014 what's happening, why it matters, what you observed"),
1837
1763
  context: z2.string().optional().describe("Optional additional context (e.g. 'Observed during context gather calls taking 700ms+')"),
@@ -1913,20 +1839,6 @@ function trackClassifierTelemetry(params) {
1913
1839
  }
1914
1840
  trackCaptureClassifierFallback(params.workspaceId, telemetry);
1915
1841
  }
1916
- function buildCollectionRequiredResult() {
1917
- return {
1918
- content: [{
1919
- type: "text",
1920
- text: "Collection is required unless `capture-without-thinking` is enabled.\n\nProvide `collection` explicitly, or enable the feature flag for this workspace."
1921
- }],
1922
- structuredContent: failure(
1923
- "VALIDATION_ERROR",
1924
- "Collection is required unless capture-without-thinking is enabled.",
1925
- "Provide collection explicitly, or enable the feature flag.",
1926
- [{ tool: "collections", description: "List available collections", parameters: { action: "list" } }]
1927
- )
1928
- };
1929
- }
1930
1842
  function buildClassifierUnknownResult() {
1931
1843
  return {
1932
1844
  content: [{
@@ -2033,7 +1945,6 @@ async function resolveCaptureCollection(params) {
2033
1945
  collection,
2034
1946
  name,
2035
1947
  description,
2036
- classifierFlagOn,
2037
1948
  workspaceId,
2038
1949
  explicitCollectionProvided
2039
1950
  } = params;
@@ -2090,9 +2001,6 @@ async function resolveCaptureCollection(params) {
2090
2001
  }
2091
2002
  };
2092
2003
  }
2093
- if (!classifierFlagOn) {
2094
- return { earlyResult: buildCollectionRequiredResult() };
2095
- }
2096
2004
  const resolved = await resolveCollection({ name, description });
2097
2005
  if (!resolved) {
2098
2006
  trackClassifierTelemetry({
@@ -2280,7 +2188,7 @@ function registerSmartCaptureTools(server) {
2280
2188
  "capture",
2281
2189
  {
2282
2190
  title: "Capture",
2283
- 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, chains, 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.",
2191
+ 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 \u2014 the classifier auto-routes when omitted.\n\nSupported collections with smart profiles: tensions, business-rules, glossary, decisions, features, audiences, strategy, standards, maps, chains, 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.",
2284
2192
  inputSchema: captureSchema.shape,
2285
2193
  annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
2286
2194
  },
@@ -2289,16 +2197,10 @@ function registerSmartCaptureTools(server) {
2289
2197
  const timingStart = Date.now();
2290
2198
  const wsCtx = await getWorkspaceContext();
2291
2199
  const explicitCollectionProvided = typeof collection === "string" && collection.trim().length > 0;
2292
- const classifierFlagOn = await isFeatureEnabled(
2293
- CAPTURE_WITHOUT_THINKING_FLAG,
2294
- wsCtx.workspaceId,
2295
- wsCtx.workspaceSlug
2296
- );
2297
2200
  const resolution = await resolveCaptureCollection({
2298
2201
  collection,
2299
2202
  name,
2300
2203
  description,
2301
- classifierFlagOn,
2302
2204
  workspaceId: wsCtx.workspaceId,
2303
2205
  explicitCollectionProvided
2304
2206
  });
@@ -2308,7 +2210,7 @@ function registerSmartCaptureTools(server) {
2308
2210
  let resolvedCollection = resolution.resolvedCollection;
2309
2211
  const classifierMeta = resolution.classifierMeta;
2310
2212
  if (!resolvedCollection) {
2311
- return buildCollectionRequiredResult();
2213
+ return buildClassifierUnknownResult();
2312
2214
  }
2313
2215
  if (resolvedCollection === "chains" && !canonicalKey && !explicitCollectionProvided && classifierMeta?.candidates?.some(
2314
2216
  (c) => c.collection === "work-packages"
@@ -8750,7 +8652,7 @@ var IMPLEMENTATION_REVIEW_WORKFLOW_DESCRIPTOR = {
8750
8652
 
8751
8653
  ## Your Behavior
8752
8654
 
8753
- 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@latest library\` then \`docs\`) for relevant testing and framework docs \u2014 not for Chain truth. Do not use Context7 MCP.
8655
+ 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.
8754
8656
  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.**
8755
8657
  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.
8756
8658
  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.
@@ -8767,7 +8669,7 @@ var IMPLEMENTATION_REVIEW_WORKFLOW_DESCRIPTOR = {
8767
8669
  - \`verify\` \u2014 glossary code mappings, cross-references vs codebase
8768
8670
  - \`review-against-rules\` (domain) \u2014 business rule compliance
8769
8671
  - \`context action=gather task="implementation review for [BET-ID]"\` \u2014 related knowledge
8770
- - Context7 CLI \u2014 \`npx ctx7@latest library\` then \`npx ctx7@latest docs\` for framework/testing docs (not Chain truth)
8672
+ - Context7 CLI \u2014 \`npx ctx7 library\` then \`npx ctx7 docs\` for framework/testing docs (not Chain truth); prefer local \`ctx7\` from \`node_modules\` in this monorepo
8771
8673
  - \`session-wrapup\` \u2014 before close
8772
8674
 
8773
8675
  ## Sub-Agent Classification (for test failures)
@@ -13457,9 +13359,22 @@ function extractSessionEntryIds(priorSessions) {
13457
13359
  }
13458
13360
  var VALID_TASK_DOMAINS = [
13459
13361
  "auth",
13362
+ "strategy",
13460
13363
  "governance",
13461
13364
  "architecture",
13462
13365
  "product",
13366
+ "product-design",
13367
+ "product-design/ux",
13368
+ "engineering",
13369
+ "engineering/frontend",
13370
+ "engineering/backend",
13371
+ "data",
13372
+ "data/analytics",
13373
+ "go-to-market",
13374
+ "go-to-market/sales",
13375
+ "strategy/outcomes",
13376
+ "product/roadmap",
13377
+ "governance/principles",
13463
13378
  "data-foundation",
13464
13379
  "chainwork",
13465
13380
  "capture-pipeline",
@@ -13497,7 +13412,7 @@ function registerOrientTool(server) {
13497
13412
  "orient",
13498
13413
  {
13499
13414
  title: "Orient \u2014 Start Here",
13500
- description: "The single entry point for starting a session. Returns workspace context with a single recommended next action for low-readiness workspaces, or a standup-style briefing for established workspaces.\n\nUse this FIRST. One call to orient replaces 3\u20135 individual tool calls.\n\nCompleting orientation unlocks write tools for the active session.\n\n**mode:** `full` (default) returns full context. `brief` returns only vision, bet/tension counts, readiness, active bet names, and last-session summary \u2014 use for mid-session re-orientation.\n\n**task:** Optional natural-language task description. When provided, returns task-scoped context (scored, relevant entries) in addition to standard orient sections.\n\n**scope:** Optional domain scope. Filters governance entries to those relevant for the specified domain. Valid values: auth, governance, architecture, product, data-foundation, chainwork, capture-pipeline, ingestion, intelligence-and-operations, review-and-learning, general.",
13415
+ description: "The single entry point for starting a session. Returns workspace context with a single recommended next action for low-readiness workspaces, or a standup-style briefing for established workspaces.\n\nUse this FIRST. One call to orient replaces 3\u20135 individual tool calls.\n\nCompleting orientation unlocks write tools for the active session.\n\n**mode:** `full` (default) returns full context. `brief` returns only vision, bet/tension counts, readiness, active bet names, and last-session summary \u2014 use for mid-session re-orientation.\n\n**task:** Optional natural-language task description. When provided, returns task-scoped context (scored, relevant entries) in addition to standard orient sections.\n\n**scope:** Optional domain scope. Filters governance entries to those relevant for the specified domain. Authority roots include strategy, product, product-design, engineering, architecture, data, governance, and go-to-market. Child scopes such as product-design/ux, engineering/frontend, data/analytics, and governance/principles are accepted. Legacy internal scopes remain accepted.",
13501
13416
  inputSchema: orientSchema,
13502
13417
  annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false }
13503
13418
  },
@@ -13640,6 +13555,18 @@ function registerOrientTool(server) {
13640
13555
  if (orientEntries?.taskContext && orientEntries.taskContext.context.length > 0) {
13641
13556
  lines.push(`Task context: ${orientEntries.taskContext.totalFound} relevant entries (${orientEntries.taskContext.confidence} confidence).`);
13642
13557
  }
13558
+ if (orientEntries?.startup?.domainRetrieval) {
13559
+ const retrieval = orientEntries.startup.domainRetrieval;
13560
+ const scopedTo = retrieval.resolvedDomainSlug ? ` (${retrieval.resolvedDomainSlug})` : "";
13561
+ lines.push(`Domain retrieval: ${retrieval.state}${scopedTo}`);
13562
+ if (retrieval.activeSource) lines.push(`Active source: ${retrieval.activeSource}`);
13563
+ if (retrieval.fallbackReason) lines.push(`Fallback: ${retrieval.fallbackReason}`);
13564
+ if (retrieval.fallbackAction) lines.push(`Action: ${retrieval.fallbackAction}`);
13565
+ if (retrieval.evidenceGap) lines.push(`Evidence gap: ${retrieval.evidenceGap}`);
13566
+ if (retrieval.directRatifiedCount != null || retrieval.inheritedRatifiedCount != null || retrieval.orgFallbackCount != null) {
13567
+ lines.push(`Domain relation evidence: ${retrieval.directRatifiedCount ?? 0} direct, ${retrieval.inheritedRatifiedCount ?? 0} inherited, ${retrieval.orgFallbackCount ?? 0} org fallback`);
13568
+ }
13569
+ }
13643
13570
  if (orientEntries?.writeBackHints && orientEntries.writeBackHints.length > 0) {
13644
13571
  lines.push("");
13645
13572
  lines.push("Write-back hints (what to capture this session):");
@@ -13873,6 +13800,16 @@ function registerOrientTool(server) {
13873
13800
  if (orientEntries?.taskContext && orientEntries.taskContext.context.length > 0) {
13874
13801
  const tc = orientEntries.taskContext;
13875
13802
  lines.push("## Task Context");
13803
+ if (orientEntries.startup?.domainRetrieval) {
13804
+ const retrieval = orientEntries.startup.domainRetrieval;
13805
+ const scopedTo = retrieval.resolvedDomainSlug ? ` (${retrieval.resolvedDomainSlug})` : "";
13806
+ lines.push(`_Domain retrieval: ${retrieval.state}${scopedTo}_`);
13807
+ if (retrieval.activeSource) lines.push(`_Active source: ${retrieval.activeSource}_`);
13808
+ if (retrieval.fallbackReason) lines.push(`_${retrieval.fallbackReason}_`);
13809
+ if (retrieval.fallbackAction) lines.push(`_${retrieval.fallbackAction}_`);
13810
+ if (retrieval.evidenceGap) lines.push(`_Evidence gap: ${retrieval.evidenceGap}_`);
13811
+ lines.push("");
13812
+ }
13876
13813
  lines.push(`_Task-scoped entries (${tc.confidence} confidence, ${tc.totalFound} relevant)`);
13877
13814
  lines.push("");
13878
13815
  for (const e of tc.context) {
@@ -14125,6 +14062,7 @@ function registerOrientTool(server) {
14125
14062
  sessionId: agentSessionId,
14126
14063
  taskGroundingStatus: hasTaskGrounding ? "task_scoped" : task ? "missing_task_context" : "workspace_only",
14127
14064
  taskGroundingRequired: !hasTaskGrounding,
14065
+ domainRetrieval: orientEntries?.startup?.domainRetrieval,
14128
14066
  orientationStatus,
14129
14067
  nextStep: hasTaskGrounding ? void 0 : 'Run `orient task="describe the work"` before substantive work.'
14130
14068
  }
@@ -14774,20 +14712,6 @@ function registerGovernanceTools(server) {
14774
14712
  annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
14775
14713
  },
14776
14714
  thinWrapper(async ({ action, proposalId, verdict, reason, status }) => {
14777
- const wsCtx = await getWorkspaceContext();
14778
- const enabled = await isFeatureEnabled(
14779
- "bet-221-governance-at-scale",
14780
- wsCtx.workspaceId,
14781
- wsCtx.workspaceSlug
14782
- );
14783
- if (!enabled) {
14784
- return failureResult(
14785
- "Governance proposals are not yet enabled for this workspace.",
14786
- "FEATURE_DISABLED",
14787
- "The bet-221-governance-at-scale flag is OFF.",
14788
- "Ask your workspace admin to enable the governance-at-scale feature flag."
14789
- );
14790
- }
14791
14715
  if (action === "list") {
14792
14716
  const proposals = await kernelQuery("governance.listProposals", {
14793
14717
  ...status ? { status } : {}
@@ -15688,6 +15612,30 @@ function createProductBrainServer() {
15688
15612
  return server;
15689
15613
  }
15690
15614
 
15615
+ // src/flags.ts
15616
+ function readViteEnvFlag(name) {
15617
+ try {
15618
+ const env = import.meta.env;
15619
+ return env?.[name];
15620
+ } catch {
15621
+ return void 0;
15622
+ }
15623
+ }
15624
+ function readProcessEnvFlag(name) {
15625
+ try {
15626
+ if (typeof process !== "undefined" && process.env) {
15627
+ return process.env[name];
15628
+ }
15629
+ } catch {
15630
+ }
15631
+ return void 0;
15632
+ }
15633
+ var KILL = readViteEnvFlag("VITE_FEATURE_KILL_SWITCH") === "true" || readProcessEnvFlag("FEATURE_KILL_SWITCH") === "true";
15634
+
15635
+ // src/featureFlags.ts
15636
+ function initFeatureFlags(_posthogClient) {
15637
+ }
15638
+
15691
15639
  export {
15692
15640
  hashKey,
15693
15641
  runWithAuth,
@@ -15697,8 +15645,8 @@ export {
15697
15645
  bootstrapHttp,
15698
15646
  getWorkspaceId,
15699
15647
  recoverSessionState,
15700
- initFeatureFlags,
15701
15648
  SERVER_VERSION,
15702
- createProductBrainServer
15649
+ createProductBrainServer,
15650
+ initFeatureFlags
15703
15651
  };
15704
- //# sourceMappingURL=chunk-L2MYREYP.js.map
15652
+ //# sourceMappingURL=chunk-264NGZNU.js.map