synthesisui 0.16.415 → 0.16.417
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/agents-chosen.js +5 -6
- package/dist/anatomy-from-sketch.js +69 -5
- package/dist/commands/connect.js +19 -21
- package/dist/copy/connect.pt-BR.js +1 -1
- package/dist/install-marks.js +14 -2
- package/dist/pick-many.js +108 -0
- package/package.json +1 -1
package/dist/agents-chosen.js
CHANGED
|
@@ -159,12 +159,11 @@ export async function chooseAgents(input) {
|
|
|
159
159
|
const pre = remembered ?? (wired.length > 0 ? wired : await detected(input.root));
|
|
160
160
|
if (!input.ask)
|
|
161
161
|
return { agents: pre, from: "evidence", unknown: [] };
|
|
162
|
-
const
|
|
163
|
-
/**
|
|
164
|
-
if (
|
|
165
|
-
return { agents: pre, from: "
|
|
166
|
-
|
|
167
|
-
return { agents: read.agents, from: "asked", unknown: read.unknown };
|
|
162
|
+
const picked = await input.ask(pre);
|
|
163
|
+
/** Sem terminal para perguntar, a evidência decide - o mesmo caminho de quem não tem TTY. */
|
|
164
|
+
if (picked === null)
|
|
165
|
+
return { agents: pre, from: "evidence", unknown: [] };
|
|
166
|
+
return { agents: picked, from: "asked", unknown: [] };
|
|
168
167
|
}
|
|
169
168
|
/**
|
|
170
169
|
* A ESCOLHA REGISTRADA - e é ela que distingue "ele desmarcou" de "sobrou arquivo no disco".
|
|
@@ -212,6 +212,23 @@ function nameOfTag(tag) {
|
|
|
212
212
|
.replace(/[^a-z0-9]+/g, "-")
|
|
213
213
|
.replace(/^-|-$/g, "");
|
|
214
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* A PROP QUE ALIMENTA O ELEMENTO, na mesma grafia dos outros nomes de part.
|
|
217
|
+
*
|
|
218
|
+
* `contentFrom` é a expressão verbatim que o JSX escreve, então ela pode ser um identificador
|
|
219
|
+
* simples (`label`), um acesso (`item.title`) ou uma chamada. Só o que é um nome simples vira
|
|
220
|
+
* nome de part: um pedaço de expressão viraria uma classe ilegível no CSS compilado, e o nó cai
|
|
221
|
+
* em `box` como antes. Medido nos dois censos: os 19 do `codelevel-ui` são todos identificadores
|
|
222
|
+
* simples, e dos 534 do `frontend-hub` a maioria também.
|
|
223
|
+
*/
|
|
224
|
+
function nameOfProp(expression) {
|
|
225
|
+
const clean = expression.trim();
|
|
226
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(clean))
|
|
227
|
+
return undefined;
|
|
228
|
+
const name = nameOfTag(clean);
|
|
229
|
+
/** `children` é a palavra do React, não do design dele - ali o nó é um slot, não uma part. */
|
|
230
|
+
return name && name !== "children" ? name : undefined;
|
|
231
|
+
}
|
|
215
232
|
/**
|
|
216
233
|
* O NOME QUE ELE MESMO DEU AO ELEMENTO - a classe do CSS Module.
|
|
217
234
|
*
|
|
@@ -535,6 +552,31 @@ consumedAt) {
|
|
|
535
552
|
const hasKids = children.length > 0;
|
|
536
553
|
let as;
|
|
537
554
|
let name;
|
|
555
|
+
/**
|
|
556
|
+
* O CONTEÚDO QUE VEM DE UMA PROP DELE É CONTEÚDO - A1 da etapa 15 (dono, 11/09).
|
|
557
|
+
*
|
|
558
|
+
* O QUE ELE VIA: o `CapabilityCard` dele desenhado como três losangos `✧` soltos, e o `Beam`
|
|
559
|
+
* como um `✧`. A pergunta dele foi a certa: *"mesmo tendo a receita vindo de um componente
|
|
560
|
+
* funcionando, não consigo entender o motivo"*.
|
|
561
|
+
*
|
|
562
|
+
* A SUPOSIÇÃO QUE ERRAVA estava escrita três linhas abaixo, em voz alta: *"a leaf box with no
|
|
563
|
+
* content of its own is a drawn SHAPE (...) `icon` is the form for 'a bare shape with no
|
|
564
|
+
* text'"*. Em React o texto quase nunca é literal no JSX - ele vem de `{label}`, `{children}`,
|
|
565
|
+
* `{value}`. **"Sem texto literal" não significa "sem conteúdo": significa "o conteúdo vem de
|
|
566
|
+
* fora".**
|
|
567
|
+
*
|
|
568
|
+
* E O COLETOR JÁ SABIA. `contentFrom` é a expressão que aquele elemento escreve, e o contrato
|
|
569
|
+
* a descreve como *"a evidência de QUAL prop alimenta este elemento"*. A derivação nunca a
|
|
570
|
+
* consultava. MEDIDO nos dois censos congelados: **19 nós** no `codelevel-ui` e **534** no
|
|
571
|
+
* `frontend-hub` - e no sistema dele isso era 36 dos 94 nós chegando como `icon`, 20 deles
|
|
572
|
+
* sem ser glifo nenhum (`label`, `value`, `title`, `eyebrow`, `meta`).
|
|
573
|
+
*
|
|
574
|
+
* É EVIDÊNCIA, NUNCA EXECUÇÃO: em outro projeto a prop tem outro nome, e o que atravessa é
|
|
575
|
+
* que ESTE elemento é onde o conteúdo entra. Por isso a regra lê a PRESENÇA do campo, e a
|
|
576
|
+
* palavra dele só vira o nome da part.
|
|
577
|
+
*/
|
|
578
|
+
const writesContent = typeof node.contentFrom === "string" &&
|
|
579
|
+
node.contentFrom.trim().length > 0;
|
|
538
580
|
if (capitalised) {
|
|
539
581
|
// A primitive's member name is the client's own word for the element.
|
|
540
582
|
name = nameOfTag(tag);
|
|
@@ -551,10 +593,25 @@ consumedAt) {
|
|
|
551
593
|
else if (form)
|
|
552
594
|
as = form;
|
|
553
595
|
else
|
|
554
|
-
as = hasKids
|
|
596
|
+
as = hasKids
|
|
597
|
+
? arrangement(classes)
|
|
598
|
+
: node.text || writesContent
|
|
599
|
+
? "text"
|
|
600
|
+
: "icon";
|
|
555
601
|
}
|
|
556
602
|
else if (byTag) {
|
|
557
|
-
|
|
603
|
+
/**
|
|
604
|
+
* A PALAVRA DELE VENCE O NOME QUE A TAG SUGERE (A5 da etapa 15).
|
|
605
|
+
*
|
|
606
|
+
* `BY_TAG` dá a um `<span>` o nome `text` e a um `<div>` nada - nomes corretos e MUDOS,
|
|
607
|
+
* que dizem o que a tag é e nada sobre o papel daquele nó no componente dele. Quando o
|
|
608
|
+
* JSX escreve `<span>{eyebrow}</span>`, `eyebrow` é a palavra que ele usa para aquilo, e
|
|
609
|
+
* é ela que precisa chegar na tela e no seletor. Perde só para um nome que também é dele
|
|
610
|
+
* e mais específico: uma classe de module, ou o membro de um componente que ele escreveu.
|
|
611
|
+
*/
|
|
612
|
+
name =
|
|
613
|
+
(writesContent ? nameOfProp(String(node.contentFrom)) : undefined) ??
|
|
614
|
+
byTag.name;
|
|
558
615
|
if (NEUTRAL.has(lower)) {
|
|
559
616
|
/**
|
|
560
617
|
* A leaf box with no content of its own is a drawn SHAPE - a dot, a
|
|
@@ -564,7 +621,7 @@ consumedAt) {
|
|
|
564
621
|
as =
|
|
565
622
|
hasKids || node.slot
|
|
566
623
|
? arrangement(classes)
|
|
567
|
-
: node.text
|
|
624
|
+
: node.text || writesContent
|
|
568
625
|
? "text"
|
|
569
626
|
: "icon";
|
|
570
627
|
}
|
|
@@ -572,7 +629,7 @@ consumedAt) {
|
|
|
572
629
|
as = byTag.as;
|
|
573
630
|
}
|
|
574
631
|
else {
|
|
575
|
-
as = hasKids ? arrangement(classes) : "icon";
|
|
632
|
+
as = hasKids ? arrangement(classes) : writesContent ? "text" : "icon";
|
|
576
633
|
}
|
|
577
634
|
/**
|
|
578
635
|
* ANYTHING HOLDING ELEMENTS ARRANGES THEM - the contract gives children to
|
|
@@ -598,9 +655,16 @@ consumedAt) {
|
|
|
598
655
|
/**
|
|
599
656
|
* `box` VIROU A ÚLTIMA OPÇÃO, não a primeira - ver `nameOfModule`. Um nó com
|
|
600
657
|
* classe de module tem nome, e o nome é dele.
|
|
658
|
+
*
|
|
659
|
+
* E A PROP QUE ALIMENTA O ELEMENTO VEM ANTES DE `box` (A5 da etapa 15). Os dois nós de
|
|
660
|
+
* texto do `CapabilityCard` dele chegavam como `box` e `box` - dois irmãos com o mesmo
|
|
661
|
+
* nome, que é o mesmo que não ter nome. `contentFrom: "label"` é a palavra que o CÓDIGO
|
|
662
|
+
* DELE escreve, e ela é melhor que qualquer coisa que a gente derivasse da tag.
|
|
601
663
|
*/
|
|
602
664
|
name =
|
|
603
|
-
nameOfModule(node.moduleClass) ??
|
|
665
|
+
nameOfModule(node.moduleClass) ??
|
|
666
|
+
(writesContent ? nameOfProp(String(node.contentFrom)) : undefined) ??
|
|
667
|
+
"box";
|
|
604
668
|
/**
|
|
605
669
|
* A NODE WHOSE CONTENT IS THE CALLER'S is a slot, and it keeps its own name
|
|
606
670
|
* and classes - the border and the hover on a `<tr>{children}</tr>` are real
|
package/dist/commands/connect.js
CHANGED
|
@@ -7,6 +7,7 @@ import { resolveRegistry } from "../config.js";
|
|
|
7
7
|
import { notAProject, projectRootFrom } from "../is-a-project.js";
|
|
8
8
|
import { fmt, say } from "../lang.js";
|
|
9
9
|
import { body, bodyWrapped, paint, section, snippet } from "../output.js";
|
|
10
|
+
import { canPick, pickMany } from "../pick-many.js";
|
|
10
11
|
import { readShellAnswer, rememberShellNo } from "../shell-answer.js";
|
|
11
12
|
import { existingRc, hasHook, pinnedInHook, rcPathFor, shellFrom, shellSnippet, withHook, } from "../shell-hook.js";
|
|
12
13
|
import { SKILLS } from "../skills.js";
|
|
@@ -226,32 +227,29 @@ version) {
|
|
|
226
227
|
async function askWhichAgents(pre) {
|
|
227
228
|
console.log("");
|
|
228
229
|
console.log(section(say("Which agents")));
|
|
229
|
-
|
|
230
|
-
const on = pre.includes(agent.id);
|
|
231
|
-
console.log(body(`${on ? "[x]" : "[ ]"} ${agent.label.padEnd(13)} ${on
|
|
232
|
-
? fmt(say("{dir} is here"), { dir: agent.evidence })
|
|
233
|
-
: fmt(say("no {dir} in this repo"), { dir: agent.evidence })}`));
|
|
234
|
-
}
|
|
230
|
+
console.log(body(paint.faint(say("Space marks, arrows move, Enter confirms - nothing is written yet."))));
|
|
235
231
|
console.log("");
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
232
|
+
if (!canPick())
|
|
233
|
+
return null;
|
|
234
|
+
const picked = await pickMany({
|
|
235
|
+
items: AGENTS.map((agent) => ({
|
|
236
|
+
id: agent.id,
|
|
237
|
+
label: agent.label,
|
|
238
|
+
note: pre.includes(agent.id)
|
|
239
|
+
? fmt(say("{dir} is here"), { dir: agent.evidence })
|
|
240
|
+
: fmt(say("no {dir} in this repo"), { dir: agent.evidence }),
|
|
241
|
+
})),
|
|
242
|
+
picked: pre,
|
|
242
243
|
});
|
|
243
|
-
|
|
244
|
-
rl.close();
|
|
245
|
-
return typed;
|
|
244
|
+
return picked;
|
|
246
245
|
}
|
|
247
246
|
/**
|
|
248
|
-
* O QUE FICOU ESCOLHIDO, ecoado antes de agir
|
|
247
|
+
* O QUE FICOU ESCOLHIDO, ecoado antes de agir.
|
|
249
248
|
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
* aconteceu.
|
|
249
|
+
* Nasceu na etapa 12 contra um engano que o seletor de agora não permite mais - lá, digitar
|
|
250
|
+
* `claude` SUBSTITUÍA a lista, e quem tinha hábito de seletor incremental perdia o outro. Com o
|
|
251
|
+
* espaço marcando, marcar é aditivo por natureza. A linha fica porque a outra metade dela continua
|
|
252
|
+
* valendo: ela é a última coisa antes das escritas, e diz o que foi para o disco.
|
|
255
253
|
*/
|
|
256
254
|
function echoChoice(agents) {
|
|
257
255
|
console.log("");
|
|
@@ -43,9 +43,9 @@ register("pt-BR", {
|
|
|
43
43
|
* razão da frase `import my design system`: onde a máquina lê, a precisão vence a gentileza.
|
|
44
44
|
*/
|
|
45
45
|
"Which agents": "Quais agentes",
|
|
46
|
+
"Space marks, arrows move, Enter confirms - nothing is written yet.": "O espaço marca, as setas movem, o Enter confirma - nada foi escrito ainda.",
|
|
46
47
|
"{dir} is here": "{dir} está aqui",
|
|
47
48
|
"no {dir} in this repo": "nenhum {dir} neste repo",
|
|
48
|
-
"Enter keeps this. To change it, type the ones you want - or `none`:": "Enter mantém isto. Para mudar, digite os que você quer - ou `none`:",
|
|
49
49
|
"· {label} not chosen - these stay in the repo: {files}": "· {label} não escolhido - estes ficam no repo: {files}",
|
|
50
50
|
"· {label} not chosen - nothing of it was written": "· {label} não escolhido - nada dele foi escrito",
|
|
51
51
|
"· Cursor is no longer wired by this CLI - what we wrote is still here: {files}": "· o Cursor não é mais fiado por este CLI - o que a gente escreveu continua aqui: {files}",
|
package/dist/install-marks.js
CHANGED
|
@@ -214,7 +214,19 @@
|
|
|
214
214
|
* O que o cliente ganha ao rodar `upgrade`: nada de uma ferramenta que ele não usa é reescrito no
|
|
215
215
|
* repositório dele.
|
|
216
216
|
*/
|
|
217
|
-
|
|
217
|
+
/**
|
|
218
|
+
* 0.16.415 -> 0.16.416 em 10/09: a lista de agentes passa a ser MARCADA com o espaço, e não
|
|
219
|
+
* digitada. Ele testou a versão anterior no próprio repositório e disse: *"eu não consigo
|
|
220
|
+
* selecionar, eu queria clicar ou selecionar com o espaço para checar"* - a tela desenhava `[x]` e
|
|
221
|
+
* `[ ]`, a forma universal de caixa de seleção, e exigia digitar o nome do agente.
|
|
222
|
+
*
|
|
223
|
+
* A marca sobe porque a ESCOLHA decide o conjunto de arquivos que cai na pasta dele, e um
|
|
224
|
+
* `connect` pinado numa versão anterior pede aquela escolha de um jeito que ele já tentou e que
|
|
225
|
+
* não funcionou.
|
|
226
|
+
*
|
|
227
|
+
* O que o cliente ganha ao rodar `upgrade`: a lista responde ao espaço, como a caixa promete.
|
|
228
|
+
*/
|
|
229
|
+
export const MATERIALISER_SINCE = "0.16.416";
|
|
218
230
|
/**
|
|
219
231
|
* A ÚLTIMA VERSÃO EM QUE O QUE O HOOK RODA MUDOU.
|
|
220
232
|
*
|
|
@@ -781,7 +793,7 @@ export const CHECKER_SINCE = "0.16.413";
|
|
|
781
793
|
* deveriam estar lá. Quem tem censo gravado precisa de um `sync` para a fila de trabalho dele ser a
|
|
782
794
|
* real; ver o livro-razão de `corpus.spec.ts`.
|
|
783
795
|
*/
|
|
784
|
-
export const READER_SINCE = "0.16.
|
|
796
|
+
export const READER_SINCE = "0.16.417";
|
|
785
797
|
/**
|
|
786
798
|
* O QUE ESTÁ INSTALADO AQUI FICOU PARA TRÁS - e as DUAS condições que fazem isso ser verdade.
|
|
787
799
|
*
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ELE MARCA COM O ESPAÇO - a lista de escolha MÚLTIPLA do terminal.
|
|
3
|
+
*
|
|
4
|
+
* O DEFEITO QUE ISTO CONSERTA, e ele foi achado por ELE testando a entrega anterior no próprio
|
|
5
|
+
* repositório (10/09): *"eu não consigo selecionar, eu queria clicar ou selecionar com o espaço
|
|
6
|
+
* para checar"*. A lista desenhava `[x]` e `[ ]` - a forma universal de caixa de seleção - e
|
|
7
|
+
* exigia digitar o nome do item. A tela prometia uma interação que não existia, e a promessa da
|
|
8
|
+
* tela é a única que a pessoa lê.
|
|
9
|
+
*
|
|
10
|
+
* O IRMÃO JÁ EXISTIA: `commands/import.ts` tem um seletor de escolha ÚNICA desde 31/07 - setas,
|
|
11
|
+
* Enter, Ctrl-C tratado, redesenho subindo N linhas. Este é o mesmo mecanismo com a diferença que
|
|
12
|
+
* importa: o espaço marca e desmarca, e a resposta é uma lista. Os dois ficam, porque a pergunta
|
|
13
|
+
* "qual" e a pergunta "quais" são diferentes na tela; o que não pode existir é uma TERCEIRA
|
|
14
|
+
* leitura de tecla neste CLI.
|
|
15
|
+
*
|
|
16
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
17
|
+
* AS TRÊS COISAS QUE PODEM DAR ERRADO FORA DO NOSSO PROGRAMA, e é por elas que este módulo é
|
|
18
|
+
* pequeno e fechado:
|
|
19
|
+
*
|
|
20
|
+
* o raw mode fica ligado o shell dele para de ecoar o que ele digita, muito depois de a
|
|
21
|
+
* gente ter terminado. Restaurado em TODA saída, inclusive a de erro
|
|
22
|
+
* Ctrl-C vira escolha sair não é escolher. O `onQuit` encerra sem resolver a promessa
|
|
23
|
+
* não há terminal não se tenta ler tecla nenhuma: quem chama decide o que fazer,
|
|
24
|
+
* e no `connect` isso já tem resposta desde a etapa 12
|
|
25
|
+
*
|
|
26
|
+
* `stdin` e `stdout` são ARGUMENTOS porque um seletor que fala com `process` direto não tem como
|
|
27
|
+
* ser provado sem um terminal - e o que precisa de prova aqui é a leitura de tecla, não o
|
|
28
|
+
* desenho.
|
|
29
|
+
*/
|
|
30
|
+
/** O terminal tem teclado? Sem isto, não se tenta ler tecla nenhuma. */
|
|
31
|
+
export const canPick = (stdin = process.stdin, stdout = process.stdout) => Boolean(stdin.isTTY) &&
|
|
32
|
+
Boolean(stdout.isTTY) &&
|
|
33
|
+
typeof stdin.setRawMode === "function";
|
|
34
|
+
const UP = "[A";
|
|
35
|
+
const DOWN = "[B";
|
|
36
|
+
const ENTER_KEYS = ["\r", "\n"];
|
|
37
|
+
const CTRL_C = "";
|
|
38
|
+
export function pickMany(input) {
|
|
39
|
+
const stdin = (input.stdin ?? process.stdin);
|
|
40
|
+
const stdout = input.stdout ?? process.stdout;
|
|
41
|
+
const items = input.items;
|
|
42
|
+
const marked = new Set(input.picked);
|
|
43
|
+
let at = 0;
|
|
44
|
+
let first = true;
|
|
45
|
+
const render = () => {
|
|
46
|
+
if (!first)
|
|
47
|
+
stdout.write(`[${items.length}A`);
|
|
48
|
+
first = false;
|
|
49
|
+
for (const [i, item] of items.entries()) {
|
|
50
|
+
const box = marked.has(item.id) ? "[x]" : "[ ]";
|
|
51
|
+
const cursor = i === at ? ">" : " ";
|
|
52
|
+
const note = item.note ? ` ${item.note}` : "";
|
|
53
|
+
stdout.write(`[2K ${cursor} ${box} ${item.label.padEnd(13)}${note}\n`);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
render();
|
|
57
|
+
return new Promise((resolve) => {
|
|
58
|
+
/**
|
|
59
|
+
* UMA SAÍDA SÓ, e é ela que garante a promessa do raw mode. Toda tecla que termina a
|
|
60
|
+
* pergunta passa por aqui - inclusive o Ctrl-C.
|
|
61
|
+
*/
|
|
62
|
+
const leave = () => {
|
|
63
|
+
stdin.off("data", onData);
|
|
64
|
+
if (stdin.isTTY)
|
|
65
|
+
stdin.setRawMode?.(false);
|
|
66
|
+
stdin.pause();
|
|
67
|
+
};
|
|
68
|
+
const onData = (buf) => {
|
|
69
|
+
const key = buf.toString();
|
|
70
|
+
if (key === CTRL_C) {
|
|
71
|
+
leave();
|
|
72
|
+
stdout.write("\n");
|
|
73
|
+
(input.onQuit ?? (() => process.exit(130)))();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (key === UP || key === "k") {
|
|
77
|
+
at = (at + items.length - 1) % items.length;
|
|
78
|
+
render();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (key === DOWN || key === "j" || key === "\t") {
|
|
82
|
+
at = (at + 1) % items.length;
|
|
83
|
+
render();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (key === " ") {
|
|
87
|
+
const id = items[at]?.id;
|
|
88
|
+
if (id !== undefined) {
|
|
89
|
+
if (marked.has(id))
|
|
90
|
+
marked.delete(id);
|
|
91
|
+
else
|
|
92
|
+
marked.add(id);
|
|
93
|
+
render();
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (ENTER_KEYS.includes(key)) {
|
|
98
|
+
leave();
|
|
99
|
+
/** Na ORDEM da lista, e não na ordem em que ele marcou - a tela é que ele leu. */
|
|
100
|
+
resolve(items.filter((i) => marked.has(i.id)).map((i) => i.id));
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
if (stdin.isTTY)
|
|
104
|
+
stdin.setRawMode?.(true);
|
|
105
|
+
stdin.resume();
|
|
106
|
+
stdin.on("data", onData);
|
|
107
|
+
});
|
|
108
|
+
}
|
package/package.json
CHANGED