@atelier-ui/create-workspace 0.2.26 β†’ 0.2.35

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 CHANGED
@@ -1,3 +1,61 @@
1
+ ## 0.2.35 (2026-09-06)
2
+
3
+ ### 🩹 Fixes
4
+
5
+ - **deps:** the manifests the sweep left behind, and one it must not touch ([460bdf9](https://github.com/DominikPieper/atelier-ui/commit/460bdf9))
6
+
7
+ ### ❀️ Thank You
8
+
9
+ - Claude Opus 5 (1M context)
10
+ - Dominik Pieper @DominikPieper
11
+
12
+ ## 0.2.34 (2026-09-05)
13
+
14
+ This was a version bump only for create-workspace to align it with other projects, there were no code changes.
15
+
16
+ ## 0.2.33 (2026-09-05)
17
+
18
+ This was a version bump only for create-workspace to align it with other projects, there were no code changes.
19
+
20
+ ## 0.2.32 (2026-09-05)
21
+
22
+ ### 🩹 Fixes
23
+
24
+ - **preflight:** make the sync claim true instead of just writing it down ([b1b52d9](https://github.com/DominikPieper/atelier-ui/commit/b1b52d9))
25
+ - **preflight:** check the ports of the environment you are actually in ([a2ff354](https://github.com/DominikPieper/atelier-ui/commit/a2ff354))
26
+
27
+ ### ❀️ Thank You
28
+
29
+ - Claude Opus 5 (1M context)
30
+ - Dominik Pieper @DominikPieper
31
+
32
+ ## 0.2.31 (2026-08-29)
33
+
34
+ This was a version bump only for create-workspace to align it with other projects, there were no code changes.
35
+
36
+ ## 0.2.30 (2026-08-29)
37
+
38
+ ### πŸš€ Features
39
+
40
+ - **tokens:** decide the counting rule, then mint the two roles it earns ([93323ec](https://github.com/DominikPieper/atelier-ui/commit/93323ec))
41
+
42
+ ### ❀️ Thank You
43
+
44
+ - Claude Opus 5 (1M context)
45
+ - Dominik Pieper @DominikPieper
46
+
47
+ ## 0.2.29 (2026-08-29)
48
+
49
+ This was a version bump only for create-workspace to align it with other projects, there were no code changes.
50
+
51
+ ## 0.2.28 (2026-08-29)
52
+
53
+ This was a version bump only for create-workspace to align it with other projects, there were no code changes.
54
+
55
+ ## 0.2.27 (2026-08-29)
56
+
57
+ This was a version bump only for create-workspace to align it with other projects, there were no code changes.
58
+
1
59
  ## 0.2.26 (2026-08-29)
2
60
 
3
61
  ### 🩹 Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atelier-ui/create-workspace",
3
- "version": "0.2.26",
3
+ "version": "0.2.35",
4
4
  "description": "Nx preset for Atelier workshop workspaces",
5
5
  "keywords": [
6
6
  "nx",
@@ -21,7 +21,7 @@
21
21
  "types": "./src/index.d.ts",
22
22
  "generators": "./generators.json",
23
23
  "dependencies": {
24
- "@nx/devkit": "23.1.1",
24
+ "@nx/devkit": "23.2.0",
25
25
  "prettier": "^3.0.0",
26
26
  "tslib": "^2.3.0"
27
27
  },
@@ -143,6 +143,23 @@
143
143
  --ui-type-action: var(--ui-font-weight-semibold) var(--ui-font-size-md) /
144
144
  var(--ui-line-height-tight) var(--ui-font-family);
145
145
 
146
+ /* The row roles (ADR-0085). Where `control` is the label ON a control and
147
+ * `action` is the text of a control that acts, these two are the text IN a
148
+ * row: a menu item, an option, a table cell, a checkbox or radio label. They
149
+ * are regular weight, not medium, and they sit at the two default sizes the
150
+ * row ladder uses β€” `row` at md for the standard rung, `row-sm` at sm for the
151
+ * compact one. Same tight leading as the rest of the ladder: a row's height
152
+ * comes from --ui-row-height-*, so its leading must not grow with the text.
153
+ *
154
+ * The ADR-0073 carve-out applies here exactly as it does above: a control's
155
+ * own VALUE text keeps its longhands. `row` describes what the row DISPLAYS,
156
+ * not what an input CONTAINS.
157
+ */
158
+ --ui-type-row: var(--ui-font-weight-normal) var(--ui-font-size-md) /
159
+ var(--ui-line-height-tight) var(--ui-font-family);
160
+ --ui-type-row-sm: var(--ui-font-weight-normal) var(--ui-font-size-sm) /
161
+ var(--ui-line-height-tight) var(--ui-font-family);
162
+
146
163
  /* ── Colours Β· light mode (default) ────────────────────────── */
147
164
 
148
165
  /* Primary β€” Conciso deep teal */
@@ -13,7 +13,7 @@
13
13
 
14
14
  import { execSync, spawnSync } from 'node:child_process';
15
15
  import { createServer } from 'node:net';
16
- import { readFileSync, existsSync } from 'node:fs';
16
+ import { readFileSync, existsSync, statSync } from 'node:fs';
17
17
  import { homedir } from 'node:os';
18
18
  import { fileURLToPath } from 'node:url';
19
19
  import { dirname, join, resolve } from 'node:path';
@@ -155,25 +155,25 @@ async function probeMcp(url) {
155
155
  // 3. On an Atelier Storybook MCP, exercise one real tool call: manifests
156
156
  // are only fetched inside tool calls, and a broken manifest fetch comes
157
157
  // back as HTTP 200 + isError β€” invisible to anything shallower.
158
- if (tools.some((t) => t?.name === 'list-all-documentation')) {
158
+ if (tools.some((t) => t?.name === 'docs-list')) {
159
159
  const call = await mcpPost(
160
160
  url,
161
161
  {
162
162
  jsonrpc: '2.0',
163
163
  id: 3,
164
164
  method: 'tools/call',
165
- params: { name: 'list-all-documentation', arguments: {} },
165
+ params: { name: 'docs-list', arguments: {} },
166
166
  },
167
167
  session,
168
168
  );
169
- if (call.error) return { level: 'broken', detail: `list-all-documentation failed (${call.error})` };
170
- if (call.status !== 200) return { level: 'broken', detail: `list-all-documentation β†’ HTTP ${call.status}` };
169
+ if (call.error) return { level: 'broken', detail: `docs-list failed (${call.error})` };
170
+ if (call.status !== 200) return { level: 'broken', detail: `docs-list β†’ HTTP ${call.status}` };
171
171
  const callMsg = parseJsonRpc(call.contentType, call.text, 3);
172
172
  if (!callMsg?.result || callMsg.result.isError) {
173
173
  const reason =
174
174
  callMsg?.result?.content?.[0]?.text ?? callMsg?.error?.message ?? 'no parseable result';
175
175
  const oneLine = String(reason).replace(/\s+/g, ' ').trim();
176
- return { level: 'broken', detail: `list-all-documentation β†’ ${oneLine.slice(0, 120)}` };
176
+ return { level: 'broken', detail: `docs-list β†’ ${oneLine.slice(0, 120)}` };
177
177
  }
178
178
  }
179
179
 
@@ -324,13 +324,15 @@ async function checkFigmaSetup() {
324
324
  ok('FIGMA_ACCESS_TOKEN', `set (${token.length} chars, REST reads enabled)`);
325
325
  }
326
326
 
327
- async function checkMcpEndpoints() {
327
+ async function checkMcpEndpoints(env) {
328
328
  const endpoints = loadMcpEndpoints();
329
329
  if (endpoints.length === 0) {
330
330
  warn(
331
331
  'MCP endpoints',
332
332
  'none configured in .mcp.json',
333
- 'Scaffold a workspace with `npx create-atelier-ui-workspace`',
333
+ env === 'clone'
334
+ ? 'The clone ships .mcp.json at the repo root with these servers β€” check it was not deleted, renamed, or run from the wrong directory'
335
+ : 'Scaffold a workspace with `npx create-atelier-ui-workspace`',
334
336
  );
335
337
  return;
336
338
  }
@@ -354,15 +356,88 @@ async function checkMcpEndpoints() {
354
356
  }
355
357
  }
356
358
 
357
- async function checkPorts() {
358
- // 4200 = the dev server (every framework serves on the @nx/vite default),
359
- // 6006 = local Storybook. Nothing in the scaffold uses 4201/4202 β€” the old
360
- // multi-framework rig did, and checking them only confused participants.
361
- const ports = [4200, 6006];
362
- for (const p of ports) {
363
- const free = await portFree(p);
364
- if (free) ok(`Port ${p}`, 'free');
365
- else warn(`Port ${p}`, 'in use', `Run \`lsof -ti :${p} | xargs kill\` (macOS/Linux)`);
359
+ // ── Environment detection ───────────────────────────────────────────
360
+ // This file ships identically into two trees: the atelier monorepo that the
361
+ // two-day cohort clones, and the single-framework workspace `create-workspace`
362
+ // scaffolds for everyone else (ADR-0084). `ROOT` sits at the same relative
363
+ // depth (two levels above tools/scripts) in both, so the ports to check
364
+ // differ by tree even though the code is one file β€” the environment logic
365
+ // has to live in here, not as a divergence between the two copies. The two
366
+ // copies are kept byte-identical by `npm run check:preflight`
367
+ // (tools/scripts/sync-preflight.mjs --check) β€” NOT by `check:sync`, which
368
+ // only checks Angular/React/Vue component drift and never looks at
369
+ // `libs/create-workspace/`.
370
+ //
371
+ // Discriminator: both `libs/spec/` (the framework-agnostic spec contract,
372
+ // libs/spec/src/index.ts β€” see CLAUDE.md) and `plan/adr/` (the decision log)
373
+ // are directories that exist only in the source monorepo β€” `create-workspace`'s
374
+ // preset generator provably never writes either one into a scaffolded
375
+ // workspace (it writes a single `workshop-<fw>` app instead; see preset.ts).
376
+ // Requiring both, rather than either alone, is deliberate: a single directory
377
+ // name is something a team could plausibly recreate inside a scaffold they
378
+ // extend (e.g. adding their own `libs/spec` package), and a false "clone"
379
+ // verdict then goes silent β€” checkPorts skips 4200/6006 entirely and reports
380
+ // an all-clear on ports it never looked at. Two thematically-linked,
381
+ // distinctively-named directories are a much smaller false-positive target,
382
+ // at the cost of one extra `statSync` call β€” still two stats, not workspace
383
+ // introspection.
384
+ //
385
+ // This does not eliminate the risk, only shrinks it, and there is no cheap
386
+ // way to shrink it further: a tree that genuinely has both directories for
387
+ // unrelated reasons is indistinguishable from a real clone without deeper
388
+ // introspection than this check is willing to pay for. That residual case
389
+ // gets no separate warning branch β€” a "some checks may be wrong" message
390
+ // with no actionable next step is not worth the extra state. The reported
391
+ // `Environment` line is the mitigation: it prints which verdict was reached
392
+ // so a participant who is not where they expect notices immediately (the
393
+ // motivation for this whole change). Fall back to `scaffold` when neither
394
+ // directory (or only one) is present: the generated workspace is the copy
395
+ // that ships to strangers, so an undetectable or partial-match tree should
396
+ // behave exactly as it did before this change.
397
+ function isDir(path) {
398
+ try {
399
+ return statSync(path).isDirectory();
400
+ } catch {
401
+ return false;
402
+ }
403
+ }
404
+
405
+ function detectEnvironment() {
406
+ const isClone = isDir(resolve(ROOT, 'libs/spec')) && isDir(resolve(ROOT, 'plan/adr'));
407
+ return isClone ? 'clone' : 'scaffold';
408
+ }
409
+
410
+ const PORTS_BY_ENV = {
411
+ // The atelier monorepo clone: the docs app and one Storybook per framework
412
+ // (libs/{angular,react,vue}/project.json's `storybook dev … --port 44xx`,
413
+ // docs/project.json:21). Nothing in the clone runs on 4200/6006.
414
+ clone: [
415
+ { port: 4300, label: 'docs' },
416
+ { port: 4400, label: 'Storybook (angular)' },
417
+ { port: 4401, label: 'Storybook (react)' },
418
+ { port: 4402, label: 'Storybook (vue)' },
419
+ ],
420
+ // The scaffolded workspace: 4200 = the dev server (every framework serves
421
+ // on the @nx/vite default), 6006 = its single local Storybook. Nothing in
422
+ // the scaffold uses 4201/4202 β€” the old multi-framework rig did, and
423
+ // checking them only confused participants.
424
+ scaffold: [
425
+ { port: 4200, label: 'dev server' },
426
+ { port: 6006, label: 'Storybook' },
427
+ ],
428
+ };
429
+
430
+ async function checkPorts(env) {
431
+ ok(
432
+ 'Environment',
433
+ env === 'clone'
434
+ ? 'atelier monorepo clone β€” checking docs (4300) + Storybook (4400/4401/4402)'
435
+ : 'scaffolded workspace β€” checking dev server (4200) + Storybook (6006)',
436
+ );
437
+ for (const { port, label } of PORTS_BY_ENV[env]) {
438
+ const free = await portFree(port);
439
+ if (free) ok(`Port ${port} (${label})`, 'free');
440
+ else warn(`Port ${port} (${label})`, 'in use', `Run \`lsof -ti :${port} | xargs kill\` (macOS/Linux)`);
366
441
  }
367
442
  }
368
443
 
@@ -377,6 +452,8 @@ async function main() {
377
452
  console.log(bold('Atelier UI Preflight'));
378
453
  console.log(dim('Checking your environment for the workshop…'));
379
454
 
455
+ const env = detectEnvironment();
456
+
380
457
  header('Runtime');
381
458
  checkNode();
382
459
  checkNpm();
@@ -389,10 +466,10 @@ async function main() {
389
466
  await checkFigmaSetup();
390
467
 
391
468
  header('MCP reachability');
392
- await checkMcpEndpoints();
469
+ await checkMcpEndpoints(env);
393
470
 
394
471
  header('Local ports');
395
- await checkPorts();
472
+ await checkPorts(env);
396
473
 
397
474
  // Summary
398
475
  const failures = results.filter((r) => r.level === 'fail').length;
@@ -124,9 +124,9 @@ function presetGenerator(tree, options) {
124
124
  const mcpSection = frameworks
125
125
  .map((f) => `### \`storybook-${f}\` MCP
126
126
  Before using any component:
127
- 1. Call \`list-all-documentation\` to get valid component IDs
128
- 2. Call \`get-documentation\` with the ID β€” never invent props
129
- 3. Call \`get-documentation-for-story\` for a specific variant
127
+ 1. Call \`docs-list\` to get valid component IDs
128
+ 2. Call \`docs-show\` with the ID β€” never invent props
129
+ 3. Call \`docs-show-story\` for a specific variant
130
130
  4. Do not use a component that is not in the docs`)
131
131
  .join('\n\n');
132
132
  tree.write('CLAUDE.md', `# Atelier Workshop