sheleg-design-skill 1.61.3 → 1.63.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/CHANGELOG.md +148 -0
- package/README.md +66 -11
- package/bin/cli.js +99 -1
- package/bin/lint.js +744 -0
- package/bin/record.js +413 -0
- package/cursor/rules/sheleg-design.mdc +22 -3
- package/kits/deskmate/src/styles.css +1 -1
- package/kits/paperclip/src/styles.css +7 -4
- package/kits/workbench/src/ProgressBar.tsx +4 -1
- package/kits/workbench/src/styles.css +10 -1
- package/package.json +3 -3
- package/plugins/sheleg-design/.claude-plugin/plugin.json +1 -1
- package/plugins/sheleg-design/skills/sheleg-design/ADA_RUBRIC.md +188 -0
- package/plugins/sheleg-design/skills/sheleg-design/APPLYING.md +7 -3
- package/plugins/sheleg-design/skills/sheleg-design/CREATIVE_DIRECTOR.md +119 -23
- package/plugins/sheleg-design/skills/sheleg-design/DESIGN_SYNC_BRIDGE.md +2 -2
- package/plugins/sheleg-design/skills/sheleg-design/FIGMA_BRIDGE.md +66 -11
- package/plugins/sheleg-design/skills/sheleg-design/KNOWLEDGE_PROVENANCE.md +42 -1
- package/plugins/sheleg-design/skills/sheleg-design/MOBILE_SURFACES.md +40 -1
- package/plugins/sheleg-design/skills/sheleg-design/MOTION_DOCTRINE.md +49 -0
- package/plugins/sheleg-design/skills/sheleg-design/MOTION_PRODUCTION.md +3 -0
- package/plugins/sheleg-design/skills/sheleg-design/SHELEG_DESIGN.md +14 -21
- package/plugins/sheleg-design/skills/sheleg-design/SKILL.md +21 -22
- package/plugins/sheleg-design/skills/sheleg-design/SLOP_MARKERS.md +223 -0
- package/plugins/sheleg-design/skills/sheleg-design/templates/director-record.md +186 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,151 @@
|
|
|
1
|
+
## 1.63.0 — the director record is a file with a validator, and a flagship is scored on a rubric that says where it came from
|
|
2
|
+
|
|
3
|
+
The director's output was a prose block at the end of `CREATIVE_DIRECTOR.md`: nothing
|
|
4
|
+
held it, so a brief with no falsifier, a taste of "modern, clean", a fork judged by a
|
|
5
|
+
rubric written afterwards and a `Markers` line nobody ran all passed as a record. The
|
|
6
|
+
plan's pipeline (contracts K3, K6, K7) needs a record the next stage can check, and a
|
|
7
|
+
quality bar for flagship surfaces that is more than the measurable table.
|
|
8
|
+
|
|
9
|
+
- **D3 — `templates/director-record.md` and `npx sheleg-design-skill --check-record
|
|
10
|
+
<file> [--json]`.** A header with `surface_class` (`flagship`, `product`,
|
|
11
|
+
`internal`, `ad`) and sixteen `## <Field>` headings: Brief, Mode, Taste, References,
|
|
12
|
+
Cast, Fork, Rubric, Critique, Markers, Alignment, Quality, Signature, Surfaces,
|
|
13
|
+
Haptics, ADA, Open. A flagship owes all sixteen; a product screen Brief, Mode,
|
|
14
|
+
References, Markers, Open; an internal tool Brief, Mode, Markers, Open; an ad Brief,
|
|
15
|
+
Mode, Taste, References, Markers, Signature, ADA, Open. Each owed field is held to
|
|
16
|
+
its rule: Brief needs Surface, Job, Constraint and Falsifier; Taste needs `Take:` and
|
|
17
|
+
`Ban:` lines with something other than generic words (English and Russian); References
|
|
18
|
+
needs five real-product URLs each with `take:`, or `none found` with `Searched:`;
|
|
19
|
+
`Fork: yes` needs a Rubric section it names, `A:`/`B:` and a `Winner:`; Rubric three
|
|
20
|
+
criteria; Critique region → defect → change triples or `clean render`; Markers a
|
|
21
|
+
`--lint` run with its revision and counts, and no S1; Signature exactly one moment
|
|
22
|
+
with What/Where/Why; ADA the class profile, every G item passing, at most two J FAIL
|
|
23
|
+
on a flagship, no J PASS without a `Labelled set:`, safe zones on an ad. Guidance
|
|
24
|
+
lives in HTML comments, so a copied template fails until it is filled. `Mode:
|
|
25
|
+
declined` with its reason passes on its own. Exit 0 valid, 1 violations as
|
|
26
|
+
`<Field>: <problem>`, 2 usage. `bin/record.js`, Node, no dependencies; its rules
|
|
27
|
+
join `--self-test`. The prose block in `CREATIVE_DIRECTOR.md` is replaced by a
|
|
28
|
+
pointer to both, keeping the doctrine on `Open`.
|
|
29
|
+
- **K6 — `ADA_RUBRIC.md`.** R1–R25, each binary, typed G (a gate no judge outvotes),
|
|
30
|
+
J (a judge on a per-task checklist) or H (the human on the contact sheet), with
|
|
31
|
+
applicability and how to verify (tool → artifact). Profiles: flagship all 25, every
|
|
32
|
+
G PASS and at most two J FAIL with reasons; product the G items R1, R3–R5, R8, R9,
|
|
33
|
+
R12–R17, R19, R20, R22; ad R7, R8, R10, R11, R14, R18, R21, R22 plus safe zones. The
|
|
34
|
+
judge: a checklist per task, pairwise only against an approved reference in both
|
|
35
|
+
orders, three samples, disagreement `uncertain` to the human, J items NOT_ASSESSED
|
|
36
|
+
until a labelled set exists, never above a G-FAIL, and never the agent that
|
|
37
|
+
rendered the surface. Every FAIL is a triple. **The file says plainly that the
|
|
38
|
+
rubric is derived and Apple publishes none**, and cites Apple's award pages with
|
|
39
|
+
the date they were read. Linked from Act 5 and `SKILL.md`.
|
|
40
|
+
- **D4 — the reference sweep is mandatory** for a new design and a redesign of a brand
|
|
41
|
+
or flagship surface (Act 2, `APPLYING.md`, `SKILL.md`), through whichever reference
|
|
42
|
+
servers the session exposes — Refero, Lazyweb, Mobbin where authorized — with an
|
|
43
|
+
empty result recorded as `none found` and where it looked.
|
|
44
|
+
- **First time right**, a new section of `CREATIVE_DIRECTOR.md`: decisions before
|
|
45
|
+
pixels in the record; the forbidden list loaded into context and the linter run;
|
|
46
|
+
one key screen in one state first; feedback only external and specific; a
|
|
47
|
+
re-render budget of one, at most two, then `unresolved` to the person; the
|
|
48
|
+
generator is not the judge.
|
|
49
|
+
- **D5 — `MOTION_DOCTRINE.md` §12, reviewing motion:** a frame strip and a run video,
|
|
50
|
+
timings judged by one `DUR-*` row each, reduced motion turned on and captured again,
|
|
51
|
+
one moment per viewport; `npx hyperframes snapshot` / `compare` as the evidence for a
|
|
52
|
+
rendered file (compare is a review surface, not a gate); `review-animations` by name.
|
|
53
|
+
It feeds the record's Quality and Critique.
|
|
54
|
+
- **D6 — `FIGMA_BRIDGE.md` §3 re-drawn.** "Motion stays in code — Figma has no easing
|
|
55
|
+
variable type" was no longer true: the official Figma plugin (2.2.120) documents
|
|
56
|
+
TIMING and EASING variables, keyframes, animation styles and `get_motion_context`
|
|
57
|
+
snippets. Durations and curves can now be published; motion read back is held to
|
|
58
|
+
the doctrine (Figma's `EASE_IN`, `…_BACK` and `BOUNCY` are banned forms in UI); the
|
|
59
|
+
scroll clock, scrubbed motion and the reduced-motion branch stay in code. Read from
|
|
60
|
+
the plugin's documentation, not confirmed by a call (B-144). New §5: a frame is
|
|
61
|
+
critiqued — `get_screenshot`, the record's rubric, triples, `get_variable_defs` —
|
|
62
|
+
before anyone approves it; and one Figma file per surface (App / Web / ASO). README,
|
|
63
|
+
the Cursor rule and `DESIGN_SYNC_BRIDGE.md` follow.
|
|
64
|
+
- **D10 — the taste profile** is the record's `Taste` field and a guide in the template
|
|
65
|
+
(what we take, what we ban, anti-patterns, the three dials); Act 1 writes it beside
|
|
66
|
+
the brief. A profile of "modern / clean" is empty. A personal profile's content stays
|
|
67
|
+
with its owner.
|
|
68
|
+
- **`MOBILE_SURFACES.md`, the axes matrix:** states × viewport × theme × text size ×
|
|
69
|
+
locale, pairwise coverage plus the mandatory pairs dark × largest text and RTL ×
|
|
70
|
+
narrowest viewport, a capture record per cell, the commands that set each axis; the
|
|
71
|
+
native state matrix is its native case, and a web render still proves no native cell.
|
|
72
|
+
- **D8 — `evals/render/`:** four fixed briefs (landing hero with motion, mobile
|
|
73
|
+
onboarding, product dashboard, paywall) and a runner that counts linter findings by
|
|
74
|
+
severity, validates each director record, and scores the G items source can decide
|
|
75
|
+
(R8, R20, R24), reporting the rest NOT_RUN; `--write` appends a dated row to
|
|
76
|
+
`test/evals/RESULTS.md`, whose new section states the method. The model generation
|
|
77
|
+
step is NOT_RUN in CI; the harness row on the hand-written sample is recorded as
|
|
78
|
+
such (B-142).
|
|
79
|
+
- **B-140 — the kits obey their own floor.** `workbench`'s progress fill slides by a
|
|
80
|
+
translate instead of transitioning `width`, `paperclip`'s lane mark grows by a scale
|
|
81
|
+
instead of `width`/`height`, both rendered before and after with the same geometry;
|
|
82
|
+
`deskmate`'s gradient word carries its waiver and reason. `node bin/cli.js --lint
|
|
83
|
+
kits` runs in `npm test` and in CI: 0 S1.
|
|
84
|
+
- **Provenance:** a new table for sources with no commit to pin — Apple's award pages,
|
|
85
|
+
the judge papers, the Figma plugin's motion skills and the HyperFrames CLI skill,
|
|
86
|
+
each `derived`, dated, with SHA256 where a file was read.
|
|
87
|
+
- **Gates:** `test/record_test.js` (49 cases through the real CLI, every rule watched
|
|
88
|
+
failing on one planted defect before `bin/record.js` existed); `validate_director_record()`
|
|
89
|
+
and `validate_ada_rubric()` with three plants; `test/render_eval_test.js` on the sample.
|
|
90
|
+
|
|
91
|
+
The `SKILL.md` body is 4747/4750 tokens: the new load-on-demand line was paid for by
|
|
92
|
+
tightening the READ FIRST paragraph and the references section.
|
|
93
|
+
|
|
94
|
+
## 1.62.0 — a visual floor with reasons and exceptions, and a linter that reads a project against it
|
|
95
|
+
|
|
96
|
+
Generated design kept shipping through reviews that had nothing to point at.
|
|
97
|
+
`CREATIVE_DIRECTOR.md` had refused impeccable's fixed style bans, rightly, because a
|
|
98
|
+
ban that cannot lose an argument contradicts "an explicit user requirement beats the
|
|
99
|
+
baseline"; what it left was no floor at all. Two private production codebases read
|
|
100
|
+
on 2026-10-07 carried emoji standing in for icons, overshoot curves, a side-stripe
|
|
101
|
+
alert card and layout-property transitions, all found by audits after the fact.
|
|
102
|
+
|
|
103
|
+
- **`SLOP_MARKERS.md`, the visual floor (catalogue version 2026-10-07).** 49 markers
|
|
104
|
+
`V001`–`V049` in eight groups (color, type, layout, icon-decor, content, motion,
|
|
105
|
+
mobile, platform), each with severity S1–S3, the tell, why it reads as a default,
|
|
106
|
+
what a designer does instead, how it is checked, the exception under which it is
|
|
107
|
+
not slop, and a commit-pinned or dated source. The floor holds with `Style pack:
|
|
108
|
+
none`; a pack may tighten it and never loosen it; a brief that asks for a marked
|
|
109
|
+
pattern wins once the director record names the marker. The three default looks
|
|
110
|
+
are markers V012, V013 and V026, and the `SHELEG_DESIGN.md` section now points
|
|
111
|
+
there. Copy tells stay with `copywriting`; no punctuation rule is adopted (in
|
|
112
|
+
Russian the em dash is normative).
|
|
113
|
+
- **`npx sheleg-design-skill --lint <dir> [--json] [--ratchet <budget.json>]
|
|
114
|
+
[--include-tests]`.** Twenty rules, one per `lint:` row: purple gradients,
|
|
115
|
+
gradient text, decorative glass, glows, the side stripe, grid and stripe
|
|
116
|
+
backgrounds, grey on colour, uniform card chrome, raw colour (under `--ratchet`),
|
|
117
|
+
default faces, emoji as icons (in markup and in data such as `icon: "📊"`, never
|
|
118
|
+
in prose), sparkles, fade-in on every section, overshoot curves, `transition:
|
|
119
|
+
all`, layout-property animation, `scale(0)`, `ease-in` and a missing reduced-motion
|
|
120
|
+
path. Exit 0 no S1, 1 an S1 or a file over its budget, 2 a usage error; S2 and S3
|
|
121
|
+
print and never block. Tests, dependencies, build output, hidden directories,
|
|
122
|
+
comments and a copied pack token layer's measured tokens are not reported. A
|
|
123
|
+
recorded exception is `sheleg-lint-allow: V0NN <reason>`, and a bare id waives
|
|
124
|
+
nothing. Written in Node like the installer, so it runs wherever `npx` does.
|
|
125
|
+
- **D9, decided.** `CREATIVE_DIRECTOR.md` records markers-not-bans in place of the
|
|
126
|
+
old refusal, and the director's record gains a `Markers` line (the lint run and
|
|
127
|
+
each waived marker with the line that earned it).
|
|
128
|
+
- **The lane references follow the umbrella's design pack.** A new design enters at
|
|
129
|
+
`references`; a redesign at `verify` + `visual-qa` + `a11y`; an update at
|
|
130
|
+
`tokens`, then `visual-qa`; an audit runs `a11y → verify → visual-qa → speed`; the
|
|
131
|
+
`--lane` list names `references`, `critique`, `visual-qa` and `speed`.
|
|
132
|
+
- **Provenance, 16 → 20 rows**, all `derived`: impeccable's detector catalogue,
|
|
133
|
+
craft floor and motion reference at `12b25ae`, and `anthropics/skills`
|
|
134
|
+
`frontend-design` at `41bbe19`. Nothing was copied.
|
|
135
|
+
- **Gates.** `validate_slop_markers()` holds the catalogue's shape and required
|
|
136
|
+
markers, and rule, id and severity in both directions between `bin/lint.js` and the
|
|
137
|
+
catalogue, with three plants; `test/lint_test.js` runs the real CLI on throwaway
|
|
138
|
+
trees (every S1 rule blocking, every quiet case quiet, the ratchet, the exit
|
|
139
|
+
contract); `--self-test` plants a defect and a clean twin for every rule.
|
|
140
|
+
|
|
141
|
+
Calibrated on the two private codebases (read-only): 107 S1 / 18 S2 and 83 S1 / 5 S2,
|
|
142
|
+
every listed defect found, the two test-fixture hits impeccable's detector reported
|
|
143
|
+
absent by default and present under `--include-tests`. One false-positive class found
|
|
144
|
+
there (a bare `"back"` string read as an ease) was fixed with a regression case. The
|
|
145
|
+
same run over this repository's kits found two layout transitions of its own (B-140).
|
|
146
|
+
The `SKILL.md` body is 4742/4750 tokens. Whether models produce fewer markers with the
|
|
147
|
+
floor loaded was not measured (plan task D8).
|
|
148
|
+
|
|
1
149
|
## 1.61.3 — every borrowed rule names its source, and the motion tools agree with the doctrine
|
|
2
150
|
|
|
3
151
|
`MOTION_DOCTRINE.md` §1–4 is Emil Kowalski's animation doctrine almost word for word
|
package/README.md
CHANGED
|
@@ -175,6 +175,9 @@ skills.
|
|
|
175
175
|
| `SKILL.md` | The agent-facing skill: discovery triggers, the principles, how to apply them, quick-reference rules, common mistakes |
|
|
176
176
|
| `SHELEG_DESIGN.md` | The full reference: architecture, layer-by-layer mechanics with code, the exact morph math, the DOM↔WebGL projection bridge, a build-from-scratch recipe, and why each piece works |
|
|
177
177
|
| `SURFACE_COMPOSITION.md` | Two decisions the pack layer does not make: the six depth layers of a scene, read before writing CSS for a cinematic page; and the handoff to `dataviz`, read before drawing a chart in any pack — token names are not uniform across the thirteen and an undefined custom property fails silently |
|
|
178
|
+
| `SLOP_MARKERS.md` | The visual floor: 49 markers of generated design (purple gradients, emoji as icons, default faces, the side stripe, the three default looks, banned motion forms…), each with severity, reason, what to do instead and the exception under which it is not slop. Holds with or without a pack; twenty of them are checked by `--lint` |
|
|
179
|
+
| `ADA_RUBRIC.md` | 25 binary checks derived from the Apple Design Awards categories (Apple publishes no rubric; the file says so and cites the pages it read), each typed as a deterministic gate, a judged item or a human one, with profiles for flagship, product and ad surfaces and the rules that keep a model judge honest |
|
|
180
|
+
| `templates/director-record.md` | The director record: one `## <Field>` per decision — brief, taste, references, fork and rubric, critique, markers, signature moment, ADA verdict, what is left open — filled before the first render and checked by `--check-record` |
|
|
178
181
|
| `MOTION_DOCTRINE.md` | Whether to animate at all, before how: the frequency table that kills motion on high-repetition paths, the easing tree and the `ease-in` ban, the duration ceiling, the forbidden forms, and the reduced-motion contract. `SKILL.md` marks it required before any animation |
|
|
179
182
|
| `MOTION_PRODUCTION.md` | What changes when motion leaves the page as a file: the four things a rendered asset gives up, the two programmatic-video tools measured side by side with the licence threshold and the pre-1.0 risk stated, which one this library recommends and the three conditions that reverse it, how a pack's token layer crosses the render seam, and where the reduced-motion obligation lands once there is no user to read a media query |
|
|
180
183
|
| `DESIGN_SYNC_BRIDGE.md` | The Claude Design contract: what a pack sends to claude.ai/design and in what shape, the rule for each of the four reference types, and the border motion does not cross |
|
|
@@ -184,6 +187,48 @@ skills.
|
|
|
184
187
|
| `styles/tokens/*.css` | The ready-made token layer per pack, copied verbatim instead of transcribed (`workbench` and `field-notes` each ship a light `:root` plus a `data-theme="dark"` twin) |
|
|
185
188
|
| `styles/STYLE_PACK_TEMPLATE.md` | The pack contract as a skeleton, so a new style is authored against the same headings rather than improvised |
|
|
186
189
|
|
|
190
|
+
### Check a project against the floor
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
npx sheleg-design-skill --lint ./src # human report, exit 1 on any S1
|
|
194
|
+
npx sheleg-design-skill --lint ./src --json # [{id, rule, severity, file, line, snippet}]
|
|
195
|
+
npx sheleg-design-skill --lint ./src --ratchet sloplint-budget.json # per-file budgets that only fall
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
The linter reads `html`, `css`, `scss`, `js`, `jsx`, `ts`, `tsx`, `vue`, `svelte`
|
|
199
|
+
and `astro`, skips dependencies, build output, hidden directories and tests
|
|
200
|
+
(`--include-tests` reads them), and checks the `lint:` rows of
|
|
201
|
+
[`SLOP_MARKERS.md`](plugins/sheleg-design/skills/sheleg-design/SLOP_MARKERS.md):
|
|
202
|
+
emoji used as icons (in markup and in data such as `icon: "📊"`), purple
|
|
203
|
+
gradients, gradient text, a default face with no declared reason, decorative
|
|
204
|
+
glass, the side stripe, glows, grid backgrounds, grey on colour, `transition: all`,
|
|
205
|
+
layout-property animation, `scale(0)`, `ease-in`, overshoot curves and a missing
|
|
206
|
+
`prefers-reduced-motion` path. Exit 0 means no S1 finding, 1 an S1 finding or a
|
|
207
|
+
file over its budget, 2 a usage error; S2 and S3 print and never block. A
|
|
208
|
+
recorded exception is a comment, `sheleg-lint-allow: V015 <reason>`, and the
|
|
209
|
+
reason is required. `--self-test` plants a defect for every rule. It is a pattern
|
|
210
|
+
reader, not a renderer: the catalogue's `review` rows stay with the render
|
|
211
|
+
critique.
|
|
212
|
+
|
|
213
|
+
### Check a director record
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
npx sheleg-design-skill --check-record docs/design/pricing/director-record.md
|
|
217
|
+
npx sheleg-design-skill --check-record docs/design/pricing/director-record.md --json
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
A design's decisions live in a record before the first pixel: a header with
|
|
221
|
+
`surface_class` (`flagship`, `product`, `internal` or `ad`) and one `## <Field>`
|
|
222
|
+
per decision, from the template in the installed skill. The class decides which
|
|
223
|
+
fields are owed — a flagship all sixteen, a product screen Brief, Mode,
|
|
224
|
+
References, Markers and Open — and each owed field is held to its rule, not just
|
|
225
|
+
its presence: a taste profile of "modern, clean" is empty, five references on
|
|
226
|
+
placeholder hosts are none, `Fork: yes` with no rubric compared nothing,
|
|
227
|
+
`Markers` must cite a `--lint` run with its revision and counts, and a flagship's
|
|
228
|
+
ADA verdict fails on any G item. A refusal is a record too: `Mode: declined` with
|
|
229
|
+
its reason passes. Exit 0 valid, 1 with each violation as `<Field>: <problem>`,
|
|
230
|
+
2 a usage error.
|
|
231
|
+
|
|
187
232
|
## What you get out of it
|
|
188
233
|
|
|
189
234
|
- **A motion methodology, not a component dump.** Scroll-linked animation,
|
|
@@ -231,9 +276,10 @@ values *onto* the pack's tokens instead of inlining hexes.
|
|
|
231
276
|
The bridge is specific because the traps are: `workbench`'s light and dark are
|
|
232
277
|
two **modes of one collection**, while `editorial-luxury`'s espresso sections
|
|
233
278
|
are surfaces and not a mode at all; Figma colors are 0..1 floats, not hex; and
|
|
234
|
-
motion
|
|
235
|
-
|
|
236
|
-
|
|
279
|
+
motion crosses only in part — durations and curves publish as Figma's TIMING and
|
|
280
|
+
EASING variables, but the scroll clock, scrubbed motion and the reduced-motion branch
|
|
281
|
+
stay code-only, and motion read back from a file is held to the doctrine — and
|
|
282
|
+
shadows are effect styles whose parts bind to variables. A value in a file with no matching token is either a gap in the pack
|
|
237
283
|
or drift in the file — the one thing it is never is a literal in a component.
|
|
238
284
|
|
|
239
285
|
## Claude Design, in one direction
|
|
@@ -259,18 +305,22 @@ stops an agent inventing motion to fill the silence.
|
|
|
259
305
|
The kits are not part of the install. `--kit` fetches one on demand, which is how
|
|
260
306
|
the skill stays documentation while still having real components to hand.
|
|
261
307
|
|
|
262
|
-
##
|
|
308
|
+
## Reference sweeps (Refero, Lazyweb, Mobbin)
|
|
263
309
|
|
|
264
310
|
A style pack locks *how it looks*. It says nothing about what a good version of
|
|
265
|
-
the screen you're about to build actually **contains**.
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
311
|
+
the screen you're about to build actually **contains**. For a new design or a
|
|
312
|
+
redesign of a brand or flagship surface the sweep is not optional: through
|
|
313
|
+
whichever reference servers are connected — Refero, [Lazyweb](https://www.lazyweb.com),
|
|
314
|
+
Mobbin where the account is authorized — the skill looks at five or more shipped
|
|
315
|
+
products for the target screen (signup, onboarding, paywall, pricing, checkout,
|
|
316
|
+
dashboard, settings) before laying anything out, and records what it takes from
|
|
317
|
+
each. An empty sweep is recorded as `none found` with where it looked.
|
|
269
318
|
|
|
270
319
|
The division of labor keeps the result one system: references inform **layout,
|
|
271
320
|
hierarchy and content order**; palette, type and motion stay the pack's. Setup
|
|
272
321
|
is a Streamable HTTP MCP server plus a per-user token — keep it out of your
|
|
273
|
-
repo.
|
|
322
|
+
repo. Without any reference server the sweep runs on the open web, and the
|
|
323
|
+
record says the servers were absent.
|
|
274
324
|
|
|
275
325
|
## Development
|
|
276
326
|
|
|
@@ -282,8 +332,8 @@ These run **in a clone of this repository**. The published npm package ships no
|
|
|
282
332
|
python3 test/validate.py # or: npm test
|
|
283
333
|
```
|
|
284
334
|
|
|
285
|
-
`npm test` is **
|
|
286
|
-
of the contract:
|
|
335
|
+
`npm test` is **several gates**, not one, and `validate.py` alone is about a
|
|
336
|
+
third of the contract:
|
|
287
337
|
|
|
288
338
|
| Gate | What it decides |
|
|
289
339
|
|---|---|
|
|
@@ -291,6 +341,11 @@ of the contract:
|
|
|
291
341
|
| `test/validate_palette.py` | contrast floors and semantic separation per theme, including three simulated dichromacies · AI-default-cluster provenance · **every contrast ratio the docs state, recomputed from the token layer** |
|
|
292
342
|
| `test/sloplint.py` | the bundle obeying its own bans, in token layers, fenced examples **and the inline CSS the packs prescribe in prose** · doctrine completeness · pack origin addressability |
|
|
293
343
|
| `node --check bin/cli.js` | the installer parses |
|
|
344
|
+
| `node test/installer_test.js` | both installers against throwaway HOMEs, above all the refusal to shadow an installed plugin |
|
|
345
|
+
| `node test/lint_test.js` | the project linter end to end: every S1 rule blocks on its plant, every quiet case stays quiet, the ratchet and the exit contract; `--self-test` checks the catalogue against the rules |
|
|
346
|
+
| `node test/record_test.js` | the director-record validator end to end: a valid record for every surface class and a declined one pass; one planted defect per rule fails, each on its own field |
|
|
347
|
+
| `node test/render_eval_test.js` | the render eval runner (`evals/render/`) on the committed hand-written sample: every count it must report |
|
|
348
|
+
| `node bin/cli.js --lint kits` | the reference kits obey the floor they ship with: no S1 marker in any kit |
|
|
294
349
|
|
|
295
350
|
Each gate ships a `--self-test` that plants a defect it must catch (`npm run
|
|
296
351
|
selftest`), rejects an unknown argument instead of silently running the normal
|
package/bin/cli.js
CHANGED
|
@@ -155,10 +155,18 @@ function parseArgs(argv) {
|
|
|
155
155
|
version: false,
|
|
156
156
|
kit: null, // --kit <pack>: materialize a reference kit instead of installing
|
|
157
157
|
out: null, // --out <path>: where the kit goes
|
|
158
|
+
lint: null, // --lint <dir>: run the project linter (bin/lint.js) on a tree
|
|
159
|
+
json: false, // --json: lint findings as a JSON array
|
|
160
|
+
ratchet: null, // --ratchet <budget.json>: per-file budgets that only fall
|
|
161
|
+
includeTests: false, // --include-tests: lint test files and fixtures too
|
|
162
|
+
selfTest: false, // --self-test: every lint rule against its planted defect
|
|
163
|
+
checkRecord: null, // --check-record <file>: validate a director record (bin/record.js)
|
|
158
164
|
error: null, // set → print help and exit non-zero
|
|
159
165
|
};
|
|
166
|
+
const seen = [];
|
|
160
167
|
for (let i = 0; i < argv.length; i++) {
|
|
161
168
|
const a = argv[i];
|
|
169
|
+
seen.push(a.split("=")[0]);
|
|
162
170
|
if (a === "--help" || a === "-h") opts.help = true;
|
|
163
171
|
else if (a === "--version" || a === "-v") opts.version = true;
|
|
164
172
|
else if (a === "--force" || a === "-f") opts.force = true;
|
|
@@ -190,10 +198,52 @@ function parseArgs(argv) {
|
|
|
190
198
|
const value = a.slice("--out=".length);
|
|
191
199
|
if (!value) opts.error = "--out needs a path";
|
|
192
200
|
else opts.out = value;
|
|
193
|
-
} else {
|
|
201
|
+
} else if (a === "--lint" || a.startsWith("--lint=")) {
|
|
202
|
+
const value = a === "--lint" ? argv[++i] : a.slice("--lint=".length);
|
|
203
|
+
if (!value || value.startsWith("-")) opts.error = "--lint needs a directory";
|
|
204
|
+
else opts.lint = value;
|
|
205
|
+
} else if (a === "--ratchet" || a.startsWith("--ratchet=")) {
|
|
206
|
+
const value = a === "--ratchet" ? argv[++i] : a.slice("--ratchet=".length);
|
|
207
|
+
if (!value || value.startsWith("-")) opts.error = "--ratchet needs a budget file";
|
|
208
|
+
else opts.ratchet = value;
|
|
209
|
+
} else if (a === "--check-record" || a.startsWith("--check-record=")) {
|
|
210
|
+
const value = a === "--check-record" ? argv[++i] : a.slice("--check-record=".length);
|
|
211
|
+
if (!value || value.startsWith("-")) opts.error = "--check-record needs a director-record file";
|
|
212
|
+
else opts.checkRecord = value;
|
|
213
|
+
} else if (a === "--json") opts.json = true;
|
|
214
|
+
else if (a === "--include-tests") opts.includeTests = true;
|
|
215
|
+
else if (a === "--self-test") opts.selfTest = true;
|
|
216
|
+
else {
|
|
194
217
|
opts.error = `unknown argument: ${a}`;
|
|
195
218
|
}
|
|
196
219
|
}
|
|
220
|
+
if (opts.error) return opts;
|
|
221
|
+
// The linter reads a tree and writes nothing, so any install or kit flag beside
|
|
222
|
+
// it is a request for two different things at once.
|
|
223
|
+
const LINT_ONLY = ["--json", "--ratchet", "--include-tests"];
|
|
224
|
+
const OTHERS = ["--cursor", "--claude", "--dir", "--force", "-f", "--kit", "--out"];
|
|
225
|
+
if (opts.selfTest && seen.some((x) => x !== "--self-test")) {
|
|
226
|
+
opts.error = "--self-test runs on its own";
|
|
227
|
+
return opts;
|
|
228
|
+
}
|
|
229
|
+
// The record validator reads one file and writes nothing; --json is the only
|
|
230
|
+
// flag it takes, and it does not run beside the linter or an install.
|
|
231
|
+
if (opts.checkRecord) {
|
|
232
|
+
const other = seen.find((x) => x !== "--check-record" && x !== "--json");
|
|
233
|
+
if (other) {
|
|
234
|
+
opts.error = `--check-record reads one record and takes only --json — ${other} does not combine with it`;
|
|
235
|
+
return opts;
|
|
236
|
+
}
|
|
237
|
+
return opts;
|
|
238
|
+
}
|
|
239
|
+
if (!opts.lint && seen.some((x) => LINT_ONLY.includes(x))) {
|
|
240
|
+
opts.error = `${seen.find((x) => LINT_ONLY.includes(x))} is only meaningful with --lint <dir>`;
|
|
241
|
+
return opts;
|
|
242
|
+
}
|
|
243
|
+
if (opts.lint && seen.some((x) => OTHERS.includes(x))) {
|
|
244
|
+
opts.error = `--lint reads a project and installs nothing — ${seen.find((x) => OTHERS.includes(x))} does not combine with it`;
|
|
245
|
+
return opts;
|
|
246
|
+
}
|
|
197
247
|
if (opts.flavor && opts.target) {
|
|
198
248
|
opts.error = "--dir cannot be combined with --cursor / --claude";
|
|
199
249
|
}
|
|
@@ -284,6 +334,29 @@ ${c("bold", "Claude Design")} ${c("dim", "(Claude Code only)")}
|
|
|
284
334
|
come out only when asked for by name. Then: npm install && npm run build,
|
|
285
335
|
and run /design-sync in that directory.
|
|
286
336
|
|
|
337
|
+
${c("bold", "Project linter")}
|
|
338
|
+
--lint <dir> Check a project against the visual floor in
|
|
339
|
+
SLOP_MARKERS.md (emoji icons, purple gradients, default
|
|
340
|
+
faces, banned motion forms and the rest of the lint: rows)
|
|
341
|
+
--json Findings as [{id, rule, severity, file, line, snippet}]
|
|
342
|
+
--ratchet <budget> Per-file budgets ({"<file>": count}) that may only fall;
|
|
343
|
+
also counts raw palette classes and hex outside tokens
|
|
344
|
+
--include-tests Read tests and fixtures too (skipped by default)
|
|
345
|
+
--self-test Every rule against its planted defect, and the
|
|
346
|
+
catalogue against the rules (the record rules too)
|
|
347
|
+
|
|
348
|
+
Exit: 0 no S1 finding, 1 an S1 finding or a file over budget, 2 usage error.
|
|
349
|
+
S2 and S3 findings print and do not change the exit code.
|
|
350
|
+
|
|
351
|
+
${c("bold", "Director record")}
|
|
352
|
+
--check-record <file> Validate a director record (templates/director-record.md
|
|
353
|
+
in the installed skill): the fields its surface_class
|
|
354
|
+
owes are present and filled by their rules
|
|
355
|
+
--json The verdict as {valid, surface_class, violations[]}
|
|
356
|
+
|
|
357
|
+
Exit: 0 valid (a declined record with its reason included), 1 violations,
|
|
358
|
+
each listed as <Field>: <problem>, 2 usage error.
|
|
359
|
+
|
|
287
360
|
${c("bold", "Default")}
|
|
288
361
|
Auto-detects: uses .cursor/ if present, else .claude/ if present,
|
|
289
362
|
otherwise creates .cursor/skills/${SKILL_SLUG}/.
|
|
@@ -540,6 +613,31 @@ function main() {
|
|
|
540
613
|
return;
|
|
541
614
|
}
|
|
542
615
|
|
|
616
|
+
if (opts.checkRecord) {
|
|
617
|
+
const record = require("./record.js");
|
|
618
|
+
const io = { out: (l) => console.log(l), err: (l) => console.error(l) };
|
|
619
|
+
process.exitCode = record.runCheckRecord({ file: opts.checkRecord, json: opts.json }, io);
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
if (opts.selfTest || opts.lint) {
|
|
624
|
+
const lint = require("./lint.js");
|
|
625
|
+
const io = { out: (l) => console.log(l), err: (l) => console.error(l) };
|
|
626
|
+
if (opts.selfTest) {
|
|
627
|
+
// Both validators answer for themselves; either failing fails the run.
|
|
628
|
+
const record = require("./record.js");
|
|
629
|
+
const a = lint.selfTest(io.out);
|
|
630
|
+
const b = record.selfTest(io.out);
|
|
631
|
+
process.exitCode = a || b ? 1 : 0;
|
|
632
|
+
} else {
|
|
633
|
+
process.exitCode = lint.runLint(
|
|
634
|
+
{ dir: opts.lint, json: opts.json, ratchet: opts.ratchet, includeTests: opts.includeTests },
|
|
635
|
+
io,
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
|
|
543
641
|
const cwd = process.cwd();
|
|
544
642
|
|
|
545
643
|
if (opts.kit) {
|