synthesisui 0.16.140 → 0.16.145

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.
@@ -0,0 +1,205 @@
1
+ /**
2
+ * O DOCTOR NA PIPELINE - anotação na linha, e uma CATRACA em vez de um pedágio.
3
+ *
4
+ * A objeção mais dura que um Design System Lead cético faz a qualquer plataforma de governança é que
5
+ * ela vive numa terceira aba: se a checagem não está no PR, no linter, no autocomplete, o engenheiro
6
+ * ignora. O doctor já respondia metade disso - roda offline, sem login, em 1,8s sobre 2797 arquivos -
7
+ * e não tinha como falar a língua de um PR.
8
+ *
9
+ * O QUE FALTAVA MAIS QUE O FORMATO, e é o achado que decidiu esta peça: o doctor só reprova com
10
+ * `--strict`, e `--strict` num repo legado é vermelho no dia um. Um portão que fica vermelho para
11
+ * sempre é um portão que alguém contorna na segunda semana - é literalmente o "pedágio" que faz times
12
+ * criarem biblioteca paralela. Então o que entra na pipeline não é um portão: é uma CATRACA.
13
+ *
14
+ * sem baseline nunca reprova, e anota tudo como aviso
15
+ * com baseline reprova SÓ quando a conta subiu - no total ou em algum arquivo
16
+ * --write-baseline congela onde vocês estão hoje, e a dívida de ontem para de doer
17
+ *
18
+ * Um arquivo que piorou reprova mesmo quando o total caiu, de propósito: num monorepo o total esconde
19
+ * um time melhorando e outro regredindo, e a regressão é a única coisa que uma catraca existe para
20
+ * pegar.
21
+ *
22
+ * NADA AQUI FALA COM A REDE nem pede login. É a mesma decisão que mantém o hook instalado: uma
23
+ * checagem que telefona para casa é uma checagem que alguém desinstala.
24
+ */
25
+ const byFileOf = (d) => {
26
+ const out = {};
27
+ for (const f of d.files)
28
+ if (f.findings.length > 0)
29
+ out[f.file] = f.findings.length;
30
+ return out;
31
+ };
32
+ /** O retrato de agora, para ser gravado e comparado depois. */
33
+ export function baselineOf(d, meta) {
34
+ return {
35
+ baseline: 1,
36
+ at: meta.at,
37
+ ...(meta.cli ? { cli: meta.cli } : {}),
38
+ ...(meta.scope ? { scope: meta.scope } : {}),
39
+ totals: { drift: d.findings.length, phantoms: d.phantomUses },
40
+ byFile: byFileOf(d),
41
+ };
42
+ }
43
+ /**
44
+ * A conta subiu?
45
+ *
46
+ * `incomparable` existe porque comparar dois escopos diferentes produz uma regressão inventada - o
47
+ * dono mediu `packages/ui` e `apps/web-dashboard` no mesmo dia, e um deles tem 2,5x os arquivos do
48
+ * outro. Nesse caso a catraca NÃO reprova e diz por quê: reprovar por diferença de input é o alarme
49
+ * falso que ensina um time a passar `--no-verify`.
50
+ */
51
+ export function compareToBaseline(d, base, now) {
52
+ const mine = byFileOf(d);
53
+ const regressed = [];
54
+ let improved = 0;
55
+ for (const [file, count] of Object.entries(mine)) {
56
+ const was = base.byFile[file] ?? 0;
57
+ if (count > was)
58
+ regressed.push({ file, was, now: count });
59
+ }
60
+ for (const [file, was] of Object.entries(base.byFile))
61
+ if ((mine[file] ?? 0) < was)
62
+ improved += 1;
63
+ const drift = { was: base.totals.drift, now: d.findings.length };
64
+ const phantoms = { was: base.totals.phantoms, now: d.phantomUses };
65
+ const scopeChanged = (base.scope ?? "") !== (now.scope ?? "");
66
+ return {
67
+ worse: !scopeChanged &&
68
+ (drift.now > drift.was ||
69
+ phantoms.now > phantoms.was ||
70
+ regressed.length > 0),
71
+ drift,
72
+ phantoms,
73
+ regressed: regressed.sort((a, b) => b.now - b.was - (a.now - a.was)),
74
+ improved,
75
+ ...(scopeChanged
76
+ ? {
77
+ incomparable: `the baseline was measured on ${base.scope ?? "the whole repo"} and this run read ${now.scope ?? "the whole repo"} - different inputs, so nothing is failed on the difference`,
78
+ }
79
+ : {}),
80
+ };
81
+ }
82
+ /** O que a mensagem de uma anotação diz, e é a mesma frase nos dois formatos. */
83
+ function messageFor(f) {
84
+ return f.token
85
+ ? `${f.literal} is written by hand, and this project's system already names it: var(${f.token}). Use the token.`
86
+ : `${f.literal} is a ${f.kind} value written by hand, and the system has no name for it yet. Name it, or file a token request: npx synthesisui request token`;
87
+ }
88
+ /**
89
+ * ANOTAÇÕES DO GITHUB, que aparecem na linha exata do diff.
90
+ *
91
+ * Nenhuma Action é necessária: o GitHub lê estes comandos do stdout de qualquer step, então a
92
+ * integração inteira é uma linha no workflow deles. Uma Action publicada seria uma peça a mais para
93
+ * manter oferecendo exactamente isto.
94
+ *
95
+ * TUDO É `warning`, e nunca `error`: a severidade que reprova é o exit code, e ela é decidida pela
96
+ * catraca. Anotação vermelha em cima de dívida herdada é o mesmo pedágio de sempre, pintado de outra
97
+ * cor.
98
+ */
99
+ export function githubAnnotations(d, cap = 200) {
100
+ const out = [];
101
+ /**
102
+ * O QUE TEM CONSERTO ÓBVIO VEM PRIMEIRO, porque o teto é real e ele decide o que chega ao PR.
103
+ *
104
+ * Um achado cujo token JÁ EXISTE é uma troca de uma linha; um valor que o sistema não nomeia é uma
105
+ * decisão de design. Ordenar assim faz o teto cortar a conversa difícil em vez do conserto fácil -
106
+ * e medido no repo do dono, sem ordem nenhuma as 200 anotações saíram todas de um diretório gerado.
107
+ */
108
+ const ranked = [...d.findings].sort((a, b) => a.token && !b.token ? -1 : b.token && !a.token ? 1 : 0);
109
+ for (const f of ranked.slice(0, cap)) {
110
+ const title = f.token
111
+ ? "Token exists for this value"
112
+ : "Design value not in the system";
113
+ /** `::` e `%` quebram o parser do GitHub, e uma anotação que não parseia desaparece calada. */
114
+ const clean = (s) => s
115
+ .replace(/%/g, "%25")
116
+ .replace(/\r/g, "%0D")
117
+ .replace(/\n/g, "%0A")
118
+ .replace(/::/g, ":");
119
+ out.push(`::warning file=${f.file},line=${f.line},title=${clean(title)}::${clean(messageFor(f))}`);
120
+ }
121
+ if (d.findings.length > cap)
122
+ out.push(`::notice::${d.findings.length - cap} more findings are in the report - annotations are capped at ${cap} so the log stays readable. Run \`npx synthesisui doctor --verbose\` locally for all of them.`);
123
+ return out;
124
+ }
125
+ /**
126
+ * SARIF 2.1.0, para quem já tem um pipeline de qualidade de código.
127
+ *
128
+ * É o formato que o code-scanning do GitHub, o SonarQube e as ferramentas de segurança já ingerem -
129
+ * então o achado entra na mesma caixa de entrada em que o time já olha, em vez de exigir uma nova.
130
+ * Mínimo e válido: `runs[].tool`, `results[]` com `ruleId`, `level`, `message`, `locations`.
131
+ */
132
+ export function sarifOf(d, cli) {
133
+ const rules = [
134
+ {
135
+ id: "synthesisui/token-exists",
136
+ shortDescription: {
137
+ text: "A design value written by hand that the system already names",
138
+ },
139
+ help: {
140
+ text: "The project's own design system declares a token with this exact value. Using the literal means a later change to the token will not reach this line.",
141
+ },
142
+ },
143
+ {
144
+ id: "synthesisui/unnamed-value",
145
+ shortDescription: {
146
+ text: "A design value the system has no name for",
147
+ },
148
+ help: {
149
+ text: "Nothing in the system holds this value. Either it belongs in the system - file a token request - or it is deliberately one-off, which is a decision worth writing down.",
150
+ },
151
+ },
152
+ ];
153
+ return JSON.stringify({
154
+ $schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
155
+ version: "2.1.0",
156
+ runs: [
157
+ {
158
+ tool: {
159
+ driver: {
160
+ name: "SynthesisUI doctor",
161
+ ...(cli ? { version: cli } : {}),
162
+ informationUri: "https://www.synthesisui.com",
163
+ rules,
164
+ },
165
+ },
166
+ results: d.findings.map((f) => ({
167
+ ruleId: f.token
168
+ ? "synthesisui/token-exists"
169
+ : "synthesisui/unnamed-value",
170
+ /** `warning` sempre: quem reprova é a catraca, não o achado. */
171
+ level: "warning",
172
+ message: { text: messageFor(f) },
173
+ locations: [
174
+ {
175
+ physicalLocation: {
176
+ artifactLocation: { uri: f.file },
177
+ region: { startLine: f.line, snippet: { text: f.excerpt } },
178
+ },
179
+ },
180
+ ],
181
+ })),
182
+ },
183
+ ],
184
+ }, null, 1);
185
+ }
186
+ /** O veredito da catraca em texto, para o log do CI - e ele lidera pela boa notícia. */
187
+ export function describeVerdict(v) {
188
+ const lines = [];
189
+ if (v.incomparable) {
190
+ lines.push(`Baseline not comparable: ${v.incomparable}.`);
191
+ return lines;
192
+ }
193
+ lines.push(`Drift ${v.drift.was} → ${v.drift.now}${v.phantoms.was || v.phantoms.now ? ` · phantom tokens ${v.phantoms.was} → ${v.phantoms.now}` : ""}.`);
194
+ if (v.improved > 0)
195
+ lines.push(`${v.improved} file${v.improved === 1 ? "" : "s"} improved since the baseline.`);
196
+ if (v.regressed.length === 0) {
197
+ lines.push("Nothing regressed. The ratchet holds.");
198
+ return lines;
199
+ }
200
+ lines.push(`${v.regressed.length} file${v.regressed.length === 1 ? "" : "s"} got worse than the baseline:`);
201
+ for (const r of v.regressed.slice(0, 10))
202
+ lines.push(` ${r.file}: ${r.was} → ${r.now}`);
203
+ lines.push("Fix those lines, or accept them into the baseline on purpose: npx synthesisui doctor --write-baseline");
204
+ return lines;
205
+ }
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ import { readFileSync } from "node:fs";
3
3
  import { add } from "./commands/add.js";
4
4
  import { adopt } from "./commands/adopt.js";
5
5
  import { advise } from "./commands/advise.js";
6
+ import { ci } from "./commands/ci.js";
6
7
  import { clean } from "./commands/clean.js";
7
8
  import { component } from "./commands/component.js";
8
9
  import { connect } from "./commands/connect.js";
@@ -17,6 +18,7 @@ import { login } from "./commands/login.js";
17
18
  import { mcp } from "./commands/mcp.js";
18
19
  import { refit } from "./commands/refit.js";
19
20
  import { request } from "./commands/request.js";
21
+ import { status } from "./commands/status.js";
20
22
  import { sync } from "./commands/sync.js";
21
23
  import { template } from "./commands/template.js";
22
24
  import { upgrade } from "./commands/upgrade.js";
@@ -44,7 +46,7 @@ Usage - governance (deterministic, FREE):
44
46
  synthesisui adopt [--write] turn the design system you ALREADY have into a contract
45
47
  synthesisui import [--scope <p>] [--usage <p>] read what you already have and make it a system
46
48
  your agent follows - without touching your CSS
47
- synthesisui connect wire your agent: the check as an editor hook, the system
49
+ synthesisui connect [--ci] wire your agent: the check as an editor hook, the system
48
50
  as MCP tools, and a contract that stops repeating itself
49
51
  synthesisui doctor [paths…] [--verbose] audit for DRIFT: every design value written by hand, the
50
52
  token your system already has for it, coherence, and the
@@ -54,8 +56,16 @@ Usage - governance (deterministic, FREE):
54
56
  something no reader would change
55
57
  synthesisui request [component|token] the queue of what your agent needed and the system
56
58
  refused to invent (--done <id> closes one)
57
- synthesisui sync send the local record - checks, fixes, open requests -
58
- to your system's dashboard (by hand, never automatic)
59
+ synthesisui sync re-measure this repo with the current reader and send it,
60
+ reusing the reading you already authored, plus the local
61
+ record - checks, fixes, open requests (--record-only skips
62
+ the measurement, --yes skips the overwrite question)
63
+ synthesisui doctor --fix [--dry] replace every hand-written value your system already
64
+ names with the token, in place
65
+ synthesisui status where this project stands: installed version, last check,
66
+ the ratchet, open requests - local first, no login needed
67
+ synthesisui ci [--write] the two steps that put drift in your PR - annotations on
68
+ the line, and a ratchet that fails only on regression
59
69
  synthesisui hook the check itself; installed by connect, run by your editor
60
70
  synthesisui mcp the system as tools; installed by connect, run by your agent
61
71
 
@@ -227,6 +237,7 @@ async function main() {
227
237
  version: CLI_VERSION,
228
238
  hook: flags.hook !== false,
229
239
  mcp: flags.mcp !== false,
240
+ ci: flags.ci === true,
230
241
  });
231
242
  return;
232
243
  case "hook":
@@ -250,8 +261,31 @@ async function main() {
250
261
  // written last week does not break.
251
262
  verbose: flags.verbose === true || flags.all === true,
252
263
  laws: flags.laws === true,
264
+ /**
265
+ * A PIPELINE - ver `ci-format.ts`. `--format` decide a língua, `--baseline` decide o que
266
+ * reprova: sem ele nada reprova por dívida herdada, com ele reprova só a regressão.
267
+ */
268
+ ...(flags.format === "github" || flags.format === "sarif"
269
+ ? { format: flags.format }
270
+ : {}),
271
+ ...(typeof flags.baseline === "string"
272
+ ? { baseline: flags.baseline }
273
+ : {}),
274
+ ...(flags.baseline === true ? { compareBaseline: true } : {}),
275
+ ...(flags.fix === true ? { fix: true } : {}),
276
+ ...(flags.dry === true ? { dry: true } : {}),
277
+ ...(flags["write-baseline"] === true ? { writeBaseline: true } : {}),
278
+ cli: CLI_VERSION,
253
279
  });
254
280
  break;
281
+ case "status":
282
+ /** A resposta do dia a dia no terminal - ver `commands/status.ts`. Local primeiro. */
283
+ await status({ dir, registry, cli: CLI_VERSION });
284
+ break;
285
+ case "ci":
286
+ /** A governança dentro do PR - ver `commands/ci.ts`. Imprime, ou grava com `--write`. */
287
+ await ci({ dir, write: flags.write === true });
288
+ break;
255
289
  case "gaps":
256
290
  /**
257
291
  * O LEDGER JÁ ESTÁ NO CENSO - isto não mede nada, tria.
@@ -422,8 +456,21 @@ async function main() {
422
456
  await clean({ dir, force: flags.force === true });
423
457
  break;
424
458
  case "sync":
425
- // A versão que reporta o estado do repo - ver `repoStateOf`.
426
- await sync({ dir, registry, cli: CLI_VERSION });
459
+ /**
460
+ * A versão que reporta o estado do repo - ver `repoStateOf` - e que MEDE: desde 05/08 o `sync`
461
+ * remede o repo com o leitor atual e reenvia, reusando a leitura já gravada. É o único comando
462
+ * do lado do cliente para fazer um conserto de leitura chegar.
463
+ *
464
+ * `--record-only` manda só o registro, e `--yes` não pergunta antes de sobrescrever receita
465
+ * escrita na tela. O default é medir e perguntar.
466
+ */
467
+ await sync({
468
+ dir,
469
+ registry,
470
+ cli: CLI_VERSION,
471
+ recordOnly: args.includes("--record-only"),
472
+ yes: args.includes("--yes"),
473
+ });
427
474
  break;
428
475
  case "request": {
429
476
  // `synthesisui request` lists; `request component --name x --for "..."`
@@ -11,8 +11,8 @@
11
11
  * CLI no longer has.
12
12
  */
13
13
  export const IMPORT_SKILL = `---
14
- name: import-design-system
15
- description: 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. "/import-design-system", "importa o meu packages/ui", "turn this app into a design system"). Drives the full pipeline - census → your reading → import → v2 proposal - and answers the questions arithmetic cannot.
14
+ name: sui-import-ds
15
+ description: 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 - census → your reading → import → v2 proposal - and answers the questions arithmetic cannot.
16
16
  ---
17
17
 
18
18
  # Import Design System
@@ -1287,4 +1287,4 @@ Say so if it comes up, rather than implying otherwise:
1287
1287
  link and check.
1288
1288
  `;
1289
1289
  /** Where it lands in the consumer's repo. */
1290
- export const IMPORT_SKILL_PATH = ".claude/skills/import-design-system/SKILL.md";
1290
+ export const IMPORT_SKILL_PATH = ".claude/skills/sui-import-ds/SKILL.md";
@@ -0,0 +1,226 @@
1
+ /**
2
+ * A SKILL DA PRIMEIRA CORRIDA, como o CLI a distribui.
3
+ *
4
+ * Mesmo motivo do `skill-import.ts`: o build é `tsc` e nada mais, então um `.md` precisaria de um
5
+ * passo de cópia que pode silenciosamente não rodar. Um módulo TypeScript não pode falhar em ser
6
+ * empacotado.
7
+ *
8
+ * ESTA É A FONTE. A cópia em `.claude/skills/` é o que o nosso editor lê, e o spec assere que as duas
9
+ * são idênticas - então divergência fica vermelha em vez de embarcar uma skill que descreve um fluxo
10
+ * que o produto não tem.
11
+ *
12
+ * E ela é instalada pelo `connect` junto com a de import, de propósito: uma skill que exige reiniciar
13
+ * o editor para aparecer é uma skill que ninguém usa na primeira corrida, que é a única em que ela
14
+ * serve (dono, 05/08).
15
+ */
16
+ export const INIT_SKILL = `---
17
+ name: sui-init
18
+ description: The first run - wire the agent, choose what to import, and hand back a system that is already optimised. Use when somebody has just installed SynthesisUI and has no system yet ("/sui-init", "set me up", "começar", "importar meu projeto"). Asks everything up front, measures while asking, and ends in four lines.
19
+ ---
20
+
21
+ # The first run
22
+
23
+ One conversation, and at the end they have a design system built out of their own code, with our
24
+ optimisations applied and a way back to the untouched version.
25
+
26
+ The purpose that decides every tie below: **read their repository and give it back as recipes with
27
+ visual, semantic and functional parity, without supposing and without inventing anything.**
28
+
29
+ \`CLAUDE.md\` rules. When the two disagree, this file is the one that is out of date.
30
+
31
+ ---
32
+
33
+ ## THE SHAPE OF THIS RUN, AND WHY IT IS THIS SHAPE
34
+
35
+ Four questions, asked before anything that needs an answer. Then silence, then four lines.
36
+
37
+ \`\`\`
38
+ 1 environment which agent they work in - and what that agent can actually get
39
+ 2 authorisation may we optimise after importing, and which parts
40
+ 3 project WHICH folder becomes the system (the only unavoidable choice)
41
+ 4 identity name, primary, theme - each already answered by the census
42
+ ---- nobody waits from here on ----
43
+ import v1 -> freeze v1 -> apply what they authorised -> four lines
44
+ 5 offer put the tokens back into their code
45
+ \`\`\`
46
+
47
+ **Every question is asked before the work, and the work starts during the questions.** A person who
48
+ answers four things in ninety seconds and then watches one bar has had a good four minutes; the same
49
+ person interrupted twice mid-silence has had a bad twenty (dono, 01/08).
50
+
51
+ **What runs in parallel, precisely.** The UNSCOPED census - \`npx synthesisui import --dry\` - needs no
52
+ answer and discovers the candidate projects, so start it the moment question 1 is on screen. The
53
+ SCOPED census needs question 3, and no amount of wishing makes it earlier. Do not promise more
54
+ parallelism than that: a progress bar waiting on a person is worse than a question.
55
+
56
+ ---
57
+
58
+ ## 1. ENVIRONMENT
59
+
60
+ Ask, because it changes what you install and it changes what you promise. Do not pretend the answers
61
+ are equivalent - they are not, and a Design System Lead spots that in one minute.
62
+
63
+ \`\`\`
64
+ Which agent do you work in?
65
+
66
+ Claude Code (recommended if you have it)
67
+ rules + 11 tools + THE CHECK: after every file write, a hook measures what you
68
+ wrote and hands the result back into the conversation. No other agent has this.
69
+ Cursor
70
+ rules (.cursor/rules) + the same 11 tools (.cursor/mcp.json). No post-write check -
71
+ Cursor has no hook that feeds context back, so the rules are read, not enforced.
72
+ Something else
73
+ rules in AGENTS.md, which most agents read. Tell me which one and I will say
74
+ honestly whether its tool format is one we know.
75
+ \`\`\`
76
+
77
+ **Never invent a config path.** If they name an agent whose format is not in that list, say so and
78
+ write \`AGENTS.md\` only. Writing a file into the wrong place is worse than not writing it: it looks
79
+ like support and delivers nothing.
80
+
81
+ Then wire it, once, with the command that is already idempotent:
82
+
83
+ \`\`\`
84
+ npx synthesisui@latest connect
85
+ \`\`\`
86
+
87
+ ## 2. AUTHORISATION - ONE QUESTION, THREE BOXES
88
+
89
+ Ask it HERE, before the import, because it is the only way the end can be four lines instead of a
90
+ negotiation.
91
+
92
+ \`\`\`
93
+ After I import, may I apply the deterministic fixes? Nothing here is AI - it is arithmetic,
94
+ and your untouched version stays frozen either way.
95
+
96
+ [x] tokens & contrast values that repeat become one named token; text that fails
97
+ contrast gets its INK changed, never its background
98
+ [x] spacing lengths off your own scale converge onto the step you named
99
+ [x] typography sizes and weights land on your own scale
100
+ \`\`\`
101
+
102
+ All three checked by default. Two things must be said in the same breath, because a default that
103
+ edits is only defensible with both:
104
+
105
+ - **v1 is frozen before anything is applied.** It is a published version, it never moves, and
106
+ restoring it is one click.
107
+ - **Value, never form.** Your axes, options, parts and layers are declarations - we do not touch
108
+ them. \`archetypeFidelity\` is excluded from this run on purpose: its fix once deleted the \`variant\`
109
+ and \`size\` a real \`Input\` declared (dono, 02/08).
110
+
111
+ If they decline all three: fine, and say what they are choosing - the system arrives exactly as
112
+ their code has it, and the optimisations wait on a button.
113
+
114
+ ## 3. PROJECT
115
+
116
+ By now the unscoped census has finished, so this is a list and not a question in the dark. The CLI
117
+ already ranks the candidates by how many tokens each declares - use its numbers, never your
118
+ impression.
119
+
120
+ \`\`\`
121
+ Where should the system come from?
122
+
123
+ packages/ui (recommended) 114 files · 47% of its values already named ·
124
+ 92 tokens of their own · every app imports it
125
+ the whole repo 2797 files · 36% · the average of one dark app and
126
+ two light ones, which is a diagnosis and not a system
127
+ apps/web-dashboard the heaviest consumer, if you want one app's vocabulary
128
+ \`\`\`
129
+
130
+ **Do not ask which styling they use.** We measure it, and better than they can answer it: the census
131
+ counts every form with a percentage and an example. Show it instead, and name what we cannot read:
132
+
133
+ \`\`\`
134
+ Measured across 2797 files: 46% Tailwind classes, 31% CSS Modules, 12% sx props.
135
+ I read all three. What I do not read yet: styled-jsx, 3% - and I will say so in the report.
136
+ \`\`\`
137
+
138
+ A wrong answer from them would override a right measurement from us. That is the whole reason this is
139
+ a statement and not a question.
140
+
141
+ ## 4. IDENTITY
142
+
143
+ Name, primary, default theme, canvas. Every one of them is already answered by the census - ask them
144
+ as separate selectable questions, recommendation first, evidence in one line. The full wording lives
145
+ in the \`sui-import-ds\` skill; do not reinvent it here, and do not ask anything it does not.
146
+
147
+ **Our kit is not a question.** A project with its own component library gets theirs alone.
148
+
149
+ ---
150
+
151
+ ## THE SILENT PART
152
+
153
+ Now nobody is asked anything. In order:
154
+
155
+ \`\`\`
156
+ 1 npx synthesisui import --scope <theirs> --usage <apps…> measures with the answers
157
+ 2 the reading anatomy, rules, roles
158
+ 3 send it their v1 exists
159
+ 4 POST /api/registry/ds/<slug>/optimize freeze v1, apply what they said yes to
160
+ { "categories": ["tokens", "spacing", "typography"] }
161
+ \`\`\`
162
+
163
+ Step 4 is ONE call on purpose: it freezes the base and applies the fixes in the same request, so
164
+ there is never a moment where an optimisation landed with no frozen version to go back to.
165
+
166
+ To show the numbers BEFORE applying - useful when they hesitated on question 2 - the same route
167
+ answers a probe and writes nothing:
168
+
169
+ \`\`\`
170
+ { "probe": true, "categories": ["tokens"] }
171
+ \`\`\`
172
+
173
+ If they authorised nothing, skip step 4 entirely. Do not call it with an empty list to be tidy: it
174
+ answers 400, and a 400 in a first run reads as a broken product.
175
+
176
+ ---
177
+
178
+ ## 5. THE FOUR LINES
179
+
180
+ This is the whole point of the run. Four lines, their numbers, no adjectives.
181
+
182
+ \`\`\`
183
+ SignalUI is yours - 275 components and 92 of your own tokens, read out of packages/ui.
184
+
185
+ v1 is frozen: your system exactly as your code has it today. It never moves.
186
+ v2 is what you are looking at - the same system with 118 deterministic fixes: contrast on
187
+ text only, lengths onto your own scale, near-identical colours unified.
188
+
189
+ Back to v1 is one click: /dashboard/mine/signalui/publish
190
+ \`\`\`
191
+
192
+ **What must never be in those lines:** how many files we scanned, how long it took, which readers
193
+ ran, what the ledger says. All of that is in the report and none of it is what somebody wants in the
194
+ first minute of owning a system.
195
+
196
+ **What must always be there:** the count of their components, the count of their own tokens, and the
197
+ way back.
198
+
199
+ ## 6. THE OFFER
200
+
201
+ One question, and only if there is something to offer - the doctor already knows the number.
202
+
203
+ \`\`\`
204
+ 340 hand-written values in your code have a token in your system now.
205
+ Want me to replace them? It only touches values your system names, and you read the diff.
206
+
207
+ npx synthesisui@latest doctor --fix
208
+ \`\`\`
209
+
210
+ If the number is zero, say nothing. An offer with no subject is noise, and this run ends better
211
+ short.
212
+
213
+ ---
214
+
215
+ ## WHAT THIS RUN NEVER DOES
216
+
217
+ - **Never asks a question the census answers.** Styling, coverage, which files exist, how many
218
+ components - those are measurements, and asking invites a wrong answer to beat a right one.
219
+ - **Never installs a file for an agent they did not name.** \`.cursor/\` in a repo that has no Cursor
220
+ is the first thing somebody deletes angrily.
221
+ - **Never applies an optimisation without a frozen v1**, and never reports one without the way back.
222
+ - **Never claims parity between agents.** The post-write check exists in one of them. Saying
223
+ otherwise is the kind of sentence that ends a trial.
224
+ - **Never ends long.** If the last screen needs scrolling, the run failed at the last step.
225
+ `;
226
+ export const INIT_SKILL_PATH = ".claude/skills/sui-init/SKILL.md";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.140",
3
+ "version": "0.16.145",
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": {