@linktr.ee/arbor-mcp 0.6.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 +556 -66
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6178,6 +6178,7 @@ function coerceEntry(raw) {
|
|
|
6178
6178
|
notWhen: asStringArray(r.notWhen),
|
|
6179
6179
|
disambiguateFrom: asDisambiguations(r.disambiguateFrom),
|
|
6180
6180
|
guideline: typeof r.guideline === "string" ? r.guideline : null,
|
|
6181
|
+
implementation: typeof r.implementation === "string" ? r.implementation : null,
|
|
6181
6182
|
decisions: asStringArray(r.decisions)
|
|
6182
6183
|
};
|
|
6183
6184
|
}
|
|
@@ -6291,7 +6292,7 @@ var listComponentsTool = {
|
|
|
6291
6292
|
// src/tools/get-component.ts
|
|
6292
6293
|
var TOOL_NAME4 = "arbor_get_component";
|
|
6293
6294
|
var TOOL_TITLE4 = "Get one Arbor component";
|
|
6294
|
-
var TOOL_DESCRIPTION4 = "Get a single Arbor component's metadata by name (case-insensitive) or id (name, id, description) PLUS its intention overlay \u2014 lifecycle status (shipped | in-progress | design-only | deprecated | researched-not-shipped), when to use it, when NOT to (and what to use instead),
|
|
6295
|
+
var TOOL_DESCRIPTION4 = "Get a single Arbor component's metadata by name (case-insensitive) or id (name, id, description) PLUS its intention overlay \u2014 lifecycle status (shipped | in-progress | design-only | deprecated | researched-not-shipped), when to use it, when NOT to (and what to use instead), what it is commonly confused with, and repo paths to its guideline and (when present) agent implementation guidance. Intention comes from Arbor's source of truth, so a design-only component absent from the live design data still resolves. Read-only; matches top-level components, not Figma variants. Does NOT return token values or rendered source \u2014 use the shadcn registry / Storybook for implementation and arbor_open_in_playroom for a runnable snippet. Does NOT change anything.";
|
|
6295
6296
|
var inputSchema4 = external_exports.object({
|
|
6296
6297
|
name: external_exports.string().trim().min(1).max(100).optional().describe('Component name (case-insensitive), e.g. "Button"'),
|
|
6297
6298
|
id: external_exports.string().trim().min(1).max(100).optional().describe("Supernova component id"),
|
|
@@ -6313,6 +6314,7 @@ var intentionSchema = external_exports.object({
|
|
|
6313
6314
|
notWhen: external_exports.array(external_exports.string()).describe("When NOT to reach for it, ideally naming the better alternative"),
|
|
6314
6315
|
disambiguateFrom: external_exports.array(disambiguationSchema).describe("Things this is commonly confused with"),
|
|
6315
6316
|
guideline: external_exports.string().nullable().describe("Repo-relative path to the long-form guideline, when one exists"),
|
|
6317
|
+
implementation: external_exports.string().nullable().describe("Repo-relative path to the agent-facing implementation guidance (code-correctness + a11y rules), when one exists"),
|
|
6316
6318
|
decisions: external_exports.array(external_exports.string()).describe("Decision-log ids (DL-NN) that explain this intention")
|
|
6317
6319
|
});
|
|
6318
6320
|
var outputSchema4 = external_exports.object({
|
|
@@ -6337,6 +6339,7 @@ async function resolveIntention(lookupName, load) {
|
|
|
6337
6339
|
notWhen: match.notWhen,
|
|
6338
6340
|
disambiguateFrom: match.disambiguateFrom.map((d) => ({ component: d.component, distinction: d.distinction })),
|
|
6339
6341
|
guideline: match.guideline,
|
|
6342
|
+
implementation: match.implementation,
|
|
6340
6343
|
decisions: match.decisions
|
|
6341
6344
|
};
|
|
6342
6345
|
return { intention, intentionSource: "remote" };
|
|
@@ -6418,11 +6421,367 @@ var getComponentTool = {
|
|
|
6418
6421
|
handler: handleGetComponent
|
|
6419
6422
|
};
|
|
6420
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
|
+
|
|
6421
6780
|
// src/tools/list-versions.ts
|
|
6422
|
-
var
|
|
6423
|
-
var
|
|
6424
|
-
var
|
|
6425
|
-
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();
|
|
6426
6785
|
var versionSchema = external_exports.object({
|
|
6427
6786
|
id: external_exports.string(),
|
|
6428
6787
|
name: external_exports.string(),
|
|
@@ -6439,7 +6798,7 @@ var manifestReleaseSchema = external_exports.object({
|
|
|
6439
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)"),
|
|
6440
6799
|
supernovaStatus: external_exports.string().nullable()
|
|
6441
6800
|
});
|
|
6442
|
-
var
|
|
6801
|
+
var outputSchema6 = external_exports.object({
|
|
6443
6802
|
source: external_exports.enum(["remote", "unavailable"]).describe("'remote' = Supernova versions fetched; 'unavailable' = proxy fetch failed"),
|
|
6444
6803
|
total: external_exports.number().describe("Number of Supernova versions returned"),
|
|
6445
6804
|
versions: external_exports.array(versionSchema).describe("Supernova versions, in the order returned"),
|
|
@@ -6451,7 +6810,7 @@ var outputSchema5 = external_exports.object({
|
|
|
6451
6810
|
}).describe("The committed release manifest (releases.json) mapping npm releases \u2194 Supernova versions"),
|
|
6452
6811
|
detail: external_exports.string().nullable().describe("Degraded reason when source is unavailable")
|
|
6453
6812
|
});
|
|
6454
|
-
var
|
|
6813
|
+
var annotations6 = { readOnlyHint: true, idempotentHint: true, openWorldHint: true, destructiveHint: false };
|
|
6455
6814
|
var SEMVER_RE = /^\d+\.\d+\.\d+(?:[-+].*)?$/;
|
|
6456
6815
|
function extractList(data) {
|
|
6457
6816
|
const wrapped = data?.result?.designSystemVersions;
|
|
@@ -6542,19 +6901,18 @@ async function handleListVersions(_args, deps = {}) {
|
|
|
6542
6901
|
return { content: [{ type: "text", text: `${versionsLine} ${manifestLine}` }], structuredContent: out };
|
|
6543
6902
|
}
|
|
6544
6903
|
var listVersionsTool = {
|
|
6545
|
-
name:
|
|
6546
|
-
config: { title:
|
|
6904
|
+
name: TOOL_NAME6,
|
|
6905
|
+
config: { title: TOOL_TITLE6, description: TOOL_DESCRIPTION6, inputSchema: inputSchema6, outputSchema: outputSchema6, annotations: annotations6 },
|
|
6547
6906
|
handler: handleListVersions
|
|
6548
6907
|
};
|
|
6549
6908
|
|
|
6550
6909
|
// src/tools/lookup-decision.ts
|
|
6551
|
-
var ARTIFACT2 = "decision-log.json";
|
|
6552
6910
|
var CHARACTER_LIMIT = 25e3;
|
|
6553
6911
|
var DEFAULT_LIMIT = 5;
|
|
6554
|
-
var
|
|
6555
|
-
var
|
|
6556
|
-
var
|
|
6557
|
-
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({
|
|
6558
6916
|
id: external_exports.string().optional().describe('A specific decision id, e.g. "DL-067" (a bare "67" also resolves)'),
|
|
6559
6917
|
tag: external_exports.string().optional().describe('Filter to entries carrying this tag (case-insensitive), e.g. "tokens"'),
|
|
6560
6918
|
query: external_exports.string().optional().describe("Keyword search across title, tags, and all sections (case-insensitive)"),
|
|
@@ -6562,17 +6920,7 @@ var inputSchema6 = external_exports.object({
|
|
|
6562
6920
|
offset: external_exports.number().int().min(0).default(0).describe("Entries to skip, for pagination"),
|
|
6563
6921
|
response_format: external_exports.enum(["markdown", "json"]).default("markdown").describe("Rendering of the text content: 'markdown' (human) or 'json' (machine)")
|
|
6564
6922
|
}).strict();
|
|
6565
|
-
var
|
|
6566
|
-
var decisionEntrySchema = external_exports.object({
|
|
6567
|
-
id: external_exports.string(),
|
|
6568
|
-
number: external_exports.number(),
|
|
6569
|
-
title: external_exports.string(),
|
|
6570
|
-
date: external_exports.string(),
|
|
6571
|
-
status: external_exports.string(),
|
|
6572
|
-
tags: external_exports.array(external_exports.string()),
|
|
6573
|
-
sections: external_exports.array(sectionSchema).describe("Ordered sections (Context, Decision, \u2026, plus any non-standard headings)")
|
|
6574
|
-
});
|
|
6575
|
-
var outputSchema6 = external_exports.object({
|
|
6923
|
+
var outputSchema7 = external_exports.object({
|
|
6576
6924
|
total: external_exports.number().describe("Total entries matching the filter (before pagination)"),
|
|
6577
6925
|
count: external_exports.number().describe("Entries actually returned (after pagination + character budget)"),
|
|
6578
6926
|
offset: external_exports.number(),
|
|
@@ -6583,25 +6931,12 @@ var outputSchema6 = external_exports.object({
|
|
|
6583
6931
|
stale: external_exports.boolean().describe("True when the snapshot is older than the freshness threshold (or unavailable)"),
|
|
6584
6932
|
entries: external_exports.array(decisionEntrySchema)
|
|
6585
6933
|
});
|
|
6586
|
-
var
|
|
6934
|
+
var annotations7 = {
|
|
6587
6935
|
readOnlyHint: true,
|
|
6588
6936
|
idempotentHint: true,
|
|
6589
6937
|
openWorldHint: true,
|
|
6590
6938
|
destructiveHint: false
|
|
6591
6939
|
};
|
|
6592
|
-
function asEntries(data) {
|
|
6593
|
-
const raw = data ?? {};
|
|
6594
|
-
if (!Array.isArray(raw.entries)) return [];
|
|
6595
|
-
const entries = [];
|
|
6596
|
-
for (const item of raw.entries) {
|
|
6597
|
-
const parsed = decisionEntrySchema.safeParse(item);
|
|
6598
|
-
if (parsed.success) entries.push(parsed.data);
|
|
6599
|
-
}
|
|
6600
|
-
return entries;
|
|
6601
|
-
}
|
|
6602
|
-
function searchBlob(e) {
|
|
6603
|
-
return [e.title, e.tags.join(" "), ...e.sections.map((s) => `${s.heading} ${s.body}`)].join("\n").toLowerCase();
|
|
6604
|
-
}
|
|
6605
6940
|
function renderEntry(e) {
|
|
6606
6941
|
const lines = [
|
|
6607
6942
|
`## ${e.id}: ${e.title}`,
|
|
@@ -6636,14 +6971,10 @@ async function handleLookupDecision(args, deps = {}) {
|
|
|
6636
6971
|
const load = deps.load ?? ((f) => loadPublishedJson(f));
|
|
6637
6972
|
const charLimit = deps.charLimit ?? CHARACTER_LIMIT;
|
|
6638
6973
|
const { id, tag, query, limit = DEFAULT_LIMIT, offset = 0, response_format = "markdown" } = args;
|
|
6639
|
-
const
|
|
6640
|
-
if (!
|
|
6641
|
-
const
|
|
6642
|
-
|
|
6643
|
-
return unavailable(offset, typeof raw.reason === "string" ? raw.reason : "degraded");
|
|
6644
|
-
}
|
|
6645
|
-
const all = asEntries(result.data);
|
|
6646
|
-
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;
|
|
6647
6978
|
const stale = generatedAt ? freshness(generatedAt, { now: deps.now }).stale : true;
|
|
6648
6979
|
let matches;
|
|
6649
6980
|
if (id !== void 0) {
|
|
@@ -6654,7 +6985,7 @@ async function handleLookupDecision(args, deps = {}) {
|
|
|
6654
6985
|
const queryLc = query?.toLowerCase();
|
|
6655
6986
|
matches = all.filter((e) => {
|
|
6656
6987
|
if (tagLc && !e.tags.some((t) => t.toLowerCase() === tagLc)) return false;
|
|
6657
|
-
if (queryLc && !
|
|
6988
|
+
if (queryLc && !decisionSearchBlob(e).includes(queryLc)) return false;
|
|
6658
6989
|
return true;
|
|
6659
6990
|
});
|
|
6660
6991
|
}
|
|
@@ -6699,13 +7030,13 @@ ${included.map(renderEntry).join("\n\n---\n\n")}${note}`;
|
|
|
6699
7030
|
};
|
|
6700
7031
|
}
|
|
6701
7032
|
var lookupDecisionTool = {
|
|
6702
|
-
name:
|
|
7033
|
+
name: TOOL_NAME7,
|
|
6703
7034
|
config: {
|
|
6704
|
-
title:
|
|
6705
|
-
description:
|
|
6706
|
-
inputSchema:
|
|
6707
|
-
outputSchema:
|
|
6708
|
-
annotations:
|
|
7035
|
+
title: TOOL_TITLE7,
|
|
7036
|
+
description: TOOL_DESCRIPTION7,
|
|
7037
|
+
inputSchema: inputSchema7,
|
|
7038
|
+
outputSchema: outputSchema7,
|
|
7039
|
+
annotations: annotations7
|
|
6709
7040
|
},
|
|
6710
7041
|
handler: handleLookupDecision
|
|
6711
7042
|
};
|
|
@@ -6932,6 +7263,22 @@ var compositions_default = [
|
|
|
6932
7263
|
name: "NotificationBadge",
|
|
6933
7264
|
code: "<NotificationBadge count={3} />"
|
|
6934
7265
|
},
|
|
7266
|
+
// ── IndicatorBadge ──
|
|
7267
|
+
{
|
|
7268
|
+
group: "IndicatorBadge",
|
|
7269
|
+
name: "IndicatorBadge (count)",
|
|
7270
|
+
code: "<IndicatorBadge count={5} />"
|
|
7271
|
+
},
|
|
7272
|
+
{
|
|
7273
|
+
group: "IndicatorBadge",
|
|
7274
|
+
name: "IndicatorBadge (dot)",
|
|
7275
|
+
code: '<IndicatorBadge dot aria-label="Unread" />'
|
|
7276
|
+
},
|
|
7277
|
+
{
|
|
7278
|
+
group: "IndicatorBadge",
|
|
7279
|
+
name: "IndicatorBadge (custom color)",
|
|
7280
|
+
code: '<IndicatorBadge count={5} color="#7B61FF" />'
|
|
7281
|
+
},
|
|
6935
7282
|
// ── Button ──
|
|
6936
7283
|
{
|
|
6937
7284
|
group: "Button",
|
|
@@ -6965,6 +7312,31 @@ var compositions_default = [
|
|
|
6965
7312
|
<Button variant="secondary">Cancel</Button>
|
|
6966
7313
|
<Button>Save changes</Button>
|
|
6967
7314
|
</div>`
|
|
7315
|
+
},
|
|
7316
|
+
// ── ButtonGroup ──
|
|
7317
|
+
{
|
|
7318
|
+
group: "ButtonGroup",
|
|
7319
|
+
name: "ButtonGroup (confirm / cancel)",
|
|
7320
|
+
code: `<ButtonGroup aria-label="Save changes">
|
|
7321
|
+
<Button variant="secondary">Cancel</Button>
|
|
7322
|
+
<Button>Save changes</Button>
|
|
7323
|
+
</ButtonGroup>`
|
|
7324
|
+
},
|
|
7325
|
+
{
|
|
7326
|
+
group: "ButtonGroup",
|
|
7327
|
+
name: "ButtonGroup (stacked)",
|
|
7328
|
+
code: `<ButtonGroup stacked aria-label="Start trial or decide later">
|
|
7329
|
+
<Button variant="accent">Start 7-day free trial</Button>
|
|
7330
|
+
<Button variant="ghost">Decide later</Button>
|
|
7331
|
+
</ButtonGroup>`
|
|
7332
|
+
},
|
|
7333
|
+
{
|
|
7334
|
+
group: "ButtonGroup",
|
|
7335
|
+
name: "ButtonGroup (auto width)",
|
|
7336
|
+
code: `<ButtonGroup fullWidth={false} aria-label="Discard or update">
|
|
7337
|
+
<Button variant="ghost">Discard changes</Button>
|
|
7338
|
+
<Button>Update everywhere</Button>
|
|
7339
|
+
</ButtonGroup>`
|
|
6968
7340
|
},
|
|
6969
7341
|
// ── CardSelect ──
|
|
6970
7342
|
{
|
|
@@ -7118,6 +7490,88 @@ var compositions_default = [
|
|
|
7118
7490
|
<DropdownMenuItem className="text-danger">Delete</DropdownMenuItem>
|
|
7119
7491
|
</DropdownMenuContent>
|
|
7120
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>`
|
|
7121
7575
|
},
|
|
7122
7576
|
// ── Elevation ──
|
|
7123
7577
|
{
|
|
@@ -7179,6 +7633,20 @@ var compositions_default = [
|
|
|
7179
7633
|
<FieldLabel>Link title</FieldLabel>
|
|
7180
7634
|
<FieldInput placeholder="My website" />
|
|
7181
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>
|
|
7182
7650
|
</FieldRoot>`
|
|
7183
7651
|
},
|
|
7184
7652
|
// ── Fieldset ──
|
|
@@ -7195,6 +7663,12 @@ var compositions_default = [
|
|
|
7195
7663
|
</RadioGroup>
|
|
7196
7664
|
</Fieldset>`
|
|
7197
7665
|
},
|
|
7666
|
+
// ── FileUpload ──
|
|
7667
|
+
{
|
|
7668
|
+
group: "FileUpload",
|
|
7669
|
+
name: "FileUpload",
|
|
7670
|
+
code: '<div className="w-full max-w-md"><FileUpload label="Select files to upload" supportingText="PDF, PNG, or JPG \u2014 up to 100MB each" /></div>'
|
|
7671
|
+
},
|
|
7198
7672
|
// ── HeaderBar ──
|
|
7199
7673
|
// Maps to figma-mappings `HeaderBar` (node 5223-5837). Title + back-button
|
|
7200
7674
|
// is the canonical small variant; HeaderBar.stories.tsx covers the full
|
|
@@ -7250,6 +7724,21 @@ var compositions_default = [
|
|
|
7250
7724
|
<NativeSelectOption value="us">United States</NativeSelectOption>
|
|
7251
7725
|
<NativeSelectOption value="uk">United Kingdom</NativeSelectOption>
|
|
7252
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>`
|
|
7253
7742
|
},
|
|
7254
7743
|
// ── Popover ──
|
|
7255
7744
|
{
|
|
@@ -7962,14 +8451,14 @@ function extractSlug(payload) {
|
|
|
7962
8451
|
}
|
|
7963
8452
|
|
|
7964
8453
|
// src/tools/open-in-playroom.ts
|
|
7965
|
-
var
|
|
8454
|
+
var TOOL_NAME8 = "arbor_open_in_playroom";
|
|
7966
8455
|
var DEPRECATED_TOOL_ALIAS = "arbor.open_in_playroom";
|
|
7967
|
-
var
|
|
7968
|
-
var
|
|
7969
|
-
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({
|
|
7970
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"')
|
|
7971
8460
|
}).strict();
|
|
7972
|
-
var
|
|
8461
|
+
var outputSchema8 = external_exports.object({
|
|
7973
8462
|
coverage: external_exports.enum(["mapped", "not_found"]).describe("mapped = resolved to a canonical snippet; not_found = no match, URL is the Playroom home"),
|
|
7974
8463
|
url: external_exports.string().describe("Playroom URL \u2014 a ?slug= link, an lz-string #?code= fallback, or the homepage"),
|
|
7975
8464
|
arborComponentName: external_exports.string().optional().describe("Resolved Arbor component name (mapped only)"),
|
|
@@ -7980,7 +8469,7 @@ var outputSchema7 = external_exports.object({
|
|
|
7980
8469
|
degraded: external_exports.literal(true).optional().describe("True when slug minting failed and the URL fell back to lz-string"),
|
|
7981
8470
|
reason: external_exports.enum(["timeout", "http_error", "network", "oversize", "malformed_response", "unconfigured"]).optional().describe("Why the result is degraded")
|
|
7982
8471
|
});
|
|
7983
|
-
var
|
|
8472
|
+
var annotations8 = {
|
|
7984
8473
|
readOnlyHint: false,
|
|
7985
8474
|
idempotentHint: true,
|
|
7986
8475
|
openWorldHint: true,
|
|
@@ -8081,14 +8570,14 @@ async function handleOpenInPlayroom(args) {
|
|
|
8081
8570
|
};
|
|
8082
8571
|
}
|
|
8083
8572
|
var openInPlayroomTool = {
|
|
8084
|
-
name:
|
|
8573
|
+
name: TOOL_NAME8,
|
|
8085
8574
|
aliases: [DEPRECATED_TOOL_ALIAS],
|
|
8086
8575
|
config: {
|
|
8087
|
-
title:
|
|
8088
|
-
description:
|
|
8089
|
-
inputSchema:
|
|
8090
|
-
outputSchema:
|
|
8091
|
-
annotations:
|
|
8576
|
+
title: TOOL_TITLE8,
|
|
8577
|
+
description: TOOL_DESCRIPTION8,
|
|
8578
|
+
inputSchema: inputSchema8,
|
|
8579
|
+
outputSchema: outputSchema8,
|
|
8580
|
+
annotations: annotations8
|
|
8092
8581
|
},
|
|
8093
8582
|
handler: handleOpenInPlayroom
|
|
8094
8583
|
};
|
|
@@ -8124,6 +8613,7 @@ var ARBOR_TOOLS = [
|
|
|
8124
8613
|
checkSyncHealthTool,
|
|
8125
8614
|
checkUxWritingTool,
|
|
8126
8615
|
lookupDecisionTool,
|
|
8616
|
+
judgeTool,
|
|
8127
8617
|
listVersionsTool,
|
|
8128
8618
|
listComponentsTool,
|
|
8129
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",
|