@decantr/cli 1.4.0 → 1.5.2
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/bin.js +2 -2
- package/dist/{chunk-OTVIAUQG.js → chunk-66RIAQLH.js} +1405 -279
- package/dist/{chunk-ZQ5FTYKG.js → chunk-6RJSFLT4.js} +537 -132
- package/dist/heal-54MKDDSQ.js +155 -0
- package/dist/index.js +2 -2
- package/dist/{upgrade-EHMDEZGC.js → upgrade-25IURU4X.js} +1 -1
- package/package.json +6 -6
- package/LICENSE +0 -21
- package/dist/heal-ZG5VJZ5J.js +0 -60
|
@@ -9,14 +9,14 @@ import {
|
|
|
9
9
|
scaffoldMinimal,
|
|
10
10
|
scaffoldProject,
|
|
11
11
|
syncRegistry
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-6RJSFLT4.js";
|
|
13
13
|
|
|
14
14
|
// src/index.ts
|
|
15
|
-
import { readFileSync as
|
|
16
|
-
import { join as
|
|
15
|
+
import { readFileSync as readFileSync15, existsSync as existsSync23, readdirSync as readdirSync6 } from "fs";
|
|
16
|
+
import { join as join23, dirname as dirname2 } from "path";
|
|
17
17
|
import { fileURLToPath } from "url";
|
|
18
18
|
import { validateEssence as validateEssence2, evaluateGuard, isV3 as isV36 } from "@decantr/essence-spec";
|
|
19
|
-
import { RegistryAPIClient as
|
|
19
|
+
import { RegistryAPIClient as RegistryAPIClient3 } from "@decantr/registry";
|
|
20
20
|
|
|
21
21
|
// src/detect.ts
|
|
22
22
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -165,10 +165,10 @@ var CYAN = "\x1B[36m";
|
|
|
165
165
|
function ask(question, defaultValue) {
|
|
166
166
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
167
167
|
const prompt = defaultValue ? `${question} ${DIM}(${defaultValue})${RESET}: ` : `${question}: `;
|
|
168
|
-
return new Promise((
|
|
168
|
+
return new Promise((resolve2) => {
|
|
169
169
|
rl.question(prompt, (answer) => {
|
|
170
170
|
rl.close();
|
|
171
|
-
|
|
171
|
+
resolve2(answer.trim() || defaultValue || "");
|
|
172
172
|
});
|
|
173
173
|
});
|
|
174
174
|
}
|
|
@@ -361,7 +361,7 @@ function mergeWithDefaults(flags, detected) {
|
|
|
361
361
|
}
|
|
362
362
|
async function runSimplifiedInit(blueprints) {
|
|
363
363
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
364
|
-
const question = (q) => new Promise((
|
|
364
|
+
const question = (q) => new Promise((resolve2) => rl.question(q, resolve2));
|
|
365
365
|
console.log("\n? What blueprint would you like to scaffold?\n");
|
|
366
366
|
console.log(" 1. Decantr default (recommended)");
|
|
367
367
|
console.log(" 2. Search registry...\n");
|
|
@@ -962,7 +962,7 @@ var GREEN4 = "\x1B[32m";
|
|
|
962
962
|
var RED3 = "\x1B[31m";
|
|
963
963
|
var DIM4 = "\x1B[2m";
|
|
964
964
|
var RESET4 = "\x1B[0m";
|
|
965
|
-
async function cmdRefresh(projectRoot = process.cwd()) {
|
|
965
|
+
async function cmdRefresh(projectRoot = process.cwd(), options = {}) {
|
|
966
966
|
const essencePath = join8(projectRoot, "decantr.essence.json");
|
|
967
967
|
if (!existsSync8(essencePath)) {
|
|
968
968
|
console.error(`${RED3}No decantr.essence.json found. Run \`decantr init\` first.${RESET4}`);
|
|
@@ -985,7 +985,8 @@ async function cmdRefresh(projectRoot = process.cwd()) {
|
|
|
985
985
|
return;
|
|
986
986
|
}
|
|
987
987
|
const registryClient = new RegistryClient({
|
|
988
|
-
cacheDir: join8(projectRoot, ".decantr", "cache")
|
|
988
|
+
cacheDir: join8(projectRoot, ".decantr", "cache"),
|
|
989
|
+
offline: options.offline
|
|
989
990
|
});
|
|
990
991
|
console.log("Regenerating derived files...\n");
|
|
991
992
|
const result = await refreshDerivedFiles(projectRoot, essence, registryClient);
|
|
@@ -2200,7 +2201,9 @@ ${YELLOW5}Next step:${RESET8} Ask your AI assistant to read ${BOLD3}.decantr/ana
|
|
|
2200
2201
|
`);
|
|
2201
2202
|
}
|
|
2202
2203
|
|
|
2203
|
-
// src/
|
|
2204
|
+
// src/commands/magic.ts
|
|
2205
|
+
import { join as join19 } from "path";
|
|
2206
|
+
import { existsSync as existsSync19 } from "fs";
|
|
2204
2207
|
var BOLD4 = "\x1B[1m";
|
|
2205
2208
|
var DIM9 = "\x1B[2m";
|
|
2206
2209
|
var RESET9 = "\x1B[0m";
|
|
@@ -2208,11 +2211,7 @@ var RED7 = "\x1B[31m";
|
|
|
2208
2211
|
var GREEN9 = "\x1B[32m";
|
|
2209
2212
|
var CYAN4 = "\x1B[36m";
|
|
2210
2213
|
var YELLOW6 = "\x1B[33m";
|
|
2211
|
-
|
|
2212
|
-
return `
|
|
2213
|
-
${BOLD4}${text}${RESET9}
|
|
2214
|
-
`;
|
|
2215
|
-
}
|
|
2214
|
+
var MAGENTA = "\x1B[35m";
|
|
2216
2215
|
function success(text) {
|
|
2217
2216
|
return `${GREEN9}${text}${RESET9}`;
|
|
2218
2217
|
}
|
|
@@ -2225,6 +2224,1022 @@ function dim(text) {
|
|
|
2225
2224
|
function cyan(text) {
|
|
2226
2225
|
return `${CYAN4}${text}${RESET9}`;
|
|
2227
2226
|
}
|
|
2227
|
+
var THEME_KEYWORDS = {
|
|
2228
|
+
dark: ["dark", "noir", "midnight"],
|
|
2229
|
+
light: ["light", "bright", "white"],
|
|
2230
|
+
neon: ["neon", "cyber", "cyberpunk", "synthwave"],
|
|
2231
|
+
glass: ["glass", "glassmorphic", "frosted", "translucent"],
|
|
2232
|
+
minimal: ["minimal", "minimalist", "clean", "simple"],
|
|
2233
|
+
warm: ["warm", "cozy", "earthy", "organic"],
|
|
2234
|
+
cool: ["cool", "cold", "icy", "arctic"],
|
|
2235
|
+
corporate: ["corporate", "enterprise", "business", "professional"],
|
|
2236
|
+
playful: ["playful", "fun", "vibrant", "colorful"],
|
|
2237
|
+
brutalist: ["brutalist", "brutal", "raw"],
|
|
2238
|
+
elegant: ["elegant", "luxury", "premium", "refined"],
|
|
2239
|
+
retro: ["retro", "vintage", "nostalgic"]
|
|
2240
|
+
};
|
|
2241
|
+
var ARCHETYPE_KEYWORDS = {
|
|
2242
|
+
"ai-chatbot": ["chat", "chatbot", "conversational", "ai-chat", "messaging"],
|
|
2243
|
+
"dashboard-analytics": ["dashboard", "analytics", "metrics", "monitoring", "overview"],
|
|
2244
|
+
"marketplace-platform": ["marketplace", "store", "shop", "ecommerce", "e-commerce"],
|
|
2245
|
+
"portfolio-creative": ["portfolio", "showcase", "gallery"],
|
|
2246
|
+
"blog-editorial": ["blog", "editorial", "journal", "magazine", "news"],
|
|
2247
|
+
"docs-knowledge": ["docs", "documentation", "knowledge", "wiki", "help-center"],
|
|
2248
|
+
"admin-panel": ["admin", "panel", "backoffice", "back-office", "management"],
|
|
2249
|
+
"saas-platform": ["saas", "platform", "subscription", "service"],
|
|
2250
|
+
"marketing-saas": ["landing", "marketing", "homepage", "launch"],
|
|
2251
|
+
"social-feed": ["social", "feed", "community", "forum"],
|
|
2252
|
+
"agent-orchestrator": ["agent", "orchestrator", "workflow", "automation", "pipeline"]
|
|
2253
|
+
};
|
|
2254
|
+
var CONSTRAINT_KEYWORDS = [
|
|
2255
|
+
"mobile-first",
|
|
2256
|
+
"mobile first",
|
|
2257
|
+
"accessible",
|
|
2258
|
+
"accessibility",
|
|
2259
|
+
"a11y",
|
|
2260
|
+
"wcag",
|
|
2261
|
+
"offline",
|
|
2262
|
+
"offline-first",
|
|
2263
|
+
"real-time",
|
|
2264
|
+
"realtime",
|
|
2265
|
+
"real time",
|
|
2266
|
+
"responsive",
|
|
2267
|
+
"high-contrast",
|
|
2268
|
+
"performance",
|
|
2269
|
+
"fast",
|
|
2270
|
+
"seo"
|
|
2271
|
+
];
|
|
2272
|
+
function parseMagicPrompt(prompt) {
|
|
2273
|
+
const lower = prompt.toLowerCase();
|
|
2274
|
+
const tokens = lower.split(/[\s,;—–\-]+/).filter(Boolean);
|
|
2275
|
+
const themeHints = [];
|
|
2276
|
+
for (const [hint, keywords] of Object.entries(THEME_KEYWORDS)) {
|
|
2277
|
+
if (keywords.some((kw) => tokens.includes(kw) || lower.includes(kw))) {
|
|
2278
|
+
themeHints.push(hint);
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
let archetype;
|
|
2282
|
+
let bestArchetypeScore = 0;
|
|
2283
|
+
for (const [archetypeId, keywords] of Object.entries(ARCHETYPE_KEYWORDS)) {
|
|
2284
|
+
const score = keywords.filter((kw) => tokens.includes(kw) || lower.includes(kw)).length;
|
|
2285
|
+
if (score > bestArchetypeScore) {
|
|
2286
|
+
bestArchetypeScore = score;
|
|
2287
|
+
archetype = archetypeId;
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
const constraints = [];
|
|
2291
|
+
for (const kw of CONSTRAINT_KEYWORDS) {
|
|
2292
|
+
if (lower.includes(kw)) {
|
|
2293
|
+
constraints.push(kw.replace(/\s+/g, "-"));
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
const personalityWords = [
|
|
2297
|
+
"confident",
|
|
2298
|
+
"bold",
|
|
2299
|
+
"sleek",
|
|
2300
|
+
"futuristic",
|
|
2301
|
+
"modern",
|
|
2302
|
+
"classic",
|
|
2303
|
+
"edgy",
|
|
2304
|
+
"sharp",
|
|
2305
|
+
"soft",
|
|
2306
|
+
"rounded",
|
|
2307
|
+
"angular",
|
|
2308
|
+
"geometric",
|
|
2309
|
+
"organic",
|
|
2310
|
+
"fluid",
|
|
2311
|
+
"rigid",
|
|
2312
|
+
"dense",
|
|
2313
|
+
"airy",
|
|
2314
|
+
"spacious",
|
|
2315
|
+
"technical",
|
|
2316
|
+
"creative",
|
|
2317
|
+
"artistic",
|
|
2318
|
+
"industrial",
|
|
2319
|
+
"natural",
|
|
2320
|
+
"techy",
|
|
2321
|
+
"hacker",
|
|
2322
|
+
"startup",
|
|
2323
|
+
"enterprise",
|
|
2324
|
+
"friendly",
|
|
2325
|
+
"serious",
|
|
2326
|
+
"formal",
|
|
2327
|
+
"casual",
|
|
2328
|
+
"approachable",
|
|
2329
|
+
"luxurious",
|
|
2330
|
+
"premium",
|
|
2331
|
+
"polished",
|
|
2332
|
+
"rough",
|
|
2333
|
+
"gritty"
|
|
2334
|
+
];
|
|
2335
|
+
const personalityHints = personalityWords.filter((w) => tokens.includes(w));
|
|
2336
|
+
const allRecognized = /* @__PURE__ */ new Set([
|
|
2337
|
+
...Object.values(THEME_KEYWORDS).flat(),
|
|
2338
|
+
...Object.values(ARCHETYPE_KEYWORDS).flat(),
|
|
2339
|
+
...CONSTRAINT_KEYWORDS.flatMap((c) => c.split(/\s+/)),
|
|
2340
|
+
...personalityWords
|
|
2341
|
+
]);
|
|
2342
|
+
const descTokens = tokens.filter(
|
|
2343
|
+
(t) => !allRecognized.has(t) && t.length > 2
|
|
2344
|
+
);
|
|
2345
|
+
const description = descTokens.length > 0 ? descTokens.join(" ") : prompt.trim();
|
|
2346
|
+
return {
|
|
2347
|
+
description,
|
|
2348
|
+
themeHints,
|
|
2349
|
+
personalityHints,
|
|
2350
|
+
constraints,
|
|
2351
|
+
archetype
|
|
2352
|
+
};
|
|
2353
|
+
}
|
|
2354
|
+
function resolveTheme(hints) {
|
|
2355
|
+
let mode = "dark";
|
|
2356
|
+
if (hints.includes("light")) mode = "light";
|
|
2357
|
+
if (hints.includes("neon") || hints.includes("glass")) return { style: "obsidianite", mode };
|
|
2358
|
+
if (hints.includes("warm") || hints.includes("elegant")) return { style: "aurealis", mode };
|
|
2359
|
+
if (hints.includes("cool") || hints.includes("minimal")) return { style: "glacialis", mode };
|
|
2360
|
+
if (hints.includes("brutalist")) return { style: "ferrocrete", mode };
|
|
2361
|
+
if (hints.includes("corporate")) return { style: "luminarum", mode };
|
|
2362
|
+
if (hints.includes("playful")) return { style: "solstice", mode };
|
|
2363
|
+
if (hints.includes("retro")) return { style: "oxidian", mode };
|
|
2364
|
+
return { style: "luminarum", mode };
|
|
2365
|
+
}
|
|
2366
|
+
function buildPersonality(intent) {
|
|
2367
|
+
if (intent.personalityHints.length > 0) {
|
|
2368
|
+
return intent.personalityHints;
|
|
2369
|
+
}
|
|
2370
|
+
if (intent.themeHints.includes("neon")) return ["bold", "futuristic"];
|
|
2371
|
+
if (intent.themeHints.includes("minimal")) return ["clean", "focused"];
|
|
2372
|
+
if (intent.themeHints.includes("corporate")) return ["professional", "reliable"];
|
|
2373
|
+
if (intent.themeHints.includes("playful")) return ["friendly", "energetic"];
|
|
2374
|
+
return ["professional"];
|
|
2375
|
+
}
|
|
2376
|
+
async function cmdMagic(prompt, projectRoot, options) {
|
|
2377
|
+
console.log("");
|
|
2378
|
+
console.log(`${MAGENTA}${BOLD4} Decantr Magic${RESET9}`);
|
|
2379
|
+
console.log("");
|
|
2380
|
+
const intent = parseMagicPrompt(prompt);
|
|
2381
|
+
console.log(`${BOLD4} Parsed intent:${RESET9}`);
|
|
2382
|
+
console.log(` Description: ${intent.description}`);
|
|
2383
|
+
if (intent.themeHints.length > 0) {
|
|
2384
|
+
console.log(` Theme: ${intent.themeHints.join(", ")}`);
|
|
2385
|
+
}
|
|
2386
|
+
if (intent.personalityHints.length > 0) {
|
|
2387
|
+
console.log(` Personality: ${intent.personalityHints.join(", ")}`);
|
|
2388
|
+
}
|
|
2389
|
+
if (intent.constraints.length > 0) {
|
|
2390
|
+
console.log(` Constraints: ${intent.constraints.join(", ")}`);
|
|
2391
|
+
}
|
|
2392
|
+
if (intent.archetype) {
|
|
2393
|
+
console.log(` Archetype: ${intent.archetype}`);
|
|
2394
|
+
}
|
|
2395
|
+
console.log("");
|
|
2396
|
+
const essencePath = join19(projectRoot, "decantr.essence.json");
|
|
2397
|
+
if (existsSync19(essencePath)) {
|
|
2398
|
+
console.log(error(" decantr.essence.json already exists in this directory."));
|
|
2399
|
+
console.log(dim(" Remove it first or use a different directory."));
|
|
2400
|
+
process.exitCode = 1;
|
|
2401
|
+
return;
|
|
2402
|
+
}
|
|
2403
|
+
const registryClient = new RegistryClient({
|
|
2404
|
+
cacheDir: join19(projectRoot, ".decantr", "cache"),
|
|
2405
|
+
apiUrl: options.registry,
|
|
2406
|
+
offline: options.offline
|
|
2407
|
+
});
|
|
2408
|
+
const apiAvailable = await registryClient.checkApiAvailability();
|
|
2409
|
+
let matchedBlueprint;
|
|
2410
|
+
let blueprintData;
|
|
2411
|
+
if (apiAvailable) {
|
|
2412
|
+
console.log(dim(" Searching registry for matching blueprints..."));
|
|
2413
|
+
const blueprintsResult = await registryClient.fetchBlueprints();
|
|
2414
|
+
const blueprints = blueprintsResult.data.items;
|
|
2415
|
+
const scored = blueprints.map((bp) => {
|
|
2416
|
+
const bpLower = `${bp.id} ${bp.name || ""} ${bp.description || ""}`.toLowerCase();
|
|
2417
|
+
let score = 0;
|
|
2418
|
+
if (intent.archetype) {
|
|
2419
|
+
const archetypeTokens = intent.archetype.split("-");
|
|
2420
|
+
for (const token of archetypeTokens) {
|
|
2421
|
+
if (bpLower.includes(token)) score += 3;
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
const descTokens = intent.description.toLowerCase().split(/\s+/);
|
|
2425
|
+
for (const token of descTokens) {
|
|
2426
|
+
if (token.length > 2 && bpLower.includes(token)) score += 2;
|
|
2427
|
+
}
|
|
2428
|
+
for (const hint of intent.themeHints) {
|
|
2429
|
+
if (bpLower.includes(hint)) score += 1;
|
|
2430
|
+
}
|
|
2431
|
+
return { id: bp.id, name: bp.name, description: bp.description, score };
|
|
2432
|
+
});
|
|
2433
|
+
scored.sort((a, b) => b.score - a.score);
|
|
2434
|
+
const best = scored[0];
|
|
2435
|
+
if (best && best.score >= 4) {
|
|
2436
|
+
matchedBlueprint = best.id;
|
|
2437
|
+
console.log("");
|
|
2438
|
+
console.log(`${BOLD4} Matched blueprint:${RESET9} ${cyan(best.id)}`);
|
|
2439
|
+
if (best.description) {
|
|
2440
|
+
console.log(` ${dim(best.description)}`);
|
|
2441
|
+
}
|
|
2442
|
+
const bpResult = await registryClient.fetchBlueprint(best.id);
|
|
2443
|
+
if (bpResult) {
|
|
2444
|
+
const rawBp = bpResult.data;
|
|
2445
|
+
blueprintData = rawBp.data ?? rawBp;
|
|
2446
|
+
}
|
|
2447
|
+
} else {
|
|
2448
|
+
console.log(dim(" No strong blueprint match found. Using archetype-based scaffold."));
|
|
2449
|
+
}
|
|
2450
|
+
} else {
|
|
2451
|
+
console.log(dim(" Offline mode \u2014 using defaults."));
|
|
2452
|
+
}
|
|
2453
|
+
const themeResolved = resolveTheme(intent.themeHints);
|
|
2454
|
+
const personality = buildPersonality(intent);
|
|
2455
|
+
const initOptions = {
|
|
2456
|
+
blueprint: matchedBlueprint,
|
|
2457
|
+
archetype: intent.archetype || blueprintData?.compose?.[0]?.archetype || blueprintData?.compose?.[0] || "dashboard-analytics",
|
|
2458
|
+
theme: themeResolved.style,
|
|
2459
|
+
mode: themeResolved.mode,
|
|
2460
|
+
shape: "rounded",
|
|
2461
|
+
target: "react",
|
|
2462
|
+
guard: "guided",
|
|
2463
|
+
density: intent.constraints.includes("mobile-first") ? "compact" : "comfortable",
|
|
2464
|
+
shell: "sidebar-main",
|
|
2465
|
+
personality,
|
|
2466
|
+
features: [],
|
|
2467
|
+
existing: false
|
|
2468
|
+
};
|
|
2469
|
+
if (blueprintData) {
|
|
2470
|
+
if (blueprintData.theme?.style) initOptions.theme = blueprintData.theme.style;
|
|
2471
|
+
if (blueprintData.theme?.mode) initOptions.mode = blueprintData.theme.mode;
|
|
2472
|
+
if (blueprintData.theme?.shape) initOptions.shape = blueprintData.theme.shape;
|
|
2473
|
+
if (blueprintData.personality) {
|
|
2474
|
+
initOptions.personality = typeof blueprintData.personality === "string" ? [blueprintData.personality] : blueprintData.personality;
|
|
2475
|
+
}
|
|
2476
|
+
if (intent.personalityHints.length > 0) {
|
|
2477
|
+
const merged = /* @__PURE__ */ new Set([...initOptions.personality, ...intent.personalityHints]);
|
|
2478
|
+
initOptions.personality = [...merged];
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
if (options.dryRun) {
|
|
2482
|
+
console.log("");
|
|
2483
|
+
console.log(`${YELLOW6}${BOLD4} Dry run \u2014 no files written${RESET9}`);
|
|
2484
|
+
console.log("");
|
|
2485
|
+
console.log(` Blueprint: ${matchedBlueprint || dim("(none \u2014 archetype-based)")}`);
|
|
2486
|
+
console.log(` Theme: ${initOptions.theme} (${initOptions.mode})`);
|
|
2487
|
+
console.log(` Personality: ${initOptions.personality.join(", ")}`);
|
|
2488
|
+
console.log(` Archetype: ${initOptions.archetype}`);
|
|
2489
|
+
console.log(` Guard: ${initOptions.guard}`);
|
|
2490
|
+
console.log(` Density: ${initOptions.density}`);
|
|
2491
|
+
console.log("");
|
|
2492
|
+
return;
|
|
2493
|
+
}
|
|
2494
|
+
console.log("");
|
|
2495
|
+
const detected = detectProject(projectRoot);
|
|
2496
|
+
let archetypeData;
|
|
2497
|
+
let composedSections;
|
|
2498
|
+
let routeMap;
|
|
2499
|
+
let patternSpecs;
|
|
2500
|
+
let topologyMarkdown = "";
|
|
2501
|
+
let themeData;
|
|
2502
|
+
let recipeData;
|
|
2503
|
+
let blueprintRecipeName;
|
|
2504
|
+
let registrySource = apiAvailable ? "api" : "cache";
|
|
2505
|
+
if (blueprintData?.compose && blueprintData.compose.length > 0) {
|
|
2506
|
+
const entries = blueprintData.compose;
|
|
2507
|
+
const archetypeMap = /* @__PURE__ */ new Map();
|
|
2508
|
+
for (const entry of entries) {
|
|
2509
|
+
const id = typeof entry === "string" ? entry : entry.archetype;
|
|
2510
|
+
const result2 = await registryClient.fetchArchetype(id);
|
|
2511
|
+
if (result2) {
|
|
2512
|
+
const raw = result2.data;
|
|
2513
|
+
archetypeMap.set(id, raw.data ?? raw);
|
|
2514
|
+
} else {
|
|
2515
|
+
archetypeMap.set(id, null);
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
composedSections = composeSections(entries, archetypeMap, blueprintData.overrides);
|
|
2519
|
+
const primaryId = typeof entries[0] === "string" ? entries[0] : entries[0].archetype;
|
|
2520
|
+
initOptions.archetype = primaryId;
|
|
2521
|
+
if (composedSections.sections[0]?.shell) {
|
|
2522
|
+
initOptions.shell = composedSections.sections[0].shell;
|
|
2523
|
+
}
|
|
2524
|
+
const allPages = composedSections.sections.flatMap(
|
|
2525
|
+
(s) => s.pages.map((p) => ({
|
|
2526
|
+
id: p.id,
|
|
2527
|
+
shell: p.shell_override || s.shell,
|
|
2528
|
+
default_layout: p.layout
|
|
2529
|
+
}))
|
|
2530
|
+
);
|
|
2531
|
+
archetypeData = {
|
|
2532
|
+
id: primaryId,
|
|
2533
|
+
pages: allPages,
|
|
2534
|
+
features: composedSections.features
|
|
2535
|
+
};
|
|
2536
|
+
routeMap = {};
|
|
2537
|
+
if (blueprintData.routes) {
|
|
2538
|
+
for (const [path, entry] of Object.entries(blueprintData.routes)) {
|
|
2539
|
+
if (entry.archetype && entry.page) {
|
|
2540
|
+
routeMap[path] = { section: entry.archetype, page: entry.page };
|
|
2541
|
+
const section = composedSections.sections.find((s) => s.id === entry.archetype);
|
|
2542
|
+
const page = section?.pages.find((p) => p.id === entry.page);
|
|
2543
|
+
if (page) page.route = path;
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
const allPatternIds = /* @__PURE__ */ new Set();
|
|
2548
|
+
for (const section of composedSections.sections) {
|
|
2549
|
+
for (const page of section.pages) {
|
|
2550
|
+
if (page.patterns) {
|
|
2551
|
+
for (const ref of page.patterns) allPatternIds.add(ref.pattern);
|
|
2552
|
+
}
|
|
2553
|
+
for (const item of page.layout) {
|
|
2554
|
+
if (typeof item === "string") allPatternIds.add(item);
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
patternSpecs = {};
|
|
2559
|
+
for (const pid of allPatternIds) {
|
|
2560
|
+
try {
|
|
2561
|
+
const result2 = await registryClient.fetchPattern(pid);
|
|
2562
|
+
if (result2) {
|
|
2563
|
+
const raw = result2.data;
|
|
2564
|
+
const inner = raw.data ?? raw;
|
|
2565
|
+
const defaultPreset = inner.default_preset || "standard";
|
|
2566
|
+
const preset = inner.presets?.[defaultPreset];
|
|
2567
|
+
patternSpecs[pid] = {
|
|
2568
|
+
description: inner.description || "",
|
|
2569
|
+
components: inner.components || [],
|
|
2570
|
+
slots: preset?.layout?.slots || {}
|
|
2571
|
+
};
|
|
2572
|
+
}
|
|
2573
|
+
} catch {
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
const zoneInputs = [];
|
|
2577
|
+
for (const entry of entries) {
|
|
2578
|
+
const arcId = typeof entry === "string" ? entry : entry.archetype;
|
|
2579
|
+
const archData = archetypeMap.get(arcId);
|
|
2580
|
+
if (archData) {
|
|
2581
|
+
const explicitRole = typeof entry === "object" && "role" in entry ? entry.role : void 0;
|
|
2582
|
+
zoneInputs.push({
|
|
2583
|
+
archetypeId: arcId,
|
|
2584
|
+
role: explicitRole || archData.role || "auxiliary",
|
|
2585
|
+
shell: archData.pages?.[0]?.shell || initOptions.shell,
|
|
2586
|
+
features: archData.features || [],
|
|
2587
|
+
description: archData.description || ""
|
|
2588
|
+
});
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
const zones = deriveZones(zoneInputs);
|
|
2592
|
+
const transitions = deriveTransitions(zones);
|
|
2593
|
+
if (zones.length > 0) {
|
|
2594
|
+
topologyMarkdown = generateTopologySection(
|
|
2595
|
+
{
|
|
2596
|
+
intent: blueprintData.description || initOptions.archetype || "Application",
|
|
2597
|
+
zones,
|
|
2598
|
+
transitions,
|
|
2599
|
+
entryPoints: {
|
|
2600
|
+
anonymous: "/",
|
|
2601
|
+
authenticated: `/${archetypeData.pages?.[0]?.id || "home"}`
|
|
2602
|
+
}
|
|
2603
|
+
},
|
|
2604
|
+
initOptions.personality
|
|
2605
|
+
);
|
|
2606
|
+
}
|
|
2607
|
+
blueprintRecipeName = blueprintData.theme?.recipe;
|
|
2608
|
+
console.log(`${BOLD4} Composition:${RESET9}`);
|
|
2609
|
+
console.log(` Sections: ${composedSections.sections.length} (${composedSections.sections.map((s) => s.id).join(", ")})`);
|
|
2610
|
+
const totalRoutes = Object.keys(routeMap).length;
|
|
2611
|
+
if (totalRoutes > 0) console.log(` Routes: ${totalRoutes}`);
|
|
2612
|
+
if (composedSections.features.length > 0) {
|
|
2613
|
+
console.log(` Features: ${composedSections.features.join(", ")}`);
|
|
2614
|
+
}
|
|
2615
|
+
console.log("");
|
|
2616
|
+
} else if (intent.archetype && apiAvailable) {
|
|
2617
|
+
const archResult = await registryClient.fetchArchetype(initOptions.archetype);
|
|
2618
|
+
if (archResult) {
|
|
2619
|
+
const raw = archResult.data;
|
|
2620
|
+
archetypeData = raw.data ?? raw;
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
if (apiAvailable && initOptions.theme) {
|
|
2624
|
+
const themeResult = await registryClient.fetchTheme(initOptions.theme);
|
|
2625
|
+
if (themeResult) {
|
|
2626
|
+
const rawTheme = themeResult.data;
|
|
2627
|
+
const theme = rawTheme.data ?? rawTheme;
|
|
2628
|
+
themeData = {
|
|
2629
|
+
seed: theme.seed,
|
|
2630
|
+
palette: theme.palette,
|
|
2631
|
+
tokens: theme.tokens,
|
|
2632
|
+
cvd_support: theme.cvd_support,
|
|
2633
|
+
typography_hints: theme.typography_hints,
|
|
2634
|
+
motion_hints: theme.motion_hints
|
|
2635
|
+
};
|
|
2636
|
+
if (theme.decorators) {
|
|
2637
|
+
recipeData = { decorators: theme.decorators };
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
const recipeName = blueprintRecipeName || initOptions.theme;
|
|
2641
|
+
const recipeResult = await registryClient.fetchRecipe(recipeName);
|
|
2642
|
+
if (recipeResult) {
|
|
2643
|
+
const rawRecipe = recipeResult.data;
|
|
2644
|
+
const recipe = rawRecipe.data ?? rawRecipe;
|
|
2645
|
+
recipeData = {
|
|
2646
|
+
decorators: recipe.decorators || recipeData?.decorators,
|
|
2647
|
+
spatial_hints: recipe.spatial_hints,
|
|
2648
|
+
radius_hints: recipe.radius_hints
|
|
2649
|
+
};
|
|
2650
|
+
}
|
|
2651
|
+
}
|
|
2652
|
+
console.log(`${BOLD4} Scaffolding...${RESET9}`);
|
|
2653
|
+
const result = await scaffoldProject(
|
|
2654
|
+
projectRoot,
|
|
2655
|
+
initOptions,
|
|
2656
|
+
detected,
|
|
2657
|
+
registryClient,
|
|
2658
|
+
archetypeData,
|
|
2659
|
+
registrySource,
|
|
2660
|
+
themeData,
|
|
2661
|
+
recipeData,
|
|
2662
|
+
topologyMarkdown,
|
|
2663
|
+
composedSections,
|
|
2664
|
+
routeMap,
|
|
2665
|
+
patternSpecs,
|
|
2666
|
+
blueprintData
|
|
2667
|
+
);
|
|
2668
|
+
console.log(` ${success("Created")} decantr.essence.json (V3.1)`);
|
|
2669
|
+
console.log(` ${success("Created")} DECANTR.md`);
|
|
2670
|
+
if (result.cssFiles && result.cssFiles.length > 0) {
|
|
2671
|
+
for (const cssFile of result.cssFiles) {
|
|
2672
|
+
const name = cssFile.split("/").pop();
|
|
2673
|
+
console.log(` ${success("Created")} src/styles/${name}`);
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
if (result.contextFiles && result.contextFiles.length > 0) {
|
|
2677
|
+
const sectionContexts = result.contextFiles.filter((f) => f.includes("section-"));
|
|
2678
|
+
const otherContexts = result.contextFiles.filter((f) => !f.includes("section-"));
|
|
2679
|
+
if (sectionContexts.length > 0) {
|
|
2680
|
+
console.log(` ${success("Created")} ${sectionContexts.length} section context(s)`);
|
|
2681
|
+
}
|
|
2682
|
+
for (const f of otherContexts) {
|
|
2683
|
+
const name = f.split("/").pop();
|
|
2684
|
+
if (name && !name.startsWith("task-") && !name.startsWith("essence-summary")) {
|
|
2685
|
+
console.log(` ${success("Created")} ${name}`);
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
if (result.gitignoreUpdated) {
|
|
2690
|
+
console.log(` ${dim(".gitignore updated")}`);
|
|
2691
|
+
}
|
|
2692
|
+
console.log("");
|
|
2693
|
+
console.log(`${BOLD4} Ready!${RESET9} Next steps:`);
|
|
2694
|
+
console.log(` 1. Read ${cyan("DECANTR.md")} to understand the design system`);
|
|
2695
|
+
console.log(` 2. Read ${cyan(".decantr/context/scaffold.md")} for the full app overview`);
|
|
2696
|
+
console.log(` 3. Start building pages from the route map`);
|
|
2697
|
+
console.log("");
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2700
|
+
// src/commands/export.ts
|
|
2701
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync10, existsSync as existsSync20, mkdirSync as mkdirSync5 } from "fs";
|
|
2702
|
+
import { join as join20, dirname } from "path";
|
|
2703
|
+
var GREEN10 = "\x1B[32m";
|
|
2704
|
+
var RED8 = "\x1B[31m";
|
|
2705
|
+
var DIM10 = "\x1B[2m";
|
|
2706
|
+
var RESET10 = "\x1B[0m";
|
|
2707
|
+
var SHADCN_MAP = {
|
|
2708
|
+
"--d-bg": "--background",
|
|
2709
|
+
"--d-text": "--foreground",
|
|
2710
|
+
"--d-surface": "--card",
|
|
2711
|
+
"--d-surface-raised": "--popover",
|
|
2712
|
+
"--d-primary": "--primary",
|
|
2713
|
+
"--d-secondary": "--secondary",
|
|
2714
|
+
"--d-accent": "--accent",
|
|
2715
|
+
"--d-border": "--border",
|
|
2716
|
+
"--d-text-muted": "--muted-foreground",
|
|
2717
|
+
"--d-error": "--destructive",
|
|
2718
|
+
"--d-radius": "--radius"
|
|
2719
|
+
};
|
|
2720
|
+
var TAILWIND_COLOR_MAP = {
|
|
2721
|
+
"--d-primary": "primary",
|
|
2722
|
+
"--d-secondary": "secondary",
|
|
2723
|
+
"--d-accent": "accent",
|
|
2724
|
+
"--d-bg": "background",
|
|
2725
|
+
"--d-surface": "surface",
|
|
2726
|
+
"--d-surface-raised": "surface-raised",
|
|
2727
|
+
"--d-border": "border",
|
|
2728
|
+
"--d-text": "foreground",
|
|
2729
|
+
"--d-text-muted": "muted",
|
|
2730
|
+
"--d-success": "success",
|
|
2731
|
+
"--d-error": "error",
|
|
2732
|
+
"--d-warning": "warning",
|
|
2733
|
+
"--d-info": "info"
|
|
2734
|
+
};
|
|
2735
|
+
function parseTokensCSS(css) {
|
|
2736
|
+
const tokens = /* @__PURE__ */ new Map();
|
|
2737
|
+
const re = /(--d-[\w-]+)\s*:\s*([^;]+);/g;
|
|
2738
|
+
let match;
|
|
2739
|
+
while ((match = re.exec(css)) !== null) {
|
|
2740
|
+
tokens.set(match[1], match[2].trim());
|
|
2741
|
+
}
|
|
2742
|
+
return tokens;
|
|
2743
|
+
}
|
|
2744
|
+
function generateShadcnCSS(tokens) {
|
|
2745
|
+
const lines = ["/* Exported by decantr export --to shadcn */", "@layer base {", " :root {"];
|
|
2746
|
+
for (const [decantrVar, shadcnVar] of Object.entries(SHADCN_MAP)) {
|
|
2747
|
+
const value = tokens.get(decantrVar);
|
|
2748
|
+
if (value) {
|
|
2749
|
+
lines.push(` ${shadcnVar}: ${value};`);
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
const textValue = tokens.get("--d-text");
|
|
2753
|
+
if (textValue) {
|
|
2754
|
+
lines.push(` --card-foreground: ${textValue};`);
|
|
2755
|
+
lines.push(` --popover-foreground: ${textValue};`);
|
|
2756
|
+
}
|
|
2757
|
+
const surfaceValue = tokens.get("--d-surface");
|
|
2758
|
+
if (surfaceValue) {
|
|
2759
|
+
lines.push(` --muted: ${surfaceValue};`);
|
|
2760
|
+
}
|
|
2761
|
+
const accentFg = tokens.get("--d-text");
|
|
2762
|
+
if (accentFg) {
|
|
2763
|
+
lines.push(` --accent-foreground: ${accentFg};`);
|
|
2764
|
+
}
|
|
2765
|
+
lines.push(` --destructive-foreground: #FAFAFA;`);
|
|
2766
|
+
lines.push(" }", "}", "");
|
|
2767
|
+
return lines.join("\n");
|
|
2768
|
+
}
|
|
2769
|
+
function generateShadcnComponentsJSON() {
|
|
2770
|
+
const config = {
|
|
2771
|
+
$schema: "https://ui.shadcn.com/schema.json",
|
|
2772
|
+
style: "default",
|
|
2773
|
+
rsc: false,
|
|
2774
|
+
tsx: true,
|
|
2775
|
+
tailwind: {
|
|
2776
|
+
config: "tailwind.config.ts",
|
|
2777
|
+
css: "src/styles/shadcn-theme.css",
|
|
2778
|
+
baseColor: "zinc"
|
|
2779
|
+
},
|
|
2780
|
+
aliases: {
|
|
2781
|
+
components: "@/components",
|
|
2782
|
+
utils: "@/lib/utils"
|
|
2783
|
+
}
|
|
2784
|
+
};
|
|
2785
|
+
return JSON.stringify(config, null, 2) + "\n";
|
|
2786
|
+
}
|
|
2787
|
+
function generateTailwindConfig(tokens) {
|
|
2788
|
+
const colors = {};
|
|
2789
|
+
for (const [decantrVar, twName] of Object.entries(TAILWIND_COLOR_MAP)) {
|
|
2790
|
+
const value = tokens.get(decantrVar);
|
|
2791
|
+
if (value) {
|
|
2792
|
+
colors[twName] = value;
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
const borderRadius = {};
|
|
2796
|
+
for (const suffix of ["", "-sm", "-lg", "-xl", "-full"]) {
|
|
2797
|
+
const key = `--d-radius${suffix}`;
|
|
2798
|
+
const value = tokens.get(key);
|
|
2799
|
+
if (value) {
|
|
2800
|
+
const name = suffix ? suffix.slice(1) : "DEFAULT";
|
|
2801
|
+
borderRadius[name] = value;
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
const boxShadow = {};
|
|
2805
|
+
for (const suffix of ["-sm", "", "-md", "-lg"]) {
|
|
2806
|
+
const key = `--d-shadow${suffix}`;
|
|
2807
|
+
const value = tokens.get(key);
|
|
2808
|
+
if (value) {
|
|
2809
|
+
const name = suffix ? suffix.slice(1) : "DEFAULT";
|
|
2810
|
+
boxShadow[name] = value;
|
|
2811
|
+
}
|
|
2812
|
+
}
|
|
2813
|
+
const lines = [
|
|
2814
|
+
"// Generated by decantr export --to tailwind",
|
|
2815
|
+
"import type { Config } from 'tailwindcss';",
|
|
2816
|
+
"",
|
|
2817
|
+
"export default {",
|
|
2818
|
+
" theme: {",
|
|
2819
|
+
" extend: {"
|
|
2820
|
+
];
|
|
2821
|
+
lines.push(" colors: {");
|
|
2822
|
+
for (const [name, value] of Object.entries(colors)) {
|
|
2823
|
+
lines.push(` '${name}': '${value}',`);
|
|
2824
|
+
}
|
|
2825
|
+
lines.push(" },");
|
|
2826
|
+
lines.push(" borderRadius: {");
|
|
2827
|
+
for (const [name, value] of Object.entries(borderRadius)) {
|
|
2828
|
+
lines.push(` '${name}': '${value}',`);
|
|
2829
|
+
}
|
|
2830
|
+
lines.push(" },");
|
|
2831
|
+
lines.push(" boxShadow: {");
|
|
2832
|
+
for (const [name, value] of Object.entries(boxShadow)) {
|
|
2833
|
+
lines.push(` '${name}': '${value}',`);
|
|
2834
|
+
}
|
|
2835
|
+
lines.push(" },");
|
|
2836
|
+
lines.push(" },", " },", "} satisfies Partial<Config>;", "");
|
|
2837
|
+
return lines.join("\n");
|
|
2838
|
+
}
|
|
2839
|
+
function generateCSSVars(tokens) {
|
|
2840
|
+
const lines = ["/* Exported by decantr export --to css-vars */", ":root {"];
|
|
2841
|
+
for (const [key, value] of tokens) {
|
|
2842
|
+
lines.push(` ${key}: ${value};`);
|
|
2843
|
+
}
|
|
2844
|
+
lines.push("}", "");
|
|
2845
|
+
return lines.join("\n");
|
|
2846
|
+
}
|
|
2847
|
+
async function cmdExport(target, projectRoot, options = {}) {
|
|
2848
|
+
const essencePath = join20(projectRoot, "decantr.essence.json");
|
|
2849
|
+
const tokensPath = join20(projectRoot, "src", "styles", "tokens.css");
|
|
2850
|
+
if (!existsSync20(essencePath)) {
|
|
2851
|
+
console.error(`${RED8}No decantr.essence.json found. Run \`decantr init\` first.${RESET10}`);
|
|
2852
|
+
process.exitCode = 1;
|
|
2853
|
+
return;
|
|
2854
|
+
}
|
|
2855
|
+
if (!existsSync20(tokensPath)) {
|
|
2856
|
+
console.error(`${RED8}No src/styles/tokens.css found. Run \`decantr refresh\` to generate tokens.${RESET10}`);
|
|
2857
|
+
process.exitCode = 1;
|
|
2858
|
+
return;
|
|
2859
|
+
}
|
|
2860
|
+
const tokensCSS = readFileSync14(tokensPath, "utf-8");
|
|
2861
|
+
const tokens = parseTokensCSS(tokensCSS);
|
|
2862
|
+
if (tokens.size === 0) {
|
|
2863
|
+
console.error(`${RED8}No --d-* tokens found in tokens.css.${RESET10}`);
|
|
2864
|
+
process.exitCode = 1;
|
|
2865
|
+
return;
|
|
2866
|
+
}
|
|
2867
|
+
switch (target) {
|
|
2868
|
+
case "shadcn": {
|
|
2869
|
+
const cssOut = options.output ?? join20(projectRoot, "src", "styles", "shadcn-theme.css");
|
|
2870
|
+
const jsonOut = join20(projectRoot, "components.json");
|
|
2871
|
+
ensureDir(cssOut);
|
|
2872
|
+
writeFileSync10(cssOut, generateShadcnCSS(tokens), "utf-8");
|
|
2873
|
+
writeFileSync10(jsonOut, generateShadcnComponentsJSON(), "utf-8");
|
|
2874
|
+
console.log(`${GREEN10}Exported shadcn theme:${RESET10}`);
|
|
2875
|
+
console.log(` ${DIM10}CSS:${RESET10} ${cssOut}`);
|
|
2876
|
+
console.log(` ${DIM10}JSON:${RESET10} ${jsonOut}`);
|
|
2877
|
+
break;
|
|
2878
|
+
}
|
|
2879
|
+
case "tailwind": {
|
|
2880
|
+
const out = options.output ?? join20(projectRoot, "tailwind.decantr.config.ts");
|
|
2881
|
+
ensureDir(out);
|
|
2882
|
+
writeFileSync10(out, generateTailwindConfig(tokens), "utf-8");
|
|
2883
|
+
console.log(`${GREEN10}Exported Tailwind config:${RESET10}`);
|
|
2884
|
+
console.log(` ${DIM10}File:${RESET10} ${out}`);
|
|
2885
|
+
break;
|
|
2886
|
+
}
|
|
2887
|
+
case "css-vars": {
|
|
2888
|
+
const out = options.output ?? join20(projectRoot, "decantr-tokens.css");
|
|
2889
|
+
ensureDir(out);
|
|
2890
|
+
writeFileSync10(out, generateCSSVars(tokens), "utf-8");
|
|
2891
|
+
console.log(`${GREEN10}Exported CSS variables:${RESET10}`);
|
|
2892
|
+
console.log(` ${DIM10}File:${RESET10} ${out}`);
|
|
2893
|
+
break;
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
function ensureDir(filePath) {
|
|
2898
|
+
const dir = dirname(filePath);
|
|
2899
|
+
if (!existsSync20(dir)) {
|
|
2900
|
+
mkdirSync5(dir, { recursive: true });
|
|
2901
|
+
}
|
|
2902
|
+
}
|
|
2903
|
+
|
|
2904
|
+
// src/commands/registry-mirror.ts
|
|
2905
|
+
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync11 } from "fs";
|
|
2906
|
+
import { join as join21 } from "path";
|
|
2907
|
+
import { RegistryAPIClient as RegistryAPIClient2 } from "@decantr/registry";
|
|
2908
|
+
var GREEN11 = "\x1B[32m";
|
|
2909
|
+
var RED9 = "\x1B[31m";
|
|
2910
|
+
var DIM11 = "\x1B[2m";
|
|
2911
|
+
var CYAN5 = "\x1B[36m";
|
|
2912
|
+
var YELLOW7 = "\x1B[33m";
|
|
2913
|
+
var RESET11 = "\x1B[0m";
|
|
2914
|
+
var ALL_CONTENT_TYPES = [
|
|
2915
|
+
"patterns",
|
|
2916
|
+
"archetypes",
|
|
2917
|
+
"themes",
|
|
2918
|
+
"recipes",
|
|
2919
|
+
"blueprints",
|
|
2920
|
+
"shells"
|
|
2921
|
+
];
|
|
2922
|
+
async function cmdRegistryMirror(projectRoot, options = {}) {
|
|
2923
|
+
const apiUrl = process.env.DECANTR_API_URL || "https://api.decantr.ai/v1";
|
|
2924
|
+
const apiClient = new RegistryAPIClient2({
|
|
2925
|
+
baseUrl: apiUrl,
|
|
2926
|
+
apiKey: process.env.DECANTR_API_KEY || void 0
|
|
2927
|
+
});
|
|
2928
|
+
const healthy = await apiClient.checkHealth();
|
|
2929
|
+
if (!healthy) {
|
|
2930
|
+
console.error(`${RED9}Registry API is unavailable. Check your connection.${RESET11}`);
|
|
2931
|
+
process.exitCode = 1;
|
|
2932
|
+
return;
|
|
2933
|
+
}
|
|
2934
|
+
const types = options.type ? [options.type] : ALL_CONTENT_TYPES;
|
|
2935
|
+
if (options.type && !ALL_CONTENT_TYPES.includes(options.type)) {
|
|
2936
|
+
console.error(`${RED9}Unknown content type: ${options.type}${RESET11}`);
|
|
2937
|
+
console.error(`${DIM11}Valid types: ${ALL_CONTENT_TYPES.join(", ")}${RESET11}`);
|
|
2938
|
+
process.exitCode = 1;
|
|
2939
|
+
return;
|
|
2940
|
+
}
|
|
2941
|
+
const cacheDir = join21(projectRoot, ".decantr", "cache");
|
|
2942
|
+
const counts = {};
|
|
2943
|
+
const failed = [];
|
|
2944
|
+
console.log(`
|
|
2945
|
+
Mirroring registry content to ${DIM11}.decantr/cache/${RESET11}
|
|
2946
|
+
`);
|
|
2947
|
+
for (const type of types) {
|
|
2948
|
+
try {
|
|
2949
|
+
const result = await apiClient.listContent(type, { namespace: "@official" });
|
|
2950
|
+
const items = result.items;
|
|
2951
|
+
const typeDir = join21(cacheDir, "@official", type);
|
|
2952
|
+
mkdirSync6(typeDir, { recursive: true });
|
|
2953
|
+
writeFileSync11(join21(typeDir, "index.json"), JSON.stringify(result, null, 2));
|
|
2954
|
+
let itemCount = 0;
|
|
2955
|
+
for (const item of items) {
|
|
2956
|
+
const slug = item.slug || item.id;
|
|
2957
|
+
if (!slug) continue;
|
|
2958
|
+
try {
|
|
2959
|
+
const fullItem = await apiClient.getContent(type, "@official", slug);
|
|
2960
|
+
writeFileSync11(join21(typeDir, `${slug}.json`), JSON.stringify(fullItem, null, 2));
|
|
2961
|
+
itemCount++;
|
|
2962
|
+
} catch {
|
|
2963
|
+
writeFileSync11(join21(typeDir, `${slug}.json`), JSON.stringify(item, null, 2));
|
|
2964
|
+
itemCount++;
|
|
2965
|
+
}
|
|
2966
|
+
}
|
|
2967
|
+
counts[type] = itemCount;
|
|
2968
|
+
console.log(` ${GREEN11}\u2713${RESET11} ${type}: ${CYAN5}${itemCount}${RESET11} items`);
|
|
2969
|
+
} catch (e) {
|
|
2970
|
+
failed.push(type);
|
|
2971
|
+
console.log(` ${RED9}\u2717${RESET11} ${type}: ${e.message}`);
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2974
|
+
const manifest = {
|
|
2975
|
+
mirrored_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2976
|
+
counts
|
|
2977
|
+
};
|
|
2978
|
+
mkdirSync6(join21(cacheDir), { recursive: true });
|
|
2979
|
+
writeFileSync11(join21(cacheDir, "mirror-manifest.json"), JSON.stringify(manifest, null, 2));
|
|
2980
|
+
const totalItems = Object.values(counts).reduce((a, b) => a + b, 0);
|
|
2981
|
+
console.log("");
|
|
2982
|
+
if (failed.length > 0) {
|
|
2983
|
+
console.log(`${YELLOW7}Mirrored ${totalItems} items (${failed.length} type(s) failed)${RESET11}`);
|
|
2984
|
+
} else {
|
|
2985
|
+
console.log(`${GREEN11}Mirrored ${totalItems} items across ${Object.keys(counts).length} types${RESET11}`);
|
|
2986
|
+
}
|
|
2987
|
+
console.log(`${DIM11}Use \`decantr init --offline\` or \`decantr refresh --offline\` to work without API.${RESET11}
|
|
2988
|
+
`);
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2991
|
+
// src/commands/new-project.ts
|
|
2992
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync7, writeFileSync as writeFileSync12 } from "fs";
|
|
2993
|
+
import { join as join22, resolve } from "path";
|
|
2994
|
+
import { execSync } from "child_process";
|
|
2995
|
+
var BOLD5 = "\x1B[1m";
|
|
2996
|
+
var DIM12 = "\x1B[2m";
|
|
2997
|
+
var RESET12 = "\x1B[0m";
|
|
2998
|
+
var RED10 = "\x1B[31m";
|
|
2999
|
+
var GREEN12 = "\x1B[32m";
|
|
3000
|
+
var CYAN6 = "\x1B[36m";
|
|
3001
|
+
var YELLOW8 = "\x1B[33m";
|
|
3002
|
+
function heading(text) {
|
|
3003
|
+
return `
|
|
3004
|
+
${BOLD5}${text}${RESET12}
|
|
3005
|
+
`;
|
|
3006
|
+
}
|
|
3007
|
+
function success2(text) {
|
|
3008
|
+
return `${GREEN12}${text}${RESET12}`;
|
|
3009
|
+
}
|
|
3010
|
+
function error2(text) {
|
|
3011
|
+
return `${RED10}${text}${RESET12}`;
|
|
3012
|
+
}
|
|
3013
|
+
function dim2(text) {
|
|
3014
|
+
return `${DIM12}${text}${RESET12}`;
|
|
3015
|
+
}
|
|
3016
|
+
function cyan2(text) {
|
|
3017
|
+
return `${CYAN6}${text}${RESET12}`;
|
|
3018
|
+
}
|
|
3019
|
+
async function cmdNewProject(projectName, options) {
|
|
3020
|
+
const projectDir = resolve(process.cwd(), projectName);
|
|
3021
|
+
if (!/^[a-z0-9][a-z0-9._-]*$/i.test(projectName)) {
|
|
3022
|
+
console.error(error2("Invalid project name. Use alphanumeric characters, hyphens, dots, or underscores."));
|
|
3023
|
+
process.exitCode = 1;
|
|
3024
|
+
return;
|
|
3025
|
+
}
|
|
3026
|
+
if (existsSync22(projectDir)) {
|
|
3027
|
+
console.error(error2(`Directory "${projectName}" already exists.`));
|
|
3028
|
+
process.exitCode = 1;
|
|
3029
|
+
return;
|
|
3030
|
+
}
|
|
3031
|
+
console.log(heading(`Creating ${projectName}...`));
|
|
3032
|
+
mkdirSync7(projectDir, { recursive: true });
|
|
3033
|
+
console.log(dim2(` Created ${projectName}/`));
|
|
3034
|
+
const packageJson = {
|
|
3035
|
+
name: projectName,
|
|
3036
|
+
private: true,
|
|
3037
|
+
version: "0.0.0",
|
|
3038
|
+
type: "module",
|
|
3039
|
+
scripts: {
|
|
3040
|
+
dev: "vite",
|
|
3041
|
+
build: "tsc -b && vite build",
|
|
3042
|
+
preview: "vite preview"
|
|
3043
|
+
},
|
|
3044
|
+
dependencies: {
|
|
3045
|
+
"react": "^19.0.0",
|
|
3046
|
+
"react-dom": "^19.0.0",
|
|
3047
|
+
"react-router-dom": "^7.0.0",
|
|
3048
|
+
"@decantr/css": "^1.0.0"
|
|
3049
|
+
},
|
|
3050
|
+
devDependencies: {
|
|
3051
|
+
"@types/react": "^19.0.0",
|
|
3052
|
+
"@types/react-dom": "^19.0.0",
|
|
3053
|
+
"@vitejs/plugin-react": "^4.0.0",
|
|
3054
|
+
"typescript": "^5.7.0",
|
|
3055
|
+
"vite": "^6.0.0"
|
|
3056
|
+
}
|
|
3057
|
+
};
|
|
3058
|
+
writeFileSync12(join22(projectDir, "package.json"), JSON.stringify(packageJson, null, 2) + "\n");
|
|
3059
|
+
console.log(dim2(" Created package.json"));
|
|
3060
|
+
const viteConfig = `import { defineConfig } from 'vite';
|
|
3061
|
+
import react from '@vitejs/plugin-react';
|
|
3062
|
+
|
|
3063
|
+
export default defineConfig({
|
|
3064
|
+
plugins: [react()],
|
|
3065
|
+
});
|
|
3066
|
+
`;
|
|
3067
|
+
writeFileSync12(join22(projectDir, "vite.config.ts"), viteConfig);
|
|
3068
|
+
console.log(dim2(" Created vite.config.ts"));
|
|
3069
|
+
const tsconfig = {
|
|
3070
|
+
compilerOptions: {
|
|
3071
|
+
target: "ES2020",
|
|
3072
|
+
useDefineForClassFields: true,
|
|
3073
|
+
lib: ["ES2020", "DOM", "DOM.Iterable"],
|
|
3074
|
+
module: "ESNext",
|
|
3075
|
+
skipLibCheck: true,
|
|
3076
|
+
moduleResolution: "bundler",
|
|
3077
|
+
allowImportingTsExtensions: true,
|
|
3078
|
+
isolatedModules: true,
|
|
3079
|
+
moduleDetection: "force",
|
|
3080
|
+
noEmit: true,
|
|
3081
|
+
jsx: "react-jsx",
|
|
3082
|
+
strict: true,
|
|
3083
|
+
noUnusedLocals: true,
|
|
3084
|
+
noUnusedParameters: true,
|
|
3085
|
+
noFallthroughCasesInSwitch: true,
|
|
3086
|
+
noUncheckedSideEffectImports: true
|
|
3087
|
+
},
|
|
3088
|
+
include: ["src"]
|
|
3089
|
+
};
|
|
3090
|
+
writeFileSync12(join22(projectDir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2) + "\n");
|
|
3091
|
+
const tsconfigApp = {
|
|
3092
|
+
compilerOptions: {
|
|
3093
|
+
tsBuildInfoFile: "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
3094
|
+
target: "ES2020",
|
|
3095
|
+
useDefineForClassFields: true,
|
|
3096
|
+
lib: ["ES2020", "DOM", "DOM.Iterable"],
|
|
3097
|
+
module: "ESNext",
|
|
3098
|
+
skipLibCheck: true,
|
|
3099
|
+
moduleResolution: "bundler",
|
|
3100
|
+
allowImportingTsExtensions: true,
|
|
3101
|
+
isolatedModules: true,
|
|
3102
|
+
moduleDetection: "force",
|
|
3103
|
+
noEmit: true,
|
|
3104
|
+
jsx: "react-jsx",
|
|
3105
|
+
strict: true,
|
|
3106
|
+
noUnusedLocals: true,
|
|
3107
|
+
noUnusedParameters: true,
|
|
3108
|
+
noFallthroughCasesInSwitch: true,
|
|
3109
|
+
noUncheckedSideEffectImports: true
|
|
3110
|
+
},
|
|
3111
|
+
include: ["src"]
|
|
3112
|
+
};
|
|
3113
|
+
writeFileSync12(join22(projectDir, "tsconfig.app.json"), JSON.stringify(tsconfigApp, null, 2) + "\n");
|
|
3114
|
+
console.log(dim2(" Created tsconfig.json"));
|
|
3115
|
+
const title = projectName.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
3116
|
+
const indexHtml = `<!doctype html>
|
|
3117
|
+
<html lang="en">
|
|
3118
|
+
<head>
|
|
3119
|
+
<meta charset="UTF-8" />
|
|
3120
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
3121
|
+
<title>${title}</title>
|
|
3122
|
+
</head>
|
|
3123
|
+
<body>
|
|
3124
|
+
<div id="root"></div>
|
|
3125
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
3126
|
+
</body>
|
|
3127
|
+
</html>
|
|
3128
|
+
`;
|
|
3129
|
+
writeFileSync12(join22(projectDir, "index.html"), indexHtml);
|
|
3130
|
+
console.log(dim2(" Created index.html"));
|
|
3131
|
+
const srcDir = join22(projectDir, "src");
|
|
3132
|
+
mkdirSync7(srcDir, { recursive: true });
|
|
3133
|
+
const mainTsx = `import { StrictMode } from 'react';
|
|
3134
|
+
import { createRoot } from 'react-dom/client';
|
|
3135
|
+
import { BrowserRouter } from 'react-router-dom';
|
|
3136
|
+
import { App } from './App';
|
|
3137
|
+
import './styles/tokens.css';
|
|
3138
|
+
import './styles/decorators.css';
|
|
3139
|
+
import './styles/global.css';
|
|
3140
|
+
|
|
3141
|
+
createRoot(document.getElementById('root')!).render(
|
|
3142
|
+
<StrictMode>
|
|
3143
|
+
<BrowserRouter>
|
|
3144
|
+
<App />
|
|
3145
|
+
</BrowserRouter>
|
|
3146
|
+
</StrictMode>,
|
|
3147
|
+
);
|
|
3148
|
+
`;
|
|
3149
|
+
writeFileSync12(join22(srcDir, "main.tsx"), mainTsx);
|
|
3150
|
+
const appTsx = `import { Routes, Route } from 'react-router-dom';
|
|
3151
|
+
|
|
3152
|
+
function WelcomePage() {
|
|
3153
|
+
return (
|
|
3154
|
+
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', minHeight: '100vh', gap: '1rem' }}>
|
|
3155
|
+
<h1>${title}</h1>
|
|
3156
|
+
<p style={{ opacity: 0.6 }}>Scaffolded with Decantr. Run <code>decantr status</code> to check project health.</p>
|
|
3157
|
+
</div>
|
|
3158
|
+
);
|
|
3159
|
+
}
|
|
3160
|
+
|
|
3161
|
+
export function App() {
|
|
3162
|
+
return (
|
|
3163
|
+
<Routes>
|
|
3164
|
+
<Route path="/" element={<WelcomePage />} />
|
|
3165
|
+
</Routes>
|
|
3166
|
+
);
|
|
3167
|
+
}
|
|
3168
|
+
`;
|
|
3169
|
+
writeFileSync12(join22(srcDir, "App.tsx"), appTsx);
|
|
3170
|
+
writeFileSync12(join22(srcDir, "vite-env.d.ts"), '/// <reference types="vite/client" />\n');
|
|
3171
|
+
mkdirSync7(join22(srcDir, "styles"), { recursive: true });
|
|
3172
|
+
console.log(dim2(" Created src/"));
|
|
3173
|
+
console.log(heading("Installing dependencies..."));
|
|
3174
|
+
const packageManager = detectPackageManager();
|
|
3175
|
+
try {
|
|
3176
|
+
execSync(`${packageManager} install`, { cwd: projectDir, stdio: "inherit" });
|
|
3177
|
+
} catch {
|
|
3178
|
+
console.log(`
|
|
3179
|
+
${YELLOW8}Dependency install failed. Run \`${packageManager} install\` manually.${RESET12}`);
|
|
3180
|
+
}
|
|
3181
|
+
console.log(heading("Initializing Decantr..."));
|
|
3182
|
+
const initFlags = ["--yes", "--existing"];
|
|
3183
|
+
if (options.blueprint) initFlags.push(`--blueprint=${options.blueprint}`);
|
|
3184
|
+
if (options.archetype) initFlags.push(`--archetype=${options.archetype}`);
|
|
3185
|
+
if (options.theme) initFlags.push(`--theme=${options.theme}`);
|
|
3186
|
+
if (options.mode) initFlags.push(`--mode=${options.mode}`);
|
|
3187
|
+
if (options.shape) initFlags.push(`--shape=${options.shape}`);
|
|
3188
|
+
if (options.offline) initFlags.push("--offline");
|
|
3189
|
+
if (options.registry) initFlags.push(`--registry=${options.registry}`);
|
|
3190
|
+
try {
|
|
3191
|
+
const cliBin = join22(__dirname, "..", "bin", "decantr.js");
|
|
3192
|
+
const cliPath = existsSync22(cliBin) ? `node ${cliBin}` : "npx decantr";
|
|
3193
|
+
execSync(`${cliPath} init ${initFlags.join(" ")}`, { cwd: projectDir, stdio: "inherit" });
|
|
3194
|
+
} catch {
|
|
3195
|
+
console.log(`
|
|
3196
|
+
${YELLOW8}Decantr init encountered issues. Run \`decantr init\` manually inside ${projectName}/.${RESET12}`);
|
|
3197
|
+
}
|
|
3198
|
+
console.log(success2(`
|
|
3199
|
+
\u2713 Project "${projectName}" created!
|
|
3200
|
+
`));
|
|
3201
|
+
console.log(` ${cyan2("cd " + projectName)}`);
|
|
3202
|
+
console.log(` ${cyan2(packageManager + " run dev")}`);
|
|
3203
|
+
console.log("");
|
|
3204
|
+
}
|
|
3205
|
+
function detectPackageManager() {
|
|
3206
|
+
if (existsSync22(join22(process.cwd(), "pnpm-lock.yaml")) || existsSync22(join22(process.cwd(), "pnpm-workspace.yaml"))) {
|
|
3207
|
+
return "pnpm";
|
|
3208
|
+
}
|
|
3209
|
+
if (existsSync22(join22(process.cwd(), "yarn.lock"))) {
|
|
3210
|
+
return "yarn";
|
|
3211
|
+
}
|
|
3212
|
+
if (existsSync22(join22(process.cwd(), "bun.lockb")) || existsSync22(join22(process.cwd(), "bun.lock"))) {
|
|
3213
|
+
return "bun";
|
|
3214
|
+
}
|
|
3215
|
+
return "npm";
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
// src/index.ts
|
|
3219
|
+
var BOLD6 = "\x1B[1m";
|
|
3220
|
+
var DIM13 = "\x1B[2m";
|
|
3221
|
+
var RESET13 = "\x1B[0m";
|
|
3222
|
+
var RED11 = "\x1B[31m";
|
|
3223
|
+
var GREEN13 = "\x1B[32m";
|
|
3224
|
+
var CYAN7 = "\x1B[36m";
|
|
3225
|
+
var YELLOW9 = "\x1B[33m";
|
|
3226
|
+
function heading2(text) {
|
|
3227
|
+
return `
|
|
3228
|
+
${BOLD6}${text}${RESET13}
|
|
3229
|
+
`;
|
|
3230
|
+
}
|
|
3231
|
+
function success3(text) {
|
|
3232
|
+
return `${GREEN13}${text}${RESET13}`;
|
|
3233
|
+
}
|
|
3234
|
+
function error3(text) {
|
|
3235
|
+
return `${RED11}${text}${RESET13}`;
|
|
3236
|
+
}
|
|
3237
|
+
function dim3(text) {
|
|
3238
|
+
return `${DIM13}${text}${RESET13}`;
|
|
3239
|
+
}
|
|
3240
|
+
function cyan3(text) {
|
|
3241
|
+
return `${CYAN7}${text}${RESET13}`;
|
|
3242
|
+
}
|
|
2228
3243
|
function extractPatternName(item) {
|
|
2229
3244
|
if (typeof item === "string") return item;
|
|
2230
3245
|
if (typeof item === "object" && item !== null) {
|
|
@@ -2267,7 +3282,7 @@ function boxedPrompt(content, title) {
|
|
|
2267
3282
|
const maxLen = Math.max(...lines.map((l) => l.length), title.length + 4);
|
|
2268
3283
|
const width = maxLen + 4;
|
|
2269
3284
|
const top = `\u250C${"\u2500".repeat(width - 2)}\u2510`;
|
|
2270
|
-
const titleLine = `\u2502 ${
|
|
3285
|
+
const titleLine = `\u2502 ${BOLD6}${title}${RESET13}${" ".repeat(width - title.length - 4)} \u2502`;
|
|
2271
3286
|
const sep2 = `\u251C${"\u2500".repeat(width - 2)}\u2524`;
|
|
2272
3287
|
const bottom = `\u2514${"\u2500".repeat(width - 2)}\u2518`;
|
|
2273
3288
|
const body = lines.map((line) => {
|
|
@@ -2281,7 +3296,7 @@ ${body}
|
|
|
2281
3296
|
${bottom}`;
|
|
2282
3297
|
}
|
|
2283
3298
|
function getAPIClient() {
|
|
2284
|
-
return new
|
|
3299
|
+
return new RegistryAPIClient3({
|
|
2285
3300
|
baseUrl: process.env.DECANTR_API_URL || void 0,
|
|
2286
3301
|
apiKey: process.env.DECANTR_API_KEY || void 0
|
|
2287
3302
|
});
|
|
@@ -2292,17 +3307,17 @@ async function cmdSearch(query, type) {
|
|
|
2292
3307
|
const response = await apiClient.search({ q: query, type });
|
|
2293
3308
|
const results = response.results;
|
|
2294
3309
|
if (results.length === 0) {
|
|
2295
|
-
console.log(
|
|
3310
|
+
console.log(dim3(`No results for "${query}"`));
|
|
2296
3311
|
return;
|
|
2297
3312
|
}
|
|
2298
|
-
console.log(
|
|
3313
|
+
console.log(heading2(`${results.length} result(s) for "${query}"`));
|
|
2299
3314
|
for (const r of results) {
|
|
2300
|
-
console.log(` ${
|
|
2301
|
-
console.log(` ${
|
|
3315
|
+
console.log(` ${cyan3(r.type.padEnd(12))} ${BOLD6}${r.slug}${RESET13}`);
|
|
3316
|
+
console.log(` ${dim3(r.description || "")}`);
|
|
2302
3317
|
console.log("");
|
|
2303
3318
|
}
|
|
2304
3319
|
} catch {
|
|
2305
|
-
console.log(
|
|
3320
|
+
console.log(dim3(`Search failed. API may be unavailable.`));
|
|
2306
3321
|
}
|
|
2307
3322
|
}
|
|
2308
3323
|
async function cmdSuggest(query, type) {
|
|
@@ -2312,40 +3327,40 @@ async function cmdSuggest(query, type) {
|
|
|
2312
3327
|
const response = await apiClient.search({ q: query, type: searchType });
|
|
2313
3328
|
const results = response.results;
|
|
2314
3329
|
if (results.length === 0) {
|
|
2315
|
-
console.log(
|
|
3330
|
+
console.log(dim3(`No suggestions for "${query}"`));
|
|
2316
3331
|
console.log("");
|
|
2317
3332
|
console.log("Try:");
|
|
2318
|
-
console.log(` ${
|
|
2319
|
-
console.log(` ${
|
|
3333
|
+
console.log(` ${cyan3("decantr list patterns")} - see all patterns`);
|
|
3334
|
+
console.log(` ${cyan3("decantr search <broader-term>")} - broaden your search`);
|
|
2320
3335
|
return;
|
|
2321
3336
|
}
|
|
2322
|
-
console.log(
|
|
3337
|
+
console.log(heading2(`Suggestions for "${query}"`));
|
|
2323
3338
|
const queryLower = query.toLowerCase();
|
|
2324
3339
|
const exact = results.filter((r) => r.slug.toLowerCase().includes(queryLower));
|
|
2325
3340
|
const related = results.filter((r) => !r.slug.toLowerCase().includes(queryLower));
|
|
2326
3341
|
if (exact.length > 0) {
|
|
2327
|
-
console.log(`${
|
|
3342
|
+
console.log(`${BOLD6}Direct matches:${RESET13}`);
|
|
2328
3343
|
for (const r of exact.slice(0, 3)) {
|
|
2329
|
-
console.log(` ${
|
|
3344
|
+
console.log(` ${cyan3(r.slug)} - ${r.description || ""}`);
|
|
2330
3345
|
}
|
|
2331
3346
|
console.log("");
|
|
2332
3347
|
}
|
|
2333
3348
|
if (related.length > 0) {
|
|
2334
|
-
console.log(`${
|
|
3349
|
+
console.log(`${BOLD6}Related:${RESET13}`);
|
|
2335
3350
|
for (const r of related.slice(0, 5)) {
|
|
2336
|
-
console.log(` ${
|
|
3351
|
+
console.log(` ${cyan3(r.slug)} - ${r.description || ""}`);
|
|
2337
3352
|
}
|
|
2338
3353
|
console.log("");
|
|
2339
3354
|
}
|
|
2340
|
-
console.log(
|
|
3355
|
+
console.log(dim3(`Use "decantr get pattern <id>" for full details`));
|
|
2341
3356
|
} catch {
|
|
2342
|
-
console.log(
|
|
3357
|
+
console.log(dim3(`Suggestion search failed. API may be unavailable.`));
|
|
2343
3358
|
}
|
|
2344
3359
|
}
|
|
2345
3360
|
async function cmdGet(type, id) {
|
|
2346
3361
|
const validTypes = ["pattern", "archetype", "recipe", "theme", "blueprint", "shell"];
|
|
2347
3362
|
if (!validTypes.includes(type)) {
|
|
2348
|
-
console.error(
|
|
3363
|
+
console.error(error3(`Invalid type "${type}". Must be one of: ${validTypes.join(", ")}`));
|
|
2349
3364
|
process.exitCode = 1;
|
|
2350
3365
|
return;
|
|
2351
3366
|
}
|
|
@@ -2359,29 +3374,29 @@ async function cmdGet(type, id) {
|
|
|
2359
3374
|
};
|
|
2360
3375
|
const apiType = typeMap[type];
|
|
2361
3376
|
const registryClient = new RegistryClient({
|
|
2362
|
-
cacheDir:
|
|
3377
|
+
cacheDir: join23(process.cwd(), ".decantr", "cache")
|
|
2363
3378
|
});
|
|
2364
3379
|
const result = await registryClient.fetchContentItem(apiType, id);
|
|
2365
3380
|
if (result) {
|
|
2366
3381
|
console.log(JSON.stringify(result.data, null, 2));
|
|
2367
3382
|
return;
|
|
2368
3383
|
}
|
|
2369
|
-
const currentDir =
|
|
3384
|
+
const currentDir = dirname2(fileURLToPath(import.meta.url));
|
|
2370
3385
|
const bundledCandidates = [
|
|
2371
|
-
|
|
3386
|
+
join23(currentDir, "bundled", apiType, `${id}.json`),
|
|
2372
3387
|
// Running from src/
|
|
2373
|
-
|
|
3388
|
+
join23(currentDir, "..", "src", "bundled", apiType, `${id}.json`),
|
|
2374
3389
|
// Running from dist/
|
|
2375
|
-
|
|
3390
|
+
join23(currentDir, "..", "bundled", apiType, `${id}.json`)
|
|
2376
3391
|
// Alternative dist layout
|
|
2377
3392
|
];
|
|
2378
|
-
const bundledPath = bundledCandidates.find((p) =>
|
|
3393
|
+
const bundledPath = bundledCandidates.find((p) => existsSync23(p)) || null;
|
|
2379
3394
|
if (bundledPath) {
|
|
2380
|
-
const data = JSON.parse(
|
|
3395
|
+
const data = JSON.parse(readFileSync15(bundledPath, "utf-8"));
|
|
2381
3396
|
console.log(JSON.stringify(data, null, 2));
|
|
2382
3397
|
return;
|
|
2383
3398
|
}
|
|
2384
|
-
console.error(
|
|
3399
|
+
console.error(error3(`${type} "${id}" not found.`));
|
|
2385
3400
|
process.exitCode = 1;
|
|
2386
3401
|
return;
|
|
2387
3402
|
}
|
|
@@ -2389,13 +3404,13 @@ function buildRegistryContext() {
|
|
|
2389
3404
|
const themeRegistry = /* @__PURE__ */ new Map();
|
|
2390
3405
|
const patternRegistry = /* @__PURE__ */ new Map();
|
|
2391
3406
|
const projectRoot = process.cwd();
|
|
2392
|
-
const cacheDir =
|
|
2393
|
-
const customDir =
|
|
2394
|
-
const cachedThemesDir =
|
|
3407
|
+
const cacheDir = join23(projectRoot, ".decantr", "cache");
|
|
3408
|
+
const customDir = join23(projectRoot, ".decantr", "custom");
|
|
3409
|
+
const cachedThemesDir = join23(cacheDir, "@official", "themes");
|
|
2395
3410
|
try {
|
|
2396
|
-
if (
|
|
3411
|
+
if (existsSync23(cachedThemesDir)) {
|
|
2397
3412
|
for (const f of readdirSync6(cachedThemesDir).filter((f2) => f2.endsWith(".json") && f2 !== "index.json")) {
|
|
2398
|
-
const data = JSON.parse(
|
|
3413
|
+
const data = JSON.parse(readFileSync15(join23(cachedThemesDir, f), "utf-8"));
|
|
2399
3414
|
if (data.id && !themeRegistry.has(data.id)) {
|
|
2400
3415
|
themeRegistry.set(data.id, { modes: data.modes || ["light", "dark"] });
|
|
2401
3416
|
}
|
|
@@ -2403,11 +3418,11 @@ function buildRegistryContext() {
|
|
|
2403
3418
|
}
|
|
2404
3419
|
} catch {
|
|
2405
3420
|
}
|
|
2406
|
-
const customThemesDir =
|
|
3421
|
+
const customThemesDir = join23(customDir, "themes");
|
|
2407
3422
|
try {
|
|
2408
|
-
if (
|
|
3423
|
+
if (existsSync23(customThemesDir)) {
|
|
2409
3424
|
for (const f of readdirSync6(customThemesDir).filter((f2) => f2.endsWith(".json"))) {
|
|
2410
|
-
const data = JSON.parse(
|
|
3425
|
+
const data = JSON.parse(readFileSync15(join23(customThemesDir, f), "utf-8"));
|
|
2411
3426
|
if (data.id) {
|
|
2412
3427
|
themeRegistry.set(`custom:${data.id}`, { modes: data.modes || ["light", "dark"] });
|
|
2413
3428
|
}
|
|
@@ -2415,11 +3430,11 @@ function buildRegistryContext() {
|
|
|
2415
3430
|
}
|
|
2416
3431
|
} catch {
|
|
2417
3432
|
}
|
|
2418
|
-
const cachedPatternsDir =
|
|
3433
|
+
const cachedPatternsDir = join23(cacheDir, "@official", "patterns");
|
|
2419
3434
|
try {
|
|
2420
|
-
if (
|
|
3435
|
+
if (existsSync23(cachedPatternsDir)) {
|
|
2421
3436
|
for (const f of readdirSync6(cachedPatternsDir).filter((f2) => f2.endsWith(".json") && f2 !== "index.json")) {
|
|
2422
|
-
const data = JSON.parse(
|
|
3437
|
+
const data = JSON.parse(readFileSync15(join23(cachedPatternsDir, f), "utf-8"));
|
|
2423
3438
|
if (data.id && !patternRegistry.has(data.id)) {
|
|
2424
3439
|
patternRegistry.set(data.id, data);
|
|
2425
3440
|
}
|
|
@@ -2430,12 +3445,12 @@ function buildRegistryContext() {
|
|
|
2430
3445
|
return { themeRegistry, patternRegistry };
|
|
2431
3446
|
}
|
|
2432
3447
|
async function cmdValidate(path) {
|
|
2433
|
-
const essencePath = path ||
|
|
3448
|
+
const essencePath = path || join23(process.cwd(), "decantr.essence.json");
|
|
2434
3449
|
let raw;
|
|
2435
3450
|
try {
|
|
2436
|
-
raw =
|
|
3451
|
+
raw = readFileSync15(essencePath, "utf-8");
|
|
2437
3452
|
} catch {
|
|
2438
|
-
console.error(
|
|
3453
|
+
console.error(error3(`Could not read ${essencePath}`));
|
|
2439
3454
|
process.exitCode = 1;
|
|
2440
3455
|
return;
|
|
2441
3456
|
}
|
|
@@ -2443,39 +3458,39 @@ async function cmdValidate(path) {
|
|
|
2443
3458
|
try {
|
|
2444
3459
|
essence = JSON.parse(raw);
|
|
2445
3460
|
} catch (e) {
|
|
2446
|
-
console.error(
|
|
3461
|
+
console.error(error3(`Invalid JSON: ${e.message}`));
|
|
2447
3462
|
process.exitCode = 1;
|
|
2448
3463
|
return;
|
|
2449
3464
|
}
|
|
2450
3465
|
const detectedVersion = isV36(essence) ? "v3" : "v2";
|
|
2451
|
-
console.log(`${
|
|
3466
|
+
console.log(`${DIM13}Detected essence version: ${detectedVersion}${RESET13}`);
|
|
2452
3467
|
const result = validateEssence2(essence);
|
|
2453
3468
|
if (result.valid) {
|
|
2454
|
-
console.log(
|
|
3469
|
+
console.log(success3(`Essence is valid (${detectedVersion}).`));
|
|
2455
3470
|
} else {
|
|
2456
|
-
console.error(
|
|
3471
|
+
console.error(error3("Validation failed:"));
|
|
2457
3472
|
for (const err of result.errors) {
|
|
2458
|
-
console.error(` ${
|
|
3473
|
+
console.error(` ${RED11}${err}${RESET13}`);
|
|
2459
3474
|
}
|
|
2460
3475
|
process.exitCode = 1;
|
|
2461
3476
|
}
|
|
2462
3477
|
if (detectedVersion === "v2" && result.valid) {
|
|
2463
|
-
console.log(`${
|
|
3478
|
+
console.log(`${YELLOW9}Tip: Run \`decantr migrate\` to upgrade to v3 format.${RESET13}`);
|
|
2464
3479
|
}
|
|
2465
3480
|
try {
|
|
2466
3481
|
const { themeRegistry, patternRegistry } = buildRegistryContext();
|
|
2467
3482
|
const violations = evaluateGuard(essence, { themeRegistry, patternRegistry });
|
|
2468
3483
|
if (violations.length > 0) {
|
|
2469
|
-
console.log(
|
|
3484
|
+
console.log(heading2("Guard violations:"));
|
|
2470
3485
|
for (const v of violations) {
|
|
2471
3486
|
const vr = v;
|
|
2472
|
-
console.log(` ${
|
|
3487
|
+
console.log(` ${YELLOW9}[${vr.rule}]${RESET13} ${vr.message}`);
|
|
2473
3488
|
if (vr.suggestion) {
|
|
2474
|
-
console.log(` ${
|
|
3489
|
+
console.log(` ${DIM13}Suggestion: ${vr.suggestion}${RESET13}`);
|
|
2475
3490
|
}
|
|
2476
3491
|
}
|
|
2477
3492
|
} else if (result.valid) {
|
|
2478
|
-
console.log(
|
|
3493
|
+
console.log(success3("No guard violations."));
|
|
2479
3494
|
}
|
|
2480
3495
|
} catch {
|
|
2481
3496
|
}
|
|
@@ -2483,59 +3498,59 @@ async function cmdValidate(path) {
|
|
|
2483
3498
|
async function cmdList(type) {
|
|
2484
3499
|
const validTypes = ["patterns", "archetypes", "recipes", "themes", "blueprints", "shells"];
|
|
2485
3500
|
if (!validTypes.includes(type)) {
|
|
2486
|
-
console.error(
|
|
3501
|
+
console.error(error3(`Invalid type "${type}". Must be one of: ${validTypes.join(", ")}`));
|
|
2487
3502
|
process.exitCode = 1;
|
|
2488
3503
|
return;
|
|
2489
3504
|
}
|
|
2490
3505
|
const registryClient = new RegistryClient({
|
|
2491
|
-
cacheDir:
|
|
3506
|
+
cacheDir: join23(process.cwd(), ".decantr", "cache")
|
|
2492
3507
|
});
|
|
2493
3508
|
const result = await registryClient.fetchContentList(type);
|
|
2494
3509
|
const items = result.data.items;
|
|
2495
3510
|
if (items.length === 0) {
|
|
2496
|
-
console.log(
|
|
3511
|
+
console.log(dim3(`No ${type} found.`));
|
|
2497
3512
|
return;
|
|
2498
3513
|
}
|
|
2499
3514
|
if (type === "themes") {
|
|
2500
3515
|
const customItems = registryClient.listCustomContent("themes");
|
|
2501
3516
|
const customIds = new Set(customItems.map((c) => c.id));
|
|
2502
3517
|
const registryItems = items.filter((i) => !customIds.has(i.id));
|
|
2503
|
-
console.log(
|
|
3518
|
+
console.log(heading2(`Registry themes (${registryItems.length}):`));
|
|
2504
3519
|
for (const item of registryItems) {
|
|
2505
|
-
console.log(` ${
|
|
3520
|
+
console.log(` ${cyan3(item.id)} ${dim3(item.description || item.name || "")}`);
|
|
2506
3521
|
}
|
|
2507
3522
|
if (customItems.length > 0) {
|
|
2508
3523
|
console.log("");
|
|
2509
|
-
console.log(
|
|
3524
|
+
console.log(heading2(`Custom themes (${customItems.length}):`));
|
|
2510
3525
|
for (const item of customItems) {
|
|
2511
|
-
console.log(` ${
|
|
3526
|
+
console.log(` ${cyan3(`custom:${item.id}`)} ${dim3(item.description || item.name || "")}`);
|
|
2512
3527
|
}
|
|
2513
3528
|
} else {
|
|
2514
3529
|
console.log("");
|
|
2515
|
-
console.log(
|
|
2516
|
-
console.log(
|
|
3530
|
+
console.log(dim3("Custom themes (0):"));
|
|
3531
|
+
console.log(dim3(' Run "decantr theme create <name>" to create a custom theme.'));
|
|
2517
3532
|
}
|
|
2518
3533
|
} else {
|
|
2519
|
-
console.log(
|
|
3534
|
+
console.log(heading2(`${items.length} ${type} found`));
|
|
2520
3535
|
for (const item of items) {
|
|
2521
|
-
console.log(` ${
|
|
3536
|
+
console.log(` ${cyan3(item.id)} ${dim3(item.description || item.name || "")}`);
|
|
2522
3537
|
}
|
|
2523
3538
|
}
|
|
2524
3539
|
}
|
|
2525
3540
|
async function cmdInit(args) {
|
|
2526
3541
|
const projectRoot = process.cwd();
|
|
2527
|
-
console.log(
|
|
3542
|
+
console.log(heading2("Decantr Project Setup"));
|
|
2528
3543
|
const detected = detectProject(projectRoot);
|
|
2529
3544
|
if (detected.existingEssence && !args.existing) {
|
|
2530
|
-
console.log(`${
|
|
3545
|
+
console.log(`${YELLOW9}Warning: decantr.essence.json already exists.${RESET13}`);
|
|
2531
3546
|
const overwrite = await confirm("Overwrite existing configuration?", false);
|
|
2532
3547
|
if (!overwrite) {
|
|
2533
|
-
console.log(
|
|
3548
|
+
console.log(dim3("Cancelled."));
|
|
2534
3549
|
return;
|
|
2535
3550
|
}
|
|
2536
3551
|
}
|
|
2537
3552
|
const registryClient = new RegistryClient({
|
|
2538
|
-
cacheDir:
|
|
3553
|
+
cacheDir: join23(projectRoot, ".decantr", "cache"),
|
|
2539
3554
|
apiUrl: args.registry,
|
|
2540
3555
|
offline: args.offline
|
|
2541
3556
|
});
|
|
@@ -2547,30 +3562,30 @@ async function cmdInit(args) {
|
|
|
2547
3562
|
} else if (!apiAvailable) {
|
|
2548
3563
|
if (!args.blueprint) {
|
|
2549
3564
|
console.log(`
|
|
2550
|
-
${
|
|
2551
|
-
console.log(
|
|
3565
|
+
${YELLOW9}You're offline. Scaffolding minimal Decantr project.${RESET13}`);
|
|
3566
|
+
console.log(dim3("Run `decantr sync` or `decantr upgrade` when online to pull full registry content.\n"));
|
|
2552
3567
|
const result2 = scaffoldMinimal(projectRoot);
|
|
2553
|
-
console.log(
|
|
3568
|
+
console.log(success3("\nProject scaffolded (minimal/offline)!\n"));
|
|
2554
3569
|
console.log(" Files created:");
|
|
2555
|
-
console.log(` ${
|
|
2556
|
-
console.log(` ${
|
|
2557
|
-
console.log(` ${
|
|
3570
|
+
console.log(` ${cyan3("decantr.essence.json")} Design specification`);
|
|
3571
|
+
console.log(` ${cyan3("DECANTR.md")} LLM instructions`);
|
|
3572
|
+
console.log(` ${cyan3(".decantr/")} Project state & custom content dirs`);
|
|
2558
3573
|
if (result2.gitignoreUpdated) {
|
|
2559
|
-
console.log(` ${
|
|
3574
|
+
console.log(` ${dim3(".gitignore updated")}`);
|
|
2560
3575
|
}
|
|
2561
3576
|
console.log("");
|
|
2562
3577
|
console.log(" Next steps:");
|
|
2563
|
-
console.log(` 1. Run ${
|
|
2564
|
-
console.log(` 2. Use ${
|
|
3578
|
+
console.log(` 1. Run ${cyan3("decantr sync")} when online`);
|
|
3579
|
+
console.log(` 2. Use ${cyan3("decantr create <type> <name>")} to create custom content`);
|
|
2565
3580
|
console.log(` 3. Review DECANTR.md for methodology`);
|
|
2566
3581
|
return;
|
|
2567
3582
|
}
|
|
2568
3583
|
console.log(`
|
|
2569
|
-
${
|
|
2570
|
-
console.log(
|
|
3584
|
+
${YELLOW9}You're offline. Scaffolding Decantr default.${RESET13}`);
|
|
3585
|
+
console.log(dim3("Run `decantr upgrade` when online, or visit decantr.ai/registry\n"));
|
|
2571
3586
|
selectedBlueprint = "default";
|
|
2572
3587
|
} else {
|
|
2573
|
-
console.log(
|
|
3588
|
+
console.log(dim3("Fetching registry content..."));
|
|
2574
3589
|
const blueprintsResult2 = await registryClient.fetchBlueprints();
|
|
2575
3590
|
registrySource = blueprintsResult2.source.type === "api" ? "api" : "cache";
|
|
2576
3591
|
const { selectedBlueprint: selected } = await runSimplifiedInit(
|
|
@@ -2578,11 +3593,9 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2578
3593
|
);
|
|
2579
3594
|
selectedBlueprint = selected || "default";
|
|
2580
3595
|
}
|
|
2581
|
-
const
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
registryClient.fetchThemes()
|
|
2585
|
-
]);
|
|
3596
|
+
const archetypesResult = await registryClient.fetchArchetypes();
|
|
3597
|
+
const blueprintsResult = await registryClient.fetchBlueprints();
|
|
3598
|
+
const themesResult = await registryClient.fetchThemes();
|
|
2586
3599
|
if (archetypesResult.source.type === "api") {
|
|
2587
3600
|
registrySource = "api";
|
|
2588
3601
|
}
|
|
@@ -2620,21 +3633,20 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2620
3633
|
options.shape = blueprint.theme.shape;
|
|
2621
3634
|
}
|
|
2622
3635
|
}
|
|
2623
|
-
if (blueprint.personality
|
|
2624
|
-
options.personality = blueprint.personality;
|
|
3636
|
+
if (blueprint.personality && (!options.personality || options.personality.length === 0 || options.personality.length === 1 && options.personality[0] === "professional")) {
|
|
3637
|
+
options.personality = typeof blueprint.personality === "string" ? [blueprint.personality] : blueprint.personality;
|
|
2625
3638
|
}
|
|
2626
3639
|
blueprintRecipeName = blueprint.theme?.recipe;
|
|
2627
3640
|
if (blueprint.compose && blueprint.compose.length > 0) {
|
|
2628
3641
|
const entries = blueprint.compose;
|
|
2629
|
-
const
|
|
3642
|
+
const results = [];
|
|
3643
|
+
for (const entry of entries) {
|
|
2630
3644
|
const id = typeof entry === "string" ? entry : entry.archetype;
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
});
|
|
2637
|
-
const results = await Promise.all(fetchPromises);
|
|
3645
|
+
const r = await registryClient.fetchArchetype(id);
|
|
3646
|
+
const raw = r?.data;
|
|
3647
|
+
const inner = raw?.data ?? raw;
|
|
3648
|
+
results.push([id, inner]);
|
|
3649
|
+
}
|
|
2638
3650
|
const archetypeMap = new Map(results.map(([id, data]) => [id, data || null]));
|
|
2639
3651
|
const composed = composeArchetypes(entries, archetypeMap);
|
|
2640
3652
|
const primaryId = typeof entries[0] === "string" ? entries[0] : entries[0].archetype;
|
|
@@ -2677,8 +3689,7 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2677
3689
|
}
|
|
2678
3690
|
patternSpecs = {};
|
|
2679
3691
|
if (allPatternIds.size > 0) {
|
|
2680
|
-
const
|
|
2681
|
-
const fetches = [...allPatternIds].map(async (pid) => {
|
|
3692
|
+
for (const pid of allPatternIds) {
|
|
2682
3693
|
try {
|
|
2683
3694
|
const result2 = await registryClient.fetchPattern(pid);
|
|
2684
3695
|
if (result2) {
|
|
@@ -2686,17 +3697,15 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2686
3697
|
const inner = raw.data ?? raw;
|
|
2687
3698
|
const defaultPreset = inner.default_preset || "standard";
|
|
2688
3699
|
const preset = inner.presets?.[defaultPreset];
|
|
2689
|
-
|
|
3700
|
+
patternSpecs[pid] = {
|
|
2690
3701
|
description: inner.description || "",
|
|
2691
3702
|
components: inner.components || [],
|
|
2692
|
-
slots: preset?.layout?.slots || {}
|
|
2693
|
-
code: preset?.code?.example || inner.code?.example || ""
|
|
3703
|
+
slots: preset?.layout?.slots || {}
|
|
2694
3704
|
};
|
|
2695
3705
|
}
|
|
2696
3706
|
} catch {
|
|
2697
3707
|
}
|
|
2698
|
-
}
|
|
2699
|
-
await Promise.all(fetches);
|
|
3708
|
+
}
|
|
2700
3709
|
}
|
|
2701
3710
|
const zoneInputs = [];
|
|
2702
3711
|
for (const entry of entries) {
|
|
@@ -2732,7 +3741,7 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2732
3741
|
) : "";
|
|
2733
3742
|
}
|
|
2734
3743
|
} else {
|
|
2735
|
-
console.log(`${
|
|
3744
|
+
console.log(`${YELLOW9} Warning: Could not fetch blueprint "${options.blueprint}". Using defaults.${RESET13}`);
|
|
2736
3745
|
}
|
|
2737
3746
|
} else if (options.archetype) {
|
|
2738
3747
|
const archetypeResult = await registryClient.fetchArchetype(options.archetype);
|
|
@@ -2740,7 +3749,7 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2740
3749
|
const rawArch = archetypeResult.data;
|
|
2741
3750
|
archetypeData = rawArch.data ?? rawArch;
|
|
2742
3751
|
} else {
|
|
2743
|
-
console.log(`${
|
|
3752
|
+
console.log(`${YELLOW9} Warning: Could not fetch archetype "${options.archetype}". Using defaults.${RESET13}`);
|
|
2744
3753
|
}
|
|
2745
3754
|
}
|
|
2746
3755
|
let themeData;
|
|
@@ -2762,10 +3771,14 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2762
3771
|
recipeData = { decorators: theme.decorators };
|
|
2763
3772
|
}
|
|
2764
3773
|
} else {
|
|
2765
|
-
console.log(`${
|
|
3774
|
+
console.log(`${YELLOW9} Warning: Could not fetch theme "${options.theme}". Using defaults.${RESET13}`);
|
|
2766
3775
|
}
|
|
2767
3776
|
const recipeName = blueprintRecipeName || options.theme;
|
|
2768
3777
|
const recipeResult = await registryClient.fetchRecipe(recipeName);
|
|
3778
|
+
if (process.env.DECANTR_DEBUG && recipeResult) {
|
|
3779
|
+
const dbg = recipeResult.data;
|
|
3780
|
+
console.error(` [debug] recipe source: ${recipeResult.source.type}, keys: ${Object.keys(dbg).join(",")}, has .data: ${"data" in dbg}, has .decorators: ${"decorators" in dbg}, inner.decorators: ${!!dbg.data?.decorators}`);
|
|
3781
|
+
}
|
|
2769
3782
|
if (recipeResult) {
|
|
2770
3783
|
const rawRecipe = recipeResult.data;
|
|
2771
3784
|
const recipe = rawRecipe.data ?? rawRecipe;
|
|
@@ -2776,7 +3789,7 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2776
3789
|
};
|
|
2777
3790
|
}
|
|
2778
3791
|
}
|
|
2779
|
-
console.log(
|
|
3792
|
+
console.log(heading2("Scaffolding project..."));
|
|
2780
3793
|
const result = await scaffoldProject(
|
|
2781
3794
|
projectRoot,
|
|
2782
3795
|
options,
|
|
@@ -2793,13 +3806,13 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2793
3806
|
patternSpecs,
|
|
2794
3807
|
blueprintData
|
|
2795
3808
|
);
|
|
2796
|
-
console.log(
|
|
3809
|
+
console.log(success3("\nProject scaffolded!\n"));
|
|
2797
3810
|
console.log(" Files created:");
|
|
2798
|
-
console.log(` ${
|
|
2799
|
-
console.log(` ${
|
|
2800
|
-
console.log(` ${
|
|
3811
|
+
console.log(` ${cyan3("decantr.essence.json")} Design specification`);
|
|
3812
|
+
console.log(` ${cyan3("DECANTR.md")} LLM instructions`);
|
|
3813
|
+
console.log(` ${cyan3(".decantr/")} Project state & cache`);
|
|
2801
3814
|
if (result.gitignoreUpdated) {
|
|
2802
|
-
console.log(` ${
|
|
3815
|
+
console.log(` ${dim3(".gitignore updated")}`);
|
|
2803
3816
|
}
|
|
2804
3817
|
console.log("");
|
|
2805
3818
|
console.log(" Next steps:");
|
|
@@ -2807,27 +3820,30 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2807
3820
|
console.log(" 2. Explore more at decantr.ai/registry");
|
|
2808
3821
|
console.log("");
|
|
2809
3822
|
console.log(" Commands:");
|
|
2810
|
-
console.log(` ${
|
|
2811
|
-
console.log(` ${
|
|
2812
|
-
console.log(` ${
|
|
2813
|
-
console.log(` ${
|
|
2814
|
-
console.log(` ${
|
|
2815
|
-
console.log(` ${
|
|
2816
|
-
console.log(` ${
|
|
2817
|
-
const essenceContent =
|
|
3823
|
+
console.log(` ${cyan3("decantr status")} Project health`);
|
|
3824
|
+
console.log(` ${cyan3("decantr search")} Search registry`);
|
|
3825
|
+
console.log(` ${cyan3("decantr get")} Fetch content details`);
|
|
3826
|
+
console.log(` ${cyan3("decantr validate")} Check essence file`);
|
|
3827
|
+
console.log(` ${cyan3("decantr upgrade")} Update to latest patterns`);
|
|
3828
|
+
console.log(` ${cyan3("decantr check")} Detect drift issues`);
|
|
3829
|
+
console.log(` ${cyan3("decantr migrate")} Migrate v2 essence to v3`);
|
|
3830
|
+
const essenceContent = readFileSync15(result.essencePath, "utf-8");
|
|
2818
3831
|
const essence = JSON.parse(essenceContent);
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
3832
|
+
if (essence.version !== "3.1.0") {
|
|
3833
|
+
const validation = validateEssence2(essence);
|
|
3834
|
+
if (!validation.valid) {
|
|
3835
|
+
console.log(error3(`
|
|
2822
3836
|
Validation warnings: ${validation.errors.join(", ")}`));
|
|
3837
|
+
}
|
|
2823
3838
|
}
|
|
2824
3839
|
console.log("");
|
|
2825
3840
|
let promptPages;
|
|
2826
3841
|
if (isV36(essence)) {
|
|
2827
|
-
|
|
3842
|
+
const allPages = essence.blueprint.sections ? essence.blueprint.sections.flatMap((s) => s.pages.map((p) => ({ ...p, _shell: s.shell }))) : essence.blueprint.pages || [];
|
|
3843
|
+
promptPages = allPages.map((p) => ({
|
|
2828
3844
|
id: p.id,
|
|
2829
|
-
shell: p.shell_override ?? essence.blueprint.shell,
|
|
2830
|
-
layout: p.layout.map((item) => typeof item === "string" ? item : extractPatternName(item))
|
|
3845
|
+
shell: p.shell_override ?? p._shell ?? essence.blueprint.shell,
|
|
3846
|
+
layout: (p.layout || []).map((item) => typeof item === "string" ? item : extractPatternName(item))
|
|
2831
3847
|
}));
|
|
2832
3848
|
} else {
|
|
2833
3849
|
promptPages = essence.structure || [{ id: "home", shell: options.shell, layout: ["hero"] }];
|
|
@@ -2847,32 +3863,32 @@ Validation warnings: ${validation.errors.join(", ")}`));
|
|
|
2847
3863
|
console.log(boxedPrompt(curatedPrompt, "Copy this prompt for your AI assistant"));
|
|
2848
3864
|
console.log("");
|
|
2849
3865
|
if (registrySource === "cache") {
|
|
2850
|
-
console.log(
|
|
3866
|
+
console.log(dim3('Run "decantr sync" when online to get the latest registry content.'));
|
|
2851
3867
|
}
|
|
2852
3868
|
}
|
|
2853
3869
|
async function cmdStatus() {
|
|
2854
3870
|
const projectRoot = process.cwd();
|
|
2855
|
-
const essencePath =
|
|
2856
|
-
const projectJsonPath =
|
|
2857
|
-
console.log(
|
|
2858
|
-
if (!
|
|
2859
|
-
console.log(`${
|
|
2860
|
-
console.log(
|
|
3871
|
+
const essencePath = join23(projectRoot, "decantr.essence.json");
|
|
3872
|
+
const projectJsonPath = join23(projectRoot, ".decantr", "project.json");
|
|
3873
|
+
console.log(heading2("Decantr Project Status"));
|
|
3874
|
+
if (!existsSync23(essencePath)) {
|
|
3875
|
+
console.log(`${RED11}No decantr.essence.json found.${RESET13}`);
|
|
3876
|
+
console.log(dim3('Run "decantr init" to create one.'));
|
|
2861
3877
|
return;
|
|
2862
3878
|
}
|
|
2863
3879
|
try {
|
|
2864
|
-
const essence = JSON.parse(
|
|
3880
|
+
const essence = JSON.parse(readFileSync15(essencePath, "utf-8"));
|
|
2865
3881
|
const validation = validateEssence2(essence);
|
|
2866
3882
|
const essenceVersion = isV36(essence) ? "v3" : "v2";
|
|
2867
|
-
console.log(`${
|
|
3883
|
+
console.log(`${BOLD6}Essence:${RESET13}`);
|
|
2868
3884
|
if (validation.valid) {
|
|
2869
|
-
console.log(` ${
|
|
3885
|
+
console.log(` ${GREEN13}Valid${RESET13} (${essenceVersion})`);
|
|
2870
3886
|
} else {
|
|
2871
|
-
console.log(` ${
|
|
3887
|
+
console.log(` ${RED11}Invalid: ${validation.errors.join(", ")}${RESET13}`);
|
|
2872
3888
|
}
|
|
2873
3889
|
if (isV36(essence)) {
|
|
2874
3890
|
const v3 = essence;
|
|
2875
|
-
console.log(` ${
|
|
3891
|
+
console.log(` ${BOLD6}DNA:${RESET13}`);
|
|
2876
3892
|
console.log(` Theme: ${v3.dna.theme.style} (${v3.dna.theme.mode})`);
|
|
2877
3893
|
console.log(` Spacing: ${v3.dna.spacing.density} density, ${v3.dna.spacing.content_gap} gap`);
|
|
2878
3894
|
console.log(` Typography: ${v3.dna.typography.scale} scale`);
|
|
@@ -2880,11 +3896,11 @@ async function cmdStatus() {
|
|
|
2880
3896
|
console.log(` Motion: ${v3.dna.motion.preference} (reduce: ${v3.dna.motion.reduce_motion})`);
|
|
2881
3897
|
console.log(` Accessibility: WCAG ${v3.dna.accessibility.wcag_level}`);
|
|
2882
3898
|
console.log(` Personality: ${v3.dna.personality.join(", ")}`);
|
|
2883
|
-
console.log(` ${
|
|
3899
|
+
console.log(` ${BOLD6}Blueprint:${RESET13}`);
|
|
2884
3900
|
console.log(` Shell: ${v3.blueprint.shell}`);
|
|
2885
3901
|
console.log(` Pages: ${v3.blueprint.pages.length}`);
|
|
2886
3902
|
console.log(` Features: ${v3.blueprint.features.length > 0 ? v3.blueprint.features.join(", ") : "none"}`);
|
|
2887
|
-
console.log(` ${
|
|
3903
|
+
console.log(` ${BOLD6}Meta:${RESET13}`);
|
|
2888
3904
|
console.log(` Archetype: ${v3.meta.archetype}`);
|
|
2889
3905
|
console.log(` Target: ${v3.meta.target}`);
|
|
2890
3906
|
console.log(` Guard: ${v3.meta.guard.mode} (DNA: ${v3.meta.guard.dna_enforcement}, Blueprint: ${v3.meta.guard.blueprint_enforcement})`);
|
|
@@ -2896,92 +3912,92 @@ async function cmdStatus() {
|
|
|
2896
3912
|
console.log(` Theme: ${theme?.style || "unknown"} (${theme?.mode || "unknown"})`);
|
|
2897
3913
|
console.log(` Guard: ${guard?.mode || "unknown"}`);
|
|
2898
3914
|
console.log(` Pages: ${(structure || []).length}`);
|
|
2899
|
-
console.log(` ${
|
|
3915
|
+
console.log(` ${YELLOW9}Tip: Run \`decantr migrate\` to upgrade to v3.${RESET13}`);
|
|
2900
3916
|
}
|
|
2901
3917
|
} catch (e) {
|
|
2902
|
-
console.log(` ${
|
|
3918
|
+
console.log(` ${RED11}Error reading essence: ${e.message}${RESET13}`);
|
|
2903
3919
|
}
|
|
2904
3920
|
console.log("");
|
|
2905
|
-
console.log(`${
|
|
2906
|
-
if (
|
|
3921
|
+
console.log(`${BOLD6}Sync Status:${RESET13}`);
|
|
3922
|
+
if (existsSync23(projectJsonPath)) {
|
|
2907
3923
|
try {
|
|
2908
|
-
const projectJson = JSON.parse(
|
|
3924
|
+
const projectJson = JSON.parse(readFileSync15(projectJsonPath, "utf-8"));
|
|
2909
3925
|
const syncStatus = projectJson.sync?.status || "unknown";
|
|
2910
3926
|
const lastSync = projectJson.sync?.lastSync || "never";
|
|
2911
3927
|
const source = projectJson.sync?.registrySource || "unknown";
|
|
2912
|
-
const statusColor = syncStatus === "synced" ?
|
|
2913
|
-
console.log(` Status: ${statusColor}${syncStatus}${
|
|
2914
|
-
console.log(` Last sync: ${
|
|
2915
|
-
console.log(` Source: ${
|
|
3928
|
+
const statusColor = syncStatus === "synced" ? GREEN13 : YELLOW9;
|
|
3929
|
+
console.log(` Status: ${statusColor}${syncStatus}${RESET13}`);
|
|
3930
|
+
console.log(` Last sync: ${dim3(lastSync)}`);
|
|
3931
|
+
console.log(` Source: ${dim3(source)}`);
|
|
2916
3932
|
} catch {
|
|
2917
|
-
console.log(` ${
|
|
3933
|
+
console.log(` ${YELLOW9}Could not read project.json${RESET13}`);
|
|
2918
3934
|
}
|
|
2919
3935
|
} else {
|
|
2920
|
-
console.log(` ${
|
|
2921
|
-
console.log(
|
|
3936
|
+
console.log(` ${YELLOW9}No .decantr/project.json found${RESET13}`);
|
|
3937
|
+
console.log(dim3(' Run "decantr init" to create project files.'));
|
|
2922
3938
|
}
|
|
2923
3939
|
}
|
|
2924
3940
|
async function cmdSync() {
|
|
2925
3941
|
const projectRoot = process.cwd();
|
|
2926
|
-
const cacheDir =
|
|
2927
|
-
console.log(
|
|
3942
|
+
const cacheDir = join23(projectRoot, ".decantr", "cache");
|
|
3943
|
+
console.log(heading2("Syncing registry content..."));
|
|
2928
3944
|
const result = await syncRegistry(cacheDir);
|
|
2929
3945
|
if (result.synced.length > 0) {
|
|
2930
|
-
console.log(
|
|
3946
|
+
console.log(success3("Sync completed successfully."));
|
|
2931
3947
|
console.log(` Synced: ${result.synced.join(", ")}`);
|
|
2932
3948
|
if (result.failed.length > 0) {
|
|
2933
|
-
console.log(` ${
|
|
3949
|
+
console.log(` ${YELLOW9}Failed: ${result.failed.join(", ")}${RESET13}`);
|
|
2934
3950
|
}
|
|
2935
3951
|
} else {
|
|
2936
|
-
console.log(`${
|
|
3952
|
+
console.log(`${YELLOW9}Could not sync: API unavailable${RESET13}`);
|
|
2937
3953
|
if (result.failed.length > 0) {
|
|
2938
|
-
console.log(` ${
|
|
3954
|
+
console.log(` ${YELLOW9}Failed: ${result.failed.join(", ")}${RESET13}`);
|
|
2939
3955
|
}
|
|
2940
3956
|
}
|
|
2941
3957
|
}
|
|
2942
3958
|
async function cmdAudit() {
|
|
2943
3959
|
const projectRoot = process.cwd();
|
|
2944
|
-
const essencePath =
|
|
2945
|
-
console.log(
|
|
2946
|
-
if (!
|
|
2947
|
-
console.log(`${
|
|
3960
|
+
const essencePath = join23(projectRoot, "decantr.essence.json");
|
|
3961
|
+
console.log(heading2("Auditing project..."));
|
|
3962
|
+
if (!existsSync23(essencePath)) {
|
|
3963
|
+
console.log(`${RED11}No decantr.essence.json found.${RESET13}`);
|
|
2948
3964
|
process.exitCode = 1;
|
|
2949
3965
|
return;
|
|
2950
3966
|
}
|
|
2951
3967
|
try {
|
|
2952
|
-
const essence = JSON.parse(
|
|
3968
|
+
const essence = JSON.parse(readFileSync15(essencePath, "utf-8"));
|
|
2953
3969
|
const validation = validateEssence2(essence);
|
|
2954
3970
|
if (!validation.valid) {
|
|
2955
|
-
console.log(`${
|
|
3971
|
+
console.log(`${RED11}Essence validation failed:${RESET13}`);
|
|
2956
3972
|
for (const err of validation.errors) {
|
|
2957
|
-
console.log(` ${
|
|
3973
|
+
console.log(` ${RED11}${err}${RESET13}`);
|
|
2958
3974
|
}
|
|
2959
3975
|
process.exitCode = 1;
|
|
2960
3976
|
return;
|
|
2961
3977
|
}
|
|
2962
|
-
console.log(
|
|
3978
|
+
console.log(success3("Essence is valid."));
|
|
2963
3979
|
const { themeRegistry, patternRegistry } = buildRegistryContext();
|
|
2964
3980
|
const violations = evaluateGuard(essence, { themeRegistry, patternRegistry });
|
|
2965
3981
|
if (violations.length > 0) {
|
|
2966
3982
|
console.log("");
|
|
2967
|
-
console.log(`${
|
|
3983
|
+
console.log(`${YELLOW9}Guard violations:${RESET13}`);
|
|
2968
3984
|
for (const v of violations) {
|
|
2969
3985
|
const vr = v;
|
|
2970
|
-
console.log(` ${
|
|
3986
|
+
console.log(` ${YELLOW9}[${vr.rule}]${RESET13} ${vr.message}`);
|
|
2971
3987
|
if (vr.suggestion) {
|
|
2972
|
-
console.log(` ${
|
|
3988
|
+
console.log(` ${DIM13}Suggestion: ${vr.suggestion}${RESET13}`);
|
|
2973
3989
|
}
|
|
2974
3990
|
}
|
|
2975
3991
|
} else {
|
|
2976
|
-
console.log(
|
|
3992
|
+
console.log(success3("No guard violations."));
|
|
2977
3993
|
}
|
|
2978
3994
|
console.log("");
|
|
2979
|
-
console.log(`${
|
|
3995
|
+
console.log(`${BOLD6}Summary:${RESET13}`);
|
|
2980
3996
|
console.log(` Pages defined: ${essence.structure.length}`);
|
|
2981
3997
|
console.log(` Guard mode: ${essence.guard.mode}`);
|
|
2982
3998
|
console.log(` Theme: ${essence.theme.style}`);
|
|
2983
3999
|
} catch (e) {
|
|
2984
|
-
console.log(`${
|
|
4000
|
+
console.log(`${RED11}Error: ${e.message}${RESET13}`);
|
|
2985
4001
|
process.exitCode = 1;
|
|
2986
4002
|
}
|
|
2987
4003
|
}
|
|
@@ -2990,17 +4006,17 @@ async function cmdTheme(args) {
|
|
|
2990
4006
|
const projectRoot = process.cwd();
|
|
2991
4007
|
if (!subcommand || subcommand === "help") {
|
|
2992
4008
|
console.log(`
|
|
2993
|
-
${
|
|
4009
|
+
${BOLD6}decantr theme${RESET13} \u2014 Manage custom themes
|
|
2994
4010
|
|
|
2995
|
-
${
|
|
2996
|
-
${
|
|
2997
|
-
${
|
|
2998
|
-
${
|
|
2999
|
-
${
|
|
3000
|
-
${
|
|
3001
|
-
${
|
|
4011
|
+
${BOLD6}Commands:${RESET13}
|
|
4012
|
+
${cyan3("create")} <name> Create a new custom theme
|
|
4013
|
+
${cyan3("create")} <name> --guided Interactive theme creation
|
|
4014
|
+
${cyan3("list")} List custom themes
|
|
4015
|
+
${cyan3("validate")} <name> Validate a custom theme
|
|
4016
|
+
${cyan3("delete")} <name> Delete a custom theme
|
|
4017
|
+
${cyan3("import")} <path> Import theme from JSON file
|
|
3002
4018
|
|
|
3003
|
-
${
|
|
4019
|
+
${BOLD6}Examples:${RESET13}
|
|
3004
4020
|
decantr theme create mytheme
|
|
3005
4021
|
decantr theme list
|
|
3006
4022
|
decantr theme validate mytheme
|
|
@@ -3012,19 +4028,19 @@ ${BOLD4}Examples:${RESET9}
|
|
|
3012
4028
|
case "create": {
|
|
3013
4029
|
const name = args[1];
|
|
3014
4030
|
if (!name) {
|
|
3015
|
-
console.error(
|
|
4031
|
+
console.error(error3("Usage: decantr theme create <name>"));
|
|
3016
4032
|
process.exitCode = 1;
|
|
3017
4033
|
return;
|
|
3018
4034
|
}
|
|
3019
4035
|
const displayName = name.charAt(0).toUpperCase() + name.slice(1).replace(/-/g, " ");
|
|
3020
4036
|
const result = createTheme(projectRoot, name, displayName);
|
|
3021
4037
|
if (result.success) {
|
|
3022
|
-
console.log(
|
|
3023
|
-
console.log(
|
|
4038
|
+
console.log(success3(`Created custom theme "${name}"`));
|
|
4039
|
+
console.log(dim3(` Path: ${result.path}`));
|
|
3024
4040
|
console.log("");
|
|
3025
|
-
console.log(`Use in essence: ${
|
|
4041
|
+
console.log(`Use in essence: ${cyan3(`"style": "custom:${name}"`)}`);
|
|
3026
4042
|
} else {
|
|
3027
|
-
console.error(
|
|
4043
|
+
console.error(error3(result.error || "Failed to create theme"));
|
|
3028
4044
|
process.exitCode = 1;
|
|
3029
4045
|
}
|
|
3030
4046
|
break;
|
|
@@ -3032,12 +4048,12 @@ ${BOLD4}Examples:${RESET9}
|
|
|
3032
4048
|
case "list": {
|
|
3033
4049
|
const themes = listCustomThemes(projectRoot);
|
|
3034
4050
|
if (themes.length === 0) {
|
|
3035
|
-
console.log(
|
|
3036
|
-
console.log(
|
|
4051
|
+
console.log(dim3("No custom themes found."));
|
|
4052
|
+
console.log(dim3('Run "decantr theme create <name>" to create one.'));
|
|
3037
4053
|
} else {
|
|
3038
|
-
console.log(
|
|
4054
|
+
console.log(heading2(`${themes.length} custom theme(s)`));
|
|
3039
4055
|
for (const theme of themes) {
|
|
3040
|
-
console.log(` ${
|
|
4056
|
+
console.log(` ${cyan3(`custom:${theme.id}`)} ${dim3(theme.description || theme.name)}`);
|
|
3041
4057
|
}
|
|
3042
4058
|
}
|
|
3043
4059
|
break;
|
|
@@ -3045,30 +4061,30 @@ ${BOLD4}Examples:${RESET9}
|
|
|
3045
4061
|
case "validate": {
|
|
3046
4062
|
const name = args[1];
|
|
3047
4063
|
if (!name) {
|
|
3048
|
-
console.error(
|
|
4064
|
+
console.error(error3("Usage: decantr theme validate <name>"));
|
|
3049
4065
|
process.exitCode = 1;
|
|
3050
4066
|
return;
|
|
3051
4067
|
}
|
|
3052
|
-
const themePath =
|
|
3053
|
-
if (!
|
|
3054
|
-
console.error(
|
|
4068
|
+
const themePath = join23(projectRoot, ".decantr", "custom", "themes", `${name}.json`);
|
|
4069
|
+
if (!existsSync23(themePath)) {
|
|
4070
|
+
console.error(error3(`Theme "${name}" not found at ${themePath}`));
|
|
3055
4071
|
process.exitCode = 1;
|
|
3056
4072
|
return;
|
|
3057
4073
|
}
|
|
3058
4074
|
try {
|
|
3059
|
-
const theme = JSON.parse(
|
|
4075
|
+
const theme = JSON.parse(readFileSync15(themePath, "utf-8"));
|
|
3060
4076
|
const result = validateCustomTheme(theme);
|
|
3061
4077
|
if (result.valid) {
|
|
3062
|
-
console.log(
|
|
4078
|
+
console.log(success3(`Custom theme "${name}" is valid`));
|
|
3063
4079
|
} else {
|
|
3064
|
-
console.error(
|
|
4080
|
+
console.error(error3("Validation failed:"));
|
|
3065
4081
|
for (const err of result.errors) {
|
|
3066
|
-
console.error(` ${
|
|
4082
|
+
console.error(` ${RED11}${err}${RESET13}`);
|
|
3067
4083
|
}
|
|
3068
4084
|
process.exitCode = 1;
|
|
3069
4085
|
}
|
|
3070
4086
|
} catch (e) {
|
|
3071
|
-
console.error(
|
|
4087
|
+
console.error(error3(`Invalid JSON: ${e.message}`));
|
|
3072
4088
|
process.exitCode = 1;
|
|
3073
4089
|
}
|
|
3074
4090
|
break;
|
|
@@ -3076,15 +4092,15 @@ ${BOLD4}Examples:${RESET9}
|
|
|
3076
4092
|
case "delete": {
|
|
3077
4093
|
const name = args[1];
|
|
3078
4094
|
if (!name) {
|
|
3079
|
-
console.error(
|
|
4095
|
+
console.error(error3("Usage: decantr theme delete <name>"));
|
|
3080
4096
|
process.exitCode = 1;
|
|
3081
4097
|
return;
|
|
3082
4098
|
}
|
|
3083
4099
|
const result = deleteTheme(projectRoot, name);
|
|
3084
4100
|
if (result.success) {
|
|
3085
|
-
console.log(
|
|
4101
|
+
console.log(success3(`Deleted custom theme "${name}"`));
|
|
3086
4102
|
} else {
|
|
3087
|
-
console.error(
|
|
4103
|
+
console.error(error3(result.error || "Failed to delete theme"));
|
|
3088
4104
|
process.exitCode = 1;
|
|
3089
4105
|
}
|
|
3090
4106
|
break;
|
|
@@ -3092,18 +4108,18 @@ ${BOLD4}Examples:${RESET9}
|
|
|
3092
4108
|
case "import": {
|
|
3093
4109
|
const sourcePath = args[1];
|
|
3094
4110
|
if (!sourcePath) {
|
|
3095
|
-
console.error(
|
|
4111
|
+
console.error(error3("Usage: decantr theme import <path>"));
|
|
3096
4112
|
process.exitCode = 1;
|
|
3097
4113
|
return;
|
|
3098
4114
|
}
|
|
3099
4115
|
const result = importTheme(projectRoot, sourcePath);
|
|
3100
4116
|
if (result.success) {
|
|
3101
|
-
console.log(
|
|
3102
|
-
console.log(
|
|
4117
|
+
console.log(success3("Theme imported successfully"));
|
|
4118
|
+
console.log(dim3(` Path: ${result.path}`));
|
|
3103
4119
|
} else {
|
|
3104
|
-
console.error(
|
|
4120
|
+
console.error(error3("Import failed:"));
|
|
3105
4121
|
for (const err of result.errors || []) {
|
|
3106
|
-
console.error(` ${
|
|
4122
|
+
console.error(` ${RED11}${err}${RESET13}`);
|
|
3107
4123
|
}
|
|
3108
4124
|
process.exitCode = 1;
|
|
3109
4125
|
}
|
|
@@ -3112,7 +4128,7 @@ ${BOLD4}Examples:${RESET9}
|
|
|
3112
4128
|
case "switch": {
|
|
3113
4129
|
const name = args[1];
|
|
3114
4130
|
if (!name) {
|
|
3115
|
-
console.error(
|
|
4131
|
+
console.error(error3("Usage: decantr theme switch <themeName> [--recipe <r>] [--shape <s>] [--mode <m>]"));
|
|
3116
4132
|
process.exitCode = 1;
|
|
3117
4133
|
return;
|
|
3118
4134
|
}
|
|
@@ -3120,15 +4136,17 @@ ${BOLD4}Examples:${RESET9}
|
|
|
3120
4136
|
break;
|
|
3121
4137
|
}
|
|
3122
4138
|
default:
|
|
3123
|
-
console.error(
|
|
4139
|
+
console.error(error3(`Unknown theme command: ${subcommand}`));
|
|
3124
4140
|
process.exitCode = 1;
|
|
3125
4141
|
}
|
|
3126
4142
|
}
|
|
3127
4143
|
function cmdHelp() {
|
|
3128
4144
|
console.log(`
|
|
3129
|
-
${
|
|
4145
|
+
${BOLD6}decantr${RESET13} \u2014 Design intelligence for AI-generated UI
|
|
3130
4146
|
|
|
3131
|
-
${
|
|
4147
|
+
${BOLD6}Usage:${RESET13}
|
|
4148
|
+
decantr new <name> [--blueprint=X] [--archetype=X] [--theme=X]
|
|
4149
|
+
decantr magic <prompt> [--dry-run]
|
|
3132
4150
|
decantr init [options]
|
|
3133
4151
|
decantr status
|
|
3134
4152
|
decantr sync
|
|
@@ -3149,7 +4167,7 @@ ${BOLD4}Usage:${RESET9}
|
|
|
3149
4167
|
decantr logout
|
|
3150
4168
|
decantr help
|
|
3151
4169
|
|
|
3152
|
-
${
|
|
4170
|
+
${BOLD6}Init Options:${RESET13}
|
|
3153
4171
|
--blueprint, -b Blueprint ID
|
|
3154
4172
|
--theme Theme ID
|
|
3155
4173
|
--mode Color mode: dark | light | auto
|
|
@@ -3163,29 +4181,35 @@ ${BOLD4}Init Options:${RESET9}
|
|
|
3163
4181
|
--yes, -y Accept defaults, skip confirmations
|
|
3164
4182
|
--registry Custom registry URL
|
|
3165
4183
|
|
|
3166
|
-
${
|
|
3167
|
-
${
|
|
3168
|
-
${
|
|
3169
|
-
${
|
|
3170
|
-
${
|
|
3171
|
-
${
|
|
3172
|
-
${
|
|
3173
|
-
${
|
|
3174
|
-
${
|
|
3175
|
-
${
|
|
3176
|
-
${
|
|
3177
|
-
${
|
|
3178
|
-
${
|
|
3179
|
-
${
|
|
3180
|
-
${
|
|
3181
|
-
${
|
|
3182
|
-
${
|
|
3183
|
-
${
|
|
3184
|
-
${
|
|
3185
|
-
${
|
|
3186
|
-
${
|
|
4184
|
+
${BOLD6}Commands:${RESET13}
|
|
4185
|
+
${cyan3("new")} Create a new project with Vite + React + Decantr
|
|
4186
|
+
${cyan3("magic")} One-liner scaffold from a natural language prompt
|
|
4187
|
+
${cyan3("init")} Initialize Decantr in an existing project (v3 essence by default)
|
|
4188
|
+
${cyan3("status")} Show project status, DNA axioms, and blueprint info
|
|
4189
|
+
${cyan3("sync")} Sync registry content from API
|
|
4190
|
+
${cyan3("audit")} Validate essence and check for drift
|
|
4191
|
+
${cyan3("migrate")} Migrate v2 essence to v3 format (with .v2.backup.json backup)
|
|
4192
|
+
${cyan3("check")} Detect drift issues (validate + guard rules) [--telemetry]
|
|
4193
|
+
${cyan3("sync-drift")} Review and resolve drift log entries
|
|
4194
|
+
${cyan3("search")} Search the registry
|
|
4195
|
+
${cyan3("suggest")} Suggest patterns or alternatives for a query
|
|
4196
|
+
${cyan3("get")} Get full details of a registry item
|
|
4197
|
+
${cyan3("list")} List items by type
|
|
4198
|
+
${cyan3("validate")} Validate essence file (v2 and v3)
|
|
4199
|
+
${cyan3("theme")} Manage custom themes (create, list, validate, delete, import)
|
|
4200
|
+
${cyan3("create")} Create a custom content item (pattern, recipe, theme, etc.)
|
|
4201
|
+
${cyan3("publish")} Publish a custom content item to the community registry
|
|
4202
|
+
${cyan3("login")} Authenticate with the Decantr registry
|
|
4203
|
+
${cyan3("logout")} Remove stored credentials
|
|
4204
|
+
${cyan3("analyze")} Scan existing project and produce analysis report
|
|
4205
|
+
${cyan3("export")} Export design tokens to framework format (shadcn, tailwind, css-vars)
|
|
4206
|
+
${cyan3("registry")} Registry management (mirror)
|
|
4207
|
+
${cyan3("upgrade")} Check for content updates from registry
|
|
4208
|
+
${cyan3("help")} Show this help
|
|
3187
4209
|
|
|
3188
|
-
${
|
|
4210
|
+
${BOLD6}Examples:${RESET13}
|
|
4211
|
+
decantr new my-app --blueprint=carbon-ai-portal
|
|
4212
|
+
decantr magic "AI chatbot with dark cyber theme \u2014 bold and futuristic"
|
|
3189
4213
|
decantr init
|
|
3190
4214
|
decantr init --blueprint=saas-dashboard --theme=luminarum --yes
|
|
3191
4215
|
decantr status
|
|
@@ -3206,6 +4230,38 @@ async function main() {
|
|
|
3206
4230
|
return;
|
|
3207
4231
|
}
|
|
3208
4232
|
switch (command) {
|
|
4233
|
+
case "new": {
|
|
4234
|
+
const newName = args[1];
|
|
4235
|
+
if (!newName) {
|
|
4236
|
+
console.error(error3("Usage: decantr new <project-name> [--blueprint=X] [--archetype=X] [--theme=X]"));
|
|
4237
|
+
process.exitCode = 1;
|
|
4238
|
+
break;
|
|
4239
|
+
}
|
|
4240
|
+
const newOpts = {};
|
|
4241
|
+
for (let i = 2; i < args.length; i++) {
|
|
4242
|
+
const arg = args[i];
|
|
4243
|
+
if (arg === "--offline") {
|
|
4244
|
+
newOpts.offline = true;
|
|
4245
|
+
} else if (arg.startsWith("--")) {
|
|
4246
|
+
const [key, value] = arg.slice(2).split("=");
|
|
4247
|
+
if (value) {
|
|
4248
|
+
newOpts[key] = value;
|
|
4249
|
+
} else if (args[i + 1] && !args[i + 1].startsWith("-")) {
|
|
4250
|
+
newOpts[key] = args[++i];
|
|
4251
|
+
}
|
|
4252
|
+
}
|
|
4253
|
+
}
|
|
4254
|
+
await cmdNewProject(newName, {
|
|
4255
|
+
blueprint: newOpts.blueprint,
|
|
4256
|
+
archetype: newOpts.archetype,
|
|
4257
|
+
theme: newOpts.theme,
|
|
4258
|
+
mode: newOpts.mode,
|
|
4259
|
+
shape: newOpts.shape,
|
|
4260
|
+
offline: newOpts.offline === true,
|
|
4261
|
+
registry: newOpts.registry
|
|
4262
|
+
});
|
|
4263
|
+
break;
|
|
4264
|
+
}
|
|
3209
4265
|
case "init": {
|
|
3210
4266
|
const initArgs = {};
|
|
3211
4267
|
for (let i = 1; i < args.length; i++) {
|
|
@@ -3241,7 +4297,7 @@ async function main() {
|
|
|
3241
4297
|
break;
|
|
3242
4298
|
}
|
|
3243
4299
|
case "upgrade": {
|
|
3244
|
-
const { cmdUpgrade } = await import("./upgrade-
|
|
4300
|
+
const { cmdUpgrade } = await import("./upgrade-25IURU4X.js");
|
|
3245
4301
|
const applyFlag = args.includes("--apply");
|
|
3246
4302
|
await cmdUpgrade(process.cwd(), { apply: applyFlag });
|
|
3247
4303
|
break;
|
|
@@ -3249,10 +4305,11 @@ async function main() {
|
|
|
3249
4305
|
case "check":
|
|
3250
4306
|
case "heal": {
|
|
3251
4307
|
if (command === "heal") {
|
|
3252
|
-
console.log(`${
|
|
4308
|
+
console.log(`${YELLOW9}Note: \`decantr heal\` is deprecated. Use \`decantr check\` instead.${RESET13}`);
|
|
3253
4309
|
}
|
|
3254
|
-
const { cmdHeal } = await import("./heal-
|
|
3255
|
-
|
|
4310
|
+
const { cmdHeal } = await import("./heal-54MKDDSQ.js");
|
|
4311
|
+
const telemetryFlag = args.includes("--telemetry");
|
|
4312
|
+
await cmdHeal(process.cwd(), { telemetry: telemetryFlag });
|
|
3256
4313
|
break;
|
|
3257
4314
|
}
|
|
3258
4315
|
case "migrate": {
|
|
@@ -3271,9 +4328,9 @@ async function main() {
|
|
|
3271
4328
|
resolveIndex: resolveNum
|
|
3272
4329
|
});
|
|
3273
4330
|
if (result.success) {
|
|
3274
|
-
console.log(
|
|
4331
|
+
console.log(success3(clearFlag ? "Drift log cleared." : "Entries resolved."));
|
|
3275
4332
|
} else {
|
|
3276
|
-
console.error(
|
|
4333
|
+
console.error(error3(result.error || "Failed"));
|
|
3277
4334
|
process.exitCode = 1;
|
|
3278
4335
|
}
|
|
3279
4336
|
} else {
|
|
@@ -3288,7 +4345,7 @@ async function main() {
|
|
|
3288
4345
|
case "search": {
|
|
3289
4346
|
const query = args[1];
|
|
3290
4347
|
if (!query) {
|
|
3291
|
-
console.error(
|
|
4348
|
+
console.error(error3("Usage: decantr search <query> [--type <type>]"));
|
|
3292
4349
|
process.exitCode = 1;
|
|
3293
4350
|
return;
|
|
3294
4351
|
}
|
|
@@ -3300,7 +4357,7 @@ async function main() {
|
|
|
3300
4357
|
case "suggest": {
|
|
3301
4358
|
const query = args[1];
|
|
3302
4359
|
if (!query) {
|
|
3303
|
-
console.error(
|
|
4360
|
+
console.error(error3("Usage: decantr suggest <query> [--type <type>]"));
|
|
3304
4361
|
process.exitCode = 1;
|
|
3305
4362
|
return;
|
|
3306
4363
|
}
|
|
@@ -3313,7 +4370,7 @@ async function main() {
|
|
|
3313
4370
|
const type = args[1];
|
|
3314
4371
|
const id = args[2];
|
|
3315
4372
|
if (!type || !id) {
|
|
3316
|
-
console.error(
|
|
4373
|
+
console.error(error3("Usage: decantr get <type> <id>"));
|
|
3317
4374
|
process.exitCode = 1;
|
|
3318
4375
|
return;
|
|
3319
4376
|
}
|
|
@@ -3323,7 +4380,7 @@ async function main() {
|
|
|
3323
4380
|
case "list": {
|
|
3324
4381
|
const type = args[1];
|
|
3325
4382
|
if (!type) {
|
|
3326
|
-
console.error(
|
|
4383
|
+
console.error(error3("Usage: decantr list <type>"));
|
|
3327
4384
|
process.exitCode = 1;
|
|
3328
4385
|
return;
|
|
3329
4386
|
}
|
|
@@ -3343,37 +4400,37 @@ async function main() {
|
|
|
3343
4400
|
if (apiKeyArg && apiKeyArg.startsWith("--api-key=")) {
|
|
3344
4401
|
const key = apiKeyArg.split("=")[1];
|
|
3345
4402
|
saveCredentials({ access_token: key, api_key: key });
|
|
3346
|
-
console.log(
|
|
4403
|
+
console.log(success3("API key saved."));
|
|
3347
4404
|
} else {
|
|
3348
|
-
console.log(
|
|
4405
|
+
console.log(heading2("Decantr Login"));
|
|
3349
4406
|
console.log(" To authenticate, get your API key from the Decantr dashboard:");
|
|
3350
4407
|
console.log("");
|
|
3351
|
-
console.log(` ${
|
|
4408
|
+
console.log(` ${cyan3("https://decantr.ai/dashboard/api-keys")}`);
|
|
3352
4409
|
console.log("");
|
|
3353
4410
|
console.log(" Then run:");
|
|
3354
|
-
console.log(` ${
|
|
4411
|
+
console.log(` ${cyan3("decantr login --api-key=<your-key>")}`);
|
|
3355
4412
|
console.log("");
|
|
3356
4413
|
console.log(" Or set the environment variable:");
|
|
3357
|
-
console.log(` ${
|
|
4414
|
+
console.log(` ${cyan3("export DECANTR_API_KEY=<your-key>")}`);
|
|
3358
4415
|
const existingCreds = getCredentials();
|
|
3359
4416
|
if (existingCreds) {
|
|
3360
4417
|
console.log("");
|
|
3361
|
-
console.log(
|
|
4418
|
+
console.log(dim3("You are currently authenticated."));
|
|
3362
4419
|
}
|
|
3363
4420
|
}
|
|
3364
4421
|
break;
|
|
3365
4422
|
}
|
|
3366
4423
|
case "logout": {
|
|
3367
4424
|
clearCredentials();
|
|
3368
|
-
console.log(
|
|
4425
|
+
console.log(success3("Logged out. Credentials removed."));
|
|
3369
4426
|
break;
|
|
3370
4427
|
}
|
|
3371
4428
|
case "create": {
|
|
3372
4429
|
const type = args[1];
|
|
3373
4430
|
const name = args[2];
|
|
3374
4431
|
if (!type || !name) {
|
|
3375
|
-
console.error(
|
|
3376
|
-
console.error(
|
|
4432
|
+
console.error(error3("Usage: decantr create <type> <name>"));
|
|
4433
|
+
console.error(dim3("Types: pattern, recipe, theme, blueprint, archetype, shell"));
|
|
3377
4434
|
process.exitCode = 1;
|
|
3378
4435
|
break;
|
|
3379
4436
|
}
|
|
@@ -3384,8 +4441,8 @@ async function main() {
|
|
|
3384
4441
|
const type = args[1];
|
|
3385
4442
|
const name = args[2];
|
|
3386
4443
|
if (!type || !name) {
|
|
3387
|
-
console.error(
|
|
3388
|
-
console.error(
|
|
4444
|
+
console.error(error3("Usage: decantr publish <type> <name>"));
|
|
4445
|
+
console.error(dim3("Types: pattern, recipe, theme, blueprint, archetype, shell"));
|
|
3389
4446
|
process.exitCode = 1;
|
|
3390
4447
|
break;
|
|
3391
4448
|
}
|
|
@@ -3393,13 +4450,26 @@ async function main() {
|
|
|
3393
4450
|
break;
|
|
3394
4451
|
}
|
|
3395
4452
|
case "refresh": {
|
|
3396
|
-
|
|
4453
|
+
const refreshOffline = args.includes("--offline");
|
|
4454
|
+
await cmdRefresh(process.cwd(), { offline: refreshOffline });
|
|
4455
|
+
break;
|
|
4456
|
+
}
|
|
4457
|
+
case "registry": {
|
|
4458
|
+
const subcommand = args[1];
|
|
4459
|
+
if (subcommand === "mirror") {
|
|
4460
|
+
const typeIdx = args.indexOf("--type");
|
|
4461
|
+
const mirrorType = typeIdx !== -1 ? args[typeIdx + 1] : void 0;
|
|
4462
|
+
await cmdRegistryMirror(process.cwd(), { type: mirrorType });
|
|
4463
|
+
} else {
|
|
4464
|
+
console.error(`${RED11}Usage: decantr registry mirror [--type <type>]${RESET13}`);
|
|
4465
|
+
process.exitCode = 1;
|
|
4466
|
+
}
|
|
3397
4467
|
break;
|
|
3398
4468
|
}
|
|
3399
4469
|
case "add": {
|
|
3400
4470
|
const subcommand = args[1];
|
|
3401
4471
|
if (!subcommand) {
|
|
3402
|
-
console.error(
|
|
4472
|
+
console.error(error3("Usage: decantr add <section|page|feature> <target>"));
|
|
3403
4473
|
process.exitCode = 1;
|
|
3404
4474
|
break;
|
|
3405
4475
|
}
|
|
@@ -3407,7 +4477,7 @@ async function main() {
|
|
|
3407
4477
|
case "section": {
|
|
3408
4478
|
const id = args[2];
|
|
3409
4479
|
if (!id) {
|
|
3410
|
-
console.error(
|
|
4480
|
+
console.error(error3("Usage: decantr add section <archetypeId>"));
|
|
3411
4481
|
process.exitCode = 1;
|
|
3412
4482
|
break;
|
|
3413
4483
|
}
|
|
@@ -3417,7 +4487,7 @@ async function main() {
|
|
|
3417
4487
|
case "page": {
|
|
3418
4488
|
const pagePath = args[2];
|
|
3419
4489
|
if (!pagePath) {
|
|
3420
|
-
console.error(
|
|
4490
|
+
console.error(error3("Usage: decantr add page <section>/<page>"));
|
|
3421
4491
|
process.exitCode = 1;
|
|
3422
4492
|
break;
|
|
3423
4493
|
}
|
|
@@ -3427,7 +4497,7 @@ async function main() {
|
|
|
3427
4497
|
case "feature": {
|
|
3428
4498
|
const feature = args[2];
|
|
3429
4499
|
if (!feature) {
|
|
3430
|
-
console.error(
|
|
4500
|
+
console.error(error3("Usage: decantr add feature <feature> [--section <id>]"));
|
|
3431
4501
|
process.exitCode = 1;
|
|
3432
4502
|
break;
|
|
3433
4503
|
}
|
|
@@ -3435,7 +4505,7 @@ async function main() {
|
|
|
3435
4505
|
break;
|
|
3436
4506
|
}
|
|
3437
4507
|
default:
|
|
3438
|
-
console.error(
|
|
4508
|
+
console.error(error3(`Unknown add subcommand: ${subcommand}. Use section, page, or feature.`));
|
|
3439
4509
|
process.exitCode = 1;
|
|
3440
4510
|
}
|
|
3441
4511
|
break;
|
|
@@ -3443,7 +4513,7 @@ async function main() {
|
|
|
3443
4513
|
case "remove": {
|
|
3444
4514
|
const subcommand = args[1];
|
|
3445
4515
|
if (!subcommand) {
|
|
3446
|
-
console.error(
|
|
4516
|
+
console.error(error3("Usage: decantr remove <section|page|feature> <target>"));
|
|
3447
4517
|
process.exitCode = 1;
|
|
3448
4518
|
break;
|
|
3449
4519
|
}
|
|
@@ -3451,7 +4521,7 @@ async function main() {
|
|
|
3451
4521
|
case "section": {
|
|
3452
4522
|
const id = args[2];
|
|
3453
4523
|
if (!id) {
|
|
3454
|
-
console.error(
|
|
4524
|
+
console.error(error3("Usage: decantr remove section <sectionId>"));
|
|
3455
4525
|
process.exitCode = 1;
|
|
3456
4526
|
break;
|
|
3457
4527
|
}
|
|
@@ -3461,7 +4531,7 @@ async function main() {
|
|
|
3461
4531
|
case "page": {
|
|
3462
4532
|
const pagePath = args[2];
|
|
3463
4533
|
if (!pagePath) {
|
|
3464
|
-
console.error(
|
|
4534
|
+
console.error(error3("Usage: decantr remove page <section>/<page>"));
|
|
3465
4535
|
process.exitCode = 1;
|
|
3466
4536
|
break;
|
|
3467
4537
|
}
|
|
@@ -3471,7 +4541,7 @@ async function main() {
|
|
|
3471
4541
|
case "feature": {
|
|
3472
4542
|
const feature = args[2];
|
|
3473
4543
|
if (!feature) {
|
|
3474
|
-
console.error(
|
|
4544
|
+
console.error(error3("Usage: decantr remove feature <feature> [--section <id>]"));
|
|
3475
4545
|
process.exitCode = 1;
|
|
3476
4546
|
break;
|
|
3477
4547
|
}
|
|
@@ -3479,7 +4549,7 @@ async function main() {
|
|
|
3479
4549
|
break;
|
|
3480
4550
|
}
|
|
3481
4551
|
default:
|
|
3482
|
-
console.error(
|
|
4552
|
+
console.error(error3(`Unknown remove subcommand: ${subcommand}. Use section, page, or feature.`));
|
|
3483
4553
|
process.exitCode = 1;
|
|
3484
4554
|
}
|
|
3485
4555
|
break;
|
|
@@ -3488,13 +4558,69 @@ async function main() {
|
|
|
3488
4558
|
cmdAnalyze(process.cwd());
|
|
3489
4559
|
break;
|
|
3490
4560
|
}
|
|
4561
|
+
case "magic": {
|
|
4562
|
+
const magicFlags = {};
|
|
4563
|
+
const promptParts = [];
|
|
4564
|
+
for (let i = 1; i < args.length; i++) {
|
|
4565
|
+
if (args[i] === "--dry-run") {
|
|
4566
|
+
magicFlags.dryRun = true;
|
|
4567
|
+
} else if (args[i] === "--offline") {
|
|
4568
|
+
magicFlags.offline = true;
|
|
4569
|
+
} else if (args[i].startsWith("--registry=")) {
|
|
4570
|
+
magicFlags.registry = args[i].split("=")[1];
|
|
4571
|
+
} else if (args[i].startsWith("--registry") && args[i + 1]) {
|
|
4572
|
+
magicFlags.registry = args[++i];
|
|
4573
|
+
} else {
|
|
4574
|
+
promptParts.push(args[i]);
|
|
4575
|
+
}
|
|
4576
|
+
}
|
|
4577
|
+
const magicPrompt = promptParts.join(" ").trim();
|
|
4578
|
+
if (!magicPrompt) {
|
|
4579
|
+
console.error(error3("Usage: decantr magic <prompt> [--dry-run] [--offline]"));
|
|
4580
|
+
console.error("");
|
|
4581
|
+
console.error(" Example:");
|
|
4582
|
+
console.error(` ${CYAN7}decantr magic "AI agent dashboard \u2014 dark, neon, confident"${RESET13}`);
|
|
4583
|
+
process.exitCode = 1;
|
|
4584
|
+
break;
|
|
4585
|
+
}
|
|
4586
|
+
await cmdMagic(magicPrompt, process.cwd(), {
|
|
4587
|
+
dryRun: magicFlags.dryRun,
|
|
4588
|
+
offline: magicFlags.offline,
|
|
4589
|
+
registry: magicFlags.registry
|
|
4590
|
+
});
|
|
4591
|
+
break;
|
|
4592
|
+
}
|
|
4593
|
+
case "export": {
|
|
4594
|
+
let exportTarget;
|
|
4595
|
+
let exportOutput;
|
|
4596
|
+
for (let i = 1; i < args.length; i++) {
|
|
4597
|
+
if (args[i] === "--to" && args[i + 1]) {
|
|
4598
|
+
exportTarget = args[++i];
|
|
4599
|
+
} else if (args[i].startsWith("--to=")) {
|
|
4600
|
+
exportTarget = args[i].split("=")[1];
|
|
4601
|
+
} else if (args[i] === "--output" && args[i + 1]) {
|
|
4602
|
+
exportOutput = args[++i];
|
|
4603
|
+
} else if (args[i].startsWith("--output=")) {
|
|
4604
|
+
exportOutput = args[i].split("=")[1];
|
|
4605
|
+
}
|
|
4606
|
+
}
|
|
4607
|
+
const validTargets = ["shadcn", "tailwind", "css-vars"];
|
|
4608
|
+
if (!exportTarget || !validTargets.includes(exportTarget)) {
|
|
4609
|
+
console.error(error3(`Usage: decantr export --to <${validTargets.join("|")}>`));
|
|
4610
|
+
process.exitCode = 1;
|
|
4611
|
+
break;
|
|
4612
|
+
}
|
|
4613
|
+
await cmdExport(exportTarget, process.cwd(), { output: exportOutput });
|
|
4614
|
+
break;
|
|
4615
|
+
}
|
|
3491
4616
|
default:
|
|
3492
|
-
console.error(
|
|
4617
|
+
console.error(error3(`Unknown command: ${command}`));
|
|
3493
4618
|
cmdHelp();
|
|
3494
4619
|
process.exitCode = 1;
|
|
3495
4620
|
}
|
|
3496
4621
|
}
|
|
3497
4622
|
main().catch((e) => {
|
|
3498
|
-
console.error(
|
|
4623
|
+
console.error(error3(e.message));
|
|
4624
|
+
if (e.stack) console.error(e.stack);
|
|
3499
4625
|
process.exitCode = 1;
|
|
3500
4626
|
});
|