synthesisui 0.16.357 → 0.16.359
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/claude-md.js +31 -0
- package/dist/commands/align.js +62 -8
- package/dist/commands/connect.js +27 -23
- package/dist/output.js +28 -0
- package/package.json +1 -1
package/dist/claude-md.js
CHANGED
|
@@ -636,6 +636,37 @@ export async function syncClaudeMd(projectRoot) {
|
|
|
636
636
|
* Um repo com `AGENTS.md` e `.cursor/` ganhou governança nos três lugares e não tinha como saber -
|
|
637
637
|
* e o que não se diz não é adotado.
|
|
638
638
|
*/
|
|
639
|
+
/**
|
|
640
|
+
* O COMANDO QUE ABRE CADA AGENTE - derivado de qual casa do bloco existe, nunca de uma lista fixa.
|
|
641
|
+
*
|
|
642
|
+
* O QUE O CLIENTE GANHA: o fim de um comando mostra o comando literal que abre o agente DELE, então
|
|
643
|
+
* "abra uma sessão nova" deixa de ser uma instrução que ele precisa traduzir (dono, 03/09: "seria
|
|
644
|
+
* legal ter os comandos para abrir, apenas para ficar fácil").
|
|
645
|
+
*
|
|
646
|
+
* MORA AO LADO DE `HOMES` de propósito. A mesma tabela que sabe onde o bloco vive é a que sabe como
|
|
647
|
+
* aquele agente abre - um agente novo entra em UM lugar. Se as duas listas morassem separadas, a
|
|
648
|
+
* segunda envelheceria calada no dia em que a primeira crescesse.
|
|
649
|
+
*
|
|
650
|
+
* O CURSOR NÃO TEM LINHA AQUI, e a ausência é a resposta certa: ele é um editor que se abre, não um
|
|
651
|
+
* comando de terminal. Inventar um `cursor .` mandaria alguém rodar o que talvez não exista no PATH
|
|
652
|
+
* dele.
|
|
653
|
+
*
|
|
654
|
+
* AS FLAGS SÃO AS QUE DISPENSAM APROVAR CADA ESCRITA, e o import escreve muito. Elas são DITAS na
|
|
655
|
+
* saída - o que a flag dispensa aparece em uma linha ao lado -, porque um comando cujo próprio nome
|
|
656
|
+
* carrega "dangerously" impresso sem uma palavra seria a gente escondendo o que ele faz.
|
|
657
|
+
*/
|
|
658
|
+
const OPENS = {
|
|
659
|
+
"CLAUDE.md": "claude --dangerously-skip-permissions",
|
|
660
|
+
"AGENTS.md": "codex --yolo",
|
|
661
|
+
};
|
|
662
|
+
/**
|
|
663
|
+
* Os comandos que abrem os agentes que ESTE repositório mostra ter - vazio quando nenhum deles
|
|
664
|
+
* carrega o bloco, e aí a saída não fala de abrir nada.
|
|
665
|
+
*/
|
|
666
|
+
export async function agentOpenCommands(projectRoot) {
|
|
667
|
+
const homes = await blockHomes(projectRoot);
|
|
668
|
+
return homes.map((h) => OPENS[h]).filter((c) => Boolean(c));
|
|
669
|
+
}
|
|
639
670
|
export async function blockHomes(projectRoot) {
|
|
640
671
|
const found = [];
|
|
641
672
|
for (const home of HOMES) {
|
package/dist/commands/align.js
CHANGED
|
@@ -2,6 +2,7 @@ import { readdir, readFile } from "node:fs/promises";
|
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
4
|
import { pinnedHookVersion } from "../agent-wiring.js";
|
|
5
|
+
import { agentOpenCommands } from "../claude-md.js";
|
|
5
6
|
import { isOlderCli } from "../cli-version.js";
|
|
6
7
|
import { readToken, resolveRegistry } from "../config.js";
|
|
7
8
|
import { unsentEvents } from "../doctor/ledger.js";
|
|
@@ -9,7 +10,7 @@ import { readRequests } from "../doctor/requests.js";
|
|
|
9
10
|
import { declaredReference } from "../group-role.js";
|
|
10
11
|
import { CHECKER_SINCE, installedBehind, MATERIALISER_SINCE, READER_SINCE, } from "../install-marks.js";
|
|
11
12
|
import { measuredScope } from "../measured-scope.js";
|
|
12
|
-
import { body } from "../output.js";
|
|
13
|
+
import { body, bodyWrapped, paint, section, snippet } from "../output.js";
|
|
13
14
|
import { SKILLS } from "../skills.js";
|
|
14
15
|
/**
|
|
15
16
|
* QUAL CLI MEDIU O CENSO EM DISCO - e era o `reader`, um inteiro, até 11/08.
|
|
@@ -625,8 +626,63 @@ export async function nextStepFor(root) {
|
|
|
625
626
|
* chutá-lo seria pior que pedir para olhar.
|
|
626
627
|
*/
|
|
627
628
|
return measured
|
|
628
|
-
?
|
|
629
|
-
|
|
629
|
+
? {
|
|
630
|
+
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>`.",
|
|
631
|
+
headline: "Your system is already in your account. Bring it in here:",
|
|
632
|
+
run: ["synthesisui list --mine", "synthesisui add <slug>"],
|
|
633
|
+
why: "Re-measuring would cost minutes and change nothing the platform already knows.",
|
|
634
|
+
}
|
|
635
|
+
: {
|
|
636
|
+
sentence: 'This repo has no design system contract yet. Ask me: "import my design system."',
|
|
637
|
+
headline: "Open a new agent session in this repo",
|
|
638
|
+
open: await agentOpenCommands(root).catch(() => []),
|
|
639
|
+
say: "import my design system",
|
|
640
|
+
why: "I read what is already in your code - the colours, the type, the shapes, the components. Nothing is invented.",
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* A AÇÃO RECOMENDADA, DESENHADA PARA SER VISTA - uma, com título, o literal copiável e o por quê.
|
|
645
|
+
*
|
|
646
|
+
* A hierarquia usa a paleta que já existe e no papel que ela declara: `blue` marca o que é VIVO (o
|
|
647
|
+
* que ele diz ou roda), `dim` é prosa secundária (o por quê e a exigência de sessão nova). A seção
|
|
648
|
+
* traz as linhas em branco de graça, que é o "respiro" pedido.
|
|
649
|
+
*/
|
|
650
|
+
export function renderNextStep(next, freshSession) {
|
|
651
|
+
const lines = [section("Do this next")];
|
|
652
|
+
/**
|
|
653
|
+
* NUMERA SÓ QUANDO SÃO DOIS PASSOS. Abrir o agente e falar com ele são duas coisas, e sem o "1" e
|
|
654
|
+
* o "2" a pessoa lê dois blocos azuis e não sabe se escolhe um ou faz os dois. Com um passo só, o
|
|
655
|
+
* número seria decoração.
|
|
656
|
+
*/
|
|
657
|
+
const numbered = Boolean(next.open?.length && next.say);
|
|
658
|
+
if (next.open?.length) {
|
|
659
|
+
lines.push(body(numbered ? `1 ${next.headline}` : next.headline), "");
|
|
660
|
+
lines.push(paint.blue(snippet(next.open)), "");
|
|
661
|
+
}
|
|
662
|
+
if (next.say) {
|
|
663
|
+
if (next.open?.length)
|
|
664
|
+
lines.push(body(numbered ? "2 and say" : "and say"), "");
|
|
665
|
+
else
|
|
666
|
+
lines.push(body(`${next.headline} and say:`), "");
|
|
667
|
+
lines.push(paint.blue(snippet([next.say])), "");
|
|
668
|
+
}
|
|
669
|
+
if (next.run?.length) {
|
|
670
|
+
lines.push(body(next.headline), "");
|
|
671
|
+
lines.push(paint.blue(snippet(next.run)), "");
|
|
672
|
+
}
|
|
673
|
+
lines.push(...bodyWrapped(next.why).map(paint.dim));
|
|
674
|
+
/** O que a flag dispensa, dito - ver `OPENS` em `claude-md.ts`. E concordando: um repo com um
|
|
675
|
+
* agente só recebe uma linha no singular, porque "those flags" sobre um comando lê como se a
|
|
676
|
+
* pessoa tivesse perdido uma opção da tela. */
|
|
677
|
+
if (next.open?.length)
|
|
678
|
+
lines.push(...bodyWrapped(next.open.length === 1
|
|
679
|
+
? "That flag lets it write without asking each time. Drop it to approve every change yourself."
|
|
680
|
+
: "Those flags let it write without asking each time. Drop them to approve every change yourself.").map(paint.dim));
|
|
681
|
+
if (freshSession)
|
|
682
|
+
lines.push(...bodyWrapped(freshSession.mcp
|
|
683
|
+
? "A new session is what loads the skills and tools just installed, and the project's tools ask for approval once - say yes."
|
|
684
|
+
: "A new session is what loads the skills just installed.").map(paint.dim));
|
|
685
|
+
return lines.join("\n");
|
|
630
686
|
}
|
|
631
687
|
/**
|
|
632
688
|
* A CAUDA DE UM COMANDO QUE TERMINOU: o que ainda está fora, ou nada.
|
|
@@ -642,10 +698,8 @@ export async function reportWhatIsLeft(root, opts = {}) {
|
|
|
642
698
|
const items = await misalignments(root, opts).catch(() => []);
|
|
643
699
|
if (items.length === 0) {
|
|
644
700
|
const next = await nextStepFor(root).catch(() => null);
|
|
645
|
-
if (next)
|
|
646
|
-
console.log(
|
|
647
|
-
console.log(body(next));
|
|
648
|
-
}
|
|
701
|
+
if (next)
|
|
702
|
+
console.log(renderNextStep(next, opts.freshSession));
|
|
649
703
|
return;
|
|
650
704
|
}
|
|
651
705
|
console.log("");
|
|
@@ -673,5 +727,5 @@ export async function align(opts) {
|
|
|
673
727
|
*/
|
|
674
728
|
const next = await nextStepFor(root).catch(() => null);
|
|
675
729
|
if (next)
|
|
676
|
-
console.log(next);
|
|
730
|
+
console.log(next.sentence);
|
|
677
731
|
}
|
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
|
}
|
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