spec-layer 0.6.0 → 0.8.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.
Files changed (3) hide show
  1. package/README.md +83 -42
  2. package/dist/cli.js +501 -151
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -60,7 +60,12 @@ against them. The Foundation landing under `tokens/` as Design Tokens Format
60
60
  Module 2025.10 files, rather than `ai/foundation.yaml`, needs 0.4.0 or later.
61
61
  `tools` and `skill` need 0.5.0 or later. `components/` in place of
62
62
  `ai/components/`, `path` in place of `aiPath` in the manifest, and the
63
- `outputs` block need 0.6.0 or later.
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. The `census` and `config_hash` blocks inside
66
+ `resolver.json`, and the `transform` and `resolved` fields in
67
+ `spec-layer.meta.json`, need 0.8.0 or later; an earlier version pulls the same
68
+ files without those fields.
64
69
 
65
70
  ## Commands
66
71
 
@@ -146,7 +151,7 @@ carry no key and are meant to be committed with the rest of the repository.
146
151
  ## Pulling part of a library
147
152
 
148
153
  By default `pull` writes the Foundation and every documented component. When
149
- your repo implements only some of them, narrow what lands in `components/`:
154
+ your repo implements only some of them, narrow what lands in `component-specs/`:
150
155
 
151
156
  ```bash
152
157
  npx spec-layer pull --only foundation # tokens and styles, no components
@@ -177,7 +182,7 @@ That stores an `include` block in `speclayer.json`:
177
182
  Selection flags on `pull` replace the stored selection for that run; they are
178
183
  never merged with it.
179
184
 
180
- The selection only decides which `components/` files are written. `bundle.json`
185
+ The selection only decides which `component-specs/` files are written. `bundle.json`
181
186
  always holds the whole library, so `list` and `show` can answer for any
182
187
  artifact, written or not, and `status` compares one hash.
183
188
 
@@ -244,7 +249,7 @@ matter what the ignore rules say.
244
249
  ```text
245
250
  .speclayer/
246
251
  bundle.json the published bundle, verbatim
247
- manifest.json every artifact indexed by content hash and path, plus the selection and outputs
252
+ manifest.json every artifact indexed by content hash and path, plus the selection, outputs, and componentSpecsDir
248
253
  tokens/ the Foundation as Design Tokens Format Module 2025.10 files
249
254
  <collection>.<mode>.json one file per collection and mode, rooted at the collection name
250
255
  styles.typography.json text styles as typography composites (when present)
@@ -252,19 +257,22 @@ matter what the ignore rules say.
252
257
  resolver.json Design Tokens Resolver Module 2025.10: sets, modifiers, order
253
258
  spec-layer.meta.json Figma ids, scopes, code syntax, publication, keyed by DTCG path
254
259
  report.json what DTCG could not express, with reasons and stable ids
255
- components/<name>.yaml one file per selected component
256
260
  outputs/
257
- web-css.map.json DTCG path -> CSS custom property, with where the name came from
258
- web-css.report.json what the CSS file could not express
259
- spec-layer/
260
- tokens.css the web token file, written in place at outputs[].path
261
+ web-css.map.json DTCG path -> CSS custom property, where the name came from, and which file declares it
262
+ web-css.report.json what the CSS files could not express
263
+ component-specs/
264
+ <name>.yaml one brief per selected component, byte-identical to Copy for AI
265
+ tokens/
266
+ index.css imports every file below, in resolver order
267
+ <collection>.css one file per single-mode collection, at :root
268
+ <collection>.<mode>.css one file per mode of a multi-mode collection; the default at :root
261
269
  ```
262
270
 
263
- Point your agent at `.speclayer/components/` and `.speclayer/tokens/`, and
264
- import the token file for your platform from where `outputs` puts it. The
265
- component YAML is the same compact form the plugin's **Copy for AI** puts on
266
- your clipboard; `bundle.json` additionally holds the full canonical artifacts
267
- if you need them.
271
+ Point your agent at `component-specs/` and `.speclayer/tokens/`, and import
272
+ `tokens/index.css` from the platform's root stylesheet. The component YAML is
273
+ the same compact form the plugin's **Copy for AI** puts on your clipboard;
274
+ `bundle.json` additionally holds the full canonical artifacts if you need
275
+ them.
268
276
 
269
277
  In `manifest.json`, an artifact the selection left unwritten has `"path":
270
278
  null`. A manifest from CLI 0.1.0 has no `selection` field and means
@@ -275,12 +283,19 @@ interrupted pull never leaves a half-written directory. `pull` refuses an
275
283
  output directory that is the current directory, a parent of it, or an existing
276
284
  non-empty directory it did not write, since the swap replaces that directory.
277
285
 
286
+ `component-specs/` and `tokens/` are written in place, not swapped. `pull`
287
+ owns exactly the files there that begin with its marker (the CSS header, or
288
+ the brief's opening `spec_layer:` lines): it replaces or removes those,
289
+ ignores dotfiles, and refuses to run when anything else is present. A
290
+ repository that already uses a path can set `componentSpecsDir` or
291
+ `outputs[].path`.
292
+
278
293
  When nothing changed since the last pull with the same selection, `pull`
279
294
  prints `Already up to date` and writes nothing. Every republish stamps a new
280
295
  export id and time into the canonical artifacts, so `bundle.json` and
281
296
  `manifest.json` change on each republish even when the content did not. The
282
- `components/` YAML files, the `tokens/` files, and the content hashes stay
283
- stable.
297
+ `component-specs/` YAML files, the `.speclayer/tokens/` files, and the
298
+ `tokens/` CSS stay stable.
284
299
 
285
300
  ## Configuring the token output
286
301
 
@@ -309,19 +324,26 @@ the report are not token files; exclude them from token globs.
309
324
 
310
325
  ## Token files for your code
311
326
 
312
- `pull` also writes a file your build compiles, one per platform output. For
313
- the web that is a CSS file of custom properties. The file is written only
314
- when that pull writes the Foundation; with `--only components` it is left
315
- exactly as it was, and `list` shows it as `not written`.
327
+ `pull` also writes a `tokens/` directory at `outputs[].path`: one CSS file
328
+ per collection and mode plus `index.css`, one per platform output. The
329
+ directory is written only when that pull writes the Foundation; with
330
+ `--only components` it is left exactly as it was, and `list` shows it as
331
+ `not written`.
316
332
 
317
333
  ```css
334
+ /* Generated by spec-layer from library lib_..., foundation sha256:..., web/css/kebab.
335
+ Do not edit. Change the design in Figma, republish, and run spec-layer pull. */
336
+
318
337
  :root {
319
338
  /* Foundation */
320
339
  --foundation-colors-blue-500: #2e72d1;
321
340
  --foundation-spacing-200: 8px;
322
- /* Mapped Colors, Light */
323
- --mapped-colors-surface-primary-default: var(--foundation-colors-blue-500);
324
341
  }
342
+ ```
343
+
344
+ ```css
345
+ /* Generated by spec-layer from library lib_..., foundation sha256:..., web/css/kebab.
346
+ Do not edit. Change the design in Figma, republish, and run spec-layer pull. */
325
347
 
326
348
  [data-theme="dark"] {
327
349
  /* Mapped Colors, Dark */
@@ -329,23 +351,42 @@ exactly as it was, and `list` shows it as `not written`.
329
351
  }
330
352
  ```
331
353
 
332
- Sets and every collection's default mode sit at `:root`; every other mode is
333
- a block under `[data-theme="<mode>"]`. Aliases stay as `var()`. A number whose
334
- Figma scopes state no unit stays a bare number. Nothing about a mode's name
335
- selects a media query; wire `data-theme` to `prefers-color-scheme` yourself if
336
- the OS should choose.
354
+ `index.css` holds the header, then one comment and one import per file, in
355
+ resolver order, and no declarations of its own:
356
+
357
+ ```css
358
+ /* Generated by spec-layer from library lib_..., foundation sha256:..., web/css/kebab.
359
+ Do not edit. Change the design in Figma, republish, and run spec-layer pull. */
360
+
361
+ /* Foundation */
362
+ @import "./foundation.css";
363
+ /* Mapped Colors, Dark */
364
+ @import "./mapped-colors.dark.css";
365
+ ```
337
366
 
338
- The file is written **in place** at the path in `speclayer.json`, outside the
339
- managed directory, so your bundler keeps watching it and the diff shows up in
340
- review. It begins with a header naming the library and the Foundation's
341
- content hash, and `pull` refuses to overwrite a file at that path that lacks
342
- the header.
367
+ A set, which has one mode by construction, writes one file named for the
368
+ collection alone, at `:root`. A collection with modes writes one file per
369
+ mode: the default mode's file sits at `:root`, every other mode's file under
370
+ `[data-theme="<mode>"]`. Aliases stay as `var()`. A number whose Figma scopes
371
+ state no unit stays a bare number. Nothing about a mode's name selects a
372
+ media query. To let the OS choose a theme, set that collection's selector to
373
+ `:root` under `modes` and import the mode's file yourself under `@media
374
+ (prefers-color-scheme: dark)`. The CLI never assumes that.
375
+
376
+ The directory is written **in place** at the path in `speclayer.json`,
377
+ outside the managed directory, so your bundler keeps watching it and the diff
378
+ shows up in review. Every file begins with a header naming the library and
379
+ the Foundation's content hash; `pull` owns exactly the files there that begin
380
+ with it, replacing or removing those, ignoring dotfiles, and refusing to run
381
+ when anything else is present. A `speclayer.json` written by CLI 0.6.0 names
382
+ a `.css` file at this path, and `pull` refuses it: set `path` to a directory
383
+ and delete the old file.
343
384
 
344
385
  ```json
345
386
  {
346
387
  "platforms": ["web"],
347
388
  "outputs": [
348
- { "platform": "web", "format": "css", "path": "spec-layer/tokens.css", "case": "kebab" }
389
+ { "platform": "web", "format": "css", "path": "tokens", "case": "kebab" }
349
390
  ]
350
391
  }
351
392
  ```
@@ -355,28 +396,28 @@ at the repository root, so the path is always on record. `pull` writes every
355
396
  entry; `"outputs": []` writes none. `case` chooses how derived names are
356
397
  spelled: `kebab` (default), `camel`, `pascal`, `snake`, or `constant`. A name
357
398
  the designer declared as `code_syntax` in Figma is used verbatim and never
358
- re-cased. `.speclayer/outputs/web-css.map.json` records every emitted name and
359
- whether it was declared or derived; two tokens that would share a name are
360
- both omitted and listed in `web-css.report.json`.
399
+ re-cased. `.speclayer/outputs/web-css.map.json` records every emitted name,
400
+ whether it was declared or derived, and which file declares it; two tokens
401
+ that would share a name are both omitted and listed in `web-css.report.json`.
361
402
 
362
403
  Two collections with modes share one attribute by default, which cannot be
363
404
  right for both; the report says so, and `modes` declares a selector per
364
405
  collection:
365
406
 
366
407
  ```json
367
- { "platform": "web", "format": "css", "path": "spec-layer/tokens.css",
408
+ { "platform": "web", "format": "css", "path": "tokens",
368
409
  "modes": { "Density": "[data-density=\"{mode}\"]" } }
369
410
  ```
370
411
 
371
412
  `root` and `modeSelector` override the defaults `:root` and
372
413
  `[data-theme="{mode}"]`; `{mode}` and `{collection}` are replaced by slugs.
373
414
  A repository that already builds tokens with Style Dictionary can keep reading
374
- `tokens/`: the CSS file is a projection of the same files, not a second
375
- source, so import one or the other.
415
+ `.speclayer/tokens/`: the CSS files are a projection of the same record, not a
416
+ second source, so import one or the other.
376
417
 
377
- Commit `.speclayer/`, `speclayer.json`, and the output paths. A repository
378
- that would rather regenerate in CI ignores them and runs `pull` there;
379
- `status` exits `2` when a pull is due.
418
+ Commit `.speclayer/`, `speclayer.json`, `component-specs/`, and the output
419
+ paths. A repository that would rather regenerate in CI ignores them and runs
420
+ `pull` there; `status` exits `2` when a pull is due.
380
421
 
381
422
  ## Exit codes
382
423