synthesisui 0.16.264 → 0.16.266

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/claude-md.js CHANGED
@@ -398,6 +398,32 @@ here: a newer reader sees styles the old one could not.`
398
398
  * servidor instala o hook, e o registro é pré-condição para este texto existir. Ensinar a instalar
399
399
  * uma capacidade que foi pré-requisito do próprio arquivo é redundância por construção.
400
400
  */
401
+ /**
402
+ * O RECALL SÓ É PROMETIDO ONDE ELE TEM ONDE CHEGAR - e este era um GAP de jornada.
403
+ *
404
+ * `recallAvailable` responde "o servidor MCP está registrado numa versão que serve `recall`", e essa
405
+ * é a pergunta certa sobre CAPACIDADE. Ela não é a pergunta sobre ALCANCE: `memoryReach` no servidor
406
+ * exige `installedSlug`, que ignora locks `adopted` porque um sistema adotado é lido do repositório e
407
+ * não tem linha de dashboard para a memória pertencer.
408
+ *
409
+ * Sem esta condição, um projeto só-adotado recebia um bootstrap dizendo *"what this project already
410
+ * DECIDED is `recall`"* e toda chamada respondia "no system installed here that belongs to a
411
+ * dashboard row" - o bootstrap prometendo uma ferramenta que não podia funcionar ali. As outras do
412
+ * bloco reduzido continuam valendo, porque `system_doctrine` e `find_token` leem arquivo local.
413
+ *
414
+ * Achado seguindo a jornada, não lendo o módulo: os dois lados estavam certos sobre a própria
415
+ * pergunta e ninguém comparava as duas.
416
+ */
417
+ const memoryReachable = recall.available && !onlyAdopted;
418
+ const memoryLine = memoryReachable
419
+ ? `
420
+
421
+ **What this project already DECIDED is \`recall\`.** Call it when you begin work on an indexed
422
+ subject, or when someone says "continue" and you need the current work-state. For the same subject
423
+ and task, call it once - an empty result means there is nothing more to retrieve, so keep working. It
424
+ tells you what was decided and why it matters; **the file on disk is still the authority for what is
425
+ written now** - read the code before you edit it, never a remembered signature.`
426
+ : "";
401
427
  const rule = recall.available
402
428
  ? onlyAdopted
403
429
  ? `**These are true without asking anyone:** use the project's OWN custom properties, exactly as
@@ -406,13 +432,7 @@ new token invented in silence - say so instead, because a new token is a decisio
406
432
  is no component index for an adopted system: the tokens ARE the contract.
407
433
 
408
434
  **What the vocabulary IS is not written here.** Fetch only the piece the task requires:
409
- \`system_doctrine\` for the rules and the voice, \`find_token\` for a value you are about to write.
410
-
411
- **What this project already DECIDED is \`recall\`.** Call it when you begin work on a subject this
412
- system names, or when someone says "continue" and you need the current work-state. For the same
413
- subject and task, call it once - an empty result means there is nothing more to retrieve, so keep
414
- working. It tells you what was decided and why it matters; **the file on disk is still the authority
415
- for what is written now** - read the code before you edit it, never a remembered signature.${selfCheck}`
435
+ \`system_doctrine\` for the rules and the voice, \`find_token\` for a value you are about to write.${memoryLine}${selfCheck}`
416
436
  : `**These are true without asking anyone:**
417
437
 
418
438
  - Semantic tokens only - \`var(--ds-color-semantic-*)\`, \`--ds-spacing-*\`. No raw values outside this
@@ -435,13 +455,7 @@ If nothing covers it, say which indexed entry you considered and why it did not
435
455
 
436
456
  describe_component its parts, variants, states and the tokens it already uses
437
457
  system_doctrine the rules and the voice of this system
438
- playbook the families, and what each one requires before it can be drawn
439
-
440
- **What this project already DECIDED is \`recall\`.** Call it when you begin work on an indexed
441
- subject, or when someone says "continue" and you need the current work-state. For the same subject
442
- and task, call it once - an empty result means there is nothing more to retrieve, so keep working. It
443
- tells you what was decided and why it matters; **the file on disk is still the authority for what is
444
- written now** - read the code before you edit it, never a remembered signature.${selfCheck}`
458
+ playbook the families, and what each one requires before it can be drawn${memoryLine}${selfCheck}`
445
459
  : onlyAdopted
446
460
  ? `**When creating or editing components, read the system's GUIDE.md and follow it:** use the
447
461
  project's OWN custom properties, exactly as the guide lists them. Do not write raw colours,
@@ -11,6 +11,7 @@ import { add } from "./add.js";
11
11
  import { reportWhatIsLeft } from "./align.js";
12
12
  import { ci } from "./ci.js";
13
13
  import { MCP_TOOL_COUNT } from "./mcp.js";
14
+ import { installedSlugs } from "./sync.js";
14
15
  /**
15
16
  * `synthesisui connect` - put the three layers where they actually run.
16
17
  *
@@ -213,6 +214,27 @@ export async function connect(opts) {
213
214
  // after them, not before.
214
215
  const contract = await syncClaudeMd(root);
215
216
  console.log(section("Connected"));
217
+ /**
218
+ * E O QUE FALTA, DITO EM VOZ ALTA - porque este comando estava mentindo por omissão.
219
+ *
220
+ * `connect` liga a conta e registra o servidor MCP. Ele NÃO instala sistema, e sem lock o bloco
221
+ * gerenciado do `CLAUDE.md` sai VAZIO: o agente não recebe índice, não recebe invariante, e as duas
222
+ * ferramentas de memória recusam com "no system installed here". A saída dizia apenas "Connected",
223
+ * que é verdade e é inútil.
224
+ *
225
+ * Custou tempo de gente de verdade: 20/08, `codelevel-monorepo`. Rodou `connect`, conferiu o
226
+ * `CLAUDE.md`, achou o bloco vazio, e não havia nada dizendo que faltava um comando. Lacuna
227
+ * silenciosa é o que faz um produto correto parecer quebrado.
228
+ *
229
+ * E SÃO DOIS CAMINHOS, não um. A primeira versão desta mensagem mandava `add <slug>` e só - o que
230
+ * desorienta justamente quem acabou de rodar `connect` num projeto que AINDA NÃO TEM sistema, que é
231
+ * a primeira corrida e o caso mais comum. Para essa pessoa o passo seguinte é a skill que este mesmo
232
+ * comando acabou de instalar, e não um comando de terminal. Quem já tem sistema na plataforma é o
233
+ * outro caso, e ele também é dito.
234
+ */
235
+ const anyInstalled = (await installedSlugs(root).catch(() => [])).length > 0;
236
+ if (!anyInstalled)
237
+ console.log(body("· no design system installed here yet - the agent has no index, and memory has nothing to belong to.\n This repo has none yet: ask the agent for `/sui-init`, or `/sui-import-ds` to turn this project into one.\n You already have one: `synthesisui list` shows them, `synthesisui add <slug>` brings it in."));
216
238
  /**
217
239
  * O QUE ESTE CLI REESCREVEU NA PASTA DO SISTEMA - dito primeiro, porque é o que a pessoa não sabia
218
240
  * que estava devendo. Ela rodou `connect` para atualizar a fiação; os arquivos do install estarem
@@ -9,8 +9,10 @@ import { readEvents } from "../doctor/ledger.js";
9
9
  import { fileRequest } from "../doctor/requests.js";
10
10
  import { diagnose, nameToWrite, scanSource } from "../doctor/scan.js";
11
11
  import { nearestToken, normalizeValue, tokenFor } from "../doctor/tokens.js";
12
- import { MEMORY_TOOLS } from "../memory/tools.js";
12
+ import { fromCensus } from "../memory/observation.js";
13
+ import { handleRecall, handleRemember, MEMORY_TOOLS } from "../memory/tools.js";
13
14
  import { repoStateOf } from "../repo-state.js";
15
+ import { detectStack } from "../stack.js";
14
16
  import { component } from "./component.js";
15
17
  import { loadSystem, walkAll } from "./doctor.js";
16
18
  import { installedSlug, remeasure } from "./sync.js";
@@ -1247,6 +1249,39 @@ cli) {
1247
1249
  * procedência. Nenhuma linha dele conta nada: quem instrumenta é `callTool`,
1248
1250
  * uma vez, para todas.
1249
1251
  */
1252
+ /**
1253
+ * O ELO QUE FALTAVA - `remember` e `recall` eram ANUNCIADOS e não despachados.
1254
+ *
1255
+ * As duas estavam na lista de `TOOLS` desde o primeiro dia, importadas no topo deste arquivo, com 152
1256
+ * testes verdes atrás delas - e sem `case` no `runTool`. Um agente que as chamasse recebia
1257
+ * `"unknown tool"` com `isError`. O `reachable.spec` não pegou porque IMPORTAR já conta como
1258
+ * alcançável, e o próprio cabeçalho dele diz o que ele não cobre: *"nothing about whether the product
1259
+ * calls it, and that gap is exactly where the work kept"*.
1260
+ *
1261
+ * Achado seguindo a jornada pelo ponto de entrada real, que é a lei 3. Um spec verde prova que a
1262
+ * função roda; não prova que o produto a chama.
1263
+ */
1264
+ async function memoryReach(root) {
1265
+ const slug = await installedSlug(root);
1266
+ if (!slug)
1267
+ return "No system installed here that belongs to a dashboard row, so there is nothing to remember about. `synthesisui add <slug>` installs one.";
1268
+ const token = await readToken();
1269
+ if (!token)
1270
+ return "Not signed in, so memory cannot be reached. A person has to run `synthesisui login` once in the terminal - it opens a browser, and I cannot complete it for them.";
1271
+ return { slug, token, base: resolveRegistry() };
1272
+ }
1273
+ /** O censo já em disco, como Observation - o mesmo insumo que o `sync` mede. */
1274
+ async function localObservation(root) {
1275
+ const raw = await readFile(join(root, "_synthesisui", "census.json"), "utf8").catch(() => null);
1276
+ if (!raw)
1277
+ return null;
1278
+ try {
1279
+ return fromCensus(JSON.parse(raw));
1280
+ }
1281
+ catch {
1282
+ return null;
1283
+ }
1284
+ }
1250
1285
  async function runTool(root, name, args) {
1251
1286
  switch (name) {
1252
1287
  case "check_file":
@@ -1406,6 +1441,68 @@ async function runTool(root, name, args) {
1406
1441
  ? `${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.`
1407
1442
  : said);
1408
1443
  }
1444
+ case "remember": {
1445
+ const reach = await memoryReach(root);
1446
+ if (typeof reach === "string")
1447
+ return fromContract(reach);
1448
+ /**
1449
+ * A OBSERVAÇÃO É EXIGIDA para work-state, e só para ele: a condição de saída tem que ser
1450
+ * verificável contra ESTE repositório, senão a memória nasce impossível de fechar.
1451
+ */
1452
+ const obs = await localObservation(root);
1453
+ if (!obs)
1454
+ return fromContract("No `_synthesisui/census.json` here, so nothing can be checked against this repository. Run `npx synthesisui sync` first - a work state whose condition nobody can measure is memory that starts lying.");
1455
+ return handleRemember(args, obs, async (payload) => {
1456
+ const res = await fetch(`${reach.base}/api/memory/${reach.slug}/remember`, {
1457
+ method: "POST",
1458
+ headers: {
1459
+ "content-type": "application/json",
1460
+ Authorization: `Bearer ${reach.token}`,
1461
+ },
1462
+ body: JSON.stringify(payload),
1463
+ }).catch(() => null);
1464
+ if (!res)
1465
+ return {
1466
+ accepted: false,
1467
+ reason: "unknown_field",
1468
+ details: "the platform could not be reached",
1469
+ };
1470
+ const said = (await res.json().catch(() => null));
1471
+ return (said ?? {
1472
+ accepted: false,
1473
+ reason: "unknown_field",
1474
+ details: `the platform answered ${res.status} with nothing readable`,
1475
+ });
1476
+ });
1477
+ }
1478
+ case "recall": {
1479
+ const reach = await memoryReach(root);
1480
+ if (typeof reach === "string")
1481
+ return fromContract(reach);
1482
+ /** A stack sai do repositório, nunca do agente: ela FILTRA, e um palpite dele cortaria regra boa. */
1483
+ const stack = await detectStack(root);
1484
+ return handleRecall(args, stack, async (request) => {
1485
+ const res = await fetch(`${reach.base}/api/memory/${reach.slug}/recall`, {
1486
+ method: "POST",
1487
+ headers: {
1488
+ "content-type": "application/json",
1489
+ Authorization: `Bearer ${reach.token}`,
1490
+ },
1491
+ body: JSON.stringify(request),
1492
+ }).catch(() => null);
1493
+ const empty = {
1494
+ carried: [],
1495
+ groups: [],
1496
+ omitted: [],
1497
+ because: res
1498
+ ? `the platform answered ${res.status}`
1499
+ : "the platform could not be reached",
1500
+ };
1501
+ if (!res?.ok)
1502
+ return empty;
1503
+ return ((await res.json().catch(() => null)) ?? empty);
1504
+ });
1505
+ }
1409
1506
  case "request_token": {
1410
1507
  const name = String(args.name ?? "");
1411
1508
  // TRIAGE AT FILING: if the system's own contract already promises this
@@ -115,7 +115,7 @@
115
115
  * O índice de componentes é BYTE-IDÊNTICO nos dois caminhos - mesma fonte canônica, mesmos nomes,
116
116
  * mesma quantidade (`claude-md.f0.spec.ts`). O corte troca uma frase, nunca o inventário.
117
117
  */
118
- export const MATERIALISER_SINCE = "0.16.264";
118
+ export const MATERIALISER_SINCE = "0.16.265";
119
119
  /**
120
120
  * A ÚLTIMA VERSÃO EM QUE O QUE O HOOK RODA MUDOU.
121
121
  *
@@ -62,7 +62,7 @@ export const MEMORY_TOOLS = [
62
62
  },
63
63
  subject: {
64
64
  type: "string",
65
- description: "What this is ABOUT - a token, a component, a library. Required for rationale.",
65
+ description: "decision and rationale only, and required for rationale. What this is ABOUT - a token, a component, a library. A work-state says it inside `exit.subject` instead, so sending it here is refused: a field nobody reads would look accepted.",
66
66
  },
67
67
  exit: {
68
68
  type: "object",
@@ -254,16 +254,62 @@ export async function handleRecall(args, stack, ask) {
254
254
  ...(args.task ? { task: args.task } : {}),
255
255
  budget,
256
256
  });
257
+ /**
258
+ * O TRABALHO EM ABERTO VEM PRIMEIRO, e é o que responde "onde eu estava".
259
+ *
260
+ * Decisões dizem como construir; o work-state diz o que estava sendo construído. Quando alguém diz
261
+ * "continue", a segunda pergunta é a única que importa - e ela vinha da plataforma e era descartada
262
+ * aqui, porque o tipo não tinha o campo.
263
+ *
264
+ * Cada linha carrega a AUTORIDADE ao lado: um estado que ninguém mediu é hipótese, e um medido há
265
+ * seis dias é a última coisa que se sabe. Sem o rótulo o agente trata os dois como fato corrente.
266
+ */
267
+ const open = (out.work ?? []).map((w) => {
268
+ const moved = w.remaining !== null
269
+ ? ` ${w.remaining} left${w.moved ? ` · ${w.moved}` : ""}`
270
+ : w.moved
271
+ ? ` ${w.moved}`
272
+ : "";
273
+ return `- ${w.goal} [${w.status} · ${w.says}]${moved}`;
274
+ });
275
+ const work = open.length > 0
276
+ ? `Open work, yours:\n${open.join("\n")}\n\nWhat this project decided:\n`
277
+ : "";
257
278
  if (out.carried.length === 0)
258
- return {
259
- body: `Nothing recorded that applies here. ${out.because}`,
260
- source: { kind: "none", reason: "system", detail: "nothing applies" },
261
- };
262
- const lines = out.carried.map((m) => `- ${m.text}${m.applies.length > 0 ? ` [${m.applies.join(", ")}]` : ""} (${m.rung})`);
279
+ return open.length > 0
280
+ ? answer(`Open work, yours:\n${open.join("\n")}\n\nNo decisions recorded that apply here. ${out.because}`, "memory:recall")
281
+ : {
282
+ body: `Nothing recorded that applies here. ${out.because}`,
283
+ source: { kind: "none", reason: "system", detail: "nothing applies" },
284
+ };
285
+ /**
286
+ * E O FRESCOR ATRAVESSA ATÉ AQUI - senão ele é um campo que ninguém lê.
287
+ *
288
+ * `memoriesOf` deriva `evidence` e `confirmedAt` do sinal `survived` mais recente, o único que
289
+ * atesta o CÓDIGO. Eles cruzavam o JSON, existiam no tipo, e esta linha renderizava só o degrau: o
290
+ * agente continuava sem distinguir "confirmada hoje" de "escrita uma vez há quarenta dias", que é
291
+ * exatamente o problema que o frescor foi criado para resolver. A mesma classe do `work`, e eu a
292
+ * reintroduzi ao consertar a primeira.
293
+ *
294
+ * O degrau responde quanto acreditamos; o frescor responde se ainda é verdade. As duas coisas vão
295
+ * juntas ou nenhuma das duas significa algo.
296
+ */
297
+ const fresh = (m) => {
298
+ if (m.type === "rationale")
299
+ return "stated by a person, never measured";
300
+ if (m.evidence === "measured" && m.confirmedAt)
301
+ return `your code backed this on ${m.confirmedAt.slice(0, 10)}`;
302
+ if (m.evidence === "declared")
303
+ return "declared, not measured yet";
304
+ if (m.evidence === "imported")
305
+ return "read from your code at import";
306
+ return "no measurement on record";
307
+ };
308
+ const lines = out.carried.map((m) => `- ${m.text}${m.applies.length > 0 ? ` [${m.applies.join(", ")}]` : ""} (${m.rung} · ${fresh(m)})`);
263
309
  const grouped = out.groups.length > 0
264
310
  ? `\n\nSame subject, so they travel together: ${out.groups
265
311
  .map((g) => g.members.join(" + "))
266
312
  .join(" · ")}`
267
313
  : "";
268
- return answer(`${lines.join("\n")}${grouped}\n\n${out.because}`, "memory:recall");
314
+ return answer(`${work}${lines.join("\n")}${grouped}\n\n${out.because}`, "memory:recall");
269
315
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.264",
3
+ "version": "0.16.266",
4
4
  "description": "Bring SynthesisUI design systems into any project - tokens, typed components, whole pages and an agent-ready CLAUDE.md manifest.",
5
5
  "type": "module",
6
6
  "bin": {