synthesisui 0.16.183 → 0.16.185

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.
@@ -8,7 +8,7 @@ import { emptyTally, internalSpecifiers, scanComponentsInto, tallyToInventory, }
8
8
  import { checkContracts } from "../doctor/contract-check.js";
9
9
  import { describeMissing, missingDependencies, summarizeMissing, } from "../doctor/dependencies.js";
10
10
  import { findFrozenBindings } from "../doctor/frozen.js";
11
- import { appendEvent, readEvents, summarize } from "../doctor/ledger.js";
11
+ import { appendEvent, readEvents, suggestionsFrom, summarize, } from "../doctor/ledger.js";
12
12
  import { bindingsFromDocument, countComponents, findOverrides, } from "../doctor/overrides.js";
13
13
  import { checkableName, readRequests, verifyAndCloseRequests, } from "../doctor/requests.js";
14
14
  import { diagnose, scanSource, siblingTokens, } from "../doctor/scan.js";
@@ -706,6 +706,9 @@ export async function doctor(opts) {
706
706
  ...(d.findings.some((f) => f.crossFamily)
707
707
  ? { crossFamily: d.findings.filter((f) => f.crossFamily).length }
708
708
  : {}),
709
+ ...(d.repeats.some((r) => r.token)
710
+ ? { matched: suggestionsFrom(d.repeats) }
711
+ : {}),
709
712
  }).catch(() => { });
710
713
  console.log("");
711
714
  if (opts.fix) {
@@ -796,6 +799,10 @@ export async function doctor(opts) {
796
799
  coverage: d.coverage,
797
800
  named: d.named,
798
801
  ...(crossFamily > 0 ? { crossFamily } : {}),
802
+ /** E os pares em si, para a plataforma reavaliar sem esperar outra medição - ver `matched`. */
803
+ ...(d.repeats.some((r) => r.token)
804
+ ? { matched: suggestionsFrom(d.repeats) }
805
+ : {}),
799
806
  });
800
807
  }
801
808
  if (hasSystem && fullRun) {
@@ -922,11 +929,17 @@ export async function doctor(opts) {
922
929
  for (const r of repeats) {
923
930
  const where = `${r.count}\u00d7 in ${r.files} file${r.files === 1 ? "" : "s"}`;
924
931
  const near = r.token ? null : nearestToken(table, r.literal);
925
- const named = r.token
926
- ? ` → ${r.token}`
927
- : near
928
- ? ` → nearest is ${near.name} (${near.value})`
929
- : "";
932
+ /**
933
+ * COINCIDÊNCIA NÃO É RESPOSTA - ver `crossFamily` em `scan.ts`. Para aquele `kind` o
934
+ * sistema não tem nome; o valor mora noutra família, e dizer só a seta afirma o contrário.
935
+ */
936
+ const named = r.crossFamily
937
+ ? ` → no ${r.kind} named for it · the value lives as ${r.token}`
938
+ : r.token
939
+ ? ` → ${r.token}`
940
+ : near
941
+ ? ` → nearest is ${near.name} (${near.value})`
942
+ : "";
930
943
  say(` ${r.literal.padEnd(w)} ${where}${named}`);
931
944
  }
932
945
  }
@@ -942,11 +955,13 @@ export async function doctor(opts) {
942
955
  // A dead end with a neighbour is not a dead end. Only for lengths -
943
956
  // "nearly the same blue" is the guess this tool must never make.
944
957
  const near = x.token ? null : nearestToken(table, x.literal);
945
- const named = x.token
946
- ? `→ ${x.token}`
947
- : near
948
- ? `→ nearest is ${near.name} (${near.value})`
949
- : "→ no token holds this value yet";
958
+ const named = x.crossFamily
959
+ ? `→ no ${x.kind} named for it · the value lives as ${x.token}`
960
+ : x.token
961
+ ? `→ ${x.token}`
962
+ : near
963
+ ? `→ nearest is ${near.name} (${near.value})`
964
+ : "→ no token holds this value yet";
950
965
  say(` ${String(x.line).padStart(4)} ${x.literal} ${named}`);
951
966
  }
952
967
  if (f.findings.length > shown.length) {
@@ -1264,8 +1279,19 @@ export async function doctor(opts) {
1264
1279
  * mentir por omissão. Muda a ordem e muda quantos de cada aparecem no teto de cinco.
1265
1280
  */
1266
1281
  const migrating = intent.intent === "migrate";
1267
- const named = d.repeats.filter((x) => x.token);
1268
- const unnamed = d.repeats.filter((x) => !x.token);
1282
+ /**
1283
+ * UMA COINCIDÊNCIA NÃO ENTRA NA LISTA DE BARATOS - ver `crossFamily` em `scan.ts`.
1284
+ *
1285
+ * A lista dos "cheap" existe para quem tem dez minutos: trocar o literal pelo token e pronto. Um
1286
+ * match de outra família não é isso - `--fix` o recusa, e o trabalho ali é decidir se aquele
1287
+ * `kind` merece um nome. Medido no repo do dono em 08/08: `2rem` era oferecido como raio porque
1288
+ * existe como degrau de tipo, dez vezes.
1289
+ *
1290
+ * Ele não some: vai para o grupo de quem NÃO tem nome, que é literalmente o que ele é para
1291
+ * aquele kind - e a linha diz onde o valor mora hoje.
1292
+ */
1293
+ const named = d.repeats.filter((x) => x.token && !x.crossFamily);
1294
+ const unnamed = d.repeats.filter((x) => !x.token || x.crossFamily);
1269
1295
  for (const r of named.slice(0, migrating ? 2 : 3)) {
1270
1296
  plan.push({
1271
1297
  rank: migrating ? 4 : 3,
@@ -1280,7 +1306,9 @@ export async function doctor(opts) {
1280
1306
  rank: migrating ? 3 : 4,
1281
1307
  what: near
1282
1308
  ? `${r.literal} - name it, or snap to ${near.name}`
1283
- : `${r.literal} - the system has no name for it`,
1309
+ : r.crossFamily
1310
+ ? `${r.literal} - no ${r.kind} named for it, and the value lives as ${r.token} in another family`
1311
+ : `${r.literal} - the system has no name for it`,
1284
1312
  size: `${r.files} file${r.files === 1 ? "" : "s"}`,
1285
1313
  cheap: false,
1286
1314
  });
@@ -82,7 +82,9 @@ export function compareToBaseline(d, base, now) {
82
82
  /** O que a mensagem de uma anotação diz, e é a mesma frase nos dois formatos. */
83
83
  function messageFor(f) {
84
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.`
85
+ ? f.crossFamily
86
+ ? `${f.literal} is written by hand, and this project's system has no ${f.kind} named for it. The value does exist elsewhere in the system - as \`${f.token}\`, in another family - so this is a decision to make, not a swap: name a ${f.kind}, or leave it.`
87
+ : `${f.literal} is written by hand, and this project's system already names it: var(${f.token}). Use the token.`
86
88
  : `${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
89
  }
88
90
  /**
@@ -27,6 +27,32 @@ import { join } from "node:path";
27
27
  * same contract every log file that survives a decade has.
28
28
  */
29
29
  export const LEDGER_FILE = "ledger.jsonl";
30
+ /**
31
+ * TETO, porque isto viaja e cai num campo `jsonb`.
32
+ *
33
+ * 72 no repo do dono, e a lista já vem ordenada por frequência - então o corte pega a cauda, que é
34
+ * onde mora o valor escrito uma vez em um arquivo. Um repo que passe disso perde a cauda e mantém
35
+ * inteiro o que decide alguma coisa.
36
+ */
37
+ const MATCHED_CAP = 300;
38
+ /**
39
+ * Os pares a mandar, do agregado que o doctor já monta.
40
+ *
41
+ * Só o que casou com um token: uma sugestão que não existe não tem família para comparar, e as 714
42
+ * sem token do repo do dono são 90% do volume e 0% da informação. A forma é estrutural de propósito -
43
+ * `Diagnosis` mora em `scan.ts`, e importar de lá para cá fecharia um ciclo.
44
+ */
45
+ export function suggestionsFrom(repeats) {
46
+ return repeats
47
+ .filter((r) => Boolean(r.token))
48
+ .slice(0, MATCHED_CAP)
49
+ .map((r) => ({
50
+ literal: r.literal,
51
+ kind: r.kind,
52
+ token: r.token,
53
+ count: r.count,
54
+ }));
55
+ }
30
56
  const ledgerPath = (root) => join(root, "_synthesisui", LEDGER_FILE);
31
57
  /** Keep the file from growing forever: past ~1MB, keep the newest half. A
32
58
  * trend needs recent history, not an archive. */
@@ -489,6 +489,7 @@ export function diagnose(files) {
489
489
  token: f.token,
490
490
  count: 1,
491
491
  files: new Set([f.file]),
492
+ ...(f.crossFamily ? { crossFamily: true } : {}),
492
493
  });
493
494
  }
494
495
  }
@@ -499,6 +500,7 @@ export function diagnose(files) {
499
500
  token: v.token,
500
501
  count: v.count,
501
502
  files: v.files.size,
503
+ ...(v.crossFamily ? { crossFamily: true } : {}),
502
504
  }))
503
505
  .filter((r) => r.count > 1)
504
506
  .sort((a, b) => b.count - a.count);
@@ -53,6 +53,9 @@ export async function repoStateOf(projectRoot, slug, cli) {
53
53
  ...(typeof doctor?.crossFamily === "number"
54
54
  ? { crossFamily: doctor.crossFamily }
55
55
  : {}),
56
+ ...(doctor?.matched && doctor.matched.length > 0
57
+ ? { matched: doctor.matched }
58
+ : {}),
56
59
  ...(typeof lock?.fetchedAt === "string"
57
60
  ? { installedAt: lock.fetchedAt }
58
61
  : {}),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.183",
3
+ "version": "0.16.185",
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": {