@productbrain/mcp 0.0.1-beta.42 → 0.0.1-beta.43

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.
@@ -25,43 +25,51 @@ import {
25
25
  startAgentSession,
26
26
  trackWriteTool,
27
27
  translateStaleToolNames
28
- } from "./chunk-4YKORBQX.js";
28
+ } from "./chunk-PQP27A3R.js";
29
29
  import {
30
30
  trackQualityCheck,
31
31
  trackQualityVerdict
32
- } from "./chunk-6R67YS23.js";
32
+ } from "./chunk-BIDMZOLE.js";
33
33
 
34
34
  // src/server.ts
35
35
  import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
36
36
 
37
37
  // src/tools/knowledge.ts
38
38
  import { z } from "zod";
39
- var LEGACY_WORKFLOW_STATUSES = /* @__PURE__ */ new Set([
39
+ var WORKFLOW_STATUS_VALUES = [
40
+ // Generic / governed collections
40
41
  "open",
41
42
  "pending",
42
- "decided",
43
43
  "proposed",
44
44
  "accepted",
45
45
  "needs-amendment",
46
46
  "withdrawn",
47
47
  "review",
48
48
  "in-progress",
49
+ "decided",
49
50
  "conflict",
50
51
  "processing",
51
52
  "closed",
53
+ // Bets lifecycle
52
54
  "shaped",
53
55
  "bet",
54
56
  "building",
55
- "shipped"
56
- ]);
57
+ "shipped",
58
+ // Assumptions lifecycle
59
+ "untested",
60
+ "testing",
61
+ "validated",
62
+ "invalidated"
63
+ ];
64
+ var LEGACY_WORKFLOW_STATUSES = new Set(WORKFLOW_STATUS_VALUES);
57
65
  var updateEntrySchema = z.object({
58
66
  entryId: z.string().describe("Entry ID to update, e.g. 'T-SUPPLIER', 'BR-001'"),
59
67
  name: z.string().optional().describe("New display name"),
60
68
  status: z.union([
61
69
  z.enum(["draft", "active", "deprecated", "archived"]),
62
- z.enum(["open", "pending", "decided", "proposed", "accepted", "needs-amendment", "withdrawn", "review", "in-progress", "conflict", "processing", "closed", "shaped", "bet", "building", "shipped"])
70
+ z.enum(WORKFLOW_STATUS_VALUES)
63
71
  ]).optional().describe("Lifecycle status: draft | active | deprecated | archived. **Workflow values (open, pending, decided\u2026) are deprecated here \u2014 use `workflowStatus` instead. Passing a workflow value as `status` will be auto-routed with a warning until 2026-09-03, then hard-errored.**"),
64
- workflowStatus: z.enum(["open", "pending", "proposed", "accepted", "needs-amendment", "withdrawn", "review", "in-progress", "decided", "conflict", "processing", "closed", "shaped", "bet", "building", "shipped"]).optional().describe("Collection workflow state (e.g. 'open', 'pending', 'decided', 'conflict', 'building', 'shipped'). Validated against collection's allowed values."),
72
+ workflowStatus: z.enum(WORKFLOW_STATUS_VALUES).optional().describe("Collection workflow state. Each collection restricts which values are valid (e.g. bets: 'shaped' | 'bet' | 'building' | 'shipped'; assumptions: 'untested' | 'testing' | 'validated' | 'invalidated'; decisions: 'pending' | 'decided'; tensions: 'open' | 'processing' | 'decided' | 'closed'). The backend will reject values invalid for the target collection."),
65
73
  data: z.record(z.unknown()).optional().describe("Fields to update (merged with existing data)"),
66
74
  order: z.number().optional().describe("New sort order"),
67
75
  canonicalKey: z.string().optional().describe("Semantic type (e.g. 'decision', 'tension'). Only changeable on draft/uncommitted entries."),
@@ -195,7 +203,7 @@ ${formatted}` }]
195
203
  },
196
204
  async ({ entryId }) => {
197
205
  requireWriteAccess();
198
- const { runContradictionCheck } = await import("./smart-capture-TD5M4WFE.js");
206
+ const { runContradictionCheck } = await import("./smart-capture-W2IALMJ5.js");
199
207
  const entry = await mcpQuery("chain.getEntry", { entryId });
200
208
  if (!entry) {
201
209
  return {
@@ -3669,11 +3677,15 @@ function generateBuildContract(ctx) {
3669
3677
 
3670
3678
  // src/tools/facilitate-validation.ts
3671
3679
  function computeCommitBlockers(opts) {
3672
- const { betEntryId, relations, betData, sessionDrafts } = opts;
3680
+ const { betEntryId, betDocId, relations, hasStrategyLink, betData, sessionDrafts } = opts;
3673
3681
  const blockers = [];
3674
3682
  const str = (key) => (betData[key] ?? "").trim();
3675
- const hasStrategyLink = relations.some((r) => r.type === "commits_to");
3676
- if (!hasStrategyLink) {
3683
+ const hasDirectionalCommitsTo = relations.some((r) => {
3684
+ if (r.type !== "commits_to") return false;
3685
+ return r.fromId === betDocId && r.toId !== betDocId;
3686
+ });
3687
+ const strategyLinked = hasStrategyLink ?? hasDirectionalCommitsTo;
3688
+ if (!strategyLinked) {
3677
3689
  blockers.push({
3678
3690
  entryId: betEntryId,
3679
3691
  blocker: "Missing strategy link",
@@ -4470,6 +4482,7 @@ async function handleRespond(args) {
4470
4482
  if (captureReady) {
4471
4483
  commitBlockers = computeCommitBlockers({
4472
4484
  betEntryId: betId,
4485
+ betDocId: refreshedBet?._id ?? betEntry._id,
4473
4486
  relations: refreshedConstellation.relations,
4474
4487
  betData: refreshedData,
4475
4488
  sessionDrafts
@@ -4609,6 +4622,7 @@ async function handleScore(args) {
4609
4622
  }
4610
4623
  const commitBlockers = computeCommitBlockers({
4611
4624
  betEntryId: betId,
4625
+ betDocId: betEntry._id,
4612
4626
  relations: constellation.relations,
4613
4627
  betData,
4614
4628
  sessionDrafts
@@ -4768,10 +4782,27 @@ async function handleCommitConstellation(args) {
4768
4782
  const relations = await mcpQuery("chain.listEntryRelations", {
4769
4783
  entryId: betId
4770
4784
  });
4785
+ const hasStrategyLink = await (async () => {
4786
+ const relatedDocIds = /* @__PURE__ */ new Set();
4787
+ for (const rel of relations) {
4788
+ if (rel.fromId === betEntry._id && rel.toId) relatedDocIds.add(rel.toId);
4789
+ if (rel.toId === betEntry._id && rel.fromId) relatedDocIds.add(rel.fromId);
4790
+ }
4791
+ for (const docId of relatedDocIds) {
4792
+ try {
4793
+ const related = await mcpQuery("chain.getEntry", { id: docId });
4794
+ if (related?.collectionSlug === "strategy") return true;
4795
+ } catch {
4796
+ }
4797
+ }
4798
+ return false;
4799
+ })();
4771
4800
  const sessionDrafts = await loadSessionDrafts(betId, betEntry._id);
4772
4801
  const commitBlockerItems = computeCommitBlockers({
4773
4802
  betEntryId: betId,
4803
+ betDocId: betEntry._id,
4774
4804
  relations,
4805
+ hasStrategyLink,
4775
4806
  betData,
4776
4807
  sessionDrafts
4777
4808
  });
@@ -4793,6 +4824,7 @@ async function handleCommitConstellation(args) {
4793
4824
  blockers: commitBlockerItems,
4794
4825
  committedIds: [],
4795
4826
  alreadyCommittedIds: [],
4827
+ proposedIds: [],
4796
4828
  failedIds: [],
4797
4829
  conflictIds: [],
4798
4830
  totalRelations: relations.length
@@ -4801,7 +4833,7 @@ async function handleCommitConstellation(args) {
4801
4833
  }
4802
4834
  let contradictionWarnings = [];
4803
4835
  try {
4804
- const { runContradictionCheck } = await import("./smart-capture-TD5M4WFE.js");
4836
+ const { runContradictionCheck } = await import("./smart-capture-W2IALMJ5.js");
4805
4837
  const descField = betData.problem ?? betData.description ?? "";
4806
4838
  contradictionWarnings = await runContradictionCheck(
4807
4839
  betEntry.name ?? betId,
@@ -4836,15 +4868,30 @@ No constellation entries were committed.`
4836
4868
  isError: true
4837
4869
  };
4838
4870
  }
4871
+ result = {
4872
+ ...result,
4873
+ committedIds: result.committedIds ?? [],
4874
+ alreadyCommittedIds: result.alreadyCommittedIds ?? [],
4875
+ proposedIds: result.proposedIds ?? [],
4876
+ failedIds: result.failedIds ?? [],
4877
+ conflictIds: result.conflictIds ?? []
4878
+ };
4839
4879
  for (const entryId of result.committedIds) {
4840
- await recordSessionActivity({ entryModified: entryId });
4880
+ try {
4881
+ const committedEntry = await mcpQuery("chain.getEntry", { entryId });
4882
+ const docId = committedEntry?._id;
4883
+ if (typeof docId === "string" && docId.length > 0) {
4884
+ await recordSessionActivity({ entryModified: docId });
4885
+ }
4886
+ } catch {
4887
+ }
4841
4888
  }
4842
4889
  const lines = [];
4843
4890
  const hasIssues = result.failedIds.length > 0 || result.conflictIds.length > 0;
4844
4891
  if (result.proposalCreated) {
4845
4892
  const linkedCount = result.committedIds.filter((id) => id !== betId).length;
4846
4893
  lines.push(
4847
- `# Proposal created`,
4894
+ hasIssues ? `# Proposal created with issues` : `# Proposal created`,
4848
4895
  "",
4849
4896
  `Workspace uses consent-based governance. \`${betId}\` was submitted as a proposal \u2014 it will be committed when approved.`,
4850
4897
  `**${linkedCount} linked entries** were committed directly (proposals apply to the bet only).`
@@ -4866,6 +4913,9 @@ No constellation entries were committed.`
4866
4913
  if (result.alreadyCommittedIds.length > 0) {
4867
4914
  lines.push("", `**Already committed:** ${result.alreadyCommittedIds.join(", ")}`);
4868
4915
  }
4916
+ if (result.proposedIds.length > 0) {
4917
+ lines.push("", `**Proposed (not yet committed):** ${result.proposedIds.join(", ")}`);
4918
+ }
4869
4919
  if (result.conflictIds.length > 0) {
4870
4920
  lines.push("", `**${result.conflictIds.length} conflicts:**`);
4871
4921
  for (const c of result.conflictIds) {
@@ -5337,10 +5387,10 @@ function formatRecoveryBlock(block) {
5337
5387
  const lines = [
5338
5388
  "## Previous Session Recovery",
5339
5389
  "",
5340
- `Session ${block.sessionId} \u2014 ${block.date}, ${block.duration}, status: ${block.status}`,
5390
+ `Previous session \u2014 ${block.date}, ${block.duration}, status: ${block.status}`,
5341
5391
  `Created (${block.created.length + (block.createdOverflow ?? 0)}): ${fmt(block.created, block.createdOverflow)}`,
5342
5392
  `Modified (${block.modified.length + (block.modifiedOverflow ?? 0)}): ${fmt(block.modified, block.modifiedOverflow)}`,
5343
- `Drafts needing attention (${block.draftsNeedingAttention.length + (block.draftsOverflow ?? 0)}): ${fmt(block.draftsNeedingAttention, block.draftsOverflow)}`,
5393
+ `Items pending review (${block.draftsNeedingAttention.length + (block.draftsOverflow ?? 0)}): ${fmt(block.draftsNeedingAttention, block.draftsOverflow)}`,
5344
5394
  `Relations created: ${block.relationsCreated}`,
5345
5395
  ""
5346
5396
  ];
@@ -5363,7 +5413,7 @@ function buildPlannedWorkSection(work, priorSessions, recoveryBlock) {
5363
5413
  }
5364
5414
  if (work.uncommittedDrafts.length > 0) {
5365
5415
  const count = work.uncommittedDrafts.length;
5366
- const label = count === 1 ? "1 uncommitted draft" : `${count} uncommitted drafts`;
5416
+ const label = count === 1 ? "1 item pending" : `${count} items pending`;
5367
5417
  const topNames = work.uncommittedDrafts.slice(0, 3).map((d) => d.name).join(", ");
5368
5418
  lines.push(`- **${label}** \u2014 ${topNames}${count > 3 ? ", ..." : ""}`);
5369
5419
  }
@@ -5583,7 +5633,7 @@ function extractionToBatchEntries(extracted) {
5583
5633
  }
5584
5634
  function getInterviewInstructions(workspaceName) {
5585
5635
  return {
5586
- systemPrompt: `You are activating the **${workspaceName}** Product Brain. Ask 1\u20132 focused questions, then extract structured knowledge and feed it to batch-capture. Everything lands as a draft \u2014 the user confirms before anything is committed.`,
5636
+ systemPrompt: `You are activating the **${workspaceName}** Product Brain. Ask 1\u20132 focused questions, then extract structured knowledge and feed it to batch-capture. Everything stays pending \u2014 the user confirms before anything is committed.`,
5587
5637
  question1: `**Q1 \u2014 What are you building and for whom?** Describe your product in 1\u20132 sentences and who it's for. (This becomes your Product Vision and primary Audience.)`,
5588
5638
  question2: `**Q2 (optional) \u2014 What's your tech stack or key domain terms?** Name a few core technologies or terms your team uses. (These become Architecture + Glossary entries.)`,
5589
5639
  extractionGuidance: `After the user answers, extract:
@@ -5613,7 +5663,7 @@ function buildInterviewResponse(workspaceName) {
5613
5663
  "",
5614
5664
  instructions.systemPrompt,
5615
5665
  "",
5616
- "I'll ask you 1\u20132 questions. Your answers become the first entries in your knowledge graph \u2014 all as drafts until you confirm.",
5666
+ "I'll ask you 1\u20132 questions. Your answers become the first entries in your knowledge graph \u2014 all pending until you confirm.",
5617
5667
  "",
5618
5668
  `**${instructions.question1}**`,
5619
5669
  "",
@@ -5621,7 +5671,7 @@ function buildInterviewResponse(workspaceName) {
5621
5671
  "",
5622
5672
  `> ${instructions.scanOffer}`,
5623
5673
  "",
5624
- '_Everything stays as a draft until you say "commit" or "looks good"._'
5674
+ "_Everything stays pending until you confirm._"
5625
5675
  ].join("\n");
5626
5676
  }
5627
5677
 
@@ -5668,27 +5718,42 @@ function registerStartTools(server) {
5668
5718
  errors.push("Readiness check unavailable \u2014 showing workspace summary.");
5669
5719
  }
5670
5720
  if (stage === "blank" && preset) {
5671
- return { content: [{ type: "text", text: await seedPreset(wsCtx, preset, agentSessionId) }] };
5721
+ const result = await seedPreset(wsCtx, preset, agentSessionId);
5722
+ return {
5723
+ content: [{ type: "text", text: result.text }],
5724
+ structuredContent: result.structuredContent
5725
+ };
5672
5726
  }
5673
5727
  if (stage === "blank") {
5674
- return { content: [{ type: "text", text: buildProjectScanResponse(wsCtx) }] };
5728
+ const scanResult = buildProjectScanResponse(wsCtx);
5729
+ return {
5730
+ content: [{ type: "text", text: scanResult.text }],
5731
+ structuredContent: scanResult.structuredContent
5732
+ };
5675
5733
  }
5676
5734
  if (stage === "seeded") {
5677
- const text = await buildSeededResponse(wsCtx, readiness, agentSessionId);
5735
+ const result = await buildSeededResponse(wsCtx, readiness, agentSessionId);
5678
5736
  void mcpMutation("chain.setOnboardingCompleted", {}).catch(() => {
5679
5737
  });
5680
- return { content: [{ type: "text", text }] };
5738
+ return {
5739
+ content: [{ type: "text", text: result.text }],
5740
+ structuredContent: result.structuredContent
5741
+ };
5681
5742
  }
5682
5743
  if (stage === "grounded" || stage === "connected") {
5683
5744
  void mcpMutation("chain.setOnboardingCompleted", {}).catch(() => {
5684
5745
  });
5685
5746
  }
5686
- return { content: [{ type: "text", text: await buildOrientResponse(wsCtx, agentSessionId, errors) }] };
5747
+ const orientResult = await buildOrientResponse(wsCtx, agentSessionId, errors);
5748
+ return {
5749
+ content: [{ type: "text", text: orientResult.text }],
5750
+ structuredContent: orientResult.structuredContent
5751
+ };
5687
5752
  }
5688
5753
  );
5689
5754
  }
5690
5755
  function buildProjectScanResponse(wsCtx) {
5691
- return [
5756
+ const text = [
5692
5757
  `# Welcome to ${wsCtx.workspaceName}`,
5693
5758
  "",
5694
5759
  "Your workspace is fresh. Let me get oriented in your codebase.",
@@ -5718,56 +5783,72 @@ function buildProjectScanResponse(wsCtx) {
5718
5783
  "",
5719
5784
  "_Prefer 5 specific entries over 8 generic ones. Skip anything that would apply to any codebase._"
5720
5785
  ].join("\n");
5786
+ return {
5787
+ text,
5788
+ structuredContent: {
5789
+ stage: "blank",
5790
+ oriented: false,
5791
+ orientationStatus: "no_session"
5792
+ }
5793
+ };
5721
5794
  }
5722
5795
  async function buildSeededResponse(wsCtx, readiness, agentSessionId) {
5796
+ const stage = readiness?.stage ?? "seeded";
5797
+ const score = readiness?.score;
5798
+ const gaps = readiness?.gaps ?? [];
5723
5799
  const lines = [
5724
5800
  `# ${wsCtx.workspaceName}`,
5725
5801
  "_Picking up where you left off._",
5726
5802
  ""
5727
5803
  ];
5728
- const stage = readiness?.stage ?? "seeded";
5729
- const score = readiness?.score;
5730
- if (score !== void 0) {
5731
- lines.push(`**Brain stage: ${stage}.** Readiness score: ${score}/100.`);
5732
- lines.push("");
5733
- }
5734
- const gaps = readiness?.gaps ?? [];
5735
5804
  if (gaps.length > 0) {
5736
- lines.push("## Top gaps to fill");
5737
- for (const gap of gaps.slice(0, 3)) {
5738
- const cta = gap.capabilityGuidance ?? gap.guidance ?? `Capture ${gap.label.toLowerCase()} to unlock this.`;
5739
- lines.push(`**${gap.label}** \u2014 ${cta}`);
5740
- }
5805
+ lines.push("Here are the most impactful gaps to fill next:");
5741
5806
  lines.push("");
5742
- if (gaps.length > 3) {
5743
- lines.push(`_${gaps.length - 3} more gap${gaps.length - 3 === 1 ? "" : "s"} \u2014 use \`health action=status\` for the full list._`);
5744
- lines.push("");
5807
+ const topGaps = gaps.slice(0, 3);
5808
+ for (let i = 0; i < topGaps.length; i++) {
5809
+ const gap = topGaps[i];
5810
+ const cta = gap.capabilityGuidance ?? gap.guidance ?? `Describe your ${gap.label.toLowerCase()}.`;
5811
+ lines.push(`${i + 1}. **${gap.label}** \u2014 ${cta}`);
5745
5812
  }
5746
- } else {
5747
- lines.push("No gaps detected \u2014 workspace is filling up nicely.");
5748
5813
  lines.push("");
5814
+ lines.push("Pick any to start \u2014 or begin with **#1** and I'll guide you through it.");
5815
+ } else {
5816
+ lines.push("No gaps detected \u2014 your workspace is filling up nicely. What would you like to work on?");
5749
5817
  }
5750
- lines.push("What would you like to work on?");
5751
- lines.push("");
5818
+ let oriented = false;
5819
+ let orientationStatus = "no_session";
5752
5820
  if (agentSessionId) {
5753
5821
  try {
5754
5822
  await mcpCall("agent.markOriented", { sessionId: agentSessionId });
5755
5823
  setSessionOriented(true);
5756
- lines.push("---");
5757
- lines.push("Orientation complete. Write tools available.");
5824
+ oriented = true;
5825
+ orientationStatus = "complete";
5758
5826
  } catch {
5759
- lines.push("---");
5760
- lines.push("_Warning: Could not mark session as oriented._");
5827
+ orientationStatus = "failed";
5761
5828
  }
5762
5829
  }
5763
- return lines.join("\n");
5830
+ return {
5831
+ text: lines.join("\n"),
5832
+ structuredContent: {
5833
+ stage,
5834
+ readinessScore: score ?? null,
5835
+ totalGaps: gaps.length,
5836
+ topGapLabels: gaps.slice(0, 3).map((g) => g.label),
5837
+ oriented,
5838
+ orientationStatus,
5839
+ ...agentSessionId ? { sessionId: agentSessionId } : {}
5840
+ }
5841
+ };
5764
5842
  }
5765
5843
  async function seedPreset(wsCtx, presetId, agentSessionId) {
5766
5844
  const preset = getPreset(presetId);
5767
5845
  if (!preset) {
5768
- return `Preset "${presetId}" not found.
5846
+ return {
5847
+ text: `Preset "${presetId}" not found.
5769
5848
 
5770
- Available presets: ${listPresets().map((p) => `\`${p.id}\``).join(", ")}`;
5849
+ Available presets: ${listPresets().map((p) => `\`${p.id}\``).join(", ")}`,
5850
+ structuredContent: { stage: "blank", error: "preset_not_found", preset: presetId }
5851
+ };
5771
5852
  }
5772
5853
  const seeded = [];
5773
5854
  const skipped = [];
@@ -5787,11 +5868,16 @@ Available presets: ${listPresets().map((p) => `\`${p.id}\``).join(", ")}`;
5787
5868
  skipped.push(`${col.name} (already exists)`);
5788
5869
  }
5789
5870
  }
5871
+ let oriented = false;
5872
+ let orientationStatus = "no_session";
5790
5873
  if (agentSessionId) {
5791
5874
  try {
5792
5875
  await mcpCall("agent.markOriented", { sessionId: agentSessionId });
5793
5876
  setSessionOriented(true);
5877
+ oriented = true;
5878
+ orientationStatus = "complete";
5794
5879
  } catch {
5880
+ orientationStatus = "failed";
5795
5881
  }
5796
5882
  }
5797
5883
  const lines = [
@@ -5813,12 +5899,20 @@ Available presets: ${listPresets().map((p) => `\`${p.id}\``).join(", ")}`;
5813
5899
  "",
5814
5900
  buildInterviewResponse(wsCtx.workspaceName),
5815
5901
  "",
5816
- "_You can also customize your structure anytime: `collections action=create`, `collections action=update`, or `collections action=list`._",
5817
- "",
5818
- "---",
5819
- "Orientation complete. Write tools are available."
5902
+ "_You can also customize your collections anytime \u2014 just ask._"
5820
5903
  );
5821
- return lines.join("\n");
5904
+ return {
5905
+ text: lines.join("\n"),
5906
+ structuredContent: {
5907
+ stage: "blank",
5908
+ preset: presetId,
5909
+ seededCollections: seeded,
5910
+ skippedCollections: skipped,
5911
+ oriented,
5912
+ orientationStatus,
5913
+ ...agentSessionId ? { sessionId: agentSessionId } : {}
5914
+ }
5915
+ };
5822
5916
  }
5823
5917
  function computeWorkspaceAge(createdAt) {
5824
5918
  if (!createdAt) return { ageDays: 0, isNeglected: false };
@@ -5869,7 +5963,7 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
5869
5963
  const isHighReadiness = readiness !== null && readiness.score >= 50;
5870
5964
  const stage = readiness?.stage ?? null;
5871
5965
  lines.push(`# ${wsCtx.workspaceName}`);
5872
- lines.push(`_Workspace \`${wsCtx.workspaceSlug}\` \u2014 Product Brain is healthy._`);
5966
+ lines.push("_Product Brain is ready._");
5873
5967
  lines.push("");
5874
5968
  if (isLowReadiness && isNeglected) {
5875
5969
  const stageNote = stage ? ` and is still at the **${stage}** stage` : "";
@@ -5877,7 +5971,7 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
5877
5971
  lines.push("Let's close the gaps \u2014 or if the current structure doesn't fit, we can reshape it.");
5878
5972
  lines.push("");
5879
5973
  } else if (isLowReadiness) {
5880
- if (stage) lines.push(`**Brain stage: ${stage}.** Let's get your workspace active.`);
5974
+ lines.push("Let's get your workspace active.");
5881
5975
  lines.push("");
5882
5976
  }
5883
5977
  if (isLowReadiness) {
@@ -5888,21 +5982,17 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
5888
5982
  lines.push("");
5889
5983
  lines.push(nextAction.cta);
5890
5984
  lines.push("");
5891
- lines.push('_Everything stays as a draft until you confirm. Say "commit" or "looks good" to promote to the Chain._');
5985
+ lines.push("_Everything I capture stays pending until you confirm._");
5892
5986
  lines.push("");
5893
5987
  const remainingGaps = (readiness.gaps ?? []).length - 1;
5894
5988
  if (remainingGaps > 0 || openTensions.length > 0) {
5895
- lines.push(`_${remainingGaps > 0 ? `${remainingGaps} more gap${remainingGaps === 1 ? "" : "s"}` : ""}${remainingGaps > 0 && openTensions.length > 0 ? " and " : ""}${openTensions.length > 0 ? `${openTensions.length} open tension${openTensions.length === 1 ? "" : "s"}` : ""} \u2014 ask "show full status" for details._`);
5989
+ lines.push(`_${remainingGaps > 0 ? `${remainingGaps} more area${remainingGaps === 1 ? "" : "s"} to cover` : ""}${remainingGaps > 0 && openTensions.length > 0 ? " and " : ""}${openTensions.length > 0 ? `${openTensions.length} open tension${openTensions.length === 1 ? "" : "s"}` : ""} \u2014 ask for full status to see everything._`);
5896
5990
  lines.push("");
5897
5991
  }
5898
5992
  }
5899
- lines.push("_Need a collection that doesn't exist yet (e.g. audiences, personas, metrics)?_");
5900
- lines.push("_Use `collections action=create` to add it, or ask me to propose collections for your domain._");
5993
+ lines.push("_Need a collection that doesn't exist yet (e.g. audiences, personas, metrics)? Just ask \u2014 I'll set it up._");
5901
5994
  lines.push("");
5902
5995
  } else if (isHighReadiness) {
5903
- if (readiness) {
5904
- lines.push(`**Brain stage: ${stage}.**`);
5905
- }
5906
5996
  let wsPrinciples = [];
5907
5997
  let wsStandards = [];
5908
5998
  let wsBusinessRules = [];
@@ -6004,7 +6094,7 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
6004
6094
  lines.push("");
6005
6095
  lines.push("## Your workspace is activated");
6006
6096
  lines.push(
6007
- `**${committed.length} committed entries** across **${committedCollections.size} collections** \u2014 your knowledge graph is alive.`
6097
+ `**${committed.length} entries** across **${committedCollections.size} collections** \u2014 your knowledge graph is alive.`
6008
6098
  );
6009
6099
  lines.push("");
6010
6100
  lines.push(
@@ -6015,7 +6105,7 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
6015
6105
  lines.push(`**View in Studio:** \`/w/${wsCtx.workspaceSlug}/studio\``);
6016
6106
  lines.push("");
6017
6107
  }
6018
- lines.push(`_Tip: Connect entries with \`graph action=suggest\` to unlock deeper context._`);
6108
+ lines.push("_Tip: Ask me to suggest connections between entries to unlock deeper context._");
6019
6109
  lines.push("");
6020
6110
  }
6021
6111
  } catch {
@@ -6028,23 +6118,30 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
6028
6118
  for (const err of errors) lines.push(`- ${err}`);
6029
6119
  lines.push("");
6030
6120
  }
6121
+ let oriented = false;
6122
+ let orientationStatus = "no_session";
6031
6123
  if (agentSessionId) {
6032
6124
  try {
6033
6125
  await mcpCall("agent.markOriented", { sessionId: agentSessionId });
6034
6126
  setSessionOriented(true);
6035
- lines.push("---");
6036
- lines.push(
6037
- `Orientation complete. Session ${agentSessionId}. Write tools available.`
6038
- );
6127
+ oriented = true;
6128
+ orientationStatus = "complete";
6039
6129
  } catch {
6040
- lines.push("---");
6041
- lines.push("_Warning: Could not mark session as oriented. Write tools may be restricted._");
6130
+ orientationStatus = "failed";
6042
6131
  }
6043
- } else {
6044
- lines.push("---");
6045
- lines.push("_No active agent session. Call `session action=start` to begin a tracked session._");
6046
6132
  }
6047
- return lines.join("\n");
6133
+ return {
6134
+ text: lines.join("\n"),
6135
+ structuredContent: {
6136
+ stage,
6137
+ readinessScore: readiness?.score ?? null,
6138
+ totalGaps: readiness?.gaps?.length ?? 0,
6139
+ openTensions: openTensions.length,
6140
+ oriented,
6141
+ orientationStatus,
6142
+ ...agentSessionId ? { sessionId: agentSessionId } : {}
6143
+ }
6144
+ };
6048
6145
  }
6049
6146
 
6050
6147
  // src/tools/usage.ts
@@ -8063,6 +8160,34 @@ function registerHealthTools(server) {
8063
8160
  } catch (e) {
8064
8161
  errors.push(`Readiness: ${e instanceof Error ? e.message : String(e)}`);
8065
8162
  }
8163
+ if (readiness?.stage === "blank") {
8164
+ const scanLines = [
8165
+ `# Welcome to ${wsCtx?.workspaceName ?? "your workspace"}`,
8166
+ "",
8167
+ "Your workspace is fresh \u2014 let's set it up.",
8168
+ "",
8169
+ "**Recommended:** call the `start` tool instead of `orient` for the best first-run experience.",
8170
+ "It will guide you through scanning your codebase and capturing initial knowledge.",
8171
+ "",
8172
+ "Or tell me about your product and I'll start capturing knowledge directly."
8173
+ ];
8174
+ if (agentSessionId) {
8175
+ try {
8176
+ await mcpCall("agent.markOriented", { sessionId: agentSessionId });
8177
+ setSessionOriented(true);
8178
+ } catch {
8179
+ }
8180
+ }
8181
+ return {
8182
+ content: [{ type: "text", text: scanLines.join("\n") }],
8183
+ structuredContent: {
8184
+ stage: "blank",
8185
+ readinessScore: readiness?.score ?? 0,
8186
+ oriented: true,
8187
+ redirectHint: "Use the `start` tool for the full guided setup experience."
8188
+ }
8189
+ };
8190
+ }
8066
8191
  const lines = [];
8067
8192
  const isLowReadiness = readiness && readiness.score < 50;
8068
8193
  if (wsCtx) {
@@ -9172,18 +9297,20 @@ You are a **coached shaping facilitator** powered by the \`facilitate\` tool. Yo
9172
9297
 
9173
9298
  ## Your Role (DEC-56 Boundary)
9174
9299
 
9175
- The **server judges** \u2014 it scores input against 5 rubric dimensions, detects overlap with existing Chain entries, and checks alignment with governance. The server returns structured coaching responses.
9300
+ The **server judges** \u2014 it scores input against 7 rubric dimensions, detects overlap with existing Chain entries, and checks alignment with governance. The server returns structured coaching responses.
9176
9301
  **You coach** \u2014 you interpret the structured response, synthesize it naturally, decide tone, follow up on weak areas, and push back when the shaping isn't sharp enough.
9177
9302
  The \`coaching.suggestedQuestion\` is a suggestion, not a script \u2014 rephrase or skip based on conversation flow.
9178
9303
 
9179
- ## Rubric Dimensions (5 scoring criteria)
9304
+ ## Rubric Dimensions (7 scoring criteria)
9180
9305
 
9181
9306
  Each scored 0-10 by the server:
9182
9307
  1. **Problem Clarity** \u2014 workaround described, who affected, frequency/severity, differentiated from existing tensions
9183
9308
  2. **Appetite Definition** \u2014 time constraint explicit, scope bounded, trade-offs acknowledged
9184
9309
  3. **Element Decomposition** \u2014 breadboard-level pieces identified, independently describable, no implementation leaks
9185
- 4. **Risk Coverage** \u2014 rabbit holes named, mitigations or acceptances, codebase-level risks
9186
- 5. **Boundary Specification** \u2014 explicit no-gos, each prevents scope creep in a specific direction
9310
+ 4. **Architecture Fit** \u2014 layer boundaries and dependency implications are explicit, aligned with existing system constraints
9311
+ 5. **Risk Coverage** \u2014 rabbit holes named, mitigations or acceptances, codebase-level risks
9312
+ 6. **Boundary Specification** \u2014 explicit no-gos, each prevents scope creep in a specific direction
9313
+ 7. **Done-When Quality** \u2014 testable completion outcomes, measurable verification criteria, and clear acceptance conditions
9187
9314
 
9188
9315
  ## How to Use the Facilitate Tool
9189
9316
 
@@ -9620,8 +9747,8 @@ var INSTRUCTIONS = [
9620
9747
  "",
9621
9748
  "## Workflow",
9622
9749
  "",
9623
- " 1. Start: call `session action=start` to begin a tracked session.",
9624
- " 2. Orient: call `orient` to load workspace context and unlock write tools.",
9750
+ " 1. Start: call `start` to begin \u2014 it starts a session automatically, detects your workspace stage, and returns the right guidance. Fresh workspaces get a guided setup; active workspaces get a standup briefing.",
9751
+ " 2. Re-orient: call `orient` mid-session for task-scoped context or a compact status refresh.",
9625
9752
  " 3. Discover: use `entries action=search` to find entries, or `entries action=list` to browse.",
9626
9753
  ' 4. Drill in: use `entries action=get entryId="..."` for full details \u2014 data, labels, relations, history.',
9627
9754
  " 5. Context: use `context action=gather` with an entryId or a task description.",
@@ -9632,8 +9759,7 @@ var INSTRUCTIONS = [
9632
9759
  " 10. Close: call `session action=close` when done \u2014 records session activity. Auto-nudges if wrapup was skipped.",
9633
9760
  "",
9634
9761
  "Write tools (capture, update-entry, relations, commit-entry) require:",
9635
- " - An active agent session (call session action=start)",
9636
- " - Completed orientation (call orient)",
9762
+ " - Call `start` to begin (starts session + loads context). Call `orient` mid-session for a refresh.",
9637
9763
  " - A readwrite API key scope",
9638
9764
  "",
9639
9765
  "Commit-on-confirm: always capture as draft first and show the user what was captured.",
@@ -9693,4 +9819,4 @@ export {
9693
9819
  SERVER_VERSION,
9694
9820
  createProductBrainServer
9695
9821
  };
9696
- //# sourceMappingURL=chunk-X7DON33G.js.map
9822
+ //# sourceMappingURL=chunk-AWDD44CN.js.map