@jvittechs/jai1-cli 0.1.106 → 1.0.1

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.js CHANGED
@@ -33,7 +33,7 @@ var NetworkError = class extends Jai1Error {
33
33
  // package.json
34
34
  var package_default = {
35
35
  name: "@jvittechs/jai1-cli",
36
- version: "0.1.106",
36
+ version: "1.0.1",
37
37
  description: "A unified CLI tool for JV-IT TECHS developers to manage Jai1 Framework. Please contact TeamAI for usage instructions.",
38
38
  type: "module",
39
39
  bin: {
@@ -480,6 +480,34 @@ var ComponentsService = class {
480
480
  this.cacheDir = join2(homedir3(), ".jai1", "cache");
481
481
  this.manifestFile = join2(projectRoot, ".jai1", "manifest.json");
482
482
  }
483
+ /**
484
+ * Expand component paths with special prefixes
485
+ *
486
+ * Supported formats:
487
+ * - Standard paths: "rule-presets/react-spa-zustand", "rules/jai1.md", "workflows/commit-it.md"
488
+ * - Package prefix: "package:core" -> expands to all components in that package
489
+ *
490
+ * @param config - Jai1 config
491
+ * @param paths - Array of component paths (may include special prefixes)
492
+ * @returns Array of expanded component paths
493
+ */
494
+ async expandPaths(config, paths) {
495
+ const expandedPaths = [];
496
+ for (const path13 of paths) {
497
+ if (path13.startsWith("package:")) {
498
+ const packageName = path13.substring("package:".length);
499
+ const components = await this.list(config);
500
+ if (packageName === "core") {
501
+ expandedPaths.push(...components.map((c) => c.filepath));
502
+ } else {
503
+ console.warn(`Warning: Unknown package '${packageName}', skipping`);
504
+ }
505
+ } else {
506
+ expandedPaths.push(path13);
507
+ }
508
+ }
509
+ return expandedPaths;
510
+ }
483
511
  /**
484
512
  * List components from API
485
513
  */
@@ -547,9 +575,6 @@ var ComponentsService = class {
547
575
  if (!component.content) {
548
576
  throw new Error(`Component ${filepath} has no content`);
549
577
  }
550
- const targetPath = join2(targetDir, filepath);
551
- const targetFolder = join2(targetPath, "..");
552
- await fs2.mkdir(targetFolder, { recursive: true });
553
578
  let checksumContent = component.content;
554
579
  if (component.contentType === "bundle" || component.contentType === "zip") {
555
580
  let bundleJson;
@@ -594,6 +619,9 @@ var ComponentsService = class {
594
619
  if (component.contentType === "markdown") {
595
620
  checksumContent = component.content;
596
621
  }
622
+ const targetPath = join2(targetDir, filepath);
623
+ const targetFolder = join2(targetPath, "..");
624
+ await fs2.mkdir(targetFolder, { recursive: true });
597
625
  await fs2.writeFile(targetPath, component.content);
598
626
  }
599
627
  await this.markInstalled(filepath, component.version, this.calculateChecksum(checksumContent));
@@ -1330,6 +1358,7 @@ var UnifiedApplyApp = ({
1330
1358
  const [cursorIndex, setCursorIndex] = useState(0);
1331
1359
  const [focusArea, setFocusArea] = useState("packages");
1332
1360
  const [selectedPackageIndex, setSelectedPackageIndex] = useState(0);
1361
+ const [activePackageFilter, setActivePackageFilter] = useState(null);
1333
1362
  const [installProgress, setInstallProgress] = useState([]);
1334
1363
  const [installStats, setInstallStats] = useState({ total: 0, completed: 0, added: 0, updated: 0, failed: 0 });
1335
1364
  const [availableIdes, setAvailableIdes] = useState([]);
@@ -1349,6 +1378,9 @@ var UnifiedApplyApp = ({
1349
1378
  setComponents(comps);
1350
1379
  setTags(tagList);
1351
1380
  setInstalledPaths(new Set(Object.keys(installed)));
1381
+ if (tagList.length > 0) {
1382
+ setActivePackageFilter(tagList[0].tag);
1383
+ }
1352
1384
  setLoading(false);
1353
1385
  } catch (err) {
1354
1386
  setError(err instanceof Error ? err.message : "Failed to load");
@@ -1359,12 +1391,17 @@ var UnifiedApplyApp = ({
1359
1391
  setAvailableIdes(getMigrationIDEs());
1360
1392
  }, []);
1361
1393
  const filteredComponents = useMemo(() => {
1362
- if (!searchQuery.trim()) return components;
1363
- const query = searchQuery.toLowerCase();
1364
- return components.filter(
1365
- (c) => c.filepath.toLowerCase().includes(query) || c.tags?.some((t) => t.toLowerCase().includes(query))
1366
- );
1367
- }, [components, searchQuery]);
1394
+ if (activePackageFilter) {
1395
+ return components.filter((c) => c.tags?.includes(activePackageFilter));
1396
+ }
1397
+ if (searchQuery.trim()) {
1398
+ const query = searchQuery.toLowerCase();
1399
+ return components.filter(
1400
+ (c) => c.filepath.toLowerCase().includes(query) || c.tags?.some((t) => t.toLowerCase().includes(query))
1401
+ );
1402
+ }
1403
+ return components;
1404
+ }, [components, searchQuery, activePackageFilter]);
1368
1405
  useInput((input5, key) => {
1369
1406
  if (viewState === "done") {
1370
1407
  if (key.return || input5 === "q" || key.escape) {
@@ -1418,9 +1455,16 @@ var UnifiedApplyApp = ({
1418
1455
  return;
1419
1456
  }
1420
1457
  if (key.tab) {
1421
- if (focusArea === "packages") setFocusArea("components");
1422
- else if (focusArea === "components") setFocusArea("search");
1423
- else setFocusArea("packages");
1458
+ if (focusArea === "packages") {
1459
+ setFocusArea("components");
1460
+ } else if (focusArea === "components") {
1461
+ setFocusArea("search");
1462
+ setActivePackageFilter(null);
1463
+ } else {
1464
+ setFocusArea("packages");
1465
+ const tag = tags[selectedPackageIndex];
1466
+ if (tag) setActivePackageFilter(tag.tag);
1467
+ }
1424
1468
  return;
1425
1469
  }
1426
1470
  if (key.escape || input5 === "q") {
@@ -1468,9 +1512,19 @@ var UnifiedApplyApp = ({
1468
1512
  }
1469
1513
  if (focusArea === "packages") {
1470
1514
  if (key.leftArrow) {
1471
- setSelectedPackageIndex((prev) => Math.max(0, prev - 1));
1515
+ setSelectedPackageIndex((prev) => {
1516
+ const newIndex = Math.max(0, prev - 1);
1517
+ const tag = tags[newIndex];
1518
+ if (tag) setActivePackageFilter(tag.tag);
1519
+ return newIndex;
1520
+ });
1472
1521
  } else if (key.rightArrow) {
1473
- setSelectedPackageIndex((prev) => Math.min(tags.length - 1, prev + 1));
1522
+ setSelectedPackageIndex((prev) => {
1523
+ const newIndex = Math.min(tags.length - 1, prev + 1);
1524
+ const tag = tags[newIndex];
1525
+ if (tag) setActivePackageFilter(tag.tag);
1526
+ return newIndex;
1527
+ });
1474
1528
  } else if (input5 === " " || key.return) {
1475
1529
  const tag = tags[selectedPackageIndex];
1476
1530
  if (tag) {
@@ -1480,7 +1534,6 @@ var UnifiedApplyApp = ({
1480
1534
  packageComponents.forEach((c) => next.add(c.filepath));
1481
1535
  return next;
1482
1536
  });
1483
- setSearchQuery(tag.tag);
1484
1537
  }
1485
1538
  }
1486
1539
  }
@@ -1601,7 +1654,7 @@ var UnifiedApplyApp = ({
1601
1654
  onChange: setSearchQuery,
1602
1655
  placeholder: "Type to filter..."
1603
1656
  }
1604
- ) : /* @__PURE__ */ React3.createElement(Text3, null, searchQuery || /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "Press Tab to search"))), /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Text3, { bold: true, dimColor: true }, "Quick Apply (Packages):"), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, flexWrap: "wrap" }, tags.slice(0, 6).map((tag, i) => {
1657
+ ) : /* @__PURE__ */ React3.createElement(Text3, null, searchQuery || /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "Press Tab to search"))), /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Text3, { bold: true, dimColor: true }, "Quick Apply (Packages):"), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, flexWrap: "wrap" }, tags.map((tag, i) => {
1605
1658
  const isSelected = focusArea === "packages" && i === selectedPackageIndex;
1606
1659
  return /* @__PURE__ */ React3.createElement(Box2, { key: tag.tag, marginRight: 1 }, /* @__PURE__ */ React3.createElement(
1607
1660
  Text3,
@@ -1615,12 +1668,12 @@ var UnifiedApplyApp = ({
1615
1668
  tag.count,
1616
1669
  "]"
1617
1670
  ));
1618
- }))), /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", borderStyle: "round", borderColor: focusArea === "components" ? "cyan" : "gray", padding: 1 }, /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Text3, { bold: true }, "Components "), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "(", filteredComponents.length, " shown", hasMore ? `, scroll for more` : "", ")")), visibleComponents.map((comp, i) => {
1671
+ })), focusArea === "packages" && tags.length > 0 && /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "\u2190 \u2192 to browse packages \xB7 Space/Enter to select package"))), /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", borderStyle: "round", borderColor: focusArea === "components" ? "cyan" : "gray", padding: 1 }, /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Text3, { bold: true }, "Components "), activePackageFilter && /* @__PURE__ */ React3.createElement(Text3, { color: "cyan" }, "[", activePackageFilter, "] "), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "(", filteredComponents.length, " shown", hasMore ? `, scroll for more` : "", ")")), visibleComponents.map((comp, i) => {
1619
1672
  const isCursor = i === cursorIndex && focusArea === "components";
1620
1673
  const isChecked = selectedPaths.has(comp.filepath);
1621
1674
  const isInstalled = installedPaths.has(comp.filepath);
1622
1675
  return /* @__PURE__ */ React3.createElement(Box2, { key: comp.filepath }, /* @__PURE__ */ React3.createElement(Text3, { color: isCursor ? "cyan" : "white" }, isCursor ? "\u276F " : " ", isChecked ? "[\u2713]" : "[ ]", " ", comp.filepath), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, " ", isInstalled ? "\u2713 installed" : "\u25CB new"));
1623
- }), filteredComponents.length === 0 && /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "No components match your search")), selectedPaths.size > 0 && /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Text3, { bold: true }, "Selected: ", selectedPaths.size, " components"), /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", marginLeft: 2 }, Array.from(selectedPaths).slice(0, 4).map((fp) => /* @__PURE__ */ React3.createElement(Text3, { key: fp, dimColor: true }, "\u{1F4CC} ", fp)), selectedPaths.size > 4 && /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, " ... and ", selectedPaths.size - 4, " more"))), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "[Tab] Switch area \xB7 [\u2191\u2193/\u2190\u2192] Navigate \xB7 [\u2423] Toggle \xB7 [A] Select all \xB7 [C] Clear \xB7 [Enter] Apply \xB7 [Q] Quit")));
1676
+ }), filteredComponents.length === 0 && /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "No components match your search")), selectedPaths.size > 0 && /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Text3, { bold: true }, "Selected: ", selectedPaths.size, " components"), /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", marginLeft: 2 }, Array.from(selectedPaths).slice(0, 4).map((fp) => /* @__PURE__ */ React3.createElement(Text3, { key: fp, dimColor: true }, "\u{1F4CC} ", fp)), selectedPaths.size > 4 && /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, " ... and ", selectedPaths.size - 4, " more"))), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, focusArea === "packages" && "[\u2190\u2192] Browse packages \xB7 [\u2423/Enter] Select package", focusArea === "components" && "[\u2191\u2193] Navigate \xB7 [\u2423] Toggle", focusArea === "search" && "Type to search...", " \xB7 [Tab] Switch area \xB7 [A] All \xB7 [C] Clear \xB7 [Enter] Apply \xB7 [Q] Quit")));
1624
1677
  };
1625
1678
 
1626
1679
  // src/commands/apply.ts
@@ -1967,7 +2020,7 @@ var MainMenuView = ({ ideContexts, onSelect }) => {
1967
2020
  {
1968
2021
  ide: "antigravity",
1969
2022
  icon: "\u{1F680}",
1970
- title: "Antigravity (Claude)",
2023
+ title: "Antigravity",
1971
2024
  itemCount: ideContexts.find((ctx) => ctx.ide === "antigravity")?.stats.totalItems || 0,
1972
2025
  available: ideContexts.some((ctx) => ctx.ide === "antigravity")
1973
2026
  },
@@ -2294,7 +2347,7 @@ var IDE_CONFIGS = {
2294
2347
  },
2295
2348
  antigravity: {
2296
2349
  id: "antigravity",
2297
- name: "Antigravity (Claude)",
2350
+ name: "Antigravity",
2298
2351
  icon: "\u{1F680}",
2299
2352
  basePath: ".agent",
2300
2353
  contentPaths: {
@@ -2795,6 +2848,25 @@ import { checkbox, confirm as confirm2, select } from "@inquirer/prompts";
2795
2848
  import fs6 from "fs/promises";
2796
2849
  import path3 from "path";
2797
2850
  import { existsSync } from "fs";
2851
+
2852
+ // src/utils/prompt-theme.ts
2853
+ var keysHelpTipWithQuit = (keys) => {
2854
+ const keyStrings = keys.map(([key, action]) => `${key} ${action}`);
2855
+ keyStrings.push("Ctrl+C quit");
2856
+ return keyStrings.join(" \u2022 ");
2857
+ };
2858
+ var checkboxTheme = {
2859
+ style: {
2860
+ keysHelpTip: keysHelpTipWithQuit
2861
+ }
2862
+ };
2863
+ var selectTheme = {
2864
+ style: {
2865
+ keysHelpTip: keysHelpTipWithQuit
2866
+ }
2867
+ };
2868
+
2869
+ // src/commands/ide/setup.ts
2798
2870
  var PERFORMANCE_GROUPS = {
2799
2871
  telemetry: {
2800
2872
  name: "Telemetry",
@@ -2981,7 +3053,8 @@ async function interactiveMode() {
2981
3053
  { name: "\u274C Disable optimization groups", value: "disable" },
2982
3054
  { name: "\u{1F680} Max Performance (enable all)", value: "max" },
2983
3055
  { name: "\u{1F504} Reset to defaults", value: "reset" }
2984
- ]
3056
+ ],
3057
+ theme: selectTheme
2985
3058
  });
2986
3059
  if (action === "max") {
2987
3060
  const allGroups = Object.keys(PERFORMANCE_GROUPS);
@@ -3000,7 +3073,8 @@ async function selectGroupsToApply(action) {
3000
3073
  try {
3001
3074
  const selectedGroups = await checkbox({
3002
3075
  message: `Select groups to ${action} (SPACE to select, ENTER to confirm):`,
3003
- choices
3076
+ choices,
3077
+ theme: checkboxTheme
3004
3078
  });
3005
3079
  if (selectedGroups.length === 0) {
3006
3080
  console.log("\n\u26A0\uFE0F No groups selected!");
@@ -3136,7 +3210,8 @@ async function runSync(options) {
3136
3210
  });
3137
3211
  selectedIdes = await checkbox2({
3138
3212
  message: "Select target IDE(s) (SPACE to select, ENTER to confirm):",
3139
- choices: ideChoices
3213
+ choices: ideChoices,
3214
+ theme: checkboxTheme
3140
3215
  });
3141
3216
  if (selectedIdes.length === 0) {
3142
3217
  console.log("\n\u26A0\uFE0F No IDE selected!");
@@ -3155,7 +3230,8 @@ async function runSync(options) {
3155
3230
  ];
3156
3231
  selectedTypes = await checkbox2({
3157
3232
  message: "Select content types to sync:",
3158
- choices: typeChoices
3233
+ choices: typeChoices,
3234
+ theme: checkboxTheme
3159
3235
  });
3160
3236
  if (selectedTypes.length === 0) {
3161
3237
  console.log("\n\u26A0\uFE0F No content type selected!");
@@ -3281,78 +3357,6 @@ var IDE_FORMATS = {
3281
3357
  // Gemini requires AGENTS.md
3282
3358
  }
3283
3359
  };
3284
- var JAI1_BASE_RULE = `# Jai1 Framework Agent
3285
-
3286
- You are an AI coding assistant integrated with the Jai1 Framework.
3287
-
3288
- ## Skills Reference
3289
-
3290
- The Jai1 Framework provides specialized skills that you can reference and apply:
3291
-
3292
- ### Pattern Detection
3293
-
3294
- When you encounter:
3295
- - **Component creation**: Reference \`@jai1-component-patterns\`
3296
- - **API development**: Reference \`@jai1-api-patterns\`
3297
- - **Database operations**: Reference \`@jai1-database-patterns\`
3298
- - **Testing**: Reference \`@jai1-testing-patterns\`
3299
- - **Deployment**: Reference \`@jai1-deployment-patterns\`
3300
-
3301
- ### Skill Loading Procedure
3302
-
3303
- 1. **Detect the task type** from user request
3304
- 2. **Load relevant skill** using \`@jai1-[skill-name]\` syntax
3305
- 3. **Apply patterns** from the skill to your response
3306
- 4. **Follow conventions** defined in the skill
3307
-
3308
- Example:
3309
- \`\`\`
3310
- User: "Create a new API endpoint for users"
3311
- Assistant:
3312
- 1. Loading @jai1-api-patterns
3313
- 2. Applying RESTful conventions
3314
- 3. Following project structure from @jai1-component-patterns
3315
- 4. Implementing validation using @jai1-testing-patterns
3316
- \`\`\`
3317
-
3318
- ### Application
3319
-
3320
- - **Always reference skills** before implementing patterns
3321
- - **Follow naming conventions** from loaded skills
3322
- - **Apply best practices** defined in skills
3323
- - **Maintain consistency** with existing codebase patterns
3324
-
3325
- ### Naming Convention
3326
-
3327
- Skills are named using the pattern: \`@jai1-[domain]-patterns\`
3328
-
3329
- Examples:
3330
- - \`@jai1-component-patterns\`
3331
- - \`@jai1-api-patterns\`
3332
- - \`@jai1-database-patterns\`
3333
- - \`@jai1-testing-patterns\`
3334
- - \`@jai1-deployment-patterns\`
3335
-
3336
- ## Best Practices
3337
-
3338
- 1. **Load before applying**: Always reference the appropriate skill before implementing
3339
- 2. **Follow project structure**: Use patterns from \`@jai1-component-patterns\`
3340
- 3. **Maintain consistency**: Apply conventions consistently across the codebase
3341
- 4. **Document decisions**: Explain why specific patterns are used
3342
-
3343
- ## Integration
3344
-
3345
- This base rule works in conjunction with:
3346
- - Project-specific rules (e.g., 01-project.md)
3347
- - Technology-specific rules (e.g., 03-frontend.md)
3348
- - Custom rules (e.g., 09-custom.md)
3349
- - AGENTS.md (if present)
3350
-
3351
- When conflicts arise, prioritize:
3352
- 1. Custom rules (09-custom.*)
3353
- 2. Project rules (01-project.*)
3354
- 3. This base rule (00-jai1.*)
3355
- `;
3356
3360
 
3357
3361
  // src/services/ide-detection.service.ts
3358
3362
  var IdeDetectionService = class {
@@ -7830,7 +7834,8 @@ async function handleInteractiveFeedback(config) {
7830
7834
  name: "\u{1F4DD} Suggestion - Share ideas or improvements",
7831
7835
  value: "suggestion"
7832
7836
  }
7833
- ]
7837
+ ],
7838
+ theme: selectTheme
7834
7839
  });
7835
7840
  const title = await input({
7836
7841
  message: "Title (max 200 characters):",
@@ -11145,7 +11150,7 @@ ${"\u2501".repeat(80)}`));
11145
11150
  } else {
11146
11151
  try {
11147
11152
  const selected = await checkbox3({
11148
- message: "Ch\u1ECDn packages \u0111\u1EC3 upgrade (\u2191\u2193 di chuy\u1EC3n \u2022 space ch\u1ECDn \u2022 a t\u1EA5t c\u1EA3 \u2022 i \u0111\u1EA3o \u2022 \u23CE x\xE1c nh\u1EADn \u2022 esc tho\xE1t):",
11153
+ message: "Ch\u1ECDn packages \u0111\u1EC3 upgrade (\u2191\u2193 di chuy\u1EC3n \u2022 space ch\u1ECDn \u2022 a t\u1EA5t c\u1EA3 \u2022 i \u0111\u1EA3o \u2022 \u23CE x\xE1c nh\u1EADn):",
11149
11154
  choices: packages.map((pkg) => {
11150
11155
  const icon = pkg.upgradeType === "major" ? "\u{1F534}" : pkg.upgradeType === "minor" ? "\u{1F7E1}" : "\u{1F7E2}";
11151
11156
  return {
@@ -11154,7 +11159,8 @@ ${"\u2501".repeat(80)}`));
11154
11159
  checked: true
11155
11160
  };
11156
11161
  }),
11157
- pageSize: 15
11162
+ pageSize: 15,
11163
+ theme: checkboxTheme
11158
11164
  });
11159
11165
  if (selected.length === 0) {
11160
11166
  console.log(chalk12.yellow("\u23F8\uFE0F Kh\xF4ng c\xF3 packages n\xE0o \u0111\u01B0\u1EE3c ch\u1ECDn\n"));
@@ -11590,7 +11596,8 @@ function createKitCreateCommand() {
11590
11596
  const answer = await select3({
11591
11597
  message: v.prompt,
11592
11598
  choices: v.options.map((opt) => ({ name: opt, value: opt })),
11593
- default: v.default
11599
+ default: v.default,
11600
+ theme: selectTheme
11594
11601
  });
11595
11602
  variables[v.name] = answer;
11596
11603
  } else {
@@ -11609,9 +11616,13 @@ function createKitCreateCommand() {
11609
11616
  if (!options.skipFramework && kit.config.framework?.apply) {
11610
11617
  console.log("\u{1F916} Applying jai1 framework...");
11611
11618
  const componentsService = new ComponentsService(targetDir);
11612
- for (const filepath of kit.config.framework.components) {
11619
+ const expandedPaths = await componentsService.expandPaths(
11620
+ config,
11621
+ kit.config.framework.components
11622
+ );
11623
+ for (const filepath of expandedPaths) {
11613
11624
  console.log(` \u{1F4E5} Installing ${filepath}...`);
11614
- await componentsService.install(config, filepath);
11625
+ await componentsService.install(config, filepath, join8(targetDir, ".jai1"));
11615
11626
  }
11616
11627
  console.log(" \u2713 Framework components applied");
11617
11628
  }
@@ -11649,7 +11660,8 @@ function createKitCreateCommand() {
11649
11660
  choices: ideChoices.map((c) => ({
11650
11661
  ...c,
11651
11662
  checked: defaultTargets.includes(c.value)
11652
- }))
11663
+ })),
11664
+ theme: checkboxTheme
11653
11665
  });
11654
11666
  selectedIdes = answer;
11655
11667
  }
@@ -11793,7 +11805,7 @@ function createRulesListCommand() {
11793
11805
  console.log(table.toString());
11794
11806
  console.log();
11795
11807
  }
11796
- console.log(chalk16.dim('\u{1F4A1} Ch\u1EA1y "jai1 rules init --preset=<slug>" \u0111\u1EC3 \xE1p d\u1EE5ng preset'));
11808
+ console.log(chalk16.dim('\u{1F4A1} Ch\u1EA1y "jai1 rules apply <name>" \u0111\u1EC3 \xE1p d\u1EE5ng preset'));
11797
11809
  } catch (error) {
11798
11810
  throw new Error(
11799
11811
  `L\u1ED7i khi t\u1EA3i presets: ${error instanceof Error ? error.message : String(error)}`
@@ -11804,9 +11816,129 @@ function createRulesListCommand() {
11804
11816
 
11805
11817
  // src/commands/rules/init.ts
11806
11818
  import { Command as Command44 } from "commander";
11819
+ import { promises as fs20 } from "fs";
11820
+ import { join as join10 } from "path";
11821
+ import { select as select4, confirm as confirm7 } from "@inquirer/prompts";
11822
+
11823
+ // src/services/project-config.service.ts
11807
11824
  import { promises as fs19 } from "fs";
11808
11825
  import { join as join9 } from "path";
11809
- import { select as select4, confirm as confirm7 } from "@inquirer/prompts";
11826
+ var ProjectConfigService = class {
11827
+ projectRoot;
11828
+ configDir;
11829
+ configPath;
11830
+ constructor(projectRoot = process.cwd()) {
11831
+ this.projectRoot = projectRoot;
11832
+ this.configDir = join9(this.projectRoot, ".jai1");
11833
+ this.configPath = join9(this.configDir, "project.json");
11834
+ }
11835
+ /**
11836
+ * Check if config file exists
11837
+ */
11838
+ async exists() {
11839
+ try {
11840
+ await fs19.access(this.configPath);
11841
+ return true;
11842
+ } catch {
11843
+ return false;
11844
+ }
11845
+ }
11846
+ /**
11847
+ * Load full project configuration
11848
+ * @returns Config object or null if not found
11849
+ */
11850
+ async load() {
11851
+ if (!await this.exists()) {
11852
+ return null;
11853
+ }
11854
+ try {
11855
+ const content = await fs19.readFile(this.configPath, "utf-8");
11856
+ return JSON.parse(content);
11857
+ } catch (error) {
11858
+ throw new Error(
11859
+ `Failed to load project config: ${error instanceof Error ? error.message : String(error)}`
11860
+ );
11861
+ }
11862
+ }
11863
+ /**
11864
+ * Save full project configuration
11865
+ * Creates .jai1 directory if it doesn't exist
11866
+ */
11867
+ async save(config) {
11868
+ try {
11869
+ await fs19.mkdir(this.configDir, { recursive: true });
11870
+ await fs19.writeFile(this.configPath, JSON.stringify(config, null, 2), "utf-8");
11871
+ } catch (error) {
11872
+ throw new Error(
11873
+ `Failed to save project config: ${error instanceof Error ? error.message : String(error)}`
11874
+ );
11875
+ }
11876
+ }
11877
+ /**
11878
+ * Load rules configuration only
11879
+ * @returns RulesConfig or null if not found
11880
+ */
11881
+ async loadRules() {
11882
+ const config = await this.load();
11883
+ return config?.rules ?? null;
11884
+ }
11885
+ /**
11886
+ * Save rules configuration
11887
+ * Merges with existing config, preserving other sections
11888
+ */
11889
+ async saveRules(rulesConfig) {
11890
+ const existingConfig = await this.load() ?? {};
11891
+ existingConfig.rules = rulesConfig;
11892
+ await this.save(existingConfig);
11893
+ }
11894
+ /**
11895
+ * Update rules configuration partially
11896
+ * Merges with existing rules config
11897
+ */
11898
+ async updateRules(partialRulesConfig) {
11899
+ const existingRules = await this.loadRules();
11900
+ if (!existingRules) {
11901
+ throw new Error('No rules configuration found. Run "jai1 rules apply" first.');
11902
+ }
11903
+ const updatedRules = {
11904
+ ...existingRules,
11905
+ ...partialRulesConfig
11906
+ };
11907
+ await this.saveRules(updatedRules);
11908
+ }
11909
+ /**
11910
+ * Add a backup entry to rules config
11911
+ * Keeps only the last 5 backups
11912
+ */
11913
+ async addBackup(backup) {
11914
+ const rules = await this.loadRules();
11915
+ if (!rules) {
11916
+ throw new Error("No rules configuration found.");
11917
+ }
11918
+ const backups = [backup, ...rules.backups ?? []].slice(0, 5);
11919
+ await this.updateRules({ backups });
11920
+ }
11921
+ /**
11922
+ * Get config file path
11923
+ */
11924
+ getConfigPath() {
11925
+ return this.configPath;
11926
+ }
11927
+ /**
11928
+ * Get config directory path (.jai1/)
11929
+ */
11930
+ getConfigDir() {
11931
+ return this.configDir;
11932
+ }
11933
+ /**
11934
+ * Get project root path
11935
+ */
11936
+ getProjectRoot() {
11937
+ return this.projectRoot;
11938
+ }
11939
+ };
11940
+
11941
+ // src/commands/rules/init.ts
11810
11942
  function createRulesInitCommand() {
11811
11943
  return new Command44("init").description("Apply rule preset to project").option("--preset <slug>", "Preset slug to apply").option("--output <format>", "Output format: cursor, agents-md, both (default: cursor)", "cursor").option("-y, --yes", "Skip confirmations").action(async (options) => {
11812
11944
  const configService = new ConfigService();
@@ -11836,7 +11968,8 @@ function createRulesInitCommand() {
11836
11968
  name: `${p.name} - ${p.description}`,
11837
11969
  value: p.slug,
11838
11970
  description: `v${p.version} | ${p.tags.join(", ")}`
11839
- }))
11971
+ })),
11972
+ theme: selectTheme
11840
11973
  });
11841
11974
  }
11842
11975
  console.log(`
@@ -11864,7 +11997,8 @@ function createRulesInitCommand() {
11864
11997
  { name: "Cursor (.cursor/rules/)", value: "cursor" },
11865
11998
  { name: "AGENTS.md (single file)", value: "agents-md" },
11866
11999
  { name: "Both", value: "both" }
11867
- ]
12000
+ ],
12001
+ theme: selectTheme
11868
12002
  });
11869
12003
  }
11870
12004
  if (!options.yes) {
@@ -11884,14 +12018,16 @@ function createRulesInitCommand() {
11884
12018
  if (outputFormat === "agents-md" || outputFormat === "both") {
11885
12019
  await applyAgentsMdFormat(bundle);
11886
12020
  }
11887
- const projectConfig = {
12021
+ const projectConfigService = new ProjectConfigService();
12022
+ const rulesConfig = {
11888
12023
  preset: bundle.preset.slug,
11889
12024
  version: bundle.preset.version,
11890
12025
  appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
12026
+ ides: [],
11891
12027
  customContext: "09-custom.mdc"
11892
12028
  };
11893
- await fs19.writeFile("jai1-rules.json", JSON.stringify(projectConfig, null, 2));
11894
- console.log("\u2713 Created jai1-rules.json");
12029
+ await projectConfigService.saveRules(rulesConfig);
12030
+ console.log("\u2713 Created .jai1/project.json");
11895
12031
  console.log("\n\u2705 Preset applied successfully!\n");
11896
12032
  console.log("Next steps:");
11897
12033
  console.log(" 1. Edit 09-custom.mdc to add project-specific rules");
@@ -11900,11 +12036,11 @@ function createRulesInitCommand() {
11900
12036
  });
11901
12037
  }
11902
12038
  async function applyCursorFormat(bundle) {
11903
- const rulesDir = join9(process.cwd(), ".cursor", "rules");
11904
- await fs19.mkdir(rulesDir, { recursive: true });
12039
+ const rulesDir = join10(process.cwd(), ".cursor", "rules");
12040
+ await fs20.mkdir(rulesDir, { recursive: true });
11905
12041
  for (const [filename, content] of Object.entries(bundle.files)) {
11906
- const filePath = join9(rulesDir, filename);
11907
- await fs19.writeFile(filePath, content, "utf-8");
12042
+ const filePath = join10(rulesDir, filename);
12043
+ await fs20.writeFile(filePath, content, "utf-8");
11908
12044
  console.log(`\u2713 Created .cursor/rules/${filename}`);
11909
12045
  }
11910
12046
  }
@@ -11931,14 +12067,14 @@ async function applyAgentsMdFormat(bundle) {
11931
12067
  }
11932
12068
  }
11933
12069
  const agentsMd = sections.join("\n");
11934
- await fs19.writeFile("AGENTS.md", agentsMd, "utf-8");
12070
+ await fs20.writeFile("AGENTS.md", agentsMd, "utf-8");
11935
12071
  console.log("\u2713 Created AGENTS.md");
11936
12072
  }
11937
12073
 
11938
12074
  // src/commands/rules/apply.ts
11939
12075
  import { Command as Command45 } from "commander";
11940
- import { promises as fs21 } from "fs";
11941
- import { join as join11 } from "path";
12076
+ import { promises as fs22 } from "fs";
12077
+ import { join as join12 } from "path";
11942
12078
  import { select as select5, confirm as confirm8, checkbox as checkbox5 } from "@inquirer/prompts";
11943
12079
 
11944
12080
  // src/services/rules-generator.service.ts
@@ -11973,11 +12109,6 @@ var RulesGeneratorService = class {
11973
12109
  */
11974
12110
  generateCursorFiles(bundle, format) {
11975
12111
  const files = [];
11976
- files.push({
11977
- path: `${format.rulesPath}/00-jai1${format.fileExtension}`,
11978
- content: this.generateCursorJai1Rule(),
11979
- description: "Jai1 Framework base rule"
11980
- });
11981
12112
  for (const [filename, content] of Object.entries(bundle.files)) {
11982
12113
  files.push({
11983
12114
  path: `${format.rulesPath}/${filename}`,
@@ -11988,28 +12119,11 @@ var RulesGeneratorService = class {
11988
12119
  }
11989
12120
  return files;
11990
12121
  }
11991
- /**
11992
- * Generate Cursor jai1 base rule
11993
- */
11994
- generateCursorJai1Rule() {
11995
- return `---
11996
- description: Jai1 Framework integration and skill loading
11997
- globs: ["**/*"]
11998
- alwaysApply: true
11999
- ---
12000
-
12001
- ${JAI1_BASE_RULE}`;
12002
- }
12003
12122
  /**
12004
12123
  * Generate Windsurf format (.md files with trigger metadata)
12005
12124
  */
12006
12125
  generateWindsurfFiles(bundle, format) {
12007
12126
  const files = [];
12008
- files.push({
12009
- path: `${format.rulesPath}/00-jai1${format.fileExtension}`,
12010
- content: this.generateWindsurfJai1Rule(),
12011
- description: "Jai1 Framework base rule"
12012
- });
12013
12127
  for (const [filename, content] of Object.entries(bundle.files)) {
12014
12128
  const converted = this.convertToWindsurf(content, filename);
12015
12129
  const newFilename = filename.replace(/\.mdc$/, ".md");
@@ -12044,27 +12158,12 @@ trigger: ${trigger}
12044
12158
 
12045
12159
  ${body.trim()}
12046
12160
  `;
12047
- }
12048
- /**
12049
- * Generate Windsurf jai1 base rule
12050
- */
12051
- generateWindsurfJai1Rule() {
12052
- return `---
12053
- trigger: always
12054
- ---
12055
-
12056
- ${JAI1_BASE_RULE}`;
12057
12161
  }
12058
12162
  /**
12059
12163
  * Generate Antigravity format (.md files with trigger metadata + @AGENTS.md reference)
12060
12164
  */
12061
12165
  generateAntigravityFiles(bundle, format) {
12062
12166
  const files = [];
12063
- files.push({
12064
- path: `${format.rulesPath}/00-jai1${format.fileExtension}`,
12065
- content: this.generateAntigravityJai1Rule(),
12066
- description: "Jai1 Framework base rule"
12067
- });
12068
12167
  for (const [filename, content] of Object.entries(bundle.files)) {
12069
12168
  const converted = this.convertToAntigravity(content, filename);
12070
12169
  const newFilename = filename.replace(/\.mdc$/, ".md");
@@ -12099,29 +12198,12 @@ trigger: ${trigger}
12099
12198
 
12100
12199
  ${body.trim()}
12101
12200
  `;
12102
- }
12103
- /**
12104
- * Generate Antigravity jai1 base rule
12105
- */
12106
- generateAntigravityJai1Rule() {
12107
- return `---
12108
- trigger: always_on
12109
- ---
12110
-
12111
- @AGENTS.md
12112
-
12113
- ${JAI1_BASE_RULE}`;
12114
12201
  }
12115
12202
  /**
12116
12203
  * Generate Claude Code format (.md files with paths metadata)
12117
12204
  */
12118
12205
  generateClaudeFiles(bundle, format) {
12119
12206
  const files = [];
12120
- files.push({
12121
- path: `${format.rulesPath}/00-jai1${format.fileExtension}`,
12122
- content: this.generateClaudeJai1Rule(),
12123
- description: "Jai1 Framework base rule"
12124
- });
12125
12207
  for (const [filename, content] of Object.entries(bundle.files)) {
12126
12208
  const converted = this.convertToClaude(content, filename);
12127
12209
  const newFilename = filename.replace(/\.mdc$/, ".md");
@@ -12155,17 +12237,6 @@ paths: ${paths}`;
12155
12237
 
12156
12238
  ${body.trim()}
12157
12239
  `;
12158
- }
12159
- /**
12160
- * Generate Claude jai1 base rule
12161
- */
12162
- generateClaudeJai1Rule() {
12163
- return `---
12164
- description: Jai1 Framework integration and skill loading
12165
- paths: **/*
12166
- ---
12167
-
12168
- ${JAI1_BASE_RULE}`;
12169
12240
  }
12170
12241
  /**
12171
12242
  * Generate AGENTS.md file (single merged file)
@@ -12175,10 +12246,8 @@ ${JAI1_BASE_RULE}`;
12175
12246
  sections.push("# AGENTS.md\n");
12176
12247
  sections.push(`<!-- Generated by jai1 rules - Preset: ${bundle.preset.slug} v${bundle.preset.version} -->
12177
12248
  `);
12178
- sections.push("## Jai1 Framework Integration\n");
12179
- sections.push(JAI1_BASE_RULE.trim());
12180
- sections.push("\n---\n");
12181
12249
  const fileOrder = [
12250
+ "00-jai1.mdc",
12182
12251
  "01-project.mdc",
12183
12252
  "02-standards.mdc",
12184
12253
  "03-frontend.mdc",
@@ -12298,8 +12367,8 @@ Follow all instructions and patterns defined in AGENTS.md above.
12298
12367
  };
12299
12368
 
12300
12369
  // src/services/backup.service.ts
12301
- import { promises as fs20 } from "fs";
12302
- import { join as join10, dirname } from "path";
12370
+ import { promises as fs21 } from "fs";
12371
+ import { join as join11, dirname } from "path";
12303
12372
  var BackupService = class {
12304
12373
  backupDir = ".jai1/backups";
12305
12374
  /**
@@ -12307,7 +12376,7 @@ var BackupService = class {
12307
12376
  */
12308
12377
  async createBackup(ides, presetSlug) {
12309
12378
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
12310
- const backupPath = join10(this.backupDir, timestamp);
12379
+ const backupPath = join11(this.backupDir, timestamp);
12311
12380
  const backedUpFiles = [];
12312
12381
  let hasContent = false;
12313
12382
  for (const ideId of ides) {
@@ -12316,7 +12385,7 @@ var BackupService = class {
12316
12385
  console.warn(`Unknown IDE format: ${ideId}, skipping backup`);
12317
12386
  continue;
12318
12387
  }
12319
- const rulesPath = format.rulesPath === "." ? process.cwd() : join10(process.cwd(), format.rulesPath);
12388
+ const rulesPath = format.rulesPath === "." ? process.cwd() : join11(process.cwd(), format.rulesPath);
12320
12389
  try {
12321
12390
  const exists = await this.pathExists(rulesPath);
12322
12391
  if (!exists) {
@@ -12329,19 +12398,19 @@ var BackupService = class {
12329
12398
  await this.backupSingleFile("GEMINI.md", backupPath, ideId, backedUpFiles);
12330
12399
  hasContent = true;
12331
12400
  } else {
12332
- const stats = await fs20.stat(rulesPath);
12401
+ const stats = await fs21.stat(rulesPath);
12333
12402
  if (stats.isDirectory()) {
12334
- const files = await fs20.readdir(rulesPath);
12403
+ const files = await fs21.readdir(rulesPath);
12335
12404
  for (const file of files) {
12336
12405
  if (file.endsWith(format.fileExtension)) {
12337
- const originalPath = join10(rulesPath, file);
12338
- const relativePath = join10(format.rulesPath, file);
12339
- const destPath = join10(backupPath, ideId, file);
12340
- await fs20.mkdir(dirname(destPath), { recursive: true });
12341
- await fs20.copyFile(originalPath, destPath);
12406
+ const originalPath = join11(rulesPath, file);
12407
+ const relativePath = join11(format.rulesPath, file);
12408
+ const destPath = join11(backupPath, ideId, file);
12409
+ await fs21.mkdir(dirname(destPath), { recursive: true });
12410
+ await fs21.copyFile(originalPath, destPath);
12342
12411
  backedUpFiles.push({
12343
12412
  originalPath: relativePath,
12344
- backupPath: join10(ideId, file),
12413
+ backupPath: join11(ideId, file),
12345
12414
  ide: ideId
12346
12415
  });
12347
12416
  hasContent = true;
@@ -12362,9 +12431,9 @@ var BackupService = class {
12362
12431
  ides,
12363
12432
  files: backedUpFiles
12364
12433
  };
12365
- await fs20.mkdir(backupPath, { recursive: true });
12366
- await fs20.writeFile(
12367
- join10(backupPath, "metadata.json"),
12434
+ await fs21.mkdir(backupPath, { recursive: true });
12435
+ await fs21.writeFile(
12436
+ join11(backupPath, "metadata.json"),
12368
12437
  JSON.stringify(metadata, null, 2),
12369
12438
  "utf-8"
12370
12439
  );
@@ -12374,18 +12443,18 @@ var BackupService = class {
12374
12443
  * Backup a single file (for AGENTS.md, GEMINI.md)
12375
12444
  */
12376
12445
  async backupSingleFile(filename, backupPath, ideId, backedUpFiles) {
12377
- const originalPath = join10(process.cwd(), filename);
12446
+ const originalPath = join11(process.cwd(), filename);
12378
12447
  try {
12379
12448
  const exists = await this.pathExists(originalPath);
12380
12449
  if (!exists) {
12381
12450
  return;
12382
12451
  }
12383
- const destPath = join10(backupPath, ideId, filename);
12384
- await fs20.mkdir(dirname(destPath), { recursive: true });
12385
- await fs20.copyFile(originalPath, destPath);
12452
+ const destPath = join11(backupPath, ideId, filename);
12453
+ await fs21.mkdir(dirname(destPath), { recursive: true });
12454
+ await fs21.copyFile(originalPath, destPath);
12386
12455
  backedUpFiles.push({
12387
12456
  originalPath: filename,
12388
- backupPath: join10(ideId, filename),
12457
+ backupPath: join11(ideId, filename),
12389
12458
  ide: ideId
12390
12459
  });
12391
12460
  } catch (error) {
@@ -12395,16 +12464,16 @@ var BackupService = class {
12395
12464
  * Restore files from a backup
12396
12465
  */
12397
12466
  async restoreBackup(backupPath) {
12398
- const metadataPath = join10(backupPath, "metadata.json");
12399
- const metadataContent = await fs20.readFile(metadataPath, "utf-8");
12467
+ const metadataPath = join11(backupPath, "metadata.json");
12468
+ const metadataContent = await fs21.readFile(metadataPath, "utf-8");
12400
12469
  const metadata = JSON.parse(metadataContent);
12401
12470
  console.log(`
12402
12471
  Restoring backup from ${metadata.timestamp}...`);
12403
12472
  for (const file of metadata.files) {
12404
- const sourcePath = join10(backupPath, file.backupPath);
12405
- const destPath = join10(process.cwd(), file.originalPath);
12406
- await fs20.mkdir(dirname(destPath), { recursive: true });
12407
- await fs20.copyFile(sourcePath, destPath);
12473
+ const sourcePath = join11(backupPath, file.backupPath);
12474
+ const destPath = join11(process.cwd(), file.originalPath);
12475
+ await fs21.mkdir(dirname(destPath), { recursive: true });
12476
+ await fs21.copyFile(sourcePath, destPath);
12408
12477
  console.log(`\u2713 Restored ${file.originalPath}`);
12409
12478
  }
12410
12479
  console.log("\n\u2705 Backup restored successfully!");
@@ -12414,18 +12483,18 @@ Restoring backup from ${metadata.timestamp}...`);
12414
12483
  */
12415
12484
  async listBackups() {
12416
12485
  try {
12417
- const backupDirPath = join10(process.cwd(), this.backupDir);
12486
+ const backupDirPath = join11(process.cwd(), this.backupDir);
12418
12487
  const exists = await this.pathExists(backupDirPath);
12419
12488
  if (!exists) {
12420
12489
  return [];
12421
12490
  }
12422
- const entries = await fs20.readdir(backupDirPath, { withFileTypes: true });
12491
+ const entries = await fs21.readdir(backupDirPath, { withFileTypes: true });
12423
12492
  const backups = [];
12424
12493
  for (const entry of entries) {
12425
12494
  if (entry.isDirectory()) {
12426
- const metadataPath = join10(backupDirPath, entry.name, "metadata.json");
12495
+ const metadataPath = join11(backupDirPath, entry.name, "metadata.json");
12427
12496
  try {
12428
- const metadataContent = await fs20.readFile(metadataPath, "utf-8");
12497
+ const metadataContent = await fs21.readFile(metadataPath, "utf-8");
12429
12498
  const metadata = JSON.parse(metadataContent);
12430
12499
  backups.push(metadata);
12431
12500
  } catch {
@@ -12442,7 +12511,7 @@ Restoring backup from ${metadata.timestamp}...`);
12442
12511
  * Delete a specific backup
12443
12512
  */
12444
12513
  async deleteBackup(timestamp) {
12445
- const backupPath = join10(process.cwd(), this.backupDir, timestamp);
12514
+ const backupPath = join11(process.cwd(), this.backupDir, timestamp);
12446
12515
  await this.deleteDirectory(backupPath);
12447
12516
  }
12448
12517
  /**
@@ -12474,7 +12543,7 @@ Restoring backup from ${metadata.timestamp}...`);
12474
12543
  */
12475
12544
  async pathExists(path13) {
12476
12545
  try {
12477
- await fs20.access(path13);
12546
+ await fs21.access(path13);
12478
12547
  return true;
12479
12548
  } catch {
12480
12549
  return false;
@@ -12489,16 +12558,16 @@ Restoring backup from ${metadata.timestamp}...`);
12489
12558
  if (!exists) {
12490
12559
  return;
12491
12560
  }
12492
- const entries = await fs20.readdir(path13, { withFileTypes: true });
12561
+ const entries = await fs21.readdir(path13, { withFileTypes: true });
12493
12562
  for (const entry of entries) {
12494
- const fullPath = join10(path13, entry.name);
12563
+ const fullPath = join11(path13, entry.name);
12495
12564
  if (entry.isDirectory()) {
12496
12565
  await this.deleteDirectory(fullPath);
12497
12566
  } else {
12498
- await fs20.unlink(fullPath);
12567
+ await fs21.unlink(fullPath);
12499
12568
  }
12500
12569
  }
12501
- await fs20.rmdir(path13);
12570
+ await fs21.rmdir(path13);
12502
12571
  } catch (error) {
12503
12572
  }
12504
12573
  }
@@ -12506,14 +12575,14 @@ Restoring backup from ${metadata.timestamp}...`);
12506
12575
  * Get backup directory path
12507
12576
  */
12508
12577
  getBackupDir() {
12509
- return join10(process.cwd(), this.backupDir);
12578
+ return join11(process.cwd(), this.backupDir);
12510
12579
  }
12511
12580
  /**
12512
12581
  * Ensure backup directory exists
12513
12582
  */
12514
12583
  async ensureBackupDir() {
12515
- const backupDirPath = join10(process.cwd(), this.backupDir);
12516
- await fs20.mkdir(backupDirPath, { recursive: true });
12584
+ const backupDirPath = join11(process.cwd(), this.backupDir);
12585
+ await fs21.mkdir(backupDirPath, { recursive: true });
12517
12586
  }
12518
12587
  };
12519
12588
 
@@ -12557,7 +12626,8 @@ function createRulesApplyCommand() {
12557
12626
  name: `${p.name} - ${p.description}`,
12558
12627
  value: p.slug,
12559
12628
  description: `v${p.version} | ${p.tags.join(", ")}`
12560
- }))
12629
+ })),
12630
+ theme: selectTheme
12561
12631
  });
12562
12632
  }
12563
12633
  console.log(`
@@ -12627,7 +12697,8 @@ function createRulesApplyCommand() {
12627
12697
  selectedIdes = await checkbox5({
12628
12698
  message: "Select IDE formats to generate (pre-selected are recommended):",
12629
12699
  choices,
12630
- required: true
12700
+ required: true,
12701
+ theme: checkboxTheme
12631
12702
  });
12632
12703
  }
12633
12704
  }
@@ -12669,21 +12740,21 @@ function createRulesApplyCommand() {
12669
12740
  }
12670
12741
  }
12671
12742
  console.log("\n\u{1F4DD} Applying preset...\n");
12672
- const rulePresetDir = join11(process.cwd(), ".jai1", "rule-preset");
12743
+ const rulePresetDir = join12(process.cwd(), ".jai1", "rule-preset");
12673
12744
  try {
12674
- await fs21.rm(rulePresetDir, { recursive: true, force: true });
12745
+ await fs22.rm(rulePresetDir, { recursive: true, force: true });
12675
12746
  } catch {
12676
12747
  }
12677
- await fs21.mkdir(rulePresetDir, { recursive: true });
12678
- await fs21.writeFile(
12679
- join11(rulePresetDir, "preset.json"),
12748
+ await fs22.mkdir(rulePresetDir, { recursive: true });
12749
+ await fs22.writeFile(
12750
+ join12(rulePresetDir, "preset.json"),
12680
12751
  JSON.stringify(bundle.preset, null, 2),
12681
12752
  "utf-8"
12682
12753
  );
12683
12754
  for (const [filename, content] of Object.entries(bundle.files)) {
12684
- const filePath = join11(rulePresetDir, filename);
12685
- await fs21.mkdir(join11(filePath, ".."), { recursive: true });
12686
- await fs21.writeFile(filePath, content, "utf-8");
12755
+ const filePath = join12(rulePresetDir, filename);
12756
+ await fs22.mkdir(join12(filePath, ".."), { recursive: true });
12757
+ await fs22.writeFile(filePath, content, "utf-8");
12687
12758
  }
12688
12759
  console.log(`\u2713 Saved preset to .jai1/rule-preset/`);
12689
12760
  const allGeneratedFiles = [];
@@ -12691,9 +12762,9 @@ function createRulesApplyCommand() {
12691
12762
  try {
12692
12763
  const files = generatorService.generateForIde(bundle, ideId);
12693
12764
  for (const file of files) {
12694
- const fullPath = join11(process.cwd(), file.path);
12695
- await fs21.mkdir(join11(fullPath, ".."), { recursive: true });
12696
- await fs21.writeFile(fullPath, file.content, "utf-8");
12765
+ const fullPath = join12(process.cwd(), file.path);
12766
+ await fs22.mkdir(join12(fullPath, ".."), { recursive: true });
12767
+ await fs22.writeFile(fullPath, file.content, "utf-8");
12697
12768
  console.log(`\u2713 [${ideId}] ${file.path}`);
12698
12769
  allGeneratedFiles.push({
12699
12770
  ide: ideId,
@@ -12705,7 +12776,8 @@ function createRulesApplyCommand() {
12705
12776
  console.error(`\u2717 Failed to generate ${ideId} files:`, error);
12706
12777
  }
12707
12778
  }
12708
- const projectConfig = {
12779
+ const projectConfigService = new ProjectConfigService();
12780
+ const rulesConfig = {
12709
12781
  preset: bundle.preset.slug,
12710
12782
  version: bundle.preset.version,
12711
12783
  appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -12720,24 +12792,18 @@ function createRulesApplyCommand() {
12720
12792
  ] : []
12721
12793
  };
12722
12794
  try {
12723
- const existingConfigPath = join11(process.cwd(), "jai1-rules.json");
12724
- const existingConfigContent = await fs21.readFile(existingConfigPath, "utf-8");
12725
- const existingConfig = JSON.parse(existingConfigContent);
12726
- if (existingConfig.backups && existingConfig.backups.length > 0) {
12727
- projectConfig.backups = [
12728
- ...projectConfig.backups,
12729
- ...existingConfig.backups.slice(0, 5)
12795
+ const existingRules = await projectConfigService.loadRules();
12796
+ if (existingRules?.backups && existingRules.backups.length > 0) {
12797
+ rulesConfig.backups = [
12798
+ ...rulesConfig.backups,
12799
+ ...existingRules.backups.slice(0, 5)
12730
12800
  // Keep last 5 backups
12731
12801
  ];
12732
12802
  }
12733
12803
  } catch {
12734
12804
  }
12735
- await fs21.writeFile(
12736
- join11(process.cwd(), "jai1-rules.json"),
12737
- JSON.stringify(projectConfig, null, 2),
12738
- "utf-8"
12739
- );
12740
- console.log("\n\u2713 Updated jai1-rules.json");
12805
+ await projectConfigService.saveRules(rulesConfig);
12806
+ console.log("\n\u2713 Updated .jai1/project.json");
12741
12807
  console.log("\n\u2705 Preset applied successfully!\n");
12742
12808
  console.log("\u{1F4CA} Summary:");
12743
12809
  console.log(` Generated ${allGeneratedFiles.length} files across ${resolvedIdes.length} IDE(s)`);
@@ -12765,7 +12831,7 @@ function createRulesApplyCommand() {
12765
12831
 
12766
12832
  // src/commands/rules/restore.ts
12767
12833
  import { Command as Command46 } from "commander";
12768
- import { join as join12 } from "path";
12834
+ import { join as join13 } from "path";
12769
12835
  import { select as select6, confirm as confirm9 } from "@inquirer/prompts";
12770
12836
  function createRulesRestoreCommand() {
12771
12837
  return new Command46("restore").description("Restore rules from a backup").option("--latest", "Restore the most recent backup").option("-y, --yes", "Skip confirmation").action(async (options) => {
@@ -12787,7 +12853,8 @@ function createRulesRestoreCommand() {
12787
12853
  name: formatBackupInfo(backup),
12788
12854
  value: backup.timestamp,
12789
12855
  description: `${backup.files.length} files | IDEs: ${backup.ides.join(", ")}`
12790
- }))
12856
+ })),
12857
+ theme: selectTheme
12791
12858
  });
12792
12859
  selectedBackup = backups.find((b) => b.timestamp === backupTimestamp);
12793
12860
  if (!selectedBackup) {
@@ -12811,7 +12878,7 @@ function createRulesRestoreCommand() {
12811
12878
  }
12812
12879
  console.log("\n\u{1F504} Restoring backup...\n");
12813
12880
  try {
12814
- const backupPath = join12(backupService.getBackupDir(), selectedBackup.timestamp);
12881
+ const backupPath = join13(backupService.getBackupDir(), selectedBackup.timestamp);
12815
12882
  await backupService.restoreBackup(backupPath);
12816
12883
  console.log("\n\u2705 Backup restored successfully!\n");
12817
12884
  console.log("\u{1F4A1} Tip: Your IDE may need to be restarted to pick up the changes.");
@@ -12837,23 +12904,41 @@ function formatTimestamp(timestamp) {
12837
12904
 
12838
12905
  // src/commands/rules/sync.ts
12839
12906
  import { Command as Command47 } from "commander";
12840
- import { promises as fs22 } from "fs";
12841
- import { join as join13 } from "path";
12842
- import { checkbox as checkbox6, confirm as confirm10 } from "@inquirer/prompts";
12907
+ import { promises as fs23 } from "fs";
12908
+ import { join as join14 } from "path";
12909
+ import { checkbox as checkbox6, confirm as confirm10, Separator } from "@inquirer/prompts";
12843
12910
  function createRulesSyncCommand() {
12844
12911
  return new Command47("sync").description("Regenerate rule outputs for all configured IDEs").option("--ides <ides>", "Comma-separated list of IDEs to sync (default: all configured)").option("--detect", "Auto-detect active IDEs instead of using config").option("-y, --yes", "Skip confirmations").action(async (options) => {
12845
- const configPath = join13(process.cwd(), "jai1-rules.json");
12846
- let projectConfig;
12912
+ const rulePresetDir = join14(process.cwd(), ".jai1", "rule-preset");
12913
+ const presetJsonPath = join14(rulePresetDir, "preset.json");
12914
+ let presetExists = false;
12915
+ let presetData = null;
12847
12916
  try {
12848
- const configContent = await fs22.readFile(configPath, "utf-8");
12849
- projectConfig = JSON.parse(configContent);
12917
+ const presetContent = await fs23.readFile(presetJsonPath, "utf-8");
12918
+ presetData = JSON.parse(presetContent);
12919
+ presetExists = true;
12850
12920
  } catch {
12921
+ }
12922
+ if (!presetExists) {
12851
12923
  throw new ValidationError(
12852
- 'No jai1-rules.json found. Run "jai1 rules apply" first.'
12924
+ 'No rule preset found in .jai1/rule-preset/\nRun "jai1 rules apply <preset>" or create a project with "jai1 kit create <kit>" first.'
12853
12925
  );
12854
12926
  }
12927
+ const projectConfigService = new ProjectConfigService();
12928
+ let rulesConfig = await projectConfigService.loadRules();
12929
+ if (!rulesConfig) {
12930
+ console.log("\u26A0\uFE0F No .jai1/project.json found, creating from preset...\n");
12931
+ rulesConfig = {
12932
+ preset: presetData.slug,
12933
+ version: presetData.version,
12934
+ appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
12935
+ ides: [],
12936
+ customContext: "09-custom.md",
12937
+ backups: []
12938
+ };
12939
+ }
12855
12940
  console.log("\u{1F504} Syncing rules...\n");
12856
- console.log(`Preset: ${projectConfig.preset} v${projectConfig.version}`);
12941
+ console.log(`Preset: ${rulesConfig.preset} v${rulesConfig.version}`);
12857
12942
  let idesToSync = [];
12858
12943
  if (options.detect) {
12859
12944
  console.log("\n\u{1F50D} Auto-detecting active IDEs...");
@@ -12884,16 +12969,16 @@ Detected ${detected.length} active IDE(s):
12884
12969
  idesToSync = detected.map((d) => d.id);
12885
12970
  } else if (options.ides) {
12886
12971
  const requested = options.ides.split(",").map((ide) => ide.trim());
12887
- const configured = projectConfig.ides || [];
12972
+ const configured = rulesConfig.ides || [];
12888
12973
  idesToSync = requested;
12889
12974
  const notConfigured = requested.filter((ide) => !configured.includes(ide));
12890
12975
  if (notConfigured.length > 0) {
12891
12976
  console.log(`
12892
12977
  \u26A0\uFE0F IDEs not in config: ${notConfigured.join(", ")}`);
12893
- console.log(" They will still be synced, but may not be in jai1-rules.json");
12978
+ console.log(" They will still be synced, but may not be in .jai1/project.json");
12894
12979
  }
12895
12980
  } else if (!options.yes) {
12896
- const currentIdes = projectConfig.ides || [];
12981
+ const currentIdes = rulesConfig.ides || [];
12897
12982
  console.log(`
12898
12983
  Current IDE(s): ${currentIdes.join(", ") || "none"}`);
12899
12984
  const detectionService = new IdeDetectionService();
@@ -12913,18 +12998,23 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
12913
12998
  }
12914
12999
  const choices = buildIdeChoices(currentIdes, detected, suggestions);
12915
13000
  idesToSync = await checkbox6({
12916
- message: "Select IDE formats to sync (pre-selected are recommended):",
13001
+ message: "Select IDE formats to sync:",
12917
13002
  choices,
12918
- required: true
13003
+ required: false,
13004
+ theme: checkboxTheme
12919
13005
  });
13006
+ if (idesToSync.includes("__quit__")) {
13007
+ console.log("Cancelled.");
13008
+ return;
13009
+ }
12920
13010
  if (idesToSync.length === 0) {
12921
13011
  console.log("Cancelled.");
12922
13012
  return;
12923
13013
  }
12924
13014
  } else {
12925
- idesToSync = projectConfig.ides || [];
13015
+ idesToSync = rulesConfig.ides || [];
12926
13016
  if (idesToSync.length === 0) {
12927
- console.log("\n\u26A0\uFE0F No IDEs configured in jai1-rules.json");
13017
+ console.log("\n\u26A0\uFE0F No IDEs configured in .jai1/project.json");
12928
13018
  console.log(" Detecting from existing files...\n");
12929
13019
  const detectionService = new IdeDetectionService();
12930
13020
  idesToSync = await detectionService.detectExistingIdes();
@@ -12945,7 +13035,7 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
12945
13035
  if (!config) {
12946
13036
  throw new ValidationError('Not initialized. Run "jai1 auth" first.');
12947
13037
  }
12948
- const presetResponse = await fetch(`${config.apiUrl}/api/rules/presets/${projectConfig.preset}`, {
13038
+ const presetResponse = await fetch(`${config.apiUrl}/api/rules/presets/${rulesConfig.preset}`, {
12949
13039
  headers: {
12950
13040
  "JAI1-Access-Key": config.accessKey
12951
13041
  }
@@ -12954,21 +13044,13 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
12954
13044
  throw new Error(`Failed to fetch preset: ${presetResponse.statusText}`);
12955
13045
  }
12956
13046
  const bundle = await presetResponse.json();
12957
- const rulePresetDir = join13(process.cwd(), ".jai1", "rule-preset");
12958
- const presetExists = await checkPathExists(rulePresetDir);
12959
- if (presetExists) {
12960
- const files = await fs22.readdir(rulePresetDir);
12961
- for (const file of files) {
12962
- if (file.endsWith(".mdc")) {
12963
- const filePath = join13(rulePresetDir, file);
12964
- const content = await fs22.readFile(filePath, "utf-8");
12965
- bundle.files[file] = content;
12966
- }
13047
+ const files = await fs23.readdir(rulePresetDir);
13048
+ for (const file of files) {
13049
+ if (file.endsWith(".mdc") || file.endsWith(".md")) {
13050
+ const filePath = join14(rulePresetDir, file);
13051
+ const content = await fs23.readFile(filePath, "utf-8");
13052
+ bundle.files[file] = content;
12967
13053
  }
12968
- } else {
12969
- console.log("\n\u26A0\uFE0F No rule preset found in .jai1/rule-preset/");
12970
- console.log(' Run "jai1 rules apply" first to apply a preset.\n');
12971
- return;
12972
13054
  }
12973
13055
  const generatorService = new RulesGeneratorService();
12974
13056
  for (const ideId of idesToSync) {
@@ -12978,25 +13060,21 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
12978
13060
  console.log(`\u26A0\uFE0F Unknown IDE format: ${ideId}, skipping`);
12979
13061
  continue;
12980
13062
  }
12981
- const files = generatorService.generateForIde(bundle, ideId);
12982
- for (const file of files) {
12983
- const fullPath = join13(process.cwd(), file.path);
12984
- await fs22.mkdir(join13(fullPath, ".."), { recursive: true });
12985
- await fs22.writeFile(fullPath, file.content, "utf-8");
13063
+ const files2 = generatorService.generateForIde(bundle, ideId);
13064
+ for (const file of files2) {
13065
+ const fullPath = join14(process.cwd(), file.path);
13066
+ await fs23.mkdir(join14(fullPath, ".."), { recursive: true });
13067
+ await fs23.writeFile(fullPath, file.content, "utf-8");
12986
13068
  }
12987
- console.log(`\u2713 ${format.name} - ${files.length} files regenerated`);
13069
+ console.log(`\u2713 ${format.name} - ${files2.length} files regenerated`);
12988
13070
  } catch (error) {
12989
13071
  console.error(`\u2717 Failed to sync ${ideId}:`, error);
12990
13072
  }
12991
13073
  }
12992
- if (JSON.stringify(projectConfig.ides) !== JSON.stringify(idesToSync)) {
12993
- projectConfig.ides = idesToSync;
12994
- await fs22.writeFile(
12995
- join13(process.cwd(), "jai1-rules.json"),
12996
- JSON.stringify(projectConfig, null, 2),
12997
- "utf-8"
12998
- );
12999
- console.log("\n\u2713 Updated jai1-rules.json with synced IDEs");
13074
+ if (JSON.stringify(rulesConfig.ides) !== JSON.stringify(idesToSync)) {
13075
+ rulesConfig.ides = idesToSync;
13076
+ await projectConfigService.saveRules(rulesConfig);
13077
+ console.log("\n\u2713 Updated .jai1/project.json with synced IDEs");
13000
13078
  }
13001
13079
  console.log("\n\u2705 Rules synced successfully!\n");
13002
13080
  console.log("\u{1F4A1} Next steps:");
@@ -13036,48 +13114,43 @@ function buildIdeChoices(currentIdes, detected, suggestions) {
13036
13114
  name: "Gemini CLI (GEMINI.md)",
13037
13115
  value: "gemini",
13038
13116
  checked: currentIdes.includes("gemini") || detected.some((d) => d.id === "gemini")
13117
+ },
13118
+ new Separator(),
13119
+ {
13120
+ name: "\u2716 Quit",
13121
+ value: "__quit__",
13122
+ checked: false
13039
13123
  }
13040
13124
  ];
13041
13125
  return choices;
13042
13126
  }
13043
- async function checkPathExists(absolutePath) {
13044
- try {
13045
- await fs22.access(absolutePath);
13046
- return true;
13047
- } catch {
13048
- return false;
13049
- }
13050
- }
13051
13127
 
13052
13128
  // src/commands/rules/info.ts
13053
13129
  import { Command as Command48 } from "commander";
13054
- import { promises as fs23 } from "fs";
13055
- import { join as join14 } from "path";
13130
+ import { promises as fs24 } from "fs";
13131
+ import { join as join15 } from "path";
13056
13132
  function createRulesInfoCommand() {
13057
13133
  return new Command48("info").description("Show current preset information").option("--json", "Output as JSON").action(async (options) => {
13058
- const configPath = join14(process.cwd(), "jai1-rules.json");
13059
- let projectConfig;
13060
- try {
13061
- const configContent = await fs23.readFile(configPath, "utf-8");
13062
- projectConfig = JSON.parse(configContent);
13063
- } catch {
13134
+ const projectConfigService = new ProjectConfigService();
13135
+ const rulesConfig = await projectConfigService.loadRules();
13136
+ if (!rulesConfig) {
13064
13137
  throw new ValidationError(
13065
- 'No jai1-rules.json found. Run "jai1 rules init" first.'
13138
+ 'No .jai1/project.json found. Run "jai1 rules apply" first.'
13066
13139
  );
13067
13140
  }
13068
13141
  if (options.json) {
13069
- console.log(JSON.stringify(projectConfig, null, 2));
13142
+ console.log(JSON.stringify(rulesConfig, null, 2));
13070
13143
  return;
13071
13144
  }
13072
13145
  console.log("\u{1F4CB} Current Preset Information\n");
13073
- const rulePresetDir = join14(process.cwd(), ".jai1", "rule-preset");
13074
- const presetJsonPath = join14(rulePresetDir, "preset.json");
13146
+ const rulePresetDir = join15(process.cwd(), ".jai1", "rule-preset");
13147
+ const presetJsonPath = join15(rulePresetDir, "preset.json");
13075
13148
  let presetMetadata = null;
13076
13149
  let presetFiles = [];
13077
13150
  try {
13078
- const presetContent = await fs23.readFile(presetJsonPath, "utf-8");
13151
+ const presetContent = await fs24.readFile(presetJsonPath, "utf-8");
13079
13152
  presetMetadata = JSON.parse(presetContent);
13080
- const files = await fs23.readdir(rulePresetDir);
13153
+ const files = await fs24.readdir(rulePresetDir);
13081
13154
  presetFiles = files.filter((f) => f.endsWith(".mdc"));
13082
13155
  } catch {
13083
13156
  }
@@ -13089,10 +13162,10 @@ function createRulesInfoCommand() {
13089
13162
  console.log(`Tags: ${presetMetadata.tags.join(", ")}`);
13090
13163
  }
13091
13164
  } else {
13092
- console.log(`Preset: ${projectConfig.preset}`);
13093
- console.log(`Version: ${projectConfig.version}`);
13165
+ console.log(`Preset: ${rulesConfig.preset}`);
13166
+ console.log(`Version: ${rulesConfig.version}`);
13094
13167
  }
13095
- console.log(`Applied at: ${new Date(projectConfig.appliedAt).toLocaleString()}`);
13168
+ console.log(`Applied at: ${new Date(rulesConfig.appliedAt).toLocaleString()}`);
13096
13169
  console.log(`
13097
13170
  Source of Truth:`);
13098
13171
  if (presetFiles.length > 0) {
@@ -13107,10 +13180,10 @@ Source of Truth:`);
13107
13180
  console.log(` \u26A0\uFE0F .jai1/rule-preset/ not found`);
13108
13181
  console.log(` Run "jai1 rules apply" to create it`);
13109
13182
  }
13110
- if (projectConfig.ides && projectConfig.ides.length > 0) {
13183
+ if (rulesConfig.ides && rulesConfig.ides.length > 0) {
13111
13184
  console.log(`
13112
- Configured IDEs (${projectConfig.ides.length}):`);
13113
- for (const ideId of projectConfig.ides) {
13185
+ Configured IDEs (${rulesConfig.ides.length}):`);
13186
+ for (const ideId of rulesConfig.ides) {
13114
13187
  const format = IDE_FORMATS[ideId];
13115
13188
  if (format) {
13116
13189
  const exists = await checkIdeFilesExist(ideId, format);
@@ -13119,15 +13192,15 @@ Configured IDEs (${projectConfig.ides.length}):`);
13119
13192
  }
13120
13193
  }
13121
13194
  }
13122
- if (projectConfig.backups && projectConfig.backups.length > 0) {
13195
+ if (rulesConfig.backups && rulesConfig.backups.length > 0) {
13123
13196
  console.log(`
13124
- Available Backups (${projectConfig.backups.length}):`);
13125
- for (const backup of projectConfig.backups.slice(0, 3)) {
13197
+ Available Backups (${rulesConfig.backups.length}):`);
13198
+ for (const backup of rulesConfig.backups.slice(0, 3)) {
13126
13199
  const timestamp = new Date(backup.timestamp).toLocaleString();
13127
13200
  console.log(` \u2022 ${timestamp} - IDEs: ${backup.ides.join(", ")}`);
13128
13201
  }
13129
- if (projectConfig.backups.length > 3) {
13130
- console.log(` ... and ${projectConfig.backups.length - 3} more`);
13202
+ if (rulesConfig.backups.length > 3) {
13203
+ console.log(` ... and ${rulesConfig.backups.length - 3} more`);
13131
13204
  }
13132
13205
  }
13133
13206
  console.log("\n\u2139\uFE0F Commands:");
@@ -13136,9 +13209,9 @@ Available Backups (${projectConfig.backups.length}):`);
13136
13209
  console.log(' \u2022 "jai1 rules apply" - Apply a different preset (replaces current)');
13137
13210
  });
13138
13211
  }
13139
- async function checkPathExists2(path13) {
13212
+ async function checkPathExists(path13) {
13140
13213
  try {
13141
- await fs23.access(join14(process.cwd(), path13));
13214
+ await fs24.access(join15(process.cwd(), path13));
13142
13215
  return true;
13143
13216
  } catch {
13144
13217
  return false;
@@ -13147,11 +13220,11 @@ async function checkPathExists2(path13) {
13147
13220
  async function checkIdeFilesExist(ideId, format) {
13148
13221
  try {
13149
13222
  if (ideId === "agentsmd") {
13150
- return await checkPathExists2("AGENTS.md");
13223
+ return await checkPathExists("AGENTS.md");
13151
13224
  } else if (ideId === "gemini") {
13152
- return await checkPathExists2("GEMINI.md");
13225
+ return await checkPathExists("GEMINI.md");
13153
13226
  } else {
13154
- return await checkPathExists2(format.rulesPath);
13227
+ return await checkPathExists(format.rulesPath);
13155
13228
  }
13156
13229
  } catch {
13157
13230
  return false;
@@ -13167,7 +13240,7 @@ function showRulesHelp() {
13167
13240
  console.log(` ${chalk17.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t preset`);
13168
13241
  console.log(` ${chalk17.cyan("init")} Kh\u1EDFi t\u1EA1o rules t\u1EEB preset`);
13169
13242
  console.log(` ${chalk17.cyan("apply")} \xC1p d\u1EE5ng preset v\xE0o project`);
13170
- console.log(` ${chalk17.cyan("sync")} \u0110\u1ED3ng b\u1ED9 rules v\u1EDBi server`);
13243
+ console.log(` ${chalk17.cyan("sync")} \u0110\u1ED3ng b\u1ED9 rules sang c\xE1c \u0111\u1ECBnh d\u1EA1ng IDE`);
13171
13244
  console.log(` ${chalk17.cyan("restore")} Kh\xF4i ph\u1EE5c rules t\u1EEB backup`);
13172
13245
  console.log();
13173
13246
  console.log(chalk17.bold("V\xED d\u1EE5:"));
@@ -13354,7 +13427,7 @@ function getInstallCommand(packageManager2) {
13354
13427
  // src/commands/clean.ts
13355
13428
  import { Command as Command51 } from "commander";
13356
13429
  import { confirm as confirm12, select as select7 } from "@inquirer/prompts";
13357
- import { join as join15 } from "path";
13430
+ import { join as join16 } from "path";
13358
13431
  function createCleanCommand() {
13359
13432
  return new Command51("clean").description("Clean up backups, cache, and temporary files").option("-y, --yes", "Skip confirmation").option("--backups", "Clean only backup files").option("--all", "Clean all (backups + cache)").action(async (options) => {
13360
13433
  await handleClean(options);
@@ -13367,7 +13440,7 @@ async function handleClean(options) {
13367
13440
  {
13368
13441
  name: "Backups",
13369
13442
  description: "Component backup files (.jai1_backup/)",
13370
- path: join15(cwd, ".jai1_backup"),
13443
+ path: join16(cwd, ".jai1_backup"),
13371
13444
  check: async () => {
13372
13445
  const backups = await service.listBackups(cwd);
13373
13446
  return { exists: backups.length > 0, count: backups.length };
@@ -13426,7 +13499,8 @@ async function handleClean(options) {
13426
13499
  })),
13427
13500
  { name: "\u{1F9F9} Clean all", value: "all" },
13428
13501
  { name: "\u274C Cancel", value: "cancel" }
13429
- ]
13502
+ ],
13503
+ theme: selectTheme
13430
13504
  });
13431
13505
  if (action === "cancel") {
13432
13506
  console.log("\n\u274C Operation cancelled.");
@@ -14358,8 +14432,8 @@ async function handleSyncProject(options) {
14358
14432
 
14359
14433
  // src/commands/framework/info.ts
14360
14434
  import { Command as Command55 } from "commander";
14361
- import { promises as fs24 } from "fs";
14362
- import { join as join16 } from "path";
14435
+ import { promises as fs25 } from "fs";
14436
+ import { join as join17 } from "path";
14363
14437
  import { homedir as homedir5 } from "os";
14364
14438
  function createInfoCommand() {
14365
14439
  const cmd = new Command55("info").description("Show jai1-client configuration and status").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
@@ -14373,7 +14447,7 @@ async function handleInfo(options) {
14373
14447
  if (!config) {
14374
14448
  throw new ValidationError('Not initialized. Run "jai1 auth" first.');
14375
14449
  }
14376
- const frameworkPath = join16(homedir5(), ".jai1", "framework");
14450
+ const frameworkPath = join17(homedir5(), ".jai1", "framework");
14377
14451
  const projectStatus = await getProjectStatus2();
14378
14452
  const info = {
14379
14453
  configPath: configService.getConfigPath(),
@@ -14408,9 +14482,9 @@ function maskKey3(key) {
14408
14482
  return "****" + key.slice(-4);
14409
14483
  }
14410
14484
  async function getProjectStatus2() {
14411
- const projectJai1 = join16(process.cwd(), ".jai1");
14485
+ const projectJai1 = join17(process.cwd(), ".jai1");
14412
14486
  try {
14413
- await fs24.access(projectJai1);
14487
+ await fs25.access(projectJai1);
14414
14488
  return { exists: true, version: "Synced" };
14415
14489
  } catch {
14416
14490
  return { exists: false };
@@ -14600,7 +14674,7 @@ function createClearBackupsCommand() {
14600
14674
  // src/commands/vscode/index.ts
14601
14675
  import { Command as Command58 } from "commander";
14602
14676
  import { checkbox as checkbox7, confirm as confirm15, select as select8 } from "@inquirer/prompts";
14603
- import fs25 from "fs/promises";
14677
+ import fs26 from "fs/promises";
14604
14678
  import path12 from "path";
14605
14679
  import { existsSync as existsSync3 } from "fs";
14606
14680
  var PERFORMANCE_GROUPS2 = {
@@ -14789,7 +14863,8 @@ async function interactiveMode2() {
14789
14863
  { name: "\u274C Disable c\xE1c nh\xF3m t\u1ED1i \u01B0u", value: "disable" },
14790
14864
  { name: "\u{1F680} Max Performance (enable t\u1EA5t c\u1EA3)", value: "max" },
14791
14865
  { name: "\u{1F504} Reset v\u1EC1 m\u1EB7c \u0111\u1ECBnh", value: "reset" }
14792
- ]
14866
+ ],
14867
+ theme: selectTheme
14793
14868
  });
14794
14869
  if (action === "max") {
14795
14870
  const allGroups = Object.keys(PERFORMANCE_GROUPS2);
@@ -14808,7 +14883,8 @@ async function selectGroupsToApply2(action) {
14808
14883
  try {
14809
14884
  const selectedGroups = await checkbox7({
14810
14885
  message: `Ch\u1ECDn c\xE1c nh\xF3m \u0111\u1EC3 ${action === "enable" ? "enable" : "disable"} (SPACE \u0111\u1EC3 ch\u1ECDn, ENTER \u0111\u1EC3 x\xE1c nh\u1EADn):`,
14811
- choices
14886
+ choices,
14887
+ theme: checkboxTheme
14812
14888
  });
14813
14889
  if (selectedGroups.length === 0) {
14814
14890
  console.log("\n\u26A0\uFE0F B\u1EA1n ch\u01B0a ch\u1ECDn nh\xF3m n\xE0o!");
@@ -14831,13 +14907,13 @@ async function applyGroups2(groupKeys, action) {
14831
14907
  return;
14832
14908
  }
14833
14909
  if (!existsSync3(vscodeDir)) {
14834
- await fs25.mkdir(vscodeDir, { recursive: true });
14910
+ await fs26.mkdir(vscodeDir, { recursive: true });
14835
14911
  console.log("\u{1F4C1} \u0110\xE3 t\u1EA1o th\u01B0 m\u1EE5c .vscode/");
14836
14912
  }
14837
14913
  let currentSettings = {};
14838
14914
  if (existsSync3(settingsPath)) {
14839
14915
  try {
14840
- const content = await fs25.readFile(settingsPath, "utf-8");
14916
+ const content = await fs26.readFile(settingsPath, "utf-8");
14841
14917
  currentSettings = JSON.parse(content);
14842
14918
  console.log("\u{1F4C4} \u0110\xE3 \u0111\u1ECDc c\xE0i \u0111\u1EB7t hi\u1EC7n t\u1EA1i t\u1EEB settings.json");
14843
14919
  } catch {
@@ -14877,7 +14953,7 @@ async function applyGroups2(groupKeys, action) {
14877
14953
  }
14878
14954
  }
14879
14955
  }
14880
- await fs25.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
14956
+ await fs26.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
14881
14957
  console.log(`
14882
14958
  \u2705 \u0110\xE3 c\u1EADp nh\u1EADt c\xE0i \u0111\u1EB7t VSCode t\u1EA1i: ${settingsPath}`);
14883
14959
  console.log("\u{1F4A1} M\u1EB9o: Kh\u1EDFi \u0111\u1ED9ng l\u1EA1i VSCode \u0111\u1EC3 \xE1p d\u1EE5ng c\xE1c thay \u0111\u1ED5i.");
@@ -14898,7 +14974,7 @@ async function resetSettings2(groupKeys) {
14898
14974
  return;
14899
14975
  }
14900
14976
  if (groupKeys.length === 0) {
14901
- await fs25.unlink(settingsPath);
14977
+ await fs26.unlink(settingsPath);
14902
14978
  console.log("\n\u2705 \u0110\xE3 x\xF3a file settings.json");
14903
14979
  } else {
14904
14980
  await applyGroups2(groupKeys, "disable");
@@ -15021,7 +15097,8 @@ async function runMigrateIde(options) {
15021
15097
  });
15022
15098
  selectedIdes = await checkbox8({
15023
15099
  message: "Ch\u1ECDn IDE(s) \u0111\u1EC3 migrate (SPACE \u0111\u1EC3 ch\u1ECDn, ENTER \u0111\u1EC3 x\xE1c nh\u1EADn):",
15024
- choices: ideChoices
15100
+ choices: ideChoices,
15101
+ theme: checkboxTheme
15025
15102
  });
15026
15103
  if (selectedIdes.length === 0) {
15027
15104
  console.log("\n\u26A0\uFE0F B\u1EA1n ch\u01B0a ch\u1ECDn IDE n\xE0o!");
@@ -15040,7 +15117,8 @@ async function runMigrateIde(options) {
15040
15117
  ];
15041
15118
  selectedTypes = await checkbox8({
15042
15119
  message: "Ch\u1ECDn content types \u0111\u1EC3 migrate:",
15043
- choices: typeChoices
15120
+ choices: typeChoices,
15121
+ theme: checkboxTheme
15044
15122
  });
15045
15123
  if (selectedTypes.length === 0) {
15046
15124
  console.log("\n\u26A0\uFE0F B\u1EA1n ch\u01B0a ch\u1ECDn content type n\xE0o!");