roast-my-design-system 5.10.0 → 5.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,26 +8,26 @@
8
8
 
9
9
  A free CLI tool (and Claude Code skill) that roasts your repo's design system with real data, then generates the rules that keep your AI agent on-system.
10
10
 
11
- > **New in 5.10: it reads Tailwind v3 shadcn, and every colour space.** shadcn on Tailwind v3 stores colour tokens as bare HSL channels for the opacity trick, so a textbook shadcn repo used to scan as "2 colours, none defined as CSS variables". It now reads its tokens, finds its greys (an oklch palette used to report 0 of them), and spots twins across notations: a token written as hsl and a hardcoded copy written as rgb are the same colour and now count as a near-identical pair. Validated before and after on six real shadcn repos.
11
+ > **New in 5.10: it reads Tailwind v3 shadcn, and every colour space.** shadcn on Tailwind v3 stores colour tokens as bare HSL channels, for the opacity trick. So a textbook shadcn repo used to scan as "2 colours, none defined as CSS variables". It now reads those tokens. It finds the greys (an oklch palette used to report 0). And it spots twins across notations: a token written as hsl and a hardcoded copy written as rgb are the same colour, and now count as a near-identical pair. Checked before and after on 6 real shadcn repos.
12
12
 
13
- > **New in 5.9: the agent card knows which doors your rules actually reach.** The report now states, as plain fact, which tools can read the rules files you have: Claude Code reads CLAUDE.md, Codex reads AGENTS.md, Cursor reads AGENTS.md and `.cursor/rules`. Where a door is missing, the card shows the one-line fix (Claude Code skips AGENTS.md; a CLAUDE.md containing the single line `@AGENTS.md` closes the gap). The scan also finds rules files nested in subfolders now, which is how monorepos really do it (twenty carries 35 AGENTS.md files; a root-only look reported 1), and it recognises GEMINI.md, `.windsurf/rules` and `.github/instructions`.
13
+ > **New in 5.9: the agent card knows which doors your rules actually reach.** The report now states, as plain fact, which tools can read the rules files you have. Claude Code reads CLAUDE.md. Codex reads AGENTS.md. Cursor reads AGENTS.md and `.cursor/rules`. Where a door is missing, the card shows the one-line fix: Claude Code skips AGENTS.md, and a CLAUDE.md containing the single line `@AGENTS.md` closes the gap. The scan also finds rules files nested in subfolders, which is how monorepos really do it (twenty carries 35 AGENTS.md files; a root-only look reported 1). It recognises GEMINI.md, `.windsurf/rules` and `.github/instructions` too.
14
14
 
15
- > **New in 5.8: every finding can explain itself.** A "why this matters" toggle under each finding unfolds the calm story behind the brutal number: how the mess arrives innocently, what it costs later, how an agent multiplies it, and why the ideal sits where it sits, benchmark medians included. Written to a plain-language standard, reviewed word by word.
15
+ > **New in 5.8: every finding can explain itself.** A "why this matters" toggle sits under each finding. It unfolds the calm story behind the brutal number. How the mess arrives innocently, what it costs later, how an agent multiplies it, and why the ideal sits where it sits, benchmark medians included. Written to a plain-language standard and reviewed word by word.
16
16
 
17
- > **New in 5.0: it runs as a local MCP server.** One command, and your agent asks the design system before writing UI, then gets the work checked after: which Button is canonical, which token holds that colour, review my changes. Local, deterministic, nothing leaves your machine. See [Live answers over MCP](#live-answers-over-mcp).
17
+ > **New in 5.0: it runs as a local MCP server.** One command, and your agent asks the design system before writing UI, then gets the work checked after. Which Button is canonical? Which token holds that colour? Review my changes. Local, deterministic, nothing leaves your machine. See [Live answers over MCP](#live-answers-over-mcp).
18
18
 
19
19
  Run it on your codebase and get, in about a second:
20
20
 
21
- - **A health score you can defend in a meeting.** 0-100, deterministic, benchmarked against Ideal Design System norms, 34 scanned public repos and 10 reputable design systems (Primer, Polaris, Carbon, shadcn/ui…).
21
+ - **A health score you can defend in a meeting.** 0 to 100, deterministic, and benchmarked against 3 yardsticks: Ideal Design System norms, 34 scanned public repos and 10 reputable design systems (Primer, Polaris, Carbon, shadcn/ui…).
22
22
  - **Per-package scores for monorepos.** One blended number hides which package is the problem: `packages/ui` scores 80 while `apps/web` scores 40, and now you can see it.
23
- - **Reads React and web components alike.** Stencil, Lit and custom elements detected by tag registration, counted by kebab tag; libraries get composition maps, not adoption accusations; earned token namespaces named in the header; and anything the scan cannot read is declared "not measured" instead of scored.
24
- - **The receipts behind it.** Every colour and its near-identical twin, every spacing value, typeface, duplicated or never-imported component, inline style and !important, with real file paths, in one self-contained HTML report you can open, Slack or email.
25
- - **The first fixes ranked by payoff, each with its prompt.** A "Where to start" list derived from your own numbers, and every move carries a copy button with a ready-made fix prompt for your agent: the finding, the files, the expected payoff, and rules that respect deliberate craft. Fix, re-run the scan, press the next button.
23
+ - **Reads React and web components alike.** Stencil, Lit and custom elements are detected by tag registration and counted by kebab tag. Libraries get composition maps, not adoption accusations. Earned token namespaces are named in the header. Anything the scan can not read is declared "not measured" instead of scored.
24
+ - **The receipts behind it.** Every colour and its near-identical twin. Every spacing value, typeface, duplicated or never-imported component, inline style and !important. All with real file paths, in one self-contained HTML report you can open, Slack or email.
25
+ - **The first fixes ranked by payoff, each with its prompt.** A "Where to start" list derived from your own numbers. Every move carries a copy button with a ready-made fix prompt for your agent: the finding, the files, the expected payoff, and rules that respect deliberate craft. Fix, re-run the scan, press the next button.
26
26
  - **Rules that stop the mess coming back.** A generated `design-system-rules.md` with canonical components, your token file, and known duplicates to avoid, so your AI agent follows your system instead of guessing at it. `--apply` injects them into every agent file you have: Claude, Cursor, GitHub Copilot, and Windsurf. Every scan also checks the rules you already have for stale references: paths that no longer exist, components named canonical that nothing imports anymore. And the agent card states which tools can actually read the rules you have (Claude Code, Codex, Cursor), with the one-line fix where a door is missing.
27
27
 
28
28
  ## Why this exists
29
29
 
30
- Your AI agent (Claude, Cursor, Copilot) builds UI by imitating what's already in your repo. If your repo has 112 colours and four Button implementations, your agent guesses which one is canonical, and it picks wrong half the time. That's why AI-generated UI looks *almost-but-not-quite* right. The first step to fixing it is seeing the mess measured.
30
+ Your AI agent (Claude, Cursor, Copilot) builds UI by imitating what's already in your repo. If your repo has 112 colours and 4 Button implementations, your agent guesses which one is canonical, and it picks wrong half the time. That's why AI-generated UI looks *almost-but-not-quite* right. The first step to fixing it is seeing the mess measured.
31
31
 
32
32
  ## Every command
33
33
 
@@ -41,7 +41,7 @@ One scan powers all of it; the flags decide what lands on disk. Combine freely.
41
41
  | `... --rules` | The same rules written to `design-system-rules.md` instead, for pasting by hand |
42
42
  | `... --card` | `roast-card.svg`: a shareable 1200x630 card with the score and worst findings. Pure SVG, embeds in a README |
43
43
  | `... --sarif` | `design-system-roast.sarif` for GitHub code scanning: upload it in CI and findings appear in the Security tab, annotated on files |
44
- | `... --mcp` | The scan as a local MCP server: five tools your agent calls while writing UI, from "is there a Button already?" to "review my changes", plus the `roast-fix` prompt that serves the top fix from a fresh scan. See [Live answers over MCP](#live-answers-over-mcp) |
44
+ | `... --mcp` | The scan as a local MCP server: 5 tools your agent calls while writing UI, from "is there a Button already?" to "review my changes", plus the `roast-fix` prompt that serves the top fix from a fresh scan. See [Live answers over MCP](#live-answers-over-mcp) |
45
45
  | `... --check` | The working tree's changed files checked against the design system, in the terminal. Exits 1 on findings, so it slots into scripts |
46
46
  | <code>...&nbsp;--by&nbsp;"Dwayne&nbsp;Hicks"</code> | A requester credit in the report header, next to the scan date |
47
47
  | <code>...&nbsp;--notes&nbsp;&lt;file.md&gt;</code> | An agent-written analysis embedded in the report as **"What the numbers mean"**: labelled as written by AI, kept apart from the measured numbers. The Claude Code skill writes and passes this automatically; the flag is here so any agent can |
@@ -57,12 +57,12 @@ One scan powers all of it; the flags decide what lands on disk. Combine freely.
57
57
 
58
58
  - **Pre-refactor audit.** Run `/roast-my-design-system` before a design-system cleanup to get the measured baseline: every colour, spacing value, duplicated component and inline style, with real file paths.
59
59
  - **Diagnosing almost-right AI output.** When Claude keeps generating UI that looks slightly off, the report shows which duplicated components and stray values it is imitating, and where the canonical ones live.
60
- - **Making the case without a meeting.** Drop the self-contained HTML report in Slack: a health score and three benchmarks (ideal norms, the 34-repo median, 10 reputable systems) argue for the design system for you.
60
+ - **Making the case without a meeting.** Drop the self-contained HTML report in Slack: a health score and 3 benchmarks (ideal norms, the 34-repo median, 10 reputable systems) argue for the design system for you.
61
61
  - **The fix loop.** Hand the report back to Claude as the punch list and work through the Where to start section, file by file.
62
62
 
63
- The full report for vercel/ai-chatbot, top to bottom — including "What the numbers mean", Claude's read of the scan, embedded right under the verdict:
63
+ The full report for vercel/ai-chatbot, top to bottom, including "What the numbers mean", Claude's read of the scan, embedded right under the verdict:
64
64
 
65
- ![The full diagnosis report for vercel/ai-chatbot in dark mode: health score, the What the numbers mean analysis written by Claude, priced Where to start moves each with its copy-the-fix-prompt button, the wrapped present with the agent rules, an agent trap callout, three-yardstick tiles, the adoption map treemap, palette forensics, spacing receipts, typography specimens, offenders, duplicates, and the component usage ledger](https://raw.githubusercontent.com/gregkozakiewicz/roast-my-design-system/main/assets/report-full-dark.png?v=5.10.0)
65
+ ![The full diagnosis report for vercel/ai-chatbot in dark mode: health score, the What the numbers mean analysis written by Claude, priced Where to start moves each with its copy-the-fix-prompt button, the wrapped present with the agent rules, an agent trap callout, 3-yardstick tiles, the adoption map treemap, palette forensics, spacing receipts, typography specimens, offenders, duplicates, and the component usage ledger](https://raw.githubusercontent.com/gregkozakiewicz/roast-my-design-system/main/assets/report-full-dark.png?v=5.10.0)
66
66
 
67
67
  The same report in light mode (one file, built-in toggle):
68
68
 
@@ -73,8 +73,8 @@ The same report in light mode (one file, built-in toggle):
73
73
  - **Deterministic scanner, not AI sampling.** A zero-dependency Node script reads *every* file (about a second on a normal repo, a few on a large monorepo) and returns the same numbers every run. Claude narrates; it never counts.
74
74
  - **Read-only.** Nothing in your repo is modified. The only outputs are a temp JSON and the HTML report.
75
75
  - **No network, no telemetry.** Everything runs locally. Nothing about your code leaves your machine. Dependency scanners such as Socket may flag URL strings in this package; they are product links and format identifiers written into generated reports, never fetched.
76
- - **Zero dependencies, enforced by the test suite.** The package installs nothing but itself, and that is a tested promise rather than a habit: the suite fails if package.json ever declares a dependency, and the exact file list npm ships is a photographed contract, so nothing can stow away in a release.
77
- - **Honest gaps.** When a repo's components register in a pattern the scan cannot read, the component tiles say "not measured" and drop out of the score. A zero the scanner never earned is presented as blindness, not discipline.
76
+ - **Zero dependencies, enforced by the test suite.** The package installs nothing but itself. That is a tested promise rather than a habit: the suite fails if package.json ever declares a dependency. The exact file list npm ships is a photographed contract too, so nothing can stow away in a release.
77
+ - **Honest gaps.** When a repo's components register in a pattern the scan can not read, the component tiles say "not measured" and drop out of the score. A zero the scanner never earned is presented as blindness, not discipline.
78
78
  - **Honest exclusions.** Test files, Storybook stories, docs sites, example apps, SVG artwork, and email templates (which *must* inline styles) are excluded, so you can't discredit the numbers on a technicality. Your own exclusions (`.roastignore`, `--exclude`) are printed in the report header with file counts, so a scoped scan can never pass itself off as the whole repo.
79
79
  - **Intent-aware counting (v3).** Runtime-computed inline styles, compound-component APIs and wrapper components are not crimes and are not counted as ones. Token-led repos are judged on their hardcoded strays, not their token architecture. Repeated arbitrary values are read as decisions without names, not drift.
80
80
  - **A real benchmark.** The "Avg Design System" yardstick comes from scanning 34 public React repos (cal.com, excalidraw, supabase, grafana, twenty, dub, langfuse…). Median: 130 colours, 17 greys, 20 duplicated components, 49 inline style blocks, 70 arbitrary Tailwind values.
@@ -96,7 +96,7 @@ lab/
96
96
  playground/
97
97
  ```
98
98
 
99
- Both routes merge, and both are loud on purpose. The harvest JSON records every active pattern and how many files it removed, and the report prints a line in the header ("2 folders excluded by .roastignore (lab/, playground/) · 946 files kept out of this scan"). You can narrow the question, but the report always says which question was asked, so a scoped score can't be quietly gamed. There is no negation and no glob syntax: plain folder prefixes, nothing clever.
99
+ Both routes merge, and both are loud on purpose. The harvest JSON records every active pattern and how many files it removed. The report prints a line in the header ("2 folders excluded by .roastignore (lab/, playground/) · 946 files kept out of this scan"). You can narrow the question, but the report always says which question was asked, so a scoped score can't be quietly gamed. There is no negation and no glob syntax: plain folder prefixes, nothing clever.
100
100
 
101
101
  ## Live answers over MCP
102
102
 
@@ -112,9 +112,9 @@ The report and the rules file describe the repo as it was at scan time. `--mcp`
112
112
 
113
113
  The loop: context before building, find while building, validate before saving, review before finishing.
114
114
 
115
- And when the goal is fixing the system rather than building on it, the `roast-fix` prompt serves the top Where-to-start move from a fresh scan, as a ready-made fix prompt (byte-identical to the report's copy buttons). Fix it, ask again, and the next move has risen to the top: the scan is the progress bar. Pass `move: 2` to jump the queue.
115
+ When the goal is fixing the system rather than building on it, the `roast-fix` prompt serves the top Where-to-start move from a fresh scan. It is a ready-made fix prompt, byte-identical to the report's copy buttons. Fix it, ask again, and the next move has risen to the top: the scan is the progress bar. Pass `move: 2` to jump the queue.
116
116
 
117
- To use it in Claude Code, type `/mcp__roast__roast-fix` in the chat (MCP prompts appear as slash commands, named after whatever you registered the server as; the `/` autocomplete menu lists them too). Add the move number to jump the queue: `/mcp__roast__roast-fix 2`. Other clients list server prompts in their own prompt picker; wherever `roast-build-ui` and `roast-review-ui` show up, `roast-fix` sits beside them.
117
+ To use it in Claude Code, type `/mcp__roast__roast-fix` in the chat. MCP prompts appear as slash commands, named after whatever you registered the server as, and the `/` autocomplete menu lists them too. Add the move number to jump the queue: `/mcp__roast__roast-fix 2`. Other clients list server prompts in their own prompt picker; wherever `roast-build-ui` and `roast-review-ui` show up, `roast-fix` sits beside them.
118
118
 
119
119
  Add it to Claude Code:
120
120
 
@@ -122,9 +122,9 @@ Add it to Claude Code:
122
122
  claude mcp add roast -- npx roast-my-design-system --mcp
123
123
  ```
124
124
 
125
- **Verified in Claude Code, Cursor, and Windsurf (now Devin Desktop)** — each tested end to end: server connected, all five tools listed, real answers in the editor's own chat. Same promise as the scan: local, read-only, one scan at startup, no port, no account, nothing about your code leaves your machine. And a clean answer reads "no measured violations found" with the list of checks attached, because a scanner can only certify what it can count.
125
+ **Verified in Claude Code, Cursor, and Windsurf (now Devin Desktop).** Each was tested end to end: server connected, all 5 tools listed, real answers in the editor's own chat. Same promise as the scan: local, read-only, one scan at startup, no port, no account, nothing about your code leaves your machine. A clean answer reads "no measured violations found" with the list of checks attached, because a scanner can only certify what it can count.
126
126
 
127
- **Cursor** — put this in `.cursor/mcp.json` inside the project (the project, not your home directory, so the scan sees one repo, not your whole disk):
127
+ **Cursor**: put this in `.cursor/mcp.json` inside the project (the project, not your home directory, so the scan sees one repo, not your whole disk):
128
128
 
129
129
  ```json
130
130
  { "mcpServers": { "roast": { "command": "npx", "args": ["roast-my-design-system", "--mcp"] } } }
@@ -132,7 +132,7 @@ claude mcp add roast -- npx roast-my-design-system --mcp
132
132
 
133
133
  Cursor holds workspace servers at arm's length until you approve them: open Settings → Tools & MCP and enable `roast` the first time. The first start takes a few seconds while npx fetches the package; Cursor retries on its own.
134
134
 
135
- **Windsurf (Devin Desktop)** — its MCP config is global (`~/.codeium/windsurf/mcp_config.json`), so name the project folder in the entry to keep the scan scoped to one repo:
135
+ **Windsurf (Devin Desktop)**: its MCP config is global (`~/.codeium/windsurf/mcp_config.json`), so name the project folder in the entry to keep the scan scoped to one repo:
136
136
 
137
137
  ```json
138
138
  { "mcpServers": { "roast": { "command": "npx", "args": ["roast-my-design-system", "--mcp", "/path/to/your/repo"] } } }
@@ -142,7 +142,7 @@ Any other MCP client can register the same stdio command.
142
142
 
143
143
  ## In CI
144
144
 
145
- The scanner already speaks SARIF, so wiring it into GitHub code scanning is six lines. Findings appear in the Security tab, annotated on the files themselves:
145
+ The scanner already speaks SARIF, so wiring it into GitHub code scanning is 6 lines. Findings appear in the Security tab, annotated on the files themselves:
146
146
 
147
147
  ```yaml
148
148
  - uses: actions/checkout@v5
@@ -154,7 +154,7 @@ The scanner already speaks SARIF, so wiring it into GitHub code scanning is six
154
154
 
155
155
  ## Install
156
156
 
157
- **No install, no Claude needed — just try it:**
157
+ **No install, no Claude needed. Just try it:**
158
158
 
159
159
  ```bash
160
160
  npx roast-my-design-system@latest
@@ -169,7 +169,7 @@ Run it inside any repo. Same scanner, same report, straight from npm. The Claude
169
169
  /plugin install roast-my-design-system@roast-my-design-system
170
170
  ```
171
171
 
172
- If those commands error, your Claude Code is likely older than the plugin marketplace feature: update Claude Code and retry, or just use the manual route below (it works everywhere and installs the same skill).
172
+ If those commands error, your Claude Code is likely older than the plugin marketplace feature. Update Claude Code and retry, or use the manual route below: it works everywhere and installs the same skill.
173
173
 
174
174
  **Manual (Claude Code, any version):**
175
175
 
@@ -204,22 +204,22 @@ Open Claude Code in the repo you want roasted and type:
204
204
  You get the roast in chat plus `design-system-roast.html` at your repo root: a self-contained page (open it, Slack it, email it, no external requests) with:
205
205
 
206
206
  - a **health score** computed from how your numbers sit against the ideal
207
- - **"What the numbers mean"**: Claude's read of your scan — which findings actually matter, which good numbers are accidents, what to fix first — embedded in the same file you'll forward, labelled as written by Claude and kept apart from the measured numbers. The score alone can flatter; this section is what keeps a shared 85/100 honest
208
- - stat tiles comparing you to all three yardsticks: Ideal, the 34-repo average, and the reputable systems
207
+ - **"What the numbers mean"**: Claude's read of your scan. Which findings actually matter, which good numbers are accidents, what to fix first. It is embedded in the same file you'll forward, labelled as written by Claude and kept apart from the measured numbers. The score alone can flatter; this section is what keeps a shared 85/100 honest
208
+ - stat tiles comparing you to all 3 yardsticks: Ideal, the 34-repo average, and the reputable systems
209
209
  - a **light/dark theme toggle** in one file
210
210
  - the usage-weighted palette bar, the grey ramp, the off-scale spacing receipts, the duplicate-component receipts with clickable file paths, and the worst-offenders ledger
211
- - a **Where to start** close: up to three moves derived from your repo's own numbers, each with a file-path receipt
211
+ - a **Where to start** close: up to 3 moves derived from your repo's own numbers, each with a file-path receipt
212
212
  - a **present** 🎁 below it: you sat through the roast, so `design-system-rules.md` is wrapped inside the report itself. Unwrap, then copy or download the agent rules generated from your scan.
213
213
 
214
214
  After the roast, the skill also offers to write `design-system-rules.md` to disk and merge it into your CLAUDE.md, `.cursor/rules` or AGENTS.md.
215
215
 
216
216
  ## Live examples
217
217
 
218
- Five real roasts of public repos, hosted as-is (the same self-contained HTML the skill generates), spanning React, Stencil and Lit:
218
+ 5 real roasts of public repos, hosted as-is (the same self-contained HTML the skill generates), spanning React, Stencil and Lit:
219
219
 
220
- - **[telekom/scale](https://gregkozakiewicz.github.io/roast-my-design-system/examples/telekom-scale.html)** — Stencil, 95 components read by tag, with Claude's notes embedded
221
- - **[adobe/spectrum-web-components](https://gregkozakiewicz.github.io/roast-my-design-system/examples/adobe-spectrum.html)** — Lit, the `--spectrum-*` namespace named in the header
222
- - **[vercel/ai-chatbot](https://gregkozakiewicz.github.io/roast-my-design-system/examples/vercel-ai-chatbot.html)** — React, with Claude's notes embedded
220
+ - **[telekom/scale](https://gregkozakiewicz.github.io/roast-my-design-system/examples/telekom-scale.html)**: Stencil, 95 components read by tag, with Claude's notes embedded
221
+ - **[adobe/spectrum-web-components](https://gregkozakiewicz.github.io/roast-my-design-system/examples/adobe-spectrum.html)**: Lit, the `--spectrum-*` namespace named in the header
222
+ - **[vercel/ai-chatbot](https://gregkozakiewicz.github.io/roast-my-design-system/examples/vercel-ai-chatbot.html)**: React, with Claude's notes embedded
223
223
  - **[excalidraw/excalidraw](https://gregkozakiewicz.github.io/roast-my-design-system/examples/excalidraw-excalidraw.html)**
224
224
  - **[dubinc/dub](https://gregkozakiewicz.github.io/roast-my-design-system/examples/dubinc-dub.html)**
225
225
 
@@ -230,7 +230,7 @@ Five real roasts of public repos, hosted as-is (the same self-contained HTML the
230
230
  | Distinct colours | ~24 | 130 | 24 |
231
231
  | Shades of grey | up to 13 | 17 | 5 |
232
232
  | Off-scale spacing values | ~12 | 34 | 6 |
233
- | Typefaces | 2–3 | 3 | 1 |
233
+ | Typefaces | 2 to 3 | 3 | 1 |
234
234
  | Border radii | up to 10 | 13 | 2 |
235
235
  | Duplicated components | 0 | 20 | 12 |
236
236
  | Inline style blocks | 0 | 49 | 12 |
@@ -249,7 +249,7 @@ Yes, the median repo is already a mess. That's the point.
249
249
 
250
250
  MIT. The code is yours to fork, modify and redistribute; the copyright notice travels with it.
251
251
 
252
- If you build a report, summary or audit of your own from this tool's scores, counts or benchmark comparisons, keep one line in it: *Built with [roast-my-design-system](https://github.com/gregkozakiewicz/roast-my-design-system) by Greg Kozakiewicz*. The scan data asks the same of AI agents that consume it.
252
+ Building your own report, summary or audit from this tool's scores, counts or benchmark comparisons? Keep one line in it: *Built with [roast-my-design-system](https://github.com/gregkozakiewicz/roast-my-design-system) by Greg Kozakiewicz*. The scan data asks the same of AI agents that consume it.
253
253
 
254
254
  **roast-my-design-system**™ and the GK mark are trademarks of Greg Kozakiewicz. Forking is welcome, republishing under this name is not: see [brand and attribution](https://gregkozakiewicz.github.io/roast-my-design-system/brand.html).
255
255
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roast-my-design-system",
3
- "version": "5.10.0",
3
+ "version": "5.10.2",
4
4
  "mcpName": "io.github.gregkozakiewicz/roast-my-design-system",
5
5
  "description": "Your AI can write the UI. This makes sure it writes your UI. A deterministic scanner scores your design system 0-100 against 34 public repos, reads React and web components (Stencil, Lit), hands you a copy-paste fix prompt for each top finding, writes rules for Claude, Cursor, Copilot and Windsurf with --apply, and runs as a local MCP server with --mcp.",
6
6
  "keywords": [
@@ -225,6 +225,13 @@ export function harvestTokens(root, styleFiles, codeFiles) {
225
225
  // hinges on this split.
226
226
  const tokenDefined = new Set();
227
227
  const tokenDefsPerFile = new Map();
228
+ // Definition sites only: stylesheets that define colour custom properties,
229
+ // and code files that are a palette. The "token file" is chosen among these
230
+ // by how many token colours they hold, never by raw --var count: jsoncrack's
231
+ // only stylesheet with a --var was the Chrome extension's (2 definitions,
232
+ // 33 strays) while the real palette sat in constants/theme.ts with 57.
233
+ const tokenColorDefsPerFile = new Map();
234
+ const paletteFiles = new Set();
228
235
  const nsDefs = new Map(); // --telekom-x: value → 'telekom' (definitions)
229
236
  const nsRefs = new Map(); // var(--telekom-x) → 'telekom' (references)
230
237
 
@@ -235,12 +242,14 @@ export function harvestTokens(root, styleFiles, codeFiles) {
235
242
  tokenDefsPerFile.set(file, (tokenDefsPerFile.get(file) ?? 0) + 1);
236
243
  const stem = m[1].split('-')[0].toLowerCase();
237
244
  if (stem) nsDefs.set(stem, (nsDefs.get(stem) ?? 0) + 1);
238
- for (const c of m[2].matchAll(HEX_RE)) tokenDefined.add(normalizeHex(c[0]));
245
+ let colourDef = false;
246
+ for (const c of m[2].matchAll(HEX_RE)) { tokenDefined.add(normalizeHex(c[0])); colourDef = true; }
239
247
  for (const c of m[2].matchAll(FUNC_COLOR_RE)) {
240
- if (!isVarRef(c[0])) tokenDefined.add(c[0].replace(/\s+/g, ' ').toLowerCase());
248
+ if (!isVarRef(c[0])) { tokenDefined.add(c[0].replace(/\s+/g, ' ').toLowerCase()); colourDef = true; }
241
249
  }
242
250
  const trip = tripletToHsl(m[2]);
243
- if (trip) { tokenDefined.add(trip); colors.add(trip, file); }
251
+ if (trip) { tokenDefined.add(trip); colors.add(trip, file); colourDef = true; }
252
+ if (colourDef) tokenColorDefsPerFile.set(file, (tokenColorDefsPerFile.get(file) ?? 0) + 1);
244
253
  }
245
254
  // References count too: a system whose tokens are defined in a package
246
255
  // dependency still answers to its namespace in every var(--telekom-...)
@@ -340,6 +349,7 @@ export function harvestTokens(root, styleFiles, codeFiles) {
340
349
  }
341
350
  if (/from\s*["']geist\/font/.test(src)) fontFamilies.add('Geist', f);
342
351
  if (PALETTE_FILE_RE.test(f)) {
352
+ paletteFiles.add(f);
343
353
  for (const m of src.matchAll(HEX_RE)) tokenDefined.add(normalizeHex(m[0]));
344
354
  }
345
355
  if (ARTWORK_RE.test(f) || RENDERER_PATH_RE.test(f) || svgHeavy(src)) continue;
@@ -404,7 +414,9 @@ export function harvestTokens(root, styleFiles, codeFiles) {
404
414
  for (const [file] of e.files) perFileDistinct.set(file, (perFileDistinct.get(file) ?? 0) + 1);
405
415
  }
406
416
  for (const e of colors.map.values()) {
407
- if ([...e.files.keys()].some((f) => /\.[jt]sx?$/.test(f) && perFileDistinct.get(f) >= 15)) tokenDefined.add(e.value);
417
+ for (const f of e.files.keys()) {
418
+ if (/\.[jt]sx?$/.test(f) && perFileDistinct.get(f) >= 15) { tokenDefined.add(e.value); paletteFiles.add(f); }
419
+ }
408
420
  }
409
421
 
410
422
  const colorList = colors.toJSON().map((c) => ({ ...c, isToken: tokenDefined.has(c.value) }));
@@ -427,7 +439,22 @@ export function harvestTokens(root, styleFiles, codeFiles) {
427
439
  .sort((a, b) => b.total - a.total)
428
440
  .slice(0, 10);
429
441
 
430
- const tokenFile = [...tokenDefsPerFile.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? null;
442
+ // The token file: among definition sites, the one holding the most token
443
+ // colours (ties: fewest strays). At least 3 token colours to earn the name;
444
+ // otherwise the repo has no token file and the report must not invent one.
445
+ const tokenColoursIn = new Map(), straysIn = new Map();
446
+ for (const e of colors.map.values()) {
447
+ for (const f of e.files.keys()) {
448
+ if (tokenDefined.has(e.value)) tokenColoursIn.set(f, (tokenColoursIn.get(f) ?? 0) + 1);
449
+ else straysIn.set(f, (straysIn.get(f) ?? 0) + 1);
450
+ }
451
+ }
452
+ const candidates = new Set([...tokenColorDefsPerFile.keys(), ...paletteFiles]);
453
+ const tokenFile = [...candidates]
454
+ .map((f) => ({ f, tokens: tokenColoursIn.get(f) ?? 0, strays: straysIn.get(f) ?? 0 }))
455
+ .filter((c) => c.tokens >= 3)
456
+ .sort((a, b) => b.tokens - a.tokens || a.strays - b.strays)[0]?.f
457
+ ?? [...tokenDefsPerFile.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? null;
431
458
 
432
459
  // Token namespace: the brand stem the repo's custom properties answer to
433
460
  // (--telekom-*), named only when earned — enough definitions, a dominant
@@ -1,4 +1,4 @@
1
1
  // Single version constant for the engine — imported by diagnose (report
2
2
  // footer) and rules (generated-by line). This is the bump spot that used to
3
3
  // live as a const inside diagnose/index.mjs.
4
- export const VERSION = '5.10.0';
4
+ export const VERSION = '5.10.2';