synthesisui 0.16.162 → 0.16.164

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.
@@ -1,4 +1,4 @@
1
- import { access, mkdir, readFile, writeFile } from "node:fs/promises";
1
+ import { access, mkdir, readFile, rm, writeFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { syncClaudeMd } from "../claude-md.js";
4
4
  import { readProjectConfig, resolveRegistry } from "../config.js";
@@ -41,6 +41,33 @@ async function writeGovernanceIgnore(projectRoot) {
41
41
  "ledger.jsonl\n" +
42
42
  "not-expressed.md\n", "utf8");
43
43
  }
44
+ /**
45
+ * APAGA A PROSA QUE ESTA VERSÃO DEIXOU DE ESCREVER.
46
+ *
47
+ * `rules.md` e `philosophy.md` eram materializados só pelo `add`, então quem instalou antes disto
48
+ * tem dois arquivos que nada mais atualiza - e um deles se apresenta como "Max authority - follow
49
+ * these first". Um arquivo que afirma autoridade máxima sobre a regra de uma versão qualquer do
50
+ * passado é pior que arquivo nenhum, e deixá-lo lá esperando alguém lembrar de apagar é exatamente
51
+ * a administração manual que esta esteira existe para não pedir (dono, 06/08).
52
+ *
53
+ * É a NOSSA pasta - o `.gitignore` que este mesmo comando escreve já a declara gerenciada -, então
54
+ * isto não toca em código dele. E é dito na saída, porque apagar em silêncio é a outra metade do
55
+ * mesmo erro.
56
+ */
57
+ async function retireMaterializedDoctrine(slugDir) {
58
+ const gone = [];
59
+ for (const name of ["rules.md", "philosophy.md"]) {
60
+ const path = join(slugDir, name);
61
+ try {
62
+ await access(path);
63
+ }
64
+ catch {
65
+ continue;
66
+ }
67
+ await rm(path).then(() => gone.push(name), () => { });
68
+ }
69
+ return gone;
70
+ }
44
71
  async function readRootLock(path) {
45
72
  try {
46
73
  return JSON.parse(await readFile(path, "utf8"));
@@ -114,7 +141,8 @@ export async function add(slug, opts) {
114
141
  };
115
142
  await writeFile(rootLockPath, `${JSON.stringify(lock, null, 2)}\n`, "utf8");
116
143
  await writeGovernanceIgnore(projectRoot);
117
- // 5b. governance rules (personal DS) → rules.md at the slug root (stable path,
144
+ const retired = await retireMaterializedDoctrine(slugDir);
145
+ // 5b. governance rules (personal DS) → doctrine.json at the slug root (stable path,
118
146
  // highest authority; the GUIDE tells the agent to read it first)
119
147
  /**
120
148
  * The structured set wins when the registry sent one, because only this side
@@ -129,13 +157,21 @@ export async function add(slug, opts) {
129
157
  const leftOut = payload.ruleSet
130
158
  ? describeFiltered(payload.ruleSet, stack)
131
159
  : null;
160
+ /**
161
+ * AS REGRAS VIAJAM COMO DADO, e não como prosa materializada.
162
+ *
163
+ * `rules.md` e `philosophy.md` eram escritos SÓ pelo `add` (e o `upgrade` chama o `add`), então
164
+ * entre um upgrade e outro eles eram uma cópia velha de um dado que é VERSIONADO do nosso lado - e
165
+ * o GUIDE chamava um deles de "maximum authority". Uma autoridade máxima que envelhece em silêncio
166
+ * é pior que nenhuma (dono, 06/08).
167
+ *
168
+ * O conteúdo não sai do disco, porque o agente precisa dele offline e sem aprovar servidor nenhum.
169
+ * O que muda é a FORMA e quem o apresenta: aqui fica o dado pinado na versão instalada, e o
170
+ * `system_doctrine` do MCP é quem o serve - dizendo qual versão ele está lendo e se existe uma mais
171
+ * nova, em vez de deixar a pessoa descobrir pela ausência.
172
+ */
132
173
  if (rules.length > 0) {
133
- const body = `# ${payload.name} - Rules\n\n` +
134
- "> Accumulated rules for this design system. **Max authority - follow these first.**\n" +
135
- `> Managed by synthesisui (edit in the studio). ${rules.length} rule(s).\n\n${rules
136
- .map((r) => `- ${r}`)
137
- .join("\n")}\n`;
138
- await writeFile(join(slugDir, "rules.md"), body, "utf8");
174
+ await writeFile(join(slugDir, "doctrine.json"), `${JSON.stringify({ version: payload.version, name: payload.name, rules }, null, 2)}\n`, "utf8");
139
175
  }
140
176
  // NO SILENT DROPS. A rule removed because it belongs to another environment is
141
177
  // a thing the person should hear once, not discover by its absence.
@@ -163,22 +199,17 @@ export async function add(slug, opts) {
163
199
  if (required.length > 0) {
164
200
  await writeFile(join(slugDir, "requires.json"), `${JSON.stringify(required, null, 2)}\n`, "utf8");
165
201
  }
166
- // 5c. structured philosophy (personal DS) → philosophy.md at the slug root.
167
- // Narrative guidance (mission, principles, voice, motion doctrine…); the
168
- // GUIDE points the agent here right after rules.md.
202
+ /** A filosofia continua sendo CONTADA na saída, e ela vive no documento - ver abaixo. */
169
203
  const philosophy = payload.document.philosophy;
170
204
  const sections = philosophy?.sections ?? [];
171
- if (sections.length > 0 || philosophy?.context) {
172
- const parts = [`# ${payload.name} - Philosophy`, ""];
173
- parts.push("> The voice and principles behind this system. Read after rules.md;", "> let it shape every screen. Managed by synthesisui (edit in the studio).", "");
174
- if (philosophy?.context) {
175
- parts.push("## What this product is", "", philosophy.context, "");
176
- }
177
- for (const s of sections) {
178
- parts.push(`## ${s.title}`, "", s.body, "");
179
- }
180
- await writeFile(join(slugDir, "philosophy.md"), `${parts.join("\n")}\n`, "utf8");
181
- }
205
+ /**
206
+ * 5c. A FILOSOFIA NÃO É MATERIALIZADA - ela já está aqui.
207
+ *
208
+ * `philosophy` é campo do DOCUMENTO versionado (`design-system.ts`), e o `design-system.json`
209
+ * escrito no passo 2 a carrega inteira. O `philosophy.md` era uma segunda cópia do mesmo dado, no
210
+ * mesmo commit, que só era reescrita no upgrade seguinte - dois caminhos para uma verdade é como
211
+ * um deles começa a discordar do outro. O `system_doctrine` lê do documento.
212
+ */
182
213
  // 6. discovery by the agent
183
214
  const claudeMd = await syncClaudeMd(projectRoot);
184
215
  // outcome line
@@ -201,12 +232,21 @@ export async function add(slug, opts) {
201
232
  "GUIDE.md",
202
233
  ];
203
234
  console.log(` v${v}/: ${files.join(", ")}`);
204
- if (rules.length > 0) {
205
- console.log(` rules.md → ${rules.length} rule(s) (read these first)`);
206
- }
207
- if (sections.length > 0 || philosophy?.context) {
208
- console.log(` philosophy.md → ${sections.length} section(s) (read after rules)`);
209
- }
235
+ /**
236
+ * O QUE O AGENTE VAI LER, e por qual porta - nomear o arquivo quando a porta é uma ferramenta
237
+ * mandaria alguém procurar um caminho que não existe mais.
238
+ */
239
+ const doctrine = [
240
+ ...(rules.length > 0 ? [`${rules.length} rule(s)`] : []),
241
+ ...(sections.length > 0 || philosophy?.context
242
+ ? [`${sections.length} section(s) of philosophy`]
243
+ : []),
244
+ ];
245
+ if (doctrine.length > 0)
246
+ console.log(` ${doctrine.join(" and ")} → your agent reads them with \`system_doctrine\`, pinned to v${v}`);
247
+ /** Nomeado, nunca em silêncio: apagar arquivo no repo de alguém se diz em voz alta. */
248
+ if (retired.length > 0)
249
+ console.log(` removed ${retired.join(" and ")} - nothing rewrote them after an install, so they stated an older version's rules as current`);
210
250
  console.log(` CLAUDE.md ${claudeMd.created ? "created" : "updated"} (${claudeMd.count} system(s) installed)`);
211
251
  if (opts.setupHints === false)
212
252
  return;
@@ -1,6 +1,7 @@
1
1
  import { readdir, readFile } from "node:fs/promises";
2
2
  import { homedir } from "node:os";
3
3
  import { join, resolve } from "node:path";
4
+ import { readToken, resolveRegistry } from "../config.js";
4
5
  import { readEvents } from "../doctor/ledger.js";
5
6
  import { measuredScope } from "../measured-scope.js";
6
7
  async function locksIn(root) {
@@ -100,6 +101,38 @@ opts = {}) {
100
101
  });
101
102
  return out;
102
103
  }
104
+ /**
105
+ * A VERSÃO PUBLICADA CONTRA A INSTALADA - o estado que nenhuma verificação cobria.
106
+ *
107
+ * É o que faz disto um produto de TIME e não de uma pessoa: alguém edita o sistema no estúdio e
108
+ * publica, e o repo de todo mundo continua com o CSS e as regras da versão anterior, sem nada dizer.
109
+ * A doutrina que o agente lê é a da versão instalada de propósito (ver `system_doctrine`), então as
110
+ * duas metades andam juntas - e é justamente por isso que a defasagem precisa ser DITA.
111
+ *
112
+ * Melhor esforço: offline, sem sessão, ou registry fora do ar não produzem linha nenhuma. Uma
113
+ * verificação de abertura não pode transformar um voo sem internet num aviso.
114
+ */
115
+ export async function versionBehind(root, opts = {}) {
116
+ const locks = (await locksIn(root)).filter((l) => l.slug && !l.adopted);
117
+ const lock = locks[0];
118
+ if (!lock?.slug || lock.version == null)
119
+ return null;
120
+ if (!(await credentials(opts.home ?? homedir())))
121
+ return null;
122
+ const token = await readToken();
123
+ if (!token)
124
+ return null;
125
+ const res = await fetch(`${lock.registry ?? resolveRegistry()}/api/registry/ds/${lock.slug}`, { headers: { Authorization: `Bearer ${token}` } }).catch(() => null);
126
+ if (!res?.ok)
127
+ return null;
128
+ const body = (await res.json().catch(() => null));
129
+ if (!body?.version || body.version <= lock.version)
130
+ return null;
131
+ return {
132
+ says: `v${body.version} of "${lock.slug}" is published and this repo is on v${lock.version}. The CSS here and the rules your agent reads are both v${lock.version} - they move together, which is why this is worth saying rather than applying.`,
133
+ run: `npx synthesisui upgrade ${lock.slug}`,
134
+ };
135
+ }
103
136
  /**
104
137
  * A linha que a sessão vê.
105
138
  *
@@ -127,6 +160,10 @@ export async function align(opts) {
127
160
  const items = await localMisalignments(root, {
128
161
  ...(opts.cli ? { cli: opts.cli } : {}),
129
162
  }).catch(() => []);
163
+ /** A única linha que custa rede, e ela some inteira quando não há rede. */
164
+ const remote = await versionBehind(root).catch(() => null);
165
+ if (remote)
166
+ items.push(remote);
130
167
  const text = describeMisalignments(items);
131
168
  if (text)
132
169
  console.log(text);
@@ -144,6 +144,7 @@ export async function loadSystem(root) {
144
144
  recipes: new Map(),
145
145
  documents: [],
146
146
  requires: [],
147
+ doctrines: [],
147
148
  };
148
149
  }
149
150
  // Several systems can live side by side; every token is prefixed --ds-, so
@@ -154,6 +155,7 @@ export async function loadSystem(root) {
154
155
  const recipes = new Map();
155
156
  const documents = [];
156
157
  const requires = [];
158
+ const doctrines = [];
157
159
  for (const slug of slugs) {
158
160
  const dir = join(dsDir, slug);
159
161
  const raw = await readFile(join(dir, ".lock"), "utf8").catch(() => "");
@@ -226,12 +228,31 @@ export async function loadSystem(root) {
226
228
  // Unreadable costs this check, not the run.
227
229
  }
228
230
  }
231
+ /** Ausente é o caso normal: um sistema sem regra nenhuma não deixa arquivo. */
232
+ const doctrineRaw = await readFile(join(dir, "doctrine.json"), "utf8").catch(() => "");
233
+ if (doctrineRaw) {
234
+ try {
235
+ const parsed = JSON.parse(doctrineRaw);
236
+ if (Array.isArray(parsed.rules))
237
+ doctrines.push({
238
+ ...(typeof parsed.version === "number"
239
+ ? { version: parsed.version }
240
+ : {}),
241
+ ...(typeof parsed.name === "string" ? { name: parsed.name } : {}),
242
+ rules: parsed.rules.filter((r) => typeof r === "string"),
243
+ });
244
+ }
245
+ catch {
246
+ // Ilegível custa a doutrina, nunca a rodada.
247
+ }
248
+ }
229
249
  }
230
250
  return {
231
251
  table: buildTable({ css, lock, source: adopted ? "adopted" : "installed" }),
232
252
  recipes,
233
253
  documents,
234
254
  requires,
255
+ doctrines,
235
256
  };
236
257
  }
237
258
  /**
@@ -3,6 +3,7 @@ import { readdir, readFile } from "node:fs/promises";
3
3
  import { join, relative, resolve } from "node:path";
4
4
  import { pinnedHookVersion } from "../agent-wiring.js";
5
5
  import { readToken, resolveRegistry } from "../config.js";
6
+ import { readEvents } from "../doctor/ledger.js";
6
7
  import { fileRequest } from "../doctor/requests.js";
7
8
  import { diagnose, scanSource } from "../doctor/scan.js";
8
9
  import { nearestToken, tokenFor } from "../doctor/tokens.js";
@@ -70,6 +71,11 @@ const TOOLS = [
70
71
  required: ["value"],
71
72
  },
72
73
  },
74
+ {
75
+ name: "system_doctrine",
76
+ description: "The rules and the philosophy of the installed system - maximum authority, read this BEFORE building anything. It answers from the version this project has pinned, which is the same version whose tokens are on disk, so the rules you follow and the values you write come from one place. It also says when a newer version has been published, without applying it.",
77
+ inputSchema: { type: "object", properties: {} },
78
+ },
73
79
  {
74
80
  name: "list_components",
75
81
  description: "The components this design system defines, with what each is for. Look here BEFORE writing any UI element from scratch - if something covers the purpose, materialize it with add_component instead.",
@@ -252,6 +258,94 @@ async function findToken(root, value) {
252
258
  // same refusal every time.
253
259
  return `No token in this system holds ${value}, and nothing is close. Do NOT invent one. Say which value you need and what you would call it, and let a person decide.`;
254
260
  }
261
+ /**
262
+ * A DOUTRINA DO SISTEMA - as regras e a filosofia, servidas em vez de materializadas.
263
+ *
264
+ * `rules.md` e `philosophy.md` eram escritos SÓ pelo `add` (e o `upgrade` chama o `add`), então entre
265
+ * um upgrade e outro eram uma cópia velha de dado que é VERSIONADO do nosso lado - e o GUIDE chamava
266
+ * um deles de "maximum authority". Uma autoridade máxima que envelhece em silêncio é pior que
267
+ * nenhuma (dono, 06/08).
268
+ *
269
+ * DA VERSÃO PINADA, e essa é a decisão que mantém as duas metades coerentes: os tokens no disco são
270
+ * os da v2, então a regra que o agente segue tem que ser a da v2. Servir o rascunho faria o agente
271
+ * escrever contra uma regra que ainda não está instalada.
272
+ *
273
+ * A existência de uma versão mais nova é DITA, nunca aplicada - aplicar é o `upgrade`, que é uma
274
+ * palavra que a pessoa já tem. Sem rede, a doutrina responde igual e a checagem é que se cala: o
275
+ * dado é local justamente para não depender de servidor nenhum.
276
+ */
277
+ async function systemDoctrine(root) {
278
+ const { documents, doctrines } = await loadSystem(root);
279
+ const rules = doctrines.flatMap((d) => d.rules);
280
+ const parts = [];
281
+ const pinned = doctrines[0]?.version;
282
+ for (const doc of documents) {
283
+ const p = doc.philosophy;
284
+ if (p?.context)
285
+ parts.push(`## What this product is\n\n${p.context}`);
286
+ for (const sec of p?.sections ?? [])
287
+ parts.push(`## ${sec.title}\n\n${sec.body}`);
288
+ }
289
+ if (rules.length === 0 && parts.length === 0)
290
+ return "This system carries no rules and no philosophy yet. Nothing here overrides your judgement - build with its tokens and its components, and say what you needed and could not find.";
291
+ const head = [
292
+ rules.length > 0
293
+ ? `# Rules - maximum authority, ${rules.length} of them${pinned ? `, as of v${pinned}` : ""}`
294
+ : "",
295
+ ...rules.map((r) => `- ${r}`),
296
+ ].filter(Boolean);
297
+ const pulse = await sessionPulse(root, pinned);
298
+ return [
299
+ ...head,
300
+ ...(parts.length > 0 ? ["", "# Philosophy", "", ...parts] : []),
301
+ ...(pulse.length > 0 ? ["", ...pulse] : []),
302
+ ].join("\n");
303
+ }
304
+ /**
305
+ * O PULSO DE UMA SESSÃO LONGA.
306
+ *
307
+ * A verificação de abertura (`align`) roda UMA vez, no minuto zero. Numa sessão de seis horas tudo o
308
+ * que ela achou é uma foto: a sessão expira na hora três, um colega publica na hora quatro, e o
309
+ * registro local cresce a tarde inteira sem subir. Nada disso volta a ser dito (dono, 06/08).
310
+ *
311
+ * Esta ferramenta é o lugar certo para o pulso porque o agente a chama ANTES de construir - que é
312
+ * exatamente o momento em que a consistência importa. Um `setInterval` avisaria no meio de outra
313
+ * coisa; isto avisa na fronteira de uma decisão.
314
+ *
315
+ * SÓ FALA QUANDO HÁ O QUE DIZER. Uma linha que aparece em toda chamada vira papel de parede numa
316
+ * ferramenta chamada dezenas de vezes por sessão, e aí ela não serve para a vez que importa.
317
+ */
318
+ async function sessionPulse(root, pinned) {
319
+ const out = [];
320
+ const slug = await installedSlug(root);
321
+ const token = await readToken();
322
+ if (slug && token && pinned != null) {
323
+ const res = await fetch(`${resolveRegistry()}/api/registry/ds/${slug}`, {
324
+ headers: { Authorization: `Bearer ${token}` },
325
+ }).catch(() => null);
326
+ /**
327
+ * 401 é a sessão que morreu no meio da sessão de trabalho, e é o pior silêncio possível: daqui
328
+ * em diante toda ferramenta falha e nenhuma diz por quê. Offline não produz linha - `res` nulo
329
+ * não distingue "sem rede" de "servidor fora", e nenhum dos dois é problema da pessoa.
330
+ */
331
+ if (res?.status === 401)
332
+ out.push("> Your session on this machine has expired, so anything that needs the platform will fail from here on. A person has to run `npx synthesisui login` once in the terminal - I cannot complete it. What is on disk still governs: the rules above are the installed version's.");
333
+ else if (res?.ok) {
334
+ const body = (await res.json().catch(() => null));
335
+ if (body?.version && body.version > pinned)
336
+ out.push(`> v${body.version} of this system has been published and this project is on v${pinned}. These rules are v${pinned}'s, and so are the tokens on disk - the two move together. \`npx synthesisui upgrade ${slug}\` brings both.`);
337
+ }
338
+ }
339
+ /**
340
+ * O REGISTRO QUE NÃO SUBIU. Ele cresce a cada escrita verificada e só o `sync` o manda, então uma
341
+ * tarde inteira de trabalho fica invisível para a plataforma - e a tela do dono pontua o repo com
342
+ * evidência de ontem. Cinquenta é onde uma sessão de verdade chega; abaixo disso é ruído.
343
+ */
344
+ const events = await readEvents(root).catch(() => []);
345
+ if (events.length >= 50)
346
+ out.push(`> ${events.length} checked writes are recorded here and have not been sent, so this repo is being scored on older evidence. \`npx synthesisui sync\` ships them.`);
347
+ return out;
348
+ }
255
349
  async function listComponents(root) {
256
350
  const { documents } = await loadSystem(root);
257
351
  const rows = [];
@@ -839,6 +933,8 @@ cli) {
839
933
  return text(await checkFile(root, String(args.path ?? ".")));
840
934
  case "find_token":
841
935
  return text(await findToken(root, String(args.value ?? "")));
936
+ case "system_doctrine":
937
+ return text(await systemDoctrine(root));
842
938
  case "list_components":
843
939
  return text(await listComponents(root));
844
940
  case "describe_component":
@@ -43,15 +43,17 @@ export async function use(slug, intent, opts) {
43
43
  const config = await readProjectConfig(root);
44
44
  const { version, name } = lock;
45
45
  const base = `_synthesisui/ds/${slug}`;
46
- // Highest-authority guidance first; only list files that actually exist.
47
- const hasRules = await exists(join(slugDir, "rules.md"));
48
- const hasPhilosophy = await exists(join(slugDir, "philosophy.md"));
46
+ /**
47
+ * A AUTORIDADE MÁXIMA VEM DE UMA FERRAMENTA, e o resto continua sendo caminho.
48
+ *
49
+ * `rules.md` e `philosophy.md` eram cópias velhas de dado versionado entre um upgrade e outro -
50
+ * ver `system_doctrine`. Um prompt que manda alguém abrir um arquivo que não existe mais é pior
51
+ * que um prompt sem a linha, então a condição passou a ser o DADO e não o arquivo.
52
+ */
53
+ const hasDoctrine = await exists(join(slugDir, "doctrine.json"));
49
54
  const readFirst = [
50
- hasRules
51
- ? `- ${base}/rules.md — project rules for this system; obey them above everything else`
52
- : "",
53
- hasPhilosophy
54
- ? `- ${base}/philosophy.md — the mission, voice and principles; let it shape every screen`
55
+ hasDoctrine
56
+ ? "- Call `system_doctrine` first — this system's rules and philosophy, pinned to the installed version; obey them above everything else. Without MCP, the same data is `doctrine.json` and the `philosophy` of `design-system.json`"
55
57
  : "",
56
58
  `- ${base}/v${version}/GUIDE.md — how to apply the system, the recipes and the token vocabulary`,
57
59
  ].filter(Boolean);
package/dist/guide.js CHANGED
@@ -426,14 +426,21 @@ own entry below says so.
426
426
  const hasRules = (payload.rules?.length ?? 0) > 0;
427
427
  const philosophy = payload.document.philosophy;
428
428
  const hasPhilosophy = (philosophy?.sections?.length ?? 0) > 0 || !!philosophy?.context;
429
- const readFirst = [
430
- hasRules
431
- ? `**Read \`_synthesisui/ds/${slug}/rules.md\` FIRST and obey it above everything else** - it carries this system's accumulated, project-specific rules; on any conflict they win.`
432
- : "",
433
- hasPhilosophy
434
- ? `**${hasRules ? "Then read" : "Read"} \`_synthesisui/ds/${slug}/philosophy.md\`** - the mission, principles, voice and motion doctrine. Let it shape every screen you build.`
435
- : "",
436
- ].filter(Boolean);
429
+ /**
430
+ * UMA PORTA SÓ, e ela é uma FERRAMENTA e não um caminho.
431
+ *
432
+ * Isto apontava para `rules.md` e `philosophy.md`, escritos apenas pelo `add` - então entre um
433
+ * upgrade e outro o agente lia uma cópia velha de dado versionado, sob a etiqueta "highest
434
+ * authority". `system_doctrine` serve os dois da versão PINADA (a mesma dos tokens no disco) e diz
435
+ * quando existe uma mais nova, em vez de deixar a defasagem ser descoberta pela ausência.
436
+ */
437
+ const readFirst = hasRules || hasPhilosophy
438
+ ? [
439
+ `**Call \`system_doctrine\` FIRST and obey it above everything else** - it carries ${hasRules
440
+ ? "this system's accumulated, project-specific rules"
441
+ : "this system's mission, principles, voice and motion doctrine"}${hasRules && hasPhilosophy ? " and its philosophy" : ""}, pinned to the version installed here. On any conflict they win. **Without that tool, read \`_synthesisui/ds/${slug}/doctrine.json\` and the \`philosophy\` of \`design-system.json\` beside it** - the same data, and the reason it stays on disk.`,
442
+ ]
443
+ : [];
437
444
  const rulesNote = readFirst.length > 0
438
445
  ? `
439
446
  ## Read first - highest authority
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.162",
3
+ "version": "0.16.164",
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": {