@decantr/cli 2.5.0 → 2.6.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 +6 -2
- 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-HO3OLDPQ.js → chunk-Q5OFRQY6.js} +852 -359
- 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 +4 -4
- 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"}`
|
|
@@ -6542,6 +6831,11 @@ function formatBlueprintPortfolioSummary(value) {
|
|
|
6542
6831
|
const alternative = portfolio.recommended_alternative ? `; recommended alternative: ${portfolio.recommended_alternative}` : "";
|
|
6543
6832
|
return `Blueprint set: ${labels.join(" + ")}${alternative}`;
|
|
6544
6833
|
}
|
|
6834
|
+
function formatRegistryListIdentifier(item) {
|
|
6835
|
+
if (!item || typeof item !== "object") return String(item ?? "");
|
|
6836
|
+
const record = item;
|
|
6837
|
+
return typeof record.slug === "string" && record.slug || typeof record.id === "string" && record.id || typeof record.name === "string" && record.name || "";
|
|
6838
|
+
}
|
|
6545
6839
|
function printBlueprintPortfolioNotice(blueprint) {
|
|
6546
6840
|
const portfolio = getBlueprintPortfolioMetadata(blueprint);
|
|
6547
6841
|
if (!portfolio) return;
|
|
@@ -6600,42 +6894,151 @@ async function cmdSearch(query, type, sort, recommended, intelligenceSource, blu
|
|
|
6600
6894
|
console.log(dim3(`Search failed. API may be unavailable.`));
|
|
6601
6895
|
}
|
|
6602
6896
|
}
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
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"));
|
|
6616
6921
|
}
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
const
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
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 {
|
|
6625
6936
|
}
|
|
6626
|
-
console.log("");
|
|
6627
6937
|
}
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
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));
|
|
6634
6959
|
}
|
|
6635
|
-
console.log(dim3(`Use "decantr get pattern <id>" for full details`));
|
|
6636
6960
|
} catch {
|
|
6637
|
-
console.log(dim3(`Suggestion search failed. API may be unavailable.`));
|
|
6638
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.'));
|
|
6639
7042
|
}
|
|
6640
7043
|
async function cmdGet(type, id) {
|
|
6641
7044
|
if (!isGetContentType(type)) {
|
|
@@ -6645,26 +7048,16 @@ async function cmdGet(type, id) {
|
|
|
6645
7048
|
}
|
|
6646
7049
|
const apiType = CONTENT_TYPE_TO_API_CONTENT_TYPE3[type];
|
|
6647
7050
|
const registryClient = new RegistryClient({
|
|
6648
|
-
cacheDir:
|
|
7051
|
+
cacheDir: join28(process.cwd(), ".decantr", "cache")
|
|
6649
7052
|
});
|
|
6650
7053
|
const result = await registryClient.fetchContentItem(apiType, id);
|
|
6651
7054
|
if (result) {
|
|
6652
7055
|
console.log(JSON.stringify(result.data, null, 2));
|
|
6653
7056
|
return;
|
|
6654
7057
|
}
|
|
6655
|
-
const
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
// Running from src/
|
|
6659
|
-
join27(currentDir, "..", "src", "bundled", apiType, `${id}.json`),
|
|
6660
|
-
// Running from dist/
|
|
6661
|
-
join27(currentDir, "..", "bundled", apiType, `${id}.json`)
|
|
6662
|
-
// Alternative dist layout
|
|
6663
|
-
];
|
|
6664
|
-
const bundledPath = bundledCandidates.find((p) => existsSync26(p)) || null;
|
|
6665
|
-
if (bundledPath) {
|
|
6666
|
-
const data = JSON.parse(readFileSync19(bundledPath, "utf-8"));
|
|
6667
|
-
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));
|
|
6668
7061
|
return;
|
|
6669
7062
|
}
|
|
6670
7063
|
console.error(error3(`${type} "${id}" not found.`));
|
|
@@ -6672,10 +7065,10 @@ async function cmdGet(type, id) {
|
|
|
6672
7065
|
return;
|
|
6673
7066
|
}
|
|
6674
7067
|
async function cmdValidate(path) {
|
|
6675
|
-
const essencePath = path ||
|
|
7068
|
+
const essencePath = path || join28(process.cwd(), "decantr.essence.json");
|
|
6676
7069
|
let raw;
|
|
6677
7070
|
try {
|
|
6678
|
-
raw =
|
|
7071
|
+
raw = readFileSync20(essencePath, "utf-8");
|
|
6679
7072
|
} catch {
|
|
6680
7073
|
console.error(error3(`Could not read ${essencePath}`));
|
|
6681
7074
|
process.exitCode = 1;
|
|
@@ -6742,7 +7135,7 @@ async function cmdList(type, sort, recommended, intelligenceSource, blueprintSet
|
|
|
6742
7135
|
return;
|
|
6743
7136
|
}
|
|
6744
7137
|
const registryClient = new RegistryClient({
|
|
6745
|
-
cacheDir:
|
|
7138
|
+
cacheDir: join28(process.cwd(), ".decantr", "cache")
|
|
6746
7139
|
});
|
|
6747
7140
|
const result = await registryClient.fetchContentList(
|
|
6748
7141
|
type,
|
|
@@ -6752,7 +7145,13 @@ async function cmdList(type, sort, recommended, intelligenceSource, blueprintSet
|
|
|
6752
7145
|
intelligenceSource,
|
|
6753
7146
|
blueprintSet
|
|
6754
7147
|
);
|
|
6755
|
-
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;
|
|
6756
7155
|
if (items.length === 0) {
|
|
6757
7156
|
console.log(dim3(`No ${type} found.`));
|
|
6758
7157
|
return;
|
|
@@ -6779,7 +7178,21 @@ async function cmdList(type, sort, recommended, intelligenceSource, blueprintSet
|
|
|
6779
7178
|
} else {
|
|
6780
7179
|
console.log(heading2(`${items.length} ${type} found`));
|
|
6781
7180
|
for (const item of items) {
|
|
6782
|
-
|
|
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
|
+
}
|
|
6783
7196
|
const intelligenceSummary = formatIntelligenceSummary(
|
|
6784
7197
|
item.intelligence
|
|
6785
7198
|
);
|
|
@@ -6805,10 +7218,10 @@ ${CYAN8}Telemetry enabled.${RESET14} Decantr will send privacy-filtered CLI prod
|
|
|
6805
7218
|
}
|
|
6806
7219
|
function readCliPackageVersion() {
|
|
6807
7220
|
const here = dirname4(fileURLToPath2(import.meta.url));
|
|
6808
|
-
const candidates = [
|
|
7221
|
+
const candidates = [join28(here, "..", "package.json"), join28(here, "..", "..", "package.json")];
|
|
6809
7222
|
for (const candidate of candidates) {
|
|
6810
7223
|
try {
|
|
6811
|
-
const pkg = JSON.parse(
|
|
7224
|
+
const pkg = JSON.parse(readFileSync20(candidate, "utf-8"));
|
|
6812
7225
|
if (pkg.version) return pkg.version;
|
|
6813
7226
|
} catch {
|
|
6814
7227
|
}
|
|
@@ -6819,19 +7232,19 @@ function timestampForFile() {
|
|
|
6819
7232
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
6820
7233
|
}
|
|
6821
7234
|
function backupExistingEssence(projectRoot, label) {
|
|
6822
|
-
const essencePath =
|
|
6823
|
-
if (!
|
|
6824
|
-
const backupPath =
|
|
7235
|
+
const essencePath = join28(projectRoot, "decantr.essence.json");
|
|
7236
|
+
if (!existsSync27(essencePath)) return null;
|
|
7237
|
+
const backupPath = join28(
|
|
6825
7238
|
projectRoot,
|
|
6826
7239
|
`decantr.essence.${label}.${timestampForFile()}.backup.json`
|
|
6827
7240
|
);
|
|
6828
|
-
|
|
7241
|
+
writeFileSync16(backupPath, readFileSync20(essencePath, "utf-8"), "utf-8");
|
|
6829
7242
|
return backupPath;
|
|
6830
7243
|
}
|
|
6831
7244
|
function writeBrownfieldProjectJson(input) {
|
|
6832
|
-
const decantrDir =
|
|
6833
|
-
|
|
6834
|
-
|
|
7245
|
+
const decantrDir = join28(input.projectRoot, ".decantr");
|
|
7246
|
+
mkdirSync13(join28(decantrDir, "context"), { recursive: true });
|
|
7247
|
+
mkdirSync13(join28(decantrDir, "cache"), { recursive: true });
|
|
6835
7248
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6836
7249
|
const projectJson = {
|
|
6837
7250
|
detected: {
|
|
@@ -6872,7 +7285,7 @@ function writeBrownfieldProjectJson(input) {
|
|
|
6872
7285
|
}
|
|
6873
7286
|
}
|
|
6874
7287
|
};
|
|
6875
|
-
|
|
7288
|
+
writeFileSync16(join28(decantrDir, "project.json"), JSON.stringify(projectJson, null, 2) + "\n");
|
|
6876
7289
|
}
|
|
6877
7290
|
async function applyAcceptedBrownfieldProposal(input) {
|
|
6878
7291
|
const proposal = readBrownfieldProposal(input.projectRoot);
|
|
@@ -6888,8 +7301,8 @@ async function applyAcceptedBrownfieldProposal(input) {
|
|
|
6888
7301
|
process.exitCode = 1;
|
|
6889
7302
|
return;
|
|
6890
7303
|
}
|
|
6891
|
-
const essencePath =
|
|
6892
|
-
const hasEssence =
|
|
7304
|
+
const essencePath = join28(input.projectRoot, "decantr.essence.json");
|
|
7305
|
+
const hasEssence = existsSync27(essencePath);
|
|
6893
7306
|
let essence;
|
|
6894
7307
|
let backupPath = null;
|
|
6895
7308
|
if (input.mode === "accept" && hasEssence) {
|
|
@@ -6903,7 +7316,7 @@ async function applyAcceptedBrownfieldProposal(input) {
|
|
|
6903
7316
|
return;
|
|
6904
7317
|
}
|
|
6905
7318
|
if (input.mode === "merge" && hasEssence) {
|
|
6906
|
-
const existing = JSON.parse(
|
|
7319
|
+
const existing = JSON.parse(readFileSync20(essencePath, "utf-8"));
|
|
6907
7320
|
if (!isV47(existing)) {
|
|
6908
7321
|
console.log(
|
|
6909
7322
|
error3(
|
|
@@ -6940,9 +7353,9 @@ async function applyAcceptedBrownfieldProposal(input) {
|
|
|
6940
7353
|
assistantBridge: input.assistantBridge,
|
|
6941
7354
|
mode: input.mode
|
|
6942
7355
|
});
|
|
6943
|
-
|
|
7356
|
+
writeFileSync16(essencePath, JSON.stringify(essence, null, 2) + "\n", "utf-8");
|
|
6944
7357
|
const registryClient = new RegistryClient({
|
|
6945
|
-
cacheDir:
|
|
7358
|
+
cacheDir: join28(input.projectRoot, ".decantr", "cache"),
|
|
6946
7359
|
offline: true,
|
|
6947
7360
|
projectRoot: input.projectRoot
|
|
6948
7361
|
});
|
|
@@ -7096,7 +7509,7 @@ async function cmdInit(args) {
|
|
|
7096
7509
|
}
|
|
7097
7510
|
}
|
|
7098
7511
|
const registryClient = new RegistryClient({
|
|
7099
|
-
cacheDir:
|
|
7512
|
+
cacheDir: join28(projectRoot, ".decantr", "cache"),
|
|
7100
7513
|
apiUrl: args.registry,
|
|
7101
7514
|
offline: args.offline,
|
|
7102
7515
|
projectRoot
|
|
@@ -7426,7 +7839,7 @@ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET14}`);
|
|
|
7426
7839
|
if (appliedRuleFiles.length > 0) {
|
|
7427
7840
|
console.log(` ${dim3(`Rule bridge applied: ${appliedRuleFiles.join(", ")}`)}`);
|
|
7428
7841
|
}
|
|
7429
|
-
if (!
|
|
7842
|
+
if (!existsSync27(join28(projectRoot, "package.json"))) {
|
|
7430
7843
|
console.log("");
|
|
7431
7844
|
console.log(
|
|
7432
7845
|
dim3(` Note: ${cyan3("decantr init")} created Decantr contract/context files only.`)
|
|
@@ -7437,7 +7850,7 @@ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET14}`);
|
|
|
7437
7850
|
)
|
|
7438
7851
|
);
|
|
7439
7852
|
}
|
|
7440
|
-
const hasCompiledPacks =
|
|
7853
|
+
const hasCompiledPacks = existsSync27(join28(projectRoot, ".decantr", "context", "scaffold-pack.md"));
|
|
7441
7854
|
console.log("");
|
|
7442
7855
|
console.log(" Next steps:");
|
|
7443
7856
|
if (hasCompiledPacks) {
|
|
@@ -7477,7 +7890,7 @@ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET14}`);
|
|
|
7477
7890
|
console.log(` ${cyan3("decantr upgrade")} Update to latest patterns`);
|
|
7478
7891
|
console.log(` ${cyan3("decantr check")} Detect drift issues`);
|
|
7479
7892
|
console.log(` ${cyan3("decantr migrate --to v4")} Migrate older essence files to v4`);
|
|
7480
|
-
const essenceContent =
|
|
7893
|
+
const essenceContent = readFileSync20(result.essencePath, "utf-8");
|
|
7481
7894
|
const essence = JSON.parse(essenceContent);
|
|
7482
7895
|
const validation = validateEssence2(essence);
|
|
7483
7896
|
if (!validation.valid) {
|
|
@@ -7533,16 +7946,16 @@ Validation warnings: ${validation.errors.join(", ")}`));
|
|
|
7533
7946
|
}
|
|
7534
7947
|
async function cmdStatus() {
|
|
7535
7948
|
const projectRoot = process.cwd();
|
|
7536
|
-
const essencePath =
|
|
7537
|
-
const projectJsonPath =
|
|
7949
|
+
const essencePath = join28(projectRoot, "decantr.essence.json");
|
|
7950
|
+
const projectJsonPath = join28(projectRoot, ".decantr", "project.json");
|
|
7538
7951
|
console.log(heading2("Decantr Project Status"));
|
|
7539
|
-
if (!
|
|
7952
|
+
if (!existsSync27(essencePath)) {
|
|
7540
7953
|
console.log(`${RED11}No decantr.essence.json found.${RESET14}`);
|
|
7541
7954
|
console.log(dim3('Run "decantr init" to create one.'));
|
|
7542
7955
|
return;
|
|
7543
7956
|
}
|
|
7544
7957
|
try {
|
|
7545
|
-
const essence = JSON.parse(
|
|
7958
|
+
const essence = JSON.parse(readFileSync20(essencePath, "utf-8"));
|
|
7546
7959
|
const validation = validateEssence2(essence);
|
|
7547
7960
|
const essenceVersion = isV47(essence) ? "v4" : "legacy";
|
|
7548
7961
|
console.log(`${BOLD7}Essence:${RESET14}`);
|
|
@@ -7590,9 +8003,9 @@ async function cmdStatus() {
|
|
|
7590
8003
|
}
|
|
7591
8004
|
console.log("");
|
|
7592
8005
|
console.log(`${BOLD7}Sync Status:${RESET14}`);
|
|
7593
|
-
if (
|
|
8006
|
+
if (existsSync27(projectJsonPath)) {
|
|
7594
8007
|
try {
|
|
7595
|
-
const projectJson = JSON.parse(
|
|
8008
|
+
const projectJson = JSON.parse(readFileSync20(projectJsonPath, "utf-8"));
|
|
7596
8009
|
const syncStatus = projectJson.sync?.status || "unknown";
|
|
7597
8010
|
const lastSync = projectJson.sync?.lastSync || "never";
|
|
7598
8011
|
const source = projectJson.sync?.registrySource || "unknown";
|
|
@@ -7610,7 +8023,7 @@ async function cmdStatus() {
|
|
|
7610
8023
|
}
|
|
7611
8024
|
async function cmdSync() {
|
|
7612
8025
|
const projectRoot = process.cwd();
|
|
7613
|
-
const cacheDir =
|
|
8026
|
+
const cacheDir = join28(projectRoot, ".decantr", "cache");
|
|
7614
8027
|
console.log(heading2("Syncing registry content..."));
|
|
7615
8028
|
const result = await syncRegistry(cacheDir);
|
|
7616
8029
|
if (result.synced.length > 0) {
|
|
@@ -7805,14 +8218,14 @@ ${BOLD7}Examples:${RESET14}
|
|
|
7805
8218
|
process.exitCode = 1;
|
|
7806
8219
|
return;
|
|
7807
8220
|
}
|
|
7808
|
-
const themePath =
|
|
7809
|
-
if (!
|
|
8221
|
+
const themePath = join28(projectRoot, ".decantr", "custom", "themes", `${name}.json`);
|
|
8222
|
+
if (!existsSync27(themePath)) {
|
|
7810
8223
|
console.error(error3(`Theme "${name}" not found at ${themePath}`));
|
|
7811
8224
|
process.exitCode = 1;
|
|
7812
8225
|
return;
|
|
7813
8226
|
}
|
|
7814
8227
|
try {
|
|
7815
|
-
const theme = JSON.parse(
|
|
8228
|
+
const theme = JSON.parse(readFileSync20(themePath, "utf-8"));
|
|
7816
8229
|
const result = validateCustomTheme(theme);
|
|
7817
8230
|
if (result.valid) {
|
|
7818
8231
|
console.log(success3(`Custom theme "${name}" is valid`));
|
|
@@ -7896,7 +8309,7 @@ ${BOLD7}Usage:${RESET14}
|
|
|
7896
8309
|
decantr check --brownfield
|
|
7897
8310
|
decantr sync-drift
|
|
7898
8311
|
decantr search <query> [--type <type>] [--sort <recommended|recent|name>] [--recommended] [--source <authored|benchmark|hybrid>]
|
|
7899
|
-
decantr suggest <query> [--type <type>]
|
|
8312
|
+
decantr suggest <query> [--type <type>] [--route <route>] [--file <path>] [--from-code]
|
|
7900
8313
|
decantr get <type> <id>
|
|
7901
8314
|
decantr list <type> [--sort <recommended|recent|name>] [--recommended] [--source <authored|benchmark|hybrid>]
|
|
7902
8315
|
decantr showcase [manifest|shortlist|verification] [--json]
|
|
@@ -7906,7 +8319,8 @@ ${BOLD7}Usage:${RESET14}
|
|
|
7906
8319
|
decantr registry critique-file <file> [--namespace <namespace>] [--json] [--essence <path>] [--treatments <path>]
|
|
7907
8320
|
decantr registry audit-project [--namespace <namespace>] [--json] [--essence <path>] [--dist <path>] [--sources <dir>]
|
|
7908
8321
|
decantr health [--format text|json|markdown] [--ci] [--fail-on error|warn|none]
|
|
7909
|
-
decantr health --evidence [--browser] [--design-tokens <path>]
|
|
8322
|
+
decantr health --evidence [--browser] [--base-url <url>] [--design-tokens <path>]
|
|
8323
|
+
decantr health --save-baseline | --since-baseline
|
|
7910
8324
|
decantr health init-ci [--force] [--project <path>] [--workspace] [--fail-on <error|warn|none>] [--cli-version <version|latest>]
|
|
7911
8325
|
decantr workspace list [--json]
|
|
7912
8326
|
decantr workspace health [--json] [--changed --since origin/main]
|
|
@@ -8013,7 +8427,7 @@ ${BOLD7}Examples:${RESET14}
|
|
|
8013
8427
|
decantr check --brownfield
|
|
8014
8428
|
decantr sync-drift
|
|
8015
8429
|
decantr search dashboard
|
|
8016
|
-
decantr suggest
|
|
8430
|
+
decantr suggest "recipe feed with infinite scroll" --route /feed --from-code
|
|
8017
8431
|
decantr list patterns
|
|
8018
8432
|
decantr showcase shortlist
|
|
8019
8433
|
decantr showcase verification --json
|
|
@@ -8030,7 +8444,7 @@ ${BOLD7}Examples:${RESET14}
|
|
|
8030
8444
|
${BOLD7}Workflow Model:${RESET14}
|
|
8031
8445
|
${cyan3("Greenfield blueprint")} decantr new my-app --blueprint=X --workflow=greenfield --adoption=decantr-css
|
|
8032
8446
|
${cyan3("Greenfield contract")} decantr init --workflow=greenfield --adoption=contract-only
|
|
8033
|
-
${cyan3("Brownfield adoption")} decantr analyze -> decantr init --existing --accept-proposal -> decantr
|
|
8447
|
+
${cyan3("Brownfield adoption")} decantr analyze -> decantr init --existing --accept-proposal -> decantr health --browser --evidence
|
|
8034
8448
|
${cyan3("Hybrid composition")} decantr add/remove, decantr theme switch, decantr registry, decantr upgrade
|
|
8035
8449
|
|
|
8036
8450
|
${BOLD7}Bootstrap adapters:${RESET14}
|
|
@@ -8070,6 +8484,9 @@ ${BOLD7}Usage:${RESET14}
|
|
|
8070
8484
|
decantr health --ci [--fail-on error|warn|none]
|
|
8071
8485
|
decantr health --prompt <finding-id>
|
|
8072
8486
|
decantr health --evidence [--browser] [--design-tokens <path>]
|
|
8487
|
+
decantr health --browser --base-url <url> --evidence
|
|
8488
|
+
decantr health --save-baseline
|
|
8489
|
+
decantr health --since-baseline
|
|
8073
8490
|
decantr health init-ci [--force] [--project <path>] [--workspace] [--fail-on error|warn|none] [--cli-version <version|latest>]
|
|
8074
8491
|
|
|
8075
8492
|
${BOLD7}Options:${RESET14}
|
|
@@ -8082,6 +8499,9 @@ ${BOLD7}Options:${RESET14}
|
|
|
8082
8499
|
--prompt Print an AI-ready remediation prompt for a finding
|
|
8083
8500
|
--evidence Emit a local Evidence Bundle JSON artifact
|
|
8084
8501
|
--browser Include optional rendered-browser setup/evidence checks
|
|
8502
|
+
--base-url Base URL for rendered route checks when --browser is enabled
|
|
8503
|
+
--save-baseline Save the current health state for later comparison
|
|
8504
|
+
--since-baseline Compare this run to .decantr/health-baseline.json
|
|
8085
8505
|
--design-tokens Compare against a Figma/Tokens Studio JSON export
|
|
8086
8506
|
|
|
8087
8507
|
${BOLD7}Examples:${RESET14}
|
|
@@ -8165,6 +8585,61 @@ ${BOLD7}Examples:${RESET14}
|
|
|
8165
8585
|
decantr studio --workspace
|
|
8166
8586
|
`);
|
|
8167
8587
|
}
|
|
8588
|
+
function cmdRegistryHelp() {
|
|
8589
|
+
console.log(`
|
|
8590
|
+
${BOLD7}decantr registry${RESET14} \u2014 Read hosted execution packs and registry intelligence
|
|
8591
|
+
|
|
8592
|
+
${BOLD7}Usage:${RESET14}
|
|
8593
|
+
decantr registry summary [--namespace <namespace>] [--json]
|
|
8594
|
+
decantr registry compile-packs [path] [--namespace <namespace>] [--json] [--write-context]
|
|
8595
|
+
decantr registry get-pack <manifest|scaffold|review|section|page|mutation> [id] [--namespace <namespace>] [--json] [--essence <path>] [--write-context]
|
|
8596
|
+
decantr registry get-pack page --route <route> [--namespace <namespace>] [--json] [--essence <path>]
|
|
8597
|
+
decantr registry critique-file <file> [--namespace <namespace>] [--json] [--essence <path>] [--treatments <path>]
|
|
8598
|
+
decantr registry audit-project [--namespace <namespace>] [--json] [--essence <path>] [--dist <path>] [--sources <dir>]
|
|
8599
|
+
`);
|
|
8600
|
+
}
|
|
8601
|
+
function cmdThemeHelp() {
|
|
8602
|
+
console.log(`
|
|
8603
|
+
${BOLD7}decantr theme${RESET14} \u2014 Manage custom themes
|
|
8604
|
+
|
|
8605
|
+
${BOLD7}Usage:${RESET14}
|
|
8606
|
+
decantr theme create <name>
|
|
8607
|
+
decantr theme create <name> --guided
|
|
8608
|
+
decantr theme list
|
|
8609
|
+
decantr theme validate <name>
|
|
8610
|
+
decantr theme delete <name>
|
|
8611
|
+
decantr theme import <path>
|
|
8612
|
+
`);
|
|
8613
|
+
}
|
|
8614
|
+
function printCommandHelp(command, args) {
|
|
8615
|
+
if (!isCommandHelpRequest(args)) return false;
|
|
8616
|
+
switch (command) {
|
|
8617
|
+
case "health":
|
|
8618
|
+
cmdHealthHelp();
|
|
8619
|
+
return true;
|
|
8620
|
+
case "content-health":
|
|
8621
|
+
cmdContentHealthHelp();
|
|
8622
|
+
return true;
|
|
8623
|
+
case "studio":
|
|
8624
|
+
cmdStudioHelp();
|
|
8625
|
+
return true;
|
|
8626
|
+
case "workspace":
|
|
8627
|
+
cmdWorkspaceHelp();
|
|
8628
|
+
return true;
|
|
8629
|
+
case "rules":
|
|
8630
|
+
cmdRulesHelp();
|
|
8631
|
+
return true;
|
|
8632
|
+
case "registry":
|
|
8633
|
+
cmdRegistryHelp();
|
|
8634
|
+
return true;
|
|
8635
|
+
case "theme":
|
|
8636
|
+
cmdThemeHelp();
|
|
8637
|
+
return true;
|
|
8638
|
+
default:
|
|
8639
|
+
cmdHelp();
|
|
8640
|
+
return true;
|
|
8641
|
+
}
|
|
8642
|
+
}
|
|
8168
8643
|
async function main() {
|
|
8169
8644
|
const args = process.argv.slice(2);
|
|
8170
8645
|
const command = args[0];
|
|
@@ -8175,10 +8650,10 @@ async function main() {
|
|
|
8175
8650
|
if (command === "--version" || command === "-v" || command === "version") {
|
|
8176
8651
|
try {
|
|
8177
8652
|
const here = dirname4(fileURLToPath2(import.meta.url));
|
|
8178
|
-
const candidates = [
|
|
8653
|
+
const candidates = [join28(here, "..", "package.json"), join28(here, "..", "..", "package.json")];
|
|
8179
8654
|
for (const candidate of candidates) {
|
|
8180
|
-
if (
|
|
8181
|
-
const pkg = JSON.parse(
|
|
8655
|
+
if (existsSync27(candidate)) {
|
|
8656
|
+
const pkg = JSON.parse(readFileSync20(candidate, "utf-8"));
|
|
8182
8657
|
if (pkg.version) {
|
|
8183
8658
|
console.log(pkg.version);
|
|
8184
8659
|
return;
|
|
@@ -8193,6 +8668,9 @@ async function main() {
|
|
|
8193
8668
|
}
|
|
8194
8669
|
return;
|
|
8195
8670
|
}
|
|
8671
|
+
if (printCommandHelp(command, args)) {
|
|
8672
|
+
return;
|
|
8673
|
+
}
|
|
8196
8674
|
switch (command) {
|
|
8197
8675
|
case "new": {
|
|
8198
8676
|
const newName = args[1];
|
|
@@ -8290,7 +8768,7 @@ async function main() {
|
|
|
8290
8768
|
`${YELLOW9}Note: \`decantr heal\` is deprecated. Use \`decantr check\` instead.${RESET14}`
|
|
8291
8769
|
);
|
|
8292
8770
|
}
|
|
8293
|
-
const { cmdHeal } = await import("./heal-
|
|
8771
|
+
const { cmdHeal } = await import("./heal-ZYD6NVGE.js");
|
|
8294
8772
|
const telemetryFlag = args.includes("--telemetry");
|
|
8295
8773
|
const brownfieldFlag = args.includes("--brownfield");
|
|
8296
8774
|
await cmdHeal(process.cwd(), { telemetry: telemetryFlag, brownfield: brownfieldFlag });
|
|
@@ -8302,7 +8780,7 @@ async function main() {
|
|
|
8302
8780
|
cmdHealthHelp();
|
|
8303
8781
|
break;
|
|
8304
8782
|
}
|
|
8305
|
-
const { cmdHealth, parseHealthArgs } = await import("./health-
|
|
8783
|
+
const { cmdHealth, parseHealthArgs } = await import("./health-ETZXWGTW.js");
|
|
8306
8784
|
await cmdHealth(process.cwd(), parseHealthArgs(args));
|
|
8307
8785
|
} catch (e) {
|
|
8308
8786
|
console.error(error3(e.message));
|
|
@@ -8330,7 +8808,7 @@ async function main() {
|
|
|
8330
8808
|
cmdStudioHelp();
|
|
8331
8809
|
break;
|
|
8332
8810
|
}
|
|
8333
|
-
const { cmdStudio, parseStudioArgs } = await import("./studio-
|
|
8811
|
+
const { cmdStudio, parseStudioArgs } = await import("./studio-MKLBUC3A.js");
|
|
8334
8812
|
await cmdStudio(process.cwd(), parseStudioArgs(args));
|
|
8335
8813
|
} catch (e) {
|
|
8336
8814
|
console.error(error3(e.message));
|
|
@@ -8344,7 +8822,7 @@ async function main() {
|
|
|
8344
8822
|
cmdWorkspaceHelp();
|
|
8345
8823
|
break;
|
|
8346
8824
|
}
|
|
8347
|
-
const { cmdWorkspace } = await import("./workspace-
|
|
8825
|
+
const { cmdWorkspace } = await import("./workspace-KSFWRZEX.js");
|
|
8348
8826
|
await cmdWorkspace(process.cwd(), args);
|
|
8349
8827
|
} catch (e) {
|
|
8350
8828
|
console.error(error3(e.message));
|
|
@@ -8427,13 +8905,22 @@ async function main() {
|
|
|
8427
8905
|
case "suggest": {
|
|
8428
8906
|
const query = args[1];
|
|
8429
8907
|
if (!query) {
|
|
8430
|
-
console.error(
|
|
8908
|
+
console.error(
|
|
8909
|
+
error3(
|
|
8910
|
+
"Usage: decantr suggest <query> [--type <type>] [--route <route>] [--file <path>] [--from-code]"
|
|
8911
|
+
)
|
|
8912
|
+
);
|
|
8431
8913
|
process.exitCode = 1;
|
|
8432
8914
|
return;
|
|
8433
8915
|
}
|
|
8434
8916
|
const typeIdx = args.indexOf("--type");
|
|
8435
8917
|
const type = typeIdx !== -1 ? args[typeIdx + 1] : void 0;
|
|
8436
|
-
|
|
8918
|
+
const routeIdx = args.indexOf("--route");
|
|
8919
|
+
const route = routeIdx !== -1 ? args[routeIdx + 1] : void 0;
|
|
8920
|
+
const fileIdx = args.indexOf("--file");
|
|
8921
|
+
const file = fileIdx !== -1 ? args[fileIdx + 1] : void 0;
|
|
8922
|
+
const fromCode = args.includes("--from-code");
|
|
8923
|
+
await cmdSuggest(query, { type, route, file, fromCode });
|
|
8437
8924
|
break;
|
|
8438
8925
|
}
|
|
8439
8926
|
case "get": {
|
|
@@ -8600,7 +9087,9 @@ async function main() {
|
|
|
8600
9087
|
const essenceIdx = args.indexOf("--essence");
|
|
8601
9088
|
const essencePath = essenceIdx !== -1 ? args[essenceIdx + 1] : void 0;
|
|
8602
9089
|
const packType = args[2] && !args[2].startsWith("--") ? args[2] : void 0;
|
|
8603
|
-
const
|
|
9090
|
+
const routeIdx = args.indexOf("--route");
|
|
9091
|
+
const route = routeIdx !== -1 ? args[routeIdx + 1] : void 0;
|
|
9092
|
+
let id = args[3] && !args[3].startsWith("--") ? args[3] : void 0;
|
|
8604
9093
|
if (!packType || !["manifest", "scaffold", "review", "section", "page", "mutation"].includes(packType)) {
|
|
8605
9094
|
console.error(
|
|
8606
9095
|
`${RED11}Usage: decantr registry get-pack <manifest|scaffold|review|section|page|mutation> [id] [--namespace <namespace>] [--json] [--essence <path>] [--write-context]${RESET14}`
|
|
@@ -8612,6 +9101,10 @@ async function main() {
|
|
|
8612
9101
|
await printHostedExecutionPackManifest(essencePath, namespace, jsonOutput, writeContext);
|
|
8613
9102
|
break;
|
|
8614
9103
|
}
|
|
9104
|
+
if (packType === "page" && route && !id) {
|
|
9105
|
+
const resolvedPath = essencePath ? resolveUserPath(essencePath) : join28(process.cwd(), "decantr.essence.json");
|
|
9106
|
+
id = resolvePagePackIdForRoute(resolvedPath, route);
|
|
9107
|
+
}
|
|
8615
9108
|
await printHostedSelectedExecutionPack(
|
|
8616
9109
|
packType,
|
|
8617
9110
|
id,
|