synthesisui 0.16.235 → 0.16.238
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/add.js +8 -10
- package/dist/commands/component.js +13 -6
- package/dist/commands/generate.js +9 -5
- package/dist/commands/mcp.js +92 -6
- package/dist/commands/refit.js +8 -5
- package/dist/guide.js +4 -3
- package/dist/install-marks.js +1 -1
- package/dist/skill-adapt.js +10 -342
- package/dist/skill-import.js +10 -1311
- package/dist/skill-init.js +10 -320
- package/package.json +1 -1
package/dist/commands/add.js
CHANGED
|
@@ -250,15 +250,13 @@ export async function add(slug, opts) {
|
|
|
250
250
|
await writeFile(join(slugDir, "requires.json"), `${JSON.stringify(required, null, 2)}\n`, "utf8");
|
|
251
251
|
}
|
|
252
252
|
/** A filosofia continua sendo CONTADA na saída, e ela vive no documento - ver abaixo. */
|
|
253
|
-
const
|
|
254
|
-
const sections = philosophy?.sections ?? [];
|
|
253
|
+
const voice = payload.voice ?? { hasContext: false, sections: 0 };
|
|
255
254
|
/**
|
|
256
|
-
* 5c. A
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
* um deles começa a discordar do outro. O `system_doctrine` lê do documento.
|
|
255
|
+
* 5c. A VOZ NÃO É MATERIALIZADA - nem no `.md` (aposentado acima) nem no
|
|
256
|
+
* `design-system.json` (R2, 16/08: o payload viaja sem `philosophy`). Ela
|
|
257
|
+
* mora na plataforma e o `system_doctrine` a busca servida - sempre a
|
|
258
|
+
* versão atual, nada em texto plano no repo. As REGRAS continuam locais
|
|
259
|
+
* (`doctrine.json`): o doctor e o hook precisam delas offline.
|
|
262
260
|
*/
|
|
263
261
|
// 6. discovery by the agent
|
|
264
262
|
const claudeMd = await syncClaudeMd(projectRoot);
|
|
@@ -288,8 +286,8 @@ export async function add(slug, opts) {
|
|
|
288
286
|
*/
|
|
289
287
|
const doctrine = [
|
|
290
288
|
...(rules.length > 0 ? [`${rules.length} rule(s)`] : []),
|
|
291
|
-
...(sections
|
|
292
|
-
? [
|
|
289
|
+
...(voice.sections > 0 || voice.hasContext
|
|
290
|
+
? [`the voice (${voice.sections} section(s), served from the platform)`]
|
|
293
291
|
: []),
|
|
294
292
|
];
|
|
295
293
|
if (doctrine.length > 0)
|
|
@@ -59,14 +59,21 @@ export async function component(slug, name, opts) {
|
|
|
59
59
|
if (!SAFE_NAME.test(res.name)) {
|
|
60
60
|
throw new RegistryError(`Registry returned an unsafe component name.`);
|
|
61
61
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
// R3 (dono, 16/08): as cópias .json/.css em _synthesisui só nascem quando
|
|
63
|
+
// são o PRODUTO do comando - `--artifacts-only` e targets não-Next. Quando
|
|
64
|
+
// o .tsx é materializado logo abaixo, ninguém as lê depois (medido na
|
|
65
|
+
// auditoria de 16/08), e cada arquivo a mais no repo dele é superfície.
|
|
66
|
+
const config = await readProjectConfig(root);
|
|
67
|
+
const artifactsAreTheProduct = opts.artifactsOnly === true || config.target !== "next";
|
|
68
|
+
if (artifactsAreTheProduct) {
|
|
69
|
+
const dir = join(root, "_synthesisui", "ds", slug, "components");
|
|
70
|
+
await mkdir(dir, { recursive: true });
|
|
71
|
+
await writeFile(join(dir, `${res.name}.json`), `${JSON.stringify(res.recipe, null, 2)}\n`, "utf8");
|
|
72
|
+
await writeFile(join(dir, `${res.name}.css`), `${res.css}\n`, "utf8");
|
|
73
|
+
console.log(`✓ ${res.name} → _synthesisui/ds/${slug}/components/${res.name}.{json,css} (${slug} v${res.version})`);
|
|
74
|
+
}
|
|
67
75
|
// 2. YOUR component - a real, importable `export function <Pascal>()` in the
|
|
68
76
|
// project's flavor (config: styles css|tailwind), under componentsDir.
|
|
69
|
-
const config = await readProjectConfig(root);
|
|
70
77
|
const wantInteractive = opts.interactive && hasInteractiveTemplate(res.name);
|
|
71
78
|
if (!opts.artifactsOnly && config.target === "next") {
|
|
72
79
|
/**
|
|
@@ -55,17 +55,21 @@ export async function generate(description, opts) {
|
|
|
55
55
|
}
|
|
56
56
|
console.log(`→ generating a component for "${slug}" at ${base} …`);
|
|
57
57
|
const res = await postGenerate(base, { slug, description, name: opts.name });
|
|
58
|
-
const dir = join(root, "_synthesisui", "ds", slug, "generated");
|
|
59
|
-
await mkdir(dir, { recursive: true });
|
|
60
|
-
await writeFile(join(dir, `${res.name}.json`), `${JSON.stringify(res.recipe, null, 2)}\n`, "utf8");
|
|
61
|
-
await writeFile(join(dir, `${res.name}.css`), `${res.css}\n`, "utf8");
|
|
62
58
|
const tries = `${res.tries} ${res.tries === 1 ? "try" : "tries"}`;
|
|
63
59
|
console.log(`✓ ${res.name} generated (${res.model}, ${tries})`);
|
|
64
|
-
console.log(` → _synthesisui/ds/${slug}/generated/${res.name}.{json,css}`);
|
|
65
60
|
// Materialize YOUR component (.tsx) too - the SAME codegen `component` uses -
|
|
66
61
|
// so a generated component is as usable as a brought-in one, not just a
|
|
67
62
|
// recipe you have to wire by hand.
|
|
68
63
|
const config = await readProjectConfig(root);
|
|
64
|
+
// R3 (dono, 16/08): as cópias .json/.css só quando são o produto - num
|
|
65
|
+
// target não-Next o .tsx abaixo não nasce, e aí elas são a entrega.
|
|
66
|
+
if (config.target !== "next") {
|
|
67
|
+
const dir = join(root, "_synthesisui", "ds", slug, "generated");
|
|
68
|
+
await mkdir(dir, { recursive: true });
|
|
69
|
+
await writeFile(join(dir, `${res.name}.json`), `${JSON.stringify(res.recipe, null, 2)}\n`, "utf8");
|
|
70
|
+
await writeFile(join(dir, `${res.name}.css`), `${res.css}\n`, "utf8");
|
|
71
|
+
console.log(` → _synthesisui/ds/${slug}/generated/${res.name}.{json,css}`);
|
|
72
|
+
}
|
|
69
73
|
let materialized = false;
|
|
70
74
|
if (config.target === "next") {
|
|
71
75
|
const version = await readActiveVersion(root, slug);
|
package/dist/commands/mcp.js
CHANGED
|
@@ -109,6 +109,25 @@ const TOOLS = [
|
|
|
109
109
|
required: ["name"],
|
|
110
110
|
},
|
|
111
111
|
},
|
|
112
|
+
{
|
|
113
|
+
name: "playbook",
|
|
114
|
+
description: "The skill playbooks (init, import, adapt) - SERVED, not shipped, so they are always current and your context only carries the step you are on. Call with { skill } to get the framing and a table of contents; then fetch ONLY the chapter for your current step with { skill, section }. Never fetch more than the step needs.",
|
|
115
|
+
inputSchema: {
|
|
116
|
+
type: "object",
|
|
117
|
+
properties: {
|
|
118
|
+
skill: {
|
|
119
|
+
type: "string",
|
|
120
|
+
enum: ["init", "import", "adapt"],
|
|
121
|
+
description: "Which playbook.",
|
|
122
|
+
},
|
|
123
|
+
section: {
|
|
124
|
+
type: "string",
|
|
125
|
+
description: "A chapter id from the toc. Omit to get the framing + toc.",
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
required: ["skill"],
|
|
129
|
+
},
|
|
130
|
+
},
|
|
112
131
|
{
|
|
113
132
|
name: "recipe_vocabulary",
|
|
114
133
|
description: "What a recipe CAN hold: every state that compiles, every preview form, the floor per kind, and the rules a media region needs. Call this BEFORE writing a recipe, not after - the reader that skipped it sent a `dark:` inside a variant and a state the compiler cannot spell, and both were dropped in silence. Served from the catalogue, so it grows as the contract grows.",
|
|
@@ -297,18 +316,55 @@ async function findToken(root, value) {
|
|
|
297
316
|
* palavra que a pessoa já tem. Sem rede, a doutrina responde igual e a checagem é que se cala: o
|
|
298
317
|
* dado é local justamente para não depender de servidor nenhum.
|
|
299
318
|
*/
|
|
319
|
+
/** A voz do sistema, servida da plataforma - ver o comentário no chamador. */
|
|
320
|
+
async function fetchVoice(root) {
|
|
321
|
+
const slug = await installedSlug(root);
|
|
322
|
+
const token = await readToken();
|
|
323
|
+
if (!slug || !token)
|
|
324
|
+
return { kind: "unreachable" };
|
|
325
|
+
const res = await fetch(`${resolveRegistry()}/api/registry/ds/${slug}?voice=1`, { headers: { Authorization: `Bearer ${token}` } }).catch(() => null);
|
|
326
|
+
if (!res?.ok)
|
|
327
|
+
return { kind: "unreachable" };
|
|
328
|
+
const body = (await res.json().catch(() => null));
|
|
329
|
+
if (!body)
|
|
330
|
+
return { kind: "unreachable" };
|
|
331
|
+
return {
|
|
332
|
+
kind: "ok",
|
|
333
|
+
voice: {
|
|
334
|
+
context: body.context ?? undefined,
|
|
335
|
+
sections: body.sections ?? [],
|
|
336
|
+
},
|
|
337
|
+
};
|
|
338
|
+
}
|
|
300
339
|
async function systemDoctrine(root) {
|
|
301
340
|
const { documents, doctrines } = await loadSystem(root);
|
|
302
341
|
const rules = doctrines.flatMap((d) => d.rules);
|
|
303
342
|
const parts = [];
|
|
304
343
|
const pinned = doctrines[0]?.version;
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
344
|
+
/**
|
|
345
|
+
* A VOZ É SERVIDA, NÃO LIDA DO DISCO (R2, dono 16/08). As REGRAS continuam
|
|
346
|
+
* locais - o doctor e o hook precisam delas offline, e essa decisão está
|
|
347
|
+
* declarada três vezes. A voz nunca foi lida por nenhum loop local: só esta
|
|
348
|
+
* ferramenta a renderiza, e ela agora pergunta à plataforma - sempre a
|
|
349
|
+
* versão atual, e nada dela em texto plano no repo. Sem rede, as regras
|
|
350
|
+
* respondem igual e a voz se declara indisponível em vez de sumir calada.
|
|
351
|
+
*
|
|
352
|
+
* Installs antigos (design-system.json com `philosophy` dentro) continuam
|
|
353
|
+
* funcionando: o disco é o fallback quando a rede não responde.
|
|
354
|
+
*/
|
|
355
|
+
const localVoice = documents.map((doc) => doc.philosophy ?? {});
|
|
356
|
+
const served = await fetchVoice(root);
|
|
357
|
+
const voices = served.kind === "ok"
|
|
358
|
+
? [served.voice]
|
|
359
|
+
: localVoice.filter((p) => p.context || (p.sections?.length ?? 0) > 0);
|
|
360
|
+
for (const p of voices) {
|
|
361
|
+
if (p.context)
|
|
308
362
|
parts.push(`## What this product is\n\n${p.context}`);
|
|
309
|
-
for (const sec of p
|
|
363
|
+
for (const sec of p.sections ?? [])
|
|
310
364
|
parts.push(`## ${sec.title}\n\n${sec.body}`);
|
|
311
365
|
}
|
|
366
|
+
if (served.kind === "unreachable" && voices.length === 0)
|
|
367
|
+
parts.push("## Voice\n\nThe voice is served from the platform and could not be reached right now. The rules above still apply in full - build with them, and fetch the voice again before writing user-facing copy.");
|
|
312
368
|
if (rules.length === 0 && parts.length === 0)
|
|
313
369
|
return "This system carries no rules and no philosophy yet. Nothing here overrides your judgement - build with its tokens and its components, and say what you needed and could not find.";
|
|
314
370
|
const head = [
|
|
@@ -408,6 +464,28 @@ async function listComponents(root) {
|
|
|
408
464
|
* properties need to be dynamic - the more we map, the more precision". A list written
|
|
409
465
|
* into a published package is a list that stops matching the contract.
|
|
410
466
|
*/
|
|
467
|
+
/**
|
|
468
|
+
* O PLAYBOOK, na fatia pedida (R1, dono 16/08): sem `section`, a moldura + o
|
|
469
|
+
* sumário; com, UM capítulo. O corpo inteiro nunca viaja - a dieta de contexto
|
|
470
|
+
* é servida por construção.
|
|
471
|
+
*/
|
|
472
|
+
async function playbook(skill, section) {
|
|
473
|
+
const answer = await askCatalogue("playbook", undefined, section ? { skill, section } : { skill });
|
|
474
|
+
if (!answer.ok)
|
|
475
|
+
return answer.because;
|
|
476
|
+
const body = answer.body;
|
|
477
|
+
if ("error" in body)
|
|
478
|
+
return [body.error, body.hint].filter(Boolean).join("\n");
|
|
479
|
+
if ("sections" in body) {
|
|
480
|
+
return [
|
|
481
|
+
body.intro,
|
|
482
|
+
"",
|
|
483
|
+
"## Chapters - fetch ONLY the one for your current step",
|
|
484
|
+
...body.sections.map((c) => `- ${c.id} - ${c.title}`),
|
|
485
|
+
].join("\n");
|
|
486
|
+
}
|
|
487
|
+
return body.body;
|
|
488
|
+
}
|
|
411
489
|
async function recipeVocabulary() {
|
|
412
490
|
const answer = await askCatalogue("vocabulary");
|
|
413
491
|
if (!answer.ok)
|
|
@@ -769,7 +847,9 @@ cli) {
|
|
|
769
847
|
}
|
|
770
848
|
})();
|
|
771
849
|
}
|
|
772
|
-
async function askCatalogue(want, post
|
|
850
|
+
async function askCatalogue(want, post,
|
|
851
|
+
/** Parâmetros extras do GET (ex.: o playbook e o capítulo pedidos). */
|
|
852
|
+
query) {
|
|
773
853
|
const token = await readToken();
|
|
774
854
|
if (!token) {
|
|
775
855
|
return {
|
|
@@ -779,7 +859,8 @@ async function askCatalogue(want, post) {
|
|
|
779
859
|
}
|
|
780
860
|
const base = resolveRegistry();
|
|
781
861
|
try {
|
|
782
|
-
const
|
|
862
|
+
const extra = query ? `&${new URLSearchParams(query).toString()}` : "";
|
|
863
|
+
const res = await fetch(`${base}/api/catalogue${post ? "" : `?want=${want}${extra}`}`, post
|
|
783
864
|
? {
|
|
784
865
|
method: "POST",
|
|
785
866
|
headers: {
|
|
@@ -966,6 +1047,11 @@ cli) {
|
|
|
966
1047
|
case "add_component":
|
|
967
1048
|
countAgentRead(root, String(args.name ?? ""), "add", cli);
|
|
968
1049
|
return text(await addComponent(root, String(args.name ?? "")));
|
|
1050
|
+
case "playbook": {
|
|
1051
|
+
const skill = String(args?.skill ?? "");
|
|
1052
|
+
const section = args?.section ? String(args.section) : undefined;
|
|
1053
|
+
return text(await playbook(skill, section));
|
|
1054
|
+
}
|
|
969
1055
|
case "recipe_vocabulary":
|
|
970
1056
|
return text(await recipeVocabulary());
|
|
971
1057
|
case "validate_recipe": {
|
package/dist/commands/refit.js
CHANGED
|
@@ -123,12 +123,15 @@ export async function refit(file, opts) {
|
|
|
123
123
|
recipe: res.recipe,
|
|
124
124
|
});
|
|
125
125
|
console.log(`✓ saved into "${slug}" (draft v${saved.version} - ships with your next publish)`);
|
|
126
|
-
// 5. materialize back into the project:
|
|
127
|
-
|
|
128
|
-
await mkdir(artifactsDir, { recursive: true });
|
|
129
|
-
await writeFile(join(artifactsDir, `${res.name}.json`), `${JSON.stringify(res.recipe, null, 2)}\n`, "utf8");
|
|
130
|
-
await writeFile(join(artifactsDir, `${res.name}.css`), `${res.css}\n`, "utf8");
|
|
126
|
+
// 5. materialize back into the project: YOUR typed component - e as cópias
|
|
127
|
+
// .json/.css só quando são o produto (target não-Next; R3, dono 16/08).
|
|
131
128
|
const config = await readProjectConfig(root);
|
|
129
|
+
if (config.target !== "next") {
|
|
130
|
+
const artifactsDir = join(root, "_synthesisui", "ds", slug, "components");
|
|
131
|
+
await mkdir(artifactsDir, { recursive: true });
|
|
132
|
+
await writeFile(join(artifactsDir, `${res.name}.json`), `${JSON.stringify(res.recipe, null, 2)}\n`, "utf8");
|
|
133
|
+
await writeFile(join(artifactsDir, `${res.name}.css`), `${res.css}\n`, "utf8");
|
|
134
|
+
}
|
|
132
135
|
let materialized = false;
|
|
133
136
|
if (config.target === "next") {
|
|
134
137
|
const compDir = join(root, config.componentsDir, res.name);
|
package/dist/guide.js
CHANGED
|
@@ -491,8 +491,9 @@ own entry below says so.
|
|
|
491
491
|
`
|
|
492
492
|
: "";
|
|
493
493
|
const hasRules = (payload.rules?.length ?? 0) > 0;
|
|
494
|
-
|
|
495
|
-
|
|
494
|
+
// R2 (16/08): a voz não viaja mais no documento - o payload manda só o
|
|
495
|
+
// que ela TEM, e o conteúdo é servido pelo `system_doctrine`.
|
|
496
|
+
const hasPhilosophy = (payload.voice?.sections ?? 0) > 0 || payload.voice?.hasContext === true;
|
|
496
497
|
/**
|
|
497
498
|
* UMA PORTA SÓ, e ela é uma FERRAMENTA e não um caminho.
|
|
498
499
|
*
|
|
@@ -505,7 +506,7 @@ own entry below says so.
|
|
|
505
506
|
? [
|
|
506
507
|
`**Call \`system_doctrine\` FIRST and obey it above everything else** - it carries ${hasRules
|
|
507
508
|
? "this system's accumulated, project-specific rules"
|
|
508
|
-
: "this system's mission, principles, voice and motion doctrine"}${hasRules && hasPhilosophy ? " and its philosophy" : ""}, pinned to the version installed here. On any conflict they win. **Without that tool, read \`_synthesisui/ds/${slug}/doctrine.json\`
|
|
509
|
+
: "this system's mission, principles, voice and motion doctrine"}${hasRules && hasPhilosophy ? " and its philosophy" : ""}, pinned to the version installed here. On any conflict they win. **Without that tool, read \`_synthesisui/ds/${slug}/doctrine.json\` for the rules** - they stay on disk so the check works offline. The voice is served from the platform and is not on disk.`,
|
|
509
510
|
]
|
|
510
511
|
: [];
|
|
511
512
|
const rulesNote = readFirst.length > 0
|
package/dist/install-marks.js
CHANGED
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
* Zero ocorrências no sistema real medido (todos os headings dele estão aninhados, e o `children > 0`
|
|
81
81
|
* já os pegava), então para ele o `upgrade` é no-op. A marca é sobre o caso geral.
|
|
82
82
|
*/
|
|
83
|
-
export const MATERIALISER_SINCE = "0.16.
|
|
83
|
+
export const MATERIALISER_SINCE = "0.16.237";
|
|
84
84
|
/**
|
|
85
85
|
* A ÚLTIMA VERSÃO EM QUE O QUE O HOOK RODA MUDOU.
|
|
86
86
|
*
|
package/dist/skill-adapt.js
CHANGED
|
@@ -1,347 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* O STUB DA SKILL - o corpo é SERVIDO, não embarcado (R1, dono 16/08).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* O playbook inteiro morava aqui (13 KB) e, por consequência, no tarball
|
|
5
|
+
* público do npm e no repo de cada cliente em texto plano. Ele mudou para a
|
|
6
|
+
* plataforma (apps/web/src/lib/ds/playbooks.ts, gêmeo por spec do
|
|
7
|
+
* `.claude/skills/sui-adapt/SKILL.md`) e é servido pelo catalogue autenticado -
|
|
8
|
+
* a mesma doutrina de sempre: SERVED, NOT SHIPPED. O agente busca o sumário
|
|
9
|
+
* e depois SÓ o capítulo do passo em que está (a dieta de contexto).
|
|
7
10
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
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.
|
|
11
|
+
* O frontmatter fica INTEIRO no stub: é a description que faz a skill ser
|
|
12
|
+
* invocada, e ela não é segredo - a esteira é.
|
|
15
13
|
*/
|
|
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
|
-
**Seu primeiro comando é a medição.** Não leia \`.mcp.json\`, não abra o censo, não liste
|
|
24
|
-
arquivo: \`check_file\` ou \`doctor <alvo>\` responde tudo isso em um passo, e é o que o resto
|
|
25
|
-
desta skill consome. Um agente que sai explorando antes gasta a paciência de quem pediu e
|
|
26
|
-
chega ao mesmo lugar.
|
|
27
|
-
|
|
28
|
-
O propósito do produto, que decide todo empate abaixo: **ler o repositório do cliente e
|
|
29
|
-
devolver receitas com paridade visual, semântica e funcional, sem supor e sem inventar
|
|
30
|
-
nada.**
|
|
31
|
-
|
|
32
|
-
Esta skill é a ponta de manutenção. As outras duas que o cliente tem são de ENTRADA -
|
|
33
|
-
\`sui-init\` e \`sui-import-ds\` transformam o repositório dele em sistema. Esta responde a
|
|
34
|
-
pergunta do dia seguinte, que ele faz apontando para uma tela: *"isso aqui está de acordo
|
|
35
|
-
com o meu design system?"*.
|
|
36
|
-
|
|
37
|
-
\`CLAUDE.md\` manda. Quando os dois divergirem, este arquivo é que está velho.
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## 0. COMO VOCÊ FALA COM ELE
|
|
42
|
-
|
|
43
|
-
**Ele quer saber o que está no projeto DELE. Nada do que está do nosso lado interessa.**
|
|
44
|
-
|
|
45
|
-
Esta seção vale acima de qualquer outra abaixo, porque uma medição perfeita escrita na
|
|
46
|
-
nossa língua é uma medição que ele não lê. Palavras que **nunca** aparecem para ele:
|
|
47
|
-
|
|
48
|
-
\`\`\`
|
|
49
|
-
ledger · census · scope · reading as ADOPTION · refresh_system · check_file
|
|
50
|
-
system_doctrine · carrier · alcançável · o número da versão do CLI · --fix --write
|
|
51
|
-
cobertura de token · o nome de qualquer comando ou ferramenta nossa
|
|
52
|
-
\`\`\`
|
|
53
|
-
|
|
54
|
-
A tradução é sempre a mesma: fale do **arquivo**, da **propriedade**, do **valor** e da
|
|
55
|
-
**variável dele**.
|
|
56
|
-
|
|
57
|
-
\`\`\`
|
|
58
|
-
em vez de diga
|
|
59
|
-
o ledger.cli está em 0.16.229 sua ferramenta está em dia
|
|
60
|
-
o scope do sistema é packages/ui este arquivo fica fora da pasta de onde o
|
|
61
|
-
seu design system nasceu, e isso é normal
|
|
62
|
-
1em → var(--ds-spacing-md) o seu projeto já chama esse valor de \`--spacing-md\`
|
|
63
|
-
cobertura de token: 0% nenhum destes valores vem do seu design system
|
|
64
|
-
rode doctor --fix --write eu troco para você, se você quiser
|
|
65
|
-
\`\`\`
|
|
66
|
-
|
|
67
|
-
**A variável que você oferece é a DELE.** O comando já devolve o nome do vocabulário dele
|
|
68
|
-
quando o repositório tem um - \`--color-lightgray-700\`, e não \`--ds-color-gray-400\`. Nunca
|
|
69
|
-
reescreva a sugestão para o nosso prefixo: renomear a variável dele não é adotar o sistema.
|
|
70
|
-
|
|
71
|
-
## 0b. O QUE ESTA SKILL NÃO FAZ
|
|
72
|
-
|
|
73
|
-
Três limites, e cada um existe por um motivo que já custou alguma coisa:
|
|
74
|
-
|
|
75
|
-
\`\`\`
|
|
76
|
-
não escreve sem propor é o repositório DELE. Escrever sem confirmação é outra
|
|
77
|
-
categoria de confiança, e uma skill que perde essa
|
|
78
|
-
confiança não é rodada uma segunda vez
|
|
79
|
-
não arquiva pedido um pedido fila uma decisão na plataforma. A skill
|
|
80
|
-
PERGUNTA; quem decide é ele
|
|
81
|
-
não inventa token, nome um agente que cala um relatório fazendo o sistema crescer é
|
|
82
|
-
nem regra pior que a deriva que ele veio medir
|
|
83
|
-
\`\`\`
|
|
84
|
-
|
|
85
|
-
## 1. O ALVO, E POR QUE O ESCOPO É DECISÃO DA SKILL
|
|
86
|
-
|
|
87
|
-
Componente quase nunca é um arquivo. \`Card.tsx\` costuma vir com \`Card.css\`, \`Card.stories.tsx\`
|
|
88
|
-
e às vezes um \`index.ts\` - e medir só o \`.tsx\` produz um número que mente por omissão: o css
|
|
89
|
-
ao lado é justamente onde os valores à mão se escondem.
|
|
90
|
-
|
|
91
|
-
\`\`\`
|
|
92
|
-
1. resolva o alvo o que ele apontou, ou o arquivo aberto, ou o que ele acabou de mexer
|
|
93
|
-
2. suba para a PASTA quando o irmão existir (mesmo nome, extensão diferente)
|
|
94
|
-
3. DIGA qual escopo você usou, com o número de arquivos
|
|
95
|
-
\`\`\`
|
|
96
|
-
|
|
97
|
-
Nunca meça os dois e escolha o maior. Diga o que mediu - **com os nomes dos arquivos**, não
|
|
98
|
-
com a palavra "escopo".
|
|
99
|
-
|
|
100
|
-
**O alvo fora da pasta importada é o caso COMUM, e não um erro.** O sistema nasceu de uma
|
|
101
|
-
pasta, e uma tela de app que consome o DS está fora dela - é literalmente o pedido
|
|
102
|
-
*"conserta essa página pro meu design system"*. Ali as variáveis valem igual, porque elas
|
|
103
|
-
são globais; o que não existe é receita daquele componente. Diga isso em uma linha, sem a
|
|
104
|
-
palavra "escopo", e siga.
|
|
105
|
-
|
|
106
|
-
## 2. MEÇA - e a medida é determinística, não sua
|
|
107
|
-
|
|
108
|
-
Duas portas, mesma resposta. Use a que a sessão tiver:
|
|
109
|
-
|
|
110
|
-
\`\`\`
|
|
111
|
-
MCP check_file { path }
|
|
112
|
-
terminal npx synthesisui doctor <alvo>
|
|
113
|
-
\`\`\`
|
|
114
|
-
|
|
115
|
-
Não recalcule nada de cabeça. O número que você reporta é o que o comando disse.
|
|
116
|
-
|
|
117
|
-
**A abertura tem três linhas e nenhuma a mais.** Ela responde "está em dia?", "o que você
|
|
118
|
-
leu?" e "como estou?", nesta ordem:
|
|
119
|
-
|
|
120
|
-
\`\`\`
|
|
121
|
-
sua ferramenta está em dia
|
|
122
|
-
|
|
123
|
-
li OverviewHeaderInformation - o componente e a folha de estilo dele, 2 arquivos
|
|
124
|
-
|
|
125
|
-
17 valores estão escritos à mão aqui, e nenhum vem do seu design system
|
|
126
|
-
\`\`\`
|
|
127
|
-
|
|
128
|
-
Se a ferramenta NÃO estiver em dia, essa primeira linha vira o primeiro item da fila, com o
|
|
129
|
-
comando à vista - uma medição feita com a versão velha responde outra coisa.
|
|
130
|
-
|
|
131
|
-
Os três grupos que o comando separa, e o que cada um vira:
|
|
132
|
-
|
|
133
|
-
\`\`\`
|
|
134
|
-
já usa uma variável nada a fazer
|
|
135
|
-
o projeto já nomeia é uma troca - vira item, com a variável DELE ao lado
|
|
136
|
-
ninguém nomeia é uma decisão dele - vira item, com as três saídas
|
|
137
|
-
\`\`\`
|
|
138
|
-
|
|
139
|
-
## 3. MONTE A FILA, E CONTE OS ITENS ANTES DE COMEÇAR
|
|
140
|
-
|
|
141
|
-
Aqui é onde esta skill se ganha ou se perde. Despejar tudo de uma vez - duas regras, quatro
|
|
142
|
-
decisões, dez trocas - não é um relatório, é uma parede. Quem lê não tem como agir; só
|
|
143
|
-
concordar ou fechar a aba.
|
|
144
|
-
|
|
145
|
-
Junte tudo o que você achou numa fila ÚNICA e **ordene por custo**:
|
|
146
|
-
|
|
147
|
-
\`\`\`
|
|
148
|
-
1º não muda um pixel troca por uma variável de mesmo valor
|
|
149
|
-
2º pode mudar o pixel \`em\` que segue a fonte, um degrau que colapsa
|
|
150
|
-
3º não é troca, é pedido variável nova, peça nova, regra nova
|
|
151
|
-
\`\`\`
|
|
152
|
-
|
|
153
|
-
Assim ele despacha o barato primeiro e para quando quiser, sem ficar devendo nada.
|
|
154
|
-
|
|
155
|
-
Anuncie o tamanho antes do primeiro item, sempre, e em uma frase que ele leia:
|
|
156
|
-
|
|
157
|
-
\`\`\`
|
|
158
|
-
achei 8 coisas. 2 não mudam nada na tela, 4 podem mudar, e 2 são decisão sua.
|
|
159
|
-
\`\`\`
|
|
160
|
-
|
|
161
|
-
## 4. UM ITEM POR VEZ, COM AS TRÊS SAÍDAS - e espere a resposta
|
|
162
|
-
|
|
163
|
-
Nunca apresente o item 2 antes de ele responder o 1. O cabeçalho carrega a posição, para
|
|
164
|
-
ele saber onde está e quanto falta. O corpo fala do componente dele, e termina numa
|
|
165
|
-
pergunta:
|
|
166
|
-
|
|
167
|
-
\`\`\`
|
|
168
|
-
item 1 de 8
|
|
169
|
-
|
|
170
|
-
OverviewHeaderInformation espaça com \`1em\` em 4 lugares, e esse valor não está
|
|
171
|
-
ligado ao seu design system.
|
|
172
|
-
|
|
173
|
-
styles.module.scss padding: 1em 0
|
|
174
|
-
padding-right: 1em
|
|
175
|
-
margin-left: 1em
|
|
176
|
-
margin-right: 0.5em
|
|
177
|
-
|
|
178
|
-
O que você quer fazer?
|
|
179
|
-
|
|
180
|
-
1 deixar como está
|
|
181
|
-
2 usar \`--spacing-md\`, que o seu projeto já tem
|
|
182
|
-
⚠ \`em\` segue a fonte do elemento, então isto pode mudar o espaçamento na tela
|
|
183
|
-
3 criar um nome novo para este valor no seu design system
|
|
184
|
-
|
|
185
|
-
[1] [2] [3] ou me diga outra coisa · [parar por aqui]
|
|
186
|
-
\`\`\`
|
|
187
|
-
|
|
188
|
-
As três saídas são sempre as mesmas, porque são as três que existem de verdade:
|
|
189
|
-
|
|
190
|
-
\`\`\`
|
|
191
|
-
1 deixar como está ele segue para o próximo, e o item entra no resumo como não mexido
|
|
192
|
-
2 usar uma variável você faz a edição e confirma em uma linha
|
|
193
|
-
SE a troca puder mudar a tela, isso vem escrito NA OPÇÃO, não depois
|
|
194
|
-
3 criar um nome novo vira um pedido, e é ele quem manda - ver o passo 6
|
|
195
|
-
\`\`\`
|
|
196
|
-
|
|
197
|
-
E \`parar por aqui\` fecha o resumo com o que andou. É sempre legítimo.
|
|
198
|
-
|
|
199
|
-
**Quando ele pedir para ver antes**, mostre as linhas e volte a perguntar - isso não conta
|
|
200
|
-
como resposta.
|
|
201
|
-
|
|
202
|
-
**Na opção 2, liste as variáveis dele quando houver mais de uma candidata.** Ele não decora
|
|
203
|
-
o vocabulário do próprio projeto, e escolher entre dois nomes é mais fácil que lembrar de
|
|
204
|
-
um.
|
|
205
|
-
|
|
206
|
-
## 5. AS REGRAS, QUE É A METADE QUE NENHUM COMANDO FAZ
|
|
207
|
-
|
|
208
|
-
O passo 2 é determinístico e sai de graça. Este não: o sistema carrega uma doutrina em
|
|
209
|
-
prosa, e **nada a verifica mecanicamente**. É aqui que você trabalha.
|
|
210
|
-
|
|
211
|
-
\`\`\`
|
|
212
|
-
MCP system_doctrine
|
|
213
|
-
terminal as regras viajam no documento instalado (_synthesisui/ds/<slug>/doctrine.json)
|
|
214
|
-
\`\`\`
|
|
215
|
-
|
|
216
|
-
Leia as regras e confronte o componente com cada uma. Três respostas possíveis por regra,
|
|
217
|
-
e a terceira é a que interessa:
|
|
218
|
-
|
|
219
|
-
\`\`\`
|
|
220
|
-
cumpre some do relatório. Diga só o total no fim
|
|
221
|
-
NÃO cumpre vira um ITEM da fila, com arquivo, linha e as três saídas
|
|
222
|
-
a regra não fala sobre isto vira um item de decisão - uma regra nova
|
|
223
|
-
\`\`\`
|
|
224
|
-
|
|
225
|
-
Uma regra que você teve que interpretar para aplicar não é "cumpre". É o terceiro caso.
|
|
226
|
-
|
|
227
|
-
E nunca escreva "3 de 5 cumpridas". Isso lê como boletim, e as 2 que faltam são justamente
|
|
228
|
-
as que TÊM conserto pronto - é a melhor notícia do relatório vestida como a pior.
|
|
229
|
-
|
|
230
|
-
## 6. OS PEDIDOS - você mostra o comando, ele roda
|
|
231
|
-
|
|
232
|
-
Quando ele escolhe a saída 3, existem três destinos, e todos existem de verdade:
|
|
233
|
-
|
|
234
|
-
\`\`\`
|
|
235
|
-
um valor sem nome no sistema
|
|
236
|
-
-> npx synthesisui request token --value "<valor>" --name "<como se chamaria>" --for "<o caso>"
|
|
237
|
-
uma peça que falta
|
|
238
|
-
-> npx synthesisui request component --name "<nome>" --for "<o caso>"
|
|
239
|
-
o sistema não tem regra sobre este caso
|
|
240
|
-
-> npx synthesisui request rule --name "<o que a regra diria>" --for "<o caso que pediu>"
|
|
241
|
-
\`\`\`
|
|
242
|
-
|
|
243
|
-
**Arquivar em nome dele seria decidir por ele** - e tirar dele a chance de dizer "não, isso
|
|
244
|
-
fica local mesmo". Antes de propor uma regra nova, leia a doutrina inteira: uma regra que já
|
|
245
|
-
existe e você não achou vira duplicata na fila, e a fila perde valor na terceira.
|
|
246
|
-
|
|
247
|
-
E o nome que você propõe para a variável nova segue a convenção DELE. Se o projeto escreve
|
|
248
|
-
\`--color-lightgray-700\`, o valor novo não vira \`--ds-color-neutral-4\`.
|
|
249
|
-
|
|
250
|
-
## 6b. A RÉGUA É O QUE DÁ PARA FAZER HOJE, E O TETO SE DIZ JUNTO
|
|
251
|
-
|
|
252
|
-
**100% quase nunca é alcançável hoje, e isso não é falha dele.** Se o sistema não tem nome
|
|
253
|
-
para \`#555\`, ninguém chega a 100% sem antes decidir criar esse nome. Um medidor que mostra
|
|
254
|
-
0% contra um teto imaginário faz trabalho completo parecer trabalho pela metade.
|
|
255
|
-
|
|
256
|
-
Os números para a conta certa já vêm do comando:
|
|
257
|
-
|
|
258
|
-
\`\`\`
|
|
259
|
-
3 valores à mão
|
|
260
|
-
2 o projeto já nomeia -> alcançável hoje: 67%
|
|
261
|
-
1 ninguém nomeia -> precisa de uma decisão dele
|
|
262
|
-
\`\`\`
|
|
263
|
-
|
|
264
|
-
Então o teto de hoje é 67% - e ao falar com ele, isso não se chama "alcançável", se chama
|
|
265
|
-
*"o máximo que dá para resolver sem você decidir nada novo"*.
|
|
266
|
-
|
|
267
|
-
## 6b-2. O ÚLTIMO ITEM DA FILA É MANDAR O REGISTRO
|
|
268
|
-
|
|
269
|
-
Cada escrita fica gravada numa fila local, e ela **nunca toca a rede** sozinha - essa
|
|
270
|
-
divisão é o que mantém a verificação instalada. Uma skill que mandasse por conta própria
|
|
271
|
-
seria a verificação ligando para casa.
|
|
272
|
-
|
|
273
|
-
Então o envio é um item como os outros, o último, e ele só acontece se ele mandar:
|
|
274
|
-
|
|
275
|
-
\`\`\`
|
|
276
|
-
item 8 de 8
|
|
277
|
-
|
|
278
|
-
você mudou 3 coisas nesta conversa. A plataforma ainda está pontuando este
|
|
279
|
-
repositório pelo que ela viu da última vez.
|
|
280
|
-
|
|
281
|
-
[mandar agora] [depois] npx synthesisui sync --record-only
|
|
282
|
-
\`\`\`
|
|
283
|
-
|
|
284
|
-
**\`--record-only\`, e não \`sync\` puro.** O \`sync\` completo re-mede tudo e reescreve as
|
|
285
|
-
receitas no RASCUNHO - fazer isso no fim de uma revisão move o chão de quem está revisando.
|
|
286
|
-
|
|
287
|
-
O \`sync\` COMPLETO é outro item, e só aparece quando algum item mexeu em FUNDAÇÃO (uma cor,
|
|
288
|
-
um degrau, uma variável nova). Aí a plataforma precisa reler, e a skill diz isso sem jargão:
|
|
289
|
-
|
|
290
|
-
\`\`\`
|
|
291
|
-
isto mexeu numa das bases do seu design system, então a plataforma precisa reler o
|
|
292
|
-
seu repositório. Você vê o resultado antes de publicar - ele reescreve as receitas no rascunho.
|
|
293
|
-
[rodar agora] [depois] npx synthesisui sync
|
|
294
|
-
\`\`\`
|
|
295
|
-
|
|
296
|
-
Se nada foi escrito, não ofereça nem um nem outro. Um item que não tem o que mandar é ruído.
|
|
297
|
-
|
|
298
|
-
## 6c. FECHE PELO QUE ANDOU, E MOSTRE O CAMINHO ATÉ 100%
|
|
299
|
-
|
|
300
|
-
\`\`\`
|
|
301
|
-
OverviewHeaderInformation 2 arquivos
|
|
302
|
-
|
|
303
|
-
dá para resolver hoje 82% é o que o seu vocabulário já cobre
|
|
304
|
-
resolvido 82% ✓ nada mecânico ficou para trás
|
|
305
|
-
deixado como estava 12 os \`em\`, que podem mexer no layout
|
|
306
|
-
esperando você 3 valores que ainda não têm nome
|
|
307
|
-
\`\`\`
|
|
308
|
-
|
|
309
|
-
Nunca "0% -> 0%". Se nada era mecânico, o teto era zero, e a frase é *"não havia nada que
|
|
310
|
-
o seu vocabulário de hoje resolvesse - o que existe são N decisões suas"*.
|
|
311
|
-
|
|
312
|
-
E quando sobrou pedido, termine com o caminho, porque ele é de dois passos e o primeiro é
|
|
313
|
-
dele:
|
|
314
|
-
|
|
315
|
-
\`\`\`
|
|
316
|
-
para chegar a 100%, faltam dois passos:
|
|
317
|
-
1. autorize o pedido no painel (ele já está na fila)
|
|
318
|
-
2. publique, e rode \`npx synthesisui upgrade\` aqui
|
|
319
|
-
depois disso, /sui-adapt neste componente fecha em 100%
|
|
320
|
-
\`\`\`
|
|
321
|
-
|
|
322
|
-
Autorizar escreve o RASCUNHO, e o repo recebe a última PUBLICADA - por isso os dois passos.
|
|
323
|
-
Prometer que \`upgrade\` sozinho traz a variável é mandar a pessoa rodar um comando que
|
|
324
|
-
responde "already at the latest version".
|
|
325
|
-
|
|
326
|
-
Se a fila esvaziou, o fim é uma linha só:
|
|
327
|
-
|
|
328
|
-
\`\`\`
|
|
329
|
-
dá para resolver hoje 100%
|
|
330
|
-
resolvido 100% ✓ este componente está inteiro no seu design system
|
|
331
|
-
\`\`\`
|
|
332
|
-
|
|
333
|
-
## 7. ROTINA
|
|
334
|
-
|
|
335
|
-
Ela foi desenhada para duas horas do dia, e a segunda é a que mais rende:
|
|
336
|
-
|
|
337
|
-
\`\`\`
|
|
338
|
-
semanal "roda o sui-adapt no dashboard" - pega deriva antes de virar hábito
|
|
339
|
-
depois de mexer componente novo, ou alteração grande: rode ANTES do commit, enquanto a
|
|
340
|
-
decisão ainda está quente e o conserto ainda é barato
|
|
341
|
-
\`\`\`
|
|
342
|
-
|
|
343
|
-
A verificação automática já roda a metade determinística a cada escrita, calada quando não
|
|
344
|
-
há o que dizer. Esta skill é o passo deliberado: ela junta a verificação, a doutrina e a
|
|
345
|
-
fila numa conversa só, e termina com o cliente decidindo - não com um relatório.
|
|
346
|
-
`;
|
|
347
14
|
export const ADAPT_SKILL_PATH = ".claude/skills/sui-adapt/SKILL.md";
|
|
15
|
+
export const ADAPT_SKILL = '---\nname: sui-adapt\ndescription: Confronta UM componente (ou uma p\u00e1gina, ou uma pasta) contra o design system instalado e adapta o que der - cobertura de token medida, conserto mec\u00e2nico proposto antes de escrever, e o que sobra classificado em "regra nova" ou "conserto local". Use quando algu\u00e9m aponta para uma pe\u00e7a e pergunta se ela est\u00e1 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\u00f5e antes de escrever, e nunca arquiva pedido em nome de ningu\u00e9m.\n---\n\n# Adaptar uma pe\u00e7a ao sistema - served live\n\nThis playbook is served from the platform, not shipped in this file - it is\nalways current, and your context only carries the step you are on.\n\n1. Call the `playbook` tool on the `synthesisui` MCP server with\n { "skill": "adapt" } - you get the framing and a table of contents.\n2. Fetch ONLY the chapter for the step you are on:\n { "skill": "adapt", "section": "<id from the toc>" }. Never fetch more\n than the current step needs.\n3. Follow it exactly. When the step is done, fetch the next chapter.\n\nIf the tool answers that you are not signed in, run `npx synthesisui login`\nin the terminal and call it again. If the `synthesisui` MCP server is not\navailable at all, run `npx synthesisui connect`, restart the session, and\ninvoke this skill again.\n';
|