spec-layer 0.5.0 → 0.7.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 +138 -17
- package/dist/cli.js +1253 -319
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,9 +14,12 @@ After publishing a library from the plugin's Library screen, it shows a setup
|
|
|
14
14
|
command. Run it once in your repository:
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
npx spec-layer setup --id lib_... --key sl_...
|
|
17
|
+
npx spec-layer setup --id lib_... --key sl_... --platform web
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
`--platform` says what you build; without it, `setup` reads the repository
|
|
21
|
+
root and writes a token file only when it finds a web signal.
|
|
22
|
+
|
|
20
23
|
That records the library id, stores the key in a gitignored
|
|
21
24
|
`speclayer.local.json`, and writes `.speclayer/`. Every later command needs no
|
|
22
25
|
flags at all:
|
|
@@ -55,15 +58,19 @@ also keeps `.speclayer/manifest.json` on one format: 0.1.0 wrote no
|
|
|
55
58
|
versions have no such command, so the setup command the plugin copies fails
|
|
56
59
|
against them. The Foundation landing under `tokens/` as Design Tokens Format
|
|
57
60
|
Module 2025.10 files, rather than `ai/foundation.yaml`, needs 0.4.0 or later.
|
|
58
|
-
`tools` and `skill` need 0.5.0 or later.
|
|
61
|
+
`tools` and `skill` need 0.5.0 or later. `components/` in place of
|
|
62
|
+
`ai/components/`, `path` in place of `aiPath` in the manifest, and the
|
|
63
|
+
`outputs` block need 0.6.0 or later. `component-specs/` beside `tokens/`,
|
|
64
|
+
`componentSpecsDir`, cwd-relative manifest paths, and the `tokens/` directory
|
|
65
|
+
need 0.7.0 or later.
|
|
59
66
|
|
|
60
67
|
## Commands
|
|
61
68
|
|
|
62
69
|
| Command | What it does |
|
|
63
70
|
|---|---|
|
|
64
|
-
| `setup --id lib_... --key sl_... [--out DIR] [selection]` | Writes `speclayer.json`, stores the key in `speclayer.local.json`, then pulls. The command the plugin copies. |
|
|
65
|
-
| `init --id lib_... [--out DIR] [selection]` | Writes `speclayer.json` so later commands need no flags. No key, no network. |
|
|
66
|
-
| `pull [--id lib_...] [--key sl_...] [selection]` | Fetches the library and writes it into `DIR` (default `.speclayer`). |
|
|
71
|
+
| `setup --id lib_... --key sl_... [--out DIR] [--platform P]... [selection]` | Writes `speclayer.json`, stores the key in `speclayer.local.json`, then pulls. The command the plugin copies. |
|
|
72
|
+
| `init --id lib_... [--out DIR] [--platform P]... [selection]` | Writes `speclayer.json` so later commands need no flags. No key, no network. |
|
|
73
|
+
| `pull [--id lib_...] [--key sl_...] [--platform P]... [selection]` | Fetches the library and writes it into `DIR` (default `.speclayer`). |
|
|
67
74
|
| `status [--id lib_...] [--key sl_...]` | Checks freshness without writing. Exits `2` when the local copy is behind. |
|
|
68
75
|
| `list` | Lists every artifact in the last pull, with its file path or `not written`. |
|
|
69
76
|
| `show foundation [--canonical]` | Prints the Foundation's DTCG document to stdout. |
|
|
@@ -141,7 +148,7 @@ carry no key and are meant to be committed with the rest of the repository.
|
|
|
141
148
|
## Pulling part of a library
|
|
142
149
|
|
|
143
150
|
By default `pull` writes the Foundation and every documented component. When
|
|
144
|
-
your repo implements only some of them, narrow what lands in `
|
|
151
|
+
your repo implements only some of them, narrow what lands in `component-specs/`:
|
|
145
152
|
|
|
146
153
|
```bash
|
|
147
154
|
npx spec-layer pull --only foundation # tokens and styles, no components
|
|
@@ -172,7 +179,7 @@ That stores an `include` block in `speclayer.json`:
|
|
|
172
179
|
Selection flags on `pull` replace the stored selection for that run; they are
|
|
173
180
|
never merged with it.
|
|
174
181
|
|
|
175
|
-
The selection only decides which `
|
|
182
|
+
The selection only decides which `component-specs/` files are written. `bundle.json`
|
|
176
183
|
always holds the whole library, so `list` and `show` can answer for any
|
|
177
184
|
artifact, written or not, and `status` compares one hash.
|
|
178
185
|
|
|
@@ -239,7 +246,7 @@ matter what the ignore rules say.
|
|
|
239
246
|
```text
|
|
240
247
|
.speclayer/
|
|
241
248
|
bundle.json the published bundle, verbatim
|
|
242
|
-
manifest.json
|
|
249
|
+
manifest.json every artifact indexed by content hash and path, plus the selection, outputs, and componentSpecsDir
|
|
243
250
|
tokens/ the Foundation as Design Tokens Format Module 2025.10 files
|
|
244
251
|
<collection>.<mode>.json one file per collection and mode, rooted at the collection name
|
|
245
252
|
styles.typography.json text styles as typography composites (when present)
|
|
@@ -247,15 +254,24 @@ matter what the ignore rules say.
|
|
|
247
254
|
resolver.json Design Tokens Resolver Module 2025.10: sets, modifiers, order
|
|
248
255
|
spec-layer.meta.json Figma ids, scopes, code syntax, publication, keyed by DTCG path
|
|
249
256
|
report.json what DTCG could not express, with reasons and stable ids
|
|
250
|
-
|
|
257
|
+
outputs/
|
|
258
|
+
web-css.map.json DTCG path -> CSS custom property, where the name came from, and which file declares it
|
|
259
|
+
web-css.report.json what the CSS files could not express
|
|
260
|
+
component-specs/
|
|
261
|
+
<name>.yaml one brief per selected component, byte-identical to Copy for AI
|
|
262
|
+
tokens/
|
|
263
|
+
index.css imports every file below, in resolver order
|
|
264
|
+
<collection>.css one file per single-mode collection, at :root
|
|
265
|
+
<collection>.<mode>.css one file per mode of a multi-mode collection; the default at :root
|
|
251
266
|
```
|
|
252
267
|
|
|
253
|
-
Point your agent at
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
you need
|
|
268
|
+
Point your agent at `component-specs/` and `.speclayer/tokens/`, and import
|
|
269
|
+
`tokens/index.css` from the platform's root stylesheet. The component YAML is
|
|
270
|
+
the same compact form the plugin's **Copy for AI** puts on your clipboard;
|
|
271
|
+
`bundle.json` additionally holds the full canonical artifacts if you need
|
|
272
|
+
them.
|
|
257
273
|
|
|
258
|
-
In `manifest.json`, an artifact the selection left unwritten has `"
|
|
274
|
+
In `manifest.json`, an artifact the selection left unwritten has `"path":
|
|
259
275
|
null`. A manifest from CLI 0.1.0 has no `selection` field and means
|
|
260
276
|
everything was written.
|
|
261
277
|
|
|
@@ -264,11 +280,19 @@ interrupted pull never leaves a half-written directory. `pull` refuses an
|
|
|
264
280
|
output directory that is the current directory, a parent of it, or an existing
|
|
265
281
|
non-empty directory it did not write, since the swap replaces that directory.
|
|
266
282
|
|
|
283
|
+
`component-specs/` and `tokens/` are written in place, not swapped. `pull`
|
|
284
|
+
owns exactly the files there that begin with its marker (the CSS header, or
|
|
285
|
+
the brief's opening `spec_layer:` lines): it replaces or removes those,
|
|
286
|
+
ignores dotfiles, and refuses to run when anything else is present. A
|
|
287
|
+
repository that already uses a path can set `componentSpecsDir` or
|
|
288
|
+
`outputs[].path`.
|
|
289
|
+
|
|
267
290
|
When nothing changed since the last pull with the same selection, `pull`
|
|
268
291
|
prints `Already up to date` and writes nothing. Every republish stamps a new
|
|
269
292
|
export id and time into the canonical artifacts, so `bundle.json` and
|
|
270
293
|
`manifest.json` change on each republish even when the content did not. The
|
|
271
|
-
`
|
|
294
|
+
`component-specs/` YAML files, the `.speclayer/tokens/` files, and the
|
|
295
|
+
`tokens/` CSS stay stable.
|
|
272
296
|
|
|
273
297
|
## Configuring the token output
|
|
274
298
|
|
|
@@ -295,6 +319,103 @@ Point Style Dictionary at `.speclayer/tokens/` and load the files
|
|
|
295
319
|
`resolver.json` names for the mode you are building. The metadata sidecar and
|
|
296
320
|
the report are not token files; exclude them from token globs.
|
|
297
321
|
|
|
322
|
+
## Token files for your code
|
|
323
|
+
|
|
324
|
+
`pull` also writes a `tokens/` directory at `outputs[].path`: one CSS file
|
|
325
|
+
per collection and mode plus `index.css`, one per platform output. The
|
|
326
|
+
directory is written only when that pull writes the Foundation; with
|
|
327
|
+
`--only components` it is left exactly as it was, and `list` shows it as
|
|
328
|
+
`not written`.
|
|
329
|
+
|
|
330
|
+
```css
|
|
331
|
+
/* Generated by spec-layer from library lib_..., foundation sha256:..., web/css/kebab.
|
|
332
|
+
Do not edit. Change the design in Figma, republish, and run spec-layer pull. */
|
|
333
|
+
|
|
334
|
+
:root {
|
|
335
|
+
/* Foundation */
|
|
336
|
+
--foundation-colors-blue-500: #2e72d1;
|
|
337
|
+
--foundation-spacing-200: 8px;
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
```css
|
|
342
|
+
/* Generated by spec-layer from library lib_..., foundation sha256:..., web/css/kebab.
|
|
343
|
+
Do not edit. Change the design in Figma, republish, and run spec-layer pull. */
|
|
344
|
+
|
|
345
|
+
[data-theme="dark"] {
|
|
346
|
+
/* Mapped Colors, Dark */
|
|
347
|
+
--mapped-colors-surface-primary-default: var(--foundation-colors-blue-900);
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
`index.css` holds the header, then one comment and one import per file, in
|
|
352
|
+
resolver order, and no declarations of its own:
|
|
353
|
+
|
|
354
|
+
```css
|
|
355
|
+
/* Generated by spec-layer from library lib_..., foundation sha256:..., web/css/kebab.
|
|
356
|
+
Do not edit. Change the design in Figma, republish, and run spec-layer pull. */
|
|
357
|
+
|
|
358
|
+
/* Foundation */
|
|
359
|
+
@import "./foundation.css";
|
|
360
|
+
/* Mapped Colors, Dark */
|
|
361
|
+
@import "./mapped-colors.dark.css";
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
A set, which has one mode by construction, writes one file named for the
|
|
365
|
+
collection alone, at `:root`. A collection with modes writes one file per
|
|
366
|
+
mode: the default mode's file sits at `:root`, every other mode's file under
|
|
367
|
+
`[data-theme="<mode>"]`. Aliases stay as `var()`. A number whose Figma scopes
|
|
368
|
+
state no unit stays a bare number. Nothing about a mode's name selects a
|
|
369
|
+
media query. To let the OS choose a theme, set that collection's selector to
|
|
370
|
+
`:root` under `modes` and import the mode's file yourself under `@media
|
|
371
|
+
(prefers-color-scheme: dark)`. The CLI never assumes that.
|
|
372
|
+
|
|
373
|
+
The directory is written **in place** at the path in `speclayer.json`,
|
|
374
|
+
outside the managed directory, so your bundler keeps watching it and the diff
|
|
375
|
+
shows up in review. Every file begins with a header naming the library and
|
|
376
|
+
the Foundation's content hash; `pull` owns exactly the files there that begin
|
|
377
|
+
with it, replacing or removing those, ignoring dotfiles, and refusing to run
|
|
378
|
+
when anything else is present. A `speclayer.json` written by CLI 0.6.0 names
|
|
379
|
+
a `.css` file at this path, and `pull` refuses it: set `path` to a directory
|
|
380
|
+
and delete the old file.
|
|
381
|
+
|
|
382
|
+
```json
|
|
383
|
+
{
|
|
384
|
+
"platforms": ["web"],
|
|
385
|
+
"outputs": [
|
|
386
|
+
{ "platform": "web", "format": "css", "path": "tokens", "case": "kebab" }
|
|
387
|
+
]
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
`setup` and `init` write this block from `--platform`, or from what they detect
|
|
392
|
+
at the repository root, so the path is always on record. `pull` writes every
|
|
393
|
+
entry; `"outputs": []` writes none. `case` chooses how derived names are
|
|
394
|
+
spelled: `kebab` (default), `camel`, `pascal`, `snake`, or `constant`. A name
|
|
395
|
+
the designer declared as `code_syntax` in Figma is used verbatim and never
|
|
396
|
+
re-cased. `.speclayer/outputs/web-css.map.json` records every emitted name,
|
|
397
|
+
whether it was declared or derived, and which file declares it; two tokens
|
|
398
|
+
that would share a name are both omitted and listed in `web-css.report.json`.
|
|
399
|
+
|
|
400
|
+
Two collections with modes share one attribute by default, which cannot be
|
|
401
|
+
right for both; the report says so, and `modes` declares a selector per
|
|
402
|
+
collection:
|
|
403
|
+
|
|
404
|
+
```json
|
|
405
|
+
{ "platform": "web", "format": "css", "path": "tokens",
|
|
406
|
+
"modes": { "Density": "[data-density=\"{mode}\"]" } }
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
`root` and `modeSelector` override the defaults `:root` and
|
|
410
|
+
`[data-theme="{mode}"]`; `{mode}` and `{collection}` are replaced by slugs.
|
|
411
|
+
A repository that already builds tokens with Style Dictionary can keep reading
|
|
412
|
+
`.speclayer/tokens/`: the CSS files are a projection of the same record, not a
|
|
413
|
+
second source, so import one or the other.
|
|
414
|
+
|
|
415
|
+
Commit `.speclayer/`, `speclayer.json`, `component-specs/`, and the output
|
|
416
|
+
paths. A repository that would rather regenerate in CI ignores them and runs
|
|
417
|
+
`pull` there; `status` exits `2` when a pull is due.
|
|
418
|
+
|
|
298
419
|
## Exit codes
|
|
299
420
|
|
|
300
421
|
| Code | Meaning |
|
|
@@ -308,5 +429,5 @@ the report are not token files; exclude them from token globs.
|
|
|
308
429
|
|
|
309
430
|
## Requirements
|
|
310
431
|
|
|
311
|
-
Node 22 or newer. Publishing
|
|
312
|
-
|
|
432
|
+
Node 22 or newer. Publishing one Figma file is free; Pro publishes up to ten.
|
|
433
|
+
Pulling needs only the library id and pull key.
|