@decantr/cli 2.5.1 → 2.7.0
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/README.md +44 -20
- package/dist/bin.js +2 -2
- package/dist/{chunk-NBJCO4G5.js → chunk-3TH5PLFO.js} +1 -1
- package/dist/{chunk-AUQXYJ7T.js → chunk-ICSLIYSX.js} +1 -1
- package/dist/{chunk-IEW2QFYI.js → chunk-KT2ROK2D.js} +553 -486
- package/dist/{chunk-OD46PCR6.js → chunk-PAF4PBD3.js} +219 -9
- package/dist/{chunk-BZWDPAHL.js → chunk-ZYHR3BGT.js} +1484 -370
- package/dist/{heal-M6PRCIIF.js → heal-ZYD6NVGE.js} +2 -2
- package/dist/{health-ZXOPGNBZ.js → health-ETZXWGTW.js} +3 -3
- package/dist/index.js +2 -2
- package/dist/{studio-LHQXHBE7.js → studio-MKLBUC3A.js} +4 -4
- package/dist/{workspace-MOLAGT2B.js → workspace-KSFWRZEX.js} +4 -4
- package/package.json +5 -5
- package/src/templates/DECANTR.md.template +5 -5
|
@@ -20,6 +20,8 @@ import {
|
|
|
20
20
|
createDoctrineMap,
|
|
21
21
|
getCliTelemetryIdentityStatus,
|
|
22
22
|
isOptedIn,
|
|
23
|
+
loadBundledContentItem,
|
|
24
|
+
loadBundledContentList,
|
|
23
25
|
optIn,
|
|
24
26
|
scanAmbientContext,
|
|
25
27
|
scanProjectInteractions,
|
|
@@ -29,11 +31,11 @@ import {
|
|
|
29
31
|
sendCliCommandTelemetry,
|
|
30
32
|
sendNewProjectCompletedTelemetry,
|
|
31
33
|
writeDoctrineMap
|
|
32
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-KT2ROK2D.js";
|
|
33
35
|
|
|
34
36
|
// src/index.ts
|
|
35
|
-
import { existsSync as
|
|
36
|
-
import { basename as basename2, dirname as dirname4, isAbsolute, join as
|
|
37
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync13, readdirSync as readdirSync7, readFileSync as readFileSync20, writeFileSync as writeFileSync16 } from "fs";
|
|
38
|
+
import { basename as basename2, dirname as dirname4, isAbsolute, join as join28, resolve as resolve4 } from "path";
|
|
37
39
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
38
40
|
import { evaluateGuard, isV4 as isV47, validateEssence as validateEssence2 } from "@decantr/essence-spec";
|
|
39
41
|
import {
|
|
@@ -45,7 +47,9 @@ import {
|
|
|
45
47
|
isContentType as isGetContentType,
|
|
46
48
|
isPublicBlueprintSet,
|
|
47
49
|
API_CONTENT_TYPES as LIST_CONTENT_TYPES,
|
|
48
|
-
|
|
50
|
+
patternToDiscoveryCandidate,
|
|
51
|
+
RegistryAPIClient as RegistryAPIClient3,
|
|
52
|
+
rankPatternCandidates
|
|
49
53
|
} from "@decantr/registry";
|
|
50
54
|
import {
|
|
51
55
|
auditProject,
|
|
@@ -1819,8 +1823,8 @@ async function cmdAddFeature(feature, args, projectRoot = process.cwd()) {
|
|
|
1819
1823
|
}
|
|
1820
1824
|
|
|
1821
1825
|
// src/commands/analyze.ts
|
|
1822
|
-
import { existsSync as
|
|
1823
|
-
import { join as
|
|
1826
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
1827
|
+
import { join as join13 } from "path";
|
|
1824
1828
|
|
|
1825
1829
|
// src/analyzers/components.ts
|
|
1826
1830
|
import { existsSync as existsSync5, readdirSync, statSync as statSync2 } from "fs";
|
|
@@ -2346,9 +2350,262 @@ function scanLayout(projectRoot) {
|
|
|
2346
2350
|
};
|
|
2347
2351
|
}
|
|
2348
2352
|
|
|
2353
|
+
// src/brownfield-intelligence.ts
|
|
2354
|
+
import {
|
|
2355
|
+
existsSync as existsSync9,
|
|
2356
|
+
mkdirSync as mkdirSync5,
|
|
2357
|
+
readdirSync as readdirSync4,
|
|
2358
|
+
readFileSync as readFileSync8,
|
|
2359
|
+
statSync as statSync4,
|
|
2360
|
+
writeFileSync as writeFileSync6
|
|
2361
|
+
} from "fs";
|
|
2362
|
+
import { join as join10, relative } from "path";
|
|
2363
|
+
var CSS_SCAN_DIRS = ["src", "app", "styles", "assets"];
|
|
2364
|
+
var CSS_MAX_FILE_SIZE = 1024 * 1024;
|
|
2365
|
+
function collectCssFiles(projectRoot) {
|
|
2366
|
+
const files = [];
|
|
2367
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2368
|
+
const skip = /* @__PURE__ */ new Set(["node_modules", ".git", ".next", ".decantr", "dist", "build", "coverage"]);
|
|
2369
|
+
const walk = (dir) => {
|
|
2370
|
+
let entries;
|
|
2371
|
+
try {
|
|
2372
|
+
entries = readdirSync4(dir, { withFileTypes: true });
|
|
2373
|
+
} catch {
|
|
2374
|
+
return;
|
|
2375
|
+
}
|
|
2376
|
+
for (const entry of entries) {
|
|
2377
|
+
if (skip.has(entry.name)) continue;
|
|
2378
|
+
const full = join10(dir, entry.name);
|
|
2379
|
+
if (entry.isDirectory()) {
|
|
2380
|
+
walk(full);
|
|
2381
|
+
continue;
|
|
2382
|
+
}
|
|
2383
|
+
if (!entry.isFile() || !entry.name.endsWith(".css")) continue;
|
|
2384
|
+
try {
|
|
2385
|
+
if (statSync4(full).size > CSS_MAX_FILE_SIZE) continue;
|
|
2386
|
+
} catch {
|
|
2387
|
+
continue;
|
|
2388
|
+
}
|
|
2389
|
+
if (!seen.has(full)) {
|
|
2390
|
+
files.push(full);
|
|
2391
|
+
seen.add(full);
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
};
|
|
2395
|
+
for (const dir of CSS_SCAN_DIRS.map((segment) => join10(projectRoot, segment))) {
|
|
2396
|
+
if (existsSync9(dir)) walk(dir);
|
|
2397
|
+
}
|
|
2398
|
+
return files.sort();
|
|
2399
|
+
}
|
|
2400
|
+
function selectorFromMatch(raw) {
|
|
2401
|
+
return raw.replace(/\s+/g, " ").trim().slice(0, 120);
|
|
2402
|
+
}
|
|
2403
|
+
function createThemeInventory(projectRoot, styling) {
|
|
2404
|
+
const files = collectCssFiles(projectRoot);
|
|
2405
|
+
const variantMap = /* @__PURE__ */ new Map();
|
|
2406
|
+
const notes = [];
|
|
2407
|
+
const ensureVariant = (id, selector, evidence) => {
|
|
2408
|
+
const existing = variantMap.get(id) ?? {
|
|
2409
|
+
id,
|
|
2410
|
+
selectors: [],
|
|
2411
|
+
tokenCount: 0,
|
|
2412
|
+
colorTokenCount: 0,
|
|
2413
|
+
evidence: []
|
|
2414
|
+
};
|
|
2415
|
+
if (!existing.selectors.includes(selector)) existing.selectors.push(selector);
|
|
2416
|
+
if (!existing.evidence.includes(evidence)) existing.evidence.push(evidence);
|
|
2417
|
+
variantMap.set(id, existing);
|
|
2418
|
+
};
|
|
2419
|
+
for (const file of files) {
|
|
2420
|
+
let content = "";
|
|
2421
|
+
try {
|
|
2422
|
+
content = readFileSync8(file, "utf-8");
|
|
2423
|
+
} catch {
|
|
2424
|
+
continue;
|
|
2425
|
+
}
|
|
2426
|
+
const rel = relative(projectRoot, file).replace(/\\/g, "/");
|
|
2427
|
+
const selectorRegex = /((?:\.[\w-]*dark[\w-]*|\[data-theme=["'][^"']+["']\]|\[data-theme=[^\]]+\]|\[data-[\w-]*theme=["'][^"']+["']\]|html\.dark|:root|\.theme-[\w-]+)[^{]*)\{/g;
|
|
2428
|
+
for (const match of content.matchAll(selectorRegex)) {
|
|
2429
|
+
const selector = selectorFromMatch(match[1] ?? "");
|
|
2430
|
+
if (!selector) continue;
|
|
2431
|
+
let id = "base";
|
|
2432
|
+
const dataTheme = selector.match(/data-(?:[\w-]*theme|theme)=["']?([^"'\]\s]+)/i);
|
|
2433
|
+
const themeClass = selector.match(/\.theme-([a-z0-9-]+)/i);
|
|
2434
|
+
if (/dark/i.test(selector)) id = "dark";
|
|
2435
|
+
if (dataTheme?.[1]) id = dataTheme[1].toLowerCase();
|
|
2436
|
+
if (themeClass?.[1]) id = themeClass[1].toLowerCase();
|
|
2437
|
+
if (selector.includes(":root")) id = "base";
|
|
2438
|
+
ensureVariant(id, selector, rel);
|
|
2439
|
+
}
|
|
2440
|
+
for (const [id, entry] of variantMap) {
|
|
2441
|
+
const scopedSelectors = entry.selectors.filter((selector) => content.includes(selector));
|
|
2442
|
+
if (scopedSelectors.length === 0) continue;
|
|
2443
|
+
const scopedText = scopedSelectors.map((selector) => {
|
|
2444
|
+
const idx = content.indexOf(selector);
|
|
2445
|
+
return idx >= 0 ? content.slice(idx, Math.min(content.length, idx + 3e3)) : "";
|
|
2446
|
+
}).join("\n");
|
|
2447
|
+
const tokens = [...scopedText.matchAll(/--[\w-]+\s*:/g)].length;
|
|
2448
|
+
const colors = [
|
|
2449
|
+
...scopedText.matchAll(
|
|
2450
|
+
/--[\w-]*(?:color|bg|fg|surface|border|accent|primary|secondary)[\w-]*\s*:/gi
|
|
2451
|
+
)
|
|
2452
|
+
].length;
|
|
2453
|
+
variantMap.set(id, {
|
|
2454
|
+
...entry,
|
|
2455
|
+
tokenCount: Math.max(entry.tokenCount, tokens),
|
|
2456
|
+
colorTokenCount: Math.max(entry.colorTokenCount, colors)
|
|
2457
|
+
});
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
if (variantMap.size === 0) {
|
|
2461
|
+
ensureVariant("base", ":root or existing component styles", "detected styling inventory");
|
|
2462
|
+
notes.push(
|
|
2463
|
+
"No explicit theme selectors were found; Decantr will treat the current UI as the existing-app authority."
|
|
2464
|
+
);
|
|
2465
|
+
}
|
|
2466
|
+
if (styling.darkMode && !variantMap.has("dark")) {
|
|
2467
|
+
ensureVariant("dark", "dark mode signal", "styling analyzer");
|
|
2468
|
+
}
|
|
2469
|
+
const variants = [...variantMap.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
2470
|
+
const modes = variants.map((variant) => variant.id);
|
|
2471
|
+
if (variants.length > 2) {
|
|
2472
|
+
notes.push(
|
|
2473
|
+
"Multiple theme variants were observed. Essence V4 remains unchanged; variants are reported here for task-time context."
|
|
2474
|
+
);
|
|
2475
|
+
}
|
|
2476
|
+
return {
|
|
2477
|
+
version: 1,
|
|
2478
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2479
|
+
localOnly: true,
|
|
2480
|
+
stylingApproach: styling.approach,
|
|
2481
|
+
darkModeDetected: styling.darkMode,
|
|
2482
|
+
modes,
|
|
2483
|
+
variants,
|
|
2484
|
+
tokens: {
|
|
2485
|
+
cssVariables: styling.cssVariables,
|
|
2486
|
+
colors: styling.colors
|
|
2487
|
+
},
|
|
2488
|
+
notes
|
|
2489
|
+
};
|
|
2490
|
+
}
|
|
2491
|
+
function readScreenshotEvidence(projectRoot) {
|
|
2492
|
+
const dir = join10(projectRoot, ".decantr", "evidence", "screenshots");
|
|
2493
|
+
if (!existsSync9(dir)) return [];
|
|
2494
|
+
try {
|
|
2495
|
+
return readdirSync4(dir).filter((file) => file.endsWith(".png") || file.endsWith(".jpg") || file.endsWith(".jpeg")).map((file) => `.decantr/evidence/screenshots/${file}`).sort();
|
|
2496
|
+
} catch {
|
|
2497
|
+
return [];
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
function backlogItems(input) {
|
|
2501
|
+
const items = [
|
|
2502
|
+
"Confirm the observed personality and visual target in `.decantr/observed-essence.proposal.json` before asking an AI agent to add net-new UI.",
|
|
2503
|
+
"Enrich high-traffic routes with precise directives: states, interactions, shared components, responsive behavior, and accessibility expectations.",
|
|
2504
|
+
"Run `decantr health --browser --base-url <url> --evidence` after the app is running to attach local screenshot evidence."
|
|
2505
|
+
];
|
|
2506
|
+
if (input.routes.routes.length > 6) {
|
|
2507
|
+
items.push(
|
|
2508
|
+
"Group mature-app routes into stable zones so future tasks can load focused page or section context instead of the whole app."
|
|
2509
|
+
);
|
|
2510
|
+
}
|
|
2511
|
+
if (input.components.componentCount > 10) {
|
|
2512
|
+
items.push("Identify shared component surfaces and record where reuse beats duplication.");
|
|
2513
|
+
}
|
|
2514
|
+
if (input.themeInventory.variants.length > 1) {
|
|
2515
|
+
items.push(
|
|
2516
|
+
"Document which routes use each observed theme variant; do not promote variants into Essence until the contract boundary is reviewed."
|
|
2517
|
+
);
|
|
2518
|
+
}
|
|
2519
|
+
if (input.features.detected.includes("chat") || input.features.detected.includes("file-upload")) {
|
|
2520
|
+
items.push(
|
|
2521
|
+
"Enrich AI/chat/upload flows with concrete interaction evidence so agents do not replace product behavior with generic patterns."
|
|
2522
|
+
);
|
|
2523
|
+
}
|
|
2524
|
+
if (input.styling.approach !== "decantr-css") {
|
|
2525
|
+
items.push(
|
|
2526
|
+
`Keep Brownfield implementation in the existing ${input.styling.approach} styling system unless adoption mode changes.`
|
|
2527
|
+
);
|
|
2528
|
+
}
|
|
2529
|
+
if (input.layout.shellPattern === "main-only" && input.routes.routes.length > 3) {
|
|
2530
|
+
items.push(
|
|
2531
|
+
"Review shell ownership; the analyzer did not find a clear nav/sidebar/footer structure for a multi-route app."
|
|
2532
|
+
);
|
|
2533
|
+
}
|
|
2534
|
+
return items;
|
|
2535
|
+
}
|
|
2536
|
+
function renderBacklog(items) {
|
|
2537
|
+
return [
|
|
2538
|
+
"# Decantr Brownfield Enrichment Backlog",
|
|
2539
|
+
"",
|
|
2540
|
+
"Use this checklist to turn the first Brownfield attach pass into durable task-time context.",
|
|
2541
|
+
"",
|
|
2542
|
+
...items.map((item) => `- [ ] ${item}`),
|
|
2543
|
+
""
|
|
2544
|
+
].join("\n");
|
|
2545
|
+
}
|
|
2546
|
+
function writeBrownfieldIntelligenceArtifacts(input) {
|
|
2547
|
+
const decantrDir = join10(input.projectRoot, ".decantr");
|
|
2548
|
+
mkdirSync5(decantrDir, { recursive: true });
|
|
2549
|
+
const themeInventory = createThemeInventory(input.projectRoot, input.styling);
|
|
2550
|
+
const screenshots = readScreenshotEvidence(input.projectRoot);
|
|
2551
|
+
const backlog = backlogItems({ ...input, themeInventory });
|
|
2552
|
+
const intelligence = {
|
|
2553
|
+
version: 1,
|
|
2554
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2555
|
+
localOnly: true,
|
|
2556
|
+
workflow: "brownfield-attach",
|
|
2557
|
+
essenceVersion: "4.0.0",
|
|
2558
|
+
project: {
|
|
2559
|
+
framework: input.project.framework,
|
|
2560
|
+
frameworkVersion: input.project.version ?? null,
|
|
2561
|
+
packageManager: input.project.packageManager,
|
|
2562
|
+
hasTypeScript: input.project.hasTypeScript,
|
|
2563
|
+
hasTailwind: input.project.hasTailwind,
|
|
2564
|
+
existingRuleFiles: input.project.existingRuleFiles
|
|
2565
|
+
},
|
|
2566
|
+
routeIntelligence: {
|
|
2567
|
+
strategy: input.routes.strategy,
|
|
2568
|
+
routeCount: input.routes.routes.length,
|
|
2569
|
+
routes: input.routes.routes.map((route) => ({
|
|
2570
|
+
path: route.path,
|
|
2571
|
+
file: route.file,
|
|
2572
|
+
hasLayout: route.hasLayout
|
|
2573
|
+
}))
|
|
2574
|
+
},
|
|
2575
|
+
componentSurface: {
|
|
2576
|
+
pageCount: input.components.pageCount,
|
|
2577
|
+
componentCount: input.components.componentCount,
|
|
2578
|
+
directories: input.components.directories
|
|
2579
|
+
},
|
|
2580
|
+
styling: {
|
|
2581
|
+
approach: input.styling.approach,
|
|
2582
|
+
configFile: input.styling.configFile ?? null,
|
|
2583
|
+
darkMode: input.styling.darkMode,
|
|
2584
|
+
cssVariableCount: input.styling.cssVariables.length,
|
|
2585
|
+
themeInventoryPath: ".decantr/theme-inventory.json"
|
|
2586
|
+
},
|
|
2587
|
+
layout: input.layout,
|
|
2588
|
+
features: input.features,
|
|
2589
|
+
dependencies: input.dependencies,
|
|
2590
|
+
evidence: {
|
|
2591
|
+
screenshotsLocalOnly: true,
|
|
2592
|
+
screenshots,
|
|
2593
|
+
visualManifestPath: existsSync9(join10(decantrDir, "evidence", "visual-manifest.json")) ? ".decantr/evidence/visual-manifest.json" : null
|
|
2594
|
+
},
|
|
2595
|
+
recommendedNextSteps: backlog
|
|
2596
|
+
};
|
|
2597
|
+
const intelligencePath = join10(decantrDir, "brownfield-intelligence.json");
|
|
2598
|
+
const themeInventoryPath = join10(decantrDir, "theme-inventory.json");
|
|
2599
|
+
const backlogPath = join10(decantrDir, "enrichment-backlog.md");
|
|
2600
|
+
writeFileSync6(intelligencePath, JSON.stringify(intelligence, null, 2) + "\n", "utf-8");
|
|
2601
|
+
writeFileSync6(themeInventoryPath, JSON.stringify(themeInventory, null, 2) + "\n", "utf-8");
|
|
2602
|
+
writeFileSync6(backlogPath, renderBacklog(backlog), "utf-8");
|
|
2603
|
+
return { intelligencePath, themeInventoryPath, backlogPath };
|
|
2604
|
+
}
|
|
2605
|
+
|
|
2349
2606
|
// src/detect.ts
|
|
2350
|
-
import { existsSync as
|
|
2351
|
-
import { join as
|
|
2607
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
2608
|
+
import { join as join11 } from "path";
|
|
2352
2609
|
var RULE_FILES = [
|
|
2353
2610
|
"CLAUDE.md",
|
|
2354
2611
|
".claude/rules",
|
|
@@ -2370,52 +2627,52 @@ function detectProject(projectRoot = process.cwd()) {
|
|
|
2370
2627
|
existingEssence: false,
|
|
2371
2628
|
projectRoot
|
|
2372
2629
|
};
|
|
2373
|
-
result.existingEssence =
|
|
2630
|
+
result.existingEssence = existsSync10(join11(projectRoot, "decantr.essence.json"));
|
|
2374
2631
|
for (const ruleFile of RULE_FILES) {
|
|
2375
|
-
if (
|
|
2632
|
+
if (existsSync10(join11(projectRoot, ruleFile))) {
|
|
2376
2633
|
result.existingRuleFiles.push(ruleFile);
|
|
2377
2634
|
}
|
|
2378
2635
|
}
|
|
2379
|
-
if (
|
|
2636
|
+
if (existsSync10(join11(projectRoot, "pnpm-lock.yaml"))) {
|
|
2380
2637
|
result.packageManager = "pnpm";
|
|
2381
|
-
} else if (
|
|
2638
|
+
} else if (existsSync10(join11(projectRoot, "yarn.lock"))) {
|
|
2382
2639
|
result.packageManager = "yarn";
|
|
2383
|
-
} else if (
|
|
2640
|
+
} else if (existsSync10(join11(projectRoot, "bun.lockb"))) {
|
|
2384
2641
|
result.packageManager = "bun";
|
|
2385
|
-
} else if (
|
|
2642
|
+
} else if (existsSync10(join11(projectRoot, "package-lock.json"))) {
|
|
2386
2643
|
result.packageManager = "npm";
|
|
2387
2644
|
}
|
|
2388
|
-
result.hasTypeScript =
|
|
2389
|
-
result.hasTailwind =
|
|
2390
|
-
if (
|
|
2645
|
+
result.hasTypeScript = existsSync10(join11(projectRoot, "tsconfig.json"));
|
|
2646
|
+
result.hasTailwind = existsSync10(join11(projectRoot, "tailwind.config.js")) || existsSync10(join11(projectRoot, "tailwind.config.ts")) || existsSync10(join11(projectRoot, "tailwind.config.mjs")) || existsSync10(join11(projectRoot, "tailwind.config.cjs"));
|
|
2647
|
+
if (existsSync10(join11(projectRoot, "next.config.js")) || existsSync10(join11(projectRoot, "next.config.ts")) || existsSync10(join11(projectRoot, "next.config.mjs"))) {
|
|
2391
2648
|
result.framework = "nextjs";
|
|
2392
2649
|
result.version = getPackageVersion(projectRoot, "next");
|
|
2393
2650
|
return result;
|
|
2394
2651
|
}
|
|
2395
|
-
if (
|
|
2652
|
+
if (existsSync10(join11(projectRoot, "nuxt.config.js")) || existsSync10(join11(projectRoot, "nuxt.config.ts"))) {
|
|
2396
2653
|
result.framework = "nuxt";
|
|
2397
2654
|
result.version = getPackageVersion(projectRoot, "nuxt");
|
|
2398
2655
|
return result;
|
|
2399
2656
|
}
|
|
2400
|
-
if (
|
|
2657
|
+
if (existsSync10(join11(projectRoot, "astro.config.mjs")) || existsSync10(join11(projectRoot, "astro.config.ts"))) {
|
|
2401
2658
|
result.framework = "astro";
|
|
2402
2659
|
result.version = getPackageVersion(projectRoot, "astro");
|
|
2403
2660
|
return result;
|
|
2404
2661
|
}
|
|
2405
|
-
if (
|
|
2662
|
+
if (existsSync10(join11(projectRoot, "svelte.config.js")) || existsSync10(join11(projectRoot, "svelte.config.ts"))) {
|
|
2406
2663
|
result.framework = "svelte";
|
|
2407
2664
|
result.version = getPackageVersion(projectRoot, "svelte");
|
|
2408
2665
|
return result;
|
|
2409
2666
|
}
|
|
2410
|
-
if (
|
|
2667
|
+
if (existsSync10(join11(projectRoot, "angular.json"))) {
|
|
2411
2668
|
result.framework = "angular";
|
|
2412
2669
|
result.version = getPackageVersion(projectRoot, "@angular/core");
|
|
2413
2670
|
return result;
|
|
2414
2671
|
}
|
|
2415
|
-
const packageJsonPath =
|
|
2416
|
-
if (
|
|
2672
|
+
const packageJsonPath = join11(projectRoot, "package.json");
|
|
2673
|
+
if (existsSync10(packageJsonPath)) {
|
|
2417
2674
|
try {
|
|
2418
|
-
const packageJson = JSON.parse(
|
|
2675
|
+
const packageJson = JSON.parse(readFileSync9(packageJsonPath, "utf-8"));
|
|
2419
2676
|
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
2420
2677
|
if (deps.next) {
|
|
2421
2678
|
result.framework = "nextjs";
|
|
@@ -2445,18 +2702,18 @@ function detectProject(projectRoot = process.cwd()) {
|
|
|
2445
2702
|
} catch {
|
|
2446
2703
|
}
|
|
2447
2704
|
}
|
|
2448
|
-
if (result.framework === "unknown" && !
|
|
2449
|
-
if (
|
|
2705
|
+
if (result.framework === "unknown" && !existsSync10(packageJsonPath)) {
|
|
2706
|
+
if (existsSync10(join11(projectRoot, "index.html"))) {
|
|
2450
2707
|
result.framework = "html";
|
|
2451
2708
|
}
|
|
2452
2709
|
}
|
|
2453
2710
|
return result;
|
|
2454
2711
|
}
|
|
2455
2712
|
function getPackageVersion(projectRoot, packageName) {
|
|
2456
|
-
const packageJsonPath =
|
|
2457
|
-
if (!
|
|
2713
|
+
const packageJsonPath = join11(projectRoot, "package.json");
|
|
2714
|
+
if (!existsSync10(packageJsonPath)) return void 0;
|
|
2458
2715
|
try {
|
|
2459
|
-
const packageJson = JSON.parse(
|
|
2716
|
+
const packageJson = JSON.parse(readFileSync9(packageJsonPath, "utf-8"));
|
|
2460
2717
|
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
2461
2718
|
const version = deps[packageName];
|
|
2462
2719
|
return version?.replace(/^\^|~/, "");
|
|
@@ -2489,8 +2746,8 @@ function formatDetection(detected) {
|
|
|
2489
2746
|
}
|
|
2490
2747
|
|
|
2491
2748
|
// src/workflow-model.ts
|
|
2492
|
-
import { existsSync as
|
|
2493
|
-
import { join as
|
|
2749
|
+
import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
|
|
2750
|
+
import { join as join12 } from "path";
|
|
2494
2751
|
function inferSuggestedShell(layout) {
|
|
2495
2752
|
if (layout.hasSidebar) return "sidebar-main";
|
|
2496
2753
|
if (layout.hasTopNav) return "top-nav-main";
|
|
@@ -2577,12 +2834,12 @@ function createBrownfieldInitSeed(detected, layout, styling) {
|
|
|
2577
2834
|
};
|
|
2578
2835
|
}
|
|
2579
2836
|
function readBrownfieldInitSeed(projectRoot) {
|
|
2580
|
-
const seedPath =
|
|
2581
|
-
if (!
|
|
2837
|
+
const seedPath = join12(projectRoot, ".decantr", "init-seed.json");
|
|
2838
|
+
if (!existsSync11(seedPath)) {
|
|
2582
2839
|
return null;
|
|
2583
2840
|
}
|
|
2584
2841
|
try {
|
|
2585
|
-
const parsed = JSON.parse(
|
|
2842
|
+
const parsed = JSON.parse(readFileSync10(seedPath, "utf-8"));
|
|
2586
2843
|
if (parsed.workflow !== "brownfield-adoption") {
|
|
2587
2844
|
return null;
|
|
2588
2845
|
}
|
|
@@ -2693,21 +2950,31 @@ ${BOLD}Analyzing project...${RESET2}
|
|
|
2693
2950
|
]
|
|
2694
2951
|
}
|
|
2695
2952
|
};
|
|
2696
|
-
const decantrDir =
|
|
2697
|
-
if (!
|
|
2698
|
-
|
|
2699
|
-
}
|
|
2700
|
-
const outputPath =
|
|
2701
|
-
const initSeedPath =
|
|
2702
|
-
const ambientPath =
|
|
2703
|
-
const doctrinePath =
|
|
2704
|
-
const reportPath =
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2953
|
+
const decantrDir = join13(projectRoot, ".decantr");
|
|
2954
|
+
if (!existsSync12(decantrDir)) {
|
|
2955
|
+
mkdirSync6(decantrDir, { recursive: true });
|
|
2956
|
+
}
|
|
2957
|
+
const outputPath = join13(decantrDir, "analysis.json");
|
|
2958
|
+
const initSeedPath = join13(decantrDir, "init-seed.json");
|
|
2959
|
+
const ambientPath = join13(decantrDir, "ambient-context.json");
|
|
2960
|
+
const doctrinePath = join13(decantrDir, "doctrine-map.json");
|
|
2961
|
+
const reportPath = join13(decantrDir, "brownfield-report.md");
|
|
2962
|
+
writeFileSync7(outputPath, JSON.stringify(analysis, null, 2) + "\n", "utf-8");
|
|
2963
|
+
writeFileSync7(initSeedPath, JSON.stringify(initSeed, null, 2) + "\n", "utf-8");
|
|
2964
|
+
writeFileSync7(ambientPath, JSON.stringify(ambient, null, 2) + "\n", "utf-8");
|
|
2708
2965
|
writeDoctrineMap(projectRoot, doctrine);
|
|
2709
2966
|
writeBrownfieldProposal(projectRoot, proposal);
|
|
2710
|
-
|
|
2967
|
+
writeFileSync7(reportPath, generateBrownfieldReport(proposal, ambient, doctrine), "utf-8");
|
|
2968
|
+
const intelligenceArtifacts = writeBrownfieldIntelligenceArtifacts({
|
|
2969
|
+
projectRoot,
|
|
2970
|
+
project,
|
|
2971
|
+
routes,
|
|
2972
|
+
components,
|
|
2973
|
+
styling,
|
|
2974
|
+
layout,
|
|
2975
|
+
features,
|
|
2976
|
+
dependencies
|
|
2977
|
+
});
|
|
2711
2978
|
console.log(`
|
|
2712
2979
|
${GREEN2}Analysis complete.${RESET2}
|
|
2713
2980
|
`);
|
|
@@ -2743,8 +3010,13 @@ ${DIM2}Written to:${RESET2} ${outputPath}`);
|
|
|
2743
3010
|
console.log(`${DIM2}Init seed:${RESET2} ${initSeedPath}`);
|
|
2744
3011
|
console.log(`${DIM2}Ambient context:${RESET2} ${ambientPath}`);
|
|
2745
3012
|
console.log(`${DIM2}Doctrine map:${RESET2} ${doctrinePath}`);
|
|
2746
|
-
console.log(
|
|
3013
|
+
console.log(
|
|
3014
|
+
`${DIM2}Observed proposal:${RESET2} ${join13(decantrDir, "observed-essence.proposal.json")}`
|
|
3015
|
+
);
|
|
2747
3016
|
console.log(`${DIM2}Brownfield report:${RESET2} ${reportPath}`);
|
|
3017
|
+
console.log(`${DIM2}Brownfield intelligence:${RESET2} ${intelligenceArtifacts.intelligencePath}`);
|
|
3018
|
+
console.log(`${DIM2}Theme inventory:${RESET2} ${intelligenceArtifacts.themeInventoryPath}`);
|
|
3019
|
+
console.log(`${DIM2}Enrichment backlog:${RESET2} ${intelligenceArtifacts.backlogPath}`);
|
|
2748
3020
|
console.log(
|
|
2749
3021
|
`
|
|
2750
3022
|
${YELLOW}Next step:${RESET2} Review ${BOLD}.decantr/brownfield-report.md${RESET2}, then run ${BOLD}decantr init --existing --accept-proposal${RESET2} to attach Decantr using the observed proposal.
|
|
@@ -2769,8 +3041,8 @@ ${YELLOW}Next step:${RESET2} Review ${BOLD}.decantr/brownfield-report.md${RESET2
|
|
|
2769
3041
|
}
|
|
2770
3042
|
|
|
2771
3043
|
// src/commands/create.ts
|
|
2772
|
-
import { existsSync as
|
|
2773
|
-
import { join as
|
|
3044
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync7, writeFileSync as writeFileSync8 } from "fs";
|
|
3045
|
+
import { join as join14 } from "path";
|
|
2774
3046
|
import {
|
|
2775
3047
|
CONTENT_TYPE_TO_API_CONTENT_TYPE,
|
|
2776
3048
|
CONTENT_TYPES
|
|
@@ -2966,23 +3238,23 @@ function cmdCreate(type, name, projectRoot = process.cwd()) {
|
|
|
2966
3238
|
}
|
|
2967
3239
|
const contentType = type;
|
|
2968
3240
|
const plural = PLURAL[contentType];
|
|
2969
|
-
const customDir =
|
|
2970
|
-
const filePath =
|
|
2971
|
-
if (
|
|
3241
|
+
const customDir = join14(projectRoot, ".decantr", "custom", plural);
|
|
3242
|
+
const filePath = join14(customDir, `${name}.json`);
|
|
3243
|
+
if (existsSync13(filePath)) {
|
|
2972
3244
|
console.error(`${type} "${name}" already exists at ${filePath}`);
|
|
2973
3245
|
process.exitCode = 1;
|
|
2974
3246
|
return;
|
|
2975
3247
|
}
|
|
2976
|
-
|
|
3248
|
+
mkdirSync7(customDir, { recursive: true });
|
|
2977
3249
|
const skeleton = getSkeleton(contentType, name, humanizeId(name));
|
|
2978
|
-
|
|
3250
|
+
writeFileSync8(filePath, JSON.stringify(skeleton, null, 2));
|
|
2979
3251
|
console.log(`Created ${type} "${name}" at ${filePath}`);
|
|
2980
3252
|
console.log(`Edit it, then publish with: decantr publish ${type} ${name}`);
|
|
2981
3253
|
}
|
|
2982
3254
|
|
|
2983
3255
|
// src/commands/export.ts
|
|
2984
|
-
import { existsSync as
|
|
2985
|
-
import { dirname as dirname2, join as
|
|
3256
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
|
|
3257
|
+
import { dirname as dirname2, join as join15 } from "path";
|
|
2986
3258
|
var GREEN3 = "\x1B[32m";
|
|
2987
3259
|
var RED2 = "\x1B[31m";
|
|
2988
3260
|
var DIM3 = "\x1B[2m";
|
|
@@ -3148,21 +3420,21 @@ function generateFigmaTokens(tokens) {
|
|
|
3148
3420
|
`;
|
|
3149
3421
|
}
|
|
3150
3422
|
async function cmdExport(target, projectRoot, options = {}) {
|
|
3151
|
-
const essencePath =
|
|
3152
|
-
const tokensPath =
|
|
3153
|
-
if (!
|
|
3423
|
+
const essencePath = join15(projectRoot, "decantr.essence.json");
|
|
3424
|
+
const tokensPath = join15(projectRoot, "src", "styles", "tokens.css");
|
|
3425
|
+
if (!existsSync14(essencePath)) {
|
|
3154
3426
|
console.error(`${RED2}No decantr.essence.json found. Run \`decantr init\` first.${RESET3}`);
|
|
3155
3427
|
process.exitCode = 1;
|
|
3156
3428
|
return;
|
|
3157
3429
|
}
|
|
3158
|
-
if (!
|
|
3430
|
+
if (!existsSync14(tokensPath)) {
|
|
3159
3431
|
console.error(
|
|
3160
3432
|
`${RED2}No src/styles/tokens.css found. Run \`decantr refresh\` to generate tokens.${RESET3}`
|
|
3161
3433
|
);
|
|
3162
3434
|
process.exitCode = 1;
|
|
3163
3435
|
return;
|
|
3164
3436
|
}
|
|
3165
|
-
const tokensCSS =
|
|
3437
|
+
const tokensCSS = readFileSync11(tokensPath, "utf-8");
|
|
3166
3438
|
const tokens = parseTokensCSS(tokensCSS);
|
|
3167
3439
|
if (tokens.size === 0) {
|
|
3168
3440
|
console.error(`${RED2}No --d-* tokens found in tokens.css.${RESET3}`);
|
|
@@ -3171,36 +3443,36 @@ async function cmdExport(target, projectRoot, options = {}) {
|
|
|
3171
3443
|
}
|
|
3172
3444
|
switch (target) {
|
|
3173
3445
|
case "shadcn": {
|
|
3174
|
-
const cssOut = options.output ??
|
|
3175
|
-
const jsonOut =
|
|
3446
|
+
const cssOut = options.output ?? join15(projectRoot, "src", "styles", "shadcn-theme.css");
|
|
3447
|
+
const jsonOut = join15(projectRoot, "components.json");
|
|
3176
3448
|
ensureDir(cssOut);
|
|
3177
|
-
|
|
3178
|
-
|
|
3449
|
+
writeFileSync9(cssOut, generateShadcnCSS(tokens), "utf-8");
|
|
3450
|
+
writeFileSync9(jsonOut, generateShadcnComponentsJSON(), "utf-8");
|
|
3179
3451
|
console.log(`${GREEN3}Exported shadcn theme:${RESET3}`);
|
|
3180
3452
|
console.log(` ${DIM3}CSS:${RESET3} ${cssOut}`);
|
|
3181
3453
|
console.log(` ${DIM3}JSON:${RESET3} ${jsonOut}`);
|
|
3182
3454
|
break;
|
|
3183
3455
|
}
|
|
3184
3456
|
case "tailwind": {
|
|
3185
|
-
const out = options.output ??
|
|
3457
|
+
const out = options.output ?? join15(projectRoot, "tailwind.decantr.config.ts");
|
|
3186
3458
|
ensureDir(out);
|
|
3187
|
-
|
|
3459
|
+
writeFileSync9(out, generateTailwindConfig(tokens), "utf-8");
|
|
3188
3460
|
console.log(`${GREEN3}Exported Tailwind config:${RESET3}`);
|
|
3189
3461
|
console.log(` ${DIM3}File:${RESET3} ${out}`);
|
|
3190
3462
|
break;
|
|
3191
3463
|
}
|
|
3192
3464
|
case "css-vars": {
|
|
3193
|
-
const out = options.output ??
|
|
3465
|
+
const out = options.output ?? join15(projectRoot, "decantr-tokens.css");
|
|
3194
3466
|
ensureDir(out);
|
|
3195
|
-
|
|
3467
|
+
writeFileSync9(out, generateCSSVars(tokens), "utf-8");
|
|
3196
3468
|
console.log(`${GREEN3}Exported CSS variables:${RESET3}`);
|
|
3197
3469
|
console.log(` ${DIM3}File:${RESET3} ${out}`);
|
|
3198
3470
|
break;
|
|
3199
3471
|
}
|
|
3200
3472
|
case "figma-tokens": {
|
|
3201
|
-
const out = options.output ??
|
|
3473
|
+
const out = options.output ?? join15(projectRoot, ".decantr", "design", "figma-tokens.json");
|
|
3202
3474
|
ensureDir(out);
|
|
3203
|
-
|
|
3475
|
+
writeFileSync9(out, generateFigmaTokens(tokens), "utf-8");
|
|
3204
3476
|
console.log(`${GREEN3}Exported Figma/Tokens Studio tokens:${RESET3}`);
|
|
3205
3477
|
console.log(` ${DIM3}File:${RESET3} ${out}`);
|
|
3206
3478
|
break;
|
|
@@ -3209,15 +3481,15 @@ async function cmdExport(target, projectRoot, options = {}) {
|
|
|
3209
3481
|
}
|
|
3210
3482
|
function ensureDir(filePath) {
|
|
3211
3483
|
const dir = dirname2(filePath);
|
|
3212
|
-
if (!
|
|
3213
|
-
|
|
3484
|
+
if (!existsSync14(dir)) {
|
|
3485
|
+
mkdirSync8(dir, { recursive: true });
|
|
3214
3486
|
}
|
|
3215
3487
|
}
|
|
3216
3488
|
|
|
3217
3489
|
// src/commands/magic.ts
|
|
3218
|
-
import { existsSync as
|
|
3490
|
+
import { existsSync as existsSync15 } from "fs";
|
|
3219
3491
|
import * as fs from "fs/promises";
|
|
3220
|
-
import { join as
|
|
3492
|
+
import { join as join16 } from "path";
|
|
3221
3493
|
var BOLD2 = "\x1B[1m";
|
|
3222
3494
|
var DIM4 = "\x1B[2m";
|
|
3223
3495
|
var RESET4 = "\x1B[0m";
|
|
@@ -3445,8 +3717,8 @@ async function cmdMagic(prompt, projectRoot, options) {
|
|
|
3445
3717
|
console.log(` Archetype: ${intent.archetype}`);
|
|
3446
3718
|
}
|
|
3447
3719
|
console.log("");
|
|
3448
|
-
const essencePath =
|
|
3449
|
-
if (
|
|
3720
|
+
const essencePath = join16(projectRoot, "decantr.essence.json");
|
|
3721
|
+
if (existsSync15(essencePath)) {
|
|
3450
3722
|
console.log(error(" decantr.essence.json already exists in this directory."));
|
|
3451
3723
|
console.log(dim(" Remove it first or use a different directory."));
|
|
3452
3724
|
process.exitCode = 1;
|
|
@@ -3471,7 +3743,7 @@ async function cmdMagic(prompt, projectRoot, options) {
|
|
|
3471
3743
|
return;
|
|
3472
3744
|
}
|
|
3473
3745
|
const registryClient = new RegistryClient({
|
|
3474
|
-
cacheDir:
|
|
3746
|
+
cacheDir: join16(projectRoot, ".decantr", "cache"),
|
|
3475
3747
|
apiUrl: options.registry,
|
|
3476
3748
|
offline: options.offline
|
|
3477
3749
|
});
|
|
@@ -3742,14 +4014,14 @@ async function cmdMagic(prompt, projectRoot, options) {
|
|
|
3742
4014
|
if (result.gitignoreUpdated) {
|
|
3743
4015
|
console.log(` ${dim(".gitignore updated")}`);
|
|
3744
4016
|
}
|
|
3745
|
-
const contextDir =
|
|
4017
|
+
const contextDir = join16(projectRoot, ".decantr", "context");
|
|
3746
4018
|
let sectionCount = 0;
|
|
3747
4019
|
try {
|
|
3748
4020
|
const files = await fs.readdir(contextDir);
|
|
3749
4021
|
sectionCount = files.filter((f) => f.startsWith("section-")).length;
|
|
3750
4022
|
} catch {
|
|
3751
4023
|
}
|
|
3752
|
-
const treatmentsPath =
|
|
4024
|
+
const treatmentsPath = join16(projectRoot, "src", "styles", "treatments.css");
|
|
3753
4025
|
let hasLayers = false;
|
|
3754
4026
|
try {
|
|
3755
4027
|
const css = await fs.readFile(treatmentsPath, "utf-8");
|
|
@@ -3782,8 +4054,8 @@ ${GREEN4}${BOLD2}Quality summary:${RESET4}`);
|
|
|
3782
4054
|
}
|
|
3783
4055
|
|
|
3784
4056
|
// src/commands/migrate.ts
|
|
3785
|
-
import { copyFileSync, existsSync as
|
|
3786
|
-
import { join as
|
|
4057
|
+
import { copyFileSync, existsSync as existsSync16, readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "fs";
|
|
4058
|
+
import { join as join17 } from "path";
|
|
3787
4059
|
import {
|
|
3788
4060
|
isV4 as isV43,
|
|
3789
4061
|
migrateToV4,
|
|
@@ -3796,12 +4068,12 @@ var YELLOW3 = "\x1B[33m";
|
|
|
3796
4068
|
var RESET5 = "\x1B[0m";
|
|
3797
4069
|
var DIM5 = "\x1B[2m";
|
|
3798
4070
|
function migrateEssenceFile(essencePath) {
|
|
3799
|
-
if (!
|
|
4071
|
+
if (!existsSync16(essencePath)) {
|
|
3800
4072
|
return { success: false, error: `File not found: ${essencePath}` };
|
|
3801
4073
|
}
|
|
3802
4074
|
let raw;
|
|
3803
4075
|
try {
|
|
3804
|
-
raw =
|
|
4076
|
+
raw = readFileSync12(essencePath, "utf-8");
|
|
3805
4077
|
} catch (e) {
|
|
3806
4078
|
return { success: false, error: `Could not read ${essencePath}: ${e.message}` };
|
|
3807
4079
|
}
|
|
@@ -3842,7 +4114,7 @@ function migrateEssenceFile(essencePath) {
|
|
|
3842
4114
|
};
|
|
3843
4115
|
}
|
|
3844
4116
|
try {
|
|
3845
|
-
|
|
4117
|
+
writeFileSync10(essencePath, JSON.stringify(v4, null, 2) + "\n");
|
|
3846
4118
|
} catch (e) {
|
|
3847
4119
|
return {
|
|
3848
4120
|
success: false,
|
|
@@ -3863,8 +4135,8 @@ async function cmdMigrate(projectRoot = process.cwd(), args = []) {
|
|
|
3863
4135
|
process.exitCode = 1;
|
|
3864
4136
|
return;
|
|
3865
4137
|
}
|
|
3866
|
-
const essencePath =
|
|
3867
|
-
if (!
|
|
4138
|
+
const essencePath = join17(projectRoot, "decantr.essence.json");
|
|
4139
|
+
if (!existsSync16(essencePath)) {
|
|
3868
4140
|
console.error(`${RED4}No decantr.essence.json found. Run \`decantr init\` first.${RESET5}`);
|
|
3869
4141
|
process.exitCode = 1;
|
|
3870
4142
|
return;
|
|
@@ -3886,7 +4158,7 @@ async function cmdMigrate(projectRoot = process.cwd(), args = []) {
|
|
|
3886
4158
|
}
|
|
3887
4159
|
if (result.essence) {
|
|
3888
4160
|
const registryClient = new RegistryClient({
|
|
3889
|
-
cacheDir:
|
|
4161
|
+
cacheDir: join17(projectRoot, ".decantr", "cache")
|
|
3890
4162
|
});
|
|
3891
4163
|
await refreshDerivedFiles(projectRoot, result.essence, registryClient);
|
|
3892
4164
|
console.log(`${GREEN5}Derived context and execution packs refreshed.${RESET5}`);
|
|
@@ -3897,50 +4169,50 @@ async function cmdMigrate(projectRoot = process.cwd(), args = []) {
|
|
|
3897
4169
|
|
|
3898
4170
|
// src/commands/new-project.ts
|
|
3899
4171
|
import { spawnSync } from "child_process";
|
|
3900
|
-
import { existsSync as
|
|
3901
|
-
import { join as
|
|
4172
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync10 } from "fs";
|
|
4173
|
+
import { join as join19, resolve as resolve2 } from "path";
|
|
3902
4174
|
import { fileURLToPath } from "url";
|
|
3903
4175
|
|
|
3904
4176
|
// src/offline-content.ts
|
|
3905
|
-
import { cpSync, existsSync as
|
|
3906
|
-
import { join as
|
|
4177
|
+
import { cpSync, existsSync as existsSync17, mkdirSync as mkdirSync9 } from "fs";
|
|
4178
|
+
import { join as join18, resolve } from "path";
|
|
3907
4179
|
var CONTENT_TYPES2 = ["archetypes", "blueprints", "patterns", "themes", "shells"];
|
|
3908
4180
|
function copyIfExists(source, target) {
|
|
3909
|
-
if (!
|
|
4181
|
+
if (!existsSync17(source)) return false;
|
|
3910
4182
|
if (resolve(source) === resolve(target)) return true;
|
|
3911
4183
|
cpSync(source, target, { recursive: true });
|
|
3912
4184
|
return true;
|
|
3913
4185
|
}
|
|
3914
4186
|
function hydrateContentRoot(projectDir, contentRoot) {
|
|
3915
|
-
if (!
|
|
3916
|
-
const customRoot =
|
|
3917
|
-
const cacheRoot =
|
|
3918
|
-
|
|
3919
|
-
|
|
4187
|
+
if (!existsSync17(contentRoot)) return false;
|
|
4188
|
+
const customRoot = join18(projectDir, ".decantr", "custom");
|
|
4189
|
+
const cacheRoot = join18(projectDir, ".decantr", "cache", "@official");
|
|
4190
|
+
mkdirSync9(customRoot, { recursive: true });
|
|
4191
|
+
mkdirSync9(cacheRoot, { recursive: true });
|
|
3920
4192
|
let copiedAny = false;
|
|
3921
4193
|
for (const type of CONTENT_TYPES2) {
|
|
3922
|
-
const sourceDir =
|
|
3923
|
-
if (!
|
|
3924
|
-
cpSync(sourceDir,
|
|
3925
|
-
cpSync(sourceDir,
|
|
4194
|
+
const sourceDir = join18(contentRoot, type);
|
|
4195
|
+
if (!existsSync17(sourceDir)) continue;
|
|
4196
|
+
cpSync(sourceDir, join18(customRoot, type), { recursive: true });
|
|
4197
|
+
cpSync(sourceDir, join18(cacheRoot, type), { recursive: true });
|
|
3926
4198
|
copiedAny = true;
|
|
3927
4199
|
}
|
|
3928
4200
|
return copiedAny;
|
|
3929
4201
|
}
|
|
3930
4202
|
function seedOfflineRegistry(projectDir, workspaceRoot) {
|
|
3931
|
-
const projectDecantrRoot =
|
|
3932
|
-
|
|
4203
|
+
const projectDecantrRoot = join18(projectDir, ".decantr");
|
|
4204
|
+
mkdirSync9(projectDecantrRoot, { recursive: true });
|
|
3933
4205
|
const configuredContentRoot = process.env.DECANTR_CONTENT_DIR ? resolve(process.env.DECANTR_CONTENT_DIR) : null;
|
|
3934
4206
|
if (configuredContentRoot && hydrateContentRoot(projectDir, configuredContentRoot)) {
|
|
3935
4207
|
return { seeded: true, strategy: "configured-content-root" };
|
|
3936
4208
|
}
|
|
3937
4209
|
const copiedCache = copyIfExists(
|
|
3938
|
-
|
|
3939
|
-
|
|
4210
|
+
join18(workspaceRoot, ".decantr", "cache"),
|
|
4211
|
+
join18(projectDecantrRoot, "cache")
|
|
3940
4212
|
);
|
|
3941
4213
|
const copiedCustom = copyIfExists(
|
|
3942
|
-
|
|
3943
|
-
|
|
4214
|
+
join18(workspaceRoot, ".decantr", "custom"),
|
|
4215
|
+
join18(projectDecantrRoot, "custom")
|
|
3944
4216
|
);
|
|
3945
4217
|
if (copiedCache || copiedCustom) {
|
|
3946
4218
|
return { seeded: true, strategy: "workspace-cache" };
|
|
@@ -4053,7 +4325,7 @@ function runArgvCommand(command, args, cwd) {
|
|
|
4053
4325
|
}
|
|
4054
4326
|
function resolveInitCommand(initFlags) {
|
|
4055
4327
|
const bundledCliEntrypoint = fileURLToPath(new URL("./bin.js", import.meta.url));
|
|
4056
|
-
const cliEntrypoint =
|
|
4328
|
+
const cliEntrypoint = existsSync18(bundledCliEntrypoint) ? bundledCliEntrypoint : process.argv[1] && existsSync18(process.argv[1]) ? process.argv[1] : null;
|
|
4057
4329
|
if (cliEntrypoint) {
|
|
4058
4330
|
return {
|
|
4059
4331
|
command: process.execPath,
|
|
@@ -4081,7 +4353,7 @@ async function cmdNewProject(projectName, options) {
|
|
|
4081
4353
|
process.exitCode = 1;
|
|
4082
4354
|
return;
|
|
4083
4355
|
}
|
|
4084
|
-
if (
|
|
4356
|
+
if (existsSync18(projectDir)) {
|
|
4085
4357
|
console.error(error2(`Directory "${projectName}" already exists.`));
|
|
4086
4358
|
process.exitCode = 1;
|
|
4087
4359
|
return;
|
|
@@ -4095,7 +4367,7 @@ async function cmdNewProject(projectName, options) {
|
|
|
4095
4367
|
return;
|
|
4096
4368
|
}
|
|
4097
4369
|
console.log(heading(`Creating ${projectName}...`));
|
|
4098
|
-
|
|
4370
|
+
mkdirSync10(projectDir, { recursive: true });
|
|
4099
4371
|
console.log(dim2(` Created ${projectName}/`));
|
|
4100
4372
|
const title = projectName.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
4101
4373
|
if (shouldBootstrapRuntime && bootstrapAdapter) {
|
|
@@ -4194,21 +4466,21 @@ ${YELLOW4}Decantr init encountered issues. Run \`decantr init\` manually inside
|
|
|
4194
4466
|
}
|
|
4195
4467
|
}
|
|
4196
4468
|
function detectPackageManager() {
|
|
4197
|
-
if (
|
|
4469
|
+
if (existsSync18(join19(process.cwd(), "pnpm-lock.yaml")) || existsSync18(join19(process.cwd(), "pnpm-workspace.yaml"))) {
|
|
4198
4470
|
return "pnpm";
|
|
4199
4471
|
}
|
|
4200
|
-
if (
|
|
4472
|
+
if (existsSync18(join19(process.cwd(), "yarn.lock"))) {
|
|
4201
4473
|
return "yarn";
|
|
4202
4474
|
}
|
|
4203
|
-
if (
|
|
4475
|
+
if (existsSync18(join19(process.cwd(), "bun.lockb")) || existsSync18(join19(process.cwd(), "bun.lock"))) {
|
|
4204
4476
|
return "bun";
|
|
4205
4477
|
}
|
|
4206
4478
|
return "npm";
|
|
4207
4479
|
}
|
|
4208
4480
|
|
|
4209
4481
|
// src/commands/publish.ts
|
|
4210
|
-
import { existsSync as
|
|
4211
|
-
import { join as
|
|
4482
|
+
import { existsSync as existsSync19, readFileSync as readFileSync13 } from "fs";
|
|
4483
|
+
import { join as join20 } from "path";
|
|
4212
4484
|
import {
|
|
4213
4485
|
API_CONTENT_TYPE_TO_CONTENT_TYPE,
|
|
4214
4486
|
CONTENT_TYPE_TO_API_CONTENT_TYPE as CONTENT_TYPE_TO_API_CONTENT_TYPE2,
|
|
@@ -4224,8 +4496,8 @@ async function cmdPublish(type, name, projectRoot = process.cwd()) {
|
|
|
4224
4496
|
}
|
|
4225
4497
|
const singularType = API_CONTENT_TYPE_TO_CONTENT_TYPE[type] || type;
|
|
4226
4498
|
const pluralType = CONTENT_TYPE_TO_API_CONTENT_TYPE2[type] || CONTENT_TYPE_TO_API_CONTENT_TYPE2[singularType] || `${type}s`;
|
|
4227
|
-
const customPath =
|
|
4228
|
-
if (!
|
|
4499
|
+
const customPath = join20(projectRoot, ".decantr", "custom", pluralType, `${name}.json`);
|
|
4500
|
+
if (!existsSync19(customPath)) {
|
|
4229
4501
|
console.error(`Custom ${singularType} "${name}" not found at ${customPath}`);
|
|
4230
4502
|
console.error(`Create one first: decantr create ${singularType} ${name}`);
|
|
4231
4503
|
process.exitCode = 1;
|
|
@@ -4233,7 +4505,7 @@ async function cmdPublish(type, name, projectRoot = process.cwd()) {
|
|
|
4233
4505
|
}
|
|
4234
4506
|
let data;
|
|
4235
4507
|
try {
|
|
4236
|
-
data = JSON.parse(
|
|
4508
|
+
data = JSON.parse(readFileSync13(customPath, "utf-8"));
|
|
4237
4509
|
} catch {
|
|
4238
4510
|
console.error(`Failed to parse ${customPath}`);
|
|
4239
4511
|
process.exitCode = 1;
|
|
@@ -4271,23 +4543,23 @@ async function cmdPublish(type, name, projectRoot = process.cwd()) {
|
|
|
4271
4543
|
}
|
|
4272
4544
|
|
|
4273
4545
|
// src/commands/refresh.ts
|
|
4274
|
-
import { existsSync as
|
|
4275
|
-
import { join as
|
|
4546
|
+
import { existsSync as existsSync20, readFileSync as readFileSync14 } from "fs";
|
|
4547
|
+
import { join as join21 } from "path";
|
|
4276
4548
|
import { isV4 as isV44 } from "@decantr/essence-spec";
|
|
4277
4549
|
var GREEN7 = "\x1B[32m";
|
|
4278
4550
|
var RED6 = "\x1B[31m";
|
|
4279
4551
|
var DIM7 = "\x1B[2m";
|
|
4280
4552
|
var RESET7 = "\x1B[0m";
|
|
4281
4553
|
async function cmdRefresh(projectRoot = process.cwd(), options = {}) {
|
|
4282
|
-
const essencePath =
|
|
4283
|
-
if (!
|
|
4554
|
+
const essencePath = join21(projectRoot, "decantr.essence.json");
|
|
4555
|
+
if (!existsSync20(essencePath)) {
|
|
4284
4556
|
console.error(`${RED6}No decantr.essence.json found. Run \`decantr init\` first.${RESET7}`);
|
|
4285
4557
|
process.exitCode = 1;
|
|
4286
4558
|
return;
|
|
4287
4559
|
}
|
|
4288
4560
|
let essence;
|
|
4289
4561
|
try {
|
|
4290
|
-
const raw =
|
|
4562
|
+
const raw = readFileSync14(essencePath, "utf-8");
|
|
4291
4563
|
const parsed = JSON.parse(raw);
|
|
4292
4564
|
if (!isV44(parsed)) {
|
|
4293
4565
|
console.error(
|
|
@@ -4303,7 +4575,7 @@ async function cmdRefresh(projectRoot = process.cwd(), options = {}) {
|
|
|
4303
4575
|
return;
|
|
4304
4576
|
}
|
|
4305
4577
|
const registryClient = new RegistryClient({
|
|
4306
|
-
cacheDir:
|
|
4578
|
+
cacheDir: join21(projectRoot, ".decantr", "cache"),
|
|
4307
4579
|
offline: options.offline
|
|
4308
4580
|
});
|
|
4309
4581
|
console.log("Regenerating derived files...\n");
|
|
@@ -4323,8 +4595,8 @@ async function cmdRefresh(projectRoot = process.cwd(), options = {}) {
|
|
|
4323
4595
|
}
|
|
4324
4596
|
|
|
4325
4597
|
// src/commands/registry-mirror.ts
|
|
4326
|
-
import { mkdirSync as
|
|
4327
|
-
import { join as
|
|
4598
|
+
import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
|
|
4599
|
+
import { join as join22 } from "path";
|
|
4328
4600
|
import { API_CONTENT_TYPES, RegistryAPIClient as RegistryAPIClient2 } from "@decantr/registry";
|
|
4329
4601
|
var GREEN8 = "\x1B[32m";
|
|
4330
4602
|
var RED7 = "\x1B[31m";
|
|
@@ -4352,7 +4624,7 @@ async function cmdRegistryMirror(projectRoot, options = {}) {
|
|
|
4352
4624
|
process.exitCode = 1;
|
|
4353
4625
|
return;
|
|
4354
4626
|
}
|
|
4355
|
-
const cacheDir =
|
|
4627
|
+
const cacheDir = join22(projectRoot, ".decantr", "cache");
|
|
4356
4628
|
const counts = {};
|
|
4357
4629
|
const failed = [];
|
|
4358
4630
|
console.log(`
|
|
@@ -4362,19 +4634,19 @@ Mirroring registry content to ${DIM8}.decantr/cache/${RESET8}
|
|
|
4362
4634
|
try {
|
|
4363
4635
|
const result = await apiClient.listContent(type, { namespace: "@official" });
|
|
4364
4636
|
const items = result.items;
|
|
4365
|
-
const typeDir =
|
|
4366
|
-
|
|
4367
|
-
|
|
4637
|
+
const typeDir = join22(cacheDir, "@official", type);
|
|
4638
|
+
mkdirSync11(typeDir, { recursive: true });
|
|
4639
|
+
writeFileSync11(join22(typeDir, "index.json"), JSON.stringify(result, null, 2));
|
|
4368
4640
|
let itemCount = 0;
|
|
4369
4641
|
for (const item of items) {
|
|
4370
4642
|
const slug = item.slug || item.id;
|
|
4371
4643
|
if (!slug) continue;
|
|
4372
4644
|
try {
|
|
4373
4645
|
const fullItem = await apiClient.getContent(type, "@official", slug);
|
|
4374
|
-
|
|
4646
|
+
writeFileSync11(join22(typeDir, `${slug}.json`), JSON.stringify(fullItem, null, 2));
|
|
4375
4647
|
itemCount++;
|
|
4376
4648
|
} catch {
|
|
4377
|
-
|
|
4649
|
+
writeFileSync11(join22(typeDir, `${slug}.json`), JSON.stringify(item, null, 2));
|
|
4378
4650
|
itemCount++;
|
|
4379
4651
|
}
|
|
4380
4652
|
}
|
|
@@ -4389,8 +4661,8 @@ Mirroring registry content to ${DIM8}.decantr/cache/${RESET8}
|
|
|
4389
4661
|
mirrored_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4390
4662
|
counts
|
|
4391
4663
|
};
|
|
4392
|
-
|
|
4393
|
-
|
|
4664
|
+
mkdirSync11(join22(cacheDir), { recursive: true });
|
|
4665
|
+
writeFileSync11(join22(cacheDir, "mirror-manifest.json"), JSON.stringify(manifest, null, 2));
|
|
4394
4666
|
const totalItems = Object.values(counts).reduce((a, b) => a + b, 0);
|
|
4395
4667
|
console.log("");
|
|
4396
4668
|
if (failed.length > 0) {
|
|
@@ -4407,23 +4679,23 @@ Mirroring registry content to ${DIM8}.decantr/cache/${RESET8}
|
|
|
4407
4679
|
}
|
|
4408
4680
|
|
|
4409
4681
|
// src/commands/remove.ts
|
|
4410
|
-
import { existsSync as
|
|
4411
|
-
import { join as
|
|
4682
|
+
import { existsSync as existsSync22, readFileSync as readFileSync15, rmSync as rmSync2, writeFileSync as writeFileSync12 } from "fs";
|
|
4683
|
+
import { join as join23 } from "path";
|
|
4412
4684
|
import { isV4 as isV45 } from "@decantr/essence-spec";
|
|
4413
4685
|
var GREEN9 = "\x1B[32m";
|
|
4414
4686
|
var RED8 = "\x1B[31m";
|
|
4415
4687
|
var DIM9 = "\x1B[2m";
|
|
4416
4688
|
var RESET9 = "\x1B[0m";
|
|
4417
4689
|
function readV4Essence2(projectRoot) {
|
|
4418
|
-
const essencePath =
|
|
4419
|
-
if (!
|
|
4690
|
+
const essencePath = join23(projectRoot, "decantr.essence.json");
|
|
4691
|
+
if (!existsSync22(essencePath)) {
|
|
4420
4692
|
console.error(`${RED8}No decantr.essence.json found. Run \`decantr init\` first.${RESET9}`);
|
|
4421
4693
|
process.exitCode = 1;
|
|
4422
4694
|
return null;
|
|
4423
4695
|
}
|
|
4424
4696
|
let parsed;
|
|
4425
4697
|
try {
|
|
4426
|
-
parsed = JSON.parse(
|
|
4698
|
+
parsed = JSON.parse(readFileSync15(essencePath, "utf-8"));
|
|
4427
4699
|
} catch (e) {
|
|
4428
4700
|
console.error(`${RED8}Could not read essence: ${e.message}${RESET9}`);
|
|
4429
4701
|
process.exitCode = 1;
|
|
@@ -4439,7 +4711,7 @@ function readV4Essence2(projectRoot) {
|
|
|
4439
4711
|
return { essence: parsed, essencePath };
|
|
4440
4712
|
}
|
|
4441
4713
|
function writeEssence2(essencePath, essence) {
|
|
4442
|
-
|
|
4714
|
+
writeFileSync12(essencePath, JSON.stringify(essence, null, 2) + "\n");
|
|
4443
4715
|
}
|
|
4444
4716
|
function recomputeGlobalFeatures(essence) {
|
|
4445
4717
|
const all = /* @__PURE__ */ new Set();
|
|
@@ -4481,14 +4753,14 @@ async function cmdRemoveSection(sectionId, args, projectRoot = process.cwd()) {
|
|
|
4481
4753
|
sections.splice(idx, 1);
|
|
4482
4754
|
recomputeGlobalFeatures(essence);
|
|
4483
4755
|
removeRoutes(essence, sectionId);
|
|
4484
|
-
const contextFile =
|
|
4485
|
-
if (
|
|
4756
|
+
const contextFile = join23(projectRoot, ".decantr", "context", `${sectionId}.md`);
|
|
4757
|
+
if (existsSync22(contextFile)) {
|
|
4486
4758
|
rmSync2(contextFile);
|
|
4487
4759
|
}
|
|
4488
4760
|
writeEssence2(essencePath, essence);
|
|
4489
4761
|
console.log(`${GREEN9}Removed section "${sectionId}".${RESET9}`);
|
|
4490
4762
|
const registryClient = new RegistryClient({
|
|
4491
|
-
cacheDir:
|
|
4763
|
+
cacheDir: join23(projectRoot, ".decantr", "cache")
|
|
4492
4764
|
});
|
|
4493
4765
|
await refreshDerivedFiles(projectRoot, essence, registryClient);
|
|
4494
4766
|
console.log(`${GREEN9}Derived files refreshed.${RESET9}`);
|
|
@@ -4524,7 +4796,7 @@ async function cmdRemovePage(path, args, projectRoot = process.cwd()) {
|
|
|
4524
4796
|
writeEssence2(essencePath, essence);
|
|
4525
4797
|
console.log(`${GREEN9}Removed page "${pageId}" from section "${sectionId}".${RESET9}`);
|
|
4526
4798
|
const registryClient = new RegistryClient({
|
|
4527
|
-
cacheDir:
|
|
4799
|
+
cacheDir: join23(projectRoot, ".decantr", "cache")
|
|
4528
4800
|
});
|
|
4529
4801
|
await refreshDerivedFiles(projectRoot, essence, registryClient);
|
|
4530
4802
|
console.log(`${GREEN9}Derived files refreshed.${RESET9}`);
|
|
@@ -4565,15 +4837,15 @@ async function cmdRemoveFeature(feature, args, projectRoot = process.cwd()) {
|
|
|
4565
4837
|
const target = sectionId ? `section "${sectionId}" and global` : "global";
|
|
4566
4838
|
console.log(`${GREEN9}Removed feature "${feature}" from ${target} features.${RESET9}`);
|
|
4567
4839
|
const registryClient = new RegistryClient({
|
|
4568
|
-
cacheDir:
|
|
4840
|
+
cacheDir: join23(projectRoot, ".decantr", "cache")
|
|
4569
4841
|
});
|
|
4570
4842
|
await refreshDerivedFiles(projectRoot, essence, registryClient);
|
|
4571
4843
|
console.log(`${GREEN9}Derived files refreshed.${RESET9}`);
|
|
4572
4844
|
}
|
|
4573
4845
|
|
|
4574
4846
|
// src/commands/sync-drift.ts
|
|
4575
|
-
import { existsSync as
|
|
4576
|
-
import { join as
|
|
4847
|
+
import { existsSync as existsSync23, readFileSync as readFileSync16, writeFileSync as writeFileSync13 } from "fs";
|
|
4848
|
+
import { join as join24 } from "path";
|
|
4577
4849
|
var GREEN10 = "\x1B[32m";
|
|
4578
4850
|
var RED9 = "\x1B[31m";
|
|
4579
4851
|
var YELLOW6 = "\x1B[33m";
|
|
@@ -4582,8 +4854,8 @@ var DIM10 = "\x1B[2m";
|
|
|
4582
4854
|
var BOLD4 = "\x1B[1m";
|
|
4583
4855
|
var CYAN5 = "\x1B[36m";
|
|
4584
4856
|
async function cmdSyncDrift(projectRoot = process.cwd()) {
|
|
4585
|
-
const driftLogPath =
|
|
4586
|
-
if (!
|
|
4857
|
+
const driftLogPath = join24(projectRoot, ".decantr", "drift-log.json");
|
|
4858
|
+
if (!existsSync23(driftLogPath)) {
|
|
4587
4859
|
console.log(`${GREEN10}No drift log found \u2014 no drift recorded.${RESET10}`);
|
|
4588
4860
|
console.log(
|
|
4589
4861
|
`${DIM10}Drift is logged when guard violations are detected during development.${RESET10}`
|
|
@@ -4592,7 +4864,7 @@ async function cmdSyncDrift(projectRoot = process.cwd()) {
|
|
|
4592
4864
|
}
|
|
4593
4865
|
let entries;
|
|
4594
4866
|
try {
|
|
4595
|
-
entries = JSON.parse(
|
|
4867
|
+
entries = JSON.parse(readFileSync16(driftLogPath, "utf-8"));
|
|
4596
4868
|
} catch {
|
|
4597
4869
|
console.error(`${RED9}Could not parse drift-log.json${RESET10}`);
|
|
4598
4870
|
process.exitCode = 1;
|
|
@@ -4638,13 +4910,13 @@ ${BOLD4}Unresolved Drift Entries (${unresolved.length})${RESET10}
|
|
|
4638
4910
|
console.log("");
|
|
4639
4911
|
}
|
|
4640
4912
|
function resolveDriftEntries(projectRoot, options) {
|
|
4641
|
-
const driftLogPath =
|
|
4642
|
-
if (!
|
|
4913
|
+
const driftLogPath = join24(projectRoot, ".decantr", "drift-log.json");
|
|
4914
|
+
if (!existsSync23(driftLogPath)) {
|
|
4643
4915
|
return { success: true };
|
|
4644
4916
|
}
|
|
4645
4917
|
if (options.clear) {
|
|
4646
4918
|
try {
|
|
4647
|
-
|
|
4919
|
+
writeFileSync13(driftLogPath, "[]");
|
|
4648
4920
|
return { success: true };
|
|
4649
4921
|
} catch (e) {
|
|
4650
4922
|
return { success: false, error: `Could not clear drift log: ${e.message}` };
|
|
@@ -4652,7 +4924,7 @@ function resolveDriftEntries(projectRoot, options) {
|
|
|
4652
4924
|
}
|
|
4653
4925
|
let entries;
|
|
4654
4926
|
try {
|
|
4655
|
-
entries = JSON.parse(
|
|
4927
|
+
entries = JSON.parse(readFileSync16(driftLogPath, "utf-8"));
|
|
4656
4928
|
} catch {
|
|
4657
4929
|
return { success: false, error: "Could not parse drift-log.json" };
|
|
4658
4930
|
}
|
|
@@ -4671,7 +4943,7 @@ function resolveDriftEntries(projectRoot, options) {
|
|
|
4671
4943
|
}
|
|
4672
4944
|
}
|
|
4673
4945
|
try {
|
|
4674
|
-
|
|
4946
|
+
writeFileSync13(driftLogPath, JSON.stringify(entries, null, 2));
|
|
4675
4947
|
return { success: true };
|
|
4676
4948
|
} catch (e) {
|
|
4677
4949
|
return { success: false, error: `Could not write drift log: ${e.message}` };
|
|
@@ -4896,8 +5168,8 @@ function trimTrailingSlashes(value) {
|
|
|
4896
5168
|
}
|
|
4897
5169
|
|
|
4898
5170
|
// src/commands/theme-switch.ts
|
|
4899
|
-
import { existsSync as
|
|
4900
|
-
import { join as
|
|
5171
|
+
import { existsSync as existsSync24, readFileSync as readFileSync17, writeFileSync as writeFileSync14 } from "fs";
|
|
5172
|
+
import { join as join25 } from "path";
|
|
4901
5173
|
import { isV4 as isV46 } from "@decantr/essence-spec";
|
|
4902
5174
|
var GREEN12 = "\x1B[32m";
|
|
4903
5175
|
var RED10 = "\x1B[31m";
|
|
@@ -4914,15 +5186,15 @@ async function cmdThemeSwitch(themeName, args, projectRoot = process.cwd()) {
|
|
|
4914
5186
|
process.exitCode = 1;
|
|
4915
5187
|
return;
|
|
4916
5188
|
}
|
|
4917
|
-
const essencePath =
|
|
4918
|
-
if (!
|
|
5189
|
+
const essencePath = join25(projectRoot, "decantr.essence.json");
|
|
5190
|
+
if (!existsSync24(essencePath)) {
|
|
4919
5191
|
console.error(`${RED10}No decantr.essence.json found. Run \`decantr init\` first.${RESET12}`);
|
|
4920
5192
|
process.exitCode = 1;
|
|
4921
5193
|
return;
|
|
4922
5194
|
}
|
|
4923
5195
|
let parsed;
|
|
4924
5196
|
try {
|
|
4925
|
-
parsed = JSON.parse(
|
|
5197
|
+
parsed = JSON.parse(readFileSync17(essencePath, "utf-8"));
|
|
4926
5198
|
} catch (e) {
|
|
4927
5199
|
console.error(`${RED10}Could not read essence: ${e.message}${RESET12}`);
|
|
4928
5200
|
process.exitCode = 1;
|
|
@@ -4973,7 +5245,7 @@ async function cmdThemeSwitch(themeName, args, projectRoot = process.cwd()) {
|
|
|
4973
5245
|
essence.dna.theme.mode = mode;
|
|
4974
5246
|
}
|
|
4975
5247
|
const registryClient = new RegistryClient({
|
|
4976
|
-
cacheDir:
|
|
5248
|
+
cacheDir: join25(projectRoot, ".decantr", "cache")
|
|
4977
5249
|
});
|
|
4978
5250
|
try {
|
|
4979
5251
|
const themeResult = await registryClient.fetchTheme(themeName);
|
|
@@ -4988,7 +5260,7 @@ async function cmdThemeSwitch(themeName, args, projectRoot = process.cwd()) {
|
|
|
4988
5260
|
}
|
|
4989
5261
|
} catch {
|
|
4990
5262
|
}
|
|
4991
|
-
|
|
5263
|
+
writeFileSync14(essencePath, JSON.stringify(essence, null, 2) + "\n");
|
|
4992
5264
|
console.log(`${GREEN12}Switched theme: ${oldThemeId} \u2192 ${themeName}${RESET12}`);
|
|
4993
5265
|
if (shape) console.log(` ${DIM12}Shape: ${shape}${RESET12}`);
|
|
4994
5266
|
if (mode) console.log(` ${DIM12}Mode: ${mode}${RESET12}`);
|
|
@@ -5296,8 +5568,8 @@ async function runSimplifiedInit(blueprints) {
|
|
|
5296
5568
|
}
|
|
5297
5569
|
|
|
5298
5570
|
// src/theme-commands.ts
|
|
5299
|
-
import { existsSync as
|
|
5300
|
-
import { join as
|
|
5571
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync12, readdirSync as readdirSync5, readFileSync as readFileSync18, rmSync as rmSync3, writeFileSync as writeFileSync15 } from "fs";
|
|
5572
|
+
import { join as join26 } from "path";
|
|
5301
5573
|
var REQUIRED_FIELDS = [
|
|
5302
5574
|
"$schema",
|
|
5303
5575
|
"id",
|
|
@@ -5357,20 +5629,20 @@ function validateCustomTheme(theme) {
|
|
|
5357
5629
|
};
|
|
5358
5630
|
}
|
|
5359
5631
|
function createTheme(projectRoot, id, name) {
|
|
5360
|
-
const customThemesDir =
|
|
5361
|
-
const themePath =
|
|
5362
|
-
const howToPath =
|
|
5363
|
-
|
|
5364
|
-
if (
|
|
5632
|
+
const customThemesDir = join26(projectRoot, ".decantr", "custom", "themes");
|
|
5633
|
+
const themePath = join26(customThemesDir, `${id}.json`);
|
|
5634
|
+
const howToPath = join26(customThemesDir, "how-to-theme.md");
|
|
5635
|
+
mkdirSync12(customThemesDir, { recursive: true });
|
|
5636
|
+
if (existsSync25(themePath)) {
|
|
5365
5637
|
return {
|
|
5366
5638
|
success: false,
|
|
5367
5639
|
error: `Theme "${id}" already exists at ${themePath}`
|
|
5368
5640
|
};
|
|
5369
5641
|
}
|
|
5370
5642
|
const skeleton = getThemeSkeleton(id, name);
|
|
5371
|
-
|
|
5372
|
-
if (!
|
|
5373
|
-
|
|
5643
|
+
writeFileSync15(themePath, JSON.stringify(skeleton, null, 2));
|
|
5644
|
+
if (!existsSync25(howToPath)) {
|
|
5645
|
+
writeFileSync15(howToPath, getHowToThemeDoc());
|
|
5374
5646
|
}
|
|
5375
5647
|
return {
|
|
5376
5648
|
success: true,
|
|
@@ -5378,17 +5650,17 @@ function createTheme(projectRoot, id, name) {
|
|
|
5378
5650
|
};
|
|
5379
5651
|
}
|
|
5380
5652
|
function listCustomThemes(projectRoot) {
|
|
5381
|
-
const customThemesDir =
|
|
5382
|
-
if (!
|
|
5653
|
+
const customThemesDir = join26(projectRoot, ".decantr", "custom", "themes");
|
|
5654
|
+
if (!existsSync25(customThemesDir)) {
|
|
5383
5655
|
return [];
|
|
5384
5656
|
}
|
|
5385
5657
|
const themes = [];
|
|
5386
5658
|
try {
|
|
5387
|
-
const files =
|
|
5659
|
+
const files = readdirSync5(customThemesDir).filter((f) => f.endsWith(".json"));
|
|
5388
5660
|
for (const file of files) {
|
|
5389
|
-
const filePath =
|
|
5661
|
+
const filePath = join26(customThemesDir, file);
|
|
5390
5662
|
try {
|
|
5391
|
-
const data = JSON.parse(
|
|
5663
|
+
const data = JSON.parse(readFileSync18(filePath, "utf-8"));
|
|
5392
5664
|
themes.push({
|
|
5393
5665
|
id: data.id || file.replace(".json", ""),
|
|
5394
5666
|
name: data.name || data.id,
|
|
@@ -5403,8 +5675,8 @@ function listCustomThemes(projectRoot) {
|
|
|
5403
5675
|
return themes;
|
|
5404
5676
|
}
|
|
5405
5677
|
function deleteTheme(projectRoot, id) {
|
|
5406
|
-
const themePath =
|
|
5407
|
-
if (!
|
|
5678
|
+
const themePath = join26(projectRoot, ".decantr", "custom", "themes", `${id}.json`);
|
|
5679
|
+
if (!existsSync25(themePath)) {
|
|
5408
5680
|
return {
|
|
5409
5681
|
success: false,
|
|
5410
5682
|
error: `Theme "${id}" not found at ${themePath}`
|
|
@@ -5421,7 +5693,7 @@ function deleteTheme(projectRoot, id) {
|
|
|
5421
5693
|
}
|
|
5422
5694
|
}
|
|
5423
5695
|
function importTheme(projectRoot, sourcePath) {
|
|
5424
|
-
if (!
|
|
5696
|
+
if (!existsSync25(sourcePath)) {
|
|
5425
5697
|
return {
|
|
5426
5698
|
success: false,
|
|
5427
5699
|
errors: [`Source file not found: ${sourcePath}`]
|
|
@@ -5429,7 +5701,7 @@ function importTheme(projectRoot, sourcePath) {
|
|
|
5429
5701
|
}
|
|
5430
5702
|
let theme;
|
|
5431
5703
|
try {
|
|
5432
|
-
theme = JSON.parse(
|
|
5704
|
+
theme = JSON.parse(readFileSync18(sourcePath, "utf-8"));
|
|
5433
5705
|
} catch (e) {
|
|
5434
5706
|
return {
|
|
5435
5707
|
success: false,
|
|
@@ -5445,14 +5717,14 @@ function importTheme(projectRoot, sourcePath) {
|
|
|
5445
5717
|
}
|
|
5446
5718
|
theme.source = "custom";
|
|
5447
5719
|
const id = theme.id;
|
|
5448
|
-
const customThemesDir =
|
|
5449
|
-
const destPath =
|
|
5450
|
-
|
|
5451
|
-
const howToPath =
|
|
5452
|
-
if (!
|
|
5453
|
-
|
|
5454
|
-
}
|
|
5455
|
-
|
|
5720
|
+
const customThemesDir = join26(projectRoot, ".decantr", "custom", "themes");
|
|
5721
|
+
const destPath = join26(customThemesDir, `${id}.json`);
|
|
5722
|
+
mkdirSync12(customThemesDir, { recursive: true });
|
|
5723
|
+
const howToPath = join26(customThemesDir, "how-to-theme.md");
|
|
5724
|
+
if (!existsSync25(howToPath)) {
|
|
5725
|
+
writeFileSync15(howToPath, getHowToThemeDoc());
|
|
5726
|
+
}
|
|
5727
|
+
writeFileSync15(destPath, JSON.stringify(theme, null, 2));
|
|
5456
5728
|
return {
|
|
5457
5729
|
success: true,
|
|
5458
5730
|
path: destPath
|
|
@@ -5460,19 +5732,19 @@ function importTheme(projectRoot, sourcePath) {
|
|
|
5460
5732
|
}
|
|
5461
5733
|
|
|
5462
5734
|
// src/workspace.ts
|
|
5463
|
-
import { existsSync as
|
|
5464
|
-
import { dirname as dirname3, join as
|
|
5735
|
+
import { existsSync as existsSync26, readdirSync as readdirSync6, readFileSync as readFileSync19 } from "fs";
|
|
5736
|
+
import { dirname as dirname3, join as join27, resolve as resolve3 } from "path";
|
|
5465
5737
|
function readPackageJson(dir) {
|
|
5466
|
-
const path =
|
|
5467
|
-
if (!
|
|
5738
|
+
const path = join27(dir, "package.json");
|
|
5739
|
+
if (!existsSync26(path)) return null;
|
|
5468
5740
|
try {
|
|
5469
|
-
return JSON.parse(
|
|
5741
|
+
return JSON.parse(readFileSync19(path, "utf-8"));
|
|
5470
5742
|
} catch {
|
|
5471
5743
|
return null;
|
|
5472
5744
|
}
|
|
5473
5745
|
}
|
|
5474
5746
|
function hasWorkspaceMarker(dir) {
|
|
5475
|
-
if (
|
|
5747
|
+
if (existsSync26(join27(dir, "pnpm-workspace.yaml")) || existsSync26(join27(dir, "turbo.json")) || existsSync26(join27(dir, "nx.json"))) {
|
|
5476
5748
|
return true;
|
|
5477
5749
|
}
|
|
5478
5750
|
const pkg = readPackageJson(dir);
|
|
@@ -5488,7 +5760,7 @@ function findWorkspaceRoot(startDir) {
|
|
|
5488
5760
|
}
|
|
5489
5761
|
}
|
|
5490
5762
|
function looksLikeApp(dir) {
|
|
5491
|
-
if (
|
|
5763
|
+
if (existsSync26(join27(dir, "next.config.js")) || existsSync26(join27(dir, "next.config.ts")) || existsSync26(join27(dir, "next.config.mjs")) || existsSync26(join27(dir, "vite.config.ts")) || existsSync26(join27(dir, "vite.config.js")) || existsSync26(join27(dir, "angular.json")) || existsSync26(join27(dir, "svelte.config.js")) || existsSync26(join27(dir, "svelte.config.ts")) || existsSync26(join27(dir, "astro.config.mjs")) || existsSync26(join27(dir, "src")) || existsSync26(join27(dir, "app")) || existsSync26(join27(dir, "pages"))) {
|
|
5492
5764
|
return true;
|
|
5493
5765
|
}
|
|
5494
5766
|
const pkg = readPackageJson(dir);
|
|
@@ -5500,11 +5772,11 @@ function looksLikeApp(dir) {
|
|
|
5500
5772
|
function listWorkspaceApps(workspaceRoot) {
|
|
5501
5773
|
const candidates = [];
|
|
5502
5774
|
for (const base of ["apps", "packages"]) {
|
|
5503
|
-
const baseDir =
|
|
5504
|
-
if (!
|
|
5505
|
-
for (const entry of
|
|
5775
|
+
const baseDir = join27(workspaceRoot, base);
|
|
5776
|
+
if (!existsSync26(baseDir)) continue;
|
|
5777
|
+
for (const entry of readdirSync6(baseDir, { withFileTypes: true })) {
|
|
5506
5778
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
5507
|
-
const candidate =
|
|
5779
|
+
const candidate = join27(baseDir, entry.name);
|
|
5508
5780
|
if (looksLikeApp(candidate)) {
|
|
5509
5781
|
candidates.push(`${base}/${entry.name}`);
|
|
5510
5782
|
}
|
|
@@ -6069,18 +6341,18 @@ function extractHostedAssetPaths(indexHtml) {
|
|
|
6069
6341
|
return [...assetPaths];
|
|
6070
6342
|
}
|
|
6071
6343
|
function readHostedDistSnapshot(distPath) {
|
|
6072
|
-
const resolvedDistPath = distPath ? resolveUserPath(distPath) :
|
|
6073
|
-
const indexPath =
|
|
6074
|
-
if (!
|
|
6344
|
+
const resolvedDistPath = distPath ? resolveUserPath(distPath) : join28(process.cwd(), "dist");
|
|
6345
|
+
const indexPath = join28(resolvedDistPath, "index.html");
|
|
6346
|
+
if (!existsSync27(indexPath)) {
|
|
6075
6347
|
return void 0;
|
|
6076
6348
|
}
|
|
6077
|
-
const indexHtml =
|
|
6349
|
+
const indexHtml = readFileSync20(indexPath, "utf-8");
|
|
6078
6350
|
const assetPaths = extractHostedAssetPaths(indexHtml);
|
|
6079
6351
|
const assets = {};
|
|
6080
6352
|
for (const assetPath of assetPaths) {
|
|
6081
|
-
const assetFilePath =
|
|
6082
|
-
if (
|
|
6083
|
-
assets[assetPath] =
|
|
6353
|
+
const assetFilePath = join28(resolvedDistPath, assetPath.replace(/^[/\\]+/, ""));
|
|
6354
|
+
if (existsSync27(assetFilePath)) {
|
|
6355
|
+
assets[assetPath] = readFileSync20(assetFilePath, "utf-8");
|
|
6084
6356
|
}
|
|
6085
6357
|
}
|
|
6086
6358
|
return {
|
|
@@ -6095,7 +6367,7 @@ function isHostedSourceSnapshotFile(path) {
|
|
|
6095
6367
|
function readHostedSourceSnapshot(sourcePath) {
|
|
6096
6368
|
if (!sourcePath) return void 0;
|
|
6097
6369
|
const resolvedSourcePath = resolveUserPath(sourcePath);
|
|
6098
|
-
if (!
|
|
6370
|
+
if (!existsSync27(resolvedSourcePath)) {
|
|
6099
6371
|
return void 0;
|
|
6100
6372
|
}
|
|
6101
6373
|
const files = {};
|
|
@@ -6109,17 +6381,17 @@ function readHostedSourceSnapshot(sourcePath) {
|
|
|
6109
6381
|
]);
|
|
6110
6382
|
const rootPrefix = basename2(resolvedSourcePath);
|
|
6111
6383
|
const walk = (absoluteDir, relativeDir) => {
|
|
6112
|
-
for (const entry of
|
|
6384
|
+
for (const entry of readdirSync7(absoluteDir, { withFileTypes: true })) {
|
|
6113
6385
|
if (ignoredDirNames.has(entry.name)) continue;
|
|
6114
|
-
const absolutePath =
|
|
6115
|
-
const relativePath =
|
|
6386
|
+
const absolutePath = join28(absoluteDir, entry.name);
|
|
6387
|
+
const relativePath = join28(relativeDir, entry.name).replace(/\\/g, "/");
|
|
6116
6388
|
if (entry.isDirectory()) {
|
|
6117
6389
|
walk(absolutePath, relativePath);
|
|
6118
6390
|
continue;
|
|
6119
6391
|
}
|
|
6120
6392
|
if (!entry.isFile()) continue;
|
|
6121
6393
|
if (!isHostedSourceSnapshotFile(relativePath)) continue;
|
|
6122
|
-
files[relativePath] =
|
|
6394
|
+
files[relativePath] = readFileSync20(absolutePath, "utf-8");
|
|
6123
6395
|
}
|
|
6124
6396
|
};
|
|
6125
6397
|
walk(resolvedSourcePath, rootPrefix);
|
|
@@ -6270,16 +6542,16 @@ async function printRegistryIntelligenceSummary(namespace, jsonOutput = false) {
|
|
|
6270
6542
|
}
|
|
6271
6543
|
async function printHostedExecutionPackBundle(essencePath, namespace, jsonOutput = false, writeContext = false) {
|
|
6272
6544
|
const client = getPublicAPIClient();
|
|
6273
|
-
const resolvedPath = essencePath ? resolveUserPath(essencePath) :
|
|
6274
|
-
if (!
|
|
6545
|
+
const resolvedPath = essencePath ? resolveUserPath(essencePath) : join28(process.cwd(), "decantr.essence.json");
|
|
6546
|
+
if (!existsSync27(resolvedPath)) {
|
|
6275
6547
|
throw new Error(`Essence file not found at ${resolvedPath}`);
|
|
6276
6548
|
}
|
|
6277
|
-
const essence = JSON.parse(
|
|
6549
|
+
const essence = JSON.parse(readFileSync20(resolvedPath, "utf-8"));
|
|
6278
6550
|
const bundle = await client.compileExecutionPacks(essence, namespace ? { namespace } : void 0);
|
|
6279
6551
|
let writtenContextPaths = [];
|
|
6280
6552
|
if (writeContext) {
|
|
6281
|
-
const contextDir =
|
|
6282
|
-
|
|
6553
|
+
const contextDir = join28(process.cwd(), ".decantr", "context");
|
|
6554
|
+
mkdirSync13(contextDir, { recursive: true });
|
|
6283
6555
|
const written = writeExecutionPackBundleArtifacts(
|
|
6284
6556
|
contextDir,
|
|
6285
6557
|
bundle
|
|
@@ -6304,7 +6576,7 @@ async function printHostedExecutionPackBundle(essencePath, namespace, jsonOutput
|
|
|
6304
6576
|
console.log(` Sections: ${typedBundle.sections.length}`);
|
|
6305
6577
|
console.log(` Mutations: ${typedBundle.mutations.length}`);
|
|
6306
6578
|
if (writeContext) {
|
|
6307
|
-
console.log(` Context bundle: ${
|
|
6579
|
+
console.log(` Context bundle: ${join28(process.cwd(), ".decantr", "context")}`);
|
|
6308
6580
|
console.log(` Files written: ${writtenContextPaths.length}`);
|
|
6309
6581
|
}
|
|
6310
6582
|
console.log("");
|
|
@@ -6315,16 +6587,33 @@ async function printHostedExecutionPackBundle(essencePath, namespace, jsonOutput
|
|
|
6315
6587
|
console.log(` ${cyan3(route.path)} -> ${pageLabel} [${patterns}]`);
|
|
6316
6588
|
}
|
|
6317
6589
|
}
|
|
6590
|
+
function resolvePagePackIdForRoute(essencePath, route) {
|
|
6591
|
+
if (!existsSync27(essencePath)) {
|
|
6592
|
+
throw new Error(`Essence file not found at ${essencePath}`);
|
|
6593
|
+
}
|
|
6594
|
+
const essence = JSON.parse(readFileSync20(essencePath, "utf-8"));
|
|
6595
|
+
if (!isV47(essence)) {
|
|
6596
|
+
throw new Error("Route-based pack resolution requires Essence v4.0.0.");
|
|
6597
|
+
}
|
|
6598
|
+
const target = essence.blueprint.routes?.[route];
|
|
6599
|
+
if (!target) {
|
|
6600
|
+
const known = Object.keys(essence.blueprint.routes ?? {}).sort();
|
|
6601
|
+
throw new Error(
|
|
6602
|
+
`Route "${route}" was not found in blueprint.routes. Known routes: ${known.join(", ") || "none"}.`
|
|
6603
|
+
);
|
|
6604
|
+
}
|
|
6605
|
+
return target.page;
|
|
6606
|
+
}
|
|
6318
6607
|
async function printHostedSelectedExecutionPack(packType, id, essencePath, namespace, jsonOutput = false, writeContext = false) {
|
|
6319
6608
|
const client = getPublicAPIClient();
|
|
6320
|
-
const resolvedPath = essencePath ? resolveUserPath(essencePath) :
|
|
6321
|
-
if (!
|
|
6609
|
+
const resolvedPath = essencePath ? resolveUserPath(essencePath) : join28(process.cwd(), "decantr.essence.json");
|
|
6610
|
+
if (!existsSync27(resolvedPath)) {
|
|
6322
6611
|
throw new Error(`Essence file not found at ${resolvedPath}`);
|
|
6323
6612
|
}
|
|
6324
6613
|
if ((packType === "section" || packType === "page" || packType === "mutation") && !id) {
|
|
6325
6614
|
throw new Error(`Pack type "${packType}" requires an id.`);
|
|
6326
6615
|
}
|
|
6327
|
-
const essence = JSON.parse(
|
|
6616
|
+
const essence = JSON.parse(readFileSync20(resolvedPath, "utf-8"));
|
|
6328
6617
|
const selected = await client.selectExecutionPack(
|
|
6329
6618
|
{
|
|
6330
6619
|
essence,
|
|
@@ -6335,17 +6624,17 @@ async function printHostedSelectedExecutionPack(packType, id, essencePath, names
|
|
|
6335
6624
|
);
|
|
6336
6625
|
let writtenContextDir = null;
|
|
6337
6626
|
if (writeContext) {
|
|
6338
|
-
const contextDir =
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6627
|
+
const contextDir = join28(process.cwd(), ".decantr", "context");
|
|
6628
|
+
mkdirSync13(contextDir, { recursive: true });
|
|
6629
|
+
writeFileSync16(
|
|
6630
|
+
join28(contextDir, "pack-manifest.json"),
|
|
6342
6631
|
JSON.stringify(selected.manifest, null, 2) + "\n"
|
|
6343
6632
|
);
|
|
6344
6633
|
const manifestEntry = selected.selector.packType === "scaffold" ? selected.manifest.scaffold : selected.selector.packType === "review" ? selected.manifest.review : selected.selector.packType === "section" ? selected.manifest.sections.find((entry) => entry.id === selected.selector.id) : selected.selector.packType === "page" ? selected.manifest.pages.find((entry) => entry.id === selected.selector.id) : selected.manifest.mutations.find((entry) => entry.id === selected.selector.id);
|
|
6345
6634
|
const markdownFile = manifestEntry?.markdown ?? `${selected.selector.packType}${selected.selector.id ? `-${selected.selector.id}` : ""}-pack.md`;
|
|
6346
6635
|
const jsonFile = manifestEntry?.json ?? `${selected.selector.packType}${selected.selector.id ? `-${selected.selector.id}` : ""}-pack.json`;
|
|
6347
|
-
|
|
6348
|
-
|
|
6636
|
+
writeFileSync16(join28(contextDir, markdownFile), selected.pack.renderedMarkdown);
|
|
6637
|
+
writeFileSync16(join28(contextDir, jsonFile), JSON.stringify(selected.pack, null, 2) + "\n");
|
|
6349
6638
|
writtenContextDir = contextDir;
|
|
6350
6639
|
}
|
|
6351
6640
|
if (jsonOutput) {
|
|
@@ -6370,20 +6659,20 @@ async function printHostedSelectedExecutionPack(packType, id, essencePath, names
|
|
|
6370
6659
|
}
|
|
6371
6660
|
async function printHostedExecutionPackManifest(essencePath, namespace, jsonOutput = false, writeContext = false) {
|
|
6372
6661
|
const client = getPublicAPIClient();
|
|
6373
|
-
const resolvedPath = essencePath ? resolveUserPath(essencePath) :
|
|
6374
|
-
if (!
|
|
6662
|
+
const resolvedPath = essencePath ? resolveUserPath(essencePath) : join28(process.cwd(), "decantr.essence.json");
|
|
6663
|
+
if (!existsSync27(resolvedPath)) {
|
|
6375
6664
|
throw new Error(`Essence file not found at ${resolvedPath}`);
|
|
6376
6665
|
}
|
|
6377
|
-
const essence = JSON.parse(
|
|
6666
|
+
const essence = JSON.parse(readFileSync20(resolvedPath, "utf-8"));
|
|
6378
6667
|
const manifest = await client.getExecutionPackManifest(
|
|
6379
6668
|
essence,
|
|
6380
6669
|
namespace ? { namespace } : void 0
|
|
6381
6670
|
);
|
|
6382
6671
|
let writtenContextDir = null;
|
|
6383
6672
|
if (writeContext) {
|
|
6384
|
-
const contextDir =
|
|
6385
|
-
|
|
6386
|
-
|
|
6673
|
+
const contextDir = join28(process.cwd(), ".decantr", "context");
|
|
6674
|
+
mkdirSync13(contextDir, { recursive: true });
|
|
6675
|
+
writeFileSync16(join28(contextDir, "pack-manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
|
|
6387
6676
|
writtenContextDir = contextDir;
|
|
6388
6677
|
}
|
|
6389
6678
|
if (jsonOutput) {
|
|
@@ -6404,14 +6693,14 @@ async function printHostedExecutionPackManifest(essencePath, namespace, jsonOutp
|
|
|
6404
6693
|
}
|
|
6405
6694
|
}
|
|
6406
6695
|
async function hydrateHostedExecutionPacksIfMissing(projectRoot, namespace = "@official") {
|
|
6407
|
-
const contextDir =
|
|
6408
|
-
const reviewPackPath =
|
|
6409
|
-
const manifestPath =
|
|
6410
|
-
if (
|
|
6696
|
+
const contextDir = join28(projectRoot, ".decantr", "context");
|
|
6697
|
+
const reviewPackPath = join28(contextDir, "review-pack.json");
|
|
6698
|
+
const manifestPath = join28(contextDir, "pack-manifest.json");
|
|
6699
|
+
if (existsSync27(reviewPackPath) && existsSync27(manifestPath)) {
|
|
6411
6700
|
return { attempted: false, hydrated: false };
|
|
6412
6701
|
}
|
|
6413
|
-
const essencePath =
|
|
6414
|
-
if (!
|
|
6702
|
+
const essencePath = join28(projectRoot, "decantr.essence.json");
|
|
6703
|
+
if (!existsSync27(essencePath)) {
|
|
6415
6704
|
return { attempted: false, hydrated: false };
|
|
6416
6705
|
}
|
|
6417
6706
|
const reviewHydration = await hydrateHostedReviewPackIfMissing(projectRoot, namespace);
|
|
@@ -6420,9 +6709,9 @@ async function hydrateHostedExecutionPacksIfMissing(projectRoot, namespace = "@o
|
|
|
6420
6709
|
}
|
|
6421
6710
|
try {
|
|
6422
6711
|
const client = getPublicAPIClient();
|
|
6423
|
-
const essence = JSON.parse(
|
|
6712
|
+
const essence = JSON.parse(readFileSync20(essencePath, "utf-8"));
|
|
6424
6713
|
const bundle = await client.compileExecutionPacks(essence, { namespace });
|
|
6425
|
-
|
|
6714
|
+
mkdirSync13(contextDir, { recursive: true });
|
|
6426
6715
|
writeExecutionPackBundleArtifacts(contextDir, bundle);
|
|
6427
6716
|
return { attempted: true, hydrated: true, scope: "bundle" };
|
|
6428
6717
|
} catch {
|
|
@@ -6430,19 +6719,19 @@ async function hydrateHostedExecutionPacksIfMissing(projectRoot, namespace = "@o
|
|
|
6430
6719
|
}
|
|
6431
6720
|
}
|
|
6432
6721
|
async function hydrateHostedReviewPackIfMissing(projectRoot, namespace = "@official") {
|
|
6433
|
-
const contextDir =
|
|
6434
|
-
const reviewPackPath =
|
|
6435
|
-
const manifestPath =
|
|
6436
|
-
if (
|
|
6722
|
+
const contextDir = join28(projectRoot, ".decantr", "context");
|
|
6723
|
+
const reviewPackPath = join28(contextDir, "review-pack.json");
|
|
6724
|
+
const manifestPath = join28(contextDir, "pack-manifest.json");
|
|
6725
|
+
if (existsSync27(reviewPackPath) && existsSync27(manifestPath)) {
|
|
6437
6726
|
return { attempted: false, hydrated: false };
|
|
6438
6727
|
}
|
|
6439
|
-
const essencePath =
|
|
6440
|
-
if (!
|
|
6728
|
+
const essencePath = join28(projectRoot, "decantr.essence.json");
|
|
6729
|
+
if (!existsSync27(essencePath)) {
|
|
6441
6730
|
return { attempted: false, hydrated: false };
|
|
6442
6731
|
}
|
|
6443
6732
|
try {
|
|
6444
6733
|
const client = getPublicAPIClient();
|
|
6445
|
-
const essence = JSON.parse(
|
|
6734
|
+
const essence = JSON.parse(readFileSync20(essencePath, "utf-8"));
|
|
6446
6735
|
const selected = await client.selectExecutionPack(
|
|
6447
6736
|
{
|
|
6448
6737
|
essence,
|
|
@@ -6450,14 +6739,14 @@ async function hydrateHostedReviewPackIfMissing(projectRoot, namespace = "@offic
|
|
|
6450
6739
|
},
|
|
6451
6740
|
{ namespace }
|
|
6452
6741
|
);
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6742
|
+
mkdirSync13(contextDir, { recursive: true });
|
|
6743
|
+
writeFileSync16(join28(contextDir, "review-pack.md"), selected.pack.renderedMarkdown);
|
|
6744
|
+
writeFileSync16(
|
|
6745
|
+
join28(contextDir, "review-pack.json"),
|
|
6457
6746
|
JSON.stringify(selected.pack, null, 2) + "\n"
|
|
6458
6747
|
);
|
|
6459
|
-
if (!
|
|
6460
|
-
|
|
6748
|
+
if (!existsSync27(manifestPath)) {
|
|
6749
|
+
writeFileSync16(manifestPath, JSON.stringify(selected.manifest, null, 2) + "\n");
|
|
6461
6750
|
}
|
|
6462
6751
|
return { attempted: true, hydrated: true, scope: "review" };
|
|
6463
6752
|
} catch {
|
|
@@ -6467,17 +6756,17 @@ async function hydrateHostedReviewPackIfMissing(projectRoot, namespace = "@offic
|
|
|
6467
6756
|
async function printHostedFileCritique(sourcePath, namespace, jsonOutput = false, essencePath, treatmentsPath) {
|
|
6468
6757
|
const client = getPublicAPIClient();
|
|
6469
6758
|
const resolvedSourcePath = resolveUserPath(sourcePath);
|
|
6470
|
-
const resolvedEssencePath = essencePath ? resolveUserPath(essencePath) :
|
|
6471
|
-
const resolvedTreatmentsPath = treatmentsPath ? resolveUserPath(treatmentsPath) :
|
|
6472
|
-
if (!
|
|
6759
|
+
const resolvedEssencePath = essencePath ? resolveUserPath(essencePath) : join28(process.cwd(), "decantr.essence.json");
|
|
6760
|
+
const resolvedTreatmentsPath = treatmentsPath ? resolveUserPath(treatmentsPath) : join28(process.cwd(), "src", "styles", "treatments.css");
|
|
6761
|
+
if (!existsSync27(resolvedSourcePath)) {
|
|
6473
6762
|
throw new Error(`Source file not found at ${resolvedSourcePath}`);
|
|
6474
6763
|
}
|
|
6475
|
-
if (!
|
|
6764
|
+
if (!existsSync27(resolvedEssencePath)) {
|
|
6476
6765
|
throw new Error(`Essence file not found at ${resolvedEssencePath}`);
|
|
6477
6766
|
}
|
|
6478
|
-
const code =
|
|
6479
|
-
const essence = JSON.parse(
|
|
6480
|
-
const treatmentsCss =
|
|
6767
|
+
const code = readFileSync20(resolvedSourcePath, "utf-8");
|
|
6768
|
+
const essence = JSON.parse(readFileSync20(resolvedEssencePath, "utf-8"));
|
|
6769
|
+
const treatmentsCss = existsSync27(resolvedTreatmentsPath) ? readFileSync20(resolvedTreatmentsPath, "utf-8") : void 0;
|
|
6481
6770
|
const report = await client.critiqueFile(
|
|
6482
6771
|
{
|
|
6483
6772
|
essence,
|
|
@@ -6501,11 +6790,11 @@ async function printHostedFileCritique(sourcePath, namespace, jsonOutput = false
|
|
|
6501
6790
|
}
|
|
6502
6791
|
async function printHostedProjectAudit(namespace, jsonOutput = false, essencePath, distPath, sourcesPath) {
|
|
6503
6792
|
const client = getPublicAPIClient();
|
|
6504
|
-
const resolvedEssencePath = essencePath ? resolveUserPath(essencePath) :
|
|
6505
|
-
if (!
|
|
6793
|
+
const resolvedEssencePath = essencePath ? resolveUserPath(essencePath) : join28(process.cwd(), "decantr.essence.json");
|
|
6794
|
+
if (!existsSync27(resolvedEssencePath)) {
|
|
6506
6795
|
throw new Error(`Essence file not found at ${resolvedEssencePath}`);
|
|
6507
6796
|
}
|
|
6508
|
-
const essence = JSON.parse(
|
|
6797
|
+
const essence = JSON.parse(readFileSync20(resolvedEssencePath, "utf-8"));
|
|
6509
6798
|
const dist = readHostedDistSnapshot(distPath);
|
|
6510
6799
|
const sources = readHostedSourceSnapshot(sourcesPath);
|
|
6511
6800
|
const report = await client.auditProject(
|
|
@@ -6523,7 +6812,7 @@ async function printHostedProjectAudit(namespace, jsonOutput = false, essencePat
|
|
|
6523
6812
|
console.log(heading2("Hosted Project Audit"));
|
|
6524
6813
|
console.log(` Essence: ${resolvedEssencePath}`);
|
|
6525
6814
|
console.log(
|
|
6526
|
-
` Dist snapshot: ${dist ? distPath ? resolveUserPath(distPath) :
|
|
6815
|
+
` Dist snapshot: ${dist ? distPath ? resolveUserPath(distPath) : join28(process.cwd(), "dist") : "none"}`
|
|
6527
6816
|
);
|
|
6528
6817
|
console.log(
|
|
6529
6818
|
` Source snapshot: ${sources && sourcesPath ? resolveUserPath(sourcesPath) : "none"}`
|
|
@@ -6605,42 +6894,151 @@ async function cmdSearch(query, type, sort, recommended, intelligenceSource, blu
|
|
|
6605
6894
|
console.log(dim3(`Search failed. API may be unavailable.`));
|
|
6606
6895
|
}
|
|
6607
6896
|
}
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6897
|
+
function isRecord(value) {
|
|
6898
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6899
|
+
}
|
|
6900
|
+
function patternCandidateFromRegistryItem(item, source) {
|
|
6901
|
+
const record = item;
|
|
6902
|
+
const data = isRecord(record.data) ? record.data : record;
|
|
6903
|
+
const slug = typeof record.slug === "string" && record.slug || typeof data.slug === "string" && data.slug || typeof data.id === "string" && data.id || typeof record.id === "string" && record.id || "pattern";
|
|
6904
|
+
return patternToDiscoveryCandidate(
|
|
6905
|
+
{
|
|
6906
|
+
...data,
|
|
6907
|
+
id: typeof data.id === "string" ? data.id : slug,
|
|
6908
|
+
slug,
|
|
6909
|
+
name: typeof data.name === "string" ? data.name : typeof record.name === "string" ? record.name : slug,
|
|
6910
|
+
description: typeof data.description === "string" ? data.description : typeof record.description === "string" ? record.description : void 0
|
|
6911
|
+
},
|
|
6912
|
+
{ source, slug }
|
|
6913
|
+
);
|
|
6914
|
+
}
|
|
6915
|
+
function readSuggestCodeContext(route, file) {
|
|
6916
|
+
const pieces = [];
|
|
6917
|
+
if (file) {
|
|
6918
|
+
const resolved = isAbsolute(file) ? file : join28(process.cwd(), file);
|
|
6919
|
+
if (existsSync27(resolved)) {
|
|
6920
|
+
pieces.push(readFileSync20(resolved, "utf-8"));
|
|
6621
6921
|
}
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
const
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6922
|
+
}
|
|
6923
|
+
if (route) {
|
|
6924
|
+
const analysisPath = join28(process.cwd(), ".decantr", "analysis.json");
|
|
6925
|
+
if (existsSync27(analysisPath)) {
|
|
6926
|
+
try {
|
|
6927
|
+
const analysis = JSON.parse(readFileSync20(analysisPath, "utf-8"));
|
|
6928
|
+
const routeEntry = analysis.routes?.routes?.find((entry) => entry.path === route);
|
|
6929
|
+
if (routeEntry?.file) {
|
|
6930
|
+
const resolved = join28(process.cwd(), routeEntry.file);
|
|
6931
|
+
if (existsSync27(resolved)) {
|
|
6932
|
+
pieces.push(readFileSync20(resolved, "utf-8"));
|
|
6933
|
+
}
|
|
6934
|
+
}
|
|
6935
|
+
} catch {
|
|
6630
6936
|
}
|
|
6631
|
-
console.log("");
|
|
6632
6937
|
}
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6938
|
+
}
|
|
6939
|
+
return pieces.join("\n\n").slice(0, 2e4);
|
|
6940
|
+
}
|
|
6941
|
+
async function loadPatternDiscoveryCandidates(registryClient) {
|
|
6942
|
+
const candidates = [];
|
|
6943
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6944
|
+
const add = (candidate) => {
|
|
6945
|
+
const key = candidate.slug || candidate.id;
|
|
6946
|
+
if (seen.has(key)) return;
|
|
6947
|
+
seen.add(key);
|
|
6948
|
+
candidates.push(candidate);
|
|
6949
|
+
};
|
|
6950
|
+
const bundledPatterns = loadBundledContentList("patterns");
|
|
6951
|
+
for (const entry of bundledPatterns) {
|
|
6952
|
+
add(patternToDiscoveryCandidate(entry.data, { source: "bundled", slug: entry.id }));
|
|
6953
|
+
}
|
|
6954
|
+
try {
|
|
6955
|
+
const result = await registryClient.fetchContentList("patterns");
|
|
6956
|
+
for (const item of result.data.items) {
|
|
6957
|
+
const source = result.source.type === "api" ? "hosted" : result.source.type;
|
|
6958
|
+
add(patternCandidateFromRegistryItem(item, source));
|
|
6639
6959
|
}
|
|
6640
|
-
console.log(dim3(`Use "decantr get pattern <id>" for full details`));
|
|
6641
6960
|
} catch {
|
|
6642
|
-
console.log(dim3(`Suggestion search failed. API may be unavailable.`));
|
|
6643
6961
|
}
|
|
6962
|
+
for (const item of registryClient.listCustomContent("patterns")) {
|
|
6963
|
+
add(patternCandidateFromRegistryItem(item, "custom"));
|
|
6964
|
+
}
|
|
6965
|
+
return candidates;
|
|
6966
|
+
}
|
|
6967
|
+
async function cmdSuggest(query, options = {}) {
|
|
6968
|
+
const searchType = options.type || "pattern";
|
|
6969
|
+
if (searchType !== "pattern" && searchType !== "patterns") {
|
|
6970
|
+
const apiClient = getAPIClient();
|
|
6971
|
+
try {
|
|
6972
|
+
const response = await apiClient.search({ q: query, type: searchType });
|
|
6973
|
+
const results = response.results;
|
|
6974
|
+
if (results.length === 0) {
|
|
6975
|
+
console.log(dim3(`No suggestions for "${query}"`));
|
|
6976
|
+
return;
|
|
6977
|
+
}
|
|
6978
|
+
console.log(heading2(`Suggestions for "${query}"`));
|
|
6979
|
+
for (const r of results.slice(0, 8)) {
|
|
6980
|
+
console.log(` ${cyan3(r.slug)} - ${r.description || r.name || ""}`);
|
|
6981
|
+
}
|
|
6982
|
+
return;
|
|
6983
|
+
} catch {
|
|
6984
|
+
console.log(dim3(`Suggestion search failed. API may be unavailable.`));
|
|
6985
|
+
return;
|
|
6986
|
+
}
|
|
6987
|
+
}
|
|
6988
|
+
const registryClient = new RegistryClient({
|
|
6989
|
+
cacheDir: join28(process.cwd(), ".decantr", "cache")
|
|
6990
|
+
});
|
|
6991
|
+
const code = options.fromCode || options.file ? readSuggestCodeContext(options.route, options.file) : "";
|
|
6992
|
+
const candidates = await loadPatternDiscoveryCandidates(registryClient);
|
|
6993
|
+
const matches = rankPatternCandidates(
|
|
6994
|
+
{
|
|
6995
|
+
query,
|
|
6996
|
+
route: options.route,
|
|
6997
|
+
code,
|
|
6998
|
+
limit: 10
|
|
6999
|
+
},
|
|
7000
|
+
candidates
|
|
7001
|
+
);
|
|
7002
|
+
if (matches.length === 0) {
|
|
7003
|
+
console.log(dim3(`No pattern suggestions for "${query}"`));
|
|
7004
|
+
console.log("");
|
|
7005
|
+
console.log("Try:");
|
|
7006
|
+
console.log(` ${cyan3("decantr list patterns")} - browse slug, name, domain, and source`);
|
|
7007
|
+
console.log(
|
|
7008
|
+
` ${cyan3('decantr suggest "<broader description>" --from-code --route <route>')} - rank from observed code`
|
|
7009
|
+
);
|
|
7010
|
+
return;
|
|
7011
|
+
}
|
|
7012
|
+
const contextBits = [
|
|
7013
|
+
options.route ? `route ${options.route}` : null,
|
|
7014
|
+
options.file ? `file ${options.file}` : null,
|
|
7015
|
+
code ? "code context" : null
|
|
7016
|
+
].filter((entry) => Boolean(entry));
|
|
7017
|
+
console.log(
|
|
7018
|
+
heading2(
|
|
7019
|
+
`Pattern suggestions for "${query}"${contextBits.length > 0 ? ` (${contextBits.join(", ")})` : ""}`
|
|
7020
|
+
)
|
|
7021
|
+
);
|
|
7022
|
+
for (const match of matches.slice(0, 8)) {
|
|
7023
|
+
const candidate = match.candidate;
|
|
7024
|
+
const slug = candidate.slug || candidate.id;
|
|
7025
|
+
const details = [
|
|
7026
|
+
candidate.name && candidate.name !== slug ? candidate.name : null,
|
|
7027
|
+
candidate.domain || candidate.category || null,
|
|
7028
|
+
candidate.source || null
|
|
7029
|
+
].filter(Boolean);
|
|
7030
|
+
console.log(
|
|
7031
|
+
` ${cyan3(slug)} score ${match.score}${details.length > 0 ? ` ${dim3(details.join(" | "))}` : ""}`
|
|
7032
|
+
);
|
|
7033
|
+
if (candidate.description) {
|
|
7034
|
+
console.log(` ${dim3(candidate.description)}`);
|
|
7035
|
+
}
|
|
7036
|
+
if (match.reasons.length > 0) {
|
|
7037
|
+
console.log(` ${dim3(`why: ${match.reasons.slice(0, 2).join("; ")}`)}`);
|
|
7038
|
+
}
|
|
7039
|
+
}
|
|
7040
|
+
console.log("");
|
|
7041
|
+
console.log(dim3('Use "decantr get pattern <slug>" for full details.'));
|
|
6644
7042
|
}
|
|
6645
7043
|
async function cmdGet(type, id) {
|
|
6646
7044
|
if (!isGetContentType(type)) {
|
|
@@ -6650,26 +7048,16 @@ async function cmdGet(type, id) {
|
|
|
6650
7048
|
}
|
|
6651
7049
|
const apiType = CONTENT_TYPE_TO_API_CONTENT_TYPE3[type];
|
|
6652
7050
|
const registryClient = new RegistryClient({
|
|
6653
|
-
cacheDir:
|
|
7051
|
+
cacheDir: join28(process.cwd(), ".decantr", "cache")
|
|
6654
7052
|
});
|
|
6655
7053
|
const result = await registryClient.fetchContentItem(apiType, id);
|
|
6656
7054
|
if (result) {
|
|
6657
7055
|
console.log(JSON.stringify(result.data, null, 2));
|
|
6658
7056
|
return;
|
|
6659
7057
|
}
|
|
6660
|
-
const
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
// Running from src/
|
|
6664
|
-
join27(currentDir, "..", "src", "bundled", apiType, `${id}.json`),
|
|
6665
|
-
// Running from dist/
|
|
6666
|
-
join27(currentDir, "..", "bundled", apiType, `${id}.json`)
|
|
6667
|
-
// Alternative dist layout
|
|
6668
|
-
];
|
|
6669
|
-
const bundledPath = bundledCandidates.find((p) => existsSync26(p)) || null;
|
|
6670
|
-
if (bundledPath) {
|
|
6671
|
-
const data = JSON.parse(readFileSync19(bundledPath, "utf-8"));
|
|
6672
|
-
console.log(JSON.stringify(data, null, 2));
|
|
7058
|
+
const bundled = loadBundledContentItem(apiType, id);
|
|
7059
|
+
if (bundled) {
|
|
7060
|
+
console.log(JSON.stringify(bundled.data, null, 2));
|
|
6673
7061
|
return;
|
|
6674
7062
|
}
|
|
6675
7063
|
console.error(error3(`${type} "${id}" not found.`));
|
|
@@ -6677,10 +7065,10 @@ async function cmdGet(type, id) {
|
|
|
6677
7065
|
return;
|
|
6678
7066
|
}
|
|
6679
7067
|
async function cmdValidate(path) {
|
|
6680
|
-
const essencePath = path ||
|
|
7068
|
+
const essencePath = path || join28(process.cwd(), "decantr.essence.json");
|
|
6681
7069
|
let raw;
|
|
6682
7070
|
try {
|
|
6683
|
-
raw =
|
|
7071
|
+
raw = readFileSync20(essencePath, "utf-8");
|
|
6684
7072
|
} catch {
|
|
6685
7073
|
console.error(error3(`Could not read ${essencePath}`));
|
|
6686
7074
|
process.exitCode = 1;
|
|
@@ -6747,7 +7135,7 @@ async function cmdList(type, sort, recommended, intelligenceSource, blueprintSet
|
|
|
6747
7135
|
return;
|
|
6748
7136
|
}
|
|
6749
7137
|
const registryClient = new RegistryClient({
|
|
6750
|
-
cacheDir:
|
|
7138
|
+
cacheDir: join28(process.cwd(), ".decantr", "cache")
|
|
6751
7139
|
});
|
|
6752
7140
|
const result = await registryClient.fetchContentList(
|
|
6753
7141
|
type,
|
|
@@ -6757,7 +7145,13 @@ async function cmdList(type, sort, recommended, intelligenceSource, blueprintSet
|
|
|
6757
7145
|
intelligenceSource,
|
|
6758
7146
|
blueprintSet
|
|
6759
7147
|
);
|
|
6760
|
-
const
|
|
7148
|
+
const bundledPatternItems = type === "patterns" ? loadBundledContentList("patterns").map((entry) => ({
|
|
7149
|
+
...entry.data,
|
|
7150
|
+
id: entry.data.id || entry.id,
|
|
7151
|
+
slug: entry.data.slug || entry.id,
|
|
7152
|
+
source: "bundled"
|
|
7153
|
+
})) : [];
|
|
7154
|
+
const items = type === "patterns" ? [...bundledPatternItems, ...result.data.items] : result.data.items;
|
|
6761
7155
|
if (items.length === 0) {
|
|
6762
7156
|
console.log(dim3(`No ${type} found.`));
|
|
6763
7157
|
return;
|
|
@@ -6784,9 +7178,21 @@ async function cmdList(type, sort, recommended, intelligenceSource, blueprintSet
|
|
|
6784
7178
|
} else {
|
|
6785
7179
|
console.log(heading2(`${items.length} ${type} found`));
|
|
6786
7180
|
for (const item of items) {
|
|
6787
|
-
|
|
6788
|
-
|
|
6789
|
-
|
|
7181
|
+
if (type === "patterns") {
|
|
7182
|
+
const pattern = item;
|
|
7183
|
+
const slug = pattern.slug || pattern.id;
|
|
7184
|
+
const domain = pattern.domain || pattern.category || pattern.tags?.[0] || "general";
|
|
7185
|
+
const source = pattern.source || (result.source.type === "api" ? "hosted" : result.source.type);
|
|
7186
|
+
const label = [pattern.name && pattern.name !== slug ? pattern.name : null, domain, source].filter(Boolean).join(" | ");
|
|
7187
|
+
console.log(` ${cyan3(slug)} ${dim3(label)}`);
|
|
7188
|
+
if (pattern.description) {
|
|
7189
|
+
console.log(` ${dim3(pattern.description)}`);
|
|
7190
|
+
}
|
|
7191
|
+
} else {
|
|
7192
|
+
console.log(
|
|
7193
|
+
` ${cyan3(formatRegistryListIdentifier(item))} ${dim3(item.description || item.name || "")}`
|
|
7194
|
+
);
|
|
7195
|
+
}
|
|
6790
7196
|
const intelligenceSummary = formatIntelligenceSummary(
|
|
6791
7197
|
item.intelligence
|
|
6792
7198
|
);
|
|
@@ -6812,10 +7218,10 @@ ${CYAN8}Telemetry enabled.${RESET14} Decantr will send privacy-filtered CLI prod
|
|
|
6812
7218
|
}
|
|
6813
7219
|
function readCliPackageVersion() {
|
|
6814
7220
|
const here = dirname4(fileURLToPath2(import.meta.url));
|
|
6815
|
-
const candidates = [
|
|
7221
|
+
const candidates = [join28(here, "..", "package.json"), join28(here, "..", "..", "package.json")];
|
|
6816
7222
|
for (const candidate of candidates) {
|
|
6817
7223
|
try {
|
|
6818
|
-
const pkg = JSON.parse(
|
|
7224
|
+
const pkg = JSON.parse(readFileSync20(candidate, "utf-8"));
|
|
6819
7225
|
if (pkg.version) return pkg.version;
|
|
6820
7226
|
} catch {
|
|
6821
7227
|
}
|
|
@@ -6826,19 +7232,19 @@ function timestampForFile() {
|
|
|
6826
7232
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
6827
7233
|
}
|
|
6828
7234
|
function backupExistingEssence(projectRoot, label) {
|
|
6829
|
-
const essencePath =
|
|
6830
|
-
if (!
|
|
6831
|
-
const backupPath =
|
|
7235
|
+
const essencePath = join28(projectRoot, "decantr.essence.json");
|
|
7236
|
+
if (!existsSync27(essencePath)) return null;
|
|
7237
|
+
const backupPath = join28(
|
|
6832
7238
|
projectRoot,
|
|
6833
7239
|
`decantr.essence.${label}.${timestampForFile()}.backup.json`
|
|
6834
7240
|
);
|
|
6835
|
-
|
|
7241
|
+
writeFileSync16(backupPath, readFileSync20(essencePath, "utf-8"), "utf-8");
|
|
6836
7242
|
return backupPath;
|
|
6837
7243
|
}
|
|
6838
7244
|
function writeBrownfieldProjectJson(input) {
|
|
6839
|
-
const decantrDir =
|
|
6840
|
-
|
|
6841
|
-
|
|
7245
|
+
const decantrDir = join28(input.projectRoot, ".decantr");
|
|
7246
|
+
mkdirSync13(join28(decantrDir, "context"), { recursive: true });
|
|
7247
|
+
mkdirSync13(join28(decantrDir, "cache"), { recursive: true });
|
|
6842
7248
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6843
7249
|
const projectJson = {
|
|
6844
7250
|
detected: {
|
|
@@ -6879,7 +7285,7 @@ function writeBrownfieldProjectJson(input) {
|
|
|
6879
7285
|
}
|
|
6880
7286
|
}
|
|
6881
7287
|
};
|
|
6882
|
-
|
|
7288
|
+
writeFileSync16(join28(decantrDir, "project.json"), JSON.stringify(projectJson, null, 2) + "\n");
|
|
6883
7289
|
}
|
|
6884
7290
|
async function applyAcceptedBrownfieldProposal(input) {
|
|
6885
7291
|
const proposal = readBrownfieldProposal(input.projectRoot);
|
|
@@ -6895,8 +7301,8 @@ async function applyAcceptedBrownfieldProposal(input) {
|
|
|
6895
7301
|
process.exitCode = 1;
|
|
6896
7302
|
return;
|
|
6897
7303
|
}
|
|
6898
|
-
const essencePath =
|
|
6899
|
-
const hasEssence =
|
|
7304
|
+
const essencePath = join28(input.projectRoot, "decantr.essence.json");
|
|
7305
|
+
const hasEssence = existsSync27(essencePath);
|
|
6900
7306
|
let essence;
|
|
6901
7307
|
let backupPath = null;
|
|
6902
7308
|
if (input.mode === "accept" && hasEssence) {
|
|
@@ -6910,7 +7316,7 @@ async function applyAcceptedBrownfieldProposal(input) {
|
|
|
6910
7316
|
return;
|
|
6911
7317
|
}
|
|
6912
7318
|
if (input.mode === "merge" && hasEssence) {
|
|
6913
|
-
const existing = JSON.parse(
|
|
7319
|
+
const existing = JSON.parse(readFileSync20(essencePath, "utf-8"));
|
|
6914
7320
|
if (!isV47(existing)) {
|
|
6915
7321
|
console.log(
|
|
6916
7322
|
error3(
|
|
@@ -6947,9 +7353,9 @@ async function applyAcceptedBrownfieldProposal(input) {
|
|
|
6947
7353
|
assistantBridge: input.assistantBridge,
|
|
6948
7354
|
mode: input.mode
|
|
6949
7355
|
});
|
|
6950
|
-
|
|
7356
|
+
writeFileSync16(essencePath, JSON.stringify(essence, null, 2) + "\n", "utf-8");
|
|
6951
7357
|
const registryClient = new RegistryClient({
|
|
6952
|
-
cacheDir:
|
|
7358
|
+
cacheDir: join28(input.projectRoot, ".decantr", "cache"),
|
|
6953
7359
|
offline: true,
|
|
6954
7360
|
projectRoot: input.projectRoot
|
|
6955
7361
|
});
|
|
@@ -7103,7 +7509,7 @@ async function cmdInit(args) {
|
|
|
7103
7509
|
}
|
|
7104
7510
|
}
|
|
7105
7511
|
const registryClient = new RegistryClient({
|
|
7106
|
-
cacheDir:
|
|
7512
|
+
cacheDir: join28(projectRoot, ".decantr", "cache"),
|
|
7107
7513
|
apiUrl: args.registry,
|
|
7108
7514
|
offline: args.offline,
|
|
7109
7515
|
projectRoot
|
|
@@ -7433,7 +7839,7 @@ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET14}`);
|
|
|
7433
7839
|
if (appliedRuleFiles.length > 0) {
|
|
7434
7840
|
console.log(` ${dim3(`Rule bridge applied: ${appliedRuleFiles.join(", ")}`)}`);
|
|
7435
7841
|
}
|
|
7436
|
-
if (!
|
|
7842
|
+
if (!existsSync27(join28(projectRoot, "package.json"))) {
|
|
7437
7843
|
console.log("");
|
|
7438
7844
|
console.log(
|
|
7439
7845
|
dim3(` Note: ${cyan3("decantr init")} created Decantr contract/context files only.`)
|
|
@@ -7444,7 +7850,7 @@ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET14}`);
|
|
|
7444
7850
|
)
|
|
7445
7851
|
);
|
|
7446
7852
|
}
|
|
7447
|
-
const hasCompiledPacks =
|
|
7853
|
+
const hasCompiledPacks = existsSync27(join28(projectRoot, ".decantr", "context", "scaffold-pack.md"));
|
|
7448
7854
|
console.log("");
|
|
7449
7855
|
console.log(" Next steps:");
|
|
7450
7856
|
if (hasCompiledPacks) {
|
|
@@ -7484,7 +7890,7 @@ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET14}`);
|
|
|
7484
7890
|
console.log(` ${cyan3("decantr upgrade")} Update to latest patterns`);
|
|
7485
7891
|
console.log(` ${cyan3("decantr check")} Detect drift issues`);
|
|
7486
7892
|
console.log(` ${cyan3("decantr migrate --to v4")} Migrate older essence files to v4`);
|
|
7487
|
-
const essenceContent =
|
|
7893
|
+
const essenceContent = readFileSync20(result.essencePath, "utf-8");
|
|
7488
7894
|
const essence = JSON.parse(essenceContent);
|
|
7489
7895
|
const validation = validateEssence2(essence);
|
|
7490
7896
|
if (!validation.valid) {
|
|
@@ -7540,16 +7946,16 @@ Validation warnings: ${validation.errors.join(", ")}`));
|
|
|
7540
7946
|
}
|
|
7541
7947
|
async function cmdStatus() {
|
|
7542
7948
|
const projectRoot = process.cwd();
|
|
7543
|
-
const essencePath =
|
|
7544
|
-
const projectJsonPath =
|
|
7949
|
+
const essencePath = join28(projectRoot, "decantr.essence.json");
|
|
7950
|
+
const projectJsonPath = join28(projectRoot, ".decantr", "project.json");
|
|
7545
7951
|
console.log(heading2("Decantr Project Status"));
|
|
7546
|
-
if (!
|
|
7952
|
+
if (!existsSync27(essencePath)) {
|
|
7547
7953
|
console.log(`${RED11}No decantr.essence.json found.${RESET14}`);
|
|
7548
7954
|
console.log(dim3('Run "decantr init" to create one.'));
|
|
7549
7955
|
return;
|
|
7550
7956
|
}
|
|
7551
7957
|
try {
|
|
7552
|
-
const essence = JSON.parse(
|
|
7958
|
+
const essence = JSON.parse(readFileSync20(essencePath, "utf-8"));
|
|
7553
7959
|
const validation = validateEssence2(essence);
|
|
7554
7960
|
const essenceVersion = isV47(essence) ? "v4" : "legacy";
|
|
7555
7961
|
console.log(`${BOLD7}Essence:${RESET14}`);
|
|
@@ -7597,9 +8003,9 @@ async function cmdStatus() {
|
|
|
7597
8003
|
}
|
|
7598
8004
|
console.log("");
|
|
7599
8005
|
console.log(`${BOLD7}Sync Status:${RESET14}`);
|
|
7600
|
-
if (
|
|
8006
|
+
if (existsSync27(projectJsonPath)) {
|
|
7601
8007
|
try {
|
|
7602
|
-
const projectJson = JSON.parse(
|
|
8008
|
+
const projectJson = JSON.parse(readFileSync20(projectJsonPath, "utf-8"));
|
|
7603
8009
|
const syncStatus = projectJson.sync?.status || "unknown";
|
|
7604
8010
|
const lastSync = projectJson.sync?.lastSync || "never";
|
|
7605
8011
|
const source = projectJson.sync?.registrySource || "unknown";
|
|
@@ -7617,7 +8023,7 @@ async function cmdStatus() {
|
|
|
7617
8023
|
}
|
|
7618
8024
|
async function cmdSync() {
|
|
7619
8025
|
const projectRoot = process.cwd();
|
|
7620
|
-
const cacheDir =
|
|
8026
|
+
const cacheDir = join28(projectRoot, ".decantr", "cache");
|
|
7621
8027
|
console.log(heading2("Syncing registry content..."));
|
|
7622
8028
|
const result = await syncRegistry(cacheDir);
|
|
7623
8029
|
if (result.synced.length > 0) {
|
|
@@ -7812,14 +8218,14 @@ ${BOLD7}Examples:${RESET14}
|
|
|
7812
8218
|
process.exitCode = 1;
|
|
7813
8219
|
return;
|
|
7814
8220
|
}
|
|
7815
|
-
const themePath =
|
|
7816
|
-
if (!
|
|
8221
|
+
const themePath = join28(projectRoot, ".decantr", "custom", "themes", `${name}.json`);
|
|
8222
|
+
if (!existsSync27(themePath)) {
|
|
7817
8223
|
console.error(error3(`Theme "${name}" not found at ${themePath}`));
|
|
7818
8224
|
process.exitCode = 1;
|
|
7819
8225
|
return;
|
|
7820
8226
|
}
|
|
7821
8227
|
try {
|
|
7822
|
-
const theme = JSON.parse(
|
|
8228
|
+
const theme = JSON.parse(readFileSync20(themePath, "utf-8"));
|
|
7823
8229
|
const result = validateCustomTheme(theme);
|
|
7824
8230
|
if (result.valid) {
|
|
7825
8231
|
console.log(success3(`Custom theme "${name}" is valid`));
|
|
@@ -7887,14 +8293,483 @@ ${BOLD7}Examples:${RESET14}
|
|
|
7887
8293
|
process.exitCode = 1;
|
|
7888
8294
|
}
|
|
7889
8295
|
}
|
|
8296
|
+
function parseLooseArgs(args, startIndex = 1) {
|
|
8297
|
+
const flags = {};
|
|
8298
|
+
const positional = [];
|
|
8299
|
+
for (let index = startIndex; index < args.length; index += 1) {
|
|
8300
|
+
const arg = args[index];
|
|
8301
|
+
if (arg === "-y") {
|
|
8302
|
+
flags.yes = true;
|
|
8303
|
+
continue;
|
|
8304
|
+
}
|
|
8305
|
+
if (arg.startsWith("--no-")) {
|
|
8306
|
+
flags[arg.slice(5)] = false;
|
|
8307
|
+
continue;
|
|
8308
|
+
}
|
|
8309
|
+
if (arg.startsWith("--")) {
|
|
8310
|
+
const body = arg.slice(2);
|
|
8311
|
+
const equalsIndex = body.indexOf("=");
|
|
8312
|
+
if (equalsIndex !== -1) {
|
|
8313
|
+
flags[body.slice(0, equalsIndex)] = body.slice(equalsIndex + 1);
|
|
8314
|
+
continue;
|
|
8315
|
+
}
|
|
8316
|
+
if (args[index + 1] && !args[index + 1].startsWith("-")) {
|
|
8317
|
+
flags[body] = args[++index];
|
|
8318
|
+
} else {
|
|
8319
|
+
flags[body] = true;
|
|
8320
|
+
}
|
|
8321
|
+
continue;
|
|
8322
|
+
}
|
|
8323
|
+
positional.push(arg);
|
|
8324
|
+
}
|
|
8325
|
+
return { flags, positional };
|
|
8326
|
+
}
|
|
8327
|
+
function flagString(flags, key) {
|
|
8328
|
+
const value = flags[key];
|
|
8329
|
+
return typeof value === "string" ? value : void 0;
|
|
8330
|
+
}
|
|
8331
|
+
function flagBoolean(flags, key, defaultValue = false) {
|
|
8332
|
+
const value = flags[key];
|
|
8333
|
+
if (typeof value === "boolean") return value;
|
|
8334
|
+
if (typeof value === "string") return value !== "false";
|
|
8335
|
+
return defaultValue;
|
|
8336
|
+
}
|
|
8337
|
+
function withoutWorkflowOnlyFlags(args) {
|
|
8338
|
+
const stripped = [];
|
|
8339
|
+
const flagsWithValues = /* @__PURE__ */ new Set(["--project"]);
|
|
8340
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
8341
|
+
const arg = args[index];
|
|
8342
|
+
if (arg === "--brownfield" || arg === "--local-patterns" || arg === "--workspace" || arg === "--baseline") {
|
|
8343
|
+
continue;
|
|
8344
|
+
}
|
|
8345
|
+
if (arg.startsWith("--project=")) {
|
|
8346
|
+
continue;
|
|
8347
|
+
}
|
|
8348
|
+
if (flagsWithValues.has(arg)) {
|
|
8349
|
+
index += 1;
|
|
8350
|
+
continue;
|
|
8351
|
+
}
|
|
8352
|
+
stripped.push(arg);
|
|
8353
|
+
}
|
|
8354
|
+
return stripped;
|
|
8355
|
+
}
|
|
8356
|
+
function resolveWorkflowProject(flags) {
|
|
8357
|
+
const projectArg = flagString(flags, "project");
|
|
8358
|
+
const workspaceInfo = resolveWorkspaceInfo(process.cwd(), projectArg);
|
|
8359
|
+
if (workspaceInfo.requiresProjectSelection) {
|
|
8360
|
+
console.log(error3("This looks like a workspace root with multiple app candidates."));
|
|
8361
|
+
console.log(dim3(`Use --project=<path>. Candidates: ${workspaceInfo.appCandidates.join(", ")}`));
|
|
8362
|
+
process.exitCode = 1;
|
|
8363
|
+
return null;
|
|
8364
|
+
}
|
|
8365
|
+
return workspaceInfo;
|
|
8366
|
+
}
|
|
8367
|
+
function printWorkflowPlan(title, steps) {
|
|
8368
|
+
console.log(heading2(title));
|
|
8369
|
+
console.log(" Decantr will run this workflow:");
|
|
8370
|
+
for (const step of steps) {
|
|
8371
|
+
console.log(` ${cyan3(step)}`);
|
|
8372
|
+
}
|
|
8373
|
+
console.log("");
|
|
8374
|
+
}
|
|
8375
|
+
async function cmdSetupWorkflow(args) {
|
|
8376
|
+
const { flags } = parseLooseArgs(args);
|
|
8377
|
+
const workspaceInfo = resolveWorkflowProject(flags);
|
|
8378
|
+
if (!workspaceInfo) return;
|
|
8379
|
+
const detected = detectProject(workspaceInfo.appRoot);
|
|
8380
|
+
const hasFootprint = detected.framework !== "unknown" || detected.packageManager !== "unknown" || detected.hasTypeScript || detected.hasTailwind || detected.existingRuleFiles.length > 0;
|
|
8381
|
+
console.log(heading2("Decantr Setup"));
|
|
8382
|
+
console.log(` Project: ${workspaceInfo.appRoot}`);
|
|
8383
|
+
console.log(` Detected: ${formatDetection(detected)}`);
|
|
8384
|
+
console.log("");
|
|
8385
|
+
if (detected.existingEssence) {
|
|
8386
|
+
console.log(`${BOLD7}Recommended path:${RESET14} maintain an attached Decantr project`);
|
|
8387
|
+
console.log(` ${cyan3('decantr task <route> "<change>"')} Prepare LLM context before edits`);
|
|
8388
|
+
console.log(` ${cyan3("decantr verify")} Run local health and drift checks`);
|
|
8389
|
+
console.log(` ${cyan3("decantr codify")} Propose project-owned UI patterns`);
|
|
8390
|
+
return;
|
|
8391
|
+
}
|
|
8392
|
+
if (hasFootprint) {
|
|
8393
|
+
console.log(`${BOLD7}Recommended path:${RESET14} brownfield adoption`);
|
|
8394
|
+
console.log(` ${cyan3("decantr adopt --yes")} Analyze, attach, and verify`);
|
|
8395
|
+
console.log(
|
|
8396
|
+
` ${cyan3("decantr adopt --base-url http://localhost:3000 --evidence --yes")} Include visual evidence`
|
|
8397
|
+
);
|
|
8398
|
+
console.log(` ${cyan3("decantr codify")} Propose local UI law`);
|
|
8399
|
+
return;
|
|
8400
|
+
}
|
|
8401
|
+
console.log(`${BOLD7}Recommended path:${RESET14} greenfield start`);
|
|
8402
|
+
console.log(` ${cyan3("decantr new my-app --blueprint=<slug>")}`);
|
|
8403
|
+
console.log(` ${cyan3("decantr init --workflow=greenfield --adoption=contract-only")}`);
|
|
8404
|
+
}
|
|
8405
|
+
async function cmdAdoptWorkflow(args) {
|
|
8406
|
+
const { flags } = parseLooseArgs(args);
|
|
8407
|
+
const workspaceInfo = resolveWorkflowProject(flags);
|
|
8408
|
+
if (!workspaceInfo) return;
|
|
8409
|
+
const projectRoot = workspaceInfo.appRoot;
|
|
8410
|
+
const dryRun = flagBoolean(flags, "dry-run");
|
|
8411
|
+
const yes = flagBoolean(flags, "yes") || flagBoolean(flags, "y");
|
|
8412
|
+
const baseUrl = flagString(flags, "base-url");
|
|
8413
|
+
const runVerify = flagBoolean(flags, "verify", true);
|
|
8414
|
+
const runBrowser = flagBoolean(flags, "browser") || Boolean(baseUrl);
|
|
8415
|
+
const evidence = flagBoolean(flags, "evidence") || runBrowser;
|
|
8416
|
+
const saveBaseline = flagBoolean(flags, "baseline", true) || flagBoolean(flags, "save-baseline");
|
|
8417
|
+
const initCi = flagBoolean(flags, "ci") || flagBoolean(flags, "init-ci");
|
|
8418
|
+
const assistantBridge = flagString(flags, "assistant-bridge");
|
|
8419
|
+
const hasEssence = existsSync27(join28(projectRoot, "decantr.essence.json"));
|
|
8420
|
+
const proposalFlag = flagBoolean(flags, "replace-essence") ? "--replace-essence" : flagBoolean(flags, "merge-proposal") || hasEssence ? "--merge-proposal" : "--accept-proposal";
|
|
8421
|
+
const steps = [
|
|
8422
|
+
"analyze current app and write .decantr/brownfield intelligence",
|
|
8423
|
+
`init --existing ${proposalFlag} as contract-only Brownfield`
|
|
8424
|
+
];
|
|
8425
|
+
if (runVerify) {
|
|
8426
|
+
steps.push(
|
|
8427
|
+
runBrowser ? "verify with Project Health, browser evidence, visual manifest, and baseline" : "verify with Project Health and baseline"
|
|
8428
|
+
);
|
|
8429
|
+
}
|
|
8430
|
+
if (initCi) {
|
|
8431
|
+
steps.push("install Project Health CI gate");
|
|
8432
|
+
}
|
|
8433
|
+
printWorkflowPlan("Decantr Adopt", steps);
|
|
8434
|
+
if (dryRun) {
|
|
8435
|
+
console.log(dim3("Dry run only. No files were written."));
|
|
8436
|
+
return;
|
|
8437
|
+
}
|
|
8438
|
+
if (!yes) {
|
|
8439
|
+
const ok = await confirm("Run this Brownfield adoption workflow?", false);
|
|
8440
|
+
if (!ok) {
|
|
8441
|
+
console.log(dim3("Cancelled."));
|
|
8442
|
+
return;
|
|
8443
|
+
}
|
|
8444
|
+
}
|
|
8445
|
+
await cmdAnalyze(projectRoot, workspaceInfo);
|
|
8446
|
+
if (process.exitCode && process.exitCode !== 0) return;
|
|
8447
|
+
await cmdInit({
|
|
8448
|
+
existing: true,
|
|
8449
|
+
yes: true,
|
|
8450
|
+
project: flagString(flags, "project"),
|
|
8451
|
+
"accept-proposal": proposalFlag === "--accept-proposal",
|
|
8452
|
+
"merge-proposal": proposalFlag === "--merge-proposal",
|
|
8453
|
+
"replace-essence": proposalFlag === "--replace-essence",
|
|
8454
|
+
"assistant-bridge": assistantBridge,
|
|
8455
|
+
telemetry: flagBoolean(flags, "telemetry")
|
|
8456
|
+
});
|
|
8457
|
+
if (process.exitCode && process.exitCode !== 0) return;
|
|
8458
|
+
if (runVerify) {
|
|
8459
|
+
const { cmdHealth } = await import("./health-ETZXWGTW.js");
|
|
8460
|
+
await cmdHealth(projectRoot, {
|
|
8461
|
+
browser: runBrowser,
|
|
8462
|
+
browserBaseUrl: baseUrl,
|
|
8463
|
+
evidence,
|
|
8464
|
+
output: evidence ? ".decantr/evidence/latest.json" : void 0,
|
|
8465
|
+
saveBaseline
|
|
8466
|
+
});
|
|
8467
|
+
}
|
|
8468
|
+
if (initCi) {
|
|
8469
|
+
const { cmdHealth } = await import("./health-ETZXWGTW.js");
|
|
8470
|
+
const ciRoot = flagString(flags, "project") ? process.cwd() : projectRoot;
|
|
8471
|
+
await cmdHealth(ciRoot, {
|
|
8472
|
+
initCi: {
|
|
8473
|
+
projectPath: flagString(flags, "project"),
|
|
8474
|
+
failOn: "error"
|
|
8475
|
+
}
|
|
8476
|
+
});
|
|
8477
|
+
}
|
|
8478
|
+
console.log("");
|
|
8479
|
+
console.log(`${BOLD7}Next useful commands:${RESET14}`);
|
|
8480
|
+
console.log(` ${cyan3('decantr task <route> "<change>"')} Give your LLM route-specific context`);
|
|
8481
|
+
console.log(` ${cyan3("decantr codify")} Propose project-owned UI patterns`);
|
|
8482
|
+
console.log(` ${cyan3("decantr verify --since-baseline")} Compare future work against this baseline`);
|
|
8483
|
+
}
|
|
8484
|
+
async function cmdVerifyWorkflow(args) {
|
|
8485
|
+
const { flags } = parseLooseArgs(args);
|
|
8486
|
+
const workspaceMode = flagBoolean(flags, "workspace");
|
|
8487
|
+
if (args[1] === "init-ci") {
|
|
8488
|
+
const { cmdHealth: cmdHealth2, parseHealthArgs: parseHealthArgs2 } = await import("./health-ETZXWGTW.js");
|
|
8489
|
+
await cmdHealth2(process.cwd(), parseHealthArgs2(["health", ...args.slice(1)]));
|
|
8490
|
+
return;
|
|
8491
|
+
}
|
|
8492
|
+
if (workspaceMode) {
|
|
8493
|
+
const { cmdWorkspace } = await import("./workspace-KSFWRZEX.js");
|
|
8494
|
+
await cmdWorkspace(process.cwd(), ["workspace", "health", ...withoutWorkflowOnlyFlags(args)]);
|
|
8495
|
+
return;
|
|
8496
|
+
}
|
|
8497
|
+
const workspaceInfo = resolveWorkflowProject(flags);
|
|
8498
|
+
if (!workspaceInfo) return;
|
|
8499
|
+
const brownfield = flagBoolean(flags, "brownfield");
|
|
8500
|
+
const localPatterns = flagBoolean(flags, "local-patterns");
|
|
8501
|
+
const evidence = flagBoolean(flags, "evidence");
|
|
8502
|
+
const baseUrl = flagString(flags, "base-url");
|
|
8503
|
+
const healthArgs = ["health", ...withoutWorkflowOnlyFlags(args)];
|
|
8504
|
+
if (flagBoolean(flags, "baseline") && !healthArgs.includes("--save-baseline")) {
|
|
8505
|
+
healthArgs.push("--save-baseline");
|
|
8506
|
+
}
|
|
8507
|
+
if (evidence && !flagString(flags, "output")) {
|
|
8508
|
+
healthArgs.push("--output", ".decantr/evidence/latest.json");
|
|
8509
|
+
}
|
|
8510
|
+
if (baseUrl && !healthArgs.includes("--browser")) {
|
|
8511
|
+
healthArgs.push("--browser");
|
|
8512
|
+
}
|
|
8513
|
+
const quietOutput = flagBoolean(flags, "json") || flagBoolean(flags, "ci") || Boolean(flagString(flags, "output"));
|
|
8514
|
+
if (!quietOutput) {
|
|
8515
|
+
console.log(heading2("Decantr Verify"));
|
|
8516
|
+
console.log(
|
|
8517
|
+
dim3(
|
|
8518
|
+
brownfield ? "Running Brownfield guard validation before Project Health." : "Running Project Health as the canonical reliability gate."
|
|
8519
|
+
)
|
|
8520
|
+
);
|
|
8521
|
+
console.log("");
|
|
8522
|
+
}
|
|
8523
|
+
let guardExitCode;
|
|
8524
|
+
if (brownfield) {
|
|
8525
|
+
const { cmdHeal, collectCheckIssues } = await import("./heal-ZYD6NVGE.js");
|
|
8526
|
+
if (quietOutput) {
|
|
8527
|
+
const result = collectCheckIssues(workspaceInfo.appRoot, { brownfield: true });
|
|
8528
|
+
guardExitCode = result.issues.some((issue) => issue.type === "error") ? 1 : void 0;
|
|
8529
|
+
} else {
|
|
8530
|
+
await cmdHeal(workspaceInfo.appRoot, { brownfield: true });
|
|
8531
|
+
guardExitCode = process.exitCode;
|
|
8532
|
+
process.exitCode = void 0;
|
|
8533
|
+
}
|
|
8534
|
+
}
|
|
8535
|
+
const { cmdHealth, parseHealthArgs } = await import("./health-ETZXWGTW.js");
|
|
8536
|
+
await cmdHealth(workspaceInfo.appRoot, parseHealthArgs(healthArgs));
|
|
8537
|
+
if (localPatterns) {
|
|
8538
|
+
const localPatternsPath = join28(workspaceInfo.appRoot, ".decantr", "local-patterns.json");
|
|
8539
|
+
if (!existsSync27(localPatternsPath)) {
|
|
8540
|
+
console.log("");
|
|
8541
|
+
console.log(
|
|
8542
|
+
`${YELLOW9}Local pattern pack missing.${RESET14} Run ${cyan3("decantr codify --accept")} after reviewing the proposal.`
|
|
8543
|
+
);
|
|
8544
|
+
process.exitCode = process.exitCode || 1;
|
|
8545
|
+
} else {
|
|
8546
|
+
console.log("");
|
|
8547
|
+
console.log(`${GREEN14}Local pattern pack found:${RESET14} ${localPatternsPath}`);
|
|
8548
|
+
}
|
|
8549
|
+
}
|
|
8550
|
+
if (guardExitCode && guardExitCode !== 0 && (!process.exitCode || process.exitCode === 0)) {
|
|
8551
|
+
process.exitCode = guardExitCode;
|
|
8552
|
+
}
|
|
8553
|
+
}
|
|
8554
|
+
function readJsonIfPresent(path) {
|
|
8555
|
+
if (!existsSync27(path)) return null;
|
|
8556
|
+
try {
|
|
8557
|
+
return JSON.parse(readFileSync20(path, "utf-8"));
|
|
8558
|
+
} catch {
|
|
8559
|
+
return null;
|
|
8560
|
+
}
|
|
8561
|
+
}
|
|
8562
|
+
async function cmdTaskWorkflow(args) {
|
|
8563
|
+
const { flags, positional } = parseLooseArgs(args);
|
|
8564
|
+
const workspaceInfo = resolveWorkflowProject(flags);
|
|
8565
|
+
if (!workspaceInfo) return;
|
|
8566
|
+
const routeInput = positional[0];
|
|
8567
|
+
if (!routeInput) {
|
|
8568
|
+
console.error(error3('Usage: decantr task <route> ["task summary"] [--project <path>] [--json]'));
|
|
8569
|
+
process.exitCode = 1;
|
|
8570
|
+
return;
|
|
8571
|
+
}
|
|
8572
|
+
const route = routeInput.startsWith("/") ? routeInput : `/${routeInput}`;
|
|
8573
|
+
const taskSummary = positional.slice(1).join(" ").trim();
|
|
8574
|
+
const essencePath = join28(workspaceInfo.appRoot, "decantr.essence.json");
|
|
8575
|
+
const essence = readJsonIfPresent(essencePath);
|
|
8576
|
+
if (!essence) {
|
|
8577
|
+
console.error(error3("No decantr.essence.json found. Run `decantr adopt` or `decantr init` first."));
|
|
8578
|
+
process.exitCode = 1;
|
|
8579
|
+
return;
|
|
8580
|
+
}
|
|
8581
|
+
if (!isV47(essence)) {
|
|
8582
|
+
console.error(error3("Task context requires Essence v4. Run `decantr migrate --to v4` first."));
|
|
8583
|
+
process.exitCode = 1;
|
|
8584
|
+
return;
|
|
8585
|
+
}
|
|
8586
|
+
const target = essence.blueprint.routes?.[route];
|
|
8587
|
+
if (!target) {
|
|
8588
|
+
const knownRoutes = Object.keys(essence.blueprint.routes ?? {}).sort();
|
|
8589
|
+
console.error(error3(`Route not found in Decantr contract: ${route}`));
|
|
8590
|
+
console.error(dim3(`Known routes: ${knownRoutes.join(", ") || "none"}`));
|
|
8591
|
+
process.exitCode = 1;
|
|
8592
|
+
return;
|
|
8593
|
+
}
|
|
8594
|
+
const section = essence.blueprint.sections.find((entry) => entry.id === target.section);
|
|
8595
|
+
const page = section?.pages.find((entry) => entry.id === target.page);
|
|
8596
|
+
const contextDir = join28(workspaceInfo.appRoot, ".decantr", "context");
|
|
8597
|
+
const manifest = readJsonIfPresent(join28(contextDir, "pack-manifest.json"));
|
|
8598
|
+
const pagePack = manifest?.pages?.find((entry) => entry.id === target.page);
|
|
8599
|
+
const sectionPack = manifest?.sections?.find((entry) => entry.id === target.section);
|
|
8600
|
+
const visualManifest = readJsonIfPresent(join28(workspaceInfo.appRoot, ".decantr", "evidence", "visual-manifest.json"));
|
|
8601
|
+
const screenshot = visualManifest?.routes?.find((entry) => entry.route === route)?.screenshot;
|
|
8602
|
+
const localPatternsPath = join28(workspaceInfo.appRoot, ".decantr", "local-patterns.json");
|
|
8603
|
+
const context = {
|
|
8604
|
+
route,
|
|
8605
|
+
task: taskSummary || null,
|
|
8606
|
+
section: target.section,
|
|
8607
|
+
page: target.page,
|
|
8608
|
+
shell: page?.shell ?? section?.shell ?? null,
|
|
8609
|
+
patterns: page?.layout?.map(extractPatternName) ?? [],
|
|
8610
|
+
read: [
|
|
8611
|
+
pagePack ? join28(".decantr/context", pagePack.markdown) : null,
|
|
8612
|
+
sectionPack ? join28(".decantr/context", sectionPack.markdown) : null,
|
|
8613
|
+
manifest?.scaffold?.markdown ? join28(".decantr/context", manifest.scaffold.markdown) : null,
|
|
8614
|
+
".decantr/context/scaffold.md",
|
|
8615
|
+
"DECANTR.md",
|
|
8616
|
+
existsSync27(localPatternsPath) ? ".decantr/local-patterns.json" : null
|
|
8617
|
+
].filter(Boolean),
|
|
8618
|
+
screenshot: screenshot ?? null
|
|
8619
|
+
};
|
|
8620
|
+
if (flagBoolean(flags, "json")) {
|
|
8621
|
+
console.log(JSON.stringify(context, null, 2));
|
|
8622
|
+
return;
|
|
8623
|
+
}
|
|
8624
|
+
console.log(heading2("Decantr Task Context"));
|
|
8625
|
+
console.log(` Route: ${cyan3(context.route)}`);
|
|
8626
|
+
console.log(` Section/page: ${context.section}/${context.page}`);
|
|
8627
|
+
if (context.shell) console.log(` Shell: ${context.shell}`);
|
|
8628
|
+
if (context.patterns.length > 0) console.log(` Patterns: ${context.patterns.join(", ")}`);
|
|
8629
|
+
if (taskSummary) console.log(` Task: ${taskSummary}`);
|
|
8630
|
+
console.log("");
|
|
8631
|
+
console.log(`${BOLD7}Read before editing:${RESET14}`);
|
|
8632
|
+
for (const path of context.read) {
|
|
8633
|
+
console.log(` ${cyan3(path)}`);
|
|
8634
|
+
}
|
|
8635
|
+
if (context.screenshot) {
|
|
8636
|
+
console.log("");
|
|
8637
|
+
console.log(`${BOLD7}Visual evidence:${RESET14}`);
|
|
8638
|
+
console.log(` ${cyan3(context.screenshot)}`);
|
|
8639
|
+
}
|
|
8640
|
+
console.log("");
|
|
8641
|
+
console.log(`${BOLD7}LLM instruction:${RESET14}`);
|
|
8642
|
+
console.log(
|
|
8643
|
+
" Preserve the existing runtime and styling system. Use the route pack, section context, local patterns, and visual evidence above as the task contract before changing code."
|
|
8644
|
+
);
|
|
8645
|
+
}
|
|
8646
|
+
async function cmdCodifyWorkflow(args) {
|
|
8647
|
+
const { flags } = parseLooseArgs(args);
|
|
8648
|
+
const workspaceInfo = resolveWorkflowProject(flags);
|
|
8649
|
+
if (!workspaceInfo) return;
|
|
8650
|
+
const decantrDir = join28(workspaceInfo.appRoot, ".decantr");
|
|
8651
|
+
const proposalPathLocal = join28(decantrDir, "local-patterns.proposal.json");
|
|
8652
|
+
const acceptedPath = join28(decantrDir, "local-patterns.json");
|
|
8653
|
+
if (flagBoolean(flags, "accept")) {
|
|
8654
|
+
if (!existsSync27(proposalPathLocal)) {
|
|
8655
|
+
console.error(error3("No .decantr/local-patterns.proposal.json found. Run `decantr codify` first."));
|
|
8656
|
+
process.exitCode = 1;
|
|
8657
|
+
return;
|
|
8658
|
+
}
|
|
8659
|
+
writeFileSync16(acceptedPath, readFileSync20(proposalPathLocal, "utf-8"), "utf-8");
|
|
8660
|
+
console.log(success3(`Accepted local pattern pack: ${acceptedPath}`));
|
|
8661
|
+
console.log(dim3("Run `decantr verify --local-patterns` to require the pack during verification."));
|
|
8662
|
+
return;
|
|
8663
|
+
}
|
|
8664
|
+
mkdirSync13(decantrDir, { recursive: true });
|
|
8665
|
+
const detected = detectProject(workspaceInfo.appRoot);
|
|
8666
|
+
const essence = readJsonIfPresent(join28(workspaceInfo.appRoot, "decantr.essence.json"));
|
|
8667
|
+
const routes = essence && isV47(essence) ? Object.keys(essence.blueprint.routes ?? {}).sort() : [];
|
|
8668
|
+
const proposal = {
|
|
8669
|
+
version: 1,
|
|
8670
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8671
|
+
status: "proposal",
|
|
8672
|
+
source: "decantr codify",
|
|
8673
|
+
project: {
|
|
8674
|
+
framework: detected.framework,
|
|
8675
|
+
packageManager: detected.packageManager,
|
|
8676
|
+
hasTailwind: detected.hasTailwind,
|
|
8677
|
+
ruleFiles: detected.existingRuleFiles,
|
|
8678
|
+
routeCount: routes.length
|
|
8679
|
+
},
|
|
8680
|
+
purpose: "Project-owned Brownfield UI law. Review and edit before accepting; Decantr does not treat this as authoritative until copied to .decantr/local-patterns.json.",
|
|
8681
|
+
patterns: [
|
|
8682
|
+
{
|
|
8683
|
+
id: "button",
|
|
8684
|
+
role: "Actions and command triggers",
|
|
8685
|
+
decide: "Define primary, secondary, tertiary, destructive, icon-only, and loading button variants from this app.",
|
|
8686
|
+
evidenceToCollect: ["component wrapper path", "allowed classes/tokens", "forbidden raw <button> usage"]
|
|
8687
|
+
},
|
|
8688
|
+
{
|
|
8689
|
+
id: "surface-card",
|
|
8690
|
+
role: "Cards, panels, and content surfaces",
|
|
8691
|
+
decide: "Define the canonical card background, border, radius, shadow, padding, and hover treatment.",
|
|
8692
|
+
evidenceToCollect: ["shared card component", "token/class recipe", "allowed density variants"]
|
|
8693
|
+
},
|
|
8694
|
+
{
|
|
8695
|
+
id: "page-shell",
|
|
8696
|
+
role: "Route shell, nav, spacing, and scroll ownership",
|
|
8697
|
+
decide: "Define which layout owns max width, gutters, sticky chrome, and scroll containers.",
|
|
8698
|
+
evidenceToCollect: ["root layout path", "page template path", "responsive breakpoints"]
|
|
8699
|
+
},
|
|
8700
|
+
{
|
|
8701
|
+
id: "form-control",
|
|
8702
|
+
role: "Inputs, labels, validation, and form actions",
|
|
8703
|
+
decide: "Define input height, label placement, error copy, disabled state, and focus treatment.",
|
|
8704
|
+
evidenceToCollect: ["form field wrapper", "validation pattern", "accessibility expectations"]
|
|
8705
|
+
}
|
|
8706
|
+
],
|
|
8707
|
+
starterRules: [
|
|
8708
|
+
"Prefer project-owned wrappers for repeated primitives once they exist.",
|
|
8709
|
+
"Avoid raw hex/rgb values in component templates unless explicitly documented as dynamic data.",
|
|
8710
|
+
"Avoid static inline styles for reusable visual treatment.",
|
|
8711
|
+
"When adding a new route, map it to an existing local pattern before inventing a new visual variant."
|
|
8712
|
+
],
|
|
8713
|
+
nextSteps: [
|
|
8714
|
+
"Edit this proposal with real component paths and token/class recipes.",
|
|
8715
|
+
"Run decantr codify --accept after review.",
|
|
8716
|
+
"Use decantr task <route> before LLM edits so local patterns appear in the task context.",
|
|
8717
|
+
"Wire mechanical enforcement through ESLint/Biome/project tests for rules Decantr cannot reliably infer."
|
|
8718
|
+
]
|
|
8719
|
+
};
|
|
8720
|
+
writeFileSync16(proposalPathLocal, JSON.stringify(proposal, null, 2) + "\n", "utf-8");
|
|
8721
|
+
console.log(success3(`Wrote local pattern proposal: ${proposalPathLocal}`));
|
|
8722
|
+
console.log(dim3("Review it, add real component paths/token recipes, then run `decantr codify --accept`."));
|
|
8723
|
+
}
|
|
8724
|
+
async function cmdContentWorkflow(args) {
|
|
8725
|
+
const subcommand = args[1] ?? "check";
|
|
8726
|
+
if (subcommand === "check" || subcommand === "health") {
|
|
8727
|
+
const { cmdContentHealth, parseContentHealthArgs } = await import("./content-health-QQHBR6XG.js");
|
|
8728
|
+
await cmdContentHealth(process.cwd(), parseContentHealthArgs(["content-health", ...args.slice(2)]));
|
|
8729
|
+
return;
|
|
8730
|
+
}
|
|
8731
|
+
if (subcommand === "create") {
|
|
8732
|
+
const type = args[2];
|
|
8733
|
+
const name = args[3];
|
|
8734
|
+
if (!type || !name) {
|
|
8735
|
+
console.error(error3("Usage: decantr content create <type> <name>"));
|
|
8736
|
+
process.exitCode = 1;
|
|
8737
|
+
return;
|
|
8738
|
+
}
|
|
8739
|
+
cmdCreate(type, name);
|
|
8740
|
+
return;
|
|
8741
|
+
}
|
|
8742
|
+
if (subcommand === "publish") {
|
|
8743
|
+
const type = args[2];
|
|
8744
|
+
const name = args[3];
|
|
8745
|
+
if (!type || !name) {
|
|
8746
|
+
console.error(error3("Usage: decantr content publish <type> <name>"));
|
|
8747
|
+
process.exitCode = 1;
|
|
8748
|
+
return;
|
|
8749
|
+
}
|
|
8750
|
+
await cmdPublish(type, name);
|
|
8751
|
+
return;
|
|
8752
|
+
}
|
|
8753
|
+
console.error(error3("Usage: decantr content <check|create|publish>"));
|
|
8754
|
+
process.exitCode = 1;
|
|
8755
|
+
}
|
|
7890
8756
|
function cmdHelp() {
|
|
7891
8757
|
console.log(`
|
|
7892
8758
|
${BOLD7}decantr${RESET14} \u2014 Design intelligence for AI-generated UI
|
|
7893
8759
|
|
|
7894
8760
|
${BOLD7}Usage:${RESET14}
|
|
8761
|
+
decantr setup [--project <path>]
|
|
7895
8762
|
decantr new <name> [--blueprint=X] [--archetype=X] [--theme=X] [--workflow=greenfield] [--adoption=decantr-css] [--telemetry]
|
|
7896
|
-
decantr
|
|
8763
|
+
decantr adopt [--project <path>] [--base-url <url>] [--evidence] [--ci] [--yes]
|
|
8764
|
+
decantr task <route> ["task summary"] [--project <path>] [--json]
|
|
8765
|
+
decantr verify [--project <path>] [--brownfield] [--local-patterns] [health options]
|
|
8766
|
+
decantr codify [--accept] [--project <path>]
|
|
8767
|
+
decantr studio [--port 4319] [--host 127.0.0.1] [--report decantr-health.json] [--workspace]
|
|
8768
|
+
|
|
8769
|
+
${BOLD7}Advanced primitives:${RESET14}
|
|
7897
8770
|
decantr init [options]
|
|
8771
|
+
decantr analyze
|
|
8772
|
+
decantr magic <prompt> [--dry-run]
|
|
7898
8773
|
decantr status
|
|
7899
8774
|
decantr sync
|
|
7900
8775
|
decantr audit [file]
|
|
@@ -7903,7 +8778,7 @@ ${BOLD7}Usage:${RESET14}
|
|
|
7903
8778
|
decantr check --brownfield
|
|
7904
8779
|
decantr sync-drift
|
|
7905
8780
|
decantr search <query> [--type <type>] [--sort <recommended|recent|name>] [--recommended] [--source <authored|benchmark|hybrid>]
|
|
7906
|
-
decantr suggest <query> [--type <type>]
|
|
8781
|
+
decantr suggest <query> [--type <type>] [--route <route>] [--file <path>] [--from-code]
|
|
7907
8782
|
decantr get <type> <id>
|
|
7908
8783
|
decantr list <type> [--sort <recommended|recent|name>] [--recommended] [--source <authored|benchmark|hybrid>]
|
|
7909
8784
|
decantr showcase [manifest|shortlist|verification] [--json]
|
|
@@ -7913,12 +8788,13 @@ ${BOLD7}Usage:${RESET14}
|
|
|
7913
8788
|
decantr registry critique-file <file> [--namespace <namespace>] [--json] [--essence <path>] [--treatments <path>]
|
|
7914
8789
|
decantr registry audit-project [--namespace <namespace>] [--json] [--essence <path>] [--dist <path>] [--sources <dir>]
|
|
7915
8790
|
decantr health [--format text|json|markdown] [--ci] [--fail-on error|warn|none]
|
|
7916
|
-
decantr health --evidence [--browser] [--design-tokens <path>]
|
|
8791
|
+
decantr health --evidence [--browser] [--base-url <url>] [--design-tokens <path>]
|
|
8792
|
+
decantr health --save-baseline | --since-baseline
|
|
7917
8793
|
decantr health init-ci [--force] [--project <path>] [--workspace] [--fail-on <error|warn|none>] [--cli-version <version|latest>]
|
|
7918
8794
|
decantr workspace list [--json]
|
|
7919
8795
|
decantr workspace health [--json] [--changed --since origin/main]
|
|
8796
|
+
decantr content check [--json] [--markdown] [--ci]
|
|
7920
8797
|
decantr content-health [--json] [--markdown] [--ci]
|
|
7921
|
-
decantr studio [--port 4319] [--host 127.0.0.1] [--report decantr-health.json] [--workspace]
|
|
7922
8798
|
decantr telemetry status [--json]
|
|
7923
8799
|
decantr telemetry explain [--json]
|
|
7924
8800
|
decantr telemetry link [--enable] [--org <slug>]
|
|
@@ -7928,7 +8804,6 @@ ${BOLD7}Usage:${RESET14}
|
|
|
7928
8804
|
decantr theme <subcommand>
|
|
7929
8805
|
decantr create <type> <name>
|
|
7930
8806
|
decantr publish <type> <name>
|
|
7931
|
-
decantr analyze
|
|
7932
8807
|
decantr login
|
|
7933
8808
|
decantr logout
|
|
7934
8809
|
decantr help
|
|
@@ -7956,14 +8831,22 @@ ${BOLD7}Init Options:${RESET14}
|
|
|
7956
8831
|
--telemetry Opt this project into privacy-filtered CLI product telemetry
|
|
7957
8832
|
|
|
7958
8833
|
${BOLD7}Commands:${RESET14}
|
|
8834
|
+
${cyan3("setup")} Detect project state and recommend the right Decantr workflow
|
|
7959
8835
|
${cyan3("new")} Create a new greenfield workspace and bootstrap the available starter adapter
|
|
8836
|
+
${cyan3("adopt")} Brownfield one-liner: analyze, attach, verify, and show next steps
|
|
8837
|
+
${cyan3("task")} Prepare route/task context for an AI coding assistant
|
|
8838
|
+
${cyan3("verify")} One reliability gate over Project Health, Brownfield checks, baselines, and evidence
|
|
8839
|
+
${cyan3("codify")} Propose or accept project-owned Brownfield UI patterns
|
|
8840
|
+
${cyan3("studio")} Open a local Project Health dashboard backed by the same report
|
|
8841
|
+
${cyan3("content")} Content-author namespace: check, create, publish
|
|
8842
|
+
|
|
8843
|
+
${BOLD7}Advanced commands:${RESET14}
|
|
7960
8844
|
${cyan3("magic")} Greenfield-first intent flow; steers existing apps into analyze + init
|
|
7961
8845
|
${cyan3("init")} Attach Decantr contract/context files to an existing project or empty workspace
|
|
7962
8846
|
${cyan3("status")} Show project status, DNA axioms, and blueprint info
|
|
7963
8847
|
${cyan3("health")} Generate a local Project Health report [--json] [--markdown] [--ci]; use health init-ci to install a GitHub Actions gate
|
|
7964
8848
|
${cyan3("workspace")} Discover and aggregate health across Decantr projects in a monorepo
|
|
7965
8849
|
${cyan3("content-health")} Generate a local registry content health report [--json] [--markdown] [--ci]
|
|
7966
|
-
${cyan3("studio")} Open a local Project Health dashboard backed by the same report
|
|
7967
8850
|
${cyan3("sync")} Sync registry content from API
|
|
7968
8851
|
${cyan3("audit")} Audit the project or critique a specific file against compiled packs
|
|
7969
8852
|
${cyan3("migrate")} Migrate older essence files to v4 format (with .pre-v4.backup.json backup)
|
|
@@ -7989,7 +8872,15 @@ ${BOLD7}Commands:${RESET14}
|
|
|
7989
8872
|
${cyan3("help")} Show this help
|
|
7990
8873
|
|
|
7991
8874
|
${BOLD7}Examples:${RESET14}
|
|
8875
|
+
decantr setup
|
|
7992
8876
|
decantr new my-app --blueprint=carbon-ai-portal
|
|
8877
|
+
decantr adopt --base-url http://localhost:3000 --evidence --yes
|
|
8878
|
+
decantr task /feed "add saved recipe actions"
|
|
8879
|
+
decantr verify --brownfield --local-patterns
|
|
8880
|
+
decantr verify --since-baseline
|
|
8881
|
+
decantr codify
|
|
8882
|
+
decantr codify --accept
|
|
8883
|
+
decantr content check --ci --fail-on error
|
|
7993
8884
|
decantr magic "AI chatbot with dark cyber theme \u2014 bold and futuristic"
|
|
7994
8885
|
decantr init
|
|
7995
8886
|
decantr analyze
|
|
@@ -8002,13 +8893,13 @@ ${BOLD7}Examples:${RESET14}
|
|
|
8002
8893
|
decantr rules apply
|
|
8003
8894
|
decantr status
|
|
8004
8895
|
decantr health
|
|
8005
|
-
decantr
|
|
8006
|
-
decantr
|
|
8007
|
-
decantr
|
|
8896
|
+
decantr verify init-ci
|
|
8897
|
+
decantr verify init-ci --project apps/web
|
|
8898
|
+
decantr verify --ci --fail-on error
|
|
8008
8899
|
decantr health --evidence --output .decantr/evidence/latest.json
|
|
8009
8900
|
decantr workspace list
|
|
8010
|
-
decantr workspace
|
|
8011
|
-
decantr content
|
|
8901
|
+
decantr verify --workspace --changed --since origin/main
|
|
8902
|
+
decantr content check --ci --fail-on error
|
|
8012
8903
|
decantr studio
|
|
8013
8904
|
decantr studio --report decantr-health.json
|
|
8014
8905
|
decantr telemetry status
|
|
@@ -8020,7 +8911,7 @@ ${BOLD7}Examples:${RESET14}
|
|
|
8020
8911
|
decantr check --brownfield
|
|
8021
8912
|
decantr sync-drift
|
|
8022
8913
|
decantr search dashboard
|
|
8023
|
-
decantr suggest
|
|
8914
|
+
decantr suggest "recipe feed with infinite scroll" --route /feed --from-code
|
|
8024
8915
|
decantr list patterns
|
|
8025
8916
|
decantr showcase shortlist
|
|
8026
8917
|
decantr showcase verification --json
|
|
@@ -8037,7 +8928,9 @@ ${BOLD7}Examples:${RESET14}
|
|
|
8037
8928
|
${BOLD7}Workflow Model:${RESET14}
|
|
8038
8929
|
${cyan3("Greenfield blueprint")} decantr new my-app --blueprint=X --workflow=greenfield --adoption=decantr-css
|
|
8039
8930
|
${cyan3("Greenfield contract")} decantr init --workflow=greenfield --adoption=contract-only
|
|
8040
|
-
${cyan3("Brownfield adoption")} decantr
|
|
8931
|
+
${cyan3("Brownfield adoption")} decantr adopt --base-url <url> --evidence --yes
|
|
8932
|
+
${cyan3("Daily LLM work")} decantr task <route> "<change>" -> decantr verify
|
|
8933
|
+
${cyan3("Project-owned law")} decantr codify -> edit proposal -> decantr codify --accept
|
|
8041
8934
|
${cyan3("Hybrid composition")} decantr add/remove, decantr theme switch, decantr registry, decantr upgrade
|
|
8042
8935
|
|
|
8043
8936
|
${BOLD7}Bootstrap adapters:${RESET14}
|
|
@@ -8077,6 +8970,9 @@ ${BOLD7}Usage:${RESET14}
|
|
|
8077
8970
|
decantr health --ci [--fail-on error|warn|none]
|
|
8078
8971
|
decantr health --prompt <finding-id>
|
|
8079
8972
|
decantr health --evidence [--browser] [--design-tokens <path>]
|
|
8973
|
+
decantr health --browser --base-url <url> --evidence
|
|
8974
|
+
decantr health --save-baseline
|
|
8975
|
+
decantr health --since-baseline
|
|
8080
8976
|
decantr health init-ci [--force] [--project <path>] [--workspace] [--fail-on error|warn|none] [--cli-version <version|latest>]
|
|
8081
8977
|
|
|
8082
8978
|
${BOLD7}Options:${RESET14}
|
|
@@ -8089,6 +8985,9 @@ ${BOLD7}Options:${RESET14}
|
|
|
8089
8985
|
--prompt Print an AI-ready remediation prompt for a finding
|
|
8090
8986
|
--evidence Emit a local Evidence Bundle JSON artifact
|
|
8091
8987
|
--browser Include optional rendered-browser setup/evidence checks
|
|
8988
|
+
--base-url Base URL for rendered route checks when --browser is enabled
|
|
8989
|
+
--save-baseline Save the current health state for later comparison
|
|
8990
|
+
--since-baseline Compare this run to .decantr/health-baseline.json
|
|
8092
8991
|
--design-tokens Compare against a Figma/Tokens Studio JSON export
|
|
8093
8992
|
|
|
8094
8993
|
${BOLD7}Examples:${RESET14}
|
|
@@ -8172,6 +9071,179 @@ ${BOLD7}Examples:${RESET14}
|
|
|
8172
9071
|
decantr studio --workspace
|
|
8173
9072
|
`);
|
|
8174
9073
|
}
|
|
9074
|
+
function cmdRegistryHelp() {
|
|
9075
|
+
console.log(`
|
|
9076
|
+
${BOLD7}decantr registry${RESET14} \u2014 Read hosted execution packs and registry intelligence
|
|
9077
|
+
|
|
9078
|
+
${BOLD7}Usage:${RESET14}
|
|
9079
|
+
decantr registry summary [--namespace <namespace>] [--json]
|
|
9080
|
+
decantr registry compile-packs [path] [--namespace <namespace>] [--json] [--write-context]
|
|
9081
|
+
decantr registry get-pack <manifest|scaffold|review|section|page|mutation> [id] [--namespace <namespace>] [--json] [--essence <path>] [--write-context]
|
|
9082
|
+
decantr registry get-pack page --route <route> [--namespace <namespace>] [--json] [--essence <path>]
|
|
9083
|
+
decantr registry critique-file <file> [--namespace <namespace>] [--json] [--essence <path>] [--treatments <path>]
|
|
9084
|
+
decantr registry audit-project [--namespace <namespace>] [--json] [--essence <path>] [--dist <path>] [--sources <dir>]
|
|
9085
|
+
`);
|
|
9086
|
+
}
|
|
9087
|
+
function cmdThemeHelp() {
|
|
9088
|
+
console.log(`
|
|
9089
|
+
${BOLD7}decantr theme${RESET14} \u2014 Manage custom themes
|
|
9090
|
+
|
|
9091
|
+
${BOLD7}Usage:${RESET14}
|
|
9092
|
+
decantr theme create <name>
|
|
9093
|
+
decantr theme create <name> --guided
|
|
9094
|
+
decantr theme list
|
|
9095
|
+
decantr theme validate <name>
|
|
9096
|
+
decantr theme delete <name>
|
|
9097
|
+
decantr theme import <path>
|
|
9098
|
+
`);
|
|
9099
|
+
}
|
|
9100
|
+
function cmdSetupHelp() {
|
|
9101
|
+
console.log(`
|
|
9102
|
+
${BOLD7}decantr setup${RESET14} \u2014 Detect the project state and recommend the right Decantr path
|
|
9103
|
+
|
|
9104
|
+
${BOLD7}Usage:${RESET14}
|
|
9105
|
+
decantr setup [--project <path>]
|
|
9106
|
+
|
|
9107
|
+
${BOLD7}Examples:${RESET14}
|
|
9108
|
+
decantr setup
|
|
9109
|
+
decantr setup --project apps/web
|
|
9110
|
+
`);
|
|
9111
|
+
}
|
|
9112
|
+
function cmdAdoptHelp() {
|
|
9113
|
+
console.log(`
|
|
9114
|
+
${BOLD7}decantr adopt${RESET14} \u2014 Brownfield one-liner: analyze, attach, verify, and show the next step
|
|
9115
|
+
|
|
9116
|
+
${BOLD7}Usage:${RESET14}
|
|
9117
|
+
decantr adopt [--project <path>] [--yes] [--dry-run]
|
|
9118
|
+
decantr adopt --base-url <url> [--evidence] [--ci] [--yes]
|
|
9119
|
+
|
|
9120
|
+
${BOLD7}Options:${RESET14}
|
|
9121
|
+
--project App path inside a workspace/monorepo
|
|
9122
|
+
--yes, -y Run without confirmation
|
|
9123
|
+
--dry-run Show the workflow without writing files
|
|
9124
|
+
--base-url Include browser evidence against this dev server URL
|
|
9125
|
+
--evidence Write .decantr/evidence/latest.json
|
|
9126
|
+
--baseline Save a health baseline (default)
|
|
9127
|
+
--no-baseline Skip baseline save
|
|
9128
|
+
--no-verify Skip the verification step
|
|
9129
|
+
--ci, --init-ci Install the Project Health CI gate after adoption
|
|
9130
|
+
--telemetry Opt this project into privacy-filtered CLI product telemetry
|
|
9131
|
+
--merge-proposal Merge the observed proposal into an existing essence
|
|
9132
|
+
--replace-essence Replace an existing essence with backup
|
|
9133
|
+
|
|
9134
|
+
${BOLD7}Examples:${RESET14}
|
|
9135
|
+
decantr adopt --yes
|
|
9136
|
+
decantr adopt --base-url http://localhost:3000 --evidence --yes
|
|
9137
|
+
decantr adopt --project apps/web --ci --yes
|
|
9138
|
+
`);
|
|
9139
|
+
}
|
|
9140
|
+
function cmdVerifyHelp() {
|
|
9141
|
+
console.log(`
|
|
9142
|
+
${BOLD7}decantr verify${RESET14} \u2014 One reliability command for local work, CI, and LLM agent loops
|
|
9143
|
+
|
|
9144
|
+
${BOLD7}Usage:${RESET14}
|
|
9145
|
+
decantr verify [--project <path>] [--brownfield] [--local-patterns]
|
|
9146
|
+
decantr verify --base-url <url> --evidence
|
|
9147
|
+
decantr verify --since-baseline
|
|
9148
|
+
decantr verify --workspace [--changed --since origin/main]
|
|
9149
|
+
decantr verify init-ci [health init-ci options]
|
|
9150
|
+
|
|
9151
|
+
${BOLD7}Examples:${RESET14}
|
|
9152
|
+
decantr verify
|
|
9153
|
+
decantr verify --brownfield --local-patterns
|
|
9154
|
+
decantr verify --base-url http://localhost:3000 --evidence
|
|
9155
|
+
decantr verify --workspace --changed --since origin/main
|
|
9156
|
+
decantr verify init-ci --project apps/web
|
|
9157
|
+
`);
|
|
9158
|
+
}
|
|
9159
|
+
function cmdTaskHelp() {
|
|
9160
|
+
console.log(`
|
|
9161
|
+
${BOLD7}decantr task${RESET14} \u2014 Prepare compact route/task context for an AI coding assistant
|
|
9162
|
+
|
|
9163
|
+
${BOLD7}Usage:${RESET14}
|
|
9164
|
+
decantr task <route> ["task summary"] [--project <path>] [--json]
|
|
9165
|
+
|
|
9166
|
+
${BOLD7}Examples:${RESET14}
|
|
9167
|
+
decantr task /feed "add saved recipe actions"
|
|
9168
|
+
decantr task /profile --json
|
|
9169
|
+
`);
|
|
9170
|
+
}
|
|
9171
|
+
function cmdCodifyHelp() {
|
|
9172
|
+
console.log(`
|
|
9173
|
+
${BOLD7}decantr codify${RESET14} \u2014 Propose or accept project-owned Brownfield UI patterns
|
|
9174
|
+
|
|
9175
|
+
${BOLD7}Usage:${RESET14}
|
|
9176
|
+
decantr codify [--project <path>]
|
|
9177
|
+
decantr codify --accept [--project <path>]
|
|
9178
|
+
|
|
9179
|
+
${BOLD7}Examples:${RESET14}
|
|
9180
|
+
decantr codify
|
|
9181
|
+
decantr codify --accept
|
|
9182
|
+
decantr verify --local-patterns
|
|
9183
|
+
`);
|
|
9184
|
+
}
|
|
9185
|
+
function cmdContentHelp() {
|
|
9186
|
+
console.log(`
|
|
9187
|
+
${BOLD7}decantr content${RESET14} \u2014 Content-author namespace for registry content repositories
|
|
9188
|
+
|
|
9189
|
+
${BOLD7}Usage:${RESET14}
|
|
9190
|
+
decantr content check [content-health options]
|
|
9191
|
+
decantr content create <type> <name>
|
|
9192
|
+
decantr content publish <type> <name>
|
|
9193
|
+
|
|
9194
|
+
${BOLD7}Examples:${RESET14}
|
|
9195
|
+
decantr content check --ci --fail-on error
|
|
9196
|
+
decantr content create pattern my-card
|
|
9197
|
+
decantr content publish pattern my-card
|
|
9198
|
+
`);
|
|
9199
|
+
}
|
|
9200
|
+
function printCommandHelp(command, args) {
|
|
9201
|
+
if (!isCommandHelpRequest(args)) return false;
|
|
9202
|
+
switch (command) {
|
|
9203
|
+
case "setup":
|
|
9204
|
+
cmdSetupHelp();
|
|
9205
|
+
return true;
|
|
9206
|
+
case "adopt":
|
|
9207
|
+
cmdAdoptHelp();
|
|
9208
|
+
return true;
|
|
9209
|
+
case "verify":
|
|
9210
|
+
cmdVerifyHelp();
|
|
9211
|
+
return true;
|
|
9212
|
+
case "task":
|
|
9213
|
+
cmdTaskHelp();
|
|
9214
|
+
return true;
|
|
9215
|
+
case "codify":
|
|
9216
|
+
cmdCodifyHelp();
|
|
9217
|
+
return true;
|
|
9218
|
+
case "content":
|
|
9219
|
+
cmdContentHelp();
|
|
9220
|
+
return true;
|
|
9221
|
+
case "health":
|
|
9222
|
+
cmdHealthHelp();
|
|
9223
|
+
return true;
|
|
9224
|
+
case "content-health":
|
|
9225
|
+
cmdContentHealthHelp();
|
|
9226
|
+
return true;
|
|
9227
|
+
case "studio":
|
|
9228
|
+
cmdStudioHelp();
|
|
9229
|
+
return true;
|
|
9230
|
+
case "workspace":
|
|
9231
|
+
cmdWorkspaceHelp();
|
|
9232
|
+
return true;
|
|
9233
|
+
case "rules":
|
|
9234
|
+
cmdRulesHelp();
|
|
9235
|
+
return true;
|
|
9236
|
+
case "registry":
|
|
9237
|
+
cmdRegistryHelp();
|
|
9238
|
+
return true;
|
|
9239
|
+
case "theme":
|
|
9240
|
+
cmdThemeHelp();
|
|
9241
|
+
return true;
|
|
9242
|
+
default:
|
|
9243
|
+
cmdHelp();
|
|
9244
|
+
return true;
|
|
9245
|
+
}
|
|
9246
|
+
}
|
|
8175
9247
|
async function main() {
|
|
8176
9248
|
const args = process.argv.slice(2);
|
|
8177
9249
|
const command = args[0];
|
|
@@ -8182,10 +9254,10 @@ async function main() {
|
|
|
8182
9254
|
if (command === "--version" || command === "-v" || command === "version") {
|
|
8183
9255
|
try {
|
|
8184
9256
|
const here = dirname4(fileURLToPath2(import.meta.url));
|
|
8185
|
-
const candidates = [
|
|
9257
|
+
const candidates = [join28(here, "..", "package.json"), join28(here, "..", "..", "package.json")];
|
|
8186
9258
|
for (const candidate of candidates) {
|
|
8187
|
-
if (
|
|
8188
|
-
const pkg = JSON.parse(
|
|
9259
|
+
if (existsSync27(candidate)) {
|
|
9260
|
+
const pkg = JSON.parse(readFileSync20(candidate, "utf-8"));
|
|
8189
9261
|
if (pkg.version) {
|
|
8190
9262
|
console.log(pkg.version);
|
|
8191
9263
|
return;
|
|
@@ -8200,7 +9272,34 @@ async function main() {
|
|
|
8200
9272
|
}
|
|
8201
9273
|
return;
|
|
8202
9274
|
}
|
|
9275
|
+
if (printCommandHelp(command, args)) {
|
|
9276
|
+
return;
|
|
9277
|
+
}
|
|
8203
9278
|
switch (command) {
|
|
9279
|
+
case "setup": {
|
|
9280
|
+
await cmdSetupWorkflow(args);
|
|
9281
|
+
break;
|
|
9282
|
+
}
|
|
9283
|
+
case "adopt": {
|
|
9284
|
+
await cmdAdoptWorkflow(args);
|
|
9285
|
+
break;
|
|
9286
|
+
}
|
|
9287
|
+
case "task": {
|
|
9288
|
+
await cmdTaskWorkflow(args);
|
|
9289
|
+
break;
|
|
9290
|
+
}
|
|
9291
|
+
case "verify": {
|
|
9292
|
+
await cmdVerifyWorkflow(args);
|
|
9293
|
+
break;
|
|
9294
|
+
}
|
|
9295
|
+
case "codify": {
|
|
9296
|
+
await cmdCodifyWorkflow(args);
|
|
9297
|
+
break;
|
|
9298
|
+
}
|
|
9299
|
+
case "content": {
|
|
9300
|
+
await cmdContentWorkflow(args);
|
|
9301
|
+
break;
|
|
9302
|
+
}
|
|
8204
9303
|
case "new": {
|
|
8205
9304
|
const newName = args[1];
|
|
8206
9305
|
if (!newName) {
|
|
@@ -8297,7 +9396,7 @@ async function main() {
|
|
|
8297
9396
|
`${YELLOW9}Note: \`decantr heal\` is deprecated. Use \`decantr check\` instead.${RESET14}`
|
|
8298
9397
|
);
|
|
8299
9398
|
}
|
|
8300
|
-
const { cmdHeal } = await import("./heal-
|
|
9399
|
+
const { cmdHeal } = await import("./heal-ZYD6NVGE.js");
|
|
8301
9400
|
const telemetryFlag = args.includes("--telemetry");
|
|
8302
9401
|
const brownfieldFlag = args.includes("--brownfield");
|
|
8303
9402
|
await cmdHeal(process.cwd(), { telemetry: telemetryFlag, brownfield: brownfieldFlag });
|
|
@@ -8309,7 +9408,7 @@ async function main() {
|
|
|
8309
9408
|
cmdHealthHelp();
|
|
8310
9409
|
break;
|
|
8311
9410
|
}
|
|
8312
|
-
const { cmdHealth, parseHealthArgs } = await import("./health-
|
|
9411
|
+
const { cmdHealth, parseHealthArgs } = await import("./health-ETZXWGTW.js");
|
|
8313
9412
|
await cmdHealth(process.cwd(), parseHealthArgs(args));
|
|
8314
9413
|
} catch (e) {
|
|
8315
9414
|
console.error(error3(e.message));
|
|
@@ -8337,7 +9436,7 @@ async function main() {
|
|
|
8337
9436
|
cmdStudioHelp();
|
|
8338
9437
|
break;
|
|
8339
9438
|
}
|
|
8340
|
-
const { cmdStudio, parseStudioArgs } = await import("./studio-
|
|
9439
|
+
const { cmdStudio, parseStudioArgs } = await import("./studio-MKLBUC3A.js");
|
|
8341
9440
|
await cmdStudio(process.cwd(), parseStudioArgs(args));
|
|
8342
9441
|
} catch (e) {
|
|
8343
9442
|
console.error(error3(e.message));
|
|
@@ -8351,7 +9450,7 @@ async function main() {
|
|
|
8351
9450
|
cmdWorkspaceHelp();
|
|
8352
9451
|
break;
|
|
8353
9452
|
}
|
|
8354
|
-
const { cmdWorkspace } = await import("./workspace-
|
|
9453
|
+
const { cmdWorkspace } = await import("./workspace-KSFWRZEX.js");
|
|
8355
9454
|
await cmdWorkspace(process.cwd(), args);
|
|
8356
9455
|
} catch (e) {
|
|
8357
9456
|
console.error(error3(e.message));
|
|
@@ -8434,13 +9533,22 @@ async function main() {
|
|
|
8434
9533
|
case "suggest": {
|
|
8435
9534
|
const query = args[1];
|
|
8436
9535
|
if (!query) {
|
|
8437
|
-
console.error(
|
|
9536
|
+
console.error(
|
|
9537
|
+
error3(
|
|
9538
|
+
"Usage: decantr suggest <query> [--type <type>] [--route <route>] [--file <path>] [--from-code]"
|
|
9539
|
+
)
|
|
9540
|
+
);
|
|
8438
9541
|
process.exitCode = 1;
|
|
8439
9542
|
return;
|
|
8440
9543
|
}
|
|
8441
9544
|
const typeIdx = args.indexOf("--type");
|
|
8442
9545
|
const type = typeIdx !== -1 ? args[typeIdx + 1] : void 0;
|
|
8443
|
-
|
|
9546
|
+
const routeIdx = args.indexOf("--route");
|
|
9547
|
+
const route = routeIdx !== -1 ? args[routeIdx + 1] : void 0;
|
|
9548
|
+
const fileIdx = args.indexOf("--file");
|
|
9549
|
+
const file = fileIdx !== -1 ? args[fileIdx + 1] : void 0;
|
|
9550
|
+
const fromCode = args.includes("--from-code");
|
|
9551
|
+
await cmdSuggest(query, { type, route, file, fromCode });
|
|
8444
9552
|
break;
|
|
8445
9553
|
}
|
|
8446
9554
|
case "get": {
|
|
@@ -8607,7 +9715,9 @@ async function main() {
|
|
|
8607
9715
|
const essenceIdx = args.indexOf("--essence");
|
|
8608
9716
|
const essencePath = essenceIdx !== -1 ? args[essenceIdx + 1] : void 0;
|
|
8609
9717
|
const packType = args[2] && !args[2].startsWith("--") ? args[2] : void 0;
|
|
8610
|
-
const
|
|
9718
|
+
const routeIdx = args.indexOf("--route");
|
|
9719
|
+
const route = routeIdx !== -1 ? args[routeIdx + 1] : void 0;
|
|
9720
|
+
let id = args[3] && !args[3].startsWith("--") ? args[3] : void 0;
|
|
8611
9721
|
if (!packType || !["manifest", "scaffold", "review", "section", "page", "mutation"].includes(packType)) {
|
|
8612
9722
|
console.error(
|
|
8613
9723
|
`${RED11}Usage: decantr registry get-pack <manifest|scaffold|review|section|page|mutation> [id] [--namespace <namespace>] [--json] [--essence <path>] [--write-context]${RESET14}`
|
|
@@ -8619,6 +9729,10 @@ async function main() {
|
|
|
8619
9729
|
await printHostedExecutionPackManifest(essencePath, namespace, jsonOutput, writeContext);
|
|
8620
9730
|
break;
|
|
8621
9731
|
}
|
|
9732
|
+
if (packType === "page" && route && !id) {
|
|
9733
|
+
const resolvedPath = essencePath ? resolveUserPath(essencePath) : join28(process.cwd(), "decantr.essence.json");
|
|
9734
|
+
id = resolvePagePackIdForRoute(resolvedPath, route);
|
|
9735
|
+
}
|
|
8622
9736
|
await printHostedSelectedExecutionPack(
|
|
8623
9737
|
packType,
|
|
8624
9738
|
id,
|