@bridge-ds/core 0.16.0 → 0.18.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.
@@ -1,4 +1,4 @@
1
- import { ck as tokenBump, aS as changeKind, L as LayeredTokenSet, aJ as aliasOf, b_ as rgbaToDtcg, bn as hexToRgba, w as parseTokenFile, q as emptyTokenFile, s as serializeTokenFile, C as moveToken, r as removeToken, bq as inheritedTypeAt, u as upsertToken, H as readTokenAt, aY as comboKey, bZ as renderedColor, b4 as contrastRatio, z as rgbaToHex, U as classifyRisk, W as RISK_LABELS, V as CONFIG_FILE, N as loadConfig, ah as ConfigError, _ as DEFAULT_TOKEN_PATHS, ag as globToRegExp, ae as slugify, Q as validateLayered, bJ as parseJson, c2 as serializeJson, bO as plainEqual, cj as toPlain, bl as getEntry, bj as fromPlain, m as modeDisplayName, A as formatValue, X as previewLinks, a1 as readPreviewComment, aR as canShip, cr as whoCanShipSentence, cm as unreadableShipTeams, c5 as soloApprover, aN as approverLists, O as tokensPath, a6 as NEEDED_BY_LABEL, K as selectTokenFiles, R as parseContrastChecks } from "./prod-branch-CSM0c2OF.js";
1
+ import { cn as tokenBump, aU as changeKind, L as LayeredTokenSet, aL as aliasOf, c1 as rgbaToDtcg, bq as hexToRgba, w as parseTokenFile, q as emptyTokenFile, s as serializeTokenFile, C as moveToken, r as removeToken, bt as inheritedTypeAt, u as upsertToken, H as readTokenAt, a_ as comboKey, c0 as renderedColor, b6 as contrastRatio, z as rgbaToHex, U as classifyRisk, W as RISK_LABELS, V as CONFIG_FILE, N as loadConfig, ah as ConfigError, _ as DEFAULT_TOKEN_PATHS, ag as globToRegExp, ae as slugify, Q as validateLayered, bM as parseJson, c5 as serializeJson, bR as plainEqual, cm as toPlain, bn as getEntry, bl as fromPlain, m as modeDisplayName, A as formatValue, X as previewLinks, a1 as readPreviewComment, aT as canShip, cv as whoCanShipSentence, cp as unreadableShipTeams, c8 as soloApprover, aP as approverLists, O as tokensPath, a6 as NEEDED_BY_LABEL, K as selectTokenFiles, R as parseContrastChecks } from "./prod-branch-DenQkr_e.js";
2
2
  function changesetBump(changes) {
3
3
  return tokenBump(changes) ?? "patch";
4
4
  }
@@ -2574,6 +2574,31 @@ function explorationRules(branch) {
2574
2574
  "- Do not mark the pull request ready for review and do not remove the exploration label: promoting is a person's decision."
2575
2575
  ];
2576
2576
  }
2577
+ const escape = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2578
+ function properWord(word, names2) {
2579
+ const bare2 = word.replace(/[^\p{L}\p{N}]+$/u, "");
2580
+ if (!bare2) return false;
2581
+ if (names2.some((n) => n === bare2)) return true;
2582
+ return /\p{Lu}/u.test(bare2.slice(1));
2583
+ }
2584
+ function lowerFirstWord(text, names2 = []) {
2585
+ const first = text.split(/\s+/)[0] ?? "";
2586
+ if (!first || properWord(first, names2)) return text;
2587
+ return `${text.charAt(0).toLowerCase()}${text.slice(1)}`;
2588
+ }
2589
+ function subjectTitle(subject, title, names2 = []) {
2590
+ const t = title.trim().replace(/\.$/, "");
2591
+ const keep = [subject, ...names2];
2592
+ const s = subject.trim();
2593
+ if (!s) return t;
2594
+ const m = new RegExp(`^${escape(s)}(?=$|[^\\p{L}\\p{N}])\\s*(:)?\\s*`, "iu").exec(t);
2595
+ if (m) {
2596
+ const rest = t.slice(m[0].length);
2597
+ if (!rest) return s;
2598
+ return m[1] ? `${s}: ${lowerFirstWord(rest, keep)}` : `${s} ${rest}`;
2599
+ }
2600
+ return `${s}: ${lowerFirstWord(t, keep)}`;
2601
+ }
2577
2602
  const REQUEST_KINDS = [
2578
2603
  { value: "new-component", label: "New component", hint: "A component the design system does not have yet." },
2579
2604
  { value: "new-variant", label: "New variant", hint: "A size, tone or state the component does not have yet." },
@@ -2589,8 +2614,7 @@ const MARK$1 = "bridge-request";
2589
2614
  const kindLabel$1 = (k) => REQUEST_KINDS.find((x) => x.value === k)?.label ?? "Other";
2590
2615
  const kindSlug = (k) => `request: ${k}`;
2591
2616
  function requestIssueTitle(r) {
2592
- const t = r.title.trim().replace(/\.$/, "");
2593
- return `${r.component}: ${t.charAt(0).toLowerCase()}${t.slice(1)}`;
2617
+ return subjectTitle(r.component, r.title);
2594
2618
  }
2595
2619
  function composeRequestIssue(r, opts = {}) {
2596
2620
  const rows = r.changes.length ? ["| What | Now | Requested |", "| --- | --- | --- |", ...r.changes.map((c) => `| ${cell$1(c.field)} | ${cell$1(c.before ?? "")} | ${cell$1(c.after ?? "")} |`)] : [];
@@ -3149,15 +3173,36 @@ function reviewStatusKind(input) {
3149
3173
  if (input.mergeable === false) return out("conflict");
3150
3174
  if (input.ci?.state === "pending") return out("ci-running");
3151
3175
  if (!input.ci || !input.approvals) return out("unknown");
3152
- if (input.mergeable === null) return out("checking");
3153
3176
  const tier = input.tier ?? 3;
3154
3177
  if (tier >= 2) {
3155
3178
  if (!input.approvals.design) return out("needs-design");
3156
3179
  if (!input.approvals.tech) return out("needs-tech");
3157
3180
  } else if (!input.approvals.approvers.length) return out("needs-approval");
3181
+ if (input.mergeable === null) return out("checking");
3158
3182
  const gate = mergeGate({ tier: input.tier, ci: input.ci, approvals: input.approvals, draft: Boolean(input.draft), mergeable: input.mergeable, state: input.state ?? "open", merged_at: input.merged_at });
3159
3183
  return gate.allowed ? out("ready") : out("unknown");
3160
3184
  }
3185
+ const REVIEW_STATUS_LABEL = {
3186
+ merged: "Merged",
3187
+ closed: "Closed",
3188
+ draft: "Draft",
3189
+ "ci-failing": "Blocked by tests",
3190
+ "changes-requested": "Changes requested",
3191
+ "ci-running": "Tests running",
3192
+ "needs-design": "Needs design approval",
3193
+ "needs-tech": "Needs tech approval",
3194
+ "needs-approval": "Needs approval",
3195
+ conflict: "Conflict with main",
3196
+ checking: "Checking for conflicts",
3197
+ ready: "Ready to merge",
3198
+ unknown: "Status unknown"
3199
+ };
3200
+ const CI_STATE_LABEL = {
3201
+ pass: "Tests pass",
3202
+ fail: "Tests fail",
3203
+ pending: "Tests running",
3204
+ none: "No automated checks"
3205
+ };
3161
3206
  const BOTS = /* @__PURE__ */ new Set(["github-actions[bot]", "app/github-actions", "changeset-bot[bot]"]);
3162
3207
  const isReleasePull = (p) => p.head.ref.startsWith("changeset-release/");
3163
3208
  function isAutomationPull(p) {
@@ -3539,6 +3584,10 @@ const SUGGESTION_STATE_LABELS = {
3539
3584
  approved: "suggestion: approved",
3540
3585
  applying: "suggestion: applying"
3541
3586
  };
3587
+ const SUGGESTION_FINAL_LABELS = {
3588
+ applied: "suggestion: applied",
3589
+ declined: "suggestion: declined"
3590
+ };
3542
3591
  const SUGGESTION_STATUS_LABEL = {
3543
3592
  "needs-review": "Needs review",
3544
3593
  "changes-requested": "Changes requested",
@@ -3759,8 +3808,8 @@ function suggestionSubject(p) {
3759
3808
  return KIND_SUBJECT[p.kind];
3760
3809
  }
3761
3810
  function suggestionIssueTitle(p) {
3762
- const t = p.title.trim().replace(/\.$/, "");
3763
- return `${suggestionSubject(p)}: ${t.charAt(0).toLowerCase()}${t.slice(1)}`;
3811
+ const names2 = [...(p.docs ?? []).map((d) => d.component), ...p.componentSpec?.component ? [p.componentSpec.component] : []];
3812
+ return subjectTitle(suggestionSubject(p), p.title, names2);
3764
3813
  }
3765
3814
  function suggestionLabels(p, opts = {}) {
3766
3815
  return [
@@ -3794,9 +3843,22 @@ function suggestionStatus(i) {
3794
3843
  if (i.outdated) return "outdated";
3795
3844
  return i.review ?? "needs-review";
3796
3845
  }
3846
+ function withOutdated(status, outdated) {
3847
+ return outdated && (status === "needs-review" || status === "changes-requested" || status === "approved") ? "outdated" : status;
3848
+ }
3797
3849
  function suggestionStateLabel(status) {
3850
+ if (status === "applied" || status === "declined") return SUGGESTION_FINAL_LABELS[status];
3798
3851
  return status in SUGGESTION_STATE_LABELS ? SUGGESTION_STATE_LABELS[status] : null;
3799
3852
  }
3853
+ const SUGGESTION_STATUS_LABELS = [...Object.values(SUGGESTION_STATE_LABELS), ...Object.values(SUGGESTION_FINAL_LABELS)];
3854
+ function suggestionLabelChanges(labels, status) {
3855
+ if (status === "outdated") return { add: [], remove: [] };
3856
+ const want = suggestionStateLabel(status);
3857
+ return {
3858
+ add: want && !labels.includes(want) ? [want] : [],
3859
+ remove: SUGGESTION_STATUS_LABELS.filter((l) => l !== want && labels.includes(l))
3860
+ };
3861
+ }
3800
3862
  const bridgeMcpInstallCommand = (repo) => `claude mcp add bridge -s user -- npx -y @bridge-ds/mcp --repo ${repo?.trim() || "<owner/name>"}`;
3801
3863
  const MARK = "bridge-suggestion";
3802
3864
  const PART_SCHEMA = "bridge.suggestion-part/1";
@@ -4185,82 +4247,403 @@ function suggestionPathProblems(p, config) {
4185
4247
  }
4186
4248
  return [...new Set(out)];
4187
4249
  }
4188
- async function createSuggestionOnGitHub(gh, payload, opts = {}) {
4189
- const issue = composeSuggestionIssue(payload, { repo: gh.fullName, ...opts });
4190
- const created = await gh.createIssue({ title: issue.title, body: issue.body, labels: issue.labels, assignees: issue.assignees });
4191
- for (const [i, part] of issue.parts.entries()) {
4192
- try {
4193
- await gh.createIssueComment(created.number, part);
4194
- } catch (err) {
4195
- const why = err instanceof Error ? err.message : String(err);
4196
- throw new GitHubError(err instanceof GitHubError ? err.status : 500, `Suggestion #${created.number} is incomplete: part ${i + 1} of ${issue.parts.length} could not be posted (${why}). Send it again, then close #${created.number}.`);
4197
- }
4198
- }
4199
- const warnings = [];
4200
- const missing = issue.labels.filter((l) => !created.labels.includes(l));
4201
- if (created.labels.length && missing.length) warnings.push(`GitHub did not keep these labels on #${created.number}: ${missing.join(", ")}.`);
4202
- return { number: created.number, url: created.url, sha: issue.sha, parts: issue.parts.length, warnings };
4250
+ const SUGGESTION_APPLY_EXIT = {
4251
+ ready: 0,
4252
+ "already-applied": 0,
4253
+ conflict: 3,
4254
+ "not-approved": 1,
4255
+ blocked: 1,
4256
+ refused: 1
4257
+ };
4258
+ const same = (a, b) => canonicalJson(a) === canonicalJson(b);
4259
+ const show = (v) => v === void 0 ? "nothing" : typeof v === "string" ? v : JSON.stringify(v);
4260
+ function tokenRoot(config) {
4261
+ const r = config.tokens.root.replace(/\/+$/, "");
4262
+ return r === "." ? "" : r;
4203
4263
  }
4204
- function linkedPullsOf(n, pulls) {
4205
- return pulls.filter((p) => closingIssueNumbers(p.body).includes(n)).map((p) => ({ number: p.number, url: p.url, state: p.state === "open" ? "open" : "closed", merged: p.state === "merged" }));
4264
+ function tokenFileOf(config, path) {
4265
+ const root = tokenRoot(config);
4266
+ if (root && !path.startsWith(`${root}/`)) return null;
4267
+ if (!selectTokenFiles(config, [path]).length) return null;
4268
+ return root ? path.slice(root.length + 1) : path;
4206
4269
  }
4207
- async function listSuggestionsOnGitHub(gh, opts = {}) {
4208
- const issues = await gh.listIssues({ label: SUGGESTION_LABEL, state: opts.state ?? "open", perPage: opts.perPage ?? 50 });
4209
- const pulls = opts.pulls ?? (issues.length ? await gh.listPullRequests("all").catch(() => []) : []);
4210
- return issues.map((i) => listItem(i, pulls));
4270
+ function planTokenEdit(set, e, changes) {
4271
+ const base = { type: "token", path: e.path, edit: e, ...e.file ? { file: e.file } : {} };
4272
+ const defsAt = (path) => set.defs.filter((d) => d.path === path && (!e.file || d.file === e.file));
4273
+ const recorded = (file, path = e.path) => changes.find((c) => c.path === path && c.file === file && (e.op === "rename" ? c.kind === "renamed" : true));
4274
+ const conflict = (detail, extra = {}) => ({ op: { ...base, state: "conflict", detail, ...extra }, notes: [] });
4275
+ const done = (detail) => ({ op: { ...base, state: "already-applied", detail }, notes: [] });
4276
+ let result;
4277
+ try {
4278
+ result = applyTokenEdits(set, [e]);
4279
+ } catch (err) {
4280
+ const why = err instanceof Error ? err.message : String(err);
4281
+ switch (e.op) {
4282
+ case "rename": {
4283
+ const hasOld = defsAt(e.path).length > 0;
4284
+ const hasNew = set.defs.some((d) => d.path === e.newPath);
4285
+ if (!hasOld && hasNew) return done(`Already renamed to ${e.newPath}.`);
4286
+ return conflict(hasOld && hasNew ? `Both ${e.path} and ${e.newPath} exist now: rename it by hand.` : `${e.path} no longer exists: ${why}`);
4287
+ }
4288
+ case "remove":
4289
+ if (!defsAt(e.path).length) return done("Already removed.");
4290
+ return conflict(why);
4291
+ case "add": {
4292
+ const existing = defsAt(e.path);
4293
+ if (!existing.length) return conflict(why);
4294
+ const want = (file) => {
4295
+ const c = recorded(file);
4296
+ if (c && c.after !== void 0) return c.after;
4297
+ return coerceValue(e.value, e.type ?? existing.find((d2) => d2.file === file)?.type);
4298
+ };
4299
+ const all = existing.every((d2) => same(d2.value, want(d2.file)));
4300
+ if (all) return done("Already added with the suggested value.");
4301
+ const d = existing.find((x) => !same(x.value, want(x.file)));
4302
+ return conflict(`Someone added ${e.path} with another value since.`, { file: d.file, after: want(d.file), now: d.value });
4303
+ }
4304
+ default:
4305
+ return conflict(defsAt(e.path).length ? why : `${e.path} no longer exists, so its value cannot be set.`);
4306
+ }
4307
+ }
4308
+ if (!result.touched.length) return done("Already has the suggested value.");
4309
+ if (e.op === "set" || e.op === "remove" || e.op === "rename") {
4310
+ for (const d of defsAt(e.path)) {
4311
+ const c = recorded(d.file, e.op === "rename" ? e.newPath ?? e.path : e.path);
4312
+ if (!c) continue;
4313
+ const okNow = same(d.value, c.before) || e.op === "set" && same(d.value, c.after);
4314
+ if (!okNow) return conflict(`${e.path} changed since it was suggested (now ${show(d.value)}, suggested from ${show(c.before)}).`, { file: d.file, before: c.before, after: c.after, now: d.value });
4315
+ }
4316
+ }
4317
+ const verb = e.op === "set" ? "Set" : e.op === "add" ? "Add" : e.op === "remove" ? "Remove" : `Rename to ${e.newPath ?? ""}`;
4318
+ return { op: { ...base, state: "apply", detail: `${verb} in ${result.touched.join(", ")}.` }, files: result.files, notes: result.notes };
4211
4319
  }
4212
- function listItem(i, pulls) {
4213
- const linkedPrs = linkedPullsOf(i.number, pulls);
4214
- const cached = suggestionStateFromLabels(i.labels);
4215
- const t = i.labels.map((l) => /^tier: ([123])$/.exec(l)?.[1]).find(Boolean);
4216
- return {
4217
- number: i.number,
4218
- title: i.title,
4219
- url: i.url,
4220
- author: i.author,
4221
- createdAt: i.createdAt,
4222
- state: i.state,
4223
- stateReason: i.stateReason,
4224
- labels: i.labels,
4225
- kind: suggestionKindFromLabels(i.labels),
4226
- tier: t ? Number(t) : void 0,
4227
- linkedPrs,
4228
- status: suggestionStatus({ state: i.state, stateReason: i.stateReason, linkedPrs, applied: cached === "applying", review: cached === "applying" ? void 0 : cached })
4229
- };
4320
+ async function planFile(ws, baseSha, f) {
4321
+ const base = { type: "file", path: f.path };
4322
+ const cur = await ws.read(f.path);
4323
+ const curSha = cur?.sha ?? null;
4324
+ const theirs = f.afterSha === null ? null : f.content ?? null;
4325
+ if (curSha === f.afterSha) return { op: { ...base, state: "already-applied", detail: f.afterSha === null ? "Already deleted." : "Already has the suggested content." } };
4326
+ if (curSha === f.beforeSha) return { op: { ...base, state: "apply", detail: f.afterSha === null ? "Delete it." : f.beforeSha === null ? "Create it." : "Replace it with the suggested content." }, content: theirs };
4327
+ if (f.beforeSha === null) return { op: { ...base, state: "conflict", detail: "The suggestion creates this file, but a different one exists now." } };
4328
+ if (!cur) return { op: { ...base, state: "conflict", detail: "The suggestion changes this file, but it was deleted since." } };
4329
+ if (theirs === null) return { op: { ...base, state: "conflict", detail: "The suggestion deletes this file, but it changed since." } };
4330
+ const original = await ws.readAt(baseSha, f.path);
4331
+ if (original === null || gitBlobSha(original) !== f.beforeSha || !ws.merge) {
4332
+ return { op: { ...base, state: "conflict", detail: original === null ? "The file changed since and its version at the suggestion base cannot be read, so it cannot be merged." : "The file changed since and cannot be merged here." } };
4333
+ }
4334
+ const merged = await ws.merge({ path: f.path, ours: cur.text, base: original, theirs });
4335
+ if (!merged.clean) return { op: { ...base, state: "conflict", detail: `The file changed since and the merge has ${merged.conflicts} conflict(s).`, hunks: merged.conflicts } };
4336
+ if (merged.text === cur.text) return { op: { ...base, state: "already-applied", detail: "Already has the suggested lines." } };
4337
+ return { op: { ...base, state: "apply-merged", detail: "The file changed since: merged with the suggested lines, no conflict." }, content: merged.text };
4230
4338
  }
4231
- async function getSuggestionOnGitHub(gh, n, gov, opts = {}) {
4232
- const [issue, comments, pulls] = await Promise.all([gh.getIssue(n), gh.listIssueComments(n), opts.pulls ? Promise.resolve(opts.pulls) : gh.listPullRequests("all").catch(() => [])]);
4233
- const parsed = parseSuggestionIssue(issue.body, { comments, author: issue.author || void 0 });
4234
- const linkedPrs = linkedPullsOf(n, pulls);
4235
- const applied = comments.flatMap((c) => parseSuggestionAppliedComment(c.body) ?? []);
4236
- const approvals = parsed.ok ? summarizeSuggestionApprovals(comments, gov, { payloadSha: parsed.sha, author: issue.author }) : void 0;
4237
- const status = suggestionStatus({
4238
- state: issue.state,
4239
- stateReason: issue.stateReason,
4240
- linkedPrs,
4241
- applied: parsed.ok && applied.some((a) => a.payloadSha === parsed.sha),
4242
- outdated: opts.outdated,
4243
- review: parsed.ok && approvals ? suggestionReviewState(parsed.payload.tier.tier, approvals) : void 0
4339
+ async function planSuggestionApply(input) {
4340
+ const { parsed, config, workspace: ws } = input;
4341
+ const payload = parsed.payload;
4342
+ const notes = [];
4343
+ const out = (outcome, message, rest2 = {}) => ({
4344
+ outcome,
4345
+ exitCode: SUGGESTION_APPLY_EXIT[outcome],
4346
+ message,
4347
+ payloadSha: parsed.sha,
4348
+ payload,
4349
+ gate: null,
4350
+ ops: [],
4351
+ writes: [],
4352
+ checks: null,
4353
+ problems: [],
4354
+ notes,
4355
+ ...payload.componentSpec ? { brief: payload.componentSpec.brief } : {},
4356
+ ...rest2
4244
4357
  });
4245
- return { issue, parsed, comments, approvals, linkedPrs, applied, status };
4246
- }
4247
- async function syncSuggestionStateLabel(gh, n, labels, status) {
4248
- const want = suggestionStateLabel(status);
4249
- let ok = true;
4250
- for (const l of Object.values(SUGGESTION_STATE_LABELS)) {
4251
- if (l === want || !labels.includes(l)) continue;
4358
+ const problems = suggestionPathProblems(payload, config);
4359
+ if (payload.checks.blocked) problems.push(`It was suggested with a failing check (${payload.checks.blocked}): ask the author to fix it and send it again.`);
4360
+ for (const f of payload.files ?? []) {
4361
+ if (f.afterSha !== null && typeof f.content !== "string") problems.push(`"${f.path}" has no content in the suggestion: it is incomplete, ask the author to send it again.`);
4362
+ else if (f.afterSha !== null && gitBlobSha(f.content) !== f.afterSha) problems.push(`"${f.path}" does not match the content it was suggested with: someone edited it by hand.`);
4363
+ }
4364
+ if (problems.length) return out("refused", "Not applied: the suggestion asks for something Bridge never writes.", { problems: [...new Set(problems)] });
4365
+ if (parsed.edited && !input.acceptEdited) {
4366
+ return out("refused", 'Not applied: the suggestion was edited by hand since Bridge wrote it. Read the change, then apply it with "accept edited" if it is right.', {
4367
+ problems: ["The payload no longer matches the sha Bridge recorded."]
4368
+ });
4369
+ }
4370
+ const root = tokenRoot(config);
4371
+ const tokenFiles = selectTokenFiles(config, await ws.list());
4372
+ const targets = [
4373
+ ...(payload.files ?? []).map((f) => f.path),
4374
+ ...(payload.docs ?? []).map((d) => d.path),
4375
+ ...payload.componentSpec ? [payload.componentSpec.specPath] : [],
4376
+ ...payload.tokens?.edits.length ? tokenFiles.map((f) => root ? `${root}/${f}` : f) : []
4377
+ ];
4378
+ if (ws.isSymlink) {
4379
+ const links = [];
4380
+ for (const p of new Set(targets)) if (await ws.isSymlink(p)) links.push(`"${p}" is a symbolic link (or inside one): Bridge never writes through links.`);
4381
+ if (links.length) return out("refused", "Not applied: the suggestion writes through a symbolic link.", { problems: links });
4382
+ }
4383
+ const governance = input.governance ?? { owners: config.governance.owners, rights: config.governance.rights ?? null, soloOwnerCanApproveBoth: config.governance.soloOwnerCanApproveBoth };
4384
+ const approvals = summarizeSuggestionApprovals(input.comments, governance, { payloadSha: parsed.sha, author: input.author });
4385
+ const review = suggestionReviewState(payload.tier.tier, approvals);
4386
+ const missing = pendingApprovals(payload.tier.tier, approvals);
4387
+ const approved = review === "approved";
4388
+ const gate = { review, missing, approvals, forced: !approved && Boolean(input.forceUnapproved), edited: parsed.edited };
4389
+ const ops = [];
4390
+ const writes = /* @__PURE__ */ new Map();
4391
+ const current = /* @__PURE__ */ new Map();
4392
+ for (const f of tokenFiles) {
4393
+ const r = await ws.read(root ? `${root}/${f}` : f);
4394
+ if (r) current.set(f, r.text);
4395
+ }
4396
+ const before = new LayeredTokenSet(config, current);
4397
+ let set = before;
4398
+ for (const e of payload.tokens?.edits ?? []) {
4399
+ const r = planTokenEdit(set, e, payload.tokens.changes);
4400
+ ops.push(r.op);
4401
+ notes.push(...r.notes);
4402
+ if (r.op.state === "apply" && r.files) {
4403
+ set = new LayeredTokenSet(config, r.files);
4404
+ }
4405
+ }
4406
+ for (const [f, text] of set.files) if (text !== before.files.get(f)) writes.set(set.repoPath(f), text);
4407
+ for (const f of payload.files ?? []) {
4408
+ if (writes.has(f.path)) {
4409
+ notes.push(`${f.path}: the token edits write this file, its copy in the suggestion is not used.`);
4410
+ continue;
4411
+ }
4412
+ const r = await planFile(ws, payload.base.sha, f);
4413
+ ops.push(r.op);
4414
+ if (r.op.state === "apply" || r.op.state === "apply-merged") writes.set(f.path, r.content ?? null);
4415
+ }
4416
+ for (const d of payload.docs ?? []) {
4417
+ const base = { type: "docs", path: d.path };
4418
+ const docs = d.after;
4419
+ let text;
4252
4420
  try {
4253
- await gh.removeLabel(n, l);
4254
- } catch {
4255
- ok = false;
4421
+ const issues = validateDocs(docs);
4422
+ if (issues.length) {
4423
+ problems.push(`${d.path}: ${issues.map((i) => i.message).join(" ")}`);
4424
+ continue;
4425
+ }
4426
+ const cur = await ws.read(d.path);
4427
+ text = docsFileText(docs, cur?.text ?? null);
4428
+ if (cur && text === cur.text) {
4429
+ ops.push({ ...base, state: "already-applied", detail: "The docs already say this." });
4430
+ continue;
4431
+ }
4432
+ ops.push({ ...base, state: "apply", detail: cur ? "Update the changed fields; other fields stay as they are now." : "Create the docs file." });
4433
+ writes.set(d.path, text);
4434
+ } catch (err) {
4435
+ problems.push(`${d.path}: the suggested docs are not a docs.json Bridge can read (${err instanceof Error ? err.message : String(err)}).`);
4256
4436
  }
4257
4437
  }
4258
- if (want && !labels.includes(want)) ok = await gh.addLabels(n, [want]) && ok;
4259
- return ok ? null : `The state label on #${n} could not be updated; the status shown in Bridge is still right.`;
4260
- }
4261
- async function reviewSuggestionOnGitHub(gh, n, input, gov) {
4262
- if (input.event === "request-changes" && !input.message?.trim()) throw new GitHubError(422, "Say what should change, in a sentence or two: the author reads it.");
4263
- const before = await getSuggestionOnGitHub(gh, n, gov);
4438
+ if (payload.componentSpec) {
4439
+ const s = payload.componentSpec;
4440
+ const text = `${JSON.stringify(s.spec, null, 2)}
4441
+ `;
4442
+ const cur = await ws.read(s.specPath);
4443
+ if (cur && cur.text === text) ops.push({ type: "component-spec", path: s.specPath, state: "already-applied", detail: "The spec file is already there." });
4444
+ else {
4445
+ ops.push({ type: "component-spec", path: s.specPath, state: "apply", detail: `Write the spec; the ${s.component} code itself is written from the brief.` });
4446
+ writes.set(s.specPath, text);
4447
+ }
4448
+ }
4449
+ const afterFiles = new Map(set.files);
4450
+ for (const [p, content] of writes) {
4451
+ const tf = tokenFileOf(config, p);
4452
+ if (!tf) continue;
4453
+ if (content === null) afterFiles.delete(tf);
4454
+ else afterFiles.set(tf, content);
4455
+ }
4456
+ const after = new LayeredTokenSet(config, afterFiles);
4457
+ const tokenChanges = diffLayered(before, after);
4458
+ const issueKey2 = (i) => `${i.code}|${i.path ?? ""}|${i.file ?? ""}|${i.message}`;
4459
+ const prevErrors = new Set(validateLayered(before).map(issueKey2));
4460
+ const newErrors = tokenChanges.length ? validateLayered(after).filter((i) => i.level === "error" && !prevErrors.has(issueKey2(i))) : [];
4461
+ let pairs = [];
4462
+ if (config.contrast && tokenChanges.length) {
4463
+ try {
4464
+ pairs = parseContrastChecks((await ws.read(config.contrast.path))?.text);
4465
+ } catch {
4466
+ notes.push(`${config.contrast.path} does not parse: contrast was not checked.`);
4467
+ }
4468
+ }
4469
+ const modes = config.contrast?.modes;
4470
+ const contrast = pairs.length ? contrastDelta(runLayeredContrast(before, pairs, modes), runLayeredContrast(after, pairs, modes)) : [];
4471
+ const newFailures = contrast.filter((d) => d.passBefore && !d.passAfter);
4472
+ const otherFiles = [...writes.keys()].filter((p) => !tokenFileOf(config, p));
4473
+ const tier = classifyRisk({ tokenChanges: tokenChanges.map((c) => ({ kind: c.kind, path: c.path, ...c.kind === "changed" ? { descriptionOnly: c.fields.length > 0 && c.fields.every((x) => x === "description") } : {} })), files: otherFiles.map((path) => ({ path })), config });
4474
+ const tierIncreased = writes.size > 0 && tier.tier > payload.tier.tier;
4475
+ const checks = { tierWhenSuggested: payload.tier.tier, tier, tierIncreased, newErrors, newFailures, contrast, tokenChanges };
4476
+ const rest = { gate, ops, checks, problems };
4477
+ if (problems.length) return out("refused", "Not applied: part of the suggestion is not valid.", rest);
4478
+ const conflicts = ops.filter((o) => o.state === "conflict");
4479
+ if (conflicts.length) {
4480
+ return out("conflict", `Not applied: ${conflicts.length} change(s) conflict with the code now. Ask the author to refresh the suggestion on today's code, or apply those parts by hand.`, rest);
4481
+ }
4482
+ if (!writes.size) return out("already-applied", "Already applied: the code has every change of this suggestion.", rest);
4483
+ if (newErrors.length || newFailures.length) {
4484
+ const why = [newErrors.length ? `${newErrors.length} new validation error(s)` : "", newFailures.length ? `${newFailures.length} contrast pair(s) would fail` : ""].filter(Boolean).join(" and ");
4485
+ return out("blocked", `Not applied: on today's code it would add ${why}. Ask the author to refresh the suggestion.`, rest);
4486
+ }
4487
+ if (tierIncreased && !input.forceUnapproved) {
4488
+ return out("blocked", `Not applied: on today's code it is a tier ${tier.tier} change, it was approved as tier ${payload.tier.tier}. It needs a new approval.`, rest);
4489
+ }
4490
+ if (!approved && !input.forceUnapproved) {
4491
+ const who = review === "changes-requested" ? `changes requested by ${approvals.changesRequested.map((l) => `@${l}`).join(", ")}` : `missing ${missing.map((m) => m === "approval" ? "an approval" : `${m} approval`).join(" and ")}`;
4492
+ return out("not-approved", `Not applied: it is not approved yet (${who}). Ask the owners to review it on the issue.`, rest);
4493
+ }
4494
+ const forced = gate.forced || tierIncreased && Boolean(input.forceUnapproved);
4495
+ return out("ready", `Ready: ${writes.size} file(s) to write${forced ? ", without every approval" : ""}.`, {
4496
+ ...rest,
4497
+ gate: { ...gate, forced },
4498
+ writes: [...writes].map(([path, content]) => ({ path, content })).sort((a, b) => a.path.localeCompare(b.path))
4499
+ });
4500
+ }
4501
+ function suggestionAppliedComment(plan, where2) {
4502
+ return composeSuggestionAppliedComment({ ...where2, payloadSha: plan.payloadSha, ...plan.gate?.forced ? { forced: true } : {} });
4503
+ }
4504
+ function suggestionApplyTable(plan) {
4505
+ const label = { "already-applied": "already applied", apply: "apply", "apply-merged": "apply (merged)", conflict: "**conflict**" };
4506
+ const cell2 = (s) => s.replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
4507
+ return ["| Change | Result | Detail |", "| --- | --- | --- |", ...plan.ops.map((o) => `| \`${cell2(o.path)}\` | ${label[o.state]} | ${cell2(o.detail)} |`)].join("\n");
4508
+ }
4509
+ async function createSuggestionOnGitHub(gh, payload, opts = {}) {
4510
+ const issue = composeSuggestionIssue(payload, { repo: gh.fullName, ...opts });
4511
+ const created = await gh.createIssue({ title: issue.title, body: issue.body, labels: issue.labels, assignees: issue.assignees });
4512
+ for (const [i, part] of issue.parts.entries()) {
4513
+ try {
4514
+ await gh.createIssueComment(created.number, part);
4515
+ } catch (err) {
4516
+ const why = err instanceof Error ? err.message : String(err);
4517
+ throw new GitHubError(err instanceof GitHubError ? err.status : 500, `Suggestion #${created.number} is incomplete: part ${i + 1} of ${issue.parts.length} could not be posted (${why}). Send it again, then close #${created.number}.`);
4518
+ }
4519
+ }
4520
+ const warnings = [];
4521
+ const missing = issue.labels.filter((l) => !created.labels.includes(l));
4522
+ if (created.labels.length && missing.length) warnings.push(`GitHub did not keep these labels on #${created.number}: ${missing.join(", ")}.`);
4523
+ return { number: created.number, url: created.url, sha: issue.sha, parts: issue.parts.length, warnings };
4524
+ }
4525
+ function linkedPullsOf(n, pulls) {
4526
+ return pulls.filter((p) => closingIssueNumbers(p.body).includes(n)).map((p) => ({ number: p.number, url: p.url, state: p.state === "open" ? "open" : "closed", merged: p.state === "merged" }));
4527
+ }
4528
+ async function listSuggestionsOnGitHub(gh, opts = {}) {
4529
+ const issues = await gh.listIssues({ label: SUGGESTION_LABEL, state: opts.state ?? "open", perPage: opts.perPage ?? 50 });
4530
+ const pulls = opts.pulls ?? (issues.length ? await gh.listPullRequests("all").catch(() => []) : []);
4531
+ return issues.map((i) => listItem(i, pulls));
4532
+ }
4533
+ function listItem(i, pulls) {
4534
+ const linkedPrs = linkedPullsOf(i.number, pulls);
4535
+ const cached = suggestionStateFromLabels(i.labels);
4536
+ const t = i.labels.map((l) => /^tier: ([123])$/.exec(l)?.[1]).find(Boolean);
4537
+ return {
4538
+ number: i.number,
4539
+ title: i.title,
4540
+ url: i.url,
4541
+ author: i.author,
4542
+ createdAt: i.createdAt,
4543
+ state: i.state,
4544
+ stateReason: i.stateReason,
4545
+ labels: i.labels,
4546
+ kind: suggestionKindFromLabels(i.labels),
4547
+ tier: t ? Number(t) : void 0,
4548
+ linkedPrs,
4549
+ status: suggestionStatus({ state: i.state, stateReason: i.stateReason, linkedPrs, applied: cached === "applying", review: cached === "applying" ? void 0 : cached })
4550
+ };
4551
+ }
4552
+ async function getSuggestionOnGitHub(gh, n, gov, opts = {}) {
4553
+ const [issue, comments, pulls] = await Promise.all([gh.getIssue(n), gh.listIssueComments(n), opts.pulls ? Promise.resolve(opts.pulls) : gh.listPullRequests("all").catch(() => [])]);
4554
+ const parsed = parseSuggestionIssue(issue.body, { comments, author: issue.author || void 0 });
4555
+ const linkedPrs = linkedPullsOf(n, pulls);
4556
+ const applied = comments.flatMap((c) => parseSuggestionAppliedComment(c.body) ?? []);
4557
+ const approvals = parsed.ok ? summarizeSuggestionApprovals(comments, gov, { payloadSha: parsed.sha, author: issue.author }) : void 0;
4558
+ const status = suggestionStatus({
4559
+ state: issue.state,
4560
+ stateReason: issue.stateReason,
4561
+ linkedPrs,
4562
+ applied: parsed.ok && applied.some((a) => a.payloadSha === parsed.sha),
4563
+ outdated: opts.outdated,
4564
+ review: parsed.ok && approvals ? suggestionReviewState(parsed.payload.tier.tier, approvals) : void 0
4565
+ });
4566
+ return { issue, parsed, comments, approvals, linkedPrs, applied, status };
4567
+ }
4568
+ async function syncSuggestionStateLabel(gh, n, labels, status) {
4569
+ const { add, remove } = suggestionLabelChanges(labels, status);
4570
+ let ok = true;
4571
+ for (const l of remove) {
4572
+ try {
4573
+ await gh.removeLabel(n, l);
4574
+ } catch {
4575
+ ok = false;
4576
+ }
4577
+ }
4578
+ if (add.length) ok = await gh.addLabels(n, add) && ok;
4579
+ return ok ? null : `The status label on #${n} could not be updated; the status shown in Bridge is still right.`;
4580
+ }
4581
+ async function markSuggestionsAppliedOnGitHub(gh, pr) {
4582
+ const applied = [];
4583
+ const warnings = [];
4584
+ for (const n of closingIssueNumbers(pr.body)) {
4585
+ try {
4586
+ const issue = await gh.getIssue(n);
4587
+ if (!issue.labels.includes(SUGGESTION_LABEL)) continue;
4588
+ const w = await syncSuggestionStateLabel(gh, n, issue.labels, "applied");
4589
+ if (w) warnings.push(w);
4590
+ else applied.push(n);
4591
+ } catch (err) {
4592
+ warnings.push(`Suggestion #${n} could not be marked applied (${err instanceof Error ? err.message : String(err)}).`);
4593
+ }
4594
+ }
4595
+ return { applied, warnings };
4596
+ }
4597
+ function githubSuggestionWorkspace(gh, ref) {
4598
+ let files;
4599
+ return {
4600
+ read: async (path) => await gh.getFile(path, ref) ?? null,
4601
+ readAt: async (sha, path) => await gh.readFile(path, sha).catch(() => void 0) ?? null,
4602
+ list: () => files ??= gh.listFiles(ref)
4603
+ };
4604
+ }
4605
+ function suggestionTouches(payload, config, path) {
4606
+ if (path === CONFIG_FILE) return true;
4607
+ if ((payload.files ?? []).some((f) => f.path === path)) return true;
4608
+ if ((payload.docs ?? []).some((d) => d.path === path)) return true;
4609
+ if (payload.componentSpec?.specPath === path) return true;
4610
+ return Boolean(payload.tokens?.edits.length) && selectTokenFiles(config, [path]).length > 0;
4611
+ }
4612
+ const MAX_COMPARE_FILES = 300;
4613
+ async function suggestionStalenessOnGitHub(gh, d, input) {
4614
+ const none = (mainSha2, mainMoved) => ({ mainSha: mainSha2, mainMoved, outdated: false, conflicts: [], plan: null });
4615
+ if (!d.parsed.ok || d.issue.state !== "open") return none(null, false);
4616
+ const payload = d.parsed.payload;
4617
+ const ref = input.ref ?? (payload.base.ref || input.config.repo?.defaultBranch || "main");
4618
+ const mainSha = await gh.getBranchSha(ref).catch(() => void 0) ?? null;
4619
+ if (!mainSha) return none(null, false);
4620
+ if (mainSha === payload.base.sha) return none(mainSha, false);
4621
+ const changed = await gh.compare(payload.base.sha, mainSha).catch(() => null);
4622
+ if (changed && changed.files.length < MAX_COMPARE_FILES && !changed.files.some((f) => suggestionTouches(payload, input.config, f.filename))) return none(mainSha, true);
4623
+ try {
4624
+ const plan = await planSuggestionApply({
4625
+ parsed: d.parsed,
4626
+ config: input.config,
4627
+ comments: d.comments,
4628
+ governance: input.governance,
4629
+ author: d.issue.author || null,
4630
+ // Only whether it still applies: approvals and a hand edit are reported apart.
4631
+ forceUnapproved: true,
4632
+ acceptEdited: true,
4633
+ workspace: githubSuggestionWorkspace(gh, mainSha)
4634
+ });
4635
+ const conflicts = plan.ops.filter((o) => o.state === "conflict");
4636
+ return { mainSha, mainMoved: true, outdated: conflicts.length > 0, conflicts, plan };
4637
+ } catch {
4638
+ return none(mainSha, true);
4639
+ }
4640
+ }
4641
+ function stalenessStatus(status, staleness) {
4642
+ return withOutdated(status, staleness?.outdated);
4643
+ }
4644
+ async function reviewSuggestionOnGitHub(gh, n, input, gov) {
4645
+ if (input.event === "request-changes" && !input.message?.trim()) throw new GitHubError(422, "Say what should change, in a sentence or two: the author reads it.");
4646
+ const before = await getSuggestionOnGitHub(gh, n, gov);
4264
4647
  if (!before.parsed.ok) throw new GitHubError(422, `#${n} cannot be reviewed: ${before.parsed.message}`);
4265
4648
  if (before.issue.state === "closed") throw new GitHubError(422, `#${n} is closed. Reopen it on GitHub to review it again.`);
4266
4649
  const sha = before.parsed.sha;
@@ -4283,8 +4666,12 @@ async function linkSuggestionPrOnGitHub(gh, n, link) {
4283
4666
  if (!link.pr && !link.branch && !link.commit) throw new GitHubError(422, "Say where it is applied: a pull request number, a branch or a commit.");
4284
4667
  const issue = await gh.getIssue(n);
4285
4668
  await gh.addComment(n, composeSuggestionAppliedComment({ by: link.by, branch: link.branch, commit: link.commit, pr: link.pr, payloadSha: link.payloadSha, forced: link.forced, at: (link.at ?? /* @__PURE__ */ new Date()).toISOString() }));
4286
- const warning = await syncSuggestionStateLabel(gh, n, issue.labels, "applying");
4287
- return { warnings: warning ? [warning] : [] };
4669
+ const status = appliedStatus(issue, link.merged);
4670
+ const warning = await syncSuggestionStateLabel(gh, n, issue.labels, status);
4671
+ return { warnings: warning ? [warning] : [], status };
4672
+ }
4673
+ function appliedStatus(issue, merged) {
4674
+ return merged || issue.state === "closed" && issue.stateReason === "completed" ? "applied" : "applying";
4288
4675
  }
4289
4676
  async function declineSuggestionOnGitHub(gh, n, reason, by) {
4290
4677
  const issue = await gh.getIssue(n);
@@ -4511,265 +4898,6 @@ async function commitFileOnGitHub(gh, input) {
4511
4898
  if (content === cur.text) return "";
4512
4899
  return gh.putFile({ path: input.path, content, branch: input.branch, message: input.message, sha: cur.sha });
4513
4900
  }
4514
- const SUGGESTION_APPLY_EXIT = {
4515
- ready: 0,
4516
- "already-applied": 0,
4517
- conflict: 3,
4518
- "not-approved": 1,
4519
- blocked: 1,
4520
- refused: 1
4521
- };
4522
- const same = (a, b) => canonicalJson(a) === canonicalJson(b);
4523
- const show = (v) => v === void 0 ? "nothing" : typeof v === "string" ? v : JSON.stringify(v);
4524
- function tokenRoot(config) {
4525
- const r = config.tokens.root.replace(/\/+$/, "");
4526
- return r === "." ? "" : r;
4527
- }
4528
- function tokenFileOf(config, path) {
4529
- const root = tokenRoot(config);
4530
- if (root && !path.startsWith(`${root}/`)) return null;
4531
- if (!selectTokenFiles(config, [path]).length) return null;
4532
- return root ? path.slice(root.length + 1) : path;
4533
- }
4534
- function planTokenEdit(set, e, changes) {
4535
- const base = { type: "token", path: e.path, edit: e, ...e.file ? { file: e.file } : {} };
4536
- const defsAt = (path) => set.defs.filter((d) => d.path === path && (!e.file || d.file === e.file));
4537
- const recorded = (file, path = e.path) => changes.find((c) => c.path === path && c.file === file && (e.op === "rename" ? c.kind === "renamed" : true));
4538
- const conflict = (detail, extra = {}) => ({ op: { ...base, state: "conflict", detail, ...extra }, notes: [] });
4539
- const done = (detail) => ({ op: { ...base, state: "already-applied", detail }, notes: [] });
4540
- let result;
4541
- try {
4542
- result = applyTokenEdits(set, [e]);
4543
- } catch (err) {
4544
- const why = err instanceof Error ? err.message : String(err);
4545
- switch (e.op) {
4546
- case "rename": {
4547
- const hasOld = defsAt(e.path).length > 0;
4548
- const hasNew = set.defs.some((d) => d.path === e.newPath);
4549
- if (!hasOld && hasNew) return done(`Already renamed to ${e.newPath}.`);
4550
- return conflict(hasOld && hasNew ? `Both ${e.path} and ${e.newPath} exist now: rename it by hand.` : `${e.path} no longer exists: ${why}`);
4551
- }
4552
- case "remove":
4553
- if (!defsAt(e.path).length) return done("Already removed.");
4554
- return conflict(why);
4555
- case "add": {
4556
- const existing = defsAt(e.path);
4557
- if (!existing.length) return conflict(why);
4558
- const want = (file) => {
4559
- const c = recorded(file);
4560
- if (c && c.after !== void 0) return c.after;
4561
- return coerceValue(e.value, e.type ?? existing.find((d2) => d2.file === file)?.type);
4562
- };
4563
- const all = existing.every((d2) => same(d2.value, want(d2.file)));
4564
- if (all) return done("Already added with the suggested value.");
4565
- const d = existing.find((x) => !same(x.value, want(x.file)));
4566
- return conflict(`Someone added ${e.path} with another value since.`, { file: d.file, after: want(d.file), now: d.value });
4567
- }
4568
- default:
4569
- return conflict(defsAt(e.path).length ? why : `${e.path} no longer exists, so its value cannot be set.`);
4570
- }
4571
- }
4572
- if (!result.touched.length) return done("Already has the suggested value.");
4573
- if (e.op === "set" || e.op === "remove" || e.op === "rename") {
4574
- for (const d of defsAt(e.path)) {
4575
- const c = recorded(d.file, e.op === "rename" ? e.newPath ?? e.path : e.path);
4576
- if (!c) continue;
4577
- const okNow = same(d.value, c.before) || e.op === "set" && same(d.value, c.after);
4578
- if (!okNow) return conflict(`${e.path} changed since it was suggested (now ${show(d.value)}, suggested from ${show(c.before)}).`, { file: d.file, before: c.before, after: c.after, now: d.value });
4579
- }
4580
- }
4581
- const verb = e.op === "set" ? "Set" : e.op === "add" ? "Add" : e.op === "remove" ? "Remove" : `Rename to ${e.newPath ?? ""}`;
4582
- return { op: { ...base, state: "apply", detail: `${verb} in ${result.touched.join(", ")}.` }, files: result.files, notes: result.notes };
4583
- }
4584
- async function planFile(ws, baseSha, f) {
4585
- const base = { type: "file", path: f.path };
4586
- const cur = await ws.read(f.path);
4587
- const curSha = cur?.sha ?? null;
4588
- const theirs = f.afterSha === null ? null : f.content ?? null;
4589
- if (curSha === f.afterSha) return { op: { ...base, state: "already-applied", detail: f.afterSha === null ? "Already deleted." : "Already has the suggested content." } };
4590
- if (curSha === f.beforeSha) return { op: { ...base, state: "apply", detail: f.afterSha === null ? "Delete it." : f.beforeSha === null ? "Create it." : "Replace it with the suggested content." }, content: theirs };
4591
- if (f.beforeSha === null) return { op: { ...base, state: "conflict", detail: "The suggestion creates this file, but a different one exists now." } };
4592
- if (!cur) return { op: { ...base, state: "conflict", detail: "The suggestion changes this file, but it was deleted since." } };
4593
- if (theirs === null) return { op: { ...base, state: "conflict", detail: "The suggestion deletes this file, but it changed since." } };
4594
- const original = await ws.readAt(baseSha, f.path);
4595
- if (original === null || gitBlobSha(original) !== f.beforeSha || !ws.merge) {
4596
- return { op: { ...base, state: "conflict", detail: original === null ? "The file changed since and its version at the suggestion base cannot be read, so it cannot be merged." : "The file changed since and cannot be merged here." } };
4597
- }
4598
- const merged = await ws.merge({ path: f.path, ours: cur.text, base: original, theirs });
4599
- if (!merged.clean) return { op: { ...base, state: "conflict", detail: `The file changed since and the merge has ${merged.conflicts} conflict(s).`, hunks: merged.conflicts } };
4600
- if (merged.text === cur.text) return { op: { ...base, state: "already-applied", detail: "Already has the suggested lines." } };
4601
- return { op: { ...base, state: "apply-merged", detail: "The file changed since: merged with the suggested lines, no conflict." }, content: merged.text };
4602
- }
4603
- async function planSuggestionApply(input) {
4604
- const { parsed, config, workspace: ws } = input;
4605
- const payload = parsed.payload;
4606
- const notes = [];
4607
- const out = (outcome, message, rest2 = {}) => ({
4608
- outcome,
4609
- exitCode: SUGGESTION_APPLY_EXIT[outcome],
4610
- message,
4611
- payloadSha: parsed.sha,
4612
- payload,
4613
- gate: null,
4614
- ops: [],
4615
- writes: [],
4616
- checks: null,
4617
- problems: [],
4618
- notes,
4619
- ...payload.componentSpec ? { brief: payload.componentSpec.brief } : {},
4620
- ...rest2
4621
- });
4622
- const problems = suggestionPathProblems(payload, config);
4623
- if (payload.checks.blocked) problems.push(`It was suggested with a failing check (${payload.checks.blocked}): ask the author to fix it and send it again.`);
4624
- for (const f of payload.files ?? []) {
4625
- if (f.afterSha !== null && typeof f.content !== "string") problems.push(`"${f.path}" has no content in the suggestion: it is incomplete, ask the author to send it again.`);
4626
- else if (f.afterSha !== null && gitBlobSha(f.content) !== f.afterSha) problems.push(`"${f.path}" does not match the content it was suggested with: someone edited it by hand.`);
4627
- }
4628
- if (problems.length) return out("refused", "Not applied: the suggestion asks for something Bridge never writes.", { problems: [...new Set(problems)] });
4629
- if (parsed.edited && !input.acceptEdited) {
4630
- return out("refused", 'Not applied: the suggestion was edited by hand since Bridge wrote it. Read the change, then apply it with "accept edited" if it is right.', {
4631
- problems: ["The payload no longer matches the sha Bridge recorded."]
4632
- });
4633
- }
4634
- const root = tokenRoot(config);
4635
- const tokenFiles = selectTokenFiles(config, await ws.list());
4636
- const targets = [
4637
- ...(payload.files ?? []).map((f) => f.path),
4638
- ...(payload.docs ?? []).map((d) => d.path),
4639
- ...payload.componentSpec ? [payload.componentSpec.specPath] : [],
4640
- ...payload.tokens?.edits.length ? tokenFiles.map((f) => root ? `${root}/${f}` : f) : []
4641
- ];
4642
- if (ws.isSymlink) {
4643
- const links = [];
4644
- for (const p of new Set(targets)) if (await ws.isSymlink(p)) links.push(`"${p}" is a symbolic link (or inside one): Bridge never writes through links.`);
4645
- if (links.length) return out("refused", "Not applied: the suggestion writes through a symbolic link.", { problems: links });
4646
- }
4647
- const governance = input.governance ?? { owners: config.governance.owners, rights: config.governance.rights ?? null, soloOwnerCanApproveBoth: config.governance.soloOwnerCanApproveBoth };
4648
- const approvals = summarizeSuggestionApprovals(input.comments, governance, { payloadSha: parsed.sha, author: input.author });
4649
- const review = suggestionReviewState(payload.tier.tier, approvals);
4650
- const missing = pendingApprovals(payload.tier.tier, approvals);
4651
- const approved = review === "approved";
4652
- const gate = { review, missing, approvals, forced: !approved && Boolean(input.forceUnapproved), edited: parsed.edited };
4653
- const ops = [];
4654
- const writes = /* @__PURE__ */ new Map();
4655
- const current = /* @__PURE__ */ new Map();
4656
- for (const f of tokenFiles) {
4657
- const r = await ws.read(root ? `${root}/${f}` : f);
4658
- if (r) current.set(f, r.text);
4659
- }
4660
- const before = new LayeredTokenSet(config, current);
4661
- let set = before;
4662
- for (const e of payload.tokens?.edits ?? []) {
4663
- const r = planTokenEdit(set, e, payload.tokens.changes);
4664
- ops.push(r.op);
4665
- notes.push(...r.notes);
4666
- if (r.op.state === "apply" && r.files) {
4667
- set = new LayeredTokenSet(config, r.files);
4668
- }
4669
- }
4670
- for (const [f, text] of set.files) if (text !== before.files.get(f)) writes.set(set.repoPath(f), text);
4671
- for (const f of payload.files ?? []) {
4672
- if (writes.has(f.path)) {
4673
- notes.push(`${f.path}: the token edits write this file, its copy in the suggestion is not used.`);
4674
- continue;
4675
- }
4676
- const r = await planFile(ws, payload.base.sha, f);
4677
- ops.push(r.op);
4678
- if (r.op.state === "apply" || r.op.state === "apply-merged") writes.set(f.path, r.content ?? null);
4679
- }
4680
- for (const d of payload.docs ?? []) {
4681
- const base = { type: "docs", path: d.path };
4682
- const docs = d.after;
4683
- let text;
4684
- try {
4685
- const issues = validateDocs(docs);
4686
- if (issues.length) {
4687
- problems.push(`${d.path}: ${issues.map((i) => i.message).join(" ")}`);
4688
- continue;
4689
- }
4690
- const cur = await ws.read(d.path);
4691
- text = docsFileText(docs, cur?.text ?? null);
4692
- if (cur && text === cur.text) {
4693
- ops.push({ ...base, state: "already-applied", detail: "The docs already say this." });
4694
- continue;
4695
- }
4696
- ops.push({ ...base, state: "apply", detail: cur ? "Update the changed fields; other fields stay as they are now." : "Create the docs file." });
4697
- writes.set(d.path, text);
4698
- } catch (err) {
4699
- problems.push(`${d.path}: the suggested docs are not a docs.json Bridge can read (${err instanceof Error ? err.message : String(err)}).`);
4700
- }
4701
- }
4702
- if (payload.componentSpec) {
4703
- const s = payload.componentSpec;
4704
- const text = `${JSON.stringify(s.spec, null, 2)}
4705
- `;
4706
- const cur = await ws.read(s.specPath);
4707
- if (cur && cur.text === text) ops.push({ type: "component-spec", path: s.specPath, state: "already-applied", detail: "The spec file is already there." });
4708
- else {
4709
- ops.push({ type: "component-spec", path: s.specPath, state: "apply", detail: `Write the spec; the ${s.component} code itself is written from the brief.` });
4710
- writes.set(s.specPath, text);
4711
- }
4712
- }
4713
- const afterFiles = new Map(set.files);
4714
- for (const [p, content] of writes) {
4715
- const tf = tokenFileOf(config, p);
4716
- if (!tf) continue;
4717
- if (content === null) afterFiles.delete(tf);
4718
- else afterFiles.set(tf, content);
4719
- }
4720
- const after = new LayeredTokenSet(config, afterFiles);
4721
- const tokenChanges = diffLayered(before, after);
4722
- const issueKey2 = (i) => `${i.code}|${i.path ?? ""}|${i.file ?? ""}|${i.message}`;
4723
- const prevErrors = new Set(validateLayered(before).map(issueKey2));
4724
- const newErrors = tokenChanges.length ? validateLayered(after).filter((i) => i.level === "error" && !prevErrors.has(issueKey2(i))) : [];
4725
- let pairs = [];
4726
- if (config.contrast && tokenChanges.length) {
4727
- try {
4728
- pairs = parseContrastChecks((await ws.read(config.contrast.path))?.text);
4729
- } catch {
4730
- notes.push(`${config.contrast.path} does not parse: contrast was not checked.`);
4731
- }
4732
- }
4733
- const modes = config.contrast?.modes;
4734
- const contrast = pairs.length ? contrastDelta(runLayeredContrast(before, pairs, modes), runLayeredContrast(after, pairs, modes)) : [];
4735
- const newFailures = contrast.filter((d) => d.passBefore && !d.passAfter);
4736
- const otherFiles = [...writes.keys()].filter((p) => !tokenFileOf(config, p));
4737
- const tier = classifyRisk({ tokenChanges: tokenChanges.map((c) => ({ kind: c.kind, path: c.path, ...c.kind === "changed" ? { descriptionOnly: c.fields.length > 0 && c.fields.every((x) => x === "description") } : {} })), files: otherFiles.map((path) => ({ path })), config });
4738
- const tierIncreased = writes.size > 0 && tier.tier > payload.tier.tier;
4739
- const checks = { tierWhenSuggested: payload.tier.tier, tier, tierIncreased, newErrors, newFailures, contrast, tokenChanges };
4740
- const rest = { gate, ops, checks, problems };
4741
- if (problems.length) return out("refused", "Not applied: part of the suggestion is not valid.", rest);
4742
- const conflicts = ops.filter((o) => o.state === "conflict");
4743
- if (conflicts.length) {
4744
- return out("conflict", `Not applied: ${conflicts.length} change(s) conflict with the code now. Ask the author to refresh the suggestion on today's code, or apply those parts by hand.`, rest);
4745
- }
4746
- if (!writes.size) return out("already-applied", "Already applied: the code has every change of this suggestion.", rest);
4747
- if (newErrors.length || newFailures.length) {
4748
- const why = [newErrors.length ? `${newErrors.length} new validation error(s)` : "", newFailures.length ? `${newFailures.length} contrast pair(s) would fail` : ""].filter(Boolean).join(" and ");
4749
- return out("blocked", `Not applied: on today's code it would add ${why}. Ask the author to refresh the suggestion.`, rest);
4750
- }
4751
- if (tierIncreased && !input.forceUnapproved) {
4752
- return out("blocked", `Not applied: on today's code it is a tier ${tier.tier} change, it was approved as tier ${payload.tier.tier}. It needs a new approval.`, rest);
4753
- }
4754
- if (!approved && !input.forceUnapproved) {
4755
- const who = review === "changes-requested" ? `changes requested by ${approvals.changesRequested.map((l) => `@${l}`).join(", ")}` : `missing ${missing.map((m) => m === "approval" ? "an approval" : `${m} approval`).join(" and ")}`;
4756
- return out("not-approved", `Not applied: it is not approved yet (${who}). Ask the owners to review it on the issue.`, rest);
4757
- }
4758
- const forced = gate.forced || tierIncreased && Boolean(input.forceUnapproved);
4759
- return out("ready", `Ready: ${writes.size} file(s) to write${forced ? ", without every approval" : ""}.`, {
4760
- ...rest,
4761
- gate: { ...gate, forced },
4762
- writes: [...writes].map(([path, content]) => ({ path, content })).sort((a, b) => a.path.localeCompare(b.path))
4763
- });
4764
- }
4765
- function suggestionAppliedComment(plan, where2) {
4766
- return composeSuggestionAppliedComment({ ...where2, payloadSha: plan.payloadSha, ...plan.gate?.forced ? { forced: true } : {} });
4767
- }
4768
- function suggestionApplyTable(plan) {
4769
- const label = { "already-applied": "already applied", apply: "apply", "apply-merged": "apply (merged)", conflict: "**conflict**" };
4770
- const cell2 = (s) => s.replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
4771
- return ["| Change | Result | Detail |", "| --- | --- | --- |", ...plan.ops.map((o) => `| \`${cell2(o.path)}\` | ${label[o.state]} | ${cell2(o.detail)} |`)].join("\n");
4772
- }
4773
4901
  function normalizeRepoPath(path) {
4774
4902
  return path.replace(/\\/g, "/").split("/").filter((s) => s && s !== ".").join("/");
4775
4903
  }
@@ -4845,7 +4973,7 @@ export {
4845
4973
  branchWithJiraKey as Z,
4846
4974
  titleWithJiraKey as _,
4847
4975
  parseChangelog as a,
4848
- assignVersions as a$,
4976
+ TOKEN_SETTINGS_URL as a$,
4849
4977
  formatFigmaSpec as a0,
4850
4978
  figmaDraftBrief as a1,
4851
4979
  parseFigmaOrigin as a2,
@@ -4856,33 +4984,33 @@ export {
4856
4984
  withFigmaOrigin as a7,
4857
4985
  WAITING_FOR_CLAUDE_LABEL as a8,
4858
4986
  DRAFTED_BY_CLAUDE_LABEL as a9,
4859
- GitHubClient as aA,
4860
- PULL_STATE_PILL as aB,
4861
- ProtectedBranchError as aC,
4862
- REDUCED_MOTION_NOTE as aD,
4863
- REQUEST_STATUS_LABEL as aE,
4864
- REVIEW_STATUS_ORDER as aF,
4865
- SPEC_FIELDS as aG,
4866
- SUGGESTION_APPLIED_MARK as aH,
4867
- SUGGESTION_APPLY_EXIT as aI,
4868
- SUGGESTION_KINDS as aJ,
4869
- SUGGESTION_LABEL as aK,
4870
- SUGGESTION_LIMITS as aL,
4871
- SUGGESTION_REVIEW_MARK as aM,
4872
- SUGGESTION_STATE_LABELS as aN,
4873
- SUGGESTION_STATUS_LABEL as aO,
4874
- SUGGESTION_TOO_LARGE as aP,
4875
- SuggestionError as aQ,
4876
- TOKEN_REJECTED_MESSAGE as aR,
4877
- TOKEN_SETTINGS_URL as aS,
4878
- TRIGGER_LABELS as aT,
4879
- UNRELEASED as aU,
4880
- acceptDocsDraft as aV,
4881
- acceptDocsText as aW,
4882
- allowedMergeMethods as aX,
4883
- anatomyLabel as aY,
4884
- assertSafeBranch as aZ,
4885
- assignRequestOnGitHub as a_,
4987
+ DOCS_EDITOR_ORIGIN as aA,
4988
+ DOCS_KEYS as aB,
4989
+ DOCS_MCP_ORIGIN as aC,
4990
+ EXPLORATION_CHECK as aD,
4991
+ EXPLORATION_STATUS_LABEL as aE,
4992
+ EXPLORATION_TIER_LABEL as aF,
4993
+ EXPLORATION_USE_CASES as aG,
4994
+ PULL_STATE_PILL as aH,
4995
+ ProtectedBranchError as aI,
4996
+ REDUCED_MOTION_NOTE as aJ,
4997
+ REQUEST_STATUS_LABEL as aK,
4998
+ REVIEW_STATUS_LABEL as aL,
4999
+ REVIEW_STATUS_ORDER as aM,
5000
+ SPEC_FIELDS as aN,
5001
+ SUGGESTION_APPLIED_MARK as aO,
5002
+ SUGGESTION_APPLY_EXIT as aP,
5003
+ SUGGESTION_FINAL_LABELS as aQ,
5004
+ SUGGESTION_KINDS as aR,
5005
+ SUGGESTION_LABEL as aS,
5006
+ SUGGESTION_LIMITS as aT,
5007
+ SUGGESTION_REVIEW_MARK as aU,
5008
+ SUGGESTION_STATE_LABELS as aV,
5009
+ SUGGESTION_STATUS_LABEL as aW,
5010
+ SUGGESTION_STATUS_LABELS as aX,
5011
+ SUGGESTION_TOO_LARGE as aY,
5012
+ SuggestionError as aZ,
5013
+ TOKEN_REJECTED_MESSAGE as a_,
4886
5014
  planComponentPush as aa,
4887
5015
  figmaOriginSection as ab,
4888
5016
  figmaBranchTitle as ac,
@@ -4895,180 +5023,194 @@ export {
4895
5023
  readRepoTexts as aj,
4896
5024
  normalizeRepoPath as ak,
4897
5025
  isUnder as al,
4898
- BULK_LIMIT as am,
4899
- COLOR_FIELDS as an,
4900
- COMPONENT_SPECS_VERSION as ao,
4901
- CONFLICT_REASON as ap,
4902
- DEFAULT_COMPONENT_SPECS_FILE as aq,
4903
- DEFAULT_PROTECTED_BRANCHES as ar,
4904
- DOCS_CHANGE_BUMP as as,
4905
- DOCS_EDITOR_ORIGIN as at,
4906
- DOCS_KEYS as au,
4907
- DOCS_MCP_ORIGIN as av,
4908
- EXPLORATION_CHECK as aw,
4909
- EXPLORATION_STATUS_LABEL as ax,
4910
- EXPLORATION_TIER_LABEL as ay,
4911
- EXPLORATION_USE_CASES as az,
5026
+ GitHubClient as am,
5027
+ effectiveTier as an,
5028
+ parseRequestIssue as ao,
5029
+ requestStatus as ap,
5030
+ listSuggestionsOnGitHub as aq,
5031
+ getSuggestionOnGitHub as ar,
5032
+ BULK_LIMIT as as,
5033
+ CI_STATE_LABEL as at,
5034
+ COLOR_FIELDS as au,
5035
+ COMPONENT_SPECS_VERSION as av,
5036
+ CONFLICT_REASON as aw,
5037
+ DEFAULT_COMPONENT_SPECS_FILE as ax,
5038
+ DEFAULT_PROTECTED_BRANCHES as ay,
5039
+ DOCS_CHANGE_BUMP as az,
4912
5040
  buildReport as b,
4913
- isMissingPermission as b$,
4914
- base64ToUtf8 as b0,
4915
- bridgeMcpInstallCommand as b1,
4916
- canOverride as b2,
4917
- canonicalJson as b3,
4918
- changeTable as b4,
4919
- changesetBump as b5,
4920
- classifyAuth as b6,
4921
- cleanDocs as b7,
4922
- clearSharedEtags as b8,
4923
- closingIssueNumbers as b9,
4924
- draftPushDescription as bA,
4925
- draftRequestText as bB,
4926
- effectiveTier as bC,
4927
- eligibleReviewers as bD,
4928
- explorationBranch as bE,
4929
- explorationRules as bF,
4930
- explorationShareMessage as bG,
4931
- explorationSlugFromBranch as bH,
4932
- figmaCell as bI,
4933
- fileModes as bJ,
4934
- filterHistory as bK,
4935
- findOpenDocsProposal as bL,
4936
- findOpenDocsProposalOnGitHub as bM,
4937
- firstParagraph as bN,
4938
- formatDocsJson as bO,
4939
- friendlyMessage as bP,
4940
- getSuggestionOnGitHub as bQ,
4941
- hasSuggestionBlock as bR,
4942
- hasWriteAccess as bS,
4943
- hashSpec as bT,
4944
- iconComponentName as bU,
4945
- installationFor as bV,
4946
- isActiveExploration as bW,
4947
- isAutomationPull as bX,
4948
- isDocsProposalBranch as bY,
4949
- isGuardrailPath as bZ,
4950
- isJiraKey as b_,
4951
- commitFileOnGitHub as ba,
4952
- componentContrastPairs as bb,
4953
- componentMotion as bc,
4954
- componentsUsingMotionToken as bd,
4955
- composeChangeset as be,
4956
- composeSuggestionAppliedComment as bf,
4957
- composeSuggestionIssue as bg,
4958
- composeSuggestionReviewComment as bh,
4959
- continueDocsProposalOnGitHub as bi,
4960
- contrastDeltaTable as bj,
4961
- countChanges as bk,
4962
- coverageSummary as bl,
4963
- cssModuleClasses as bm,
4964
- declineComment as bn,
4965
- declineReasonProblems as bo,
4966
- declineRequestOnGitHub as bp,
4967
- declineSuggestionOnGitHub as bq,
4968
- defaultDocsFooter as br,
4969
- docsAuthorLine as bs,
4970
- docsFileText as bt,
4971
- docsProposal as bu,
4972
- docsSectionShapes as bv,
4973
- docsSlug as bw,
4974
- docsSuggestionPayload as bx,
4975
- docsText as by,
4976
- draftChangeLine as bz,
5041
+ hasWriteAccess as b$,
5042
+ TRIGGER_LABELS as b0,
5043
+ UNRELEASED as b1,
5044
+ acceptDocsDraft as b2,
5045
+ acceptDocsText as b3,
5046
+ allowedMergeMethods as b4,
5047
+ anatomyLabel as b5,
5048
+ appliedStatus as b6,
5049
+ assertSafeBranch as b7,
5050
+ assignRequestOnGitHub as b8,
5051
+ assignVersions as b9,
5052
+ declineSuggestionOnGitHub as bA,
5053
+ defaultDocsFooter as bB,
5054
+ docsAuthorLine as bC,
5055
+ docsFileText as bD,
5056
+ docsProposal as bE,
5057
+ docsSectionShapes as bF,
5058
+ docsSlug as bG,
5059
+ docsSuggestionPayload as bH,
5060
+ docsText as bI,
5061
+ draftChangeLine as bJ,
5062
+ draftPushDescription as bK,
5063
+ draftRequestText as bL,
5064
+ eligibleReviewers as bM,
5065
+ explorationBranch as bN,
5066
+ explorationRules as bO,
5067
+ explorationShareMessage as bP,
5068
+ explorationSlugFromBranch as bQ,
5069
+ figmaCell as bR,
5070
+ fileModes as bS,
5071
+ filterHistory as bT,
5072
+ findOpenDocsProposal as bU,
5073
+ findOpenDocsProposalOnGitHub as bV,
5074
+ firstParagraph as bW,
5075
+ formatDocsJson as bX,
5076
+ friendlyMessage as bY,
5077
+ githubSuggestionWorkspace as bZ,
5078
+ hasSuggestionBlock as b_,
5079
+ base64ToUtf8 as ba,
5080
+ bridgeMcpInstallCommand as bb,
5081
+ canOverride as bc,
5082
+ canonicalJson as bd,
5083
+ changeTable as be,
5084
+ changesetBump as bf,
5085
+ classifyAuth as bg,
5086
+ cleanDocs as bh,
5087
+ clearSharedEtags as bi,
5088
+ closingIssueNumbers as bj,
5089
+ commitFileOnGitHub as bk,
5090
+ componentContrastPairs as bl,
5091
+ componentMotion as bm,
5092
+ componentsUsingMotionToken as bn,
5093
+ composeChangeset as bo,
5094
+ composeSuggestionAppliedComment as bp,
5095
+ composeSuggestionIssue as bq,
5096
+ composeSuggestionReviewComment as br,
5097
+ continueDocsProposalOnGitHub as bs,
5098
+ contrastDeltaTable as bt,
5099
+ countChanges as bu,
5100
+ coverageSummary as bv,
5101
+ cssModuleClasses as bw,
5102
+ declineComment as bx,
5103
+ declineReasonProblems as by,
5104
+ declineRequestOnGitHub as bz,
4977
5105
  collectHistory as c,
4978
- suggestionApplyTable as c$,
4979
- isReleasePull as c0,
4980
- isTokenRejected as c1,
4981
- jiraIssueFields as c2,
4982
- jiraKeyOf as c3,
4983
- joinRepoPath as c4,
4984
- linkRequestToExplorationOnGitHub as c5,
4985
- linkSuggestionPrOnGitHub as c6,
4986
- linkedPullsOf as c7,
4987
- listSuggestionsOnGitHub as c8,
4988
- loadRepoFiles as c9,
4989
- prNumberFromSubject as cA,
4990
- prefillDocs as cB,
4991
- proposeDocsOnGitHub as cC,
4992
- propsTable as cD,
4993
- pullState as cE,
4994
- pushChangeRows as cF,
4995
- rateLimitMessage as cG,
4996
- releasePull as cH,
4997
- reopenEligibility as cI,
4998
- requestBrief as cJ,
4999
- requestImagePath as cK,
5000
- requestIssueTitle as cL,
5001
- requestStatus as cM,
5002
- requiredApprovals as cN,
5003
- requiredPermission as cO,
5004
- reviewQueue as cP,
5005
- reviewStatusKind as cQ,
5006
- reviewSuggestionOnGitHub as cR,
5007
- reviewersToAsk as cS,
5008
- sectionsNeedingReview as cT,
5009
- sha256Hex as cU,
5010
- shortVariant as cV,
5011
- shortVariants as cW,
5012
- specHash as cX,
5013
- specsUsage as cY,
5014
- suggestionAllowedRoots as cZ,
5015
- suggestionAppliedComment as c_,
5016
- memoryEtagStore as ca,
5017
- memorySource as cb,
5018
- mergeGate as cc,
5019
- missingPermissionMessage as cd,
5020
- missingProse as ce,
5021
- modeLabel as cf,
5022
- needsDocsReview as cg,
5023
- openPulls as ch,
5024
- overrideReasonProblem as ci,
5025
- parseChangesetText as cj,
5026
- parseJiraKey as ck,
5027
- parseOverrideComment as cl,
5028
- parseRepo as cm,
5029
- parseRequestIssue as cn,
5030
- parseStructureChange as co,
5031
- parseSuggestionAppliedComment as cp,
5032
- parseSuggestionIssue as cq,
5033
- parseSuggestionReviewComment as cr,
5034
- parseTier as cs,
5035
- patchDocsJson as ct,
5036
- pendingApprovals as cu,
5037
- permissionLabel as cv,
5038
- planRestore as cw,
5039
- planRevert as cx,
5040
- planSuggestionApply as cy,
5041
- planToJson as cz,
5106
+ sectionsNeedingReview as c$,
5107
+ hashSpec as c0,
5108
+ iconComponentName as c1,
5109
+ installationFor as c2,
5110
+ isActiveExploration as c3,
5111
+ isAutomationPull as c4,
5112
+ isDocsProposalBranch as c5,
5113
+ isGuardrailPath as c6,
5114
+ isJiraKey as c7,
5115
+ isMissingPermission as c8,
5116
+ isReleasePull as c9,
5117
+ parseSuggestionReviewComment as cA,
5118
+ parseTier as cB,
5119
+ patchDocsJson as cC,
5120
+ pendingApprovals as cD,
5121
+ permissionLabel as cE,
5122
+ planRestore as cF,
5123
+ planRevert as cG,
5124
+ planSuggestionApply as cH,
5125
+ planToJson as cI,
5126
+ prNumberFromSubject as cJ,
5127
+ prefillDocs as cK,
5128
+ proposeDocsOnGitHub as cL,
5129
+ propsTable as cM,
5130
+ pullState as cN,
5131
+ pushChangeRows as cO,
5132
+ rateLimitMessage as cP,
5133
+ releasePull as cQ,
5134
+ reopenEligibility as cR,
5135
+ requestBrief as cS,
5136
+ requestImagePath as cT,
5137
+ requestIssueTitle as cU,
5138
+ requiredApprovals as cV,
5139
+ requiredPermission as cW,
5140
+ reviewQueue as cX,
5141
+ reviewStatusKind as cY,
5142
+ reviewSuggestionOnGitHub as cZ,
5143
+ reviewersToAsk as c_,
5144
+ isTokenRejected as ca,
5145
+ jiraIssueFields as cb,
5146
+ jiraKeyOf as cc,
5147
+ joinRepoPath as cd,
5148
+ linkRequestToExplorationOnGitHub as ce,
5149
+ linkSuggestionPrOnGitHub as cf,
5150
+ linkedPullsOf as cg,
5151
+ loadRepoFiles as ch,
5152
+ lowerFirstWord as ci,
5153
+ markSuggestionsAppliedOnGitHub as cj,
5154
+ memoryEtagStore as ck,
5155
+ memorySource as cl,
5156
+ mergeGate as cm,
5157
+ missingPermissionMessage as cn,
5158
+ missingProse as co,
5159
+ modeLabel as cp,
5160
+ needsDocsReview as cq,
5161
+ openPulls as cr,
5162
+ overrideReasonProblem as cs,
5163
+ parseChangesetText as ct,
5164
+ parseJiraKey as cu,
5165
+ parseOverrideComment as cv,
5166
+ parseRepo as cw,
5167
+ parseStructureChange as cx,
5168
+ parseSuggestionAppliedComment as cy,
5169
+ parseSuggestionIssue as cz,
5042
5170
  applyTokenEdits as d,
5043
- suggestionIssueTitle as d0,
5044
- suggestionKindFromLabels as d1,
5045
- suggestionLabels as d2,
5046
- suggestionPathProblems as d3,
5047
- suggestionPayloadSha as d4,
5048
- suggestionReviewState as d5,
5049
- suggestionShapeProblems as d6,
5050
- suggestionStateLabel as d7,
5051
- suggestionSubject as d8,
5052
- summarizeApprovals as d9,
5053
- summarizeCi as da,
5054
- summarizeSuggestionApprovals as db,
5055
- syncSuggestionStateLabel as dc,
5056
- titleOf as dd,
5057
- toChangeSummary as de,
5058
- tokenCssVar as df,
5059
- tokenFingerprint as dg,
5060
- tokenHistory as dh,
5061
- unsafePathProblem as di,
5062
- utf8ToBase64 as dj,
5063
- validateComponentSpec as dk,
5064
- validateDocs as dl,
5065
- validateIntegrations as dm,
5066
- variantName as dn,
5067
- versionTimeline as dp,
5068
- webStorageEtagStore as dq,
5069
- withAuthorLine as dr,
5070
- withRequestExploration as ds,
5071
- withoutJiraKey as dt,
5171
+ sha256Hex as d0,
5172
+ shortVariant as d1,
5173
+ shortVariants as d2,
5174
+ specHash as d3,
5175
+ specsUsage as d4,
5176
+ stalenessStatus as d5,
5177
+ subjectTitle as d6,
5178
+ suggestionAllowedRoots as d7,
5179
+ suggestionAppliedComment as d8,
5180
+ suggestionApplyTable as d9,
5181
+ validateIntegrations as dA,
5182
+ variantName as dB,
5183
+ versionTimeline as dC,
5184
+ webStorageEtagStore as dD,
5185
+ withAuthorLine as dE,
5186
+ withOutdated as dF,
5187
+ withRequestExploration as dG,
5188
+ withoutJiraKey as dH,
5189
+ suggestionIssueTitle as da,
5190
+ suggestionKindFromLabels as db,
5191
+ suggestionLabelChanges as dc,
5192
+ suggestionLabels as dd,
5193
+ suggestionPathProblems as de,
5194
+ suggestionPayloadSha as df,
5195
+ suggestionReviewState as dg,
5196
+ suggestionShapeProblems as dh,
5197
+ suggestionStalenessOnGitHub as di,
5198
+ suggestionStateLabel as dj,
5199
+ suggestionSubject as dk,
5200
+ suggestionTouches as dl,
5201
+ summarizeApprovals as dm,
5202
+ summarizeCi as dn,
5203
+ summarizeSuggestionApprovals as dp,
5204
+ syncSuggestionStateLabel as dq,
5205
+ titleOf as dr,
5206
+ toChangeSummary as ds,
5207
+ tokenCssVar as dt,
5208
+ tokenFingerprint as du,
5209
+ tokenHistory as dv,
5210
+ unsafePathProblem as dw,
5211
+ utf8ToBase64 as dx,
5212
+ validateComponentSpec as dy,
5213
+ validateDocs as dz,
5072
5214
  buildHistoryEntry as e,
5073
5215
  coerceValue as f,
5074
5216
  checkLayeredPair as g,
@@ -5092,4 +5234,4 @@ export {
5092
5234
  explorationNote as y,
5093
5235
  explorationNotePath as z
5094
5236
  };
5095
- //# sourceMappingURL=repo-source-73kARDJv.js.map
5237
+ //# sourceMappingURL=repo-source-CRfhLO87.js.map