@productbrain/mcp 0.0.1-beta.80 → 0.0.1-beta.82

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.
@@ -3653,9 +3653,6 @@ function getWorkflowTemplateMetadata(descriptor) {
3653
3653
  name: descriptor.template.name
3654
3654
  };
3655
3655
  }
3656
- function getWorkflowContextCollection(descriptor) {
3657
- return getWorkflowPrimaryRecordCollection(descriptor);
3658
- }
3659
3656
  function describeWorkflowPrimaryOutput(descriptor) {
3660
3657
  const { routing } = descriptor.output.primaryRecord;
3661
3658
  if (routing.mode === "fixed") {
@@ -3869,7 +3866,7 @@ The retro must never fail silently. Always communicate state.`
3869
3866
  var SHAPE_WORKFLOW_DESCRIPTOR = {
3870
3867
  id: "shape",
3871
3868
  name: "Shape a Bet",
3872
- shortDescription: "Integration-aware shaping: maps the system before designing, traces five paths per element, surfaces governance conflicts at design time. Uses `facilitate` for scoring and Chain capture.",
3869
+ shortDescription: "Integration-aware shaping: maps the system before designing, traces five paths per element, surfaces governance conflicts at design time. Scoring is in the shaping skill; uses capture and facilitate action=commit-constellation for Chain capture.",
3873
3870
  template: {
3874
3871
  slug: "shape-a-bet",
3875
3872
  name: "Shape a Bet"
@@ -4446,7 +4443,7 @@ ${wf.shortDescription}
4446
4443
  ${roundList}
4447
4444
 
4448
4445
  **Output**: ${outputDescription}
4449
- _Use the \`run-workflow\` prompt with workflow="${wf.id}" to start._`;
4446
+ _Trigger via Facilitator Mode: say "run retro", "shape this", "review implementation", or "process change" in Cursor._`;
4450
4447
  }
4451
4448
  function validateFinalWorkflowCheckpoint(workflow, roundId) {
4452
4449
  const finalRound = workflow.rounds.at(-1);
@@ -4495,7 +4492,7 @@ function registerWorkflowTools(server) {
4495
4492
  "workflows",
4496
4493
  {
4497
4494
  title: "Workflows",
4498
- description: "Chainwork workflow discovery, checkpointing, and run inspection. Three actions:\n\n- **list**: Browse available workflows \u2014 retro, shape-a-bet, IDM, etc. Use the `run-workflow` prompt to launch one.\n- **checkpoint**: Record round output during Facilitator Mode. Persists progress. At completion, some workflows support isFinal=true to create the summary chain entry.\n- **get-run**: Inspect a persisted workflow run by run ID or by workflow ID in the current session.",
4495
+ description: "Chainwork workflow discovery, checkpointing, and run inspection. Three actions:\n\n- **list**: Browse available workflows \u2014 retro, shape-a-bet, implementation-review, process-change. Triggered by Facilitator Mode in Cursor (e.g. 'run retro', 'shape this', 'review implementation').\n- **checkpoint**: Record round output during Facilitator Mode. Persists progress. At completion, some workflows support isFinal=true to create the summary chain entry.\n- **get-run**: Inspect a persisted workflow run by run ID or by workflow ID in the current session.",
4499
4496
  inputSchema: workflowsSchema,
4500
4497
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }
4501
4498
  },
@@ -4565,7 +4562,7 @@ async function handleList3() {
4565
4562
  type: "text",
4566
4563
  text: `# Available Chainwork Workflows
4567
4564
 
4568
- ${workflows.length} workflow(s) available. Use the \`run-workflow\` prompt to launch one.
4565
+ ${workflows.length} workflow(s) available. Trigger via Facilitator Mode in Cursor (say "run retro", "shape this", "review implementation", or "process change").
4569
4566
 
4570
4567
  ---
4571
4568
 
@@ -9860,197 +9857,12 @@ ${entry.labels.map((l) => `- ${l.name ?? l.slug}`).join("\n")}`);
9860
9857
  }
9861
9858
 
9862
9859
  // src/prompts/index.ts
9863
- import { z as z23 } from "zod";
9864
-
9865
- // src/tools/project-scan.ts
9866
9860
  import { z as z22 } from "zod";
9867
- var scanExtractionSchema = z22.object({
9868
- vision: z22.string().min(10).optional().describe("Product purpose from README"),
9869
- audience: z22.string().optional().describe("Primary user or customer segment"),
9870
- techStack: z22.array(z22.string()).optional().describe("Key technologies from package.json"),
9871
- modules: z22.array(z22.object({ name: z22.string(), purpose: z22.string() })).max(8).optional().describe("Top-level modules from folder structure"),
9872
- businessRules: z22.array(z22.string()).max(10).optional().describe("System constraints from .cursorrules/AGENTS.md/CLAUDE.md"),
9873
- conventions: z22.array(z22.string()).max(8).optional().describe("Coding or process standards from .cursorrules"),
9874
- keyDecisions: z22.array(z22.string()).optional().describe("Significant architectural decisions from git log or README"),
9875
- tensions: z22.array(z22.string()).optional().describe("Friction points or known trade-offs"),
9876
- keyTerms: z22.array(z22.string()).optional().describe("Domain-specific vocabulary found in files")
9877
- });
9878
- var COLLECTION_PRIORITY = {
9879
- strategy: 0,
9880
- audiences: 1,
9881
- architecture: 2,
9882
- decisions: 3,
9883
- "business-rules": 4,
9884
- standards: 5,
9885
- glossary: 6,
9886
- tensions: 7
9887
- };
9888
- var GENERIC_MODULE_NAMES = /* @__PURE__ */ new Set(["src", "lib", "utils"]);
9889
- function truncate(str, maxLen) {
9890
- return str.length <= maxLen ? str : str.slice(0, maxLen);
9891
- }
9892
- function firstNWords(str, n) {
9893
- return str.split(/\s+/).slice(0, n).join(" ");
9894
- }
9895
- function scanToBatchEntries(extracted) {
9896
- const entries = [];
9897
- const techStackGlossaryNames = /* @__PURE__ */ new Set();
9898
- if (extracted.vision) {
9899
- entries.push({
9900
- collection: "strategy",
9901
- name: "Product Vision",
9902
- description: extracted.vision
9903
- });
9904
- }
9905
- if (extracted.audience) {
9906
- entries.push({
9907
- collection: "audiences",
9908
- name: firstNWords(extracted.audience, 6),
9909
- description: extracted.audience
9910
- });
9911
- }
9912
- if (extracted.techStack && extracted.techStack.length > 0) {
9913
- entries.push({
9914
- collection: "architecture",
9915
- name: "Tech Stack",
9916
- description: `Technology choices: ${extracted.techStack.join(", ")}`
9917
- });
9918
- for (const tech of extracted.techStack.slice(0, 3)) {
9919
- const name = tech;
9920
- techStackGlossaryNames.add(name.toLowerCase());
9921
- entries.push({
9922
- collection: "glossary",
9923
- name,
9924
- description: `${tech} \u2014 part of the tech stack`
9925
- });
9926
- }
9927
- }
9928
- if (extracted.modules) {
9929
- let count = 0;
9930
- for (const mod of extracted.modules) {
9931
- if (count >= 5) break;
9932
- if (GENERIC_MODULE_NAMES.has(mod.name.toLowerCase())) continue;
9933
- entries.push({
9934
- collection: "architecture",
9935
- name: mod.name,
9936
- description: mod.purpose
9937
- });
9938
- count++;
9939
- }
9940
- }
9941
- for (const rule of extracted.businessRules ?? []) {
9942
- entries.push({
9943
- collection: "business-rules",
9944
- name: truncate(rule, 70),
9945
- description: rule
9946
- });
9947
- }
9948
- for (const convention of extracted.conventions ?? []) {
9949
- entries.push({
9950
- collection: "standards",
9951
- name: truncate(convention, 70),
9952
- description: convention
9953
- });
9954
- }
9955
- for (const decision of extracted.keyDecisions ?? []) {
9956
- entries.push({
9957
- collection: "decisions",
9958
- name: truncate(decision, 80),
9959
- description: decision
9960
- });
9961
- }
9962
- for (const tension of extracted.tensions ?? []) {
9963
- entries.push({
9964
- collection: "tensions",
9965
- name: truncate(tension, 80),
9966
- description: tension
9967
- });
9968
- }
9969
- for (const term of extracted.keyTerms ?? []) {
9970
- if (techStackGlossaryNames.has(term.toLowerCase())) continue;
9971
- entries.push({
9972
- collection: "glossary",
9973
- name: term,
9974
- description: `Core domain term: ${term}`
9975
- });
9976
- }
9977
- const seen = /* @__PURE__ */ new Set();
9978
- const deduped = entries.filter((e) => {
9979
- const key = `${e.collection}:${e.name.toLowerCase()}`;
9980
- if (seen.has(key)) return false;
9981
- seen.add(key);
9982
- return true;
9983
- });
9984
- if (deduped.length <= 30) return deduped;
9985
- return deduped.sort((a, b) => {
9986
- const pa = COLLECTION_PRIORITY[a.collection] ?? 99;
9987
- const pb = COLLECTION_PRIORITY[b.collection] ?? 99;
9988
- return pa - pb;
9989
- }).slice(0, 30);
9990
- }
9991
-
9992
- // src/tools/draft-review.ts
9993
- var REVIEW_COLLECTION_ORDER = [
9994
- "strategy",
9995
- "chains",
9996
- "audiences",
9997
- "architecture",
9998
- "decisions",
9999
- "business-rules",
10000
- "standards",
10001
- "glossary",
10002
- "tensions"
10003
- ];
10004
- function groupDraftsByCollection(drafts) {
10005
- const buckets = /* @__PURE__ */ new Map();
10006
- for (const draft of drafts) {
10007
- const existing = buckets.get(draft.collection);
10008
- if (existing) {
10009
- existing.push(draft);
10010
- } else {
10011
- buckets.set(draft.collection, [draft]);
10012
- }
10013
- }
10014
- const knownKeys = REVIEW_COLLECTION_ORDER.filter((k) => buckets.has(k));
10015
- const unknownKeys = [...buckets.keys()].filter((k) => !REVIEW_COLLECTION_ORDER.includes(k)).sort();
10016
- const ordered = /* @__PURE__ */ new Map();
10017
- for (const key of [...knownKeys, ...unknownKeys]) {
10018
- ordered.set(key, buckets.get(key));
10019
- }
10020
- return ordered;
10021
- }
10022
- var COLLECTION_DISPLAY_NAMES = {
10023
- "business-rules": "Business Rules",
10024
- "tracking-events": "Tracking Events"
10025
- };
10026
- function collectionLabel(slug) {
10027
- return COLLECTION_DISPLAY_NAMES[slug] ?? slug.charAt(0).toUpperCase() + slug.slice(1);
10028
- }
10029
- function formatDraftGroups(grouped, total) {
10030
- if (total === 0) {
10031
- return "No drafts to review \u2014 your workspace is fully committed.";
10032
- }
10033
- const lines = [`## Drafts Ready for Review (${total} total)`];
10034
- let counter = 1;
10035
- for (const [collection, entries] of grouped) {
10036
- const label = collectionLabel(collection);
10037
- const count = entries.length;
10038
- lines.push(`
10039
- ### ${label} (${count} ${count === 1 ? "entry" : "entries"})`);
10040
- for (const entry of entries) {
10041
- lines.push(`${counter}. ${entry.name} [${entry.entryId}]`);
10042
- counter++;
10043
- }
10044
- }
10045
- return lines.join("\n");
10046
- }
10047
-
10048
- // src/prompts/index.ts
10049
9861
  function registerPrompts(server) {
10050
9862
  server.prompt(
10051
9863
  "review-against-rules",
10052
9864
  "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.",
10053
- { domain: z23.string().describe("Business rule domain (e.g. 'Identity & Access', 'Governance & Decision-Making')") },
9865
+ { domain: z22.string().describe("Business rule domain (e.g. 'Identity & Access', 'Governance & Decision-Making')") },
10054
9866
  async ({ domain }) => {
10055
9867
  const entries = await mcpQuery("chain.listEntries", { collectionSlug: "business-rules" });
10056
9868
  const rules = entries.filter((e) => e.data?.domain === domain);
@@ -10103,7 +9915,7 @@ Provide a structured review with a compliance status for each rule (COMPLIANT /
10103
9915
  server.prompt(
10104
9916
  "name-check",
10105
9917
  "Check variable names, field names, or API names against the glossary for terminology alignment. Flags drift from canonical terms.",
10106
- { names: z23.string().describe("Comma-separated list of names to check (e.g. 'vendor_id, compliance_level, formulator_type')") },
9918
+ { names: z22.string().describe("Comma-separated list of names to check (e.g. 'vendor_id, compliance_level, formulator_type')") },
10107
9919
  async ({ names }) => {
10108
9920
  const terms = await mcpQuery("chain.listEntries", { collectionSlug: "glossary" });
10109
9921
  const glossaryContext = terms.map(
@@ -10139,7 +9951,7 @@ Format as a table: Name | Status | Canonical Form | Action Needed`
10139
9951
  server.prompt(
10140
9952
  "draft-decision-record",
10141
9953
  "Draft a structured decision record from a description of what was decided. Includes context from recent decisions and relevant rules.",
10142
- { context: z23.string().describe("Description of the decision (e.g. 'We decided to use MRSL v3.1 as the conformance baseline because...')") },
9954
+ { context: z22.string().describe("Description of the decision (e.g. 'We decided to use MRSL v3.1 as the conformance baseline because...')") },
10143
9955
  async ({ context }) => {
10144
9956
  const recentDecisions = await mcpQuery("chain.listEntries", { collectionSlug: "decisions" });
10145
9957
  const sorted = [...recentDecisions].sort((a, b) => (b.data?.date ?? "") > (a.data?.date ?? "") ? 1 : -1).slice(0, 5);
@@ -10177,8 +9989,8 @@ After drafting, I can log it using the capture tool with collection "decisions".
10177
9989
  "draft-rule-from-context",
10178
9990
  "Draft a new business rule from an observation or discovery made while coding. Fetches existing rules for the domain to ensure consistency.",
10179
9991
  {
10180
- observation: z23.string().describe("What you observed or discovered (e.g. 'Suppliers can have multiple org types in Gateway')"),
10181
- domain: z23.string().describe("Which domain this rule belongs to (e.g. 'Governance & Decision-Making')")
9992
+ observation: z22.string().describe("What you observed or discovered (e.g. 'Suppliers can have multiple org types in Gateway')"),
9993
+ domain: z22.string().describe("Which domain this rule belongs to (e.g. 'Governance & Decision-Making')")
10182
9994
  },
10183
9995
  async ({ observation, domain }) => {
10184
9996
  const allRules = await mcpQuery("chain.listEntries", { collectionSlug: "business-rules" });
@@ -10215,575 +10027,6 @@ Make sure the rule is consistent with existing rules and doesn't contradict them
10215
10027
  };
10216
10028
  }
10217
10029
  );
10218
- server.prompt(
10219
- "run-workflow",
10220
- "Launch any registered Chainwork workflow in Facilitator Mode. Returns the full workflow definition, facilitation instructions, and round structure. The agent enters Facilitator Mode and guides the participant through each round.",
10221
- {
10222
- workflow: z23.string().describe(
10223
- "Workflow ID to run. Available: " + listWorkflows().map((w) => `'${w.id}' (${w.name})`).join(", ")
10224
- ),
10225
- context: z23.string().optional().describe(
10226
- "Optional context from the participant (e.g., 'retro on last sprint', 'shape the Chainwork API bet')"
10227
- )
10228
- },
10229
- async ({ workflow: workflowId, context }) => {
10230
- const wf = getWorkflow(workflowId);
10231
- const descriptor = getWorkflowDescriptor(workflowId);
10232
- if (!wf) {
10233
- const available = listWorkflows().map((w) => `- **${w.id}**: ${w.name} \u2014 ${w.shortDescription}`).join("\n");
10234
- return {
10235
- messages: [
10236
- {
10237
- role: "user",
10238
- content: {
10239
- type: "text",
10240
- text: `Workflow "${workflowId}" not found.
10241
-
10242
- Available workflows:
10243
- ${available}
10244
-
10245
- Use one of these IDs to run a workflow.`
10246
- }
10247
- }
10248
- ]
10249
- };
10250
- }
10251
- let kbContext = "";
10252
- const contextCollection = descriptor ? getWorkflowContextCollection(descriptor) : wf.kbOutputCollection ?? null;
10253
- try {
10254
- if (contextCollection) {
10255
- const recentDecisions = await mcpQuery("chain.listEntries", {
10256
- collectionSlug: contextCollection
10257
- });
10258
- const sorted = [...recentDecisions].sort((a, b) => (b.data?.date ?? "") > (a.data?.date ?? "") ? 1 : -1).slice(0, 5);
10259
- if (sorted.length > 0) {
10260
- kbContext = `
10261
- ## Recent ${contextCollection} entries (for context)
10262
- ` + sorted.map((d) => `- ${d.entryId ?? ""}: ${d.name} [${d.status}]`).join("\n");
10263
- }
10264
- }
10265
- } catch {
10266
- kbContext = "\n_Could not load chain context \u2014 proceed without it._";
10267
- }
10268
- const roundsPlan = wf.rounds.map(
10269
- (r) => `### Round ${r.num}: ${r.label}
10270
- **Type**: ${r.type} | **Duration**: ~${r.maxDurationHint ?? "5 min"}
10271
- **Instruction**: ${r.instruction}
10272
- **Facilitator guidance**: ${r.facilitatorGuidance}
10273
- ` + (r.questions ? r.questions.map(
10274
- (q) => `**Question**: ${q.prompt}
10275
- ` + (q.options ? q.options.map((o) => ` - ${o.id}: ${o.label}`).join("\n") : " _(open response)_")
10276
- ).join("\n") : "") + `
10277
- **Output**: Capture to \`${r.outputSchema.field}\` (${r.outputSchema.format})
10278
- **Checkpoint**: \`workflows action="checkpoint" workflowId="${wf.id}" roundId="${r.id}"\``
10279
- ).join("\n\n---\n\n");
10280
- const contextLine = context ? `
10281
- The participant provided this context: "${context}"
10282
- Use it \u2014 don't make them repeat themselves.
10283
- ` : "";
10284
- const outputDescription = descriptor ? describeWorkflowPrimaryOutput(descriptor) : wf.kbOutputCollection ? `Primary record routes to \`${wf.kbOutputCollection}\`.` : "Primary output routing resolved at runtime.";
10285
- const summaryCapture = descriptor ? getWorkflowSummaryCaptureDefinition(descriptor) : wf.kbOutputCollection && wf.kbOutputTemplate ? {
10286
- routing: {
10287
- mode: "fixed",
10288
- collection: wf.kbOutputCollection
10289
- },
10290
- nameTemplate: wf.kbOutputTemplate.nameTemplate,
10291
- descriptionField: wf.kbOutputTemplate.descriptionField,
10292
- descriptionSource: "final-output-text"
10293
- } : null;
10294
- const chainOutputBlock = summaryCapture ? `Primary record routes to \`${summaryCapture.routing.mode === "fixed" ? summaryCapture.routing.collection : "classifier"}\`.
10295
- Summary capture template: ${summaryCapture.nameTemplate}
10296
- Description field: ${summaryCapture.descriptionField ?? "collection default"}
10297
- ` : `${outputDescription}
10298
- `;
10299
- return {
10300
- messages: [
10301
- {
10302
- role: "user",
10303
- content: {
10304
- type: "text",
10305
- text: `# ${wf.icon} ${wf.name} Workflow \u2014 Facilitator Mode
10306
-
10307
- ${wf.shortDescription}
10308
- ` + contextLine + `
10309
- ---
10310
-
10311
- ## Facilitator Instructions
10312
-
10313
- ${wf.facilitatorPreamble}
10314
-
10315
- ---
10316
-
10317
- ## Checkpointing & Resume
10318
-
10319
- 1. Before starting, check for an existing run: \`workflows action="get-run" workflowId="${wf.id}"\`.
10320
- 2. If an active run exists, resume from its \`currentRoundId\`.
10321
- 3. After EACH round: \`workflows action="checkpoint" workflowId="${wf.id}" roundId="<round-id>" output=<round-output>\`.
10322
- ` + (wf.facilitatorPreamble.includes("commit-constellation") ? `4. Finalize via \`facilitate action=commit-constellation\` \u2014 do NOT use \`isFinal: true\`.
10323
- ` : `4. Only call \`isFinal: true\` from the terminal round (or pass \`summaryEntryId=<primary-entry-id>\` to finalize with a linked entry).
10324
- `) + `5. If a checkpoint fails, continue \u2014 the conversation is the backup.
10325
-
10326
- ---
10327
-
10328
- ## Rounds
10329
-
10330
- ${roundsPlan}
10331
-
10332
- ---
10333
-
10334
- ## Error Recovery
10335
-
10336
- ${wf.errorRecovery}
10337
-
10338
- ---
10339
-
10340
- ## Chain Output
10341
-
10342
- ${chainOutputBlock}` + kbContext + `
10343
-
10344
- ---
10345
-
10346
- **BEGIN THE WORKFLOW NOW.** Start with Round 01. Create a Plan first.`
10347
- }
10348
- }
10349
- ]
10350
- };
10351
- }
10352
- );
10353
- server.prompt(
10354
- "capture-and-connect",
10355
- "Guided workflow: capture a knowledge entry, discover graph connections, create relations, and prepare for commit. Encodes the capture \u2192 suggest \u2192 batch-create \u2192 commit choreography as a step-by-step guide.",
10356
- {
10357
- collection: z23.string().describe("Collection slug (e.g. 'glossary', 'business-rules', 'decisions', 'tensions')"),
10358
- name: z23.string().describe("Entry name"),
10359
- description: z23.string().describe("Entry description")
10360
- },
10361
- async ({ collection, name, description }) => {
10362
- const collections = await mcpQuery("chain.listCollections");
10363
- const col = collections.find((c) => c.slug === collection);
10364
- const colName = col ? col.name : collection;
10365
- return {
10366
- messages: [{
10367
- role: "user",
10368
- content: {
10369
- type: "text",
10370
- text: `# Capture & Connect Workflow
10371
-
10372
- You are capturing a new **${colName}** entry and connecting it to the knowledge graph.
10373
-
10374
- ## Entry Details
10375
- - **Collection:** ${collection}
10376
- - **Name:** ${name}
10377
- - **Description:** ${description}
10378
-
10379
- ## Steps (execute in order)
10380
-
10381
- ### Step 1: Capture
10382
- \`\`\`
10383
- capture collection="${collection}" name="${name}" description="${description}"
10384
- \`\`\`
10385
- Review the quality scorecard. Note the returned \`entryId\`.
10386
-
10387
- ### Step 2: Discover Connections
10388
- \`\`\`
10389
- graph action=suggest entryId="<ID from step 1>"
10390
- \`\`\`
10391
- Review the top 3 quick wins. Accept the ones that make sense.
10392
-
10393
- ### Step 3: Create Relations
10394
- \`\`\`
10395
- relations action=batch-create relations=[...]
10396
- \`\`\`
10397
- Use the suggested relations from step 2.
10398
-
10399
- ### Step 4: Present for Commit
10400
- Show the user what was captured and connected. Ask for confirmation.
10401
- Only call \`commit-entry\` when the user explicitly confirms.
10402
-
10403
- **Begin with Step 1 now.**`
10404
- }
10405
- }]
10406
- };
10407
- }
10408
- );
10409
- server.prompt(
10410
- "deep-dive",
10411
- "Explore everything the Chain knows about a topic or entry. Assembles entry details, graph context, related business rules, and glossary terms into a comprehensive briefing.",
10412
- {
10413
- topic: z23.string().describe("Entry ID (e.g. 'BR-001') or topic to explore (e.g. 'authentication')")
10414
- },
10415
- async ({ topic }) => {
10416
- const isEntryId = /^[A-Z]+-\d+$/i.test(topic) || /^[A-Z]+-[a-z0-9]+$/i.test(topic);
10417
- let contextInstructions;
10418
- if (isEntryId) {
10419
- contextInstructions = `The topic is an entry ID: \`${topic}\`
10420
-
10421
- ## Steps
10422
-
10423
- ### 1. Get the entry
10424
- \`\`\`
10425
- entries action=get entryId="${topic}"
10426
- \`\`\`
10427
-
10428
- ### 2. Gather full context
10429
- \`\`\`
10430
- context action=gather entryId="${topic}" mode="graph"
10431
- \`\`\`
10432
-
10433
- ### 3. Check direct relations
10434
- \`\`\`
10435
- graph action=find entryId="${topic}"
10436
- \`\`\`
10437
-
10438
- ### 4. Synthesize
10439
- Combine everything into a comprehensive briefing:
10440
- - What this entry IS (data, status, collection)
10441
- - How it connects to the rest of the Chain (relations, context)
10442
- - What business rules govern it
10443
- - What glossary terms it uses
10444
- - What gaps or opportunities exist (missing relations, low quality score)
10445
-
10446
- Present the briefing, then ask if the user wants to explore any connection deeper.`;
10447
- } else {
10448
- contextInstructions = `The topic is a natural-language query: "${topic}"
10449
-
10450
- ## Steps
10451
-
10452
- ### 1. Search the Chain
10453
- \`\`\`
10454
- entries action=search query="${topic}"
10455
- \`\`\`
10456
-
10457
- ### 2. Load task context
10458
- \`\`\`
10459
- context action=gather task="${topic}"
10460
- \`\`\`
10461
-
10462
- ### 3. Explore top results
10463
- For the top 3 most relevant entries from search, get their full details:
10464
- \`\`\`
10465
- entries action=get entryId="<ID>"
10466
- \`\`\`
10467
-
10468
- ### 4. Synthesize
10469
- Combine everything into a comprehensive briefing about "${topic}":
10470
- - What the Chain knows about this topic
10471
- - Key entries, business rules, and glossary terms
10472
- - How different entries relate to each other
10473
- - What gaps exist in the Chain's knowledge
10474
-
10475
- Present the briefing, then ask if the user wants to capture new knowledge or explore deeper.`;
10476
- }
10477
- return {
10478
- messages: [{
10479
- role: "user",
10480
- content: {
10481
- type: "text",
10482
- text: `# Deep Dive: ${topic}
10483
-
10484
- Explore everything the Chain knows about this topic.
10485
-
10486
- ${contextInstructions}`
10487
- }
10488
- }]
10489
- };
10490
- }
10491
- );
10492
- server.prompt(
10493
- "pre-commit-check",
10494
- "Run a readiness check before committing an entry to the Chain. Validates quality score, required relations, and business rule compliance.",
10495
- {
10496
- entryId: z23.string().describe("Entry ID to check (e.g. 'GT-019', 'DEC-005')")
10497
- },
10498
- async ({ entryId }) => {
10499
- return {
10500
- messages: [{
10501
- role: "user",
10502
- content: {
10503
- type: "text",
10504
- text: `# Pre-Commit Check: ${entryId}
10505
-
10506
- Run a full readiness check before committing this entry to the Chain.
10507
-
10508
- ## Steps (execute all, report results)
10509
-
10510
- ### 1. Quality Check
10511
- \`\`\`
10512
- quality action=check entryId="${entryId}"
10513
- \`\`\`
10514
- Target: 8/10 or higher. If below, identify what to improve.
10515
-
10516
- ### 2. Re-evaluate in Commit Context
10517
- \`\`\`
10518
- quality action=re-evaluate entryId="${entryId}" context="commit"
10519
- \`\`\`
10520
- Check semantic quality criteria (specificity, actionability, testability).
10521
-
10522
- ### 3. Check Relations
10523
- \`\`\`
10524
- graph action=find entryId="${entryId}"
10525
- \`\`\`
10526
- Verify the entry has at least one relation. Strategy entries MUST have a strategy link.
10527
-
10528
- ### 4. Discover Missing Connections
10529
- \`\`\`
10530
- graph action=suggest entryId="${entryId}"
10531
- \`\`\`
10532
- Review suggestions. Create high-confidence relations before committing.
10533
-
10534
- ### 5. Readiness Verdict
10535
- Present a summary:
10536
- - **Quality:** X/10 (pass if >= 8)
10537
- - **Relations:** X connections (pass if >= 1)
10538
- - **Strategy link:** present/missing (required for governed collections)
10539
- - **Semantic quality:** X/3 criteria pass
10540
- - **Verdict:** READY TO COMMIT / NEEDS IMPROVEMENT
10541
-
10542
- If ready, ask the user to confirm. If not, suggest specific improvements.
10543
-
10544
- **Begin with Step 1 now.**`
10545
- }
10546
- }]
10547
- };
10548
- }
10549
- );
10550
- server.prompt(
10551
- "project-scan",
10552
- "Scan local project files to extract structured knowledge for Product Brain. The IDE agent reads README.md, package.json, .cursorrules, folder structure, and recent git history, then extracts vision, tech stack, modules, business rules, conventions, decisions, and domain terms into batch-capture entries. The trust boundary is confirmation before capture: only capture entries the user explicitly keeps, then let normal workspace governance apply.",
10553
- {
10554
- workspaceContext: z23.string().optional().describe("Optional context about the workspace preset and existing collections (paste from start/orient output)")
10555
- },
10556
- async ({ workspaceContext }) => {
10557
- let collectionsContext = "";
10558
- try {
10559
- const cols = await mcpQuery("chain.listCollections");
10560
- if (cols?.length) {
10561
- const colList = cols.map((c) => `- \`${c.slug}\`: ${c.name}`).join("\n");
10562
- collectionsContext = `
10563
- ## Available Collections
10564
- ${colList}
10565
- `;
10566
- }
10567
- } catch {
10568
- }
10569
- const schemaFields = Object.entries(scanExtractionSchema.shape).map(([key, field]) => `- \`${key}\`: ${field.description ?? ""}`).join("\n");
10570
- const exampleEntries = scanToBatchEntries({
10571
- vision: "A knowledge management tool for solo developers working with AI coding assistants",
10572
- audience: "Solo developers using Cursor, Claude Code, or similar AI IDEs",
10573
- techStack: ["SvelteKit", "Convex", "TypeScript"],
10574
- modules: [{ name: "Product Brain MCP", purpose: "MCP server that exposes knowledge capture tools to the IDE agent" }],
10575
- businessRules: ["All public functions must validate arguments with Zod before processing"],
10576
- conventions: ["Use batch-capture for multi-entry writes; never call capture in a loop"],
10577
- keyDecisions: ["Use Convex for real-time backend \u2014 avoids managing infrastructure"],
10578
- keyTerms: ["Chain", "Capture", "Orientation"],
10579
- tensions: ["Activation requires skill \u2014 users need to know what to capture before value compounds"]
10580
- });
10581
- const exampleOutput = JSON.stringify({ entries: exampleEntries.slice(0, 5) }, null, 2);
10582
- return {
10583
- messages: [{
10584
- role: "user",
10585
- content: {
10586
- type: "text",
10587
- text: `# Project Scan \u2014 Agent-Side Knowledge Extraction
10588
-
10589
- You are scanning the user's local project to extract structured knowledge for Product Brain.
10590
- PB runs remotely \u2014 you read the files and extract, then call \`batch-capture\` after confirmation.
10591
-
10592
- ` + (workspaceContext ? `## Workspace Context
10593
- ${workspaceContext}
10594
-
10595
- ` : "") + collectionsContext + `## Step 1: Read Project Files
10596
-
10597
- Read these files in order. Skip gracefully if not present.
10598
-
10599
- 1. **\`README.md\`** \u2014 Look for: product purpose (vision), who it's for (audience), key concepts, architecture notes, known issues
10600
- 2. **\`package.json\`** \u2014 Look for: \`name\`, \`description\`, and **dependencies** \u2014 identify the framework, database, auth library, UI library (these become tech stack + glossary). Ignore dev tools and test libraries.
10601
- 3. **\`tsconfig.json\` / \`tsconfig.app.json\`** \u2014 Look for: path aliases (e.g. \`@/\`) that hint at module structure
10602
- 4. **Top-level folder structure** (list directories only, max 2 levels) \u2014 Look for: named modules, feature folders, service boundaries. Note names that are specific enough to be meaningful.
10603
- 5. **\`.cursorrules\` / \`CLAUDE.md\` / \`AGENTS.md\`** \u2014 Look for: **business rules** (constraints the system enforces), **conventions** (how work is done here), no-gos, domain vocabulary
10604
- 6. **\`git log --oneline -20\`** \u2014 Look for: commits that imply decisions ("migrate from X to Y", "switch to Z for reason"), repeated friction ("fix broken", "revert", "hotfix same area twice")
10605
-
10606
- **ICP stack only (V1):** README, package.json, tsconfig, folder structure, .cursorrules/CLAUDE.md/AGENTS.md, git log.
10607
- Do not attempt Python, Rust, Go, or mobile-specific files in this pass.
10608
-
10609
- ## Step 2: Extract Structured Data
10610
-
10611
- After reading all files, extract into this schema:
10612
-
10613
- \`\`\`
10614
- ${schemaFields}
10615
- \`\`\`
10616
-
10617
- **Field-by-field guidance:**
10618
- - \`vision\`: one sentence product purpose. Source: README intro or package.json description. Required if README exists.
10619
- - \`audience\`: who it's for. Source: README target-user section or "built for" language.
10620
- - \`techStack\`: main runtime dependencies only \u2014 framework, database, auth, UI. Max 8. Not dev tools, test libs, or linters.
10621
- - \`modules\`: named subsystems with clear purposes. Source: folder structure + tsconfig path aliases. Skip generic names (src, lib, utils alone). Max 5.
10622
- - \`businessRules\`: constraints the system enforces. Source: .cursorrules/AGENTS.md. Write as: "All X must Y" or "The system must Z". Keep as written.
10623
- - \`conventions\`: how work is done here. Source: .cursorrules standards sections. Write as: "Use X not Y" or "Always do Z".
10624
- - \`keyDecisions\`: architectural choices made, preferably with rationale. Source: git log patterns, README architecture notes. Each as a declarative statement ("Use Convex for real-time backend").
10625
- - \`tensions\`: known friction or trade-offs. Source: git log hotfixes/reverts, README "known issues".
10626
- - \`keyTerms\`: domain vocabulary specific to this project \u2014 not tech names, but product/business terms. Source: README, .cursorrules.
10627
-
10628
- **Quality rules (RH2 mitigation):**
10629
- - Only include items with direct evidence from the files \u2014 no inference, no hallucination
10630
- - Prefer 8 precise entries over 20 vague ones
10631
- - Business rules and conventions must be actual constraints, not generic advice
10632
- - Empty arrays beat fabricated data
10633
-
10634
- ## Step 3: Validate Before Presenting
10635
-
10636
- Before presenting, check each entry:
10637
- - vision is at least 10 characters if present
10638
- - no duplicate names within the same collection
10639
- - each entry has a non-empty name and description
10640
- - techStack items are actual technologies, not version strings or config flags
10641
- - module names are specific (not just "src", "lib", "utils")
10642
-
10643
- Drop any entry that fails. If no vision AND no techStack exist, the scan is too sparse \u2014 tell the user which files were missing and stop.
10644
-
10645
- ## Step 4: Present for Confirmation
10646
-
10647
- Present inferred entries as a numbered list grouped by collection. Ask which to keep.
10648
- **Do NOT call batch-capture yet.** Only after the user confirms.
10649
-
10650
- ## Step 5: Map to batch-capture
10651
-
10652
- After confirmation, map entries using these rules:
10653
- - \`vision\` \u2192 strategy, name: "Product Vision"
10654
- - \`audience\` \u2192 audiences
10655
- - \`techStack\` \u2192 architecture ("Tech Stack") + top 3 items individually \u2192 glossary
10656
- - \`modules\` \u2192 architecture (1 per module, max 5)
10657
- - \`businessRules\` \u2192 business-rules (1 per rule)
10658
- - \`conventions\` \u2192 standards (1 per convention)
10659
- - \`keyDecisions\` \u2192 decisions (1 per decision)
10660
- - \`tensions\` \u2192 tensions (1 per tension)
10661
- - \`keyTerms\` \u2192 glossary (skip names already added from techStack)
10662
-
10663
- **Example output:**
10664
- \`\`\`json
10665
- ${exampleOutput}
10666
- \`\`\`
10667
-
10668
- Call batch-capture with confirmed entries only. Omit \`autoCommit\` to follow workspace governance.
10669
- If \`failed > 0\`, inspect \`failedEntries\` and retry those individually.
10670
-
10671
- ## Step 6: Connect + Prove Value
10672
-
10673
- After capture:
10674
- 1. Call \`graph action=suggest\` on 2\u20133 key entries (vision, architecture, a decision)
10675
- 2. Invent a plausible next task the user might actually do and call \`context action=gather task="<that task>"\`
10676
- 3. Present what Product Brain now knows in that task context \u2014 this is the proof moment
10677
- 4. Show one clear result summary so the user sees exactly what was added
10678
-
10679
- **Begin with Step 1 now.** Read the files, then report what you found before extracting.`
10680
- }
10681
- }]
10682
- };
10683
- }
10684
- );
10685
- server.prompt(
10686
- "review-drafts",
10687
- "Review all uncommitted workspace drafts grouped by collection and commit them in batches. After each committed batch, surfaces the most non-obvious graph connection found as the WOW moment \u2014 the signal that Product Brain has started compounding.",
10688
- {
10689
- focus: z23.string().optional().describe("Optional: a specific collection to review first (e.g. 'glossary', 'decisions')")
10690
- },
10691
- async ({ focus }) => {
10692
- let allDrafts = [];
10693
- try {
10694
- const raw = await mcpQuery("chain.listEntries", { status: "draft" });
10695
- if (Array.isArray(raw)) {
10696
- allDrafts = raw.filter(
10697
- (e) => typeof e === "object" && e !== null && typeof e.entryId === "string" && typeof e.name === "string" && typeof e.collection === "string"
10698
- );
10699
- }
10700
- } catch {
10701
- }
10702
- if (focus) {
10703
- allDrafts.sort((a, b) => {
10704
- const aMatch = a.collection === focus ? -1 : 0;
10705
- const bMatch = b.collection === focus ? -1 : 0;
10706
- return aMatch - bMatch;
10707
- });
10708
- }
10709
- const grouped = groupDraftsByCollection(allDrafts);
10710
- const draftList = formatDraftGroups(grouped, allDrafts.length);
10711
- const hasDrafts = allDrafts.length > 0;
10712
- return {
10713
- messages: [{
10714
- role: "user",
10715
- content: {
10716
- type: "text",
10717
- text: `# Review Drafts \u2014 Batch Commit Queue
10718
-
10719
- ` + (hasDrafts ? draftList + "\n\n" : "No drafts found in this workspace. Everything is already committed.\n\n") + (hasDrafts ? `---
10720
-
10721
- ## How to proceed
10722
-
10723
- Work through each collection group with the user. The goal is: every entry the user wants to keep gets committed.
10724
-
10725
- **Step 1 \u2014 Offer a starting point**
10726
-
10727
- Ask the user which group to tackle first, or suggest the most important one. A good default: start with Strategy and Decisions \u2014 these are high-signal and there are usually few of them. Glossary and Architecture entries can be batched quickly afterward.
10728
-
10729
- **Step 2 \u2014 Confirm, then commit**
10730
-
10731
- For each group the user confirms, call \`commit-entry\` once per entry. These must be sequential \u2014 commit each before moving to the next.
10732
-
10733
- \`\`\`
10734
- commit-entry entryId="[ID]"
10735
- \`\`\`
10736
-
10737
- Report any failures and skip rather than block. If commit returns \`proposal_created\`, tell the user \u2014 this workspace uses governed commits and proposals need approval.
10738
-
10739
- **Step 3 \u2014 Surface the WOW connection (once per committed batch)**
10740
-
10741
- After finishing a collection group, call \`graph action=suggest\` on up to 2 of the most important entries you just committed:
10742
- - First choice: any strategy entry
10743
- - Second choice: architecture or decisions entries
10744
- - Maximum: 2 \`graph action=suggest\` calls per batch \u2014 not one per entry
10745
-
10746
- From the suggestions returned, pick the **most non-obvious connection** \u2014 the one that crosses collection boundaries (e.g. a vision entry connecting to a decision, not just two glossary terms). Prefer suggestions with specific reasoning over generic ones. Ignore suggestions with no reasoning.
10747
-
10748
- If a strong connection exists, present it exactly like this:
10749
-
10750
- ---
10751
- **The graph noticed something.**
10752
-
10753
- Your **[Entry A]** connects to **[Entry B]** \u2014 [reasoning from the graph, in one sentence].
10754
-
10755
- You didn't explain this relationship. Product Brain inferred it from what you've built so far. This is the compounding starting.
10756
-
10757
- Want to add this link?
10758
- \`\`\`
10759
- relations action=create from=[A] to=[B] type=[relationType]
10760
- \`\`\`
10761
- ---
10762
-
10763
- If there are no specific suggestions (or all reasoning is generic), skip this step silently. Do not fabricate a connection.
10764
-
10765
- **Step 4 \u2014 The offer**
10766
-
10767
- After the first batch is committed (especially if it includes a strategy or decisions entry), offer:
10768
-
10769
- > "Ask me anything about your product \u2014 I'll answer using everything you just committed."
10770
-
10771
- This is not a formality. If the user asks a question, actually answer it using \`context action=gather task="<their question>"\`. Show them what the Brain now knows, not just that it captured the data.
10772
-
10773
- **Step 5 \u2014 Continue or wrap**
10774
-
10775
- After each batch, ask if the user wants to continue with the next group. Once all groups are done, close with a summary:
10776
-
10777
- - How many entries were committed this session
10778
- - Which collections are now populated
10779
- - One sentence: what Product Brain now knows about their product
10780
-
10781
- End with: **"Your Brain is ready."**` : `Your workspace has no uncommitted entries. If you recently captured new knowledge, check that \`batch-capture\` completed successfully and try \`entries action=list status=draft\`.`)
10782
- }
10783
- }]
10784
- };
10785
- }
10786
- );
10787
10030
  }
10788
10031
 
10789
10032
  // src/server.ts
@@ -10898,4 +10141,4 @@ export {
10898
10141
  SERVER_VERSION,
10899
10142
  createProductBrainServer
10900
10143
  };
10901
- //# sourceMappingURL=chunk-MDGV4MHW.js.map
10144
+ //# sourceMappingURL=chunk-WXT35272.js.map