roast-my-design-system 5.12.0 → 5.12.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
@@ -33,23 +33,32 @@ Your AI agent (Claude, Cursor, Copilot) builds UI by imitating what's already in
33
33
 
34
34
  One scan powers all of it; the flags decide what lands on disk. Combine freely.
35
35
 
36
- | Command                                                             | What you get |
36
+ **Run it yourself.** Type these in a terminal and you get a result.
37
+
38
+ | Command | What you get |
37
39
  |---|---|
38
- | <code>npx&nbsp;roast-my-design-system@latest</code> | The scan and `design-system-roast.html`, opened in your browser |
39
- | <code>npx&nbsp;roast-my-design-system@latest&nbsp;&lt;path&gt;</code> | Scan a different repo than the current directory |
40
+ | `npx roast-my-design-system@latest` | The scan and `design-system-roast.html`, opened in your browser |
41
+ | `... <path>` | Scan a different repo than the current directory |
40
42
  | `... --apply` | The generated agent rules injected straight into every agent file you have: `CLAUDE.md`, `AGENTS.md`, `.cursorrules`, `.cursor/rules/`, `.windsurfrules` and `.github/copilot-instructions.md`, inside a marked block. Re-running replaces only that block, never your own text. Windsurf and Copilot get a compact variant sized for their limits |
41
43
  | `... --rules` | The same rules written to `design-system-rules.md` instead, for pasting by hand |
42
44
  | `... --card` | `roast-card.svg`: a shareable 1200x630 card with the score and worst findings. Pure SVG, embeds in a README |
43
45
  | `... --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: 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
46
  | `... --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
- | <code>...&nbsp;--by&nbsp;"Dwayne&nbsp;Hicks"</code> | A requester credit in the report header, next to the scan date |
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 |
48
- | <code>...&nbsp;--section&nbsp;"Title"&nbsp;&lt;file.md&gt;</code> | An agent-written chapter appended after the notes, same styling, same written-by-AI label, with sub-headings allowed. Repeatable, so analysis that outgrows the notes still lives inside the report instead of a hand-built page |
49
- | <code>...&nbsp;--exclude&nbsp;lab/</code> | Leave a folder out of the scan (repeat the flag or comma-separate). Or list folders in a `.roastignore` file at the repo root. Either way the report says so in the header; see [Scoping the scan](#scoping-the-scan) |
47
+ | `... --exclude lab/` | Leave a folder out of the scan (repeat the flag or comma-separate). Or list folders in a `.roastignore` file at the repo root. Either way the report says so in the header; see [Scoping the scan](#scoping-the-scan) |
50
48
  | `... --json` | The scan summary as JSON on stdout, for scripts and pipelines |
51
- | <code>...&nbsp;--theme&nbsp;light</code>&nbsp;/ <code>--out&nbsp;&lt;file&gt;</code>&nbsp;/ <code>--no-open</code> | Light report, custom report path, don't open the browser |
52
- | <code>/roast-my-design-system</code> (in&nbsp;Claude&nbsp;Code) | The full experience: the roast in chat *and* embedded in the report as "What the numbers mean", the rules offer, and the fix loop with Claude on your own numbers |
49
+ | `... --by "Dwayne Hicks"` | Puts a name in the report header, for when you ran it for someone else |
50
+ | `... --theme light` / `--out <file>` / `--no-open` / `--open` | Light report, custom report path, never open the browser, always open it |
51
+
52
+ **For your agent.** These need an agent: two of them carry writing only an agent can produce, and two are doors it works through rather than output you read.
53
+
54
+ | Command | What you get |
55
+ |---|---|
56
+ | `... --notes <file.md>` | The agent's read of this scan, embedded in the report as **"What the numbers mean"**: which findings matter, which good numbers are accidents, what to fix first. Labelled as written by AI and kept apart from the measured numbers. The Claude Code skill writes and passes it automatically; the flag is here so any agent can |
57
+ | `... --section "Title" <file.md>` | A further agent-written chapter after the notes, same styling, same label, sub-headings allowed. Repeatable, so analysis that outgrows the notes still lives inside the report instead of a hand-built page |
58
+ | `... --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) |
59
+ | `/roast-my-design-system` (in Claude Code) | The full experience: the roast in chat *and* embedded in the report as "What the numbers mean", the rules offer, and the fix loop with Claude on your own numbers |
60
+
61
+ Not to be confused with each other: **"Why this matters"** is generic, ships with the tool and reads the same in every report. **"What the numbers mean"** is your agent's read of your repo, and only appears when an agent passed it.
53
62
 
54
63
  **One scan writes rules for every agent: Claude, Cursor, GitHub Copilot, and Windsurf.** Every scan also checks the agent rules you already have and flags stale references, no flag needed.
55
64
 
@@ -62,11 +71,11 @@ One scan powers all of it; the flags decide what lands on disk. Combine freely.
62
71
 
63
72
  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
73
 
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.12.0)
74
+ ![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.12.2)
66
75
 
67
76
  The same report in light mode (one file, built-in toggle):
68
77
 
69
- ![The diagnosis report in light mode](https://raw.githubusercontent.com/gregkozakiewicz/roast-my-design-system/main/assets/report-light-hero.png?v=5.12.0)
78
+ ![The diagnosis report in light mode](https://raw.githubusercontent.com/gregkozakiewicz/roast-my-design-system/main/assets/report-light-hero.png?v=5.12.2)
70
79
 
71
80
  ## What makes the numbers trustworthy
72
81
 
package/bin/roast.mjs CHANGED
@@ -63,10 +63,13 @@ if (flag('help') || flag('h')) {
63
63
 
64
64
  Usage: npx roast-my-design-system@latest [path] [options]
65
65
 
66
+ Run it yourself
67
+
66
68
  path repo to scan (default: current directory)
67
69
  --theme <t> dark | light (default: dark)
68
70
  --out <file> report path (default: design-system-roast.html in the repo)
69
71
  --no-open write the report without opening it
72
+ --open open it even when the output is piped or an agent is running
70
73
  --rules also write design-system-rules.md: agent rules (for
71
74
  CLAUDE.md / .cursor/rules) generated from the scan
72
75
  --apply inject the rules straight into your agent files (CLAUDE.md,
@@ -78,16 +81,8 @@ Usage: npx roast-my-design-system@latest [path] [options]
78
81
  the score and worst findings (pure SVG, embeds in READMEs)
79
82
  --sarif also write design-system-roast.sarif for GitHub code
80
83
  scanning: findings annotated on files in the Security tab
81
- --by <name> put a requester credit in the report header, next to the
82
- scan date ("commissioned by <name>")
83
- --notes <file> embed an agent-written analysis (markdown-lite) in the
84
- report as "What the numbers mean", labelled as written by
85
- AI and kept apart from the measured numbers. The Claude
86
- Code skill writes and passes this automatically
87
- --section "Title" <file>
88
- append an agent-written chapter after the notes, same
89
- markdown-lite plus "## " sub-headings, same written-by-AI
90
- label. Repeatable, one chapter per --section
84
+ --by <name> put a name in the report header, for when you ran it for
85
+ someone else ("commissioned by <name>")
91
86
  --exclude <p> leave a folder out of the scan (repo-relative, e.g.
92
87
  --exclude lab/ --exclude piglet/ or --exclude lab/,piglet/;
93
88
  same as listing it in a .roastignore file at the repo root).
@@ -97,6 +92,17 @@ Usage: npx roast-my-design-system@latest [path] [options]
97
92
  --check check the working tree's changed files (git diff + untracked)
98
93
  against the design system and print the findings; exits 1
99
94
  when something is over the line, so it composes with scripts
95
+
96
+ For your agent (a plain terminal has no agent to write these)
97
+
98
+ --notes <file> the agent's read of this scan, embedded in the report as
99
+ "What the numbers mean", labelled as written by AI and kept
100
+ apart from the measured numbers. The Claude Code skill
101
+ writes and passes this automatically
102
+ --section "Title" <file>
103
+ a further agent-written chapter after the notes, same
104
+ markdown-lite plus "## " sub-headings, same label.
105
+ Repeatable, one chapter per --section
100
106
  --mcp run as a local MCP server (stdio) so your agent can query
101
107
  the design system live: context, canonical components,
102
108
  tokens, validation. Add to your client, e.g. Claude Code:
@@ -133,12 +139,38 @@ const wantApply = flag('apply') === true;
133
139
  const wantCard = flag('card') === true;
134
140
  const wantSarif = flag('sarif') === true;
135
141
  const asJson = flag('json') === true;
136
- const noOpen = flag('no-open') === true || asJson;
142
+ // Opening a browser tab is a courtesy to a person watching a terminal. When
143
+ // an agent runs the scan (Claude Code, Cursor, Codex) it captures stdout, so
144
+ // there is no terminal and no one to look — and an agent verifying a fix runs
145
+ // this repeatedly, throwing a tab in the user's face each time (Greg, working
146
+ // through the fix loop, 2026-09-10). No TTY, no tab. `--open` forces it back
147
+ // for anyone piping output who still wants the report.
148
+ const interactive = process.stdout.isTTY === true && !process.env.CI;
149
+ const forceOpen = flag('open') === true;
150
+ // flag() consumes the argument, so ask once and keep the answer.
151
+ const askedNoOpen = flag('no-open') === true;
152
+ const noOpen = askedNoOpen || asJson;
137
153
  const theme = opt('theme', 'dark');
138
154
  const commissionedBy = opt('by', null);
139
155
  const notesFile = opt('notes', null);
140
156
  const sections = optPairs('section');
141
157
  const excludes = optAll('exclude');
158
+ // Reject an unreadable --notes / --section before scanning, not after: the
159
+ // scan is the expensive part, and dying on a flag once the work is done is
160
+ // the rudest possible order (Greg watched it happen, 2026-09-10). diagnose
161
+ // checks these too; this is the early door with the same words.
162
+ for (const [flagName, file] of [
163
+ ...(notesFile ? [['--notes', notesFile]] : []),
164
+ ...sections.map(([, f]) => ['--section', f]),
165
+ ]) {
166
+ if (existsSync(resolve(file))) continue;
167
+ console.error(`${flagName}: cannot read ${file}
168
+ ${flagName} is for an agent to pass: the file holds its writing about this
169
+ scan, which the report embeds. Running by hand? Leave the flag off, or use
170
+ the Claude Code skill, which writes it for you.`);
171
+ process.exit(1);
172
+ }
173
+
142
174
  const target = resolve(argv.find((a) => !a.startsWith('--')) || process.cwd());
143
175
  if (!existsSync(target) || !statSync(target).isDirectory()) {
144
176
  console.error(`Not a directory: ${target}`);
@@ -162,6 +194,16 @@ function run(script, args, env) {
162
194
  const say = (s) => { if (!asJson) console.log(s); };
163
195
 
164
196
  say(`roast-my-design-system ${VERSION} · read-only scan, nothing leaves your machine`);
197
+ // The score this repo had last time, read from the report about to be
198
+ // overwritten. It decides whether a browser tab is worth throwing at anyone:
199
+ // an agent working a fix runs the scan repeatedly, and only the run that
200
+ // actually moves the number is worth looking at.
201
+ let previousScore = null;
202
+ try {
203
+ const prev = readFileSync(outPath, 'utf8').match(/<!-- rmds-score: (\d+|na) -->/);
204
+ if (prev) previousScore = prev[1];
205
+ } catch { /* no previous report: this is the first look, so it is worth opening */ }
206
+
165
207
  // the harvest goes to a temp dir this wrapper deletes right after; tell the
166
208
  // script so it does not print a path that will be gone seconds later
167
209
  run('harvest/index.mjs', [target, '--out', harvestPath,
@@ -239,7 +281,20 @@ if (!asJson) {
239
281
  say(`\n${FEEDBACK_ASK} ${FEEDBACK_CTA}:\n${feedbackUrl(VERSION)}\n${STAR_ASK} ${STAR_CTA}:\n${STAR_URL}`);
240
282
  }
241
283
 
242
- if (!noOpen) {
284
+ // A person at a terminal asked for this, so they get the report. An agent
285
+ // did not: it gets the tab only when the number moved, which is the moment
286
+ // worth seeing (Greg, working a fix loop through Claude Code, 2026-09-10 —
287
+ // several windows opened while the agent was still working).
288
+ // `summary` was parsed before the temp dir was removed; reading the file
289
+ // here would find nothing.
290
+ const currentScore = summary?.score === undefined || summary?.score === null ? null : String(summary.score);
291
+ const scoreMoved = previousScore === null || (currentScore !== null && currentScore !== previousScore);
292
+ const quietRun = !interactive && !forceOpen && !scoreMoved;
293
+ if (quietRun && !asJson && !askedNoOpen) {
294
+ console.log(`\n (score unchanged at ${currentScore ?? '?'}, so no browser tab. Add --open if you want one.)`);
295
+ }
296
+
297
+ if (!noOpen && !quietRun) {
243
298
  // Windows: `start` treats a first quoted arg as the window TITLE, and Node
244
299
  // quotes paths containing spaces — pass an empty title so the path lands
245
300
  // in the file slot. Linux: xdg-open may be absent (headless, WSL); the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roast-my-design-system",
3
- "version": "5.12.0",
3
+ "version": "5.12.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": [
@@ -138,7 +138,16 @@ const notesPath = arg('notes', null);
138
138
  let notesText = null;
139
139
  if (notesPath) {
140
140
  try { notesText = readFileSync(resolve(notesPath), 'utf8').trim() || null; }
141
- catch { console.error(`--notes: cannot read ${notesPath}`); process.exit(1); }
141
+ catch {
142
+ // Typed by hand more often than not: the flag reads like "add a note",
143
+ // but the file holds an agent's analysis of this scan, and a plain
144
+ // terminal has no agent to write one (Greg, 2026-09-10).
145
+ console.error(`--notes: cannot read ${notesPath}
146
+ --notes is for an agent to pass: the file holds its read of this scan,
147
+ which the report embeds as "What the numbers mean". Running by hand?
148
+ Leave the flag off, or use the Claude Code skill, which writes it for you.`);
149
+ process.exit(1);
150
+ }
142
151
  }
143
152
 
144
153
  // --section "Title" file.md, repeatable; unreadable files are hard errors for
@@ -152,7 +161,12 @@ for (let i = 3; i < process.argv.length; i++) {
152
161
  }
153
162
  let text;
154
163
  try { text = readFileSync(resolve(file), 'utf8').trim(); }
155
- catch { console.error(`--section: cannot read ${file}`); process.exit(1); }
164
+ catch {
165
+ console.error(`--section: cannot read ${file}
166
+ --section is for an agent to pass: the file holds a chapter it wrote,
167
+ rendered inside the report. Running by hand? Leave the flag off.`);
168
+ process.exit(1);
169
+ }
156
170
  if (text) extraSections.push({ title, text });
157
171
  i += 2;
158
172
  }
@@ -1649,6 +1663,7 @@ const html = `<!doctype html>
1649
1663
  .author:hover { text-decoration:underline; }
1650
1664
  </style>
1651
1665
  <!-- rmds-schema: ${NS_TAG} -->
1666
+ <!-- rmds-score: ${healthScore ?? 'na'} -->
1652
1667
  </head><body>
1653
1668
  <button class="theme-toggle glass" aria-label="Switch between light and dark mode" onclick="(function(){var r=document.documentElement,t=r.getAttribute('data-theme')==='light'?'dark':'light';r.setAttribute('data-theme',t);try{localStorage.setItem('roast-theme',t)}catch(e){}})()">
1654
1669
  <span class="tt-lab tt-day">Day</span>
@@ -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.12.0';
4
+ export const VERSION = '5.12.2';