@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.
- package/dist/index.js +144 -4
- 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