@bridge-ds/mcp 0.3.3 → 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-7HYTZVRW.js";
7
- import "./chunk-K3HY24MZ.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-7HYTZVRW.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-K3HY24MZ.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-K3HY24MZ.js";
14
+ } from "./chunk-7DBLDJIL.js";
11
15
 
12
16
  // ../visual/src/run.ts
13
17
  import { existsSync as existsSync3 } from "node:fs";
@@ -123,148 +127,6 @@ function impactedModules(index, input) {
123
127
  return { modules, globalHits };
124
128
  }
125
129
 
126
- // ../core/src/component-spec.ts
127
- var COMPONENT_SPECS_VERSION = 1;
128
- var variantKeyOf = (props) => Object.keys(props).sort().map((k) => `${k}=${props[k]}`).join(", ") || "Default";
129
- function parseComponentSpecs(text) {
130
- let data;
131
- try {
132
- data = JSON.parse(text);
133
- } catch {
134
- throw new Error("component-specs.json is not valid JSON.");
135
- }
136
- const f = data;
137
- if (!f || typeof f !== "object" || !Array.isArray(f.components)) throw new Error('component-specs.json has no "components" list.');
138
- if (f.version !== COMPONENT_SPECS_VERSION) throw new Error(`component-specs.json is version ${String(f.version)}; this tool reads version ${COMPONENT_SPECS_VERSION}.`);
139
- return f;
140
- }
141
-
142
- // ../core/src/fidelity-references.ts
143
- var FIGMA_REFERENCES_DIR = "figma-references";
144
- var FIGMA_REFERENCES_MANIFEST = `${FIGMA_REFERENCES_DIR}/manifest.json`;
145
- var FIGMA_REFERENCE_STORY = "internal-figma-reference--variant";
146
- var FIGMA_FILL_WIDTH_TOKEN = "size.80";
147
- var STATE_PSEUDO = {
148
- default: "rest",
149
- rest: "rest",
150
- enabled: "rest",
151
- hover: "hover",
152
- hovered: "hover",
153
- pressed: "active",
154
- active: "active",
155
- focus: "focus-visible",
156
- focused: "focus-visible",
157
- "focus visible": "focus-visible",
158
- disabled: "disabled"
159
- };
160
- var iconName = (component) => component.replace(/^Icon\//, "");
161
- var exportName = (name) => name.replace(/[^A-Za-z0-9]+/g, "");
162
- function planReference(spec, variant, file) {
163
- if (spec.view === "popup") return { ok: false, reason: "drawn open in Figma; the code image shows it closed" };
164
- const props = { ...spec.fixed ?? {} };
165
- let pseudo;
166
- for (const axis of spec.axes) {
167
- const value = variant.props[axis.name];
168
- if (value === void 0) return { ok: false, reason: `the variant has no ${axis.name} value` };
169
- if (axis.prop) {
170
- props[axis.prop] = value === "true" ? true : value === "false" ? false : value;
171
- continue;
172
- }
173
- if (axis.name === "Checked") {
174
- props.checked = value === "true";
175
- if (value === "mixed") props.indeterminate = true;
176
- continue;
177
- }
178
- if (axis.name.toLowerCase() !== "state") return { ok: false, reason: `${axis.name} has no matching option in code` };
179
- const s = STATE_PSEUDO[value.toLowerCase()];
180
- if (!s) return { ok: false, reason: `the ${value} state has no code equivalent to render` };
181
- if (s === "disabled") props.disabled = true;
182
- else if (s !== "rest") pseudo = s;
183
- }
184
- const root = spec.parts.find((p) => !p.parent);
185
- if (!root) return { ok: false, reason: "the spec has no root layer" };
186
- const children = (id) => spec.parts.filter((p) => p.parent === id);
187
- const textOf = (part) => {
188
- const swapTo = variant.slots?.[part.id] ?? part.swap?.default;
189
- if (part.swap && swapTo) {
190
- const slot = file?.slots?.find((s) => s.name === swapTo);
191
- if (!slot) return { reason: `${swapTo} is not in the specs` };
192
- if (slot.kind !== "text" || !slot.text) return { reason: `Figma shows a placeholder (${swapTo}) that code has no example of` };
193
- return slot.text.default;
194
- }
195
- const prop = spec.properties.find((p) => p.name === part.text && p.type === "TEXT");
196
- return typeof prop?.default === "string" ? prop.default : part.name;
197
- };
198
- let text;
199
- const icons = {};
200
- for (const prop of spec.properties) {
201
- if (prop.type === "TEXT") {
202
- const hidden = spec.properties.some((b) => b.type === "BOOLEAN" && b.prop === prop.prop && b.default === false);
203
- if (hidden && prop.prop) continue;
204
- if (prop.prop === "children") text = String(prop.default);
205
- else if (prop.prop) props[prop.prop] = String(prop.default);
206
- continue;
207
- }
208
- const iconPart = spec.parts.find((p) => p.kind === "icon" && p.visible === prop.name);
209
- if (prop.default === true && !iconPart && prop.prop && props[prop.prop] === void 0 && spec.example?.p?.[prop.prop] === void 0) props[prop.prop] = true;
210
- }
211
- for (const part of spec.parts) {
212
- if (part.kind !== "icon" || !part.swap) continue;
213
- const shown = part.visible ? spec.properties.find((p) => p.name === part.visible)?.default === true : true;
214
- if (!shown) continue;
215
- if (!part.swap.prop) return { ok: false, reason: `the ${part.name} icon has no matching option in code` };
216
- icons[part.swap.prop] = iconName(variant.slots?.[part.id] ?? part.swap.default);
217
- }
218
- let parts;
219
- const loose = spec.properties.filter((p) => p.type === "TEXT" && !p.prop);
220
- const frames = children(root.id).filter((p) => p.kind === "frame");
221
- const compound = text === void 0 && frames.length > 0 && frames.every((f) => spec.parts.some((p) => p.parent === f.id && p.kind === "text" && (p.swap || p.text === f.name)));
222
- if (compound) {
223
- parts = [];
224
- for (const f of frames) {
225
- const t = spec.parts.find((p) => p.parent === f.id && p.kind === "text");
226
- const value = textOf(t);
227
- if (typeof value !== "string") return { ok: false, reason: value.reason };
228
- parts.push({ component: `${exportName(spec.name)}${exportName(f.name)}`, text: value });
229
- }
230
- }
231
- for (const p of loose) {
232
- if (p.name === "Value") continue;
233
- const part = spec.parts.find((x) => x.text === p.name);
234
- if (compound && part && part.parent !== root.id) continue;
235
- if (part && /placeholder$/.test(part.id)) {
236
- props.placeholder = String(p.default);
237
- continue;
238
- }
239
- return { ok: false, reason: `the ${p.name} text has no matching option in code` };
240
- }
241
- const example = spec.example;
242
- const exampleChildren = example?.children?.length ? example.children : void 0;
243
- return {
244
- ok: true,
245
- render: {
246
- component: exportName(spec.view || spec.fixed || example ? example?.c ?? spec.component ?? spec.name : spec.name),
247
- props: { ...example?.p ?? {}, ...props },
248
- ...exampleChildren ? { children: exampleChildren } : {},
249
- ...text !== void 0 && !exampleChildren ? { text } : {},
250
- ...parts && !exampleChildren ? { parts } : {},
251
- ...Object.keys(icons).length ? { icons } : {},
252
- ...pseudo ? { pseudo } : {},
253
- ...root.fill ? { width: FIGMA_FILL_WIDTH_TOKEN } : {}
254
- }
255
- };
256
- }
257
- function referenceImagePath(component, key) {
258
- const slug = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
259
- const file = key.split(",").map((kv) => slug(kv)).filter(Boolean).join("__");
260
- return `${slug(component)}/${file || "default"}.png`;
261
- }
262
- function planReferences(file) {
263
- return file.components.flatMap(
264
- (spec) => spec.variants.map((v) => ({ component: spec.name, specHash: spec.hash, key: variantKeyOf(v.props), plan: planReference(spec, v, file) }))
265
- );
266
- }
267
-
268
130
  // ../visual/src/capture.ts
269
131
  import { mkdir, writeFile } from "node:fs/promises";
270
132
  import { dirname } from "node:path";
@@ -722,9 +584,9 @@ function selectImpact(input) {
722
584
  app
723
585
  };
724
586
  }
725
- function storyId(title, exportName2) {
587
+ function storyId(title, exportName) {
726
588
  const sanitize = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-+/, "").replace(/-+$/, "");
727
- const startCase = exportName2.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z])([A-Z][a-z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
589
+ const startCase = exportName.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z])([A-Z][a-z])/g, "$1 $2").replace(/[_-]+/g, " ").trim();
728
590
  return `${sanitize(title)}--${sanitize(startCase)}`;
729
591
  }
730
592
  function storiesFromSource(files) {
@@ -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.3",
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.6.0",
58
- "@bridge-ds/visual": "^0.3.0",
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"