synthesisui 0.16.462 → 0.16.463

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,5 +1,5 @@
1
1
  import { execFile } from "node:child_process";
2
- import { stat } from "node:fs/promises";
2
+ import { readdir, stat } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
4
  import { promisify } from "node:util";
5
5
  const run = promisify(execFile);
@@ -25,8 +25,44 @@ async function listed(root) {
25
25
  if (line.trim())
26
26
  out.push(line.trim());
27
27
  }
28
+ if (out.length === 0 && !(await isGitRepo(root)))
29
+ return walked(root);
28
30
  return [...new Set(out)];
29
31
  }
32
+ /**
33
+ * FORA DO GIT, A PASTA É A LISTA - rodada 3 do refinamento, 27/09. O leigo-3 nasceu sem repositório, e
34
+ * as duas perguntas ao git voltavam vazias: o fim do turno não checava arquivo nenhum, nunca - o ledger
35
+ * sem uma linha `hook`, o envio com `count: 0`, o dashboard inteiro do agente sem checagem. Sem git, a
36
+ * resposta é andar pela pasta; o filtro pelo relógio do turno (em `changedSince`) continua valendo.
37
+ */
38
+ const WALK_SKIP = new Set(["node_modules", ".next", "dist", "build", "out", "_synthesisui", "coverage"]);
39
+ const WALK_MAX = 5000;
40
+ async function isGitRepo(root) {
41
+ const { stdout } = await run("git", ["rev-parse", "--is-inside-work-tree"], { cwd: root }).catch(() => ({
42
+ stdout: "",
43
+ }));
44
+ return stdout.trim() === "true";
45
+ }
46
+ async function walked(root) {
47
+ const out = [];
48
+ const go = async (rel) => {
49
+ if (out.length >= WALK_MAX)
50
+ return;
51
+ for (const entry of await readdir(join(root, rel), { withFileTypes: true }).catch(() => [])) {
52
+ if (entry.name.startsWith(".") || WALK_SKIP.has(entry.name))
53
+ continue;
54
+ const next = rel ? `${rel}/${entry.name}` : entry.name;
55
+ if (entry.isDirectory())
56
+ await go(next);
57
+ else if (entry.isFile())
58
+ out.push(next);
59
+ if (out.length >= WALK_MAX)
60
+ return;
61
+ }
62
+ };
63
+ await go("");
64
+ return out;
65
+ }
30
66
  /**
31
67
  * OS ARQUIVOS INTERESSANTES QUE MUDARAM DESDE `since`, do mais recente para o mais antigo.
32
68
  *
@@ -1,3 +1,4 @@
1
+ import { armTurnClock } from "./hook.js";
1
2
  import { mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
2
3
  import { dirname, join } from "node:path";
3
4
  import { codexPinBefore, exists, wireAgent } from "../agent-wiring.js";
@@ -477,6 +478,9 @@ export async function connect(opts) {
477
478
  ? say("✓ what the check records is sent to your dashboard at the end of each turn - your system changes only from a commit on a clean main")
478
479
  : say("· what the check records stays on this machine - run `npx synthesisui sync` to send it"));
479
480
  }
481
+ /** O relógio do turno nasce aqui - ver `armTurnClock`: o primeiro turno depois do `connect` é checado. */
482
+ if (want.hook && wired.hook !== "skipped")
483
+ await armTurnClock(root);
480
484
  if (want.hook && wired.hook !== "skipped") {
481
485
  row(wired.hook !== "already there", (() => {
482
486
  return wired.hook === "added"
@@ -88,6 +88,17 @@ const startClock = (root) => writeFile(ledgerPath(root), "", { flag: "a" }).catc
88
88
  const TURN_CLOCK = "_synthesisui/.turn-clock";
89
89
  const turnClockOf = (root) => stat(join(root, TURN_CLOCK)).then((s) => s.mtimeMs, () => 0);
90
90
  const moveTurnClock = (root) => writeFile(join(root, TURN_CLOCK), "", "utf8").catch(() => { });
91
+ /**
92
+ * O RELÓGIO NASCE NO `connect`, E NÃO NO PRIMEIRO TURNO - rodada 3 do refinamento, 27/09.
93
+ *
94
+ * Sem relógio, o primeiro fim de turno só o acertava e calava (ver `startClock`). Isso protegia o
95
+ * repositório com trabalho antigo em aberto, e cobrava um preço que ninguém tinha medido: num projeto
96
+ * NOVO o primeiro turno é o trabalho inteiro. O dashboard do leigo-3 - cinco arquivos - e as telas das
97
+ * rodadas 1 e 2 nunca foram checados: o `ledger.jsonl` tinha zero linhas `hook` e o envio marcava
98
+ * `count: 0`. Armado na instalação, o relógio deixa de fora o que veio antes do `connect` (a intenção
99
+ * de antes) e vê tudo o que veio depois. `flag: "wx"`: um relógio que já existe não recua.
100
+ */
101
+ export const armTurnClock = (root) => writeFile(join(root, TURN_CLOCK), "", { encoding: "utf8", flag: "wx" }).catch(() => { });
91
102
  /**
92
103
  * QUANTOS ARQUIVOS UM COMANDO DE SHELL PODE FAZER O PRODUTO RELATAR DE UMA VEZ.
93
104
  *
@@ -630,8 +630,22 @@ export class ThemeVocab extends Set {
630
630
  }
631
631
  const sameValue = (a, b) => a.trim().toLowerCase().replace(/\s+/g, " ") === b.trim().toLowerCase().replace(/\s+/g, " ");
632
632
  /** A chave de cor que ele escreve: pelo `ThemeVocab` quando há valores, pelo nome quando não. */
633
+ /**
634
+ * O DEGRAU DA PALETA PELO NOME - `{color.coral.700}` -> `coral-700` - rodada 3 do refinamento, 27/09.
635
+ *
636
+ * O badge do Zephyr pede degraus (`{color.coral.700}`, `{color.teal.700}`…), o projeto do leigo-3 declara
637
+ * `--color-coral-700` com o mesmo valor, e o badge saía com `#c2410c` cru: pelo nome só os papéis
638
+ * semânticos eram traduzidos, e pelo valor só quando UMA variável segura aquele valor - ali duas seguravam.
639
+ * Só vale pelo `ThemeVocab`, que confere o valor.
640
+ */
641
+ const paletteKey = (v) => {
642
+ const m = /^\{color\.([a-z0-9-]+)\.([a-z0-9-]+)\}$/.exec(v.trim());
643
+ if (!m || m[1] === "semantic" || m[1] === "series")
644
+ return null;
645
+ return `${m[1]}-${m[2]}`;
646
+ };
633
647
  const theirColorKey = (value, themeVars) => {
634
- const key = colorKey(value);
648
+ const key = colorKey(value) ?? (themeVars instanceof ThemeVocab ? paletteKey(value) : null);
635
649
  if (themeVars instanceof ThemeVocab)
636
650
  return themeVars.nameFor(value, "color", key);
637
651
  return key && minted(themeVars, `--color-${key}`) ? key : null;
package/dist/guarantee.js CHANGED
@@ -1,5 +1,5 @@
1
- import { readFile, writeFile } from "node:fs/promises";
2
- import { join } from "node:path";
1
+ import { readFile, stat, writeFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
3
  import { hasHook } from "./agent-wiring.js";
4
4
  import { ungovernedIn } from "./governed.js";
5
5
  /**
@@ -104,7 +104,14 @@ system) {
104
104
  * invisível: um arquivo novo nunca apagou nada, então a checagem cala com razão e a pessoa lê
105
105
  * esse silêncio como aprovação.
106
106
  */
107
- lines.push(plain("What a file no longer has is measured against your last commit. A file git has never seen has no before, so nothing is reported as removed there."));
107
+ /**
108
+ * SEM GIT, A METADE DO "ANTES" NÃO EXISTE - rodada 3 do refinamento, 27/09. O leigo-3 nasceu sem
109
+ * repositório (o `create-next-app` dele não criou um), e a frase de baixo falava de "your last commit"
110
+ * numa pasta que não tem commit nenhum. Dizer é o que transforma o silêncio em um passo.
111
+ */
112
+ lines.push(plain((await inGitRepo(root))
113
+ ? "What a file no longer has is measured against your last commit. A file git has never seen has no before, so nothing is reported as removed there."
114
+ : "This folder is not in a git repository, so nothing can be measured against a last commit - what a file lost goes unseen. `git init` and a first commit turn that on."));
108
115
  lines.push(system.utilities.size > 0
109
116
  ? {
110
117
  text: "A value counts as coming from your system in two spellings: `var(--token)`, and the {n} utility names your own theme generates - `bg-primary`, `p-6`, `rounded-lg`.",
@@ -169,3 +176,16 @@ export async function guaranteeIfNew(root, system) {
169
176
  await writeFile(join(root, SAID), `${answer.fingerprint}\n`, "utf8").catch(() => { });
170
177
  return answer.lines;
171
178
  }
179
+ /** Esta pasta, ou uma acima dela, tem `.git`? - um monorepo tem o `.git` na raiz, não no app. */
180
+ async function inGitRepo(root) {
181
+ let at = root;
182
+ for (let i = 0; i < 40; i += 1) {
183
+ if (await stat(join(at, ".git")).then(() => true, () => false))
184
+ return true;
185
+ const up = dirname(at);
186
+ if (up === at)
187
+ return false;
188
+ at = up;
189
+ }
190
+ return false;
191
+ }
@@ -264,7 +264,12 @@
264
264
  * animação, e os componentes com `as` passam a ter o tipo genérico - rodada 1 do refinamento. Uma pasta
265
265
  * escrita antes ensina o agente a colar uma fonte que não carrega.
266
266
  */
267
- export const MATERIALISER_SINCE = "0.16.462";
267
+ /**
268
+ * 0.16.462 -> 0.16.463 em 27/09, e o passo 1 dá **SIM**: o gerador que o `upgrade` usa passa a achar pelo
269
+ * nome os degraus da paleta que o projeto declara (`{color.coral.700}` -> `bg-coral-700`) - o badge do
270
+ * Zephyr saía com quatro cores cruas (rodada 3 do refinamento).
271
+ */
272
+ export const MATERIALISER_SINCE = "0.16.463";
268
273
  /**
269
274
  * A ÚLTIMA VERSÃO EM QUE O QUE O HOOK RODA MUDOU.
270
275
  *
@@ -420,7 +425,13 @@ export const COUNTED_DIFFERENTLY = "this run counts a value as named only when Y
420
425
  * hand" no `@theme` que o leigo copiou do GUIDE). Um hook fixado antes continua acusando o vocabulário do
421
426
  * projeto como valor à mão, em todo turno.
422
427
  */
423
- export const CHECKER_SINCE = "0.16.458";
428
+ /**
429
+ * 0.16.458 -> 0.16.463 em 27/09, e o passo 1 dá **SIM**: o fim do turno passa a checar o primeiro turno
430
+ * depois do `connect` (o relógio nasce na instalação) e a enxergar os arquivos de uma pasta sem git -
431
+ * rodada 3 do refinamento: o dashboard do leigo-3 inteiro nunca foi checado. Um hook anterior segue calado
432
+ * nos dois casos.
433
+ */
434
+ export const CHECKER_SINCE = "0.16.463";
424
435
  /**
425
436
  * A ÚLTIMA VERSÃO EM QUE OS LEITORES PASSARAM A PRODUZIR UM CENSO DIFERENTE.
426
437
  *
package/dist/registry.js CHANGED
@@ -53,7 +53,13 @@ export async function fetchDesignSystem(base, slug, version) {
53
53
  url.searchParams.set("version", String(version));
54
54
  const res = await request(url.toString());
55
55
  if (res.status === 404) {
56
- throw new RegistryError(`No design system published with slug "${slug}"${version != null ? ` at version v${version}` : ""}. Run \`synthesisui list\` to see what's available.`);
56
+ throw new RegistryError(
57
+ /**
58
+ * A CONTA É A PRIMEIRA SUSPEITA - rodada 3 do refinamento, 27/09. O fork `zephyr` do dono estava na
59
+ * conta do navegador, e o terminal, logado em outra: o registry só entrega um sistema que não é
60
+ * público à conta dona, e a frase dizia "no design system published". Ele parou ali.
61
+ */
62
+ `No design system "${slug}"${version != null ? ` at version v${version}` : ""} that this machine can see. If it is a system in your account, this machine may be signed in to another one - run \`npx synthesisui@latest login\` with the account that owns it. Otherwise, \`synthesisui list\` shows what is public.`);
57
63
  }
58
64
  if (!res.ok) {
59
65
  throw new RegistryError(`Registry responded ${res.status} while fetching "${slug}".`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.462",
3
+ "version": "0.16.463",
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": {