@bridge-ds/core 0.17.0 → 0.19.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.
Files changed (64) hide show
  1. package/bridge.config.schema.json +16 -0
  2. package/dist/activity.d.ts +207 -0
  3. package/dist/audit/index.js +3 -3
  4. package/dist/audit/report.d.ts +17 -2
  5. package/dist/branch-actions.d.ts +7 -1
  6. package/dist/build-requests-github.d.ts +25 -0
  7. package/dist/build-requests.d.ts +403 -0
  8. package/dist/changelog-model.d.ts +102 -0
  9. package/dist/component-spec.d.ts +3 -1
  10. package/dist/config.d.ts +21 -0
  11. package/dist/css-color.d.ts +25 -0
  12. package/dist/docs-proposal.d.ts +1 -0
  13. package/dist/explorations.d.ts +1 -0
  14. package/dist/fidelity-references.d.ts +12 -0
  15. package/dist/figma-fix-loop.d.ts +153 -0
  16. package/dist/figma-fixes.d.ts +169 -0
  17. package/dist/figma-push-github.d.ts +7 -2
  18. package/dist/figma-push.d.ts +15 -2
  19. package/dist/gap-case.d.ts +144 -0
  20. package/dist/github-client.d.ts +31 -0
  21. package/dist/id-map.d.ts +10 -0
  22. package/dist/index.d.ts +14 -0
  23. package/dist/index.js +3944 -774
  24. package/dist/index.js.map +1 -1
  25. package/dist/layered-review.d.ts +2 -0
  26. package/dist/measure-status.d.ts +127 -0
  27. package/dist/measured-only.d.ts +28 -0
  28. package/dist/measured-spec.d.ts +349 -0
  29. package/dist/measured-tokens.d.ts +25 -0
  30. package/dist/motion-docs.d.ts +51 -0
  31. package/dist/node/audit.d.ts +11 -3
  32. package/dist/node/index.d.ts +1 -0
  33. package/dist/node/index.js +1637 -24
  34. package/dist/node/index.js.map +1 -1
  35. package/dist/node/measure-script.d.ts +1 -0
  36. package/dist/node/measure.d.ts +212 -0
  37. package/dist/node/spec-css.d.ts +9 -1
  38. package/dist/node/tokens-script.d.ts +3 -0
  39. package/dist/pr-composer.d.ts +5 -0
  40. package/dist/previews.d.ts +4 -1
  41. package/dist/{prod-branch-DenQkr_e.js → prod-branch-Bdx_2bRQ.js} +197 -159
  42. package/dist/prod-branch-Bdx_2bRQ.js.map +1 -0
  43. package/dist/prod-branch.d.ts +1 -1
  44. package/dist/repo-model.d.ts +5 -1
  45. package/dist/repo-source-github.d.ts +8 -0
  46. package/dist/{repo-source-B4jNZhyC.js → repo-source-m0Tc603o.js} +1406 -629
  47. package/dist/repo-source-m0Tc603o.js.map +1 -0
  48. package/dist/repo-source.d.ts +6 -0
  49. package/dist/{report-DVEmUr0p.js → report-DFPKygfk.js} +17 -6
  50. package/dist/{report-DVEmUr0p.js.map → report-DFPKygfk.js.map} +1 -1
  51. package/dist/requester.d.ts +14 -0
  52. package/dist/requests.d.ts +2 -0
  53. package/dist/review-gate.d.ts +183 -2
  54. package/dist/risk.d.ts +12 -0
  55. package/dist/share-branch.d.ts +1 -1
  56. package/dist/snapshot.d.ts +34 -0
  57. package/dist/suggestion-review.d.ts +2 -0
  58. package/dist/suggestions-github.d.ts +21 -0
  59. package/dist/suggestions.d.ts +6 -0
  60. package/dist/token-proposal.d.ts +7 -0
  61. package/dist/try-in-product.d.ts +9 -0
  62. package/package.json +1 -1
  63. package/dist/prod-branch-DenQkr_e.js.map +0 -1
  64. package/dist/repo-source-B4jNZhyC.js.map +0 -1
@@ -0,0 +1,153 @@
1
+ import { type ComponentSpec, type SpecValue } from './component-spec';
2
+ import type { FidelityComponentResult, FidelityReport } from './fidelity';
3
+ import { type AppliedFix, type FigmaFix, type FigmaFixesFile, type FixedMeasuredSpecsFile } from './figma-fixes';
4
+ import { type GapBuiltVariant, type GapCase, type GapDifference } from './gap-case';
5
+ import type { MeasuredSpecsFile } from './measured-spec';
6
+ /** Where the plugin's Check fidelity run lives in the team's repo (or on the requests branch), next to measured-specs.json. */
7
+ export declare const DEFAULT_FIDELITY_RUN_FILE = "figma-fidelity.json";
8
+ /** Rounds of try per component before the loop gives up and proposes an honest status instead. */
9
+ export declare const FIX_LOOP_MAX_ROUNDS = 3;
10
+ /** The model the CLI loop runner asks when the team's API key is set and no model is named. */
11
+ export declare const FIX_LOOP_MODEL = "claude-sonnet-5-5";
12
+ /**
13
+ * One Check fidelity run, as the plugin saves it: the report (the shot referee), and per set what the layers read back
14
+ * (values, hidden parts, boxes: they let the gap say where space is missing) and a reference to each Figma render.
15
+ */
16
+ export interface FigmaFidelityRun {
17
+ version: 1;
18
+ report: FidelityReport;
19
+ /** Set name -> variant key -> what the plugin read back from the layers. */
20
+ built?: Record<string, Record<string, GapBuiltVariant>> | undefined;
21
+ /** Set name -> variant key -> node id or exported PNG. */
22
+ figmaRenders?: Record<string, Record<string, string>> | undefined;
23
+ /** measuredAt of the measured-specs.json the pull drew from. */
24
+ measuredAt?: string | undefined;
25
+ }
26
+ /**
27
+ * Reads a fidelity run: the plugin's run file, a bare FidelityReport ("Copy the report"), or one component's result.
28
+ * Throws a sentence that says what is wrong.
29
+ */
30
+ export declare function parseFidelityRun(input: string | unknown): FigmaFidelityRun;
31
+ export interface FigmaGapRow {
32
+ component: string;
33
+ /** Differs: the shots (or the measure) say so. Can't check: nothing to compare, or a status fix says so. */
34
+ status: 'differs' | 'cant-check';
35
+ /** One sentence for a designer. */
36
+ summary: string;
37
+ /** What said it: the plugin's Check fidelity (shots), the measure's --check (values), or a status fix. */
38
+ source: 'fidelity' | 'measure' | 'figma-fixes';
39
+ checked?: number | undefined;
40
+ differing?: number | undefined;
41
+ /** Fix ids figma-fixes.json already applies to it. */
42
+ fixes?: string[] | undefined;
43
+ }
44
+ /**
45
+ * The components whose Figma build differs from their story, or that cannot be checked: from the fidelity run when
46
+ * there is one (the shots are the referee), else from the measure's statuses (`bridge specs --measure --check`). A
47
+ * status fix in figma-fixes.json shows with its reason. Matching components are left out.
48
+ */
49
+ export declare function figmaGaps(measured: FixedMeasuredSpecsFile | MeasuredSpecsFile, run?: FigmaFidelityRun): FigmaGapRow[];
50
+ export interface GapCaseForInput {
51
+ component: string;
52
+ /** measured-specs.json as measured (the team's fixes are applied here). */
53
+ measured: MeasuredSpecsFile;
54
+ /** figma-fixes.json, when the repo has one. */
55
+ fixes?: FigmaFixesFile | undefined;
56
+ run?: FigmaFidelityRun | undefined;
57
+ }
58
+ export interface GapCaseFor {
59
+ gap: GapCase;
60
+ /** The measure with the team's fixes on (what the pull reads). */
61
+ measured: FixedMeasuredSpecsFile;
62
+ /** The set the pull draws from it (specsFromMeasure), when the measure gives one. */
63
+ spec?: ComponentSpec | undefined;
64
+ fidelity?: FidelityComponentResult | undefined;
65
+ shots: Record<string, {
66
+ file: string;
67
+ }>;
68
+ }
69
+ /** The gap case of one component, built the way the plugin pulls it: fixes applied, set rebuilt, the run's verdicts. */
70
+ export declare function gapCaseFor(input: GapCaseForInput): GapCaseFor;
71
+ export declare const FIX_SCHEMA_SUMMARY = "figma-fixes.json fixes (version 1). Each fix is one JSON object:\n- Every fix: \"id\" (lower case, digits, dashes, unique), \"kind\", \"component\" (the Figma set, e.g. \"TextInput\"), \"reason\" (one plain sentence, no em dash), optional \"by\": \"claude\", optional \"where\": { \"args\": { \"<arg>\": value }, \"state\": \"rest\" | \"hover\" | \"active\" | \"focus-visible\" | \"disabled\" } (absent: every variant).\n- kind \"part\": \"part\" is a part id from the gap case (\"root\", \"cds--text-input-label-wrapper\") or \".class\". At least one of: \"set\" { field: token path | px number | null (colors only) | \"full\" (cornerRadius only) }, \"width\" / \"height\": \"fixed\" (the measured size) | px number | \"hug\" | \"fill\", \"direction\": \"HORIZONTAL\" | \"VERTICAL\", \"align\": { \"primary\": MIN|CENTER|MAX|SPACE_BETWEEN, \"counter\": MIN|CENTER|MAX|STRETCH }, \"overlay\": true | false | { \"top\": 3, \"left\": 3 }, \"hide\": true (alone; never the root). Fields: fill, stroke, strokeWeight, strokeTopWeight, strokeRightWeight, strokeBottomWeight, strokeLeftWeight, cornerRadius, paddingTop, paddingRight, paddingBottom, paddingLeft, itemSpacing, counterAxisSpacing, width, height, minWidth, minHeight, opacity, textFill, fontFamily, fontSize, fontWeight, lineHeight, letterSpacing, textDecoration.\n- kind \"axis\": \"action\" \"map\" (\"arg\", \"property\" and/or \"values\" { code value: Figma value }), \"ignore\" (\"arg\"), or \"prune\" (\"where\" names the variants to drop).\n- kind \"story\": \"where\" names the variant, \"use\": { \"story\": \"<story id>\", \"args\"?, \"state\"? } is the measured render to draw it from.\n- kind \"status\": \"status\": \"differs\" | \"cant-check\" with the reason; never \"matches\" (only the shots say a component matches). Without \"status\": a note.\nRules: fix only the component you were given; prefer the smallest fix that explains the differences (a part's size \"fixed\" when Figma hugs what code fixes, a missing padding, a hidden part); never invent a part id or a token: use the ones in the gap case.";
72
+ export type FixDiffResult = 'resolved' | 'closer' | 'same' | 'worse' | 'unknown';
73
+ export type FixVerdict = 'closer' | 'same' | 'worse';
74
+ export interface TriedDifference {
75
+ text: string;
76
+ part: string;
77
+ property: GapDifference['property'];
78
+ /** Code (the measure). */
79
+ expected: SpecValue | boolean;
80
+ /** Figma now (the gap case), and what the rebuilt set draws with the fixes. */
81
+ before: SpecValue | boolean;
82
+ after: SpecValue | boolean | undefined;
83
+ deltaBefore?: number | undefined;
84
+ deltaAfter?: number | undefined;
85
+ result: FixDiffResult;
86
+ /** The size after is worked out from the layer boxes and the changed paddings, gaps and fixed sizes. */
87
+ estimated?: boolean | undefined;
88
+ }
89
+ export interface TriedVariant {
90
+ key: string;
91
+ story?: string | undefined;
92
+ /** gone: the fixes drop this variant (prune, ignore). */
93
+ verdict: FixVerdict | 'gone';
94
+ differences: TriedDifference[];
95
+ /** Differences the fixes add: the rebuilt set now disagrees with the code where it agreed. */
96
+ regressions: GapDifference[];
97
+ }
98
+ export interface TryFixesResult {
99
+ status: 'invalid' | 'checked';
100
+ component: string;
101
+ /** Validation problems (status invalid): fix them and try again. */
102
+ problems: string[];
103
+ /** One record per proposed fix: applied, not-found (its part or story is not in this measure), already. */
104
+ applied: AppliedFix[];
105
+ /** The values referee's verdict on the whole: closer, same or worse. */
106
+ verdict: FixVerdict;
107
+ referee: 'values';
108
+ variants: TriedVariant[];
109
+ /** One sentence. */
110
+ summary: string;
111
+ /** Lines for Claude: per variant, each difference before and after. */
112
+ detail: string;
113
+ /** The figma-fixes.json the fixes make (the repo's fixes, with these added or replacing the same ids). */
114
+ file?: FigmaFixesFile | undefined;
115
+ }
116
+ export interface TryFixesInput extends GapCaseForInput {
117
+ /** The fixes to try, added to the repo's (a fix with the id of one already there replaces it). */
118
+ proposed: unknown[];
119
+ }
120
+ /** The repo's fixes with the proposed ones: a proposed fix replaces one with the same id, the others are appended. */
121
+ export declare function mergeFigmaFixes(existing: FigmaFixesFile | undefined, proposed: FigmaFix[]): FigmaFixesFile;
122
+ /**
123
+ * The values referee: validates the proposed fixes, lays them on the measure, rebuilds the set the way the plugin pulls
124
+ * it, and compares each variant the gap case lists with what the code renders, before and after. A difference is
125
+ * resolved, closer, the same or worse; a difference the fixes add elsewhere is a regression. The verdict is closer when
126
+ * more moved closer than got worse. Offline: no Figma, no shots. The pixel check runs in the plugin after the pull.
127
+ */
128
+ export declare function tryFigmaFixes(input: TryFixesInput): TryFixesResult;
129
+ /** Plain words for one fix, for the pull request body. */
130
+ export declare function fixWords(f: FigmaFix): string;
131
+ /** A part fix that sizes, places or lays out a part: the values referee can't judge it, Check fidelity does. */
132
+ export declare function touchesLayout(f: FigmaFix): boolean;
133
+ /** The pull request body of a figma-fixes.json change: the gap, each fix with its reason, before and after. */
134
+ export declare function figmaFixesPrBody(input: {
135
+ component: string;
136
+ summary: string;
137
+ gap: GapCase;
138
+ tried: TryFixesResult;
139
+ fixes: FigmaFix[];
140
+ tool: string;
141
+ }): string;
142
+ export interface FixAttempt {
143
+ fixes: unknown[];
144
+ /** Validation problems, or the try's detail. */
145
+ feedback: string;
146
+ verdict?: FixVerdict | undefined;
147
+ }
148
+ /** The prompt for one round: the gap case, the schema, and what the earlier rounds gave. Asks for fix objects only. */
149
+ export declare function fixPrompt(gap: GapCase, attempts?: FixAttempt[]): string;
150
+ /** The fix objects in a reply: a JSON array (or { "fixes": [...] }), in a code fence or not. Throws a sentence otherwise. */
151
+ export declare function parseFixReply(text: string): unknown[];
152
+ /** A status fix for a component that did not converge: what the loop proposes after its last round. */
153
+ export declare function statusFixFor(gap: GapCase, existingIds?: string[]): FigmaFix;
@@ -0,0 +1,169 @@
1
+ import { type SpecField, type SpecLayout, type SpecValue } from './component-spec';
2
+ import type { MeasuredSpecsComponent, MeasuredSpecsFile, MeasuredSpecsStory } from './measured-spec';
3
+ import { type GapState } from './gap-case';
4
+ export declare const FIGMA_FIXES_VERSION = 1;
5
+ /** Where the fixes live in the team's repo when nothing else is said. */
6
+ export declare const DEFAULT_FIGMA_FIXES_FILE = "figma-fixes.json";
7
+ export type FixArgValue = string | number | boolean;
8
+ /** Which variants a fix applies to: stories whose props (args over defaults, plus overrides) include `args`, in `state`. */
9
+ export interface FixWhere {
10
+ args?: Record<string, FixArgValue> | undefined;
11
+ state?: GapState | undefined;
12
+ }
13
+ interface FixBase {
14
+ /** Stable and unique in the file: lower case, digits and dashes ("toggle-knob-radius"). Provenance names it. */
15
+ id: string;
16
+ /** The Figma set: the last segment of the stories' title ("Toggle"), case ignored. */
17
+ component: string;
18
+ /** Why, in plain words: read in review and shown where the fix applies. */
19
+ reason: string;
20
+ /** Who wrote it: "claude" or a GitHub login. */
21
+ by?: string | undefined;
22
+ /** Only these variants. Absent: every story of the component. */
23
+ where?: FixWhere | undefined;
24
+ }
25
+ export type FixSize = 'hug' | 'fill' | 'fixed' | number;
26
+ export type FixPrimary = SpecLayout['primary'];
27
+ export type FixCounter = SpecLayout['counter'];
28
+ export type FixPin = {
29
+ top?: number;
30
+ right?: number;
31
+ bottom?: number;
32
+ left?: number;
33
+ };
34
+ /** (a) A spec patch on one part of the measured render. */
35
+ export interface PartFix extends FixBase {
36
+ kind: 'part';
37
+ /** Part id ("cds--toggle-switch", "root"), or a CSS class with a dot (".cds--toggle__switch"): every part with it. */
38
+ part: string;
39
+ /** Field values: a token path, a px number, null (no paint, colors only), or "full" for a corner radius (pill). */
40
+ set?: Partial<Record<SpecField, SpecValue | 'full'>> | undefined;
41
+ /** fixed: the measured size (or this number, px), hug: its content, fill: its container. */
42
+ width?: FixSize | undefined;
43
+ height?: FixSize | undefined;
44
+ /** Auto layout of a frame part. */
45
+ direction?: SpecLayout['direction'] | undefined;
46
+ align?: {
47
+ primary?: FixPrimary | undefined;
48
+ counter?: FixCounter | undefined;
49
+ } | undefined;
50
+ /** true: over its parent, filling it (out of auto layout). An object: pinned at these offsets (px). false: back in the flow. */
51
+ overlay?: boolean | FixPin | undefined;
52
+ /** Not drawn (with everything inside it). */
53
+ hide?: boolean | undefined;
54
+ }
55
+ /** (b) How a story arg becomes a Figma property. */
56
+ export interface AxisFix extends FixBase {
57
+ kind: 'axis';
58
+ /**
59
+ * map: name the property (`property`) and its values (`values`: code value -> Figma value).
60
+ * ignore: the arg is not a property; only stories at its default value are drawn.
61
+ * prune: drop the variants `where` names (a combination code allows that the library never shows).
62
+ */
63
+ action: 'map' | 'ignore' | 'prune';
64
+ arg?: string | undefined;
65
+ property?: string | undefined;
66
+ values?: Record<string, string> | undefined;
67
+ }
68
+ /** (c) The story a variant is drawn from. */
69
+ export interface StoryFix extends FixBase {
70
+ kind: 'story';
71
+ /** The story to use, as measured: its id, and the args and state of the measured entry when there are several. */
72
+ use: {
73
+ story: string;
74
+ args?: Record<string, FixArgValue> | undefined;
75
+ state?: GapState | undefined;
76
+ };
77
+ }
78
+ /** (d) A note, or the status to show with its reason, when a variant will not converge. Never "matches". */
79
+ export interface StatusFix extends FixBase {
80
+ kind: 'status';
81
+ /** Absent: a note only. */
82
+ status?: 'differs' | 'cant-check' | undefined;
83
+ }
84
+ export type FigmaFix = PartFix | AxisFix | StoryFix | StatusFix;
85
+ export interface FigmaFixesFile {
86
+ $schema?: string | undefined;
87
+ version: typeof FIGMA_FIXES_VERSION;
88
+ fixes: FigmaFix[];
89
+ }
90
+ export declare class FigmaFixesError extends Error {
91
+ readonly problems: string[];
92
+ constructor(problems: string[]);
93
+ }
94
+ /**
95
+ * Every problem in a fixes file, in plain words (empty when it is valid): version, unique ids, a component and a reason
96
+ * on every fix, known kinds and keys, and each kind's fields.
97
+ */
98
+ export declare function validateFigmaFixes(data: unknown): string[];
99
+ /** Reads figma-fixes.json (text or parsed JSON); throws FigmaFixesError with every problem. */
100
+ export declare function parseFigmaFixes(input: string | unknown): FigmaFixesFile;
101
+ /** What applyFixes writes on a component entry for the pull: Figma names, dropped args and combinations, status. */
102
+ export interface FigmaHints {
103
+ /** Arg -> Figma property name. */
104
+ properties?: Record<string, string> | undefined;
105
+ /** Arg -> code value -> Figma value. */
106
+ values?: Record<string, Record<string, string>> | undefined;
107
+ /** Args that are not Figma properties. */
108
+ ignored?: string[] | undefined;
109
+ /** Variant combinations dropped. */
110
+ pruned?: FixWhere[] | undefined;
111
+ /** Status to show for the component, or for some variants (`where`), with the reason. */
112
+ status?: {
113
+ status: 'differs' | 'cant-check';
114
+ reason: string;
115
+ fix: string;
116
+ where?: FixWhere | undefined;
117
+ }[] | undefined;
118
+ notes?: {
119
+ text: string;
120
+ fix: string;
121
+ where?: FixWhere | undefined;
122
+ }[] | undefined;
123
+ /** Ids of the fixes applied to this component, in file order. */
124
+ fixes?: string[] | undefined;
125
+ }
126
+ export type FixedComponent = MeasuredSpecsComponent & {
127
+ figma?: FigmaHints | undefined;
128
+ };
129
+ /** A measured story a fix changed: the ids of those fixes. */
130
+ export type FixedStory = MeasuredSpecsStory & {
131
+ fixes?: string[] | undefined;
132
+ };
133
+ export interface AppliedFix {
134
+ id: string;
135
+ kind: FigmaFix['kind'];
136
+ component: string;
137
+ /** applied: it changed what the pull draws. not-found: nothing matched (the code or the measure moved: re-check it). already: the input had it. */
138
+ result: 'applied' | 'not-found' | 'already';
139
+ /** Story entries it changed. */
140
+ stories: number;
141
+ /** Plain words: what it did, or what it did not find. */
142
+ detail: string;
143
+ }
144
+ /** The pull's input with the fixes laid on, and where they came from. */
145
+ export type FixedMeasuredSpecsFile = Omit<MeasuredSpecsFile, 'stories' | 'components'> & {
146
+ stories: FixedStory[];
147
+ components?: FixedComponent[] | undefined;
148
+ /** The fixes run that made this file. */
149
+ figmaFixes?: {
150
+ applied: AppliedFix[];
151
+ } | undefined;
152
+ };
153
+ /** A story is one of `where`'s variants: every arg it names, at that value, and the state. */
154
+ export declare function storyMatchesWhere(s: MeasuredSpecsStory, where: FixWhere | undefined): boolean;
155
+ /**
156
+ * The measured specs the pull reads, with the fixes laid on, and one provenance record per fix. Pure: the input is not
157
+ * changed. Fixes run in file order; a fix whose component, part or story is not in this measure is reported
158
+ * `not-found` (the code or the measure moved: re-check it) and changes nothing. A file that already carries a fix run
159
+ * (`figmaFixes`) keeps those fixes and only runs the new ones.
160
+ */
161
+ export declare function applyFixes(measured: MeasuredSpecsFile | FixedMeasuredSpecsFile, fixes: FigmaFixesFile | FigmaFix[]): {
162
+ file: FixedMeasuredSpecsFile;
163
+ applied: AppliedFix[];
164
+ };
165
+ /** A part fix in a few words, for provenance and review: "toggle switch: cornerRadius full, width fixed". */
166
+ export declare function describePartFix(f: PartFix): string;
167
+ /** One line per fix result, for the CLI and the plugin's pull report. */
168
+ export declare function appliedFixLines(applied: AppliedFix[]): string[];
169
+ export {};
@@ -117,6 +117,7 @@ export declare function figmaChangeOnGitHub(gh: GitHubClient, n: number, opts?:
117
117
  body: string;
118
118
  mergeable: boolean | null;
119
119
  requestedReviewers: string[];
120
+ autoMerge: boolean;
120
121
  };
121
122
  origin: FigmaOrigin;
122
123
  status: FigmaPushStatus;
@@ -127,6 +128,7 @@ export declare function figmaChangeOnGitHub(gh: GitHubClient, n: number, opts?:
127
128
  body: string;
128
129
  mergeable: boolean | null;
129
130
  requestedReviewers: string[];
131
+ autoMerge: boolean;
130
132
  };
131
133
  origin: FigmaOrigin;
132
134
  status: FigmaPushStatus;
@@ -137,12 +139,15 @@ export declare function figmaChangeOnGitHub(gh: GitHubClient, n: number, opts?:
137
139
  brief: string;
138
140
  }>;
139
141
  /**
140
- * Claude wrote the code: the branch is labelled drafted by Claude AI (no longer waiting) and its description says so.
141
- * Refused while the branch only carries the spec, so the label never claims code that is not there.
142
+ * Claude wrote the code: the pull request is marked ready for review, then the branch is labelled drafted-by-claude
143
+ * (no longer waiting) and its description says so. Refused while the branch only carries the spec, so the label never
144
+ * claims code that is not there. Ready goes first: when GitHub refuses it, nothing else changed, so the label never
145
+ * says "drafted" on a branch reviewers cannot see yet. Running it again after a partial failure finishes the job.
142
146
  */
143
147
  export declare function markFigmaChangeDraftedOnGitHub(gh: GitHubClient, n: number): Promise<{
144
148
  status: FigmaPushStatus;
145
149
  files: string[];
150
+ ready: boolean;
146
151
  }>;
147
152
  /** Where each pushed Figma component's library branch stands (by pull request number), for the plugin's Pull. */
148
153
  export declare function figmaPushStatusesOnGitHub(gh: GitHubClient, numbers: number[]): Promise<Map<number, FigmaPushStatus>>;
@@ -14,11 +14,24 @@ export interface Rebind {
14
14
  allVariants: boolean;
15
15
  before: string[];
16
16
  after: string[];
17
+ /** Values set by hand where a variable went (the property was unbound), with the variables that hold each. */
18
+ raw?: UnboundValue[] | undefined;
19
+ }
20
+ /** A value set by hand in Figma ("#FF00AA", "12") and the Figma variables whose value equals it ("radius/control"). */
21
+ export interface UnboundValue {
22
+ value: string;
23
+ matches?: string[] | undefined;
17
24
  }
18
25
  /** An edit the scan cannot read with confidence, and what the designer does about it. */
19
26
  export interface PushIssue {
20
27
  what: string;
21
28
  todo: string;
29
+ /** A property set by hand: where, and its values, so the designer sees them and can bind a variable that holds one. */
30
+ raw?: {
31
+ kind: string;
32
+ variants: string[];
33
+ values: UnboundValue[];
34
+ } | undefined;
22
35
  }
23
36
  /**
24
37
  * A component change from Figma, structured: what the designer sees in the Push tab, and what the library branch
@@ -111,7 +124,7 @@ export interface ComponentPushPlan {
111
124
  /**
112
125
  * How a component change from Figma goes back to the library. A token swap is exact when the property was painted by a
113
126
  * component token (one variable before, one after), that token paints nothing else, and the new variable is a token
114
- * the library has: the token then points to the new variable. Anything else that needs code is drafted by Claude AI;
127
+ * the library has: the token then points to the new variable. Anything else that needs code is built by a developer with Claude;
115
128
  * what cannot be read with confidence is listed with what to do.
116
129
  */
117
130
  export declare function planComponentPush(d: ComponentPushDiff, ctx: PushTokenContext): ComponentPushPlan;
@@ -194,7 +207,7 @@ export declare const figmaBranchTitle: (d: Pick<ComponentPushDiff, "component" |
194
207
  export type FigmaPushStatusKey = 'waiting-for-claude' | 'drafted' | 'in-review' | 'shipped' | 'closed';
195
208
  export interface FigmaPushStatus {
196
209
  key: FigmaPushStatusKey;
197
- /** "Waiting for Claude AI", "Shipped". */
210
+ /** "Waiting for a developer", "Shipped". */
198
211
  label: string;
199
212
  /** One sentence for the designer. */
200
213
  sentence: string;
@@ -0,0 +1,144 @@
1
+ import { type ComponentSpec, type SpecField, type SpecPart, type SpecValue, type SpecVariant } from './component-spec';
2
+ import type { FidelityComponentResult, Size } from './fidelity';
3
+ import { type MeasuredBox, type MeasuredSpec, type MeasuredSpecsFile, type MeasuredSpecsStory, type MeasuredState } from './measured-spec';
4
+ export declare const GAP_CASE_VERSION = 1;
5
+ /** How a story reaches a variant: at rest, a forced pseudo state, or disabled (an arg). */
6
+ export type GapState = 'rest' | MeasuredState | 'disabled';
7
+ /**
8
+ * What a difference is about. A Figma field (fill, paddingTop, cornerRadius, height...), or: `presence` (a part one side
9
+ * draws and the other does not), `sizing` (fixed in code, hugging in Figma), `direction` (auto layout direction).
10
+ */
11
+ export type GapProperty = SpecField | 'presence' | 'sizing' | 'direction';
12
+ /** One concrete difference. `expected` is the code (the measure), `actual` is Figma (what was drawn). */
13
+ export interface GapDifference {
14
+ /** Plain words: "Height 72 in Figma vs 84 in code: 12 px missing below the label wrapper". */
15
+ text: string;
16
+ /** Part id, as the measure and the built set name it ("root", "cds--toggle-switch"). */
17
+ part: string;
18
+ /** The part in words ("toggle switch"). */
19
+ partName: string;
20
+ property: GapProperty;
21
+ /** Code: token path, px number, null (no paint), `present`, `fixed`, `HORIZONTAL`... */
22
+ expected: SpecValue | boolean;
23
+ /** Figma: the same kinds, `missing`, `hidden`, `hug`... */
24
+ actual: SpecValue | boolean;
25
+ /** Numbers: actual minus expected, px (negative: Figma is smaller). */
26
+ delta?: number | undefined;
27
+ }
28
+ export interface GapStoryRef {
29
+ /** Storybook story id. */
30
+ id: string;
31
+ name?: string | undefined;
32
+ /** Story URL the measure opened (args in it). */
33
+ url?: string | undefined;
34
+ /** The props it rendered with (args over the component defaults), and the args Bridge passed to reach the variant. */
35
+ props: Record<string, string | number | boolean>;
36
+ overrides?: Record<string, string | number | boolean> | undefined;
37
+ state: GapState;
38
+ }
39
+ export interface GapVariant {
40
+ /** Figma variant key (variantKeyOf: "Size=md, State=Default"). */
41
+ key: string;
42
+ /** Figma props of the variant. */
43
+ props: Record<string, string>;
44
+ /** The referee's verdict for this variant: the plugin's Check fidelity (`unchecked` without one). */
45
+ verdict: 'differs' | 'unchecked';
46
+ /** Different pixels in percent, and both sizes, when the shot was compared. */
47
+ percent?: number | undefined;
48
+ size?: {
49
+ figma: Size;
50
+ code: Size;
51
+ } | undefined;
52
+ story?: GapStoryRef | undefined;
53
+ /** The story's screenshot, relative to measured-specs.json (the referee image). */
54
+ shot?: string | undefined;
55
+ /** What Figma drew for this variant: a node id or an exported PNG, when the plugin passes one. */
56
+ figmaRender?: string | undefined;
57
+ differences: GapDifference[];
58
+ /** Why the list is empty when the shots differ: no story, or the measure agrees and only the images tell. */
59
+ unexplained?: string | undefined;
60
+ }
61
+ export interface GapCase {
62
+ version: typeof GAP_CASE_VERSION;
63
+ component: string;
64
+ storybook: string;
65
+ /** `shots`: the variants come from the fidelity check. `values`: no check was passed, variants with differences. */
66
+ referee: 'shots' | 'values';
67
+ /** Variants compared, and how many differ. */
68
+ checked: number;
69
+ differing: number;
70
+ /** One sentence for a designer. */
71
+ summary: string;
72
+ /** Lines for a developer (or Claude): story, args, state, shot and every delta per variant. */
73
+ detail: string;
74
+ variants: GapVariant[];
75
+ /** Fixes from figma-fixes.json already applied to this component (the patched measure says so). */
76
+ fixes?: string[] | undefined;
77
+ }
78
+ /** What the plugin reads back from one drawn variant, laid over the spec it drew from. All optional. */
79
+ export interface GapBuiltVariant {
80
+ /** Field values the layers hold (part id -> field -> value), when they differ from the spec's. */
81
+ values?: SpecVariant['values'] | undefined;
82
+ /** Parts hidden in this variant. */
83
+ hidden?: string[] | undefined;
84
+ /** Part id -> its box relative to the variant's top-left, px: lets the gap say where space is missing. */
85
+ boxes?: Record<string, MeasuredBox> | undefined;
86
+ }
87
+ export interface GapCaseInput {
88
+ /** Set name ("Toggle"): the last segment of the stories' title. */
89
+ component: string;
90
+ measured: MeasuredSpecsFile;
91
+ /** What the pull drew the set from (specsFromMeasure's spec, or the overlaid static spec). */
92
+ spec?: ComponentSpec | undefined;
93
+ /** The plugin's Check fidelity result for this set: the referee. */
94
+ fidelity?: FidelityComponentResult | undefined;
95
+ /** Variant key -> what the layers read back. */
96
+ built?: Record<string, GapBuiltVariant> | undefined;
97
+ /** Variant key -> the story shot the set was compared with (MeasuredShots[set]). */
98
+ shots?: Record<string, {
99
+ file: string;
100
+ }> | undefined;
101
+ /** Variant key -> a reference to what Figma drew (node id, PNG path). */
102
+ figmaRenders?: Record<string, string> | undefined;
103
+ /** Token path -> its value in the measured page. Default: the measure's resolved values and token harvest. */
104
+ resolve?: ((tokenPath: string) => string | number | undefined) | undefined;
105
+ }
106
+ /** The stories of a set: the last title segment, case and punctuation ignored. */
107
+ export declare function storiesOfComponent(file: Pick<MeasuredSpecsFile, 'stories'>, component: string): MeasuredSpecsStory[];
108
+ /** A story's state: its forced pseudo state, else disabled when the arg says so, else at rest. */
109
+ export declare function storyState(s: Pick<MeasuredSpecsStory, 'state' | 'props' | 'overrides'>): GapState;
110
+ /**
111
+ * The story a Figma variant was drawn from: the one whose shot it was compared with, else the first (single instance
112
+ * before galleries) whose props, overrides and state give the variant's axis values.
113
+ */
114
+ export declare function storyForVariant(spec: Pick<ComponentSpec, 'axes'>, props: Record<string, string>, stories: MeasuredSpecsStory[], shot?: {
115
+ file: string;
116
+ }): MeasuredSpecsStory | undefined;
117
+ /** A part in words: the measure's ids are class names ("cds--toggle__switch" -> "toggle switch"). */
118
+ export declare function partWords(part: Pick<SpecPart, 'id' | 'name'> | undefined, id?: string): string;
119
+ /** Token path -> value, from the measure: components[].resolved, then the token harvest (by CSS name). */
120
+ export declare function measuredResolver(file: Pick<MeasuredSpecsFile, 'components' | 'tokens'>): (path: string) => string | number | undefined;
121
+ export interface VariantGapInput {
122
+ /** The parts the set was drawn with, and this variant's values and hidden parts. */
123
+ parts: SpecPart[];
124
+ variant: Pick<SpecVariant, 'values' | 'hidden' | 'layout'>;
125
+ properties?: ComponentSpec['properties'] | undefined;
126
+ built?: GapBuiltVariant | undefined;
127
+ measured: MeasuredSpec;
128
+ /** The fidelity check's sizes (CSS px), when the shot was compared. */
129
+ size?: {
130
+ figma: Size;
131
+ code: Size;
132
+ } | undefined;
133
+ resolve?: ((tokenPath: string) => string | number | undefined) | undefined;
134
+ }
135
+ /**
136
+ * The differences between what Figma drew for one variant and what the code renders (its measured story): the size and
137
+ * where it comes from, parts one side lacks, fixed sizes Figma hugs, auto layout direction, then field values.
138
+ */
139
+ export declare function variantDifferences(input: VariantGapInput): GapDifference[];
140
+ /**
141
+ * The gap case of one component: the variants the referee says differ (the fidelity check; without one, every variant
142
+ * with a difference), each with its story, args, state, shot, Figma render and the differences in words and deltas.
143
+ */
144
+ export declare function buildGapCase(input: GapCaseInput): GapCase;
@@ -15,6 +15,8 @@ export interface GitHubErrorInfo {
15
15
  auth?: GitHubAuthState | undefined;
16
16
  /** The permission the call needed, when GitHub or the endpoint tells (only with auth "missing-permission"). */
17
17
  permission?: GitHubPermission | undefined;
18
+ /** For a rate limit (status 429): when the limit resets, when GitHub or Bridge said. */
19
+ resetsAt?: Date | undefined;
18
20
  }
19
21
  export declare class GitHubError extends Error {
20
22
  readonly status: number;
@@ -23,6 +25,8 @@ export declare class GitHubError extends Error {
23
25
  readonly auth: GitHubAuthState | undefined;
24
26
  /** With auth "missing-permission": the permission the call needed, when known. */
25
27
  readonly permission: GitHubPermission | undefined;
28
+ /** For a rate limit (status 429): when the limit resets, when known. */
29
+ readonly resetsAt: Date | undefined;
26
30
  constructor(status: number, message: string, detail?: string | undefined, info?: GitHubErrorInfo);
27
31
  }
28
32
  /** True when `err` says GitHub refused the token itself (revoked, regenerated or expired). */
@@ -96,6 +100,8 @@ export interface Review {
96
100
  state: string;
97
101
  body: string;
98
102
  submittedAt?: string | undefined;
103
+ /** The commit the review was given on (GitHub `commit_id`). */
104
+ commitId?: string | undefined;
99
105
  }
100
106
  export interface PullRequestSummary {
101
107
  number: number;
@@ -112,6 +118,11 @@ export interface PullRequestSummary {
112
118
  updatedAt: string;
113
119
  /** Description, when the list endpoint returns it (component change requests read "Closes #n" from it). */
114
120
  body?: string | undefined;
121
+ /** ISO dates, when GitHub returns them (build requests: in progress since, shipped on). */
122
+ createdAt?: string | undefined;
123
+ mergedAt?: string | undefined;
124
+ /** Who is assigned (logins), when any. */
125
+ assignees?: string[] | undefined;
115
126
  }
116
127
  export interface ClientOptions {
117
128
  /** API base, for GitHub Enterprise or tests. */
@@ -340,8 +351,23 @@ export declare class GitHubClient {
340
351
  */
341
352
  private paged;
342
353
  getRepo(): Promise<RepoInfo>;
354
+ /**
355
+ * What the token may write here, found without writing anything: each probe is invalid on purpose (a zero sha, a
356
+ * missing head branch, an empty title), so GitHub answers 422 when the token has the permission and 403 or 404 when
357
+ * it does not. repo.permissions.push says what the user may do, not what a fine-grained token may do.
358
+ */
359
+ writeAccess(base: string): Promise<{
360
+ contents: boolean;
361
+ pullRequests: boolean;
362
+ issues: boolean;
363
+ }>;
343
364
  /** Commit sha a branch points to, or undefined when the branch does not exist. */
344
365
  getBranchSha(branch: string): Promise<string | undefined>;
366
+ /**
367
+ * Whether a branch exists, through the matching-refs list: it answers 200 with an empty list when the branch is
368
+ * missing, so asking never leaves a 404 in the browser console (an optional branch like bridge-data).
369
+ */
370
+ hasBranch(branch: string): Promise<boolean>;
345
371
  getCommitTree(commitSha: string): Promise<string>;
346
372
  getTree(treeSha: string, recursive?: boolean): Promise<{
347
373
  entries: TreeEntry[];
@@ -609,6 +635,7 @@ export declare class GitHubClient {
609
635
  body: string;
610
636
  mergeable: boolean | null;
611
637
  requestedReviewers: string[];
638
+ autoMerge: boolean;
612
639
  }>;
613
640
  listPullRequestFiles(n: number): Promise<PullRequestFile[]>;
614
641
  updatePullRequest(n: number, patch: {
@@ -782,4 +809,8 @@ export interface IssueSummary {
782
809
  author: string;
783
810
  createdAt: string;
784
811
  updatedAt: string;
812
+ /** Who is assigned (logins), when any (build requests: "Ana is on it"). */
813
+ assignees?: string[] | undefined;
814
+ /** ISO date it was closed, when closed. */
815
+ closedAt?: string | undefined;
785
816
  }
package/dist/id-map.d.ts CHANGED
@@ -17,6 +17,16 @@ export interface ReconcileResult {
17
17
  export declare function levenshtein(a: string, b: string): number;
18
18
  export declare function pathSimilarity(a: string, b: string): number;
19
19
  export declare const SIMILARITY_THRESHOLD = 0.5;
20
+ /**
21
+ * Tokens the code renamed between two commits: removed and added between `base` and `head` with the same type, tier
22
+ * and values and a similar path. One pair per token; a token that matches more than one counterpart equally is left
23
+ * as a removal and an addition (no guess). Lets a pull rename the Figma variable instead of orphaning it and adding a
24
+ * new one, so everything bound to it keeps its binding.
25
+ */
26
+ export declare function remoteRenames(base: Map<string, LogicalToken>, head: Map<string, LogicalToken>): {
27
+ from: LogicalToken;
28
+ to: LogicalToken;
29
+ }[];
20
30
  /**
21
31
  * Align Figma token ids with Git ids. `git` is every token known on the Git side (base and head).
22
32
  * Heuristic candidates are restricted to Git tokens that no Figma variable claims.