synthesisui 0.16.244 → 0.16.247
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/mcp.js +101 -5
- package/dist/compose-context.js +210 -0
- package/dist/skill-compose.js +22 -0
- package/dist/skills.js +17 -0
- package/package.json +1 -1
package/dist/commands/mcp.js
CHANGED
|
@@ -3,6 +3,7 @@ import { readdir, readFile } from "node:fs/promises";
|
|
|
3
3
|
import { join, relative, resolve } from "node:path";
|
|
4
4
|
import { provenanceAction, toolSlug, withSource, } from "../agent-provenance.js";
|
|
5
5
|
import { pinnedHookVersion } from "../agent-wiring.js";
|
|
6
|
+
import { composePlan, FAMILIES, isFamily, noFamilyAnswer, renderPlan, } from "../compose-context.js";
|
|
6
7
|
import { readToken, resolveRegistry } from "../config.js";
|
|
7
8
|
import { readEvents } from "../doctor/ledger.js";
|
|
8
9
|
import { fileRequest } from "../doctor/requests.js";
|
|
@@ -122,14 +123,14 @@ const TOOLS = [
|
|
|
122
123
|
},
|
|
123
124
|
{
|
|
124
125
|
name: "playbook",
|
|
125
|
-
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.",
|
|
126
|
+
description: "The skill playbooks (init, import, adapt, compose) - 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. `compose` is the hierarchy to walk when this system has no recipe for what you were asked to build - the layers it DOES have, in order.",
|
|
126
127
|
inputSchema: {
|
|
127
128
|
type: "object",
|
|
128
129
|
properties: {
|
|
129
130
|
skill: {
|
|
130
131
|
type: "string",
|
|
131
|
-
enum: ["init", "import", "adapt"],
|
|
132
|
-
description: "Which playbook.",
|
|
132
|
+
enum: ["init", "import", "adapt", "compose"],
|
|
133
|
+
description: "Which playbook. `compose` is the one for building something this system has no recipe for.",
|
|
133
134
|
},
|
|
134
135
|
section: {
|
|
135
136
|
type: "string",
|
|
@@ -184,6 +185,24 @@ const TOOLS = [
|
|
|
184
185
|
required: ["recipes"],
|
|
185
186
|
},
|
|
186
187
|
},
|
|
188
|
+
{
|
|
189
|
+
name: "compose_context",
|
|
190
|
+
description: "The layers to build something this system has NO recipe for, in one call: the recipes of that family to copy from, the tokens they already use with their values, the system's rules, and the floor and checklist for the family. YOU say which family it is - you just read the request, and a keyword guess of ours would be worse. Call this instead of asking for doctrine, vocabulary and each token separately; the laws of markup and the voice are named in the answer with the one fetch each needs.",
|
|
191
|
+
inputSchema: {
|
|
192
|
+
type: "object",
|
|
193
|
+
properties: {
|
|
194
|
+
family: {
|
|
195
|
+
type: "string",
|
|
196
|
+
description: `Which family this behaves like: ${FAMILIES.join(", ")}. Pick by BEHAVIOUR, never by the name in the request. If a part fits none of them, send what you would call it and the answer says what to do.`,
|
|
197
|
+
},
|
|
198
|
+
intent: {
|
|
199
|
+
type: "string",
|
|
200
|
+
description: "What you are building, in a few words - it is echoed back so your summary can name it.",
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
required: ["family"],
|
|
204
|
+
},
|
|
205
|
+
},
|
|
187
206
|
{
|
|
188
207
|
name: "request_component",
|
|
189
208
|
description: "File a component request when nothing in the catalogue covers what you need. This is the OTHER HALF of the refusal rule: you already say which entry you considered and why it did not fit - said in chat, that reasoning evaporates; filed here, it becomes the queue the system's author works from. File it at the moment you build the workaround, while the reasoning is still yours.",
|
|
@@ -962,7 +981,16 @@ async function describeComponent(root, name) {
|
|
|
962
981
|
* vale ser construído, e para quais famílias primeiro.
|
|
963
982
|
*/
|
|
964
983
|
return {
|
|
965
|
-
|
|
984
|
+
/**
|
|
985
|
+
* ANTES ESTA FRASE TERMINAVA EM "não escreva do zero" E DEIXAVA O AGENTE
|
|
986
|
+
* SOZINHO - e o `request_component` completava com "siga com a sua solução
|
|
987
|
+
* alternativa". Juntas, elas diziam à esteira para registrar o improviso
|
|
988
|
+
* em vez de guiá-lo (dono, 18/08).
|
|
989
|
+
*
|
|
990
|
+
* Não ter a receita não é cair no zero: as camadas base existem, e agora
|
|
991
|
+
* há um playbook que as percorre em ordem. A frase aponta para ele.
|
|
992
|
+
*/
|
|
993
|
+
body: `This system has no component called "${name}". Run list_components to see what it does have - a near miss still means composing WITH it rather than around it.\n\nIf nothing covers it, do not write from scratch and do not improvise: call playbook { "skill": "compose" }. It walks what this system DOES have, in order - its rules and voice, its tokens, the family this belongs to and what that family requires, and the laws that decide the markup. File request_component along the way so the gap becomes a decision somebody can make.`,
|
|
966
994
|
source: {
|
|
967
995
|
kind: "none",
|
|
968
996
|
reason: "recipe",
|
|
@@ -1186,6 +1214,74 @@ async function runTool(root, name, args) {
|
|
|
1186
1214
|
return fromContract(await validateRecipes(root, Array.isArray(args.recipes)
|
|
1187
1215
|
? args.recipes
|
|
1188
1216
|
: []));
|
|
1217
|
+
case "compose_context": {
|
|
1218
|
+
/**
|
|
1219
|
+
* A ORQUESTRAÇÃO - e o que ela reúne é do SISTEMA DELE, com o nosso
|
|
1220
|
+
* esqueleto de julgamento por cima.
|
|
1221
|
+
*
|
|
1222
|
+
* Uma chamada em vez de cinco, e a procedência disto é `composed`: o
|
|
1223
|
+
* primeiro caminho da esteira que emite esse kind, que até aqui existia no
|
|
1224
|
+
* vocabulário e não em resposta nenhuma.
|
|
1225
|
+
*/
|
|
1226
|
+
const asked = String(args.family ?? "")
|
|
1227
|
+
.trim()
|
|
1228
|
+
.toLowerCase();
|
|
1229
|
+
const intent = String(args.intent ?? "").trim();
|
|
1230
|
+
if (!isFamily(asked)) {
|
|
1231
|
+
return {
|
|
1232
|
+
body: noFamilyAnswer(asked || "(nothing)"),
|
|
1233
|
+
source: {
|
|
1234
|
+
kind: "none",
|
|
1235
|
+
reason: "family",
|
|
1236
|
+
detail: `no family fits "${asked}"`,
|
|
1237
|
+
},
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
const { documents, table, doctrines, requires } = await loadSystem(root);
|
|
1241
|
+
if (documents.length === 0) {
|
|
1242
|
+
return {
|
|
1243
|
+
body: "No design system installed here, so there is nothing to compose from. Run `synthesisui add <slug>` first - composing against nothing would be inventing.",
|
|
1244
|
+
source: {
|
|
1245
|
+
kind: "none",
|
|
1246
|
+
reason: "system",
|
|
1247
|
+
detail: "no system installed",
|
|
1248
|
+
},
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1251
|
+
const plan = composePlan({
|
|
1252
|
+
documents,
|
|
1253
|
+
table,
|
|
1254
|
+
rules: doctrines.flatMap((d) => d.rules),
|
|
1255
|
+
/**
|
|
1256
|
+
* A regra tem a FRASE e o PACOTE em campos separados (`text`,
|
|
1257
|
+
* `requires`) - mandar a frase sozinha esconderia o que instalar, e
|
|
1258
|
+
* mandar só o pacote esconderia o porquê.
|
|
1259
|
+
*/
|
|
1260
|
+
requires: requires.map((r) => ({
|
|
1261
|
+
rule: r.requires ? `${r.requires} - ${r.text}` : r.text,
|
|
1262
|
+
})),
|
|
1263
|
+
family: asked,
|
|
1264
|
+
intent,
|
|
1265
|
+
});
|
|
1266
|
+
/** O checklist é servido; sem rede o resto do plano continua valendo. */
|
|
1267
|
+
const answer = await askCatalogue("vocabulary");
|
|
1268
|
+
const v = answer.ok
|
|
1269
|
+
? answer.body
|
|
1270
|
+
: null;
|
|
1271
|
+
const vocabulary = v
|
|
1272
|
+
? {
|
|
1273
|
+
floor: v.floor?.[asked] ?? [],
|
|
1274
|
+
checklist: v.checklist?.[asked] ?? [],
|
|
1275
|
+
}
|
|
1276
|
+
: null;
|
|
1277
|
+
return {
|
|
1278
|
+
body: renderPlan(plan, vocabulary),
|
|
1279
|
+
source: {
|
|
1280
|
+
kind: "composed",
|
|
1281
|
+
detail: `${asked} · ${plan.siblings.length} recipe${plan.siblings.length === 1 ? "" : "s"} and ${plan.tokens.length} token${plan.tokens.length === 1 ? "" : "s"} from this system`,
|
|
1282
|
+
},
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1189
1285
|
case "request_component": {
|
|
1190
1286
|
const r = await fileRequest(root, {
|
|
1191
1287
|
kind: "component",
|
|
@@ -1194,7 +1290,7 @@ async function runTool(root, name, args) {
|
|
|
1194
1290
|
considered: args.considered ? String(args.considered) : undefined,
|
|
1195
1291
|
file: args.file ? String(args.file) : undefined,
|
|
1196
1292
|
});
|
|
1197
|
-
return fromContract(`Filed as ${r.id}. It shows up in \`synthesisui doctor\` until a person authors it or closes it
|
|
1293
|
+
return fromContract(`Filed as ${r.id}. It shows up in \`synthesisui doctor\` until a person authors it or closes it.\n\nNow build it properly rather than working around it: playbook { "skill": "compose" } walks the layers this system already declares, so what you hand back matches it even without a recipe. Say in your summary that the request exists, and which parts were composed rather than taken from the system.`);
|
|
1198
1294
|
}
|
|
1199
1295
|
case "refresh_system": {
|
|
1200
1296
|
/**
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* O CONTEXTO MÍNIMO PARA CONSTRUIR ALGO QUE O SISTEMA NÃO TEM.
|
|
3
|
+
*
|
|
4
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
5
|
+
* AS DUAS FONTES, E A PROPORÇÃO ENTRE ELAS SE INVERTE SOZINHA
|
|
6
|
+
*
|
|
7
|
+
* O material que enche este contexto é DELE - os tokens que as receitas dele já
|
|
8
|
+
* usam, as regras que ele declarou, as bibliotecas que o repositório dele
|
|
9
|
+
* carrega. É isso que faz o componente novo nascer parecido com o sistema dele
|
|
10
|
+
* em vez de parecido com o nosso gosto.
|
|
11
|
+
*
|
|
12
|
+
* O que é NOSSO é pequeno de propósito e não fala de nenhum componente
|
|
13
|
+
* específico: as sete famílias, o checklist de cada uma e as leis de marcação.
|
|
14
|
+
* Sem essa metade, um sistema de dois componentes - e existem quatro deles em
|
|
15
|
+
* produção, medido em 18/08 - não teria nada a oferecer justamente a quem mais
|
|
16
|
+
* precisa de orientação.
|
|
17
|
+
*
|
|
18
|
+
* Sistema maduro: "o seu input usa estes sete tokens, use os mesmos" (paridade).
|
|
19
|
+
* Sistema novo: "um campo precisa destas cinco coisas, o seu sistema ainda não
|
|
20
|
+
* nomeia três delas" (orientação, com a lacuna declarada).
|
|
21
|
+
*
|
|
22
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
23
|
+
* A FAMÍLIA VEM DE QUEM PEDE, e a plataforma valida (decisão do dono, 18/08)
|
|
24
|
+
*
|
|
25
|
+
* Quem chama esta ferramenta é um modelo que acabou de ler o pedido em linguagem
|
|
26
|
+
* natural. Ele classifica melhor que qualquer heurística de palavras que a gente
|
|
27
|
+
* escrevesse aqui, e classificar com IA hospedada gastaria Ink para responder o
|
|
28
|
+
* que já foi respondido do outro lado da chamada.
|
|
29
|
+
*
|
|
30
|
+
* Então o agente informa a família, a gente confere contra o contrato, e quando
|
|
31
|
+
* ela não é nenhuma das sete isso não é erro: é uma lacuna de FAMÍLIA, que é uma
|
|
32
|
+
* decisão diferente de um componente faltando.
|
|
33
|
+
*/
|
|
34
|
+
/** As sete que o contrato conhece - o mesmo enum de `componentPreviewKind`. */
|
|
35
|
+
export const FAMILIES = [
|
|
36
|
+
"action",
|
|
37
|
+
"control",
|
|
38
|
+
"field",
|
|
39
|
+
"surface",
|
|
40
|
+
"pill",
|
|
41
|
+
"indicator",
|
|
42
|
+
"text",
|
|
43
|
+
];
|
|
44
|
+
export function isFamily(value) {
|
|
45
|
+
return FAMILIES.includes(value);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* OS TOKENS QUE ESTA FAMÍLIA JÁ USA, lidos das receitas dela.
|
|
49
|
+
*
|
|
50
|
+
* Uma referência de token num documento é `{color.semantic.canvas}` - a mesma
|
|
51
|
+
* forma que o compilador resolve. Então "quais tokens um campo usa neste
|
|
52
|
+
* sistema" é uma pergunta que o próprio documento responde, sem tabela nossa e
|
|
53
|
+
* sem palpite.
|
|
54
|
+
*
|
|
55
|
+
* MEDIDO no signalui (37 componentes, 74 referências no total): field 37,
|
|
56
|
+
* surface 37, control 33, action 24, pill 17, text 15, indicator 12. O corte por
|
|
57
|
+
* volume é modesto num sistema pequeno - o valor aqui não é economia de
|
|
58
|
+
* contexto, é PARIDADE: o agente copia o que existe em vez de escolher.
|
|
59
|
+
*/
|
|
60
|
+
export function tokensOfFamily(documents, family) {
|
|
61
|
+
const refs = new Set();
|
|
62
|
+
for (const doc of documents) {
|
|
63
|
+
const components = doc
|
|
64
|
+
.components;
|
|
65
|
+
if (!components)
|
|
66
|
+
continue;
|
|
67
|
+
for (const recipe of Object.values(components)) {
|
|
68
|
+
if (recipe?.preview?.kind !== family)
|
|
69
|
+
continue;
|
|
70
|
+
for (const ref of JSON.stringify(recipe).match(/\{[a-z0-9.]+\}/gi) ??
|
|
71
|
+
[]) {
|
|
72
|
+
/**
|
|
73
|
+
* `{onPressedChange}` e afins aparecem no mesmo formato e NÃO são
|
|
74
|
+
* tokens - são nomes de prop que a receita cita. Um token tem caminho:
|
|
75
|
+
* família, e depois pelo menos um degrau.
|
|
76
|
+
*/
|
|
77
|
+
if (ref.includes("."))
|
|
78
|
+
refs.add(ref);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return [...refs].sort();
|
|
83
|
+
}
|
|
84
|
+
/** As receitas desta família que o sistema já tem - de onde copiar. */
|
|
85
|
+
export function recipesOfFamily(documents, family) {
|
|
86
|
+
const out = [];
|
|
87
|
+
for (const doc of documents) {
|
|
88
|
+
const components = doc
|
|
89
|
+
.components;
|
|
90
|
+
if (!components)
|
|
91
|
+
continue;
|
|
92
|
+
for (const [name, recipe] of Object.entries(components)) {
|
|
93
|
+
if (recipe?.preview?.kind === family) {
|
|
94
|
+
out.push({
|
|
95
|
+
name,
|
|
96
|
+
...(recipe.description ? { description: recipe.description } : null),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return out.sort((a, b) => a.name.localeCompare(b.name));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* O VALOR DE CADA REFERÊNCIA, quando a tabela o conhece.
|
|
105
|
+
*
|
|
106
|
+
* `{radius.md}` sozinho não diz nada a quem vai escrever css; `{radius.md} =
|
|
107
|
+
* 0.5rem` diz. A tabela de tokens é a mesma que o `find_token` consulta, então
|
|
108
|
+
* a resposta aqui e a resposta dele nunca divergem.
|
|
109
|
+
*/
|
|
110
|
+
export function valueOf(table, ref) {
|
|
111
|
+
const path = ref.slice(1, -1);
|
|
112
|
+
/** `color.semantic.canvas` -> `--ds-color-semantic-canvas`. */
|
|
113
|
+
const name = `--ds-${path.replace(/\./g, "-")}`;
|
|
114
|
+
return table.byName.get(name) ?? null;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* A MONTAGEM - pura, e é por isso que ela mora fora do handler.
|
|
118
|
+
*
|
|
119
|
+
* O handler faz I/O (`loadSystem`, a rede do catalogue). O que decide o que
|
|
120
|
+
* entra no contexto é isto, e é o que um spec consegue exercer com um documento
|
|
121
|
+
* de verdade sem subir nada.
|
|
122
|
+
*/
|
|
123
|
+
export function composePlan(input) {
|
|
124
|
+
const refs = tokensOfFamily(input.documents, input.family);
|
|
125
|
+
return {
|
|
126
|
+
family: input.family,
|
|
127
|
+
intent: input.intent,
|
|
128
|
+
siblings: recipesOfFamily(input.documents, input.family),
|
|
129
|
+
tokens: refs.map((ref) => ({ ref, value: valueOf(input.table, ref) })),
|
|
130
|
+
rules: [...input.rules],
|
|
131
|
+
requires: [...input.requires],
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* O PLANO COMO O AGENTE O LÊ - e a ordem é a das camadas, não a da comodidade.
|
|
136
|
+
*
|
|
137
|
+
* Cada bloco diz de onde veio: o que é do sistema dele, o que é checklist nosso,
|
|
138
|
+
* e o que ninguém decidiu ainda. É a mesma gramática da linha `Source:` que toda
|
|
139
|
+
* resposta carrega, um nível abaixo - e é o que permite ao agente relatar
|
|
140
|
+
* procedência por parte em vez de por resposta.
|
|
141
|
+
*/
|
|
142
|
+
export function renderPlan(plan,
|
|
143
|
+
/** O checklist e o piso desta família, servidos do catálogo. `null` sem rede. */
|
|
144
|
+
vocabulary) {
|
|
145
|
+
const out = [
|
|
146
|
+
`COMPOSING: ${plan.intent || plan.family}`,
|
|
147
|
+
`FAMILY: ${plan.family} - every part below answers to this family's checklist.`,
|
|
148
|
+
"",
|
|
149
|
+
];
|
|
150
|
+
out.push("── FROM THIS SYSTEM ────────────────────────────────────────");
|
|
151
|
+
if (plan.siblings.length > 0) {
|
|
152
|
+
out.push(`Recipes in this family, to copy from rather than to choose against (${plan.siblings.length}):`, ...plan.siblings.map((s) => ` ${s.name}${s.description ? ` - ${s.description}` : ""}`));
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
out.push("This system has NO recipe in this family yet. Nothing here to copy, so", "every decision below is one you are making for the first time - name them", "in your summary, and file what the system should have named.");
|
|
156
|
+
}
|
|
157
|
+
out.push("");
|
|
158
|
+
if (plan.tokens.length > 0) {
|
|
159
|
+
out.push(`Tokens those recipes already use (${plan.tokens.length}) - use these before any literal:`, ...plan.tokens.map((t) => ` ${t.ref}${t.value ? ` = ${t.value}` : " (name declared, value not on disk)"}`));
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
out.push("No token is used by this family yet. Ask `find_token` for each value you", "need, and file `request_token` for the ones nothing holds - never invent a name.");
|
|
163
|
+
}
|
|
164
|
+
if (plan.rules.length > 0) {
|
|
165
|
+
out.push("", `Rules - maximum authority, ${plan.rules.length} of them:`, ...plan.rules.map((r) => ` - ${r}`));
|
|
166
|
+
}
|
|
167
|
+
if (plan.requires.length > 0) {
|
|
168
|
+
out.push("", "Packages this system says the project needs:", ...plan.requires.map((r) => ` - ${r.rule}`));
|
|
169
|
+
}
|
|
170
|
+
out.push("", "── THE FLOOR FOR THIS FAMILY ───────────────────────────────");
|
|
171
|
+
if (vocabulary) {
|
|
172
|
+
if (vocabulary.floor.length > 0) {
|
|
173
|
+
out.push("Below this it cannot be told apart from the page:");
|
|
174
|
+
out.push(...vocabulary.floor.map((f) => ` ${f}`));
|
|
175
|
+
}
|
|
176
|
+
out.push("", "Work this list and say which items this system's own recipes answer:", ...vocabulary.checklist.map((c) => ` - ${c}`));
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
out.push("The checklist is served from the platform and could not be reached right", "now. Everything above still holds - fetch it with `recipe_vocabulary`", "before you call the component finished.");
|
|
180
|
+
}
|
|
181
|
+
out.push("", "── WHAT IS NOT HERE ────────────────────────────────────────", "The laws that decide the markup (which tag, whose layout wins, why", "accessibility moves the ink) are one fetch away and not repeated here:", ' playbook { "skill": "compose", "section": "path-2-layer-3-the-laws-that-decide-the-markup" }', "", "The voice - how this product speaks - is not in this answer either. Call", "`system_doctrine` before writing any user-facing copy inside this component.", "", "When you are done: `validate_recipe` if you are writing one, then", "`request_component` for what the system should have had, and report which", "parts came from the system and which you composed.");
|
|
182
|
+
return out.join("\n");
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* A RESPOSTA QUANDO A FAMÍLIA NÃO É NENHUMA DAS SETE.
|
|
186
|
+
*
|
|
187
|
+
* Não é erro e não é uma lista de sugestões parecidas: é um fato sobre o
|
|
188
|
+
* contrato, e o pedido de quem o lê é diferente do pedido de um componente. Um
|
|
189
|
+
* gráfico, um mapa, uma grade de calendário não são `surface` mal classificado -
|
|
190
|
+
* forçá-los ali produziria um piso que não significa nada.
|
|
191
|
+
*/
|
|
192
|
+
export function noFamilyAnswer(asked) {
|
|
193
|
+
return [
|
|
194
|
+
`"${asked}" is not one of the seven families this contract can draw: ${FAMILIES.join(", ")}.`,
|
|
195
|
+
"",
|
|
196
|
+
"That is a real answer, not a rejection. Two things are true at once:",
|
|
197
|
+
"",
|
|
198
|
+
" 1. Everything ABOVE the family layer still holds in full - the system's",
|
|
199
|
+
" rules, its tokens, and the laws of markup. Ask `find_token` per value,",
|
|
200
|
+
" `system_doctrine` for the rules, and fetch the laws chapter of the",
|
|
201
|
+
" compose playbook.",
|
|
202
|
+
" 2. What is missing is a KIND, not a component - and that is a different",
|
|
203
|
+
" decision for the system's owner. File it:",
|
|
204
|
+
" request_component with what the thing is and why no family fits.",
|
|
205
|
+
"",
|
|
206
|
+
"Then decompose: parts of it probably DO have families. A region that holds",
|
|
207
|
+
"things is a surface, a control inside it is a control - ask again for those",
|
|
208
|
+
"parts, and compose the rest without a family, saying so.",
|
|
209
|
+
].join("\n");
|
|
210
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* O STUB DA SKILL DE COMPOSIÇÃO - o corpo é SERVIDO, como os outros três.
|
|
3
|
+
*
|
|
4
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
5
|
+
* POR QUE ESTA SKILL EXISTE (dono, 18/08)
|
|
6
|
+
*
|
|
7
|
+
* As três primeiras cobrem trazer um sistema para dentro e mantê-lo. Nenhuma
|
|
8
|
+
* cobria o gesto mais comum do dia a dia: alguém pede um componente que o
|
|
9
|
+
* sistema não tem.
|
|
10
|
+
*
|
|
11
|
+
* E o que a plataforma respondia nesse caso eram duas frases, medidas:
|
|
12
|
+
* `describe_component` dizia *"não escreva do zero"* e `request_component`
|
|
13
|
+
* dizia, na frase seguinte, *"siga com a sua solução alternativa"*. Ou seja, a
|
|
14
|
+
* esteira registrava que o agente improvisou em vez de guiar a composição - o
|
|
15
|
+
* oposto de uma hierarquia de conhecimento.
|
|
16
|
+
*
|
|
17
|
+
* A tese do dono: não ter a receita não é cair no zero. As camadas base
|
|
18
|
+
* existem - tokens, regras, a família e o que o tipo dela exige, as leis de
|
|
19
|
+
* marcação - e compor a partir delas é tão valioso quanto ter a receita pronta.
|
|
20
|
+
*/
|
|
21
|
+
export const COMPOSE_SKILL_PATH = ".claude/skills/sui-compose/SKILL.md";
|
|
22
|
+
export const COMPOSE_SKILL = '---\nname: sui-compose\ndescription: Build ANY UI element the installed design system has no recipe for - without inventing values, tags or accessibility. Use whenever you are about to write a UI element from scratch, when `describe_component` answers that the system does not have it, or when what was asked for is a composite nobody has written yet - whatever it is called (an editor, a palette, a table, a chart, a layout region: the playbook does not care which). Walks the layers the system DOES have, in order, decomposes the request into families, and declares what it could not cover.\n---\n\n# Compose what the system does not have - 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": "compose" } - you get the framing and a table of contents.\n2. Fetch ONLY the chapter for the step you are on:\n { "skill": "compose", "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\nThe first chapter tells you which of the two paths you are on - the system has\na recipe, or nobody has written one yet - and the rest walk the layers in order.\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';
|
package/dist/skills.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ADAPT_SKILL, ADAPT_SKILL_PATH } from "./skill-adapt.js";
|
|
2
|
+
import { COMPOSE_SKILL, COMPOSE_SKILL_PATH } from "./skill-compose.js";
|
|
2
3
|
import { IMPORT_SKILL, IMPORT_SKILL_PATH } from "./skill-import.js";
|
|
3
4
|
import { INIT_SKILL, INIT_SKILL_PATH } from "./skill-init.js";
|
|
4
5
|
/**
|
|
@@ -36,4 +37,20 @@ export const SKILLS = [
|
|
|
36
37
|
label: "/sui-adapt",
|
|
37
38
|
what: "one component against the system, and what to do about it",
|
|
38
39
|
},
|
|
40
|
+
/**
|
|
41
|
+
* A DE CONSTRUÇÃO, e ela é a que responde ao pedido mais comum de todos:
|
|
42
|
+
* "faz um componente X" quando o sistema não tem X.
|
|
43
|
+
*
|
|
44
|
+
* As três acima são sobre o sistema - trazer, manter, conferir. Esta é sobre o
|
|
45
|
+
* TRABALHO de quem usa o sistema, e é a única que roda todo dia. Ela existe
|
|
46
|
+
* porque a resposta anterior a esse pedido era o agente improvisar sozinho: a
|
|
47
|
+
* esteira dizia "não escreva do zero" e, na frase seguinte, "siga com a sua
|
|
48
|
+
* solução alternativa" (dono, 18/08).
|
|
49
|
+
*/
|
|
50
|
+
{
|
|
51
|
+
path: COMPOSE_SKILL_PATH,
|
|
52
|
+
source: COMPOSE_SKILL,
|
|
53
|
+
label: "/sui-compose",
|
|
54
|
+
what: "build what the system does not have, layer by layer",
|
|
55
|
+
},
|
|
39
56
|
];
|
package/package.json
CHANGED