rojao-ds 0.1.0-next.6

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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +73 -0
  3. package/dist/cli.js +402 -0
  4. package/package.json +59 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rojão Studio
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # rojao-ds
2
+
3
+ A CLI do [Rojão DS](https://ds.rojao.ai): a sua marca vira o tema do design system e o arquivo de
4
+ regras que a IA (Claude Code, Cursor, outros agentes) lê para construir as telas.
5
+
6
+ ```bash
7
+ npx rojao-ds@next init
8
+ ```
9
+
10
+ Pergunta a cor da marca e escreve, na pasta atual:
11
+
12
+ - `rds-theme.css` — o tema da marca (claro, escuro e a placa de marca `.ds-plate`);
13
+ - `CLAUDE.md` — ou `.cursorrules` / `AGENTS.md`, o que já existir no projeto.
14
+
15
+ Depois:
16
+
17
+ ```bash
18
+ pnpm add @rojaostudio/ds@next
19
+ ```
20
+
21
+ ```css
22
+ /* CSS raiz do app: o tema vem DEPOIS */
23
+ @import "@rojaostudio/ds/styles/rds.css";
24
+ @import "./rds-theme.css";
25
+ ```
26
+
27
+ ## De onde vem a marca
28
+
29
+ | Opção | Entrada |
30
+ |---|---|
31
+ | `--color <hex>` | uma cor; o motor deriva o resto |
32
+ | `--recipe <arquivo>` | o `recipe.json` baixado em [ds.rojao.ai](https://ds.rojao.ai) |
33
+ | `--table <arquivo>` | a tabela da marca exportada do Figma [RDS] por `figma/export-brand.js` (vem no `@rojaostudio/ds-core`), formato `rds-brand-table/1` |
34
+
35
+ Sem nenhuma delas, a cor é perguntada no terminal.
36
+
37
+ ## Opções
38
+
39
+ | Opção | |
40
+ |---|---|
41
+ | `-n, --name <nome>` | nome da marca (padrão: o do recipe/tabela, senão o do `package.json`) |
42
+ | `--target <alvo>` | `claude`, `cursor` ou `agents` (padrão: o arquivo de regras que já existir, senão `claude`) |
43
+ | `-o, --out <arquivo>` | onde escrever o tema (padrão: `rds-theme.css`) |
44
+ | `-y, --yes` | sobrescreve o tema sem perguntar |
45
+ | `-h, --help` | ajuda |
46
+
47
+ O arquivo de regras nunca é sobrescrito: a CLI escreve só o bloco entre `<!-- rojao-ds:start -->` e
48
+ `<!-- rojao-ds:end -->`. Se o arquivo já existe sem o bloco, ele é acrescentado no fim; se já tem, só
49
+ o bloco é trocado e o resto do arquivo fica como está. Rodar de novo não duplica nada.
50
+
51
+ O tema (`rds-theme.css`) é sobrescrito, então pede confirmação. Sem terminal (CI, script) e sem
52
+ `--yes`, a CLI recusa e não escreve nada.
53
+
54
+ ## Migrar um projeto 0.x/1.x para a 2.0
55
+
56
+ ```bash
57
+ npx rojao-ds@next migrate ./meu-app # dry-run
58
+ npx rojao-ds@next migrate ./meu-app --apply # escreve
59
+ ```
60
+
61
+ É um atalho para `npx @rojaostudio/ds-codemod@next` (sem o `@next` depois da 2.0.0), com os mesmos argumentos: o codemod é baixado na
62
+ hora pelo npx, e por isso o `ts-morph` (que traz o compilador do TypeScript) não entra na CLI.
63
+
64
+ ## Sem dependência
65
+
66
+ Node 20+. A única dependência é o motor, [`@rojaostudio/ds-core`](https://www.npmjs.com/package/@rojaostudio/ds-core):
67
+ a CLI é uma casca fina sobre `generateRdsTheme`, `rdsThemeFromTable`, `emitRdsCss` e `emitClaudeMd`.
68
+ Quem prefere script usa o motor direto.
69
+
70
+ ## Licença
71
+
72
+ MIT — veja [LICENSE](./LICENSE). O nome e a marca não: veja
73
+ [TRADEMARK.md](https://github.com/rojaostudio/ds/blob/main/TRADEMARK.md).
package/dist/cli.js ADDED
@@ -0,0 +1,402 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import { spawn } from "child_process";
5
+ import { readFileSync as readFileSync3 } from "fs";
6
+ import { createInterface } from "readline/promises";
7
+
8
+ // src/init.ts
9
+ import { existsSync, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
10
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "path";
11
+ import { parseArgs } from "util";
12
+
13
+ // src/prerelease.ts
14
+ import { readFileSync } from "fs";
15
+ function isPrerelease() {
16
+ try {
17
+ const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
18
+ return Boolean(pkg.version?.includes("-"));
19
+ } catch {
20
+ return false;
21
+ }
22
+ }
23
+ var onTag = (name) => isPrerelease() ? `${name}@next` : name;
24
+
25
+ // src/migrate.ts
26
+ var CODEMOD_PACKAGE = onTag("@rojaostudio/ds-codemod");
27
+ var MIGRATE_HELP = `rojao-ds migrate \u2014 leva um projeto do @rojaostudio/ds 0.x/1.x para a 2.0
28
+
29
+ Uso:
30
+ npx rojao-ds migrate <pasta-do-projeto> [--apply] [--verbose] [--report <arquivo.json>]
31
+
32
+ \xC9 um atalho para:
33
+ npx ${CODEMOD_PACKAGE} <pasta-do-projeto> [...]
34
+
35
+ Sem --apply \xE9 um dry-run: mostra o plano e os casos manuais (arquivo:linha) e n\xE3o escreve nada.
36
+ `;
37
+ function codemodCommand(args) {
38
+ return { command: "npx", args: ["--yes", CODEMOD_PACKAGE, ...args] };
39
+ }
40
+ async function runMigrate(args, io) {
41
+ if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
42
+ io.out(MIGRATE_HELP);
43
+ return args.length === 0 ? 1 : 0;
44
+ }
45
+ if (!io.spawn) {
46
+ io.err("\u2717 este ambiente n\xE3o consegue rodar o npx.");
47
+ return 1;
48
+ }
49
+ const { command, args: forwarded } = codemodCommand(args);
50
+ io.err(`\u2192 ${command} ${forwarded.join(" ")}`);
51
+ return io.spawn(command, forwarded);
52
+ }
53
+
54
+ // src/init.ts
55
+ import {
56
+ emitClaudeMd,
57
+ emitRdsCss,
58
+ generateRdsTheme,
59
+ rdsThemeFromTable
60
+ } from "@rojaostudio/ds-core/generate";
61
+ var TARGETS = {
62
+ claude: "CLAUDE.md",
63
+ agents: "AGENTS.md",
64
+ cursor: ".cursorrules"
65
+ };
66
+ var DEFAULT_OUT = "rds-theme.css";
67
+ var HELP = `rojao-ds \u2014 o tema da sua marca no Roj\xE3o DS 2.0
68
+
69
+ Uso:
70
+ npx rojao-ds init [op\xE7\xF5es]
71
+ npx rojao-ds migrate <pasta> [--apply] migra um projeto 0.x/1.x para a 2.0 (via npx @rojaostudio/ds-codemod)
72
+
73
+ Gera, na pasta atual:
74
+ \u2022 o tema da marca (${DEFAULT_OUT}), para importar DEPOIS de @rojaostudio/ds/styles/rds.css;
75
+ \u2022 o arquivo de regras da IA (CLAUDE.md, .cursorrules ou AGENTS.md). Se ele j\xE1 existe, s\xF3 o
76
+ bloco entre <!-- rojao-ds:start --> e <!-- rojao-ds:end --> \xE9 escrito; o resto fica.
77
+
78
+ De onde vem a marca (escolha uma; sem nenhuma, a cor \xE9 perguntada):
79
+ -c, --color <hex> a cor da marca, ex.: #7C3AED
80
+ -r, --recipe <arquivo> o recipe.json baixado em ds.rojao.ai
81
+ -t, --table <arquivo> a tabela exportada do Figma (figma/export-brand.js, rds-brand-table/1)
82
+
83
+ Op\xE7\xF5es:
84
+ -n, --name <nome> nome da marca (padr\xE3o: o do recipe/tabela, sen\xE3o o do package.json)
85
+ --target <alvo> claude | cursor | agents (padr\xE3o: o arquivo que j\xE1 existir, sen\xE3o claude)
86
+ -o, --out <arquivo> onde escrever o tema (padr\xE3o: ${DEFAULT_OUT})
87
+ -y, --yes sobrescreve o tema sem perguntar
88
+ -h, --help mostra esta ajuda
89
+ -v, --version mostra a vers\xE3o
90
+
91
+ Exemplos:
92
+ npx rojao-ds init --color "#7C3AED"
93
+ npx rojao-ds init --recipe ./marca.recipe.json --target cursor
94
+ npx rojao-ds init --table ./brand/marca.rds.json --out src/styles/tema.css --yes
95
+ `;
96
+ var CliError = class extends Error {
97
+ };
98
+ var HEX = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i;
99
+ function normalizeHex(v) {
100
+ const m = v.trim().match(HEX);
101
+ if (!m) return null;
102
+ const h = m[1].length === 3 ? [...m[1]].map((c) => c + c).join("") : m[1];
103
+ return `#${h.toUpperCase()}`;
104
+ }
105
+ function slug(v) {
106
+ return v.replace(/^@[^/]+\//, "").normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 32);
107
+ }
108
+ function readJson(cwd, file, what) {
109
+ const path = resolve(cwd, file);
110
+ if (!existsSync(path)) throw new CliError(`${what} n\xE3o encontrado: ${file}`);
111
+ try {
112
+ return JSON.parse(readFileSync2(path, "utf8"));
113
+ } catch (e) {
114
+ throw new CliError(`${what} n\xE3o \xE9 um JSON v\xE1lido (${file}): ${e.message}`);
115
+ }
116
+ }
117
+ function projectName(cwd) {
118
+ const pkg = join(cwd, "package.json");
119
+ if (!existsSync(pkg)) return null;
120
+ try {
121
+ const name = JSON.parse(readFileSync2(pkg, "utf8")).name;
122
+ return typeof name === "string" && slug(name) ? slug(name) : null;
123
+ } catch {
124
+ return null;
125
+ }
126
+ }
127
+ function fromColor(hex, name) {
128
+ return { name, brand: { primary: hex }, surface: "zinc", text: "zinc", fonts: { body: "inter" } };
129
+ }
130
+ function fromRecipe(raw, name) {
131
+ if (!raw || typeof raw !== "object") throw new CliError("recipe inv\xE1lido: esperava um objeto JSON.");
132
+ const { $version: _v, ...rest } = raw;
133
+ const brand = rest.brand;
134
+ if (!brand || typeof brand.primary !== "string" || !brand.primary) {
135
+ throw new CliError("recipe inv\xE1lido: falta brand.primary (a cor da marca).");
136
+ }
137
+ const primary = normalizeHex(brand.primary) ?? brand.primary;
138
+ const own = typeof rest.name === "string" ? slug(rest.name) : "";
139
+ return {
140
+ surface: "zinc",
141
+ text: "zinc",
142
+ fonts: { body: "inter" },
143
+ ...rest,
144
+ name: name ?? (own || "marca"),
145
+ brand: { ...brand, primary }
146
+ };
147
+ }
148
+ function asTable(raw) {
149
+ const t = raw;
150
+ if (!t || typeof t !== "object" || !t.modes || !t.primitives) {
151
+ throw new CliError("tabela inv\xE1lida: esperava o formato rds-brand-table/1 (com modes e primitives), exportado por figma/export-brand.js.");
152
+ }
153
+ if (t.$schema && t.$schema !== "rds-brand-table/1") {
154
+ throw new CliError(`tabela em formato desconhecido: ${t.$schema} (esperava rds-brand-table/1).`);
155
+ }
156
+ return t;
157
+ }
158
+ var BLOCK_START = "<!-- rojao-ds:start -->";
159
+ var BLOCK_END = "<!-- rojao-ds:end -->";
160
+ function rulesBlock(md) {
161
+ return `${BLOCK_START}
162
+ ${md.replace(/\s+$/, "")}
163
+ ${BLOCK_END}`;
164
+ }
165
+ function mergeRulesBlock(existing, block) {
166
+ const start = existing.indexOf(BLOCK_START);
167
+ const end = start === -1 ? -1 : existing.indexOf(BLOCK_END, start);
168
+ if (start !== -1 && end !== -1) {
169
+ return { text: existing.slice(0, start) + block + existing.slice(end + BLOCK_END.length), mode: "replaced" };
170
+ }
171
+ const head = existing.replace(/\s+$/, "");
172
+ return { text: head ? `${head}
173
+
174
+ ${block}
175
+ ` : `${block}
176
+ `, mode: "appended" };
177
+ }
178
+ async function askColor(io) {
179
+ for (let i = 0; i < 3; i++) {
180
+ const hex = normalizeHex(await io.ask("Cor da marca (hex, ex.: #7C3AED): "));
181
+ if (hex) return hex;
182
+ io.err(" N\xE3o \xE9 uma cor hex. Use 6 d\xEDgitos, como #7C3AED.");
183
+ }
184
+ throw new CliError("cor n\xE3o informada.");
185
+ }
186
+ async function resolveBrand(v, io) {
187
+ const modes = [v.color, v.recipe, v.table].filter((x) => x !== void 0);
188
+ if (modes.length > 1) throw new CliError("use s\xF3 uma fonte de marca: --color, --recipe ou --table.");
189
+ const name = v.name !== void 0 ? slug(v.name) || void 0 : void 0;
190
+ try {
191
+ if (v.table !== void 0) {
192
+ const table = asTable(readJson(io.cwd, v.table, "tabela"));
193
+ const n = name ?? (slug(table.name ?? "") || "marca");
194
+ const theme = rdsThemeFromTable(table);
195
+ return { label: `tabela do Figma (${v.table})`, name: n, theme, source: { ...table, name: n } };
196
+ }
197
+ if (v.recipe !== void 0) {
198
+ const def2 = fromRecipe(readJson(io.cwd, v.recipe, "recipe"), name);
199
+ return { label: `recipe (${v.recipe})`, name: def2.name, theme: generateRdsTheme(def2), source: def2 };
200
+ }
201
+ let hex;
202
+ if (v.color !== void 0) {
203
+ const h = normalizeHex(v.color);
204
+ if (!h) throw new CliError(`cor inv\xE1lida: ${v.color} (use hex, ex.: #7C3AED).`);
205
+ hex = h;
206
+ } else if (io.interactive) {
207
+ hex = await askColor(io);
208
+ } else {
209
+ throw new CliError("informe a marca: --color <hex>, --recipe <arquivo> ou --table <arquivo>.");
210
+ }
211
+ const def = fromColor(hex, name ?? projectName(io.cwd) ?? "marca");
212
+ return { label: `cor ${hex}`, name: def.name, theme: generateRdsTheme(def), source: def };
213
+ } catch (e) {
214
+ if (e instanceof CliError) throw e;
215
+ throw new CliError(`n\xE3o foi poss\xEDvel gerar o tema: ${e.message}`);
216
+ }
217
+ }
218
+ async function resolveTarget(v, io) {
219
+ if (v.target !== void 0) {
220
+ const t = v.target.toLowerCase();
221
+ const byFile = Object.keys(TARGETS).find((k) => TARGETS[k].toLowerCase() === t);
222
+ if (t in TARGETS) return t;
223
+ if (byFile) return byFile;
224
+ throw new CliError(`alvo inv\xE1lido: ${v.target} (use claude, cursor ou agents).`);
225
+ }
226
+ const found = Object.keys(TARGETS).filter((k) => existsSync(join(io.cwd, TARGETS[k])));
227
+ if (found.length <= 1) return found[0] ?? "claude";
228
+ if (!io.interactive || v.yes) {
229
+ io.out(`Encontrei ${found.map((k) => TARGETS[k]).join(", ")}; uso ${TARGETS[found[0]]} (--target escolhe outro).`);
230
+ return found[0];
231
+ }
232
+ const lista = found.map((k, i2) => `${i2 + 1}) ${TARGETS[k]}`).join(" ");
233
+ const r = (await io.ask(`Encontrei mais de um arquivo de regras: ${lista}. Qual usar? [1] `)).trim();
234
+ const i = r === "" ? 0 : Number(r) - 1;
235
+ if (!Number.isInteger(i) || !found[i]) throw new CliError(`op\xE7\xE3o inv\xE1lida: ${r}`);
236
+ return found[i];
237
+ }
238
+ async function confirm(io, pergunta) {
239
+ const r = (await io.ask(`${pergunta} (s/N) `)).trim().toLowerCase();
240
+ return r === "s" || r === "sim" || r === "y" || r === "yes";
241
+ }
242
+ function importPath(cwd, abs) {
243
+ const rel = relative(cwd, abs).split(sep).join("/");
244
+ return rel.startsWith(".") ? rel : `./${rel}`;
245
+ }
246
+ function installCommand(cwd) {
247
+ const ds = onTag("@rojaostudio/ds");
248
+ if (existsSync(join(cwd, "pnpm-lock.yaml"))) return `pnpm add ${ds}`;
249
+ if (existsSync(join(cwd, "yarn.lock"))) return `yarn add ${ds}`;
250
+ if (existsSync(join(cwd, "bun.lockb")) || existsSync(join(cwd, "bun.lock"))) return `bun add ${ds}`;
251
+ if (existsSync(join(cwd, "package-lock.json"))) return `npm install ${ds}`;
252
+ return `pnpm add ${ds}`;
253
+ }
254
+ var OPTIONS = {
255
+ color: { type: "string", short: "c" },
256
+ recipe: { type: "string", short: "r" },
257
+ table: { type: "string", short: "t" },
258
+ name: { type: "string", short: "n" },
259
+ target: { type: "string" },
260
+ out: { type: "string", short: "o" },
261
+ yes: { type: "boolean", short: "y" },
262
+ help: { type: "boolean", short: "h" },
263
+ version: { type: "boolean", short: "v" }
264
+ };
265
+ async function run(argv, io, version2 = "0.0.0") {
266
+ if (argv[0] === "migrate") return runMigrate(argv.slice(1), io);
267
+ let values;
268
+ let positionals;
269
+ try {
270
+ ({ values, positionals } = parseArgs({ args: argv, options: OPTIONS, allowPositionals: true, strict: true }));
271
+ } catch (e) {
272
+ io.err(`\u2717 ${e.message.replace(/\. To specify.*$/s, "")}`);
273
+ io.err(" Veja: npx rojao-ds --help");
274
+ return 1;
275
+ }
276
+ if (values.version) {
277
+ io.out(version2);
278
+ return 0;
279
+ }
280
+ const [command, ...extra] = positionals;
281
+ if (values.help || command === void 0 || command === "help") {
282
+ io.out(HELP);
283
+ return command === void 0 && !values.help ? 1 : 0;
284
+ }
285
+ if (command !== "init") {
286
+ io.err(`\u2717 comando desconhecido: ${command}. Os comandos s\xE3o init e migrate.`);
287
+ io.err(" Veja: npx rojao-ds --help");
288
+ return 1;
289
+ }
290
+ if (extra.length) {
291
+ io.err(`\u2717 argumento a mais: ${extra.join(" ")}`);
292
+ return 1;
293
+ }
294
+ try {
295
+ return await init(values, io);
296
+ } catch (e) {
297
+ if (e instanceof CliError) {
298
+ io.err(`\u2717 ${e.message}`);
299
+ return 1;
300
+ }
301
+ throw e;
302
+ }
303
+ }
304
+ async function init(v, io) {
305
+ const brand = await resolveBrand(v, io);
306
+ const target = await resolveTarget(v, io);
307
+ const out = v.out ?? DEFAULT_OUT;
308
+ if (!out.endsWith(".css")) throw new CliError(`--out precisa terminar em .css: ${out}`);
309
+ const cssAbs = isAbsolute(out) ? out : resolve(io.cwd, out);
310
+ const rulesAbs = join(io.cwd, TARGETS[target]);
311
+ const cssImport = importPath(io.cwd, cssAbs);
312
+ const css = { abs: cssAbs, label: importPath(io.cwd, cssAbs).replace(/^\.\//, ""), content: emitRdsCss(brand.theme) };
313
+ const rules = {
314
+ abs: rulesAbs,
315
+ label: TARGETS[target],
316
+ block: rulesBlock(emitClaudeMd(brand.source, { theme: brand.theme, cssFile: cssImport.replace(/^\.\//, ""), target }))
317
+ };
318
+ const cssExists = existsSync(css.abs);
319
+ if (cssExists && !v.yes && !io.interactive) {
320
+ io.err(`\u2717 ${css.label} j\xE1 existe.`);
321
+ io.err(" Nada foi escrito. Rode de novo com --yes para sobrescrever.");
322
+ return 1;
323
+ }
324
+ io.out(`Marca: ${brand.name} \u2014 ${brand.label}`);
325
+ if (cssExists && !v.yes && !await confirm(io, `${css.label} j\xE1 existe. Sobrescrever?`)) {
326
+ io.out(`\u2013 ${css.label} mantido`);
327
+ } else {
328
+ mkdirSync(dirname(css.abs), { recursive: true });
329
+ writeFileSync(css.abs, css.content);
330
+ io.out(`\u2713 ${css.label} ${cssExists ? "atualizado" : "criado"}`);
331
+ }
332
+ if (!existsSync(rules.abs)) {
333
+ writeFileSync(rules.abs, `${rules.block}
334
+ `);
335
+ io.out(`\u2713 ${rules.label} criado`);
336
+ } else {
337
+ const before = readFileSync2(rules.abs, "utf8");
338
+ const { text, mode } = mergeRulesBlock(before, rules.block);
339
+ if (text === before) io.out(`\u2713 ${rules.label} j\xE1 est\xE1 em dia`);
340
+ else {
341
+ writeFileSync(rules.abs, text);
342
+ io.out(`\u2713 ${rules.label}: bloco do Roj\xE3o DS ${mode === "replaced" ? "atualizado" : "acrescentado no fim"} (o resto do arquivo foi mantido)`);
343
+ }
344
+ }
345
+ io.out("");
346
+ io.out("Pr\xF3ximos passos:");
347
+ io.out(` 1. ${installCommand(io.cwd)}`);
348
+ io.out(" 2. No CSS raiz do app (o tema vem DEPOIS):");
349
+ io.out(' @import "@rojaostudio/ds/styles/rds.css";');
350
+ io.out(` @import "${cssImport}";`);
351
+ if (cssImport.slice(2).includes("/") || cssImport.startsWith("../")) {
352
+ io.out(" (caminho a partir da raiz do projeto: ajuste ao lugar do seu CSS)");
353
+ }
354
+ io.out(` 3. Pe\xE7a a tela para a IA: ela l\xEA o ${TARGETS[target]}.`);
355
+ return 0;
356
+ }
357
+
358
+ // src/cli.ts
359
+ function version() {
360
+ try {
361
+ const pkg = JSON.parse(readFileSync3(new URL("../package.json", import.meta.url), "utf8"));
362
+ return pkg.version ?? "0.0.0";
363
+ } catch {
364
+ return "0.0.0";
365
+ }
366
+ }
367
+ var prompt = {};
368
+ var interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
369
+ var code = await run(
370
+ process.argv.slice(2),
371
+ {
372
+ cwd: process.cwd(),
373
+ out: (l) => process.stdout.write(`${l}
374
+ `),
375
+ err: (l) => process.stderr.write(`${l}
376
+ `),
377
+ ask: (q) => {
378
+ prompt.rl ??= createInterface({ input: process.stdin, output: process.stdout });
379
+ return prompt.rl.question(q);
380
+ },
381
+ interactive,
382
+ // npx é um .cmd no Windows, e o Node 20+ só roda .cmd com shell. Os argumentos vêm do próprio usuário.
383
+ spawn: (command, args) => new Promise((done) => {
384
+ const win = process.platform === "win32";
385
+ const argv = win ? args.map((a) => /[\s"]/.test(a) ? `"${a.replace(/"/g, '\\"')}"` : a) : args;
386
+ const child = spawn(command, argv, { stdio: "inherit", shell: win });
387
+ child.on("error", (e) => {
388
+ process.stderr.write(`\u2717 n\xE3o consegui rodar ${command}: ${e.message}
389
+ `);
390
+ done(1);
391
+ });
392
+ child.on("exit", (code2) => done(code2 ?? 1));
393
+ })
394
+ },
395
+ version()
396
+ ).catch((e) => {
397
+ process.stderr.write(`\u2717 erro inesperado: ${e instanceof Error ? e.message : String(e)}
398
+ `);
399
+ return 1;
400
+ });
401
+ prompt.rl?.close();
402
+ process.exitCode = code;
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "rojao-ds",
3
+ "version": "0.1.0-next.6",
4
+ "description": "CLI do Rojão DS — `npx rojao-ds init` gera o tema da sua marca e o arquivo de regras para a IA (CLAUDE.md, .cursorrules, AGENTS.md).",
5
+ "license": "MIT",
6
+ "author": "Rojão Studio (https://rojao.studio)",
7
+ "homepage": "https://ds.rojao.ai",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/rojaostudio/ds.git",
11
+ "directory": "packages/cli"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/rojaostudio/ds/issues"
15
+ },
16
+ "keywords": [
17
+ "design-system",
18
+ "theme-generator",
19
+ "cli",
20
+ "claude",
21
+ "cursor",
22
+ "agents",
23
+ "css-variables"
24
+ ],
25
+ "type": "module",
26
+ "bin": {
27
+ "rojao-ds": "./dist/cli.js"
28
+ },
29
+ "files": [
30
+ "LICENSE",
31
+ "README.md",
32
+ "dist"
33
+ ],
34
+ "engines": {
35
+ "node": ">=20"
36
+ },
37
+ "dependencies": {
38
+ "@rojaostudio/ds-core": "1.1.0-next.8"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^20.19.43",
42
+ "eslint": "^9.18.0",
43
+ "tsup": "^8.5.1",
44
+ "typescript": "^5",
45
+ "vitest": "^4.1.7",
46
+ "@rojao/config": "0.0.0"
47
+ },
48
+ "publishConfig": {
49
+ "registry": "https://registry.npmjs.org",
50
+ "access": "public",
51
+ "provenance": false
52
+ },
53
+ "scripts": {
54
+ "build": "tsup",
55
+ "test": "vitest run",
56
+ "typecheck": "tsc --noEmit",
57
+ "lint": "eslint ."
58
+ }
59
+ }