synthesisui 0.1.11 → 0.1.13

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.
@@ -53,6 +53,17 @@ export async function add(slug, opts) {
53
53
  fetchedAt: new Date().toISOString(),
54
54
  };
55
55
  await writeFile(rootLockPath, `${JSON.stringify(lock, null, 2)}\n`, "utf8");
56
+ // 5b. governance rules (personal DS) → rules.md at the slug root (stable path,
57
+ // highest authority; the GUIDE tells the agent to read it first)
58
+ const rules = payload.rules ?? [];
59
+ if (rules.length > 0) {
60
+ const body = `# ${payload.name} — Rules\n\n` +
61
+ "> Accumulated rules for this design system. **Max authority — follow these first.**\n" +
62
+ `> Managed by synthesisui (edit in the studio). ${rules.length} rule(s).\n\n${rules
63
+ .map((r) => `- ${r}`)
64
+ .join("\n")}\n`;
65
+ await writeFile(join(slugDir, "rules.md"), body, "utf8");
66
+ }
56
67
  // 6. discovery by the agent
57
68
  const claudeMd = await syncClaudeMd(projectRoot);
58
69
  // outcome line
@@ -75,6 +86,9 @@ export async function add(slug, opts) {
75
86
  "GUIDE.md",
76
87
  ];
77
88
  console.log(` v${v}/: ${files.join(", ")}`);
89
+ if (rules.length > 0) {
90
+ console.log(` rules.md → ${rules.length} rule(s) (read these first)`);
91
+ }
78
92
  console.log(` CLAUDE.md ${claudeMd.created ? "created" : "updated"} (${claudeMd.count} system(s) installed)`);
79
93
  console.log("");
80
94
  console.log("Next steps:");
@@ -0,0 +1,53 @@
1
+ import { mkdir, readdir, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { resolveRegistry } from "../config.js";
4
+ import { postGenerate, RegistryError } from "../registry.js";
5
+ /** Slugs materialized under `_synthesisui/ds/` in the project. */
6
+ async function installedSlugs(root) {
7
+ try {
8
+ const entries = await readdir(join(root, "_synthesisui", "ds"), {
9
+ withFileTypes: true,
10
+ });
11
+ return entries.filter((e) => e.isDirectory()).map((e) => e.name);
12
+ }
13
+ catch {
14
+ return [];
15
+ }
16
+ }
17
+ /**
18
+ * Generates a token-only component recipe for the project's design system
19
+ * (chat-gen PRO, hosted) and materializes it additively under
20
+ * `_synthesisui/ds/<slug>/generated/`. The recipe wears the DS by construction;
21
+ * the prompt lives server-side. Nothing else in the project is touched.
22
+ */
23
+ export async function generate(description, opts) {
24
+ const base = resolveRegistry(opts.registry);
25
+ const root = opts.dir ?? process.cwd();
26
+ let slug = opts.ds;
27
+ if (!slug) {
28
+ const slugs = await installedSlugs(root);
29
+ if (slugs.length === 1) {
30
+ slug = slugs[0];
31
+ }
32
+ else if (slugs.length === 0) {
33
+ throw new RegistryError("No design system installed here. Run `synthesisui add <slug>` first, or pass --ds <slug>.");
34
+ }
35
+ else {
36
+ throw new RegistryError(`Multiple design systems installed (${slugs.join(", ")}). Pick one with --ds <slug>.`);
37
+ }
38
+ }
39
+ console.log(`→ generating a component for "${slug}" at ${base} …`);
40
+ const res = await postGenerate(base, { slug, description, name: opts.name });
41
+ const dir = join(root, "_synthesisui", "ds", slug, "generated");
42
+ await mkdir(dir, { recursive: true });
43
+ await writeFile(join(dir, `${res.name}.json`), `${JSON.stringify(res.recipe, null, 2)}\n`, "utf8");
44
+ await writeFile(join(dir, `${res.name}.css`), `${res.css}\n`, "utf8");
45
+ const tries = `${res.tries} ${res.tries === 1 ? "try" : "tries"}`;
46
+ console.log(`✓ ${res.name} generated (${res.model}, ${tries})`);
47
+ console.log(` → _synthesisui/ds/${slug}/generated/${res.name}.{json,css}`);
48
+ console.log("");
49
+ console.log("Use it:");
50
+ console.log(` • @import "_synthesisui/ds/${slug}/generated/${res.name}.css" in your CSS`);
51
+ console.log(` • <div data-ds="${slug}"><div class="ds-${res.name}">…</div></div>`);
52
+ console.log(` (${res.usage.inputTokens} in / ${res.usage.outputTokens} out tokens — recipe is additive; nothing else changed)`);
53
+ }
package/dist/guide.js CHANGED
@@ -100,6 +100,18 @@ its typographic identity. Load them once (any one approach):
100
100
  const artifactList = Object.keys(payload.artifacts)
101
101
  .map((f) => `\`${f}\``)
102
102
  .join(", ");
103
+ const hasRules = (payload.rules?.length ?? 0) > 0;
104
+ const rulesNote = hasRules
105
+ ? `
106
+ ## Rules — highest authority
107
+
108
+ **Read \`_synthesisui/ds/${slug}/rules.md\` FIRST and obey it above everything else in this guide.**
109
+ It carries this system's accumulated, project-specific rules; on any conflict they win over the
110
+ generic guidance below.
111
+
112
+ ---
113
+ `
114
+ : "";
103
115
  return `# Design System: ${name}
104
116
 
105
117
  > Generated by \`synthesisui add ${slug}\` (v${version}). **Do not edit by hand** —
@@ -114,7 +126,7 @@ ${meta.sourceUrl ? `**Reinterpretation of:** ${meta.sourceUrl}` : "**Original sy
114
126
  ${meta.narrative}
115
127
 
116
128
  ---
117
-
129
+ ${rulesNote}
118
130
  ## How to apply
119
131
 
120
132
  1. Import the tokens once in your project's global CSS:
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { add } from "./commands/add.js";
3
3
  import { advise } from "./commands/advise.js";
4
+ import { generate } from "./commands/generate.js";
4
5
  import { list } from "./commands/list.js";
5
6
  import { login } from "./commands/login.js";
6
7
  import { RegistryError } from "./registry.js";
@@ -11,11 +12,14 @@ Usage:
11
12
  synthesisui list [options] list the published design systems
12
13
  synthesisui add <slug> [options] materialize a DS into _synthesisui/ds/<slug>/
13
14
  synthesisui advise "<value prop>" engagement-pattern proposals for this project (login required)
15
+ synthesisui generate "<desc>" generate a token-only component recipe for your DS (login required)
14
16
 
15
17
  Options:
16
18
  --registry <url> registry URL (or env SYNTHESISUI_REGISTRY_URL)
17
19
  --dir <path> consumer project root (default: current directory)
18
20
  --version <n> install a specific version (default: latest)
21
+ --ds <slug> target design system for generate (default: the installed one)
22
+ --name <name> preferred component name for generate
19
23
  -h, --help this help
20
24
 
21
25
  Examples:
@@ -25,6 +29,7 @@ Examples:
25
29
  synthesisui add halogen --version 3
26
30
  synthesisui add halogen --registry http://localhost:3737
27
31
  synthesisui advise "habit-building app for tracking personal finances"
32
+ synthesisui generate "an upgrade banner with a title, message and a primary CTA"
28
33
  `;
29
34
  /** Extracts simple `--flag value` pairs and the remaining positionals. */
30
35
  function parseFlags(argv) {
@@ -97,6 +102,18 @@ async function main() {
97
102
  await advise(valueProp, { registry, dir });
98
103
  break;
99
104
  }
105
+ case "generate": {
106
+ const description = args.join(" ").trim();
107
+ if (!description) {
108
+ console.error('error: describe the component — `synthesisui generate "<description>"`');
109
+ process.exitCode = 1;
110
+ return;
111
+ }
112
+ const ds = typeof flags.ds === "string" ? flags.ds : undefined;
113
+ const name = typeof flags.name === "string" ? flags.name : undefined;
114
+ await generate(description, { registry, dir, ds, name });
115
+ break;
116
+ }
100
117
  default:
101
118
  console.error(`unknown command: "${command}"\n`);
102
119
  console.log(HELP);
package/dist/registry.js CHANGED
@@ -68,3 +68,29 @@ export async function postAdvisor(base, context) {
68
68
  }
69
69
  return (await res.json());
70
70
  }
71
+ /**
72
+ * Gera uma recipe token-only (chat-gen PRO) via `POST /api/ai/generate`.
73
+ * Gated + metered server-side: 401 = sem login, 429 = cota diária estourada.
74
+ */
75
+ export async function postGenerate(base, payload) {
76
+ let res;
77
+ try {
78
+ res = await fetch(`${base}/api/ai/generate`, {
79
+ method: "POST",
80
+ headers: { "content-type": "application/json", ...(await authHeaders()) },
81
+ body: JSON.stringify(payload),
82
+ });
83
+ }
84
+ catch {
85
+ throw new RegistryError(`Could not reach the registry at ${base}. ` +
86
+ `Check the URL (--registry / SYNTHESISUI_REGISTRY_URL) and your connection.`);
87
+ }
88
+ if (res.status === 401) {
89
+ throw new RegistryError("Not authenticated. Run `synthesisui login` first.");
90
+ }
91
+ if (!res.ok) {
92
+ const body = (await res.json().catch(() => ({})));
93
+ throw new RegistryError(body.message ?? `Generate responded ${res.status}.`);
94
+ }
95
+ return (await res.json());
96
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "Traz design systems do SynthesisUI para qualquer projeto (materializa em _local/ds/).",
5
5
  "type": "module",
6
6
  "bin": {