synthesisui 0.16.234 → 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/doctor/coverage.js +1 -1
- package/dist/guide.js +8 -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/doctor/coverage.js
CHANGED
|
@@ -136,7 +136,7 @@ composition) {
|
|
|
136
136
|
* look próprio nos seus arquivos"; a receita final é decidida depois, com a paleta em
|
|
137
137
|
* mão, e as duas contas não são a mesma. Prometer a segunda aqui seria inventar precisão.
|
|
138
138
|
*/
|
|
139
|
-
` and ${composition} have no look of their own in your files - a screen that composes others, in a grid, passing data down. Those arrive as COMPOSITION recipes: the tree, the slots, and what each child receives, with no palette to
|
|
139
|
+
` and ${composition} have no look of their own in your files - a screen that composes others, in a grid, passing data down. Those arrive as COMPOSITION recipes: the tree, the slots, and what each child receives, with no palette to score. Nothing was lost; it is a different kind of recipe.`);
|
|
140
140
|
}
|
|
141
141
|
for (const { shape, files, examples } of c.counts) {
|
|
142
142
|
const share = Math.round((files / c.components) * 100);
|
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
|
|
@@ -524,6 +525,10 @@ ${readFirst.map((l) => `- ${l}`).join("\n")}
|
|
|
524
525
|
|
|
525
526
|
${meta.tagline}
|
|
526
527
|
|
|
528
|
+
Each component here ships as a **recipe** - the same artifact the platform
|
|
529
|
+
calls its blueprint: structure, allowed tokens, states and laws, compiled
|
|
530
|
+
from real code.
|
|
531
|
+
|
|
527
532
|
**Mood:** ${meta.mood.join(" · ")}
|
|
528
533
|
**Default scheme:** ${meta.scheme}${hasAlt ? ` (supports a toggle to ${altScheme})` : ""}
|
|
529
534
|
${meta.sourceUrl ? `**Reinterpretation of:** ${meta.sourceUrl}` : "**Original system.**"}
|
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
|
*
|