@productbrain/mcp 0.0.1-beta.41 → 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-M264FY2V.js";
28
+ } from "./chunk-PQP27A3R.js";
29
29
  import {
30
30
  trackQualityCheck,
31
31
  trackQualityVerdict
32
- } from "./chunk-P7ABQEFK.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"]).optional().describe("Collection workflow state (e.g. 'open', 'pending', 'decided', 'conflict'). 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-GH4CXVVX.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",
@@ -4121,6 +4133,7 @@ async function handleStart2(args) {
4121
4133
  const sc = orient?.strategicContext;
4122
4134
  const parts = [];
4123
4135
  if (sc?.vision) parts.push(`Vision: ${sc.vision}`);
4136
+ if (sc?.purpose) parts.push(`Purpose: ${sc.purpose}`);
4124
4137
  if (sc?.activeBetCount) parts.push(`${sc.activeBetCount} active bet(s)`);
4125
4138
  if (sc?.activeTensionCount) parts.push(`${sc.activeTensionCount} open tension(s)`);
4126
4139
  if (orient?.activeBets?.length) {
@@ -4469,6 +4482,7 @@ async function handleRespond(args) {
4469
4482
  if (captureReady) {
4470
4483
  commitBlockers = computeCommitBlockers({
4471
4484
  betEntryId: betId,
4485
+ betDocId: refreshedBet?._id ?? betEntry._id,
4472
4486
  relations: refreshedConstellation.relations,
4473
4487
  betData: refreshedData,
4474
4488
  sessionDrafts
@@ -4608,6 +4622,7 @@ async function handleScore(args) {
4608
4622
  }
4609
4623
  const commitBlockers = computeCommitBlockers({
4610
4624
  betEntryId: betId,
4625
+ betDocId: betEntry._id,
4611
4626
  relations: constellation.relations,
4612
4627
  betData,
4613
4628
  sessionDrafts
@@ -4767,10 +4782,27 @@ async function handleCommitConstellation(args) {
4767
4782
  const relations = await mcpQuery("chain.listEntryRelations", {
4768
4783
  entryId: betId
4769
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
+ })();
4770
4800
  const sessionDrafts = await loadSessionDrafts(betId, betEntry._id);
4771
4801
  const commitBlockerItems = computeCommitBlockers({
4772
4802
  betEntryId: betId,
4803
+ betDocId: betEntry._id,
4773
4804
  relations,
4805
+ hasStrategyLink,
4774
4806
  betData,
4775
4807
  sessionDrafts
4776
4808
  });
@@ -4792,6 +4824,7 @@ async function handleCommitConstellation(args) {
4792
4824
  blockers: commitBlockerItems,
4793
4825
  committedIds: [],
4794
4826
  alreadyCommittedIds: [],
4827
+ proposedIds: [],
4795
4828
  failedIds: [],
4796
4829
  conflictIds: [],
4797
4830
  totalRelations: relations.length
@@ -4800,7 +4833,7 @@ async function handleCommitConstellation(args) {
4800
4833
  }
4801
4834
  let contradictionWarnings = [];
4802
4835
  try {
4803
- const { runContradictionCheck } = await import("./smart-capture-GH4CXVVX.js");
4836
+ const { runContradictionCheck } = await import("./smart-capture-W2IALMJ5.js");
4804
4837
  const descField = betData.problem ?? betData.description ?? "";
4805
4838
  contradictionWarnings = await runContradictionCheck(
4806
4839
  betEntry.name ?? betId,
@@ -4835,24 +4868,40 @@ No constellation entries were committed.`
4835
4868
  isError: true
4836
4869
  };
4837
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
+ };
4838
4879
  for (const entryId of result.committedIds) {
4839
- 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
+ }
4840
4888
  }
4841
4889
  const lines = [];
4890
+ const hasIssues = result.failedIds.length > 0 || result.conflictIds.length > 0;
4842
4891
  if (result.proposalCreated) {
4843
4892
  const linkedCount = result.committedIds.filter((id) => id !== betId).length;
4844
4893
  lines.push(
4845
- `# Proposal created`,
4894
+ hasIssues ? `# Proposal created with issues` : `# Proposal created`,
4846
4895
  "",
4847
4896
  `Workspace uses consent-based governance. \`${betId}\` was submitted as a proposal \u2014 it will be committed when approved.`,
4848
4897
  `**${linkedCount} linked entries** were committed directly (proposals apply to the bet only).`
4849
4898
  );
4850
4899
  } else {
4851
4900
  lines.push(
4852
- `# Published`,
4901
+ hasIssues ? `# Published with issues` : `# Published`,
4853
4902
  "",
4854
4903
  `**${result.committedIds.length} entries** committed to the Chain, **${result.totalRelations} connections** preserved.`,
4855
- `\`${betId}\` and its full constellation are now source of truth.`
4904
+ hasIssues ? `\`${betId}\` was partially committed. Review conflicts/failed entries below before considering the constellation complete.` : `\`${betId}\` and its full constellation are now source of truth.`
4856
4905
  );
4857
4906
  }
4858
4907
  if (contradictionWarnings.length > 0) {
@@ -4864,6 +4913,9 @@ No constellation entries were committed.`
4864
4913
  if (result.alreadyCommittedIds.length > 0) {
4865
4914
  lines.push("", `**Already committed:** ${result.alreadyCommittedIds.join(", ")}`);
4866
4915
  }
4916
+ if (result.proposedIds.length > 0) {
4917
+ lines.push("", `**Proposed (not yet committed):** ${result.proposedIds.join(", ")}`);
4918
+ }
4867
4919
  if (result.conflictIds.length > 0) {
4868
4920
  lines.push("", `**${result.conflictIds.length} conflicts:**`);
4869
4921
  for (const c of result.conflictIds) {
@@ -5335,10 +5387,10 @@ function formatRecoveryBlock(block) {
5335
5387
  const lines = [
5336
5388
  "## Previous Session Recovery",
5337
5389
  "",
5338
- `Session ${block.sessionId} \u2014 ${block.date}, ${block.duration}, status: ${block.status}`,
5390
+ `Previous session \u2014 ${block.date}, ${block.duration}, status: ${block.status}`,
5339
5391
  `Created (${block.created.length + (block.createdOverflow ?? 0)}): ${fmt(block.created, block.createdOverflow)}`,
5340
5392
  `Modified (${block.modified.length + (block.modifiedOverflow ?? 0)}): ${fmt(block.modified, block.modifiedOverflow)}`,
5341
- `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)}`,
5342
5394
  `Relations created: ${block.relationsCreated}`,
5343
5395
  ""
5344
5396
  ];
@@ -5361,7 +5413,7 @@ function buildPlannedWorkSection(work, priorSessions, recoveryBlock) {
5361
5413
  }
5362
5414
  if (work.uncommittedDrafts.length > 0) {
5363
5415
  const count = work.uncommittedDrafts.length;
5364
- const label = count === 1 ? "1 uncommitted draft" : `${count} uncommitted drafts`;
5416
+ const label = count === 1 ? "1 item pending" : `${count} items pending`;
5365
5417
  const topNames = work.uncommittedDrafts.slice(0, 3).map((d) => d.name).join(", ");
5366
5418
  lines.push(`- **${label}** \u2014 ${topNames}${count > 3 ? ", ..." : ""}`);
5367
5419
  }
@@ -5581,7 +5633,7 @@ function extractionToBatchEntries(extracted) {
5581
5633
  }
5582
5634
  function getInterviewInstructions(workspaceName) {
5583
5635
  return {
5584
- 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.`,
5585
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.)`,
5586
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.)`,
5587
5639
  extractionGuidance: `After the user answers, extract:
@@ -5611,7 +5663,7 @@ function buildInterviewResponse(workspaceName) {
5611
5663
  "",
5612
5664
  instructions.systemPrompt,
5613
5665
  "",
5614
- "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.",
5615
5667
  "",
5616
5668
  `**${instructions.question1}**`,
5617
5669
  "",
@@ -5619,7 +5671,7 @@ function buildInterviewResponse(workspaceName) {
5619
5671
  "",
5620
5672
  `> ${instructions.scanOffer}`,
5621
5673
  "",
5622
- '_Everything stays as a draft until you say "commit" or "looks good"._'
5674
+ "_Everything stays pending until you confirm._"
5623
5675
  ].join("\n");
5624
5676
  }
5625
5677
 
@@ -5666,27 +5718,42 @@ function registerStartTools(server) {
5666
5718
  errors.push("Readiness check unavailable \u2014 showing workspace summary.");
5667
5719
  }
5668
5720
  if (stage === "blank" && preset) {
5669
- 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
+ };
5670
5726
  }
5671
5727
  if (stage === "blank") {
5672
- 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
+ };
5673
5733
  }
5674
5734
  if (stage === "seeded") {
5675
- const text = await buildSeededResponse(wsCtx, readiness, agentSessionId);
5735
+ const result = await buildSeededResponse(wsCtx, readiness, agentSessionId);
5676
5736
  void mcpMutation("chain.setOnboardingCompleted", {}).catch(() => {
5677
5737
  });
5678
- return { content: [{ type: "text", text }] };
5738
+ return {
5739
+ content: [{ type: "text", text: result.text }],
5740
+ structuredContent: result.structuredContent
5741
+ };
5679
5742
  }
5680
5743
  if (stage === "grounded" || stage === "connected") {
5681
5744
  void mcpMutation("chain.setOnboardingCompleted", {}).catch(() => {
5682
5745
  });
5683
5746
  }
5684
- 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
+ };
5685
5752
  }
5686
5753
  );
5687
5754
  }
5688
5755
  function buildProjectScanResponse(wsCtx) {
5689
- return [
5756
+ const text = [
5690
5757
  `# Welcome to ${wsCtx.workspaceName}`,
5691
5758
  "",
5692
5759
  "Your workspace is fresh. Let me get oriented in your codebase.",
@@ -5716,56 +5783,72 @@ function buildProjectScanResponse(wsCtx) {
5716
5783
  "",
5717
5784
  "_Prefer 5 specific entries over 8 generic ones. Skip anything that would apply to any codebase._"
5718
5785
  ].join("\n");
5786
+ return {
5787
+ text,
5788
+ structuredContent: {
5789
+ stage: "blank",
5790
+ oriented: false,
5791
+ orientationStatus: "no_session"
5792
+ }
5793
+ };
5719
5794
  }
5720
5795
  async function buildSeededResponse(wsCtx, readiness, agentSessionId) {
5796
+ const stage = readiness?.stage ?? "seeded";
5797
+ const score = readiness?.score;
5798
+ const gaps = readiness?.gaps ?? [];
5721
5799
  const lines = [
5722
5800
  `# ${wsCtx.workspaceName}`,
5723
5801
  "_Picking up where you left off._",
5724
5802
  ""
5725
5803
  ];
5726
- const stage = readiness?.stage ?? "seeded";
5727
- const score = readiness?.score;
5728
- if (score !== void 0) {
5729
- lines.push(`**Brain stage: ${stage}.** Readiness score: ${score}/100.`);
5730
- lines.push("");
5731
- }
5732
- const gaps = readiness?.gaps ?? [];
5733
5804
  if (gaps.length > 0) {
5734
- lines.push("## Top gaps to fill");
5735
- for (const gap of gaps.slice(0, 3)) {
5736
- const cta = gap.capabilityGuidance ?? gap.guidance ?? `Capture ${gap.label.toLowerCase()} to unlock this.`;
5737
- lines.push(`**${gap.label}** \u2014 ${cta}`);
5738
- }
5805
+ lines.push("Here are the most impactful gaps to fill next:");
5739
5806
  lines.push("");
5740
- if (gaps.length > 3) {
5741
- lines.push(`_${gaps.length - 3} more gap${gaps.length - 3 === 1 ? "" : "s"} \u2014 use \`health action=status\` for the full list._`);
5742
- 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}`);
5743
5812
  }
5744
- } else {
5745
- lines.push("No gaps detected \u2014 workspace is filling up nicely.");
5746
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?");
5747
5817
  }
5748
- lines.push("What would you like to work on?");
5749
- lines.push("");
5818
+ let oriented = false;
5819
+ let orientationStatus = "no_session";
5750
5820
  if (agentSessionId) {
5751
5821
  try {
5752
5822
  await mcpCall("agent.markOriented", { sessionId: agentSessionId });
5753
5823
  setSessionOriented(true);
5754
- lines.push("---");
5755
- lines.push("Orientation complete. Write tools available.");
5824
+ oriented = true;
5825
+ orientationStatus = "complete";
5756
5826
  } catch {
5757
- lines.push("---");
5758
- lines.push("_Warning: Could not mark session as oriented._");
5827
+ orientationStatus = "failed";
5759
5828
  }
5760
5829
  }
5761
- 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
+ };
5762
5842
  }
5763
5843
  async function seedPreset(wsCtx, presetId, agentSessionId) {
5764
5844
  const preset = getPreset(presetId);
5765
5845
  if (!preset) {
5766
- return `Preset "${presetId}" not found.
5846
+ return {
5847
+ text: `Preset "${presetId}" not found.
5767
5848
 
5768
- 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
+ };
5769
5852
  }
5770
5853
  const seeded = [];
5771
5854
  const skipped = [];
@@ -5785,11 +5868,16 @@ Available presets: ${listPresets().map((p) => `\`${p.id}\``).join(", ")}`;
5785
5868
  skipped.push(`${col.name} (already exists)`);
5786
5869
  }
5787
5870
  }
5871
+ let oriented = false;
5872
+ let orientationStatus = "no_session";
5788
5873
  if (agentSessionId) {
5789
5874
  try {
5790
5875
  await mcpCall("agent.markOriented", { sessionId: agentSessionId });
5791
5876
  setSessionOriented(true);
5877
+ oriented = true;
5878
+ orientationStatus = "complete";
5792
5879
  } catch {
5880
+ orientationStatus = "failed";
5793
5881
  }
5794
5882
  }
5795
5883
  const lines = [
@@ -5811,12 +5899,20 @@ Available presets: ${listPresets().map((p) => `\`${p.id}\``).join(", ")}`;
5811
5899
  "",
5812
5900
  buildInterviewResponse(wsCtx.workspaceName),
5813
5901
  "",
5814
- "_You can also customize your structure anytime: `collections action=create`, `collections action=update`, or `collections action=list`._",
5815
- "",
5816
- "---",
5817
- "Orientation complete. Write tools are available."
5902
+ "_You can also customize your collections anytime \u2014 just ask._"
5818
5903
  );
5819
- 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
+ };
5820
5916
  }
5821
5917
  function computeWorkspaceAge(createdAt) {
5822
5918
  if (!createdAt) return { ageDays: 0, isNeglected: false };
@@ -5867,7 +5963,7 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
5867
5963
  const isHighReadiness = readiness !== null && readiness.score >= 50;
5868
5964
  const stage = readiness?.stage ?? null;
5869
5965
  lines.push(`# ${wsCtx.workspaceName}`);
5870
- lines.push(`_Workspace \`${wsCtx.workspaceSlug}\` \u2014 Product Brain is healthy._`);
5966
+ lines.push("_Product Brain is ready._");
5871
5967
  lines.push("");
5872
5968
  if (isLowReadiness && isNeglected) {
5873
5969
  const stageNote = stage ? ` and is still at the **${stage}** stage` : "";
@@ -5875,7 +5971,7 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
5875
5971
  lines.push("Let's close the gaps \u2014 or if the current structure doesn't fit, we can reshape it.");
5876
5972
  lines.push("");
5877
5973
  } else if (isLowReadiness) {
5878
- if (stage) lines.push(`**Brain stage: ${stage}.** Let's get your workspace active.`);
5974
+ lines.push("Let's get your workspace active.");
5879
5975
  lines.push("");
5880
5976
  }
5881
5977
  if (isLowReadiness) {
@@ -5886,21 +5982,17 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
5886
5982
  lines.push("");
5887
5983
  lines.push(nextAction.cta);
5888
5984
  lines.push("");
5889
- 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._");
5890
5986
  lines.push("");
5891
5987
  const remainingGaps = (readiness.gaps ?? []).length - 1;
5892
5988
  if (remainingGaps > 0 || openTensions.length > 0) {
5893
- 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._`);
5894
5990
  lines.push("");
5895
5991
  }
5896
5992
  }
5897
- lines.push("_Need a collection that doesn't exist yet (e.g. audiences, personas, metrics)?_");
5898
- 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._");
5899
5994
  lines.push("");
5900
5995
  } else if (isHighReadiness) {
5901
- if (readiness) {
5902
- lines.push(`**Brain stage: ${stage}.**`);
5903
- }
5904
5996
  let wsPrinciples = [];
5905
5997
  let wsStandards = [];
5906
5998
  let wsBusinessRules = [];
@@ -6002,7 +6094,7 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
6002
6094
  lines.push("");
6003
6095
  lines.push("## Your workspace is activated");
6004
6096
  lines.push(
6005
- `**${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.`
6006
6098
  );
6007
6099
  lines.push("");
6008
6100
  lines.push(
@@ -6013,7 +6105,7 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
6013
6105
  lines.push(`**View in Studio:** \`/w/${wsCtx.workspaceSlug}/studio\``);
6014
6106
  lines.push("");
6015
6107
  }
6016
- 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._");
6017
6109
  lines.push("");
6018
6110
  }
6019
6111
  } catch {
@@ -6026,23 +6118,30 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
6026
6118
  for (const err of errors) lines.push(`- ${err}`);
6027
6119
  lines.push("");
6028
6120
  }
6121
+ let oriented = false;
6122
+ let orientationStatus = "no_session";
6029
6123
  if (agentSessionId) {
6030
6124
  try {
6031
6125
  await mcpCall("agent.markOriented", { sessionId: agentSessionId });
6032
6126
  setSessionOriented(true);
6033
- lines.push("---");
6034
- lines.push(
6035
- `Orientation complete. Session ${agentSessionId}. Write tools available.`
6036
- );
6127
+ oriented = true;
6128
+ orientationStatus = "complete";
6037
6129
  } catch {
6038
- lines.push("---");
6039
- lines.push("_Warning: Could not mark session as oriented. Write tools may be restricted._");
6130
+ orientationStatus = "failed";
6040
6131
  }
6041
- } else {
6042
- lines.push("---");
6043
- lines.push("_No active agent session. Call `session action=start` to begin a tracked session._");
6044
6132
  }
6045
- 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
+ };
6046
6145
  }
6047
6146
 
6048
6147
  // src/tools/usage.ts
@@ -8061,6 +8160,34 @@ function registerHealthTools(server) {
8061
8160
  } catch (e) {
8062
8161
  errors.push(`Readiness: ${e instanceof Error ? e.message : String(e)}`);
8063
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
+ }
8064
8191
  const lines = [];
8065
8192
  const isLowReadiness = readiness && readiness.score < 50;
8066
8193
  if (wsCtx) {
@@ -8077,6 +8204,7 @@ function registerHealthTools(server) {
8077
8204
  if (orientEntries?.strategicContext) {
8078
8205
  const sc = orientEntries.strategicContext;
8079
8206
  if (sc.vision) lines.push(`Vision: ${sc.vision}`);
8207
+ if (sc.purpose) lines.push(`Purpose: ${sc.purpose}`);
8080
8208
  if (sc.productAreaCount != null && sc.productAreaCount > 0) {
8081
8209
  lines.push(`Product areas (${sc.productAreaCount}): ${(sc.productAreas ?? []).join(", ")}`);
8082
8210
  }
@@ -8172,6 +8300,7 @@ function registerHealthTools(server) {
8172
8300
  const sc = orientEntries.strategicContext;
8173
8301
  lines.push("## Strategic Context");
8174
8302
  if (sc.vision) lines.push(`**Vision:** ${sc.vision}`);
8303
+ if (sc.purpose) lines.push(`**Purpose:** ${sc.purpose}`);
8175
8304
  if (sc.productAreaCount != null && sc.productAreaCount > 0) {
8176
8305
  lines.push(`**Product areas (${sc.productAreaCount}):** ${(sc.productAreas ?? []).join(", ")}`);
8177
8306
  }
@@ -9122,6 +9251,8 @@ Description field: ${wf.kbOutputTemplate.descriptionField}
9122
9251
  const orient = await mcpQuery("chain.getOrientEntries", {});
9123
9252
  const sc = orient?.strategicContext;
9124
9253
  if (sc?.vision) strategicContext += `**Vision:** ${sc.vision}
9254
+ `;
9255
+ if (sc?.purpose) strategicContext += `**Purpose:** ${sc.purpose}
9125
9256
  `;
9126
9257
  if (sc?.currentBet) strategicContext += `**Current bet:** ${sc.currentBet}
9127
9258
  `;
@@ -9166,18 +9297,20 @@ You are a **coached shaping facilitator** powered by the \`facilitate\` tool. Yo
9166
9297
 
9167
9298
  ## Your Role (DEC-56 Boundary)
9168
9299
 
9169
- 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.
9170
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.
9171
9302
  The \`coaching.suggestedQuestion\` is a suggestion, not a script \u2014 rephrase or skip based on conversation flow.
9172
9303
 
9173
- ## Rubric Dimensions (5 scoring criteria)
9304
+ ## Rubric Dimensions (7 scoring criteria)
9174
9305
 
9175
9306
  Each scored 0-10 by the server:
9176
9307
  1. **Problem Clarity** \u2014 workaround described, who affected, frequency/severity, differentiated from existing tensions
9177
9308
  2. **Appetite Definition** \u2014 time constraint explicit, scope bounded, trade-offs acknowledged
9178
9309
  3. **Element Decomposition** \u2014 breadboard-level pieces identified, independently describable, no implementation leaks
9179
- 4. **Risk Coverage** \u2014 rabbit holes named, mitigations or acceptances, codebase-level risks
9180
- 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
9181
9314
 
9182
9315
  ## How to Use the Facilitate Tool
9183
9316
 
@@ -9614,8 +9747,8 @@ var INSTRUCTIONS = [
9614
9747
  "",
9615
9748
  "## Workflow",
9616
9749
  "",
9617
- " 1. Start: call `session action=start` to begin a tracked session.",
9618
- " 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.",
9619
9752
  " 3. Discover: use `entries action=search` to find entries, or `entries action=list` to browse.",
9620
9753
  ' 4. Drill in: use `entries action=get entryId="..."` for full details \u2014 data, labels, relations, history.',
9621
9754
  " 5. Context: use `context action=gather` with an entryId or a task description.",
@@ -9626,8 +9759,7 @@ var INSTRUCTIONS = [
9626
9759
  " 10. Close: call `session action=close` when done \u2014 records session activity. Auto-nudges if wrapup was skipped.",
9627
9760
  "",
9628
9761
  "Write tools (capture, update-entry, relations, commit-entry) require:",
9629
- " - An active agent session (call session action=start)",
9630
- " - Completed orientation (call orient)",
9762
+ " - Call `start` to begin (starts session + loads context). Call `orient` mid-session for a refresh.",
9631
9763
  " - A readwrite API key scope",
9632
9764
  "",
9633
9765
  "Commit-on-confirm: always capture as draft first and show the user what was captured.",
@@ -9687,4 +9819,4 @@ export {
9687
9819
  SERVER_VERSION,
9688
9820
  createProductBrainServer
9689
9821
  };
9690
- //# sourceMappingURL=chunk-5IH3KEAZ.js.map
9822
+ //# sourceMappingURL=chunk-AWDD44CN.js.map