synthesisui 0.16.150 → 0.16.152
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/agent-wiring.js +16 -2
- package/dist/claude-md.js +34 -2
- package/dist/commands/connect.js +7 -3
- package/dist/commands/mcp.js +46 -0
- package/dist/commands/status.js +6 -0
- package/dist/commands/sync.js +48 -28
- package/package.json +1 -1
package/dist/agent-wiring.js
CHANGED
|
@@ -71,7 +71,7 @@ async function wireHook(root, command) {
|
|
|
71
71
|
* O MESMO SERVIDOR MCP, NOS CAMINHOS QUE CADA AGENTE LÊ.
|
|
72
72
|
*
|
|
73
73
|
* `.mcp.json` é o escopo-de-projeto do Claude Code; o Cursor lê `.cursor/mcp.json` com o MESMO shape
|
|
74
|
-
* (`mcpServers`). Então cobrir os dois é o mesmo JSON num segundo caminho, e as
|
|
74
|
+
* (`mcpServers`). Então cobrir os dois é o mesmo JSON num segundo caminho, e as ferramentas passam
|
|
75
75
|
* a existir nos dois editores sem uma linha de motor novo - era a lacuna mais barata da análise de DX
|
|
76
76
|
* (05/08).
|
|
77
77
|
*
|
|
@@ -94,9 +94,23 @@ async function wireMcpAt(root, version, rel) {
|
|
|
94
94
|
synthesisui: {
|
|
95
95
|
type: "stdio",
|
|
96
96
|
command: "npx",
|
|
97
|
+
/**
|
|
98
|
+
* O MCP FLUTUA, E O HOOK NÃO - e a diferença é o custo, não o gosto.
|
|
99
|
+
*
|
|
100
|
+
* O comentário do `hookCommand` mede 720ms-1,5s POR ESCRITA e conclui que um hook cujo
|
|
101
|
+
* comportamento muda sob você a cada edit é indebugável. Está certo, e não vale para aqui:
|
|
102
|
+
* um servidor MCP sobe UMA VEZ quando o editor abre a sessão e fica estável durante ela.
|
|
103
|
+
*
|
|
104
|
+
* Pinado, ele nunca ficava mais novo - e a regra que manda o agente comparar `ledger.cli`
|
|
105
|
+
* com a versão do servidor quase nunca disparava, porque os dois congelavam juntos no dia
|
|
106
|
+
* do `connect` (dono, 06/08). Com `@latest`, um leitor publicado hoje alcança o repo dele
|
|
107
|
+
* na próxima vez que ele abrir o editor, sem ninguém rodar nada.
|
|
108
|
+
*
|
|
109
|
+
* O install local continua ganhando: quem tem o CLI no `node_modules` escolheu a versão.
|
|
110
|
+
*/
|
|
97
111
|
args: local
|
|
98
112
|
? ["--no-install", "synthesisui", "mcp"]
|
|
99
|
-
: [
|
|
113
|
+
: ["synthesisui@latest", "mcp"],
|
|
100
114
|
},
|
|
101
115
|
},
|
|
102
116
|
}, null, 2)}\n`, "utf8");
|
package/dist/claude-md.js
CHANGED
|
@@ -166,6 +166,38 @@ shadcn components wear this system and need no edits from you. Without it they
|
|
|
166
166
|
stay on shadcn's defaults, which looks like the design system failing rather than
|
|
167
167
|
one import missing - so check for it before you add a shadcn component, and add
|
|
168
168
|
it if it is not there.`;
|
|
169
|
+
/**
|
|
170
|
+
* A REGRA QUE TIRA O "O DEV FAZ ALGO", e ela vale para todo agente que lê este bloco.
|
|
171
|
+
*
|
|
172
|
+
* Um leitor novo do CLI precisa olhar o código do cliente, e a esteira não guarda esse código - então
|
|
173
|
+
* a medição é irredutível na máquina dele. O que era redutível é o GATILHO: até 06/08 era uma pessoa
|
|
174
|
+
* lembrando de rodar `sync` num momento em que ela precisava escolher, e um conserto que depende disso
|
|
175
|
+
* alcança a minoria dos clientes.
|
|
176
|
+
*
|
|
177
|
+
* A checagem é LOCAL e grátis: o censo grava qual CLI o mediu (`ledger.cli`) e o servidor MCP É o CLI,
|
|
178
|
+
* então comparar as duas versões é aritmética em disco. Nada aqui telefona para casa - é a mesma
|
|
179
|
+
* decisão que mantém o hook instalado.
|
|
180
|
+
*/
|
|
181
|
+
const REFRESH = `
|
|
182
|
+
|
|
183
|
+
**If the measurement is older than the reader, refresh it before you write UI.**
|
|
184
|
+
Read \`_synthesisui/census.json\` → \`ledger.cli\` and compare it with the version
|
|
185
|
+
of the \`synthesisui\` MCP server you are talking to. When they differ, call
|
|
186
|
+
\`refresh_system\` once. It re-measures this repo with the current reader, reuses
|
|
187
|
+
the reading that was already authored, and sends it - a newer reader sees styles
|
|
188
|
+
the old one could not, and until it runs, this project's system is missing them.
|
|
189
|
+
|
|
190
|
+
It never asks anyone anything, and it never overwrites a recipe a person wrote on
|
|
191
|
+
the screen: when a send would rewrite one, it refuses and names them. If it says
|
|
192
|
+
nothing changed, nothing changed - carry on.
|
|
193
|
+
|
|
194
|
+
**And if it reports that a newer CLI is published, say so to the person and stop
|
|
195
|
+
there.** You cannot fix that one: the check that runs after your writes is pinned
|
|
196
|
+
to the version installed at setup, and the tools you are holding were started by
|
|
197
|
+
the editor. One command rewires both - \`npx synthesisui@latest connect\` - and it
|
|
198
|
+
takes effect the next time the editor starts a session. Telling them is the whole
|
|
199
|
+
job; doing it yourself would leave them with a file that says one version and a
|
|
200
|
+
process running another.`;
|
|
169
201
|
const VERIFY = `
|
|
170
202
|
|
|
171
203
|
**Verify with a build, not a running server.** If you do start a dev server, stop
|
|
@@ -185,7 +217,7 @@ const SELF_CHECK_MANUAL = `
|
|
|
185
217
|
It names the token for every hardcoded value it finds. Fix what it names, run it
|
|
186
218
|
again, and only then move to the next thing. Do not batch this up for the end -
|
|
187
219
|
a component checked while you still have its context is a two-line fix; the same
|
|
188
|
-
component found in a repo-wide audit next week is an archaeology exercise.${REFUSAL}
|
|
220
|
+
component found in a repo-wide audit next week is an archaeology exercise.${REFUSAL}${REFRESH}
|
|
189
221
|
|
|
190
222
|
Better than remembering: \`npx synthesisui@latest connect\` installs the check as
|
|
191
223
|
an editor hook, so it runs on its own and this paragraph stops being your job.${VERIFY}`;
|
|
@@ -196,7 +228,7 @@ token this project already has for anything hardcoded. You do not need to run
|
|
|
196
228
|
\`doctor\` by hand, and you should not wait for a final pass.
|
|
197
229
|
|
|
198
230
|
When it names something, fix it in that file before moving to the next one -
|
|
199
|
-
you still have the context, so it is a two-line change.${REFUSAL}${VERIFY}`;
|
|
231
|
+
you still have the context, so it is a two-line change.${REFUSAL}${REFRESH}${VERIFY}`;
|
|
200
232
|
/**
|
|
201
233
|
* THE INTERFACE LANGUAGE, READ FROM THE PROJECT RATHER THAN ASKED FOR.
|
|
202
234
|
*
|
package/dist/commands/connect.js
CHANGED
|
@@ -6,6 +6,7 @@ import { body, paint, section, snippet } from "../output.js";
|
|
|
6
6
|
import { IMPORT_SKILL, IMPORT_SKILL_PATH } from "../skill-import.js";
|
|
7
7
|
import { INIT_SKILL, INIT_SKILL_PATH } from "../skill-init.js";
|
|
8
8
|
import { ci } from "./ci.js";
|
|
9
|
+
import { MCP_TOOL_COUNT } from "./mcp.js";
|
|
9
10
|
/**
|
|
10
11
|
* `synthesisui connect` - put the three layers where they actually run.
|
|
11
12
|
*
|
|
@@ -44,13 +45,16 @@ export async function connect(opts) {
|
|
|
44
45
|
console.log(snippet([wired.command]));
|
|
45
46
|
}
|
|
46
47
|
/**
|
|
47
|
-
* OS CAMINHOS DE MCP, POR NOME - e o número
|
|
48
|
-
*
|
|
48
|
+
* OS CAMINHOS DE MCP, POR NOME - e o número vem da LISTA, não da minha memória.
|
|
49
|
+
*
|
|
50
|
+
* Dizia "four tools" desde quando eram quatro; eu troquei por 11 contando o `serverInfo` como
|
|
51
|
+
* ferramenta, e eram 10. Um número que alguém digita envelhece na primeira ferramenta nova - este é
|
|
52
|
+
* lido de `MCP_TOOL_COUNT`, que é `TOOLS.length`.
|
|
49
53
|
*/
|
|
50
54
|
if (want.mcp && wired.mcp !== "skipped") {
|
|
51
55
|
for (const one of wired.mcp)
|
|
52
56
|
console.log(body(one.status === "added"
|
|
53
|
-
? `✓ ${one.path.padEnd(22)}
|
|
57
|
+
? `✓ ${one.path.padEnd(22)} ${MCP_TOOL_COUNT} tools, so the agent can ask instead of guess`
|
|
54
58
|
: `· ${one.path.padEnd(22)} already had it`));
|
|
55
59
|
}
|
|
56
60
|
/**
|
package/dist/commands/mcp.js
CHANGED
|
@@ -7,6 +7,7 @@ import { nearestToken, tokenFor } from "../doctor/tokens.js";
|
|
|
7
7
|
import { repoStateOf } from "../repo-state.js";
|
|
8
8
|
import { component } from "./component.js";
|
|
9
9
|
import { loadSystem, walkAll } from "./doctor.js";
|
|
10
|
+
import { installedSlug, remeasure } from "./sync.js";
|
|
10
11
|
const send = (msg) => {
|
|
11
12
|
process.stdout.write(`${JSON.stringify(msg)}\n`);
|
|
12
13
|
};
|
|
@@ -37,6 +38,11 @@ async function capturing(run) {
|
|
|
37
38
|
}
|
|
38
39
|
// ── The tools ────────────────────────────────────────────────────────────────
|
|
39
40
|
const TOOLS = [
|
|
41
|
+
{
|
|
42
|
+
name: "refresh_system",
|
|
43
|
+
description: "Re-measure this repository with the reader you are running and send it to the design system, reusing the reading already authored. Call this when `_synthesisui/census.json` was measured by an OLDER CLI than this server - the tool tells you the two versions and refuses politely when there is nothing to refresh. It never asks a person anything: when a recipe in the draft was written on the screen and this send would rewrite it, it refuses and names them instead. Nothing on the platform is deleted and no published version moves.",
|
|
44
|
+
inputSchema: { type: "object", properties: {} },
|
|
45
|
+
},
|
|
40
46
|
{
|
|
41
47
|
name: "check_file",
|
|
42
48
|
description: "Check one file (or folder) against the installed design system. Returns token coverage, every hardcoded value with the token this project already has for it, and any --ds- name the system does not declare. Run this after writing or editing any UI file, before moving on.",
|
|
@@ -187,6 +193,14 @@ const TOOLS = [
|
|
|
187
193
|
},
|
|
188
194
|
},
|
|
189
195
|
];
|
|
196
|
+
/**
|
|
197
|
+
* QUANTAS FERRAMENTAS ESTE SERVIDOR SERVE, lido da lista.
|
|
198
|
+
*
|
|
199
|
+
* O `connect` imprimia o número à mão: ficou em "four tools" muito depois de serem mais, e quando eu
|
|
200
|
+
* corrigi escrevi 11 contando o `serverInfo` como ferramenta - eram 10. Um número digitado envelhece na
|
|
201
|
+
* primeira ferramenta nova.
|
|
202
|
+
*/
|
|
203
|
+
export const MCP_TOOL_COUNT = TOOLS.length;
|
|
190
204
|
async function checkFile(root, path) {
|
|
191
205
|
const { table } = await loadSystem(root);
|
|
192
206
|
if (table.byName.size === 0)
|
|
@@ -855,6 +869,38 @@ cli) {
|
|
|
855
869
|
});
|
|
856
870
|
return text(`Filed as ${r.id}. It shows up in \`synthesisui doctor\` until a person authors it or closes it - keep building with your workaround, and say in your summary that the request exists.`);
|
|
857
871
|
}
|
|
872
|
+
case "refresh_system": {
|
|
873
|
+
/**
|
|
874
|
+
* O ELO QUE TIRA O "O DEV FAZ ALGO" - e ele é uma ferramenta e não um comando de propósito.
|
|
875
|
+
*
|
|
876
|
+
* Um leitor novo do CLI precisa OLHAR o código do cliente, e a gente não guarda esse código: a
|
|
877
|
+
* medição é irredutível na máquina dele. O que era redutível é o GATILHO - até 06/08 era uma
|
|
878
|
+
* pessoa lembrando de um comando num momento que ela precisava escolher. Aqui é o agente,
|
|
879
|
+
* dentro de uma sessão que ela já começou, seguindo a regra que o bloco gerenciado carrega.
|
|
880
|
+
*
|
|
881
|
+
* Chama o MESMO `remeasure` do `sync`, capturando a saída: um segundo caminho de medição
|
|
882
|
+
* discordaria do primeiro na primeira mudança, e a diferença apareceria no sistema de um cliente.
|
|
883
|
+
*
|
|
884
|
+
* ENVIA SOZINHO quando não há nada em risco, e RECUSA nomeando quando há - sem TTY o guarda de
|
|
885
|
+
* rascunho responde "não" por construção, então o pior caso automatizado é uma resposta dizendo
|
|
886
|
+
* o que não foi feito e por quê (dono, 06/08).
|
|
887
|
+
*/
|
|
888
|
+
const slug = await installedSlug(root);
|
|
889
|
+
if (!slug)
|
|
890
|
+
return text("No system installed here that belongs to a dashboard row, so there is nothing to refresh. `synthesisui add <slug>` installs one; an adopted system is described here and owned elsewhere.");
|
|
891
|
+
const token = await readToken();
|
|
892
|
+
if (!token)
|
|
893
|
+
return text("Not signed in, so nothing could be sent. A person has to run `synthesisui login` once in the terminal - it opens a browser, and I cannot complete it for them.");
|
|
894
|
+
const { out } = await capturing(() => remeasure({
|
|
895
|
+
root,
|
|
896
|
+
base: resolveRegistry(),
|
|
897
|
+
token,
|
|
898
|
+
slug,
|
|
899
|
+
cli: VERSION,
|
|
900
|
+
}));
|
|
901
|
+
return text(out.trim() ||
|
|
902
|
+
"Nothing was reported by the refresh - which means it found nothing to measure.");
|
|
903
|
+
}
|
|
858
904
|
case "request_token": {
|
|
859
905
|
const name = String(args.name ?? "");
|
|
860
906
|
// TRIAGE AT FILING: if the system's own contract already promises this
|
package/dist/commands/status.js
CHANGED
|
@@ -139,6 +139,12 @@ export async function status(opts) {
|
|
|
139
139
|
: `${owned.slug} on the platform did not come from an import, so there is no measurement to compare.`));
|
|
140
140
|
if (remote.hasCensus)
|
|
141
141
|
console.log(body(paint.dim("`synthesisui sync` measures this repo again with today's reader and sends it.")));
|
|
142
|
+
/** E a distância até o leitor publicado, com o comando que a fecha - ver `sync`. */
|
|
143
|
+
if (remote.publishedCli && opts.cli && remote.publishedCli !== opts.cli) {
|
|
144
|
+
console.log("");
|
|
145
|
+
console.log(body(`Your CLI is ${opts.cli} and ${remote.publishedCli} is published - a newer reader sees styles this one cannot.`));
|
|
146
|
+
console.log(paint.blue(snippet(["npx synthesisui@latest connect"])));
|
|
147
|
+
}
|
|
142
148
|
}
|
|
143
149
|
/** "2 hours ago", sem dependência - e `Invalid Date` nunca chega à tela. */
|
|
144
150
|
function ageOf(iso) {
|
package/dist/commands/sync.js
CHANGED
|
@@ -3,7 +3,7 @@ import { join, resolve } from "node:path";
|
|
|
3
3
|
import { readToken, resolveRegistry } from "../config.js";
|
|
4
4
|
import { readEvents } from "../doctor/ledger.js";
|
|
5
5
|
import { checkableName, closeRequest, readRequests, verifyAndCloseRequests, } from "../doctor/requests.js";
|
|
6
|
-
import { body, paint, section } from "../output.js";
|
|
6
|
+
import { body, paint, section, snippet } from "../output.js";
|
|
7
7
|
import { repoStateOf } from "../repo-state.js";
|
|
8
8
|
import { resolveReadParts, takeCensus } from "./import.js";
|
|
9
9
|
/**
|
|
@@ -53,32 +53,7 @@ export async function sync(opts) {
|
|
|
53
53
|
console.log(body(" synthesisui login"));
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
const dsDir = join(root, "_synthesisui", "ds");
|
|
58
|
-
let slug = null;
|
|
59
|
-
try {
|
|
60
|
-
const slugs = (await readdir(dsDir, { withFileTypes: true }))
|
|
61
|
-
.filter((e) => e.isDirectory())
|
|
62
|
-
.map((e) => e.name);
|
|
63
|
-
for (const s of slugs) {
|
|
64
|
-
const raw = await readFile(join(dsDir, s, ".lock"), "utf8").catch(() => "");
|
|
65
|
-
try {
|
|
66
|
-
const lock = JSON.parse(raw);
|
|
67
|
-
// An adopted system is described, not owned on the platform - there is
|
|
68
|
-
// no dashboard row to sync into (yet).
|
|
69
|
-
if (lock.slug && !lock.adopted) {
|
|
70
|
-
slug = lock.slug;
|
|
71
|
-
break;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
// unreadable lock - try the next
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
// no _synthesisui at all
|
|
81
|
-
}
|
|
56
|
+
const slug = await installedSlug(root);
|
|
82
57
|
if (!slug) {
|
|
83
58
|
console.log(section("Sync"));
|
|
84
59
|
console.log(body("No installed system here, so there is nowhere to sync to."));
|
|
@@ -180,7 +155,7 @@ export async function sync(opts) {
|
|
|
180
155
|
* avisar.
|
|
181
156
|
* 5. envia para a rota que atualiza o RASCUNHO pelo slug - nada publicado se move.
|
|
182
157
|
*/
|
|
183
|
-
async function remeasure(args) {
|
|
158
|
+
export async function remeasure(args) {
|
|
184
159
|
const { root, base, token, slug } = args;
|
|
185
160
|
const head = await fetch(`${base}/api/registry/ds/${slug}/reading`, {
|
|
186
161
|
headers: { Authorization: `Bearer ${token}` },
|
|
@@ -202,6 +177,20 @@ async function remeasure(args) {
|
|
|
202
177
|
console.log(body(`"${slug}" did not come from an import, so there is no measurement to refresh. Import it first: npx synthesisui import`));
|
|
203
178
|
return;
|
|
204
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* A DISTÂNCIA ATÉ O CLI PUBLICADO, dita onde ele já está.
|
|
182
|
+
*
|
|
183
|
+
* O hook fica pinado na versão do dia do `connect` - por um motivo medido: 720ms por escrita, e um
|
|
184
|
+
* check que muda sob você é indebugável. O preço é que ele não avança sozinho, e a única pessoa que
|
|
185
|
+
* pode movê-lo é quem roda o comando. Então o número é dito aqui, com o comando ao lado, em vez de
|
|
186
|
+
* viver numa tela que alguém precisa lembrar de abrir.
|
|
187
|
+
*/
|
|
188
|
+
if (stored.publishedCli && args.cli && stored.publishedCli !== args.cli) {
|
|
189
|
+
console.log(section("A newer reader is published"));
|
|
190
|
+
console.log(body(`You are running ${args.cli} and ${stored.publishedCli} is published. The measurement below uses ${args.cli}.`));
|
|
191
|
+
console.log(paint.blue(snippet(["npx synthesisui@latest connect"])));
|
|
192
|
+
console.log(body(paint.faint("That rewires the hook and the tools to the new version - the editor picks them up on the next session.")));
|
|
193
|
+
}
|
|
205
194
|
console.log(section("Measuring your repository again"));
|
|
206
195
|
console.log(body(paint.faint(`${stored.scope ? `scope ${stored.scope}` : "the whole repo"} · the reading you already authored travels unchanged`)));
|
|
207
196
|
const census = await takeCensus(stored.scope ? join(root, stored.scope) : root, {
|
|
@@ -296,3 +285,34 @@ async function askToOverwrite() {
|
|
|
296
285
|
rl.close();
|
|
297
286
|
}
|
|
298
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* O SLUG QUE ESTE PROJETO TEM INSTALADO, ou `null`.
|
|
290
|
+
*
|
|
291
|
+
* Uma função e não duas cópias: o `sync` e a ferramenta `refresh_system` do MCP precisam da mesma
|
|
292
|
+
* busca, e a regra que ela carrega - um DS ADOTADO é descrito aqui e pertencido em outro lugar, então
|
|
293
|
+
* não tem linha no dashboard para onde sincronizar - discordaria entre duas cópias na primeira vez que
|
|
294
|
+
* alguém a mudasse.
|
|
295
|
+
*/
|
|
296
|
+
export async function installedSlug(root) {
|
|
297
|
+
const dsDir = join(root, "_synthesisui", "ds");
|
|
298
|
+
try {
|
|
299
|
+
const slugs = (await readdir(dsDir, { withFileTypes: true }))
|
|
300
|
+
.filter((e) => e.isDirectory())
|
|
301
|
+
.map((e) => e.name);
|
|
302
|
+
for (const name of slugs) {
|
|
303
|
+
const raw = await readFile(join(dsDir, name, ".lock"), "utf8").catch(() => "");
|
|
304
|
+
try {
|
|
305
|
+
const lock = JSON.parse(raw);
|
|
306
|
+
if (lock.slug && !lock.adopted)
|
|
307
|
+
return lock.slug;
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
// unreadable lock - try the next
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
// no _synthesisui at all
|
|
316
|
+
}
|
|
317
|
+
return null;
|
|
318
|
+
}
|
package/package.json
CHANGED