@mikulgohil/ai-kit 1.4.0 → 1.5.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/dist/index.js CHANGED
@@ -15,7 +15,7 @@ var GUIDES_DIR = path.join(PACKAGE_ROOT, "guides");
15
15
  var DOCS_SCAFFOLDS_DIR = path.join(PACKAGE_ROOT, "docs-scaffolds");
16
16
  var AGENTS_DIR = path.join(PACKAGE_ROOT, "agents");
17
17
  var CONTEXTS_DIR = path.join(PACKAGE_ROOT, "contexts");
18
- var VERSION = "1.4.0";
18
+ var VERSION = "1.5.0";
19
19
  var AI_KIT_CONFIG_FILE = "ai-kit.config.json";
20
20
  var GENERATED_FILES = {
21
21
  claudeMd: "CLAUDE.md",
@@ -37,17 +37,18 @@ var TEMPLATE_FRAGMENTS = [
37
37
  "tailwind",
38
38
  "typescript",
39
39
  "monorepo",
40
- "figma"
40
+ "figma",
41
+ "static-site"
41
42
  ];
42
43
 
43
44
  // src/commands/init.ts
44
- import path17 from "path";
45
- import fs8 from "fs-extra";
46
- import { select, confirm } from "@inquirer/prompts";
45
+ import path20 from "path";
46
+ import fs9 from "fs-extra";
47
+ import { select } from "@inquirer/prompts";
47
48
  import ora from "ora";
48
49
 
49
50
  // src/scanner/index.ts
50
- import path10 from "path";
51
+ import path13 from "path";
51
52
 
52
53
  // src/utils.ts
53
54
  import fs from "fs-extra";
@@ -423,12 +424,289 @@ function detectMcpServers(projectPath) {
423
424
  };
424
425
  }
425
426
 
427
+ // src/scanner/design-tokens.ts
428
+ import path10 from "path";
429
+ function detectDesignTokens2(projectPath) {
430
+ const globalsCss = readFileSafe(
431
+ path10.join(projectPath, "src", "app", "globals.css")
432
+ );
433
+ if (globalsCss && globalsCss.includes("@theme")) {
434
+ return parseThemeInline(globalsCss);
435
+ }
436
+ const twConfigPaths = [
437
+ path10.join(projectPath, "tailwind.config.ts"),
438
+ path10.join(projectPath, "tailwind.config.js"),
439
+ path10.join(projectPath, "tailwind.config.mjs")
440
+ ];
441
+ for (const twPath of twConfigPaths) {
442
+ const content = readFileSafe(twPath);
443
+ if (content) {
444
+ return parseTailwindConfig(content);
445
+ }
446
+ }
447
+ if (globalsCss) {
448
+ return parseCssVariables(globalsCss);
449
+ }
450
+ return {
451
+ detected: false,
452
+ colors: [],
453
+ spacing: [],
454
+ fonts: [],
455
+ breakpoints: [],
456
+ source: "none"
457
+ };
458
+ }
459
+ function parseThemeInline(css) {
460
+ const colors = [];
461
+ const spacing = [];
462
+ const fonts = [];
463
+ const breakpoints = [];
464
+ const themeMatch = css.match(/@theme\s*\{([\s\S]*?)\}/);
465
+ if (!themeMatch) {
466
+ return { detected: false, colors: [], spacing: [], fonts: [], breakpoints: [], source: "none" };
467
+ }
468
+ const themeBody = themeMatch[1];
469
+ const lines = themeBody.split("\n");
470
+ for (const line of lines) {
471
+ const trimmed = line.trim();
472
+ if (!trimmed || trimmed.startsWith("/*")) continue;
473
+ const colorMatch = trimmed.match(/^--color-([^:]+):/);
474
+ if (colorMatch) {
475
+ colors.push(colorMatch[1].trim());
476
+ continue;
477
+ }
478
+ const spacingMatch = trimmed.match(/^--spacing-([^:]+):/);
479
+ if (spacingMatch) {
480
+ spacing.push(spacingMatch[1].trim());
481
+ continue;
482
+ }
483
+ const fontMatch = trimmed.match(/^--font-([^:]+):/);
484
+ if (fontMatch) {
485
+ fonts.push(fontMatch[1].trim());
486
+ continue;
487
+ }
488
+ const bpMatch = trimmed.match(/^--breakpoint-([^:]+):/);
489
+ if (bpMatch) {
490
+ breakpoints.push(bpMatch[1].trim());
491
+ continue;
492
+ }
493
+ }
494
+ return {
495
+ detected: colors.length > 0 || fonts.length > 0,
496
+ colors,
497
+ spacing,
498
+ fonts,
499
+ breakpoints,
500
+ source: "theme-inline"
501
+ };
502
+ }
503
+ function parseTailwindConfig(content) {
504
+ const colors = [];
505
+ const spacing = [];
506
+ const fonts = [];
507
+ const breakpoints = [];
508
+ const colorBlock = content.match(/colors\s*:\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}/);
509
+ if (colorBlock) {
510
+ const colorKeys = colorBlock[1].matchAll(/['"]?(\w[\w-]*)['"]?\s*:/g);
511
+ for (const match of colorKeys) {
512
+ if (!["DEFAULT", "transparent", "current", "inherit"].includes(match[1])) {
513
+ colors.push(match[1]);
514
+ }
515
+ }
516
+ }
517
+ const fontBlock = content.match(/fontFamily\s*:\s*\{([^}]*)\}/);
518
+ if (fontBlock) {
519
+ const fontKeys = fontBlock[1].matchAll(/['"]?(\w[\w-]*)['"]?\s*:/g);
520
+ for (const match of fontKeys) {
521
+ fonts.push(match[1]);
522
+ }
523
+ }
524
+ const spacingBlock = content.match(/spacing\s*:\s*\{([^}]*)\}/);
525
+ if (spacingBlock) {
526
+ const spacingKeys = spacingBlock[1].matchAll(/['"]?(\w[\w-]*)['"]?\s*:/g);
527
+ for (const match of spacingKeys) {
528
+ spacing.push(match[1]);
529
+ }
530
+ }
531
+ const screensBlock = content.match(/screens\s*:\s*\{([^}]*)\}/);
532
+ if (screensBlock) {
533
+ const bpKeys = screensBlock[1].matchAll(/['"]?(\w[\w-]*)['"]?\s*:/g);
534
+ for (const match of bpKeys) {
535
+ breakpoints.push(match[1]);
536
+ }
537
+ }
538
+ return {
539
+ detected: colors.length > 0 || fonts.length > 0,
540
+ colors: [...new Set(colors)],
541
+ spacing: [...new Set(spacing)],
542
+ fonts: [...new Set(fonts)],
543
+ breakpoints: [...new Set(breakpoints)],
544
+ source: "tailwind-config"
545
+ };
546
+ }
547
+ function parseCssVariables(css) {
548
+ const colors = [];
549
+ const spacing = [];
550
+ const fonts = [];
551
+ const varMatches = css.matchAll(/--([^:]+)\s*:/g);
552
+ for (const match of varMatches) {
553
+ const name = match[1].trim();
554
+ if (name.startsWith("color-") || name.includes("primary") || name.includes("secondary") || name.includes("accent") || name.includes("background") || name.includes("foreground")) {
555
+ colors.push(name);
556
+ } else if (name.startsWith("spacing-") || name.startsWith("space-") || name.includes("gap")) {
557
+ spacing.push(name);
558
+ } else if (name.startsWith("font-") || name.includes("family") || name.includes("text-")) {
559
+ fonts.push(name);
560
+ }
561
+ }
562
+ return {
563
+ detected: colors.length > 0,
564
+ colors: [...new Set(colors)],
565
+ spacing: [...new Set(spacing)],
566
+ fonts: [...new Set(fonts)],
567
+ breakpoints: [],
568
+ source: colors.length > 0 ? "css-variables" : "none"
569
+ };
570
+ }
571
+
572
+ // src/scanner/static-site.ts
573
+ import path11 from "path";
574
+ import fs2 from "fs-extra";
575
+ function detectStaticSite(projectPath, pkg) {
576
+ const hasStaticExport = checkStaticExport(projectPath);
577
+ const hasGenerateStaticParams = checkGenerateStaticParams(projectPath);
578
+ const hasRevalidate = checkRevalidatePatterns(projectPath);
579
+ const hasServerActions = checkServerActions(projectPath);
580
+ const hasApiRoutes = checkApiRoutes(projectPath);
581
+ let outputMode;
582
+ if (hasStaticExport) {
583
+ outputMode = "export";
584
+ } else if (hasGenerateStaticParams && !hasServerActions && !hasApiRoutes) {
585
+ outputMode = hasRevalidate ? "isr" : "export";
586
+ } else if (hasServerActions || hasApiRoutes) {
587
+ outputMode = hasGenerateStaticParams ? "hybrid" : "ssr";
588
+ } else {
589
+ outputMode = "ssr";
590
+ }
591
+ const isStatic = outputMode === "export" || outputMode === "isr";
592
+ return {
593
+ isStatic,
594
+ outputMode,
595
+ hasGenerateStaticParams,
596
+ hasRevalidate,
597
+ hasStaticExport
598
+ };
599
+ }
600
+ function checkStaticExport(projectPath) {
601
+ const configPaths = [
602
+ path11.join(projectPath, "next.config.js"),
603
+ path11.join(projectPath, "next.config.ts"),
604
+ path11.join(projectPath, "next.config.mjs")
605
+ ];
606
+ for (const configPath of configPaths) {
607
+ const content = readFileSafe(configPath);
608
+ if (content && /output\s*:\s*['"]export['"]/.test(content)) {
609
+ return true;
610
+ }
611
+ }
612
+ return false;
613
+ }
614
+ function checkGenerateStaticParams(projectPath) {
615
+ const appDirs = [
616
+ path11.join(projectPath, "app"),
617
+ path11.join(projectPath, "src", "app")
618
+ ];
619
+ for (const appDir of appDirs) {
620
+ if (dirExists(appDir) && hasPatternInDir(appDir, /generateStaticParams/)) {
621
+ return true;
622
+ }
623
+ }
624
+ const pagesDirs = [
625
+ path11.join(projectPath, "pages"),
626
+ path11.join(projectPath, "src", "pages")
627
+ ];
628
+ for (const pagesDir of pagesDirs) {
629
+ if (dirExists(pagesDir) && hasPatternInDir(pagesDir, /getStaticPaths/)) {
630
+ return true;
631
+ }
632
+ }
633
+ return false;
634
+ }
635
+ function checkRevalidatePatterns(projectPath) {
636
+ const appDirs = [
637
+ path11.join(projectPath, "app"),
638
+ path11.join(projectPath, "src", "app")
639
+ ];
640
+ for (const appDir of appDirs) {
641
+ if (dirExists(appDir) && hasPatternInDir(appDir, /revalidate\s*[:=]/)) {
642
+ return true;
643
+ }
644
+ }
645
+ return false;
646
+ }
647
+ function checkServerActions(projectPath) {
648
+ const srcDirs = [
649
+ path11.join(projectPath, "app"),
650
+ path11.join(projectPath, "src")
651
+ ];
652
+ for (const srcDir of srcDirs) {
653
+ if (dirExists(srcDir) && hasPatternInDir(srcDir, /['"]use server['"]/)) {
654
+ return true;
655
+ }
656
+ }
657
+ return false;
658
+ }
659
+ function checkApiRoutes(projectPath) {
660
+ const appApiDirs = [
661
+ path11.join(projectPath, "app", "api"),
662
+ path11.join(projectPath, "src", "app", "api")
663
+ ];
664
+ for (const apiDir of appApiDirs) {
665
+ if (dirExists(apiDir)) return true;
666
+ }
667
+ const pagesApiDirs = [
668
+ path11.join(projectPath, "pages", "api"),
669
+ path11.join(projectPath, "src", "pages", "api")
670
+ ];
671
+ for (const apiDir of pagesApiDirs) {
672
+ if (dirExists(apiDir)) return true;
673
+ }
674
+ return false;
675
+ }
676
+ var SCAN_IGNORE = ["node_modules", ".next", ".git", "dist", "build", ".turbo"];
677
+ function hasPatternInDir(dir, pattern, depth = 0) {
678
+ if (depth > 5) return false;
679
+ try {
680
+ const entries = fs2.readdirSync(dir, { withFileTypes: true });
681
+ for (const entry of entries) {
682
+ if (SCAN_IGNORE.includes(entry.name)) continue;
683
+ const full = path11.join(dir, entry.name);
684
+ if (entry.isDirectory()) {
685
+ if (hasPatternInDir(full, pattern, depth + 1)) return true;
686
+ } else if (/\.(tsx?|jsx?|mjs)$/.test(entry.name)) {
687
+ const content = readFileSafe(full);
688
+ if (content && pattern.test(content)) return true;
689
+ }
690
+ }
691
+ } catch {
692
+ }
693
+ return false;
694
+ }
695
+
696
+ // src/scanner/aiignore.ts
697
+ import path12 from "path";
698
+ function loadAiIgnorePatterns(projectPath) {
699
+ const content = readFileSafe(path12.join(projectPath, ".aiignore"));
700
+ if (!content) return [];
701
+ return content.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
702
+ }
703
+
426
704
  // src/scanner/index.ts
427
705
  async function scanProject(projectPath) {
428
- const pkgPath = path10.join(projectPath, "package.json");
706
+ const pkgPath = path13.join(projectPath, "package.json");
429
707
  const pkg = readJsonSafe(pkgPath) || {};
430
708
  const scripts = pkg.scripts || {};
431
- const projectName = pkg.name || path10.basename(projectPath);
709
+ const projectName = pkg.name || path13.basename(projectPath);
432
710
  const nextjsResult = detectNextjs(projectPath, pkg);
433
711
  const sitecoreResult = detectSitecore(pkg);
434
712
  const stylingResult = detectStyling(projectPath, pkg);
@@ -438,6 +716,9 @@ async function scanProject(projectPath) {
438
716
  const figmaResult = detectFigma(projectPath, pkg);
439
717
  const toolsResult = detectTools(projectPath, pkg);
440
718
  const mcpResult = detectMcpServers(projectPath);
719
+ const designTokensResult = detectDesignTokens2(projectPath);
720
+ const staticSiteResult = detectStaticSite(projectPath, pkg);
721
+ const aiIgnorePatterns = loadAiIgnorePatterns(projectPath);
441
722
  const figmaDetected = figmaResult.figmaMcp || figmaResult.figmaCodeCli || figmaResult.designTokens;
442
723
  return {
443
724
  ...nextjsResult,
@@ -449,6 +730,9 @@ async function scanProject(projectPath) {
449
730
  detected: figmaDetected,
450
731
  ...figmaResult
451
732
  },
733
+ designTokens: designTokensResult,
734
+ staticSite: staticSiteResult,
735
+ aiIgnorePatterns,
452
736
  tools: toolsResult,
453
737
  mcpServers: mcpResult,
454
738
  packageManager,
@@ -459,10 +743,10 @@ async function scanProject(projectPath) {
459
743
  }
460
744
 
461
745
  // src/generator/assembler.ts
462
- import path11 from "path";
463
- import fs2 from "fs-extra";
746
+ import path14 from "path";
747
+ import fs3 from "fs-extra";
464
748
  function readTemplate(relativePath) {
465
- const fullPath = path11.join(TEMPLATES_DIR, relativePath);
749
+ const fullPath = path14.join(TEMPLATES_DIR, relativePath);
466
750
  const content = readFileSafe(fullPath);
467
751
  if (!content) {
468
752
  throw new Error(`Template not found: ${relativePath}`);
@@ -470,12 +754,12 @@ function readTemplate(relativePath) {
470
754
  return content.trim();
471
755
  }
472
756
  function loadCustomFragments(projectPath) {
473
- const customDir = path11.join(projectPath, ".ai-kit", "fragments");
474
- if (!fs2.existsSync(customDir)) return [];
757
+ const customDir = path14.join(projectPath, ".ai-kit", "fragments");
758
+ if (!fs3.existsSync(customDir)) return [];
475
759
  try {
476
- const files = fs2.readdirSync(customDir).filter((f) => f.endsWith(".md"));
760
+ const files = fs3.readdirSync(customDir).filter((f) => f.endsWith(".md"));
477
761
  return files.map((f) => {
478
- const content = fs2.readFileSync(path11.join(customDir, f), "utf-8");
762
+ const content = fs3.readFileSync(path14.join(customDir, f), "utf-8");
479
763
  return content.trim();
480
764
  }).filter(Boolean);
481
765
  } catch {
@@ -548,6 +832,9 @@ function selectFragments(scan) {
548
832
  if (scan.figma?.detected) {
549
833
  fragments.push("figma");
550
834
  }
835
+ if (scan.staticSite?.isStatic) {
836
+ fragments.push("static-site");
837
+ }
551
838
  return fragments;
552
839
  }
553
840
  function generateClaudeMd(scan, options) {
@@ -587,13 +874,34 @@ function buildVariables(scan) {
587
874
  const scripts = Object.entries(scan.scripts).filter(
588
875
  ([key]) => ["dev", "build", "start", "lint", "test", "type-check", "typecheck"].includes(key)
589
876
  ).map(([key, value]) => `- \`${scan.packageManager} run ${key}\` \u2192 \`${value}\``).join("\n");
877
+ let designTokenSummary = "";
878
+ if (scan.designTokens?.detected) {
879
+ const parts = [];
880
+ if (scan.designTokens.colors.length > 0) {
881
+ parts.push(`Colors: ${scan.designTokens.colors.slice(0, 15).join(", ")}${scan.designTokens.colors.length > 15 ? ` (+${scan.designTokens.colors.length - 15} more)` : ""}`);
882
+ }
883
+ if (scan.designTokens.fonts.length > 0) {
884
+ parts.push(`Fonts: ${scan.designTokens.fonts.join(", ")}`);
885
+ }
886
+ if (scan.designTokens.spacing.length > 0) {
887
+ parts.push(`Spacing: ${scan.designTokens.spacing.slice(0, 10).join(", ")}${scan.designTokens.spacing.length > 10 ? ` (+${scan.designTokens.spacing.length - 10} more)` : ""}`);
888
+ }
889
+ if (scan.designTokens.breakpoints.length > 0) {
890
+ parts.push(`Breakpoints: ${scan.designTokens.breakpoints.join(", ")}`);
891
+ }
892
+ designTokenSummary = parts.map((p) => `- ${p}`).join("\n");
893
+ }
894
+ const aiIgnoreSummary = scan.aiIgnorePatterns.length > 0 ? scan.aiIgnorePatterns.map((p) => `- \`${p}\``).join("\n") : "";
590
895
  return {
591
896
  projectName: scan.projectName,
592
897
  techStack: techStack.join(" \xB7 "),
593
898
  packageManager: scan.packageManager,
594
899
  routerType: scan.routerType || "unknown",
595
900
  scripts: scripts || "- No scripts detected",
596
- framework: scan.framework
901
+ framework: scan.framework,
902
+ designTokens: designTokenSummary || "- No design tokens detected",
903
+ aiIgnorePatterns: aiIgnoreSummary || "- No .aiignore file",
904
+ outputMode: scan.staticSite?.outputMode || "ssr"
597
905
  };
598
906
  }
599
907
 
@@ -665,6 +973,10 @@ var MDC_CONFIG = {
665
973
  figma: {
666
974
  description: "Figma-to-code workflow and design token rules",
667
975
  globs: "src/components/**/*.{ts,tsx}, tokens/**/*"
976
+ },
977
+ "static-site": {
978
+ description: "Static site generation (SSG/ISR) rules and patterns",
979
+ globs: "app/**/*.{ts,tsx}, src/app/**/*.{ts,tsx}, next.config.*"
668
980
  }
669
981
  };
670
982
  function generateMdcFiles(scan) {
@@ -709,7 +1021,8 @@ function generateConfig(scan, templates, commands, guides, options) {
709
1021
  hooks: options?.hooks || false,
710
1022
  hookProfile: options?.hookProfile || "standard",
711
1023
  strictness: options?.strictness || "standard",
712
- customFragments: options?.customFragments || []
1024
+ customFragments: options?.customFragments || [],
1025
+ tools: options?.tools || { claude: true, cursor: true }
713
1026
  };
714
1027
  }
715
1028
 
@@ -916,8 +1229,8 @@ function generateSettingsLocal(scan, profile = "standard") {
916
1229
  }
917
1230
 
918
1231
  // src/copier/skills.ts
919
- import path12 from "path";
920
- import fs3 from "fs-extra";
1232
+ import path15 from "path";
1233
+ import fs4 from "fs-extra";
921
1234
  var AVAILABLE_SKILLS = [
922
1235
  "prompt-help",
923
1236
  "review",
@@ -1015,35 +1328,35 @@ var SKILL_DESCRIPTIONS = {
1015
1328
  async function copySkills(targetDir) {
1016
1329
  const copied = [];
1017
1330
  for (const skill of AVAILABLE_SKILLS) {
1018
- const src = path12.join(COMMANDS_DIR, `${skill}.md`);
1019
- if (!await fs3.pathExists(src)) continue;
1020
- const content = await fs3.readFile(src, "utf-8");
1331
+ const src = path15.join(COMMANDS_DIR, `${skill}.md`);
1332
+ if (!await fs4.pathExists(src)) continue;
1333
+ const content = await fs4.readFile(src, "utf-8");
1021
1334
  const description = SKILL_DESCRIPTIONS[skill] || skill;
1022
- const claudeSkillDir = path12.join(targetDir, ".claude", "skills", skill);
1023
- await fs3.ensureDir(claudeSkillDir);
1024
- await fs3.writeFile(
1025
- path12.join(claudeSkillDir, "SKILL.md"),
1335
+ const claudeSkillDir = path15.join(targetDir, ".claude", "skills", skill);
1336
+ await fs4.ensureDir(claudeSkillDir);
1337
+ await fs4.writeFile(
1338
+ path15.join(claudeSkillDir, "SKILL.md"),
1026
1339
  content,
1027
1340
  "utf-8"
1028
1341
  );
1029
- const cursorSkillDir = path12.join(targetDir, ".cursor", "skills", skill);
1030
- await fs3.ensureDir(cursorSkillDir);
1031
- await fs3.writeFile(
1032
- path12.join(cursorSkillDir, "SKILL.md"),
1342
+ const cursorSkillDir = path15.join(targetDir, ".cursor", "skills", skill);
1343
+ await fs4.ensureDir(cursorSkillDir);
1344
+ await fs4.writeFile(
1345
+ path15.join(cursorSkillDir, "SKILL.md"),
1033
1346
  content,
1034
1347
  "utf-8"
1035
1348
  );
1036
- const legacyDir = path12.join(targetDir, ".claude", "commands");
1037
- await fs3.ensureDir(legacyDir);
1038
- await fs3.copy(src, path12.join(legacyDir, `${skill}.md`), { overwrite: true });
1349
+ const legacyDir = path15.join(targetDir, ".claude", "commands");
1350
+ await fs4.ensureDir(legacyDir);
1351
+ await fs4.copy(src, path15.join(legacyDir, `${skill}.md`), { overwrite: true });
1039
1352
  copied.push(skill);
1040
1353
  }
1041
1354
  return copied;
1042
1355
  }
1043
1356
 
1044
1357
  // src/copier/guides.ts
1045
- import path13 from "path";
1046
- import fs4 from "fs-extra";
1358
+ import path16 from "path";
1359
+ import fs5 from "fs-extra";
1047
1360
  var AVAILABLE_GUIDES = [
1048
1361
  "getting-started",
1049
1362
  "prompt-playbook",
@@ -1053,14 +1366,14 @@ var AVAILABLE_GUIDES = [
1053
1366
  "hooks-and-agents"
1054
1367
  ];
1055
1368
  async function copyGuides(targetDir) {
1056
- const guidesTarget = path13.join(targetDir, "ai-kit", "guides");
1057
- await fs4.ensureDir(guidesTarget);
1369
+ const guidesTarget = path16.join(targetDir, "ai-kit", "guides");
1370
+ await fs5.ensureDir(guidesTarget);
1058
1371
  const copied = [];
1059
1372
  for (const guide of AVAILABLE_GUIDES) {
1060
- const src = path13.join(GUIDES_DIR, `${guide}.md`);
1061
- const dest = path13.join(guidesTarget, `${guide}.md`);
1062
- if (await fs4.pathExists(src)) {
1063
- await fs4.copy(src, dest, { overwrite: true });
1373
+ const src = path16.join(GUIDES_DIR, `${guide}.md`);
1374
+ const dest = path16.join(guidesTarget, `${guide}.md`);
1375
+ if (await fs5.pathExists(src)) {
1376
+ await fs5.copy(src, dest, { overwrite: true });
1064
1377
  copied.push(guide);
1065
1378
  }
1066
1379
  }
@@ -1068,21 +1381,21 @@ async function copyGuides(targetDir) {
1068
1381
  }
1069
1382
 
1070
1383
  // src/copier/docs.ts
1071
- import path14 from "path";
1072
- import fs5 from "fs-extra";
1384
+ import path17 from "path";
1385
+ import fs6 from "fs-extra";
1073
1386
  var DOC_SCAFFOLDS = ["mistakes-log", "decisions-log", "time-log"];
1074
1387
  async function scaffoldDocs(targetDir) {
1075
- const docsTarget = path14.join(targetDir, "docs");
1076
- await fs5.ensureDir(docsTarget);
1388
+ const docsTarget = path17.join(targetDir, "docs");
1389
+ await fs6.ensureDir(docsTarget);
1077
1390
  const created = [];
1078
1391
  for (const doc of DOC_SCAFFOLDS) {
1079
- const src = path14.join(DOCS_SCAFFOLDS_DIR, `${doc}.md`);
1080
- const dest = path14.join(docsTarget, `${doc}.md`);
1081
- if (await fs5.pathExists(dest)) {
1392
+ const src = path17.join(DOCS_SCAFFOLDS_DIR, `${doc}.md`);
1393
+ const dest = path17.join(docsTarget, `${doc}.md`);
1394
+ if (await fs6.pathExists(dest)) {
1082
1395
  continue;
1083
1396
  }
1084
- if (await fs5.pathExists(src)) {
1085
- await fs5.copy(src, dest);
1397
+ if (await fs6.pathExists(src)) {
1398
+ await fs6.copy(src, dest);
1086
1399
  created.push(doc);
1087
1400
  }
1088
1401
  }
@@ -1090,8 +1403,8 @@ async function scaffoldDocs(targetDir) {
1090
1403
  }
1091
1404
 
1092
1405
  // src/copier/agents.ts
1093
- import path15 from "path";
1094
- import fs6 from "fs-extra";
1406
+ import path18 from "path";
1407
+ import fs7 from "fs-extra";
1095
1408
  var UNIVERSAL_AGENTS = [
1096
1409
  "planner",
1097
1410
  "code-reviewer",
@@ -1116,22 +1429,22 @@ var CONDITIONAL_AGENTS = [
1116
1429
  }
1117
1430
  ];
1118
1431
  async function copyAgents(targetDir, scan) {
1119
- const agentsTarget = path15.join(targetDir, ".claude", "agents");
1120
- await fs6.ensureDir(agentsTarget);
1432
+ const agentsTarget = path18.join(targetDir, ".claude", "agents");
1433
+ await fs7.ensureDir(agentsTarget);
1121
1434
  const copied = [];
1122
1435
  for (const agent of UNIVERSAL_AGENTS) {
1123
- const src = path15.join(AGENTS_DIR, `${agent}.md`);
1124
- if (!await fs6.pathExists(src)) continue;
1125
- await fs6.copy(src, path15.join(agentsTarget, `${agent}.md`), {
1436
+ const src = path18.join(AGENTS_DIR, `${agent}.md`);
1437
+ if (!await fs7.pathExists(src)) continue;
1438
+ await fs7.copy(src, path18.join(agentsTarget, `${agent}.md`), {
1126
1439
  overwrite: true
1127
1440
  });
1128
1441
  copied.push(agent);
1129
1442
  }
1130
1443
  for (const { name, condition } of CONDITIONAL_AGENTS) {
1131
1444
  if (!condition(scan)) continue;
1132
- const src = path15.join(AGENTS_DIR, `${name}.md`);
1133
- if (!await fs6.pathExists(src)) continue;
1134
- await fs6.copy(src, path15.join(agentsTarget, `${name}.md`), {
1445
+ const src = path18.join(AGENTS_DIR, `${name}.md`);
1446
+ if (!await fs7.pathExists(src)) continue;
1447
+ await fs7.copy(src, path18.join(agentsTarget, `${name}.md`), {
1135
1448
  overwrite: true
1136
1449
  });
1137
1450
  copied.push(name);
@@ -1140,17 +1453,17 @@ async function copyAgents(targetDir, scan) {
1140
1453
  }
1141
1454
 
1142
1455
  // src/copier/contexts.ts
1143
- import path16 from "path";
1144
- import fs7 from "fs-extra";
1456
+ import path19 from "path";
1457
+ import fs8 from "fs-extra";
1145
1458
  var AVAILABLE_CONTEXTS = ["dev", "review", "research"];
1146
1459
  async function copyContexts(targetDir) {
1147
- const contextsTarget = path16.join(targetDir, ".claude", "contexts");
1148
- await fs7.ensureDir(contextsTarget);
1460
+ const contextsTarget = path19.join(targetDir, ".claude", "contexts");
1461
+ await fs8.ensureDir(contextsTarget);
1149
1462
  const copied = [];
1150
1463
  for (const context of AVAILABLE_CONTEXTS) {
1151
- const src = path16.join(CONTEXTS_DIR, `${context}.md`);
1152
- if (!await fs7.pathExists(src)) continue;
1153
- await fs7.copy(src, path16.join(contextsTarget, `${context}.md`), {
1464
+ const src = path19.join(CONTEXTS_DIR, `${context}.md`);
1465
+ if (!await fs8.pathExists(src)) continue;
1466
+ await fs8.copy(src, path19.join(contextsTarget, `${context}.md`), {
1154
1467
  overwrite: true
1155
1468
  });
1156
1469
  copied.push(context);
@@ -1160,18 +1473,36 @@ async function copyContexts(targetDir) {
1160
1473
 
1161
1474
  // src/commands/init.ts
1162
1475
  async function initCommand(targetPath) {
1163
- const projectDir = path17.resolve(targetPath || process.cwd());
1476
+ const projectDir = path20.resolve(targetPath || process.cwd());
1164
1477
  logSection("AI Kit \u2014 Project Setup");
1165
1478
  logInfo(`Scanning: ${projectDir}`);
1166
- const configPath = path17.join(projectDir, AI_KIT_CONFIG_FILE);
1479
+ const configPath = path20.join(projectDir, AI_KIT_CONFIG_FILE);
1480
+ let savedConfig = null;
1481
+ let reuseMode = false;
1167
1482
  if (fileExists(configPath)) {
1168
- const overwrite = await confirm({
1169
- message: "AI Kit is already configured in this project. Re-initialize?",
1170
- default: false
1171
- });
1172
- if (!overwrite) {
1173
- logInfo("Cancelled. Use `ai-kit update` to refresh configs.");
1174
- return;
1483
+ savedConfig = readJsonSafe(configPath);
1484
+ if (savedConfig) {
1485
+ logSection("Existing Profile Found");
1486
+ logInfo(`Tools: ${formatToolsLabel(savedConfig.tools)}`);
1487
+ logInfo(`Strictness: ${savedConfig.strictness}`);
1488
+ logInfo(`Hook Profile: ${savedConfig.hookProfile}`);
1489
+ logInfo(`Generated: ${savedConfig.generatedAt}`);
1490
+ logInfo(`Version: v${savedConfig.version}`);
1491
+ console.log("");
1492
+ const action = await select({
1493
+ message: "What would you like to do?",
1494
+ choices: [
1495
+ { name: "Update \u2014 re-scan project, keep saved settings", value: "reuse" },
1496
+ { name: "Re-configure \u2014 change settings from scratch", value: "fresh" },
1497
+ { name: "Cancel", value: "cancel" }
1498
+ ],
1499
+ default: "reuse"
1500
+ });
1501
+ if (action === "cancel") {
1502
+ logInfo("Cancelled.");
1503
+ return;
1504
+ }
1505
+ reuseMode = action === "reuse";
1175
1506
  }
1176
1507
  }
1177
1508
  const spinner = ora("Scanning project...").start();
@@ -1192,13 +1523,24 @@ async function initCommand(targetPath) {
1192
1523
  logInfo(`Monorepo: ${scan.monorepo ? `Yes (${scan.monorepoTool})` : "No"}`);
1193
1524
  logInfo(`Package Manager: ${scan.packageManager}`);
1194
1525
  logInfo(`Formatter: ${scan.tools.biome ? "Biome" : scan.tools.prettier ? "Prettier" : "None detected"}`);
1195
- const clarifications = await askClarifications(scan);
1196
- scan = applyClarifications(scan, clarifications);
1197
- const tools = await selectTools();
1198
- const strictness = await selectStrictness();
1199
- const hookProfile = await selectHookProfile();
1526
+ if (scan.staticSite?.isStatic) {
1527
+ logInfo(`Static Site: Yes (${scan.staticSite.outputMode})`);
1528
+ }
1529
+ if (scan.designTokens?.detected) {
1530
+ logInfo(`Design Tokens: ${scan.designTokens.colors.length} colors, ${scan.designTokens.fonts.length} fonts (${scan.designTokens.source})`);
1531
+ }
1532
+ if (scan.aiIgnorePatterns.length > 0) {
1533
+ logInfo(`.aiignore: ${scan.aiIgnorePatterns.length} patterns loaded`);
1534
+ }
1535
+ if (!reuseMode) {
1536
+ const clarifications = await askClarifications(scan);
1537
+ scan = applyClarifications(scan, clarifications);
1538
+ }
1539
+ const tools = reuseMode && savedConfig?.tools ? savedConfig.tools : await selectTools();
1540
+ const strictness = reuseMode && savedConfig?.strictness ? savedConfig.strictness : await selectStrictness();
1541
+ const hookProfile = reuseMode && savedConfig?.hookProfile ? savedConfig.hookProfile : await selectHookProfile();
1200
1542
  const customFragments = loadCustomFragments(projectDir);
1201
- const conflict = await selectConflictStrategy(projectDir);
1543
+ const conflict = reuseMode ? "overwrite" : await selectConflictStrategy(projectDir);
1202
1544
  logSection("Generating Files");
1203
1545
  const results = await generate(projectDir, scan, tools, conflict, {
1204
1546
  strictness,
@@ -1228,6 +1570,13 @@ async function initCommand(targetPath) {
1228
1570
  logInfo("Run `ai-kit audit` to check your AI agent configuration health.");
1229
1571
  logInfo("Check ai-kit/guides/getting-started.md to get started.");
1230
1572
  }
1573
+ function formatToolsLabel(tools) {
1574
+ if (!tools) return "Unknown";
1575
+ if (tools.claude && tools.cursor) return "Claude Code + Cursor";
1576
+ if (tools.claude) return "Claude Code only";
1577
+ if (tools.cursor) return "Cursor only";
1578
+ return "None";
1579
+ }
1231
1580
  function formatFramework(scan) {
1232
1581
  if (scan.framework === "nextjs") {
1233
1582
  const version = scan.nextjsVersion ? ` ${scan.nextjsVersion}` : "";
@@ -1304,7 +1653,7 @@ async function selectHookProfile() {
1304
1653
  });
1305
1654
  }
1306
1655
  async function selectConflictStrategy(projectDir) {
1307
- const hasExisting = fileExists(path17.join(projectDir, GENERATED_FILES.claudeMd)) || fileExists(path17.join(projectDir, GENERATED_FILES.cursorRules));
1656
+ const hasExisting = fileExists(path20.join(projectDir, GENERATED_FILES.claudeMd)) || fileExists(path20.join(projectDir, GENERATED_FILES.cursorRules));
1308
1657
  if (!hasExisting) return "overwrite";
1309
1658
  return select({
1310
1659
  message: "Existing AI config files detected. How should we handle conflicts?",
@@ -1334,10 +1683,10 @@ async function generate(projectDir, scan, tools, conflict, opts) {
1334
1683
  docs: []
1335
1684
  };
1336
1685
  if (tools.claude) {
1337
- const claudeMdPath = path17.join(projectDir, GENERATED_FILES.claudeMd);
1686
+ const claudeMdPath = path20.join(projectDir, GENERATED_FILES.claudeMd);
1338
1687
  if (conflict === "overwrite" || !fileExists(claudeMdPath)) {
1339
1688
  const content = generateClaudeMd(scan, { strictness: opts?.strictness, customFragments: opts?.customFragments });
1340
- await fs8.writeFile(claudeMdPath, content, "utf-8");
1689
+ await fs9.writeFile(claudeMdPath, content, "utf-8");
1341
1690
  result.claudeMd = true;
1342
1691
  } else {
1343
1692
  logWarning("CLAUDE.md exists, skipping");
@@ -1346,26 +1695,26 @@ async function generate(projectDir, scan, tools, conflict, opts) {
1346
1695
  result.agents = await copyAgents(projectDir, scan);
1347
1696
  result.contexts = await copyContexts(projectDir);
1348
1697
  const hookProfile = opts?.hookProfile || "standard";
1349
- const settingsLocalPath = path17.join(projectDir, GENERATED_FILES.claudeSettingsLocal);
1698
+ const settingsLocalPath = path20.join(projectDir, GENERATED_FILES.claudeSettingsLocal);
1350
1699
  const settingsLocal = generateSettingsLocal(scan, hookProfile);
1351
- await fs8.ensureDir(path17.dirname(settingsLocalPath));
1352
- await fs8.writeJson(settingsLocalPath, settingsLocal, { spaces: 2 });
1700
+ await fs9.ensureDir(path20.dirname(settingsLocalPath));
1701
+ await fs9.writeJson(settingsLocalPath, settingsLocal, { spaces: 2 });
1353
1702
  result.hooks = true;
1354
1703
  }
1355
1704
  if (tools.cursor) {
1356
- const cursorPath = path17.join(projectDir, GENERATED_FILES.cursorRules);
1705
+ const cursorPath = path20.join(projectDir, GENERATED_FILES.cursorRules);
1357
1706
  if (conflict === "overwrite" || !fileExists(cursorPath)) {
1358
1707
  const content = generateCursorRules(scan, { strictness: opts?.strictness, customFragments: opts?.customFragments });
1359
- await fs8.writeFile(cursorPath, content, "utf-8");
1708
+ await fs9.writeFile(cursorPath, content, "utf-8");
1360
1709
  result.cursorRules = true;
1361
1710
  } else {
1362
1711
  logWarning(".cursorrules exists, skipping");
1363
1712
  }
1364
- const mdcDir = path17.join(projectDir, GENERATED_FILES.cursorMdcDir);
1365
- await fs8.ensureDir(mdcDir);
1713
+ const mdcDir = path20.join(projectDir, GENERATED_FILES.cursorMdcDir);
1714
+ await fs9.ensureDir(mdcDir);
1366
1715
  const mdcFiles = generateMdcFiles(scan);
1367
1716
  for (const mdc of mdcFiles) {
1368
- await fs8.writeFile(path17.join(mdcDir, mdc.filename), mdc.content, "utf-8");
1717
+ await fs9.writeFile(path20.join(mdcDir, mdc.filename), mdc.content, "utf-8");
1369
1718
  }
1370
1719
  result.cursorMdcFiles = mdcFiles.length;
1371
1720
  }
@@ -1380,10 +1729,11 @@ async function generate(projectDir, scan, tools, conflict, opts) {
1380
1729
  agents: result.agents,
1381
1730
  contexts: result.contexts,
1382
1731
  hooks: result.hooks,
1383
- hookProfile: opts?.hookProfile
1732
+ hookProfile: opts?.hookProfile,
1733
+ tools
1384
1734
  });
1385
- await fs8.writeJson(
1386
- path17.join(projectDir, AI_KIT_CONFIG_FILE),
1735
+ await fs9.writeJson(
1736
+ path20.join(projectDir, AI_KIT_CONFIG_FILE),
1387
1737
  config,
1388
1738
  { spaces: 2 }
1389
1739
  );
@@ -1459,13 +1809,13 @@ function showRecommendations(scan) {
1459
1809
  }
1460
1810
 
1461
1811
  // src/commands/update.ts
1462
- import path18 from "path";
1463
- import fs9 from "fs-extra";
1812
+ import path21 from "path";
1813
+ import fs10 from "fs-extra";
1464
1814
  import ora2 from "ora";
1465
1815
  import { confirm as confirm2 } from "@inquirer/prompts";
1466
1816
  async function updateCommand(targetPath) {
1467
- const projectDir = path18.resolve(targetPath || process.cwd());
1468
- const configPath = path18.join(projectDir, AI_KIT_CONFIG_FILE);
1817
+ const projectDir = path21.resolve(targetPath || process.cwd());
1818
+ const configPath = path21.join(projectDir, AI_KIT_CONFIG_FILE);
1469
1819
  if (!fileExists(configPath)) {
1470
1820
  logError("No ai-kit.config.json found. Run `ai-kit init` first.");
1471
1821
  return;
@@ -1493,37 +1843,39 @@ async function updateCommand(targetPath) {
1493
1843
  logSection("Updating Files");
1494
1844
  const strictness = existingConfig.strictness || "standard";
1495
1845
  const hookProfile = existingConfig.hookProfile || "standard";
1846
+ const tools = existingConfig.tools || { claude: true, cursor: true };
1496
1847
  const customFragments = loadCustomFragments(projectDir);
1497
1848
  const genOpts = { strictness, customFragments };
1849
+ logInfo(`Using saved profile \u2014 Tools: ${tools.claude && tools.cursor ? "Claude Code + Cursor" : tools.claude ? "Claude Code" : "Cursor"} \xB7 Strictness: ${strictness} \xB7 Hooks: ${hookProfile}`);
1498
1850
  const templates = [];
1499
- if (existingConfig.templates.includes("CLAUDE.md") || fileExists(path18.join(projectDir, GENERATED_FILES.claudeMd))) {
1500
- const claudeMdPath = path18.join(projectDir, GENERATED_FILES.claudeMd);
1851
+ if (tools.claude && (existingConfig.templates.includes("CLAUDE.md") || fileExists(path21.join(projectDir, GENERATED_FILES.claudeMd)))) {
1852
+ const claudeMdPath = path21.join(projectDir, GENERATED_FILES.claudeMd);
1501
1853
  const newContent = generateClaudeMd(scan, genOpts);
1502
1854
  const existing = readFileSafe(claudeMdPath);
1503
1855
  if (existing) {
1504
- await fs9.writeFile(claudeMdPath, mergeWithMarkers(existing, newContent), "utf-8");
1856
+ await fs10.writeFile(claudeMdPath, mergeWithMarkers(existing, newContent), "utf-8");
1505
1857
  } else {
1506
- await fs9.writeFile(claudeMdPath, newContent, "utf-8");
1858
+ await fs10.writeFile(claudeMdPath, newContent, "utf-8");
1507
1859
  }
1508
1860
  templates.push("CLAUDE.md");
1509
1861
  logSuccess("CLAUDE.md updated");
1510
1862
  }
1511
- if (existingConfig.templates.includes(".cursorrules") || fileExists(path18.join(projectDir, GENERATED_FILES.cursorRules))) {
1512
- const cursorRulesPath = path18.join(projectDir, GENERATED_FILES.cursorRules);
1863
+ if (tools.cursor && (existingConfig.templates.includes(".cursorrules") || fileExists(path21.join(projectDir, GENERATED_FILES.cursorRules)))) {
1864
+ const cursorRulesPath = path21.join(projectDir, GENERATED_FILES.cursorRules);
1513
1865
  const newContent = generateCursorRules(scan, genOpts);
1514
1866
  const existing = readFileSafe(cursorRulesPath);
1515
1867
  if (existing) {
1516
- await fs9.writeFile(cursorRulesPath, mergeWithMarkers(existing, newContent), "utf-8");
1868
+ await fs10.writeFile(cursorRulesPath, mergeWithMarkers(existing, newContent), "utf-8");
1517
1869
  } else {
1518
- await fs9.writeFile(cursorRulesPath, newContent, "utf-8");
1870
+ await fs10.writeFile(cursorRulesPath, newContent, "utf-8");
1519
1871
  }
1520
1872
  templates.push(".cursorrules");
1521
1873
  logSuccess(".cursorrules updated");
1522
- const mdcDir = path18.join(projectDir, GENERATED_FILES.cursorMdcDir);
1523
- await fs9.ensureDir(mdcDir);
1874
+ const mdcDir = path21.join(projectDir, GENERATED_FILES.cursorMdcDir);
1875
+ await fs10.ensureDir(mdcDir);
1524
1876
  const mdcFiles = generateMdcFiles(scan);
1525
1877
  for (const mdc of mdcFiles) {
1526
- await fs9.writeFile(path18.join(mdcDir, mdc.filename), mdc.content, "utf-8");
1878
+ await fs10.writeFile(path21.join(mdcDir, mdc.filename), mdc.content, "utf-8");
1527
1879
  }
1528
1880
  logSuccess(`${mdcFiles.length} .cursor/rules/*.mdc files updated`);
1529
1881
  }
@@ -1534,10 +1886,10 @@ async function updateCommand(targetPath) {
1534
1886
  const contexts = await copyContexts(projectDir);
1535
1887
  logSuccess(`${contexts.length} context modes updated (.claude/contexts/)`);
1536
1888
  if (existingConfig.hooks !== false) {
1537
- const settingsLocalPath = path18.join(projectDir, GENERATED_FILES.claudeSettingsLocal);
1889
+ const settingsLocalPath = path21.join(projectDir, GENERATED_FILES.claudeSettingsLocal);
1538
1890
  const settingsLocal = generateSettingsLocal(scan, hookProfile);
1539
- await fs9.ensureDir(path18.dirname(settingsLocalPath));
1540
- await fs9.writeJson(settingsLocalPath, settingsLocal, { spaces: 2 });
1891
+ await fs10.ensureDir(path21.dirname(settingsLocalPath));
1892
+ await fs10.writeJson(settingsLocalPath, settingsLocal, { spaces: 2 });
1541
1893
  logSuccess(`Hooks updated (profile: ${hookProfile})`);
1542
1894
  }
1543
1895
  const guides = await copyGuides(projectDir);
@@ -1547,20 +1899,21 @@ async function updateCommand(targetPath) {
1547
1899
  agents,
1548
1900
  contexts,
1549
1901
  hooks: existingConfig.hooks !== false,
1550
- hookProfile
1902
+ hookProfile,
1903
+ tools
1551
1904
  });
1552
- await fs9.writeJson(configPath, config, { spaces: 2 });
1905
+ await fs10.writeJson(configPath, config, { spaces: 2 });
1553
1906
  logSuccess("ai-kit.config.json updated");
1554
1907
  console.log("");
1555
1908
  logInfo("All AI configs refreshed with latest project scan.");
1556
1909
  }
1557
1910
 
1558
1911
  // src/commands/reset.ts
1559
- import path19 from "path";
1560
- import fs10 from "fs-extra";
1912
+ import path22 from "path";
1913
+ import fs11 from "fs-extra";
1561
1914
  import { confirm as confirm3 } from "@inquirer/prompts";
1562
1915
  async function resetCommand(targetPath) {
1563
- const projectDir = path19.resolve(targetPath || process.cwd());
1916
+ const projectDir = path22.resolve(targetPath || process.cwd());
1564
1917
  logSection("AI Kit \u2014 Reset");
1565
1918
  logWarning("This will remove all AI Kit generated files:");
1566
1919
  logInfo(` - ${GENERATED_FILES.claudeMd}`);
@@ -1569,7 +1922,7 @@ async function resetCommand(targetPath) {
1569
1922
  logInfo(` - ${GENERATED_FILES.claudeCommands}/`);
1570
1923
  logInfo(` - ${GENERATED_FILES.claudeSkills}/`);
1571
1924
  logInfo(` - ${GENERATED_FILES.cursorSkills}/`);
1572
- logInfo(` - ai-kit/`);
1925
+ logInfo(` - ai-kit/ (includes component-registry, patterns, guides)`);
1573
1926
  logInfo(` - ${AI_KIT_CONFIG_FILE}`);
1574
1927
  console.log("");
1575
1928
  const proceed = await confirm3({
@@ -1581,44 +1934,44 @@ async function resetCommand(targetPath) {
1581
1934
  return;
1582
1935
  }
1583
1936
  const removed = [];
1584
- const claudeMdPath = path19.join(projectDir, GENERATED_FILES.claudeMd);
1937
+ const claudeMdPath = path22.join(projectDir, GENERATED_FILES.claudeMd);
1585
1938
  if (fileExists(claudeMdPath)) {
1586
- await fs10.remove(claudeMdPath);
1939
+ await fs11.remove(claudeMdPath);
1587
1940
  removed.push(GENERATED_FILES.claudeMd);
1588
1941
  }
1589
- const cursorPath = path19.join(projectDir, GENERATED_FILES.cursorRules);
1942
+ const cursorPath = path22.join(projectDir, GENERATED_FILES.cursorRules);
1590
1943
  if (fileExists(cursorPath)) {
1591
- await fs10.remove(cursorPath);
1944
+ await fs11.remove(cursorPath);
1592
1945
  removed.push(GENERATED_FILES.cursorRules);
1593
1946
  }
1594
- const cursorMdcDir = path19.join(projectDir, GENERATED_FILES.cursorMdcDir);
1947
+ const cursorMdcDir = path22.join(projectDir, GENERATED_FILES.cursorMdcDir);
1595
1948
  if (fileExists(cursorMdcDir)) {
1596
- await fs10.remove(cursorMdcDir);
1949
+ await fs11.remove(cursorMdcDir);
1597
1950
  removed.push(GENERATED_FILES.cursorMdcDir);
1598
1951
  }
1599
- const commandsDir = path19.join(projectDir, GENERATED_FILES.claudeCommands);
1952
+ const commandsDir = path22.join(projectDir, GENERATED_FILES.claudeCommands);
1600
1953
  if (fileExists(commandsDir)) {
1601
- await fs10.remove(commandsDir);
1954
+ await fs11.remove(commandsDir);
1602
1955
  removed.push(GENERATED_FILES.claudeCommands);
1603
1956
  }
1604
- const claudeSkillsDir = path19.join(projectDir, GENERATED_FILES.claudeSkills);
1957
+ const claudeSkillsDir = path22.join(projectDir, GENERATED_FILES.claudeSkills);
1605
1958
  if (fileExists(claudeSkillsDir)) {
1606
- await fs10.remove(claudeSkillsDir);
1959
+ await fs11.remove(claudeSkillsDir);
1607
1960
  removed.push(GENERATED_FILES.claudeSkills);
1608
1961
  }
1609
- const cursorSkillsDir = path19.join(projectDir, GENERATED_FILES.cursorSkills);
1962
+ const cursorSkillsDir = path22.join(projectDir, GENERATED_FILES.cursorSkills);
1610
1963
  if (fileExists(cursorSkillsDir)) {
1611
- await fs10.remove(cursorSkillsDir);
1964
+ await fs11.remove(cursorSkillsDir);
1612
1965
  removed.push(GENERATED_FILES.cursorSkills);
1613
1966
  }
1614
- const aiKitDir = path19.join(projectDir, "ai-kit");
1967
+ const aiKitDir = path22.join(projectDir, "ai-kit");
1615
1968
  if (fileExists(aiKitDir)) {
1616
- await fs10.remove(aiKitDir);
1969
+ await fs11.remove(aiKitDir);
1617
1970
  removed.push("ai-kit/");
1618
1971
  }
1619
- const configPath = path19.join(projectDir, AI_KIT_CONFIG_FILE);
1972
+ const configPath = path22.join(projectDir, AI_KIT_CONFIG_FILE);
1620
1973
  if (fileExists(configPath)) {
1621
- await fs10.remove(configPath);
1974
+ await fs11.remove(configPath);
1622
1975
  removed.push(AI_KIT_CONFIG_FILE);
1623
1976
  }
1624
1977
  logSection("Reset Complete");
@@ -1630,8 +1983,8 @@ async function resetCommand(targetPath) {
1630
1983
  }
1631
1984
 
1632
1985
  // src/commands/tokens.ts
1633
- import path20 from "path";
1634
- import fs11 from "fs-extra";
1986
+ import path23 from "path";
1987
+ import fs12 from "fs-extra";
1635
1988
  import chalk2 from "chalk";
1636
1989
  import ora3 from "ora";
1637
1990
  import os from "os";
@@ -1641,14 +1994,14 @@ var PRICING = {
1641
1994
  };
1642
1995
  var PLAN_BUDGET = 20;
1643
1996
  function findSessionFiles() {
1644
- const claudeDir = path20.join(os.homedir(), ".claude", "projects");
1645
- if (!fs11.existsSync(claudeDir)) return [];
1997
+ const claudeDir = path23.join(os.homedir(), ".claude", "projects");
1998
+ if (!fs12.existsSync(claudeDir)) return [];
1646
1999
  const files = [];
1647
2000
  function walkDir(dir) {
1648
2001
  try {
1649
- const entries = fs11.readdirSync(dir, { withFileTypes: true });
2002
+ const entries = fs12.readdirSync(dir, { withFileTypes: true });
1650
2003
  for (const entry of entries) {
1651
- const full = path20.join(dir, entry.name);
2004
+ const full = path23.join(dir, entry.name);
1652
2005
  if (entry.isDirectory()) {
1653
2006
  walkDir(full);
1654
2007
  } else if (entry.name.endsWith(".jsonl")) {
@@ -1663,7 +2016,7 @@ function findSessionFiles() {
1663
2016
  }
1664
2017
  function parseSessionFile(filePath) {
1665
2018
  try {
1666
- const content = fs11.readFileSync(filePath, "utf-8");
2019
+ const content = fs12.readFileSync(filePath, "utf-8");
1667
2020
  const lines = content.split("\n").filter((l) => l.trim());
1668
2021
  const usage = {
1669
2022
  inputTokens: 0,
@@ -1700,11 +2053,11 @@ function parseSessionFile(filePath) {
1700
2053
  }
1701
2054
  if (usage.inputTokens === 0 && usage.outputTokens === 0) return null;
1702
2055
  if (!sessionDate) {
1703
- const stat = fs11.statSync(filePath);
2056
+ const stat = fs12.statSync(filePath);
1704
2057
  sessionDate = stat.mtime.toISOString().slice(0, 10);
1705
2058
  }
1706
- const sessionId = path20.basename(filePath, ".jsonl");
1707
- const projectName = path20.basename(path20.dirname(filePath));
2059
+ const sessionId = path23.basename(filePath, ".jsonl");
2060
+ const projectName = path23.basename(path23.dirname(filePath));
1708
2061
  return {
1709
2062
  sessionId,
1710
2063
  filePath,
@@ -1933,13 +2286,13 @@ ${chalk2.dim("Tip: Use /understand before modifying unfamiliar code \u2014")}`
1933
2286
  );
1934
2287
  console.log("");
1935
2288
  if (options.csv) {
1936
- const csvPath = path20.join(process.cwd(), "token-usage.csv");
2289
+ const csvPath = path23.join(process.cwd(), "token-usage.csv");
1937
2290
  const csvHeader = "Date,Sessions,Input Tokens,Output Tokens,Cache Tokens,Cost\n";
1938
2291
  const daily = aggregateByDate(sessions);
1939
2292
  const csvRows = daily.map(
1940
2293
  (d) => `${d.date},${d.sessions},${d.usage.inputTokens},${d.usage.outputTokens},${d.usage.cacheReadTokens},${d.cost.toFixed(2)}`
1941
2294
  ).join("\n");
1942
- await fs11.writeFile(csvPath, csvHeader + csvRows, "utf-8");
2295
+ await fs12.writeFile(csvPath, csvHeader + csvRows, "utf-8");
1943
2296
  logSuccess(`CSV exported to ${csvPath}`);
1944
2297
  }
1945
2298
  if (options.export) {
@@ -1976,13 +2329,13 @@ async function exportDashboard(allSessions, todaySessions, weekSessions, monthSe
1976
2329
  }
1977
2330
  };
1978
2331
  const outputDir = process.cwd();
1979
- const dataPath = path20.join(outputDir, "token-data.json");
1980
- const dashboardSrc = path20.join(PACKAGE_ROOT, "templates", "token-dashboard.html");
1981
- const dashboardDest = path20.join(outputDir, "token-dashboard.html");
1982
- await fs11.writeJson(dataPath, exportData, { spaces: 2 });
2332
+ const dataPath = path23.join(outputDir, "token-data.json");
2333
+ const dashboardSrc = path23.join(PACKAGE_ROOT, "templates", "token-dashboard.html");
2334
+ const dashboardDest = path23.join(outputDir, "token-dashboard.html");
2335
+ await fs12.writeJson(dataPath, exportData, { spaces: 2 });
1983
2336
  logInfo(`Token data written to ${dataPath}`);
1984
- if (await fs11.pathExists(dashboardSrc)) {
1985
- await fs11.copy(dashboardSrc, dashboardDest, { overwrite: true });
2337
+ if (await fs12.pathExists(dashboardSrc)) {
2338
+ await fs12.copy(dashboardSrc, dashboardDest, { overwrite: true });
1986
2339
  logInfo(`Dashboard copied to ${dashboardDest}`);
1987
2340
  } else {
1988
2341
  logWarning("Dashboard template not found. Skipping HTML export.");
@@ -1999,12 +2352,12 @@ async function exportDashboard(allSessions, todaySessions, weekSessions, monthSe
1999
2352
  }
2000
2353
 
2001
2354
  // src/commands/doctor.ts
2002
- import path21 from "path";
2355
+ import path24 from "path";
2003
2356
  import chalk3 from "chalk";
2004
2357
  import ora4 from "ora";
2005
2358
  async function doctorCommand(targetPath) {
2006
- const projectDir = path21.resolve(targetPath || process.cwd());
2007
- const configPath = path21.join(projectDir, AI_KIT_CONFIG_FILE);
2359
+ const projectDir = path24.resolve(targetPath || process.cwd());
2360
+ const configPath = path24.join(projectDir, AI_KIT_CONFIG_FILE);
2008
2361
  let passed = 0;
2009
2362
  let warnings = 0;
2010
2363
  let issues = 0;
@@ -2036,7 +2389,7 @@ async function doctorCommand(targetPath) {
2036
2389
  }
2037
2390
  for (const template of config.templates) {
2038
2391
  const templateFile = template === "CLAUDE.md" ? GENERATED_FILES.claudeMd : template === ".cursorrules" ? GENERATED_FILES.cursorRules : template;
2039
- const templatePath = path21.join(projectDir, templateFile);
2392
+ const templatePath = path24.join(projectDir, templateFile);
2040
2393
  if (fileExists(templatePath)) {
2041
2394
  logSuccess(`${template} exists and in sync`);
2042
2395
  passed++;
@@ -2047,8 +2400,8 @@ async function doctorCommand(targetPath) {
2047
2400
  }
2048
2401
  const missingSkills = [];
2049
2402
  for (const skill of config.commands) {
2050
- const claudeSkillPath = path21.join(projectDir, GENERATED_FILES.claudeSkills, skill);
2051
- const cursorSkillPath = path21.join(projectDir, GENERATED_FILES.cursorSkills, skill);
2403
+ const claudeSkillPath = path24.join(projectDir, GENERATED_FILES.claudeSkills, skill);
2404
+ const cursorSkillPath = path24.join(projectDir, GENERATED_FILES.cursorSkills, skill);
2052
2405
  if (!fileExists(claudeSkillPath) && !fileExists(cursorSkillPath)) {
2053
2406
  missingSkills.push(skill);
2054
2407
  }
@@ -2062,10 +2415,10 @@ async function doctorCommand(targetPath) {
2062
2415
  );
2063
2416
  issues++;
2064
2417
  }
2065
- const guidesDir = path21.join(projectDir, "ai-kit", "guides");
2418
+ const guidesDir = path24.join(projectDir, "ai-kit", "guides");
2066
2419
  const missingGuides = [];
2067
2420
  for (const guide of config.guides) {
2068
- const guidePath = path21.join(guidesDir, guide);
2421
+ const guidePath = path24.join(guidesDir, guide);
2069
2422
  if (!fileExists(guidePath)) {
2070
2423
  missingGuides.push(guide);
2071
2424
  }
@@ -2203,13 +2556,13 @@ function compareScanResults(previous, current) {
2203
2556
  }
2204
2557
 
2205
2558
  // src/commands/diff.ts
2206
- import path22 from "path";
2207
- import fs12 from "fs-extra";
2559
+ import path25 from "path";
2560
+ import fs13 from "fs-extra";
2208
2561
  import chalk4 from "chalk";
2209
2562
  import ora5 from "ora";
2210
2563
  async function diffCommand(targetPath) {
2211
- const projectDir = path22.resolve(targetPath || process.cwd());
2212
- const configPath = path22.join(projectDir, AI_KIT_CONFIG_FILE);
2564
+ const projectDir = path25.resolve(targetPath || process.cwd());
2565
+ const configPath = path25.join(projectDir, AI_KIT_CONFIG_FILE);
2213
2566
  console.log(chalk4.bold("AI Kit \u2014 Diff (dry run)\n"));
2214
2567
  if (!fileExists(configPath)) {
2215
2568
  logError("No ai-kit.config.json found. Run `ai-kit init` first.");
@@ -2276,11 +2629,11 @@ async function diffCommand(targetPath) {
2276
2629
  if (cursorRulesStatus.status === "modified") modified++;
2277
2630
  else if (cursorRulesStatus.status === "added") added++;
2278
2631
  else unchanged++;
2279
- const skillsDir = path22.join(projectDir, GENERATED_FILES.claudeSkills);
2632
+ const skillsDir = path25.join(projectDir, GENERATED_FILES.claudeSkills);
2280
2633
  const skillCount = countFilesInDir(skillsDir);
2281
2634
  console.log(chalk4.dim(` unchanged ${GENERATED_FILES.claudeSkills}/ (${skillCount} skills)`));
2282
2635
  unchanged++;
2283
- const guidesDir = path22.join(projectDir, AI_KIT_FOLDER_NAME, "guides");
2636
+ const guidesDir = path25.join(projectDir, AI_KIT_FOLDER_NAME, "guides");
2284
2637
  const guideCount = existingConfig.guides?.length || 0;
2285
2638
  console.log(chalk4.dim(` unchanged ai-kit/guides/ (${guideCount} guides)`));
2286
2639
  unchanged++;
@@ -2389,7 +2742,7 @@ function diffStack(oldScan, newScan) {
2389
2742
  return changes;
2390
2743
  }
2391
2744
  function diffGeneratedFile(projectDir, filename, config, generate2, oldFragments, newFragments) {
2392
- const filePath = path22.join(projectDir, filename);
2745
+ const filePath = path25.join(projectDir, filename);
2393
2746
  const currentContent = readFileSafe(filePath);
2394
2747
  const newContent = generate2();
2395
2748
  if (!currentContent) {
@@ -2426,8 +2779,8 @@ function logFileChange(result) {
2426
2779
  }
2427
2780
  function countFilesInDir(dirPath) {
2428
2781
  try {
2429
- if (!fs12.existsSync(dirPath)) return 0;
2430
- const entries = fs12.readdirSync(dirPath);
2782
+ if (!fs13.existsSync(dirPath)) return 0;
2783
+ const entries = fs13.readdirSync(dirPath);
2431
2784
  return entries.filter((e) => !e.startsWith(".")).length;
2432
2785
  } catch {
2433
2786
  return 0;
@@ -2435,8 +2788,8 @@ function countFilesInDir(dirPath) {
2435
2788
  }
2436
2789
 
2437
2790
  // src/commands/export.ts
2438
- import path23 from "path";
2439
- import fs13 from "fs-extra";
2791
+ import path26 from "path";
2792
+ import fs14 from "fs-extra";
2440
2793
  import ora6 from "ora";
2441
2794
  import { select as select2 } from "@inquirer/prompts";
2442
2795
  var EXPORT_TARGETS = {
@@ -2472,9 +2825,9 @@ function toCline(content) {
2472
2825
  ${stripped}`;
2473
2826
  }
2474
2827
  async function exportCommand(targetPath, options) {
2475
- const projectDir = path23.resolve(targetPath || process.cwd());
2476
- const configPath = path23.join(projectDir, AI_KIT_CONFIG_FILE);
2477
- const claudeMdPath = path23.join(projectDir, GENERATED_FILES.claudeMd);
2828
+ const projectDir = path26.resolve(targetPath || process.cwd());
2829
+ const configPath = path26.join(projectDir, AI_KIT_CONFIG_FILE);
2830
+ const claudeMdPath = path26.join(projectDir, GENERATED_FILES.claudeMd);
2478
2831
  logSection("AI Kit \u2014 Export");
2479
2832
  if (!fileExists(configPath) && !fileExists(claudeMdPath)) {
2480
2833
  logError("No ai-kit.config.json or CLAUDE.md found. Run `ai-kit init` first.");
@@ -2516,9 +2869,9 @@ async function exportCommand(targetPath, options) {
2516
2869
  for (const fmt2 of formats) {
2517
2870
  const target = EXPORT_TARGETS[fmt2];
2518
2871
  const transformer = transformers[fmt2];
2519
- const outputPath = path23.join(projectDir, target.file);
2872
+ const outputPath = path26.join(projectDir, target.file);
2520
2873
  const transformed = transformer(claudeContent);
2521
- await fs13.writeFile(outputPath, transformed, "utf-8");
2874
+ await fs14.writeFile(outputPath, transformed, "utf-8");
2522
2875
  exported++;
2523
2876
  }
2524
2877
  spinner.succeed("Export complete");
@@ -2534,7 +2887,7 @@ async function exportCommand(targetPath, options) {
2534
2887
  }
2535
2888
 
2536
2889
  // src/commands/stats.ts
2537
- import path24 from "path";
2890
+ import path27 from "path";
2538
2891
  import chalk5 from "chalk";
2539
2892
  var SKILL_CATEGORIES = {
2540
2893
  "Getting Started": ["prompt-help", "understand"],
@@ -2637,8 +2990,8 @@ var MCP_DISPLAY_NAMES = {
2637
2990
  perplexity: "Perplexity"
2638
2991
  };
2639
2992
  async function statsCommand(targetPath) {
2640
- const projectDir = path24.resolve(targetPath || process.cwd());
2641
- const configPath = path24.join(projectDir, AI_KIT_CONFIG_FILE);
2993
+ const projectDir = path27.resolve(targetPath || process.cwd());
2994
+ const configPath = path27.join(projectDir, AI_KIT_CONFIG_FILE);
2642
2995
  logSection("AI Kit \u2014 Project Stats");
2643
2996
  console.log("");
2644
2997
  if (!fileExists(configPath)) {
@@ -2732,21 +3085,21 @@ async function statsCommand(targetPath) {
2732
3085
  }
2733
3086
 
2734
3087
  // src/commands/audit.ts
2735
- import path25 from "path";
2736
- import fs14 from "fs-extra";
3088
+ import path28 from "path";
3089
+ import fs15 from "fs-extra";
2737
3090
  import chalk6 from "chalk";
2738
3091
  async function auditCommand(targetPath) {
2739
- const projectDir = path25.resolve(targetPath || process.cwd());
3092
+ const projectDir = path28.resolve(targetPath || process.cwd());
2740
3093
  logSection("AI Kit \u2014 Security & Configuration Audit");
2741
3094
  logInfo(`Auditing: ${projectDir}`);
2742
3095
  const checks = [];
2743
- const configPath = path25.join(projectDir, AI_KIT_CONFIG_FILE);
3096
+ const configPath = path28.join(projectDir, AI_KIT_CONFIG_FILE);
2744
3097
  if (fileExists(configPath)) {
2745
3098
  checks.push({ name: "Config file", status: "pass", message: "ai-kit.config.json found" });
2746
3099
  } else {
2747
3100
  checks.push({ name: "Config file", status: "fail", message: "ai-kit.config.json missing \u2014 run `ai-kit init`" });
2748
3101
  }
2749
- const claudeMdPath = path25.join(projectDir, GENERATED_FILES.claudeMd);
3102
+ const claudeMdPath = path28.join(projectDir, GENERATED_FILES.claudeMd);
2750
3103
  const claudeMd = readFileSafe(claudeMdPath);
2751
3104
  if (claudeMd) {
2752
3105
  if (claudeMd.includes("AI-KIT:START") && claudeMd.includes("AI-KIT:END")) {
@@ -2771,7 +3124,7 @@ async function auditCommand(targetPath) {
2771
3124
  checks.push({ name: "Secrets in CLAUDE.md", status: "pass", message: "No secrets detected" });
2772
3125
  }
2773
3126
  }
2774
- const settingsLocalPath = path25.join(projectDir, GENERATED_FILES.claudeSettingsLocal);
3127
+ const settingsLocalPath = path28.join(projectDir, GENERATED_FILES.claudeSettingsLocal);
2775
3128
  if (fileExists(settingsLocalPath)) {
2776
3129
  const settings2 = readJsonSafe(settingsLocalPath);
2777
3130
  if (settings2?.hooks) {
@@ -2782,14 +3135,14 @@ async function auditCommand(targetPath) {
2782
3135
  } else {
2783
3136
  checks.push({ name: "Hooks", status: "warn", message: "No hooks configured \u2014 run `ai-kit init` to generate" });
2784
3137
  }
2785
- const agentsDir = path25.join(projectDir, GENERATED_FILES.claudeAgents);
2786
- if (await fs14.pathExists(agentsDir)) {
2787
- const agentFiles = (await fs14.readdir(agentsDir)).filter((f) => f.endsWith(".md"));
3138
+ const agentsDir = path28.join(projectDir, GENERATED_FILES.claudeAgents);
3139
+ if (await fs15.pathExists(agentsDir)) {
3140
+ const agentFiles = (await fs15.readdir(agentsDir)).filter((f) => f.endsWith(".md"));
2788
3141
  if (agentFiles.length > 0) {
2789
3142
  checks.push({ name: "Agents", status: "pass", message: `${agentFiles.length} agent(s) configured` });
2790
3143
  let invalidAgents = 0;
2791
3144
  for (const file of agentFiles) {
2792
- const content = readFileSafe(path25.join(agentsDir, file));
3145
+ const content = readFileSafe(path28.join(agentsDir, file));
2793
3146
  if (content && !content.startsWith("---")) {
2794
3147
  invalidAgents++;
2795
3148
  }
@@ -2803,18 +3156,18 @@ async function auditCommand(targetPath) {
2803
3156
  } else {
2804
3157
  checks.push({ name: "Agents", status: "warn", message: "No agents directory \u2014 run `ai-kit init` to generate" });
2805
3158
  }
2806
- const contextsDir = path25.join(projectDir, GENERATED_FILES.claudeContexts);
2807
- if (await fs14.pathExists(contextsDir)) {
2808
- const contextFiles = (await fs14.readdir(contextsDir)).filter((f) => f.endsWith(".md"));
3159
+ const contextsDir = path28.join(projectDir, GENERATED_FILES.claudeContexts);
3160
+ if (await fs15.pathExists(contextsDir)) {
3161
+ const contextFiles = (await fs15.readdir(contextsDir)).filter((f) => f.endsWith(".md"));
2809
3162
  checks.push({ name: "Contexts", status: contextFiles.length > 0 ? "pass" : "warn", message: `${contextFiles.length} context mode(s) available` });
2810
3163
  } else {
2811
3164
  checks.push({ name: "Contexts", status: "warn", message: "No contexts directory" });
2812
3165
  }
2813
- const skillsDir = path25.join(projectDir, GENERATED_FILES.claudeSkills);
2814
- if (await fs14.pathExists(skillsDir)) {
2815
- const skillDirs = (await fs14.readdir(skillsDir)).filter(async (f) => {
3166
+ const skillsDir = path28.join(projectDir, GENERATED_FILES.claudeSkills);
3167
+ if (await fs15.pathExists(skillsDir)) {
3168
+ const skillDirs = (await fs15.readdir(skillsDir)).filter(async (f) => {
2816
3169
  try {
2817
- return (await fs14.stat(path25.join(skillsDir, f))).isDirectory();
3170
+ return (await fs15.stat(path28.join(skillsDir, f))).isDirectory();
2818
3171
  } catch {
2819
3172
  return false;
2820
3173
  }
@@ -2823,7 +3176,7 @@ async function auditCommand(targetPath) {
2823
3176
  } else {
2824
3177
  checks.push({ name: "Skills", status: "warn", message: "No skills directory" });
2825
3178
  }
2826
- const gitignorePath = path25.join(projectDir, ".gitignore");
3179
+ const gitignorePath = path28.join(projectDir, ".gitignore");
2827
3180
  const gitignore = readFileSafe(gitignorePath);
2828
3181
  if (gitignore) {
2829
3182
  const envIgnored = gitignore.includes(".env") || gitignore.includes(".env.local");
@@ -2839,7 +3192,7 @@ async function auditCommand(targetPath) {
2839
3192
  checks.push({ name: "Settings gitignore", status: "warn", message: "settings.local.json not in .gitignore \u2014 may leak local config" });
2840
3193
  }
2841
3194
  }
2842
- const settingsPath = path25.join(projectDir, ".claude", "settings.json");
3195
+ const settingsPath = path28.join(projectDir, ".claude", "settings.json");
2843
3196
  const settings = readFileSafe(settingsPath);
2844
3197
  if (settings) {
2845
3198
  const hasEnvVarsInSettings = /(?:api[_-]?key|token|secret|password)\s*[:=]\s*"[^"]+"/i.test(settings);
@@ -2872,8 +3225,8 @@ async function auditCommand(targetPath) {
2872
3225
  }
2873
3226
 
2874
3227
  // src/commands/health.ts
2875
- import path26 from "path";
2876
- import fs15 from "fs-extra";
3228
+ import path29 from "path";
3229
+ import fs16 from "fs-extra";
2877
3230
  import chalk7 from "chalk";
2878
3231
  import ora7 from "ora";
2879
3232
  function gradeFromScore(score) {
@@ -2914,7 +3267,7 @@ function checkSetup(projectDir, config) {
2914
3267
  detail: `config v${config.version} \u2260 CLI v${VERSION} \u2014 run \`ai-kit update\``
2915
3268
  });
2916
3269
  }
2917
- const claudeMd = readFileSafe(path26.join(projectDir, GENERATED_FILES.claudeMd));
3270
+ const claudeMd = readFileSafe(path29.join(projectDir, GENERATED_FILES.claudeMd));
2918
3271
  if (claudeMd && claudeMd.includes("AI-KIT:START")) {
2919
3272
  checks.push({ name: "CLAUDE.md", status: "pass", detail: "Present with markers" });
2920
3273
  } else if (claudeMd) {
@@ -2922,22 +3275,22 @@ function checkSetup(projectDir, config) {
2922
3275
  } else {
2923
3276
  checks.push({ name: "CLAUDE.md", status: "fail", detail: "Not found" });
2924
3277
  }
2925
- if (fileExists(path26.join(projectDir, GENERATED_FILES.cursorRules))) {
3278
+ if (fileExists(path29.join(projectDir, GENERATED_FILES.cursorRules))) {
2926
3279
  checks.push({ name: ".cursorrules", status: "pass", detail: "Present" });
2927
3280
  } else {
2928
3281
  checks.push({ name: ".cursorrules", status: "warn", detail: "Not generated" });
2929
3282
  }
2930
- const skillsDir = path26.join(projectDir, GENERATED_FILES.claudeSkills);
3283
+ const skillsDir = path29.join(projectDir, GENERATED_FILES.claudeSkills);
2931
3284
  if (dirExists(skillsDir)) {
2932
3285
  const count = config.commands.length;
2933
3286
  checks.push({ name: "Skills", status: "pass", detail: `${count} installed` });
2934
3287
  } else {
2935
3288
  checks.push({ name: "Skills", status: "warn", detail: "No skills directory" });
2936
3289
  }
2937
- const agentsDir = path26.join(projectDir, GENERATED_FILES.claudeAgents);
3290
+ const agentsDir = path29.join(projectDir, GENERATED_FILES.claudeAgents);
2938
3291
  if (dirExists(agentsDir)) {
2939
3292
  try {
2940
- const agentFiles = fs15.readdirSync(agentsDir).filter((f) => f.endsWith(".md"));
3293
+ const agentFiles = fs16.readdirSync(agentsDir).filter((f) => f.endsWith(".md"));
2941
3294
  checks.push({ name: "Agents", status: "pass", detail: `${agentFiles.length} configured` });
2942
3295
  } catch {
2943
3296
  checks.push({ name: "Agents", status: "warn", detail: "Could not read agents" });
@@ -2945,7 +3298,7 @@ function checkSetup(projectDir, config) {
2945
3298
  } else {
2946
3299
  checks.push({ name: "Agents", status: "warn", detail: "Not configured" });
2947
3300
  }
2948
- const settingsLocal = readFileSafe(path26.join(projectDir, GENERATED_FILES.claudeSettingsLocal));
3301
+ const settingsLocal = readFileSafe(path29.join(projectDir, GENERATED_FILES.claudeSettingsLocal));
2949
3302
  if (settingsLocal && settingsLocal.includes('"hooks"')) {
2950
3303
  checks.push({ name: "Hooks", status: "pass", detail: "Configured" });
2951
3304
  } else {
@@ -2955,7 +3308,7 @@ function checkSetup(projectDir, config) {
2955
3308
  }
2956
3309
  function checkSecurity(projectDir) {
2957
3310
  const checks = [];
2958
- const claudeMd = readFileSafe(path26.join(projectDir, GENERATED_FILES.claudeMd));
3311
+ const claudeMd = readFileSafe(path29.join(projectDir, GENERATED_FILES.claudeMd));
2959
3312
  if (claudeMd) {
2960
3313
  const secretPatterns = [
2961
3314
  /(?:api[_-]?key|secret|token|password|credential)\s*[:=]\s*['"][^'"]+['"]/i,
@@ -2970,7 +3323,7 @@ function checkSecurity(projectDir) {
2970
3323
  detail: hasSecrets ? "Potential secrets detected \u2014 remove immediately" : "Clean"
2971
3324
  });
2972
3325
  }
2973
- const gitignore = readFileSafe(path26.join(projectDir, ".gitignore"));
3326
+ const gitignore = readFileSafe(path29.join(projectDir, ".gitignore"));
2974
3327
  if (gitignore) {
2975
3328
  const envIgnored = gitignore.includes(".env") || gitignore.includes(".env.local");
2976
3329
  checks.push({
@@ -2979,7 +3332,7 @@ function checkSecurity(projectDir) {
2979
3332
  detail: envIgnored ? "Protected" : "NOT gitignored \u2014 add .env to .gitignore"
2980
3333
  });
2981
3334
  }
2982
- const settingsJson = readFileSafe(path26.join(projectDir, ".claude", "settings.json"));
3335
+ const settingsJson = readFileSafe(path29.join(projectDir, ".claude", "settings.json"));
2983
3336
  if (settingsJson) {
2984
3337
  const hasHardcoded = /(?:api[_-]?key|token|secret|password)\s*[:=]\s*"[^"]+"/i.test(settingsJson);
2985
3338
  checks.push({
@@ -3018,6 +3371,33 @@ function checkStack(config) {
3018
3371
  detail: scan.monorepoTool || "Detected"
3019
3372
  });
3020
3373
  }
3374
+ if (scan.staticSite?.isStatic) {
3375
+ checks.push({
3376
+ name: "Static Site",
3377
+ status: "pass",
3378
+ detail: `${scan.staticSite.outputMode} mode`
3379
+ });
3380
+ }
3381
+ if (scan.designTokens?.detected) {
3382
+ checks.push({
3383
+ name: "Design Tokens",
3384
+ status: "pass",
3385
+ detail: `${scan.designTokens.colors.length} colors, ${scan.designTokens.fonts.length} fonts (${scan.designTokens.source})`
3386
+ });
3387
+ } else if (scan.styling.includes("tailwind")) {
3388
+ checks.push({
3389
+ name: "Design Tokens",
3390
+ status: "warn",
3391
+ detail: "Tailwind detected but no custom tokens found \u2014 AI may use arbitrary values"
3392
+ });
3393
+ }
3394
+ if (scan.aiIgnorePatterns?.length > 0) {
3395
+ checks.push({
3396
+ name: ".aiignore",
3397
+ status: "pass",
3398
+ detail: `${scan.aiIgnorePatterns.length} patterns loaded`
3399
+ });
3400
+ }
3021
3401
  return { title: "Stack Detection", checks };
3022
3402
  }
3023
3403
  function checkTools(scan) {
@@ -3061,7 +3441,7 @@ function checkDocs(projectDir) {
3061
3441
  { name: "Time Log", path: "docs/time-log.md" }
3062
3442
  ];
3063
3443
  for (const doc of docsToCheck) {
3064
- const content = readFileSafe(path26.join(projectDir, doc.path));
3444
+ const content = readFileSafe(path29.join(projectDir, doc.path));
3065
3445
  if (content) {
3066
3446
  const hasEntries = content.includes("## 20") || content.split("---").length > 2;
3067
3447
  checks.push({
@@ -3076,8 +3456,8 @@ function checkDocs(projectDir) {
3076
3456
  return { title: "Documentation", checks };
3077
3457
  }
3078
3458
  async function healthCommand(targetPath) {
3079
- const projectDir = path26.resolve(targetPath || process.cwd());
3080
- const configPath = path26.join(projectDir, AI_KIT_CONFIG_FILE);
3459
+ const projectDir = path29.resolve(targetPath || process.cwd());
3460
+ const configPath = path29.join(projectDir, AI_KIT_CONFIG_FILE);
3081
3461
  console.log("");
3082
3462
  logSection("AI Kit \u2014 Project Health");
3083
3463
  console.log(chalk7.dim(` ${projectDir}`));
@@ -3161,8 +3541,8 @@ async function healthCommand(targetPath) {
3161
3541
  }
3162
3542
 
3163
3543
  // src/commands/patterns.ts
3164
- import path27 from "path";
3165
- import fs16 from "fs-extra";
3544
+ import path30 from "path";
3545
+ import fs17 from "fs-extra";
3166
3546
  import chalk8 from "chalk";
3167
3547
  import ora8 from "ora";
3168
3548
  function buildPatternCategories() {
@@ -3246,9 +3626,9 @@ var IGNORE_DIRS = [
3246
3626
  ];
3247
3627
  function walkTsFiles(dir, files) {
3248
3628
  try {
3249
- const entries = fs16.readdirSync(dir, { withFileTypes: true });
3629
+ const entries = fs17.readdirSync(dir, { withFileTypes: true });
3250
3630
  for (const entry of entries) {
3251
- const full = path27.join(dir, entry.name);
3631
+ const full = path30.join(dir, entry.name);
3252
3632
  if (entry.isDirectory()) {
3253
3633
  if (IGNORE_DIRS.includes(entry.name)) continue;
3254
3634
  walkTsFiles(full, files);
@@ -3260,8 +3640,8 @@ function walkTsFiles(dir, files) {
3260
3640
  }
3261
3641
  }
3262
3642
  async function patternsCommand(targetPath) {
3263
- const projectDir = path27.resolve(targetPath || process.cwd());
3264
- const configPath = path27.join(projectDir, AI_KIT_CONFIG_FILE);
3643
+ const projectDir = path30.resolve(targetPath || process.cwd());
3644
+ const configPath = path30.join(projectDir, AI_KIT_CONFIG_FILE);
3265
3645
  console.log("");
3266
3646
  logSection("AI Kit \u2014 Pattern Library");
3267
3647
  console.log(chalk8.dim(` ${projectDir}`));
@@ -3272,7 +3652,7 @@ async function patternsCommand(targetPath) {
3272
3652
  }
3273
3653
  const spinner = ora8("Scanning for code patterns...").start();
3274
3654
  const files = [];
3275
- const srcDir = path27.join(projectDir, "src");
3655
+ const srcDir = path30.join(projectDir, "src");
3276
3656
  if (dirExists(srcDir)) {
3277
3657
  walkTsFiles(srcDir, files);
3278
3658
  } else {
@@ -3294,7 +3674,7 @@ async function patternsCommand(targetPath) {
3294
3674
  if (pattern.regex.test(lines2[i])) {
3295
3675
  pattern.matches.push({
3296
3676
  pattern: pattern.label,
3297
- file: path27.relative(projectDir, file),
3677
+ file: path30.relative(projectDir, file),
3298
3678
  line: i + 1
3299
3679
  });
3300
3680
  }
@@ -3322,9 +3702,9 @@ async function patternsCommand(targetPath) {
3322
3702
  logInfo("No recognizable patterns found.");
3323
3703
  return;
3324
3704
  }
3325
- const outputDir = path27.join(projectDir, "ai-kit");
3326
- fs16.ensureDirSync(outputDir);
3327
- const outputPath = path27.join(outputDir, "patterns.md");
3705
+ const outputDir = path30.join(projectDir, "ai-kit");
3706
+ fs17.ensureDirSync(outputDir);
3707
+ const outputPath = path30.join(outputDir, "patterns.md");
3328
3708
  const lines = [
3329
3709
  "# Code Patterns",
3330
3710
  "",
@@ -3357,7 +3737,7 @@ async function patternsCommand(targetPath) {
3357
3737
  lines.push("");
3358
3738
  }
3359
3739
  }
3360
- fs16.writeFileSync(outputPath, lines.join("\n"), "utf-8");
3740
+ fs17.writeFileSync(outputPath, lines.join("\n"), "utf-8");
3361
3741
  console.log("");
3362
3742
  logSuccess(`Pattern library written to ${chalk8.cyan("ai-kit/patterns.md")}`);
3363
3743
  logInfo(`Total: ${chalk8.bold(String(totalPatterns))} pattern occurrences across ${chalk8.bold(String(files.length))} files`);
@@ -3365,14 +3745,14 @@ async function patternsCommand(targetPath) {
3365
3745
  }
3366
3746
 
3367
3747
  // src/commands/dead-code.ts
3368
- import path29 from "path";
3369
- import fs18 from "fs-extra";
3748
+ import path32 from "path";
3749
+ import fs19 from "fs-extra";
3370
3750
  import chalk9 from "chalk";
3371
3751
  import ora9 from "ora";
3372
3752
 
3373
3753
  // src/scanner/components.ts
3374
- import path28 from "path";
3375
- import fs17 from "fs-extra";
3754
+ import path31 from "path";
3755
+ import fs18 from "fs-extra";
3376
3756
  var COMPONENT_DIRS = [
3377
3757
  "src/components",
3378
3758
  "src/Components",
@@ -3399,7 +3779,7 @@ function findComponentFiles(projectPath) {
3399
3779
  const files = [];
3400
3780
  const directories = /* @__PURE__ */ new Set();
3401
3781
  for (const dir of COMPONENT_DIRS) {
3402
- const fullDir = path28.join(projectPath, dir);
3782
+ const fullDir = path31.join(projectPath, dir);
3403
3783
  if (dirExists(fullDir)) {
3404
3784
  walkForComponents(fullDir, files, directories);
3405
3785
  }
@@ -3408,9 +3788,9 @@ function findComponentFiles(projectPath) {
3408
3788
  }
3409
3789
  function walkForComponents(dir, files, directories) {
3410
3790
  try {
3411
- const entries = fs17.readdirSync(dir, { withFileTypes: true });
3791
+ const entries = fs18.readdirSync(dir, { withFileTypes: true });
3412
3792
  for (const entry of entries) {
3413
- const full = path28.join(dir, entry.name);
3793
+ const full = path31.join(dir, entry.name);
3414
3794
  if (entry.isDirectory()) {
3415
3795
  if (IGNORE_PATTERNS.includes(entry.name)) continue;
3416
3796
  walkForComponents(full, files, directories);
@@ -3450,7 +3830,7 @@ function extractComponentName(filePath, content) {
3450
3830
  /export\s+(?:const|function)\s+(\w+)/
3451
3831
  );
3452
3832
  if (namedExport) return namedExport[1];
3453
- return path28.basename(filePath, ".tsx");
3833
+ return path31.basename(filePath, ".tsx");
3454
3834
  }
3455
3835
  function extractExportType(content) {
3456
3836
  const hasDefault = /export\s+default\s/.test(content);
@@ -3539,7 +3919,7 @@ function extractDependencies(content) {
3539
3919
  for (const match of imports) {
3540
3920
  const importPath = match[1];
3541
3921
  if (importPath.startsWith(".") || importPath.startsWith("..")) {
3542
- const basename = path28.basename(importPath).replace(/\.\w+$/, "");
3922
+ const basename = path31.basename(importPath).replace(/\.\w+$/, "");
3543
3923
  if (/^[A-Z]/.test(basename)) {
3544
3924
  deps.push(basename);
3545
3925
  }
@@ -3548,33 +3928,33 @@ function extractDependencies(content) {
3548
3928
  return deps;
3549
3929
  }
3550
3930
  function checkForTests(componentPath, componentName) {
3551
- const dir = path28.dirname(componentPath);
3552
- const base = path28.basename(componentPath, ".tsx");
3931
+ const dir = path31.dirname(componentPath);
3932
+ const base = path31.basename(componentPath, ".tsx");
3553
3933
  for (const suffix of TEST_SUFFIXES) {
3554
- if (fs17.existsSync(path28.join(dir, base + suffix))) return true;
3934
+ if (fs18.existsSync(path31.join(dir, base + suffix))) return true;
3555
3935
  }
3556
- const testsDir = path28.join(dir, "__tests__");
3936
+ const testsDir = path31.join(dir, "__tests__");
3557
3937
  if (dirExists(testsDir)) {
3558
3938
  for (const suffix of TEST_SUFFIXES) {
3559
- if (fs17.existsSync(path28.join(testsDir, base + suffix))) return true;
3560
- if (fs17.existsSync(path28.join(testsDir, componentName + suffix))) return true;
3939
+ if (fs18.existsSync(path31.join(testsDir, base + suffix))) return true;
3940
+ if (fs18.existsSync(path31.join(testsDir, componentName + suffix))) return true;
3561
3941
  }
3562
3942
  }
3563
3943
  return false;
3564
3944
  }
3565
3945
  function checkForStory(componentPath, componentName) {
3566
- const dir = path28.dirname(componentPath);
3567
- const base = path28.basename(componentPath, ".tsx");
3946
+ const dir = path31.dirname(componentPath);
3947
+ const base = path31.basename(componentPath, ".tsx");
3568
3948
  for (const suffix of STORY_SUFFIXES) {
3569
- if (fs17.existsSync(path28.join(dir, base + suffix))) return true;
3570
- if (fs17.existsSync(path28.join(dir, componentName + suffix))) return true;
3949
+ if (fs18.existsSync(path31.join(dir, base + suffix))) return true;
3950
+ if (fs18.existsSync(path31.join(dir, componentName + suffix))) return true;
3571
3951
  }
3572
3952
  return false;
3573
3953
  }
3574
3954
  function checkForAiDoc(componentPath) {
3575
- const dir = path28.dirname(componentPath);
3576
- const base = path28.basename(componentPath, ".tsx");
3577
- return fs17.existsSync(path28.join(dir, `${base}.ai.md`)) || fs17.existsSync(path28.join(dir, "component.ai.md"));
3955
+ const dir = path31.dirname(componentPath);
3956
+ const base = path31.basename(componentPath, ".tsx");
3957
+ return fs18.existsSync(path31.join(dir, `${base}.ai.md`)) || fs18.existsSync(path31.join(dir, "component.ai.md"));
3578
3958
  }
3579
3959
  function categorizeComponent(relativePath) {
3580
3960
  const lower = relativePath.toLowerCase();
@@ -3592,7 +3972,7 @@ function parseComponent(filePath, projectPath) {
3592
3972
  const content = readFileSafe(filePath);
3593
3973
  if (!content) return null;
3594
3974
  const name = extractComponentName(filePath, content);
3595
- const relativePath = path28.relative(projectPath, filePath);
3975
+ const relativePath = path31.relative(projectPath, filePath);
3596
3976
  return {
3597
3977
  name,
3598
3978
  filePath,
@@ -3638,9 +4018,9 @@ var IGNORE_DIRS2 = [
3638
4018
  ];
3639
4019
  function collectAllFiles(dir, files) {
3640
4020
  try {
3641
- const entries = fs18.readdirSync(dir, { withFileTypes: true });
4021
+ const entries = fs19.readdirSync(dir, { withFileTypes: true });
3642
4022
  for (const entry of entries) {
3643
- const full = path29.join(dir, entry.name);
4023
+ const full = path32.join(dir, entry.name);
3644
4024
  if (entry.isDirectory()) {
3645
4025
  if (IGNORE_DIRS2.includes(entry.name)) continue;
3646
4026
  collectAllFiles(full, files);
@@ -3652,12 +4032,12 @@ function collectAllFiles(dir, files) {
3652
4032
  }
3653
4033
  }
3654
4034
  function isTestOrStoryFile(filePath) {
3655
- const name = path29.basename(filePath).toLowerCase();
4035
+ const name = path32.basename(filePath).toLowerCase();
3656
4036
  return name.includes(".test.") || name.includes(".spec.") || name.includes(".stories.") || name.includes("__tests__") || name.includes("__mocks__");
3657
4037
  }
3658
4038
  async function deadCodeCommand(targetPath) {
3659
- const projectDir = path29.resolve(targetPath || process.cwd());
3660
- const configPath = path29.join(projectDir, AI_KIT_CONFIG_FILE);
4039
+ const projectDir = path32.resolve(targetPath || process.cwd());
4040
+ const configPath = path32.join(projectDir, AI_KIT_CONFIG_FILE);
3661
4041
  console.log("");
3662
4042
  logSection("AI Kit \u2014 Dead Code Report");
3663
4043
  console.log(chalk9.dim(` ${projectDir}`));
@@ -3674,8 +4054,8 @@ async function deadCodeCommand(targetPath) {
3674
4054
  }
3675
4055
  spinner.text = `Found ${scanResult.components.length} components. Checking imports...`;
3676
4056
  const allFiles = [];
3677
- const srcDir = path29.join(projectDir, "src");
3678
- if (fs18.existsSync(srcDir)) {
4057
+ const srcDir = path32.join(projectDir, "src");
4058
+ if (fs19.existsSync(srcDir)) {
3679
4059
  collectAllFiles(srcDir, allFiles);
3680
4060
  } else {
3681
4061
  collectAllFiles(projectDir, allFiles);
@@ -3698,7 +4078,7 @@ async function deadCodeCommand(targetPath) {
3698
4078
  `(?:import|from)\\s+.*['"][^'"]*(?:/|\\b)${escapeRegex(component.name)}(?:[/'"]|\\b)`
3699
4079
  );
3700
4080
  for (const [file, content] of fileContents) {
3701
- if (path29.resolve(file) === path29.resolve(componentFile)) continue;
4081
+ if (path32.resolve(file) === path32.resolve(componentFile)) continue;
3702
4082
  if (namePattern.test(content)) {
3703
4083
  importCount++;
3704
4084
  if (isTestOrStoryFile(file)) {
@@ -3777,8 +4157,8 @@ function escapeRegex(str) {
3777
4157
  }
3778
4158
 
3779
4159
  // src/commands/drift.ts
3780
- import path30 from "path";
3781
- import fs19 from "fs-extra";
4160
+ import path33 from "path";
4161
+ import fs20 from "fs-extra";
3782
4162
  import chalk10 from "chalk";
3783
4163
  import ora10 from "ora";
3784
4164
  function parseAiDocFrontmatter(content) {
@@ -3814,14 +4194,14 @@ function parseAiDocFrontmatter(content) {
3814
4194
  return { props, fields };
3815
4195
  }
3816
4196
  function findAiDocPath(componentPath) {
3817
- const dir = path30.dirname(componentPath);
3818
- const base = path30.basename(componentPath, ".tsx");
4197
+ const dir = path33.dirname(componentPath);
4198
+ const base = path33.basename(componentPath, ".tsx");
3819
4199
  const candidates = [
3820
- path30.join(dir, `${base}.ai.md`),
3821
- path30.join(dir, "component.ai.md")
4200
+ path33.join(dir, `${base}.ai.md`),
4201
+ path33.join(dir, "component.ai.md")
3822
4202
  ];
3823
4203
  for (const candidate of candidates) {
3824
- if (fs19.existsSync(candidate)) return candidate;
4204
+ if (fs20.existsSync(candidate)) return candidate;
3825
4205
  }
3826
4206
  return null;
3827
4207
  }
@@ -3862,8 +4242,8 @@ function analyzeDrift(component, projectDir) {
3862
4242
  };
3863
4243
  }
3864
4244
  async function driftCommand(targetPath) {
3865
- const projectDir = path30.resolve(targetPath || process.cwd());
3866
- const configPath = path30.join(projectDir, AI_KIT_CONFIG_FILE);
4245
+ const projectDir = path33.resolve(targetPath || process.cwd());
4246
+ const configPath = path33.join(projectDir, AI_KIT_CONFIG_FILE);
3867
4247
  console.log("");
3868
4248
  logSection("AI Kit \u2014 Component Drift Detector");
3869
4249
  console.log(chalk10.dim(` ${projectDir}`));
@@ -3964,6 +4344,144 @@ async function driftCommand(targetPath) {
3964
4344
  console.log("");
3965
4345
  }
3966
4346
 
4347
+ // src/commands/component-registry.ts
4348
+ import path34 from "path";
4349
+ import fs22 from "fs-extra";
4350
+ import chalk11 from "chalk";
4351
+ import ora11 from "ora";
4352
+
4353
+ // src/generator/component-docs.ts
4354
+ import fs21 from "fs-extra";
4355
+ function calculateHealthScore(component) {
4356
+ let score = 0;
4357
+ const max = 100;
4358
+ if (component.props.length > 0) score += 15;
4359
+ if (component.hasTests) score += 25;
4360
+ if (component.hasStory) score += 20;
4361
+ if (component.hasAiDoc) score += 15;
4362
+ const sc = component.sitecore;
4363
+ if (sc.datasourceFields.length > 0 || sc.renderingParams.length > 0 || sc.placeholders.length > 0) {
4364
+ score += 10;
4365
+ }
4366
+ if (component.dependencies.length > 0) score += 5;
4367
+ if (component.exportType === "default" || component.exportType === "both") score += 10;
4368
+ return Math.min(score, max);
4369
+ }
4370
+
4371
+ // src/commands/component-registry.ts
4372
+ async function componentRegistryCommand(targetPath) {
4373
+ const projectDir = path34.resolve(targetPath || process.cwd());
4374
+ console.log("");
4375
+ logSection("AI Kit \u2014 Component Registry");
4376
+ console.log(chalk11.dim(` ${projectDir}`));
4377
+ console.log("");
4378
+ if (!fileExists(path34.join(projectDir, AI_KIT_CONFIG_FILE))) {
4379
+ logWarning("ai-kit.config.json not found. Run `ai-kit init` first.");
4380
+ return;
4381
+ }
4382
+ const spinner = ora11("Scanning components...").start();
4383
+ const scanResult = scanComponents(projectDir);
4384
+ if (scanResult.components.length === 0) {
4385
+ spinner.fail("No components found.");
4386
+ return;
4387
+ }
4388
+ spinner.succeed(`Found ${scanResult.components.length} components in ${scanResult.directories.length} directories`);
4389
+ const entries = scanResult.components.map((c) => ({
4390
+ name: c.name,
4391
+ path: c.relativePath,
4392
+ category: c.category,
4393
+ props: c.props,
4394
+ exportType: c.exportType,
4395
+ dependencies: c.dependencies,
4396
+ sitecore: {
4397
+ datasourceFields: c.sitecore.datasourceFields,
4398
+ renderingParams: c.sitecore.renderingParams,
4399
+ placeholders: c.sitecore.placeholders
4400
+ },
4401
+ health: {
4402
+ score: calculateHealthScore(c),
4403
+ hasTests: c.hasTests,
4404
+ hasStory: c.hasStory,
4405
+ hasAiDoc: c.hasAiDoc
4406
+ }
4407
+ }));
4408
+ const categories = {};
4409
+ for (const entry of entries) {
4410
+ categories[entry.category] = (categories[entry.category] || 0) + 1;
4411
+ }
4412
+ const pkgPath = path34.join(projectDir, "package.json");
4413
+ const pkg = fs22.readJsonSync(pkgPath, { throws: false }) || {};
4414
+ const registry = {
4415
+ version: "1.0.0",
4416
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
4417
+ projectName: pkg.name || path34.basename(projectDir),
4418
+ totalComponents: entries.length,
4419
+ categories,
4420
+ components: entries
4421
+ };
4422
+ const outputPath = path34.join(projectDir, "ai-kit", "component-registry.json");
4423
+ await fs22.ensureDir(path34.dirname(outputPath));
4424
+ await fs22.writeJson(outputPath, registry, { spaces: 2 });
4425
+ const mdPath = path34.join(projectDir, "ai-kit", "component-registry.md");
4426
+ const md = generateRegistryMarkdown(registry);
4427
+ await fs22.writeFile(mdPath, md, "utf-8");
4428
+ console.log("");
4429
+ console.log(` ${chalk11.bold("Categories:")}`);
4430
+ for (const [cat, count] of Object.entries(categories).sort((a, b) => b[1] - a[1])) {
4431
+ console.log(` ${chalk11.cyan(String(count).padStart(3))} ${cat}`);
4432
+ }
4433
+ const avgHealth = Math.round(entries.reduce((sum, e) => sum + e.health.score, 0) / entries.length);
4434
+ const withTests = entries.filter((e) => e.health.hasTests).length;
4435
+ const withStories = entries.filter((e) => e.health.hasStory).length;
4436
+ const withDocs = entries.filter((e) => e.health.hasAiDoc).length;
4437
+ console.log("");
4438
+ console.log(` ${chalk11.bold("Health:")}`);
4439
+ console.log(` Average score: ${chalk11.cyan(String(avgHealth))}/100`);
4440
+ console.log(` With tests: ${chalk11.cyan(String(withTests))}/${entries.length}`);
4441
+ console.log(` With stories: ${chalk11.cyan(String(withStories))}/${entries.length}`);
4442
+ console.log(` With .ai.md docs: ${chalk11.cyan(String(withDocs))}/${entries.length}`);
4443
+ console.log("");
4444
+ logSuccess(`Registry written to ${chalk11.cyan("ai-kit/component-registry.json")}`);
4445
+ logSuccess(`Summary written to ${chalk11.cyan("ai-kit/component-registry.md")}`);
4446
+ logInfo("AI agents can use this registry to discover existing components before creating new ones.");
4447
+ console.log("");
4448
+ }
4449
+ function generateRegistryMarkdown(registry) {
4450
+ const lines = [
4451
+ "# Component Registry",
4452
+ "",
4453
+ `> Auto-generated by ai-kit on ${registry.generatedAt.split("T")[0]}`,
4454
+ `> ${registry.totalComponents} components in ${registry.projectName}`,
4455
+ "",
4456
+ "## How to Use",
4457
+ "",
4458
+ "Before creating a new component, check this registry to see if one already exists.",
4459
+ "AI agents should reference this file to avoid duplicating components.",
4460
+ ""
4461
+ ];
4462
+ const grouped = /* @__PURE__ */ new Map();
4463
+ for (const comp of registry.components) {
4464
+ const list = grouped.get(comp.category) || [];
4465
+ list.push(comp);
4466
+ grouped.set(comp.category, list);
4467
+ }
4468
+ for (const [category, components] of grouped) {
4469
+ lines.push(`## ${category.charAt(0).toUpperCase() + category.slice(1)} (${components.length})`);
4470
+ lines.push("");
4471
+ lines.push("| Component | Path | Props | Tests | Story | Health |");
4472
+ lines.push("|-----------|------|-------|-------|-------|--------|");
4473
+ for (const comp of components) {
4474
+ const propsCount = comp.props.length;
4475
+ const tests = comp.health.hasTests ? "Yes" : "No";
4476
+ const story = comp.health.hasStory ? "Yes" : "No";
4477
+ const health = `${comp.health.score}/100`;
4478
+ lines.push(`| ${comp.name} | \`${comp.path}\` | ${propsCount} | ${tests} | ${story} | ${health} |`);
4479
+ }
4480
+ lines.push("");
4481
+ }
4482
+ return lines.join("\n");
4483
+ }
4484
+
3967
4485
  // src/index.ts
3968
4486
  var program = new Command();
3969
4487
  program.name("ai-kit").description(
@@ -4112,5 +4630,16 @@ program.command("drift").description("Detect drift between component code and .a
4112
4630
  process.exit(1);
4113
4631
  }
4114
4632
  });
4633
+ program.command("component-registry").description("Generate a component registry for AI agent discovery").argument("[path]", "Project directory (defaults to current directory)").action(async (targetPath) => {
4634
+ try {
4635
+ await componentRegistryCommand(targetPath);
4636
+ } catch (err) {
4637
+ if (err.name === "ExitPromptError") {
4638
+ process.exit(0);
4639
+ }
4640
+ console.error(err);
4641
+ process.exit(1);
4642
+ }
4643
+ });
4115
4644
  program.parse();
4116
4645
  //# sourceMappingURL=index.js.map