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
|
-
|
|
36
|
+
**Run it yourself.** Type these in a terminal and you get a result.
|
|
37
|
+
|
|
38
|
+
| Command | What you get |
|
|
37
39
|
|---|---|
|
|
38
|
-
|
|
|
39
|
-
| <
|
|
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
|
-
|
|
|
47
|
-
| <code>... --notes <file.md></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>... --section "Title" <file.md></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>... --exclude 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
|
-
|
|
|
52
|
-
|
|
|
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
|
-

|
|
66
75
|
|
|
67
76
|
The same report in light mode (one file, built-in toggle):
|
|
68
77
|
|
|
69
|
-

|
|
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
|
|
82
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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 {
|
|
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 {
|
|
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>
|