synthesisui 0.16.368 → 0.16.370
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 +10 -0
- package/dist/commands/mcp.js +108 -0
- package/dist/install-marks.js +1 -1
- package/dist/their-vars.js +23 -3
- package/package.json +1 -1
package/dist/commands/add.js
CHANGED
|
@@ -554,6 +554,16 @@ export async function add(slug, opts) {
|
|
|
554
554
|
if (aligned.dropped.length > 0) {
|
|
555
555
|
console.log(line(` ${aligned.kept} Tailwind utilit${aligned.kept === 1 ? "y" : "ies"} now point at your own decisions; ${aligned.dropped.length} were left alone because the value would be ours (${aligned.dropped.slice(0, 3).join(", ")}${aligned.dropped.length > 3 ? ", …" : ""}) - your classes keep meaning what they mean today.`));
|
|
556
556
|
}
|
|
557
|
+
/**
|
|
558
|
+
* O CICLO RECUSADO É DITO, e não engolido - senão o cliente lê um número menor sem saber por quê.
|
|
559
|
+
*
|
|
560
|
+
* A folha traz a ponte (`--ease-in-out: var(--ds-motion-easings-in-out)`) para que a utility dele
|
|
561
|
+
* resolva pelo sistema. Apontar de volta fecharia um ciclo, e um ciclo de custom property não deixa
|
|
562
|
+
* o valor errado: invalida a propriedade no elemento e em todos os descendentes. Recusar é o certo,
|
|
563
|
+
* e a linha diz que nada foi perdido - a utility dele continua resolvendo pelo sistema.
|
|
564
|
+
*/
|
|
565
|
+
if (theirVars.cycles > 0)
|
|
566
|
+
console.log(` ${theirVars.cycles} ${theirVars.cycles === 1 ? "value" : "values"} already had the bridge pointing the other way, so ${theirVars.cycles === 1 ? "it was" : "they were"} left alone - your own utility still resolves through this system, and pointing back would have cancelled both.`);
|
|
557
567
|
if (theirVars.pointed > 0)
|
|
558
568
|
console.log(` ${theirVars.pointed} value${theirVars.pointed === 1 ? "" : "s"} in tokens.css now point at the name YOUR code already gives ${theirVars.pointed === 1 ? "it" : "them"} - change yours and the system follows${theirVars.pruned > 0 ? `; ${theirVars.pruned} matched but your build does not emit ${theirVars.pruned === 1 ? "that name" : "those names"}, so ${theirVars.pruned === 1 ? "it keeps" : "they keep"} the value` : ""}`);
|
|
559
569
|
/**
|
package/dist/commands/mcp.js
CHANGED
|
@@ -201,6 +201,24 @@ const TOOLS = [
|
|
|
201
201
|
required: ["skill"],
|
|
202
202
|
},
|
|
203
203
|
},
|
|
204
|
+
{
|
|
205
|
+
name: "apply_upgrade",
|
|
206
|
+
description: "The optimisation this system can write on its OWN, and the one question to ask before writing it. Call it with { slug } to GET the question - it comes back composed, with their numbers in it, and you deliver it as it is rather than rephrasing it. Only after they say yes, call again with { slug, confirmed: true }. What it writes never adds a word they did not write and never moves a value they chose: a literal only becomes a reference when it resolves to the same value. Everything that would need a NAME from them stays out and comes back counted, so you can say what is still waiting. Do NOT call this with confirmed:true off your own judgement - the whole point of the question is that the answer is theirs. AND THIS RUN ENDS WHEN IT IS APPLIED: report what changed in one short block and stop. If you found something else worth doing - a wiring gap, a defect, a fix waiting - name it in ONE line and offer it as a next step, never as a second investigation in the same breath. A run that answers `optimise my design system` with a nine-row impact table and a second three-option decision has buried the result it was asked for: whatever you measured is worth its own turn, where they can arrive with attention instead of spending what is left of it.",
|
|
207
|
+
inputSchema: {
|
|
208
|
+
type: "object",
|
|
209
|
+
properties: {
|
|
210
|
+
slug: {
|
|
211
|
+
type: "string",
|
|
212
|
+
description: "The system's slug, as the import printed it.",
|
|
213
|
+
},
|
|
214
|
+
confirmed: {
|
|
215
|
+
type: "boolean",
|
|
216
|
+
description: "false or omitted: returns the question and the numbers, writes nothing. true: applies it - only after they answered yes in their own words.",
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
required: ["slug"],
|
|
220
|
+
},
|
|
221
|
+
},
|
|
204
222
|
{
|
|
205
223
|
name: "recipe_vocabulary",
|
|
206
224
|
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.",
|
|
@@ -603,6 +621,54 @@ async function listComponents(root) {
|
|
|
603
621
|
* sumário; com, UM capítulo. O corpo inteiro nunca viaja - a dieta de contexto
|
|
604
622
|
* é servida por construção.
|
|
605
623
|
*/
|
|
624
|
+
/**
|
|
625
|
+
* A OTIMIZAÇÃO QUE UMA RESPOSTA "SIM" ESCREVE - e a pergunta que a precede, composta na plataforma.
|
|
626
|
+
*
|
|
627
|
+
* O QUE O CLIENTE GANHA: ele acaba de importar e o agente lhe faz UMA pergunta ali mesmo, em vez de
|
|
628
|
+
* lhe entregar um link para uma tela com quarenta e seis decisões enfileiradas. Ele responde, e o
|
|
629
|
+
* sistema é otimizado na mesma conversa.
|
|
630
|
+
*
|
|
631
|
+
* A PERGUNTA NÃO É COMPOSTA AQUI NEM PELO AGENTE, e essa é a decisão que faz o texto sobreviver: o
|
|
632
|
+
* agente escreve bem e escreve DIFERENTE a cada execução, e uma pergunta que muda de forma a cada
|
|
633
|
+
* vez é uma paráfrase e não uma pergunta do produto. Foi assim que dois fechamentos consecutivos
|
|
634
|
+
* afirmaram que 198 declarações não chegaram a receita nenhuma quando 22 utilities dele já estavam
|
|
635
|
+
* no sistema - o resumo dropou exatamente a metade que tinha acabado de entrar. Então o texto vem
|
|
636
|
+
* pronto de `askToUpgrade`, com os números da medição dentro dele.
|
|
637
|
+
*
|
|
638
|
+
* DOIS PASSOS DE PROPÓSITO. Sem `confirmed` nada é escrito: é o que impede a jornada de pular a
|
|
639
|
+
* pergunta, que é a única coisa nela que pertence à pessoa.
|
|
640
|
+
*/
|
|
641
|
+
async function applyUpgrade(slug, confirmed) {
|
|
642
|
+
const answer = await askPlatform(`/api/registry/ds/${encodeURIComponent(slug)}/upgrade`, confirmed ? "POST" : "GET");
|
|
643
|
+
if (!answer.ok)
|
|
644
|
+
return answer.because;
|
|
645
|
+
const body = answer.body;
|
|
646
|
+
if (body.error)
|
|
647
|
+
return `${body.error}${body.message ? `: ${body.message}` : ""}`;
|
|
648
|
+
if (!confirmed) {
|
|
649
|
+
if (!body.ask)
|
|
650
|
+
return "Nothing here is safe to apply on its own - ask them nothing, and say the system is already on its own names.";
|
|
651
|
+
return [
|
|
652
|
+
"ASK THEM THIS, and deliver it as it is - it is composed here so the numbers cannot drift:",
|
|
653
|
+
"",
|
|
654
|
+
body.ask,
|
|
655
|
+
"",
|
|
656
|
+
`Then, and only then: call apply_upgrade again with { slug: "${slug}", confirmed: true }. If they say no, say what they are choosing - the system stays exactly as their code has it, and the optimisation waits on a button.`,
|
|
657
|
+
].join("\n");
|
|
658
|
+
}
|
|
659
|
+
if (body.applied === false)
|
|
660
|
+
return body.message ?? "Nothing was applied.";
|
|
661
|
+
return [
|
|
662
|
+
`Done - v${body.version} published.`,
|
|
663
|
+
`${body.repointed ?? 0} literal${body.repointed === 1 ? "" : "s"} now follow their own tokens, and ${body.named ?? 0} value${body.named === 1 ? "" : "s"} became a named token.`,
|
|
664
|
+
body.waiting
|
|
665
|
+
? `${body.waiting} decision${body.waiting === 1 ? "" : "s"} still need a word only they have - say the number, and that those wait on their own screen.`
|
|
666
|
+
: "",
|
|
667
|
+
"The version they had before is published and frozen, so going back is one click.",
|
|
668
|
+
]
|
|
669
|
+
.filter(Boolean)
|
|
670
|
+
.join(" ");
|
|
671
|
+
}
|
|
606
672
|
async function playbook(skill, section) {
|
|
607
673
|
const answer = await askCatalogue("playbook", undefined, section ? { skill, section } : { skill });
|
|
608
674
|
if (!answer.ok)
|
|
@@ -993,6 +1059,45 @@ cli) {
|
|
|
993
1059
|
}
|
|
994
1060
|
})();
|
|
995
1061
|
}
|
|
1062
|
+
/**
|
|
1063
|
+
* UMA ROTA DA PLATAFORMA QUE NÃO É O CATÁLOGO, com a mesma sessão do device-login.
|
|
1064
|
+
*
|
|
1065
|
+
* `askCatalogue` monta `/api/catalogue?want=…` na própria assinatura, o que é certo para o que É
|
|
1066
|
+
* catálogo - e a otimização não é: ela ESCREVE no sistema dele, e uma escrita atrás de um `?want=`
|
|
1067
|
+
* leria como leitura. Então a única coisa compartilhada é o que deve ser: o token e o host.
|
|
1068
|
+
*
|
|
1069
|
+
* O MODO DE FALHA É DITO EM VOZ ALTA, nas três formas em que ele acontece - sem sessão, resposta de
|
|
1070
|
+
* erro, sem rede. Um `catch` que devolve silêncio faria o agente concluir que não havia nada a
|
|
1071
|
+
* fazer, e o cliente perderia a otimização sem nunca saber que ela existia.
|
|
1072
|
+
*/
|
|
1073
|
+
async function askPlatform(path, method) {
|
|
1074
|
+
const token = await readToken();
|
|
1075
|
+
if (!token)
|
|
1076
|
+
return {
|
|
1077
|
+
ok: false,
|
|
1078
|
+
because: "Not signed in, so the platform could not answer. Run `synthesisui login` in the terminal and call this again.",
|
|
1079
|
+
};
|
|
1080
|
+
try {
|
|
1081
|
+
const res = await fetch(`${resolveRegistry()}${path}`, {
|
|
1082
|
+
method,
|
|
1083
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
1084
|
+
});
|
|
1085
|
+
if (!res.ok)
|
|
1086
|
+
return {
|
|
1087
|
+
ok: false,
|
|
1088
|
+
because: res.status === 404
|
|
1089
|
+
? "No system by that slug on this account - check the slug the import printed."
|
|
1090
|
+
: `The platform answered ${res.status}. ${res.status === 401 ? "Run `synthesisui login` and try again." : "Nothing was written."}`,
|
|
1091
|
+
};
|
|
1092
|
+
return { ok: true, body: await res.json() };
|
|
1093
|
+
}
|
|
1094
|
+
catch {
|
|
1095
|
+
return {
|
|
1096
|
+
ok: false,
|
|
1097
|
+
because: "No network, so the platform could not answer. Nothing was written - say that, and offer to try again rather than describing what would have happened.",
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
996
1101
|
async function askCatalogue(want, post,
|
|
997
1102
|
/** Parâmetros extras do GET (ex.: o playbook e o capítulo pedidos). */
|
|
998
1103
|
query) {
|
|
@@ -1547,6 +1652,9 @@ cli) {
|
|
|
1547
1652
|
const section = args?.section ? String(args.section) : undefined;
|
|
1548
1653
|
return fromContract(await playbook(skill, section));
|
|
1549
1654
|
}
|
|
1655
|
+
case "apply_upgrade": {
|
|
1656
|
+
return fromContract(await applyUpgrade(String(args?.slug ?? ""), args?.confirmed === true));
|
|
1657
|
+
}
|
|
1550
1658
|
case "recipe_vocabulary":
|
|
1551
1659
|
return fromContract(await recipeVocabulary());
|
|
1552
1660
|
case "validate_recipe": {
|
package/dist/install-marks.js
CHANGED
|
@@ -170,7 +170,7 @@
|
|
|
170
170
|
* O que o cliente ganha ao rodar `upgrade`: o agente dele no Codex passa a poder PERGUNTAR ao
|
|
171
171
|
* sistema, em vez de só receber as regras e adivinhar o resto.
|
|
172
172
|
*/
|
|
173
|
-
export const MATERIALISER_SINCE = "0.16.
|
|
173
|
+
export const MATERIALISER_SINCE = "0.16.370";
|
|
174
174
|
/**
|
|
175
175
|
* A ÚLTIMA VERSÃO EM QUE O QUE O HOOK RODA MUDOU.
|
|
176
176
|
*
|
package/dist/their-vars.js
CHANGED
|
@@ -101,11 +101,22 @@ export function pointAtTheirNames(css, theirs, resolvable) {
|
|
|
101
101
|
* buildou não tem como provar o que resolve, e um par afirmado sem prova é pior que par nenhum.
|
|
102
102
|
*/
|
|
103
103
|
if (!resolvable || theirs.byName.size === 0)
|
|
104
|
-
return { css, pointed: 0, pruned: 0, pairs: [] };
|
|
104
|
+
return { css, pointed: 0, pruned: 0, cycles: 0, pairs: [] };
|
|
105
105
|
const ours = buildTable({ css, source: "installed" });
|
|
106
106
|
const alias = theirNames(ours, theirs);
|
|
107
|
+
/**
|
|
108
|
+
* A PONTE QUE JÁ ESTÁ NA FOLHA: o nome DELE -> o nosso token para o qual ele já aponta.
|
|
109
|
+
*
|
|
110
|
+
* Lido da própria folha e não de uma lista, porque quem decide o que é bridgeado é o compilador e a
|
|
111
|
+
* lista dele muda: hoje são famílias, pesos, easings, escala de tipo e gradientes, e amanhã pode ser
|
|
112
|
+
* outra. Uma cópia da regra aqui divergiria no dia em que uma família entrasse lá.
|
|
113
|
+
*/
|
|
114
|
+
const bridged = new Map();
|
|
115
|
+
for (const [, theirName, ourName] of css.matchAll(/^\s*(--[a-zA-Z0-9_-]+)\s*:\s*var\((--ds-[a-zA-Z0-9_-]+)\)\s*;/gm))
|
|
116
|
+
bridged.set(theirName, ourName);
|
|
107
117
|
let pointed = 0;
|
|
108
118
|
let pruned = 0;
|
|
119
|
+
let cycles = 0;
|
|
109
120
|
/** Ver `PointedAt.pairs`: o mapa sai do mesmo casamento que reescreve a linha. */
|
|
110
121
|
const pairs = [];
|
|
111
122
|
const out = css.replace(/^(\s*)(--ds-[a-zA-Z0-9_-]+)(\s*:\s*)([^;]+);/gm, (line, indent, name, sep, value) => {
|
|
@@ -118,6 +129,15 @@ export function pointAtTheirNames(css, theirs, resolvable) {
|
|
|
118
129
|
const theirName = alias.get(`${kind}:${normalizeValue(value.trim(), ours.rootPx)}`)?.name;
|
|
119
130
|
if (!theirName || theirName === name)
|
|
120
131
|
return line;
|
|
132
|
+
/**
|
|
133
|
+
* A PONTE JÁ APONTA PARA CÁ - então apontar de volta fecha um ciclo, e um ciclo não deixa o
|
|
134
|
+
* valor errado: deixa a propriedade INVÁLIDA. A utility dele já resolve pelo nosso token, que é
|
|
135
|
+
* o que a ponte existe para fazer, então esta linha não tem nada a ganhar e tudo a perder.
|
|
136
|
+
*/
|
|
137
|
+
if (bridged.get(theirName) === name) {
|
|
138
|
+
cycles += 1;
|
|
139
|
+
return line;
|
|
140
|
+
}
|
|
121
141
|
if (!resolvable.has(theirName)) {
|
|
122
142
|
pruned += 1;
|
|
123
143
|
return line;
|
|
@@ -126,7 +146,7 @@ export function pointAtTheirNames(css, theirs, resolvable) {
|
|
|
126
146
|
pairs.push({ ours: name, theirs: theirName, value: value.trim() });
|
|
127
147
|
return `${indent}${name}${sep}var(${theirName});`;
|
|
128
148
|
});
|
|
129
|
-
return { css: out, pointed, pruned, pairs };
|
|
149
|
+
return { css: out, pointed, pruned, cycles, pairs };
|
|
130
150
|
}
|
|
131
151
|
/**
|
|
132
152
|
* O PREFIXO DA NOSSA VARIÁVEL -> A FAMÍLIA, e a chave que `theirNames` devolve é `<família>:<valor>`.
|
|
@@ -151,7 +171,7 @@ const FAMILY_KIND = {
|
|
|
151
171
|
export async function pointTokensAtTheirNames(root, payload) {
|
|
152
172
|
const css = payload.artifacts["tokens.css"] ?? "";
|
|
153
173
|
if (!css)
|
|
154
|
-
return { css, pointed: 0, pruned: 0, pairs: [] };
|
|
174
|
+
return { css, pointed: 0, pruned: 0, cycles: 0, pairs: [] };
|
|
155
175
|
const [theirs, resolvable] = await Promise.all([
|
|
156
176
|
harvestTheirCss(root),
|
|
157
177
|
resolvableVars(root),
|
package/package.json
CHANGED