synthesisui 0.16.356 → 0.16.358
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/align.js +36 -8
- package/dist/commands/connect.js +27 -23
- package/dist/doctor/their-names.js +50 -9
- package/dist/output.js +28 -0
- package/package.json +1 -1
package/dist/commands/align.js
CHANGED
|
@@ -9,7 +9,7 @@ import { readRequests } from "../doctor/requests.js";
|
|
|
9
9
|
import { declaredReference } from "../group-role.js";
|
|
10
10
|
import { CHECKER_SINCE, installedBehind, MATERIALISER_SINCE, READER_SINCE, } from "../install-marks.js";
|
|
11
11
|
import { measuredScope } from "../measured-scope.js";
|
|
12
|
-
import { body } from "../output.js";
|
|
12
|
+
import { body, bodyWrapped, paint, section, snippet } from "../output.js";
|
|
13
13
|
import { SKILLS } from "../skills.js";
|
|
14
14
|
/**
|
|
15
15
|
* QUAL CLI MEDIU O CENSO EM DISCO - e era o `reader`, um inteiro, até 11/08.
|
|
@@ -625,8 +625,38 @@ export async function nextStepFor(root) {
|
|
|
625
625
|
* chutá-lo seria pior que pedir para olhar.
|
|
626
626
|
*/
|
|
627
627
|
return measured
|
|
628
|
-
?
|
|
629
|
-
|
|
628
|
+
? {
|
|
629
|
+
sentence: "This repo has been measured and has no system installed here. If you already imported, the system is in your account: `synthesisui list --mine`, then `synthesisui add <slug>`.",
|
|
630
|
+
headline: "Your system is already in your account. Bring it in here:",
|
|
631
|
+
run: ["synthesisui list --mine", "synthesisui add <slug>"],
|
|
632
|
+
why: "Re-measuring would cost minutes and change nothing the platform already knows.",
|
|
633
|
+
}
|
|
634
|
+
: {
|
|
635
|
+
sentence: 'This repo has no design system contract yet. Ask me: "import my design system."',
|
|
636
|
+
headline: "Open a new agent session in this repo and say:",
|
|
637
|
+
say: "import my design system",
|
|
638
|
+
why: "I read what is already in your code - the colours, the type, the shapes, the components. Nothing is invented.",
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* A AÇÃO RECOMENDADA, DESENHADA PARA SER VISTA - uma, com título, o literal copiável e o por quê.
|
|
643
|
+
*
|
|
644
|
+
* A hierarquia usa a paleta que já existe e no papel que ela declara: `blue` marca o que é VIVO (o
|
|
645
|
+
* que ele diz ou roda), `dim` é prosa secundária (o por quê e a exigência de sessão nova). A seção
|
|
646
|
+
* traz as linhas em branco de graça, que é o "respiro" pedido.
|
|
647
|
+
*/
|
|
648
|
+
export function renderNextStep(next, freshSession) {
|
|
649
|
+
const lines = [section("Do this next"), body(next.headline), ""];
|
|
650
|
+
if (next.say)
|
|
651
|
+
lines.push(paint.blue(snippet([next.say])), "");
|
|
652
|
+
if (next.run?.length)
|
|
653
|
+
lines.push(paint.blue(snippet(next.run)), "");
|
|
654
|
+
lines.push(...bodyWrapped(next.why).map(paint.dim));
|
|
655
|
+
if (freshSession)
|
|
656
|
+
lines.push(...bodyWrapped(freshSession.mcp
|
|
657
|
+
? "A new session is what loads the skills and tools just installed, and the project's tools ask for approval once - say yes."
|
|
658
|
+
: "A new session is what loads the skills just installed.").map(paint.dim));
|
|
659
|
+
return lines.join("\n");
|
|
630
660
|
}
|
|
631
661
|
/**
|
|
632
662
|
* A CAUDA DE UM COMANDO QUE TERMINOU: o que ainda está fora, ou nada.
|
|
@@ -642,10 +672,8 @@ export async function reportWhatIsLeft(root, opts = {}) {
|
|
|
642
672
|
const items = await misalignments(root, opts).catch(() => []);
|
|
643
673
|
if (items.length === 0) {
|
|
644
674
|
const next = await nextStepFor(root).catch(() => null);
|
|
645
|
-
if (next)
|
|
646
|
-
console.log(
|
|
647
|
-
console.log(body(next));
|
|
648
|
-
}
|
|
675
|
+
if (next)
|
|
676
|
+
console.log(renderNextStep(next, opts.freshSession));
|
|
649
677
|
return;
|
|
650
678
|
}
|
|
651
679
|
console.log("");
|
|
@@ -673,5 +701,5 @@ export async function align(opts) {
|
|
|
673
701
|
*/
|
|
674
702
|
const next = await nextStepFor(root).catch(() => null);
|
|
675
703
|
if (next)
|
|
676
|
-
console.log(next);
|
|
704
|
+
console.log(next.sentence);
|
|
677
705
|
}
|
package/dist/commands/connect.js
CHANGED
|
@@ -153,7 +153,13 @@ version) {
|
|
|
153
153
|
if (next !== current) {
|
|
154
154
|
const was = pinnedInHook(current);
|
|
155
155
|
console.log("");
|
|
156
|
-
|
|
156
|
+
/**
|
|
157
|
+
* DITA, E EM SEGUNDO PLANO. O dono pediu em 03/09 que ela saísse da tela; ela FICA porque
|
|
158
|
+
* reporta uma escrita FORA do repositório, e "um `.zshrc` que muda sem uma palavra é a
|
|
159
|
+
* definição de invasivo" é decisão dele mesmo, de 11/08. O que muda é o peso: cinza claro,
|
|
160
|
+
* como toda prosa secundária, para ela informar sem disputar com a ação recomendada.
|
|
161
|
+
*/
|
|
162
|
+
console.log(body(paint.dim(`Updating the terminal check in ${rc} - ${was ? `it was pinned to ${was}` : "it was on an unpinned version"}, moving it to ${version}.`)));
|
|
157
163
|
await writeFile(rc, next, "utf8").catch(() => { });
|
|
158
164
|
}
|
|
159
165
|
return;
|
|
@@ -379,18 +385,19 @@ export async function connect(opts) {
|
|
|
379
385
|
: `✓ ${skill.label.padEnd(22)} updated to this CLI's pipeline`));
|
|
380
386
|
}
|
|
381
387
|
/**
|
|
382
|
-
* O CI: escrito
|
|
383
|
-
*
|
|
388
|
+
* O CI: escrito quando pedido, e NÃO MAIS OFERECIDO aqui.
|
|
389
|
+
*
|
|
390
|
+
* A oferta era verdadeira e estava no lugar errado: ela ocupava o espaço imediatamente antes da
|
|
391
|
+
* ação recomendada, num repositório onde a pessoa ainda não tem sistema nenhum - então o produto
|
|
392
|
+
* sugeria um portão de PR para uma dívida que ainda não existe. Ela mora agora na documentação do
|
|
393
|
+
* `connect` (dono, 03/09: "vamos deixar isso para uma documentação dentro do synthesisui").
|
|
394
|
+
*
|
|
395
|
+
* `--ci` continua fazendo tudo que fazia: quem pede, recebe.
|
|
384
396
|
*/
|
|
385
397
|
if (opts.ci) {
|
|
386
398
|
console.log("");
|
|
387
399
|
await ci({ dir: root, write: true });
|
|
388
400
|
}
|
|
389
|
-
else {
|
|
390
|
-
console.log("");
|
|
391
|
-
console.log(body(paint.dim("Your PRs can carry this too: annotations on the exact line, and a check that fails only when the count goes UP - never on the debt you already have.")));
|
|
392
|
-
console.log(paint.blue(snippet(["npx synthesisui@latest ci"])));
|
|
393
|
-
}
|
|
394
401
|
/** A mesma versão que a fiação do editor recebe - os dois pinam no mesmo número. */
|
|
395
402
|
await offerShellHook(opts.shell === true, opts.version);
|
|
396
403
|
/**
|
|
@@ -409,16 +416,13 @@ export async function connect(opts) {
|
|
|
409
416
|
const needsRestart = moved(wired.hook) ||
|
|
410
417
|
moved(wired.session) ||
|
|
411
418
|
(Array.isArray(wired.mcp) && wired.mcp.some((m) => moved(m.status)));
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
await reportWhatIsLeft(root, { cli: opts.version });
|
|
419
|
+
const mcpMoved = Boolean(want.mcp &&
|
|
420
|
+
Array.isArray(wired.mcp) &&
|
|
421
|
+
wired.mcp.some((m) => moved(m.status)));
|
|
422
|
+
await reportWhatIsLeft(root, {
|
|
423
|
+
cli: opts.version,
|
|
424
|
+
...(needsRestart ? { freshSession: { mcp: mcpMoved } } : {}),
|
|
425
|
+
});
|
|
422
426
|
/**
|
|
423
427
|
* O CUSTO DO HOOK, DITO SEM UM NÚMERO QUE NÃO É NOSSO.
|
|
424
428
|
*
|
|
@@ -434,10 +438,10 @@ export async function connect(opts) {
|
|
|
434
438
|
*/
|
|
435
439
|
if (wired.command.startsWith("npx synthesisui@")) {
|
|
436
440
|
console.log("");
|
|
437
|
-
console.log(body("The hook runs through npx, which resolves this package against the"));
|
|
438
|
-
console.log(body("registry on every edit - that wait is the network, not the check itself"));
|
|
439
|
-
console.log(body("(the analysis is about 80ms). Adding synthesisui to your devDependencies"));
|
|
440
|
-
console.log(body("makes npx resolve it locally instead, which is several times faster;"));
|
|
441
|
-
console.log(body("run this again afterwards and it will switch by itself."));
|
|
441
|
+
console.log(body(paint.dim("The hook runs through npx, which resolves this package against the")));
|
|
442
|
+
console.log(body(paint.dim("registry on every edit - that wait is the network, not the check itself")));
|
|
443
|
+
console.log(body(paint.dim("(the analysis is about 80ms). Adding synthesisui to your devDependencies")));
|
|
444
|
+
console.log(body(paint.dim("makes npx resolve it locally instead, which is several times faster;")));
|
|
445
|
+
console.log(body(paint.dim("run this again afterwards and it will switch by itself.")));
|
|
442
446
|
}
|
|
443
447
|
}
|
|
@@ -59,23 +59,49 @@ const segments = (name) => name.replace(/^--/, "").split("-");
|
|
|
59
59
|
*
|
|
60
60
|
* 1. quantos segmentos ele compartilha com o nosso token daquela família
|
|
61
61
|
* `--ds-radius-xs` puxa `--radius-xs` (2) e não `--spacing` (0)
|
|
62
|
-
* 2.
|
|
62
|
+
* 2. quantos segmentos dele são um PAPEL que este sistema declara - ver `roles`
|
|
63
|
+
* 3. a convenção dominante dele - o primeiro segmento mais frequente entre os tokens dele
|
|
63
64
|
* no repo real, `--color-*` (57) contra `--dashboard-*` (25)
|
|
64
|
-
*
|
|
65
|
+
* 4. ordem de declaração, que é o desempate que sempre existe e nunca inventa
|
|
66
|
+
*
|
|
67
|
+
* O CRITÉRIO 2 NASCEU DE UMA DECISÃO DO DONO (03/09), e o critério 1 não alcançava o caso. No
|
|
68
|
+
* sistema dele `#f59e0b` é `--color-tier-gold` E `--color-feedback-warning`, e medindo o censo:
|
|
69
|
+
* `tier-gold` tem 59 menções contra 7, e uma escada própria (bronze 13, prata 13). Pela contagem
|
|
70
|
+
* bruta ele venceria - e vencia, sempre que o nosso token daquele valor não existisse para o
|
|
71
|
+
* critério 1 comparar: aí caía na ORDEM DE DECLARAÇÃO, e `tier-gold` vem antes no CSS dele.
|
|
72
|
+
*
|
|
73
|
+
* A ESCOLHA NÃO É PELO MAIS FREQUENTE, É PELO ERRO QUE SE ANUNCIA. `feedback-warning` escrito num
|
|
74
|
+
* badge de nível mostra "warning" onde devia ser ouro, e quem lê corrige. `tier-gold` escrito num
|
|
75
|
+
* alerta pinta a cor CERTA com o nome de outro domínio - a tela fica visualmente correta e ninguém
|
|
76
|
+
* corrige nunca. Entre dois nomes dele, o que descreve um PAPEL erra alto; o que descreve um
|
|
77
|
+
* domínio de negócio erra calado.
|
|
65
78
|
*/
|
|
66
|
-
function pick(candidates, ours, convention
|
|
79
|
+
function pick(candidates, ours, convention,
|
|
80
|
+
/**
|
|
81
|
+
* OS PAPÉIS QUE ESTE SISTEMA DECLARA - o critério 2, e o único que decide quando não há token
|
|
82
|
+
* nosso daquele valor para comparar.
|
|
83
|
+
*
|
|
84
|
+
* DERIVADO, nunca escrito: são os segmentos finais dos papéis semânticos do próprio sistema, e
|
|
85
|
+
* por isso a regra vale em qualquer projeto. Um projeto que chame o aviso de `atencao` puxa
|
|
86
|
+
* `atencao`; nenhum nome de família deste ou daquele repositório entra aqui (`INV-GERAL-07`).
|
|
87
|
+
*/
|
|
88
|
+
roles) {
|
|
67
89
|
const mine = ours ? new Set(segments(ours)) : null;
|
|
68
90
|
let best = candidates[0];
|
|
69
|
-
let bestScore = [-1, -1];
|
|
91
|
+
let bestScore = [-1, -1, -1];
|
|
70
92
|
for (const name of candidates) {
|
|
71
93
|
const parts = segments(name);
|
|
72
94
|
const shared = mine ? parts.filter((p) => mine.has(p)).length : 0;
|
|
73
95
|
const score = [
|
|
74
96
|
shared,
|
|
97
|
+
parts.filter((p) => roles.has(p)).length,
|
|
75
98
|
convention.get(parts[0] ?? "") ?? 0,
|
|
76
99
|
];
|
|
77
100
|
if (score[0] > bestScore[0] ||
|
|
78
|
-
(score[0] === bestScore[0] && score[1] > bestScore[1])
|
|
101
|
+
(score[0] === bestScore[0] && score[1] > bestScore[1]) ||
|
|
102
|
+
(score[0] === bestScore[0] &&
|
|
103
|
+
score[1] === bestScore[1] &&
|
|
104
|
+
score[2] > bestScore[2])) {
|
|
79
105
|
best = name;
|
|
80
106
|
bestScore = score;
|
|
81
107
|
}
|
|
@@ -106,14 +132,29 @@ function pick(candidates, ours, convention) {
|
|
|
106
132
|
* sistema do dono, `#f59e0b` é `--color-tier-gold` E `--color-feedback-warning` - dois conceitos
|
|
107
133
|
* dele, gamificação e estado -, e o relatório dizia `→ {color.tier-gold}` como se fosse fato.
|
|
108
134
|
*/
|
|
109
|
-
function escolha(candidates, ours, convention) {
|
|
110
|
-
const name = pick(candidates, ours, convention);
|
|
135
|
+
function escolha(candidates, ours, convention, roles) {
|
|
136
|
+
const name = pick(candidates, ours, convention, roles);
|
|
111
137
|
return { name, also: candidates.filter((c) => c !== name) };
|
|
112
138
|
}
|
|
113
139
|
export function theirNames(ours, theirs) {
|
|
114
140
|
const out = new Map();
|
|
115
141
|
if (theirs.byName.size === 0)
|
|
116
142
|
return out;
|
|
143
|
+
/**
|
|
144
|
+
* OS PAPÉIS QUE ESTE SISTEMA DECLARA, derivados dos NOSSOS nomes - ver o critério 2 de `pick`.
|
|
145
|
+
*
|
|
146
|
+
* O último segmento de cada token semântico é o papel: `--ds-color-semantic-warning` dá
|
|
147
|
+
* `warning`. Sai daqui e não de uma lista escrita porque uma lista seria o nosso vocabulário
|
|
148
|
+
* virando régua - um sistema que chame o aviso de `atencao` tem que puxar `atencao`.
|
|
149
|
+
*/
|
|
150
|
+
const roles = new Set();
|
|
151
|
+
for (const name of ours.byName.keys()) {
|
|
152
|
+
if (!name.includes("-semantic-"))
|
|
153
|
+
continue;
|
|
154
|
+
const last = segments(name).at(-1);
|
|
155
|
+
if (last)
|
|
156
|
+
roles.add(last);
|
|
157
|
+
}
|
|
117
158
|
/** A convenção dominante dele, contada e não suposta - ver `pick`. */
|
|
118
159
|
const convention = new Map();
|
|
119
160
|
for (const name of theirs.byName.keys()) {
|
|
@@ -151,7 +192,7 @@ export function theirNames(ours, theirs) {
|
|
|
151
192
|
: candidates.filter((n) => familySays(kind, n));
|
|
152
193
|
if (usable.length === 0)
|
|
153
194
|
continue;
|
|
154
|
-
out.set(key, escolha(usable, name, convention));
|
|
195
|
+
out.set(key, escolha(usable, name, convention, roles));
|
|
155
196
|
}
|
|
156
197
|
}
|
|
157
198
|
for (const [value, candidates] of theirs.byValue) {
|
|
@@ -161,7 +202,7 @@ export function theirNames(ours, theirs) {
|
|
|
161
202
|
const key = `${kind}:${value}`;
|
|
162
203
|
if (out.has(key))
|
|
163
204
|
continue;
|
|
164
|
-
out.set(key, escolha(candidates, null, convention));
|
|
205
|
+
out.set(key, escolha(candidates, null, convention, roles));
|
|
165
206
|
}
|
|
166
207
|
}
|
|
167
208
|
return out;
|
package/dist/output.js
CHANGED
|
@@ -20,6 +20,34 @@ export function snippet(lines) {
|
|
|
20
20
|
export function body(line) {
|
|
21
21
|
return ` ${line}`;
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* PROSA LONGA QUEBRADA NA MESMA LARGURA QUE O RESTO DA TELA RESPEITA.
|
|
25
|
+
*
|
|
26
|
+
* Todo parágrafo daqui foi quebrado À MÃO até agora, uma string por linha - o que funciona para
|
|
27
|
+
* texto fixo e não funciona para texto que vem de dado. Medido em 03/09 na ação recomendada do
|
|
28
|
+
* `connect`: a linha do "por quê" saiu com 103 colunas num desenho de 66, e num terminal estreito
|
|
29
|
+
* ela dobra no meio de uma palavra, exatamente na frase que a pessoa mais precisa ler.
|
|
30
|
+
*
|
|
31
|
+
* Quebra por PALAVRA e nunca no meio de uma; uma palavra maior que a largura fica sozinha na linha
|
|
32
|
+
* em vez de ser cortada. Já indenta como `body`, porque a indentação conta para a largura.
|
|
33
|
+
*/
|
|
34
|
+
export function bodyWrapped(text, width = WIDTH) {
|
|
35
|
+
const lines = [];
|
|
36
|
+
let current = "";
|
|
37
|
+
for (const word of text.split(/\s+/).filter(Boolean)) {
|
|
38
|
+
if (!current)
|
|
39
|
+
current = word;
|
|
40
|
+
else if (`${current} ${word}`.length <= width)
|
|
41
|
+
current += ` ${word}`;
|
|
42
|
+
else {
|
|
43
|
+
lines.push(current);
|
|
44
|
+
current = word;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (current)
|
|
48
|
+
lines.push(current);
|
|
49
|
+
return lines.map(body);
|
|
50
|
+
}
|
|
23
51
|
/**
|
|
24
52
|
* Colour, used the way the product uses motion: a quiet base and a few
|
|
25
53
|
* deliberate accents (dono, 30/07: "um cinza mais escuro e um mais claro,
|
package/package.json
CHANGED