synthesisui 0.16.167 → 0.16.170
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.
- package/dist/commands/add.js +1 -0
- package/dist/commands/align.js +16 -0
- package/dist/commands/connect.js +68 -1
- package/dist/commands/import.js +4 -3
- package/dist/commands/upgrade.js +6 -1
- package/dist/doctor/reachability.js +19 -5
- package/dist/index.js +3 -1
- package/package.json +1 -1
package/dist/commands/add.js
CHANGED
|
@@ -136,6 +136,7 @@ export async function add(slug, opts) {
|
|
|
136
136
|
version: payload.version,
|
|
137
137
|
registry: base,
|
|
138
138
|
fetchedAt: new Date().toISOString(),
|
|
139
|
+
...(opts.cli ? { cli: opts.cli } : {}),
|
|
139
140
|
...(scope ? { scope } : {}),
|
|
140
141
|
...(usage.length > 0 ? { usage } : {}),
|
|
141
142
|
};
|
package/dist/commands/align.js
CHANGED
|
@@ -94,6 +94,22 @@ opts = {}) {
|
|
|
94
94
|
says: `${events.length} checked write${events.length === 1 ? "" : "s"} recorded here and not sent - the platform is scoring this repo on older evidence.`,
|
|
95
95
|
run: "npx synthesisui sync",
|
|
96
96
|
});
|
|
97
|
+
/**
|
|
98
|
+
* O DÉCIMO ESTADO: os ARQUIVOS deste install foram escritos por um CLI anterior.
|
|
99
|
+
*
|
|
100
|
+
* Os nove primeiros nunca cobriram isto, e ele é sorrateiro: o sistema pode estar na ÚLTIMA versão
|
|
101
|
+
* do DS e ter uma pasta escrita por um CLI de dez versões atrás. Em 07/08 foi a doutrina - o `add`
|
|
102
|
+
* de hoje escreve `doctrine.json` e o de ontem materializava dois `.md` -, e o `upgrade` sai sem
|
|
103
|
+
* fazer nada quando não há gap de versão, então nenhum comando alcançava aquele estado.
|
|
104
|
+
*
|
|
105
|
+
* O `connect` conserta na hora (ver `refreshInstall`), e esta linha existe para quem ainda não
|
|
106
|
+
* rodou: a verificação de abertura é o único lugar que fala sem ser perguntado.
|
|
107
|
+
*/
|
|
108
|
+
if (cli && lock.cli && lock.cli !== cli)
|
|
109
|
+
out.push({
|
|
110
|
+
says: `the files under _synthesisui/ds/${lock.slug} were written by CLI ${lock.cli} and you are running ${cli} - the design system's version has not changed, but what this CLI writes into that folder has.`,
|
|
111
|
+
run: "npx synthesisui connect",
|
|
112
|
+
});
|
|
97
113
|
if (cli && lock.version == null)
|
|
98
114
|
out.push({
|
|
99
115
|
says: `"${lock.slug}" has no version recorded, so nothing can tell whether the CSS in this repo is the one the platform publishes.`,
|
package/dist/commands/connect.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
1
|
+
import { mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { wireAgent } from "../agent-wiring.js";
|
|
4
4
|
import { blockHomes, syncClaudeMd } from "../claude-md.js";
|
|
5
5
|
import { body, paint, section, snippet } from "../output.js";
|
|
6
6
|
import { IMPORT_SKILL, IMPORT_SKILL_PATH } from "../skill-import.js";
|
|
7
7
|
import { INIT_SKILL, INIT_SKILL_PATH } from "../skill-init.js";
|
|
8
|
+
import { add } from "./add.js";
|
|
8
9
|
import { ci } from "./ci.js";
|
|
9
10
|
import { MCP_TOOL_COUNT } from "./mcp.js";
|
|
10
11
|
/**
|
|
@@ -28,16 +29,82 @@ import { MCP_TOOL_COUNT } from "./mcp.js";
|
|
|
28
29
|
* um cliente que rodou `connect` há um mês não sabe que o nome mudou.
|
|
29
30
|
*/
|
|
30
31
|
const LEGACY_SKILLS = ["import-design-system"];
|
|
32
|
+
/**
|
|
33
|
+
* OS ARQUIVOS DO INSTALL, PÔSTOS EM DIA COM ESTE CLI.
|
|
34
|
+
*
|
|
35
|
+
* A fiação era reconciliada (hook, MCP, CLAUDE.md, skills) e os arquivos do sistema instalado não.
|
|
36
|
+
* Medido em 07/08: o `add` de hoje serve a doutrina por ferramenta e escreve `doctrine.json`; o de
|
|
37
|
+
* ontem materializava `rules.md` e `philosophy.md`. Um repo que rodou `connect` e `sync` ficava com os
|
|
38
|
+
* dois `.md` velhos, sem `doctrine.json`, sem escopo no `.lock` - e o `upgrade` SAI SEM FAZER NADA
|
|
39
|
+
* quando não há gap de versão, então nenhum dos seis comandos alcançava esse estado.
|
|
40
|
+
*
|
|
41
|
+
* O `connect` é o lugar honesto: é o comando que a pessoa roda quando atualiza o CLI, e a pergunta que
|
|
42
|
+
* ela já está fazendo é "me põe em dia".
|
|
43
|
+
*
|
|
44
|
+
* REMATERIALIZA A MESMA VERSÃO, nunca uma nova: `add` com a `version` que o `.lock` pina. Subir de
|
|
45
|
+
* versão é decisão dela e continua sendo o `upgrade`.
|
|
46
|
+
*
|
|
47
|
+
* SILENCIOSO QUANDO JÁ ESTÁ EM DIA, e sem rede não faz nada - um `connect` que falha por estar num
|
|
48
|
+
* avião seria pior que a defasagem que ele conserta.
|
|
49
|
+
*/
|
|
50
|
+
async function refreshInstall(root, cli, registry) {
|
|
51
|
+
const dsDir = join(root, "_synthesisui", "ds");
|
|
52
|
+
const names = await readdir(dsDir, { withFileTypes: true }).catch(() => []);
|
|
53
|
+
for (const entry of names) {
|
|
54
|
+
if (!entry.isDirectory())
|
|
55
|
+
continue;
|
|
56
|
+
const raw = await readFile(join(dsDir, entry.name, ".lock"), "utf8").catch(() => null);
|
|
57
|
+
if (!raw)
|
|
58
|
+
continue;
|
|
59
|
+
let lock;
|
|
60
|
+
try {
|
|
61
|
+
lock = JSON.parse(raw);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
/** Um DS adotado é descrito aqui e pertencido em outro lugar - não há o que rematerializar. */
|
|
67
|
+
if (!lock.slug || lock.adopted || typeof lock.version !== "number")
|
|
68
|
+
continue;
|
|
69
|
+
if (lock.cli === cli)
|
|
70
|
+
continue;
|
|
71
|
+
const done = await add(lock.slug, {
|
|
72
|
+
...(registry ? { registry } : {}),
|
|
73
|
+
dir: root,
|
|
74
|
+
version: lock.version,
|
|
75
|
+
setupHints: false,
|
|
76
|
+
cli,
|
|
77
|
+
}).then(() => true, () => false);
|
|
78
|
+
if (!done)
|
|
79
|
+
return null;
|
|
80
|
+
return { slug: lock.slug, ...(lock.cli ? { was: lock.cli } : {}) };
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
31
84
|
export async function connect(opts) {
|
|
32
85
|
const root = opts.dir ?? process.cwd();
|
|
33
86
|
// Both unless one is explicitly turned off - somebody who says `--no-hook`
|
|
34
87
|
// means it, and somebody who says nothing wants the thing to work.
|
|
35
88
|
const want = { hook: opts.hook !== false, mcp: opts.mcp !== false };
|
|
89
|
+
/**
|
|
90
|
+
* OS ARQUIVOS DO INSTALL ANTES DA FIAÇÃO: o `add` reescreve o `CLAUDE.md` por dentro, e o
|
|
91
|
+
* `syncClaudeMd` abaixo tem que ser o último a falar sobre ele.
|
|
92
|
+
*/
|
|
93
|
+
const refreshed = await refreshInstall(root, opts.version, opts.registry).catch(() => null);
|
|
36
94
|
const wired = await wireAgent(root, opts.version, want);
|
|
37
95
|
// The block reads the settings we just wrote, so it must be regenerated
|
|
38
96
|
// after them, not before.
|
|
39
97
|
await syncClaudeMd(root);
|
|
40
98
|
console.log(section("Connected"));
|
|
99
|
+
/**
|
|
100
|
+
* O QUE ESTE CLI REESCREVEU NA PASTA DO SISTEMA - dito primeiro, porque é o que a pessoa não sabia
|
|
101
|
+
* que estava devendo. Ela rodou `connect` para atualizar a fiação; os arquivos do install estarem
|
|
102
|
+
* velhos era invisível, e o `upgrade` não alcançava (ver `refreshInstall`).
|
|
103
|
+
*/
|
|
104
|
+
if (refreshed)
|
|
105
|
+
console.log(body(`✓ _synthesisui/ds/${refreshed.slug}/ rewritten by this CLI${refreshed.was
|
|
106
|
+
? ` - it was written by ${refreshed.was}`
|
|
107
|
+
: " - it carried no CLI version, so it predates this"}`));
|
|
41
108
|
if (want.hook) {
|
|
42
109
|
console.log(body(wired.hook === "added"
|
|
43
110
|
? "✓ .claude/settings.json the check now runs after every write"
|
package/dist/commands/import.js
CHANGED
|
@@ -982,10 +982,11 @@ export async function takeCensus(root, opts) {
|
|
|
982
982
|
* percorrer o repo inteiro de novo por um dado que este laço já tem na mão. Ver `reachability.ts`.
|
|
983
983
|
*/
|
|
984
984
|
const edges = new Map();
|
|
985
|
-
|
|
985
|
+
/** Por raiz: `@/` é uma pasta diferente em cada app - ver `reachabilityOf`. */
|
|
986
|
+
const graphRoots = [];
|
|
986
987
|
for (const u of opts?.usage ?? []) {
|
|
987
988
|
const uInternal = await internalSpecifiers(u.path);
|
|
988
|
-
|
|
989
|
+
graphRoots.push({ path: u.path, aliases: await aliasesOf(u.path) });
|
|
989
990
|
for await (const file of walk(u.path)) {
|
|
990
991
|
const src = await readFile(file, "utf8").catch(() => "");
|
|
991
992
|
if (!src)
|
|
@@ -1231,7 +1232,7 @@ export async function takeCensus(root, opts) {
|
|
|
1231
1232
|
* O GRAFO SÓ FECHA DEPOIS QUE TODOS OS ARQUIVOS FORAM VISTOS: um especificador só resolve para um
|
|
1232
1233
|
* arquivo que já esteja na lista, então isto não pode rodar dentro do laço que a preenche.
|
|
1233
1234
|
*/
|
|
1234
|
-
const reachability = edges.size > 0 ? await reachabilityOf({ edges,
|
|
1235
|
+
const reachability = edges.size > 0 ? await reachabilityOf({ edges, roots: graphRoots }) : null;
|
|
1235
1236
|
const coverage = summarizeCoverage(shapes, defined.length || componentFiles, Object.keys(looks).length, Object.values(looks).reduce((n, look) => n +
|
|
1236
1237
|
Object.keys(look.base).length +
|
|
1237
1238
|
Object.keys(look.dark ?? {}).length +
|
package/dist/commands/upgrade.js
CHANGED
|
@@ -125,7 +125,12 @@ export async function upgrade(slug, opts) {
|
|
|
125
125
|
}
|
|
126
126
|
// 1. re-materialize the artifacts (v<latest>/ + root re-exports + .lock);
|
|
127
127
|
// setup hints suppressed - an upgrade means the app is already wired.
|
|
128
|
-
await add(slug, {
|
|
128
|
+
await add(slug, {
|
|
129
|
+
registry: opts.registry,
|
|
130
|
+
dir: root,
|
|
131
|
+
setupHints: false,
|
|
132
|
+
...(opts.cli ? { cli: opts.cli } : {}),
|
|
133
|
+
});
|
|
129
134
|
// 2. regenerate YOUR materialized components (the ones `component` wrote)
|
|
130
135
|
const config = await readProjectConfig(root);
|
|
131
136
|
const regenerated = [];
|
|
@@ -215,13 +215,17 @@ export async function reachabilityOf(input) {
|
|
|
215
215
|
!/\/[a-z.-]+\.config\.[jt]sx?$/.test(f)));
|
|
216
216
|
const graph = new Map();
|
|
217
217
|
let unresolved = 0;
|
|
218
|
+
/** A raiz MAIS LONGA que contém o arquivo - um app dentro de outro resolve pelo mais específico. */
|
|
219
|
+
const rootOf = (file) => input.roots
|
|
220
|
+
.filter((r) => file.startsWith(`${r.path}/`))
|
|
221
|
+
.sort((a, b) => b.path.length - a.path.length)[0]?.aliases ?? [];
|
|
218
222
|
for (const [file, specs] of input.edges) {
|
|
219
223
|
if (!known.has(file))
|
|
220
224
|
continue;
|
|
225
|
+
const aliases = rootOf(file);
|
|
221
226
|
const out = [];
|
|
222
227
|
for (const spec of specs) {
|
|
223
|
-
const mine = spec.startsWith(".") ||
|
|
224
|
-
input.aliases.some((a) => spec.startsWith(a.prefix));
|
|
228
|
+
const mine = spec.startsWith(".") || aliases.some((a) => spec.startsWith(a.prefix));
|
|
225
229
|
/**
|
|
226
230
|
* Pacote de terceiro não é aresta deste repo. Mas um apelido LOCAL que nenhum alias casa é uma
|
|
227
231
|
* aresta PERDIDA - e era descartado aqui em silêncio, o que subia o número de mortos sem a
|
|
@@ -232,7 +236,7 @@ export async function reachabilityOf(input) {
|
|
|
232
236
|
unresolved += 1;
|
|
233
237
|
continue;
|
|
234
238
|
}
|
|
235
|
-
const hit = await toFile(known, file, spec,
|
|
239
|
+
const hit = await toFile(known, file, spec, aliases, isFile);
|
|
236
240
|
if (hit)
|
|
237
241
|
out.push(hit);
|
|
238
242
|
/**
|
|
@@ -256,11 +260,21 @@ export async function reachabilityOf(input) {
|
|
|
256
260
|
if (!seen.has(next))
|
|
257
261
|
queue.push(next);
|
|
258
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* ALCANÇADOS DENTRO DO UNIVERSO, e não `seen.size`.
|
|
265
|
+
*
|
|
266
|
+
* A travessia atravessa para fora: um alias que aponta para outro pacote do monorepo resolve no
|
|
267
|
+
* disco e entra em `seen`, mas não está em `known` - o universo é o que foi varrido. Contar
|
|
268
|
+
* `seen.size` inflava `reached` com nós de fora e fazia `reached + unreached` não fechar com
|
|
269
|
+
* `files`, o que produziu um "19% morto" que na verdade é 41% (dono, 07/08). Um número que não
|
|
270
|
+
* fecha com a própria soma é um número que ninguém pode checar.
|
|
271
|
+
*/
|
|
272
|
+
const unreached = [...known].filter((f) => !seen.has(f)).sort();
|
|
259
273
|
return {
|
|
260
274
|
entries: entries.length,
|
|
261
275
|
files: known.size,
|
|
262
|
-
reached:
|
|
263
|
-
unreached
|
|
276
|
+
reached: known.size - unreached.length,
|
|
277
|
+
unreached,
|
|
264
278
|
unresolved,
|
|
265
279
|
};
|
|
266
280
|
}
|
package/dist/index.js
CHANGED
|
@@ -241,6 +241,7 @@ async function main() {
|
|
|
241
241
|
case "connect":
|
|
242
242
|
await connect({
|
|
243
243
|
dir,
|
|
244
|
+
...(registry ? { registry } : {}),
|
|
244
245
|
version: CLI_VERSION,
|
|
245
246
|
hook: flags.hook !== false,
|
|
246
247
|
mcp: flags.mcp !== false,
|
|
@@ -379,7 +380,7 @@ async function main() {
|
|
|
379
380
|
return;
|
|
380
381
|
}
|
|
381
382
|
}
|
|
382
|
-
await add(slug, { registry, dir, version });
|
|
383
|
+
await add(slug, { registry, dir, version, cli: CLI_VERSION });
|
|
383
384
|
break;
|
|
384
385
|
}
|
|
385
386
|
case "login":
|
|
@@ -512,6 +513,7 @@ async function main() {
|
|
|
512
513
|
await upgrade(slug, {
|
|
513
514
|
registry,
|
|
514
515
|
dir,
|
|
516
|
+
cli: CLI_VERSION,
|
|
515
517
|
force: flags.force === true,
|
|
516
518
|
from: typeof flags.from === "string" ? Number(flags.from) : undefined,
|
|
517
519
|
});
|
package/package.json
CHANGED