roast-my-design-system 4.4.0 → 4.5.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
|
@@ -81,6 +81,18 @@ playground/
|
|
|
81
81
|
|
|
82
82
|
Both routes merge, and both are loud on purpose. The harvest JSON records every active pattern and how many files it removed, and the report prints a line in the header ("2 folders excluded by .roastignore (lab/, playground/) · 946 files kept out of this scan"). You can narrow the question, but the report always says which question was asked, so a scoped score can't be quietly gamed. There is no negation and no glob syntax: plain folder prefixes, nothing clever.
|
|
83
83
|
|
|
84
|
+
## In CI
|
|
85
|
+
|
|
86
|
+
The scanner already speaks SARIF, so wiring it into GitHub code scanning is six lines. Findings appear in the Security tab, annotated on the files themselves:
|
|
87
|
+
|
|
88
|
+
```yaml
|
|
89
|
+
- uses: actions/checkout@v4
|
|
90
|
+
- run: npx roast-my-design-system . --sarif --no-open
|
|
91
|
+
- uses: github/codeql-action/upload-sarif@v3
|
|
92
|
+
with:
|
|
93
|
+
sarif_file: design-system-roast.sarif
|
|
94
|
+
```
|
|
95
|
+
|
|
84
96
|
## Install
|
|
85
97
|
|
|
86
98
|
**No install, no Claude needed — just try it:**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "roast-my-design-system",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.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, scopes the scan with .roastignore, and writes rules for Claude, Cursor, GitHub Copilot and Windsurf with --apply.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"design-system",
|
|
@@ -449,6 +449,32 @@ function paletteSection() {
|
|
|
449
449
|
</section>`;
|
|
450
450
|
}
|
|
451
451
|
|
|
452
|
+
// Agent traps: findings that do not just sit there but multiply, because an
|
|
453
|
+
// agent reads the repo as instruction. Copy only, no new measurement, and a
|
|
454
|
+
// trap renders only when its mechanism is real for this repo, so the marker
|
|
455
|
+
// stays scarce enough to mean something.
|
|
456
|
+
function trapBox(text) {
|
|
457
|
+
return `<div class="trap">${ICONS.warn}<span><b>Agent trap.</b> ${text}</span></div>`;
|
|
458
|
+
}
|
|
459
|
+
function dupesTrap() {
|
|
460
|
+
const hard = exactDupes.filter((d) => !d.wrapped);
|
|
461
|
+
if (hard.length < 2) return '';
|
|
462
|
+
const top = hard[0];
|
|
463
|
+
return trapBox(`<${esc(top.name)}> alone has ${top.files.length} implementations. An agent asked for one cannot tell which is canonical, so it picks at random or writes another, and every wrong pick becomes the example the next agent copies. This is the finding that multiplies itself.`);
|
|
464
|
+
}
|
|
465
|
+
function spacingTrap() {
|
|
466
|
+
const candidates = [...spacing, ...(arbitrary ?? [])].filter((s) => s.count >= 15)
|
|
467
|
+
.sort((a, b) => b.count - a.count);
|
|
468
|
+
if (candidates.length) {
|
|
469
|
+
const t = candidates[0];
|
|
470
|
+
return trapBox(`${esc(t.value)} appears ${t.count} times. An agent looking for how this repo does spacing reads repetition as intent, so it will write occurrence ${t.count + 1}. The most copied pattern here is the one you least want copied.`);
|
|
471
|
+
}
|
|
472
|
+
if (twSpacing.length > 0 && spacing.length >= 10) {
|
|
473
|
+
return trapBox(`Two spacing dialects coexist here: Tailwind steps and ${spacing.length} raw CSS values. A human knows which is legacy. An agent sees two valid options and matches whichever file it opened last, so every edit is a coin toss between systems.`);
|
|
474
|
+
}
|
|
475
|
+
return '';
|
|
476
|
+
}
|
|
477
|
+
|
|
452
478
|
function spacingBars() {
|
|
453
479
|
const hasTw = twSpacing.length > 0;
|
|
454
480
|
const merged = [...spacing.map((s) => ({ label: s.value, count: s.count, off: hasTw })),
|
|
@@ -465,7 +491,7 @@ function spacingBars() {
|
|
|
465
491
|
<div class="chips-row">${arbitrary.slice(0, 10).map((a) => `<span class="vchip bad" title="${esc(a.files?.[0]?.file ?? '')}">${esc(a.value)} ×${a.count}</span>`).join('')}${arbitrary.length > 10 ? `<span class="vchip dim">+${arbitrary.length - 10} more</span>` : ''}</div></div>` : '';
|
|
466
492
|
return `<section class="glass pad">
|
|
467
493
|
${sectionHead(`${n(spacingTotal)} off-scale spacing values`, `a disciplined repo keeps these around a dozen · on-scale Tailwind steps (·) shown for context · off-scale in coral`)}
|
|
468
|
-
<div class="bars">${rows}</div>${arb}</section>`;
|
|
494
|
+
<div class="bars">${rows}</div>${arb}${spacingTrap()}</section>`;
|
|
469
495
|
}
|
|
470
496
|
|
|
471
497
|
function duplicatesSection() {
|
|
@@ -494,6 +520,7 @@ function duplicatesSection() {
|
|
|
494
520
|
</div>`).join('');
|
|
495
521
|
return `<div class="glass pad half">
|
|
496
522
|
${sectionHead('Duplicated components', 'an agent asking which one is canonical gets several plausible answers. Paths open in VS Code')}
|
|
523
|
+
${dupesTrap()}
|
|
497
524
|
<div class="fams">${iconCard}${dupeCards}${famCards}</div></div>`;
|
|
498
525
|
}
|
|
499
526
|
|
|
@@ -967,6 +994,10 @@ const html = `<!doctype html>
|
|
|
967
994
|
.nods { display:flex; gap:12px; align-items:flex-start; background:var(--nods-bg); border:1px solid var(--nods-line);
|
|
968
995
|
border-radius:16px; padding:14px 16px; margin-top:20px; font-size:14px; }
|
|
969
996
|
.nods .ic { position:static; flex-shrink:0; width:22px; height:22px; }
|
|
997
|
+
.trap { display:flex; gap:11px; align-items:flex-start; background:var(--coral-soft); border:1px solid var(--bad-ring);
|
|
998
|
+
border-radius:14px; padding:12px 14px; margin-top:16px; font-size:13px; line-height:1.6; color:var(--dim); }
|
|
999
|
+
.trap b { color:var(--coral); font-weight:700; letter-spacing:.02em; }
|
|
1000
|
+
.trap .ic { position:static; flex-shrink:0; width:18px; height:18px; margin-top:2px; }
|
|
970
1001
|
|
|
971
1002
|
/* verdict hero card */
|
|
972
1003
|
.verdict-card { position:relative; overflow:hidden; margin-top:22px; padding:26px 28px; }
|