synthesisui 0.16.176 → 0.16.178

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
@@ -412,6 +412,7 @@ export async function syncClaudeMd(projectRoot) {
412
412
  const installed = await readInstalled(projectRoot);
413
413
  const region = await renderRegion(projectRoot, installed);
414
414
  let createdAny = false;
415
+ const changed = [];
415
416
  for (const home of HOMES) {
416
417
  const path = join(projectRoot, home.path);
417
418
  let existing = null;
@@ -435,6 +436,7 @@ export async function syncClaudeMd(projectRoot) {
435
436
  await mkdir(dirname(path), { recursive: true }).catch(() => { });
436
437
  await writeFile(path, `${home.frontmatter ?? ""}${region}\n`, "utf8");
437
438
  createdAny = true;
439
+ changed.push(home.path);
438
440
  continue;
439
441
  }
440
442
  const startIdx = existing.indexOf(START);
@@ -450,10 +452,12 @@ export async function syncClaudeMd(projectRoot) {
450
452
  const sep = existing.endsWith("\n") ? "\n" : "\n\n";
451
453
  next = `${existing}${sep}${region}\n`;
452
454
  }
453
- if (next !== existing)
455
+ if (next !== existing) {
454
456
  await writeFile(path, next, "utf8");
457
+ changed.push(home.path);
458
+ }
455
459
  }
456
- return { created: createdAny, count: installed.length };
460
+ return { created: createdAny, count: installed.length, changed };
457
461
  }
458
462
  /**
459
463
  * Onde o bloco JÁ está, para o `connect` poder dizer isso em vez de ficar calado.
@@ -39,6 +39,8 @@ async function writeGovernanceIgnore(projectRoot) {
39
39
  "# both are rewritten in full on every run and would conflict on every merge.\n" +
40
40
  "census.json\n" +
41
41
  "ledger.jsonl\n" +
42
+ /** O cursor de envio descreve ESTE clone, não o repositório - ver `markSent`. */
43
+ ".ledger-sent\n" +
42
44
  "not-expressed.md\n", "utf8");
43
45
  }
44
46
  /**
@@ -2,7 +2,7 @@ import { readdir, readFile } from "node:fs/promises";
2
2
  import { homedir } from "node:os";
3
3
  import { join, resolve } from "node:path";
4
4
  import { readToken, resolveRegistry } from "../config.js";
5
- import { readEvents } from "../doctor/ledger.js";
5
+ import { unsentEvents } from "../doctor/ledger.js";
6
6
  import { measuredScope } from "../measured-scope.js";
7
7
  import { READER } from "../reader-version.js";
8
8
  /**
@@ -124,7 +124,12 @@ opts = {}) {
124
124
  */
125
125
  run: "npx synthesisui sync",
126
126
  });
127
- const events = await readEvents(root).catch(() => []);
127
+ /**
128
+ * SÓ O QUE NÃO SUBIU - ver `unsentEvents`. Isto contava o arquivo inteiro, e como o ledger é
129
+ * append-only e o `sync` manda tudo (a plataforma deduplica), a linha nunca mais saía da tela e o
130
+ * número só crescia.
131
+ */
132
+ const events = await unsentEvents(root).catch(() => []);
128
133
  if (events.length > 0)
129
134
  out.push({
130
135
  says: `${events.length} checked write${events.length === 1 ? "" : "s"} recorded here and not sent - the platform is scoring this repo on older evidence.`,
@@ -173,7 +173,7 @@ export async function connect(opts) {
173
173
  const wired = await wireAgent(root, opts.version, want);
174
174
  // The block reads the settings we just wrote, so it must be regenerated
175
175
  // after them, not before.
176
- await syncClaudeMd(root);
176
+ const contract = await syncClaudeMd(root);
177
177
  console.log(section("Connected"));
178
178
  /**
179
179
  * O QUE ESTE CLI REESCREVEU NA PASTA DO SISTEMA - dito primeiro, porque é o que a pessoa não sabia
@@ -231,8 +231,18 @@ export async function connect(opts) {
231
231
  * qual ferramenta ela usa. Dizer os nomes é o que faz um time descobrir que a governança já chegou
232
232
  * no editor dele: até 05/08 ela só alcançava quem usa Claude Code, e ninguém tinha como saber.
233
233
  */
234
- for (const home of await blockHomes(root))
235
- console.log(body(`✓ ${home.padEnd(22)} ${home === "CLAUDE.md" ? "rewritten for what is installed" : "the same rules, where this agent reads them"}`));
234
+ /**
235
+ * `✓` PARA O QUE MUDOU - ver `syncClaudeMd.changed`. Esta linha era a única do bloco que
236
+ * marcava mudança sempre, e um `connect` seguido dizia "rewritten" sobre um arquivo intocado.
237
+ */
238
+ for (const home of await blockHomes(root)) {
239
+ const moved = contract.changed.includes(home);
240
+ console.log(body(`${moved ? "✓" : "·"} ${home.padEnd(22)} ${moved
241
+ ? home === "CLAUDE.md"
242
+ ? "rewritten for what is installed"
243
+ : "the same rules, where this agent reads them"
244
+ : "already says what is installed"}`));
245
+ }
236
246
  /**
237
247
  * The fourth layer, and the one that had no installer at all: the import
238
248
  * skill existed only in our own repo, so the only way to get it was to clone
@@ -1,7 +1,7 @@
1
1
  import { readdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { join, resolve } from "node:path";
3
3
  import { readToken, resolveRegistry } from "../config.js";
4
- import { readEvents } from "../doctor/ledger.js";
4
+ import { markSent, readEvents } from "../doctor/ledger.js";
5
5
  import { checkableName, closeRequest, readRequests, verifyAndCloseRequests, } from "../doctor/requests.js";
6
6
  import { measuredScope, rememberScope } from "../measured-scope.js";
7
7
  import { body, paint, section, snippet } from "../output.js";
@@ -114,6 +114,11 @@ export async function sync(opts) {
114
114
  console.log(body(err.message ?? `The registry answered ${res.status}.`));
115
115
  return;
116
116
  }
117
+ /**
118
+ * O QUE SUBIU, ANOTADO - senão o `align` continua pedindo o envio do que acabou de ser enviado.
119
+ * Depois da resposta OK, e nunca antes: marcar um envio que falhou perderia os eventos de vez.
120
+ */
121
+ await markSent(root, events);
117
122
  const out = (await res.json());
118
123
  console.log(section("Sync"));
119
124
  console.log(body(`${out.eventsReceived} checks sent, ${out.eventsNew} new. ${out.requestsNow} open request${out.requestsNow === 1 ? "" : "s"}.`));
@@ -46,6 +46,55 @@ export async function appendEvent(root, event) {
46
46
  // is a ledger that gets the hook uninstalled.
47
47
  }
48
48
  }
49
+ /**
50
+ * ATÉ ONDE ESTE REPO JÁ MANDOU - e a ausência disto fazia o `align` mentir para sempre.
51
+ *
52
+ * O ledger é APPEND-ONLY e o `sync` manda o arquivo inteiro; a plataforma deduplica (em 07/08 uma
53
+ * rodada reportou "6 checks sent, 1 new"). Só que o `align` contava TODAS as linhas e dizia "6
54
+ * checked writes recorded here and not sent" - sobre cinco que tinham subido minutos antes. O número
55
+ * só crescia, então aquela linha nunca mais sairia da tela, e um aviso permanente é um aviso que a
56
+ * pessoa aprende a pular (dono, 07/08: *"quer apostar que você vai achar algo errado?"*).
57
+ *
58
+ * A CONTAGEM E A HORA, as duas: a contagem responde sozinha enquanto o arquivo só cresce, e a hora
59
+ * dá como perceber que ele foi trocado por outro - aí a contagem antiga não vale mais nada.
60
+ */
61
+ const SENT_FILE = ".ledger-sent";
62
+ const sentPath = (root) => join(root, "_synthesisui", SENT_FILE);
63
+ export async function readSent(root) {
64
+ const raw = await readFile(sentPath(root), "utf8").catch(() => "");
65
+ if (!raw)
66
+ return { count: 0 };
67
+ try {
68
+ const v = JSON.parse(raw);
69
+ return {
70
+ count: typeof v.count === "number" && v.count >= 0 ? v.count : 0,
71
+ ...(typeof v.at === "string" ? { at: v.at } : {}),
72
+ };
73
+ }
74
+ catch {
75
+ return { count: 0 };
76
+ }
77
+ }
78
+ export async function markSent(root, events) {
79
+ const last = events[events.length - 1]?.at;
80
+ await writeFile(sentPath(root), `${JSON.stringify({ count: events.length, ...(last ? { at: last } : {}) })}\n`, "utf8").catch(() => {
81
+ /** O registro nunca pode custar o envio: o pior caso é o aviso reaparecer uma vez. */
82
+ });
83
+ }
84
+ /**
85
+ * Os eventos que este repo ainda não mandou.
86
+ *
87
+ * Se a contagem gravada é maior que o arquivo, alguém o trocou ou apagou - e aí a marca não descreve
88
+ * mais este arquivo. Contar tudo de novo é a leitura honesta: um envio a mais custa nada, e a
89
+ * plataforma deduplica.
90
+ */
91
+ export async function unsentEvents(root) {
92
+ const all = await readEvents(root);
93
+ const { count } = await readSent(root);
94
+ if (count <= 0 || count > all.length)
95
+ return all;
96
+ return all.slice(count);
97
+ }
49
98
  export async function readEvents(root) {
50
99
  const raw = await readFile(ledgerPath(root), "utf8").catch(() => "");
51
100
  if (!raw)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.176",
3
+ "version": "0.16.178",
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": {