@productbrain/mcp 0.0.1-beta.143 → 0.0.1-beta.148
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.
|
@@ -996,6 +996,34 @@ var FIELD_TYPE_DEFAULTS = {
|
|
|
996
996
|
"date": null
|
|
997
997
|
};
|
|
998
998
|
|
|
999
|
+
// src/lib/collectionCache.ts
|
|
1000
|
+
var cachedCollections = null;
|
|
1001
|
+
var cachedBySlug = null;
|
|
1002
|
+
async function getCollections() {
|
|
1003
|
+
if (cachedCollections !== null) return cachedCollections;
|
|
1004
|
+
const result = await mcpQuery("chain.listCollections");
|
|
1005
|
+
cachedCollections = result ?? [];
|
|
1006
|
+
cachedBySlug = new Map(cachedCollections.map((c) => [c.slug, c]));
|
|
1007
|
+
return cachedCollections;
|
|
1008
|
+
}
|
|
1009
|
+
async function getCollectionBySlug(slug) {
|
|
1010
|
+
await getCollections();
|
|
1011
|
+
return cachedBySlug.get(slug);
|
|
1012
|
+
}
|
|
1013
|
+
async function isGoverned(slug) {
|
|
1014
|
+
const col = await getCollectionBySlug(slug);
|
|
1015
|
+
return !!col?.governed;
|
|
1016
|
+
}
|
|
1017
|
+
async function getByNavGroup(navGroup) {
|
|
1018
|
+
const cols = await getCollections();
|
|
1019
|
+
return cols.filter((c) => c.navGroup === navGroup);
|
|
1020
|
+
}
|
|
1021
|
+
async function isWorkPlanningCollection(slug) {
|
|
1022
|
+
const col = await getCollectionBySlug(slug);
|
|
1023
|
+
const layer = col?.thinkingLayer;
|
|
1024
|
+
return layer === "delivery-container" || layer === "delivery-grouping" || layer === "delivery-work";
|
|
1025
|
+
}
|
|
1026
|
+
|
|
999
1027
|
// src/tools/smart-capture.ts
|
|
1000
1028
|
function normalizeMatchText(value) {
|
|
1001
1029
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, " ").replace(/\s+/g, " ").trim();
|
|
@@ -1123,7 +1151,7 @@ function inferStrategyCategory(name, description) {
|
|
|
1123
1151
|
if (bestScore === 0) return "strategy";
|
|
1124
1152
|
return null;
|
|
1125
1153
|
}
|
|
1126
|
-
var
|
|
1154
|
+
var PROFILE_ENRICHMENTS = /* @__PURE__ */ new Map([
|
|
1127
1155
|
["tensions", {
|
|
1128
1156
|
governedDraft: false,
|
|
1129
1157
|
descriptionField: "description",
|
|
@@ -1515,10 +1543,77 @@ var FALLBACK_PROFILE = {
|
|
|
1515
1543
|
COMMON_CHECKS.hasType
|
|
1516
1544
|
]
|
|
1517
1545
|
};
|
|
1546
|
+
async function buildRuntimeProfile(slug) {
|
|
1547
|
+
const col = await getCollectionBySlug(slug);
|
|
1548
|
+
const enrichment = PROFILE_ENRICHMENTS.get(slug);
|
|
1549
|
+
if (!col && !enrichment) return FALLBACK_PROFILE;
|
|
1550
|
+
const governedDraft = col?.governed ?? enrichment?.governedDraft ?? false;
|
|
1551
|
+
let descriptionField = enrichment?.descriptionField ?? "description";
|
|
1552
|
+
if (!enrichment?.descriptionField && col) {
|
|
1553
|
+
const bodyField = col.fields.find(
|
|
1554
|
+
(f) => f.zone === "body" || f.displayHint === "textarea"
|
|
1555
|
+
);
|
|
1556
|
+
if (bodyField) descriptionField = bodyField.key;
|
|
1557
|
+
}
|
|
1558
|
+
let qualityChecks = [
|
|
1559
|
+
COMMON_CHECKS.clearName,
|
|
1560
|
+
COMMON_CHECKS.hasDescription,
|
|
1561
|
+
COMMON_CHECKS.hasRelations,
|
|
1562
|
+
COMMON_CHECKS.hasType
|
|
1563
|
+
];
|
|
1564
|
+
if (enrichment?.qualityChecks) {
|
|
1565
|
+
qualityChecks = enrichment.qualityChecks;
|
|
1566
|
+
} else if (col?.qualityCriteria?.length) {
|
|
1567
|
+
const dbChecks = col.qualityCriteria.map((qc) => ({
|
|
1568
|
+
id: `db-${qc.field}-${qc.rule}`,
|
|
1569
|
+
label: `${qc.field} ${qc.rule === "required" ? "provided" : qc.rule === "min_length" ? `min ${qc.value} chars` : `matches ${qc.value}`}`,
|
|
1570
|
+
check: (ctx) => {
|
|
1571
|
+
const val = ctx.data[qc.field];
|
|
1572
|
+
if (qc.rule === "required") return val !== void 0 && val !== null && val !== "";
|
|
1573
|
+
if (qc.rule === "min_length") return typeof val === "string" && val.length >= Number(qc.value ?? 0);
|
|
1574
|
+
if (qc.rule === "pattern") return typeof val === "string" && new RegExp(qc.value ?? "").test(val);
|
|
1575
|
+
return true;
|
|
1576
|
+
},
|
|
1577
|
+
suggestion: () => `Fill in ${qc.field} (${qc.rule}).`
|
|
1578
|
+
}));
|
|
1579
|
+
qualityChecks = [
|
|
1580
|
+
COMMON_CHECKS.clearName,
|
|
1581
|
+
COMMON_CHECKS.hasDescription,
|
|
1582
|
+
...dbChecks,
|
|
1583
|
+
COMMON_CHECKS.hasRelations,
|
|
1584
|
+
COMMON_CHECKS.hasType
|
|
1585
|
+
];
|
|
1586
|
+
}
|
|
1587
|
+
return {
|
|
1588
|
+
governedDraft,
|
|
1589
|
+
descriptionField,
|
|
1590
|
+
defaults: enrichment?.defaults ?? [],
|
|
1591
|
+
recommendedRelationTypes: enrichment?.recommendedRelationTypes ?? FALLBACK_PROFILE.recommendedRelationTypes,
|
|
1592
|
+
qualityChecks,
|
|
1593
|
+
inferField: enrichment?.inferField
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
var profileCache = null;
|
|
1597
|
+
async function getProfile(slug) {
|
|
1598
|
+
if (!profileCache) profileCache = /* @__PURE__ */ new Map();
|
|
1599
|
+
const cached = profileCache.get(slug);
|
|
1600
|
+
if (cached) return cached;
|
|
1601
|
+
const profile = await buildRuntimeProfile(slug);
|
|
1602
|
+
profileCache.set(slug, profile);
|
|
1603
|
+
return profile;
|
|
1604
|
+
}
|
|
1518
1605
|
function extractSearchTerms(name, description) {
|
|
1519
1606
|
const text = `${name} ${description}`;
|
|
1520
1607
|
return text.replace(/[^\w\s]/g, " ").split(/\s+/).filter((w) => w.length > 3).slice(0, 8).join(" ");
|
|
1521
1608
|
}
|
|
1609
|
+
var hubCollectionCache = null;
|
|
1610
|
+
async function populateHubCache() {
|
|
1611
|
+
if (hubCollectionCache !== null) return;
|
|
1612
|
+
const cols = await getCollections();
|
|
1613
|
+
hubCollectionCache = new Set(
|
|
1614
|
+
cols.filter((c) => c.governanceRole === "steering" || c.thinkingLayer === "strategy").map((c) => c.slug)
|
|
1615
|
+
);
|
|
1616
|
+
}
|
|
1522
1617
|
function computeLinkConfidence(candidate, sourceName, sourceDescription, sourceCollection, candidateCollection) {
|
|
1523
1618
|
const text = `${sourceName} ${sourceDescription}`.toLowerCase();
|
|
1524
1619
|
const candidateName = candidate.name.toLowerCase();
|
|
@@ -1535,8 +1630,7 @@ function computeLinkConfidence(candidate, sourceName, sourceDescription, sourceC
|
|
|
1535
1630
|
if (matchingWords.length > 0) {
|
|
1536
1631
|
reasons.push(`word overlap (${matchingWords.slice(0, 3).join(", ")})`);
|
|
1537
1632
|
}
|
|
1538
|
-
|
|
1539
|
-
if (HUB_COLLECTIONS.has(candidateCollection)) {
|
|
1633
|
+
if (hubCollectionCache?.has(candidateCollection)) {
|
|
1540
1634
|
score += 15;
|
|
1541
1635
|
reasons.push("hub collection");
|
|
1542
1636
|
}
|
|
@@ -1614,11 +1708,11 @@ async function checkEntryQuality(entryId) {
|
|
|
1614
1708
|
quality: { score: 0, maxScore: 10, checks: [] }
|
|
1615
1709
|
};
|
|
1616
1710
|
}
|
|
1617
|
-
const collections = await
|
|
1711
|
+
const collections = await getCollections();
|
|
1618
1712
|
const collMap = /* @__PURE__ */ new Map();
|
|
1619
1713
|
for (const c of collections) collMap.set(c._id, c.slug);
|
|
1620
1714
|
const collectionSlug = collMap.get(entry.collectionId) ?? "unknown";
|
|
1621
|
-
const profile =
|
|
1715
|
+
const profile = await getProfile(collectionSlug);
|
|
1622
1716
|
const relations = await mcpQuery("chain.listEntryRelations", { entryId });
|
|
1623
1717
|
const linksCreated = [];
|
|
1624
1718
|
for (const r of relations) {
|
|
@@ -1659,15 +1753,6 @@ async function checkEntryQuality(entryId) {
|
|
|
1659
1753
|
}
|
|
1660
1754
|
return { text: lines.join("\n"), quality };
|
|
1661
1755
|
}
|
|
1662
|
-
var GOVERNED_COLLECTIONS = /* @__PURE__ */ new Set([
|
|
1663
|
-
"glossary",
|
|
1664
|
-
"business-rules",
|
|
1665
|
-
"principles",
|
|
1666
|
-
"standards",
|
|
1667
|
-
"strategy",
|
|
1668
|
-
"features",
|
|
1669
|
-
"architecture"
|
|
1670
|
-
]);
|
|
1671
1756
|
var AUTO_LINK_CONFIDENCE_THRESHOLD = 35;
|
|
1672
1757
|
var MAX_AUTO_LINKS = 5;
|
|
1673
1758
|
var MAX_SUGGESTIONS = 5;
|
|
@@ -2109,7 +2194,7 @@ function registerSmartCaptureTools(server) {
|
|
|
2109
2194
|
)
|
|
2110
2195
|
};
|
|
2111
2196
|
}
|
|
2112
|
-
const profile =
|
|
2197
|
+
const profile = await getProfile(resolvedCollection);
|
|
2113
2198
|
const col = await mcpQuery("chain.getCollection", { slug: resolvedCollection });
|
|
2114
2199
|
if (!col) {
|
|
2115
2200
|
const displayName = resolvedCollection.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
@@ -2172,7 +2257,8 @@ Or use \`collections action=list\` to see available collections.`
|
|
|
2172
2257
|
}
|
|
2173
2258
|
}
|
|
2174
2259
|
data[profile.descriptionField || "description"] = description;
|
|
2175
|
-
const
|
|
2260
|
+
const colMeta = await getCollectionBySlug(resolvedCollection);
|
|
2261
|
+
const isBetCapture = canonicalKey === "work_package" || colMeta?.defaultCanonicalKey === "work_package";
|
|
2176
2262
|
let entryWarnings = [];
|
|
2177
2263
|
const shouldDecompose = (resolvedCollection === "strategy" || isBetCapture) && description.trim().length > 0;
|
|
2178
2264
|
if (shouldDecompose) {
|
|
@@ -2260,9 +2346,10 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
|
|
|
2260
2346
|
const skipAutoDiscovery = links && links.length > 0;
|
|
2261
2347
|
const searchQuery = extractSearchTerms(name, description);
|
|
2262
2348
|
if (searchQuery && !skipAutoDiscovery) {
|
|
2349
|
+
await populateHubCache();
|
|
2263
2350
|
const [searchResults, allCollections] = await Promise.all([
|
|
2264
2351
|
mcpQuery("chain.searchEntries", { query: searchQuery }),
|
|
2265
|
-
|
|
2352
|
+
getCollections()
|
|
2266
2353
|
]);
|
|
2267
2354
|
const collMap = /* @__PURE__ */ new Map();
|
|
2268
2355
|
for (const c of allCollections) collMap.set(c._id, c.slug);
|
|
@@ -2506,7 +2593,7 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
|
|
|
2506
2593
|
} else {
|
|
2507
2594
|
lines.push(`**${name}** promoted to SSOT on the Chain.`);
|
|
2508
2595
|
}
|
|
2509
|
-
if (
|
|
2596
|
+
if (await isGoverned(resolvedCollection)) {
|
|
2510
2597
|
lines.push(`_Note: \`${resolvedCollection}\` is a governed collection \u2014 ensure this entry has been reviewed._`);
|
|
2511
2598
|
}
|
|
2512
2599
|
} else if (finalStatus === "proposed") {
|
|
@@ -2742,7 +2829,8 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
|
|
|
2742
2829
|
data: `Batch capturing ${entries.length} entries...`,
|
|
2743
2830
|
logger: "product-brain"
|
|
2744
2831
|
});
|
|
2745
|
-
const allCollections = await
|
|
2832
|
+
const allCollections = await getCollections();
|
|
2833
|
+
await populateHubCache();
|
|
2746
2834
|
const collCache = /* @__PURE__ */ new Map();
|
|
2747
2835
|
for (const c of allCollections) collCache.set(c.slug, c);
|
|
2748
2836
|
const collIdToSlug = /* @__PURE__ */ new Map();
|
|
@@ -2779,7 +2867,7 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
|
|
|
2779
2867
|
confidence = resolved.confidence;
|
|
2780
2868
|
confidenceTier = resolved.tier;
|
|
2781
2869
|
}
|
|
2782
|
-
const profile =
|
|
2870
|
+
const profile = await getProfile(resolvedSlug);
|
|
2783
2871
|
const col = collCache.get(resolvedSlug);
|
|
2784
2872
|
if (!col) {
|
|
2785
2873
|
results.push({
|
|
@@ -10141,7 +10229,8 @@ function buildPlannedWork(allEntries) {
|
|
|
10141
10229
|
if (entry.status === "draft") {
|
|
10142
10230
|
result.uncommittedDrafts.push({ name: entry.name, collection });
|
|
10143
10231
|
}
|
|
10144
|
-
|
|
10232
|
+
const isTension = entry.canonicalKey === "tension" || collection === "tensions";
|
|
10233
|
+
if (isTension && entry.workflowStatus === "open") {
|
|
10145
10234
|
result.openTensions.push({ name: entry.name, entryId: entry.entryId ?? entry._id });
|
|
10146
10235
|
}
|
|
10147
10236
|
if (entry.workflowStatus === "in-progress") {
|
|
@@ -10339,7 +10428,7 @@ function buildDocCompletenessSection(collections) {
|
|
|
10339
10428
|
const lines = renderDocCompletenessLines(report);
|
|
10340
10429
|
return { lines, errorCount: report.summary.errors, warningCount: report.summary.warnings };
|
|
10341
10430
|
}
|
|
10342
|
-
function runAlignmentCheck(task, activeBets, taskContextHits) {
|
|
10431
|
+
async function runAlignmentCheck(task, activeBets, taskContextHits) {
|
|
10343
10432
|
const betNames = activeBets.map((b) => b.name);
|
|
10344
10433
|
const taskWords = extractKeywords(task);
|
|
10345
10434
|
const matchingBet = activeBets.find((b) => {
|
|
@@ -10349,9 +10438,12 @@ function runAlignmentCheck(task, activeBets, taskContextHits) {
|
|
|
10349
10438
|
if (matchingBet) {
|
|
10350
10439
|
return { aligned: true, matchedBet: matchingBet.name, matchSource: "active_bet", betNames };
|
|
10351
10440
|
}
|
|
10352
|
-
const betHits =
|
|
10353
|
-
|
|
10354
|
-
|
|
10441
|
+
const betHits = [];
|
|
10442
|
+
for (const e of taskContextHits ?? []) {
|
|
10443
|
+
if (e.collectionSlug && await isWorkPlanningCollection(e.collectionSlug)) {
|
|
10444
|
+
betHits.push(e);
|
|
10445
|
+
}
|
|
10446
|
+
}
|
|
10355
10447
|
if (betHits.length > 0) {
|
|
10356
10448
|
return { aligned: true, matchedBet: betHits[0]?.name ?? null, matchSource: "task_context", betNames };
|
|
10357
10449
|
}
|
|
@@ -11111,12 +11203,16 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors, task) {
|
|
|
11111
11203
|
let wsStandards = [];
|
|
11112
11204
|
let wsBusinessRules = [];
|
|
11113
11205
|
try {
|
|
11114
|
-
|
|
11206
|
+
const govCollections = await getByNavGroup("governance");
|
|
11207
|
+
const govSlugs = govCollections.map((c) => c.slug);
|
|
11208
|
+
for (const slug of govSlugs) {
|
|
11115
11209
|
const entries = await mcpQuery("chain.listEntries", { collectionSlug: slug });
|
|
11116
11210
|
const active = (entries ?? []).filter((e) => e.status === "active");
|
|
11117
|
-
|
|
11118
|
-
|
|
11119
|
-
if (slug === "
|
|
11211
|
+
const colDef = govCollections.find((c) => c.slug === slug);
|
|
11212
|
+
const ck = colDef?.defaultCanonicalKey ?? slug;
|
|
11213
|
+
if (ck === "principle" || slug === "principles") wsPrinciples = [...wsPrinciples, ...active];
|
|
11214
|
+
else if (ck === "standard" || slug === "standards") wsStandards = [...wsStandards, ...active];
|
|
11215
|
+
else if (ck === "business_rule" || slug === "business-rules") wsBusinessRules = [...wsBusinessRules, ...active];
|
|
11120
11216
|
}
|
|
11121
11217
|
} catch {
|
|
11122
11218
|
}
|
|
@@ -13387,7 +13483,7 @@ function registerOrientTool(server) {
|
|
|
13387
13483
|
));
|
|
13388
13484
|
}
|
|
13389
13485
|
if (orientEntries) {
|
|
13390
|
-
const result = runAlignmentCheck(
|
|
13486
|
+
const result = await runAlignmentCheck(
|
|
13391
13487
|
task,
|
|
13392
13488
|
orientEntries.activeBets ?? [],
|
|
13393
13489
|
orientEntries.taskContext?.context
|
|
@@ -15130,4 +15226,4 @@ export {
|
|
|
15130
15226
|
SERVER_VERSION,
|
|
15131
15227
|
createProductBrainServer
|
|
15132
15228
|
};
|
|
15133
|
-
//# sourceMappingURL=chunk-
|
|
15229
|
+
//# sourceMappingURL=chunk-TTZKY2OH.js.map
|