@bridge-ds/mcp 0.3.2 → 0.4.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.
@@ -11,6 +11,8 @@ export interface StartExplorationInput {
11
11
  /** Where it is started from (the MCP tool says mcp, the CLI says cli), and the AI agent behind it when known. */
12
12
  source?: ExplorationSource | undefined;
13
13
  agent?: string | undefined;
14
+ /** The product it is tried in (key or name, bridge.config.json "products"). Default: the product of the current folder or repo. */
15
+ product?: string | undefined;
14
16
  dryRun?: boolean | undefined;
15
17
  }
16
18
  /**
@@ -20,6 +22,7 @@ export interface StartExplorationInput {
20
22
  */
21
23
  export declare function startExploration(ctx: BridgeContext, a: StartExplorationInput): Promise<{
22
24
  branch: string;
25
+ product: string;
23
26
  slug: string;
24
27
  base: string;
25
28
  title: string;
@@ -46,6 +49,7 @@ export declare function startExploration(ctx: BridgeContext, a: StartExploration
46
49
  }[];
47
50
  next: string;
48
51
  branch: string;
52
+ product: string;
49
53
  slug: string;
50
54
  base: string;
51
55
  title: string;
@@ -60,6 +64,7 @@ export declare function startExploration(ctx: BridgeContext, a: StartExploration
60
64
  commit: string;
61
65
  next: string;
62
66
  branch: string;
67
+ product: string;
63
68
  slug: string;
64
69
  base: string;
65
70
  title: string;
package/dist/index.js CHANGED
@@ -3,8 +3,8 @@ import {
3
3
  VERSION,
4
4
  optionsFromArgv,
5
5
  serveStdio
6
- } from "./chunk-MTQCDV65.js";
7
- import "./chunk-X455XY4E.js";
6
+ } from "./chunk-NYFH322K.js";
7
+ import "./chunk-7DBLDJIL.js";
8
8
 
9
9
  // src/index.ts
10
10
  var argv = process.argv.slice(2);
package/dist/lib.d.ts CHANGED
@@ -11,6 +11,8 @@ export * as brand from './brand.js';
11
11
  export * as requests from './requests.js';
12
12
  export * as explorations from './explorations.js';
13
13
  export * as drafts from './drafts.js';
14
+ export * as branches from './branches.js';
15
+ export * as studio from './studio.js';
14
16
  export { DOCS_DRAFT_MODEL, anthropicReplyText, anthropicRequest, formatDocsJson, mergeDocsDraft, missingProse, parseDocsReply } from '@bridge-ds/core';
15
17
  export interface ServeOptions extends ContextOptions {
16
18
  }
package/dist/lib.js CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  anthropicReplyText,
9
9
  anthropicRequest,
10
10
  auditSnippet,
11
+ branches_exports,
11
12
  brand_exports,
12
13
  core,
13
14
  createServer,
@@ -22,13 +23,14 @@ import {
22
23
  optionsFromArgv,
23
24
  parseDocsReply,
24
25
  requests_exports,
25
- serveStdio
26
- } from "./chunk-MTQCDV65.js";
26
+ serveStdio,
27
+ studio_exports
28
+ } from "./chunk-NYFH322K.js";
27
29
  import {
28
30
  CONFIG_FILE,
29
31
  loadConfig,
30
32
  toConfigJson
31
- } from "./chunk-X455XY4E.js";
33
+ } from "./chunk-7DBLDJIL.js";
32
34
  export {
33
35
  BridgeContext,
34
36
  CONFIG_FILE,
@@ -40,6 +42,7 @@ export {
40
42
  anthropicReplyText,
41
43
  anthropicRequest,
42
44
  auditSnippet,
45
+ branches_exports as branches,
43
46
  brand_exports as brand,
44
47
  core,
45
48
  createServer,
@@ -56,5 +59,6 @@ export {
56
59
  parseDocsReply,
57
60
  requests_exports as requests,
58
61
  serveStdio,
62
+ studio_exports as studio,
59
63
  toConfigJson
60
64
  };
package/dist/ops.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { type ScaffoldInput } from './components.js';
2
- import type { BridgeContext } from './context.js';
3
2
  import { type BridgeConfig, type GitHubClient } from '@bridge-ds/core';
4
3
  import { type Combo, type PairInput, type TokenEdit, type TokenSet } from './core-adapter.js';
4
+ import { BridgeContext } from './context.js';
5
5
  /** Accepts `color.text.muted`, `--color-text-muted` or `var(--color-text-muted)`. */
6
6
  export declare function normalizeTokenPath(set: TokenSet, input: string): string;
7
7
  export declare function getConfig(ctx: BridgeContext): Promise<{
@@ -274,7 +274,75 @@ export declare function proposeTokenChange(ctx: BridgeContext, a: {
274
274
  summary?: string;
275
275
  dryRun?: boolean;
276
276
  branch?: string | undefined;
277
+ pr?: number | string | undefined;
278
+ jira?: string | undefined;
279
+ product?: string | undefined;
277
280
  }): Promise<{
281
+ status: string;
282
+ pr: number;
283
+ message: string;
284
+ } | {
285
+ hint: string;
286
+ pr: number;
287
+ branch: string;
288
+ tier: 1 | 2 | 3;
289
+ changes: {
290
+ kind: "added" | "changed" | "renamed" | "removed";
291
+ path: string;
292
+ file: string;
293
+ before: string | undefined;
294
+ after: string | undefined;
295
+ }[];
296
+ contrast: {
297
+ moved: number;
298
+ newFailures: import("@bridge-ds/core").ContrastDelta[];
299
+ };
300
+ notes: string[];
301
+ status: string;
302
+ why: string;
303
+ newErrors: import("@bridge-ds/core").LayerIssue[];
304
+ message?: undefined;
305
+ } | {
306
+ files: string[];
307
+ pr: number;
308
+ branch: string;
309
+ tier: 1 | 2 | 3;
310
+ changes: {
311
+ kind: "added" | "changed" | "renamed" | "removed";
312
+ path: string;
313
+ file: string;
314
+ before: string | undefined;
315
+ after: string | undefined;
316
+ }[];
317
+ contrast: {
318
+ moved: number;
319
+ newFailures: import("@bridge-ds/core").ContrastDelta[];
320
+ };
321
+ notes: string[];
322
+ status: string;
323
+ message?: undefined;
324
+ } | {
325
+ commit: string;
326
+ url: string;
327
+ said: string;
328
+ pr: number;
329
+ branch: string;
330
+ tier: 1 | 2 | 3;
331
+ changes: {
332
+ kind: "added" | "changed" | "renamed" | "removed";
333
+ path: string;
334
+ file: string;
335
+ before: string | undefined;
336
+ after: string | undefined;
337
+ }[];
338
+ contrast: {
339
+ moved: number;
340
+ newFailures: import("@bridge-ds/core").ContrastDelta[];
341
+ };
342
+ notes: string[];
343
+ status: string;
344
+ message?: undefined;
345
+ } | {
278
346
  status: string;
279
347
  message: string;
280
348
  } | {
@@ -501,6 +569,7 @@ export declare function proposeTokenChange(ctx: BridgeContext, a: {
501
569
  url: string;
502
570
  labels: string[];
503
571
  draft: boolean;
572
+ product: string;
504
573
  files: string[];
505
574
  prBody: string;
506
575
  title: string;
@@ -68,6 +68,8 @@ export interface CreateRequestInput {
68
68
  source?: RequestSource | undefined;
69
69
  /** Exploration branch to implement it on (`explore/<slug>`), instead of a pull request to main. */
70
70
  exploration?: string | undefined;
71
+ /** The product it is for (key or name, bridge.config.json "products"). Default: the product of the current folder or repo. */
72
+ product?: string | undefined;
71
73
  dryRun?: boolean | undefined;
72
74
  }
73
75
  export declare function createRequest(ctx: BridgeContext, a: CreateRequestInput): Promise<{
@@ -1,13 +1,17 @@
1
1
  import {
2
+ FIGMA_REFERENCE_STORY,
2
3
  LayeredTokenSet,
3
4
  comboKey,
4
5
  diffLayered,
5
6
  git,
6
7
  listFilesAt,
7
8
  loadConfig,
9
+ parseComponentSpecs,
10
+ planReferences,
11
+ referenceImagePath,
8
12
  selectTokenFiles,
9
13
  showFile
10
- } from "./chunk-X455XY4E.js";
14
+ } from "./chunk-7DBLDJIL.js";
11
15
 
12
16
  // ../visual/src/run.ts
13
17
  import { existsSync as existsSync3 } from "node:fs";
@@ -146,8 +150,8 @@ async function launch(o = {}) {
146
150
  args: ["--lang=en-US", "--disable-gpu", "--font-render-hinting=none", "--disable-lcd-text", "--force-color-profile=srgb", "--hide-scrollbars", "--disable-skia-runtime-opts", "--disable-renderer-backgrounding"]
147
151
  });
148
152
  }
149
- async function newContext(browser, viewport) {
150
- const ctx = await browser.newContext({ viewport, deviceScaleFactor: 1, reducedMotion: "reduce", colorScheme: "light", locale: "en-US", timezoneId: "UTC", serviceWorkers: "block" });
153
+ async function newContext(browser, viewport, deviceScaleFactor = 1) {
154
+ const ctx = await browser.newContext({ viewport, deviceScaleFactor, reducedMotion: "reduce", colorScheme: "light", locale: "en-US", timezoneId: "UTC", serviceWorkers: "block" });
151
155
  await ctx.addInitScript(INIT_SCRIPT);
152
156
  await ctx.route(
153
157
  /^https?:\/\/(?!127\.0\.0\.1[:/]|localhost[:/])/,
@@ -1096,12 +1100,106 @@ async function runImpact(o) {
1096
1100
  log(`${result.summary} Manifest: ${relative2(process.cwd(), result.manifestPath) || result.manifestPath}`);
1097
1101
  return result;
1098
1102
  }
1103
+
1104
+ // ../visual/src/references.ts
1105
+ import { existsSync as existsSync4 } from "node:fs";
1106
+ import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
1107
+ import { dirname as dirname3, join as join4 } from "node:path";
1108
+ async function captureFigmaReferences(o) {
1109
+ const log = o.log ?? (() => void 0);
1110
+ const scale = o.scale ?? 2;
1111
+ const brand = o.brand ?? "bridge";
1112
+ const theme = o.theme ?? "light";
1113
+ if (!existsSync4(join4(o.storybook, "index.json"))) throw new Error(`No built Storybook in ${o.storybook}: run npm run build-storybook first.`);
1114
+ const index = JSON.parse(await readFile4(join4(o.storybook, "index.json"), "utf8"));
1115
+ if (!index.entries[FIGMA_REFERENCE_STORY]) throw new Error(`The Storybook build has no ${FIGMA_REFERENCE_STORY} story (packages/react/src/stories/FigmaReference.stories.tsx).`);
1116
+ const specs = parseComponentSpecs(await readFile4(o.specs, "utf8"));
1117
+ const jobs = planReferences(specs).filter((j) => !o.only?.length || o.only.includes(j.component));
1118
+ const manifest = {
1119
+ version: 1,
1120
+ tool: "@bridge-ds/visual",
1121
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1122
+ sha: o.sha,
1123
+ scale,
1124
+ mode: { brand, theme, density: "comfortable" },
1125
+ storyId: FIGMA_REFERENCE_STORY,
1126
+ components: {}
1127
+ };
1128
+ for (const j of jobs) manifest.components[j.component] ??= { specHash: j.specHash, variants: {} };
1129
+ let captured = 0;
1130
+ let skipped = 0;
1131
+ const served = await serveDir(o.storybook);
1132
+ const browser = await launch();
1133
+ try {
1134
+ const ctx = await newContext(browser, { width: 1200, height: 800 }, scale);
1135
+ const page = await newPage(ctx);
1136
+ await page.goto(storyUrl(served.url, FIGMA_REFERENCE_STORY, brand, theme), { waitUntil: "load", timeout: 3e4 });
1137
+ await page.waitForSelector("[data-fidelity-root], [data-fidelity-error]", { state: "attached", timeout: 2e4 });
1138
+ await settle(page, { busy: false });
1139
+ log(`Capturing ${jobs.length} variants at ${scale}x (${brand} ${theme})`);
1140
+ for (const j of jobs) {
1141
+ const variants = manifest.components[j.component].variants;
1142
+ if (!j.plan.ok) {
1143
+ variants[j.key] = { skipped: j.plan.reason };
1144
+ skipped++;
1145
+ continue;
1146
+ }
1147
+ const plan = JSON.stringify(j.plan.render);
1148
+ await page.evaluate(
1149
+ ({ id, plan: plan2 }) => new Promise((resolve2) => {
1150
+ const ch = window.__STORYBOOK_ADDONS_CHANNEL__;
1151
+ const done = () => {
1152
+ clearTimeout(timer);
1153
+ ch.off("storyRendered", done);
1154
+ resolve2();
1155
+ };
1156
+ const timer = setTimeout(done, 1e4);
1157
+ ch.on("storyRendered", done);
1158
+ ch.emit("updateStoryArgs", { storyId: id, updatedArgs: { plan: plan2 } });
1159
+ }),
1160
+ { id: FIGMA_REFERENCE_STORY, plan }
1161
+ );
1162
+ await settle(page, { network: false, quiet: 2, interval: 40, busy: false });
1163
+ const error = await page.locator("[data-fidelity-error]").first().textContent({ timeout: 200 }).catch(() => null);
1164
+ let target = page.locator("[data-fidelity-root] > *").first();
1165
+ if (!error && !await target.count()) {
1166
+ const marked = page.locator("[data-fidelity-target]").first();
1167
+ await marked.waitFor({ state: "visible", timeout: 3e3 }).catch(() => void 0);
1168
+ await settle(page, { network: false, quiet: 2, interval: 40, busy: false });
1169
+ target = marked;
1170
+ }
1171
+ if (error || !await target.count()) {
1172
+ variants[j.key] = { skipped: error ?? "the code example did not render" };
1173
+ skipped++;
1174
+ continue;
1175
+ }
1176
+ const file = referenceImagePath(j.component, j.key);
1177
+ const path = join4(o.out, file);
1178
+ await mkdir4(dirname3(path), { recursive: true });
1179
+ await target.screenshot({ path, omitBackground: true, animations: "disabled", caret: "hide", scale: "device" });
1180
+ const box = await target.boundingBox();
1181
+ variants[j.key] = { file, width: Math.round(box?.width ?? 0), height: Math.round(box?.height ?? 0) };
1182
+ captured++;
1183
+ }
1184
+ await ctx.close();
1185
+ } finally {
1186
+ await browser.close().catch(() => void 0);
1187
+ await served.close();
1188
+ }
1189
+ await mkdir4(o.out, { recursive: true });
1190
+ const manifestPath = join4(o.out, "manifest.json");
1191
+ await writeFile4(manifestPath, `${JSON.stringify(manifest, null, 2)}
1192
+ `);
1193
+ log(`${captured} images, ${skipped} variants skipped. ${manifestPath}`);
1194
+ return { manifest, manifestPath, captured, skipped };
1195
+ }
1099
1196
  export {
1100
1197
  COMMENT_MARKER,
1101
1198
  DEFAULT_ROUTES,
1102
1199
  DEFAULT_TENANTS,
1103
1200
  boxesFromCells,
1104
1201
  boxesFromDiff,
1202
+ captureFigmaReferences,
1105
1203
  changedTokens,
1106
1204
  decodePng,
1107
1205
  diffPngs,
@@ -0,0 +1,107 @@
1
+ import type { BridgeContext } from './context.js';
2
+ /**
3
+ * Save a component's guidance (docs.json) the way the Library edit mode does: merged into the docs on the branch,
4
+ * cleaned, validated (a summary, no empty items, no em dash), then either one new branch sent for review (Tier 1,
5
+ * patch changeset, the Jira key first in its name) or one commit on an open branch (`pr`).
6
+ */
7
+ export declare function saveDocs(ctx: BridgeContext, a: {
8
+ component: string;
9
+ docs: Record<string, unknown>;
10
+ pr?: number | string | undefined;
11
+ title?: string | undefined;
12
+ notes?: string | undefined;
13
+ jira?: string | undefined;
14
+ product?: string | undefined;
15
+ dryRun?: boolean | undefined;
16
+ }): Promise<{
17
+ status: "dry-run";
18
+ pr: number;
19
+ path: string;
20
+ content: string;
21
+ commit?: undefined;
22
+ said?: undefined;
23
+ url?: undefined;
24
+ title?: undefined;
25
+ product?: undefined;
26
+ } | {
27
+ status: "committed" | "unchanged";
28
+ pr: number;
29
+ path: string;
30
+ commit: string | null;
31
+ said: string;
32
+ content?: undefined;
33
+ url?: undefined;
34
+ title?: undefined;
35
+ product?: undefined;
36
+ } | {
37
+ status: "unchanged";
38
+ path: string;
39
+ said: string;
40
+ pr?: undefined;
41
+ content?: undefined;
42
+ commit?: undefined;
43
+ url?: undefined;
44
+ title?: undefined;
45
+ product?: undefined;
46
+ } | {
47
+ said?: string | undefined;
48
+ status: "preview" | "dry-run";
49
+ path: string;
50
+ content: string;
51
+ product: string;
52
+ pr?: undefined;
53
+ commit?: undefined;
54
+ url?: undefined;
55
+ title?: undefined;
56
+ } | {
57
+ status: "sent-for-review";
58
+ pr: number;
59
+ url: string;
60
+ title: string;
61
+ path: string;
62
+ product: string;
63
+ said: string;
64
+ content?: undefined;
65
+ commit?: undefined;
66
+ }>;
67
+ /** The settings a designer reads in Studio Settings, safe to show: roles, who ships, the owner override, protection, products, previews. */
68
+ export declare function getSettings(ctx: BridgeContext): Promise<{
69
+ repo: string | null;
70
+ main: string;
71
+ roles: {
72
+ teamsNotRead?: string[] | undefined;
73
+ designApproval: string[];
74
+ techApproval: string[];
75
+ ship: string;
76
+ };
77
+ ownerOverride: {
78
+ on: boolean;
79
+ who: string[];
80
+ reason: string;
81
+ };
82
+ protection: string;
83
+ products: {
84
+ key: string;
85
+ name: string;
86
+ default: boolean;
87
+ }[];
88
+ previews: string[];
89
+ visualImpact: import("@bridge-ds/core").VisualImpactMode;
90
+ note: string;
91
+ }>;
92
+ /** Which components are in Figma (Studio's "In Figma" column), and whether code images exist for the fidelity check. */
93
+ export declare function figmaCoverage(ctx: BridgeContext, a?: {
94
+ component?: string | undefined;
95
+ }): Promise<{
96
+ components: never[];
97
+ summary: string;
98
+ fidelity?: undefined;
99
+ } | {
100
+ summary: string;
101
+ components: {
102
+ why?: string | undefined;
103
+ name: string;
104
+ inFigma: string;
105
+ }[];
106
+ fidelity: string;
107
+ }>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bridge-ds/mcp",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "description": "Bridge MCP server: lets Claude (Claude Code, Claude Desktop or any MCP client) read, check and propose changes to a DTCG design system repo through pull requests.",
6
6
  "license": "MIT",
@@ -54,8 +54,8 @@
54
54
  "build:package": "node ../../scripts/build-packages.mjs mcp"
55
55
  },
56
56
  "dependencies": {
57
- "@bridge-ds/core": "^0.5.0",
58
- "@bridge-ds/visual": "^0.2.3",
57
+ "@bridge-ds/core": "^0.7.0",
58
+ "@bridge-ds/visual": "^0.3.1",
59
59
  "@modelcontextprotocol/sdk": "1.31.0",
60
60
  "typescript": "5.9.3",
61
61
  "zod": "3.25.76"