synthesisui 0.16.421 → 0.16.423

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.
@@ -7,14 +7,12 @@ import { readProjectConfig, resolveRegistry } from "../config.js";
7
7
  import { detectAppDirs } from "../global-sheet.js";
8
8
  import { hasInteractiveTemplate, interactiveTemplate, } from "../interactive-templates.js";
9
9
  import { body, section, snippet } from "../output.js";
10
- import { findCollision, installedThemeVars, reactMajorOf, readInstalledConvention, readInstalledScheme, } from "../project-facts.js";
10
+ import { findCollision, reactMajorOf, readInstalledConvention, readInstalledScheme, theirThemeVars, } from "../project-facts.js";
11
11
  import { fetchComponent, fetchDesignSystem, RegistryError, } from "../registry.js";
12
- import { installedThemeCss, whatOnlyTheSheetResolves, } from "../sheet-needed.js";
13
12
  import { flavourResolver } from "../styles-flavour.js";
14
13
  import { inTheirTongue, projectTongue, sumSpoken, tongueFromArtifacts, } from "../their-tongue.js";
15
14
  import { resolvableVars } from "../their-vars.js";
16
15
  import { readCensus, unreadComment, unreadForComponent, } from "../unread-for-component.js";
17
- import { readWiring } from "../wiring-read.js";
18
16
  import { editedSinceWritten, readWritten, recordWritten } from "../written.js";
19
17
  /**
20
18
  * Writes the shared `cn.ts` next to the components, built from THIS project's
@@ -72,18 +70,6 @@ export function localName(blueprint, asked) {
72
70
  * The component's styles reference the DS tokens, so the system itself must be
73
71
  * installed (`synthesisui add <slug>`) for `tokens.css`/`theme.css` to resolve.
74
72
  */
75
- /**
76
- * O QUE NESTE ARQUIVO PRECISA DA FOLHA, dito uma vez.
77
- *
78
- * A frase aparece nos três desfechos do bloco de setup (está aí · está no repo · falta), e três
79
- * redações da mesma medição são três respostas no dia em que uma for editada.
80
- */
81
- function whatNeedsIt(need) {
82
- const some = (list) => `${list.slice(0, 3).join(", ")}${list.length > 3 ? `, +${list.length - 3}` : ""}`;
83
- return need.variables.length > 0
84
- ? some(need.variables)
85
- : `the ${some(need.classes)} ${need.classes.length === 1 ? "utility" : "utilities"}`;
86
- }
87
73
  export async function component(slug, name, opts) {
88
74
  const base = resolveRegistry(opts.registry);
89
75
  const root = opts.dir ?? process.cwd();
@@ -130,19 +116,12 @@ export async function component(slug, name, opts) {
130
116
  * faz, feita aqui e jogada fora depois de traduzir.
131
117
  *
132
118
  * UMA IDA A MAIS A' REDE, e so' neste caminho: quem ja' instalou o sistema le' do `.lock` como
133
- * sempre. O `catch` mantem o comportamento antigo - sem tradução, e o bloco de setup abaixo diz
134
- * que a folha e' o caminho.
119
+ * sempre. O `catch` mantem o comportamento antigo - sem traducao, e o relato abaixo DIZ que nada
120
+ * foi traduzido, em vez de deixar a pessoa descobrir pela cor que nao apareceu.
135
121
  */
136
122
  const fromRegistry = installedTongue
137
123
  ? null
138
124
  : await fetchDesignSystem(base, slug, opts.version).catch(() => null);
139
- /** A leitura do sistema NAO chegou - rede fora, ou sistema privado sem sessao. Ver o relato. */
140
- const installedSheetMissing = !installedTongue && fromRegistry === null;
141
- /**
142
- * A NOSSA FOLHA ESTA' NA PASTA DELE? - o `.lock` responde, e e' o mesmo arquivo que o `add`
143
- * escreve. Desde o A1 esta pergunta deixou de ser retorica: `component` roda sem ele.
144
- */
145
- const installedHere = await readFile(join(root, "_synthesisui", "ds", slug, ".lock"), "utf8").then(() => true, () => false);
146
125
  const tongue = installedTongue ??
147
126
  (fromRegistry
148
127
  ? await tongueFromArtifacts(root, fromRegistry.artifacts)
@@ -157,13 +136,6 @@ export async function component(slug, name, opts) {
157
136
  * sai no fim - depois da linha que nomeia o que foi escrito.
158
137
  */
159
138
  let spoken = null;
160
- /**
161
- * OS BYTES QUE FORAM A DISCO - é sobre ELES que a pergunta "precisa da folha?" se responde.
162
- *
163
- * Pela mesma razão que `spoken` é preenchido em cada caminho de escrita: perguntar sobre o
164
- * recipe, ou sobre o css antes da tradução, seria responder sobre um arquivo que ninguém abriu.
165
- */
166
- let writtenSource = "";
167
139
  /**
168
140
  * O VEREDITO DA RAIZ - ver `GeneratedRoot` em `component-codegen.ts`.
169
141
  *
@@ -181,7 +153,6 @@ export async function component(slug, name, opts) {
181
153
  const sheet = tongue ? inTheirTongue(res.css, tongue) : null;
182
154
  if (sheet)
183
155
  spoken = sheet;
184
- writtenSource = sheet ? sheet.css : res.css;
185
156
  await writeFile(join(dir, `${res.name}.css`), `${sheet ? sheet.css : res.css}\n`, "utf8");
186
157
  console.log(`✓ ${res.name} → _synthesisui/ds/${slug}/components/${res.name}.{json,css} (${slug} v${res.version})`);
187
158
  }
@@ -380,7 +351,6 @@ export async function component(slug, name, opts) {
380
351
  ];
381
352
  for (const f of written)
382
353
  await writeFile(join(compDir, f.filename), f.content, "utf8");
383
- writtenSource = written.map((f) => f.content).join("\n");
384
354
  /** O fingerprint do que escrevemos - é o que protege a edição dele no upgrade (T7). */
385
355
  await recordWritten(join(root, "_synthesisui", "ds", slug), local, written);
386
356
  filenames = written.map((f) => f.filename);
@@ -405,7 +375,17 @@ export async function component(slug, name, opts) {
405
375
  * O ARQUIVO e o EXPORT levam o nome dele; a CLASSE continua sendo a do blueprint. Um
406
376
  * `<CardPreview>` vestindo `.ds-card` está estilizado certo e não faz sombra em nada dele.
407
377
  */
408
- local, await readInstalledScheme(root, slug), tongue, await installedThemeVars(root, slug));
378
+ local, await readInstalledScheme(root, slug), tongue,
379
+ /**
380
+ * AS PASTAS DE APP, para a resposta ser a INTERSEÇÃO delas - ver `theirThemeVars`.
381
+ *
382
+ * Um componente materializado vai para uma pasta que num monorepo os dois apps importam, e
383
+ * ele não sabe em qual será usado. Varrer da raiz faria o `@theme` do app A responder por
384
+ * um componente escrito no app B: emitimos `bg-brand`, aquele app não gera a classe, e a
385
+ * propriedade renderiza NADA - pior que o valor arbitrário, porque nada falta na tela. É a
386
+ * mesma lição de `INV-VOLTA-12`, que saiu com a instrução de import e não com a topologia.
387
+ */
388
+ await theirThemeVars(root, await detectAppDirs(root, config.pagesDir)));
409
389
  spoken = said;
410
390
  root_ = generatedRoot;
411
391
  /**
@@ -430,7 +410,6 @@ export async function component(slug, name, opts) {
430
410
  }));
431
411
  for (const f of written)
432
412
  await writeFile(join(compDir, f.filename), f.content, "utf8");
433
- writtenSource = written.map((f) => f.content).join("\n");
434
413
  await recordWritten(join(root, "_synthesisui", "ds", slug), local, written);
435
414
  if (flavour === "tailwind")
436
415
  await writeCn(root, compDir, slug);
@@ -449,11 +428,37 @@ export async function component(slug, name, opts) {
449
428
  * pergunta é uma: o que foi escrito ainda precisa da nossa folha? Enquanto isto era calculado
450
429
  * antes da materialização, a resposta descrevia outro arquivo.
451
430
  */
431
+ /**
432
+ * `left` É A FALHA, E ELE SAI PRIMEIRO - fora de qualquer condição sobre o que deu certo.
433
+ *
434
+ * O DEFEITO QUE ISTO CONSERTA, achado pela revisão de QA do fecho: este aviso estava ANINHADO em
435
+ * `named > 0 || inlined > 0`. O caso em que ele mais importa é justamente o que zera os dois - uma
436
+ * folha instalada de versão anterior, que não declara a variável que o componente novo usa. Ali
437
+ * `named=0`, `inlined=0`, `left=["--ds-…"]`, o byte com a nossa grafia ia para o repositório dele,
438
+ * e o terminal não dizia uma palavra: o ramo `else if (!tongue)` também não pega, porque o
439
+ * vocabulário EXISTE.
440
+ *
441
+ * É o skew de versão que `component-codegen.ts` já documenta como tendo acontecido, e a lei 8 na
442
+ * forma mais cara dela - a lacuna calada no único momento em que a lacuna é real.
443
+ */
444
+ if (spoken && spoken.left.length > 0)
445
+ console.log(` ${spoken.left.length} value${spoken.left.length === 1 ? "" : "s"} the system declares no value for (${spoken.left.slice(0, 3).join(", ")}${spoken.left.length > 3 ? ", …" : ""}) - ${spoken.left.length === 1 ? "that declaration is" : "those declarations are"} dropped by the browser. Run \`npx synthesisui upgrade ${slug}\` to re-read the system; if it persists, please report it.`);
452
446
  if (spoken && (spoken.named > 0 || spoken.inlined > 0)) {
453
447
  console.log(` ${spoken.named} reference${spoken.named === 1 ? "" : "s"} now speak${spoken.named === 1 ? "s" : ""} the name YOUR code gives the value${spoken.inlined > 0 ? `, and ${spoken.inlined} carr${spoken.inlined === 1 ? "ies" : "y"} the value because your code names no token for it` : ""}.`);
454
- console.log(spoken.left.length === 0
455
- ? ` No variable in what was just written points at our stylesheet - they are all names YOUR code declares.`
456
- : ` ${spoken.left.length} still point${spoken.left.length === 1 ? "s" : ""} at our stylesheet (${spoken.left.slice(0, 3).join(", ")}${spoken.left.length > 3 ? ", …" : ""}), so tokens.css carries ${spoken.left.length === 1 ? "it" : "those"}.`);
448
+ if (spoken.left.length === 0)
449
+ console.log(` Nothing in what was just written points at us - every value is a name YOUR code declares, or the value itself.`);
450
+ /**
451
+ * O QUE CONGELOU ENTRE OS ESQUEMAS - ver `Tongue.frozen`, e é lei 8 na forma mais barata dela.
452
+ *
453
+ * Um valor que muda com o tema e que o código dele não nomeia sai como o literal do esquema
454
+ * base: renderiza certo hoje, e não vira quando ele alterna. É o tipo de coisa que ele nota na
455
+ * tela e não tem como explicar - e nomear aquele valor no próprio vocabulário é o conserto, que
456
+ * é decisão dele.
457
+ *
458
+ * MEDIDO em 11/09 na folha do `codelevel`: 3 de 119.
459
+ */
460
+ if (spoken.froze > 0)
461
+ console.log(` ${spoken.froze} of ${spoken.froze === 1 ? "those changes" : "those change"} with the colour scheme, and ${spoken.froze === 1 ? "it is" : "they are"} written here as the value of the resting one - so ${spoken.froze === 1 ? "it does" : "they do"} not flip when the scheme does. Name ${spoken.froze === 1 ? "it" : "them"} in your own code and the next run writes your name instead.`);
457
462
  /**
458
463
  * E DIZER DE ONDE VEIO O VOCABULARIO, quando o sistema NAO esta' instalado aqui.
459
464
  *
@@ -464,179 +469,56 @@ export async function component(slug, name, opts) {
464
469
  */
465
470
  if (fromRegistry)
466
471
  console.log(` Nothing of ours was installed to do that - the system was read at v${fromRegistry.version}, and the names came from your own code.`);
467
- }
468
- else if (!tongue) {
469
472
  /**
470
- * A LACUNA DECLARADA (lei 8): nada foi traduzido, e ISSO SE DIZ.
473
+ * E POR QUE NENHUM NOME DELE ENTROU, quando nenhum entrou - lei 8, e a causa tem conserto.
471
474
  *
472
- * O QUE ACONTECIA SEM ESTA LINHA: o `.tsx` saia com `var(--ds-*)` cru e o terminal ficava
473
- * mudo. A pessoa abre o arquivo, ve' variaveis que o `globals.css` dela nao declara, e a
474
- * unica forma de descobrir por que a cor nao apareceu e' ir ler o nosso codigo.
475
+ * `their-vars.ts` recusa todo par que o BUILD dele não emite, por desenho: um nome afirmado sem
476
+ * prova pinta a cor errada no dia em que ele o renomear. Num clone fresco isso é o caso normal,
477
+ * e o efeito é um arquivo inteiro de valores literais - que funciona, e que perde a ligação com
478
+ * as decisões dele.
475
479
  *
476
- * E OS DOIS MOTIVOS SAO DIFERENTES, entao a frase distingue: o repositorio dela nao nomeia
477
- * nenhum destes valores (o caso comum, e nao ha' o que fazer), ou a leitura do sistema nao
478
- * chegou - rede fora, sistema privado sem sessao. O segundo tem conserto e o primeiro nao.
480
+ * A frase que este ramo substituiu dizia *"nada foi traduzido"*, e ela era verdade quando a
481
+ * tradução dependia do mapa. Desde 11/09 a folha responde sozinha: sempre há tradução, e o que
482
+ * varia é se ela achou NOMES dele ou só valores.
479
483
  */
480
- /**
481
- * E AS TRES CAUSAS SAO DIFERENTES, entao a frase distingue - so' uma delas tem conserto na
482
- * mao dele, e ela e' a mais provavel das tres.
483
- *
484
- * a leitura nao chegou rede fora, sessao ausente, sistema privado
485
- * o build dele nao existe `resolvableVars` recusa todo par que o build nao emite, por
486
- * desenho: um nome afirmado sem prova pinta a cor errada no dia
487
- * em que ele o renomear. Num clone fresco isso e' o caso normal
488
- * o codigo dele nao nomeia nada a unica que o silencio de fato descrevia
489
- */
490
- const noBuild = !installedSheetMissing && (await resolvableVars(root)) === null;
491
- console.log(installedSheetMissing
492
- ? ` Nothing was translated: "${slug}" could not be read from the registry just now, so the variables stayed as ours. Check your connection, or run: npx synthesisui login`
493
- : noBuild
494
- ? ` Nothing was translated: this project has no build output to read, and a name we cannot see your build emit is a name we will not write. Run your build once and ask for it again - the file then speaks your own names.`
495
- : ` Nothing was translated: your code names none of the values this system declares yet, so the variables stayed as ours and tokens.css is what resolves them.`);
484
+ if (spoken.named === 0 && spoken.inlined > 0) {
485
+ const noBuild = (await resolvableVars(root)) === null;
486
+ console.log(noBuild
487
+ ? ` None of them is a name yet: this project has no build output to read, and a name we cannot see your build emit is a name we will not write. Run your build once and ask again - the file then speaks your own names.`
488
+ : ` None of them is a name yet: your code names none of the values this system declares. The values above work as they are; naming one is yours to decide.`);
489
+ }
496
490
  }
497
- // ── DX: concrete paths + copy-pasteable snippets, with breathing room ──
498
- const tailwind = flavour === "tailwind";
499
- const imports = tailwind
500
- ? [
501
- `@import "tailwindcss";`,
502
- `@import "../_synthesisui/ds/${slug}/tokens.css";`,
503
- `@import "../_synthesisui/ds/${slug}/theme.css";`,
504
- ]
505
- : [`@import "../_synthesisui/ds/${slug}/tokens.css";`];
506
- /**
507
- * O SETUP SÓ É COBRADO QUANDO ESTE ARQUIVO PRECISA DELE - ver `sheet-needed.ts`.
508
- *
509
- * O DEFEITO, apontado pelo dono olhando a própria tela (07/09): o comando terminava dizendo
510
- * *"No variable in what was just written points at our stylesheet"* e, três linhas abaixo,
511
- * imprimia **One-time setup** mandando colar dois `@import`. As duas frases não podem ser
512
- * verdadeiras ao mesmo tempo, e a segunda pedia uma dependência de runtime para um arquivo que
513
- * não a usa.
514
- *
515
- * A VERSÃO ANTERIOR DESTE BLOCO já tinha visto metade do problema e parou na metade: manteve o
516
- * setup e acrescentou *"o que estes imports ainda trazem são os utilitários do sistema e o
517
- * movimento"*. A razão é real - `INV-VOLTA-13` deixa no `@theme` os nomes que só o sistema tem -
518
- * mas ela era AFIRMADA, nunca medida. No `codelevel` aquele `@theme` declara 50 variáveis, todas
519
- * `--animate-*` e `--max-width-*`, e o componente gerado não veste nenhuma delas: as duas
520
- * animações que ele usa são declaradas pelo `globals.css` DELE.
521
- *
522
- * Então a pergunta passa a ser feita: sobrou variável nossa, ou classe que só o nosso `@theme`
523
- * gera? Se nenhuma das duas, o arquivo está isolado e o bloco diz isso - com o que o import ainda
524
- * daria, para a escolha continuar sendo dele.
525
- */
526
- const need = whatOnlyTheSheetResolves({
527
- source: writtenSource,
491
+ else if (!tongue) {
528
492
  /**
529
- * O `@theme` DO SISTEMA - do disco quando ele instalou, do registry quando nao.
493
+ * NEM A FOLHA VEIO - e desde 11/09 é a ÚNICA causa que resta para não haver tradução.
530
494
  *
531
- * A pergunta que este bloco faz e' *"sobrou classe que so' o nosso @theme gera?"*. Sem o
532
- * `add`, `installedThemeCss` responde vazio, e vazio leria como "nao sobrou nada" - uma
533
- * resposta certa por acidente que ficaria errada no dia em que sobrasse.
534
- */
535
- themeCss: fromRegistry?.artifacts?.["theme.css"] ??
536
- (await installedThemeCss(root, slug)),
537
- /** O vocabulário DELE sai da conta - ver `theirNames`. O mapa do `.lock` é a via mais barata. */
538
- theirNames: tongue?.names ? [...tongue.names.values()] : [],
539
- /**
540
- * A TERCEIRA FONTE NÃO É MEDIDA AQUI, e o vazio é a resposta EXPLÍCITA disso.
495
+ * A pasta não existe e a leitura do registry não chegou: rede fora, sessão ausente, sistema
496
+ * privado. O arquivo sai com a nossa grafia porque não há de onde tirar nem nome nem valor, e
497
+ * isso SE DIZ - a pessoa abre o arquivo, vê variáveis que o `globals.css` dela não declara, e a
498
+ * única forma de descobrir por que a cor não apareceu seria ler o nosso código.
541
499
  *
542
- * As classes de receita que a folha declara entrariam nesta conta se esta linha entregasse o
543
- * `tokens.css` instalado - e o efeito está medido em `steps/11-fora-do-escopo.md`: este comando
544
- * diz *"This file needs nothing else… It is yours."* sobre um componente cujo estilo inteiro vem
545
- * de `.ds-button`. Ligar a medição aqui é mudar o que este comando promete, com o bloco de setup
546
- * dele para redesenhar: é etapa própria. O que o argumento obrigatório garante é que a omissão
547
- * está ESCRITA, e não escondida num parâmetro que ninguém passou.
500
+ * As outras duas causas que este ramo distinguia - o build ausente e o código que não nomeia
501
+ * nada - deixaram de impedir a tradução, e viraram a linha logo acima.
548
502
  */
549
- sheetCss: "",
550
- });
551
- if (!need.needed) {
552
- console.log(section("This file needs nothing else"));
553
- console.log(body("Every variable and class in it is declared by your own code - no import, no scope"));
554
- console.log(body("attribute, nothing at runtime. It is yours."));
555
- console.log("");
556
- console.log(body(`The system stays what it is here: the reference your agent reads to build ON it.`));
557
- console.log(body(`If you later want the utilities only its own @theme generates, that is when`));
558
- console.log(body(`importing earns its place - and \`doctor\` says so.`));
503
+ console.log(` Nothing was translated: "${slug}" could not be read from the registry just now, so the variables stayed as ours. Check your connection, or run: npx synthesisui login`);
559
504
  }
560
505
  /**
561
- * E O "USE IT" SAI NOS DOIS CAMINHOS - a primeira versão disto era um `return`, e ela levava
562
- * junto a única linha que diz COMO importar o componente. Dizer "está isolado" e sonegar o
563
- * `import { ButtonSample }` troca um defeito por outro.
564
- */
565
- /**
566
- * A FIAÇÃO JÁ ESTÁ AQUI? - ver `wiring-read.ts`, a MESMA leitura que o `doctor` usa.
506
+ * E NÃO HÁ SETUP A PEDIR - `INV-GERAL-13`, e este bloco tinha 120 linhas até 11/09.
567
507
  *
568
- * MEDIDO em 08/09, nos dois apps do ato 2B: com os dois `@import` colados e o
569
- * `data-ds="<slug>"` no `<body>`, este comando ainda imprimia os passos 1 e 2 para colar de
570
- * novo. Ele perguntava se o arquivo PRECISA da folha e nunca se a folha já estava lá.
508
+ * O QUE ELE FAZIA: media se o arquivo recém-escrito ainda apontava para a nossa folha e, quando
509
+ * sim, imprimia **One-time setup** com dois `@import` e um `data-ds` para colar. O dono leu isso
510
+ * na própria tela e a frase dele fecha o assunto: *"isso nunca deve acontecer, a gente havia
511
+ * combinado (...) garanta que nunca mais você irá falar desse import e nem solicitar e nem
512
+ * precisar nos projetos"*.
571
513
  *
572
- * As duas perguntas continuam sendo feitas, e nesta ordem: `need` decide se o setup é
573
- * necessário, `wiring` decide se ele ainda está pendente.
574
- */
575
- const wiring = need.needed ? await readWiring(root, slug) : null;
576
- /**
577
- * O ADAPTADOR CONTA quando o que precisa da folha são UTILITÁRIOS - eles só existem pelo
578
- * `@theme` do `theme.css`. Dizer "está tudo aí" com o `tokens.css` sozinho declararia resolvido
579
- * exatamente o que continua faltando.
580
- */
581
- const sheetsThere = wiring?.imported === true && (!tailwind || wiring?.themed === true);
582
- /**
583
- * E A RESPOSTA É DO PROJETO, NÃO DO APP - então num repositório com mais de um app ela não
584
- * decide, e o comando diz isso em vez de afirmar (`INV-VOLTA-12`: monorepo é a forma comum).
514
+ * O QUE MATOU A PERGUNTA foi a causa, não o texto. Nenhuma variável nossa chega ao arquivo
515
+ * (`speak`), e nenhum utilitário nomeado depende do nosso `@theme` (`theirThemeVars`). Não sobra
516
+ * nada para uma folha nossa resolver - então não há o que perguntar, e a leitura da fiação
517
+ * (`readWiring`, `sheet-needed.ts`) saiu junto com a cobrança.
585
518
  *
586
- * `readWiring` varre a partir da raiz e responde "existe, em algum lugar daqui". Com dois apps
587
- * servidos, um fiado e o outro não, um responderia pelo outro - e o arquivo que este comando
588
- * acabou de escrever vai para uma pasta só de componentes, que não desambigua qual app o usa.
519
+ * O relatório da tradução, logo acima, é o que substitui: ele diz o que aconteceu com os bytes
520
+ * que foram a disco, e o que sobrou quando algo sobrou.
589
521
  */
590
- const appDirs = await detectAppDirs(root, config.pagesDir ?? "app");
591
- const oneApp = appDirs.length <= 1;
592
- const alreadyWired = sheetsThere && wiring?.scoped === true;
593
- if (need.needed && alreadyWired && !oneApp) {
594
- console.log(section(`This file needs the sheet - and this repo has it`));
595
- console.log(body(`What needs it: ${whatNeedsIt(need)}.`));
596
- console.log(body(`Measured across this repository, not per app: ${slug}'s sheet is imported and`));
597
- console.log(body(`data-ds="${slug}" is set somewhere in it. There ${appDirs.length === 2 ? "are two apps" : `are ${appDirs.length} apps`} here (${appDirs.slice(0, 2).join(", ")}${appDirs.length > 2 ? ", …" : ""}),`));
598
- console.log(body(`so if this file lands in one that does not carry them, run \`doctor\` inside it.`));
599
- }
600
- if (need.needed && alreadyWired && oneApp) {
601
- console.log(section(`This file needs the sheet - and it is already there`));
602
- console.log(body(`What needs it: ${whatNeedsIt(need)}.`));
603
- console.log(body(`This project already imports ${slug}'s ${tailwind ? "tokens.css and theme.css" : "tokens.css"} and carries data-ds="${slug}",`));
604
- console.log(body(wiring?.fontsMapped
605
- ? `and its type is mapped onto the system's family tokens. Nothing to set up.`
606
- : `so nothing to set up here. \`doctor\` says whether the type is mapped too.`));
607
- }
608
- if (need.needed && !alreadyWired) {
609
- console.log(section(`One-time setup (once per app, for "${slug}")`));
610
- /**
611
- * O PASSO ZERO VEM PRIMEIRO QUANDO A FOLHA NAO EXISTE - e desde o A1 este e' o caso comum.
612
- *
613
- * O QUE ACONTECIA: o passo 1 mandava colar `@import ".../tokens.css"` e o aviso de que o
614
- * sistema nao esta' instalado saia na ULTIMA linha, entre parenteses. Quem nunca rodou `add`
615
- * - agora um caminho normal, porque o `component` deixou de exigi-lo - lia a instrucao de
616
- * cima para baixo e importava um caminho que nao existe. O erro do build nao fala de
617
- * install: fala de um arquivo ausente.
618
- *
619
- * O motivo antes da instrucao, e nao depois dela.
620
- */
621
- if (!installedHere)
622
- console.log(body(`First: "${slug}" is not installed in this project yet, so the file the import below points at does not exist. Run \`npx synthesisui add ${slug}\` before pasting it.`));
623
- console.log(body(need.variables.length > 0
624
- ? `(what still needs the sheet here: ${need.variables.slice(0, 3).join(", ")}${need.variables.length > 3 ? `, +${need.variables.length - 3}` : ""} - your code names no value for ${need.variables.length === 1 ? "it" : "them"})`
625
- : `(what still needs the sheet here: the ${need.classes.slice(0, 3).join(", ")}${need.classes.length > 3 ? `, +${need.classes.length - 3}` : ""} ${need.classes.length === 1 ? "utility" : "utilities"}, which only this system's @theme generates)`));
626
- console.log("");
627
- console.log(body(`1. Import the design system in your GLOBAL stylesheet, e.g. app/globals.css`));
628
- console.log(body(` (the path is relative to that file - hence the leading ../):`));
629
- console.log("");
630
- console.log(snippet(imports));
631
- console.log("");
632
- console.log(body(`2. Scope your app: add data-ds="${slug}" to a ROOT element, e.g. app/layout.tsx:`));
633
- console.log("");
634
- console.log(snippet([`<body data-ds="${slug}">{children}</body>`]));
635
- if (installedHere) {
636
- console.log("");
637
- console.log(body(`(If you haven't installed the system yet, run: synthesisui add ${slug})`));
638
- }
639
- }
640
522
  /**
641
523
  * O ELEMENTO QUE SAIU NÃO ATIVA O QUE A RECEITA PEDE - e isso se diz, em vez de sumir (lei 8).
642
524
  *
@@ -709,11 +591,24 @@ export async function component(slug, name, opts) {
709
591
  ]));
710
592
  }
711
593
  else {
712
- console.log(snippet([
713
- `@import "../_synthesisui/ds/${slug}/components/${res.name}.css";`,
714
- "",
715
- `<div class="ds-${res.name}">…</div>`,
716
- ]));
594
+ /**
595
+ * O CAMINHO DOS ARTEFATOS NÃO MANDA IMPORTAR DA NOSSA PASTA - `INV-GERAL-13`, 11/09.
596
+ *
597
+ * A instrução era `@import "../_synthesisui/ds/<slug>/components/<name>.css"`, e ela criava
598
+ * exatamente a dependência que esta etapa fechou: a folha dele passava a apontar para dentro da
599
+ * nossa pasta, e o build dele quebrava no dia em que aquela pasta mudasse de nome ou saísse do
600
+ * versionamento (é o defeito medido em 24/08, com `Can't resolve` e exit code 1).
601
+ *
602
+ * O ARQUIVO JÁ É DELE, e é isso que torna a resposta simples: ele passou pela porta de tradução
603
+ * um pouco acima, então cada valor nele é um nome que o código dele declara ou o valor em si.
604
+ * Copiá-lo para onde as folhas dele vivem não perde nada, e o que fica em `_synthesisui/` segue
605
+ * sendo a nossa referência.
606
+ */
607
+ console.log(body("The stylesheet above already speaks your vocabulary -"));
608
+ console.log(body("copy it next to the markup that uses it, wherever your"));
609
+ console.log(body("stylesheets live. Nothing of ours has to be loaded:"));
610
+ console.log("");
611
+ console.log(snippet([`<div class="ds-${res.name}">…</div>`]));
717
612
  }
718
613
  console.log("");
719
614
  }
@@ -39,6 +39,26 @@ import { installedSlugs } from "./sync.js";
39
39
  * um cliente que rodou `connect` há um mês não sabe que o nome mudou.
40
40
  */
41
41
  const LEGACY_SKILLS = ["import-design-system"];
42
+ /**
43
+ * SKILLS QUE SAÍRAM DO PRODUTO - e a régua aqui é o OPOSTO da de cima.
44
+ *
45
+ * `LEGACY_SKILLS` é renomeação: o mesmo playbook mudou de nome, e apagar a pasta antiga é o conserto
46
+ * porque a nova ocupa o lugar dela. Aqui o playbook deixou de existir, e não há substituto - então a
47
+ * regra é a que ele fixou em 10/09 sobre os agentes: **desmarcar nunca apaga, mas NOMEIA o que
48
+ * ficou**. Uma pasta que some sozinha de `.claude/skills/` do repositório dele é a plataforma
49
+ * editando o repositório de alguém sem pedir.
50
+ *
51
+ * `sui-configure-ds` existia para uma coisa só: *"set up an installed design system in the app so
52
+ * its tokens actually reach the browser - the stylesheet import"*. Nada no app dele carrega nada
53
+ * nosso (`INV-GERAL-13`), então ela não tem mais objeto - e um agente que a invocar hoje vai
54
+ * executar um playbook que pede exatamente o que o produto promete nunca pedir.
55
+ */
56
+ const RETIRED_SKILLS = [
57
+ {
58
+ dir: "sui-configure-ds",
59
+ why: "retired - nothing of ours loads in your app any more",
60
+ },
61
+ ];
42
62
  /**
43
63
  * OS ARQUIVOS DO INSTALL, PÔSTOS EM DIA COM ESTE CLI.
44
64
  *
@@ -566,6 +586,27 @@ export async function connect(opts) {
566
586
  await rm(dir, { recursive: true, force: true }).catch(() => { });
567
587
  row(true, `✕ /${legacy.padEnd(21)} ${say("removed - renamed to /sui-import-ds")}`);
568
588
  }
589
+ /**
590
+ * A SKILL APOSENTADA É NOMEADA, e o arquivo fica - ver `RETIRED_SKILLS`.
591
+ *
592
+ * Com o caminho literal, porque uma linha que diz *"uma skill saiu"* devolve a ele o trabalho de
593
+ * procurar qual (pedido dele, 09/09). Só aparece para quem TEM o arquivo: avisar sobre uma pasta
594
+ * que nunca existiu é ruído.
595
+ */
596
+ for (const retired of RETIRED_SKILLS) {
597
+ const path = join(".claude", "skills", retired.dir, "SKILL.md");
598
+ if (!(await exists(join(root, path))))
599
+ continue;
600
+ /**
601
+ * O CAMINHO VAI NO `under` DA MESMA LINHA, e não numa linha própria.
602
+ *
603
+ * `row(false, …)` não IMPRIME: ele entra na contagem de *"N outras peças já atuais"*, que é o
604
+ * mecanismo que mantém a tela curta. Uma segunda chamada para o caminho fazia a linha da skill
605
+ * sair sem ele - exatamente o *"uma linha que espera ele e não diz ONDE"* que o formato de
606
+ * resposta proíbe. Medido rodando o comando: a prova de A6 pegou.
607
+ */
608
+ row(true, `✕ /${retired.dir.padEnd(21)} ${say(retired.why)}`, `${say("it stays in your repo - yours to delete")}: ${path}`);
609
+ }
569
610
  /** Se ALGUMA skill mudou nesta rodada - é o que autoriza a frase da sessão nova a citá-las. */
570
611
  let skillsMoved = false;
571
612
  for (const skill of skills) {