synthesisui 0.16.220 → 0.16.222

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.
@@ -7,6 +7,7 @@ import { readToken, resolveRegistry } from "../config.js";
7
7
  import { unsentEvents } from "../doctor/ledger.js";
8
8
  import { CHECKER_SINCE, installedBehind, MATERIALISER_SINCE, READER_SINCE, } from "../install-marks.js";
9
9
  import { measuredScope } from "../measured-scope.js";
10
+ import { SKILLS } from "../skills.js";
10
11
  /**
11
12
  * QUAL CLI MEDIU O CENSO EM DISCO - e era o `reader`, um inteiro, até 11/08.
12
13
  *
@@ -150,6 +151,36 @@ opts = {}) {
150
151
  */
151
152
  run: "npx synthesisui sync",
152
153
  });
154
+ /**
155
+ * A SKILL QUE ESTE CLI TEM E ESTE REPO NÃO - e sem isto ela nunca chegaria.
156
+ *
157
+ * `connect` é idempotente e atualiza sozinho, mas ninguém roda um comando de novo sem motivo: uma
158
+ * skill nova ficava esperando o acaso. Aqui ela vira uma linha do alinho, que é a única coisa que
159
+ * fala com a pessoa sem ela pedir.
160
+ *
161
+ * Compara o CONTEÚDO e não só a existência: uma skill velha descreve um fluxo que este CLI já não
162
+ * tem, e isso é pior que não ter skill nenhuma - quem lê não tem como perceber.
163
+ */
164
+ const installedSkills = await Promise.all(SKILLS.map((skill) => readFile(join(root, skill.path), "utf8").catch(() => null)));
165
+ /**
166
+ * E SÓ FALA COM QUEM JÁ TEM ALGUMA - senão isto vira o alarme que ensina uma palavra nova a quem
167
+ * não pediu.
168
+ *
169
+ * Um repositório sem skill nenhuma nunca ligou o agente, e pode nem usar Claude Code. Dizer a essa
170
+ * pessoa que "uma skill está faltando" é nomear uma ausência que ela escolheu, e `align` é a única
171
+ * superfície que fala sem ser chamada - a regra dela é ficar calada no estado saudável.
172
+ *
173
+ * Com uma instalada, o silêncio passa a ser o erro: ela ligou o agente, e uma skill nova ficaria
174
+ * esperando o acaso de alguém rodar `connect` de novo.
175
+ */
176
+ const staleSkills = SKILLS.filter((skill, i) => installedSkills[i] !== skill.source).map((skill) => skill.label);
177
+ if (installedSkills.some((have) => have != null) && staleSkills.length > 0)
178
+ out.push({
179
+ says: staleSkills.length === 1
180
+ ? `${staleSkills[0]} is missing or older than this CLI - it is a skill your agent can invoke, and it is not here.`
181
+ : `${staleSkills.length} skills are missing or older than this CLI (${staleSkills.join(", ")}) - your agent can invoke them, and they are not here.`,
182
+ run: "npx synthesisui connect",
183
+ });
153
184
  /**
154
185
  * SÓ O QUE NÃO SUBIU - ver `unsentEvents`. Isto contava o arquivo inteiro, e como o ledger é
155
186
  * append-only e o `sync` manda tudo (a plataforma deduplica), a linha nunca mais saía da tela e o
@@ -6,8 +6,7 @@ import { resolveRegistry } from "../config.js";
6
6
  import { body, paint, section, snippet } from "../output.js";
7
7
  import { readShellAnswer, rememberShellNo } from "../shell-answer.js";
8
8
  import { existingRc, hasHook, pinnedInHook, rcPathFor, shellFrom, shellSnippet, withHook, } from "../shell-hook.js";
9
- import { IMPORT_SKILL, IMPORT_SKILL_PATH } from "../skill-import.js";
10
- import { INIT_SKILL, INIT_SKILL_PATH } from "../skill-init.js";
9
+ import { SKILLS } from "../skills.js";
11
10
  import { add } from "./add.js";
12
11
  import { reportWhatIsLeft } from "./align.js";
13
12
  import { ci } from "./ci.js";
@@ -293,24 +292,19 @@ export async function connect(opts) {
293
292
  * something change" - so nobody has to remember a second command.
294
293
  */
295
294
  /**
296
- * AS DUAS SKILLS, e o `/sui-init` é a que importa mais aqui: é a PRIMEIRA CORRIDA, e uma skill que
295
+ * AS TRÊS SKILLS, e o `/sui-init` é a que importa mais aqui: é a PRIMEIRA CORRIDA, e uma skill que
297
296
  * só aparece depois de reiniciar o editor não serve para a primeira corrida de ninguém. Ela vem no
298
- * mesmo `connect` que ainda vai pedir o reinício - então quando a pessoa reabre, as duas existem.
297
+ * mesmo `connect` que ainda vai pedir o reinício - então quando a pessoa reabre, as três existem.
298
+ *
299
+ * A TERCEIRA É DE MANUTENÇÃO, e ela é de outra natureza: as duas primeiras são de ENTRADA e rodam
300
+ * uma vez. `/sui-adapt` responde a pergunta do dia seguinte - *"isso aqui está de acordo com o meu
301
+ * design system?"* -, apontando para um componente, toda semana ou depois de mexer em alguma coisa.
302
+ *
303
+ * Ela quase ficou de fora daqui, e teria sido o erro de sempre: uma skill que só existe no NOSSO
304
+ * repositório é uma skill que só nós rodamos, e o caso de uso inteiro dela é o cliente rodando
305
+ * sozinho. Meia jornada não é meio valor.
299
306
  */
300
- const skills = [
301
- {
302
- path: INIT_SKILL_PATH,
303
- source: INIT_SKILL,
304
- label: "/sui-init",
305
- what: "the first run, start to finish",
306
- },
307
- {
308
- path: IMPORT_SKILL_PATH,
309
- source: IMPORT_SKILL,
310
- label: "/sui-import-ds",
311
- what: "the import, orchestrated",
312
- },
313
- ];
307
+ const skills = SKILLS;
314
308
  /**
315
309
  * A PASTA VELHA SAI, e isto é obrigatório numa renomeação de skill distribuída.
316
310
  *
@@ -130,6 +130,20 @@ export async function* walkAll(roots) {
130
130
  * and the recipes `add` put next to them in `design-system.json`. Those
131
131
  * recipes are why the component pass can exist at all - a linter has no idea
132
132
  * what `ds-button` promised. */
133
+ /**
134
+ * COMO CADA PEDIDO SE LÊ NUMA LINHA - e o `else` desta expressão mentia.
135
+ *
136
+ * Ela dizia `component "<nome>"` para tudo que não fosse token, então um pedido de REGRA aparecia no
137
+ * doctor como se fosse um componente pedido. Uma tela que rotula errado é pior que uma que não
138
+ * rotula: quem lê decide em cima do rótulo.
139
+ */
140
+ function requestLabel(r) {
141
+ if (r.kind === "token")
142
+ return `token ${r.value} as ${r.name}`;
143
+ if (r.kind === "rule")
144
+ return `rule "${r.name}"`;
145
+ return `component "${r.name}"`;
146
+ }
133
147
  export async function loadSystem(root) {
134
148
  const dsDir = join(root, "_synthesisui", "ds");
135
149
  let slugs;
@@ -941,7 +955,7 @@ export async function doctor(opts) {
941
955
  console.log("");
942
956
  console.log(section("What your agent asked for"));
943
957
  for (const r of requests.slice(0, 8)) {
944
- console.log(body(` ${r.id} ${r.kind === "token" ? `token ${r.value} as ${r.name}` : `component "${r.name}"`}${r.area === "platform" ? " [platform]" : ""}`));
958
+ console.log(body(` ${r.id} ${requestLabel(r)}${r.area === "platform" ? " [platform]" : ""}`));
945
959
  console.log(body(` for: ${r.purpose}`));
946
960
  if (r.considered)
947
961
  console.log(body(` considered: ${r.considered}`));
@@ -200,6 +200,29 @@ const TOOLS = [
200
200
  required: ["value", "name", "purpose"],
201
201
  },
202
202
  },
203
+ {
204
+ name: "request_rule",
205
+ description: "File a rule request when this component does something the system's doctrine does not cover, and you had to decide alone. Read `system_doctrine` first: if a rule already answers it, follow it instead of filing. This is for the case with no rule - what the rule would say, and the case that asked for it. Do NOT invent a convention and move on; a decision that lives only in one file is not a decision of the system.",
206
+ inputSchema: {
207
+ type: "object",
208
+ properties: {
209
+ name: {
210
+ type: "string",
211
+ description: "What the rule would say, in one line: 'a card that opens a dialog carries the trigger, never the panel'",
212
+ },
213
+ purpose: {
214
+ type: "string",
215
+ description: "The case that asked for it - what you were building when no rule answered.",
216
+ },
217
+ considered: {
218
+ type: "string",
219
+ description: "Which existing rules you read and why they did not answer.",
220
+ },
221
+ file: { type: "string", description: "Where the case lives." },
222
+ },
223
+ required: ["name", "purpose"],
224
+ },
225
+ },
203
226
  ];
204
227
  /**
205
228
  * QUANTAS FERRAMENTAS ESTE SERVIDOR SERVE, lido da lista.
@@ -1037,6 +1060,24 @@ cli) {
1037
1060
  ? `Filed as ${r.id} and routed to the synthesisui platform team - the system's contract already promises this and the shipped css does not deliver it. No one needs to act: it closes itself when an update lands. Keep the quiet base meanwhile.`
1038
1061
  : `Filed as ${r.id}. Do not add the token yourself - the request shows up in \`synthesisui doctor\` for a person to decide.`);
1039
1062
  }
1063
+ case "request_rule": {
1064
+ /**
1065
+ * SEM TRIAGEM AUTOMÁTICA, e de propósito.
1066
+ *
1067
+ * `request_token` sabe rotear para a plataforma quando o contrato do sistema já promete o
1068
+ * nome - é uma pergunta que a máquina responde. "Isto deveria ser uma regra?" não é: ela é a
1069
+ * decisão de quem é dono do sistema, e roteá-la sozinho seria inventar a resposta em vez de
1070
+ * abrir a pergunta.
1071
+ */
1072
+ const r = await fileRequest(root, {
1073
+ kind: "rule",
1074
+ name: String(args.name ?? ""),
1075
+ purpose: String(args.purpose ?? ""),
1076
+ considered: args.considered ? String(args.considered) : undefined,
1077
+ file: args.file ? String(args.file) : undefined,
1078
+ });
1079
+ return text(`Filed as ${r.id}. Do not adopt the convention as if it were a rule - it shows up in \`synthesisui doctor\` and travels to the system's queue, for the owner to make it a rule or decline it.`);
1080
+ }
1040
1081
  default:
1041
1082
  return text(`No tool named ${name}.`, true);
1042
1083
  }
@@ -1,5 +1,5 @@
1
1
  import { resolve } from "node:path";
2
- import { closeRequest, fileRequest, readRequests } from "../doctor/requests.js";
2
+ import { closeRequest, fileRequest, KINDS, readRequests, } from "../doctor/requests.js";
3
3
  import { body, section } from "../output.js";
4
4
  /**
5
5
  * `synthesisui request` - the queue of what the agent needed and was refused.
@@ -12,6 +12,12 @@ import { body, section } from "../output.js";
12
12
  * Closing is deliberately explicit. A request nobody got to is still a
13
13
  * request; nothing here expires.
14
14
  */
15
+ /** Como cada tipo se lê numa linha - um lugar, três telas. */
16
+ const labelOf = (r) => r.kind === "token"
17
+ ? `token ${r.value} as ${r.name}`
18
+ : r.kind === "rule"
19
+ ? `rule "${r.name}"`
20
+ : `component "${r.name}"`;
15
21
  export async function request(opts) {
16
22
  const root = resolve(opts.dir ?? process.cwd());
17
23
  if (opts.done) {
@@ -29,7 +35,7 @@ export async function request(opts) {
29
35
  return;
30
36
  }
31
37
  for (const r of all) {
32
- console.log(body(` ${r.id} ${r.kind === "token" ? `token ${r.value} as ${r.name}` : `component "${r.name}"`}`));
38
+ console.log(body(` ${r.id} ${labelOf(r)}`));
33
39
  console.log(body(` for: ${r.purpose}`));
34
40
  if (r.considered)
35
41
  console.log(body(` considered: ${r.considered}`));
@@ -40,14 +46,23 @@ export async function request(opts) {
40
46
  console.log(body("Close one: synthesisui request --done <id>"));
41
47
  return;
42
48
  }
43
- if (opts.kind !== "component" && opts.kind !== "token") {
44
- console.log(`Unknown kind "${opts.kind}" - component or token.`);
49
+ if (!KINDS.has(opts.kind)) {
50
+ console.log(`Unknown kind "${opts.kind}" - component, token or rule.`);
45
51
  return;
46
52
  }
53
+ /**
54
+ * UMA REGRA PRECISA DO CASO, e é por isso que ela exige `--for` como as outras.
55
+ *
56
+ * "Isto deveria ser uma regra" sem o caso que a motivou é uma opinião. Com o caso, quem decidir do
57
+ * outro lado tem o que a doutrina não cobria e onde isso apareceu - que é a diferença entre uma
58
+ * fila que vira decisão e uma que vira backlog.
59
+ */
47
60
  if (!opts.name || !opts.purpose || (opts.kind === "token" && !opts.value)) {
48
61
  console.log(opts.kind === "token"
49
62
  ? "A token request needs --value, --name and --for."
50
- : "A component request needs --name and --for.");
63
+ : opts.kind === "rule"
64
+ ? "A rule request needs --name and --for - what the rule would say, and the case that asked for it."
65
+ : "A component request needs --name and --for.");
51
66
  return;
52
67
  }
53
68
  const r = await fileRequest(root, {
@@ -22,6 +22,8 @@ import { join } from "node:path";
22
22
  * how a team shares a queue.
23
23
  */
24
24
  export const REQUESTS_FILE = "requests.jsonl";
25
+ /** Os tipos que a fila aceita, num lugar só - ver `GapRequest.kind`. */
26
+ export const KINDS = new Set(["component", "token", "rule"]);
25
27
  const path = (root) => join(root, "_synthesisui", REQUESTS_FILE);
26
28
  /** Stable-enough id from content: 6 chars, collision-safe at queue scale. */
27
29
  function idOf(kind, name, at) {
@@ -65,7 +67,14 @@ export async function readRequests(root) {
65
67
  continue;
66
68
  try {
67
69
  const r = JSON.parse(line);
68
- if (r?.id && r.name && (r.kind === "component" || r.kind === "token"))
70
+ /**
71
+ * O FILTRO DA LEITURA - e ele é um dos três lugares onde um tipo novo some CALADO.
72
+ *
73
+ * Uma linha com `kind` desconhecido é descartada aqui sem erro, então acrescentar um tipo sem
74
+ * passar por este ponto produz um pedido que o agente arquiva, o arquivo guarda, e ninguém
75
+ * nunca lê.
76
+ */
77
+ if (r?.id && r.name && KINDS.has(r.kind))
69
78
  out.push(r);
70
79
  }
71
80
  catch {
@@ -0,0 +1,195 @@
1
+ /**
2
+ * A SKILL DE MANUTENÇÃO, como o CLI a distribui.
3
+ *
4
+ * Mesmo motivo do `skill-init.ts` e do `skill-import.ts`: o build é `tsc` e nada mais, então um `.md`
5
+ * precisaria de um passo de cópia que pode silenciosamente não rodar. Um módulo TypeScript não pode
6
+ * falhar em ser empacotado.
7
+ *
8
+ * ESTA É A FONTE. A cópia em `.claude/skills/` é o que o nosso editor lê, e o spec assere que as duas
9
+ * são idênticas.
10
+ *
11
+ * E ela é a TERCEIRA que o `connect` instala - as outras duas são de ENTRADA (primeira corrida,
12
+ * import). Esta responde a pergunta do dia seguinte, apontando para uma tela: *"isso aqui está de
13
+ * acordo com o meu design system?"*. Deixá-la fora do `connect` faria dela uma skill nossa, e o caso
14
+ * de uso que a motivou é o cliente rodando sozinho toda semana.
15
+ */
16
+ export const ADAPT_SKILL = `---
17
+ name: sui-adapt
18
+ description: Confronta UM componente (ou uma página, ou uma pasta) contra o design system instalado e adapta o que der - cobertura de token medida, conserto mecânico proposto antes de escrever, e o que sobra classificado em "regra nova" ou "conserto local". Use quando alguém aponta para uma peça e pergunta se ela está de acordo com o sistema (ex. "/sui-adapt components/ui/card", "analisa esse componente aqui", "isso aqui segue o meu design system?"), como rotina semanal, ou logo depois de criar/alterar um componente. Mede antes de propor, propõe antes de escrever, e nunca arquiva pedido em nome de ninguém.
19
+ ---
20
+
21
+ # Adaptar uma peça ao sistema
22
+
23
+ O propósito do produto, que decide todo empate abaixo: **ler o repositório do cliente e
24
+ devolver receitas com paridade visual, semântica e funcional, sem supor e sem inventar
25
+ nada.**
26
+
27
+ Esta skill é a ponta de manutenção. As outras duas que o cliente tem são de ENTRADA -
28
+ \`sui-init\` e \`sui-import-ds\` transformam o repositório dele em sistema. Esta responde a
29
+ pergunta do dia seguinte, que ele faz apontando para uma tela: *"isso aqui está de acordo
30
+ com o meu design system?"*.
31
+
32
+ \`CLAUDE.md\` manda. Quando os dois divergirem, este arquivo é que está velho.
33
+
34
+ ---
35
+
36
+ ## 0. O QUE ESTA SKILL NÃO FAZ
37
+
38
+ Três limites, e cada um existe por um motivo que já custou alguma coisa:
39
+
40
+ \`\`\`
41
+ não escreve sem propor é o repositório DELE. \`--fix --write\` sem confirmação é
42
+ outra categoria de confiança, e uma skill que perde essa
43
+ confiança não é rodada uma segunda vez
44
+ não arquiva pedido \`request\` fila uma decisão na plataforma. A skill MOSTRA o
45
+ comando; quem roda é ele
46
+ não inventa token, nome um agente que cala um relatório fazendo o sistema crescer é
47
+ nem regra pior que a deriva que ele veio medir
48
+ \`\`\`
49
+
50
+ ## 1. O ALVO, E POR QUE O ESCOPO É DECISÃO DA SKILL
51
+
52
+ Componente quase nunca é um arquivo. \`Card.tsx\` costuma vir com \`Card.css\`, \`Card.stories.tsx\`
53
+ e às vezes um \`index.ts\` - e medir só o \`.tsx\` produz um número que mente por omissão: o css
54
+ ao lado é justamente onde os valores à mão se escondem.
55
+
56
+ \`\`\`
57
+ 1. resolva o alvo o que ele apontou, ou o arquivo aberto, ou o que ele acabou de mexer
58
+ 2. suba para a PASTA quando o irmão existir (mesmo nome, extensão diferente)
59
+ 3. DIGA qual escopo você usou, com o número de arquivos
60
+ \`\`\`
61
+
62
+ Nunca meça os dois e escolha o maior. Diga o que mediu.
63
+
64
+ ## 2. MEÇA - e a medida é determinística, não sua
65
+
66
+ Duas portas, mesma resposta. Use a que a sessão tiver:
67
+
68
+ \`\`\`
69
+ MCP check_file { path }
70
+ terminal npx synthesisui doctor <alvo>
71
+ \`\`\`
72
+
73
+ O que volta, medido num componente real (\`ArticleCard\`, 13/08):
74
+
75
+ \`\`\`
76
+ SignalUI v7 - 181 tokens, 1 file read
77
+ scope: packages/ui/src/lib/SignalUI/organisms/ArticleCard/ArticleCard.tsx
78
+
79
+ Token coverage ░░░░░░░░░░░░░░░░░░░░░░░░ 0%
80
+ 0 from the system, 2 by hand
81
+ 50% is one command away - 1 of those have a name waiting
82
+ \`\`\`
83
+
84
+ Três números, e eles já vêm separados por natureza:
85
+
86
+ \`\`\`
87
+ from the system já usa o vocabulário. Nada a fazer
88
+ have a name waiting o sistema JÁ nomeia esse valor -> mecânico, é o passo 3
89
+ no name for it o sistema não nomeia -> DECISÃO dele, é o passo 5
90
+ \`\`\`
91
+
92
+ Não recalcule nada disso de cabeça. O número que você reporta é o que o comando disse.
93
+
94
+ ## 3. PROPONHA O MECÂNICO, E SÓ DEPOIS ESCREVA
95
+
96
+ \`--fix\` mostra; \`--write\` escreve. Rode o primeiro, cole a saída, espere.
97
+
98
+ \`\`\`
99
+ npx synthesisui doctor <alvo> --fix
100
+ \`\`\`
101
+
102
+ \`\`\`
103
+ Would replace 1 hand-written value with the token your system already has, across 1 file.
104
+ var(--ds-color-ocean-50) · 1 time
105
+ 1 finding left: values your system has no name for. Those are decisions.
106
+ \`\`\`
107
+
108
+ Ofereça exatamente três saídas, nesta ordem:
109
+
110
+ \`\`\`
111
+ confirmar npx synthesisui doctor <alvo> --fix --write
112
+ à mão você lista as trocas e ele edita - use quando ele quiser revisar linha a linha
113
+ cancelar e a medição fica, que já é resultado: ele sabe onde está
114
+ \`\`\`
115
+
116
+ \`--fix\` só troca o que o sistema DELE já nomeia. Ele nunca inventa um nome, e é por isso
117
+ que o conserto pode ser mecânico.
118
+
119
+ ## 4. AS REGRAS, QUE É A METADE QUE NENHUM COMANDO FAZ
120
+
121
+ O passo 2 é determinístico e sai de graça. Este não: o sistema carrega uma doutrina em
122
+ prosa, e **nada a verifica mecanicamente**. É aqui que você trabalha.
123
+
124
+ \`\`\`
125
+ MCP system_doctrine
126
+ terminal as regras viajam no documento instalado (_synthesisui/ds/<slug>/doctrine.json)
127
+ \`\`\`
128
+
129
+ Leia as regras e confronte o componente com cada uma. Três respostas possíveis por regra,
130
+ e a terceira é a que interessa:
131
+
132
+ \`\`\`
133
+ cumpre diga em uma linha, sem cerimônia
134
+ NÃO cumpre cite a regra, o lugar no arquivo, e proponha o conserto
135
+ a regra não fala sobre isto <- o passo 5
136
+ \`\`\`
137
+
138
+ Uma regra que você teve que interpretar para aplicar não é "cumpre". É o terceiro caso.
139
+
140
+ ## 5. O QUE SOBROU, CLASSIFICADO - e cada classe tem um destino
141
+
142
+ Aqui a pergunta deixa de ser "está certo?" e passa a ser **"isto vira sistema, ou fica
143
+ aqui?"**. Três destinos, e todos existem como comando:
144
+
145
+ \`\`\`
146
+ valor sem nome no sistema
147
+ -> npx synthesisui request token --value "<valor>" --name "<como se chamaria>" --for "<o caso>"
148
+ ou, pelo MCP: request_token
149
+
150
+ peça que falta
151
+ -> npx synthesisui request component --name "<nome>" --for "<o caso>"
152
+ ou: request_component
153
+
154
+ a doutrina não cobre este caso
155
+ -> npx synthesisui request rule --name "<o que a regra diria>" --for "<o caso que pediu>"
156
+ ou: request_rule
157
+ \`\`\`
158
+
159
+ **Você mostra o comando. Ele roda.** Um pedido é uma decisão entrando na fila do sistema
160
+ dele - e a fila viaja para a plataforma no próximo \`sync\`, que é o que a torna
161
+ compartilhável quando houver time. Arquivar em nome dele seria decidir por ele e ainda
162
+ tirar dele a chance de dizer "não, isso fica local mesmo".
163
+
164
+ Antes de propor \`request rule\`, leia a doutrina inteira. Uma regra que já existe e você não
165
+ achou vira uma regra duplicada na fila, e a fila perde valor na terceira duplicata.
166
+
167
+ ## 6. FECHE EM QUATRO LINHAS
168
+
169
+ Sem prosa. O cliente precisa decidir, não auditar:
170
+
171
+ \`\`\`
172
+ <Componente> <n> arquivos
173
+ cobertura X% -> Y% (<n> trocas escritas · <n> continuam à mão)
174
+ regras <n> de <n> cumpridas <as que não, nomeadas>
175
+ na sua mão <n> decisões <token · componente · regra>, com o comando ao lado
176
+ \`\`\`
177
+
178
+ Se nada mudou, diga isso e pare. Uma skill que sempre encontra trabalho é uma skill que
179
+ inventa trabalho.
180
+
181
+ ## 7. ROTINA
182
+
183
+ Ela foi desenhada para duas horas do dia, e a segunda é a que mais rende:
184
+
185
+ \`\`\`
186
+ semanal "roda o sui-adapt no dashboard" - pega deriva antes de virar hábito
187
+ depois de mexer componente novo, ou alteração grande: rode ANTES do commit, enquanto a
188
+ decisão ainda está quente e o conserto ainda é barato
189
+ \`\`\`
190
+
191
+ O hook (\`PostToolUse\`) já roda a metade determinística a cada escrita, calado quando não há
192
+ o que dizer. Esta skill é o passo deliberado: ela junta o hook, a doutrina e a fila numa
193
+ conversa só, e termina com o cliente decidindo - não com um relatório.
194
+ `;
195
+ export const ADAPT_SKILL_PATH = ".claude/skills/sui-adapt/SKILL.md";
package/dist/skills.js ADDED
@@ -0,0 +1,39 @@
1
+ import { ADAPT_SKILL, ADAPT_SKILL_PATH } from "./skill-adapt.js";
2
+ import { IMPORT_SKILL, IMPORT_SKILL_PATH } from "./skill-import.js";
3
+ import { INIT_SKILL, INIT_SKILL_PATH } from "./skill-init.js";
4
+ /**
5
+ * AS SKILLS QUE O CLI DISTRIBUI, numa lista só - e ela existe porque DOIS comandos precisam dela.
6
+ *
7
+ * `connect` escreve; `align` cobra o que falta. Enquanto a lista morava dentro do `connect`, o
8
+ * `align` não tinha como saber que existia uma terceira - e uma skill nova só chegava a quem, por
9
+ * conta própria, rodasse `connect` de novo. Ninguém roda um comando de novo sem motivo.
10
+ *
11
+ * É a lei 8 no caso mais barato dela: a lacuna existe, a gente sabe qual é, e dizer custa uma linha.
12
+ */
13
+ export const SKILLS = [
14
+ {
15
+ path: INIT_SKILL_PATH,
16
+ source: INIT_SKILL,
17
+ label: "/sui-init",
18
+ what: "the first run, start to finish",
19
+ },
20
+ {
21
+ path: IMPORT_SKILL_PATH,
22
+ source: IMPORT_SKILL,
23
+ label: "/sui-import-ds",
24
+ what: "the import, orchestrated",
25
+ },
26
+ /**
27
+ * A DE MANUTENÇÃO, e ela é de outra natureza que as duas acima.
28
+ *
29
+ * As duas primeiras são de ENTRADA: rodam uma vez, e depois nunca mais. Esta responde a pergunta do
30
+ * dia seguinte, apontando para uma tela - *"isso aqui está de acordo com o meu design system?"* -,
31
+ * toda semana ou depois de mexer em alguma coisa. É a primeira que uma pessoa roda mais de uma vez.
32
+ */
33
+ {
34
+ path: ADAPT_SKILL_PATH,
35
+ source: ADAPT_SKILL,
36
+ label: "/sui-adapt",
37
+ what: "one component against the system, and what to do about it",
38
+ },
39
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.220",
3
+ "version": "0.16.222",
4
4
  "description": "Bring SynthesisUI design systems into any project - tokens, typed components, whole pages and an agent-ready CLAUDE.md manifest.",
5
5
  "type": "module",
6
6
  "bin": {