@linktr.ee/arbor-mcp 0.7.0 → 0.8.0
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/index.js +505 -65
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6421,11 +6421,367 @@ var getComponentTool = {
|
|
|
6421
6421
|
handler: handleGetComponent
|
|
6422
6422
|
};
|
|
6423
6423
|
|
|
6424
|
+
// src/lib/decisions.ts
|
|
6425
|
+
var DECISION_LOG_ARTIFACT = "decision-log.json";
|
|
6426
|
+
var decisionSectionSchema = external_exports.object({ heading: external_exports.string(), body: external_exports.string() });
|
|
6427
|
+
var decisionEntrySchema = external_exports.object({
|
|
6428
|
+
id: external_exports.string(),
|
|
6429
|
+
number: external_exports.number(),
|
|
6430
|
+
title: external_exports.string(),
|
|
6431
|
+
date: external_exports.string(),
|
|
6432
|
+
status: external_exports.string(),
|
|
6433
|
+
tags: external_exports.array(external_exports.string()),
|
|
6434
|
+
sections: external_exports.array(decisionSectionSchema).describe("Ordered sections (Context, Decision, \u2026, plus any non-standard headings)")
|
|
6435
|
+
});
|
|
6436
|
+
function coerceEntries(data) {
|
|
6437
|
+
const raw = data ?? {};
|
|
6438
|
+
if (!Array.isArray(raw.entries)) return [];
|
|
6439
|
+
const entries = [];
|
|
6440
|
+
for (const item of raw.entries) {
|
|
6441
|
+
const parsed = decisionEntrySchema.safeParse(item);
|
|
6442
|
+
if (parsed.success) entries.push(parsed.data);
|
|
6443
|
+
}
|
|
6444
|
+
return entries;
|
|
6445
|
+
}
|
|
6446
|
+
async function loadDecisionEntries(load = loadPublishedJson) {
|
|
6447
|
+
const res = await load(DECISION_LOG_ARTIFACT);
|
|
6448
|
+
if (!res.ok) return { ok: false, reason: res.reason };
|
|
6449
|
+
const raw = res.data;
|
|
6450
|
+
if (raw.degraded === true || !Array.isArray(raw.entries)) {
|
|
6451
|
+
return { ok: false, reason: typeof raw.reason === "string" ? raw.reason : "degraded" };
|
|
6452
|
+
}
|
|
6453
|
+
return {
|
|
6454
|
+
ok: true,
|
|
6455
|
+
entries: coerceEntries(res.data),
|
|
6456
|
+
generatedAt: typeof raw.generatedAt === "string" ? raw.generatedAt : null
|
|
6457
|
+
};
|
|
6458
|
+
}
|
|
6459
|
+
function decisionSearchBlob(e) {
|
|
6460
|
+
return [e.title, e.tags.join(" "), ...e.sections.map((s) => `${s.heading} ${s.body}`)].join("\n").toLowerCase();
|
|
6461
|
+
}
|
|
6462
|
+
|
|
6463
|
+
// src/lib/research.ts
|
|
6464
|
+
var RESEARCH_ARTIFACT = "research-corpus.json";
|
|
6465
|
+
var RESEARCH_AREAS = ["component-research", "principle", "guideline"];
|
|
6466
|
+
var AREA_SET = new Set(RESEARCH_AREAS);
|
|
6467
|
+
function asStringArray2(v) {
|
|
6468
|
+
return Array.isArray(v) ? v.filter((s) => typeof s === "string") : [];
|
|
6469
|
+
}
|
|
6470
|
+
function coerceEntry2(raw) {
|
|
6471
|
+
if (!raw || typeof raw !== "object") return null;
|
|
6472
|
+
const r = raw;
|
|
6473
|
+
const path = typeof r.path === "string" ? r.path : "";
|
|
6474
|
+
const area = typeof r.area === "string" ? r.area : "";
|
|
6475
|
+
if (!path || !AREA_SET.has(area)) return null;
|
|
6476
|
+
return {
|
|
6477
|
+
path,
|
|
6478
|
+
area,
|
|
6479
|
+
title: typeof r.title === "string" ? r.title : path,
|
|
6480
|
+
tags: asStringArray2(r.tags),
|
|
6481
|
+
sourceUrls: asStringArray2(r.sourceUrls),
|
|
6482
|
+
crawledAt: typeof r.crawledAt === "string" ? r.crawledAt : null,
|
|
6483
|
+
excerpt: typeof r.excerpt === "string" ? r.excerpt : ""
|
|
6484
|
+
};
|
|
6485
|
+
}
|
|
6486
|
+
async function loadResearch(load = loadPublishedJson) {
|
|
6487
|
+
const res = await load(RESEARCH_ARTIFACT);
|
|
6488
|
+
if (!res.ok) return { ok: false, reason: res.reason };
|
|
6489
|
+
const data = res.data;
|
|
6490
|
+
if (data.degraded === true || !Array.isArray(data.entries)) {
|
|
6491
|
+
return { ok: false, reason: "degraded" };
|
|
6492
|
+
}
|
|
6493
|
+
const entries = [];
|
|
6494
|
+
for (const raw of data.entries) {
|
|
6495
|
+
const entry = coerceEntry2(raw);
|
|
6496
|
+
if (entry) entries.push(entry);
|
|
6497
|
+
}
|
|
6498
|
+
return { ok: true, entries, generatedAt: typeof data.generatedAt === "string" ? data.generatedAt : null };
|
|
6499
|
+
}
|
|
6500
|
+
|
|
6501
|
+
// src/lib/judge.ts
|
|
6502
|
+
var MAX_PRECEDENTS = 4;
|
|
6503
|
+
var MAX_CROSS_SYSTEM = 3;
|
|
6504
|
+
var GUIDANCE = `Write the recommendation as four corners \u2014 Recommendation / Why / Risk it removes / Simplest path (what NOT to build) \u2014 citing ONLY what is in this pack: the component intention, the decision precedents, and the cross-system research entries (cite an entry's sourceUrls for the external-DS side of the "Why"). Verify each precedent or research entry actually addresses the question before citing it; presence in the pack is not proof it is on-topic. If nothing addresses it, abstain: recommend escalating to design review rather than synthesizing a precedent. Mark confidence "grounded" only when a precedent directly addresses the question and sources are fresh; "qualified" when evidence is related, intention-only, or stale; "abstain" when nothing addresses it. Default to recommending less.`;
|
|
6505
|
+
var CROSS_SYSTEM_NOTE_UNAVAILABLE = "Cross-system research could not be loaded on this surface. The in-repo design-system-judgment skill reads docs/design-system/research/** and guidelines/** directly; over MCP, cite Arbor precedent only and treat the cross-system corner as unavailable.";
|
|
6506
|
+
var CROSS_SYSTEM_NOTE_AVAILABLE = `Cross-system evidence from Arbor's research corpus \u2014 cite an entry's sourceUrls (external-DS docs) for the "Why" cross-system corner. Verify each is on-topic; an empty list means the corpus was searched and nothing matched the question.`;
|
|
6507
|
+
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
6508
|
+
"should",
|
|
6509
|
+
"could",
|
|
6510
|
+
"would",
|
|
6511
|
+
"what",
|
|
6512
|
+
"which",
|
|
6513
|
+
"when",
|
|
6514
|
+
"where",
|
|
6515
|
+
"does",
|
|
6516
|
+
"with",
|
|
6517
|
+
"this",
|
|
6518
|
+
"that",
|
|
6519
|
+
"have",
|
|
6520
|
+
"from",
|
|
6521
|
+
"into",
|
|
6522
|
+
"about",
|
|
6523
|
+
"need",
|
|
6524
|
+
"want",
|
|
6525
|
+
"make",
|
|
6526
|
+
"using",
|
|
6527
|
+
"there",
|
|
6528
|
+
"their",
|
|
6529
|
+
"them",
|
|
6530
|
+
"they",
|
|
6531
|
+
"will",
|
|
6532
|
+
"just",
|
|
6533
|
+
"also",
|
|
6534
|
+
"than",
|
|
6535
|
+
"then",
|
|
6536
|
+
"some",
|
|
6537
|
+
"such",
|
|
6538
|
+
"only",
|
|
6539
|
+
"more",
|
|
6540
|
+
"most",
|
|
6541
|
+
"over",
|
|
6542
|
+
"component",
|
|
6543
|
+
"arbor"
|
|
6544
|
+
]);
|
|
6545
|
+
function buildTerms(question, componentName) {
|
|
6546
|
+
const out = /* @__PURE__ */ new Set();
|
|
6547
|
+
const push = (s) => {
|
|
6548
|
+
const t = s.toLowerCase().trim();
|
|
6549
|
+
if (t.length >= 4 && !STOPWORDS.has(t)) out.add(t);
|
|
6550
|
+
};
|
|
6551
|
+
for (const tok of question.split(/[^a-zA-Z0-9]+/)) push(tok);
|
|
6552
|
+
if (componentName) for (const tok of componentName.split(/[^a-zA-Z0-9]+/)) push(tok);
|
|
6553
|
+
return [...out];
|
|
6554
|
+
}
|
|
6555
|
+
function toPrecedent(e, relevance) {
|
|
6556
|
+
return {
|
|
6557
|
+
id: e.id,
|
|
6558
|
+
title: e.title,
|
|
6559
|
+
date: e.date,
|
|
6560
|
+
status: e.status,
|
|
6561
|
+
tags: e.tags,
|
|
6562
|
+
relevance,
|
|
6563
|
+
sections: e.sections.map((s) => ({ heading: s.heading, body: s.body }))
|
|
6564
|
+
};
|
|
6565
|
+
}
|
|
6566
|
+
var AREA_RANK = { "component-research": 0, principle: 1, guideline: 2 };
|
|
6567
|
+
function matchResearch(entries, terms, max) {
|
|
6568
|
+
if (!terms.length) return [];
|
|
6569
|
+
return entries.map((e) => {
|
|
6570
|
+
const haystack = `${e.title}
|
|
6571
|
+
${e.tags.join(" ")}
|
|
6572
|
+
${e.path}
|
|
6573
|
+
${e.excerpt}`.toLowerCase();
|
|
6574
|
+
const score = terms.reduce((n, t) => haystack.includes(t) ? n + 1 : n, 0);
|
|
6575
|
+
return { e, score };
|
|
6576
|
+
}).filter((x) => x.score > 0).sort(
|
|
6577
|
+
(a, b) => b.score - a.score || (AREA_RANK[a.e.area] ?? 9) - (AREA_RANK[b.e.area] ?? 9) || a.e.path.localeCompare(b.e.path)
|
|
6578
|
+
).slice(0, max).map(({ e }) => ({ path: e.path, title: e.title, excerpt: e.excerpt, sourceUrls: e.sourceUrls }));
|
|
6579
|
+
}
|
|
6580
|
+
async function judge(input, deps = {}) {
|
|
6581
|
+
const loadInt = deps.loadIntentions ?? (() => loadIntentions());
|
|
6582
|
+
const loadDec = deps.loadDecisions ?? (() => loadDecisionEntries());
|
|
6583
|
+
const loadRes = deps.loadResearch ?? (() => loadResearch());
|
|
6584
|
+
const maxPrecedents = deps.maxPrecedents ?? MAX_PRECEDENTS;
|
|
6585
|
+
const maxCrossSystem = deps.maxCrossSystem ?? MAX_CROSS_SYSTEM;
|
|
6586
|
+
const question = input.question;
|
|
6587
|
+
const componentName = input.component?.trim() || null;
|
|
6588
|
+
const terms = buildTerms(question, componentName);
|
|
6589
|
+
const intRes = await loadInt();
|
|
6590
|
+
let component = null;
|
|
6591
|
+
let componentSource;
|
|
6592
|
+
let intentionsGeneratedAt = null;
|
|
6593
|
+
let linkedDecisionIds = [];
|
|
6594
|
+
if (!intRes.ok) {
|
|
6595
|
+
componentSource = "unavailable";
|
|
6596
|
+
} else {
|
|
6597
|
+
intentionsGeneratedAt = intRes.generatedAt;
|
|
6598
|
+
const match = componentName ? intRes.byKey.get(normalizeComponentKey(componentName)) : void 0;
|
|
6599
|
+
if (match) {
|
|
6600
|
+
component = {
|
|
6601
|
+
name: match.name,
|
|
6602
|
+
status: match.status,
|
|
6603
|
+
shippedVersion: match.shippedVersion,
|
|
6604
|
+
useWhen: match.useWhen,
|
|
6605
|
+
notWhen: match.notWhen,
|
|
6606
|
+
disambiguateFrom: match.disambiguateFrom.map((d) => ({ component: d.component, distinction: d.distinction })),
|
|
6607
|
+
guideline: match.guideline,
|
|
6608
|
+
implementation: match.implementation
|
|
6609
|
+
};
|
|
6610
|
+
linkedDecisionIds = match.decisions;
|
|
6611
|
+
componentSource = "matched";
|
|
6612
|
+
} else {
|
|
6613
|
+
componentSource = "absent";
|
|
6614
|
+
}
|
|
6615
|
+
}
|
|
6616
|
+
const decRes = await loadDec();
|
|
6617
|
+
const decisionLogAvailable = decRes.ok;
|
|
6618
|
+
let decisionLogGeneratedAt = null;
|
|
6619
|
+
let decisionLogStale = true;
|
|
6620
|
+
const precedents = [];
|
|
6621
|
+
const sources = [];
|
|
6622
|
+
if (intRes.ok) sources.push("component-intentions.json");
|
|
6623
|
+
if (decRes.ok) {
|
|
6624
|
+
decisionLogGeneratedAt = decRes.generatedAt;
|
|
6625
|
+
decisionLogStale = decRes.generatedAt ? freshness(decRes.generatedAt, { now: deps.now }).stale : true;
|
|
6626
|
+
sources.push("decision-log.json");
|
|
6627
|
+
const byId = /* @__PURE__ */ new Map();
|
|
6628
|
+
for (const e of decRes.entries) byId.set(e.id.toUpperCase(), e);
|
|
6629
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6630
|
+
for (const rawId of linkedDecisionIds) {
|
|
6631
|
+
const e = byId.get(rawId.toUpperCase());
|
|
6632
|
+
if (e && !seen.has(e.id)) {
|
|
6633
|
+
seen.add(e.id);
|
|
6634
|
+
precedents.push(toPrecedent(e, "component"));
|
|
6635
|
+
}
|
|
6636
|
+
}
|
|
6637
|
+
if (terms.length) {
|
|
6638
|
+
const matches = decRes.entries.filter((e) => !seen.has(e.id)).filter((e) => {
|
|
6639
|
+
const blob = decisionSearchBlob(e);
|
|
6640
|
+
return terms.some((t) => blob.includes(t));
|
|
6641
|
+
}).sort((a, b) => b.number - a.number);
|
|
6642
|
+
for (const e of matches) {
|
|
6643
|
+
if (precedents.length >= maxPrecedents) break;
|
|
6644
|
+
seen.add(e.id);
|
|
6645
|
+
precedents.push(toPrecedent(e, "keyword"));
|
|
6646
|
+
}
|
|
6647
|
+
}
|
|
6648
|
+
if (precedents.length > maxPrecedents) precedents.length = maxPrecedents;
|
|
6649
|
+
for (const p of precedents) sources.push(`DL: ${p.id}`);
|
|
6650
|
+
}
|
|
6651
|
+
const resRes = await loadRes();
|
|
6652
|
+
let crossSystem;
|
|
6653
|
+
if (resRes.ok) {
|
|
6654
|
+
const entries = matchResearch(resRes.entries, terms, maxCrossSystem);
|
|
6655
|
+
crossSystem = { available: true, note: CROSS_SYSTEM_NOTE_AVAILABLE, entries };
|
|
6656
|
+
sources.push("research-corpus.json");
|
|
6657
|
+
for (const e of entries) sources.push(`research: ${e.path}`);
|
|
6658
|
+
} else {
|
|
6659
|
+
crossSystem = { available: false, note: CROSS_SYSTEM_NOTE_UNAVAILABLE, entries: [] };
|
|
6660
|
+
}
|
|
6661
|
+
const hasComponentPrecedent = precedents.some((p) => p.relevance === "component");
|
|
6662
|
+
const deprecatedWithAlt = component?.status === "deprecated" && component.disambiguateFrom.length > 0;
|
|
6663
|
+
const unresolvedLinks = linkedDecisionIds.length > 0 && !hasComponentPrecedent;
|
|
6664
|
+
let evidence;
|
|
6665
|
+
let evidenceReason;
|
|
6666
|
+
if (!component && precedents.length === 0) {
|
|
6667
|
+
evidence = "none";
|
|
6668
|
+
evidenceReason = componentSource === "unavailable" || !decisionLogAvailable ? "Grounding sources were unavailable \u2014 could not determine precedent. Do not assume none exists." : "No matching component intention and no decision precedent \u2014 nothing to ground a recommendation on.";
|
|
6669
|
+
} else if (hasComponentPrecedent || deprecatedWithAlt) {
|
|
6670
|
+
evidence = "direct";
|
|
6671
|
+
if (hasComponentPrecedent) {
|
|
6672
|
+
const ids = precedents.filter((p) => p.relevance === "component").map((p) => p.id).join(", ");
|
|
6673
|
+
evidenceReason = `Decision precedent linked directly to ${component?.name ?? "the component"} (${ids}).`;
|
|
6674
|
+
if (decisionLogStale) evidenceReason += " Note: the decision-log snapshot is stale \u2014 qualify accordingly.";
|
|
6675
|
+
} else {
|
|
6676
|
+
evidenceReason = `${component?.name} is deprecated with a named replacement in its intention.`;
|
|
6677
|
+
}
|
|
6678
|
+
} else if (unresolvedLinks) {
|
|
6679
|
+
evidence = "none";
|
|
6680
|
+
const where = decisionLogAvailable ? "they are absent from the current decision-log snapshot" : "the decision log is unavailable";
|
|
6681
|
+
const kw = precedents.length ? ` ${precedents.length} keyword-matched precedent(s) are present but secondary \u2014 verify each is on-topic.` : "";
|
|
6682
|
+
evidenceReason = `${component?.name ?? "The component"} is governed by linked decision(s) ${linkedDecisionIds.join(", ")}, but ${where} \u2014 treat as degraded; do not assume no precedent exists.${kw}`;
|
|
6683
|
+
} else {
|
|
6684
|
+
evidence = "related";
|
|
6685
|
+
evidenceReason = component ? `Component intention matched (status: ${component.status}) but no decision is linked to it${precedents.length ? `; ${precedents.length} keyword-matched precedent(s) may or may not be on-topic` : ""}.` : `${precedents.length} keyword-matched precedent(s) found; verify each is on-topic.`;
|
|
6686
|
+
}
|
|
6687
|
+
return {
|
|
6688
|
+
question,
|
|
6689
|
+
component,
|
|
6690
|
+
componentSource,
|
|
6691
|
+
precedents,
|
|
6692
|
+
evidence,
|
|
6693
|
+
evidenceReason,
|
|
6694
|
+
crossSystem,
|
|
6695
|
+
freshness: {
|
|
6696
|
+
decisionLog: { generatedAt: decisionLogGeneratedAt, stale: decisionLogStale, available: decisionLogAvailable },
|
|
6697
|
+
intentions: { generatedAt: intentionsGeneratedAt, available: intRes.ok }
|
|
6698
|
+
},
|
|
6699
|
+
sources,
|
|
6700
|
+
guidance: GUIDANCE
|
|
6701
|
+
};
|
|
6702
|
+
}
|
|
6703
|
+
|
|
6704
|
+
// src/tools/judge.ts
|
|
6705
|
+
var TOOL_NAME5 = "arbor_judge";
|
|
6706
|
+
var TOOL_TITLE5 = "Make a grounded Arbor design-system judgment";
|
|
6707
|
+
var TOOL_DESCRIPTION5 = `Make a grounded design-system judgment call for Arbor. Given a decision or question (and optionally the component it concerns), this assembles the grounding in ONE call \u2014 the component's intention (status, when-to-use/not, disambiguation), the decision-log precedents linked to it, and keyword-matched decisions \u2014 and reports how much grounding exists (direct | related | none) plus a cite-or-abstain instruction. It does NOT write the recommendation; it returns real precedent so you can crystallize a Dieter-Rams answer (Recommendation / Why / Risk it removes / Simplest path) citing only what's in the pack, or abstain when nothing addresses the question. Read-only. Cross-system research is not on this surface yet (v1.x) \u2014 cite Arbor precedent only. Use for "should we\u2026", "which component\u2026", "is X overkill", "is there precedent for\u2026".`;
|
|
6708
|
+
var inputSchema5 = external_exports.object({
|
|
6709
|
+
question: external_exports.string().trim().min(1).max(500).describe('The design-system decision or question, e.g. "Should FileUpload also render the selected-file list?"'),
|
|
6710
|
+
component: external_exports.string().trim().min(1).max(100).optional().describe('The Arbor component the question concerns, if any (case-insensitive), e.g. "FileUpload" or "file-upload"')
|
|
6711
|
+
}).strict();
|
|
6712
|
+
var precedentSchema = external_exports.object({
|
|
6713
|
+
id: external_exports.string(),
|
|
6714
|
+
title: external_exports.string(),
|
|
6715
|
+
date: external_exports.string(),
|
|
6716
|
+
status: external_exports.string(),
|
|
6717
|
+
tags: external_exports.array(external_exports.string()),
|
|
6718
|
+
relevance: external_exports.enum(["component", "keyword"]).describe("'component' = linked via the component's intention.decisions[]; 'keyword' = matched the question"),
|
|
6719
|
+
sections: external_exports.array(external_exports.object({ heading: external_exports.string(), body: external_exports.string() }))
|
|
6720
|
+
});
|
|
6721
|
+
var componentSchema = external_exports.object({
|
|
6722
|
+
name: external_exports.string(),
|
|
6723
|
+
status: external_exports.enum(["shipped", "in-progress", "design-only", "deprecated", "researched-not-shipped"]),
|
|
6724
|
+
shippedVersion: external_exports.string().nullable(),
|
|
6725
|
+
useWhen: external_exports.array(external_exports.string()),
|
|
6726
|
+
notWhen: external_exports.array(external_exports.string()),
|
|
6727
|
+
disambiguateFrom: external_exports.array(external_exports.object({ component: external_exports.string(), distinction: external_exports.string() })),
|
|
6728
|
+
guideline: external_exports.string().nullable(),
|
|
6729
|
+
implementation: external_exports.string().nullable()
|
|
6730
|
+
});
|
|
6731
|
+
var outputSchema5 = external_exports.object({
|
|
6732
|
+
question: external_exports.string(),
|
|
6733
|
+
component: componentSchema.nullable().describe("The matched component intention, or null"),
|
|
6734
|
+
componentSource: external_exports.enum(["matched", "absent", "unavailable"]),
|
|
6735
|
+
precedents: external_exports.array(precedentSchema).describe("Real decision-log entries actually retrieved \u2014 the only DLs safe to cite"),
|
|
6736
|
+
evidence: external_exports.enum(["direct", "related", "none"]).describe(
|
|
6737
|
+
`How much grounding is available (NOT the final answer confidence \u2014 verify each precedent is on-topic). 'none' also covers degraded/unavailable sources, including a component whose linked decisions couldn't be loaded \u2014 it never means "no precedent exists".`
|
|
6738
|
+
),
|
|
6739
|
+
evidenceReason: external_exports.string(),
|
|
6740
|
+
crossSystem: external_exports.object({
|
|
6741
|
+
available: external_exports.boolean(),
|
|
6742
|
+
note: external_exports.string(),
|
|
6743
|
+
entries: external_exports.array(external_exports.object({ path: external_exports.string(), title: external_exports.string(), excerpt: external_exports.string(), sourceUrls: external_exports.array(external_exports.string()) })).describe(
|
|
6744
|
+
'Cross-system research entries to cite for the "Why" corner \u2014 each carries external-DS sourceUrls; empty when the corpus is unavailable or nothing matched'
|
|
6745
|
+
)
|
|
6746
|
+
}),
|
|
6747
|
+
freshness: external_exports.object({
|
|
6748
|
+
decisionLog: external_exports.object({ generatedAt: external_exports.string().nullable(), stale: external_exports.boolean(), available: external_exports.boolean() }),
|
|
6749
|
+
intentions: external_exports.object({ generatedAt: external_exports.string().nullable(), available: external_exports.boolean() })
|
|
6750
|
+
}),
|
|
6751
|
+
sources: external_exports.array(external_exports.string()).describe("What was actually read \u2014 the audit trail for cite-or-abstain"),
|
|
6752
|
+
guidance: external_exports.string().describe("How to turn this pack into a recommendation (or abstain)")
|
|
6753
|
+
});
|
|
6754
|
+
var annotations5 = {
|
|
6755
|
+
readOnlyHint: true,
|
|
6756
|
+
idempotentHint: true,
|
|
6757
|
+
openWorldHint: true,
|
|
6758
|
+
destructiveHint: false
|
|
6759
|
+
};
|
|
6760
|
+
function summarize(pack) {
|
|
6761
|
+
const head = pack.evidence === "none" ? "No direct grounding" : pack.evidence === "direct" ? "Direct precedent available" : "Related evidence only";
|
|
6762
|
+
const comp = pack.component ? ` ${pack.component.name} (${pack.component.status}).` : "";
|
|
6763
|
+
const dls = pack.precedents.length ? ` Precedents: ${pack.precedents.map((p) => p.id).join(", ")}.` : "";
|
|
6764
|
+
const stale = pack.freshness.decisionLog.stale && pack.freshness.decisionLog.available ? " \u26A0 decision-log snapshot is stale." : "";
|
|
6765
|
+
return `${head} for: "${pack.question}".${comp}${dls} ${pack.evidenceReason}${stale}
|
|
6766
|
+
|
|
6767
|
+
${pack.guidance}`;
|
|
6768
|
+
}
|
|
6769
|
+
async function handleJudge(args, deps = {}) {
|
|
6770
|
+
const pack = await judge({ question: args.question, component: args.component }, deps);
|
|
6771
|
+
const out = pack;
|
|
6772
|
+
return { content: [{ type: "text", text: summarize(pack) }], structuredContent: out };
|
|
6773
|
+
}
|
|
6774
|
+
var judgeTool = {
|
|
6775
|
+
name: TOOL_NAME5,
|
|
6776
|
+
config: { title: TOOL_TITLE5, description: TOOL_DESCRIPTION5, inputSchema: inputSchema5, outputSchema: outputSchema5, annotations: annotations5 },
|
|
6777
|
+
handler: handleJudge
|
|
6778
|
+
};
|
|
6779
|
+
|
|
6424
6780
|
// src/tools/list-versions.ts
|
|
6425
|
-
var
|
|
6426
|
-
var
|
|
6427
|
-
var
|
|
6428
|
-
var
|
|
6781
|
+
var TOOL_NAME6 = "arbor_list_versions";
|
|
6782
|
+
var TOOL_TITLE6 = "List Arbor design system versions";
|
|
6783
|
+
var TOOL_DESCRIPTION6 = "List the version history of the Arbor design system from Supernova (id, name, semver, created date, read-only flag, draft / latest-released markers) via the Arbor federation proxy, plus the committed release manifest (npm release \u2194 Supernova version). Read-only metadata. Does NOT return token values (use the compiled CSS / registry) and does NOT change anything.";
|
|
6784
|
+
var inputSchema6 = external_exports.object({}).strict();
|
|
6429
6785
|
var versionSchema = external_exports.object({
|
|
6430
6786
|
id: external_exports.string(),
|
|
6431
6787
|
name: external_exports.string(),
|
|
@@ -6442,7 +6798,7 @@ var manifestReleaseSchema = external_exports.object({
|
|
|
6442
6798
|
supernovaVersionId: external_exports.string().nullable().describe("The frozen Supernova version id this release was cut as (null until the cutter + stamper run \u2014 later phases)"),
|
|
6443
6799
|
supernovaStatus: external_exports.string().nullable()
|
|
6444
6800
|
});
|
|
6445
|
-
var
|
|
6801
|
+
var outputSchema6 = external_exports.object({
|
|
6446
6802
|
source: external_exports.enum(["remote", "unavailable"]).describe("'remote' = Supernova versions fetched; 'unavailable' = proxy fetch failed"),
|
|
6447
6803
|
total: external_exports.number().describe("Number of Supernova versions returned"),
|
|
6448
6804
|
versions: external_exports.array(versionSchema).describe("Supernova versions, in the order returned"),
|
|
@@ -6454,7 +6810,7 @@ var outputSchema5 = external_exports.object({
|
|
|
6454
6810
|
}).describe("The committed release manifest (releases.json) mapping npm releases \u2194 Supernova versions"),
|
|
6455
6811
|
detail: external_exports.string().nullable().describe("Degraded reason when source is unavailable")
|
|
6456
6812
|
});
|
|
6457
|
-
var
|
|
6813
|
+
var annotations6 = { readOnlyHint: true, idempotentHint: true, openWorldHint: true, destructiveHint: false };
|
|
6458
6814
|
var SEMVER_RE = /^\d+\.\d+\.\d+(?:[-+].*)?$/;
|
|
6459
6815
|
function extractList(data) {
|
|
6460
6816
|
const wrapped = data?.result?.designSystemVersions;
|
|
@@ -6545,19 +6901,18 @@ async function handleListVersions(_args, deps = {}) {
|
|
|
6545
6901
|
return { content: [{ type: "text", text: `${versionsLine} ${manifestLine}` }], structuredContent: out };
|
|
6546
6902
|
}
|
|
6547
6903
|
var listVersionsTool = {
|
|
6548
|
-
name:
|
|
6549
|
-
config: { title:
|
|
6904
|
+
name: TOOL_NAME6,
|
|
6905
|
+
config: { title: TOOL_TITLE6, description: TOOL_DESCRIPTION6, inputSchema: inputSchema6, outputSchema: outputSchema6, annotations: annotations6 },
|
|
6550
6906
|
handler: handleListVersions
|
|
6551
6907
|
};
|
|
6552
6908
|
|
|
6553
6909
|
// src/tools/lookup-decision.ts
|
|
6554
|
-
var ARTIFACT2 = "decision-log.json";
|
|
6555
6910
|
var CHARACTER_LIMIT = 25e3;
|
|
6556
6911
|
var DEFAULT_LIMIT = 5;
|
|
6557
|
-
var
|
|
6558
|
-
var
|
|
6559
|
-
var
|
|
6560
|
-
var
|
|
6912
|
+
var TOOL_NAME7 = "arbor_lookup_decision";
|
|
6913
|
+
var TOOL_TITLE7 = "Look up an Arbor decision-log entry";
|
|
6914
|
+
var TOOL_DESCRIPTION7 = 'Look up Arbor decision-log entries \u2014 the record of WHY design-system decisions were made \u2014 by id (e.g. "DL-067"), tag, or keyword. Keyword search spans the title, tags, and every section (context, decision, outcome, rationale, lessons, references, \u2026). Results are newest-first and paginated. Use this for "why did Arbor decide X?" / "what resolved <problem>?". Does NOT return component metadata or token values (those are separate); it is the decision/rationale record only.';
|
|
6915
|
+
var inputSchema7 = external_exports.object({
|
|
6561
6916
|
id: external_exports.string().optional().describe('A specific decision id, e.g. "DL-067" (a bare "67" also resolves)'),
|
|
6562
6917
|
tag: external_exports.string().optional().describe('Filter to entries carrying this tag (case-insensitive), e.g. "tokens"'),
|
|
6563
6918
|
query: external_exports.string().optional().describe("Keyword search across title, tags, and all sections (case-insensitive)"),
|
|
@@ -6565,17 +6920,7 @@ var inputSchema6 = external_exports.object({
|
|
|
6565
6920
|
offset: external_exports.number().int().min(0).default(0).describe("Entries to skip, for pagination"),
|
|
6566
6921
|
response_format: external_exports.enum(["markdown", "json"]).default("markdown").describe("Rendering of the text content: 'markdown' (human) or 'json' (machine)")
|
|
6567
6922
|
}).strict();
|
|
6568
|
-
var
|
|
6569
|
-
var decisionEntrySchema = external_exports.object({
|
|
6570
|
-
id: external_exports.string(),
|
|
6571
|
-
number: external_exports.number(),
|
|
6572
|
-
title: external_exports.string(),
|
|
6573
|
-
date: external_exports.string(),
|
|
6574
|
-
status: external_exports.string(),
|
|
6575
|
-
tags: external_exports.array(external_exports.string()),
|
|
6576
|
-
sections: external_exports.array(sectionSchema).describe("Ordered sections (Context, Decision, \u2026, plus any non-standard headings)")
|
|
6577
|
-
});
|
|
6578
|
-
var outputSchema6 = external_exports.object({
|
|
6923
|
+
var outputSchema7 = external_exports.object({
|
|
6579
6924
|
total: external_exports.number().describe("Total entries matching the filter (before pagination)"),
|
|
6580
6925
|
count: external_exports.number().describe("Entries actually returned (after pagination + character budget)"),
|
|
6581
6926
|
offset: external_exports.number(),
|
|
@@ -6586,25 +6931,12 @@ var outputSchema6 = external_exports.object({
|
|
|
6586
6931
|
stale: external_exports.boolean().describe("True when the snapshot is older than the freshness threshold (or unavailable)"),
|
|
6587
6932
|
entries: external_exports.array(decisionEntrySchema)
|
|
6588
6933
|
});
|
|
6589
|
-
var
|
|
6934
|
+
var annotations7 = {
|
|
6590
6935
|
readOnlyHint: true,
|
|
6591
6936
|
idempotentHint: true,
|
|
6592
6937
|
openWorldHint: true,
|
|
6593
6938
|
destructiveHint: false
|
|
6594
6939
|
};
|
|
6595
|
-
function asEntries(data) {
|
|
6596
|
-
const raw = data ?? {};
|
|
6597
|
-
if (!Array.isArray(raw.entries)) return [];
|
|
6598
|
-
const entries = [];
|
|
6599
|
-
for (const item of raw.entries) {
|
|
6600
|
-
const parsed = decisionEntrySchema.safeParse(item);
|
|
6601
|
-
if (parsed.success) entries.push(parsed.data);
|
|
6602
|
-
}
|
|
6603
|
-
return entries;
|
|
6604
|
-
}
|
|
6605
|
-
function searchBlob(e) {
|
|
6606
|
-
return [e.title, e.tags.join(" "), ...e.sections.map((s) => `${s.heading} ${s.body}`)].join("\n").toLowerCase();
|
|
6607
|
-
}
|
|
6608
6940
|
function renderEntry(e) {
|
|
6609
6941
|
const lines = [
|
|
6610
6942
|
`## ${e.id}: ${e.title}`,
|
|
@@ -6639,14 +6971,10 @@ async function handleLookupDecision(args, deps = {}) {
|
|
|
6639
6971
|
const load = deps.load ?? ((f) => loadPublishedJson(f));
|
|
6640
6972
|
const charLimit = deps.charLimit ?? CHARACTER_LIMIT;
|
|
6641
6973
|
const { id, tag, query, limit = DEFAULT_LIMIT, offset = 0, response_format = "markdown" } = args;
|
|
6642
|
-
const
|
|
6643
|
-
if (!
|
|
6644
|
-
const
|
|
6645
|
-
|
|
6646
|
-
return unavailable(offset, typeof raw.reason === "string" ? raw.reason : "degraded");
|
|
6647
|
-
}
|
|
6648
|
-
const all = asEntries(result.data);
|
|
6649
|
-
const generatedAt = typeof raw.generatedAt === "string" ? raw.generatedAt : null;
|
|
6974
|
+
const res = await loadDecisionEntries(load);
|
|
6975
|
+
if (!res.ok) return unavailable(offset, res.reason);
|
|
6976
|
+
const all = res.entries;
|
|
6977
|
+
const generatedAt = res.generatedAt;
|
|
6650
6978
|
const stale = generatedAt ? freshness(generatedAt, { now: deps.now }).stale : true;
|
|
6651
6979
|
let matches;
|
|
6652
6980
|
if (id !== void 0) {
|
|
@@ -6657,7 +6985,7 @@ async function handleLookupDecision(args, deps = {}) {
|
|
|
6657
6985
|
const queryLc = query?.toLowerCase();
|
|
6658
6986
|
matches = all.filter((e) => {
|
|
6659
6987
|
if (tagLc && !e.tags.some((t) => t.toLowerCase() === tagLc)) return false;
|
|
6660
|
-
if (queryLc && !
|
|
6988
|
+
if (queryLc && !decisionSearchBlob(e).includes(queryLc)) return false;
|
|
6661
6989
|
return true;
|
|
6662
6990
|
});
|
|
6663
6991
|
}
|
|
@@ -6702,13 +7030,13 @@ ${included.map(renderEntry).join("\n\n---\n\n")}${note}`;
|
|
|
6702
7030
|
};
|
|
6703
7031
|
}
|
|
6704
7032
|
var lookupDecisionTool = {
|
|
6705
|
-
name:
|
|
7033
|
+
name: TOOL_NAME7,
|
|
6706
7034
|
config: {
|
|
6707
|
-
title:
|
|
6708
|
-
description:
|
|
6709
|
-
inputSchema:
|
|
6710
|
-
outputSchema:
|
|
6711
|
-
annotations:
|
|
7035
|
+
title: TOOL_TITLE7,
|
|
7036
|
+
description: TOOL_DESCRIPTION7,
|
|
7037
|
+
inputSchema: inputSchema7,
|
|
7038
|
+
outputSchema: outputSchema7,
|
|
7039
|
+
annotations: annotations7
|
|
6712
7040
|
},
|
|
6713
7041
|
handler: handleLookupDecision
|
|
6714
7042
|
};
|
|
@@ -7162,6 +7490,88 @@ var compositions_default = [
|
|
|
7162
7490
|
<DropdownMenuItem className="text-danger">Delete</DropdownMenuItem>
|
|
7163
7491
|
</DropdownMenuContent>
|
|
7164
7492
|
</DropdownMenu>`
|
|
7493
|
+
},
|
|
7494
|
+
// ── Menu ──
|
|
7495
|
+
{
|
|
7496
|
+
group: "Menu",
|
|
7497
|
+
name: "Menu",
|
|
7498
|
+
code: `<Menu>
|
|
7499
|
+
<MenuTrigger asChild>
|
|
7500
|
+
<Button variant="outline">Options</Button>
|
|
7501
|
+
</MenuTrigger>
|
|
7502
|
+
<MenuContent>
|
|
7503
|
+
<MenuSectionHeading>Actions</MenuSectionHeading>
|
|
7504
|
+
<MenuItem>Edit</MenuItem>
|
|
7505
|
+
<MenuItem>Duplicate</MenuItem>
|
|
7506
|
+
<MenuItem>Share</MenuItem>
|
|
7507
|
+
<MenuDivider />
|
|
7508
|
+
<MenuItem destructive>Delete</MenuItem>
|
|
7509
|
+
</MenuContent>
|
|
7510
|
+
</Menu>`
|
|
7511
|
+
},
|
|
7512
|
+
// ── MenuItem ──
|
|
7513
|
+
// Maps figma-mappings `MenuItem` (Menu Item node 9417-8883, type=default).
|
|
7514
|
+
{
|
|
7515
|
+
group: "MenuItem",
|
|
7516
|
+
name: "MenuItem",
|
|
7517
|
+
code: `<Menu>
|
|
7518
|
+
<MenuTrigger asChild>
|
|
7519
|
+
<Button variant="outline">Options</Button>
|
|
7520
|
+
</MenuTrigger>
|
|
7521
|
+
<MenuContent>
|
|
7522
|
+
<MenuItem>Edit</MenuItem>
|
|
7523
|
+
<MenuItem description="Manage who can access">Sharing</MenuItem>
|
|
7524
|
+
<MenuItem destructive>Delete</MenuItem>
|
|
7525
|
+
</MenuContent>
|
|
7526
|
+
</Menu>`
|
|
7527
|
+
},
|
|
7528
|
+
// ── MenuCheckboxItem ──
|
|
7529
|
+
// Maps figma-mappings `MenuCheckboxItem` (Menu Item node 9417-8883, type=checkbox).
|
|
7530
|
+
{
|
|
7531
|
+
group: "MenuCheckboxItem",
|
|
7532
|
+
name: "MenuCheckboxItem",
|
|
7533
|
+
code: `<Menu>
|
|
7534
|
+
<MenuTrigger asChild>
|
|
7535
|
+
<Button variant="outline">View options</Button>
|
|
7536
|
+
</MenuTrigger>
|
|
7537
|
+
<MenuContent>
|
|
7538
|
+
<MenuCheckboxItem checked>Show analytics</MenuCheckboxItem>
|
|
7539
|
+
<MenuCheckboxItem>Show archived</MenuCheckboxItem>
|
|
7540
|
+
</MenuContent>
|
|
7541
|
+
</Menu>`
|
|
7542
|
+
},
|
|
7543
|
+
// ── MenuRadioItem ──
|
|
7544
|
+
// Maps figma-mappings `MenuRadioItem` (Menu Item node 9417-8883, type=radio).
|
|
7545
|
+
{
|
|
7546
|
+
group: "MenuRadioItem",
|
|
7547
|
+
name: "MenuRadioItem",
|
|
7548
|
+
code: `<Menu>
|
|
7549
|
+
<MenuTrigger asChild>
|
|
7550
|
+
<Button variant="outline">Density</Button>
|
|
7551
|
+
</MenuTrigger>
|
|
7552
|
+
<MenuContent>
|
|
7553
|
+
<MenuRadioGroup value="comfortable">
|
|
7554
|
+
<MenuRadioItem value="comfortable">Comfortable</MenuRadioItem>
|
|
7555
|
+
<MenuRadioItem value="compact">Compact</MenuRadioItem>
|
|
7556
|
+
</MenuRadioGroup>
|
|
7557
|
+
</MenuContent>
|
|
7558
|
+
</Menu>`
|
|
7559
|
+
},
|
|
7560
|
+
// ── MenuSectionHeading ──
|
|
7561
|
+
// Maps figma-mappings `MenuSectionHeading` (node 12345-33298).
|
|
7562
|
+
{
|
|
7563
|
+
group: "MenuSectionHeading",
|
|
7564
|
+
name: "MenuSectionHeading",
|
|
7565
|
+
code: `<Menu>
|
|
7566
|
+
<MenuTrigger asChild>
|
|
7567
|
+
<Button variant="outline">Account</Button>
|
|
7568
|
+
</MenuTrigger>
|
|
7569
|
+
<MenuContent>
|
|
7570
|
+
<MenuSectionHeading>Signed in as Jordan</MenuSectionHeading>
|
|
7571
|
+
<MenuItem>Profile</MenuItem>
|
|
7572
|
+
<MenuItem>Billing</MenuItem>
|
|
7573
|
+
</MenuContent>
|
|
7574
|
+
</Menu>`
|
|
7165
7575
|
},
|
|
7166
7576
|
// ── Elevation ──
|
|
7167
7577
|
{
|
|
@@ -7223,6 +7633,20 @@ var compositions_default = [
|
|
|
7223
7633
|
<FieldLabel>Link title</FieldLabel>
|
|
7224
7634
|
<FieldInput placeholder="My website" />
|
|
7225
7635
|
</FieldControl>
|
|
7636
|
+
</FieldRoot>`
|
|
7637
|
+
},
|
|
7638
|
+
// ── TextArea (Field.Textarea) — DNG-763 ──
|
|
7639
|
+
// The Figma "Text Area" component maps to the code Field compound rendered
|
|
7640
|
+
// with FieldTextarea; the figma-mappings `TextArea` key needs a composition
|
|
7641
|
+
// group of the same name so the MCP/Playroom coverage gate resolves it.
|
|
7642
|
+
{
|
|
7643
|
+
group: "TextArea",
|
|
7644
|
+
name: "Text Area",
|
|
7645
|
+
code: `<FieldRoot>
|
|
7646
|
+
<FieldControl>
|
|
7647
|
+
<FieldLabel>Bio</FieldLabel>
|
|
7648
|
+
<FieldTextarea placeholder="Tell us about yourself" />
|
|
7649
|
+
</FieldControl>
|
|
7226
7650
|
</FieldRoot>`
|
|
7227
7651
|
},
|
|
7228
7652
|
// ── Fieldset ──
|
|
@@ -7300,6 +7724,21 @@ var compositions_default = [
|
|
|
7300
7724
|
<NativeSelectOption value="us">United States</NativeSelectOption>
|
|
7301
7725
|
<NativeSelectOption value="uk">United Kingdom</NativeSelectOption>
|
|
7302
7726
|
</NativeSelect>`
|
|
7727
|
+
},
|
|
7728
|
+
// ── OneTimePasswordField ──
|
|
7729
|
+
{
|
|
7730
|
+
group: "OneTimePasswordField",
|
|
7731
|
+
name: "One-time password field",
|
|
7732
|
+
code: `<OneTimePasswordField maxLength={6}>
|
|
7733
|
+
<OneTimePasswordFieldGroup>
|
|
7734
|
+
<OneTimePasswordFieldSlot index={0} />
|
|
7735
|
+
<OneTimePasswordFieldSlot index={1} />
|
|
7736
|
+
<OneTimePasswordFieldSlot index={2} />
|
|
7737
|
+
<OneTimePasswordFieldSlot index={3} />
|
|
7738
|
+
<OneTimePasswordFieldSlot index={4} />
|
|
7739
|
+
<OneTimePasswordFieldSlot index={5} />
|
|
7740
|
+
</OneTimePasswordFieldGroup>
|
|
7741
|
+
</OneTimePasswordField>`
|
|
7303
7742
|
},
|
|
7304
7743
|
// ── Popover ──
|
|
7305
7744
|
{
|
|
@@ -8012,14 +8451,14 @@ function extractSlug(payload) {
|
|
|
8012
8451
|
}
|
|
8013
8452
|
|
|
8014
8453
|
// src/tools/open-in-playroom.ts
|
|
8015
|
-
var
|
|
8454
|
+
var TOOL_NAME8 = "arbor_open_in_playroom";
|
|
8016
8455
|
var DEPRECATED_TOOL_ALIAS = "arbor.open_in_playroom";
|
|
8017
|
-
var
|
|
8018
|
-
var
|
|
8019
|
-
var
|
|
8456
|
+
var TOOL_TITLE8 = "Open in Playroom";
|
|
8457
|
+
var TOOL_DESCRIPTION8 = `Given an Arbor component name (PascalCase), return the Playroom URL with the canonical snippet pre-loaded. coverage="mapped" resolves to a canonical snippet (use with high confidence); coverage="not_found" links to the Playroom homepage (surface this to the human rather than following it silently). Does NOT do fuzzy/Figma-name matching \u2014 that is the Figma plugin's selection-driven path.`;
|
|
8458
|
+
var inputSchema8 = external_exports.object({
|
|
8020
8459
|
componentName: external_exports.string().trim().min(1, "componentName is required and must be a non-empty string").describe('Arbor PascalCase component name, e.g. "Button" or "HeaderBar"')
|
|
8021
8460
|
}).strict();
|
|
8022
|
-
var
|
|
8461
|
+
var outputSchema8 = external_exports.object({
|
|
8023
8462
|
coverage: external_exports.enum(["mapped", "not_found"]).describe("mapped = resolved to a canonical snippet; not_found = no match, URL is the Playroom home"),
|
|
8024
8463
|
url: external_exports.string().describe("Playroom URL \u2014 a ?slug= link, an lz-string #?code= fallback, or the homepage"),
|
|
8025
8464
|
arborComponentName: external_exports.string().optional().describe("Resolved Arbor component name (mapped only)"),
|
|
@@ -8030,7 +8469,7 @@ var outputSchema7 = external_exports.object({
|
|
|
8030
8469
|
degraded: external_exports.literal(true).optional().describe("True when slug minting failed and the URL fell back to lz-string"),
|
|
8031
8470
|
reason: external_exports.enum(["timeout", "http_error", "network", "oversize", "malformed_response", "unconfigured"]).optional().describe("Why the result is degraded")
|
|
8032
8471
|
});
|
|
8033
|
-
var
|
|
8472
|
+
var annotations8 = {
|
|
8034
8473
|
readOnlyHint: false,
|
|
8035
8474
|
idempotentHint: true,
|
|
8036
8475
|
openWorldHint: true,
|
|
@@ -8131,14 +8570,14 @@ async function handleOpenInPlayroom(args) {
|
|
|
8131
8570
|
};
|
|
8132
8571
|
}
|
|
8133
8572
|
var openInPlayroomTool = {
|
|
8134
|
-
name:
|
|
8573
|
+
name: TOOL_NAME8,
|
|
8135
8574
|
aliases: [DEPRECATED_TOOL_ALIAS],
|
|
8136
8575
|
config: {
|
|
8137
|
-
title:
|
|
8138
|
-
description:
|
|
8139
|
-
inputSchema:
|
|
8140
|
-
outputSchema:
|
|
8141
|
-
annotations:
|
|
8576
|
+
title: TOOL_TITLE8,
|
|
8577
|
+
description: TOOL_DESCRIPTION8,
|
|
8578
|
+
inputSchema: inputSchema8,
|
|
8579
|
+
outputSchema: outputSchema8,
|
|
8580
|
+
annotations: annotations8
|
|
8142
8581
|
},
|
|
8143
8582
|
handler: handleOpenInPlayroom
|
|
8144
8583
|
};
|
|
@@ -8174,6 +8613,7 @@ var ARBOR_TOOLS = [
|
|
|
8174
8613
|
checkSyncHealthTool,
|
|
8175
8614
|
checkUxWritingTool,
|
|
8176
8615
|
lookupDecisionTool,
|
|
8616
|
+
judgeTool,
|
|
8177
8617
|
listVersionsTool,
|
|
8178
8618
|
listComponentsTool,
|
|
8179
8619
|
getComponentTool
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linktr.ee/arbor-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Model Context Protocol server exposing Arbor design system tools: Playroom snippets, Figma→code sync health, UX-writing checks, decision-log lookup, and federated component/version metadata.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"arbor",
|