@bridge-ds/core 0.16.0 → 0.17.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
+ allowedMergeMethods 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
+ EXPLORATION_USE_CASES as aA,
4988
+ GitHubClient as aB,
4989
+ PULL_STATE_PILL as aC,
4990
+ ProtectedBranchError as aD,
4991
+ REDUCED_MOTION_NOTE as aE,
4992
+ REQUEST_STATUS_LABEL as aF,
4993
+ REVIEW_STATUS_LABEL as aG,
4994
+ REVIEW_STATUS_ORDER as aH,
4995
+ SPEC_FIELDS as aI,
4996
+ SUGGESTION_APPLIED_MARK as aJ,
4997
+ SUGGESTION_APPLY_EXIT as aK,
4998
+ SUGGESTION_FINAL_LABELS as aL,
4999
+ SUGGESTION_KINDS as aM,
5000
+ SUGGESTION_LABEL as aN,
5001
+ SUGGESTION_LIMITS as aO,
5002
+ SUGGESTION_REVIEW_MARK as aP,
5003
+ SUGGESTION_STATE_LABELS as aQ,
5004
+ SUGGESTION_STATUS_LABEL as aR,
5005
+ SUGGESTION_STATUS_LABELS as aS,
5006
+ SUGGESTION_TOO_LARGE as aT,
5007
+ SuggestionError as aU,
5008
+ TOKEN_REJECTED_MESSAGE as aV,
5009
+ TOKEN_SETTINGS_URL as aW,
5010
+ TRIGGER_LABELS as aX,
5011
+ UNRELEASED as aY,
5012
+ acceptDocsDraft as aZ,
5013
+ acceptDocsText as a_,
4886
5014
  planComponentPush as aa,
4887
5015
  figmaOriginSection as ab,
4888
5016
  figmaBranchTitle as ac,
@@ -4896,179 +5024,193 @@ export {
4896
5024
  normalizeRepoPath as ak,
4897
5025
  isUnder as al,
4898
5026
  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,
5027
+ CI_STATE_LABEL as an,
5028
+ COLOR_FIELDS as ao,
5029
+ COMPONENT_SPECS_VERSION as ap,
5030
+ CONFLICT_REASON as aq,
5031
+ DEFAULT_COMPONENT_SPECS_FILE as ar,
5032
+ DEFAULT_PROTECTED_BRANCHES as as,
5033
+ DOCS_CHANGE_BUMP as at,
5034
+ DOCS_EDITOR_ORIGIN as au,
5035
+ DOCS_KEYS as av,
5036
+ DOCS_MCP_ORIGIN as aw,
5037
+ EXPLORATION_CHECK as ax,
5038
+ EXPLORATION_STATUS_LABEL as ay,
5039
+ EXPLORATION_TIER_LABEL 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
+ installationFor as b$,
5042
+ anatomyLabel as b0,
5043
+ appliedStatus as b1,
5044
+ assertSafeBranch as b2,
5045
+ assignRequestOnGitHub as b3,
5046
+ assignVersions as b4,
5047
+ base64ToUtf8 as b5,
5048
+ bridgeMcpInstallCommand as b6,
5049
+ canOverride as b7,
5050
+ canonicalJson as b8,
5051
+ changeTable as b9,
5052
+ docsSectionShapes as bA,
5053
+ docsSlug as bB,
5054
+ docsSuggestionPayload as bC,
5055
+ docsText as bD,
5056
+ draftChangeLine as bE,
5057
+ draftPushDescription as bF,
5058
+ draftRequestText as bG,
5059
+ effectiveTier as bH,
5060
+ eligibleReviewers as bI,
5061
+ explorationBranch as bJ,
5062
+ explorationRules as bK,
5063
+ explorationShareMessage as bL,
5064
+ explorationSlugFromBranch as bM,
5065
+ figmaCell as bN,
5066
+ fileModes as bO,
5067
+ filterHistory as bP,
5068
+ findOpenDocsProposal as bQ,
5069
+ findOpenDocsProposalOnGitHub as bR,
5070
+ firstParagraph as bS,
5071
+ formatDocsJson as bT,
5072
+ friendlyMessage as bU,
5073
+ getSuggestionOnGitHub as bV,
5074
+ githubSuggestionWorkspace as bW,
5075
+ hasSuggestionBlock as bX,
5076
+ hasWriteAccess as bY,
5077
+ hashSpec as bZ,
5078
+ iconComponentName as b_,
5079
+ changesetBump as ba,
5080
+ classifyAuth as bb,
5081
+ cleanDocs as bc,
5082
+ clearSharedEtags as bd,
5083
+ closingIssueNumbers as be,
5084
+ commitFileOnGitHub as bf,
5085
+ componentContrastPairs as bg,
5086
+ componentMotion as bh,
5087
+ componentsUsingMotionToken as bi,
5088
+ composeChangeset as bj,
5089
+ composeSuggestionAppliedComment as bk,
5090
+ composeSuggestionIssue as bl,
5091
+ composeSuggestionReviewComment as bm,
5092
+ continueDocsProposalOnGitHub as bn,
5093
+ contrastDeltaTable as bo,
5094
+ countChanges as bp,
5095
+ coverageSummary as bq,
5096
+ cssModuleClasses as br,
5097
+ declineComment as bs,
5098
+ declineReasonProblems as bt,
5099
+ declineRequestOnGitHub as bu,
5100
+ declineSuggestionOnGitHub as bv,
5101
+ defaultDocsFooter as bw,
5102
+ docsAuthorLine as bx,
5103
+ docsFileText as by,
5104
+ docsProposal 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
+ isActiveExploration as c0,
5108
+ isAutomationPull as c1,
5109
+ isDocsProposalBranch as c2,
5110
+ isGuardrailPath as c3,
5111
+ isJiraKey as c4,
5112
+ isMissingPermission as c5,
5113
+ isReleasePull as c6,
5114
+ isTokenRejected as c7,
5115
+ jiraIssueFields as c8,
5116
+ jiraKeyOf as c9,
5117
+ parseTier as cA,
5118
+ patchDocsJson as cB,
5119
+ pendingApprovals as cC,
5120
+ permissionLabel as cD,
5121
+ planRestore as cE,
5122
+ planRevert as cF,
5123
+ planSuggestionApply as cG,
5124
+ planToJson as cH,
5125
+ prNumberFromSubject as cI,
5126
+ prefillDocs as cJ,
5127
+ proposeDocsOnGitHub as cK,
5128
+ propsTable as cL,
5129
+ pullState as cM,
5130
+ pushChangeRows as cN,
5131
+ rateLimitMessage as cO,
5132
+ releasePull as cP,
5133
+ reopenEligibility as cQ,
5134
+ requestBrief as cR,
5135
+ requestImagePath as cS,
5136
+ requestIssueTitle as cT,
5137
+ requestStatus 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
+ joinRepoPath as ca,
5145
+ linkRequestToExplorationOnGitHub as cb,
5146
+ linkSuggestionPrOnGitHub as cc,
5147
+ linkedPullsOf as cd,
5148
+ listSuggestionsOnGitHub as ce,
5149
+ loadRepoFiles as cf,
5150
+ lowerFirstWord as cg,
5151
+ markSuggestionsAppliedOnGitHub as ch,
5152
+ memoryEtagStore as ci,
5153
+ memorySource as cj,
5154
+ mergeGate as ck,
5155
+ missingPermissionMessage as cl,
5156
+ missingProse as cm,
5157
+ modeLabel as cn,
5158
+ needsDocsReview as co,
5159
+ openPulls as cp,
5160
+ overrideReasonProblem as cq,
5161
+ parseChangesetText as cr,
5162
+ parseJiraKey as cs,
5163
+ parseOverrideComment as ct,
5164
+ parseRepo as cu,
5165
+ parseRequestIssue as cv,
5166
+ parseStructureChange as cw,
5167
+ parseSuggestionAppliedComment as cx,
5168
+ parseSuggestionIssue as cy,
5169
+ parseSuggestionReviewComment 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-B4jNZhyC.js.map