@decantr/cli 1.0.0-beta.4 → 1.0.0-beta.5

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 (2) hide show
  1. package/dist/index.js +144 -4
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { readFileSync } from "fs";
4
+ import { readFileSync, writeFileSync, existsSync } from "fs";
5
5
  import { join } from "path";
6
+ import { createInterface } from "readline";
6
7
  import { validateEssence, evaluateGuard } from "@decantr/essence-spec";
7
8
  import { createResolver, createRegistryClient } from "@decantr/registry";
8
9
  var BOLD = "\x1B[1m";
@@ -158,11 +159,147 @@ async function cmdList(type) {
158
159
  console.log(dim(`No ${type} found.`));
159
160
  }
160
161
  }
162
+ function ask(question, defaultValue) {
163
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
164
+ const prompt = defaultValue ? `${question} ${dim(`(${defaultValue})`)}: ` : `${question}: `;
165
+ return new Promise((resolve) => {
166
+ rl.question(prompt, (answer) => {
167
+ rl.close();
168
+ resolve(answer.trim() || defaultValue || "");
169
+ });
170
+ });
171
+ }
172
+ async function select(question, options, defaultIdx = 0) {
173
+ console.log(`
174
+ ${BOLD}${question}${RESET}`);
175
+ for (let i = 0; i < options.length; i++) {
176
+ const marker = i === defaultIdx ? `${GREEN}>${RESET}` : " ";
177
+ console.log(` ${marker} ${i + 1}. ${options[i]}`);
178
+ }
179
+ const answer = await ask(`Choose (1-${options.length})`, String(defaultIdx + 1));
180
+ const idx = parseInt(answer, 10) - 1;
181
+ return options[Math.max(0, Math.min(idx, options.length - 1))];
182
+ }
183
+ async function cmdInit() {
184
+ console.log(heading("Create a new Decantr project"));
185
+ const essencePath = join(process.cwd(), "decantr.essence.json");
186
+ if (existsSync(essencePath)) {
187
+ const overwrite = await ask("decantr.essence.json already exists. Overwrite?", "n");
188
+ if (overwrite.toLowerCase() !== "y") {
189
+ console.log(dim("Cancelled."));
190
+ return;
191
+ }
192
+ }
193
+ let archetypes = [];
194
+ try {
195
+ const res = await fetch("https://decantr-registry.fly.dev/v1/archetypes");
196
+ if (res.ok) {
197
+ const data = await res.json();
198
+ archetypes = data.items;
199
+ }
200
+ } catch {
201
+ }
202
+ if (archetypes.length === 0) {
203
+ archetypes = [
204
+ { id: "saas-dashboard", description: "Analytics dashboard with KPIs and data tables" },
205
+ { id: "ecommerce", description: "Online store with product catalog" },
206
+ { id: "portfolio", description: "Personal or agency portfolio site" },
207
+ { id: "marketing-landing", description: "Product marketing landing page" },
208
+ { id: "gaming-platform", description: "Gaming community hub" },
209
+ { id: "content-site", description: "Blog or content site" }
210
+ ];
211
+ }
212
+ const archetypeOptions = archetypes.map((a) => `${a.id} ${dim(`\u2014 ${a.description || ""}`)}`);
213
+ const selectedArchetype = await select("What are you building?", archetypeOptions);
214
+ const archetypeId = selectedArchetype.split(" ")[0];
215
+ let themes = [];
216
+ try {
217
+ const res = await fetch("https://decantr-registry.fly.dev/v1/themes");
218
+ if (res.ok) {
219
+ const data = await res.json();
220
+ themes = data.items;
221
+ }
222
+ } catch {
223
+ }
224
+ if (themes.length === 0) {
225
+ themes = [
226
+ { id: "luminarum", description: "Dark geometric canvas with vibrant accents" },
227
+ { id: "clean", description: "Professional, minimal, universal" },
228
+ { id: "glassmorphism", description: "Frosted glass aesthetic" }
229
+ ];
230
+ }
231
+ const themeOptions = themes.map((t) => `${t.id} ${dim(`\u2014 ${t.description || ""}`)}`);
232
+ const selectedTheme = await select("Choose a theme", themeOptions);
233
+ const themeId = selectedTheme.split(" ")[0];
234
+ const mode = await select("Mode", ["dark", "light"], 0);
235
+ const shape = await select("Shape", ["pill", "rounded", "sharp"], 0);
236
+ const target = await select("Target framework", ["react", "vue", "svelte", "html"], 0);
237
+ let pages = [];
238
+ try {
239
+ const res = await fetch(`https://decantr-registry.fly.dev/v1/archetypes/${archetypeId}`);
240
+ if (res.ok) {
241
+ const data = await res.json();
242
+ if (data.pages) pages = data.pages;
243
+ }
244
+ } catch {
245
+ }
246
+ const structure = pages.length > 0 ? pages.map((p) => ({
247
+ id: p.id,
248
+ shell: p.shell || "sidebar-main",
249
+ layout: p.default_layout || []
250
+ })) : [{ id: "home", shell: "full-bleed", layout: ["hero-split"] }];
251
+ let features = [];
252
+ try {
253
+ const res = await fetch(`https://decantr-registry.fly.dev/v1/archetypes/${archetypeId}`);
254
+ if (res.ok) {
255
+ const data = await res.json();
256
+ if (data.features) features = data.features;
257
+ }
258
+ } catch {
259
+ }
260
+ const essence = {
261
+ version: "2.0.0",
262
+ archetype: archetypeId,
263
+ theme: {
264
+ style: themeId,
265
+ mode,
266
+ recipe: themeId,
267
+ shape
268
+ },
269
+ personality: ["professional"],
270
+ platform: { type: "spa", routing: "hash" },
271
+ structure,
272
+ features,
273
+ guard: { enforce_style: true, enforce_recipe: true, mode: "strict" },
274
+ density: { level: "comfortable", content_gap: "_gap4" },
275
+ target
276
+ };
277
+ writeFileSync(essencePath, JSON.stringify(essence, null, 2) + "\n");
278
+ console.log(success(`
279
+ Created decantr.essence.json`));
280
+ console.log(dim(` Archetype: ${archetypeId}`));
281
+ console.log(dim(` Theme: ${themeId} (${mode})`));
282
+ console.log(dim(` Pages: ${structure.map((s) => s.id).join(", ")}`));
283
+ console.log(dim(` Target: ${target}`));
284
+ const validation = validateEssence(essence);
285
+ if (validation.valid) {
286
+ console.log(success(" Validation: passed"));
287
+ } else {
288
+ console.log(error(` Validation: ${validation.errors.join(", ")}`));
289
+ }
290
+ console.log(heading("Next steps"));
291
+ console.log(` 1. Open your AI assistant (Claude, Cursor, etc.)`);
292
+ console.log(` 2. Tell it to read ${cyan("decantr.essence.json")} before generating code`);
293
+ console.log(` 3. The essence file defines your theme, pages, and patterns`);
294
+ console.log(` 4. Run ${cyan("decantr validate")} after changes to check for drift
295
+ `);
296
+ }
161
297
  function cmdHelp() {
162
298
  console.log(`
163
299
  ${BOLD}decantr${RESET} \u2014 Design intelligence for AI-generated UI
164
300
 
165
301
  ${BOLD}Usage:${RESET}
302
+ decantr init
166
303
  decantr search <query> [--type pattern|archetype|recipe|theme]
167
304
  decantr get <type> <id>
168
305
  decantr list <type>
@@ -170,6 +307,7 @@ ${BOLD}Usage:${RESET}
170
307
  decantr help
171
308
 
172
309
  ${BOLD}Commands:${RESET}
310
+ ${cyan("init")} Create a new decantr.essence.json \u2014 pick an archetype, theme, and target
173
311
  ${cyan("search")} Search the registry for patterns, archetypes, recipes, themes
174
312
  ${cyan("get")} Get full details of a registry item as JSON
175
313
  ${cyan("list")} List all items of a type (patterns, archetypes, recipes, themes, blueprints)
@@ -177,15 +315,13 @@ ${BOLD}Commands:${RESET}
177
315
  ${cyan("help")} Show this help message
178
316
 
179
317
  ${BOLD}Examples:${RESET}
318
+ decantr init
180
319
  decantr search dashboard
181
320
  decantr search kpi --type pattern
182
321
  decantr get pattern kpi-grid
183
322
  decantr get recipe luminarum
184
- decantr get theme luminarum
185
323
  decantr list patterns
186
- decantr list themes
187
324
  decantr validate
188
- decantr validate ./my-project/decantr.essence.json
189
325
  `);
190
326
  }
191
327
  async function main() {
@@ -196,6 +332,10 @@ async function main() {
196
332
  return;
197
333
  }
198
334
  switch (command) {
335
+ case "init": {
336
+ await cmdInit();
337
+ break;
338
+ }
199
339
  case "search": {
200
340
  const query = args[1];
201
341
  if (!query) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decantr/cli",
3
- "version": "1.0.0-beta.4",
3
+ "version": "1.0.0-beta.5",
4
4
  "description": "Decantr CLI — search the registry, validate essence files, and access design intelligence from the terminal",
5
5
  "license": "MIT",
6
6
  "repository": {