roast-my-design-system 9.1.0 → 9.1.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
|
@@ -63,9 +63,9 @@ This tool draws the map.
|
|
|
63
63
|
|
|
64
64
|
## What you get
|
|
65
65
|
|
|
66
|
-
### Where
|
|
66
|
+
### Where will your agent have to guess?
|
|
67
67
|
|
|
68
|
-
The places where the repo has no answer yet.
|
|
68
|
+
The first thing the report says: the places where the repo has no answer yet, or "No gaps found".
|
|
69
69
|
|
|
70
70
|
- the gap, in one line
|
|
71
71
|
- the files that prove it
|
|
@@ -142,11 +142,11 @@ Eleven reports, hosted exactly as the tool writes them. Every number determinist
|
|
|
142
142
|
|
|
143
143
|
The full report for vercel/ai-chatbot. The verdict answers what the agent will learn here, the gap section comes first, then "What the repo teaches the agent", Claude's read of the scan:
|
|
144
144
|
|
|
145
|
-

|
|
146
146
|
|
|
147
147
|
The same report in light mode (one file, built-in toggle):
|
|
148
148
|
|
|
149
|
-

|
|
150
150
|
|
|
151
151
|
## Why the numbers hold
|
|
152
152
|
|
|
@@ -348,7 +348,7 @@ You get the roast in chat plus `design-system-roast.html` at your repo root, a s
|
|
|
348
348
|
- a **light/dark theme toggle** in one file
|
|
349
349
|
- 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
|
|
350
350
|
- a **Fix what makes agents guess** list: up to 3 moves derived from your repo's own numbers, each with a file-path receipt
|
|
351
|
-
-
|
|
351
|
+
- **Where will your agent have to guess?** opens the report: the gaps where the repo has no answer yet, with the files that prove each one and the move that closes it, or "No gaps found"
|
|
352
352
|
- **Give the agent the answers**: `design-system-rules.md` wrapped inside the report as a present. Unwrap, then copy or download the agent rules generated from your scan
|
|
353
353
|
|
|
354
354
|
After the roast, the skill offers to write `design-system-rules.md` to disk and merge it into your CLAUDE.md, `.cursor/rules` or AGENTS.md.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "roast-my-design-system",
|
|
3
|
-
"version": "9.1.
|
|
3
|
+
"version": "9.1.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 112 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": [
|
|
@@ -351,15 +351,23 @@ let verdict = noSystemLikely
|
|
|
351
351
|
? 'This repo is in good shape, and your agent has rules to read. Keep them in step with the code.'
|
|
352
352
|
: 'This repo is in good shape. The gap is documentation: your agent still can\'t see the system.')
|
|
353
353
|
: findings.slice(0, 3).join('. ') + '.';
|
|
354
|
-
// The lead line answers the report's question
|
|
355
|
-
//
|
|
356
|
-
//
|
|
354
|
+
// The lead line answers the report's first question, where will the agent
|
|
355
|
+
// have to guess, before the measured verdict does. A repo with an answer gets
|
|
356
|
+
// copied; a repo with no answer gets guessed at. A gap named here is one
|
|
357
|
+
// lib/gaps admitted after a fleet probe; nothing else is called a gap. The
|
|
358
|
+
// card never talks about the scan's reach: a reader came for a diagnosis,
|
|
359
|
+
// not for the limits of the instrument (Greg, 28 Sep 2026).
|
|
357
360
|
const gapLead = (noSystemLikely || fresh) ? '' : (() => {
|
|
358
361
|
const gaps = h.gaps ?? [];
|
|
359
|
-
|
|
360
|
-
const named = gaps.map((g) => g.title.charAt(0).toLowerCase() + g.title.slice(1));
|
|
361
|
-
return `Most of this repo gives an agent something to copy. ${gaps.length === 1 ? 'One gap remains' : `${gaps.length} gaps remain`}: ${esc(named.join('; '))}.`;
|
|
362
|
+
return gaps.length ? `${gaps.length} gap${gaps.length === 1 ? '' : 's'} found.` : 'No gaps found.';
|
|
362
363
|
})();
|
|
364
|
+
// Under the headline: the condition, and nothing about the scan's reach.
|
|
365
|
+
// What the fixes do to the score is said once, in the fixes section.
|
|
366
|
+
function gapSub() {
|
|
367
|
+
return (h.gaps ?? []).length
|
|
368
|
+
? 'The agent has no clear answer for these parts of the system.'
|
|
369
|
+
: 'Your repo gives the agent a clear answer for every check in this scan.';
|
|
370
|
+
}
|
|
363
371
|
if (bench && findings.length && !fresh) {
|
|
364
372
|
const core = [['colors', tokenLed ? colorStrays : colors.length], ['greys', tokenLed ? greyStrays : greys.length],
|
|
365
373
|
['spacing', spacingTotal], ['typefaces', typefaces.length], ['exactDuplicates', hardDupes.length], ['inlineStyles', inline.count]];
|
|
@@ -1213,9 +1221,9 @@ function whereToStartSection() {
|
|
|
1213
1221
|
for (const item of top3) if (item.metric && item.after !== undefined && item.delta > 0) applied.set(item.metric, item.after);
|
|
1214
1222
|
const after = projectedScore(applied);
|
|
1215
1223
|
startProjection = { count: top3.length, after };
|
|
1216
|
-
const words = ['One
|
|
1224
|
+
const words = ['One fix', 'Two fixes', 'Three fixes'][top3.length - 1];
|
|
1217
1225
|
const head = healthScore !== null && after > healthScore
|
|
1218
|
-
? `${words}
|
|
1226
|
+
? `${words} could raise the design system health score from <b class="proj">${healthScore} to ${after}</b>.`
|
|
1219
1227
|
: `${words} to increase your score.`;
|
|
1220
1228
|
const chip = (item) => item.delta > 0
|
|
1221
1229
|
? `<span class="delta" title="once all of them are done">+${item.delta}</span>`
|
|
@@ -1257,24 +1265,17 @@ function whereToStartSection() {
|
|
|
1257
1265
|
// A gap is a place where the next piece of UI has nothing to reuse (lib/gaps).
|
|
1258
1266
|
// The September 2026 runs showed agents inventing exactly there; the section
|
|
1259
1267
|
// names the gap, the files that prove it and the one move that closes it.
|
|
1260
|
-
function
|
|
1268
|
+
function gapsList() {
|
|
1261
1269
|
if (noSystemLikely || fresh) return '';
|
|
1262
1270
|
const gaps = h.gaps ?? [];
|
|
1263
|
-
if (!gaps.length)
|
|
1264
|
-
|
|
1265
|
-
${sectionHead('Where agents will invent', 'No gap found. This scan checks for one kind of gap so far, a chart palette, and this repo either has one or has no charts. The list grows as more gap kinds are measured on real agent runs; a clean result here says the known gaps are closed, and no more than that.')}
|
|
1266
|
-
</section>`;
|
|
1267
|
-
}
|
|
1268
|
-
const head = `${gaps.length === 1 ? 'One place' : `${gaps.length} places`} where this repo gives an agent nothing to copy. Asked to build there, an agent invents, because that is what the repo taught it. Nothing here is a violation: there is no rule yet to break. Define the answer once and every agent after that inherits it.`;
|
|
1269
|
-
return `<section class="glass pad" style="margin-top:16px">
|
|
1270
|
-
${sectionHead('Where agents will invent', head)}
|
|
1271
|
-
<div class="ledger">${gaps.map((g, i) => `
|
|
1271
|
+
if (!gaps.length) return '';
|
|
1272
|
+
return `<div class="ledger gaps-list">${gaps.map((g, i) => `
|
|
1272
1273
|
<div class="ledger-row gap-row">
|
|
1273
1274
|
<span class="ledger-idx">${String(i + 1).padStart(2, '0')}</span>
|
|
1274
1275
|
<div class="start-body"><div class="start-head"><div class="start-title">${esc(g.title)}</div></div><div class="sub">${esc(g.detail)}</div>
|
|
1275
1276
|
<div class="sub" style="margin-top:6px"><b>Close it:</b> ${esc(g.fix)}</div>
|
|
1276
1277
|
${g.files?.length ? `<div class="chips-row" style="margin-top:8px">${g.files.map((f) => `<span class="chip mono">${esc(f)}</span>`).join('')}</div>` : ''}</div>
|
|
1277
|
-
</div>`).join('')}</div
|
|
1278
|
+
</div>`).join('')}</div>`;
|
|
1278
1279
|
}
|
|
1279
1280
|
|
|
1280
1281
|
// ---------- package by package ----------
|
|
@@ -1560,16 +1561,16 @@ function ogTags() {
|
|
|
1560
1561
|
// index of what rendered, and what is not the team's and not counted.
|
|
1561
1562
|
function sidePanel() {
|
|
1562
1563
|
const date = esc((h.harvestedAt ?? '').slice(0, 10));
|
|
1563
|
-
const def = 'How
|
|
1564
|
+
const def = 'How well this repo follows the design system rules Roast checks.';
|
|
1564
1565
|
const lift = startProjection && healthScore !== null && startProjection.after > healthScore
|
|
1565
|
-
? ` <b>${['One fix
|
|
1566
|
+
? ` <b>${['One fix', 'Two fixes', 'Three fixes'][startProjection.count - 1] ?? 'The fixes'} could raise it to ${startProjection.after}.</b>` : '';
|
|
1566
1567
|
const rp = P.shadcn?.registryPaint ?? null;
|
|
1567
1568
|
const bd = breakdown.ownScore !== null && breakdown.installedPoints > 0 && rp
|
|
1568
1569
|
? `<div class="bd">Of which <b>${breakdown.installedPoints} point${breakdown.installedPoints === 1 ? '' : 's'}</b> come from installed code you did not write: ${esc(rp.dirs.map((d) => basename(d)).join(', '))} (${n(rp.files)} file${rp.files === 1 ? '' : 's'}, ${n(rp.tinUses)} palette colour${rp.tinUses === 1 ? '' : 's'}). Your own code alone would score <b>${breakdown.ownScore}</b>. Kept in the score because your agent reads those files like everything else; left out of the fixes because they are not yours to edit.</div>` : '';
|
|
1569
1570
|
const scoreBlock = healthScore !== null
|
|
1570
|
-
? `<div class="score">${eyebrow('
|
|
1571
|
+
? `<div class="score">${eyebrow('Design system health')}<div class="val">${healthScore}<span class="slash">/</span><span class="of">100</span></div><div class="def">${def}${lift}</div>${bd}</div>`
|
|
1571
1572
|
: noSystemLikely
|
|
1572
|
-
? `<div class="score muted">${eyebrow('
|
|
1573
|
+
? `<div class="score muted">${eyebrow('Design system health')}<div class="val">—</div><div class="note">nothing here was measured · see the note</div><div class="def">${def}</div></div>` : '';
|
|
1573
1574
|
const chips = `<div class="chips">${stack.map((c) => `<span class="chip">${esc(c)}</span>`).join('')}${(P.shadcn?.sheet?.tweakcnPresent ?? 0) >= 10 ? '<span class="chip">tweakcn theme</span>' : ''}${P.shadcn?.lint ? '<span class="chip">shadcn/lint</span>' : ''}${dsUnrecognised ? '<span class="chip chip-dim">design system: unrecognised</span>' : ''}${legacyChip ? `<span class="chip chip-dim">${esc(legacyChip)}</span>` : ''}${agentFiles.map((c) => `<span class="chip chip-agent">${esc(c.file)}</span>`).join('')}</div>`;
|
|
1574
1575
|
const facts = [
|
|
1575
1576
|
shadcnReceipt(),
|
|
@@ -1787,6 +1788,7 @@ ${ogTags()}
|
|
|
1787
1788
|
.verdict-card .b2 { left:-40px; bottom:-80px; width:224px; height:224px; background:var(--blob2); filter:blur(48px); }
|
|
1788
1789
|
.verdict { position:relative; font:600 clamp(18px,2.6vw,24px)/1.45 var(--disp); letter-spacing:-.01em; margin-top:10px; }
|
|
1789
1790
|
.verdict-sub { position:relative; margin-top:12px; font-size:14.5px; line-height:1.6; color:var(--dim); }
|
|
1791
|
+
.verdict-card .gaps-list { position:relative; margin-top:18px; }
|
|
1790
1792
|
.gap-empty { padding:0 26px 22px; color:var(--dim); font-size:14px; line-height:1.6; }
|
|
1791
1793
|
.gap-why { margin-top:6px; color:var(--dim); font-size:13.5px; }
|
|
1792
1794
|
|
|
@@ -2110,12 +2112,10 @@ ${sidePanel()}
|
|
|
2110
2112
|
: `There is most likely <b>no design system in this repo</b>: almost no colour or spacing values were found. Styling may live outside this codebase (CDN stylesheets, a parent repo, or generated output). No score is given.`}</span></div>` : ''}
|
|
2111
2113
|
<div class="glass verdict-card">
|
|
2112
2114
|
<div class="blob b1"></div><div class="blob b2"></div>
|
|
2113
|
-
${eyebrow(noSystemLikely || fresh ? 'Summary' : '
|
|
2114
|
-
${gapLead ? `<div class="verdict">${gapLead}</div><div class="verdict-sub">${
|
|
2115
|
+
${eyebrow(noSystemLikely || fresh ? 'Summary' : 'Where will your agent have to guess?')}
|
|
2116
|
+
${gapLead ? `<div class="verdict">${gapLead}</div><div class="verdict-sub">${gapSub()}</div>${gapsList()}` : `<div class="verdict">${esc(verdict)}</div>`}
|
|
2115
2117
|
</div>
|
|
2116
2118
|
|
|
2117
|
-
${gapsSection()}
|
|
2118
|
-
|
|
2119
2119
|
${notesSection()}
|
|
2120
2120
|
|
|
2121
2121
|
${extraSectionsHtml()}
|
|
@@ -1,7 +1,7 @@
|
|
|
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 = '9.1.
|
|
4
|
+
export const VERSION = '9.1.2';
|
|
5
5
|
// The shape of harvest.json and summary.json. Bumped only when a field is
|
|
6
6
|
// renamed, removed or changes meaning; a new field is not a new schema. A
|
|
7
7
|
// tool comparing two scans compares like with like by this number, not by VERSION.
|