roast-my-design-system 5.0.2 → 5.1.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.
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
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.
|
|
8
8
|
|
|
9
|
+
> **New in 5.1: the roast's analysis now ships inside the report.** Run as the Claude Code skill, the report gains a "What the numbers mean" section — Claude's read of your scan, in the same shareable file as the score, so the analysis reaches whoever the report is forwarded to. Labelled as written by AI, never mixed into the measurement.
|
|
10
|
+
|
|
9
11
|
> **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).
|
|
10
12
|
|
|
11
13
|
Run it on your codebase and get, in about a second:
|
|
@@ -35,10 +37,11 @@ One scan powers all of it; the flags decide what lands on disk. Combine freely.
|
|
|
35
37
|
| `... --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". See [Live answers over MCP](#live-answers-over-mcp) |
|
|
36
38
|
| `... --check` | The working tree's changed files checked against the design system, in the terminal. Exits 1 on findings, so it slots into scripts |
|
|
37
39
|
| <code>... --by "Dwayne Hicks"</code> | A requester credit in the report header, next to the scan date |
|
|
40
|
+
| <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 |
|
|
38
41
|
| <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) |
|
|
39
42
|
| `... --json` | The scan summary as JSON on stdout, for scripts and pipelines |
|
|
40
43
|
| <code>... --theme light</code> / <code>--out <file></code> / <code>--no-open</code> | Light report, custom report path, don't open the browser |
|
|
41
|
-
| <code>/roast-my-design-system</code> (in Claude Code) | The full experience: the roast in chat
|
|
44
|
+
| <code>/roast-my-design-system</code> (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 |
|
|
42
45
|
|
|
43
46
|
**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.
|
|
44
47
|
|
|
@@ -171,6 +174,7 @@ Open Claude Code in the repo you want roasted and type:
|
|
|
171
174
|
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:
|
|
172
175
|
|
|
173
176
|
- a **health score** computed from how your numbers sit against the ideal
|
|
177
|
+
- **"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
|
|
174
178
|
- stat tiles comparing you to all three yardsticks: Ideal, the 34-repo average, and the reputable systems
|
|
175
179
|
- a **light/dark theme toggle** in one file
|
|
176
180
|
- 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
|
package/bin/roast.mjs
CHANGED
|
@@ -65,6 +65,10 @@ Usage: npx roast-my-design-system [path] [options]
|
|
|
65
65
|
scanning: findings annotated on files in the Security tab
|
|
66
66
|
--by <name> put a requester credit in the report header, next to the
|
|
67
67
|
scan date ("commissioned by <name>")
|
|
68
|
+
--notes <file> embed an agent-written analysis (markdown-lite) in the
|
|
69
|
+
report as "What the numbers mean", labelled as written by
|
|
70
|
+
AI and kept apart from the measured numbers. The Claude
|
|
71
|
+
Code skill writes and passes this automatically
|
|
68
72
|
--exclude <p> leave a folder out of the scan (repo-relative, e.g.
|
|
69
73
|
--exclude lab/ --exclude piglet/ or --exclude lab/,piglet/;
|
|
70
74
|
same as listing it in a .roastignore file at the repo root).
|
|
@@ -113,6 +117,7 @@ const asJson = flag('json') === true;
|
|
|
113
117
|
const noOpen = flag('no-open') === true || asJson;
|
|
114
118
|
const theme = opt('theme', 'dark');
|
|
115
119
|
const commissionedBy = opt('by', null);
|
|
120
|
+
const notesFile = opt('notes', null);
|
|
116
121
|
const excludes = optAll('exclude');
|
|
117
122
|
const target = resolve(argv.find((a) => !a.startsWith('--')) || process.cwd());
|
|
118
123
|
if (!existsSync(target) || !statSync(target).isDirectory()) {
|
|
@@ -143,7 +148,8 @@ run('harvest/index.mjs', [target, '--out', harvestPath,
|
|
|
143
148
|
...excludes.flatMap((e) => ['--exclude', e])], { ROAST_EPHEMERAL_OUT: '1' });
|
|
144
149
|
say('');
|
|
145
150
|
run('diagnose/index.mjs', [harvestPath, '--out', outPath, '--theme', theme, '--summary', summaryPath,
|
|
146
|
-
...(commissionedBy ? ['--by', commissionedBy] : [])
|
|
151
|
+
...(commissionedBy ? ['--by', commissionedBy] : []),
|
|
152
|
+
...(notesFile ? ['--notes', resolve(notesFile)] : [])]);
|
|
147
153
|
|
|
148
154
|
// The verdict leads, the evidence follows: harvest details print here, after
|
|
149
155
|
// the diagnosis, rendered from harvest.json via the same lines the direct
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "roast-my-design-system",
|
|
3
|
-
"version": "5.0
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"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, writes rules for Claude, Cursor, Copilot and Windsurf with --apply, and runs as a local MCP server with --mcp.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"design-system",
|
|
@@ -6,6 +6,13 @@
|
|
|
6
6
|
*
|
|
7
7
|
* node src/diagnose/index.mjs harvest.json [--out diagnosis.html] [--theme dark|light]
|
|
8
8
|
* (--theme sets the initial mode; the page itself has a light/dark toggle)
|
|
9
|
+
*
|
|
10
|
+
* --notes <file.md> embeds an agent-written analysis ("What the numbers
|
|
11
|
+
* mean") between the verdict and the punch list, clearly labelled as
|
|
12
|
+
* written-by-AI so it never reads as part of the measurement. The file is
|
|
13
|
+
* markdown-lite: paragraphs, **bold**, `code`, and "- " lists. Reruns of
|
|
14
|
+
* this script (e.g. --by credit) must pass --notes again or the section
|
|
15
|
+
* is gone — which is why a missing notes file is a hard error, not a skip.
|
|
9
16
|
*/
|
|
10
17
|
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
11
18
|
import { resolve, basename, join, dirname } from 'node:path';
|
|
@@ -116,6 +123,14 @@ const themeName = arg('theme', 'dark');
|
|
|
116
123
|
// Requester credit ("commissioned by"): their name up top next to the scan
|
|
117
124
|
// date; the generated-by authorship stays in the footer, never confused.
|
|
118
125
|
const commissionedBy = arg('by', null);
|
|
126
|
+
// Agent-written analysis to embed (see the header comment). Read eagerly so
|
|
127
|
+
// a bad path fails the run instead of silently shipping a report without it.
|
|
128
|
+
const notesPath = arg('notes', null);
|
|
129
|
+
let notesText = null;
|
|
130
|
+
if (notesPath) {
|
|
131
|
+
try { notesText = readFileSync(resolve(notesPath), 'utf8').trim() || null; }
|
|
132
|
+
catch { console.error(`--notes: cannot read ${notesPath}`); process.exit(1); }
|
|
133
|
+
}
|
|
119
134
|
const T = THEMES[themeName];
|
|
120
135
|
if (!T) { console.error(`Unknown theme "${themeName}" (dark | light)`); process.exit(1); }
|
|
121
136
|
|
|
@@ -694,6 +709,35 @@ function giftSection() {
|
|
|
694
709
|
</section>`;
|
|
695
710
|
}
|
|
696
711
|
|
|
712
|
+
// ---------- agent-written analysis (--notes) ----------
|
|
713
|
+
// Markdown-lite renderer: escape everything first, then allow exactly
|
|
714
|
+
// **bold**, `code` and "- " bullet lists. No raw HTML ever passes through,
|
|
715
|
+
// so a hostile notes file cannot inject into the report.
|
|
716
|
+
function notesInline(s) {
|
|
717
|
+
return esc(s)
|
|
718
|
+
.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
|
|
719
|
+
.replace(/`([^`]+)`/g, '<span class="mono">$1</span>');
|
|
720
|
+
}
|
|
721
|
+
function notesBody(md) {
|
|
722
|
+
return md.split(/\n\s*\n/).map((block) => {
|
|
723
|
+
const lines = block.trim().split('\n');
|
|
724
|
+
if (lines.every((l) => /^[-*] /.test(l.trim()))) {
|
|
725
|
+
return `<ul>${lines.map((l) => `<li>${notesInline(l.trim().slice(2))}</li>`).join('')}</ul>`;
|
|
726
|
+
}
|
|
727
|
+
return `<p>${notesInline(block.trim())}</p>`;
|
|
728
|
+
}).join('');
|
|
729
|
+
}
|
|
730
|
+
function notesSection() {
|
|
731
|
+
if (!notesText) return '';
|
|
732
|
+
return `<section class="glass pad notes-sec">
|
|
733
|
+
<div class="sec-head">
|
|
734
|
+
${eyebrow(`Written by ${esc(arg('notes-author', 'Claude'))} from this scan · ${esc((h.harvestedAt ?? '').slice(0, 10))} · not part of the measurement`)}
|
|
735
|
+
<h2>What the numbers mean</h2>
|
|
736
|
+
</div>
|
|
737
|
+
<div class="notes-body">${notesBody(notesText)}</div>
|
|
738
|
+
</section>`;
|
|
739
|
+
}
|
|
740
|
+
|
|
697
741
|
function whereToStartSection() {
|
|
698
742
|
const c = [];
|
|
699
743
|
if (agentFiles.length === 0) c.push({ score: 60, metric: null, title: 'Write the agent rules file',
|
|
@@ -958,6 +1002,14 @@ const html = `<!doctype html>
|
|
|
958
1002
|
|
|
959
1003
|
.eyebrow { font:700 10.5px/1.4 var(--sans); letter-spacing:.16em; text-transform:uppercase; color:var(--dim); }
|
|
960
1004
|
.sec-head h2 { font:600 19px/1.3 var(--disp); letter-spacing:-.01em; }
|
|
1005
|
+
/* agent-written analysis (--notes): same glass, but an accent spine and a
|
|
1006
|
+
written-by label keep prose visibly apart from measurement */
|
|
1007
|
+
.notes-sec { border-left:3px solid var(--accent); }
|
|
1008
|
+
.notes-sec .sec-head .eyebrow { margin-bottom:6px; }
|
|
1009
|
+
.notes-body { display:flex; flex-direction:column; gap:14px; margin-top:10px;
|
|
1010
|
+
font-size:15px; line-height:1.65; color:var(--text); }
|
|
1011
|
+
.notes-body ul { margin:0; padding-left:20px; display:flex; flex-direction:column; gap:6px; }
|
|
1012
|
+
.notes-body .mono { font-family:var(--mono); font-size:.92em; }
|
|
961
1013
|
.sec-head .sub { margin-top:3px; }
|
|
962
1014
|
.sub { color:var(--dim); font-size:13.5px; }
|
|
963
1015
|
.h3d { font:600 16px/1.3 var(--disp); letter-spacing:-.01em; margin-top:3px; }
|
|
@@ -1258,6 +1310,8 @@ const html = `<!doctype html>
|
|
|
1258
1310
|
</div>
|
|
1259
1311
|
</header>
|
|
1260
1312
|
|
|
1313
|
+
${notesSection()}
|
|
1314
|
+
|
|
1261
1315
|
${whereToStartSection()}
|
|
1262
1316
|
|
|
1263
1317
|
${giftSection()}
|
|
@@ -1341,6 +1395,7 @@ if (summaryPath) {
|
|
|
1341
1395
|
repo: repoName,
|
|
1342
1396
|
version: VERSION,
|
|
1343
1397
|
...(commissionedBy ? { commissionedBy } : {}),
|
|
1398
|
+
...(notesText ? { notesEmbedded: true } : {}),
|
|
1344
1399
|
...(h.exclusions ? { exclusions: h.exclusions } : {}),
|
|
1345
1400
|
score: healthScore,
|
|
1346
1401
|
noSystemLikely,
|