@decantr/cli 1.5.0 → 1.5.3
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-6K6ZPDT4.js → chunk-B5OX2EXL.js} +565 -359
- package/dist/{chunk-SUNMRG3P.js → chunk-QZVA3PFR.js} +1381 -255
- package/dist/heal-54MKDDSQ.js +155 -0
- package/dist/index.js +2 -2
- package/dist/{upgrade-I2RUTNAT.js → upgrade-LTLUPBZQ.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-B5OX2EXL.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,1023 @@ 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
|
+
treatment_overrides: recipe.treatment_overrides
|
|
2650
|
+
};
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
console.log(`${BOLD4} Scaffolding...${RESET9}`);
|
|
2654
|
+
const result = await scaffoldProject(
|
|
2655
|
+
projectRoot,
|
|
2656
|
+
initOptions,
|
|
2657
|
+
detected,
|
|
2658
|
+
registryClient,
|
|
2659
|
+
archetypeData,
|
|
2660
|
+
registrySource,
|
|
2661
|
+
themeData,
|
|
2662
|
+
recipeData,
|
|
2663
|
+
topologyMarkdown,
|
|
2664
|
+
composedSections,
|
|
2665
|
+
routeMap,
|
|
2666
|
+
patternSpecs,
|
|
2667
|
+
blueprintData
|
|
2668
|
+
);
|
|
2669
|
+
console.log(` ${success("Created")} decantr.essence.json (V3.1)`);
|
|
2670
|
+
console.log(` ${success("Created")} DECANTR.md`);
|
|
2671
|
+
if (result.cssFiles && result.cssFiles.length > 0) {
|
|
2672
|
+
for (const cssFile of result.cssFiles) {
|
|
2673
|
+
const name = cssFile.split("/").pop();
|
|
2674
|
+
console.log(` ${success("Created")} src/styles/${name}`);
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
if (result.contextFiles && result.contextFiles.length > 0) {
|
|
2678
|
+
const sectionContexts = result.contextFiles.filter((f) => f.includes("section-"));
|
|
2679
|
+
const otherContexts = result.contextFiles.filter((f) => !f.includes("section-"));
|
|
2680
|
+
if (sectionContexts.length > 0) {
|
|
2681
|
+
console.log(` ${success("Created")} ${sectionContexts.length} section context(s)`);
|
|
2682
|
+
}
|
|
2683
|
+
for (const f of otherContexts) {
|
|
2684
|
+
const name = f.split("/").pop();
|
|
2685
|
+
if (name && !name.startsWith("task-") && !name.startsWith("essence-summary")) {
|
|
2686
|
+
console.log(` ${success("Created")} ${name}`);
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
if (result.gitignoreUpdated) {
|
|
2691
|
+
console.log(` ${dim(".gitignore updated")}`);
|
|
2692
|
+
}
|
|
2693
|
+
console.log("");
|
|
2694
|
+
console.log(`${BOLD4} Ready!${RESET9} Next steps:`);
|
|
2695
|
+
console.log(` 1. Read ${cyan("DECANTR.md")} to understand the design system`);
|
|
2696
|
+
console.log(` 2. Read ${cyan(".decantr/context/scaffold.md")} for the full app overview`);
|
|
2697
|
+
console.log(` 3. Start building pages from the route map`);
|
|
2698
|
+
console.log("");
|
|
2699
|
+
}
|
|
2700
|
+
|
|
2701
|
+
// src/commands/export.ts
|
|
2702
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync10, existsSync as existsSync20, mkdirSync as mkdirSync5 } from "fs";
|
|
2703
|
+
import { join as join20, dirname } from "path";
|
|
2704
|
+
var GREEN10 = "\x1B[32m";
|
|
2705
|
+
var RED8 = "\x1B[31m";
|
|
2706
|
+
var DIM10 = "\x1B[2m";
|
|
2707
|
+
var RESET10 = "\x1B[0m";
|
|
2708
|
+
var SHADCN_MAP = {
|
|
2709
|
+
"--d-bg": "--background",
|
|
2710
|
+
"--d-text": "--foreground",
|
|
2711
|
+
"--d-surface": "--card",
|
|
2712
|
+
"--d-surface-raised": "--popover",
|
|
2713
|
+
"--d-primary": "--primary",
|
|
2714
|
+
"--d-secondary": "--secondary",
|
|
2715
|
+
"--d-accent": "--accent",
|
|
2716
|
+
"--d-border": "--border",
|
|
2717
|
+
"--d-text-muted": "--muted-foreground",
|
|
2718
|
+
"--d-error": "--destructive",
|
|
2719
|
+
"--d-radius": "--radius"
|
|
2720
|
+
};
|
|
2721
|
+
var TAILWIND_COLOR_MAP = {
|
|
2722
|
+
"--d-primary": "primary",
|
|
2723
|
+
"--d-secondary": "secondary",
|
|
2724
|
+
"--d-accent": "accent",
|
|
2725
|
+
"--d-bg": "background",
|
|
2726
|
+
"--d-surface": "surface",
|
|
2727
|
+
"--d-surface-raised": "surface-raised",
|
|
2728
|
+
"--d-border": "border",
|
|
2729
|
+
"--d-text": "foreground",
|
|
2730
|
+
"--d-text-muted": "muted",
|
|
2731
|
+
"--d-success": "success",
|
|
2732
|
+
"--d-error": "error",
|
|
2733
|
+
"--d-warning": "warning",
|
|
2734
|
+
"--d-info": "info"
|
|
2735
|
+
};
|
|
2736
|
+
function parseTokensCSS(css) {
|
|
2737
|
+
const tokens = /* @__PURE__ */ new Map();
|
|
2738
|
+
const re = /(--d-[\w-]+)\s*:\s*([^;]+);/g;
|
|
2739
|
+
let match;
|
|
2740
|
+
while ((match = re.exec(css)) !== null) {
|
|
2741
|
+
tokens.set(match[1], match[2].trim());
|
|
2742
|
+
}
|
|
2743
|
+
return tokens;
|
|
2744
|
+
}
|
|
2745
|
+
function generateShadcnCSS(tokens) {
|
|
2746
|
+
const lines = ["/* Exported by decantr export --to shadcn */", "@layer base {", " :root {"];
|
|
2747
|
+
for (const [decantrVar, shadcnVar] of Object.entries(SHADCN_MAP)) {
|
|
2748
|
+
const value = tokens.get(decantrVar);
|
|
2749
|
+
if (value) {
|
|
2750
|
+
lines.push(` ${shadcnVar}: ${value};`);
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
const textValue = tokens.get("--d-text");
|
|
2754
|
+
if (textValue) {
|
|
2755
|
+
lines.push(` --card-foreground: ${textValue};`);
|
|
2756
|
+
lines.push(` --popover-foreground: ${textValue};`);
|
|
2757
|
+
}
|
|
2758
|
+
const surfaceValue = tokens.get("--d-surface");
|
|
2759
|
+
if (surfaceValue) {
|
|
2760
|
+
lines.push(` --muted: ${surfaceValue};`);
|
|
2761
|
+
}
|
|
2762
|
+
const accentFg = tokens.get("--d-text");
|
|
2763
|
+
if (accentFg) {
|
|
2764
|
+
lines.push(` --accent-foreground: ${accentFg};`);
|
|
2765
|
+
}
|
|
2766
|
+
lines.push(` --destructive-foreground: #FAFAFA;`);
|
|
2767
|
+
lines.push(" }", "}", "");
|
|
2768
|
+
return lines.join("\n");
|
|
2769
|
+
}
|
|
2770
|
+
function generateShadcnComponentsJSON() {
|
|
2771
|
+
const config = {
|
|
2772
|
+
$schema: "https://ui.shadcn.com/schema.json",
|
|
2773
|
+
style: "default",
|
|
2774
|
+
rsc: false,
|
|
2775
|
+
tsx: true,
|
|
2776
|
+
tailwind: {
|
|
2777
|
+
config: "tailwind.config.ts",
|
|
2778
|
+
css: "src/styles/shadcn-theme.css",
|
|
2779
|
+
baseColor: "zinc"
|
|
2780
|
+
},
|
|
2781
|
+
aliases: {
|
|
2782
|
+
components: "@/components",
|
|
2783
|
+
utils: "@/lib/utils"
|
|
2784
|
+
}
|
|
2785
|
+
};
|
|
2786
|
+
return JSON.stringify(config, null, 2) + "\n";
|
|
2787
|
+
}
|
|
2788
|
+
function generateTailwindConfig(tokens) {
|
|
2789
|
+
const colors = {};
|
|
2790
|
+
for (const [decantrVar, twName] of Object.entries(TAILWIND_COLOR_MAP)) {
|
|
2791
|
+
const value = tokens.get(decantrVar);
|
|
2792
|
+
if (value) {
|
|
2793
|
+
colors[twName] = value;
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
const borderRadius = {};
|
|
2797
|
+
for (const suffix of ["", "-sm", "-lg", "-xl", "-full"]) {
|
|
2798
|
+
const key = `--d-radius${suffix}`;
|
|
2799
|
+
const value = tokens.get(key);
|
|
2800
|
+
if (value) {
|
|
2801
|
+
const name = suffix ? suffix.slice(1) : "DEFAULT";
|
|
2802
|
+
borderRadius[name] = value;
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
const boxShadow = {};
|
|
2806
|
+
for (const suffix of ["-sm", "", "-md", "-lg"]) {
|
|
2807
|
+
const key = `--d-shadow${suffix}`;
|
|
2808
|
+
const value = tokens.get(key);
|
|
2809
|
+
if (value) {
|
|
2810
|
+
const name = suffix ? suffix.slice(1) : "DEFAULT";
|
|
2811
|
+
boxShadow[name] = value;
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
const lines = [
|
|
2815
|
+
"// Generated by decantr export --to tailwind",
|
|
2816
|
+
"import type { Config } from 'tailwindcss';",
|
|
2817
|
+
"",
|
|
2818
|
+
"export default {",
|
|
2819
|
+
" theme: {",
|
|
2820
|
+
" extend: {"
|
|
2821
|
+
];
|
|
2822
|
+
lines.push(" colors: {");
|
|
2823
|
+
for (const [name, value] of Object.entries(colors)) {
|
|
2824
|
+
lines.push(` '${name}': '${value}',`);
|
|
2825
|
+
}
|
|
2826
|
+
lines.push(" },");
|
|
2827
|
+
lines.push(" borderRadius: {");
|
|
2828
|
+
for (const [name, value] of Object.entries(borderRadius)) {
|
|
2829
|
+
lines.push(` '${name}': '${value}',`);
|
|
2830
|
+
}
|
|
2831
|
+
lines.push(" },");
|
|
2832
|
+
lines.push(" boxShadow: {");
|
|
2833
|
+
for (const [name, value] of Object.entries(boxShadow)) {
|
|
2834
|
+
lines.push(` '${name}': '${value}',`);
|
|
2835
|
+
}
|
|
2836
|
+
lines.push(" },");
|
|
2837
|
+
lines.push(" },", " },", "} satisfies Partial<Config>;", "");
|
|
2838
|
+
return lines.join("\n");
|
|
2839
|
+
}
|
|
2840
|
+
function generateCSSVars(tokens) {
|
|
2841
|
+
const lines = ["/* Exported by decantr export --to css-vars */", ":root {"];
|
|
2842
|
+
for (const [key, value] of tokens) {
|
|
2843
|
+
lines.push(` ${key}: ${value};`);
|
|
2844
|
+
}
|
|
2845
|
+
lines.push("}", "");
|
|
2846
|
+
return lines.join("\n");
|
|
2847
|
+
}
|
|
2848
|
+
async function cmdExport(target, projectRoot, options = {}) {
|
|
2849
|
+
const essencePath = join20(projectRoot, "decantr.essence.json");
|
|
2850
|
+
const tokensPath = join20(projectRoot, "src", "styles", "tokens.css");
|
|
2851
|
+
if (!existsSync20(essencePath)) {
|
|
2852
|
+
console.error(`${RED8}No decantr.essence.json found. Run \`decantr init\` first.${RESET10}`);
|
|
2853
|
+
process.exitCode = 1;
|
|
2854
|
+
return;
|
|
2855
|
+
}
|
|
2856
|
+
if (!existsSync20(tokensPath)) {
|
|
2857
|
+
console.error(`${RED8}No src/styles/tokens.css found. Run \`decantr refresh\` to generate tokens.${RESET10}`);
|
|
2858
|
+
process.exitCode = 1;
|
|
2859
|
+
return;
|
|
2860
|
+
}
|
|
2861
|
+
const tokensCSS = readFileSync14(tokensPath, "utf-8");
|
|
2862
|
+
const tokens = parseTokensCSS(tokensCSS);
|
|
2863
|
+
if (tokens.size === 0) {
|
|
2864
|
+
console.error(`${RED8}No --d-* tokens found in tokens.css.${RESET10}`);
|
|
2865
|
+
process.exitCode = 1;
|
|
2866
|
+
return;
|
|
2867
|
+
}
|
|
2868
|
+
switch (target) {
|
|
2869
|
+
case "shadcn": {
|
|
2870
|
+
const cssOut = options.output ?? join20(projectRoot, "src", "styles", "shadcn-theme.css");
|
|
2871
|
+
const jsonOut = join20(projectRoot, "components.json");
|
|
2872
|
+
ensureDir(cssOut);
|
|
2873
|
+
writeFileSync10(cssOut, generateShadcnCSS(tokens), "utf-8");
|
|
2874
|
+
writeFileSync10(jsonOut, generateShadcnComponentsJSON(), "utf-8");
|
|
2875
|
+
console.log(`${GREEN10}Exported shadcn theme:${RESET10}`);
|
|
2876
|
+
console.log(` ${DIM10}CSS:${RESET10} ${cssOut}`);
|
|
2877
|
+
console.log(` ${DIM10}JSON:${RESET10} ${jsonOut}`);
|
|
2878
|
+
break;
|
|
2879
|
+
}
|
|
2880
|
+
case "tailwind": {
|
|
2881
|
+
const out = options.output ?? join20(projectRoot, "tailwind.decantr.config.ts");
|
|
2882
|
+
ensureDir(out);
|
|
2883
|
+
writeFileSync10(out, generateTailwindConfig(tokens), "utf-8");
|
|
2884
|
+
console.log(`${GREEN10}Exported Tailwind config:${RESET10}`);
|
|
2885
|
+
console.log(` ${DIM10}File:${RESET10} ${out}`);
|
|
2886
|
+
break;
|
|
2887
|
+
}
|
|
2888
|
+
case "css-vars": {
|
|
2889
|
+
const out = options.output ?? join20(projectRoot, "decantr-tokens.css");
|
|
2890
|
+
ensureDir(out);
|
|
2891
|
+
writeFileSync10(out, generateCSSVars(tokens), "utf-8");
|
|
2892
|
+
console.log(`${GREEN10}Exported CSS variables:${RESET10}`);
|
|
2893
|
+
console.log(` ${DIM10}File:${RESET10} ${out}`);
|
|
2894
|
+
break;
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
function ensureDir(filePath) {
|
|
2899
|
+
const dir = dirname(filePath);
|
|
2900
|
+
if (!existsSync20(dir)) {
|
|
2901
|
+
mkdirSync5(dir, { recursive: true });
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
|
|
2905
|
+
// src/commands/registry-mirror.ts
|
|
2906
|
+
import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync11 } from "fs";
|
|
2907
|
+
import { join as join21 } from "path";
|
|
2908
|
+
import { RegistryAPIClient as RegistryAPIClient2 } from "@decantr/registry";
|
|
2909
|
+
var GREEN11 = "\x1B[32m";
|
|
2910
|
+
var RED9 = "\x1B[31m";
|
|
2911
|
+
var DIM11 = "\x1B[2m";
|
|
2912
|
+
var CYAN5 = "\x1B[36m";
|
|
2913
|
+
var YELLOW7 = "\x1B[33m";
|
|
2914
|
+
var RESET11 = "\x1B[0m";
|
|
2915
|
+
var ALL_CONTENT_TYPES = [
|
|
2916
|
+
"patterns",
|
|
2917
|
+
"archetypes",
|
|
2918
|
+
"themes",
|
|
2919
|
+
"recipes",
|
|
2920
|
+
"blueprints",
|
|
2921
|
+
"shells"
|
|
2922
|
+
];
|
|
2923
|
+
async function cmdRegistryMirror(projectRoot, options = {}) {
|
|
2924
|
+
const apiUrl = process.env.DECANTR_API_URL || "https://api.decantr.ai/v1";
|
|
2925
|
+
const apiClient = new RegistryAPIClient2({
|
|
2926
|
+
baseUrl: apiUrl,
|
|
2927
|
+
apiKey: process.env.DECANTR_API_KEY || void 0
|
|
2928
|
+
});
|
|
2929
|
+
const healthy = await apiClient.checkHealth();
|
|
2930
|
+
if (!healthy) {
|
|
2931
|
+
console.error(`${RED9}Registry API is unavailable. Check your connection.${RESET11}`);
|
|
2932
|
+
process.exitCode = 1;
|
|
2933
|
+
return;
|
|
2934
|
+
}
|
|
2935
|
+
const types = options.type ? [options.type] : ALL_CONTENT_TYPES;
|
|
2936
|
+
if (options.type && !ALL_CONTENT_TYPES.includes(options.type)) {
|
|
2937
|
+
console.error(`${RED9}Unknown content type: ${options.type}${RESET11}`);
|
|
2938
|
+
console.error(`${DIM11}Valid types: ${ALL_CONTENT_TYPES.join(", ")}${RESET11}`);
|
|
2939
|
+
process.exitCode = 1;
|
|
2940
|
+
return;
|
|
2941
|
+
}
|
|
2942
|
+
const cacheDir = join21(projectRoot, ".decantr", "cache");
|
|
2943
|
+
const counts = {};
|
|
2944
|
+
const failed = [];
|
|
2945
|
+
console.log(`
|
|
2946
|
+
Mirroring registry content to ${DIM11}.decantr/cache/${RESET11}
|
|
2947
|
+
`);
|
|
2948
|
+
for (const type of types) {
|
|
2949
|
+
try {
|
|
2950
|
+
const result = await apiClient.listContent(type, { namespace: "@official" });
|
|
2951
|
+
const items = result.items;
|
|
2952
|
+
const typeDir = join21(cacheDir, "@official", type);
|
|
2953
|
+
mkdirSync6(typeDir, { recursive: true });
|
|
2954
|
+
writeFileSync11(join21(typeDir, "index.json"), JSON.stringify(result, null, 2));
|
|
2955
|
+
let itemCount = 0;
|
|
2956
|
+
for (const item of items) {
|
|
2957
|
+
const slug = item.slug || item.id;
|
|
2958
|
+
if (!slug) continue;
|
|
2959
|
+
try {
|
|
2960
|
+
const fullItem = await apiClient.getContent(type, "@official", slug);
|
|
2961
|
+
writeFileSync11(join21(typeDir, `${slug}.json`), JSON.stringify(fullItem, null, 2));
|
|
2962
|
+
itemCount++;
|
|
2963
|
+
} catch {
|
|
2964
|
+
writeFileSync11(join21(typeDir, `${slug}.json`), JSON.stringify(item, null, 2));
|
|
2965
|
+
itemCount++;
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
counts[type] = itemCount;
|
|
2969
|
+
console.log(` ${GREEN11}\u2713${RESET11} ${type}: ${CYAN5}${itemCount}${RESET11} items`);
|
|
2970
|
+
} catch (e) {
|
|
2971
|
+
failed.push(type);
|
|
2972
|
+
console.log(` ${RED9}\u2717${RESET11} ${type}: ${e.message}`);
|
|
2973
|
+
}
|
|
2974
|
+
}
|
|
2975
|
+
const manifest = {
|
|
2976
|
+
mirrored_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2977
|
+
counts
|
|
2978
|
+
};
|
|
2979
|
+
mkdirSync6(join21(cacheDir), { recursive: true });
|
|
2980
|
+
writeFileSync11(join21(cacheDir, "mirror-manifest.json"), JSON.stringify(manifest, null, 2));
|
|
2981
|
+
const totalItems = Object.values(counts).reduce((a, b) => a + b, 0);
|
|
2982
|
+
console.log("");
|
|
2983
|
+
if (failed.length > 0) {
|
|
2984
|
+
console.log(`${YELLOW7}Mirrored ${totalItems} items (${failed.length} type(s) failed)${RESET11}`);
|
|
2985
|
+
} else {
|
|
2986
|
+
console.log(`${GREEN11}Mirrored ${totalItems} items across ${Object.keys(counts).length} types${RESET11}`);
|
|
2987
|
+
}
|
|
2988
|
+
console.log(`${DIM11}Use \`decantr init --offline\` or \`decantr refresh --offline\` to work without API.${RESET11}
|
|
2989
|
+
`);
|
|
2990
|
+
}
|
|
2991
|
+
|
|
2992
|
+
// src/commands/new-project.ts
|
|
2993
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync7, writeFileSync as writeFileSync12 } from "fs";
|
|
2994
|
+
import { join as join22, resolve } from "path";
|
|
2995
|
+
import { execSync } from "child_process";
|
|
2996
|
+
var BOLD5 = "\x1B[1m";
|
|
2997
|
+
var DIM12 = "\x1B[2m";
|
|
2998
|
+
var RESET12 = "\x1B[0m";
|
|
2999
|
+
var RED10 = "\x1B[31m";
|
|
3000
|
+
var GREEN12 = "\x1B[32m";
|
|
3001
|
+
var CYAN6 = "\x1B[36m";
|
|
3002
|
+
var YELLOW8 = "\x1B[33m";
|
|
3003
|
+
function heading(text) {
|
|
3004
|
+
return `
|
|
3005
|
+
${BOLD5}${text}${RESET12}
|
|
3006
|
+
`;
|
|
3007
|
+
}
|
|
3008
|
+
function success2(text) {
|
|
3009
|
+
return `${GREEN12}${text}${RESET12}`;
|
|
3010
|
+
}
|
|
3011
|
+
function error2(text) {
|
|
3012
|
+
return `${RED10}${text}${RESET12}`;
|
|
3013
|
+
}
|
|
3014
|
+
function dim2(text) {
|
|
3015
|
+
return `${DIM12}${text}${RESET12}`;
|
|
3016
|
+
}
|
|
3017
|
+
function cyan2(text) {
|
|
3018
|
+
return `${CYAN6}${text}${RESET12}`;
|
|
3019
|
+
}
|
|
3020
|
+
async function cmdNewProject(projectName, options) {
|
|
3021
|
+
const projectDir = resolve(process.cwd(), projectName);
|
|
3022
|
+
if (!/^[a-z0-9][a-z0-9._-]*$/i.test(projectName)) {
|
|
3023
|
+
console.error(error2("Invalid project name. Use alphanumeric characters, hyphens, dots, or underscores."));
|
|
3024
|
+
process.exitCode = 1;
|
|
3025
|
+
return;
|
|
3026
|
+
}
|
|
3027
|
+
if (existsSync22(projectDir)) {
|
|
3028
|
+
console.error(error2(`Directory "${projectName}" already exists.`));
|
|
3029
|
+
process.exitCode = 1;
|
|
3030
|
+
return;
|
|
3031
|
+
}
|
|
3032
|
+
console.log(heading(`Creating ${projectName}...`));
|
|
3033
|
+
mkdirSync7(projectDir, { recursive: true });
|
|
3034
|
+
console.log(dim2(` Created ${projectName}/`));
|
|
3035
|
+
const packageJson = {
|
|
3036
|
+
name: projectName,
|
|
3037
|
+
private: true,
|
|
3038
|
+
version: "0.0.0",
|
|
3039
|
+
type: "module",
|
|
3040
|
+
scripts: {
|
|
3041
|
+
dev: "vite",
|
|
3042
|
+
build: "tsc -b && vite build",
|
|
3043
|
+
preview: "vite preview"
|
|
3044
|
+
},
|
|
3045
|
+
dependencies: {
|
|
3046
|
+
"react": "^19.0.0",
|
|
3047
|
+
"react-dom": "^19.0.0",
|
|
3048
|
+
"react-router-dom": "^7.0.0",
|
|
3049
|
+
"@decantr/css": "^1.0.0"
|
|
3050
|
+
},
|
|
3051
|
+
devDependencies: {
|
|
3052
|
+
"@types/react": "^19.0.0",
|
|
3053
|
+
"@types/react-dom": "^19.0.0",
|
|
3054
|
+
"@vitejs/plugin-react": "^4.0.0",
|
|
3055
|
+
"typescript": "^5.7.0",
|
|
3056
|
+
"vite": "^6.0.0"
|
|
3057
|
+
}
|
|
3058
|
+
};
|
|
3059
|
+
writeFileSync12(join22(projectDir, "package.json"), JSON.stringify(packageJson, null, 2) + "\n");
|
|
3060
|
+
console.log(dim2(" Created package.json"));
|
|
3061
|
+
const viteConfig = `import { defineConfig } from 'vite';
|
|
3062
|
+
import react from '@vitejs/plugin-react';
|
|
3063
|
+
|
|
3064
|
+
export default defineConfig({
|
|
3065
|
+
plugins: [react()],
|
|
3066
|
+
});
|
|
3067
|
+
`;
|
|
3068
|
+
writeFileSync12(join22(projectDir, "vite.config.ts"), viteConfig);
|
|
3069
|
+
console.log(dim2(" Created vite.config.ts"));
|
|
3070
|
+
const tsconfig = {
|
|
3071
|
+
compilerOptions: {
|
|
3072
|
+
target: "ES2020",
|
|
3073
|
+
useDefineForClassFields: true,
|
|
3074
|
+
lib: ["ES2020", "DOM", "DOM.Iterable"],
|
|
3075
|
+
module: "ESNext",
|
|
3076
|
+
skipLibCheck: true,
|
|
3077
|
+
moduleResolution: "bundler",
|
|
3078
|
+
allowImportingTsExtensions: true,
|
|
3079
|
+
isolatedModules: true,
|
|
3080
|
+
moduleDetection: "force",
|
|
3081
|
+
noEmit: true,
|
|
3082
|
+
jsx: "react-jsx",
|
|
3083
|
+
strict: true,
|
|
3084
|
+
noUnusedLocals: true,
|
|
3085
|
+
noUnusedParameters: true,
|
|
3086
|
+
noFallthroughCasesInSwitch: true,
|
|
3087
|
+
noUncheckedSideEffectImports: true
|
|
3088
|
+
},
|
|
3089
|
+
include: ["src"]
|
|
3090
|
+
};
|
|
3091
|
+
writeFileSync12(join22(projectDir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2) + "\n");
|
|
3092
|
+
const tsconfigApp = {
|
|
3093
|
+
compilerOptions: {
|
|
3094
|
+
tsBuildInfoFile: "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
3095
|
+
target: "ES2020",
|
|
3096
|
+
useDefineForClassFields: true,
|
|
3097
|
+
lib: ["ES2020", "DOM", "DOM.Iterable"],
|
|
3098
|
+
module: "ESNext",
|
|
3099
|
+
skipLibCheck: true,
|
|
3100
|
+
moduleResolution: "bundler",
|
|
3101
|
+
allowImportingTsExtensions: true,
|
|
3102
|
+
isolatedModules: true,
|
|
3103
|
+
moduleDetection: "force",
|
|
3104
|
+
noEmit: true,
|
|
3105
|
+
jsx: "react-jsx",
|
|
3106
|
+
strict: true,
|
|
3107
|
+
noUnusedLocals: true,
|
|
3108
|
+
noUnusedParameters: true,
|
|
3109
|
+
noFallthroughCasesInSwitch: true,
|
|
3110
|
+
noUncheckedSideEffectImports: true
|
|
3111
|
+
},
|
|
3112
|
+
include: ["src"]
|
|
3113
|
+
};
|
|
3114
|
+
writeFileSync12(join22(projectDir, "tsconfig.app.json"), JSON.stringify(tsconfigApp, null, 2) + "\n");
|
|
3115
|
+
console.log(dim2(" Created tsconfig.json"));
|
|
3116
|
+
const title = projectName.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
3117
|
+
const indexHtml = `<!doctype html>
|
|
3118
|
+
<html lang="en">
|
|
3119
|
+
<head>
|
|
3120
|
+
<meta charset="UTF-8" />
|
|
3121
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
3122
|
+
<title>${title}</title>
|
|
3123
|
+
</head>
|
|
3124
|
+
<body>
|
|
3125
|
+
<div id="root"></div>
|
|
3126
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
3127
|
+
</body>
|
|
3128
|
+
</html>
|
|
3129
|
+
`;
|
|
3130
|
+
writeFileSync12(join22(projectDir, "index.html"), indexHtml);
|
|
3131
|
+
console.log(dim2(" Created index.html"));
|
|
3132
|
+
const srcDir = join22(projectDir, "src");
|
|
3133
|
+
mkdirSync7(srcDir, { recursive: true });
|
|
3134
|
+
const mainTsx = `import { StrictMode } from 'react';
|
|
3135
|
+
import { createRoot } from 'react-dom/client';
|
|
3136
|
+
import { BrowserRouter } from 'react-router-dom';
|
|
3137
|
+
import { App } from './App';
|
|
3138
|
+
import './styles/tokens.css';
|
|
3139
|
+
import './styles/treatments.css';
|
|
3140
|
+
import './styles/global.css';
|
|
3141
|
+
|
|
3142
|
+
createRoot(document.getElementById('root')!).render(
|
|
3143
|
+
<StrictMode>
|
|
3144
|
+
<BrowserRouter>
|
|
3145
|
+
<App />
|
|
3146
|
+
</BrowserRouter>
|
|
3147
|
+
</StrictMode>,
|
|
3148
|
+
);
|
|
3149
|
+
`;
|
|
3150
|
+
writeFileSync12(join22(srcDir, "main.tsx"), mainTsx);
|
|
3151
|
+
const appTsx = `import { Routes, Route } from 'react-router-dom';
|
|
3152
|
+
|
|
3153
|
+
function WelcomePage() {
|
|
3154
|
+
return (
|
|
3155
|
+
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', minHeight: '100vh', gap: '1rem' }}>
|
|
3156
|
+
<h1>${title}</h1>
|
|
3157
|
+
<p style={{ opacity: 0.6 }}>Scaffolded with Decantr. Run <code>decantr status</code> to check project health.</p>
|
|
3158
|
+
</div>
|
|
3159
|
+
);
|
|
3160
|
+
}
|
|
3161
|
+
|
|
3162
|
+
export function App() {
|
|
3163
|
+
return (
|
|
3164
|
+
<Routes>
|
|
3165
|
+
<Route path="/" element={<WelcomePage />} />
|
|
3166
|
+
</Routes>
|
|
3167
|
+
);
|
|
3168
|
+
}
|
|
3169
|
+
`;
|
|
3170
|
+
writeFileSync12(join22(srcDir, "App.tsx"), appTsx);
|
|
3171
|
+
writeFileSync12(join22(srcDir, "vite-env.d.ts"), '/// <reference types="vite/client" />\n');
|
|
3172
|
+
mkdirSync7(join22(srcDir, "styles"), { recursive: true });
|
|
3173
|
+
console.log(dim2(" Created src/"));
|
|
3174
|
+
console.log(heading("Installing dependencies..."));
|
|
3175
|
+
const packageManager = detectPackageManager();
|
|
3176
|
+
try {
|
|
3177
|
+
execSync(`${packageManager} install`, { cwd: projectDir, stdio: "inherit" });
|
|
3178
|
+
} catch {
|
|
3179
|
+
console.log(`
|
|
3180
|
+
${YELLOW8}Dependency install failed. Run \`${packageManager} install\` manually.${RESET12}`);
|
|
3181
|
+
}
|
|
3182
|
+
console.log(heading("Initializing Decantr..."));
|
|
3183
|
+
const initFlags = ["--yes", "--existing"];
|
|
3184
|
+
if (options.blueprint) initFlags.push(`--blueprint=${options.blueprint}`);
|
|
3185
|
+
if (options.archetype) initFlags.push(`--archetype=${options.archetype}`);
|
|
3186
|
+
if (options.theme) initFlags.push(`--theme=${options.theme}`);
|
|
3187
|
+
if (options.mode) initFlags.push(`--mode=${options.mode}`);
|
|
3188
|
+
if (options.shape) initFlags.push(`--shape=${options.shape}`);
|
|
3189
|
+
if (options.offline) initFlags.push("--offline");
|
|
3190
|
+
if (options.registry) initFlags.push(`--registry=${options.registry}`);
|
|
3191
|
+
try {
|
|
3192
|
+
const cliBin = join22(__dirname, "..", "bin", "decantr.js");
|
|
3193
|
+
const cliPath = existsSync22(cliBin) ? `node ${cliBin}` : "npx decantr";
|
|
3194
|
+
execSync(`${cliPath} init ${initFlags.join(" ")}`, { cwd: projectDir, stdio: "inherit" });
|
|
3195
|
+
} catch {
|
|
3196
|
+
console.log(`
|
|
3197
|
+
${YELLOW8}Decantr init encountered issues. Run \`decantr init\` manually inside ${projectName}/.${RESET12}`);
|
|
3198
|
+
}
|
|
3199
|
+
console.log(success2(`
|
|
3200
|
+
\u2713 Project "${projectName}" created!
|
|
3201
|
+
`));
|
|
3202
|
+
console.log(` ${cyan2("cd " + projectName)}`);
|
|
3203
|
+
console.log(` ${cyan2(packageManager + " run dev")}`);
|
|
3204
|
+
console.log("");
|
|
3205
|
+
}
|
|
3206
|
+
function detectPackageManager() {
|
|
3207
|
+
if (existsSync22(join22(process.cwd(), "pnpm-lock.yaml")) || existsSync22(join22(process.cwd(), "pnpm-workspace.yaml"))) {
|
|
3208
|
+
return "pnpm";
|
|
3209
|
+
}
|
|
3210
|
+
if (existsSync22(join22(process.cwd(), "yarn.lock"))) {
|
|
3211
|
+
return "yarn";
|
|
3212
|
+
}
|
|
3213
|
+
if (existsSync22(join22(process.cwd(), "bun.lockb")) || existsSync22(join22(process.cwd(), "bun.lock"))) {
|
|
3214
|
+
return "bun";
|
|
3215
|
+
}
|
|
3216
|
+
return "npm";
|
|
3217
|
+
}
|
|
3218
|
+
|
|
3219
|
+
// src/index.ts
|
|
3220
|
+
var BOLD6 = "\x1B[1m";
|
|
3221
|
+
var DIM13 = "\x1B[2m";
|
|
3222
|
+
var RESET13 = "\x1B[0m";
|
|
3223
|
+
var RED11 = "\x1B[31m";
|
|
3224
|
+
var GREEN13 = "\x1B[32m";
|
|
3225
|
+
var CYAN7 = "\x1B[36m";
|
|
3226
|
+
var YELLOW9 = "\x1B[33m";
|
|
3227
|
+
function heading2(text) {
|
|
3228
|
+
return `
|
|
3229
|
+
${BOLD6}${text}${RESET13}
|
|
3230
|
+
`;
|
|
3231
|
+
}
|
|
3232
|
+
function success3(text) {
|
|
3233
|
+
return `${GREEN13}${text}${RESET13}`;
|
|
3234
|
+
}
|
|
3235
|
+
function error3(text) {
|
|
3236
|
+
return `${RED11}${text}${RESET13}`;
|
|
3237
|
+
}
|
|
3238
|
+
function dim3(text) {
|
|
3239
|
+
return `${DIM13}${text}${RESET13}`;
|
|
3240
|
+
}
|
|
3241
|
+
function cyan3(text) {
|
|
3242
|
+
return `${CYAN7}${text}${RESET13}`;
|
|
3243
|
+
}
|
|
2228
3244
|
function extractPatternName(item) {
|
|
2229
3245
|
if (typeof item === "string") return item;
|
|
2230
3246
|
if (typeof item === "object" && item !== null) {
|
|
@@ -2267,7 +3283,7 @@ function boxedPrompt(content, title) {
|
|
|
2267
3283
|
const maxLen = Math.max(...lines.map((l) => l.length), title.length + 4);
|
|
2268
3284
|
const width = maxLen + 4;
|
|
2269
3285
|
const top = `\u250C${"\u2500".repeat(width - 2)}\u2510`;
|
|
2270
|
-
const titleLine = `\u2502 ${
|
|
3286
|
+
const titleLine = `\u2502 ${BOLD6}${title}${RESET13}${" ".repeat(width - title.length - 4)} \u2502`;
|
|
2271
3287
|
const sep2 = `\u251C${"\u2500".repeat(width - 2)}\u2524`;
|
|
2272
3288
|
const bottom = `\u2514${"\u2500".repeat(width - 2)}\u2518`;
|
|
2273
3289
|
const body = lines.map((line) => {
|
|
@@ -2281,7 +3297,7 @@ ${body}
|
|
|
2281
3297
|
${bottom}`;
|
|
2282
3298
|
}
|
|
2283
3299
|
function getAPIClient() {
|
|
2284
|
-
return new
|
|
3300
|
+
return new RegistryAPIClient3({
|
|
2285
3301
|
baseUrl: process.env.DECANTR_API_URL || void 0,
|
|
2286
3302
|
apiKey: process.env.DECANTR_API_KEY || void 0
|
|
2287
3303
|
});
|
|
@@ -2292,17 +3308,17 @@ async function cmdSearch(query, type) {
|
|
|
2292
3308
|
const response = await apiClient.search({ q: query, type });
|
|
2293
3309
|
const results = response.results;
|
|
2294
3310
|
if (results.length === 0) {
|
|
2295
|
-
console.log(
|
|
3311
|
+
console.log(dim3(`No results for "${query}"`));
|
|
2296
3312
|
return;
|
|
2297
3313
|
}
|
|
2298
|
-
console.log(
|
|
3314
|
+
console.log(heading2(`${results.length} result(s) for "${query}"`));
|
|
2299
3315
|
for (const r of results) {
|
|
2300
|
-
console.log(` ${
|
|
2301
|
-
console.log(` ${
|
|
3316
|
+
console.log(` ${cyan3(r.type.padEnd(12))} ${BOLD6}${r.slug}${RESET13}`);
|
|
3317
|
+
console.log(` ${dim3(r.description || "")}`);
|
|
2302
3318
|
console.log("");
|
|
2303
3319
|
}
|
|
2304
3320
|
} catch {
|
|
2305
|
-
console.log(
|
|
3321
|
+
console.log(dim3(`Search failed. API may be unavailable.`));
|
|
2306
3322
|
}
|
|
2307
3323
|
}
|
|
2308
3324
|
async function cmdSuggest(query, type) {
|
|
@@ -2312,40 +3328,40 @@ async function cmdSuggest(query, type) {
|
|
|
2312
3328
|
const response = await apiClient.search({ q: query, type: searchType });
|
|
2313
3329
|
const results = response.results;
|
|
2314
3330
|
if (results.length === 0) {
|
|
2315
|
-
console.log(
|
|
3331
|
+
console.log(dim3(`No suggestions for "${query}"`));
|
|
2316
3332
|
console.log("");
|
|
2317
3333
|
console.log("Try:");
|
|
2318
|
-
console.log(` ${
|
|
2319
|
-
console.log(` ${
|
|
3334
|
+
console.log(` ${cyan3("decantr list patterns")} - see all patterns`);
|
|
3335
|
+
console.log(` ${cyan3("decantr search <broader-term>")} - broaden your search`);
|
|
2320
3336
|
return;
|
|
2321
3337
|
}
|
|
2322
|
-
console.log(
|
|
3338
|
+
console.log(heading2(`Suggestions for "${query}"`));
|
|
2323
3339
|
const queryLower = query.toLowerCase();
|
|
2324
3340
|
const exact = results.filter((r) => r.slug.toLowerCase().includes(queryLower));
|
|
2325
3341
|
const related = results.filter((r) => !r.slug.toLowerCase().includes(queryLower));
|
|
2326
3342
|
if (exact.length > 0) {
|
|
2327
|
-
console.log(`${
|
|
3343
|
+
console.log(`${BOLD6}Direct matches:${RESET13}`);
|
|
2328
3344
|
for (const r of exact.slice(0, 3)) {
|
|
2329
|
-
console.log(` ${
|
|
3345
|
+
console.log(` ${cyan3(r.slug)} - ${r.description || ""}`);
|
|
2330
3346
|
}
|
|
2331
3347
|
console.log("");
|
|
2332
3348
|
}
|
|
2333
3349
|
if (related.length > 0) {
|
|
2334
|
-
console.log(`${
|
|
3350
|
+
console.log(`${BOLD6}Related:${RESET13}`);
|
|
2335
3351
|
for (const r of related.slice(0, 5)) {
|
|
2336
|
-
console.log(` ${
|
|
3352
|
+
console.log(` ${cyan3(r.slug)} - ${r.description || ""}`);
|
|
2337
3353
|
}
|
|
2338
3354
|
console.log("");
|
|
2339
3355
|
}
|
|
2340
|
-
console.log(
|
|
3356
|
+
console.log(dim3(`Use "decantr get pattern <id>" for full details`));
|
|
2341
3357
|
} catch {
|
|
2342
|
-
console.log(
|
|
3358
|
+
console.log(dim3(`Suggestion search failed. API may be unavailable.`));
|
|
2343
3359
|
}
|
|
2344
3360
|
}
|
|
2345
3361
|
async function cmdGet(type, id) {
|
|
2346
3362
|
const validTypes = ["pattern", "archetype", "recipe", "theme", "blueprint", "shell"];
|
|
2347
3363
|
if (!validTypes.includes(type)) {
|
|
2348
|
-
console.error(
|
|
3364
|
+
console.error(error3(`Invalid type "${type}". Must be one of: ${validTypes.join(", ")}`));
|
|
2349
3365
|
process.exitCode = 1;
|
|
2350
3366
|
return;
|
|
2351
3367
|
}
|
|
@@ -2359,29 +3375,29 @@ async function cmdGet(type, id) {
|
|
|
2359
3375
|
};
|
|
2360
3376
|
const apiType = typeMap[type];
|
|
2361
3377
|
const registryClient = new RegistryClient({
|
|
2362
|
-
cacheDir:
|
|
3378
|
+
cacheDir: join23(process.cwd(), ".decantr", "cache")
|
|
2363
3379
|
});
|
|
2364
3380
|
const result = await registryClient.fetchContentItem(apiType, id);
|
|
2365
3381
|
if (result) {
|
|
2366
3382
|
console.log(JSON.stringify(result.data, null, 2));
|
|
2367
3383
|
return;
|
|
2368
3384
|
}
|
|
2369
|
-
const currentDir =
|
|
3385
|
+
const currentDir = dirname2(fileURLToPath(import.meta.url));
|
|
2370
3386
|
const bundledCandidates = [
|
|
2371
|
-
|
|
3387
|
+
join23(currentDir, "bundled", apiType, `${id}.json`),
|
|
2372
3388
|
// Running from src/
|
|
2373
|
-
|
|
3389
|
+
join23(currentDir, "..", "src", "bundled", apiType, `${id}.json`),
|
|
2374
3390
|
// Running from dist/
|
|
2375
|
-
|
|
3391
|
+
join23(currentDir, "..", "bundled", apiType, `${id}.json`)
|
|
2376
3392
|
// Alternative dist layout
|
|
2377
3393
|
];
|
|
2378
|
-
const bundledPath = bundledCandidates.find((p) =>
|
|
3394
|
+
const bundledPath = bundledCandidates.find((p) => existsSync23(p)) || null;
|
|
2379
3395
|
if (bundledPath) {
|
|
2380
|
-
const data = JSON.parse(
|
|
3396
|
+
const data = JSON.parse(readFileSync15(bundledPath, "utf-8"));
|
|
2381
3397
|
console.log(JSON.stringify(data, null, 2));
|
|
2382
3398
|
return;
|
|
2383
3399
|
}
|
|
2384
|
-
console.error(
|
|
3400
|
+
console.error(error3(`${type} "${id}" not found.`));
|
|
2385
3401
|
process.exitCode = 1;
|
|
2386
3402
|
return;
|
|
2387
3403
|
}
|
|
@@ -2389,13 +3405,13 @@ function buildRegistryContext() {
|
|
|
2389
3405
|
const themeRegistry = /* @__PURE__ */ new Map();
|
|
2390
3406
|
const patternRegistry = /* @__PURE__ */ new Map();
|
|
2391
3407
|
const projectRoot = process.cwd();
|
|
2392
|
-
const cacheDir =
|
|
2393
|
-
const customDir =
|
|
2394
|
-
const cachedThemesDir =
|
|
3408
|
+
const cacheDir = join23(projectRoot, ".decantr", "cache");
|
|
3409
|
+
const customDir = join23(projectRoot, ".decantr", "custom");
|
|
3410
|
+
const cachedThemesDir = join23(cacheDir, "@official", "themes");
|
|
2395
3411
|
try {
|
|
2396
|
-
if (
|
|
3412
|
+
if (existsSync23(cachedThemesDir)) {
|
|
2397
3413
|
for (const f of readdirSync6(cachedThemesDir).filter((f2) => f2.endsWith(".json") && f2 !== "index.json")) {
|
|
2398
|
-
const data = JSON.parse(
|
|
3414
|
+
const data = JSON.parse(readFileSync15(join23(cachedThemesDir, f), "utf-8"));
|
|
2399
3415
|
if (data.id && !themeRegistry.has(data.id)) {
|
|
2400
3416
|
themeRegistry.set(data.id, { modes: data.modes || ["light", "dark"] });
|
|
2401
3417
|
}
|
|
@@ -2403,11 +3419,11 @@ function buildRegistryContext() {
|
|
|
2403
3419
|
}
|
|
2404
3420
|
} catch {
|
|
2405
3421
|
}
|
|
2406
|
-
const customThemesDir =
|
|
3422
|
+
const customThemesDir = join23(customDir, "themes");
|
|
2407
3423
|
try {
|
|
2408
|
-
if (
|
|
3424
|
+
if (existsSync23(customThemesDir)) {
|
|
2409
3425
|
for (const f of readdirSync6(customThemesDir).filter((f2) => f2.endsWith(".json"))) {
|
|
2410
|
-
const data = JSON.parse(
|
|
3426
|
+
const data = JSON.parse(readFileSync15(join23(customThemesDir, f), "utf-8"));
|
|
2411
3427
|
if (data.id) {
|
|
2412
3428
|
themeRegistry.set(`custom:${data.id}`, { modes: data.modes || ["light", "dark"] });
|
|
2413
3429
|
}
|
|
@@ -2415,11 +3431,11 @@ function buildRegistryContext() {
|
|
|
2415
3431
|
}
|
|
2416
3432
|
} catch {
|
|
2417
3433
|
}
|
|
2418
|
-
const cachedPatternsDir =
|
|
3434
|
+
const cachedPatternsDir = join23(cacheDir, "@official", "patterns");
|
|
2419
3435
|
try {
|
|
2420
|
-
if (
|
|
3436
|
+
if (existsSync23(cachedPatternsDir)) {
|
|
2421
3437
|
for (const f of readdirSync6(cachedPatternsDir).filter((f2) => f2.endsWith(".json") && f2 !== "index.json")) {
|
|
2422
|
-
const data = JSON.parse(
|
|
3438
|
+
const data = JSON.parse(readFileSync15(join23(cachedPatternsDir, f), "utf-8"));
|
|
2423
3439
|
if (data.id && !patternRegistry.has(data.id)) {
|
|
2424
3440
|
patternRegistry.set(data.id, data);
|
|
2425
3441
|
}
|
|
@@ -2430,12 +3446,12 @@ function buildRegistryContext() {
|
|
|
2430
3446
|
return { themeRegistry, patternRegistry };
|
|
2431
3447
|
}
|
|
2432
3448
|
async function cmdValidate(path) {
|
|
2433
|
-
const essencePath = path ||
|
|
3449
|
+
const essencePath = path || join23(process.cwd(), "decantr.essence.json");
|
|
2434
3450
|
let raw;
|
|
2435
3451
|
try {
|
|
2436
|
-
raw =
|
|
3452
|
+
raw = readFileSync15(essencePath, "utf-8");
|
|
2437
3453
|
} catch {
|
|
2438
|
-
console.error(
|
|
3454
|
+
console.error(error3(`Could not read ${essencePath}`));
|
|
2439
3455
|
process.exitCode = 1;
|
|
2440
3456
|
return;
|
|
2441
3457
|
}
|
|
@@ -2443,39 +3459,39 @@ async function cmdValidate(path) {
|
|
|
2443
3459
|
try {
|
|
2444
3460
|
essence = JSON.parse(raw);
|
|
2445
3461
|
} catch (e) {
|
|
2446
|
-
console.error(
|
|
3462
|
+
console.error(error3(`Invalid JSON: ${e.message}`));
|
|
2447
3463
|
process.exitCode = 1;
|
|
2448
3464
|
return;
|
|
2449
3465
|
}
|
|
2450
3466
|
const detectedVersion = isV36(essence) ? "v3" : "v2";
|
|
2451
|
-
console.log(`${
|
|
3467
|
+
console.log(`${DIM13}Detected essence version: ${detectedVersion}${RESET13}`);
|
|
2452
3468
|
const result = validateEssence2(essence);
|
|
2453
3469
|
if (result.valid) {
|
|
2454
|
-
console.log(
|
|
3470
|
+
console.log(success3(`Essence is valid (${detectedVersion}).`));
|
|
2455
3471
|
} else {
|
|
2456
|
-
console.error(
|
|
3472
|
+
console.error(error3("Validation failed:"));
|
|
2457
3473
|
for (const err of result.errors) {
|
|
2458
|
-
console.error(` ${
|
|
3474
|
+
console.error(` ${RED11}${err}${RESET13}`);
|
|
2459
3475
|
}
|
|
2460
3476
|
process.exitCode = 1;
|
|
2461
3477
|
}
|
|
2462
3478
|
if (detectedVersion === "v2" && result.valid) {
|
|
2463
|
-
console.log(`${
|
|
3479
|
+
console.log(`${YELLOW9}Tip: Run \`decantr migrate\` to upgrade to v3 format.${RESET13}`);
|
|
2464
3480
|
}
|
|
2465
3481
|
try {
|
|
2466
3482
|
const { themeRegistry, patternRegistry } = buildRegistryContext();
|
|
2467
3483
|
const violations = evaluateGuard(essence, { themeRegistry, patternRegistry });
|
|
2468
3484
|
if (violations.length > 0) {
|
|
2469
|
-
console.log(
|
|
3485
|
+
console.log(heading2("Guard violations:"));
|
|
2470
3486
|
for (const v of violations) {
|
|
2471
3487
|
const vr = v;
|
|
2472
|
-
console.log(` ${
|
|
3488
|
+
console.log(` ${YELLOW9}[${vr.rule}]${RESET13} ${vr.message}`);
|
|
2473
3489
|
if (vr.suggestion) {
|
|
2474
|
-
console.log(` ${
|
|
3490
|
+
console.log(` ${DIM13}Suggestion: ${vr.suggestion}${RESET13}`);
|
|
2475
3491
|
}
|
|
2476
3492
|
}
|
|
2477
3493
|
} else if (result.valid) {
|
|
2478
|
-
console.log(
|
|
3494
|
+
console.log(success3("No guard violations."));
|
|
2479
3495
|
}
|
|
2480
3496
|
} catch {
|
|
2481
3497
|
}
|
|
@@ -2483,59 +3499,59 @@ async function cmdValidate(path) {
|
|
|
2483
3499
|
async function cmdList(type) {
|
|
2484
3500
|
const validTypes = ["patterns", "archetypes", "recipes", "themes", "blueprints", "shells"];
|
|
2485
3501
|
if (!validTypes.includes(type)) {
|
|
2486
|
-
console.error(
|
|
3502
|
+
console.error(error3(`Invalid type "${type}". Must be one of: ${validTypes.join(", ")}`));
|
|
2487
3503
|
process.exitCode = 1;
|
|
2488
3504
|
return;
|
|
2489
3505
|
}
|
|
2490
3506
|
const registryClient = new RegistryClient({
|
|
2491
|
-
cacheDir:
|
|
3507
|
+
cacheDir: join23(process.cwd(), ".decantr", "cache")
|
|
2492
3508
|
});
|
|
2493
3509
|
const result = await registryClient.fetchContentList(type);
|
|
2494
3510
|
const items = result.data.items;
|
|
2495
3511
|
if (items.length === 0) {
|
|
2496
|
-
console.log(
|
|
3512
|
+
console.log(dim3(`No ${type} found.`));
|
|
2497
3513
|
return;
|
|
2498
3514
|
}
|
|
2499
3515
|
if (type === "themes") {
|
|
2500
3516
|
const customItems = registryClient.listCustomContent("themes");
|
|
2501
3517
|
const customIds = new Set(customItems.map((c) => c.id));
|
|
2502
3518
|
const registryItems = items.filter((i) => !customIds.has(i.id));
|
|
2503
|
-
console.log(
|
|
3519
|
+
console.log(heading2(`Registry themes (${registryItems.length}):`));
|
|
2504
3520
|
for (const item of registryItems) {
|
|
2505
|
-
console.log(` ${
|
|
3521
|
+
console.log(` ${cyan3(item.id)} ${dim3(item.description || item.name || "")}`);
|
|
2506
3522
|
}
|
|
2507
3523
|
if (customItems.length > 0) {
|
|
2508
3524
|
console.log("");
|
|
2509
|
-
console.log(
|
|
3525
|
+
console.log(heading2(`Custom themes (${customItems.length}):`));
|
|
2510
3526
|
for (const item of customItems) {
|
|
2511
|
-
console.log(` ${
|
|
3527
|
+
console.log(` ${cyan3(`custom:${item.id}`)} ${dim3(item.description || item.name || "")}`);
|
|
2512
3528
|
}
|
|
2513
3529
|
} else {
|
|
2514
3530
|
console.log("");
|
|
2515
|
-
console.log(
|
|
2516
|
-
console.log(
|
|
3531
|
+
console.log(dim3("Custom themes (0):"));
|
|
3532
|
+
console.log(dim3(' Run "decantr theme create <name>" to create a custom theme.'));
|
|
2517
3533
|
}
|
|
2518
3534
|
} else {
|
|
2519
|
-
console.log(
|
|
3535
|
+
console.log(heading2(`${items.length} ${type} found`));
|
|
2520
3536
|
for (const item of items) {
|
|
2521
|
-
console.log(` ${
|
|
3537
|
+
console.log(` ${cyan3(item.id)} ${dim3(item.description || item.name || "")}`);
|
|
2522
3538
|
}
|
|
2523
3539
|
}
|
|
2524
3540
|
}
|
|
2525
3541
|
async function cmdInit(args) {
|
|
2526
3542
|
const projectRoot = process.cwd();
|
|
2527
|
-
console.log(
|
|
3543
|
+
console.log(heading2("Decantr Project Setup"));
|
|
2528
3544
|
const detected = detectProject(projectRoot);
|
|
2529
3545
|
if (detected.existingEssence && !args.existing) {
|
|
2530
|
-
console.log(`${
|
|
3546
|
+
console.log(`${YELLOW9}Warning: decantr.essence.json already exists.${RESET13}`);
|
|
2531
3547
|
const overwrite = await confirm("Overwrite existing configuration?", false);
|
|
2532
3548
|
if (!overwrite) {
|
|
2533
|
-
console.log(
|
|
3549
|
+
console.log(dim3("Cancelled."));
|
|
2534
3550
|
return;
|
|
2535
3551
|
}
|
|
2536
3552
|
}
|
|
2537
3553
|
const registryClient = new RegistryClient({
|
|
2538
|
-
cacheDir:
|
|
3554
|
+
cacheDir: join23(projectRoot, ".decantr", "cache"),
|
|
2539
3555
|
apiUrl: args.registry,
|
|
2540
3556
|
offline: args.offline
|
|
2541
3557
|
});
|
|
@@ -2547,30 +3563,30 @@ async function cmdInit(args) {
|
|
|
2547
3563
|
} else if (!apiAvailable) {
|
|
2548
3564
|
if (!args.blueprint) {
|
|
2549
3565
|
console.log(`
|
|
2550
|
-
${
|
|
2551
|
-
console.log(
|
|
3566
|
+
${YELLOW9}You're offline. Scaffolding minimal Decantr project.${RESET13}`);
|
|
3567
|
+
console.log(dim3("Run `decantr sync` or `decantr upgrade` when online to pull full registry content.\n"));
|
|
2552
3568
|
const result2 = scaffoldMinimal(projectRoot);
|
|
2553
|
-
console.log(
|
|
3569
|
+
console.log(success3("\nProject scaffolded (minimal/offline)!\n"));
|
|
2554
3570
|
console.log(" Files created:");
|
|
2555
|
-
console.log(` ${
|
|
2556
|
-
console.log(` ${
|
|
2557
|
-
console.log(` ${
|
|
3571
|
+
console.log(` ${cyan3("decantr.essence.json")} Design specification`);
|
|
3572
|
+
console.log(` ${cyan3("DECANTR.md")} LLM instructions`);
|
|
3573
|
+
console.log(` ${cyan3(".decantr/")} Project state & custom content dirs`);
|
|
2558
3574
|
if (result2.gitignoreUpdated) {
|
|
2559
|
-
console.log(` ${
|
|
3575
|
+
console.log(` ${dim3(".gitignore updated")}`);
|
|
2560
3576
|
}
|
|
2561
3577
|
console.log("");
|
|
2562
3578
|
console.log(" Next steps:");
|
|
2563
|
-
console.log(` 1. Run ${
|
|
2564
|
-
console.log(` 2. Use ${
|
|
3579
|
+
console.log(` 1. Run ${cyan3("decantr sync")} when online`);
|
|
3580
|
+
console.log(` 2. Use ${cyan3("decantr create <type> <name>")} to create custom content`);
|
|
2565
3581
|
console.log(` 3. Review DECANTR.md for methodology`);
|
|
2566
3582
|
return;
|
|
2567
3583
|
}
|
|
2568
3584
|
console.log(`
|
|
2569
|
-
${
|
|
2570
|
-
console.log(
|
|
3585
|
+
${YELLOW9}You're offline. Scaffolding Decantr default.${RESET13}`);
|
|
3586
|
+
console.log(dim3("Run `decantr upgrade` when online, or visit decantr.ai/registry\n"));
|
|
2571
3587
|
selectedBlueprint = "default";
|
|
2572
3588
|
} else {
|
|
2573
|
-
console.log(
|
|
3589
|
+
console.log(dim3("Fetching registry content..."));
|
|
2574
3590
|
const blueprintsResult2 = await registryClient.fetchBlueprints();
|
|
2575
3591
|
registrySource = blueprintsResult2.source.type === "api" ? "api" : "cache";
|
|
2576
3592
|
const { selectedBlueprint: selected } = await runSimplifiedInit(
|
|
@@ -2618,8 +3634,8 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2618
3634
|
options.shape = blueprint.theme.shape;
|
|
2619
3635
|
}
|
|
2620
3636
|
}
|
|
2621
|
-
if (blueprint.personality
|
|
2622
|
-
options.personality = blueprint.personality;
|
|
3637
|
+
if (blueprint.personality && (!options.personality || options.personality.length === 0 || options.personality.length === 1 && options.personality[0] === "professional")) {
|
|
3638
|
+
options.personality = typeof blueprint.personality === "string" ? [blueprint.personality] : blueprint.personality;
|
|
2623
3639
|
}
|
|
2624
3640
|
blueprintRecipeName = blueprint.theme?.recipe;
|
|
2625
3641
|
if (blueprint.compose && blueprint.compose.length > 0) {
|
|
@@ -2726,7 +3742,7 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2726
3742
|
) : "";
|
|
2727
3743
|
}
|
|
2728
3744
|
} else {
|
|
2729
|
-
console.log(`${
|
|
3745
|
+
console.log(`${YELLOW9} Warning: Could not fetch blueprint "${options.blueprint}". Using defaults.${RESET13}`);
|
|
2730
3746
|
}
|
|
2731
3747
|
} else if (options.archetype) {
|
|
2732
3748
|
const archetypeResult = await registryClient.fetchArchetype(options.archetype);
|
|
@@ -2734,7 +3750,7 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2734
3750
|
const rawArch = archetypeResult.data;
|
|
2735
3751
|
archetypeData = rawArch.data ?? rawArch;
|
|
2736
3752
|
} else {
|
|
2737
|
-
console.log(`${
|
|
3753
|
+
console.log(`${YELLOW9} Warning: Could not fetch archetype "${options.archetype}". Using defaults.${RESET13}`);
|
|
2738
3754
|
}
|
|
2739
3755
|
}
|
|
2740
3756
|
let themeData;
|
|
@@ -2756,7 +3772,7 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2756
3772
|
recipeData = { decorators: theme.decorators };
|
|
2757
3773
|
}
|
|
2758
3774
|
} else {
|
|
2759
|
-
console.log(`${
|
|
3775
|
+
console.log(`${YELLOW9} Warning: Could not fetch theme "${options.theme}". Using defaults.${RESET13}`);
|
|
2760
3776
|
}
|
|
2761
3777
|
const recipeName = blueprintRecipeName || options.theme;
|
|
2762
3778
|
const recipeResult = await registryClient.fetchRecipe(recipeName);
|
|
@@ -2770,11 +3786,12 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2770
3786
|
recipeData = {
|
|
2771
3787
|
decorators: recipe.decorators || recipeData?.decorators,
|
|
2772
3788
|
spatial_hints: recipe.spatial_hints,
|
|
2773
|
-
radius_hints: recipe.radius_hints
|
|
3789
|
+
radius_hints: recipe.radius_hints,
|
|
3790
|
+
treatment_overrides: recipe.treatment_overrides
|
|
2774
3791
|
};
|
|
2775
3792
|
}
|
|
2776
3793
|
}
|
|
2777
|
-
console.log(
|
|
3794
|
+
console.log(heading2("Scaffolding project..."));
|
|
2778
3795
|
const result = await scaffoldProject(
|
|
2779
3796
|
projectRoot,
|
|
2780
3797
|
options,
|
|
@@ -2791,13 +3808,13 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2791
3808
|
patternSpecs,
|
|
2792
3809
|
blueprintData
|
|
2793
3810
|
);
|
|
2794
|
-
console.log(
|
|
3811
|
+
console.log(success3("\nProject scaffolded!\n"));
|
|
2795
3812
|
console.log(" Files created:");
|
|
2796
|
-
console.log(` ${
|
|
2797
|
-
console.log(` ${
|
|
2798
|
-
console.log(` ${
|
|
3813
|
+
console.log(` ${cyan3("decantr.essence.json")} Design specification`);
|
|
3814
|
+
console.log(` ${cyan3("DECANTR.md")} LLM instructions`);
|
|
3815
|
+
console.log(` ${cyan3(".decantr/")} Project state & cache`);
|
|
2799
3816
|
if (result.gitignoreUpdated) {
|
|
2800
|
-
console.log(` ${
|
|
3817
|
+
console.log(` ${dim3(".gitignore updated")}`);
|
|
2801
3818
|
}
|
|
2802
3819
|
console.log("");
|
|
2803
3820
|
console.log(" Next steps:");
|
|
@@ -2805,19 +3822,19 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2805
3822
|
console.log(" 2. Explore more at decantr.ai/registry");
|
|
2806
3823
|
console.log("");
|
|
2807
3824
|
console.log(" Commands:");
|
|
2808
|
-
console.log(` ${
|
|
2809
|
-
console.log(` ${
|
|
2810
|
-
console.log(` ${
|
|
2811
|
-
console.log(` ${
|
|
2812
|
-
console.log(` ${
|
|
2813
|
-
console.log(` ${
|
|
2814
|
-
console.log(` ${
|
|
2815
|
-
const essenceContent =
|
|
3825
|
+
console.log(` ${cyan3("decantr status")} Project health`);
|
|
3826
|
+
console.log(` ${cyan3("decantr search")} Search registry`);
|
|
3827
|
+
console.log(` ${cyan3("decantr get")} Fetch content details`);
|
|
3828
|
+
console.log(` ${cyan3("decantr validate")} Check essence file`);
|
|
3829
|
+
console.log(` ${cyan3("decantr upgrade")} Update to latest patterns`);
|
|
3830
|
+
console.log(` ${cyan3("decantr check")} Detect drift issues`);
|
|
3831
|
+
console.log(` ${cyan3("decantr migrate")} Migrate v2 essence to v3`);
|
|
3832
|
+
const essenceContent = readFileSync15(result.essencePath, "utf-8");
|
|
2816
3833
|
const essence = JSON.parse(essenceContent);
|
|
2817
3834
|
if (essence.version !== "3.1.0") {
|
|
2818
3835
|
const validation = validateEssence2(essence);
|
|
2819
3836
|
if (!validation.valid) {
|
|
2820
|
-
console.log(
|
|
3837
|
+
console.log(error3(`
|
|
2821
3838
|
Validation warnings: ${validation.errors.join(", ")}`));
|
|
2822
3839
|
}
|
|
2823
3840
|
}
|
|
@@ -2848,32 +3865,32 @@ Validation warnings: ${validation.errors.join(", ")}`));
|
|
|
2848
3865
|
console.log(boxedPrompt(curatedPrompt, "Copy this prompt for your AI assistant"));
|
|
2849
3866
|
console.log("");
|
|
2850
3867
|
if (registrySource === "cache") {
|
|
2851
|
-
console.log(
|
|
3868
|
+
console.log(dim3('Run "decantr sync" when online to get the latest registry content.'));
|
|
2852
3869
|
}
|
|
2853
3870
|
}
|
|
2854
3871
|
async function cmdStatus() {
|
|
2855
3872
|
const projectRoot = process.cwd();
|
|
2856
|
-
const essencePath =
|
|
2857
|
-
const projectJsonPath =
|
|
2858
|
-
console.log(
|
|
2859
|
-
if (!
|
|
2860
|
-
console.log(`${
|
|
2861
|
-
console.log(
|
|
3873
|
+
const essencePath = join23(projectRoot, "decantr.essence.json");
|
|
3874
|
+
const projectJsonPath = join23(projectRoot, ".decantr", "project.json");
|
|
3875
|
+
console.log(heading2("Decantr Project Status"));
|
|
3876
|
+
if (!existsSync23(essencePath)) {
|
|
3877
|
+
console.log(`${RED11}No decantr.essence.json found.${RESET13}`);
|
|
3878
|
+
console.log(dim3('Run "decantr init" to create one.'));
|
|
2862
3879
|
return;
|
|
2863
3880
|
}
|
|
2864
3881
|
try {
|
|
2865
|
-
const essence = JSON.parse(
|
|
3882
|
+
const essence = JSON.parse(readFileSync15(essencePath, "utf-8"));
|
|
2866
3883
|
const validation = validateEssence2(essence);
|
|
2867
3884
|
const essenceVersion = isV36(essence) ? "v3" : "v2";
|
|
2868
|
-
console.log(`${
|
|
3885
|
+
console.log(`${BOLD6}Essence:${RESET13}`);
|
|
2869
3886
|
if (validation.valid) {
|
|
2870
|
-
console.log(` ${
|
|
3887
|
+
console.log(` ${GREEN13}Valid${RESET13} (${essenceVersion})`);
|
|
2871
3888
|
} else {
|
|
2872
|
-
console.log(` ${
|
|
3889
|
+
console.log(` ${RED11}Invalid: ${validation.errors.join(", ")}${RESET13}`);
|
|
2873
3890
|
}
|
|
2874
3891
|
if (isV36(essence)) {
|
|
2875
3892
|
const v3 = essence;
|
|
2876
|
-
console.log(` ${
|
|
3893
|
+
console.log(` ${BOLD6}DNA:${RESET13}`);
|
|
2877
3894
|
console.log(` Theme: ${v3.dna.theme.style} (${v3.dna.theme.mode})`);
|
|
2878
3895
|
console.log(` Spacing: ${v3.dna.spacing.density} density, ${v3.dna.spacing.content_gap} gap`);
|
|
2879
3896
|
console.log(` Typography: ${v3.dna.typography.scale} scale`);
|
|
@@ -2881,11 +3898,11 @@ async function cmdStatus() {
|
|
|
2881
3898
|
console.log(` Motion: ${v3.dna.motion.preference} (reduce: ${v3.dna.motion.reduce_motion})`);
|
|
2882
3899
|
console.log(` Accessibility: WCAG ${v3.dna.accessibility.wcag_level}`);
|
|
2883
3900
|
console.log(` Personality: ${v3.dna.personality.join(", ")}`);
|
|
2884
|
-
console.log(` ${
|
|
3901
|
+
console.log(` ${BOLD6}Blueprint:${RESET13}`);
|
|
2885
3902
|
console.log(` Shell: ${v3.blueprint.shell}`);
|
|
2886
3903
|
console.log(` Pages: ${v3.blueprint.pages.length}`);
|
|
2887
3904
|
console.log(` Features: ${v3.blueprint.features.length > 0 ? v3.blueprint.features.join(", ") : "none"}`);
|
|
2888
|
-
console.log(` ${
|
|
3905
|
+
console.log(` ${BOLD6}Meta:${RESET13}`);
|
|
2889
3906
|
console.log(` Archetype: ${v3.meta.archetype}`);
|
|
2890
3907
|
console.log(` Target: ${v3.meta.target}`);
|
|
2891
3908
|
console.log(` Guard: ${v3.meta.guard.mode} (DNA: ${v3.meta.guard.dna_enforcement}, Blueprint: ${v3.meta.guard.blueprint_enforcement})`);
|
|
@@ -2897,92 +3914,92 @@ async function cmdStatus() {
|
|
|
2897
3914
|
console.log(` Theme: ${theme?.style || "unknown"} (${theme?.mode || "unknown"})`);
|
|
2898
3915
|
console.log(` Guard: ${guard?.mode || "unknown"}`);
|
|
2899
3916
|
console.log(` Pages: ${(structure || []).length}`);
|
|
2900
|
-
console.log(` ${
|
|
3917
|
+
console.log(` ${YELLOW9}Tip: Run \`decantr migrate\` to upgrade to v3.${RESET13}`);
|
|
2901
3918
|
}
|
|
2902
3919
|
} catch (e) {
|
|
2903
|
-
console.log(` ${
|
|
3920
|
+
console.log(` ${RED11}Error reading essence: ${e.message}${RESET13}`);
|
|
2904
3921
|
}
|
|
2905
3922
|
console.log("");
|
|
2906
|
-
console.log(`${
|
|
2907
|
-
if (
|
|
3923
|
+
console.log(`${BOLD6}Sync Status:${RESET13}`);
|
|
3924
|
+
if (existsSync23(projectJsonPath)) {
|
|
2908
3925
|
try {
|
|
2909
|
-
const projectJson = JSON.parse(
|
|
3926
|
+
const projectJson = JSON.parse(readFileSync15(projectJsonPath, "utf-8"));
|
|
2910
3927
|
const syncStatus = projectJson.sync?.status || "unknown";
|
|
2911
3928
|
const lastSync = projectJson.sync?.lastSync || "never";
|
|
2912
3929
|
const source = projectJson.sync?.registrySource || "unknown";
|
|
2913
|
-
const statusColor = syncStatus === "synced" ?
|
|
2914
|
-
console.log(` Status: ${statusColor}${syncStatus}${
|
|
2915
|
-
console.log(` Last sync: ${
|
|
2916
|
-
console.log(` Source: ${
|
|
3930
|
+
const statusColor = syncStatus === "synced" ? GREEN13 : YELLOW9;
|
|
3931
|
+
console.log(` Status: ${statusColor}${syncStatus}${RESET13}`);
|
|
3932
|
+
console.log(` Last sync: ${dim3(lastSync)}`);
|
|
3933
|
+
console.log(` Source: ${dim3(source)}`);
|
|
2917
3934
|
} catch {
|
|
2918
|
-
console.log(` ${
|
|
3935
|
+
console.log(` ${YELLOW9}Could not read project.json${RESET13}`);
|
|
2919
3936
|
}
|
|
2920
3937
|
} else {
|
|
2921
|
-
console.log(` ${
|
|
2922
|
-
console.log(
|
|
3938
|
+
console.log(` ${YELLOW9}No .decantr/project.json found${RESET13}`);
|
|
3939
|
+
console.log(dim3(' Run "decantr init" to create project files.'));
|
|
2923
3940
|
}
|
|
2924
3941
|
}
|
|
2925
3942
|
async function cmdSync() {
|
|
2926
3943
|
const projectRoot = process.cwd();
|
|
2927
|
-
const cacheDir =
|
|
2928
|
-
console.log(
|
|
3944
|
+
const cacheDir = join23(projectRoot, ".decantr", "cache");
|
|
3945
|
+
console.log(heading2("Syncing registry content..."));
|
|
2929
3946
|
const result = await syncRegistry(cacheDir);
|
|
2930
3947
|
if (result.synced.length > 0) {
|
|
2931
|
-
console.log(
|
|
3948
|
+
console.log(success3("Sync completed successfully."));
|
|
2932
3949
|
console.log(` Synced: ${result.synced.join(", ")}`);
|
|
2933
3950
|
if (result.failed.length > 0) {
|
|
2934
|
-
console.log(` ${
|
|
3951
|
+
console.log(` ${YELLOW9}Failed: ${result.failed.join(", ")}${RESET13}`);
|
|
2935
3952
|
}
|
|
2936
3953
|
} else {
|
|
2937
|
-
console.log(`${
|
|
3954
|
+
console.log(`${YELLOW9}Could not sync: API unavailable${RESET13}`);
|
|
2938
3955
|
if (result.failed.length > 0) {
|
|
2939
|
-
console.log(` ${
|
|
3956
|
+
console.log(` ${YELLOW9}Failed: ${result.failed.join(", ")}${RESET13}`);
|
|
2940
3957
|
}
|
|
2941
3958
|
}
|
|
2942
3959
|
}
|
|
2943
3960
|
async function cmdAudit() {
|
|
2944
3961
|
const projectRoot = process.cwd();
|
|
2945
|
-
const essencePath =
|
|
2946
|
-
console.log(
|
|
2947
|
-
if (!
|
|
2948
|
-
console.log(`${
|
|
3962
|
+
const essencePath = join23(projectRoot, "decantr.essence.json");
|
|
3963
|
+
console.log(heading2("Auditing project..."));
|
|
3964
|
+
if (!existsSync23(essencePath)) {
|
|
3965
|
+
console.log(`${RED11}No decantr.essence.json found.${RESET13}`);
|
|
2949
3966
|
process.exitCode = 1;
|
|
2950
3967
|
return;
|
|
2951
3968
|
}
|
|
2952
3969
|
try {
|
|
2953
|
-
const essence = JSON.parse(
|
|
3970
|
+
const essence = JSON.parse(readFileSync15(essencePath, "utf-8"));
|
|
2954
3971
|
const validation = validateEssence2(essence);
|
|
2955
3972
|
if (!validation.valid) {
|
|
2956
|
-
console.log(`${
|
|
3973
|
+
console.log(`${RED11}Essence validation failed:${RESET13}`);
|
|
2957
3974
|
for (const err of validation.errors) {
|
|
2958
|
-
console.log(` ${
|
|
3975
|
+
console.log(` ${RED11}${err}${RESET13}`);
|
|
2959
3976
|
}
|
|
2960
3977
|
process.exitCode = 1;
|
|
2961
3978
|
return;
|
|
2962
3979
|
}
|
|
2963
|
-
console.log(
|
|
3980
|
+
console.log(success3("Essence is valid."));
|
|
2964
3981
|
const { themeRegistry, patternRegistry } = buildRegistryContext();
|
|
2965
3982
|
const violations = evaluateGuard(essence, { themeRegistry, patternRegistry });
|
|
2966
3983
|
if (violations.length > 0) {
|
|
2967
3984
|
console.log("");
|
|
2968
|
-
console.log(`${
|
|
3985
|
+
console.log(`${YELLOW9}Guard violations:${RESET13}`);
|
|
2969
3986
|
for (const v of violations) {
|
|
2970
3987
|
const vr = v;
|
|
2971
|
-
console.log(` ${
|
|
3988
|
+
console.log(` ${YELLOW9}[${vr.rule}]${RESET13} ${vr.message}`);
|
|
2972
3989
|
if (vr.suggestion) {
|
|
2973
|
-
console.log(` ${
|
|
3990
|
+
console.log(` ${DIM13}Suggestion: ${vr.suggestion}${RESET13}`);
|
|
2974
3991
|
}
|
|
2975
3992
|
}
|
|
2976
3993
|
} else {
|
|
2977
|
-
console.log(
|
|
3994
|
+
console.log(success3("No guard violations."));
|
|
2978
3995
|
}
|
|
2979
3996
|
console.log("");
|
|
2980
|
-
console.log(`${
|
|
3997
|
+
console.log(`${BOLD6}Summary:${RESET13}`);
|
|
2981
3998
|
console.log(` Pages defined: ${essence.structure.length}`);
|
|
2982
3999
|
console.log(` Guard mode: ${essence.guard.mode}`);
|
|
2983
4000
|
console.log(` Theme: ${essence.theme.style}`);
|
|
2984
4001
|
} catch (e) {
|
|
2985
|
-
console.log(`${
|
|
4002
|
+
console.log(`${RED11}Error: ${e.message}${RESET13}`);
|
|
2986
4003
|
process.exitCode = 1;
|
|
2987
4004
|
}
|
|
2988
4005
|
}
|
|
@@ -2991,17 +4008,17 @@ async function cmdTheme(args) {
|
|
|
2991
4008
|
const projectRoot = process.cwd();
|
|
2992
4009
|
if (!subcommand || subcommand === "help") {
|
|
2993
4010
|
console.log(`
|
|
2994
|
-
${
|
|
4011
|
+
${BOLD6}decantr theme${RESET13} \u2014 Manage custom themes
|
|
2995
4012
|
|
|
2996
|
-
${
|
|
2997
|
-
${
|
|
2998
|
-
${
|
|
2999
|
-
${
|
|
3000
|
-
${
|
|
3001
|
-
${
|
|
3002
|
-
${
|
|
4013
|
+
${BOLD6}Commands:${RESET13}
|
|
4014
|
+
${cyan3("create")} <name> Create a new custom theme
|
|
4015
|
+
${cyan3("create")} <name> --guided Interactive theme creation
|
|
4016
|
+
${cyan3("list")} List custom themes
|
|
4017
|
+
${cyan3("validate")} <name> Validate a custom theme
|
|
4018
|
+
${cyan3("delete")} <name> Delete a custom theme
|
|
4019
|
+
${cyan3("import")} <path> Import theme from JSON file
|
|
3003
4020
|
|
|
3004
|
-
${
|
|
4021
|
+
${BOLD6}Examples:${RESET13}
|
|
3005
4022
|
decantr theme create mytheme
|
|
3006
4023
|
decantr theme list
|
|
3007
4024
|
decantr theme validate mytheme
|
|
@@ -3013,19 +4030,19 @@ ${BOLD4}Examples:${RESET9}
|
|
|
3013
4030
|
case "create": {
|
|
3014
4031
|
const name = args[1];
|
|
3015
4032
|
if (!name) {
|
|
3016
|
-
console.error(
|
|
4033
|
+
console.error(error3("Usage: decantr theme create <name>"));
|
|
3017
4034
|
process.exitCode = 1;
|
|
3018
4035
|
return;
|
|
3019
4036
|
}
|
|
3020
4037
|
const displayName = name.charAt(0).toUpperCase() + name.slice(1).replace(/-/g, " ");
|
|
3021
4038
|
const result = createTheme(projectRoot, name, displayName);
|
|
3022
4039
|
if (result.success) {
|
|
3023
|
-
console.log(
|
|
3024
|
-
console.log(
|
|
4040
|
+
console.log(success3(`Created custom theme "${name}"`));
|
|
4041
|
+
console.log(dim3(` Path: ${result.path}`));
|
|
3025
4042
|
console.log("");
|
|
3026
|
-
console.log(`Use in essence: ${
|
|
4043
|
+
console.log(`Use in essence: ${cyan3(`"style": "custom:${name}"`)}`);
|
|
3027
4044
|
} else {
|
|
3028
|
-
console.error(
|
|
4045
|
+
console.error(error3(result.error || "Failed to create theme"));
|
|
3029
4046
|
process.exitCode = 1;
|
|
3030
4047
|
}
|
|
3031
4048
|
break;
|
|
@@ -3033,12 +4050,12 @@ ${BOLD4}Examples:${RESET9}
|
|
|
3033
4050
|
case "list": {
|
|
3034
4051
|
const themes = listCustomThemes(projectRoot);
|
|
3035
4052
|
if (themes.length === 0) {
|
|
3036
|
-
console.log(
|
|
3037
|
-
console.log(
|
|
4053
|
+
console.log(dim3("No custom themes found."));
|
|
4054
|
+
console.log(dim3('Run "decantr theme create <name>" to create one.'));
|
|
3038
4055
|
} else {
|
|
3039
|
-
console.log(
|
|
4056
|
+
console.log(heading2(`${themes.length} custom theme(s)`));
|
|
3040
4057
|
for (const theme of themes) {
|
|
3041
|
-
console.log(` ${
|
|
4058
|
+
console.log(` ${cyan3(`custom:${theme.id}`)} ${dim3(theme.description || theme.name)}`);
|
|
3042
4059
|
}
|
|
3043
4060
|
}
|
|
3044
4061
|
break;
|
|
@@ -3046,30 +4063,30 @@ ${BOLD4}Examples:${RESET9}
|
|
|
3046
4063
|
case "validate": {
|
|
3047
4064
|
const name = args[1];
|
|
3048
4065
|
if (!name) {
|
|
3049
|
-
console.error(
|
|
4066
|
+
console.error(error3("Usage: decantr theme validate <name>"));
|
|
3050
4067
|
process.exitCode = 1;
|
|
3051
4068
|
return;
|
|
3052
4069
|
}
|
|
3053
|
-
const themePath =
|
|
3054
|
-
if (!
|
|
3055
|
-
console.error(
|
|
4070
|
+
const themePath = join23(projectRoot, ".decantr", "custom", "themes", `${name}.json`);
|
|
4071
|
+
if (!existsSync23(themePath)) {
|
|
4072
|
+
console.error(error3(`Theme "${name}" not found at ${themePath}`));
|
|
3056
4073
|
process.exitCode = 1;
|
|
3057
4074
|
return;
|
|
3058
4075
|
}
|
|
3059
4076
|
try {
|
|
3060
|
-
const theme = JSON.parse(
|
|
4077
|
+
const theme = JSON.parse(readFileSync15(themePath, "utf-8"));
|
|
3061
4078
|
const result = validateCustomTheme(theme);
|
|
3062
4079
|
if (result.valid) {
|
|
3063
|
-
console.log(
|
|
4080
|
+
console.log(success3(`Custom theme "${name}" is valid`));
|
|
3064
4081
|
} else {
|
|
3065
|
-
console.error(
|
|
4082
|
+
console.error(error3("Validation failed:"));
|
|
3066
4083
|
for (const err of result.errors) {
|
|
3067
|
-
console.error(` ${
|
|
4084
|
+
console.error(` ${RED11}${err}${RESET13}`);
|
|
3068
4085
|
}
|
|
3069
4086
|
process.exitCode = 1;
|
|
3070
4087
|
}
|
|
3071
4088
|
} catch (e) {
|
|
3072
|
-
console.error(
|
|
4089
|
+
console.error(error3(`Invalid JSON: ${e.message}`));
|
|
3073
4090
|
process.exitCode = 1;
|
|
3074
4091
|
}
|
|
3075
4092
|
break;
|
|
@@ -3077,15 +4094,15 @@ ${BOLD4}Examples:${RESET9}
|
|
|
3077
4094
|
case "delete": {
|
|
3078
4095
|
const name = args[1];
|
|
3079
4096
|
if (!name) {
|
|
3080
|
-
console.error(
|
|
4097
|
+
console.error(error3("Usage: decantr theme delete <name>"));
|
|
3081
4098
|
process.exitCode = 1;
|
|
3082
4099
|
return;
|
|
3083
4100
|
}
|
|
3084
4101
|
const result = deleteTheme(projectRoot, name);
|
|
3085
4102
|
if (result.success) {
|
|
3086
|
-
console.log(
|
|
4103
|
+
console.log(success3(`Deleted custom theme "${name}"`));
|
|
3087
4104
|
} else {
|
|
3088
|
-
console.error(
|
|
4105
|
+
console.error(error3(result.error || "Failed to delete theme"));
|
|
3089
4106
|
process.exitCode = 1;
|
|
3090
4107
|
}
|
|
3091
4108
|
break;
|
|
@@ -3093,18 +4110,18 @@ ${BOLD4}Examples:${RESET9}
|
|
|
3093
4110
|
case "import": {
|
|
3094
4111
|
const sourcePath = args[1];
|
|
3095
4112
|
if (!sourcePath) {
|
|
3096
|
-
console.error(
|
|
4113
|
+
console.error(error3("Usage: decantr theme import <path>"));
|
|
3097
4114
|
process.exitCode = 1;
|
|
3098
4115
|
return;
|
|
3099
4116
|
}
|
|
3100
4117
|
const result = importTheme(projectRoot, sourcePath);
|
|
3101
4118
|
if (result.success) {
|
|
3102
|
-
console.log(
|
|
3103
|
-
console.log(
|
|
4119
|
+
console.log(success3("Theme imported successfully"));
|
|
4120
|
+
console.log(dim3(` Path: ${result.path}`));
|
|
3104
4121
|
} else {
|
|
3105
|
-
console.error(
|
|
4122
|
+
console.error(error3("Import failed:"));
|
|
3106
4123
|
for (const err of result.errors || []) {
|
|
3107
|
-
console.error(` ${
|
|
4124
|
+
console.error(` ${RED11}${err}${RESET13}`);
|
|
3108
4125
|
}
|
|
3109
4126
|
process.exitCode = 1;
|
|
3110
4127
|
}
|
|
@@ -3113,7 +4130,7 @@ ${BOLD4}Examples:${RESET9}
|
|
|
3113
4130
|
case "switch": {
|
|
3114
4131
|
const name = args[1];
|
|
3115
4132
|
if (!name) {
|
|
3116
|
-
console.error(
|
|
4133
|
+
console.error(error3("Usage: decantr theme switch <themeName> [--recipe <r>] [--shape <s>] [--mode <m>]"));
|
|
3117
4134
|
process.exitCode = 1;
|
|
3118
4135
|
return;
|
|
3119
4136
|
}
|
|
@@ -3121,15 +4138,17 @@ ${BOLD4}Examples:${RESET9}
|
|
|
3121
4138
|
break;
|
|
3122
4139
|
}
|
|
3123
4140
|
default:
|
|
3124
|
-
console.error(
|
|
4141
|
+
console.error(error3(`Unknown theme command: ${subcommand}`));
|
|
3125
4142
|
process.exitCode = 1;
|
|
3126
4143
|
}
|
|
3127
4144
|
}
|
|
3128
4145
|
function cmdHelp() {
|
|
3129
4146
|
console.log(`
|
|
3130
|
-
${
|
|
4147
|
+
${BOLD6}decantr${RESET13} \u2014 Design intelligence for AI-generated UI
|
|
3131
4148
|
|
|
3132
|
-
${
|
|
4149
|
+
${BOLD6}Usage:${RESET13}
|
|
4150
|
+
decantr new <name> [--blueprint=X] [--archetype=X] [--theme=X]
|
|
4151
|
+
decantr magic <prompt> [--dry-run]
|
|
3133
4152
|
decantr init [options]
|
|
3134
4153
|
decantr status
|
|
3135
4154
|
decantr sync
|
|
@@ -3150,7 +4169,7 @@ ${BOLD4}Usage:${RESET9}
|
|
|
3150
4169
|
decantr logout
|
|
3151
4170
|
decantr help
|
|
3152
4171
|
|
|
3153
|
-
${
|
|
4172
|
+
${BOLD6}Init Options:${RESET13}
|
|
3154
4173
|
--blueprint, -b Blueprint ID
|
|
3155
4174
|
--theme Theme ID
|
|
3156
4175
|
--mode Color mode: dark | light | auto
|
|
@@ -3164,29 +4183,35 @@ ${BOLD4}Init Options:${RESET9}
|
|
|
3164
4183
|
--yes, -y Accept defaults, skip confirmations
|
|
3165
4184
|
--registry Custom registry URL
|
|
3166
4185
|
|
|
3167
|
-
${
|
|
3168
|
-
${
|
|
3169
|
-
${
|
|
3170
|
-
${
|
|
3171
|
-
${
|
|
3172
|
-
${
|
|
3173
|
-
${
|
|
3174
|
-
${
|
|
3175
|
-
${
|
|
3176
|
-
${
|
|
3177
|
-
${
|
|
3178
|
-
${
|
|
3179
|
-
${
|
|
3180
|
-
${
|
|
3181
|
-
${
|
|
3182
|
-
${
|
|
3183
|
-
${
|
|
3184
|
-
${
|
|
3185
|
-
${
|
|
3186
|
-
${
|
|
3187
|
-
${
|
|
4186
|
+
${BOLD6}Commands:${RESET13}
|
|
4187
|
+
${cyan3("new")} Create a new project with Vite + React + Decantr
|
|
4188
|
+
${cyan3("magic")} One-liner scaffold from a natural language prompt
|
|
4189
|
+
${cyan3("init")} Initialize Decantr in an existing project (v3 essence by default)
|
|
4190
|
+
${cyan3("status")} Show project status, DNA axioms, and blueprint info
|
|
4191
|
+
${cyan3("sync")} Sync registry content from API
|
|
4192
|
+
${cyan3("audit")} Validate essence and check for drift
|
|
4193
|
+
${cyan3("migrate")} Migrate v2 essence to v3 format (with .v2.backup.json backup)
|
|
4194
|
+
${cyan3("check")} Detect drift issues (validate + guard rules) [--telemetry]
|
|
4195
|
+
${cyan3("sync-drift")} Review and resolve drift log entries
|
|
4196
|
+
${cyan3("search")} Search the registry
|
|
4197
|
+
${cyan3("suggest")} Suggest patterns or alternatives for a query
|
|
4198
|
+
${cyan3("get")} Get full details of a registry item
|
|
4199
|
+
${cyan3("list")} List items by type
|
|
4200
|
+
${cyan3("validate")} Validate essence file (v2 and v3)
|
|
4201
|
+
${cyan3("theme")} Manage custom themes (create, list, validate, delete, import)
|
|
4202
|
+
${cyan3("create")} Create a custom content item (pattern, recipe, theme, etc.)
|
|
4203
|
+
${cyan3("publish")} Publish a custom content item to the community registry
|
|
4204
|
+
${cyan3("login")} Authenticate with the Decantr registry
|
|
4205
|
+
${cyan3("logout")} Remove stored credentials
|
|
4206
|
+
${cyan3("analyze")} Scan existing project and produce analysis report
|
|
4207
|
+
${cyan3("export")} Export design tokens to framework format (shadcn, tailwind, css-vars)
|
|
4208
|
+
${cyan3("registry")} Registry management (mirror)
|
|
4209
|
+
${cyan3("upgrade")} Check for content updates from registry
|
|
4210
|
+
${cyan3("help")} Show this help
|
|
3188
4211
|
|
|
3189
|
-
${
|
|
4212
|
+
${BOLD6}Examples:${RESET13}
|
|
4213
|
+
decantr new my-app --blueprint=carbon-ai-portal
|
|
4214
|
+
decantr magic "AI chatbot with dark cyber theme \u2014 bold and futuristic"
|
|
3190
4215
|
decantr init
|
|
3191
4216
|
decantr init --blueprint=saas-dashboard --theme=luminarum --yes
|
|
3192
4217
|
decantr status
|
|
@@ -3207,6 +4232,38 @@ async function main() {
|
|
|
3207
4232
|
return;
|
|
3208
4233
|
}
|
|
3209
4234
|
switch (command) {
|
|
4235
|
+
case "new": {
|
|
4236
|
+
const newName = args[1];
|
|
4237
|
+
if (!newName) {
|
|
4238
|
+
console.error(error3("Usage: decantr new <project-name> [--blueprint=X] [--archetype=X] [--theme=X]"));
|
|
4239
|
+
process.exitCode = 1;
|
|
4240
|
+
break;
|
|
4241
|
+
}
|
|
4242
|
+
const newOpts = {};
|
|
4243
|
+
for (let i = 2; i < args.length; i++) {
|
|
4244
|
+
const arg = args[i];
|
|
4245
|
+
if (arg === "--offline") {
|
|
4246
|
+
newOpts.offline = true;
|
|
4247
|
+
} else if (arg.startsWith("--")) {
|
|
4248
|
+
const [key, value] = arg.slice(2).split("=");
|
|
4249
|
+
if (value) {
|
|
4250
|
+
newOpts[key] = value;
|
|
4251
|
+
} else if (args[i + 1] && !args[i + 1].startsWith("-")) {
|
|
4252
|
+
newOpts[key] = args[++i];
|
|
4253
|
+
}
|
|
4254
|
+
}
|
|
4255
|
+
}
|
|
4256
|
+
await cmdNewProject(newName, {
|
|
4257
|
+
blueprint: newOpts.blueprint,
|
|
4258
|
+
archetype: newOpts.archetype,
|
|
4259
|
+
theme: newOpts.theme,
|
|
4260
|
+
mode: newOpts.mode,
|
|
4261
|
+
shape: newOpts.shape,
|
|
4262
|
+
offline: newOpts.offline === true,
|
|
4263
|
+
registry: newOpts.registry
|
|
4264
|
+
});
|
|
4265
|
+
break;
|
|
4266
|
+
}
|
|
3210
4267
|
case "init": {
|
|
3211
4268
|
const initArgs = {};
|
|
3212
4269
|
for (let i = 1; i < args.length; i++) {
|
|
@@ -3242,7 +4299,7 @@ async function main() {
|
|
|
3242
4299
|
break;
|
|
3243
4300
|
}
|
|
3244
4301
|
case "upgrade": {
|
|
3245
|
-
const { cmdUpgrade } = await import("./upgrade-
|
|
4302
|
+
const { cmdUpgrade } = await import("./upgrade-LTLUPBZQ.js");
|
|
3246
4303
|
const applyFlag = args.includes("--apply");
|
|
3247
4304
|
await cmdUpgrade(process.cwd(), { apply: applyFlag });
|
|
3248
4305
|
break;
|
|
@@ -3250,10 +4307,11 @@ async function main() {
|
|
|
3250
4307
|
case "check":
|
|
3251
4308
|
case "heal": {
|
|
3252
4309
|
if (command === "heal") {
|
|
3253
|
-
console.log(`${
|
|
4310
|
+
console.log(`${YELLOW9}Note: \`decantr heal\` is deprecated. Use \`decantr check\` instead.${RESET13}`);
|
|
3254
4311
|
}
|
|
3255
|
-
const { cmdHeal } = await import("./heal-
|
|
3256
|
-
|
|
4312
|
+
const { cmdHeal } = await import("./heal-54MKDDSQ.js");
|
|
4313
|
+
const telemetryFlag = args.includes("--telemetry");
|
|
4314
|
+
await cmdHeal(process.cwd(), { telemetry: telemetryFlag });
|
|
3257
4315
|
break;
|
|
3258
4316
|
}
|
|
3259
4317
|
case "migrate": {
|
|
@@ -3272,9 +4330,9 @@ async function main() {
|
|
|
3272
4330
|
resolveIndex: resolveNum
|
|
3273
4331
|
});
|
|
3274
4332
|
if (result.success) {
|
|
3275
|
-
console.log(
|
|
4333
|
+
console.log(success3(clearFlag ? "Drift log cleared." : "Entries resolved."));
|
|
3276
4334
|
} else {
|
|
3277
|
-
console.error(
|
|
4335
|
+
console.error(error3(result.error || "Failed"));
|
|
3278
4336
|
process.exitCode = 1;
|
|
3279
4337
|
}
|
|
3280
4338
|
} else {
|
|
@@ -3289,7 +4347,7 @@ async function main() {
|
|
|
3289
4347
|
case "search": {
|
|
3290
4348
|
const query = args[1];
|
|
3291
4349
|
if (!query) {
|
|
3292
|
-
console.error(
|
|
4350
|
+
console.error(error3("Usage: decantr search <query> [--type <type>]"));
|
|
3293
4351
|
process.exitCode = 1;
|
|
3294
4352
|
return;
|
|
3295
4353
|
}
|
|
@@ -3301,7 +4359,7 @@ async function main() {
|
|
|
3301
4359
|
case "suggest": {
|
|
3302
4360
|
const query = args[1];
|
|
3303
4361
|
if (!query) {
|
|
3304
|
-
console.error(
|
|
4362
|
+
console.error(error3("Usage: decantr suggest <query> [--type <type>]"));
|
|
3305
4363
|
process.exitCode = 1;
|
|
3306
4364
|
return;
|
|
3307
4365
|
}
|
|
@@ -3314,7 +4372,7 @@ async function main() {
|
|
|
3314
4372
|
const type = args[1];
|
|
3315
4373
|
const id = args[2];
|
|
3316
4374
|
if (!type || !id) {
|
|
3317
|
-
console.error(
|
|
4375
|
+
console.error(error3("Usage: decantr get <type> <id>"));
|
|
3318
4376
|
process.exitCode = 1;
|
|
3319
4377
|
return;
|
|
3320
4378
|
}
|
|
@@ -3324,7 +4382,7 @@ async function main() {
|
|
|
3324
4382
|
case "list": {
|
|
3325
4383
|
const type = args[1];
|
|
3326
4384
|
if (!type) {
|
|
3327
|
-
console.error(
|
|
4385
|
+
console.error(error3("Usage: decantr list <type>"));
|
|
3328
4386
|
process.exitCode = 1;
|
|
3329
4387
|
return;
|
|
3330
4388
|
}
|
|
@@ -3344,37 +4402,37 @@ async function main() {
|
|
|
3344
4402
|
if (apiKeyArg && apiKeyArg.startsWith("--api-key=")) {
|
|
3345
4403
|
const key = apiKeyArg.split("=")[1];
|
|
3346
4404
|
saveCredentials({ access_token: key, api_key: key });
|
|
3347
|
-
console.log(
|
|
4405
|
+
console.log(success3("API key saved."));
|
|
3348
4406
|
} else {
|
|
3349
|
-
console.log(
|
|
4407
|
+
console.log(heading2("Decantr Login"));
|
|
3350
4408
|
console.log(" To authenticate, get your API key from the Decantr dashboard:");
|
|
3351
4409
|
console.log("");
|
|
3352
|
-
console.log(` ${
|
|
4410
|
+
console.log(` ${cyan3("https://decantr.ai/dashboard/api-keys")}`);
|
|
3353
4411
|
console.log("");
|
|
3354
4412
|
console.log(" Then run:");
|
|
3355
|
-
console.log(` ${
|
|
4413
|
+
console.log(` ${cyan3("decantr login --api-key=<your-key>")}`);
|
|
3356
4414
|
console.log("");
|
|
3357
4415
|
console.log(" Or set the environment variable:");
|
|
3358
|
-
console.log(` ${
|
|
4416
|
+
console.log(` ${cyan3("export DECANTR_API_KEY=<your-key>")}`);
|
|
3359
4417
|
const existingCreds = getCredentials();
|
|
3360
4418
|
if (existingCreds) {
|
|
3361
4419
|
console.log("");
|
|
3362
|
-
console.log(
|
|
4420
|
+
console.log(dim3("You are currently authenticated."));
|
|
3363
4421
|
}
|
|
3364
4422
|
}
|
|
3365
4423
|
break;
|
|
3366
4424
|
}
|
|
3367
4425
|
case "logout": {
|
|
3368
4426
|
clearCredentials();
|
|
3369
|
-
console.log(
|
|
4427
|
+
console.log(success3("Logged out. Credentials removed."));
|
|
3370
4428
|
break;
|
|
3371
4429
|
}
|
|
3372
4430
|
case "create": {
|
|
3373
4431
|
const type = args[1];
|
|
3374
4432
|
const name = args[2];
|
|
3375
4433
|
if (!type || !name) {
|
|
3376
|
-
console.error(
|
|
3377
|
-
console.error(
|
|
4434
|
+
console.error(error3("Usage: decantr create <type> <name>"));
|
|
4435
|
+
console.error(dim3("Types: pattern, recipe, theme, blueprint, archetype, shell"));
|
|
3378
4436
|
process.exitCode = 1;
|
|
3379
4437
|
break;
|
|
3380
4438
|
}
|
|
@@ -3385,8 +4443,8 @@ async function main() {
|
|
|
3385
4443
|
const type = args[1];
|
|
3386
4444
|
const name = args[2];
|
|
3387
4445
|
if (!type || !name) {
|
|
3388
|
-
console.error(
|
|
3389
|
-
console.error(
|
|
4446
|
+
console.error(error3("Usage: decantr publish <type> <name>"));
|
|
4447
|
+
console.error(dim3("Types: pattern, recipe, theme, blueprint, archetype, shell"));
|
|
3390
4448
|
process.exitCode = 1;
|
|
3391
4449
|
break;
|
|
3392
4450
|
}
|
|
@@ -3394,13 +4452,26 @@ async function main() {
|
|
|
3394
4452
|
break;
|
|
3395
4453
|
}
|
|
3396
4454
|
case "refresh": {
|
|
3397
|
-
|
|
4455
|
+
const refreshOffline = args.includes("--offline");
|
|
4456
|
+
await cmdRefresh(process.cwd(), { offline: refreshOffline });
|
|
4457
|
+
break;
|
|
4458
|
+
}
|
|
4459
|
+
case "registry": {
|
|
4460
|
+
const subcommand = args[1];
|
|
4461
|
+
if (subcommand === "mirror") {
|
|
4462
|
+
const typeIdx = args.indexOf("--type");
|
|
4463
|
+
const mirrorType = typeIdx !== -1 ? args[typeIdx + 1] : void 0;
|
|
4464
|
+
await cmdRegistryMirror(process.cwd(), { type: mirrorType });
|
|
4465
|
+
} else {
|
|
4466
|
+
console.error(`${RED11}Usage: decantr registry mirror [--type <type>]${RESET13}`);
|
|
4467
|
+
process.exitCode = 1;
|
|
4468
|
+
}
|
|
3398
4469
|
break;
|
|
3399
4470
|
}
|
|
3400
4471
|
case "add": {
|
|
3401
4472
|
const subcommand = args[1];
|
|
3402
4473
|
if (!subcommand) {
|
|
3403
|
-
console.error(
|
|
4474
|
+
console.error(error3("Usage: decantr add <section|page|feature> <target>"));
|
|
3404
4475
|
process.exitCode = 1;
|
|
3405
4476
|
break;
|
|
3406
4477
|
}
|
|
@@ -3408,7 +4479,7 @@ async function main() {
|
|
|
3408
4479
|
case "section": {
|
|
3409
4480
|
const id = args[2];
|
|
3410
4481
|
if (!id) {
|
|
3411
|
-
console.error(
|
|
4482
|
+
console.error(error3("Usage: decantr add section <archetypeId>"));
|
|
3412
4483
|
process.exitCode = 1;
|
|
3413
4484
|
break;
|
|
3414
4485
|
}
|
|
@@ -3418,7 +4489,7 @@ async function main() {
|
|
|
3418
4489
|
case "page": {
|
|
3419
4490
|
const pagePath = args[2];
|
|
3420
4491
|
if (!pagePath) {
|
|
3421
|
-
console.error(
|
|
4492
|
+
console.error(error3("Usage: decantr add page <section>/<page>"));
|
|
3422
4493
|
process.exitCode = 1;
|
|
3423
4494
|
break;
|
|
3424
4495
|
}
|
|
@@ -3428,7 +4499,7 @@ async function main() {
|
|
|
3428
4499
|
case "feature": {
|
|
3429
4500
|
const feature = args[2];
|
|
3430
4501
|
if (!feature) {
|
|
3431
|
-
console.error(
|
|
4502
|
+
console.error(error3("Usage: decantr add feature <feature> [--section <id>]"));
|
|
3432
4503
|
process.exitCode = 1;
|
|
3433
4504
|
break;
|
|
3434
4505
|
}
|
|
@@ -3436,7 +4507,7 @@ async function main() {
|
|
|
3436
4507
|
break;
|
|
3437
4508
|
}
|
|
3438
4509
|
default:
|
|
3439
|
-
console.error(
|
|
4510
|
+
console.error(error3(`Unknown add subcommand: ${subcommand}. Use section, page, or feature.`));
|
|
3440
4511
|
process.exitCode = 1;
|
|
3441
4512
|
}
|
|
3442
4513
|
break;
|
|
@@ -3444,7 +4515,7 @@ async function main() {
|
|
|
3444
4515
|
case "remove": {
|
|
3445
4516
|
const subcommand = args[1];
|
|
3446
4517
|
if (!subcommand) {
|
|
3447
|
-
console.error(
|
|
4518
|
+
console.error(error3("Usage: decantr remove <section|page|feature> <target>"));
|
|
3448
4519
|
process.exitCode = 1;
|
|
3449
4520
|
break;
|
|
3450
4521
|
}
|
|
@@ -3452,7 +4523,7 @@ async function main() {
|
|
|
3452
4523
|
case "section": {
|
|
3453
4524
|
const id = args[2];
|
|
3454
4525
|
if (!id) {
|
|
3455
|
-
console.error(
|
|
4526
|
+
console.error(error3("Usage: decantr remove section <sectionId>"));
|
|
3456
4527
|
process.exitCode = 1;
|
|
3457
4528
|
break;
|
|
3458
4529
|
}
|
|
@@ -3462,7 +4533,7 @@ async function main() {
|
|
|
3462
4533
|
case "page": {
|
|
3463
4534
|
const pagePath = args[2];
|
|
3464
4535
|
if (!pagePath) {
|
|
3465
|
-
console.error(
|
|
4536
|
+
console.error(error3("Usage: decantr remove page <section>/<page>"));
|
|
3466
4537
|
process.exitCode = 1;
|
|
3467
4538
|
break;
|
|
3468
4539
|
}
|
|
@@ -3472,7 +4543,7 @@ async function main() {
|
|
|
3472
4543
|
case "feature": {
|
|
3473
4544
|
const feature = args[2];
|
|
3474
4545
|
if (!feature) {
|
|
3475
|
-
console.error(
|
|
4546
|
+
console.error(error3("Usage: decantr remove feature <feature> [--section <id>]"));
|
|
3476
4547
|
process.exitCode = 1;
|
|
3477
4548
|
break;
|
|
3478
4549
|
}
|
|
@@ -3480,7 +4551,7 @@ async function main() {
|
|
|
3480
4551
|
break;
|
|
3481
4552
|
}
|
|
3482
4553
|
default:
|
|
3483
|
-
console.error(
|
|
4554
|
+
console.error(error3(`Unknown remove subcommand: ${subcommand}. Use section, page, or feature.`));
|
|
3484
4555
|
process.exitCode = 1;
|
|
3485
4556
|
}
|
|
3486
4557
|
break;
|
|
@@ -3489,14 +4560,69 @@ async function main() {
|
|
|
3489
4560
|
cmdAnalyze(process.cwd());
|
|
3490
4561
|
break;
|
|
3491
4562
|
}
|
|
4563
|
+
case "magic": {
|
|
4564
|
+
const magicFlags = {};
|
|
4565
|
+
const promptParts = [];
|
|
4566
|
+
for (let i = 1; i < args.length; i++) {
|
|
4567
|
+
if (args[i] === "--dry-run") {
|
|
4568
|
+
magicFlags.dryRun = true;
|
|
4569
|
+
} else if (args[i] === "--offline") {
|
|
4570
|
+
magicFlags.offline = true;
|
|
4571
|
+
} else if (args[i].startsWith("--registry=")) {
|
|
4572
|
+
magicFlags.registry = args[i].split("=")[1];
|
|
4573
|
+
} else if (args[i].startsWith("--registry") && args[i + 1]) {
|
|
4574
|
+
magicFlags.registry = args[++i];
|
|
4575
|
+
} else {
|
|
4576
|
+
promptParts.push(args[i]);
|
|
4577
|
+
}
|
|
4578
|
+
}
|
|
4579
|
+
const magicPrompt = promptParts.join(" ").trim();
|
|
4580
|
+
if (!magicPrompt) {
|
|
4581
|
+
console.error(error3("Usage: decantr magic <prompt> [--dry-run] [--offline]"));
|
|
4582
|
+
console.error("");
|
|
4583
|
+
console.error(" Example:");
|
|
4584
|
+
console.error(` ${CYAN7}decantr magic "AI agent dashboard \u2014 dark, neon, confident"${RESET13}`);
|
|
4585
|
+
process.exitCode = 1;
|
|
4586
|
+
break;
|
|
4587
|
+
}
|
|
4588
|
+
await cmdMagic(magicPrompt, process.cwd(), {
|
|
4589
|
+
dryRun: magicFlags.dryRun,
|
|
4590
|
+
offline: magicFlags.offline,
|
|
4591
|
+
registry: magicFlags.registry
|
|
4592
|
+
});
|
|
4593
|
+
break;
|
|
4594
|
+
}
|
|
4595
|
+
case "export": {
|
|
4596
|
+
let exportTarget;
|
|
4597
|
+
let exportOutput;
|
|
4598
|
+
for (let i = 1; i < args.length; i++) {
|
|
4599
|
+
if (args[i] === "--to" && args[i + 1]) {
|
|
4600
|
+
exportTarget = args[++i];
|
|
4601
|
+
} else if (args[i].startsWith("--to=")) {
|
|
4602
|
+
exportTarget = args[i].split("=")[1];
|
|
4603
|
+
} else if (args[i] === "--output" && args[i + 1]) {
|
|
4604
|
+
exportOutput = args[++i];
|
|
4605
|
+
} else if (args[i].startsWith("--output=")) {
|
|
4606
|
+
exportOutput = args[i].split("=")[1];
|
|
4607
|
+
}
|
|
4608
|
+
}
|
|
4609
|
+
const validTargets = ["shadcn", "tailwind", "css-vars"];
|
|
4610
|
+
if (!exportTarget || !validTargets.includes(exportTarget)) {
|
|
4611
|
+
console.error(error3(`Usage: decantr export --to <${validTargets.join("|")}>`));
|
|
4612
|
+
process.exitCode = 1;
|
|
4613
|
+
break;
|
|
4614
|
+
}
|
|
4615
|
+
await cmdExport(exportTarget, process.cwd(), { output: exportOutput });
|
|
4616
|
+
break;
|
|
4617
|
+
}
|
|
3492
4618
|
default:
|
|
3493
|
-
console.error(
|
|
4619
|
+
console.error(error3(`Unknown command: ${command}`));
|
|
3494
4620
|
cmdHelp();
|
|
3495
4621
|
process.exitCode = 1;
|
|
3496
4622
|
}
|
|
3497
4623
|
}
|
|
3498
4624
|
main().catch((e) => {
|
|
3499
|
-
console.error(
|
|
4625
|
+
console.error(error3(e.message));
|
|
3500
4626
|
if (e.stack) console.error(e.stack);
|
|
3501
4627
|
process.exitCode = 1;
|
|
3502
4628
|
});
|