@productbrain/mcp 0.0.1-beta.31 → 0.0.1-beta.32
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.
- package/dist/{chunk-AZHB7KMP.js → chunk-THAYIBTL.js} +353 -19
- package/dist/chunk-THAYIBTL.js.map +1 -0
- package/dist/{chunk-3JSR5JCU.js → chunk-UOT3O5FN.js} +24 -21
- package/dist/chunk-UOT3O5FN.js.map +1 -0
- package/dist/http.js +2 -2
- package/dist/index.js +2 -2
- package/dist/{smart-capture-X7BWHT4P.js → smart-capture-6TQM35CV.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-3JSR5JCU.js.map +0 -1
- package/dist/chunk-AZHB7KMP.js.map +0 -1
- /package/dist/{smart-capture-X7BWHT4P.js.map → smart-capture-6TQM35CV.js.map} +0 -0
|
@@ -22,11 +22,10 @@ import {
|
|
|
22
22
|
requireWriteAccess,
|
|
23
23
|
runWithToolContext,
|
|
24
24
|
setSessionOriented,
|
|
25
|
-
setWriteToolsEnabled,
|
|
26
25
|
startAgentSession,
|
|
27
26
|
trackWriteTool,
|
|
28
27
|
translateStaleToolNames
|
|
29
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-UOT3O5FN.js";
|
|
30
29
|
import {
|
|
31
30
|
trackQualityCheck,
|
|
32
31
|
trackQualityVerdict
|
|
@@ -126,7 +125,7 @@ ${formatted}` }]
|
|
|
126
125
|
},
|
|
127
126
|
async ({ entryId }) => {
|
|
128
127
|
requireWriteAccess();
|
|
129
|
-
const { runContradictionCheck } = await import("./smart-capture-
|
|
128
|
+
const { runContradictionCheck } = await import("./smart-capture-6TQM35CV.js");
|
|
130
129
|
const entry = await mcpQuery("chain.getEntry", { entryId });
|
|
131
130
|
if (!entry) {
|
|
132
131
|
return {
|
|
@@ -282,7 +281,8 @@ function registerEntriesTools(server) {
|
|
|
282
281
|
title: "Entries",
|
|
283
282
|
description: 'Read entries from the Chain. One tool for all entry reading.\n\n- **list**: Browse entries with optional filters (collection, status, tag, label). Use collections action=list first to discover slugs.\n- **get**: Fetch a single entry by ID \u2014 full record with data, labels, relations, history.\n- **batch**: Fetch multiple entries (max 20) in one call. Same shape as get per entry.\n- **search**: Full-text search across entries. Scope by collection or filter by status.\n\nUse `entries action=get entryId="..."` to fetch one. Use `entries action=search query="..."` to discover.',
|
|
284
283
|
inputSchema: entriesSchema,
|
|
285
|
-
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false }
|
|
284
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
285
|
+
_meta: { ui: { resourceUri: "ui://entries/entry-cards.html" } }
|
|
286
286
|
},
|
|
287
287
|
async (args) => {
|
|
288
288
|
const parsed = entriesSchema.safeParse(args);
|
|
@@ -371,6 +371,7 @@ async function handleGet(entryId) {
|
|
|
371
371
|
name: String(e.name ?? ""),
|
|
372
372
|
collection: String(e.canonicalKey ?? ""),
|
|
373
373
|
status: String(e.status ?? ""),
|
|
374
|
+
entries: [{ entryId: e.entryId, name: e.name, status: e.status, collectionName: String(e.collectionName ?? e.canonicalKey ?? "\u2014") }],
|
|
374
375
|
...e.data && typeof e.data === "object" ? { data: e.data } : {},
|
|
375
376
|
...Array.isArray(e.relations) && e.relations.length > 0 ? {
|
|
376
377
|
relations: e.relations.map((r) => ({
|
|
@@ -543,13 +544,24 @@ async function handleSearch(server, query, collection, status) {
|
|
|
543
544
|
status: String(e.status ?? ""),
|
|
544
545
|
...e.score != null ? { score: e.score } : {}
|
|
545
546
|
}));
|
|
547
|
+
const entriesForView = structuredResults.map((e) => ({
|
|
548
|
+
entryId: e.entryId,
|
|
549
|
+
name: e.name,
|
|
550
|
+
status: e.status,
|
|
551
|
+
collectionName: e.collection
|
|
552
|
+
}));
|
|
546
553
|
return {
|
|
547
554
|
content: [{ type: "text", text: `${header}
|
|
548
555
|
|
|
549
556
|
${formatted}
|
|
550
557
|
|
|
551
558
|
${footer}` }],
|
|
552
|
-
structuredContent: {
|
|
559
|
+
structuredContent: {
|
|
560
|
+
results: structuredResults,
|
|
561
|
+
entries: entriesForView,
|
|
562
|
+
total: structuredResults.length,
|
|
563
|
+
query
|
|
564
|
+
}
|
|
553
565
|
};
|
|
554
566
|
}
|
|
555
567
|
|
|
@@ -594,7 +606,8 @@ function registerGraphTools(server) {
|
|
|
594
606
|
title: "Graph",
|
|
595
607
|
description: "Read from the knowledge graph. Two actions:\n\n- **find**: Traverse relations from a specific entry \u2014 shows incoming/outgoing connections. Use after entries action=get to explore connections. This is NOT full-text search (use entries action=search).\n- **suggest**: Discover potential relations for an entry using graph-aware intelligence. Returns ranked suggestions with confidence scores. Use relations action=create or relations action=batch-create to create the ones that make sense.",
|
|
596
608
|
inputSchema: graphSchema,
|
|
597
|
-
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false }
|
|
609
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
610
|
+
_meta: { ui: { resourceUri: "ui://graph/constellation.html" } }
|
|
598
611
|
},
|
|
599
612
|
async (args) => {
|
|
600
613
|
const parsed = graphSchema.safeParse(args);
|
|
@@ -686,11 +699,26 @@ async function handleFind(entryId, direction) {
|
|
|
686
699
|
type: r.type,
|
|
687
700
|
direction: r.isOutgoing ? "outgoing" : "incoming"
|
|
688
701
|
}));
|
|
702
|
+
const nodes = [
|
|
703
|
+
{ id: entryId, name: sourceEntry.name, collectionName: "" },
|
|
704
|
+
...shown.map((r) => ({
|
|
705
|
+
id: r.otherEntryId ?? String(Math.random()),
|
|
706
|
+
name: r.otherName,
|
|
707
|
+
collectionName: r.colSlug
|
|
708
|
+
}))
|
|
709
|
+
];
|
|
710
|
+
const edges = shown.filter((r) => r.otherEntryId).map((r) => ({
|
|
711
|
+
source: entryId,
|
|
712
|
+
target: r.otherEntryId,
|
|
713
|
+
type: r.type
|
|
714
|
+
}));
|
|
689
715
|
return {
|
|
690
716
|
content: [{ type: "text", text: lines.join("\n") }],
|
|
691
717
|
structuredContent: {
|
|
692
718
|
entryId,
|
|
693
719
|
relations: structuredRelations,
|
|
720
|
+
nodes,
|
|
721
|
+
edges,
|
|
694
722
|
total: relations.length
|
|
695
723
|
}
|
|
696
724
|
};
|
|
@@ -755,11 +783,26 @@ async function handleSuggest(entryId, limit, depth) {
|
|
|
755
783
|
confidence: s.score,
|
|
756
784
|
reason: s.reasoning
|
|
757
785
|
}));
|
|
786
|
+
const nodes = [
|
|
787
|
+
{ id: sourceId, name: resolved?.name ?? entryId, collectionName: "" },
|
|
788
|
+
...suggestions.map((s) => ({
|
|
789
|
+
id: s.entryId ?? String(Math.random()),
|
|
790
|
+
name: s.name,
|
|
791
|
+
collectionName: s.collectionSlug
|
|
792
|
+
}))
|
|
793
|
+
];
|
|
794
|
+
const edges = suggestions.filter((s) => s.entryId).map((s) => ({
|
|
795
|
+
source: sourceId,
|
|
796
|
+
target: s.entryId,
|
|
797
|
+
type: s.recommendedRelationType || "suggested"
|
|
798
|
+
}));
|
|
758
799
|
return {
|
|
759
800
|
content: [{ type: "text", text: lines.join("\n") }],
|
|
760
801
|
structuredContent: {
|
|
761
802
|
entryId: sourceId,
|
|
762
803
|
suggestions: structuredSuggestions,
|
|
804
|
+
nodes,
|
|
805
|
+
edges,
|
|
763
806
|
total: suggestions.length
|
|
764
807
|
}
|
|
765
808
|
};
|
|
@@ -963,7 +1006,8 @@ function registerContextTools(server) {
|
|
|
963
1006
|
title: "Context",
|
|
964
1007
|
description: "Assemble knowledge context in one call. Two actions:\n\n- **gather**: Three modes \u2014 (1) By entry: traverse the graph around a specific entry. (2) By task: auto-load relevant domain knowledge for a natural-language task. (3) Graph mode (entryId + mode='graph'): enhanced traversal with provenance paths.\n- **build**: Structured build spec for any entry \u2014 data, related entries, business rules, glossary terms, chain refs. Use when starting a build. Requires active session.",
|
|
965
1008
|
inputSchema: contextSchema,
|
|
966
|
-
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false }
|
|
1009
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
1010
|
+
_meta: { ui: { resourceUri: "ui://graph/constellation.html" } }
|
|
967
1011
|
},
|
|
968
1012
|
async (args) => {
|
|
969
1013
|
const parsed = contextSchema.safeParse(args);
|
|
@@ -1048,7 +1092,24 @@ Use \`graph action=suggest\` to discover potential connections.`
|
|
|
1048
1092
|
}
|
|
1049
1093
|
lines2.push("");
|
|
1050
1094
|
}
|
|
1051
|
-
|
|
1095
|
+
const rootId2 = result2.root.entryId ?? "root";
|
|
1096
|
+
const nodes2 = [
|
|
1097
|
+
{ id: rootId2, name: result2.root.name, collectionName: "" },
|
|
1098
|
+
...result2.context.map((e) => ({
|
|
1099
|
+
id: e.entryId ?? String(Math.random()),
|
|
1100
|
+
name: e.name,
|
|
1101
|
+
collectionName: e.collectionName
|
|
1102
|
+
}))
|
|
1103
|
+
];
|
|
1104
|
+
const edges2 = result2.context.filter((e) => e.entryId).map((e) => ({
|
|
1105
|
+
source: rootId2,
|
|
1106
|
+
target: e.entryId,
|
|
1107
|
+
type: e.relationType
|
|
1108
|
+
}));
|
|
1109
|
+
return {
|
|
1110
|
+
content: [{ type: "text", text: lines2.join("\n") }],
|
|
1111
|
+
structuredContent: { nodes: nodes2, edges: edges2 }
|
|
1112
|
+
};
|
|
1052
1113
|
}
|
|
1053
1114
|
if (task && !entryId) {
|
|
1054
1115
|
await server.sendLoggingMessage({
|
|
@@ -1101,7 +1162,15 @@ _Consider capturing domain knowledge discovered during this task via \`capture\`
|
|
|
1101
1162
|
lines2.push("");
|
|
1102
1163
|
}
|
|
1103
1164
|
lines2.push(`_Use \`entries action=get\` for full details on any entry._`);
|
|
1104
|
-
|
|
1165
|
+
const taskEntries = result2.context.map((e) => ({
|
|
1166
|
+
entryId: e.entryId ?? String(Math.random()),
|
|
1167
|
+
name: e.name,
|
|
1168
|
+
collectionName: e.collectionName
|
|
1169
|
+
}));
|
|
1170
|
+
return {
|
|
1171
|
+
content: [{ type: "text", text: lines2.join("\n") }],
|
|
1172
|
+
structuredContent: { entries: taskEntries }
|
|
1173
|
+
};
|
|
1105
1174
|
}
|
|
1106
1175
|
await server.sendLoggingMessage({
|
|
1107
1176
|
level: "info",
|
|
@@ -1145,7 +1214,24 @@ Use \`graph action=suggest\` to discover potential connections, or \`relations a
|
|
|
1145
1214
|
}
|
|
1146
1215
|
lines.push("");
|
|
1147
1216
|
}
|
|
1148
|
-
|
|
1217
|
+
const rootId = result.root.entryId ?? "root";
|
|
1218
|
+
const nodes = [
|
|
1219
|
+
{ id: rootId, name: result.root.name, collectionName: "" },
|
|
1220
|
+
...result.related.map((e) => ({
|
|
1221
|
+
id: e.entryId ?? String(Math.random()),
|
|
1222
|
+
name: e.name,
|
|
1223
|
+
collectionName: e.collectionName
|
|
1224
|
+
}))
|
|
1225
|
+
];
|
|
1226
|
+
const edges = result.related.filter((e) => e.entryId).map((e) => ({
|
|
1227
|
+
source: rootId,
|
|
1228
|
+
target: e.entryId,
|
|
1229
|
+
type: e.relationType
|
|
1230
|
+
}));
|
|
1231
|
+
return {
|
|
1232
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
1233
|
+
structuredContent: { nodes, edges }
|
|
1234
|
+
};
|
|
1149
1235
|
}
|
|
1150
1236
|
async function handleBuild(server, entryId, maxHops) {
|
|
1151
1237
|
requireActiveSession();
|
|
@@ -1769,7 +1855,6 @@ async function handleStart() {
|
|
|
1769
1855
|
""
|
|
1770
1856
|
);
|
|
1771
1857
|
}
|
|
1772
|
-
setWriteToolsEnabled(true);
|
|
1773
1858
|
lines.push(
|
|
1774
1859
|
`Session ${result.sessionId} active. Initiated by ${result.initiatedBy}. Workspace ${result.workspaceName}. Scope: ${result.toolsScope}. Write tools available after orient.`
|
|
1775
1860
|
);
|
|
@@ -1813,7 +1898,6 @@ async function handleClose() {
|
|
|
1813
1898
|
}
|
|
1814
1899
|
}
|
|
1815
1900
|
await closeAgentSession();
|
|
1816
|
-
setWriteToolsEnabled(false);
|
|
1817
1901
|
const lines = [
|
|
1818
1902
|
`Session ${sessionId} closed.`,
|
|
1819
1903
|
""
|
|
@@ -2924,6 +3008,70 @@ function buildPlannedWorkSection(work, priorSessions, recoveryBlock) {
|
|
|
2924
3008
|
return lines;
|
|
2925
3009
|
}
|
|
2926
3010
|
|
|
3011
|
+
// src/tools/orient-shared.ts
|
|
3012
|
+
function runAlignmentCheck(task, activeBets, taskContextHits) {
|
|
3013
|
+
const betNames = activeBets.map((b) => b.name);
|
|
3014
|
+
const taskWords = task.toLowerCase().split(/\s+/).filter((w) => w.length > 3);
|
|
3015
|
+
const matchingBet = activeBets.find((b) => {
|
|
3016
|
+
const name = (b.name ?? "").toLowerCase();
|
|
3017
|
+
return taskWords.some((w) => name.includes(w));
|
|
3018
|
+
});
|
|
3019
|
+
const betHits = (taskContextHits ?? []).filter(
|
|
3020
|
+
(e) => e.collectionSlug === "bets"
|
|
3021
|
+
);
|
|
3022
|
+
const aligned = !!matchingBet || betHits.length > 0;
|
|
3023
|
+
const matchedBet = matchingBet?.name ?? betHits[0]?.name ?? null;
|
|
3024
|
+
return { aligned, matchedBet, betNames };
|
|
3025
|
+
}
|
|
3026
|
+
function buildAlignmentCheckLines(result) {
|
|
3027
|
+
const lines = ["## Alignment Check"];
|
|
3028
|
+
if (result.aligned) {
|
|
3029
|
+
lines.push(
|
|
3030
|
+
`Task appears related to active bet: **${result.matchedBet}**. Proceed.`
|
|
3031
|
+
);
|
|
3032
|
+
} else if (result.betNames.length === 0) {
|
|
3033
|
+
lines.push(
|
|
3034
|
+
"No active bets in workspace. Consider shaping a bet before building."
|
|
3035
|
+
);
|
|
3036
|
+
} else {
|
|
3037
|
+
lines.push("**This task does not match any active bet.**");
|
|
3038
|
+
lines.push(`Active bets: ${result.betNames.join(", ")}`);
|
|
3039
|
+
lines.push(
|
|
3040
|
+
"Stop and confirm with the user before designing. This may be out of scope."
|
|
3041
|
+
);
|
|
3042
|
+
}
|
|
3043
|
+
lines.push("");
|
|
3044
|
+
return lines;
|
|
3045
|
+
}
|
|
3046
|
+
var CORE_PROTOCOL = [
|
|
3047
|
+
'**Search before proposing.** Before suggesting new features, architecture, or changes, search the Chain: `entries action=search query="<relevant terms>"`. Build on what exists.',
|
|
3048
|
+
"**Reference by ID.** When discussing a topic that has Chain entries, cite them by entry ID (e.g. `DEC-50`, `PRI-3`). This keeps conversations grounded in shared knowledge.",
|
|
3049
|
+
"**Check scope.** If the proposed work doesn't fall under an active bet, stop and say so. Do not design implementation for out-of-scope work without explicit user go-ahead.",
|
|
3050
|
+
`**Capture continuously.** When a decision, tension, insight, or new term surfaces during work, capture it as a draft immediately. Don't defer to "later."`,
|
|
3051
|
+
"**Validate against governance.** Before finalizing any proposal, check it against the Workspace Governance section above. If it conflicts with a principle, standard, or business rule, flag the conflict explicitly."
|
|
3052
|
+
];
|
|
3053
|
+
function buildOperatingProtocol(workspaceStandards) {
|
|
3054
|
+
const lines = [
|
|
3055
|
+
"## Operating Protocol",
|
|
3056
|
+
"_How to work in this workspace. Follow these before and during every task._",
|
|
3057
|
+
""
|
|
3058
|
+
];
|
|
3059
|
+
for (let i = 0; i < CORE_PROTOCOL.length; i++) {
|
|
3060
|
+
lines.push(`${i + 1}. ${CORE_PROTOCOL[i]}`);
|
|
3061
|
+
}
|
|
3062
|
+
if (workspaceStandards && workspaceStandards.length > 0) {
|
|
3063
|
+
lines.push("");
|
|
3064
|
+
lines.push("**Workspace-specific standards:**");
|
|
3065
|
+
for (const s of workspaceStandards) {
|
|
3066
|
+
const id = s.entryId ?? s.name;
|
|
3067
|
+
const desc = s.description ? ` \u2014 ${s.description.slice(0, 120)}${s.description.length > 120 ? "..." : ""}` : "";
|
|
3068
|
+
lines.push(`- \`${id}\` **${s.name}**${desc}`);
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3071
|
+
lines.push("");
|
|
3072
|
+
return lines;
|
|
3073
|
+
}
|
|
3074
|
+
|
|
2927
3075
|
// src/tools/start.ts
|
|
2928
3076
|
var startSchema = z13.object({
|
|
2929
3077
|
preset: z13.string().optional().describe(
|
|
@@ -3152,6 +3300,60 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
|
|
|
3152
3300
|
if (readiness) {
|
|
3153
3301
|
lines.push(`Readiness: ${readiness.score}% (${readiness.passedChecks}/${readiness.totalChecks}).`);
|
|
3154
3302
|
}
|
|
3303
|
+
let wsStandards = [];
|
|
3304
|
+
try {
|
|
3305
|
+
const govSlugs = ["principles", "standards", "business-rules"];
|
|
3306
|
+
const govLabels = {
|
|
3307
|
+
principles: "Principles",
|
|
3308
|
+
standards: "Standards",
|
|
3309
|
+
"business-rules": "Business Rules"
|
|
3310
|
+
};
|
|
3311
|
+
const govEntries = [];
|
|
3312
|
+
for (const slug of govSlugs) {
|
|
3313
|
+
const entries = await mcpQuery("chain.listEntries", { collectionSlug: slug });
|
|
3314
|
+
const active = (entries ?? []).filter((e) => e.status === "active" || e.status === "verified");
|
|
3315
|
+
if (active.length > 0) {
|
|
3316
|
+
govEntries.push({ slug, entries: active });
|
|
3317
|
+
if (slug === "standards") wsStandards = active;
|
|
3318
|
+
}
|
|
3319
|
+
}
|
|
3320
|
+
if (govEntries.length > 0) {
|
|
3321
|
+
lines.push("");
|
|
3322
|
+
lines.push("## Workspace Governance \u2014 constraints");
|
|
3323
|
+
lines.push("_These constrain what you build. If your proposal conflicts with any of these, stop and flag it._");
|
|
3324
|
+
lines.push("");
|
|
3325
|
+
for (const g of govEntries) {
|
|
3326
|
+
lines.push(`**${govLabels[g.slug]}:**`);
|
|
3327
|
+
for (const e of g.entries.slice(0, 8)) {
|
|
3328
|
+
lines.push(`- \`${e.entryId ?? e.name}\` ${e.name}`);
|
|
3329
|
+
}
|
|
3330
|
+
lines.push("");
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3333
|
+
} catch {
|
|
3334
|
+
}
|
|
3335
|
+
let activeBets = [];
|
|
3336
|
+
try {
|
|
3337
|
+
const betEntries = await mcpQuery("chain.listEntries", { collectionSlug: "bets" });
|
|
3338
|
+
activeBets = (betEntries ?? []).filter((e) => e.status === "active" && e.data?.horizon === "now").slice(0, 8);
|
|
3339
|
+
if (activeBets.length > 0) {
|
|
3340
|
+
lines.push("");
|
|
3341
|
+
lines.push("## Active bets \u2014 current scope");
|
|
3342
|
+
lines.push("_These define what you're building now. Work outside these bets requires explicit user confirmation before designing._");
|
|
3343
|
+
lines.push("");
|
|
3344
|
+
for (const e of activeBets) {
|
|
3345
|
+
lines.push(`- \`${e.entryId ?? e.name}\` ${e.name}`);
|
|
3346
|
+
}
|
|
3347
|
+
lines.push("");
|
|
3348
|
+
}
|
|
3349
|
+
} catch {
|
|
3350
|
+
}
|
|
3351
|
+
const standardsForProtocol = wsStandards.map((e) => ({
|
|
3352
|
+
entryId: e.entryId,
|
|
3353
|
+
name: e.name,
|
|
3354
|
+
description: typeof e.data?.description === "string" ? e.data.description : typeof e.description === "string" ? e.description : void 0
|
|
3355
|
+
}));
|
|
3356
|
+
lines.push(...buildOperatingProtocol(standardsForProtocol));
|
|
3155
3357
|
const plannedWork = await queryPlannedWork();
|
|
3156
3358
|
if (hasPlannedWork(plannedWork)) {
|
|
3157
3359
|
lines.push("");
|
|
@@ -5337,6 +5539,14 @@ function registerHealthTools(server) {
|
|
|
5337
5539
|
}
|
|
5338
5540
|
lines.push("");
|
|
5339
5541
|
}
|
|
5542
|
+
if (task && orientEntries) {
|
|
5543
|
+
const result = runAlignmentCheck(
|
|
5544
|
+
task,
|
|
5545
|
+
orientEntries.activeBets ?? [],
|
|
5546
|
+
orientEntries.taskContext?.context
|
|
5547
|
+
);
|
|
5548
|
+
lines.push(...buildAlignmentCheckLines(result));
|
|
5549
|
+
}
|
|
5340
5550
|
if (orientEntries) {
|
|
5341
5551
|
const fmt = (e) => {
|
|
5342
5552
|
const type = e.canonicalKey ?? "generic";
|
|
@@ -5344,7 +5554,9 @@ function registerHealthTools(server) {
|
|
|
5344
5554
|
return `- \`${e.entryId ?? e._id}\` [${type} \xB7 ${stratum}] ${e.name}`;
|
|
5345
5555
|
};
|
|
5346
5556
|
if (orientEntries.activeBets?.length > 0) {
|
|
5347
|
-
lines.push("## Active bets");
|
|
5557
|
+
lines.push("## Active bets \u2014 current scope");
|
|
5558
|
+
lines.push("_These define what you're building now. Work outside these bets requires explicit user confirmation before designing._");
|
|
5559
|
+
lines.push("");
|
|
5348
5560
|
for (const e of orientEntries.activeBets) {
|
|
5349
5561
|
lines.push(fmt(e));
|
|
5350
5562
|
const tensions = e.linkedTensions;
|
|
@@ -5381,16 +5593,40 @@ function registerHealthTools(server) {
|
|
|
5381
5593
|
orientEntries.staleEntries.forEach((e) => lines.push(fmt(e)));
|
|
5382
5594
|
lines.push("");
|
|
5383
5595
|
}
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5596
|
+
const hasPrinciples = orientEntries.principles?.length > 0;
|
|
5597
|
+
const hasStandards = orientEntries.standards?.length > 0;
|
|
5598
|
+
const hasBusinessRules = orientEntries.businessRules.length > 0;
|
|
5599
|
+
if (hasPrinciples || hasStandards || hasBusinessRules) {
|
|
5600
|
+
lines.push("## Workspace Governance \u2014 constraints");
|
|
5601
|
+
lines.push("_These constrain what you build. If your proposal conflicts with any of these, stop and flag it. Do not design around them without explicit user confirmation._");
|
|
5387
5602
|
lines.push("");
|
|
5603
|
+
if (hasPrinciples) {
|
|
5604
|
+
lines.push("**Principles** \u2014 beliefs that guide decisions:");
|
|
5605
|
+
orientEntries.principles.forEach((e) => lines.push(fmt(e)));
|
|
5606
|
+
lines.push("");
|
|
5607
|
+
}
|
|
5608
|
+
if (hasStandards) {
|
|
5609
|
+
lines.push("**Standards** \u2014 conventions for how work is done:");
|
|
5610
|
+
orientEntries.standards.forEach((e) => lines.push(fmt(e)));
|
|
5611
|
+
lines.push("");
|
|
5612
|
+
}
|
|
5613
|
+
if (hasBusinessRules) {
|
|
5614
|
+
lines.push("**Business Rules** \u2014 system constraints:");
|
|
5615
|
+
orientEntries.businessRules.forEach((e) => lines.push(fmt(e)));
|
|
5616
|
+
lines.push("");
|
|
5617
|
+
}
|
|
5388
5618
|
}
|
|
5389
5619
|
if (orientEntries.architectureNotes.length > 0) {
|
|
5390
5620
|
lines.push("## Architecture notes");
|
|
5391
5621
|
orientEntries.architectureNotes.forEach((e) => lines.push(fmt(e)));
|
|
5392
5622
|
lines.push("");
|
|
5393
5623
|
}
|
|
5624
|
+
const standardsForProtocol = (orientEntries.standards ?? []).map((e) => ({
|
|
5625
|
+
entryId: e.entryId,
|
|
5626
|
+
name: e.name,
|
|
5627
|
+
description: typeof e.description === "string" ? e.description : void 0
|
|
5628
|
+
}));
|
|
5629
|
+
lines.push(...buildOperatingProtocol(standardsForProtocol));
|
|
5394
5630
|
}
|
|
5395
5631
|
const plannedWork = await queryPlannedWork();
|
|
5396
5632
|
if (hasPlannedWork(plannedWork)) {
|
|
@@ -5457,7 +5693,13 @@ function registerHealthTools(server) {
|
|
|
5457
5693
|
}
|
|
5458
5694
|
|
|
5459
5695
|
// src/resources/index.ts
|
|
5696
|
+
import { readFile } from "fs/promises";
|
|
5697
|
+
import { join, resolve as resolve3 } from "path";
|
|
5460
5698
|
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5699
|
+
var UI_VIEWS = {
|
|
5700
|
+
"ui://entries/entry-cards.html": "src/entry-cards/index.html",
|
|
5701
|
+
"ui://graph/constellation.html": "src/graph-constellation/index.html"
|
|
5702
|
+
};
|
|
5461
5703
|
function formatEntryMarkdown(entry) {
|
|
5462
5704
|
const id = entry.entryId ? `${entry.entryId}: ` : "";
|
|
5463
5705
|
const lines = [`## ${id}${entry.name} [${entry.status}]`];
|
|
@@ -5830,6 +6072,23 @@ ${entry.labels.map((l) => `- ${l.name ?? l.slug}`).join("\n")}`);
|
|
|
5830
6072
|
};
|
|
5831
6073
|
}
|
|
5832
6074
|
);
|
|
6075
|
+
const viewsBase = resolve3(process.cwd(), "packages", "mcp-views", "dist");
|
|
6076
|
+
for (const [uri, filePath] of Object.entries(UI_VIEWS)) {
|
|
6077
|
+
server.resource(`ui-view-${uri.replace(/[^a-z0-9]/gi, "-")}`, uri, async (uriObj) => {
|
|
6078
|
+
try {
|
|
6079
|
+
const html = await readFile(join(viewsBase, filePath), "utf-8");
|
|
6080
|
+
return { contents: [{ uri: uriObj.href, text: html, mimeType: "text/html" }] };
|
|
6081
|
+
} catch {
|
|
6082
|
+
return {
|
|
6083
|
+
contents: [{
|
|
6084
|
+
uri: uriObj.href,
|
|
6085
|
+
text: "View not found. Run npm run build in packages/mcp-views.",
|
|
6086
|
+
mimeType: "text/plain"
|
|
6087
|
+
}]
|
|
6088
|
+
};
|
|
6089
|
+
}
|
|
6090
|
+
});
|
|
6091
|
+
}
|
|
5833
6092
|
}
|
|
5834
6093
|
|
|
5835
6094
|
// src/prompts/index.ts
|
|
@@ -6110,6 +6369,75 @@ Description field: ${wf.kbOutputTemplate.descriptionField}
|
|
|
6110
6369
|
};
|
|
6111
6370
|
}
|
|
6112
6371
|
);
|
|
6372
|
+
server.prompt(
|
|
6373
|
+
"shape-a-bet",
|
|
6374
|
+
"Choreograph shaping a product bet using the constellation capture pattern. Guides through problem framing, appetite, solution elements, risk identification, boundaries, and constellation capture. Use when the user wants to shape a bet, define a pitch, or scope work using Shape Up methodology.",
|
|
6375
|
+
{
|
|
6376
|
+
idea: z19.string().describe("Brief description of the idea or feature to shape (e.g. 'Improve the glossary editing flow')")
|
|
6377
|
+
},
|
|
6378
|
+
async ({ idea }) => {
|
|
6379
|
+
return {
|
|
6380
|
+
messages: [{
|
|
6381
|
+
role: "user",
|
|
6382
|
+
content: {
|
|
6383
|
+
type: "text",
|
|
6384
|
+
text: `# Shape a Bet: ${idea}
|
|
6385
|
+
|
|
6386
|
+
You are shaping a product bet using the **constellation capture pattern**. The bet will live on the Chain with connected elements, tensions, and decisions.
|
|
6387
|
+
|
|
6388
|
+
## Prerequisites
|
|
6389
|
+
1. **Start session** (if not already): \`session action=start\`
|
|
6390
|
+
2. **Orient**: \`orient\` \u2014 loads workspace context and recent activity
|
|
6391
|
+
|
|
6392
|
+
## Shaping Steps (execute in order)
|
|
6393
|
+
|
|
6394
|
+
### Step 1: Problem Framing
|
|
6395
|
+
Discuss the problem with the user. Narrow to a specific, concrete problem statement. Capture the problem in 2\u20133 sentences.
|
|
6396
|
+
|
|
6397
|
+
### Step 2: Set Appetite
|
|
6398
|
+
Agree on appetite (e.g. "2 weeks", "6 weeks"). This bounds the solution.
|
|
6399
|
+
|
|
6400
|
+
### Step 3: Solution Elements
|
|
6401
|
+
Identify 3\u20135 solution elements. For each element that is a distinct feature, capture it:
|
|
6402
|
+
\`\`\`
|
|
6403
|
+
capture collection="features" name="<Element Name>" description="<What this element does>"
|
|
6404
|
+
\`\`\`
|
|
6405
|
+
Note the entryIds for relation linking.
|
|
6406
|
+
|
|
6407
|
+
### Step 4: Rabbit Holes
|
|
6408
|
+
Identify risks. For each risk that is a known tension, capture or link:
|
|
6409
|
+
\`\`\`
|
|
6410
|
+
capture collection="tensions" name="<Risk>" description="<Risk, patch, mitigation>"
|
|
6411
|
+
\`\`\`
|
|
6412
|
+
Or link existing tensions.
|
|
6413
|
+
|
|
6414
|
+
### Step 5: No-Gos
|
|
6415
|
+
Declare what is out of scope. List explicitly.
|
|
6416
|
+
|
|
6417
|
+
### Step 6: Constellation Capture
|
|
6418
|
+
Capture the bet and connect the constellation:
|
|
6419
|
+
\`\`\`
|
|
6420
|
+
capture collection="bets" name="<Bet Name>" description="<Problem statement>" data='{"problem":"...","appetite":"...","elements":"...","rabbitHoles":"...","noGos":"..."}'
|
|
6421
|
+
\`\`\`
|
|
6422
|
+
Then create relations:
|
|
6423
|
+
\`\`\`
|
|
6424
|
+
relations action=batch-create relations=[
|
|
6425
|
+
{from:"<element-entry-id>",to:"<bet-entry-id>",type:"part_of"},
|
|
6426
|
+
{from:"<tension-entry-id>",to:"<element-or-bet-id>",type:"constrains"},
|
|
6427
|
+
...
|
|
6428
|
+
]
|
|
6429
|
+
\`\`\`
|
|
6430
|
+
Use \`graph action=suggest entryId="<bet-id>"\` to discover more connections.
|
|
6431
|
+
|
|
6432
|
+
### Step 7: Present & Commit
|
|
6433
|
+
Show the user the captured bet and constellation. The capture response includes a **View in Studio** link for bets \u2014 share it. Only call \`commit-entry\` when the user confirms.
|
|
6434
|
+
|
|
6435
|
+
**Begin with Step 1.** Ask the user to describe the problem they want to solve.`
|
|
6436
|
+
}
|
|
6437
|
+
}]
|
|
6438
|
+
};
|
|
6439
|
+
}
|
|
6440
|
+
);
|
|
6113
6441
|
server.prompt(
|
|
6114
6442
|
"capture-and-connect",
|
|
6115
6443
|
"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.",
|
|
@@ -6310,7 +6638,7 @@ If ready, ask the user to confirm. If not, suggest specific improvements.
|
|
|
6310
6638
|
}
|
|
6311
6639
|
|
|
6312
6640
|
// src/server.ts
|
|
6313
|
-
var SERVER_VERSION = "0.
|
|
6641
|
+
var SERVER_VERSION = "0.7.0";
|
|
6314
6642
|
var INSTRUCTIONS = [
|
|
6315
6643
|
"Product Brain \u2014 the single source of truth for product knowledge.",
|
|
6316
6644
|
"Terminology, standards, and core data all live here \u2014 no need to check external docs.",
|
|
@@ -6368,6 +6696,13 @@ var INSTRUCTIONS = [
|
|
|
6368
6696
|
"Only call `commit-entry` when the user explicitly confirms (e.g. 'commit', 'looks good', 'yes').",
|
|
6369
6697
|
"This builds trust \u2014 the Chain (main) is SSOT; nothing goes there without user consent.",
|
|
6370
6698
|
"",
|
|
6699
|
+
"Alignment-first: before proposing or building anything, check orient's Workspace Governance",
|
|
6700
|
+
"and Active bets sections. These are constraints, not reference material.",
|
|
6701
|
+
"If the proposal conflicts with a principle, standard, or business rule \u2014 stop, flag the conflict,",
|
|
6702
|
+
"and get explicit user confirmation before proceeding. Do not design around governance.",
|
|
6703
|
+
"If the work is outside the scope of active bets \u2014 stop, name the gap, and ask the user",
|
|
6704
|
+
"to confirm before designing. Do not propose implementation for out-of-scope work without a go-ahead.",
|
|
6705
|
+
"",
|
|
6371
6706
|
"Workspace setup: use `collections action=create` and `collections action=update` to shape the workspace",
|
|
6372
6707
|
"structure with the user. Ask what they need to track; presets are starting points, not fixed.",
|
|
6373
6708
|
"",
|
|
@@ -6406,7 +6741,6 @@ function createProductBrainServer() {
|
|
|
6406
6741
|
registerUsageTools(server);
|
|
6407
6742
|
registerResources(server);
|
|
6408
6743
|
registerPrompts(server);
|
|
6409
|
-
setWriteToolsEnabled(false);
|
|
6410
6744
|
return server;
|
|
6411
6745
|
}
|
|
6412
6746
|
|
|
@@ -6414,4 +6748,4 @@ export {
|
|
|
6414
6748
|
SERVER_VERSION,
|
|
6415
6749
|
createProductBrainServer
|
|
6416
6750
|
};
|
|
6417
|
-
//# sourceMappingURL=chunk-
|
|
6751
|
+
//# sourceMappingURL=chunk-THAYIBTL.js.map
|