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>...&nbsp;--by&nbsp;"Dwayne&nbsp;Hicks"</code> | A requester credit in the report header, next to the scan date |
40
+ | <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 |
38
41
  | <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) |
39
42
  | `... --json` | The scan summary as JSON on stdout, for scripts and pipelines |
40
43
  | <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 |
41
- | <code>/roast-my-design-system</code> (in&nbsp;Claude&nbsp;Code) | The full experience: the roast in chat, the report, the rules offer, and the fix loop with Claude on your own numbers |
44
+ | <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 |
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.2",
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,
@@ -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.0.2';
4
+ export const VERSION = '5.1.0';