@bridge-ds/cli 0.7.0 → 0.9.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 +501 -172
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1813,10 +1813,10 @@ ${" ".repeat(column - 1)}${expr}${suffix}` : `${prefix}${expr}${suffix}`;
|
|
|
1813
1813
|
out.themeRefs += r.themeRefs;
|
|
1814
1814
|
return;
|
|
1815
1815
|
}
|
|
1816
|
-
const
|
|
1817
|
-
if (
|
|
1818
|
-
const body =
|
|
1819
|
-
const r = auditCssText(body, "css", ds, { pos, offset: a.valueStart +
|
|
1816
|
+
const str2 = /^(\s*)([`'"])([\s\S]*)\2\s*$/.exec(a.value);
|
|
1817
|
+
if (str2) {
|
|
1818
|
+
const body = str2[3].replace(/\$\{[^}]*\}/g, (m) => " ".repeat(m.length));
|
|
1819
|
+
const r = auditCssText(body, "css", ds, { pos, offset: a.valueStart + str2[1].length + 1, source: "style" });
|
|
1820
1820
|
out.findings.push(...r.findings);
|
|
1821
1821
|
out.tokenUsages += r.tokenUsages;
|
|
1822
1822
|
out.themeRefs += r.themeRefs;
|
|
@@ -2176,8 +2176,8 @@ function parseOwnerEntry(raw) {
|
|
|
2176
2176
|
var teamKey = (org, slug) => `@${org.toLowerCase()}/${slug.toLowerCase()}`;
|
|
2177
2177
|
function ownerEntryProblems(raw) {
|
|
2178
2178
|
if (raw === void 0) return [];
|
|
2179
|
-
const
|
|
2180
|
-
if (!
|
|
2179
|
+
const isObj3 = (x) => Boolean(x) && typeof x === "object" && !Array.isArray(x);
|
|
2180
|
+
if (!isObj3(raw)) return ['"governance.owners" must be { "design": [people or teams], "tech": [people or teams] }'];
|
|
2181
2181
|
const out = [];
|
|
2182
2182
|
for (const [k, v] of Object.entries(raw)) {
|
|
2183
2183
|
if (k !== "design" && k !== "tech") {
|
|
@@ -2562,7 +2562,8 @@ function defineConfig(input) {
|
|
|
2562
2562
|
minReasonLength: g.override.minReasonLength ?? DEFAULT_OVERRIDE.minReasonLength
|
|
2563
2563
|
}
|
|
2564
2564
|
} : {},
|
|
2565
|
-
...g.rights ? { rights: normalizeRights(g.rights) } : {}
|
|
2565
|
+
...g.rights ? { rights: normalizeRights(g.rights) } : {},
|
|
2566
|
+
...g.soloOwnerCanApproveBoth === true ? { soloOwnerCanApproveBoth: true } : {}
|
|
2566
2567
|
},
|
|
2567
2568
|
products: input.products?.length ? input.products.map((p) => ({ ...p })) : DEFAULT_PRODUCTS.map((p) => ({ ...p })),
|
|
2568
2569
|
changes: input.changes === "direct" ? "direct" : DEFAULT_CHANGES_MODE,
|
|
@@ -2686,6 +2687,7 @@ function loadConfig(source) {
|
|
|
2686
2687
|
}
|
|
2687
2688
|
}
|
|
2688
2689
|
problems.push(...rightsProblems(gov.rights));
|
|
2690
|
+
if (gov.soloOwnerCanApproveBoth !== void 0 && typeof gov.soloOwnerCanApproveBoth !== "boolean") problems.push('"governance.soloOwnerCanApproveBoth" must be true or false');
|
|
2689
2691
|
}
|
|
2690
2692
|
}
|
|
2691
2693
|
problems.push(...visualImpactProblems(d.visualImpact));
|
|
@@ -3336,25 +3338,25 @@ var GitHubClient = class _GitHubClient {
|
|
|
3336
3338
|
return pr ? { number: pr.number, url: pr.html_url, title: pr.title, draft: !!pr.draft, headRef: pr.head.ref } : void 0;
|
|
3337
3339
|
}
|
|
3338
3340
|
/** Best effort: labels need the repo to allow it; failure is not fatal. */
|
|
3339
|
-
async addLabels(
|
|
3341
|
+
async addLabels(issueNumber3, labels) {
|
|
3340
3342
|
try {
|
|
3341
|
-
await this.request("POST", `${this.repoPath}/issues/${
|
|
3343
|
+
await this.request("POST", `${this.repoPath}/issues/${issueNumber3}/labels`, { labels });
|
|
3342
3344
|
return true;
|
|
3343
3345
|
} catch {
|
|
3344
3346
|
return false;
|
|
3345
3347
|
}
|
|
3346
3348
|
}
|
|
3347
|
-
async addComment(
|
|
3348
|
-
await this.request("POST", `${this.repoPath}/issues/${
|
|
3349
|
+
async addComment(issueNumber3, body) {
|
|
3350
|
+
await this.request("POST", `${this.repoPath}/issues/${issueNumber3}/comments`, { body });
|
|
3349
3351
|
}
|
|
3350
3352
|
/**
|
|
3351
3353
|
* Comments on an issue or pull request, oldest first (first 100: the sticky preview comment is posted early).
|
|
3352
3354
|
* `authorAssociation` is GitHub's OWNER, MEMBER, COLLABORATOR, CONTRIBUTOR, NONE... (suggestion approvals read it).
|
|
3353
3355
|
*/
|
|
3354
|
-
async listIssueComments(
|
|
3356
|
+
async listIssueComments(issueNumber3) {
|
|
3355
3357
|
const r = await this.request(
|
|
3356
3358
|
"GET",
|
|
3357
|
-
`${this.repoPath}/issues/${
|
|
3359
|
+
`${this.repoPath}/issues/${issueNumber3}/comments?per_page=100`
|
|
3358
3360
|
);
|
|
3359
3361
|
return r.map((c) => ({ id: c.id, body: c.body ?? "", author: c.user?.login ?? "unknown", updatedAt: c.updated_at, createdAt: c.created_at ?? c.updated_at, authorAssociation: c.author_association ?? "NONE" }));
|
|
3360
3362
|
}
|
|
@@ -3671,8 +3673,8 @@ var GitHubClient = class _GitHubClient {
|
|
|
3671
3673
|
});
|
|
3672
3674
|
return { id: r.id, name: r.name, status: r.status, conclusion: r.conclusion, url: r.html_url };
|
|
3673
3675
|
}
|
|
3674
|
-
async removeLabel(
|
|
3675
|
-
await this.request("DELETE", `${this.repoPath}/issues/${
|
|
3676
|
+
async removeLabel(issueNumber3, label) {
|
|
3677
|
+
await this.request("DELETE", `${this.repoPath}/issues/${issueNumber3}/labels/${encodeURIComponent(label)}`);
|
|
3676
3678
|
}
|
|
3677
3679
|
// --- branch actions shared by Studio, the MCP server and the CLI (branch-actions.ts) ---
|
|
3678
3680
|
/** The account the token belongs to (GET /user). Needs a token. */
|
|
@@ -3901,6 +3903,27 @@ function jiraIssueFields(r, jira, issueUrl) {
|
|
|
3901
3903
|
};
|
|
3902
3904
|
}
|
|
3903
3905
|
|
|
3906
|
+
// ../core/src/suggestions.ts
|
|
3907
|
+
var SUGGESTION_STATUS_LABEL = {
|
|
3908
|
+
"needs-review": "Needs review",
|
|
3909
|
+
"changes-requested": "Changes requested",
|
|
3910
|
+
approved: "Approved, ready to apply",
|
|
3911
|
+
applying: "Being applied",
|
|
3912
|
+
applied: "Applied",
|
|
3913
|
+
declined: "Declined",
|
|
3914
|
+
outdated: "Outdated"
|
|
3915
|
+
};
|
|
3916
|
+
|
|
3917
|
+
// ../core/src/suggestion-apply.ts
|
|
3918
|
+
var SUGGESTION_APPLY_EXIT = {
|
|
3919
|
+
ready: 0,
|
|
3920
|
+
"already-applied": 0,
|
|
3921
|
+
conflict: 3,
|
|
3922
|
+
"not-approved": 1,
|
|
3923
|
+
blocked: 1,
|
|
3924
|
+
refused: 1
|
|
3925
|
+
};
|
|
3926
|
+
|
|
3904
3927
|
// src/index.ts
|
|
3905
3928
|
import { fileURLToPath } from "node:url";
|
|
3906
3929
|
|
|
@@ -4230,6 +4253,81 @@ function planReferences(file) {
|
|
|
4230
4253
|
);
|
|
4231
4254
|
}
|
|
4232
4255
|
|
|
4256
|
+
// ../core/src/storybook.ts
|
|
4257
|
+
var StorybookIndexError = class extends Error {
|
|
4258
|
+
constructor(code, message, status) {
|
|
4259
|
+
super(message);
|
|
4260
|
+
this.code = code;
|
|
4261
|
+
this.status = status;
|
|
4262
|
+
this.name = "StorybookIndexError";
|
|
4263
|
+
}
|
|
4264
|
+
code;
|
|
4265
|
+
status;
|
|
4266
|
+
};
|
|
4267
|
+
var isObj2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
4268
|
+
var str = (v) => typeof v === "string" && v.trim() ? v : void 0;
|
|
4269
|
+
function entryFrom(key, raw, v3) {
|
|
4270
|
+
if (!isObj2(raw)) return null;
|
|
4271
|
+
const id = str(raw.id) ?? key;
|
|
4272
|
+
const title = str(raw.title) ?? str(raw.kind);
|
|
4273
|
+
const name = str(raw.name) ?? str(raw.story);
|
|
4274
|
+
if (!id || !title || !name) return null;
|
|
4275
|
+
const params = isObj2(raw.parameters) ? raw.parameters : {};
|
|
4276
|
+
const type = raw.type === "docs" || raw.type === "story" ? raw.type : v3 && params.docsOnly === true ? "docs" : "story";
|
|
4277
|
+
const e = {
|
|
4278
|
+
id,
|
|
4279
|
+
title,
|
|
4280
|
+
name,
|
|
4281
|
+
type,
|
|
4282
|
+
importPath: str(raw.importPath) ?? str(params.fileName) ?? "",
|
|
4283
|
+
tags: Array.isArray(raw.tags) ? raw.tags.filter((t) => typeof t === "string") : []
|
|
4284
|
+
};
|
|
4285
|
+
const subtype = str(raw.subtype);
|
|
4286
|
+
if (subtype) e.subtype = subtype;
|
|
4287
|
+
const exportName2 = str(raw.exportName);
|
|
4288
|
+
if (exportName2) e.exportName = exportName2;
|
|
4289
|
+
const componentPath = str(raw.componentPath);
|
|
4290
|
+
if (componentPath) e.componentPath = componentPath;
|
|
4291
|
+
return e;
|
|
4292
|
+
}
|
|
4293
|
+
function groupStorybookComponents(entries) {
|
|
4294
|
+
const byTitle = /* @__PURE__ */ new Map();
|
|
4295
|
+
for (const e of entries) {
|
|
4296
|
+
let c = byTitle.get(e.title);
|
|
4297
|
+
if (!c) {
|
|
4298
|
+
const parts = e.title.split("/").map((p) => p.trim()).filter(Boolean);
|
|
4299
|
+
c = { title: e.title, name: parts[parts.length - 1] ?? e.title, group: parts.slice(0, -1), stories: [], docs: [], tags: [] };
|
|
4300
|
+
byTitle.set(e.title, c);
|
|
4301
|
+
}
|
|
4302
|
+
if (e.type === "docs") c.docs.push(e);
|
|
4303
|
+
else if (e.subtype !== "test") c.stories.push(e);
|
|
4304
|
+
for (const t of e.tags) if (!c.tags.includes(t)) c.tags.push(t);
|
|
4305
|
+
}
|
|
4306
|
+
const out = [...byTitle.values()].filter((c) => c.stories.length || c.docs.length);
|
|
4307
|
+
for (const c of out) c.tags.sort();
|
|
4308
|
+
return out.sort((a, b) => a.title.localeCompare(b.title));
|
|
4309
|
+
}
|
|
4310
|
+
function parseStorybookIndex(json, source) {
|
|
4311
|
+
if (!isObj2(json)) throw new StorybookIndexError("not-json", "The Storybook index is not a JSON object.");
|
|
4312
|
+
if (typeof json.loginUrl === "string" && !("entries" in json) && !("stories" in json)) {
|
|
4313
|
+
throw new StorybookIndexError("private", "This Storybook is private: it asks for a sign-in before showing its index.");
|
|
4314
|
+
}
|
|
4315
|
+
const map = isObj2(json.entries) ? json.entries : isObj2(json.stories) ? json.stories : null;
|
|
4316
|
+
if (!map) throw new StorybookIndexError("empty", "This file has no Storybook entries.");
|
|
4317
|
+
const v3 = !isObj2(json.entries);
|
|
4318
|
+
const entries = Object.entries(map).map(([k, raw]) => entryFrom(k, raw, v3)).filter((e) => e !== null);
|
|
4319
|
+
if (!entries.length) throw new StorybookIndexError("empty", "This Storybook index lists no stories or docs.");
|
|
4320
|
+
const version = typeof json.v === "number" ? json.v : v3 ? 3 : 4;
|
|
4321
|
+
return { version, source: source ?? (v3 ? "stories.json" : "index.json"), entries, components: groupStorybookComponents(entries) };
|
|
4322
|
+
}
|
|
4323
|
+
var pairs = (o) => Object.entries(o).map(([k, v]) => `${encodeURIComponent(k)}:${encodeURIComponent(v)}`).join(";");
|
|
4324
|
+
function storybookIframeUrl(base, id, o = {}) {
|
|
4325
|
+
let url = `${base.replace(/\/+$/, "")}/iframe.html?id=${encodeURIComponent(id)}&viewMode=${o.viewMode ?? "story"}`;
|
|
4326
|
+
if (o.globals && Object.keys(o.globals).length) url += `&globals=${pairs(o.globals)}`;
|
|
4327
|
+
if (o.args && Object.keys(o.args).length) url += `&args=${pairs(o.args)}`;
|
|
4328
|
+
return url;
|
|
4329
|
+
}
|
|
4330
|
+
|
|
4233
4331
|
// ../visual/src/capture.ts
|
|
4234
4332
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
4235
4333
|
import { dirname as dirname2 } from "node:path";
|
|
@@ -4624,7 +4722,7 @@ function routeUrl(base, route, tenant, theme) {
|
|
|
4624
4722
|
return `${base.replace(/\/$/, "")}/?tenant=${encodeURIComponent(tenant)}&theme=${encodeURIComponent(theme)}&density=comfortable&lang=en#${route.pathByTenant?.[tenant] ?? route.path}`;
|
|
4625
4723
|
}
|
|
4626
4724
|
function storyUrl(base, id, brand2, theme) {
|
|
4627
|
-
return
|
|
4725
|
+
return storybookIframeUrl(base, id, { globals: { brand: brand2, theme, density: "comfortable" } });
|
|
4628
4726
|
}
|
|
4629
4727
|
var shotKey = (...parts) => parts.map((p) => p.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-|-$/g, "")).join("__");
|
|
4630
4728
|
|
|
@@ -4915,8 +5013,8 @@ function changedTokens(before, after) {
|
|
|
4915
5013
|
return [...modes.entries()].map(([path, m]) => ({ path, kind: direct.has(path) ? "direct" : "alias", modes: m })).sort((a, b) => Number(a.kind === "alias") - Number(b.kind === "alias") || a.path.localeCompare(b.path));
|
|
4916
5014
|
}
|
|
4917
5015
|
async function readIndexJson(dir) {
|
|
4918
|
-
const
|
|
4919
|
-
return
|
|
5016
|
+
const index = parseStorybookIndex(JSON.parse(await readFile3(join4(dir, "index.json"), "utf8")), "index.json");
|
|
5017
|
+
return index.entries.filter((e) => e.type === "story").map(({ id, title, name, importPath }) => ({ id, title, name, importPath }));
|
|
4920
5018
|
}
|
|
4921
5019
|
async function planImpact(o) {
|
|
4922
5020
|
const root = resolve2(o.root);
|
|
@@ -5710,7 +5808,196 @@ ${EXPLORE_HELP}`);
|
|
|
5710
5808
|
// src/branch.ts
|
|
5711
5809
|
import { readFile as readFile6 } from "node:fs/promises";
|
|
5712
5810
|
import { resolve as resolve3 } from "node:path";
|
|
5713
|
-
import { BridgeContext as
|
|
5811
|
+
import { BridgeContext as BridgeContext5, branches, studio } from "@bridge-ds/mcp";
|
|
5812
|
+
|
|
5813
|
+
// src/suggest.ts
|
|
5814
|
+
import { BridgeContext as BridgeContext4, suggestions } from "@bridge-ds/mcp";
|
|
5815
|
+
var SuggestUsageError = class extends Error {
|
|
5816
|
+
};
|
|
5817
|
+
var SUGGESTIONS_HELP = `bridge suggestions: changes filed as GitHub issues (label bridge-suggestion), applied by a developer
|
|
5818
|
+
|
|
5819
|
+
Usage:
|
|
5820
|
+
bridge suggestions [list] [--status needs-review|changes-requested|approved|applying|applied|declined|outdated|all]
|
|
5821
|
+
[--kind token-change|docs|component-spec|brand|revert|config] [--json]
|
|
5822
|
+
bridge suggestions show <n> [--json]
|
|
5823
|
+
the change, the checks, its version, who approved it and what is missing
|
|
5824
|
+
bridge suggestions link <n> --pr <m> | --branch <name>
|
|
5825
|
+
record the pull request that applies it (it must say "Closes #<n>")
|
|
5826
|
+
bridge apply <n> [--write | --patch] [--dry-run] [--accept-edited] [--force-unapproved] [--branch name] [--json]
|
|
5827
|
+
apply it on today's code: a new local branch by default (working tree untouched, nothing
|
|
5828
|
+
pushed), --write into the working tree, --patch prints a patch for git am. --dry-run shows
|
|
5829
|
+
the plan and the diff and writes nothing
|
|
5830
|
+
bridge review <n> --approve | --request-changes --message "..." | --decline --message "..." [--version <sha>]
|
|
5831
|
+
as the owner your GitHub token belongs to; counts only for owners in bridge.config.json
|
|
5832
|
+
|
|
5833
|
+
GitHub token: GITHUB_TOKEN or BRIDGE_GITHUB_TOKEN, else \`gh auth token\`. Reading a public repo works without one.
|
|
5834
|
+
Exit codes (apply): 0 applied, already applied or a clean dry run; 1 not approved, blocked by a check or refused;
|
|
5835
|
+
2 usage or setup error; 3 conflict with today's code.`;
|
|
5836
|
+
var flag3 = (a, k) => a.flags[k]?.[a.flags[k].length - 1];
|
|
5837
|
+
var has3 = (a, k) => Boolean(a.flags[k]);
|
|
5838
|
+
function changesFlag(a) {
|
|
5839
|
+
if (has3(a, "suggest") && has3(a, "direct")) throw new SuggestUsageError("Pass --suggest or --direct, not both.");
|
|
5840
|
+
return has3(a, "direct") ? "direct" : has3(a, "suggest") ? "suggest" : void 0;
|
|
5841
|
+
}
|
|
5842
|
+
function tokenFrom2(io) {
|
|
5843
|
+
return io.env.BRIDGE_GITHUB_TOKEN || io.env.GITHUB_TOKEN || io.ghToken();
|
|
5844
|
+
}
|
|
5845
|
+
async function context3(root, io) {
|
|
5846
|
+
const token = tokenFrom2(io);
|
|
5847
|
+
return BridgeContext4.create({ root, ...token ? { token } : {}, ...io.fetch ? { fetch: io.fetch } : {} });
|
|
5848
|
+
}
|
|
5849
|
+
function issueNumber2(raw, usage) {
|
|
5850
|
+
const n = Number((raw ?? "").replace(/^#/, ""));
|
|
5851
|
+
if (!raw || !Number.isInteger(n) || n <= 0) throw new SuggestUsageError(`Usage: ${usage}`);
|
|
5852
|
+
return n;
|
|
5853
|
+
}
|
|
5854
|
+
var STATUSES = ["needs-review", "changes-requested", "approved", "applying", "applied", "declined", "outdated", "all"];
|
|
5855
|
+
var KINDS = ["token-change", "docs", "component-spec", "brand", "revert", "config"];
|
|
5856
|
+
async function runSuggestions(a, root, io = defaultIO()) {
|
|
5857
|
+
const sub = a._[1] ?? "list";
|
|
5858
|
+
if (sub === "help" || has3(a, "help")) return io.print(SUGGESTIONS_HELP), 0;
|
|
5859
|
+
switch (sub) {
|
|
5860
|
+
case "list": {
|
|
5861
|
+
const status = flag3(a, "status") ?? "all";
|
|
5862
|
+
if (!STATUSES.includes(status)) throw new SuggestUsageError(`--status takes ${STATUSES.join(", ")}, got "${status}"`);
|
|
5863
|
+
const kind = flag3(a, "kind");
|
|
5864
|
+
if (kind && !KINDS.includes(kind)) throw new SuggestUsageError(`--kind takes ${KINDS.join(", ")}, got "${kind}"`);
|
|
5865
|
+
const ctx = await context3(root, io);
|
|
5866
|
+
const r = await suggestions.listSuggestionsOp(ctx, { status, ...kind ? { kind } : {} });
|
|
5867
|
+
if (has3(a, "json")) return io.print(JSON.stringify(r, null, 2)), 0;
|
|
5868
|
+
if (!r.suggestions.length) return io.print(`No ${status === "all" ? "open " : `${status} `}suggestion in ${r.repo}.`), 0;
|
|
5869
|
+
for (const x of r.suggestions) {
|
|
5870
|
+
const prs = x.linkedPrs.length ? ` PR ${x.linkedPrs.map((p) => `#${p.number}${p.merged ? " merged" : ` ${p.state}`}`).join(", ")}` : "";
|
|
5871
|
+
io.print(`#${String(x.number).padEnd(5)} ${x.said.padEnd(25)} ${(x.kind ?? "").padEnd(14)} ${x.tier ? `tier ${x.tier}` : " "} ${x.title}${prs}`);
|
|
5872
|
+
}
|
|
5873
|
+
io.print(`
|
|
5874
|
+
${r.next}`);
|
|
5875
|
+
return 0;
|
|
5876
|
+
}
|
|
5877
|
+
case "show": {
|
|
5878
|
+
const n = issueNumber2(a._[2], "bridge suggestions show <number>");
|
|
5879
|
+
const ctx = await context3(root, io);
|
|
5880
|
+
const r = await suggestions.getSuggestionOp(ctx, { number: n });
|
|
5881
|
+
if (has3(a, "json")) return io.print(JSON.stringify(r, null, 2)), 0;
|
|
5882
|
+
io.print(`#${r.number} ${r.title}`);
|
|
5883
|
+
io.print(`${r.said}${r.kind ? `, ${r.kind}` : ""}${r.tier ? `, tier ${r.tier} (${r.bump})` : ""}${r.source ? `, from ${r.source}` : ""}${r.suggestedBy ? ` by @${r.suggestedBy}` : ""}`);
|
|
5884
|
+
io.print(r.url);
|
|
5885
|
+
if (r.problem) {
|
|
5886
|
+
io.print(`
|
|
5887
|
+
${r.problem}`);
|
|
5888
|
+
return 1;
|
|
5889
|
+
}
|
|
5890
|
+
if (r.summary) io.print(`
|
|
5891
|
+
${r.summary}`);
|
|
5892
|
+
const c = r.change ?? {};
|
|
5893
|
+
if (c.tokens?.length) {
|
|
5894
|
+
io.print("\nTokens:");
|
|
5895
|
+
for (const t of c.tokens) io.print(` ${t.kind ?? t.op} ${t.path}${t.before !== void 0 || t.after !== void 0 ? `: ${fmt(t.before)} -> ${fmt(t.after)}` : ""}`);
|
|
5896
|
+
}
|
|
5897
|
+
for (const d of c.docs ?? []) io.print(`Docs: ${d.component} (${d.path})`);
|
|
5898
|
+
if (c.componentSpec) io.print(`Component spec: ${c.componentSpec.component} (${c.componentSpec.specPath})`);
|
|
5899
|
+
for (const f of c.files ?? []) io.print(`File: ${f.change} ${f.path}`);
|
|
5900
|
+
io.print(`
|
|
5901
|
+
Version ${r.version}${r.edited ? ` (${r.edited})` : ""}`);
|
|
5902
|
+
io.print(`Approved by: ${r.approvals.approvedBy.map((l) => `@${l}`).join(", ") || "nobody yet"}`);
|
|
5903
|
+
if (r.approvals.missing.length) io.print(`Missing: ${r.approvals.missing.join(" and ")}`);
|
|
5904
|
+
if (r.approvals.soloOwner) io.print(r.approvals.soloOwner);
|
|
5905
|
+
if (r.approvals.changesRequestedBy.length) io.print(`Changes requested by: ${r.approvals.changesRequestedBy.map((l) => `@${l}`).join(", ")}`);
|
|
5906
|
+
for (const p of r.linkedPrs ?? []) io.print(`Pull request: #${p.number} ${p.merged ? "merged" : p.state} ${p.url}`);
|
|
5907
|
+
io.print(`
|
|
5908
|
+
Next: ${r.next}`);
|
|
5909
|
+
return 0;
|
|
5910
|
+
}
|
|
5911
|
+
case "link": {
|
|
5912
|
+
const n = issueNumber2(a._[2], "bridge suggestions link <number> --pr <m>");
|
|
5913
|
+
const prRaw = flag3(a, "pr");
|
|
5914
|
+
const pr = prRaw ? issueNumber2(prRaw, "bridge suggestions link <number> --pr <m>") : void 0;
|
|
5915
|
+
const branch = flag3(a, "branch");
|
|
5916
|
+
if (!pr && !branch) throw new SuggestUsageError("Usage: bridge suggestions link <number> --pr <m> (or --branch <name>)");
|
|
5917
|
+
const ctx = await context3(root, io);
|
|
5918
|
+
const r = await suggestions.linkSuggestionOp(ctx, { number: n, ...pr ? { pr } : {}, ...branch ? { branch } : {} });
|
|
5919
|
+
if (has3(a, "json")) return io.print(JSON.stringify(r, null, 2)), 0;
|
|
5920
|
+
io.print(`#${n} is being applied${pr ? ` in #${pr}` : branch ? ` on ${branch}` : ""}.`);
|
|
5921
|
+
for (const w of r.warnings ?? []) io.error(`warning: ${w}`);
|
|
5922
|
+
return 0;
|
|
5923
|
+
}
|
|
5924
|
+
default:
|
|
5925
|
+
throw new SuggestUsageError(`Unknown suggestions command "${sub}".
|
|
5926
|
+
|
|
5927
|
+
${SUGGESTIONS_HELP}`);
|
|
5928
|
+
}
|
|
5929
|
+
}
|
|
5930
|
+
var fmt = (v) => v === void 0 ? "(none)" : typeof v === "string" ? v : JSON.stringify(v);
|
|
5931
|
+
async function runApply(a, root, io = defaultIO()) {
|
|
5932
|
+
if (has3(a, "help") || a._[1] === "help") return io.print(SUGGESTIONS_HELP), 0;
|
|
5933
|
+
const n = issueNumber2(a._[1], "bridge apply <number> [--write | --patch] [--dry-run]");
|
|
5934
|
+
if (has3(a, "write") && has3(a, "patch")) throw new SuggestUsageError("Pass --write or --patch, not both.");
|
|
5935
|
+
const mode2 = has3(a, "write") ? "write" : has3(a, "patch") ? "patch" : "branch";
|
|
5936
|
+
const ctx = await context3(root, io);
|
|
5937
|
+
if (ctx.mode !== "local" || !ctx.isGit) throw new SuggestUsageError("bridge apply runs in a git checkout of the design system: cd into it, or pass --root.");
|
|
5938
|
+
const branch = flag3(a, "branch");
|
|
5939
|
+
const r = await suggestions.applySuggestionOp(ctx, {
|
|
5940
|
+
number: n,
|
|
5941
|
+
mode: has3(a, "dry-run") ? "dry-run" : mode2,
|
|
5942
|
+
acceptEdited: has3(a, "accept-edited"),
|
|
5943
|
+
forceUnapproved: has3(a, "force-unapproved"),
|
|
5944
|
+
...branch ? { branch } : {}
|
|
5945
|
+
});
|
|
5946
|
+
const code = typeof r.exitCode === "number" ? r.exitCode : SUGGESTION_APPLY_EXIT[r.outcome] ?? 1;
|
|
5947
|
+
if (has3(a, "json")) return io.print(JSON.stringify(r, null, 2)), code;
|
|
5948
|
+
if (mode2 === "patch" && r.patch && !has3(a, "dry-run")) {
|
|
5949
|
+
io.print(r.patch.trimEnd());
|
|
5950
|
+
io.error(r.message);
|
|
5951
|
+
for (const w of r.warnings ?? []) io.error(`warning: ${w}`);
|
|
5952
|
+
return code;
|
|
5953
|
+
}
|
|
5954
|
+
io.print(`#${n}: ${r.message}`);
|
|
5955
|
+
if (r.table) io.print(`
|
|
5956
|
+
${r.table}`);
|
|
5957
|
+
for (const p of r.problems ?? []) io.print(` ${p}`);
|
|
5958
|
+
for (const note of r.notes ?? []) io.print(` note: ${note}`);
|
|
5959
|
+
if (r.diff) io.print(`
|
|
5960
|
+
${r.diff.trimEnd()}`);
|
|
5961
|
+
if (r.brief) io.print(`
|
|
5962
|
+
Brief to implement it:
|
|
5963
|
+
${r.brief}`);
|
|
5964
|
+
if (r.branch) io.print(`
|
|
5965
|
+
branch: ${r.branch}${r.commit ? ` (${String(r.commit).slice(0, 7)})` : ""}`);
|
|
5966
|
+
for (const w of r.warnings ?? []) io.error(`warning: ${w}`);
|
|
5967
|
+
if (r.next && r.next !== r.message) io.print(`
|
|
5968
|
+
Next: ${r.next}`);
|
|
5969
|
+
return code;
|
|
5970
|
+
}
|
|
5971
|
+
async function runReview(a, root, io = defaultIO()) {
|
|
5972
|
+
if (has3(a, "help") || a._[1] === "help") return io.print(SUGGESTIONS_HELP), 0;
|
|
5973
|
+
const usage = 'bridge review <number> --approve | --request-changes --message "..." | --decline --message "..."';
|
|
5974
|
+
const n = issueNumber2(a._[1], usage);
|
|
5975
|
+
const events = ["approve", "request-changes", "decline", "comment"].filter((e) => has3(a, e));
|
|
5976
|
+
if (events.length !== 1) throw new SuggestUsageError(`Usage: ${usage}`);
|
|
5977
|
+
const event = events[0];
|
|
5978
|
+
const inline = flag3(a, event);
|
|
5979
|
+
const message = flag3(a, "message") ?? (inline && inline !== "true" ? inline : void 0);
|
|
5980
|
+
if ((event === "request-changes" || event === "decline" || event === "comment") && !message?.trim()) throw new SuggestUsageError(`--${event} needs --message "..." (the author reads it).`);
|
|
5981
|
+
const role = flag3(a, "role");
|
|
5982
|
+
if (role && !["design", "tech", "approval"].includes(role)) throw new SuggestUsageError(`--role takes design, tech or approval, got "${role}"`);
|
|
5983
|
+
const ctx = await context3(root, io);
|
|
5984
|
+
const version = flag3(a, "version");
|
|
5985
|
+
const r = await suggestions.reviewSuggestionOp(ctx, {
|
|
5986
|
+
number: n,
|
|
5987
|
+
event,
|
|
5988
|
+
...message ? { message } : {},
|
|
5989
|
+
...role ? { role } : {},
|
|
5990
|
+
...version ? { payloadSha: version } : {}
|
|
5991
|
+
});
|
|
5992
|
+
if (has3(a, "json")) return io.print(JSON.stringify(r, null, 2)), 0;
|
|
5993
|
+
const done = event === "approve" ? "Approved" : event === "request-changes" ? "Changes requested on" : event === "decline" ? "Declined" : "Commented on";
|
|
5994
|
+
io.print(`${done} #${n}${r.version ? ` (version ${r.version})` : ""}. Now: ${SUGGESTION_STATUS_LABEL[r.status] ?? r.status}.`);
|
|
5995
|
+
for (const w of r.warnings ?? []) io.error(`warning: ${w}`);
|
|
5996
|
+
if (r.next) io.print(`Next: ${r.next}`);
|
|
5997
|
+
return r.counts === false ? 1 : 0;
|
|
5998
|
+
}
|
|
5999
|
+
|
|
6000
|
+
// src/branch.ts
|
|
5714
6001
|
var BRANCH_HELP = `bridge branch: branches in review, the same actions as Bridge Studio
|
|
5715
6002
|
|
|
5716
6003
|
Usage:
|
|
@@ -5740,18 +6027,18 @@ Usage:
|
|
|
5740
6027
|
|
|
5741
6028
|
Start work for a product: bridge propose|explore start|request create ... --product <key> [--jira DS-42].
|
|
5742
6029
|
GitHub token: GITHUB_TOKEN or BRIDGE_GITHUB_TOKEN, else \`gh auth token\`. Exit codes: 0 ok, 2 usage or setup error.`;
|
|
5743
|
-
var
|
|
5744
|
-
var
|
|
5745
|
-
async function
|
|
6030
|
+
var flag4 = (a, k) => a.flags[k]?.[a.flags[k].length - 1];
|
|
6031
|
+
var has4 = (a, k) => Boolean(a.flags[k]);
|
|
6032
|
+
async function context4(root, io) {
|
|
5746
6033
|
const token = io.env.BRIDGE_GITHUB_TOKEN || io.env.GITHUB_TOKEN || io.ghToken();
|
|
5747
|
-
return
|
|
6034
|
+
return BridgeContext5.create({ root, ...token ? { token } : {}, ...io.fetch ? { fetch: io.fetch } : {} });
|
|
5748
6035
|
}
|
|
5749
6036
|
function number(a, at, usage) {
|
|
5750
6037
|
const raw = a._[at];
|
|
5751
6038
|
if (!raw) throw new RequestUsageError(`Usage: ${usage}`);
|
|
5752
6039
|
return branches.branchNumber(raw);
|
|
5753
6040
|
}
|
|
5754
|
-
function
|
|
6041
|
+
function pairs2(list2, what) {
|
|
5755
6042
|
const out = {};
|
|
5756
6043
|
for (const s of list2 ?? []) {
|
|
5757
6044
|
const i = s.lastIndexOf("=");
|
|
@@ -5763,16 +6050,16 @@ function pairs(list2, what) {
|
|
|
5763
6050
|
var said = (r) => String(r.said ?? r.summary ?? r.status ?? "Done.");
|
|
5764
6051
|
async function runBranch(a, root, io = defaultIO()) {
|
|
5765
6052
|
const sub = a._[1];
|
|
5766
|
-
if (!sub || sub === "help" ||
|
|
6053
|
+
if (!sub || sub === "help" || has4(a, "help")) {
|
|
5767
6054
|
io.print(BRANCH_HELP);
|
|
5768
|
-
return sub ||
|
|
6055
|
+
return sub || has4(a, "help") ? 0 : 2;
|
|
5769
6056
|
}
|
|
5770
|
-
const json =
|
|
5771
|
-
const ctx = await
|
|
6057
|
+
const json = has4(a, "json");
|
|
6058
|
+
const ctx = await context4(root, io);
|
|
5772
6059
|
const out = (r, text) => (json ? io.print(JSON.stringify(r, null, 2)) : text(), 0);
|
|
5773
6060
|
switch (sub) {
|
|
5774
6061
|
case "list": {
|
|
5775
|
-
const limit =
|
|
6062
|
+
const limit = flag4(a, "limit");
|
|
5776
6063
|
const r = await branches.whatNeedsMe(ctx, limit ? { limit: Number(limit) } : {});
|
|
5777
6064
|
return out(r, () => {
|
|
5778
6065
|
io.print(r.summary);
|
|
@@ -5784,6 +6071,7 @@ async function runBranch(a, root, io = defaultIO()) {
|
|
|
5784
6071
|
return out(r, () => {
|
|
5785
6072
|
io.print(`#${r.pr} ${r.title} (${r.product.name})`);
|
|
5786
6073
|
io.print(`${r.status.label}. ${r.approvalNeeded} needed; approved by ${r.approvals.approvedBy.map((x) => `@${x}`).join(", ") || "nobody yet"}.`);
|
|
6074
|
+
if (r.approvals.soloOwner) io.print(r.approvals.soloOwner);
|
|
5787
6075
|
if (r.merge.blockedBy?.length) for (const x of r.merge.blockedBy) io.print(` ${x}`);
|
|
5788
6076
|
io.print(`Next: ${r.next}`);
|
|
5789
6077
|
io.print(r.url);
|
|
@@ -5806,28 +6094,28 @@ async function runBranch(a, root, io = defaultIO()) {
|
|
|
5806
6094
|
}
|
|
5807
6095
|
case "review": {
|
|
5808
6096
|
const n = number(a, 2, 'bridge branch review <n> --approve | --request-changes "..." | --comment "..."');
|
|
5809
|
-
const decision =
|
|
6097
|
+
const decision = has4(a, "approve") ? "approve" : has4(a, "request-changes") ? "request_changes" : has4(a, "comment") ? "comment" : null;
|
|
5810
6098
|
if (!decision) throw new RequestUsageError('Say what you decide: --approve, --request-changes "what to change" or --comment "text".');
|
|
5811
|
-
const body = decision === "approve" ?
|
|
6099
|
+
const body = decision === "approve" ? flag4(a, "body") : flag4(a, decision === "request_changes" ? "request-changes" : "comment");
|
|
5812
6100
|
const r = await branches.reviewBranch(ctx, { pr: n, decision, ...body && body !== "true" ? { body } : {} });
|
|
5813
6101
|
return out(r, () => io.print(r.said));
|
|
5814
6102
|
}
|
|
5815
6103
|
case "ask": {
|
|
5816
|
-
const r = await branches.askSomeoneElse(ctx, { pr: number(a, 2, "bridge branch ask <n>"), dryRun:
|
|
6104
|
+
const r = await branches.askSomeoneElse(ctx, { pr: number(a, 2, "bridge branch ask <n>"), dryRun: has4(a, "dry-run") });
|
|
5817
6105
|
return out(r, () => io.print(r.said));
|
|
5818
6106
|
}
|
|
5819
6107
|
case "comment": {
|
|
5820
6108
|
const n = number(a, 2, 'bridge branch comment <n> "text"');
|
|
5821
|
-
const body = a._.slice(3).join(" ") ||
|
|
6109
|
+
const body = a._.slice(3).join(" ") || flag4(a, "body");
|
|
5822
6110
|
if (!body) throw new RequestUsageError('Usage: bridge branch comment <n> "text" [--reply-to <id>]');
|
|
5823
|
-
const reply =
|
|
6111
|
+
const reply = flag4(a, "reply-to");
|
|
5824
6112
|
const r = await branches.commentOnBranch(ctx, { pr: n, body, ...reply ? { replyTo: Number(reply) } : {} });
|
|
5825
6113
|
return out(r, () => io.print(r.said));
|
|
5826
6114
|
}
|
|
5827
6115
|
case "merge": {
|
|
5828
6116
|
const n = number(a, 2, 'bridge branch merge <n> [--override "reason"] [--dry-run]');
|
|
5829
|
-
const reason =
|
|
5830
|
-
const r = await branches.mergeBranch(ctx, { pr: n, ...reason ? { overrideReason: reason } : {}, dryRun:
|
|
6117
|
+
const reason = flag4(a, "override");
|
|
6118
|
+
const r = await branches.mergeBranch(ctx, { pr: n, ...reason ? { overrideReason: reason } : {}, dryRun: has4(a, "dry-run") });
|
|
5831
6119
|
return out(r, () => io.print(r.said));
|
|
5832
6120
|
}
|
|
5833
6121
|
case "close": {
|
|
@@ -5840,9 +6128,9 @@ async function runBranch(a, root, io = defaultIO()) {
|
|
|
5840
6128
|
}
|
|
5841
6129
|
case "update": {
|
|
5842
6130
|
const n = number(a, 2, "bridge branch update <n> [--choice key=mine|theirs] [--dry-run]");
|
|
5843
|
-
const choices =
|
|
6131
|
+
const choices = pairs2(a.flags.choice, "choice");
|
|
5844
6132
|
for (const [k, v] of Object.entries(choices)) if (v !== "mine" && v !== "theirs") throw new RequestUsageError(`--choice ${k}= takes mine or theirs, got "${v}".`);
|
|
5845
|
-
const r = await branches.updateFromMain(ctx, { pr: n, ...Object.keys(choices).length ? { choices } : {}, dryRun:
|
|
6133
|
+
const r = await branches.updateFromMain(ctx, { pr: n, ...Object.keys(choices).length ? { choices } : {}, dryRun: has4(a, "dry-run") });
|
|
5846
6134
|
return out(r, () => {
|
|
5847
6135
|
io.print(said(r));
|
|
5848
6136
|
for (const c of r.conflicts ?? []) io.print(` ${c.key} (${c.token}, default ${c.default ?? "none: pick one"})`);
|
|
@@ -5858,7 +6146,7 @@ Share with a product team: ${r.share.install ? r.share.install : r.share.sentenc
|
|
|
5858
6146
|
}
|
|
5859
6147
|
case "combine": {
|
|
5860
6148
|
const prs = a._.slice(2);
|
|
5861
|
-
const picks = Object.fromEntries(Object.entries(
|
|
6149
|
+
const picks = Object.fromEntries(Object.entries(pairs2(a.flags.pick, "pick")).map(([k, v]) => [k, branches.branchNumber(v)]));
|
|
5862
6150
|
const r = await branches.combineBranches(ctx, { prs, ...Object.keys(picks).length ? { picks } : {} });
|
|
5863
6151
|
return out(r, () => {
|
|
5864
6152
|
io.print(`${r.tokens} tokens change together.`);
|
|
@@ -5874,8 +6162,8 @@ ${BRANCH_HELP}`);
|
|
|
5874
6162
|
}
|
|
5875
6163
|
}
|
|
5876
6164
|
async function runStudioRead(cmd, a, root, io = defaultIO()) {
|
|
5877
|
-
const ctx = await
|
|
5878
|
-
const json =
|
|
6165
|
+
const ctx = await context4(root, io);
|
|
6166
|
+
const json = has4(a, "json");
|
|
5879
6167
|
if (cmd === "products") {
|
|
5880
6168
|
const r2 = await branches.listProducts(ctx);
|
|
5881
6169
|
if (json) return io.print(JSON.stringify(r2, null, 2)), 0;
|
|
@@ -5889,6 +6177,7 @@ This folder: ${r2.current.name}. Pick another with --product <key>.`);
|
|
|
5889
6177
|
if (json) return io.print(JSON.stringify(r2, null, 2)), 0;
|
|
5890
6178
|
io.print(`Design approval: ${r2.roles.designApproval.join(", ") || "any reviewer"}`);
|
|
5891
6179
|
io.print(`Tech approval: ${r2.roles.techApproval.join(", ") || "any reviewer"}`);
|
|
6180
|
+
if (r2.roles.soloOwner) io.print(r2.roles.soloOwner);
|
|
5892
6181
|
io.print(`Merge: ${r2.roles.ship}`);
|
|
5893
6182
|
io.print(`Owner override: ${r2.ownerOverride.on ? `${r2.ownerOverride.who.join(", ")}. ${r2.ownerOverride.reason}` : r2.ownerOverride.reason}`);
|
|
5894
6183
|
io.print(`Protection: ${r2.protection}`);
|
|
@@ -5904,7 +6193,7 @@ This folder: ${r2.current.name}. Pick another with --product <key>.`);
|
|
|
5904
6193
|
}
|
|
5905
6194
|
async function runDocsSave(a, root, io = defaultIO()) {
|
|
5906
6195
|
const component = a._[2];
|
|
5907
|
-
const file =
|
|
6196
|
+
const file = flag4(a, "file");
|
|
5908
6197
|
if (!component || !file) throw new RequestUsageError('Usage: bridge docs save <Component> --file docs.json [--pr <n>] [--title "..."] [--jira DS-42] [--product admin] [--dry-run]');
|
|
5909
6198
|
let docs;
|
|
5910
6199
|
try {
|
|
@@ -5912,28 +6201,32 @@ async function runDocsSave(a, root, io = defaultIO()) {
|
|
|
5912
6201
|
} catch (e) {
|
|
5913
6202
|
throw new RequestUsageError(`${file} is not a readable JSON file: ${e.message}`);
|
|
5914
6203
|
}
|
|
5915
|
-
const ctx = await
|
|
5916
|
-
const pr =
|
|
6204
|
+
const ctx = await context4(root, io);
|
|
6205
|
+
const pr = flag4(a, "pr");
|
|
6206
|
+
const changes = changesFlag(a);
|
|
5917
6207
|
const r = await studio.saveDocs(ctx, {
|
|
5918
6208
|
component,
|
|
5919
6209
|
docs,
|
|
5920
6210
|
...pr ? { pr } : {},
|
|
5921
|
-
...
|
|
5922
|
-
...
|
|
5923
|
-
...
|
|
5924
|
-
...
|
|
5925
|
-
|
|
6211
|
+
...flag4(a, "title") ? { title: flag4(a, "title") } : {},
|
|
6212
|
+
...flag4(a, "notes") ? { notes: flag4(a, "notes") } : {},
|
|
6213
|
+
...flag4(a, "jira") ? { jira: flag4(a, "jira") } : {},
|
|
6214
|
+
...flag4(a, "product") ? { product: flag4(a, "product") } : {},
|
|
6215
|
+
...changes ? { changes } : {},
|
|
6216
|
+
source: "cli",
|
|
6217
|
+
dryRun: has4(a, "dry-run")
|
|
5926
6218
|
});
|
|
5927
|
-
if (
|
|
6219
|
+
if (has4(a, "json")) io.print(JSON.stringify(r, null, 2));
|
|
5928
6220
|
else io.print(r.status === "dry-run" || r.status === "preview" ? `${r.said ?? "Dry run, nothing written."}
|
|
5929
6221
|
|
|
5930
6222
|
${r.path}:
|
|
5931
|
-
${r.content}` : String(r.said)
|
|
6223
|
+
${r.content}` : `${String(r.said)}${r.next ? `
|
|
6224
|
+
${String(r.next)}` : ""}`);
|
|
5932
6225
|
return 0;
|
|
5933
6226
|
}
|
|
5934
6227
|
|
|
5935
6228
|
// src/pieces.ts
|
|
5936
|
-
import { BridgeContext as
|
|
6229
|
+
import { BridgeContext as BridgeContext6, branches as branches2, pieces } from "@bridge-ds/mcp";
|
|
5937
6230
|
var PIECES_HELP = `bridge pieces: a product branch and the library
|
|
5938
6231
|
|
|
5939
6232
|
Usage:
|
|
@@ -5948,10 +6241,10 @@ Usage:
|
|
|
5948
6241
|
bridge pieces switch <n> once the library shipped: main into the product branch
|
|
5949
6242
|
|
|
5950
6243
|
GitHub token: GITHUB_TOKEN or BRIDGE_GITHUB_TOKEN, else \`gh auth token\`. Exit codes: 0 ok, 2 usage or setup error.`;
|
|
5951
|
-
var
|
|
5952
|
-
async function
|
|
6244
|
+
var has5 = (a, k) => Boolean(a.flags[k]);
|
|
6245
|
+
async function context5(root, io) {
|
|
5953
6246
|
const token = io.env.BRIDGE_GITHUB_TOKEN || io.env.GITHUB_TOKEN || io.ghToken();
|
|
5954
|
-
return
|
|
6247
|
+
return BridgeContext6.create({ root, ...token ? { token } : {}, ...io.fetch ? { fetch: io.fetch } : {} });
|
|
5955
6248
|
}
|
|
5956
6249
|
function number2(a, at, usage) {
|
|
5957
6250
|
const raw = a._[at];
|
|
@@ -5965,12 +6258,12 @@ function piece(a, at, usage) {
|
|
|
5965
6258
|
}
|
|
5966
6259
|
async function runPieces(a, root, io = defaultIO()) {
|
|
5967
6260
|
const sub = a._[1];
|
|
5968
|
-
if (!sub || sub === "help" ||
|
|
6261
|
+
if (!sub || sub === "help" || has5(a, "help")) {
|
|
5969
6262
|
io.print(PIECES_HELP);
|
|
5970
|
-
return sub ||
|
|
6263
|
+
return sub || has5(a, "help") ? 0 : 2;
|
|
5971
6264
|
}
|
|
5972
|
-
const json =
|
|
5973
|
-
const ctx = await
|
|
6265
|
+
const json = has5(a, "json");
|
|
6266
|
+
const ctx = await context5(root, io);
|
|
5974
6267
|
const out = (r, text) => (json ? io.print(JSON.stringify(r, null, 2)) : text(), 0);
|
|
5975
6268
|
switch (sub) {
|
|
5976
6269
|
case "status": {
|
|
@@ -5985,9 +6278,10 @@ async function runPieces(a, root, io = defaultIO()) {
|
|
|
5985
6278
|
}
|
|
5986
6279
|
case "open": {
|
|
5987
6280
|
const usage = "bridge pieces open <n> <piece>";
|
|
5988
|
-
const
|
|
6281
|
+
const changes = changesFlag(a);
|
|
6282
|
+
const r = await pieces.openLibraryBranchOp(ctx, { pr: number2(a, 2, usage), piece: piece(a, 3, usage), usePreview: !has5(a, "no-preview"), dryRun: has5(a, "dry-run"), source: "cli", ...changes ? { changes } : {} });
|
|
5989
6283
|
return out(r, () => {
|
|
5990
|
-
io.print(`${r.status === "dry-run" ? "Would open" : r.status === "already-linked" ? "Already linked" : "Opened"} a ${r.opens}: ${r.title}, needed by ${r.neededBy}.`);
|
|
6284
|
+
io.print(`${r.status === "dry-run" ? "Would open" : r.status === "already-linked" ? "Already linked" : r.status === "suggested" ? "Suggested" : "Opened"} a ${r.opens}: ${r.title}, needed by ${r.neededBy}.`);
|
|
5991
6285
|
if (r.library) io.print(` #${r.library.number} ${r.library.url}`);
|
|
5992
6286
|
if (r.preview) io.print(` ${r.preview.said}`);
|
|
5993
6287
|
for (const w of r.warnings ?? []) io.print(` ${w}`);
|
|
@@ -6004,7 +6298,7 @@ async function runPieces(a, root, io = defaultIO()) {
|
|
|
6004
6298
|
return out(r, () => io.print(r.said));
|
|
6005
6299
|
}
|
|
6006
6300
|
default: {
|
|
6007
|
-
const r = await pieces.missingPiecesOp(ctx, { pr: number2(a, 1, "bridge pieces <n> [--record]"), record:
|
|
6301
|
+
const r = await pieces.missingPiecesOp(ctx, { pr: number2(a, 1, "bridge pieces <n> [--record]"), record: has5(a, "record") });
|
|
6008
6302
|
return out(r, () => {
|
|
6009
6303
|
io.print(`#${r.pr} ${r.branch} (${r.product}): ${r.pieces.length ? `${r.pieces.length} ${r.pieces.length === 1 ? "piece is" : "pieces are"} missing from the library` : "nothing is missing from the library"}, ${r.files} files read.`);
|
|
6010
6304
|
for (const p of r.pieces) io.print(` ${p.id} ${p.title}: ${p.summary} Opens ${p.opens}.${"linked" in p ? ` Linked: #${p.linked}.` : ""}`);
|
|
@@ -6020,7 +6314,7 @@ ${r.fixes.sample.map((s) => ` ${s}`).join("\n")}`);
|
|
|
6020
6314
|
// src/figma.ts
|
|
6021
6315
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
6022
6316
|
import { resolve as resolve4 } from "node:path";
|
|
6023
|
-
import { BridgeContext as
|
|
6317
|
+
import { BridgeContext as BridgeContext7, branches as branches3, figma } from "@bridge-ds/mcp";
|
|
6024
6318
|
var FIGMA_HELP = `bridge figma: component changes sent from Figma
|
|
6025
6319
|
|
|
6026
6320
|
Usage:
|
|
@@ -6034,10 +6328,10 @@ Usage:
|
|
|
6034
6328
|
token change, or a branch with the spec for Claude AI
|
|
6035
6329
|
|
|
6036
6330
|
GitHub token: GITHUB_TOKEN or BRIDGE_GITHUB_TOKEN, else \`gh auth token\`. Exit codes: 0 ok, 2 usage or setup error.`;
|
|
6037
|
-
var
|
|
6038
|
-
async function
|
|
6331
|
+
var has6 = (a, k) => Boolean(a.flags[k]);
|
|
6332
|
+
async function context6(root, io) {
|
|
6039
6333
|
const token = io.env.BRIDGE_GITHUB_TOKEN || io.env.GITHUB_TOKEN || io.ghToken();
|
|
6040
|
-
return
|
|
6334
|
+
return BridgeContext7.create({ root, ...token ? { token } : {}, ...io.fetch ? { fetch: io.fetch } : {} });
|
|
6041
6335
|
}
|
|
6042
6336
|
function number3(a, usage) {
|
|
6043
6337
|
const raw = a._[2];
|
|
@@ -6046,17 +6340,17 @@ function number3(a, usage) {
|
|
|
6046
6340
|
}
|
|
6047
6341
|
async function runFigma(a, root, io = defaultIO()) {
|
|
6048
6342
|
const sub = a._[1];
|
|
6049
|
-
if (sub === "help" ||
|
|
6343
|
+
if (sub === "help" || has6(a, "help")) {
|
|
6050
6344
|
io.print(FIGMA_HELP);
|
|
6051
6345
|
return 0;
|
|
6052
6346
|
}
|
|
6053
|
-
const json =
|
|
6054
|
-
const ctx = await
|
|
6347
|
+
const json = has6(a, "json");
|
|
6348
|
+
const ctx = await context6(root, io);
|
|
6055
6349
|
const out = (r, text) => (json ? io.print(JSON.stringify(r, null, 2)) : text(), 0);
|
|
6056
6350
|
switch (sub) {
|
|
6057
6351
|
case void 0:
|
|
6058
6352
|
case "list": {
|
|
6059
|
-
const r = await figma.figmaChangesOp(ctx, { state:
|
|
6353
|
+
const r = await figma.figmaChangesOp(ctx, { state: has6(a, "all") ? "all" : "open" });
|
|
6060
6354
|
return out(r, () => {
|
|
6061
6355
|
for (const c of r.changes) io.print(`#${c.pr} ${c.title} (${c.route}): ${c.status}. ${c.said}`);
|
|
6062
6356
|
io.print(`Next: ${r.next}`);
|
|
@@ -6064,7 +6358,7 @@ async function runFigma(a, root, io = defaultIO()) {
|
|
|
6064
6358
|
}
|
|
6065
6359
|
case "show": {
|
|
6066
6360
|
const r = await figma.getFigmaChangeOp(ctx, { pr: number3(a, "bridge figma show <n>") });
|
|
6067
|
-
if (
|
|
6361
|
+
if (has6(a, "draft")) {
|
|
6068
6362
|
if (!r.brief) throw new RequestUsageError(`#${r.pr} is an exact token change: there is no code to draft.`);
|
|
6069
6363
|
if (!io.hasClaude()) throw new RequestUsageError("`claude` is not on PATH: install Claude Code, or paste the brief from `bridge figma show <n>` into Claude.");
|
|
6070
6364
|
return io.runClaude(r.brief, root);
|
|
@@ -6089,12 +6383,14 @@ ${r.brief}`);
|
|
|
6089
6383
|
} catch (err) {
|
|
6090
6384
|
throw new RequestUsageError(`${file} could not be read as JSON: ${err.message}`);
|
|
6091
6385
|
}
|
|
6092
|
-
const
|
|
6386
|
+
const changes = changesFlag(a);
|
|
6387
|
+
const r = await figma.pushFigmaComponentOp(ctx, { diff: diff2, dryRun: has6(a, "dry-run"), source: "cli", ...changes ? { changes } : {} });
|
|
6093
6388
|
return out(r, () => {
|
|
6094
6389
|
io.print(`${r.component}: ${r.route}. ${r.said}`);
|
|
6095
6390
|
for (const x of r.reasons ?? []) io.print(` ${x}`);
|
|
6096
6391
|
for (const x of r.fixInFigma ?? []) io.print(` Fix in Figma: ${x.what} ${x.todo}`);
|
|
6097
6392
|
if (r.library) io.print(` #${r.library.number} ${r.library.url}`);
|
|
6393
|
+
if (r.suggestion) io.print(` suggestion #${r.suggestion.number} ${r.suggestion.url}`);
|
|
6098
6394
|
io.print(`Next: ${r.next}`);
|
|
6099
6395
|
});
|
|
6100
6396
|
}
|
|
@@ -6135,7 +6431,7 @@ async function rolesReport(opts) {
|
|
|
6135
6431
|
}
|
|
6136
6432
|
|
|
6137
6433
|
// src/index.ts
|
|
6138
|
-
import { brand, BridgeContext as
|
|
6434
|
+
import { brand, BridgeContext as BridgeContext8, CONFIG_FILE as CONFIG_FILE2, core, detectConfig, GitRefSource, history as hist, LocalSource, ops, optionsFromArgv, serveStdio, toConfigJson, VERSION } from "@bridge-ds/mcp";
|
|
6139
6435
|
var MCP_PACKAGE = "@bridge-ds/mcp";
|
|
6140
6436
|
var HELP = `bridge ${VERSION}: design system as code, for you and your Claude
|
|
6141
6437
|
|
|
@@ -6151,8 +6447,10 @@ Usage:
|
|
|
6151
6447
|
bridge diff <base> [--json|--md] designer-readable token diff: working tree vs <base> (git ref)
|
|
6152
6448
|
bridge propose --title "..." --set path=value [--set ...] [--file f | --mode theme=dark]
|
|
6153
6449
|
[--edits edits.json] [--summary "..."] [--branch explore/<name>] [--pr <n>]
|
|
6154
|
-
[--product <key>] [--jira DS-42] [--dry-run]
|
|
6155
|
-
validate + contrast
|
|
6450
|
+
[--product <key>] [--jira DS-42] [--suggest | --direct] [--dry-run]
|
|
6451
|
+
validate + contrast, then a suggestion (a GitHub issue a developer
|
|
6452
|
+
applies with bridge apply) or, with "changes": "direct" in
|
|
6453
|
+
bridge.config.json or --direct, a new local bridge/* branch (never pushes).
|
|
6156
6454
|
--branch: write the edits to the checked-out exploration instead.
|
|
6157
6455
|
--pr: add them to that branch in review (GitHub token).
|
|
6158
6456
|
--product: the product it is for (bridge products); --jira: the
|
|
@@ -6160,9 +6458,10 @@ Usage:
|
|
|
6160
6458
|
bridge brand list [--json] brands: slug, name, default, primary action color, theme overrides
|
|
6161
6459
|
bridge brand create <slug> --from bridge --primary <hex|hue> --neutral <hex|hue>
|
|
6162
6460
|
[--name "Acme"] [--accent x] [--link x] [--radius soft|balanced|crisp|i,c,co,o]
|
|
6163
|
-
[--heading font] [--text font] [--notes "..."] [--dry-run] [--json]
|
|
6164
|
-
new brand: generated ramps, contrast gate for every theme,
|
|
6165
|
-
new local bridge/brand-<slug> branch
|
|
6461
|
+
[--heading font] [--text font] [--notes "..."] [--suggest | --direct] [--dry-run] [--json]
|
|
6462
|
+
new brand: generated ramps, contrast gate for every theme, then a
|
|
6463
|
+
suggestion, or in direct mode a new local bridge/brand-<slug> branch
|
|
6464
|
+
(never pushes, never main).
|
|
6166
6465
|
Exit 1 when blocked
|
|
6167
6466
|
bridge impact [--base main] [--head HEAD] [--out dir] [--only stories|app] [--threshold 0.03]
|
|
6168
6467
|
[--plan] [--json] [--image-base url] [--artifact-url url] [--keep-unchanged]
|
|
@@ -6176,9 +6475,9 @@ Usage:
|
|
|
6176
6475
|
Needs Chrome installed
|
|
6177
6476
|
bridge history [token] [--json] [--limit n] [--author a] [--tier n] [--version v]
|
|
6178
6477
|
token history: versions, merged changes, who, when, why
|
|
6179
|
-
bridge revert <pr|commit|id> [--dry-run] [--restore-conflicts] [--use-before path@file] [--json]
|
|
6180
|
-
undo a merged token change against HEAD: conflicts shown,
|
|
6181
|
-
new local bridge/* branch (never pushes
|
|
6478
|
+
bridge revert <pr|commit|id> [--dry-run] [--restore-conflicts] [--use-before path@file] [--suggest | --direct] [--json]
|
|
6479
|
+
undo a merged token change against HEAD: conflicts shown, then a
|
|
6480
|
+
suggestion, or in direct mode a new local bridge/* branch (never pushes)
|
|
6182
6481
|
bridge audit <path> [--json [out.json]] [--md [out.md]] [--fail-under 80] [--baseline prev.json]
|
|
6183
6482
|
[--label "..."] [--ds dir] [--no-regress] [--allowlist known.json]
|
|
6184
6483
|
design system adoption in a product folder: token and component
|
|
@@ -6221,6 +6520,17 @@ Usage:
|
|
|
6221
6520
|
a product branch and the library (\`bridge pieces help\`): what it adds
|
|
6222
6521
|
that the library lacks, the library branch each piece opens (linked
|
|
6223
6522
|
both ways), its preview in the product branch, the published version
|
|
6523
|
+
bridge suggestions [list | show <n> | link <n> --pr <m>] [--status approved] [--json]
|
|
6524
|
+
suggestions: changes filed as GitHub issues (label bridge-suggestion)
|
|
6525
|
+
that the owners approve and a developer applies (\`bridge suggestions help\`)
|
|
6526
|
+
bridge apply <n> [--write | --patch] [--dry-run] [--accept-edited] [--force-unapproved] [--json]
|
|
6527
|
+
apply suggestion #n on today's code: a new local branch (working tree
|
|
6528
|
+
untouched, nothing pushed), --write into the working tree, --patch for
|
|
6529
|
+
git am. Exit 0 applied or already applied, 1 not approved or blocked,
|
|
6530
|
+
2 usage, 3 conflict
|
|
6531
|
+
bridge review <n> --approve | --request-changes --message "..." | --decline --message "..."
|
|
6532
|
+
review a suggestion as its owner (counts only for owners in
|
|
6533
|
+
bridge.config.json, on the version you saw)
|
|
6224
6534
|
bridge figma [show <n> | drafted <n> | push <diff.json>]
|
|
6225
6535
|
component changes sent from Figma (\`bridge figma help\`): exact token
|
|
6226
6536
|
changes and branches Claude AI drafts from the spec
|
|
@@ -6234,8 +6544,11 @@ Usage:
|
|
|
6234
6544
|
Options:
|
|
6235
6545
|
--root <dir> repo root (default: current directory)
|
|
6236
6546
|
--ds <dir> audit: design system repo with bridge.config.json (default: --root, or the nearest one above)
|
|
6237
|
-
GITHUB_TOKEN env, optional: lets
|
|
6238
|
-
|
|
6547
|
+
GITHUB_TOKEN env, optional: lets bridge file suggestions and requests (Issues: read and write) and the
|
|
6548
|
+
MCP server open PRs on a remote repo in direct mode (else \`gh auth token\`). Never needed for
|
|
6549
|
+
local token work in direct mode.
|
|
6550
|
+
--suggest | --direct how a change reaches the code for this command (propose, brand create, revert,
|
|
6551
|
+
docs save, pieces open, figma push). Default: "changes" in bridge.config.json, else suggest.
|
|
6239
6552
|
`;
|
|
6240
6553
|
function parse(argv) {
|
|
6241
6554
|
const out = { _: [], flags: {} };
|
|
@@ -6245,23 +6558,32 @@ function parse(argv) {
|
|
|
6245
6558
|
const [k, inline] = a.slice(2).split(/=(.*)/s, 2);
|
|
6246
6559
|
const next = argv[i + 1];
|
|
6247
6560
|
const fileArg = k === "json" && /\.json$/i.test(next ?? "") || k === "md" && /\.(md|markdown)$/i.test(next ?? "");
|
|
6248
|
-
const takesValue = inline === void 0 && next !== void 0 && !next.startsWith("--") && (fileArg || !["force", "json", "md", "strict", "dry-run", "help", "fix", "write", "no-regress", "plan", "keep-unchanged", "restore-conflicts", "breaking", "claude", "all", "offline", "approve"].includes(k));
|
|
6561
|
+
const takesValue = inline === void 0 && next !== void 0 && !next.startsWith("--") && (fileArg || !["force", "json", "md", "strict", "dry-run", "help", "fix", "write", "no-regress", "plan", "keep-unchanged", "restore-conflicts", "breaking", "claude", "all", "offline", "approve", "patch", "accept-edited", "force-unapproved", "suggest", "direct"].includes(k));
|
|
6249
6562
|
(out.flags[k] ??= []).push(inline ?? (takesValue ? argv[++i] : "true"));
|
|
6250
6563
|
} else out._.push(a);
|
|
6251
6564
|
}
|
|
6252
6565
|
return out;
|
|
6253
6566
|
}
|
|
6254
|
-
var
|
|
6255
|
-
var
|
|
6567
|
+
var flag5 = (a, k) => a.flags[k]?.[a.flags[k].length - 1];
|
|
6568
|
+
var has7 = (a, k) => Boolean(a.flags[k]);
|
|
6256
6569
|
var print = (s) => process.stdout.write(`${s}
|
|
6257
6570
|
`);
|
|
6258
6571
|
var modeArg = (s) => s ? Object.fromEntries(s.split(",").map((kv) => kv.split("="))) : void 0;
|
|
6572
|
+
var githubToken = () => process.env.BRIDGE_GITHUB_TOKEN || process.env.GITHUB_TOKEN || defaultIO().ghToken();
|
|
6573
|
+
function printSent(r) {
|
|
6574
|
+
if (r.status === "suggested") print(`suggestion: #${r.number} ${r.url}`);
|
|
6575
|
+
if (r.message) print(String(r.message));
|
|
6576
|
+
if (r.branch && r.status !== "suggested") print(`branch: ${r.branch}`);
|
|
6577
|
+
for (const w of r.warnings ?? []) print(`warning: ${w}`);
|
|
6578
|
+
if (r.next) print(String(r.next));
|
|
6579
|
+
if (r.hint) print(String(r.hint));
|
|
6580
|
+
}
|
|
6259
6581
|
async function init(a, root) {
|
|
6260
6582
|
const cfgPath = join6(root, CONFIG_FILE2);
|
|
6261
6583
|
let config;
|
|
6262
|
-
if (existsSync6(cfgPath) && !
|
|
6584
|
+
if (existsSync6(cfgPath) && !has7(a, "force")) {
|
|
6263
6585
|
print(`${CONFIG_FILE2} exists, keeping it (--force to regenerate).`);
|
|
6264
|
-
config = (await (await
|
|
6586
|
+
config = (await (await BridgeContext8.create({ root })).config()).config;
|
|
6265
6587
|
} else {
|
|
6266
6588
|
const src = new LocalSource(root);
|
|
6267
6589
|
const files = await src.list("");
|
|
@@ -6289,14 +6611,14 @@ async function init(a, root) {
|
|
|
6289
6611
|
print(` claude mcp add bridge -- ${desktop.command} ${desktop.args.join(" ")}`);
|
|
6290
6612
|
}
|
|
6291
6613
|
async function check(a, root) {
|
|
6292
|
-
const ctx = await
|
|
6614
|
+
const ctx = await BridgeContext8.create({ root });
|
|
6293
6615
|
const v = await ops.validateTokens(ctx, { includeWarnings: true });
|
|
6294
6616
|
const c = await ops.checkContrast(ctx, {});
|
|
6295
6617
|
const warnings = "warnings" in v ? v.warnings ?? [] : [];
|
|
6296
|
-
const failed = !v.valid || "failing" in c && (c.failing ?? 0) > 0 ||
|
|
6618
|
+
const failed = !v.valid || "failing" in c && (c.failing ?? 0) > 0 || has7(a, "strict") && warnings.length > 0;
|
|
6297
6619
|
const protection = await protectionOf(a, root, ctx);
|
|
6298
6620
|
const roles = await rolesOf(a, root, ctx);
|
|
6299
|
-
if (
|
|
6621
|
+
if (has7(a, "json")) {
|
|
6300
6622
|
print(JSON.stringify({ ok: !failed, validation: v, contrast: c, ...protection ? { protection: protection.state } : {}, ...roles?.teams.length ? { teams: roles.teams } : {} }, null, 2));
|
|
6301
6623
|
return failed ? 1 : 0;
|
|
6302
6624
|
}
|
|
@@ -6315,7 +6637,7 @@ async function check(a, root) {
|
|
|
6315
6637
|
return failed ? 1 : 0;
|
|
6316
6638
|
}
|
|
6317
6639
|
async function protectionOf(a, root, ctx) {
|
|
6318
|
-
if (
|
|
6640
|
+
if (has7(a, "offline") || process.env.BRIDGE_OFFLINE === "1") return void 0;
|
|
6319
6641
|
const { config } = await ctx.config();
|
|
6320
6642
|
const repo = config.repo ? `${config.repo.owner}/${config.repo.name}` : await originRepo(root);
|
|
6321
6643
|
if (!repo) return void 0;
|
|
@@ -6323,7 +6645,7 @@ async function protectionOf(a, root, ctx) {
|
|
|
6323
6645
|
return protectionReport({ repo, defaultBranch: config.repo?.defaultBranch ?? "main", token });
|
|
6324
6646
|
}
|
|
6325
6647
|
async function rolesOf(a, root, ctx) {
|
|
6326
|
-
if (
|
|
6648
|
+
if (has7(a, "offline") || process.env.BRIDGE_OFFLINE === "1") return void 0;
|
|
6327
6649
|
const { config } = await ctx.config();
|
|
6328
6650
|
const repo = config.repo ? `${config.repo.owner}/${config.repo.name}` : await originRepo(root);
|
|
6329
6651
|
if (!repo) return void 0;
|
|
@@ -6333,9 +6655,9 @@ async function rolesOf(a, root, ctx) {
|
|
|
6333
6655
|
async function tokens(a, root) {
|
|
6334
6656
|
const q = a._.slice(1).join(" ");
|
|
6335
6657
|
if (!q) throw new UsageError("bridge tokens <query>");
|
|
6336
|
-
const ctx = await
|
|
6337
|
-
const r = await ops.searchTokens(ctx, { query: q, limit: Number(
|
|
6338
|
-
if (
|
|
6658
|
+
const ctx = await BridgeContext8.create({ root });
|
|
6659
|
+
const r = await ops.searchTokens(ctx, { query: q, limit: Number(flag5(a, "limit") ?? 20) });
|
|
6660
|
+
if (has7(a, "json")) return print(JSON.stringify(r, null, 2));
|
|
6339
6661
|
if (!r.results.length) return print(`No token matches "${q}".`);
|
|
6340
6662
|
const w = Math.max(...r.results.map((x) => x.path.length));
|
|
6341
6663
|
for (const t of r.results) print(`${t.path.padEnd(w)} ${String(t.value).padEnd(10)} ${(t.tier ?? "").padEnd(9)} ${t.description ?? ""}`);
|
|
@@ -6343,20 +6665,20 @@ async function tokens(a, root) {
|
|
|
6343
6665
|
async function diff(a, root) {
|
|
6344
6666
|
const base = a._[1];
|
|
6345
6667
|
if (!base) throw new UsageError("bridge diff <base> (a git ref: main, HEAD~1, a sha)");
|
|
6346
|
-
const ctx = await
|
|
6668
|
+
const ctx = await BridgeContext8.create({ root });
|
|
6347
6669
|
const { config } = await ctx.config();
|
|
6348
6670
|
const before = await core.loadTokenSet(new GitRefSource(root, base), config);
|
|
6349
6671
|
const after = await ctx.tokens();
|
|
6350
6672
|
const changes = core.diff(before, after);
|
|
6351
|
-
const
|
|
6352
|
-
const delta2 =
|
|
6673
|
+
const pairs3 = await core.readContrastPairs(ctx.source, config).catch(() => []);
|
|
6674
|
+
const delta2 = pairs3.length ? core.contrastDelta(core.runContrast(before, pairs3, config.contrast?.modes), core.runContrast(after, pairs3, config.contrast?.modes)) : [];
|
|
6353
6675
|
const tier = core.classify(changes, [], config);
|
|
6354
6676
|
const imp = core.impact(before, after, changes, 30);
|
|
6355
|
-
if (
|
|
6677
|
+
if (has7(a, "json")) {
|
|
6356
6678
|
print(JSON.stringify({ base, changes, contrast: delta2, tier, impact: imp }, null, 2));
|
|
6357
6679
|
return 0;
|
|
6358
6680
|
}
|
|
6359
|
-
if (
|
|
6681
|
+
if (has7(a, "md")) {
|
|
6360
6682
|
print(`## Token changes since \`${base}\`
|
|
6361
6683
|
|
|
6362
6684
|
${core.tokenTable(changes)}
|
|
@@ -6391,26 +6713,27 @@ ${imp.total} other token(s) change value through aliases:`);
|
|
|
6391
6713
|
return delta2.some((d) => d.passBefore && !d.passAfter) ? 1 : 0;
|
|
6392
6714
|
}
|
|
6393
6715
|
async function propose(a, root) {
|
|
6394
|
-
const title =
|
|
6716
|
+
const title = flag5(a, "title");
|
|
6395
6717
|
if (!title) throw new UsageError('bridge propose --title "Darken muted text" --set color.text.muted={palette.neutral.700} --mode theme=light');
|
|
6396
6718
|
const edits = [];
|
|
6397
|
-
const file =
|
|
6398
|
-
const mode2 = modeArg(
|
|
6719
|
+
const file = flag5(a, "file");
|
|
6720
|
+
const mode2 = modeArg(flag5(a, "mode"));
|
|
6399
6721
|
for (const s of a.flags.set ?? []) {
|
|
6400
6722
|
const i = s.indexOf("=");
|
|
6401
6723
|
if (i < 0) throw new UsageError(`--set needs path=value, got "${s}"`);
|
|
6402
6724
|
edits.push({ op: "set", path: s.slice(0, i), value: s.slice(i + 1), ...file ? { file } : {}, ...mode2 ? { mode: mode2 } : {} });
|
|
6403
6725
|
}
|
|
6404
|
-
const editsFile =
|
|
6726
|
+
const editsFile = flag5(a, "edits");
|
|
6405
6727
|
if (editsFile) edits.push(...JSON.parse(await readFile7(resolve5(editsFile), "utf8")));
|
|
6406
6728
|
if (!edits.length) throw new UsageError("Give at least one --set path=value or --edits file.json");
|
|
6407
|
-
const pr =
|
|
6408
|
-
const
|
|
6409
|
-
const
|
|
6410
|
-
const
|
|
6411
|
-
const
|
|
6412
|
-
const
|
|
6413
|
-
const
|
|
6729
|
+
const pr = flag5(a, "pr");
|
|
6730
|
+
const changes = changesFlag(a);
|
|
6731
|
+
const token = pr || changes !== "direct" ? githubToken() : void 0;
|
|
6732
|
+
const ctx = await BridgeContext8.create({ root, ...token ? { token } : {} });
|
|
6733
|
+
const summary = flag5(a, "summary");
|
|
6734
|
+
const branch = flag5(a, "branch");
|
|
6735
|
+
const jira = flag5(a, "jira");
|
|
6736
|
+
const product = flag5(a, "product");
|
|
6414
6737
|
const r = await ops.proposeTokenChange(ctx, {
|
|
6415
6738
|
title,
|
|
6416
6739
|
edits,
|
|
@@ -6419,34 +6742,36 @@ async function propose(a, root) {
|
|
|
6419
6742
|
...pr ? { pr } : {},
|
|
6420
6743
|
...jira ? { jira } : {},
|
|
6421
6744
|
...product ? { product } : {},
|
|
6422
|
-
|
|
6745
|
+
...changes ? { changes } : {},
|
|
6746
|
+
source: "cli",
|
|
6747
|
+
dryRun: has7(a, "dry-run")
|
|
6423
6748
|
});
|
|
6424
|
-
if (
|
|
6749
|
+
if (has7(a, "json")) print(JSON.stringify(r, null, 2));
|
|
6425
6750
|
else {
|
|
6426
6751
|
print(`status: ${r.status}${r.why ? ` (${r.why})` : ""}`);
|
|
6427
6752
|
if (r.tier) print(`tier ${r.tier}, ${r.bump}`);
|
|
6428
6753
|
for (const c of r.changes ?? []) print(` ${c.kind} ${c.path}: ${c.before ?? ""} -> ${c.after ?? ""}`);
|
|
6429
|
-
|
|
6430
|
-
if (r.next) print(String(r.next));
|
|
6431
|
-
if (r.hint) print(String(r.hint));
|
|
6754
|
+
printSent(r);
|
|
6432
6755
|
if (r.status === "dry-run") print(`
|
|
6433
|
-
${r.prBody}`);
|
|
6756
|
+
${r.suggestion?.body ?? r.prBody}`);
|
|
6434
6757
|
}
|
|
6435
6758
|
return r.status === "blocked" ? 1 : 0;
|
|
6436
6759
|
}
|
|
6437
6760
|
async function brandCmd(a, root) {
|
|
6438
6761
|
const sub = a._[1];
|
|
6439
|
-
const
|
|
6762
|
+
const changes = changesFlag(a);
|
|
6763
|
+
const token = sub === "create" && changes !== "direct" ? githubToken() : void 0;
|
|
6764
|
+
const ctx = await BridgeContext8.create({ root, ...token ? { token } : {} });
|
|
6440
6765
|
if (sub === "list") {
|
|
6441
6766
|
const r2 = await brand.listBrands(ctx);
|
|
6442
|
-
if (
|
|
6767
|
+
if (has7(a, "json")) print(JSON.stringify(r2.brands, null, 2));
|
|
6443
6768
|
else for (const b of r2.brands) print(`${b.default ? "*" : " "} ${b.slug.padEnd(14)} ${b.name.padEnd(18)} ${b.primary ?? ""}${b.overrides.length ? ` overrides: ${b.overrides.join(", ")}` : ""}`);
|
|
6444
6769
|
return 0;
|
|
6445
6770
|
}
|
|
6446
6771
|
if (sub !== "create") throw new UsageError('bridge brand list | bridge brand create <slug> --from bridge --primary "#1F5F3A" --neutral "#2A2F2C"');
|
|
6447
6772
|
const slug = a._[2];
|
|
6448
6773
|
if (!slug) throw new UsageError('bridge brand create <slug> --from bridge --primary "#1F5F3A" --neutral "#2A2F2C"');
|
|
6449
|
-
const opt = (k) =>
|
|
6774
|
+
const opt = (k) => flag5(a, k);
|
|
6450
6775
|
const fonts = { ...opt("heading") ? { heading: opt("heading") } : {}, ...opt("text") ? { text: opt("text") } : {} };
|
|
6451
6776
|
const r = await brand.proposeBrand(ctx, {
|
|
6452
6777
|
slug,
|
|
@@ -6459,9 +6784,11 @@ async function brandCmd(a, root) {
|
|
|
6459
6784
|
...opt("radius") ? { radius: opt("radius") } : {},
|
|
6460
6785
|
...Object.keys(fonts).length ? { fonts } : {},
|
|
6461
6786
|
...opt("notes") ? { notes: opt("notes") } : {},
|
|
6462
|
-
|
|
6787
|
+
...changes ? { changes } : {},
|
|
6788
|
+
source: "cli",
|
|
6789
|
+
dryRun: has7(a, "dry-run")
|
|
6463
6790
|
});
|
|
6464
|
-
if (
|
|
6791
|
+
if (has7(a, "json")) {
|
|
6465
6792
|
const { fileContents: _f, ...rest } = r;
|
|
6466
6793
|
print(JSON.stringify(rest, null, 2));
|
|
6467
6794
|
return r.status === "blocked" ? 1 : 0;
|
|
@@ -6476,13 +6803,11 @@ async function brandCmd(a, root) {
|
|
|
6476
6803
|
for (const i of r.issues) print(` ${i.level}: ${i.message}`);
|
|
6477
6804
|
print("files:");
|
|
6478
6805
|
for (const f of r.files) print(` ${f.action} ${f.path}`);
|
|
6479
|
-
|
|
6480
|
-
if (r.next) print(String(r.next));
|
|
6481
|
-
if (r.hint) print(String(r.hint));
|
|
6806
|
+
printSent(r);
|
|
6482
6807
|
return r.status === "blocked" ? 1 : 0;
|
|
6483
6808
|
}
|
|
6484
6809
|
async function figmaReferences(a, root) {
|
|
6485
|
-
let sha =
|
|
6810
|
+
let sha = flag5(a, "sha");
|
|
6486
6811
|
if (!sha) {
|
|
6487
6812
|
try {
|
|
6488
6813
|
sha = execFileSync2("git", ["rev-parse", "HEAD"], { cwd: root, encoding: "utf8" }).trim();
|
|
@@ -6490,33 +6815,33 @@ async function figmaReferences(a, root) {
|
|
|
6490
6815
|
sha = "unknown";
|
|
6491
6816
|
}
|
|
6492
6817
|
}
|
|
6493
|
-
const only = (
|
|
6818
|
+
const only = (flag5(a, "only") ?? "").split(",").filter(Boolean);
|
|
6494
6819
|
const r = await captureFigmaReferences({
|
|
6495
|
-
storybook: resolve5(root,
|
|
6496
|
-
specs: resolve5(root,
|
|
6497
|
-
out: resolve5(root,
|
|
6820
|
+
storybook: resolve5(root, flag5(a, "storybook") ?? "storybook-static"),
|
|
6821
|
+
specs: resolve5(root, flag5(a, "specs") ?? "packages/react/component-specs.json"),
|
|
6822
|
+
out: resolve5(root, flag5(a, "out") ?? "figma-references"),
|
|
6498
6823
|
sha,
|
|
6499
6824
|
...only.length ? { only } : {},
|
|
6500
|
-
log:
|
|
6825
|
+
log: has7(a, "json") ? void 0 : (line) => print(line)
|
|
6501
6826
|
});
|
|
6502
|
-
if (
|
|
6827
|
+
if (has7(a, "json")) print(JSON.stringify({ manifest: r.manifestPath, captured: r.captured, skipped: r.skipped }, null, 2));
|
|
6503
6828
|
return 0;
|
|
6504
6829
|
}
|
|
6505
6830
|
async function impact(a, root) {
|
|
6506
|
-
const only = (
|
|
6831
|
+
const only = (flag5(a, "only") ?? "").split(",").filter(Boolean);
|
|
6507
6832
|
for (const x of only) if (x !== "stories" && x !== "app") throw new UsageError(`--only takes stories or app, got "${x}"`);
|
|
6508
|
-
const num2 = (k) =>
|
|
6833
|
+
const num2 = (k) => flag5(a, k) !== void 0 ? Number(flag5(a, k)) : void 0;
|
|
6509
6834
|
const threshold = num2("threshold");
|
|
6510
6835
|
if (threshold !== void 0 && !(threshold >= 0 && threshold <= 1)) throw new UsageError("--threshold is between 0 and 1 (pixelmatch color distance)");
|
|
6511
|
-
const imageBase =
|
|
6512
|
-
const artifactUrl =
|
|
6513
|
-
const out =
|
|
6514
|
-
const cacheDir =
|
|
6836
|
+
const imageBase = flag5(a, "image-base");
|
|
6837
|
+
const artifactUrl = flag5(a, "artifact-url");
|
|
6838
|
+
const out = flag5(a, "out");
|
|
6839
|
+
const cacheDir = flag5(a, "cache");
|
|
6515
6840
|
const maxStories = num2("max-stories");
|
|
6516
6841
|
const r = await runImpact({
|
|
6517
6842
|
root,
|
|
6518
|
-
base:
|
|
6519
|
-
head:
|
|
6843
|
+
base: flag5(a, "base") ?? "main",
|
|
6844
|
+
head: flag5(a, "head") ?? "HEAD",
|
|
6520
6845
|
...out ? { out: resolve5(out) } : {},
|
|
6521
6846
|
...only.length ? { only } : {},
|
|
6522
6847
|
...threshold !== void 0 ? { threshold } : {},
|
|
@@ -6524,15 +6849,15 @@ async function impact(a, root) {
|
|
|
6524
6849
|
...maxStories ? { maxStoriesPerComponent: maxStories } : {},
|
|
6525
6850
|
...imageBase !== void 0 ? { imageBase } : {},
|
|
6526
6851
|
...artifactUrl ? { artifactUrl } : {},
|
|
6527
|
-
plan:
|
|
6528
|
-
keepUnchanged:
|
|
6852
|
+
plan: has7(a, "plan"),
|
|
6853
|
+
keepUnchanged: has7(a, "keep-unchanged")
|
|
6529
6854
|
});
|
|
6530
|
-
if (
|
|
6855
|
+
if (has7(a, "json")) {
|
|
6531
6856
|
print(JSON.stringify({ summary: r.summary, manifest: r.manifestPath, comment: r.commentPath ?? null, ...r.manifest.summary, selection: r.manifest.selection }, null, 2));
|
|
6532
6857
|
return 0;
|
|
6533
6858
|
}
|
|
6534
6859
|
const sel = r.manifest.selection;
|
|
6535
|
-
if (
|
|
6860
|
+
if (has7(a, "plan")) {
|
|
6536
6861
|
print(`Plan ${r.manifest.base.ref} (${r.manifest.base.sha.slice(0, 7)}) -> ${r.manifest.head.ref} (${r.manifest.head.sha.slice(0, 7)})`);
|
|
6537
6862
|
print(` tokens changing value: ${sel.tokens.length}${sel.tokens.length ? ` (${sel.tokens.filter((t) => t.kind === "direct").map((t) => t.path).slice(0, 6).join(", ")})` : ""}`);
|
|
6538
6863
|
print(` components: ${sel.components.map((c) => c.id).join(", ") || "none"}`);
|
|
@@ -6548,12 +6873,12 @@ async function impact(a, root) {
|
|
|
6548
6873
|
}
|
|
6549
6874
|
var day = (iso) => iso.slice(0, 10);
|
|
6550
6875
|
async function historyCmd(a, root) {
|
|
6551
|
-
const ctx = await
|
|
6876
|
+
const ctx = await BridgeContext8.create({ root });
|
|
6552
6877
|
const token = a._[1];
|
|
6553
|
-
const limit = Number(
|
|
6878
|
+
const limit = Number(flag5(a, "limit") ?? 20);
|
|
6554
6879
|
if (token) {
|
|
6555
6880
|
const r2 = await hist.getTokenHistory(ctx, { path: token, limit });
|
|
6556
|
-
if (
|
|
6881
|
+
if (has7(a, "json")) return print(JSON.stringify(r2, null, 2)), 0;
|
|
6557
6882
|
if (!r2.total) return print(`No recorded change of ${r2.path} in ${r2.source}.`), 0;
|
|
6558
6883
|
print(`${r2.path}: ${r2.total} change(s) (${r2.source})
|
|
6559
6884
|
`);
|
|
@@ -6564,9 +6889,9 @@ async function historyCmd(a, root) {
|
|
|
6564
6889
|
}
|
|
6565
6890
|
return 0;
|
|
6566
6891
|
}
|
|
6567
|
-
const tier =
|
|
6568
|
-
const r = await hist.getHistory(ctx, { limit, ...
|
|
6569
|
-
if (
|
|
6892
|
+
const tier = flag5(a, "tier");
|
|
6893
|
+
const r = await hist.getHistory(ctx, { limit, ...flag5(a, "author") ? { author: flag5(a, "author") } : {}, ...tier ? { tier: Number(tier) } : {}, ...flag5(a, "version") ? { version: flag5(a, "version") } : {}, ...has7(a, "breaking") ? { breaking: true } : {} });
|
|
6894
|
+
if (has7(a, "json")) return print(JSON.stringify(r, null, 2)), 0;
|
|
6570
6895
|
print(`${r.source}${r.currentVersion ? `, package version ${r.currentVersion}` : ""}`);
|
|
6571
6896
|
if (!r.total) return print(r.note ?? "No token change matches."), 0;
|
|
6572
6897
|
for (const v of r.versions) print(` ${v.version.padEnd(11)} ${day(v.date)} ${v.entries} change(s), ${v.tokenChanges} token edit(s)${v.breaking ? ", breaking" : ""}`);
|
|
@@ -6584,10 +6909,12 @@ ${r.truncated}`);
|
|
|
6584
6909
|
async function revertCmd(a, root) {
|
|
6585
6910
|
const target = a._[1];
|
|
6586
6911
|
if (!target) throw new UsageError("bridge revert <pr|commit|id> [--dry-run] (ids from `bridge history`)");
|
|
6587
|
-
const
|
|
6912
|
+
const changes = changesFlag(a);
|
|
6913
|
+
const token = changes !== "direct" ? githubToken() : void 0;
|
|
6914
|
+
const ctx = await BridgeContext8.create({ root, ...token ? { token } : {} });
|
|
6588
6915
|
const resolutions = Object.fromEntries((a.flags["use-before"] ?? []).map((k) => [k, "use-before"]));
|
|
6589
|
-
const r = await hist.proposeRevert(ctx, { target, resolutions, restoreConflicts:
|
|
6590
|
-
if (
|
|
6916
|
+
const r = await hist.proposeRevert(ctx, { target, resolutions, restoreConflicts: has7(a, "restore-conflicts"), dryRun: has7(a, "dry-run"), ...changes ? { changes } : {}, source: "cli" });
|
|
6917
|
+
if (has7(a, "json")) {
|
|
6591
6918
|
print(JSON.stringify(r, null, 2));
|
|
6592
6919
|
return r.status === "blocked" ? 1 : 0;
|
|
6593
6920
|
}
|
|
@@ -6602,35 +6929,34 @@ async function revertCmd(a, root) {
|
|
|
6602
6929
|
}
|
|
6603
6930
|
for (const s of r.skipped ?? []) print(` skipped ${s}`);
|
|
6604
6931
|
if (r.contrast?.moved) print(`contrast: ${r.contrast.moved} pair(s) move, ${r.contrast.newFailures.length} new failure(s)`);
|
|
6605
|
-
|
|
6606
|
-
if (r.next) print(String(r.next));
|
|
6932
|
+
printSent(r);
|
|
6607
6933
|
if (r.status === "dry-run") print(`
|
|
6608
|
-
${r.prBody}`);
|
|
6934
|
+
${r.suggestion?.body ?? r.prBody}`);
|
|
6609
6935
|
return r.status === "blocked" ? 1 : 0;
|
|
6610
6936
|
}
|
|
6611
6937
|
async function audit(a, root) {
|
|
6612
6938
|
const target = a._[1];
|
|
6613
6939
|
if (!target) throw new UsageError("bridge audit <path> (a product folder or file that uses the design system)");
|
|
6614
6940
|
if (!existsSync6(target)) throw new UsageError(`No such file or folder: ${target}`);
|
|
6615
|
-
const dsRoot =
|
|
6941
|
+
const dsRoot = flag5(a, "ds") ? resolve5(flag5(a, "ds")) : existsSync6(join6(root, CONFIG_FILE2)) ? root : findDesignSystemRoot(root);
|
|
6616
6942
|
if (!dsRoot) throw new UsageError(`No ${CONFIG_FILE2} found: run bridge audit from the design system repo or pass --ds <dir>.`);
|
|
6617
6943
|
const ds = loadDesignSystem(dsRoot);
|
|
6618
|
-
if (
|
|
6619
|
-
const write =
|
|
6944
|
+
if (has7(a, "fix")) {
|
|
6945
|
+
const write = has7(a, "write");
|
|
6620
6946
|
const changes = fixCss(target, ds.index, write);
|
|
6621
6947
|
const n = changes.reduce((acc, c) => acc + c.edits.length, 0);
|
|
6622
6948
|
for (const c of changes) for (const e of c.edits) print(`${c.file}:${e.line}:${e.column} ${e.from} -> ${e.to}`);
|
|
6623
6949
|
print(n ? `${write ? "Replaced" : "Would replace"} ${n} value(s) in ${changes.length} file(s) with exact token matches.${write ? "" : " Run again with --write to apply."}` : "Nothing to fix: no CSS value equals a token exactly.");
|
|
6624
6950
|
return 0;
|
|
6625
6951
|
}
|
|
6626
|
-
const baselinePath =
|
|
6952
|
+
const baselinePath = flag5(a, "baseline");
|
|
6627
6953
|
let baseline;
|
|
6628
6954
|
if (baselinePath) {
|
|
6629
6955
|
if (!existsSync6(baselinePath)) throw new UsageError(`Baseline not found: ${baselinePath}`);
|
|
6630
6956
|
baseline = JSON.parse(await readFile7(baselinePath, "utf8"));
|
|
6631
6957
|
}
|
|
6632
|
-
const label =
|
|
6633
|
-
const allowlistPath =
|
|
6958
|
+
const label = flag5(a, "label");
|
|
6959
|
+
const allowlistPath = flag5(a, "allowlist");
|
|
6634
6960
|
if (allowlistPath && !existsSync6(allowlistPath)) throw new UsageError(`Allowlist not found: ${allowlistPath}`);
|
|
6635
6961
|
let allowlist;
|
|
6636
6962
|
try {
|
|
@@ -6640,8 +6966,8 @@ async function audit(a, root) {
|
|
|
6640
6966
|
}
|
|
6641
6967
|
const report = runAudit(target, { ds: dsRoot, version: VERSION, ...label ? { label } : {}, ...baseline ? { baseline } : {}, ...allowlist ? { allowlist } : {} });
|
|
6642
6968
|
const md = auditMarkdown(report);
|
|
6643
|
-
const jsonOut =
|
|
6644
|
-
const mdOut =
|
|
6969
|
+
const jsonOut = flag5(a, "json");
|
|
6970
|
+
const mdOut = flag5(a, "md");
|
|
6645
6971
|
if (jsonOut && jsonOut !== "true") await writeFile5(jsonOut, `${JSON.stringify(report, null, 2)}
|
|
6646
6972
|
`);
|
|
6647
6973
|
if (mdOut && mdOut !== "true") await writeFile5(mdOut, `${md}
|
|
@@ -6681,7 +7007,7 @@ async function audit(a, root) {
|
|
|
6681
7007
|
for (const f of report.files) for (const e of f.ignoreErrors ?? []) process.stderr.write(`${report.target.path}/${f.path}:${e.line}: ${e.message}
|
|
6682
7008
|
`);
|
|
6683
7009
|
let code = 0;
|
|
6684
|
-
const failUnder =
|
|
7010
|
+
const failUnder = flag5(a, "fail-under");
|
|
6685
7011
|
if (failUnder !== void 0) {
|
|
6686
7012
|
const min = Number(failUnder);
|
|
6687
7013
|
if (!Number.isFinite(min)) throw new UsageError("--fail-under needs a number, e.g. --fail-under 80");
|
|
@@ -6692,7 +7018,7 @@ async function audit(a, root) {
|
|
|
6692
7018
|
code = 1;
|
|
6693
7019
|
}
|
|
6694
7020
|
}
|
|
6695
|
-
if (
|
|
7021
|
+
if (has7(a, "no-regress") && report.baseline) {
|
|
6696
7022
|
const b = report.baseline;
|
|
6697
7023
|
if ((b.tokenAdoption.delta ?? 0) < 0 || (b.componentAdoption.delta ?? 0) < 0 || b.hardcoded.delta > 0) {
|
|
6698
7024
|
process.stderr.write("bridge audit: adoption regressed since the baseline\n");
|
|
@@ -6704,9 +7030,9 @@ async function audit(a, root) {
|
|
|
6704
7030
|
async function docsCmd(a, root) {
|
|
6705
7031
|
const [, sub, name] = a._;
|
|
6706
7032
|
if (sub !== "draft" || !name) throw new UsageError("Usage: bridge docs draft <Component> [--json]");
|
|
6707
|
-
const ctx = await
|
|
7033
|
+
const ctx = await BridgeContext8.create({ root });
|
|
6708
7034
|
const d = await ops.draftDocsOp(ctx, { name });
|
|
6709
|
-
if (
|
|
7035
|
+
if (has7(a, "json")) {
|
|
6710
7036
|
print(JSON.stringify({ component: d.component, path: d.path, exists: d.exists, prefill: d.prefill, prompt: d.prompt }, null, 2));
|
|
6711
7037
|
return 0;
|
|
6712
7038
|
}
|
|
@@ -6737,17 +7063,20 @@ async function main() {
|
|
|
6737
7063
|
const argv = process.argv.slice(2);
|
|
6738
7064
|
const a = parse(argv);
|
|
6739
7065
|
const cmd = a._[0];
|
|
6740
|
-
const root = resolve5(
|
|
7066
|
+
const root = resolve5(flag5(a, "root") ?? process.cwd());
|
|
6741
7067
|
if (cmd === "request") return runRequest(a, root);
|
|
6742
7068
|
if (cmd === "explore") return runExplore(a, root);
|
|
6743
7069
|
if (cmd === "branch") return runBranch(a, root);
|
|
6744
7070
|
if (cmd === "pieces") return runPieces(a, root);
|
|
6745
7071
|
if (cmd === "figma") return runFigma(a, root);
|
|
7072
|
+
if (cmd === "suggestions" || cmd === "suggest") return runSuggestions(a, root);
|
|
7073
|
+
if (cmd === "apply") return runApply(a, root);
|
|
7074
|
+
if (cmd === "review") return runReview(a, root);
|
|
6746
7075
|
if (cmd === "products" || cmd === "settings" || cmd === "coverage") return runStudioRead(cmd, a, root);
|
|
6747
7076
|
if (cmd === "docs" && a._[1] === "save") return runDocsSave(a, root);
|
|
6748
|
-
if (!cmd ||
|
|
7077
|
+
if (!cmd || has7(a, "help") || cmd === "help") {
|
|
6749
7078
|
print(HELP);
|
|
6750
|
-
return cmd ||
|
|
7079
|
+
return cmd || has7(a, "help") ? 0 : 2;
|
|
6751
7080
|
}
|
|
6752
7081
|
if (cmd === "--version" || cmd === "version") return print(VERSION), 0;
|
|
6753
7082
|
switch (cmd) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bridge-ds/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "bridge CLI: set up Bridge in a design system repo, check tokens in CI, search and diff tokens, propose changes, start the MCP server.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"build:package": "node ../../scripts/build-packages.mjs cli"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@bridge-ds/mcp": "^0.
|
|
47
|
-
"@bridge-ds/visual": "^0.3.
|
|
46
|
+
"@bridge-ds/mcp": "^0.8.0",
|
|
47
|
+
"@bridge-ds/visual": "^0.3.7",
|
|
48
48
|
"typescript": "5.9.3"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|