synthesisui 0.16.362 → 0.16.364
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 +20 -0
- package/dist/commands/gaps.js +45 -2
- package/dist/index.js +2 -0
- package/dist/mcp-approval.js +79 -0
- package/package.json +1 -1
package/dist/commands/align.js
CHANGED
|
@@ -10,6 +10,7 @@ import { readRequests } from "../doctor/requests.js";
|
|
|
10
10
|
import { declaredReference } from "../group-role.js";
|
|
11
11
|
import { CHECKER_SINCE, installedBehind, MATERIALISER_SINCE, READER_SINCE, } from "../install-marks.js";
|
|
12
12
|
import { fmt, say } from "../lang.js";
|
|
13
|
+
import { mcpAwaitingApproval } from "../mcp-approval.js";
|
|
13
14
|
import { measuredScope } from "../measured-scope.js";
|
|
14
15
|
import { body, bodyWrapped, paint, section, snippet } from "../output.js";
|
|
15
16
|
import { SKILLS } from "../skills.js";
|
|
@@ -96,6 +97,25 @@ export async function localMisalignments(root,
|
|
|
96
97
|
opts = {}) {
|
|
97
98
|
const { cli, home = homedir() } = opts;
|
|
98
99
|
const out = [];
|
|
100
|
+
/**
|
|
101
|
+
* O PORTÃO QUE MATOU UM IMPORT INTEIRO, dito na sessão em que ele está fechado.
|
|
102
|
+
*
|
|
103
|
+
* Medido em 03/09: o agente mediu 262 arquivos, leu 78 componentes, descreveu a paleta dele - e
|
|
104
|
+
* parou, porque o playbook do import é servido pelo MCP e a aprovação estava pendente. Isto vem
|
|
105
|
+
* ANTES de qualquer outro desalinho porque é o único que impede a jornada principal de começar:
|
|
106
|
+
* um repo sem sistema não está desalinhado, e um repo cujas ferramentas ninguém aprovou não pode
|
|
107
|
+
* nem medir de volta.
|
|
108
|
+
*
|
|
109
|
+
* `/mcp` e não um comando de terminal: quem aprova é o agente, e um `npx` aqui mandaria a pessoa
|
|
110
|
+
* ao lugar errado. Ver `mcp-approval.ts` para de onde o estado é lido, e para o silêncio quando
|
|
111
|
+
* não há como saber.
|
|
112
|
+
*/
|
|
113
|
+
if ((await mcpAwaitingApproval(root, home).catch(() => null)) === true)
|
|
114
|
+
out.push({
|
|
115
|
+
says: "The system's tools are declared here and still waiting for your approval - until you give it, I cannot reach the import playbook, only measure your files.",
|
|
116
|
+
run: "/mcp",
|
|
117
|
+
then: "then say: import my design system",
|
|
118
|
+
});
|
|
99
119
|
const locks = (await locksIn(root)).filter((l) => l.slug && !l.adopted);
|
|
100
120
|
/** Sem sistema instalado não há alinho a cobrar - não é desalinho, é um repo sem DS. */
|
|
101
121
|
if (locks.length === 0)
|
package/dist/commands/gaps.js
CHANGED
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
import { readdir, readFile } from "node:fs/promises";
|
|
16
16
|
import { join } from "node:path";
|
|
17
17
|
import { describeTriage, triageLedger } from "../doctor/gap-triage.js";
|
|
18
|
-
import { body, section } from "../output.js";
|
|
18
|
+
import { body, bodyWrapped, paint, section } from "../output.js";
|
|
19
19
|
export async function gaps(opts) {
|
|
20
20
|
const root = opts.dir ?? process.cwd();
|
|
21
21
|
const path = opts.census ?? join(root, "_synthesisui", "census.json");
|
|
22
22
|
const raw = await readFile(path, "utf8").catch(() => null);
|
|
23
|
-
console.log(section("What this pipeline did not read"));
|
|
23
|
+
console.log(section(opts.brief ? "What we read" : "What this pipeline did not read"));
|
|
24
24
|
if (!raw) {
|
|
25
25
|
console.log(body(`No measurement at ${path}. Run \`synthesisui import --dry\` first - that is what measures your files, and it writes the result there.`));
|
|
26
26
|
return;
|
|
@@ -46,6 +46,8 @@ export async function gaps(opts) {
|
|
|
46
46
|
console.log(body(`This measurement was written by a tool version that did not count style fragments yet, so the numbers do not exist rather than being zero. Run \`synthesisui import --dry\` again with ${opts.cli} to measure.`));
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
|
+
if (opts.brief)
|
|
50
|
+
return brief(ledger, values);
|
|
49
51
|
for (const line of describeTriage(triageLedger(ledger, opts.cli), values))
|
|
50
52
|
console.log(body(line));
|
|
51
53
|
/**
|
|
@@ -91,6 +93,47 @@ export async function gaps(opts) {
|
|
|
91
93
|
* vazia. Em qualquer um dos três não há nada a dizer, e uma frase que aparece sempre é uma frase que
|
|
92
94
|
* ninguém lê.
|
|
93
95
|
*/
|
|
96
|
+
/**
|
|
97
|
+
* O RESULTADO E UMA AÇÃO - o fechamento de um import.
|
|
98
|
+
*
|
|
99
|
+
* TRÊS DECISÕES DE DESENHO, e cada uma responde a algo que o dono viu na tela em 03/09:
|
|
100
|
+
*
|
|
101
|
+
* O PERCENTUAL EM AZUL. `paint.blue` é a cor que este produto reserva para o que está VIVO - a
|
|
102
|
+
* barra de cobertura, os comandos, os links. A cobertura é o número que resume a entrega, e num
|
|
103
|
+
* bloco de prosa cinza ela desaparecia ("deixe com um tom de outra cor").
|
|
104
|
+
*
|
|
105
|
+
* A LACUNA EM UMA LINHA, COM ENDEREÇO. Ela continua dita - quantos fragmentos, em quantos
|
|
106
|
+
* lugares - e a LISTA mora no comando sem `--brief`. Uma lacuna sem número seria silêncio; uma
|
|
107
|
+
* lista no momento da entrega é pedir desculpa quando era hora de mostrar o resultado.
|
|
108
|
+
*
|
|
109
|
+
* UMA CTA, COM O QUE ELA GANHA. O v2 já existe como rascunho; o que faltava era dizer o que
|
|
110
|
+
* aprová-lo FAZ - agrupar cores quase iguais, nomear valor que carrega peso e não tem nome,
|
|
111
|
+
* resolver o valor que carrega dois nomes dele. Um link sem consequência dita é um link que
|
|
112
|
+
* espera para sempre.
|
|
113
|
+
*/
|
|
114
|
+
async function brief(ledger, values) {
|
|
115
|
+
const places = ledger.unread.length;
|
|
116
|
+
if (!values)
|
|
117
|
+
return;
|
|
118
|
+
const decisions = values.seen - values.structure;
|
|
119
|
+
const closed = values.interpreted + values.answered;
|
|
120
|
+
const pct = decisions > 0 ? Math.round((closed / decisions) * 100) : 0;
|
|
121
|
+
console.log(bodyWrapped(`${paint.blue(paint.strong(`${pct}%`))} of your design decisions are in the system - ${closed} of ${decisions}.`).join("\n"));
|
|
122
|
+
/**
|
|
123
|
+
* O QUE FALTA, NA MESMA UNIDADE DA LINHA DE CIMA - e a primeira versão desta função errou
|
|
124
|
+
* exatamente nisso.
|
|
125
|
+
*
|
|
126
|
+
* Ela dizia "222 fragments are not", ao lado de "920 de 1008": quem lê subtrai 88 e vê 222. É o
|
|
127
|
+
* mesmo defeito de duas unidades vizinhas que este PR conserta duas linhas acima, e eu o
|
|
128
|
+
* reintroduzi na frase nova. O resto vai em PONTOS PERCENTUAIS, que é a unidade que a pessoa
|
|
129
|
+
* acabou de ler, e o número de LUGARES é endereço e não contagem concorrente.
|
|
130
|
+
*
|
|
131
|
+
* E cala em 100%: "the other 0%" é uma linha que faz o leitor procurar o que não existe.
|
|
132
|
+
*/
|
|
133
|
+
if (pct >= 100 || places === 0)
|
|
134
|
+
return;
|
|
135
|
+
console.log(bodyWrapped(paint.dim(`The other ${100 - pct}% sits in ${places} ${places === 1 ? "place" : "places"} - \`synthesisui gaps\` lists each one with the file and the line.`)).join("\n"));
|
|
136
|
+
}
|
|
94
137
|
async function carriedByGlobals(root) {
|
|
95
138
|
const dir = join(root, "_synthesisui", "ds");
|
|
96
139
|
const slugs = await readdir(dir).catch(() => []);
|
package/dist/index.js
CHANGED
|
@@ -347,6 +347,8 @@ async function main() {
|
|
|
347
347
|
*/
|
|
348
348
|
await gaps({
|
|
349
349
|
dir,
|
|
350
|
+
/** `--brief`: o fechamento de um import - o resultado e UMA ação. Ver `gaps.ts`. */
|
|
351
|
+
...(boolFlag(flags, "brief") ? { brief: true } : {}),
|
|
350
352
|
census: typeof flags.census === "string" ? flags.census : undefined,
|
|
351
353
|
cli: CLI_VERSION,
|
|
352
354
|
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* AS FERRAMENTAS DECLARADAS E ESPERANDO APROVAÇÃO - o portão que matou um import inteiro.
|
|
5
|
+
*
|
|
6
|
+
* O QUE O CLIENTE GANHA: a sessão em que a aprovação está pendente é a sessão que lhe diz isso.
|
|
7
|
+
* Antes ele descobria no meio do import, quando o agente já tinha medido o repositório e não tinha
|
|
8
|
+
* como continuar.
|
|
9
|
+
*
|
|
10
|
+
* MEDIDO em 03/09, no segundo import real do dono. Ele rodou `import my design system`, o agente
|
|
11
|
+
* mediu 262 arquivos, leu 78 componentes, descreveu a paleta dele - e parou:
|
|
12
|
+
*
|
|
13
|
+
* synthesisui: npx synthesisui@latest mcp - ⏸ Pending approval
|
|
14
|
+
*
|
|
15
|
+
* O playbook do import é SERVIDO pelo MCP, então sem aprovação não há import: só medição. E o
|
|
16
|
+
* `connect` avisa - em uma linha cinza, a terceira de um bloco secundário. Um portão que interrompe
|
|
17
|
+
* a jornada principal não pode ser anunciado como nota de pé de página.
|
|
18
|
+
*
|
|
19
|
+
* A ORIGEM DO DADO. O Claude Code guarda a decisão no `~/.claude.json`, por projeto:
|
|
20
|
+
*
|
|
21
|
+
* enabledMcpjsonServers aprovados
|
|
22
|
+
* disabledMcpjsonServers recusados
|
|
23
|
+
*
|
|
24
|
+
* Um servidor declarado no `.mcp.json` que não está em nenhuma das duas listas está PENDENTE - e é
|
|
25
|
+
* exatamente o estado dele. Medido: as duas listas vazias, para aquele projeto.
|
|
26
|
+
*
|
|
27
|
+
* SÓ LEITURA, NUNCA ESCRITA. Aquele arquivo é a configuração pessoal dele e vale para todos os
|
|
28
|
+
* projetos dele - a mesma fronteira do `.zshrc`, que este produto só atravessa perguntando.
|
|
29
|
+
* Escrever a aprovação por ele seria decidir em nome dele sobre um portão de segurança.
|
|
30
|
+
*
|
|
31
|
+
* E CALA QUANDO NÃO SABE. Arquivo ausente, JSON que não parseia, projeto sem entrada, formato
|
|
32
|
+
* diferente do que a gente conhece: em todos, resposta `null` e nenhuma linha na tela. Um aviso
|
|
33
|
+
* baseado num formato que mudou é pior que silêncio, porque manda a pessoa procurar um botão que
|
|
34
|
+
* talvez não exista mais.
|
|
35
|
+
*/
|
|
36
|
+
/** O nome do nosso servidor no `.mcp.json` - ver `wireMcpAt`. */
|
|
37
|
+
const SERVER = "synthesisui";
|
|
38
|
+
/**
|
|
39
|
+
* `true` quando o nosso servidor está declarado no repositório e a decisão dele ainda não foi
|
|
40
|
+
* tomada. `null` quando não há como saber - e aí quem chama não diz nada.
|
|
41
|
+
*/
|
|
42
|
+
export async function mcpAwaitingApproval(root, home) {
|
|
43
|
+
const declared = await readFile(join(root, ".mcp.json"), "utf8").then((raw) => {
|
|
44
|
+
try {
|
|
45
|
+
const j = JSON.parse(raw);
|
|
46
|
+
return Boolean(j.mcpServers && SERVER in j.mcpServers);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}, () => false);
|
|
52
|
+
/** Sem declaração não há aprovação pendente: o caso é OUTRO, e o `connect` já o cobre. */
|
|
53
|
+
if (declared !== true)
|
|
54
|
+
return declared === null ? null : false;
|
|
55
|
+
const raw = await readFile(join(home, ".claude.json"), "utf8").catch(() => null);
|
|
56
|
+
if (raw == null)
|
|
57
|
+
return null;
|
|
58
|
+
let conf;
|
|
59
|
+
try {
|
|
60
|
+
conf = JSON.parse(raw);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
const entry = conf.projects?.[root];
|
|
66
|
+
/**
|
|
67
|
+
* SEM ENTRADA PARA ESTE PROJETO a pessoa provavelmente nunca abriu o agente aqui - e aí não há
|
|
68
|
+
* aprovação pendente, há uma sessão que ainda não aconteceu. Dizer "aprove" a quem não foi
|
|
69
|
+
* perguntado é inventar um estado.
|
|
70
|
+
*/
|
|
71
|
+
if (!entry)
|
|
72
|
+
return null;
|
|
73
|
+
const enabled = entry.enabledMcpjsonServers;
|
|
74
|
+
const disabled = entry.disabledMcpjsonServers;
|
|
75
|
+
/** Formato diferente do que a gente conhece: silêncio, nunca um palpite. */
|
|
76
|
+
if (!Array.isArray(enabled) || !Array.isArray(disabled))
|
|
77
|
+
return null;
|
|
78
|
+
return !enabled.includes(SERVER) && !disabled.includes(SERVER);
|
|
79
|
+
}
|
package/package.json
CHANGED