@motion-proto/live-tokens 0.75.0 → 0.76.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 +35 -0
- package/README.md +14 -4
- package/bin/cli.mjs +12 -58
- package/bin/setup-claude.mjs +110 -0
- package/package.json +1 -1
- package/template/_gitignore +4 -0
- package/template/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.76.0 — The skills directory matches the release
|
|
4
|
+
|
|
5
|
+
### Changed (breaking)
|
|
6
|
+
|
|
7
|
+
- **`setup-claude --force` now makes `.claude/skills/` match the release.** It
|
|
8
|
+
replaced each bundled skill by merging over it, which left two kinds of stale
|
|
9
|
+
text behind: a skill a release renamed kept its old directory, where it
|
|
10
|
+
shadowed the replacement, and a reference file a release dropped survived
|
|
11
|
+
inside a skill that was otherwise current. Both are invisible until a model
|
|
12
|
+
reads them. A forced run now replaces each bundled directory outright and
|
|
13
|
+
removes any `live-tokens-` skill the release no longer ships, naming each one
|
|
14
|
+
it removed. A skill under any other name is the project's own and is never
|
|
15
|
+
touched, and an unforced run still deletes nothing. 0.75.0's rename of
|
|
16
|
+
`live-tokens-build-page` to `live-tokens-create-page` is the case that
|
|
17
|
+
prompted this; the changelog then told a reader to delete the directory by
|
|
18
|
+
hand.
|
|
19
|
+
|
|
20
|
+
Before a forced run, copy any bundled skill you edited in place to a name of
|
|
21
|
+
your own. A `live-tokens-` directory this release does not ship is deleted
|
|
22
|
+
without a prompt, and one it does ship is replaced rather than merged.
|
|
23
|
+
|
|
24
|
+
`setup-claude` moves out of `bin/cli.mjs` into `bin/setup-claude.mjs` as
|
|
25
|
+
`runSetupClaude` and `formatSetupResult`, matching `create.mjs`, and gains
|
|
26
|
+
`bin/setup-claude.test.ts`.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- **The `create` template installs the skills from `postinstall` and gitignores
|
|
31
|
+
them.** A scaffolded project no longer commits nine skill directories the
|
|
32
|
+
package owns, so a version bump stops showing twenty changed files that
|
|
33
|
+
nobody wrote. The skills instead refresh on every `npm install`. The
|
|
34
|
+
postinstall ends in `|| exit 0`: `setup-claude` refuses to run on Windows,
|
|
35
|
+
and a skills copy must never fail an install. A project created before this
|
|
36
|
+
release adds the two lines itself; the README carries them.
|
|
37
|
+
|
|
3
38
|
## 0.75.0 — A component passes when the tests pass
|
|
4
39
|
|
|
5
40
|
### Added
|
package/README.md
CHANGED
|
@@ -322,7 +322,7 @@ npx @motion-proto/live-tokens <command>
|
|
|
322
322
|
| Command | What it does |
|
|
323
323
|
|---|---|
|
|
324
324
|
| `create <dir> [--force]` | Scaffold a new Svelte + Vite app wired up with live-tokens. |
|
|
325
|
-
| `setup-claude [--force]` | Install the bundled Claude Code skills into `./.claude/skills
|
|
325
|
+
| `setup-claude [--force]` | Install the bundled Claude Code skills into `./.claude/skills/`; `--force` makes the directory match this release. |
|
|
326
326
|
| `components [id] [--json]` | List every component the project has, shipped and its own, with the props each takes; with an id, its props, variants, tokens, and defaults. |
|
|
327
327
|
| `tokens [--scale <name>] [--json]` | List every design token the project's `tokens.css` declares, by scale, with its value. |
|
|
328
328
|
| `report [--json]` | The project as facts: pending migrations, tokens each component reads, which page renders which component, and both checkers' findings by rule. Always exits 0. |
|
|
@@ -346,10 +346,20 @@ The package bundles nine Claude Code skills. They encode the conventions this RE
|
|
|
346
346
|
npx @motion-proto/live-tokens setup-claude
|
|
347
347
|
```
|
|
348
348
|
|
|
349
|
-
This copies every bundled skill into `./.claude/skills/` in the current directory
|
|
349
|
+
This copies every bundled skill into `./.claude/skills/` in the current directory, leaving any that already exist. macOS and Linux only.
|
|
350
350
|
|
|
351
|
-
|
|
352
|
-
|
|
351
|
+
Re-run it with `--force` after upgrading. That makes the directory match the release: each bundled skill is replaced outright, so a reference file the release dropped goes with it, and a `live-tokens-` skill the release no longer ships is removed. A skill under any other name is the project's own and is never touched. Without `--force` nothing is deleted and an existing skill is left alone, so a renamed skill keeps shadowing its replacement until a forced run clears it.
|
|
352
|
+
|
|
353
|
+
A project created with `create` runs the forced form from `postinstall` and gitignores the copy, so the skills track the installed version with nothing to commit. To do the same in an existing project:
|
|
354
|
+
|
|
355
|
+
```jsonc
|
|
356
|
+
// package.json
|
|
357
|
+
"postinstall": "live-tokens setup-claude --force || exit 0"
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
```
|
|
361
|
+
# .gitignore
|
|
362
|
+
.claude/skills/live-tokens-*/
|
|
353
363
|
```
|
|
354
364
|
|
|
355
365
|
### `live-tokens-pick-component`
|
package/bin/cli.mjs
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// save-theme <name> Compose the live state into themes/<slug>.json and open it.
|
|
15
15
|
// migrate [...] Reconcile tokens.css, the data tree, and route references.
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { writeSync } from 'node:fs';
|
|
18
18
|
import { dirname, join, resolve } from 'node:path';
|
|
19
19
|
import { fileURLToPath } from 'node:url';
|
|
20
20
|
import process from 'node:process';
|
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
} from './migrate.mjs';
|
|
41
41
|
import { runMigrateRoutes, formatRouteResult } from './migrate-routes.mjs';
|
|
42
42
|
import { runCreate, formatCreateResult } from './create.mjs';
|
|
43
|
+
import { runSetupClaude, formatSetupResult } from './setup-claude.mjs';
|
|
43
44
|
import { runSetColors, formatSetColorsResult } from './set-colors.mjs';
|
|
44
45
|
import { runSetGeometry, formatSetGeometryResult } from './set-geometry.mjs';
|
|
45
46
|
import { runSetType, formatSetTypeResult } from './set-type.mjs';
|
|
@@ -407,61 +408,14 @@ if (process.platform === 'win32') {
|
|
|
407
408
|
fail('setup-claude is macOS/Linux only.');
|
|
408
409
|
}
|
|
409
410
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
);
|
|
421
|
-
|
|
422
|
-
if (skills.length === 0) {
|
|
423
|
-
fail('No bundled skills to install.');
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
const destSkills = join(process.cwd(), '.claude', 'skills');
|
|
427
|
-
mkdirSync(destSkills, { recursive: true });
|
|
428
|
-
|
|
429
|
-
let installed = 0;
|
|
430
|
-
let skipped = 0;
|
|
431
|
-
for (const skill of skills) {
|
|
432
|
-
const src = join(srcSkills, skill);
|
|
433
|
-
const dest = join(destSkills, skill);
|
|
434
|
-
if (existsSync(dest) && !force) {
|
|
435
|
-
console.log(` skip ${skill} (already exists; pass --force to overwrite)`);
|
|
436
|
-
skipped++;
|
|
437
|
-
continue;
|
|
438
|
-
}
|
|
439
|
-
cpSync(src, dest, { recursive: true });
|
|
440
|
-
console.log(` ok ${skill}`);
|
|
441
|
-
installed++;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
console.log(`\n${installed} installed, ${skipped} skipped, in ${destSkills}`);
|
|
445
|
-
|
|
446
|
-
const SAMPLE_PROMPTS = {
|
|
447
|
-
'live-tokens-create-page': 'build a pricing page using live-tokens components',
|
|
448
|
-
'live-tokens-pick-component': "what's the difference between TabBar and SegmentedControl?",
|
|
449
|
-
'live-tokens-create-component': 'author a new Toggle component for my live-tokens project',
|
|
450
|
-
'live-tokens-create-theme': 'make me a bright and cheerful theme',
|
|
451
|
-
'live-tokens-set-colors': 'give me a cooler palette, same fonts',
|
|
452
|
-
'live-tokens-set-type': 'pair some fonts for this theme',
|
|
453
|
-
'live-tokens-set-geometry': 'make the buttons pill shaped',
|
|
454
|
-
'live-tokens-fix-findings': 'make check:design pass',
|
|
455
|
-
'live-tokens-check-compliance': 'check this project against the design system',
|
|
456
|
-
};
|
|
457
|
-
|
|
458
|
-
const installedSamples = skills
|
|
459
|
-
.map((s) => SAMPLE_PROMPTS[s] && [s, SAMPLE_PROMPTS[s]])
|
|
460
|
-
.filter(Boolean);
|
|
461
|
-
|
|
462
|
-
if (installedSamples.length > 0) {
|
|
463
|
-
console.log(`\nIn Claude Code, prompts like these auto-trigger the matching skill:`);
|
|
464
|
-
for (const [skill, prompt] of installedSamples) {
|
|
465
|
-
console.log(` "${prompt}"\n ${skill}`);
|
|
466
|
-
}
|
|
411
|
+
try {
|
|
412
|
+
const result = runSetupClaude({
|
|
413
|
+
pkgRoot,
|
|
414
|
+
cwd: process.cwd(),
|
|
415
|
+
force: rest.includes('--force'),
|
|
416
|
+
});
|
|
417
|
+
console.log(formatSetupResult(result));
|
|
418
|
+
process.exit(0);
|
|
419
|
+
} catch (err) {
|
|
420
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
467
421
|
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// Installs the bundled Claude Code skills into a project's ./.claude/skills/.
|
|
2
|
+
//
|
|
3
|
+
// Claude Code discovers skills only under .claude/skills/, never inside
|
|
4
|
+
// node_modules, so the package's own copy has to be duplicated into each
|
|
5
|
+
// project that wants them. That makes this command the only thing keeping the
|
|
6
|
+
// two trees in step, and `--force` has to mean "make the destination match this
|
|
7
|
+
// release" rather than "write over the files I happen to ship today":
|
|
8
|
+
//
|
|
9
|
+
// - a skill this release renamed leaves its old directory behind, where it
|
|
10
|
+
// shadows the new one with stale instructions;
|
|
11
|
+
// - a reference file this release dropped survives inside a skill that is
|
|
12
|
+
// otherwise current, because cpSync merges into an existing directory.
|
|
13
|
+
//
|
|
14
|
+
// Both are invisible until a model reads the stale text, so --force replaces
|
|
15
|
+
// each directory outright and prunes the ones this release no longer ships.
|
|
16
|
+
|
|
17
|
+
import { cpSync, existsSync, mkdirSync, readdirSync, rmSync, statSync } from 'node:fs';
|
|
18
|
+
import { join } from 'node:path';
|
|
19
|
+
|
|
20
|
+
// The package owns this prefix in a project's skills directory. A skill without
|
|
21
|
+
// it is the project's own and is never touched, whatever the flags say.
|
|
22
|
+
const SKILL_PREFIX = 'live-tokens-';
|
|
23
|
+
|
|
24
|
+
const SAMPLE_PROMPTS = {
|
|
25
|
+
'live-tokens-create-page': 'build a pricing page using live-tokens components',
|
|
26
|
+
'live-tokens-pick-component': "what's the difference between TabBar and SegmentedControl?",
|
|
27
|
+
'live-tokens-create-component': 'author a new Toggle component for my live-tokens project',
|
|
28
|
+
'live-tokens-create-theme': 'make me a bright and cheerful theme',
|
|
29
|
+
'live-tokens-set-colors': 'give me a cooler palette, same fonts',
|
|
30
|
+
'live-tokens-set-type': 'pair some fonts for this theme',
|
|
31
|
+
'live-tokens-set-geometry': 'make the buttons pill shaped',
|
|
32
|
+
'live-tokens-fix-findings': 'make check:design pass',
|
|
33
|
+
'live-tokens-check-compliance': 'check this project against the design system',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
function directoriesIn(dir) {
|
|
37
|
+
if (!existsSync(dir)) return [];
|
|
38
|
+
return readdirSync(dir).filter((name) => statSync(join(dir, name)).isDirectory());
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function runSetupClaude({ pkgRoot, cwd, force = false }) {
|
|
42
|
+
const srcSkills = join(pkgRoot, '.claude', 'skills');
|
|
43
|
+
|
|
44
|
+
if (!existsSync(srcSkills)) {
|
|
45
|
+
throw new Error(`No bundled skills found at ${srcSkills}. Is the package installed correctly?`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const shipped = directoriesIn(srcSkills);
|
|
49
|
+
if (shipped.length === 0) {
|
|
50
|
+
throw new Error('No bundled skills to install.');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const destSkills = join(cwd, '.claude', 'skills');
|
|
54
|
+
mkdirSync(destSkills, { recursive: true });
|
|
55
|
+
|
|
56
|
+
const installed = [];
|
|
57
|
+
const skipped = [];
|
|
58
|
+
const removed = [];
|
|
59
|
+
|
|
60
|
+
for (const skill of shipped) {
|
|
61
|
+
const dest = join(destSkills, skill);
|
|
62
|
+
if (existsSync(dest) && !force) {
|
|
63
|
+
skipped.push(skill);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
// Replace rather than merge: a file this release dropped must not survive
|
|
67
|
+
// inside a directory that otherwise looks current.
|
|
68
|
+
rmSync(dest, { recursive: true, force: true });
|
|
69
|
+
cpSync(join(srcSkills, skill), dest, { recursive: true });
|
|
70
|
+
installed.push(skill);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (force) {
|
|
74
|
+
for (const name of directoriesIn(destSkills)) {
|
|
75
|
+
if (!name.startsWith(SKILL_PREFIX) || shipped.includes(name)) continue;
|
|
76
|
+
rmSync(join(destSkills, name), { recursive: true, force: true });
|
|
77
|
+
removed.push(name);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return { installed, skipped, removed, destSkills, shipped };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function formatSetupResult({ installed, skipped, removed, destSkills, shipped }) {
|
|
85
|
+
const lines = [];
|
|
86
|
+
|
|
87
|
+
for (const skill of shipped) {
|
|
88
|
+
if (installed.includes(skill)) lines.push(` ok ${skill}`);
|
|
89
|
+
else lines.push(` skip ${skill} (already exists; pass --force to overwrite)`);
|
|
90
|
+
}
|
|
91
|
+
for (const skill of removed) {
|
|
92
|
+
lines.push(` gone ${skill} (this release no longer ships it; removed)`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const counts = [`${installed.length} installed`, `${skipped.length} skipped`];
|
|
96
|
+
if (removed.length > 0) counts.push(`${removed.length} removed`);
|
|
97
|
+
lines.push(`\n${counts.join(', ')}, in ${destSkills}`);
|
|
98
|
+
|
|
99
|
+
if (skipped.length > 0 && removed.length === 0) {
|
|
100
|
+
lines.push(`\nRe-run with --force to bring every skill up to this release.`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const samples = shipped.map((s) => SAMPLE_PROMPTS[s] && [s, SAMPLE_PROMPTS[s]]).filter(Boolean);
|
|
104
|
+
if (samples.length > 0) {
|
|
105
|
+
lines.push(`\nIn Claude Code, prompts like these auto-trigger the matching skill:`);
|
|
106
|
+
for (const [skill, prompt] of samples) lines.push(` "${prompt}"\n ${skill}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return lines.join('\n');
|
|
110
|
+
}
|
package/package.json
CHANGED
package/template/_gitignore
CHANGED
|
@@ -7,6 +7,10 @@ dist-ssr
|
|
|
7
7
|
playwright-report/
|
|
8
8
|
test-results/
|
|
9
9
|
|
|
10
|
+
# The live-tokens skills are a copy of what the package ships; postinstall
|
|
11
|
+
# refreshes them, so they are the package's to version, not this project's.
|
|
12
|
+
.claude/skills/live-tokens-*/
|
|
13
|
+
|
|
10
14
|
.DS_Store
|
|
11
15
|
.vscode/*
|
|
12
16
|
!.vscode/extensions.json
|
package/template/package.json
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"preview": "vite preview",
|
|
10
10
|
"check": "svelte-check --tsconfig ./tsconfig.json",
|
|
11
11
|
"check:design": "live-tokens check-page && live-tokens check-component",
|
|
12
|
-
"test:design": "live-tokens check-component --tests"
|
|
12
|
+
"test:design": "live-tokens check-component --tests",
|
|
13
|
+
"postinstall": "live-tokens setup-claude --force || exit 0"
|
|
13
14
|
},
|
|
14
15
|
"dependencies": {
|
|
15
16
|
"@motion-proto/live-tokens": "__LT_VERSION__"
|