synthesisui 0.16.163 → 0.16.165
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 +33 -2
- package/dist/commands/mcp.js +42 -20
- package/dist/commands/use.js +1 -1
- package/dist/guide.js +1 -1
- package/dist/skill-import.js +16 -0
- package/package.json +1 -1
package/dist/commands/add.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
1
|
+
import { access, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { syncClaudeMd } from "../claude-md.js";
|
|
4
4
|
import { readProjectConfig, resolveRegistry } from "../config.js";
|
|
@@ -41,6 +41,33 @@ async function writeGovernanceIgnore(projectRoot) {
|
|
|
41
41
|
"ledger.jsonl\n" +
|
|
42
42
|
"not-expressed.md\n", "utf8");
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* APAGA A PROSA QUE ESTA VERSÃO DEIXOU DE ESCREVER.
|
|
46
|
+
*
|
|
47
|
+
* `rules.md` e `philosophy.md` eram materializados só pelo `add`, então quem instalou antes disto
|
|
48
|
+
* tem dois arquivos que nada mais atualiza - e um deles se apresenta como "Max authority - follow
|
|
49
|
+
* these first". Um arquivo que afirma autoridade máxima sobre a regra de uma versão qualquer do
|
|
50
|
+
* passado é pior que arquivo nenhum, e deixá-lo lá esperando alguém lembrar de apagar é exatamente
|
|
51
|
+
* a administração manual que esta esteira existe para não pedir (dono, 06/08).
|
|
52
|
+
*
|
|
53
|
+
* É a NOSSA pasta - o `.gitignore` que este mesmo comando escreve já a declara gerenciada -, então
|
|
54
|
+
* isto não toca em código dele. E é dito na saída, porque apagar em silêncio é a outra metade do
|
|
55
|
+
* mesmo erro.
|
|
56
|
+
*/
|
|
57
|
+
async function retireMaterializedDoctrine(slugDir) {
|
|
58
|
+
const gone = [];
|
|
59
|
+
for (const name of ["rules.md", "philosophy.md"]) {
|
|
60
|
+
const path = join(slugDir, name);
|
|
61
|
+
try {
|
|
62
|
+
await access(path);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
await rm(path).then(() => gone.push(name), () => { });
|
|
68
|
+
}
|
|
69
|
+
return gone;
|
|
70
|
+
}
|
|
44
71
|
async function readRootLock(path) {
|
|
45
72
|
try {
|
|
46
73
|
return JSON.parse(await readFile(path, "utf8"));
|
|
@@ -114,7 +141,8 @@ export async function add(slug, opts) {
|
|
|
114
141
|
};
|
|
115
142
|
await writeFile(rootLockPath, `${JSON.stringify(lock, null, 2)}\n`, "utf8");
|
|
116
143
|
await writeGovernanceIgnore(projectRoot);
|
|
117
|
-
|
|
144
|
+
const retired = await retireMaterializedDoctrine(slugDir);
|
|
145
|
+
// 5b. governance rules (personal DS) → doctrine.json at the slug root (stable path,
|
|
118
146
|
// highest authority; the GUIDE tells the agent to read it first)
|
|
119
147
|
/**
|
|
120
148
|
* The structured set wins when the registry sent one, because only this side
|
|
@@ -216,6 +244,9 @@ export async function add(slug, opts) {
|
|
|
216
244
|
];
|
|
217
245
|
if (doctrine.length > 0)
|
|
218
246
|
console.log(` ${doctrine.join(" and ")} → your agent reads them with \`system_doctrine\`, pinned to v${v}`);
|
|
247
|
+
/** Nomeado, nunca em silêncio: apagar arquivo no repo de alguém se diz em voz alta. */
|
|
248
|
+
if (retired.length > 0)
|
|
249
|
+
console.log(` removed ${retired.join(" and ")} - nothing rewrote them after an install, so they stated an older version's rules as current`);
|
|
219
250
|
console.log(` CLAUDE.md ${claudeMd.created ? "created" : "updated"} (${claudeMd.count} system(s) installed)`);
|
|
220
251
|
if (opts.setupHints === false)
|
|
221
252
|
return;
|
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 { pinnedHookVersion } from "../agent-wiring.js";
|
|
5
5
|
import { readToken, resolveRegistry } from "../config.js";
|
|
6
|
+
import { readEvents } from "../doctor/ledger.js";
|
|
6
7
|
import { fileRequest } from "../doctor/requests.js";
|
|
7
8
|
import { diagnose, scanSource } from "../doctor/scan.js";
|
|
8
9
|
import { nearestToken, tokenFor } from "../doctor/tokens.js";
|
|
@@ -293,36 +294,57 @@ async function systemDoctrine(root) {
|
|
|
293
294
|
: "",
|
|
294
295
|
...rules.map((r) => `- ${r}`),
|
|
295
296
|
].filter(Boolean);
|
|
296
|
-
const
|
|
297
|
+
const pulse = await sessionPulse(root, pinned);
|
|
297
298
|
return [
|
|
298
299
|
...head,
|
|
299
300
|
...(parts.length > 0 ? ["", "# Philosophy", "", ...parts] : []),
|
|
300
|
-
...(
|
|
301
|
+
...(pulse.length > 0 ? ["", ...pulse] : []),
|
|
301
302
|
].join("\n");
|
|
302
303
|
}
|
|
303
304
|
/**
|
|
304
|
-
*
|
|
305
|
+
* O PULSO DE UMA SESSÃO LONGA.
|
|
305
306
|
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
307
|
+
* A verificação de abertura (`align`) roda UMA vez, no minuto zero. Numa sessão de seis horas tudo o
|
|
308
|
+
* que ela achou é uma foto: a sessão expira na hora três, um colega publica na hora quatro, e o
|
|
309
|
+
* registro local cresce a tarde inteira sem subir. Nada disso volta a ser dito (dono, 06/08).
|
|
310
|
+
*
|
|
311
|
+
* Esta ferramenta é o lugar certo para o pulso porque o agente a chama ANTES de construir - que é
|
|
312
|
+
* exatamente o momento em que a consistência importa. Um `setInterval` avisaria no meio de outra
|
|
313
|
+
* coisa; isto avisa na fronteira de uma decisão.
|
|
314
|
+
*
|
|
315
|
+
* SÓ FALA QUANDO HÁ O QUE DIZER. Uma linha que aparece em toda chamada vira papel de parede numa
|
|
316
|
+
* ferramenta chamada dezenas de vezes por sessão, e aí ela não serve para a vez que importa.
|
|
309
317
|
*/
|
|
310
|
-
async function
|
|
311
|
-
|
|
312
|
-
return null;
|
|
318
|
+
async function sessionPulse(root, pinned) {
|
|
319
|
+
const out = [];
|
|
313
320
|
const slug = await installedSlug(root);
|
|
314
321
|
const token = await readToken();
|
|
315
|
-
if (
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
322
|
+
if (slug && token && pinned != null) {
|
|
323
|
+
const res = await fetch(`${resolveRegistry()}/api/registry/ds/${slug}`, {
|
|
324
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
325
|
+
}).catch(() => null);
|
|
326
|
+
/**
|
|
327
|
+
* 401 é a sessão que morreu no meio da sessão de trabalho, e é o pior silêncio possível: daqui
|
|
328
|
+
* em diante toda ferramenta falha e nenhuma diz por quê. Offline não produz linha - `res` nulo
|
|
329
|
+
* não distingue "sem rede" de "servidor fora", e nenhum dos dois é problema da pessoa.
|
|
330
|
+
*/
|
|
331
|
+
if (res?.status === 401)
|
|
332
|
+
out.push("> Your session on this machine has expired, so anything that needs the platform will fail from here on. A person has to run `npx synthesisui login` once in the terminal - I cannot complete it. What is on disk still governs: the rules above are the installed version's.");
|
|
333
|
+
else if (res?.ok) {
|
|
334
|
+
const body = (await res.json().catch(() => null));
|
|
335
|
+
if (body?.version && body.version > pinned)
|
|
336
|
+
out.push(`> v${body.version} of this system has been published and this project is on v${pinned}. These rules are v${pinned}'s, and so are the tokens on disk - the two move together. \`npx synthesisui upgrade ${slug}\` brings both.`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* O REGISTRO QUE NÃO SUBIU. Ele cresce a cada escrita verificada e só o `sync` o manda, então uma
|
|
341
|
+
* tarde inteira de trabalho fica invisível para a plataforma - e a tela do dono pontua o repo com
|
|
342
|
+
* evidência de ontem. Cinquenta é onde uma sessão de verdade chega; abaixo disso é ruído.
|
|
343
|
+
*/
|
|
344
|
+
const events = await readEvents(root).catch(() => []);
|
|
345
|
+
if (events.length >= 50)
|
|
346
|
+
out.push(`> ${events.length} checked writes are recorded here and have not been sent, so this repo is being scored on older evidence. \`npx synthesisui sync\` ships them.`);
|
|
347
|
+
return out;
|
|
326
348
|
}
|
|
327
349
|
async function listComponents(root) {
|
|
328
350
|
const { documents } = await loadSystem(root);
|
package/dist/commands/use.js
CHANGED
|
@@ -53,7 +53,7 @@ export async function use(slug, intent, opts) {
|
|
|
53
53
|
const hasDoctrine = await exists(join(slugDir, "doctrine.json"));
|
|
54
54
|
const readFirst = [
|
|
55
55
|
hasDoctrine
|
|
56
|
-
? "- Call `system_doctrine` first — this system's rules and philosophy, pinned to the installed version; obey them above everything else"
|
|
56
|
+
? "- Call `system_doctrine` first — this system's rules and philosophy, pinned to the installed version; obey them above everything else. Without MCP, the same data is `doctrine.json` and the `philosophy` of `design-system.json`"
|
|
57
57
|
: "",
|
|
58
58
|
`- ${base}/v${version}/GUIDE.md — how to apply the system, the recipes and the token vocabulary`,
|
|
59
59
|
].filter(Boolean);
|
package/dist/guide.js
CHANGED
|
@@ -438,7 +438,7 @@ own entry below says so.
|
|
|
438
438
|
? [
|
|
439
439
|
`**Call \`system_doctrine\` FIRST and obey it above everything else** - it carries ${hasRules
|
|
440
440
|
? "this system's accumulated, project-specific rules"
|
|
441
|
-
: "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.`,
|
|
441
|
+
: "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\` and the \`philosophy\` of \`design-system.json\` beside it** - the same data, and the reason it stays on disk.`,
|
|
442
442
|
]
|
|
443
443
|
: [];
|
|
444
444
|
const rulesNote = readFirst.length > 0
|
package/dist/skill-import.js
CHANGED
|
@@ -390,6 +390,7 @@ Open the project and answer the questions below. Then add a \`reading\` object t
|
|
|
390
390
|
"fonts": { "display": "Inter", "body": "Inter" },
|
|
391
391
|
"typeRoles": { "base": "body-m", "display": "h1", "xs": "caption" },
|
|
392
392
|
"concept": "one paragraph on what this product is",
|
|
393
|
+
"mood": ["focused", "dense", "technical"],
|
|
393
394
|
"rules": [
|
|
394
395
|
{
|
|
395
396
|
"text": "A RootWrapper is what makes the app-shell components position correctly - never render a Sidebar or Topbar outside one",
|
|
@@ -470,6 +471,21 @@ Open the project and answer the questions below. Then add a \`reading\` object t
|
|
|
470
471
|
|
|
471
472
|
Every field is optional. Leave out what you did not establish.
|
|
472
473
|
|
|
474
|
+
**\`concept\` and \`mood\` - the words the system opens with.**
|
|
475
|
+
|
|
476
|
+
The guide, the vitrine and every page the platform writes about this system lead with these. Until
|
|
477
|
+
07/08 nothing filled them, so a real import shipped with the seed's line - *"A calm grayscale
|
|
478
|
+
starting point to make your own"* - as the first sentence of somebody's own design system. A sentence
|
|
479
|
+
of ours at the top of their system says, in the first line, that nothing was read.
|
|
480
|
+
|
|
481
|
+
\`concept\` is one paragraph on what this product IS, in their words where you can find them: the
|
|
482
|
+
README, the docs site, the introduction their library ships with. \`mood\` is two to six words for how
|
|
483
|
+
the product FEELS - \`focused\`, \`dense\`, \`editorial\`, \`playful\`. It is a judgement and it is yours to
|
|
484
|
+
make, which is exactly why the arithmetic cannot: no reader infers a mood from a hex.
|
|
485
|
+
|
|
486
|
+
Leave \`mood\` out when you did not form one. The seed's three words stay and the import SAYS they are
|
|
487
|
+
ours, which is honest - three words you picked to fill a field are not.
|
|
488
|
+
|
|
473
489
|
**\`themes\` - which themes the product actually ships, and which one it opens in.**
|
|
474
490
|
|
|
475
491
|
This is the field that matters most and the one only you can fill. The ladder can say which
|
package/package.json
CHANGED