synthesisui 0.16.243 → 0.16.244
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-provenance.js +92 -0
- package/dist/commands/mcp.js +148 -41
- package/package.json +1 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DE ONDE VEIO A RESPOSTA - e é um DADO, não uma frase (dono, 18/08).
|
|
3
|
+
*
|
|
4
|
+
* O pedido veio com a razão embutida: *"eu trataria a linha Source como algo
|
|
5
|
+
* estrutural mesmo, não só visual. Isso pode virar uma métrica de maturidade
|
|
6
|
+
* depois - porcentagem de respostas por receita, por composição, por
|
|
7
|
+
* improviso."*
|
|
8
|
+
*
|
|
9
|
+
* Então a procedência nasce como estrutura e o texto é DERIVADO dela. O
|
|
10
|
+
* contrário - escrever a frase à mão em cada resposta - produz duas verdades no
|
|
11
|
+
* dia em que uma delas for editada, e a que vai para a métrica é sempre a que
|
|
12
|
+
* ninguém leu.
|
|
13
|
+
*
|
|
14
|
+
* ─────────────────────────────────────────────────────────────────────────
|
|
15
|
+
* O QUE CADA KIND SIGNIFICA, e a fronteira é a GARANTIA
|
|
16
|
+
*
|
|
17
|
+
* contract veio do que este sistema declara - receita, token, regra,
|
|
18
|
+
* vocabulário. A plataforma responde por isso.
|
|
19
|
+
* composed montado a partir de camadas porque não havia receita para o
|
|
20
|
+
* pedido. Vale, e vale menos que um contrato. (Nasce com o
|
|
21
|
+
* compose_context; hoje nada devolve isto, e é de propósito -
|
|
22
|
+
* um kind que ninguém emite é um número que mente por otimismo.)
|
|
23
|
+
* pointer um ponteiro para conhecimento de terceiro. Não é nosso, não é
|
|
24
|
+
* garantido, e a resposta diz o pacote. Mesma gramática que a
|
|
25
|
+
* plataforma já usa: "3rd-party · <pacote>".
|
|
26
|
+
* none este sistema não tinha nada para o pedido. É o kind mais
|
|
27
|
+
* valioso dos quatro: é ele que mede o improviso, e é o evento
|
|
28
|
+
* que a plataforma não contava.
|
|
29
|
+
*
|
|
30
|
+
* A TAXA DE IMPROVISO é `none` sobre o total, e ela é a pergunta que decide se
|
|
31
|
+
* a composição vale ser construída - respondida com dado em vez de sensação.
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* A LINHA QUE O AGENTE E A PESSOA LEEM - derivada, nunca escrita à mão.
|
|
35
|
+
*
|
|
36
|
+
* Ela serve dois leitores de uma vez: o dev sabe o quanto confiar no que
|
|
37
|
+
* acabou de receber, e o agente sabe se está pisando em contrato ou em
|
|
38
|
+
* composição. Um agente que não sabe a diferença trata as duas com a mesma
|
|
39
|
+
* confiança, e é assim que uma suposição nossa entra no repositório dele com
|
|
40
|
+
* cara de regra.
|
|
41
|
+
*/
|
|
42
|
+
export function sourceLine(source) {
|
|
43
|
+
const label = source.kind === "pointer"
|
|
44
|
+
? "3rd-party pointer"
|
|
45
|
+
: source.kind === "none"
|
|
46
|
+
? "none"
|
|
47
|
+
: source.kind;
|
|
48
|
+
return source.detail
|
|
49
|
+
? `Source: ${label} · ${source.detail}`
|
|
50
|
+
: `Source: ${label}`;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* O texto com a procedência colada no fim - uma linha em branco antes, para
|
|
54
|
+
* ela não se misturar com a última frase da resposta.
|
|
55
|
+
*/
|
|
56
|
+
export function withSource(answer) {
|
|
57
|
+
return `${answer.body}\n\n${sourceLine(answer.source)}`;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* O VALOR GRAVADO, e o prefixo existe para o eixo ser legível numa tabela que
|
|
61
|
+
* já carrega outra coisa.
|
|
62
|
+
*
|
|
63
|
+
* `agent_reads.action` é `text` sem CHECK no banco (medido em produção), então
|
|
64
|
+
* um valor novo não custa migração - e uma coluna nova custaria a tabela
|
|
65
|
+
* inteira até a migração rodar. `src-` marca de qual eixo a linha fala:
|
|
66
|
+
* `describe`/`add` são gesto, `tool` é chamada, `src-*` é procedência.
|
|
67
|
+
*
|
|
68
|
+
* E UMA LACUNA LEVA O MOTIVO NO PRÓPRIO VALOR: `src-none-recipe`. Assim a
|
|
69
|
+
* pergunta agregada continua sendo um `like 'src-none%'` e a pergunta por
|
|
70
|
+
* motivo é a mesma consulta com o valor inteiro - sem coluna nova, e sem
|
|
71
|
+
* perder o total.
|
|
72
|
+
*
|
|
73
|
+
* Recebe a procedência INTEIRA e não o `kind`: com o kind sozinho, o motivo
|
|
74
|
+
* seria um segundo argumento que alguém esquece, e a linha gravada voltaria a
|
|
75
|
+
* ser um `none` cego.
|
|
76
|
+
*/
|
|
77
|
+
export function provenanceAction(source) {
|
|
78
|
+
return source.kind === "none"
|
|
79
|
+
? `src-none-${source.reason}`
|
|
80
|
+
: `src-${source.kind}`;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* O NOME DA FERRAMENTA COMO A TABELA O GUARDA.
|
|
84
|
+
*
|
|
85
|
+
* A coluna `component` aceita `^[a-z][a-z0-9-]*$` na rota - a mesma forma de um
|
|
86
|
+
* slug de receita. As ferramentas são snake_case, então elas viajam em
|
|
87
|
+
* kebab-case; sem isto o ping seria recusado com 400 e a instrumentação
|
|
88
|
+
* silenciaria justamente na ferramenta nova.
|
|
89
|
+
*/
|
|
90
|
+
export function toolSlug(tool) {
|
|
91
|
+
return tool.replace(/_/g, "-").toLowerCase();
|
|
92
|
+
}
|
package/dist/commands/mcp.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { readdir, readFile } from "node:fs/promises";
|
|
3
3
|
import { join, relative, resolve } from "node:path";
|
|
4
|
+
import { provenanceAction, toolSlug, withSource, } from "../agent-provenance.js";
|
|
4
5
|
import { pinnedHookVersion } from "../agent-wiring.js";
|
|
5
6
|
import { readToken, resolveRegistry } from "../config.js";
|
|
6
7
|
import { readEvents } from "../doctor/ledger.js";
|
|
@@ -14,6 +15,16 @@ import { installedSlug, remeasure } from "./sync.js";
|
|
|
14
15
|
const send = (msg) => {
|
|
15
16
|
process.stdout.write(`${JSON.stringify(msg)}\n`);
|
|
16
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* A RESPOSTA QUE VEIO DO CONTRATO - o atalho, porque é o caso comum.
|
|
20
|
+
*
|
|
21
|
+
* `detail` fica de fora quando a própria resposta já nomeia a fonte (o token,
|
|
22
|
+
* o componente, a versão): repetir aquilo na linha de procedência é ruído.
|
|
23
|
+
*/
|
|
24
|
+
const fromContract = (body, detail) => ({
|
|
25
|
+
body,
|
|
26
|
+
source: { kind: "contract", ...(detail ? { detail } : null) },
|
|
27
|
+
});
|
|
17
28
|
/** A tool result is always text: readable by the model, and by a person
|
|
18
29
|
* watching the transcript when something goes wrong. */
|
|
19
30
|
const text = (body, isError = false) => ({
|
|
@@ -254,7 +265,10 @@ export const MCP_TOOL_COUNT = TOOLS.length;
|
|
|
254
265
|
async function checkFile(root, path) {
|
|
255
266
|
const { table } = await loadSystem(root);
|
|
256
267
|
if (table.byName.size === 0)
|
|
257
|
-
return
|
|
268
|
+
return {
|
|
269
|
+
body: "No design system installed here, so there is nothing to check against. Run `synthesisui init` or `synthesisui adopt`.",
|
|
270
|
+
source: { kind: "none", reason: "system", detail: "no system installed" },
|
|
271
|
+
};
|
|
258
272
|
const scope = resolve(root, path);
|
|
259
273
|
const reports = [];
|
|
260
274
|
for await (const file of walkAll([scope])) {
|
|
@@ -263,7 +277,7 @@ async function checkFile(root, path) {
|
|
|
263
277
|
reports.push(scanSource(relative(root, file), src, table));
|
|
264
278
|
}
|
|
265
279
|
if (reports.length === 0)
|
|
266
|
-
return `Nothing readable at ${path}
|
|
280
|
+
return fromContract(`Nothing readable at ${path}.`);
|
|
267
281
|
const d = diagnose(reports);
|
|
268
282
|
const out = [
|
|
269
283
|
`${table.name ?? table.slug}: ${d.coverage}% of design values come from the system.`,
|
|
@@ -284,12 +298,16 @@ async function checkFile(root, path) {
|
|
|
284
298
|
}
|
|
285
299
|
if (d.findings.length === 0 && phantoms.length === 0)
|
|
286
300
|
out.push("", "Nothing to fix here.");
|
|
287
|
-
return out.join("\n");
|
|
301
|
+
return fromContract(out.join("\n"));
|
|
288
302
|
}
|
|
289
303
|
async function findToken(root, value) {
|
|
290
304
|
const { table } = await loadSystem(root);
|
|
291
|
-
if (table.byName.size === 0)
|
|
292
|
-
return
|
|
305
|
+
if (table.byName.size === 0) {
|
|
306
|
+
return {
|
|
307
|
+
body: "No design system installed here.",
|
|
308
|
+
source: { kind: "none", reason: "system", detail: "no system installed" },
|
|
309
|
+
};
|
|
310
|
+
}
|
|
293
311
|
/**
|
|
294
312
|
* O NOME DELE PRIMEIRO, como no doctor (`nameToWrite`): `loadSystem` já devolve a tabela COM os
|
|
295
313
|
* aliases dele (`withTheirNames`) e este era o único leitor que nunca os consultava. Sem o `kind`
|
|
@@ -306,16 +324,27 @@ async function findToken(root, value) {
|
|
|
306
324
|
const exact = tokenFor(table, value);
|
|
307
325
|
if (exact) {
|
|
308
326
|
const shown = theirNameFor(value) ?? exact;
|
|
309
|
-
return `${value} is ${shown} in this project. Use var(${shown})
|
|
327
|
+
return fromContract(`${value} is ${shown} in this project. Use var(${shown}).`);
|
|
310
328
|
}
|
|
311
329
|
const near = nearestToken(table, value);
|
|
312
330
|
if (near) {
|
|
313
331
|
const shown = theirNameFor(near.value) ?? near.name;
|
|
314
|
-
|
|
332
|
+
/** Perto não é o valor: o sistema NÃO tem este, e a métrica precisa saber. */
|
|
333
|
+
return {
|
|
334
|
+
body: `No token holds ${value}. The closest is ${shown} at ${near.value}. If that is what you meant, use it - if it genuinely is not, say so rather than inventing a token.`,
|
|
335
|
+
source: {
|
|
336
|
+
kind: "none",
|
|
337
|
+
reason: "token",
|
|
338
|
+
detail: `no token for ${value}`,
|
|
339
|
+
},
|
|
340
|
+
};
|
|
315
341
|
}
|
|
316
342
|
// Same words the managed block uses. A refusal is only useful if it is the
|
|
317
343
|
// same refusal every time.
|
|
318
|
-
return
|
|
344
|
+
return {
|
|
345
|
+
body: `No token in this system holds ${value}, and nothing is close. Do NOT invent one. Say which value you need and what you would call it, and let a person decide.`,
|
|
346
|
+
source: { kind: "none", reason: "token", detail: `no token for ${value}` },
|
|
347
|
+
};
|
|
319
348
|
}
|
|
320
349
|
/**
|
|
321
350
|
* A DOUTRINA DO SISTEMA - as regras e a filosofia, servidas em vez de materializadas.
|
|
@@ -801,18 +830,28 @@ async function validateRecipes(root, entries) {
|
|
|
801
830
|
* had 26 names against a catalogue of 41.
|
|
802
831
|
*/
|
|
803
832
|
/**
|
|
804
|
-
* O CONTADOR DO AGENTE, fire-and-forget
|
|
805
|
-
*
|
|
806
|
-
*
|
|
807
|
-
*
|
|
833
|
+
* O CONTADOR DO AGENTE, fire-and-forget e agora CENTRAL.
|
|
834
|
+
*
|
|
835
|
+
* Ele nasceu para uma tela: o UI Kit mostra "vezes que esta receita foi consultada pelo
|
|
836
|
+
* Agente". E era chamado à mão em duas ferramentas de treze, o que quer dizer que onze
|
|
837
|
+
* ferramentas nunca contaram nada - inclusive a única que sabe dizer que o sistema NÃO
|
|
838
|
+
* tinha o que foi pedido.
|
|
839
|
+
*
|
|
840
|
+
* Agora o `callTool` o chama uma vez, para toda ferramenta, com a procedência da
|
|
841
|
+
* resposta. O dono foi explícito sobre por que centralizar (18/08): *"isso vira
|
|
842
|
+
* comportamento padrão e não depende de disciplina futura."* Um contador chamado dentro
|
|
843
|
+
* de um `case` é um contador que a próxima ferramenta esquece.
|
|
844
|
+
*
|
|
845
|
+
* Nunca bloqueia a resposta: sem sessão ou sem rede, não conta e não quebra - telemetria
|
|
846
|
+
* que atrasa a ferramenta é telemetria que alguém desliga.
|
|
808
847
|
*/
|
|
809
|
-
function
|
|
848
|
+
function recordAgentCall(root, call,
|
|
810
849
|
/** A versão que está reportando - ver `repoStateOf`. Ausente = não reporta estado. */
|
|
811
850
|
cli) {
|
|
812
851
|
void (async () => {
|
|
813
852
|
try {
|
|
814
853
|
const token = await readToken();
|
|
815
|
-
if (!token
|
|
854
|
+
if (!token)
|
|
816
855
|
return;
|
|
817
856
|
// O slug vem do .lock do sistema instalado - a mesma resolução do sync.
|
|
818
857
|
const dsDir = join(root, "_synthesisui", "ds");
|
|
@@ -853,8 +892,10 @@ cli) {
|
|
|
853
892
|
*/
|
|
854
893
|
body: JSON.stringify({
|
|
855
894
|
slug,
|
|
856
|
-
|
|
857
|
-
|
|
895
|
+
tool: toolSlug(call.tool),
|
|
896
|
+
source: provenanceAction(call.source),
|
|
897
|
+
...(call.component ? { component: call.component } : {}),
|
|
898
|
+
...(call.action ? { action: call.action } : {}),
|
|
858
899
|
...(repo ? { repo } : {}),
|
|
859
900
|
}),
|
|
860
901
|
});
|
|
@@ -912,7 +953,22 @@ async function describeComponent(root, name) {
|
|
|
912
953
|
break;
|
|
913
954
|
}
|
|
914
955
|
if (!recipe) {
|
|
915
|
-
|
|
956
|
+
/**
|
|
957
|
+
* O EVENTO MAIS VALIOSO DA PLATAFORMA, e até 18/08 ele não era contado.
|
|
958
|
+
*
|
|
959
|
+
* Aqui é onde o agente descobre que vai ter que improvisar. A frase já
|
|
960
|
+
* existia; o que não existia era o REGISTRO - então ninguém sabia quantas
|
|
961
|
+
* vezes, nem para quê. É este número que decide se compor conhecimento
|
|
962
|
+
* vale ser construído, e para quais famílias primeiro.
|
|
963
|
+
*/
|
|
964
|
+
return {
|
|
965
|
+
body: `This system has no component called "${name}". Run list_components to see what it does have - and if nothing covers what you need, file request_component rather than writing one from scratch.`,
|
|
966
|
+
source: {
|
|
967
|
+
kind: "none",
|
|
968
|
+
reason: "recipe",
|
|
969
|
+
detail: `no recipe for "${name}"`,
|
|
970
|
+
},
|
|
971
|
+
};
|
|
916
972
|
}
|
|
917
973
|
const out = [
|
|
918
974
|
`${name}${recipe.description ? ` - ${recipe.description}` : ""}`,
|
|
@@ -1036,51 +1092,98 @@ async function describeComponent(root, name) {
|
|
|
1036
1092
|
if (laws.length === 0 && needed.length === 0) {
|
|
1037
1093
|
out.push("", "No rules govern this component yet. Build with it, and if you find yourself working around it, file request_component with the reasoning.");
|
|
1038
1094
|
}
|
|
1039
|
-
return out.join("\n");
|
|
1095
|
+
return fromContract(out.join("\n"));
|
|
1040
1096
|
}
|
|
1041
1097
|
async function addComponent(root, name) {
|
|
1042
1098
|
const { table } = await loadSystem(root);
|
|
1043
|
-
if (!table.slug)
|
|
1044
|
-
return
|
|
1099
|
+
if (!table.slug) {
|
|
1100
|
+
return {
|
|
1101
|
+
body: "No design system installed here.",
|
|
1102
|
+
source: { kind: "none", reason: "system", detail: "no system installed" },
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1045
1105
|
const { out } = await capturing(() => component(table.slug, name, { dir: root }));
|
|
1046
|
-
return `${out}\n\nIt is real code in this project now - import it and extend it rather than writing your own
|
|
1106
|
+
return fromContract(`${out}\n\nIt is real code in this project now - import it and extend it rather than writing your own.`);
|
|
1047
1107
|
}
|
|
1048
1108
|
// ── The protocol ─────────────────────────────────────────────────────────────
|
|
1109
|
+
/**
|
|
1110
|
+
* AS FERRAMENTAS QUE FALAM DE UM COMPONENTE PELO NOME - as únicas cujo alvo é
|
|
1111
|
+
* um slug de receita, e por isso as únicas que gravam o alvo.
|
|
1112
|
+
*
|
|
1113
|
+
* Um valor como `#2563eb` (o alvo de `find_token`) não é nome de componente e
|
|
1114
|
+
* seria recusado pela rota, que valida a coluna como slug. Então o alvo só
|
|
1115
|
+
* viaja quando é um: para o resto, o eixo da procedência é a própria
|
|
1116
|
+
* ferramenta.
|
|
1117
|
+
*/
|
|
1118
|
+
const BY_COMPONENT = {
|
|
1119
|
+
describe_component: "describe",
|
|
1120
|
+
add_component: "add",
|
|
1121
|
+
};
|
|
1122
|
+
/**
|
|
1123
|
+
* O ÚNICO PONTO DE INSTRUMENTAÇÃO - e é isto que o dono pediu (18/08): *"vira
|
|
1124
|
+
* comportamento padrão e não depende de disciplina futura."*
|
|
1125
|
+
*
|
|
1126
|
+
* A ferramenta responde, a chamada é registrada com a procedência daquela
|
|
1127
|
+
* resposta, e a linha `Source:` é colada ao texto a partir do MESMO dado que
|
|
1128
|
+
* foi gravado. Não há como o número e a frase discordarem, porque não há duas
|
|
1129
|
+
* fontes.
|
|
1130
|
+
*
|
|
1131
|
+
* Uma ferramenta nova entra no switch e passa a contar sem tocar nesta função.
|
|
1132
|
+
*/
|
|
1049
1133
|
async function callTool(root, name, args,
|
|
1050
|
-
/** A versão que reporta o estado do repo no ping - ver `
|
|
1134
|
+
/** A versão que reporta o estado do repo no ping - ver `repoStateOf`. */
|
|
1051
1135
|
cli) {
|
|
1136
|
+
const answer = await runTool(root, name, args);
|
|
1137
|
+
const gesture = BY_COMPONENT[name];
|
|
1138
|
+
const target = gesture
|
|
1139
|
+
? String(args.name ?? "")
|
|
1140
|
+
.trim()
|
|
1141
|
+
.toLowerCase()
|
|
1142
|
+
: "";
|
|
1143
|
+
recordAgentCall(root, {
|
|
1144
|
+
tool: name,
|
|
1145
|
+
source: answer.source,
|
|
1146
|
+
...(gesture ? { action: gesture } : {}),
|
|
1147
|
+
...(target ? { component: target } : {}),
|
|
1148
|
+
}, cli);
|
|
1149
|
+
return text(withSource(answer), answer.isError);
|
|
1150
|
+
}
|
|
1151
|
+
/**
|
|
1152
|
+
* O SWITCH PURO - ele resolve a ferramenta e devolve a resposta COM a
|
|
1153
|
+
* procedência. Nenhuma linha dele conta nada: quem instrumenta é `callTool`,
|
|
1154
|
+
* uma vez, para todas.
|
|
1155
|
+
*/
|
|
1156
|
+
async function runTool(root, name, args) {
|
|
1052
1157
|
switch (name) {
|
|
1053
1158
|
case "check_file":
|
|
1054
|
-
return
|
|
1159
|
+
return checkFile(root, String(args.path ?? "."));
|
|
1055
1160
|
case "find_token":
|
|
1056
|
-
return
|
|
1161
|
+
return findToken(root, String(args.value ?? ""));
|
|
1057
1162
|
case "system_doctrine":
|
|
1058
|
-
return
|
|
1163
|
+
return fromContract(await systemDoctrine(root));
|
|
1059
1164
|
case "list_components":
|
|
1060
|
-
return
|
|
1165
|
+
return fromContract(await listComponents(root));
|
|
1061
1166
|
case "describe_component":
|
|
1062
|
-
|
|
1063
|
-
return text(await describeComponent(root, String(args.name ?? "")));
|
|
1167
|
+
return describeComponent(root, String(args.name ?? ""));
|
|
1064
1168
|
case "add_component":
|
|
1065
|
-
|
|
1066
|
-
return text(await addComponent(root, String(args.name ?? "")));
|
|
1169
|
+
return addComponent(root, String(args.name ?? ""));
|
|
1067
1170
|
case "playbook": {
|
|
1068
1171
|
const skill = String(args?.skill ?? "");
|
|
1069
1172
|
const section = args?.section ? String(args.section) : undefined;
|
|
1070
|
-
return
|
|
1173
|
+
return fromContract(await playbook(skill, section));
|
|
1071
1174
|
}
|
|
1072
1175
|
case "recipe_vocabulary":
|
|
1073
|
-
return
|
|
1176
|
+
return fromContract(await recipeVocabulary());
|
|
1074
1177
|
case "validate_recipe": {
|
|
1075
1178
|
// Same resolution as the batch: an `at` the validator cannot see reads as
|
|
1076
1179
|
// an empty node and earns a false floor verdict (dono, 01/08).
|
|
1077
1180
|
const [only] = await resolveAtNodes(root, [
|
|
1078
1181
|
{ name: String(args.name ?? "this component"), recipe: args.recipe },
|
|
1079
1182
|
]);
|
|
1080
|
-
return
|
|
1183
|
+
return fromContract(await validateRecipe(String(args.name ?? "this component"), only?.recipe ?? args.recipe));
|
|
1081
1184
|
}
|
|
1082
1185
|
case "validate_recipes":
|
|
1083
|
-
return
|
|
1186
|
+
return fromContract(await validateRecipes(root, Array.isArray(args.recipes)
|
|
1084
1187
|
? args.recipes
|
|
1085
1188
|
: []));
|
|
1086
1189
|
case "request_component": {
|
|
@@ -1091,7 +1194,7 @@ cli) {
|
|
|
1091
1194
|
considered: args.considered ? String(args.considered) : undefined,
|
|
1092
1195
|
file: args.file ? String(args.file) : undefined,
|
|
1093
1196
|
});
|
|
1094
|
-
return
|
|
1197
|
+
return fromContract(`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.`);
|
|
1095
1198
|
}
|
|
1096
1199
|
case "refresh_system": {
|
|
1097
1200
|
/**
|
|
@@ -1111,10 +1214,10 @@ cli) {
|
|
|
1111
1214
|
*/
|
|
1112
1215
|
const slug = await installedSlug(root);
|
|
1113
1216
|
if (!slug)
|
|
1114
|
-
return
|
|
1217
|
+
return fromContract("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.");
|
|
1115
1218
|
const token = await readToken();
|
|
1116
1219
|
if (!token)
|
|
1117
|
-
return
|
|
1220
|
+
return fromContract("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.");
|
|
1118
1221
|
const { out } = await capturing(() => remeasure({
|
|
1119
1222
|
root,
|
|
1120
1223
|
base: resolveRegistry(),
|
|
@@ -1137,7 +1240,7 @@ cli) {
|
|
|
1137
1240
|
const behind = pinned && pinned !== VERSION ? pinned : null;
|
|
1138
1241
|
const said = out.trim() ||
|
|
1139
1242
|
"Nothing was reported by the refresh - which means it found nothing to measure.";
|
|
1140
|
-
return
|
|
1243
|
+
return fromContract(behind
|
|
1141
1244
|
? `${said}\n\nUPDATE THE DESIGN SYSTEM BEFORE CONTINUING.\nThe check that runs after your writes is pinned to ${behind} and these tools are ${VERSION}.\n\n npx synthesisui@latest connect\n\nAsk the person, run it for them, and then say this: close and reopen the editor. The hook and the tools only rewire on a new session, so nothing you write before that is checked by the reader you are holding.`
|
|
1142
1245
|
: said);
|
|
1143
1246
|
}
|
|
@@ -1159,7 +1262,7 @@ cli) {
|
|
|
1159
1262
|
file: args.file ? String(args.file) : undefined,
|
|
1160
1263
|
...(area ? { area } : null),
|
|
1161
1264
|
});
|
|
1162
|
-
return
|
|
1265
|
+
return fromContract(area
|
|
1163
1266
|
? `Filed as ${r.id} and routed to the synthesisui platform team - the system's contract already promises this and the shipped css does not deliver it. No one needs to act: it closes itself when an update lands. Keep the quiet base meanwhile.`
|
|
1164
1267
|
: `Filed as ${r.id}. Do not add the token yourself - the request shows up in \`synthesisui doctor\` for a person to decide.`);
|
|
1165
1268
|
}
|
|
@@ -1179,10 +1282,14 @@ cli) {
|
|
|
1179
1282
|
considered: args.considered ? String(args.considered) : undefined,
|
|
1180
1283
|
file: args.file ? String(args.file) : undefined,
|
|
1181
1284
|
});
|
|
1182
|
-
return
|
|
1285
|
+
return fromContract(`Filed as ${r.id}. Do not adopt the convention as if it were a rule - it shows up in \`synthesisui doctor\` and travels to the system's queue, for the owner to make it a rule or decline it.`);
|
|
1183
1286
|
}
|
|
1184
1287
|
default:
|
|
1185
|
-
return
|
|
1288
|
+
return {
|
|
1289
|
+
body: `No tool named ${name}.`,
|
|
1290
|
+
source: { kind: "none", reason: "tool", detail: "unknown tool" },
|
|
1291
|
+
isError: true,
|
|
1292
|
+
};
|
|
1186
1293
|
}
|
|
1187
1294
|
}
|
|
1188
1295
|
/**
|
package/package.json
CHANGED