design-system-enforcer 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +381 -80
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +381 -80
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +71 -71
package/dist/cli.cjs
CHANGED
|
@@ -24,10 +24,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/cli.ts
|
|
27
|
-
var
|
|
28
|
-
var
|
|
27
|
+
var import_node_fs8 = __toESM(require("fs"), 1);
|
|
28
|
+
var import_node_path8 = __toESM(require("path"), 1);
|
|
29
29
|
var import_commander = require("commander");
|
|
30
|
-
var
|
|
30
|
+
var import_picocolors3 = __toESM(require("picocolors"), 1);
|
|
31
31
|
|
|
32
32
|
// src/config/loader.ts
|
|
33
33
|
var import_node_fs3 = __toESM(require("fs"), 1);
|
|
@@ -2443,12 +2443,283 @@ function generateMarkdownRules(config) {
|
|
|
2443
2443
|
`;
|
|
2444
2444
|
}
|
|
2445
2445
|
|
|
2446
|
+
// src/doctor/index.ts
|
|
2447
|
+
function computeDoctorScore(result) {
|
|
2448
|
+
const categoryViolations = {
|
|
2449
|
+
typography: 0,
|
|
2450
|
+
geometry: 0,
|
|
2451
|
+
components: 0,
|
|
2452
|
+
colors: 0,
|
|
2453
|
+
wrapping: 0,
|
|
2454
|
+
tables: 0
|
|
2455
|
+
};
|
|
2456
|
+
const fileCountMap = {};
|
|
2457
|
+
result.violations.forEach((v) => {
|
|
2458
|
+
fileCountMap[v.file] = (fileCountMap[v.file] || 0) + 1;
|
|
2459
|
+
if (v.rule.startsWith("font-") || v.rule.includes("text-size") || v.rule.includes("uppercase")) {
|
|
2460
|
+
categoryViolations.typography++;
|
|
2461
|
+
} else if (v.rule.includes("wrapping")) {
|
|
2462
|
+
categoryViolations.wrapping++;
|
|
2463
|
+
} else if (v.rule.startsWith("control-") || v.rule.startsWith("corner-radius")) {
|
|
2464
|
+
categoryViolations.geometry++;
|
|
2465
|
+
} else if (v.rule.includes("color") || v.rule.includes("gradient") || v.rule.includes("blur")) {
|
|
2466
|
+
categoryViolations.colors++;
|
|
2467
|
+
} else if (v.rule.startsWith("table-")) {
|
|
2468
|
+
categoryViolations.tables++;
|
|
2469
|
+
} else {
|
|
2470
|
+
categoryViolations.components++;
|
|
2471
|
+
}
|
|
2472
|
+
});
|
|
2473
|
+
const filesCount = Math.max(1, result.scannedFilesCount || 1);
|
|
2474
|
+
function calcScore(violations) {
|
|
2475
|
+
const penaltyPerViolation = 100 / (filesCount * 2);
|
|
2476
|
+
return Math.max(0, Math.round(100 - violations * penaltyPerViolation));
|
|
2477
|
+
}
|
|
2478
|
+
const categories = [
|
|
2479
|
+
{ name: "Typography Scale", category: "typography", violations: categoryViolations.typography, score: calcScore(categoryViolations.typography) },
|
|
2480
|
+
{ name: "Geometry & Radii", category: "geometry", violations: categoryViolations.geometry, score: calcScore(categoryViolations.geometry) },
|
|
2481
|
+
{ name: "Wrap Integrity", category: "wrapping", violations: categoryViolations.wrapping, score: calcScore(categoryViolations.wrapping) },
|
|
2482
|
+
{ name: "Component Inventory", category: "components", violations: categoryViolations.components, score: calcScore(categoryViolations.components) },
|
|
2483
|
+
{ name: "Color Purity", category: "colors", violations: categoryViolations.colors, score: calcScore(categoryViolations.colors) },
|
|
2484
|
+
{ name: "Table Invariants", category: "tables", violations: categoryViolations.tables, score: calcScore(categoryViolations.tables) }
|
|
2485
|
+
];
|
|
2486
|
+
const totalScore = Math.round(categories.reduce((acc, c) => acc + c.score, 0) / categories.length);
|
|
2487
|
+
let grade = "F";
|
|
2488
|
+
if (totalScore >= 98) grade = "A+";
|
|
2489
|
+
else if (totalScore >= 90) grade = "A";
|
|
2490
|
+
else if (totalScore >= 80) grade = "B";
|
|
2491
|
+
else if (totalScore >= 70) grade = "C";
|
|
2492
|
+
else if (totalScore >= 60) grade = "D";
|
|
2493
|
+
const fileBreakdown = Object.entries(fileCountMap).map(([file, violations]) => ({ file, violations })).sort((a, b) => b.violations - a.violations).slice(0, 5);
|
|
2494
|
+
return {
|
|
2495
|
+
overallScore: totalScore,
|
|
2496
|
+
grade,
|
|
2497
|
+
scannedFiles: filesCount,
|
|
2498
|
+
totalViolations: result.violations.length,
|
|
2499
|
+
categories,
|
|
2500
|
+
fileBreakdown
|
|
2501
|
+
};
|
|
2502
|
+
}
|
|
2503
|
+
|
|
2504
|
+
// src/doctor/formatter.ts
|
|
2505
|
+
var import_picocolors2 = __toESM(require("picocolors"), 1);
|
|
2506
|
+
function renderProgressBar(percentage, width = 20) {
|
|
2507
|
+
const filledChars = Math.round(percentage / 100 * width);
|
|
2508
|
+
const emptyChars = width - filledChars;
|
|
2509
|
+
let colorFn = import_picocolors2.default.green;
|
|
2510
|
+
if (percentage < 70) colorFn = import_picocolors2.default.red;
|
|
2511
|
+
else if (percentage < 90) colorFn = import_picocolors2.default.yellow;
|
|
2512
|
+
const bar = colorFn("\u2588".repeat(filledChars)) + import_picocolors2.default.dim("\u2591".repeat(emptyChars));
|
|
2513
|
+
return `${bar} ${colorFn(percentage.toString().padStart(3) + "%")}`;
|
|
2514
|
+
}
|
|
2515
|
+
function formatDoctorOutput(report) {
|
|
2516
|
+
const lines = [];
|
|
2517
|
+
lines.push("");
|
|
2518
|
+
lines.push(import_picocolors2.default.bold(import_picocolors2.default.cyan("========================================================")));
|
|
2519
|
+
lines.push(import_picocolors2.default.bold(import_picocolors2.default.cyan(" DESIGN SYSTEM ENFORCER - HEALTH REPORT ")));
|
|
2520
|
+
lines.push(import_picocolors2.default.bold(import_picocolors2.default.cyan("========================================================")));
|
|
2521
|
+
lines.push("");
|
|
2522
|
+
let gradeColor = import_picocolors2.default.green;
|
|
2523
|
+
if (report.grade === "B") gradeColor = import_picocolors2.default.cyan;
|
|
2524
|
+
else if (report.grade === "C") gradeColor = import_picocolors2.default.yellow;
|
|
2525
|
+
else if (report.grade === "D" || report.grade === "F") gradeColor = import_picocolors2.default.red;
|
|
2526
|
+
lines.push(
|
|
2527
|
+
` ${import_picocolors2.default.bold("Overall Score:")} ${gradeColor(import_picocolors2.default.bold(report.overallScore + "/100"))} [${gradeColor(import_picocolors2.default.bold(report.grade))}]`
|
|
2528
|
+
);
|
|
2529
|
+
lines.push(` ${import_picocolors2.default.bold("Scanned Files:")} ${import_picocolors2.default.white(report.scannedFiles)}`);
|
|
2530
|
+
lines.push(` ${import_picocolors2.default.bold("Total Issues:")} ${report.totalViolations === 0 ? import_picocolors2.default.green("0") : import_picocolors2.default.red(report.totalViolations)}`);
|
|
2531
|
+
lines.push("");
|
|
2532
|
+
lines.push(import_picocolors2.default.bold(" Category Compliance:"));
|
|
2533
|
+
report.categories.forEach((cat) => {
|
|
2534
|
+
const paddedName = cat.name.padEnd(22, " ");
|
|
2535
|
+
const issueCount = cat.violations > 0 ? import_picocolors2.default.dim(`(${cat.violations} issues)`) : "";
|
|
2536
|
+
lines.push(` ${import_picocolors2.default.dim(paddedName)} ${renderProgressBar(cat.score)} ${issueCount}`);
|
|
2537
|
+
});
|
|
2538
|
+
lines.push("");
|
|
2539
|
+
if (report.fileBreakdown.length > 0) {
|
|
2540
|
+
lines.push(import_picocolors2.default.bold(import_picocolors2.default.yellow(" Top Offending Files:")));
|
|
2541
|
+
report.fileBreakdown.forEach((item, idx) => {
|
|
2542
|
+
lines.push(` ${idx + 1}. ${import_picocolors2.default.cyan(item.file)}: ${import_picocolors2.default.red(item.violations + " violations")}`);
|
|
2543
|
+
});
|
|
2544
|
+
lines.push("");
|
|
2545
|
+
lines.push(import_picocolors2.default.dim(" Run 'dse check' for detailed line-by-line violation reports."));
|
|
2546
|
+
lines.push(import_picocolors2.default.dim(" Run 'dse fix' to automatically remediate common violations."));
|
|
2547
|
+
} else {
|
|
2548
|
+
lines.push(import_picocolors2.default.bold(import_picocolors2.default.green(" \u2714 All design tokens and component invariants are 100% compliant!")));
|
|
2549
|
+
}
|
|
2550
|
+
lines.push("");
|
|
2551
|
+
return lines.join("\n");
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
// src/fixer/index.ts
|
|
2555
|
+
var import_node_fs6 = __toESM(require("fs"), 1);
|
|
2556
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
2557
|
+
async function runDseFix(config, cwd = process.cwd()) {
|
|
2558
|
+
const scanResult = await runDseScan(config, cwd);
|
|
2559
|
+
const violationsByFile = {};
|
|
2560
|
+
scanResult.violations.forEach((v) => {
|
|
2561
|
+
if (!violationsByFile[v.file]) {
|
|
2562
|
+
violationsByFile[v.file] = [];
|
|
2563
|
+
}
|
|
2564
|
+
violationsByFile[v.file].push(v);
|
|
2565
|
+
});
|
|
2566
|
+
const details = [];
|
|
2567
|
+
let totalModifications = 0;
|
|
2568
|
+
for (const [relPath, violations] of Object.entries(violationsByFile)) {
|
|
2569
|
+
const fullPath = import_node_path6.default.resolve(cwd, relPath);
|
|
2570
|
+
if (!import_node_fs6.default.existsSync(fullPath)) continue;
|
|
2571
|
+
let content = import_node_fs6.default.readFileSync(fullPath, "utf-8");
|
|
2572
|
+
const originalContent = content;
|
|
2573
|
+
const fileFixes = [];
|
|
2574
|
+
if (violations.some((v) => v.rule === "no-wrapping-compact-elements")) {
|
|
2575
|
+
const before = content;
|
|
2576
|
+
content = content.replace(/(<(?:Button|IconButton|Chip|Badge)\b[^>]*className=["'`][^"'`]*?)\b(?:whitespace-normal|whitespace-pre-wrap|flex-wrap)\b([^"'`]*?["'`])/g, (match, prefix, suffix) => {
|
|
2577
|
+
return `${prefix}whitespace-nowrap shrink-0${suffix}`;
|
|
2578
|
+
});
|
|
2579
|
+
if (before !== content) {
|
|
2580
|
+
fileFixes.push("Replaced wrapping classes with 'whitespace-nowrap shrink-0' on compact controls");
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
if (violations.some((v) => v.rule === "no-raw-html-elements" && v.message.includes("<button>"))) {
|
|
2584
|
+
const before = content;
|
|
2585
|
+
content = content.replace(/<button(\s|>)/g, "<Button$1");
|
|
2586
|
+
content = content.replace(/<\/button>/g, "</Button>");
|
|
2587
|
+
if (before !== content) {
|
|
2588
|
+
if (!content.includes("import { Button }") && !content.includes("import Button") && !relPath.includes("components/ui/button")) {
|
|
2589
|
+
content = `import { Button } from "@/components/ui/button";
|
|
2590
|
+
` + content;
|
|
2591
|
+
}
|
|
2592
|
+
fileFixes.push("Upgraded raw <button> to <Button>");
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
if (violations.some((v) => v.rule === "no-raw-html-elements" && v.message.includes("<select>"))) {
|
|
2596
|
+
const before = content;
|
|
2597
|
+
content = content.replace(/<select(\s|>)/g, "<Dropdown$1");
|
|
2598
|
+
content = content.replace(/<\/select>/g, "</Dropdown>");
|
|
2599
|
+
if (before !== content) {
|
|
2600
|
+
if (!content.includes("import { Dropdown }") && !content.includes("import Dropdown") && !relPath.includes("components/ui/dropdown")) {
|
|
2601
|
+
content = `import { Dropdown } from "@/components/ui/dropdown";
|
|
2602
|
+
` + content;
|
|
2603
|
+
}
|
|
2604
|
+
fileFixes.push("Upgraded raw <select> to <Dropdown>");
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
if (violations.some((v) => v.rule === "no-raw-html-elements" && v.message.includes("<textarea>"))) {
|
|
2608
|
+
const before = content;
|
|
2609
|
+
content = content.replace(/<textarea(\s|>)/g, "<Textarea$1");
|
|
2610
|
+
content = content.replace(/<\/textarea>/g, "</Textarea>");
|
|
2611
|
+
if (before !== content) {
|
|
2612
|
+
if (!content.includes("import { Textarea }") && !content.includes("import Textarea") && !relPath.includes("components/ui/textarea")) {
|
|
2613
|
+
content = `import { Textarea } from "@/components/ui/textarea";
|
|
2614
|
+
` + content;
|
|
2615
|
+
}
|
|
2616
|
+
fileFixes.push("Upgraded raw <textarea> to <Textarea>");
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
if (violations.some((v) => v.rule === "no-arbitrary-text-size")) {
|
|
2620
|
+
const before = content;
|
|
2621
|
+
content = content.replace(/\btext-\[(?:13|13\.5|14|14\.5|15|15\.5)px\]\b/g, "text-sm");
|
|
2622
|
+
content = content.replace(/\btext-\[(?:11|11\.5|12|12\.5)px\]\b/g, "text-xs");
|
|
2623
|
+
content = content.replace(/\btext-\[(?:16|17)px\]\b/g, "text-base");
|
|
2624
|
+
if (before !== content) {
|
|
2625
|
+
fileFixes.push("Standardized arbitrary font sizes to compliant token scales");
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
if (violations.some((v) => v.rule === "no-backdrop-blur")) {
|
|
2629
|
+
const before = content;
|
|
2630
|
+
content = content.replace(/\bbackdrop-blur(?:-(?:none|sm|md|lg|xl|2xl|3xl))?\b/g, "");
|
|
2631
|
+
if (before !== content) {
|
|
2632
|
+
fileFixes.push("Removed non-compliant backdrop-blur classes");
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
if (content !== originalContent) {
|
|
2636
|
+
import_node_fs6.default.writeFileSync(fullPath, content, "utf-8");
|
|
2637
|
+
totalModifications += fileFixes.length;
|
|
2638
|
+
details.push({
|
|
2639
|
+
file: relPath,
|
|
2640
|
+
fixes: fileFixes
|
|
2641
|
+
});
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
return {
|
|
2645
|
+
filesFixed: details.length,
|
|
2646
|
+
totalModifications,
|
|
2647
|
+
details
|
|
2648
|
+
};
|
|
2649
|
+
}
|
|
2650
|
+
|
|
2651
|
+
// src/guardrails/index.ts
|
|
2652
|
+
var import_node_fs7 = __toESM(require("fs"), 1);
|
|
2653
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
2654
|
+
function generateAgentInstructions(config) {
|
|
2655
|
+
const allowedRadii = config.tokens?.geometry?.allowedRadii?.join(", ") || "0px, 8px, 12px, 16px, 9999px";
|
|
2656
|
+
const maxControlHeight = config.tokens?.geometry?.maxControlHeight || 36;
|
|
2657
|
+
const bannedColors = config.tokens?.colors?.bannedColorFamilies?.join(", ") || "slate, gray, zinc, stone, etc.";
|
|
2658
|
+
return `<!-- BEGIN:DSE-AGENT-RULES -->
|
|
2659
|
+
# DESIGN SYSTEM ENFORCER (DSE) - MANDATORY RULES
|
|
2660
|
+
|
|
2661
|
+
You MUST adhere to these design system invariants for all UI generation:
|
|
2662
|
+
|
|
2663
|
+
1. COMPONENT INVARIANTS:
|
|
2664
|
+
- NEVER use raw HTML: <button>, <select>, <textarea>, <table>, <input type="checkbox">, <input type="radio">.
|
|
2665
|
+
- ALWAYS use standard primitives: <Button>, <Dropdown>, <Textarea>, <Table>, <Checkbox>, <Chip>.
|
|
2666
|
+
- Never create pseudo-chips using raw <div> or <span> with background fills and borders. Use <Chip>.
|
|
2667
|
+
|
|
2668
|
+
2. WRAP INTEGRITY (COMPACT ELEMENTS):
|
|
2669
|
+
- Interactive compact elements (<Button>, <Chip>, <Badge>, <TableHead>, tabs) MUST NOT wrap text.
|
|
2670
|
+
- ALWAYS include 'whitespace-nowrap shrink-0' on compact interactive components.
|
|
2671
|
+
|
|
2672
|
+
3. GEOMETRY & RADII:
|
|
2673
|
+
- Standard control height for buttons and inputs is strictly max ${maxControlHeight}px ('h-[36px]' or 'h-9').
|
|
2674
|
+
- Allowed corner radii tokens: ${allowedRadii}. Never use random arbitrary radii.
|
|
2675
|
+
|
|
2676
|
+
4. COLOR SCALE & PURITY:
|
|
2677
|
+
- Strictly neutral palette for base surfaces and borders ('neutral-100' through 'neutral-900').
|
|
2678
|
+
- Banned arbitrary color families: ${bannedColors}.
|
|
2679
|
+
- Never use arbitrary inline hex colors ('#1a2b3c') in utility classes. Use design system CSS variables ('var(--primary)').
|
|
2680
|
+
|
|
2681
|
+
5. BEFORE FINISHING UI EDITS:
|
|
2682
|
+
- Run 'npx dse check' to verify zero design system violations.
|
|
2683
|
+
<!-- END:DSE-AGENT-RULES -->
|
|
2684
|
+
`;
|
|
2685
|
+
}
|
|
2686
|
+
function installPreCommitHook(cwd = process.cwd()) {
|
|
2687
|
+
const gitDir = import_node_path7.default.resolve(cwd, ".git");
|
|
2688
|
+
if (!import_node_fs7.default.existsSync(gitDir)) {
|
|
2689
|
+
throw new Error("No .git repository found in this directory.");
|
|
2690
|
+
}
|
|
2691
|
+
const hooksDir = import_node_path7.default.resolve(gitDir, "hooks");
|
|
2692
|
+
if (!import_node_fs7.default.existsSync(hooksDir)) {
|
|
2693
|
+
import_node_fs7.default.mkdirSync(hooksDir, { recursive: true });
|
|
2694
|
+
}
|
|
2695
|
+
const hookPath = import_node_path7.default.resolve(hooksDir, "pre-commit");
|
|
2696
|
+
const hookScript = `#!/usr/bin/env sh
|
|
2697
|
+
# Design System Enforcer (DSE) Pre-Commit Hook
|
|
2698
|
+
echo "
|
|
2699
|
+
\u{1F50D} Running Design System Enforcer pre-commit check..."
|
|
2700
|
+
|
|
2701
|
+
if command -v npx >/dev/null 2>&1; then
|
|
2702
|
+
npx dse check --strict
|
|
2703
|
+
RESULT=$?
|
|
2704
|
+
if [ $RESULT -ne 0 ]; then
|
|
2705
|
+
echo "
|
|
2706
|
+
\u274C Commit rejected: Design System violations found."
|
|
2707
|
+
echo "\u{1F4A1} Run 'npx dse fix' or 'npx dse check' to review issues.
|
|
2708
|
+
"
|
|
2709
|
+
exit 1
|
|
2710
|
+
fi
|
|
2711
|
+
fi
|
|
2712
|
+
`;
|
|
2713
|
+
import_node_fs7.default.writeFileSync(hookPath, hookScript, { mode: 493 });
|
|
2714
|
+
return true;
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2446
2717
|
// src/cli.ts
|
|
2447
2718
|
var program = new import_commander.Command();
|
|
2448
|
-
program.name("dse").description("Design System Enforcer (DSE) - AST-based Linter and Scaffold Engine").version("1.0.
|
|
2719
|
+
program.name("dse").description("Design System Enforcer (DSE) - AST-based Linter and Scaffold Engine").version("1.0.4");
|
|
2449
2720
|
program.command("check [targetDir]", { isDefault: true }).description("Scan codebase and enforce Design System rules").option("-c, --config <path>", "Path to custom dse.config.js / dse.config.json").option("-f, --format <format>", "Output format: 'stylish' | 'json'", "stylish").option("--strict", "Treat warnings as errors", false).action(async (targetDirArg, options) => {
|
|
2450
2721
|
try {
|
|
2451
|
-
const cwd = targetDirArg ?
|
|
2722
|
+
const cwd = targetDirArg ? import_node_path8.default.resolve(process.cwd(), targetDirArg) : process.cwd();
|
|
2452
2723
|
const config = await loadConfig(cwd, options.config);
|
|
2453
2724
|
const result = await runDseScan(config, cwd);
|
|
2454
2725
|
if (options.format === "json") {
|
|
@@ -2461,76 +2732,125 @@ program.command("check [targetDir]", { isDefault: true }).description("Scan code
|
|
|
2461
2732
|
process.exit(1);
|
|
2462
2733
|
}
|
|
2463
2734
|
} catch (err) {
|
|
2464
|
-
console.error(
|
|
2735
|
+
console.error(import_picocolors3.default.red(`
|
|
2736
|
+
\u2716 Error running scan: ${err.message}`));
|
|
2737
|
+
process.exit(1);
|
|
2738
|
+
}
|
|
2739
|
+
});
|
|
2740
|
+
program.command("doctor [targetDir]").description("Perform a visual Design System health audit and compute compliance score").option("-c, --config <path>", "Path to custom dse.config.js / dse.config.json").action(async (targetDirArg, options) => {
|
|
2741
|
+
try {
|
|
2742
|
+
const cwd = targetDirArg ? import_node_path8.default.resolve(process.cwd(), targetDirArg) : process.cwd();
|
|
2743
|
+
const config = await loadConfig(cwd, options.config);
|
|
2744
|
+
const result = await runDseScan(config, cwd);
|
|
2745
|
+
const report = computeDoctorScore(result);
|
|
2746
|
+
console.log(formatDoctorOutput(report));
|
|
2747
|
+
} catch (err) {
|
|
2748
|
+
console.error(import_picocolors3.default.red(`
|
|
2749
|
+
\u2716 Error generating health report: ${err.message}`));
|
|
2750
|
+
process.exit(1);
|
|
2751
|
+
}
|
|
2752
|
+
});
|
|
2753
|
+
program.command("fix [targetDir]").description("Automatically remediate common Design System violations in place").option("-c, --config <path>", "Path to custom dse.config.js / dse.config.json").action(async (targetDirArg, options) => {
|
|
2754
|
+
try {
|
|
2755
|
+
const cwd = targetDirArg ? import_node_path8.default.resolve(process.cwd(), targetDirArg) : process.cwd();
|
|
2756
|
+
const config = await loadConfig(cwd, options.config);
|
|
2757
|
+
console.log(import_picocolors3.default.cyan("\n\u{1F527} Running Design System Enforcer Auto-Fixer...\n"));
|
|
2758
|
+
const fixReport = await runDseFix(config, cwd);
|
|
2759
|
+
if (fixReport.filesFixed === 0) {
|
|
2760
|
+
console.log(import_picocolors3.default.green("\u2714 No auto-fixable violations found. Your codebase is clean!"));
|
|
2761
|
+
} else {
|
|
2762
|
+
console.log(import_picocolors3.default.bold(import_picocolors3.default.green(`\u2714 Successfully fixed ${fixReport.totalModifications} issues across ${fixReport.filesFixed} files:
|
|
2763
|
+
`)));
|
|
2764
|
+
fixReport.details.forEach((item) => {
|
|
2765
|
+
console.log(` ${import_picocolors3.default.bold(import_picocolors3.default.cyan(item.file))}:`);
|
|
2766
|
+
item.fixes.forEach((f) => console.log(` - ${import_picocolors3.default.dim(f)}`));
|
|
2767
|
+
});
|
|
2768
|
+
}
|
|
2769
|
+
console.log("");
|
|
2770
|
+
} catch (err) {
|
|
2771
|
+
console.error(import_picocolors3.default.red(`
|
|
2772
|
+
\u2716 Error during auto-fix: ${err.message}`));
|
|
2465
2773
|
process.exit(1);
|
|
2466
2774
|
}
|
|
2467
2775
|
});
|
|
2468
|
-
program.command("
|
|
2776
|
+
program.command("detect [targetDir]").description("Inspect project architecture, framework, libraries, and design tokens autonomously").action((targetDirArg) => {
|
|
2777
|
+
const cwd = targetDirArg ? import_node_path8.default.resolve(process.cwd(), targetDirArg) : process.cwd();
|
|
2778
|
+
const info = detectProject(cwd);
|
|
2779
|
+
console.log(import_picocolors3.default.bold(import_picocolors3.default.cyan("\n\u{1F50D} DSE Autonomous Project Diagnostics\n")));
|
|
2780
|
+
console.log(` ${import_picocolors3.default.bold("Target Directory:")} ${info.root}`);
|
|
2781
|
+
console.log(` ${import_picocolors3.default.bold("Framework:")} ${import_picocolors3.default.green(info.framework)}`);
|
|
2782
|
+
console.log(` ${import_picocolors3.default.bold("Monorepo:")} ${info.isMonorepo ? import_picocolors3.default.yellow("Yes") : "No"}`);
|
|
2783
|
+
if (info.isMonorepo && info.monorepoWorkspaces.length > 0) {
|
|
2784
|
+
console.log(` ${import_picocolors3.default.bold("Workspaces:")} ${info.monorepoWorkspaces.join(", ")}`);
|
|
2785
|
+
}
|
|
2786
|
+
console.log(` ${import_picocolors3.default.bold("Libraries:")} ${info.libraries.length ? import_picocolors3.default.magenta(info.libraries.join(", ")) : "None"}`);
|
|
2787
|
+
console.log(` ${import_picocolors3.default.bold("Tailwind CSS:")} ${info.hasTailwind ? import_picocolors3.default.green(info.tailwindVersion || "Yes") : "No"}`);
|
|
2788
|
+
console.log(` ${import_picocolors3.default.bold("TypeScript:")} ${info.hasTypeScript ? "Yes" : "No"}`);
|
|
2789
|
+
console.log(` ${import_picocolors3.default.bold("Tokens File:")} ${info.tokensFile ? import_picocolors3.default.cyan(info.tokensFile) : import_picocolors3.default.dim("None (using preset defaults)")}`);
|
|
2790
|
+
console.log(` ${import_picocolors3.default.bold("Default Preset:")} ${import_picocolors3.default.yellow(info.preset)}`);
|
|
2791
|
+
console.log(` ${import_picocolors3.default.bold("Auto Includes:")} ${info.suggestedIncludes.join(", ")}`);
|
|
2792
|
+
console.log(` ${import_picocolors3.default.bold("Auto Ignores:")} ${info.suggestedIgnores.slice(0, 5).join(", ")} (+${Math.max(0, info.suggestedIgnores.length - 5)} more)`);
|
|
2793
|
+
console.log("");
|
|
2794
|
+
});
|
|
2795
|
+
program.command("export-agent").description("Export ultra-compact prompt rules for Cursor, Claude, Antigravity, or Copilot").option("-w, --write <file>", "Target file to write/append into (e.g. .cursorrules, AGENTS.md)").action(async (options) => {
|
|
2469
2796
|
const cwd = process.cwd();
|
|
2470
|
-
|
|
2471
|
-
const
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2797
|
+
const config = await loadConfig(cwd);
|
|
2798
|
+
const instructions = generateAgentInstructions(config);
|
|
2799
|
+
if (options.write) {
|
|
2800
|
+
const targetPath = import_node_path8.default.resolve(cwd, options.write);
|
|
2801
|
+
if (import_node_fs8.default.existsSync(targetPath)) {
|
|
2802
|
+
let existing = import_node_fs8.default.readFileSync(targetPath, "utf-8");
|
|
2803
|
+
const startTag = "<!-- BEGIN:DSE-AGENT-RULES -->";
|
|
2804
|
+
const endTag = "<!-- END:DSE-AGENT-RULES -->";
|
|
2805
|
+
if (existing.includes(startTag) && existing.includes(endTag)) {
|
|
2806
|
+
const regex = new RegExp(`${startTag}[\\s\\S]*?${endTag}`, "g");
|
|
2807
|
+
existing = existing.replace(regex, instructions.trim());
|
|
2808
|
+
} else {
|
|
2809
|
+
existing = `${existing.trim()}
|
|
2810
|
+
|
|
2811
|
+
${instructions.trim()}
|
|
2812
|
+
`;
|
|
2484
2813
|
}
|
|
2485
|
-
|
|
2486
|
-
console.
|
|
2814
|
+
import_node_fs8.default.writeFileSync(targetPath, existing, "utf-8");
|
|
2815
|
+
console.log(import_picocolors3.default.green(`\u2714 Updated AI guardrails in ${options.write}`));
|
|
2816
|
+
} else {
|
|
2817
|
+
import_node_fs8.default.writeFileSync(targetPath, instructions, "utf-8");
|
|
2818
|
+
console.log(import_picocolors3.default.green(`\u2714 Created ${options.write} with AI guardrails`));
|
|
2487
2819
|
}
|
|
2488
|
-
}
|
|
2489
|
-
if (options.withPrimitives) {
|
|
2490
|
-
console.log(import_picocolors2.default.cyan("Scaffolding primitives and theme tokens..."));
|
|
2491
|
-
const created = scaffoldAllPrimitives(cwd);
|
|
2492
|
-
created.forEach((file) => {
|
|
2493
|
-
console.log(import_picocolors2.default.green("\u2714 Created:"), import_picocolors2.default.dim(import_node_path6.default.relative(cwd, file)));
|
|
2494
|
-
});
|
|
2495
2820
|
} else {
|
|
2496
|
-
console.log(
|
|
2497
|
-
import_picocolors2.default.dim("\nTip: Run 'npx dse scaffold' to generate rule-compliant base primitives and tokens anytime.")
|
|
2498
|
-
);
|
|
2821
|
+
console.log(instructions);
|
|
2499
2822
|
}
|
|
2500
|
-
console.log(import_picocolors2.default.bold(import_picocolors2.default.green("\n\u2714 DSE setup complete! Run 'npm run dse' to verify your project.\n")));
|
|
2501
2823
|
});
|
|
2502
|
-
program.command("
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
);
|
|
2824
|
+
program.command("hook [action]").description("Install or manage git pre-commit hooks to block non-compliant commits").action((action) => {
|
|
2825
|
+
if (action === "install" || !action) {
|
|
2826
|
+
try {
|
|
2827
|
+
installPreCommitHook(process.cwd());
|
|
2828
|
+
console.log(import_picocolors3.default.bold(import_picocolors3.default.green("\n\u2714 Successfully installed pre-commit hook in .git/hooks/pre-commit!")));
|
|
2829
|
+
console.log(import_picocolors3.default.dim(" Every git commit will now be checked with 'dse check --strict'.\n"));
|
|
2830
|
+
} catch (err) {
|
|
2831
|
+
console.error(import_picocolors3.default.red(`
|
|
2832
|
+
\u2716 Could not install hook: ${err.message}
|
|
2833
|
+
`));
|
|
2513
2834
|
}
|
|
2514
2835
|
}
|
|
2515
|
-
console.log("");
|
|
2516
2836
|
});
|
|
2517
|
-
program.command("
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2837
|
+
program.command("init").description("Initialize DSE configuration in current repository").option("-p, --preset <name>", "Preset: berserk | recommended | minimal | python", "berserk").action((options) => {
|
|
2838
|
+
scaffoldConfig(process.cwd(), options.preset);
|
|
2839
|
+
});
|
|
2840
|
+
program.command("scaffold-tokens").description("Generate official design system tokens TypeScript/CSS file").action(() => {
|
|
2841
|
+
scaffoldTokens(process.cwd());
|
|
2842
|
+
});
|
|
2843
|
+
program.command("scaffold-primitives").description("Scaffold all canonical zero-compromise design system UI primitives").action(() => {
|
|
2844
|
+
scaffoldAllPrimitives(process.cwd());
|
|
2525
2845
|
});
|
|
2526
2846
|
program.command("export-rules").description("Export rules in Markdown format for AI coding assistants (AGENTS.md, .cursorrules, CLAUDE.md)").option("-w, --write <file>", "Write directly to target file (e.g. AGENTS.md)").action(async (options) => {
|
|
2527
2847
|
const cwd = process.cwd();
|
|
2528
2848
|
const config = await loadConfig(cwd);
|
|
2529
2849
|
const rulesMd = generateMarkdownRules(config);
|
|
2530
2850
|
if (options.write) {
|
|
2531
|
-
const targetFile =
|
|
2532
|
-
if (
|
|
2533
|
-
let existing =
|
|
2851
|
+
const targetFile = import_node_path8.default.resolve(cwd, options.write);
|
|
2852
|
+
if (import_node_fs8.default.existsSync(targetFile)) {
|
|
2853
|
+
let existing = import_node_fs8.default.readFileSync(targetFile, "utf-8");
|
|
2534
2854
|
const startTag = "<!-- BEGIN:design-system-enforcer-rules -->";
|
|
2535
2855
|
const endTag = "<!-- END:design-system-enforcer-rules -->";
|
|
2536
2856
|
if (existing.includes(startTag) && existing.includes(endTag)) {
|
|
@@ -2542,34 +2862,15 @@ program.command("export-rules").description("Export rules in Markdown format for
|
|
|
2542
2862
|
${rulesMd.trim()}
|
|
2543
2863
|
`;
|
|
2544
2864
|
}
|
|
2545
|
-
|
|
2546
|
-
console.log(
|
|
2865
|
+
import_node_fs8.default.writeFileSync(targetFile, existing, "utf-8");
|
|
2866
|
+
console.log(import_picocolors3.default.green(`\u2714 Updated rules in ${options.write}`));
|
|
2547
2867
|
} else {
|
|
2548
|
-
|
|
2549
|
-
console.log(
|
|
2868
|
+
import_node_fs8.default.writeFileSync(targetFile, rulesMd, "utf-8");
|
|
2869
|
+
console.log(import_picocolors3.default.green(`\u2714 Created ${options.write} with design system rules`));
|
|
2550
2870
|
}
|
|
2551
2871
|
} else {
|
|
2552
2872
|
console.log(rulesMd);
|
|
2553
2873
|
}
|
|
2554
2874
|
});
|
|
2555
|
-
program.command("detect [targetDir]").description("Inspect project architecture, framework, libraries, and design tokens autonomously").action((targetDirArg) => {
|
|
2556
|
-
const cwd = targetDirArg ? import_node_path6.default.resolve(process.cwd(), targetDirArg) : process.cwd();
|
|
2557
|
-
const info = detectProject(cwd);
|
|
2558
|
-
console.log(import_picocolors2.default.bold(import_picocolors2.default.cyan("\n\u{1F50D} DSE Autonomous Project Diagnostics\n")));
|
|
2559
|
-
console.log(` ${import_picocolors2.default.bold("Target Directory:")} ${info.root}`);
|
|
2560
|
-
console.log(` ${import_picocolors2.default.bold("Framework:")} ${import_picocolors2.default.green(info.framework)}`);
|
|
2561
|
-
console.log(` ${import_picocolors2.default.bold("Monorepo:")} ${info.isMonorepo ? import_picocolors2.default.yellow("Yes") : "No"}`);
|
|
2562
|
-
if (info.isMonorepo && info.monorepoWorkspaces.length > 0) {
|
|
2563
|
-
console.log(` ${import_picocolors2.default.bold("Workspaces:")} ${info.monorepoWorkspaces.join(", ")}`);
|
|
2564
|
-
}
|
|
2565
|
-
console.log(` ${import_picocolors2.default.bold("Libraries:")} ${info.libraries.length ? import_picocolors2.default.magenta(info.libraries.join(", ")) : "None"}`);
|
|
2566
|
-
console.log(` ${import_picocolors2.default.bold("Tailwind CSS:")} ${info.hasTailwind ? import_picocolors2.default.green(info.tailwindVersion || "Yes") : "No"}`);
|
|
2567
|
-
console.log(` ${import_picocolors2.default.bold("TypeScript:")} ${info.hasTypeScript ? "Yes" : "No"}`);
|
|
2568
|
-
console.log(` ${import_picocolors2.default.bold("Tokens File:")} ${info.tokensFile ? import_picocolors2.default.cyan(info.tokensFile) : import_picocolors2.default.dim("None (using preset defaults)")}`);
|
|
2569
|
-
console.log(` ${import_picocolors2.default.bold("Default Preset:")} ${import_picocolors2.default.yellow(info.preset)}`);
|
|
2570
|
-
console.log(` ${import_picocolors2.default.bold("Auto Includes:")} ${info.suggestedIncludes.join(", ")}`);
|
|
2571
|
-
console.log(` ${import_picocolors2.default.bold("Auto Ignores:")} ${info.suggestedIgnores.slice(0, 5).join(", ")} (+${Math.max(0, info.suggestedIgnores.length - 5)} more)`);
|
|
2572
|
-
console.log("");
|
|
2573
|
-
});
|
|
2574
2875
|
program.parse(process.argv);
|
|
2575
2876
|
//# sourceMappingURL=cli.cjs.map
|