synthesisui 0.16.239 → 0.16.241

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.
@@ -417,6 +417,7 @@ sxSpacing) {
417
417
  (kind === "headless" || kind === "motion" || kind === "icon")) {
418
418
  node_.from = measured.from;
419
419
  node_.refName = measured.tag;
420
+ // a versão vem do apanhador geral abaixo - um lugar só, depois de todos os ramos
420
421
  }
421
422
  }
422
423
  /**
@@ -431,6 +432,20 @@ sxSpacing) {
431
432
  if (measured?.motionProps)
432
433
  node_.motionProps = measured.motionProps;
433
434
  }
435
+ /**
436
+ * TODO NÓ COM PACOTE LEVA A VERSÃO - o apanhador geral, depois de todos os ramos.
437
+ *
438
+ * A costura já falhou QUATRO vezes ramo a ramo (frontier-kind, import 09/08, o ramo de
439
+ * fronteira, o ramo icon/motion), porque cada caminho novo que carimba `from` esquecia de
440
+ * carimbar a versão. Medido no caminho DERIVADO em 16/08: base-ui com 1 de 23 nós
441
+ * versionados e lucide com 11 de 45 - os nós nasciam no `anatomyFromSketch` com `from` e
442
+ * sem versão, e nenhum ramo daqui os alcançava. Um lugar só, depois de todos.
443
+ */
444
+ if (node_.from && !node_.version) {
445
+ const v = deps?.[packageRoot(String(node_.from))] ?? deps?.[String(node_.from)];
446
+ if (v)
447
+ node_.version = v;
448
+ }
434
449
  // The NAME is what carries the styles, so it is claimed even when nothing
435
450
  // resolved: the tree triggers on presence, and structure is most of the
436
451
  // value. A node with no name is pure structure and that is legitimate.
@@ -36,7 +36,7 @@ import { frontierKind, packageRoot } from "../frontier-kind.js";
36
36
  import { withLibraryStructure } from "../library-structure.js";
37
37
  import { body, paint, section } from "../output.js";
38
38
  import { phase, startProgress } from "../progress.js";
39
- import { detectStack, resolveDeps } from "../stack.js";
39
+ import { detectStack, resolveDeps, stackVersions } from "../stack.js";
40
40
  import { walk, walkAll } from "./doctor.js";
41
41
  /**
42
42
  * How many distinct values travel, PER KIND.
@@ -1739,18 +1739,23 @@ export async function takeCensus(root, opts) {
1739
1739
  }));
1740
1740
  const pkgRaw = await readFile(join(root, "package.json"), "utf8").catch(() => null);
1741
1741
  let name = null;
1742
- /** Their pinned ranges, so a library signal cites the version rather than going stale. */
1743
- let versions = {};
1744
1742
  if (pkgRaw) {
1745
1743
  try {
1746
- const pkg = JSON.parse(pkgRaw);
1747
- name = pkg.name ?? null;
1748
- versions = { ...pkg.devDependencies, ...pkg.dependencies };
1744
+ name = JSON.parse(pkgRaw).name ?? null;
1749
1745
  }
1750
1746
  catch {
1751
1747
  // an unreadable package.json costs the name, not the run
1752
1748
  }
1753
1749
  }
1750
+ /**
1751
+ * Their pinned ranges, so a library signal cites the version rather than going stale.
1752
+ *
1753
+ * PELO RESOLVEDOR, nunca pela leitura crua de um nível: com `--scope packages/ui` este `root` é a
1754
+ * pasta do escopo, cujo manifesto tem ZERO deps - e 15 de 15 bibliotecas do censo vivo saíam sem
1755
+ * versão (medido 16/08). `resolveDeps` sobe até a raiz do workspace e anda para os irmãos que os
1756
+ * `workspaces` declaram, que é onde recharts/chakra/mui realmente moram.
1757
+ */
1758
+ const versions = await resolveDeps(root).catch(() => ({}));
1754
1759
  finishSignals(signals, importTally, versions);
1755
1760
  // Every name their CSS defines, from the same harvest the token table came
1756
1761
  // from - so a reference is judged against what actually exists, not against
@@ -1862,7 +1867,13 @@ export async function takeCensus(root, opts) {
1862
1867
  const classStyle = detectClassStyle(sources);
1863
1868
  return {
1864
1869
  census: 1,
1865
- project: { name, stack: await detectStack(root) },
1870
+ project: {
1871
+ name,
1872
+ stack: await detectStack(root),
1873
+ ...(Object.keys(stackVersions(versions)).length > 0
1874
+ ? { versions: stackVersions(versions) }
1875
+ : {}),
1876
+ },
1866
1877
  declared: Object.fromEntries(table.byName),
1867
1878
  ...(Object.keys(keyframes).length > 0 ? { keyframes } : {}),
1868
1879
  ...(animations.size > 0 ? { animations: [...animations].sort() } : {}),
@@ -89,7 +89,18 @@
89
89
  * "YOUR tokens, under YOUR names"). É a fatia de CLI da decisão de 16/08: toda superfície fala a
90
90
  * língua do cliente.
91
91
  */
92
- export const MATERIALISER_SINCE = "0.16.239";
92
+ /**
93
+ * 0.16.239 -> 0.16.241 em 16/08, e é SIM: o arquivo que cai na pasta dele muda de CONDUTA, não de
94
+ * bytes decorativos. O stub de `sui-import-ds` respondia ao 401 do playbook com "run
95
+ * `npx synthesisui login` and call it again" - o que faz o agente parar a conversa e cobrar uma conta
96
+ * antes de mostrar um único número do repo dele. O `import --dry` nunca precisou de conta (ele
97
+ * retorna antes de ler credenciais), então o stub novo manda MEDIR primeiro, ler o censo de volta nos
98
+ * nomes dele, e só então pedir o login - e, para o MCP ausente, exigir sessão NOVA em vez de sugerir
99
+ * uma nova tentativa que não pode funcionar.
100
+ *
101
+ * Quem instalou antes desta versão tem o stub que estanca: o `upgrade`/`connect` é o que alcança.
102
+ */
103
+ export const MATERIALISER_SINCE = "0.16.241";
93
104
  /**
94
105
  * A ÚLTIMA VERSÃO EM QUE O QUE O HOOK RODA MUDOU.
95
106
  *
@@ -184,8 +195,14 @@ export const CHECKER_SINCE = "0.16.232";
184
195
  * comparação com UM literal, e a divisão do motivo as tirou dele em silêncio
185
196
  *
186
197
  * Um censo medido antes disto carrega as duas leituras erradas.
198
+ *
199
+ * 0.16.240 a VERSÃO passa a viajar (fase B1, 16/08): `project.versions` (frameworks pinados),
200
+ * 15 de 15 `signals.libraries` com versão (o leitor de manifesto lia o package.json
201
+ * do ESCOPO, que tinha zero deps), e todo nó de fronteira com pacote carimba o range
202
+ * (era 53% no melhor caminho; o derivado dava 38%). Um censo medido antes disto não
203
+ * tem versão nenhuma - e o mapa de blueprints por versão não tem o que consultar.
187
204
  */
188
- export const READER_SINCE = "0.16.215";
205
+ export const READER_SINCE = "0.16.240";
189
206
  /**
190
207
  * O QUE ESTÁ INSTALADO AQUI FICOU PARA TRÁS - e as DUAS condições que fazem isso ser verdade.
191
208
  *
@@ -12,4 +12,4 @@
12
12
  * invocada, e ela não é segredo - a esteira é.
13
13
  */
14
14
  export const IMPORT_SKILL_PATH = ".claude/skills/sui-import-ds/SKILL.md";
15
- export const IMPORT_SKILL = '---\nname: sui-import-ds\ndescription: Turn a codebase the user ALREADY has into a SynthesisUI design system. Use when someone points at an existing repo, app or component library and asks to import it, adopt it, bring it in, or "make a design system from this" (e.g. "/sui-import-ds", "importa o meu packages/ui", "turn this app into a design system"). Drives the full pipeline - measurement \u2192 your reading of the app \u2192 import \u2192 upgrade proposal - and answers the questions arithmetic cannot.\n---\n\n# Import Design System - served live\n\nThis playbook is served from the platform, not shipped in this file - it is\nalways current, and your context only carries the step you are on.\n\n1. Call the `playbook` tool on the `synthesisui` MCP server with\n { "skill": "import" } - you get the framing and a table of contents.\n2. Fetch ONLY the chapter for the step you are on:\n { "skill": "import", "section": "<id from the toc>" }. Never fetch more\n than the current step needs.\n3. Follow it exactly. When the step is done, fetch the next chapter.\n\nIf the tool answers that you are not signed in, run `npx synthesisui login`\nin the terminal and call it again. If the `synthesisui` MCP server is not\navailable at all, run `npx synthesisui connect`, restart the session, and\ninvoke this skill again.\n';
15
+ export const IMPORT_SKILL = '---\nname: sui-import-ds\ndescription: Turn a codebase the user ALREADY has into a SynthesisUI design system. Use when someone points at an existing repo, app or component library and asks to import it, adopt it, bring it in, or "make a design system from this" (e.g. "/sui-import-ds", "importa o meu packages/ui", "turn this app into a design system"). Drives the full pipeline - measurement \u2192 your reading of the app \u2192 import \u2192 upgrade proposal - and answers the questions arithmetic cannot.\n---\n\n# Import Design System - served live\n\nThis playbook is served from the platform, not shipped in this file - it is\nalways current, and your context only carries the step you are on.\n\n1. Call the `playbook` tool on the `synthesisui` MCP server with\n { "skill": "import" } - you get the framing and a table of contents.\n2. Fetch ONLY the chapter for the step you are on:\n { "skill": "import", "section": "<id from the toc>" }. Never fetch more\n than the current step needs.\n3. Follow it exactly. When the step is done, fetch the next chapter.\n\n## If something is missing, they skipped a step - do not stall them\n\nThe supported order is `login`, then `connect`, then open this session. When\nit was followed, neither case below happens.\n\nNOT SIGNED IN. Do not stop and wait. Measure first, so they see their own\nrepo before being asked for anything:\n\n1. Run `npx synthesisui@latest import --dry` - it is local, free, needs no\n account, and writes `_synthesisui/census.json`.\n2. Read that file and tell them what is in THEIR repo, in THEIR names: how\n many declarations, how many components, how much already runs on tokens.\n3. Then: "to turn this into a system on your account, run\n `npx synthesisui@latest login` and tell me when it is done."\n\nNO `synthesisui` MCP SERVER AT ALL. Tell them to run\n`npx synthesisui@latest connect` and then START A NEW SESSION - tools are read\nwhen a session opens, so this one cannot pick it up. Say it plainly and stop\nthere; carrying on in a session that cannot reach the playbook is guessing.\n';
package/dist/stack.js CHANGED
@@ -9,7 +9,7 @@
9
9
  * One implementation, because two detectors that disagreed would put a rule in a
10
10
  * project the census said was something else.
11
11
  */
12
- import { readFile } from "node:fs/promises";
12
+ import { readdir, readFile } from "node:fs/promises";
13
13
  import { join } from "node:path";
14
14
  /**
15
15
  * Dependencies as the project actually resolves them - which means reading
@@ -19,36 +19,98 @@ import { join } from "node:path";
19
19
  * Next + React + Tailwind app, because a workspace hoists those to the root and
20
20
  * the leaf package.json lists only its own icons. Three levels up covers every
21
21
  * pnpm/npm workspace layout without wandering into someone's home directory.
22
+ *
23
+ * E PARA OS LADOS TAMBÉM, pelos `workspaces` do manifesto que os declara. Subir
24
+ * sozinho deixou 15 de 15 bibliotecas do censo vivo sem versão (16/08): o escopo
25
+ * era `packages/ui` (zero deps) e recharts/chakra/mui moram em `apps/*`, um
26
+ * IRMÃO - invisível para uma caminhada que só sobe. O componente medido vem do
27
+ * app que usa o sistema, então o manifesto daquele app é evidência tanto quanto
28
+ * o da raiz.
22
29
  */
23
30
  export async function resolveDeps(root) {
24
31
  const deps = {};
25
- let dir = root;
26
- for (let up = 0; up < 4; up++) {
32
+ const workspaceDirs = [];
33
+ const readManifest = async (dir) => {
27
34
  const raw = await readFile(join(dir, "package.json"), "utf8").catch(() => null);
28
- if (raw) {
29
- try {
30
- const p = JSON.parse(raw);
31
- // The nearest package.json wins on a version clash; we only ever ask
32
- // whether a name is present, so first-seen is enough.
33
- for (const [k, v] of Object.entries({
34
- ...p.dependencies,
35
- ...p.devDependencies,
36
- })) {
37
- if (deps[k] == null)
38
- deps[k] = String(v);
39
- }
35
+ if (!raw)
36
+ return;
37
+ try {
38
+ const p = JSON.parse(raw);
39
+ // The nearest package.json wins on a version clash; we only ever ask
40
+ // whether a name is present, so first-seen is enough. The up-chain is
41
+ // read before any sibling, so siblings only ever FILL, never override.
42
+ for (const [k, v] of Object.entries({
43
+ ...p.dependencies,
44
+ ...p.devDependencies,
45
+ })) {
46
+ if (deps[k] == null)
47
+ deps[k] = String(v);
40
48
  }
41
- catch {
42
- // unreadable manifest costs the detection, not the run
49
+ const globs = Array.isArray(p.workspaces)
50
+ ? p.workspaces
51
+ : Array.isArray(p.workspaces?.packages)
52
+ ? p.workspaces.packages
53
+ : [];
54
+ for (const g of globs) {
55
+ if (typeof g !== "string")
56
+ continue;
57
+ // Only the two shapes real manifests use: `packages/*` and a literal
58
+ // path. Anything fancier is skipped rather than guessed at.
59
+ if (g.endsWith("/*")) {
60
+ const parent = join(dir, g.slice(0, -2));
61
+ const children = await readdir(parent, {
62
+ withFileTypes: true,
63
+ }).catch(() => []);
64
+ for (const c of children)
65
+ if (c.isDirectory())
66
+ workspaceDirs.push(join(parent, c.name));
67
+ }
68
+ else if (!g.includes("*")) {
69
+ workspaceDirs.push(join(dir, g));
70
+ }
43
71
  }
44
72
  }
73
+ catch {
74
+ // unreadable manifest costs the detection, not the run
75
+ }
76
+ };
77
+ let dir = root;
78
+ for (let up = 0; up < 4; up++) {
79
+ await readManifest(dir);
45
80
  const parent = join(dir, "..");
46
81
  if (parent === dir)
47
82
  break;
48
83
  dir = parent;
49
84
  }
85
+ for (const w of workspaceDirs)
86
+ await readManifest(w);
50
87
  return deps;
51
88
  }
89
+ /**
90
+ * OS FRAMEWORKS DO PROJETO, COM A VERSÃO PINADA - o que `Census.project.versions` carrega.
91
+ *
92
+ * `detectStack` colapsa `next@16.0.8` na string `"next"`, e nada no censo guardava versão de
93
+ * framework nenhuma (medido 16/08: zero em todos os campos). Curado de propósito: framework aqui,
94
+ * biblioteca por biblioteca em `signals.libraries` - duas listas com dois papéis, nunca o
95
+ * package.json inteiro dentro do censo.
96
+ */
97
+ const STACK_PACKAGES = [
98
+ "next",
99
+ "react",
100
+ "react-dom",
101
+ "vue",
102
+ "svelte",
103
+ "vite",
104
+ "tailwindcss",
105
+ "typescript",
106
+ ];
107
+ export function stackVersions(deps) {
108
+ const out = {};
109
+ for (const name of STACK_PACKAGES)
110
+ if (deps[name])
111
+ out[name] = deps[name];
112
+ return out;
113
+ }
52
114
  export async function detectStack(root) {
53
115
  const stack = [];
54
116
  const has = async (f) => (await readFile(join(root, f), "utf8").catch(() => null)) !== null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.239",
3
+ "version": "0.16.241",
4
4
  "description": "Bring SynthesisUI design systems into any project - tokens, typed components, whole pages and an agent-ready CLAUDE.md manifest.",
5
5
  "type": "module",
6
6
  "bin": {