synthesisui 0.16.456 → 0.16.458
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/auto-sync.js +45 -4
- package/dist/cn-codegen.js +40 -3
- package/dist/commands/connect.js +2 -2
- package/dist/commands/mcp.js +9 -1
- package/dist/copy/connect.pt-BR.js +2 -2
- package/dist/doctor/scan.js +19 -0
- package/dist/install-marks.js +12 -1
- package/package.json +1 -1
package/dist/auto-sync.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
1
|
+
import { execFile, spawn } from "node:child_process";
|
|
2
2
|
import { readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
/**
|
|
@@ -54,11 +54,50 @@ const lockPath = (root) => join(root, "_synthesisui", ".sync-started");
|
|
|
54
54
|
* `stdio: "ignore"` também é a razão de ele nunca travar: sem terminal, toda pergunta do `sync`
|
|
55
55
|
* (`askToOverwrite`, `askForScope`) responde não sozinha.
|
|
56
56
|
*/
|
|
57
|
+
/**
|
|
58
|
+
* O CENSO SÓ COM A MAIN LIMPA, DEPOIS DE UM COMMIT - jornada "o sistema só muda com commit", E02 (dono,
|
|
59
|
+
* 27/09: *"o censo só sobe com a pasta limpa, depois de um commit"* e *"só na branch principal (main):
|
|
60
|
+
* o sistema acompanha o que foi integrado, e commit em branch de trabalho só manda as checagens"*).
|
|
61
|
+
*
|
|
62
|
+
* Devolve o commit quando o fim do turno pode mandar o censo: a branch é `main`, `git status` não tem
|
|
63
|
+
* nada (nem arquivo modificado, nem não versionado), e o commit não é o do último censo. Qualquer
|
|
64
|
+
* outra coisa - sem git, fora de um repositório, branch de trabalho, pasta suja, git que demora - é
|
|
65
|
+
* `null`, e o fim do turno manda só as checagens.
|
|
66
|
+
*
|
|
67
|
+
* O COMMIT DO ÚLTIMO CENSO MORA NO `.sync-started`: o arquivo já é deste módulo, já está fora do git,
|
|
68
|
+
* e o conteúdo dele era vazio (só a data servia, como trava). Um arquivo novo mudaria o `.gitignore`
|
|
69
|
+
* que o `connect` escreve.
|
|
70
|
+
*
|
|
71
|
+
* A LACUNA DECLARADA: o commit é anotado quando o envio sai, não quando ele termina. Um envio que
|
|
72
|
+
* falhar (sem rede, sessão vencida) deixa aquele commit sem censo até o próximo commit na main - ou
|
|
73
|
+
* até alguém rodar `sync`.
|
|
74
|
+
*/
|
|
75
|
+
const git = (root, args) => new Promise((ok, fail) => {
|
|
76
|
+
execFile("git", args, { cwd: root, timeout: 3000 }, (err, out) => err ? fail(err) : ok(String(out).trim()));
|
|
77
|
+
});
|
|
78
|
+
const lastCensusHead = async (root) => (await readFile(lockPath(root), "utf8").catch(() => "")).trim();
|
|
79
|
+
export async function censusDue(root) {
|
|
80
|
+
try {
|
|
81
|
+
if ((await git(root, ["rev-parse", "--abbrev-ref", "HEAD"])) !== "main")
|
|
82
|
+
return null;
|
|
83
|
+
if ((await git(root, ["status", "--porcelain"])) !== "")
|
|
84
|
+
return null;
|
|
85
|
+
const head = await git(root, ["rev-parse", "HEAD"]);
|
|
86
|
+
if (!head || head === (await lastCensusHead(root)))
|
|
87
|
+
return null;
|
|
88
|
+
return head;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
57
94
|
export async function startSync(root) {
|
|
58
95
|
const last = await stat(lockPath(root)).then((s) => s.mtimeMs, () => 0);
|
|
59
96
|
if (Date.now() - last < SYNC_GAP_MS)
|
|
60
97
|
return false;
|
|
61
|
-
await
|
|
98
|
+
const head = await censusDue(root);
|
|
99
|
+
/** A trava pela data, e o commit do último censo pelo conteúdo - que só muda quando o censo sai. */
|
|
100
|
+
await writeFile(lockPath(root), head ?? (await lastCensusHead(root)), "utf8");
|
|
62
101
|
const entry = process.argv[1];
|
|
63
102
|
if (!entry) {
|
|
64
103
|
await rm(lockPath(root), { force: true });
|
|
@@ -70,9 +109,11 @@ export async function startSync(root) {
|
|
|
70
109
|
* sobem só as checagens (...) nada publica de trabalho em andamento"*). O `sync` inteiro remede o
|
|
71
110
|
* repositório e manda o censo, e a rota do censo chama o Autopilot: um turno pela metade, numa
|
|
72
111
|
* branch de trabalho, podia virar versão publicada (medido no CodeLevel, 27/09 00:49).
|
|
73
|
-
* `--record-only` manda o registro e o estado do repositório, e não remede.
|
|
112
|
+
* `--record-only` manda o registro e o estado do repositório, e não remede. O `sync` inteiro só
|
|
113
|
+
* sai quando `censusDue` diz que a main está limpa num commit novo (E02).
|
|
74
114
|
*/
|
|
75
|
-
const
|
|
115
|
+
const args = head ? ["sync"] : ["sync", "--record-only"];
|
|
116
|
+
const child = spawn(process.execPath, [entry, ...args], {
|
|
76
117
|
cwd: root,
|
|
77
118
|
detached: true,
|
|
78
119
|
stdio: "ignore",
|
package/dist/cn-codegen.js
CHANGED
|
@@ -58,6 +58,14 @@ export function readVocabulary(themeCss) {
|
|
|
58
58
|
fontWeights: weights,
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* The type scale every Tailwind v4 project has, whether the system declares it or not. Oscilla's
|
|
63
|
+
* theme.css declares only `--text-display`, so `cn("text-xs", "text-2xl")` kept both and the
|
|
64
|
+
* stylesheet order decided (refinement round 1, 27/09).
|
|
65
|
+
*/
|
|
66
|
+
const TAILWIND_TEXT_SIZES = [
|
|
67
|
+
"xs", "sm", "base", "lg", "xl", "2xl", "3xl", "4xl", "5xl", "6xl", "7xl", "8xl", "9xl",
|
|
68
|
+
];
|
|
61
69
|
const set = (names) => `new Set([${names.map((n) => JSON.stringify(n)).join(", ")}])`;
|
|
62
70
|
/** The `cn.ts` a project gets, written against ITS system's vocabulary. */
|
|
63
71
|
export function emitCn(slug, vocab) {
|
|
@@ -84,7 +92,7 @@ export function emitCn(slug, vocab) {
|
|
|
84
92
|
|
|
85
93
|
/** This system's own namespaces, so \`text-*\` and \`font-*\` are never a guess. */
|
|
86
94
|
const COLORS = ${set(vocab.colors)};
|
|
87
|
-
const TEXT_SIZES = ${set(vocab.textSizes)};
|
|
95
|
+
const TEXT_SIZES = ${set([...new Set([...TAILWIND_TEXT_SIZES, ...vocab.textSizes])])};
|
|
88
96
|
const FONT_FAMILIES = ${set(vocab.fontFamilies)};
|
|
89
97
|
const FONT_WEIGHTS = ${set(vocab.fontWeights)};
|
|
90
98
|
|
|
@@ -97,8 +105,6 @@ const PREFIXES = [
|
|
|
97
105
|
"rounded-tl", "rounded-tr", "rounded-br", "rounded-bl",
|
|
98
106
|
"rounded-t", "rounded-r", "rounded-b", "rounded-l",
|
|
99
107
|
"rounded",
|
|
100
|
-
"border-t", "border-r", "border-b", "border-l", "border-x", "border-y",
|
|
101
|
-
"border",
|
|
102
108
|
"px", "py", "pt", "pr", "pb", "pl", "ps", "pe", "p",
|
|
103
109
|
"mx", "my", "mt", "mr", "mb", "ml", "ms", "me", "m",
|
|
104
110
|
"gap-x", "gap-y", "gap",
|
|
@@ -124,6 +130,36 @@ const DISPLAYS = new Set([
|
|
|
124
130
|
const FLEX_DIRECTIONS = new Set(["row", "col", "row-reverse", "col-reverse"]);
|
|
125
131
|
const FLEX_WRAPS = new Set(["wrap", "nowrap", "wrap-reverse"]);
|
|
126
132
|
|
|
133
|
+
/**
|
|
134
|
+
* \`border\` heads THREE properties: the width (\`border\`, \`border-2\`), the style
|
|
135
|
+
* (\`border-dashed\`) and the colour (\`border-surface\`). Read as one prefix,
|
|
136
|
+
* \`border border-surface\` kept only the colour and the line vanished - every
|
|
137
|
+
* card, input and badge in the project, in silence (refinement round 1, 27/09).
|
|
138
|
+
*/
|
|
139
|
+
const BORDER_SIDES = new Set(["t", "r", "b", "l", "x", "y", "s", "e"]);
|
|
140
|
+
const BORDER_STYLES = new Set(["solid", "dashed", "dotted", "double", "hidden", "none"]);
|
|
141
|
+
const COLOR_KEYWORDS = new Set(["transparent", "current", "inherit", "black", "white"]);
|
|
142
|
+
|
|
143
|
+
function borderGroup(variants: string, rest: string, cls: string): string {
|
|
144
|
+
const first = rest.split("-")[0];
|
|
145
|
+
const side = BORDER_SIDES.has(first) ? \`-\${first}\` : "";
|
|
146
|
+
const tail = side ? rest.slice(first.length + 1) : rest;
|
|
147
|
+
const isWidth =
|
|
148
|
+
tail === "" ||
|
|
149
|
+
[...tail].every((ch) => ch >= "0" && ch <= "9") ||
|
|
150
|
+
(tail.startsWith("[") && !isArbitraryColor(tail));
|
|
151
|
+
if (isWidth) return \`\${variants}border\${side}-width\`;
|
|
152
|
+
if (BORDER_STYLES.has(tail)) return \`\${variants}border-style\`;
|
|
153
|
+
if (COLORS.has(tail) || COLOR_KEYWORDS.has(tail) || isArbitraryColor(tail)) {
|
|
154
|
+
return \`\${variants}border\${side}-color\`;
|
|
155
|
+
}
|
|
156
|
+
return \`\${variants}\${cls}\`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function isArbitraryColor(v: string): boolean {
|
|
160
|
+
return /^\\[(color:|#|rgba?\\(|hsla?\\(|oklch\\(|oklab\\(|var\\(--color)/.test(v);
|
|
161
|
+
}
|
|
162
|
+
|
|
127
163
|
/**
|
|
128
164
|
* The property this class sets, or the class itself when we cannot tell.
|
|
129
165
|
*
|
|
@@ -169,6 +205,7 @@ function groupOf(cls: string): string {
|
|
|
169
205
|
if (FONT_FAMILIES.has(rest)) return \`\${variants}font-family\`;
|
|
170
206
|
return \`\${variants}\${cls}\`;
|
|
171
207
|
}
|
|
208
|
+
if (head === "border") return borderGroup(variants, rest, cls);
|
|
172
209
|
|
|
173
210
|
for (const p of PREFIXES) {
|
|
174
211
|
if (base === p || base.startsWith(\`\${p}-\`)) return \`\${variants}\${p}\`;
|
package/dist/commands/connect.js
CHANGED
|
@@ -474,7 +474,7 @@ export async function connect(opts) {
|
|
|
474
474
|
if (before === null && now !== null)
|
|
475
475
|
await writeAutoSync(root, now);
|
|
476
476
|
row(before === null, now === true
|
|
477
|
-
? say("✓ what the check records is sent to your dashboard at the end of each turn")
|
|
477
|
+
? say("✓ what the check records is sent to your dashboard at the end of each turn - your system changes only from a commit on a clean main")
|
|
478
478
|
: say("· what the check records stays on this machine - run `npx synthesisui sync` to send it"));
|
|
479
479
|
}
|
|
480
480
|
if (want.hook && wired.hook !== "skipped") {
|
|
@@ -875,7 +875,7 @@ async function askToSendAtTurnEnd() {
|
|
|
875
875
|
const { createInterface } = await import("node:readline/promises");
|
|
876
876
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
877
877
|
try {
|
|
878
|
-
const answer = await rl.question(` ${say("send what the check records to your dashboard at the end of each turn?")} [y/N]: `);
|
|
878
|
+
const answer = await rl.question(` ${say("send what the check records to your dashboard at the end of each turn? Your system itself changes only from a commit on a clean main.")} [y/N]: `);
|
|
879
879
|
return /^(y(es)?|s(im)?)$/i.test(answer.trim());
|
|
880
880
|
}
|
|
881
881
|
catch {
|
package/dist/commands/mcp.js
CHANGED
|
@@ -409,7 +409,15 @@ async function checkFile(root, path) {
|
|
|
409
409
|
for (const f of d.findings.slice(0, 40)) {
|
|
410
410
|
out.push(nameToWrite(f)
|
|
411
411
|
? ` ${f.file}:${f.line} ${f.literal} - this repo calls it ${nameToWrite(f)}`
|
|
412
|
-
:
|
|
412
|
+
: /**
|
|
413
|
+
* O SISTEMA SEGURA, O PROJETO AINDA NÃO NOMEIA - rodada 1 do refinamento, 27/09. Dizer "no
|
|
414
|
+
* token holds this value" sobre o `160ms` que o sistema declara fez o leigo concluir que o
|
|
415
|
+
* sistema discordava dele mesmo. O nome continua fora da frase: a grafia `--ds-*` é nossa
|
|
416
|
+
* (`INV-GERAL-13`), e batizar o valor no código dele é decisão dele.
|
|
417
|
+
*/
|
|
418
|
+
f.token
|
|
419
|
+
? ` ${f.file}:${f.line} ${f.literal} - the system holds this value, and your code has no name for it yet`
|
|
420
|
+
: ` ${f.file}:${f.line} ${f.literal} - no token holds this value`);
|
|
413
421
|
}
|
|
414
422
|
out.push("", "Replace the ones that have a token. For a value with none, do NOT invent a token: say which value it is and what you would call it, and let a person decide.");
|
|
415
423
|
}
|
|
@@ -34,9 +34,9 @@ register("pt-BR", {
|
|
|
34
34
|
// ── a fiação do agente ──
|
|
35
35
|
"✓ .claude/settings.json the check now runs at the end of each agent turn": "✓ .claude/settings.json a verificação roda no fim de cada turno do agente",
|
|
36
36
|
"✓ .claude/settings.json the check moved from every write to the end of each agent turn": "✓ .claude/settings.json a verificação saiu de cada escrita e foi para o fim de cada turno do agente",
|
|
37
|
-
"✓ what the check records is sent to your dashboard at the end of each turn": "✓ o que a verificação registra vai para o seu painel no fim de cada turno",
|
|
37
|
+
"✓ what the check records is sent to your dashboard at the end of each turn - your system changes only from a commit on a clean main": "✓ o que a verificação registra vai para o seu painel no fim de cada turno - o seu sistema só muda com um commit na main limpa",
|
|
38
38
|
"· what the check records stays on this machine - run `npx synthesisui sync` to send it": "· o que a verificação registra fica nesta máquina - rode `npx synthesisui sync` para enviar",
|
|
39
|
-
"send what the check records to your dashboard at the end of each turn?": "enviar o que a verificação registra para o seu painel no fim de cada turno?",
|
|
39
|
+
"send what the check records to your dashboard at the end of each turn? Your system itself changes only from a commit on a clean main.": "enviar o que a verificação registra para o seu painel no fim de cada turno? O seu sistema em si só muda com um commit na main limpa.",
|
|
40
40
|
"· Codex has no end-of-turn hook we can hold - run `npx synthesisui doctor` to check what it wrote": "· o Codex não tem um fim de turno que a gente possa segurar - rode `npx synthesisui doctor` para checar o que ele escreveu",
|
|
41
41
|
"· .claude/settings.json already had it": "· .claude/settings.json já estava lá",
|
|
42
42
|
"✓ .claude/settings.json each session opens with what is missing": "✓ .claude/settings.json cada sessão abre com o que falta",
|
package/dist/doctor/scan.js
CHANGED
|
@@ -515,6 +515,15 @@ function scanCore(file, source, table) {
|
|
|
515
515
|
};
|
|
516
516
|
}
|
|
517
517
|
let svgDepth = 0;
|
|
518
|
+
/**
|
|
519
|
+
* DENTRO DE `@theme` OU `:root`, UMA DECLARAÇÃO É O VOCABULÁRIO, NÃO UM USO - rodada 1 do refinamento,
|
|
520
|
+
* 27/09. O leigo copiou os valores do sistema para o `@theme` do projeto novo, como o GUIDE ensina, e o
|
|
521
|
+
* `check_file` respondeu "17% (...) 19 written by hand", linha por linha, com o conserto circular
|
|
522
|
+
* "#0a100d - this repo calls it --color-canvas" na própria linha `--color-canvas: #0a100d;`.
|
|
523
|
+
* Profundidade de chaves, e não uma flag, porque `@theme` pode abrir bloco dentro de bloco.
|
|
524
|
+
*/
|
|
525
|
+
let tokenBlockDepth = 0;
|
|
526
|
+
let braceDepth = 0;
|
|
518
527
|
source.split("\n").forEach((raw, i) => {
|
|
519
528
|
const line = raw.trim();
|
|
520
529
|
/**
|
|
@@ -528,6 +537,14 @@ function scanCore(file, source, table) {
|
|
|
528
537
|
*/
|
|
529
538
|
const indent = line ? raw.indexOf(line) : 0;
|
|
530
539
|
const at = i + 1;
|
|
540
|
+
const depthBefore = braceDepth;
|
|
541
|
+
const opensTokenBlock = /^(@theme\b|:root\b)/.test(line) && line.includes("{");
|
|
542
|
+
if (opensTokenBlock && tokenBlockDepth === 0)
|
|
543
|
+
tokenBlockDepth = depthBefore + 1;
|
|
544
|
+
const inTokenBlock = tokenBlockDepth > 0 && (opensTokenBlock || depthBefore >= tokenBlockDepth);
|
|
545
|
+
braceDepth = Math.max(0, braceDepth + (line.match(/\{/g) ?? []).length - (line.match(/\}/g) ?? []).length);
|
|
546
|
+
if (tokenBlockDepth > 0 && braceDepth < tokenBlockDepth)
|
|
547
|
+
tokenBlockDepth = 0;
|
|
531
548
|
tokenUses += countTokenUses(line, table);
|
|
532
549
|
/**
|
|
533
550
|
* O QUE ESTE ARQUIVO DECLARA E O QUE ELE USA DE FORA DO SISTEMA - ver `FileReport.ownDeclared`.
|
|
@@ -577,6 +594,8 @@ function scanCore(file, source, table) {
|
|
|
577
594
|
const declares = DECLARES_TOKEN.exec(line);
|
|
578
595
|
if (declares && table.byName.has(declares[1].toLowerCase()))
|
|
579
596
|
return;
|
|
597
|
+
if (declares && inTokenBlock)
|
|
598
|
+
return;
|
|
580
599
|
// `rgba(${r}, ${g}, ${b}, ${a})` is code computing a colour, not a colour
|
|
581
600
|
// written by hand - flagging it would be telling someone to tokenize a
|
|
582
601
|
// variable. And the same literal twice in one declaration (a two-stop
|
package/dist/install-marks.js
CHANGED
|
@@ -397,7 +397,18 @@ export const COUNTED_DIFFERENTLY = "this run counts a value as named only when Y
|
|
|
397
397
|
* turno, e o censo chama o Autopilot: trabalho pela metade pode virar versão. Quem está atrás precisa
|
|
398
398
|
* saber.
|
|
399
399
|
*/
|
|
400
|
-
|
|
400
|
+
/**
|
|
401
|
+
* 0.16.456 -> 0.16.457 em 27/09, e o passo 1 dá **SIM**: a 0.16.456 foi publicada antes da E02 da
|
|
402
|
+
* mesma jornada, e o hook fixado nela manda só o registro em todo turno - nunca o censo, nem com a
|
|
403
|
+
* main limpa num commit novo. Quem está nela não vê o sistema mudar depois do commit, e precisa saber.
|
|
404
|
+
*/
|
|
405
|
+
/**
|
|
406
|
+
* 0.16.457 -> 0.16.458 em 27/09, e o passo 1 dá **SIM**: a varredura que o hook roda deixa de acusar as
|
|
407
|
+
* declarações dentro de `@theme` e `:root` (rodada 1 do refinamento - o `check_file` dava "19 written by
|
|
408
|
+
* hand" no `@theme` que o leigo copiou do GUIDE). Um hook fixado antes continua acusando o vocabulário do
|
|
409
|
+
* projeto como valor à mão, em todo turno.
|
|
410
|
+
*/
|
|
411
|
+
export const CHECKER_SINCE = "0.16.458";
|
|
401
412
|
/**
|
|
402
413
|
* A ÚLTIMA VERSÃO EM QUE OS LEITORES PASSARAM A PRODUZIR UM CENSO DIFERENTE.
|
|
403
414
|
*
|
package/package.json
CHANGED