@jvittechs/jai1-cli 0.1.106 → 1.0.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/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.0",
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));
@@ -1359,12 +1387,18 @@ var UnifiedApplyApp = ({
1359
1387
  setAvailableIdes(getMigrationIDEs());
1360
1388
  }, []);
1361
1389
  const filteredComponents = useMemo(() => {
1390
+ if (focusArea === "packages" && tags.length > 0) {
1391
+ const selectedTag = tags[selectedPackageIndex];
1392
+ if (selectedTag) {
1393
+ return components.filter((c) => c.tags?.includes(selectedTag.tag));
1394
+ }
1395
+ }
1362
1396
  if (!searchQuery.trim()) return components;
1363
1397
  const query = searchQuery.toLowerCase();
1364
1398
  return components.filter(
1365
1399
  (c) => c.filepath.toLowerCase().includes(query) || c.tags?.some((t) => t.toLowerCase().includes(query))
1366
1400
  );
1367
- }, [components, searchQuery]);
1401
+ }, [components, searchQuery, focusArea, selectedPackageIndex, tags]);
1368
1402
  useInput((input5, key) => {
1369
1403
  if (viewState === "done") {
1370
1404
  if (key.return || input5 === "q" || key.escape) {
@@ -1480,7 +1514,6 @@ var UnifiedApplyApp = ({
1480
1514
  packageComponents.forEach((c) => next.add(c.filepath));
1481
1515
  return next;
1482
1516
  });
1483
- setSearchQuery(tag.tag);
1484
1517
  }
1485
1518
  }
1486
1519
  }
@@ -1601,7 +1634,7 @@ var UnifiedApplyApp = ({
1601
1634
  onChange: setSearchQuery,
1602
1635
  placeholder: "Type to filter..."
1603
1636
  }
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) => {
1637
+ ) : /* @__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
1638
  const isSelected = focusArea === "packages" && i === selectedPackageIndex;
1606
1639
  return /* @__PURE__ */ React3.createElement(Box2, { key: tag.tag, marginRight: 1 }, /* @__PURE__ */ React3.createElement(
1607
1640
  Text3,
@@ -1615,12 +1648,12 @@ var UnifiedApplyApp = ({
1615
1648
  tag.count,
1616
1649
  "]"
1617
1650
  ));
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) => {
1651
+ })), 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 "), focusArea === "packages" && tags.length > 0 && /* @__PURE__ */ React3.createElement(Text3, { color: "cyan" }, "[", tags[selectedPackageIndex]?.tag, "] "), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "(", filteredComponents.length, " shown", hasMore ? `, scroll for more` : "", ")")), visibleComponents.map((comp, i) => {
1619
1652
  const isCursor = i === cursorIndex && focusArea === "components";
1620
1653
  const isChecked = selectedPaths.has(comp.filepath);
1621
1654
  const isInstalled = installedPaths.has(comp.filepath);
1622
1655
  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")));
1656
+ }), 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
1657
  };
1625
1658
 
1626
1659
  // src/commands/apply.ts
@@ -1967,7 +2000,7 @@ var MainMenuView = ({ ideContexts, onSelect }) => {
1967
2000
  {
1968
2001
  ide: "antigravity",
1969
2002
  icon: "\u{1F680}",
1970
- title: "Antigravity (Claude)",
2003
+ title: "Antigravity",
1971
2004
  itemCount: ideContexts.find((ctx) => ctx.ide === "antigravity")?.stats.totalItems || 0,
1972
2005
  available: ideContexts.some((ctx) => ctx.ide === "antigravity")
1973
2006
  },
@@ -2294,7 +2327,7 @@ var IDE_CONFIGS = {
2294
2327
  },
2295
2328
  antigravity: {
2296
2329
  id: "antigravity",
2297
- name: "Antigravity (Claude)",
2330
+ name: "Antigravity",
2298
2331
  icon: "\u{1F680}",
2299
2332
  basePath: ".agent",
2300
2333
  contentPaths: {
@@ -2795,6 +2828,25 @@ import { checkbox, confirm as confirm2, select } from "@inquirer/prompts";
2795
2828
  import fs6 from "fs/promises";
2796
2829
  import path3 from "path";
2797
2830
  import { existsSync } from "fs";
2831
+
2832
+ // src/utils/prompt-theme.ts
2833
+ var keysHelpTipWithQuit = (keys) => {
2834
+ const keyStrings = keys.map(([key, action]) => `${key} ${action}`);
2835
+ keyStrings.push("Ctrl+C quit");
2836
+ return keyStrings.join(" \u2022 ");
2837
+ };
2838
+ var checkboxTheme = {
2839
+ style: {
2840
+ keysHelpTip: keysHelpTipWithQuit
2841
+ }
2842
+ };
2843
+ var selectTheme = {
2844
+ style: {
2845
+ keysHelpTip: keysHelpTipWithQuit
2846
+ }
2847
+ };
2848
+
2849
+ // src/commands/ide/setup.ts
2798
2850
  var PERFORMANCE_GROUPS = {
2799
2851
  telemetry: {
2800
2852
  name: "Telemetry",
@@ -2981,7 +3033,8 @@ async function interactiveMode() {
2981
3033
  { name: "\u274C Disable optimization groups", value: "disable" },
2982
3034
  { name: "\u{1F680} Max Performance (enable all)", value: "max" },
2983
3035
  { name: "\u{1F504} Reset to defaults", value: "reset" }
2984
- ]
3036
+ ],
3037
+ theme: selectTheme
2985
3038
  });
2986
3039
  if (action === "max") {
2987
3040
  const allGroups = Object.keys(PERFORMANCE_GROUPS);
@@ -3000,7 +3053,8 @@ async function selectGroupsToApply(action) {
3000
3053
  try {
3001
3054
  const selectedGroups = await checkbox({
3002
3055
  message: `Select groups to ${action} (SPACE to select, ENTER to confirm):`,
3003
- choices
3056
+ choices,
3057
+ theme: checkboxTheme
3004
3058
  });
3005
3059
  if (selectedGroups.length === 0) {
3006
3060
  console.log("\n\u26A0\uFE0F No groups selected!");
@@ -3136,7 +3190,8 @@ async function runSync(options) {
3136
3190
  });
3137
3191
  selectedIdes = await checkbox2({
3138
3192
  message: "Select target IDE(s) (SPACE to select, ENTER to confirm):",
3139
- choices: ideChoices
3193
+ choices: ideChoices,
3194
+ theme: checkboxTheme
3140
3195
  });
3141
3196
  if (selectedIdes.length === 0) {
3142
3197
  console.log("\n\u26A0\uFE0F No IDE selected!");
@@ -3155,7 +3210,8 @@ async function runSync(options) {
3155
3210
  ];
3156
3211
  selectedTypes = await checkbox2({
3157
3212
  message: "Select content types to sync:",
3158
- choices: typeChoices
3213
+ choices: typeChoices,
3214
+ theme: checkboxTheme
3159
3215
  });
3160
3216
  if (selectedTypes.length === 0) {
3161
3217
  console.log("\n\u26A0\uFE0F No content type selected!");
@@ -3281,78 +3337,6 @@ var IDE_FORMATS = {
3281
3337
  // Gemini requires AGENTS.md
3282
3338
  }
3283
3339
  };
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
3340
 
3357
3341
  // src/services/ide-detection.service.ts
3358
3342
  var IdeDetectionService = class {
@@ -7830,7 +7814,8 @@ async function handleInteractiveFeedback(config) {
7830
7814
  name: "\u{1F4DD} Suggestion - Share ideas or improvements",
7831
7815
  value: "suggestion"
7832
7816
  }
7833
- ]
7817
+ ],
7818
+ theme: selectTheme
7834
7819
  });
7835
7820
  const title = await input({
7836
7821
  message: "Title (max 200 characters):",
@@ -11145,7 +11130,7 @@ ${"\u2501".repeat(80)}`));
11145
11130
  } else {
11146
11131
  try {
11147
11132
  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):",
11133
+ 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
11134
  choices: packages.map((pkg) => {
11150
11135
  const icon = pkg.upgradeType === "major" ? "\u{1F534}" : pkg.upgradeType === "minor" ? "\u{1F7E1}" : "\u{1F7E2}";
11151
11136
  return {
@@ -11154,7 +11139,8 @@ ${"\u2501".repeat(80)}`));
11154
11139
  checked: true
11155
11140
  };
11156
11141
  }),
11157
- pageSize: 15
11142
+ pageSize: 15,
11143
+ theme: checkboxTheme
11158
11144
  });
11159
11145
  if (selected.length === 0) {
11160
11146
  console.log(chalk12.yellow("\u23F8\uFE0F Kh\xF4ng c\xF3 packages n\xE0o \u0111\u01B0\u1EE3c ch\u1ECDn\n"));
@@ -11590,7 +11576,8 @@ function createKitCreateCommand() {
11590
11576
  const answer = await select3({
11591
11577
  message: v.prompt,
11592
11578
  choices: v.options.map((opt) => ({ name: opt, value: opt })),
11593
- default: v.default
11579
+ default: v.default,
11580
+ theme: selectTheme
11594
11581
  });
11595
11582
  variables[v.name] = answer;
11596
11583
  } else {
@@ -11609,9 +11596,13 @@ function createKitCreateCommand() {
11609
11596
  if (!options.skipFramework && kit.config.framework?.apply) {
11610
11597
  console.log("\u{1F916} Applying jai1 framework...");
11611
11598
  const componentsService = new ComponentsService(targetDir);
11612
- for (const filepath of kit.config.framework.components) {
11599
+ const expandedPaths = await componentsService.expandPaths(
11600
+ config,
11601
+ kit.config.framework.components
11602
+ );
11603
+ for (const filepath of expandedPaths) {
11613
11604
  console.log(` \u{1F4E5} Installing ${filepath}...`);
11614
- await componentsService.install(config, filepath);
11605
+ await componentsService.install(config, filepath, join8(targetDir, ".jai1"));
11615
11606
  }
11616
11607
  console.log(" \u2713 Framework components applied");
11617
11608
  }
@@ -11649,7 +11640,8 @@ function createKitCreateCommand() {
11649
11640
  choices: ideChoices.map((c) => ({
11650
11641
  ...c,
11651
11642
  checked: defaultTargets.includes(c.value)
11652
- }))
11643
+ })),
11644
+ theme: checkboxTheme
11653
11645
  });
11654
11646
  selectedIdes = answer;
11655
11647
  }
@@ -11793,7 +11785,7 @@ function createRulesListCommand() {
11793
11785
  console.log(table.toString());
11794
11786
  console.log();
11795
11787
  }
11796
- console.log(chalk16.dim('\u{1F4A1} Ch\u1EA1y "jai1 rules init --preset=<slug>" \u0111\u1EC3 \xE1p d\u1EE5ng preset'));
11788
+ console.log(chalk16.dim('\u{1F4A1} Ch\u1EA1y "jai1 rules apply <name>" \u0111\u1EC3 \xE1p d\u1EE5ng preset'));
11797
11789
  } catch (error) {
11798
11790
  throw new Error(
11799
11791
  `L\u1ED7i khi t\u1EA3i presets: ${error instanceof Error ? error.message : String(error)}`
@@ -11804,9 +11796,129 @@ function createRulesListCommand() {
11804
11796
 
11805
11797
  // src/commands/rules/init.ts
11806
11798
  import { Command as Command44 } from "commander";
11799
+ import { promises as fs20 } from "fs";
11800
+ import { join as join10 } from "path";
11801
+ import { select as select4, confirm as confirm7 } from "@inquirer/prompts";
11802
+
11803
+ // src/services/project-config.service.ts
11807
11804
  import { promises as fs19 } from "fs";
11808
11805
  import { join as join9 } from "path";
11809
- import { select as select4, confirm as confirm7 } from "@inquirer/prompts";
11806
+ var ProjectConfigService = class {
11807
+ projectRoot;
11808
+ configDir;
11809
+ configPath;
11810
+ constructor(projectRoot = process.cwd()) {
11811
+ this.projectRoot = projectRoot;
11812
+ this.configDir = join9(this.projectRoot, ".jai1");
11813
+ this.configPath = join9(this.configDir, "project.json");
11814
+ }
11815
+ /**
11816
+ * Check if config file exists
11817
+ */
11818
+ async exists() {
11819
+ try {
11820
+ await fs19.access(this.configPath);
11821
+ return true;
11822
+ } catch {
11823
+ return false;
11824
+ }
11825
+ }
11826
+ /**
11827
+ * Load full project configuration
11828
+ * @returns Config object or null if not found
11829
+ */
11830
+ async load() {
11831
+ if (!await this.exists()) {
11832
+ return null;
11833
+ }
11834
+ try {
11835
+ const content = await fs19.readFile(this.configPath, "utf-8");
11836
+ return JSON.parse(content);
11837
+ } catch (error) {
11838
+ throw new Error(
11839
+ `Failed to load project config: ${error instanceof Error ? error.message : String(error)}`
11840
+ );
11841
+ }
11842
+ }
11843
+ /**
11844
+ * Save full project configuration
11845
+ * Creates .jai1 directory if it doesn't exist
11846
+ */
11847
+ async save(config) {
11848
+ try {
11849
+ await fs19.mkdir(this.configDir, { recursive: true });
11850
+ await fs19.writeFile(this.configPath, JSON.stringify(config, null, 2), "utf-8");
11851
+ } catch (error) {
11852
+ throw new Error(
11853
+ `Failed to save project config: ${error instanceof Error ? error.message : String(error)}`
11854
+ );
11855
+ }
11856
+ }
11857
+ /**
11858
+ * Load rules configuration only
11859
+ * @returns RulesConfig or null if not found
11860
+ */
11861
+ async loadRules() {
11862
+ const config = await this.load();
11863
+ return config?.rules ?? null;
11864
+ }
11865
+ /**
11866
+ * Save rules configuration
11867
+ * Merges with existing config, preserving other sections
11868
+ */
11869
+ async saveRules(rulesConfig) {
11870
+ const existingConfig = await this.load() ?? {};
11871
+ existingConfig.rules = rulesConfig;
11872
+ await this.save(existingConfig);
11873
+ }
11874
+ /**
11875
+ * Update rules configuration partially
11876
+ * Merges with existing rules config
11877
+ */
11878
+ async updateRules(partialRulesConfig) {
11879
+ const existingRules = await this.loadRules();
11880
+ if (!existingRules) {
11881
+ throw new Error('No rules configuration found. Run "jai1 rules apply" first.');
11882
+ }
11883
+ const updatedRules = {
11884
+ ...existingRules,
11885
+ ...partialRulesConfig
11886
+ };
11887
+ await this.saveRules(updatedRules);
11888
+ }
11889
+ /**
11890
+ * Add a backup entry to rules config
11891
+ * Keeps only the last 5 backups
11892
+ */
11893
+ async addBackup(backup) {
11894
+ const rules = await this.loadRules();
11895
+ if (!rules) {
11896
+ throw new Error("No rules configuration found.");
11897
+ }
11898
+ const backups = [backup, ...rules.backups ?? []].slice(0, 5);
11899
+ await this.updateRules({ backups });
11900
+ }
11901
+ /**
11902
+ * Get config file path
11903
+ */
11904
+ getConfigPath() {
11905
+ return this.configPath;
11906
+ }
11907
+ /**
11908
+ * Get config directory path (.jai1/)
11909
+ */
11910
+ getConfigDir() {
11911
+ return this.configDir;
11912
+ }
11913
+ /**
11914
+ * Get project root path
11915
+ */
11916
+ getProjectRoot() {
11917
+ return this.projectRoot;
11918
+ }
11919
+ };
11920
+
11921
+ // src/commands/rules/init.ts
11810
11922
  function createRulesInitCommand() {
11811
11923
  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
11924
  const configService = new ConfigService();
@@ -11836,7 +11948,8 @@ function createRulesInitCommand() {
11836
11948
  name: `${p.name} - ${p.description}`,
11837
11949
  value: p.slug,
11838
11950
  description: `v${p.version} | ${p.tags.join(", ")}`
11839
- }))
11951
+ })),
11952
+ theme: selectTheme
11840
11953
  });
11841
11954
  }
11842
11955
  console.log(`
@@ -11864,7 +11977,8 @@ function createRulesInitCommand() {
11864
11977
  { name: "Cursor (.cursor/rules/)", value: "cursor" },
11865
11978
  { name: "AGENTS.md (single file)", value: "agents-md" },
11866
11979
  { name: "Both", value: "both" }
11867
- ]
11980
+ ],
11981
+ theme: selectTheme
11868
11982
  });
11869
11983
  }
11870
11984
  if (!options.yes) {
@@ -11884,14 +11998,16 @@ function createRulesInitCommand() {
11884
11998
  if (outputFormat === "agents-md" || outputFormat === "both") {
11885
11999
  await applyAgentsMdFormat(bundle);
11886
12000
  }
11887
- const projectConfig = {
12001
+ const projectConfigService = new ProjectConfigService();
12002
+ const rulesConfig = {
11888
12003
  preset: bundle.preset.slug,
11889
12004
  version: bundle.preset.version,
11890
12005
  appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
12006
+ ides: [],
11891
12007
  customContext: "09-custom.mdc"
11892
12008
  };
11893
- await fs19.writeFile("jai1-rules.json", JSON.stringify(projectConfig, null, 2));
11894
- console.log("\u2713 Created jai1-rules.json");
12009
+ await projectConfigService.saveRules(rulesConfig);
12010
+ console.log("\u2713 Created .jai1/project.json");
11895
12011
  console.log("\n\u2705 Preset applied successfully!\n");
11896
12012
  console.log("Next steps:");
11897
12013
  console.log(" 1. Edit 09-custom.mdc to add project-specific rules");
@@ -11900,11 +12016,11 @@ function createRulesInitCommand() {
11900
12016
  });
11901
12017
  }
11902
12018
  async function applyCursorFormat(bundle) {
11903
- const rulesDir = join9(process.cwd(), ".cursor", "rules");
11904
- await fs19.mkdir(rulesDir, { recursive: true });
12019
+ const rulesDir = join10(process.cwd(), ".cursor", "rules");
12020
+ await fs20.mkdir(rulesDir, { recursive: true });
11905
12021
  for (const [filename, content] of Object.entries(bundle.files)) {
11906
- const filePath = join9(rulesDir, filename);
11907
- await fs19.writeFile(filePath, content, "utf-8");
12022
+ const filePath = join10(rulesDir, filename);
12023
+ await fs20.writeFile(filePath, content, "utf-8");
11908
12024
  console.log(`\u2713 Created .cursor/rules/${filename}`);
11909
12025
  }
11910
12026
  }
@@ -11931,14 +12047,14 @@ async function applyAgentsMdFormat(bundle) {
11931
12047
  }
11932
12048
  }
11933
12049
  const agentsMd = sections.join("\n");
11934
- await fs19.writeFile("AGENTS.md", agentsMd, "utf-8");
12050
+ await fs20.writeFile("AGENTS.md", agentsMd, "utf-8");
11935
12051
  console.log("\u2713 Created AGENTS.md");
11936
12052
  }
11937
12053
 
11938
12054
  // src/commands/rules/apply.ts
11939
12055
  import { Command as Command45 } from "commander";
11940
- import { promises as fs21 } from "fs";
11941
- import { join as join11 } from "path";
12056
+ import { promises as fs22 } from "fs";
12057
+ import { join as join12 } from "path";
11942
12058
  import { select as select5, confirm as confirm8, checkbox as checkbox5 } from "@inquirer/prompts";
11943
12059
 
11944
12060
  // src/services/rules-generator.service.ts
@@ -11973,11 +12089,6 @@ var RulesGeneratorService = class {
11973
12089
  */
11974
12090
  generateCursorFiles(bundle, format) {
11975
12091
  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
12092
  for (const [filename, content] of Object.entries(bundle.files)) {
11982
12093
  files.push({
11983
12094
  path: `${format.rulesPath}/${filename}`,
@@ -11988,28 +12099,11 @@ var RulesGeneratorService = class {
11988
12099
  }
11989
12100
  return files;
11990
12101
  }
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
12102
  /**
12004
12103
  * Generate Windsurf format (.md files with trigger metadata)
12005
12104
  */
12006
12105
  generateWindsurfFiles(bundle, format) {
12007
12106
  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
12107
  for (const [filename, content] of Object.entries(bundle.files)) {
12014
12108
  const converted = this.convertToWindsurf(content, filename);
12015
12109
  const newFilename = filename.replace(/\.mdc$/, ".md");
@@ -12044,27 +12138,12 @@ trigger: ${trigger}
12044
12138
 
12045
12139
  ${body.trim()}
12046
12140
  `;
12047
- }
12048
- /**
12049
- * Generate Windsurf jai1 base rule
12050
- */
12051
- generateWindsurfJai1Rule() {
12052
- return `---
12053
- trigger: always
12054
- ---
12055
-
12056
- ${JAI1_BASE_RULE}`;
12057
12141
  }
12058
12142
  /**
12059
12143
  * Generate Antigravity format (.md files with trigger metadata + @AGENTS.md reference)
12060
12144
  */
12061
12145
  generateAntigravityFiles(bundle, format) {
12062
12146
  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
12147
  for (const [filename, content] of Object.entries(bundle.files)) {
12069
12148
  const converted = this.convertToAntigravity(content, filename);
12070
12149
  const newFilename = filename.replace(/\.mdc$/, ".md");
@@ -12099,29 +12178,12 @@ trigger: ${trigger}
12099
12178
 
12100
12179
  ${body.trim()}
12101
12180
  `;
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
12181
  }
12115
12182
  /**
12116
12183
  * Generate Claude Code format (.md files with paths metadata)
12117
12184
  */
12118
12185
  generateClaudeFiles(bundle, format) {
12119
12186
  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
12187
  for (const [filename, content] of Object.entries(bundle.files)) {
12126
12188
  const converted = this.convertToClaude(content, filename);
12127
12189
  const newFilename = filename.replace(/\.mdc$/, ".md");
@@ -12155,17 +12217,6 @@ paths: ${paths}`;
12155
12217
 
12156
12218
  ${body.trim()}
12157
12219
  `;
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
12220
  }
12170
12221
  /**
12171
12222
  * Generate AGENTS.md file (single merged file)
@@ -12175,10 +12226,8 @@ ${JAI1_BASE_RULE}`;
12175
12226
  sections.push("# AGENTS.md\n");
12176
12227
  sections.push(`<!-- Generated by jai1 rules - Preset: ${bundle.preset.slug} v${bundle.preset.version} -->
12177
12228
  `);
12178
- sections.push("## Jai1 Framework Integration\n");
12179
- sections.push(JAI1_BASE_RULE.trim());
12180
- sections.push("\n---\n");
12181
12229
  const fileOrder = [
12230
+ "00-jai1.mdc",
12182
12231
  "01-project.mdc",
12183
12232
  "02-standards.mdc",
12184
12233
  "03-frontend.mdc",
@@ -12298,8 +12347,8 @@ Follow all instructions and patterns defined in AGENTS.md above.
12298
12347
  };
12299
12348
 
12300
12349
  // src/services/backup.service.ts
12301
- import { promises as fs20 } from "fs";
12302
- import { join as join10, dirname } from "path";
12350
+ import { promises as fs21 } from "fs";
12351
+ import { join as join11, dirname } from "path";
12303
12352
  var BackupService = class {
12304
12353
  backupDir = ".jai1/backups";
12305
12354
  /**
@@ -12307,7 +12356,7 @@ var BackupService = class {
12307
12356
  */
12308
12357
  async createBackup(ides, presetSlug) {
12309
12358
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
12310
- const backupPath = join10(this.backupDir, timestamp);
12359
+ const backupPath = join11(this.backupDir, timestamp);
12311
12360
  const backedUpFiles = [];
12312
12361
  let hasContent = false;
12313
12362
  for (const ideId of ides) {
@@ -12316,7 +12365,7 @@ var BackupService = class {
12316
12365
  console.warn(`Unknown IDE format: ${ideId}, skipping backup`);
12317
12366
  continue;
12318
12367
  }
12319
- const rulesPath = format.rulesPath === "." ? process.cwd() : join10(process.cwd(), format.rulesPath);
12368
+ const rulesPath = format.rulesPath === "." ? process.cwd() : join11(process.cwd(), format.rulesPath);
12320
12369
  try {
12321
12370
  const exists = await this.pathExists(rulesPath);
12322
12371
  if (!exists) {
@@ -12329,19 +12378,19 @@ var BackupService = class {
12329
12378
  await this.backupSingleFile("GEMINI.md", backupPath, ideId, backedUpFiles);
12330
12379
  hasContent = true;
12331
12380
  } else {
12332
- const stats = await fs20.stat(rulesPath);
12381
+ const stats = await fs21.stat(rulesPath);
12333
12382
  if (stats.isDirectory()) {
12334
- const files = await fs20.readdir(rulesPath);
12383
+ const files = await fs21.readdir(rulesPath);
12335
12384
  for (const file of files) {
12336
12385
  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);
12386
+ const originalPath = join11(rulesPath, file);
12387
+ const relativePath = join11(format.rulesPath, file);
12388
+ const destPath = join11(backupPath, ideId, file);
12389
+ await fs21.mkdir(dirname(destPath), { recursive: true });
12390
+ await fs21.copyFile(originalPath, destPath);
12342
12391
  backedUpFiles.push({
12343
12392
  originalPath: relativePath,
12344
- backupPath: join10(ideId, file),
12393
+ backupPath: join11(ideId, file),
12345
12394
  ide: ideId
12346
12395
  });
12347
12396
  hasContent = true;
@@ -12362,9 +12411,9 @@ var BackupService = class {
12362
12411
  ides,
12363
12412
  files: backedUpFiles
12364
12413
  };
12365
- await fs20.mkdir(backupPath, { recursive: true });
12366
- await fs20.writeFile(
12367
- join10(backupPath, "metadata.json"),
12414
+ await fs21.mkdir(backupPath, { recursive: true });
12415
+ await fs21.writeFile(
12416
+ join11(backupPath, "metadata.json"),
12368
12417
  JSON.stringify(metadata, null, 2),
12369
12418
  "utf-8"
12370
12419
  );
@@ -12374,18 +12423,18 @@ var BackupService = class {
12374
12423
  * Backup a single file (for AGENTS.md, GEMINI.md)
12375
12424
  */
12376
12425
  async backupSingleFile(filename, backupPath, ideId, backedUpFiles) {
12377
- const originalPath = join10(process.cwd(), filename);
12426
+ const originalPath = join11(process.cwd(), filename);
12378
12427
  try {
12379
12428
  const exists = await this.pathExists(originalPath);
12380
12429
  if (!exists) {
12381
12430
  return;
12382
12431
  }
12383
- const destPath = join10(backupPath, ideId, filename);
12384
- await fs20.mkdir(dirname(destPath), { recursive: true });
12385
- await fs20.copyFile(originalPath, destPath);
12432
+ const destPath = join11(backupPath, ideId, filename);
12433
+ await fs21.mkdir(dirname(destPath), { recursive: true });
12434
+ await fs21.copyFile(originalPath, destPath);
12386
12435
  backedUpFiles.push({
12387
12436
  originalPath: filename,
12388
- backupPath: join10(ideId, filename),
12437
+ backupPath: join11(ideId, filename),
12389
12438
  ide: ideId
12390
12439
  });
12391
12440
  } catch (error) {
@@ -12395,16 +12444,16 @@ var BackupService = class {
12395
12444
  * Restore files from a backup
12396
12445
  */
12397
12446
  async restoreBackup(backupPath) {
12398
- const metadataPath = join10(backupPath, "metadata.json");
12399
- const metadataContent = await fs20.readFile(metadataPath, "utf-8");
12447
+ const metadataPath = join11(backupPath, "metadata.json");
12448
+ const metadataContent = await fs21.readFile(metadataPath, "utf-8");
12400
12449
  const metadata = JSON.parse(metadataContent);
12401
12450
  console.log(`
12402
12451
  Restoring backup from ${metadata.timestamp}...`);
12403
12452
  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);
12453
+ const sourcePath = join11(backupPath, file.backupPath);
12454
+ const destPath = join11(process.cwd(), file.originalPath);
12455
+ await fs21.mkdir(dirname(destPath), { recursive: true });
12456
+ await fs21.copyFile(sourcePath, destPath);
12408
12457
  console.log(`\u2713 Restored ${file.originalPath}`);
12409
12458
  }
12410
12459
  console.log("\n\u2705 Backup restored successfully!");
@@ -12414,18 +12463,18 @@ Restoring backup from ${metadata.timestamp}...`);
12414
12463
  */
12415
12464
  async listBackups() {
12416
12465
  try {
12417
- const backupDirPath = join10(process.cwd(), this.backupDir);
12466
+ const backupDirPath = join11(process.cwd(), this.backupDir);
12418
12467
  const exists = await this.pathExists(backupDirPath);
12419
12468
  if (!exists) {
12420
12469
  return [];
12421
12470
  }
12422
- const entries = await fs20.readdir(backupDirPath, { withFileTypes: true });
12471
+ const entries = await fs21.readdir(backupDirPath, { withFileTypes: true });
12423
12472
  const backups = [];
12424
12473
  for (const entry of entries) {
12425
12474
  if (entry.isDirectory()) {
12426
- const metadataPath = join10(backupDirPath, entry.name, "metadata.json");
12475
+ const metadataPath = join11(backupDirPath, entry.name, "metadata.json");
12427
12476
  try {
12428
- const metadataContent = await fs20.readFile(metadataPath, "utf-8");
12477
+ const metadataContent = await fs21.readFile(metadataPath, "utf-8");
12429
12478
  const metadata = JSON.parse(metadataContent);
12430
12479
  backups.push(metadata);
12431
12480
  } catch {
@@ -12442,7 +12491,7 @@ Restoring backup from ${metadata.timestamp}...`);
12442
12491
  * Delete a specific backup
12443
12492
  */
12444
12493
  async deleteBackup(timestamp) {
12445
- const backupPath = join10(process.cwd(), this.backupDir, timestamp);
12494
+ const backupPath = join11(process.cwd(), this.backupDir, timestamp);
12446
12495
  await this.deleteDirectory(backupPath);
12447
12496
  }
12448
12497
  /**
@@ -12474,7 +12523,7 @@ Restoring backup from ${metadata.timestamp}...`);
12474
12523
  */
12475
12524
  async pathExists(path13) {
12476
12525
  try {
12477
- await fs20.access(path13);
12526
+ await fs21.access(path13);
12478
12527
  return true;
12479
12528
  } catch {
12480
12529
  return false;
@@ -12489,16 +12538,16 @@ Restoring backup from ${metadata.timestamp}...`);
12489
12538
  if (!exists) {
12490
12539
  return;
12491
12540
  }
12492
- const entries = await fs20.readdir(path13, { withFileTypes: true });
12541
+ const entries = await fs21.readdir(path13, { withFileTypes: true });
12493
12542
  for (const entry of entries) {
12494
- const fullPath = join10(path13, entry.name);
12543
+ const fullPath = join11(path13, entry.name);
12495
12544
  if (entry.isDirectory()) {
12496
12545
  await this.deleteDirectory(fullPath);
12497
12546
  } else {
12498
- await fs20.unlink(fullPath);
12547
+ await fs21.unlink(fullPath);
12499
12548
  }
12500
12549
  }
12501
- await fs20.rmdir(path13);
12550
+ await fs21.rmdir(path13);
12502
12551
  } catch (error) {
12503
12552
  }
12504
12553
  }
@@ -12506,14 +12555,14 @@ Restoring backup from ${metadata.timestamp}...`);
12506
12555
  * Get backup directory path
12507
12556
  */
12508
12557
  getBackupDir() {
12509
- return join10(process.cwd(), this.backupDir);
12558
+ return join11(process.cwd(), this.backupDir);
12510
12559
  }
12511
12560
  /**
12512
12561
  * Ensure backup directory exists
12513
12562
  */
12514
12563
  async ensureBackupDir() {
12515
- const backupDirPath = join10(process.cwd(), this.backupDir);
12516
- await fs20.mkdir(backupDirPath, { recursive: true });
12564
+ const backupDirPath = join11(process.cwd(), this.backupDir);
12565
+ await fs21.mkdir(backupDirPath, { recursive: true });
12517
12566
  }
12518
12567
  };
12519
12568
 
@@ -12557,7 +12606,8 @@ function createRulesApplyCommand() {
12557
12606
  name: `${p.name} - ${p.description}`,
12558
12607
  value: p.slug,
12559
12608
  description: `v${p.version} | ${p.tags.join(", ")}`
12560
- }))
12609
+ })),
12610
+ theme: selectTheme
12561
12611
  });
12562
12612
  }
12563
12613
  console.log(`
@@ -12627,7 +12677,8 @@ function createRulesApplyCommand() {
12627
12677
  selectedIdes = await checkbox5({
12628
12678
  message: "Select IDE formats to generate (pre-selected are recommended):",
12629
12679
  choices,
12630
- required: true
12680
+ required: true,
12681
+ theme: checkboxTheme
12631
12682
  });
12632
12683
  }
12633
12684
  }
@@ -12669,21 +12720,21 @@ function createRulesApplyCommand() {
12669
12720
  }
12670
12721
  }
12671
12722
  console.log("\n\u{1F4DD} Applying preset...\n");
12672
- const rulePresetDir = join11(process.cwd(), ".jai1", "rule-preset");
12723
+ const rulePresetDir = join12(process.cwd(), ".jai1", "rule-preset");
12673
12724
  try {
12674
- await fs21.rm(rulePresetDir, { recursive: true, force: true });
12725
+ await fs22.rm(rulePresetDir, { recursive: true, force: true });
12675
12726
  } catch {
12676
12727
  }
12677
- await fs21.mkdir(rulePresetDir, { recursive: true });
12678
- await fs21.writeFile(
12679
- join11(rulePresetDir, "preset.json"),
12728
+ await fs22.mkdir(rulePresetDir, { recursive: true });
12729
+ await fs22.writeFile(
12730
+ join12(rulePresetDir, "preset.json"),
12680
12731
  JSON.stringify(bundle.preset, null, 2),
12681
12732
  "utf-8"
12682
12733
  );
12683
12734
  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");
12735
+ const filePath = join12(rulePresetDir, filename);
12736
+ await fs22.mkdir(join12(filePath, ".."), { recursive: true });
12737
+ await fs22.writeFile(filePath, content, "utf-8");
12687
12738
  }
12688
12739
  console.log(`\u2713 Saved preset to .jai1/rule-preset/`);
12689
12740
  const allGeneratedFiles = [];
@@ -12691,9 +12742,9 @@ function createRulesApplyCommand() {
12691
12742
  try {
12692
12743
  const files = generatorService.generateForIde(bundle, ideId);
12693
12744
  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");
12745
+ const fullPath = join12(process.cwd(), file.path);
12746
+ await fs22.mkdir(join12(fullPath, ".."), { recursive: true });
12747
+ await fs22.writeFile(fullPath, file.content, "utf-8");
12697
12748
  console.log(`\u2713 [${ideId}] ${file.path}`);
12698
12749
  allGeneratedFiles.push({
12699
12750
  ide: ideId,
@@ -12705,7 +12756,8 @@ function createRulesApplyCommand() {
12705
12756
  console.error(`\u2717 Failed to generate ${ideId} files:`, error);
12706
12757
  }
12707
12758
  }
12708
- const projectConfig = {
12759
+ const projectConfigService = new ProjectConfigService();
12760
+ const rulesConfig = {
12709
12761
  preset: bundle.preset.slug,
12710
12762
  version: bundle.preset.version,
12711
12763
  appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -12720,24 +12772,18 @@ function createRulesApplyCommand() {
12720
12772
  ] : []
12721
12773
  };
12722
12774
  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)
12775
+ const existingRules = await projectConfigService.loadRules();
12776
+ if (existingRules?.backups && existingRules.backups.length > 0) {
12777
+ rulesConfig.backups = [
12778
+ ...rulesConfig.backups,
12779
+ ...existingRules.backups.slice(0, 5)
12730
12780
  // Keep last 5 backups
12731
12781
  ];
12732
12782
  }
12733
12783
  } catch {
12734
12784
  }
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");
12785
+ await projectConfigService.saveRules(rulesConfig);
12786
+ console.log("\n\u2713 Updated .jai1/project.json");
12741
12787
  console.log("\n\u2705 Preset applied successfully!\n");
12742
12788
  console.log("\u{1F4CA} Summary:");
12743
12789
  console.log(` Generated ${allGeneratedFiles.length} files across ${resolvedIdes.length} IDE(s)`);
@@ -12765,7 +12811,7 @@ function createRulesApplyCommand() {
12765
12811
 
12766
12812
  // src/commands/rules/restore.ts
12767
12813
  import { Command as Command46 } from "commander";
12768
- import { join as join12 } from "path";
12814
+ import { join as join13 } from "path";
12769
12815
  import { select as select6, confirm as confirm9 } from "@inquirer/prompts";
12770
12816
  function createRulesRestoreCommand() {
12771
12817
  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 +12833,8 @@ function createRulesRestoreCommand() {
12787
12833
  name: formatBackupInfo(backup),
12788
12834
  value: backup.timestamp,
12789
12835
  description: `${backup.files.length} files | IDEs: ${backup.ides.join(", ")}`
12790
- }))
12836
+ })),
12837
+ theme: selectTheme
12791
12838
  });
12792
12839
  selectedBackup = backups.find((b) => b.timestamp === backupTimestamp);
12793
12840
  if (!selectedBackup) {
@@ -12811,7 +12858,7 @@ function createRulesRestoreCommand() {
12811
12858
  }
12812
12859
  console.log("\n\u{1F504} Restoring backup...\n");
12813
12860
  try {
12814
- const backupPath = join12(backupService.getBackupDir(), selectedBackup.timestamp);
12861
+ const backupPath = join13(backupService.getBackupDir(), selectedBackup.timestamp);
12815
12862
  await backupService.restoreBackup(backupPath);
12816
12863
  console.log("\n\u2705 Backup restored successfully!\n");
12817
12864
  console.log("\u{1F4A1} Tip: Your IDE may need to be restarted to pick up the changes.");
@@ -12837,23 +12884,41 @@ function formatTimestamp(timestamp) {
12837
12884
 
12838
12885
  // src/commands/rules/sync.ts
12839
12886
  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";
12887
+ import { promises as fs23 } from "fs";
12888
+ import { join as join14 } from "path";
12889
+ import { checkbox as checkbox6, confirm as confirm10, Separator } from "@inquirer/prompts";
12843
12890
  function createRulesSyncCommand() {
12844
12891
  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;
12892
+ const rulePresetDir = join14(process.cwd(), ".jai1", "rule-preset");
12893
+ const presetJsonPath = join14(rulePresetDir, "preset.json");
12894
+ let presetExists = false;
12895
+ let presetData = null;
12847
12896
  try {
12848
- const configContent = await fs22.readFile(configPath, "utf-8");
12849
- projectConfig = JSON.parse(configContent);
12897
+ const presetContent = await fs23.readFile(presetJsonPath, "utf-8");
12898
+ presetData = JSON.parse(presetContent);
12899
+ presetExists = true;
12850
12900
  } catch {
12901
+ }
12902
+ if (!presetExists) {
12851
12903
  throw new ValidationError(
12852
- 'No jai1-rules.json found. Run "jai1 rules apply" first.'
12904
+ 'No rule preset found in .jai1/rule-preset/\nRun "jai1 rules apply <preset>" or create a project with "jai1 kit create <kit>" first.'
12853
12905
  );
12854
12906
  }
12907
+ const projectConfigService = new ProjectConfigService();
12908
+ let rulesConfig = await projectConfigService.loadRules();
12909
+ if (!rulesConfig) {
12910
+ console.log("\u26A0\uFE0F No .jai1/project.json found, creating from preset...\n");
12911
+ rulesConfig = {
12912
+ preset: presetData.slug,
12913
+ version: presetData.version,
12914
+ appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
12915
+ ides: [],
12916
+ customContext: "09-custom.md",
12917
+ backups: []
12918
+ };
12919
+ }
12855
12920
  console.log("\u{1F504} Syncing rules...\n");
12856
- console.log(`Preset: ${projectConfig.preset} v${projectConfig.version}`);
12921
+ console.log(`Preset: ${rulesConfig.preset} v${rulesConfig.version}`);
12857
12922
  let idesToSync = [];
12858
12923
  if (options.detect) {
12859
12924
  console.log("\n\u{1F50D} Auto-detecting active IDEs...");
@@ -12884,16 +12949,16 @@ Detected ${detected.length} active IDE(s):
12884
12949
  idesToSync = detected.map((d) => d.id);
12885
12950
  } else if (options.ides) {
12886
12951
  const requested = options.ides.split(",").map((ide) => ide.trim());
12887
- const configured = projectConfig.ides || [];
12952
+ const configured = rulesConfig.ides || [];
12888
12953
  idesToSync = requested;
12889
12954
  const notConfigured = requested.filter((ide) => !configured.includes(ide));
12890
12955
  if (notConfigured.length > 0) {
12891
12956
  console.log(`
12892
12957
  \u26A0\uFE0F IDEs not in config: ${notConfigured.join(", ")}`);
12893
- console.log(" They will still be synced, but may not be in jai1-rules.json");
12958
+ console.log(" They will still be synced, but may not be in .jai1/project.json");
12894
12959
  }
12895
12960
  } else if (!options.yes) {
12896
- const currentIdes = projectConfig.ides || [];
12961
+ const currentIdes = rulesConfig.ides || [];
12897
12962
  console.log(`
12898
12963
  Current IDE(s): ${currentIdes.join(", ") || "none"}`);
12899
12964
  const detectionService = new IdeDetectionService();
@@ -12913,18 +12978,23 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
12913
12978
  }
12914
12979
  const choices = buildIdeChoices(currentIdes, detected, suggestions);
12915
12980
  idesToSync = await checkbox6({
12916
- message: "Select IDE formats to sync (pre-selected are recommended):",
12981
+ message: "Select IDE formats to sync:",
12917
12982
  choices,
12918
- required: true
12983
+ required: false,
12984
+ theme: checkboxTheme
12919
12985
  });
12986
+ if (idesToSync.includes("__quit__")) {
12987
+ console.log("Cancelled.");
12988
+ return;
12989
+ }
12920
12990
  if (idesToSync.length === 0) {
12921
12991
  console.log("Cancelled.");
12922
12992
  return;
12923
12993
  }
12924
12994
  } else {
12925
- idesToSync = projectConfig.ides || [];
12995
+ idesToSync = rulesConfig.ides || [];
12926
12996
  if (idesToSync.length === 0) {
12927
- console.log("\n\u26A0\uFE0F No IDEs configured in jai1-rules.json");
12997
+ console.log("\n\u26A0\uFE0F No IDEs configured in .jai1/project.json");
12928
12998
  console.log(" Detecting from existing files...\n");
12929
12999
  const detectionService = new IdeDetectionService();
12930
13000
  idesToSync = await detectionService.detectExistingIdes();
@@ -12945,7 +13015,7 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
12945
13015
  if (!config) {
12946
13016
  throw new ValidationError('Not initialized. Run "jai1 auth" first.');
12947
13017
  }
12948
- const presetResponse = await fetch(`${config.apiUrl}/api/rules/presets/${projectConfig.preset}`, {
13018
+ const presetResponse = await fetch(`${config.apiUrl}/api/rules/presets/${rulesConfig.preset}`, {
12949
13019
  headers: {
12950
13020
  "JAI1-Access-Key": config.accessKey
12951
13021
  }
@@ -12954,21 +13024,13 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
12954
13024
  throw new Error(`Failed to fetch preset: ${presetResponse.statusText}`);
12955
13025
  }
12956
13026
  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
- }
13027
+ const files = await fs23.readdir(rulePresetDir);
13028
+ for (const file of files) {
13029
+ if (file.endsWith(".mdc") || file.endsWith(".md")) {
13030
+ const filePath = join14(rulePresetDir, file);
13031
+ const content = await fs23.readFile(filePath, "utf-8");
13032
+ bundle.files[file] = content;
12967
13033
  }
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
13034
  }
12973
13035
  const generatorService = new RulesGeneratorService();
12974
13036
  for (const ideId of idesToSync) {
@@ -12978,25 +13040,21 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
12978
13040
  console.log(`\u26A0\uFE0F Unknown IDE format: ${ideId}, skipping`);
12979
13041
  continue;
12980
13042
  }
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");
13043
+ const files2 = generatorService.generateForIde(bundle, ideId);
13044
+ for (const file of files2) {
13045
+ const fullPath = join14(process.cwd(), file.path);
13046
+ await fs23.mkdir(join14(fullPath, ".."), { recursive: true });
13047
+ await fs23.writeFile(fullPath, file.content, "utf-8");
12986
13048
  }
12987
- console.log(`\u2713 ${format.name} - ${files.length} files regenerated`);
13049
+ console.log(`\u2713 ${format.name} - ${files2.length} files regenerated`);
12988
13050
  } catch (error) {
12989
13051
  console.error(`\u2717 Failed to sync ${ideId}:`, error);
12990
13052
  }
12991
13053
  }
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");
13054
+ if (JSON.stringify(rulesConfig.ides) !== JSON.stringify(idesToSync)) {
13055
+ rulesConfig.ides = idesToSync;
13056
+ await projectConfigService.saveRules(rulesConfig);
13057
+ console.log("\n\u2713 Updated .jai1/project.json with synced IDEs");
13000
13058
  }
13001
13059
  console.log("\n\u2705 Rules synced successfully!\n");
13002
13060
  console.log("\u{1F4A1} Next steps:");
@@ -13036,48 +13094,43 @@ function buildIdeChoices(currentIdes, detected, suggestions) {
13036
13094
  name: "Gemini CLI (GEMINI.md)",
13037
13095
  value: "gemini",
13038
13096
  checked: currentIdes.includes("gemini") || detected.some((d) => d.id === "gemini")
13097
+ },
13098
+ new Separator(),
13099
+ {
13100
+ name: "\u2716 Quit",
13101
+ value: "__quit__",
13102
+ checked: false
13039
13103
  }
13040
13104
  ];
13041
13105
  return choices;
13042
13106
  }
13043
- async function checkPathExists(absolutePath) {
13044
- try {
13045
- await fs22.access(absolutePath);
13046
- return true;
13047
- } catch {
13048
- return false;
13049
- }
13050
- }
13051
13107
 
13052
13108
  // src/commands/rules/info.ts
13053
13109
  import { Command as Command48 } from "commander";
13054
- import { promises as fs23 } from "fs";
13055
- import { join as join14 } from "path";
13110
+ import { promises as fs24 } from "fs";
13111
+ import { join as join15 } from "path";
13056
13112
  function createRulesInfoCommand() {
13057
13113
  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 {
13114
+ const projectConfigService = new ProjectConfigService();
13115
+ const rulesConfig = await projectConfigService.loadRules();
13116
+ if (!rulesConfig) {
13064
13117
  throw new ValidationError(
13065
- 'No jai1-rules.json found. Run "jai1 rules init" first.'
13118
+ 'No .jai1/project.json found. Run "jai1 rules apply" first.'
13066
13119
  );
13067
13120
  }
13068
13121
  if (options.json) {
13069
- console.log(JSON.stringify(projectConfig, null, 2));
13122
+ console.log(JSON.stringify(rulesConfig, null, 2));
13070
13123
  return;
13071
13124
  }
13072
13125
  console.log("\u{1F4CB} Current Preset Information\n");
13073
- const rulePresetDir = join14(process.cwd(), ".jai1", "rule-preset");
13074
- const presetJsonPath = join14(rulePresetDir, "preset.json");
13126
+ const rulePresetDir = join15(process.cwd(), ".jai1", "rule-preset");
13127
+ const presetJsonPath = join15(rulePresetDir, "preset.json");
13075
13128
  let presetMetadata = null;
13076
13129
  let presetFiles = [];
13077
13130
  try {
13078
- const presetContent = await fs23.readFile(presetJsonPath, "utf-8");
13131
+ const presetContent = await fs24.readFile(presetJsonPath, "utf-8");
13079
13132
  presetMetadata = JSON.parse(presetContent);
13080
- const files = await fs23.readdir(rulePresetDir);
13133
+ const files = await fs24.readdir(rulePresetDir);
13081
13134
  presetFiles = files.filter((f) => f.endsWith(".mdc"));
13082
13135
  } catch {
13083
13136
  }
@@ -13089,10 +13142,10 @@ function createRulesInfoCommand() {
13089
13142
  console.log(`Tags: ${presetMetadata.tags.join(", ")}`);
13090
13143
  }
13091
13144
  } else {
13092
- console.log(`Preset: ${projectConfig.preset}`);
13093
- console.log(`Version: ${projectConfig.version}`);
13145
+ console.log(`Preset: ${rulesConfig.preset}`);
13146
+ console.log(`Version: ${rulesConfig.version}`);
13094
13147
  }
13095
- console.log(`Applied at: ${new Date(projectConfig.appliedAt).toLocaleString()}`);
13148
+ console.log(`Applied at: ${new Date(rulesConfig.appliedAt).toLocaleString()}`);
13096
13149
  console.log(`
13097
13150
  Source of Truth:`);
13098
13151
  if (presetFiles.length > 0) {
@@ -13107,10 +13160,10 @@ Source of Truth:`);
13107
13160
  console.log(` \u26A0\uFE0F .jai1/rule-preset/ not found`);
13108
13161
  console.log(` Run "jai1 rules apply" to create it`);
13109
13162
  }
13110
- if (projectConfig.ides && projectConfig.ides.length > 0) {
13163
+ if (rulesConfig.ides && rulesConfig.ides.length > 0) {
13111
13164
  console.log(`
13112
- Configured IDEs (${projectConfig.ides.length}):`);
13113
- for (const ideId of projectConfig.ides) {
13165
+ Configured IDEs (${rulesConfig.ides.length}):`);
13166
+ for (const ideId of rulesConfig.ides) {
13114
13167
  const format = IDE_FORMATS[ideId];
13115
13168
  if (format) {
13116
13169
  const exists = await checkIdeFilesExist(ideId, format);
@@ -13119,15 +13172,15 @@ Configured IDEs (${projectConfig.ides.length}):`);
13119
13172
  }
13120
13173
  }
13121
13174
  }
13122
- if (projectConfig.backups && projectConfig.backups.length > 0) {
13175
+ if (rulesConfig.backups && rulesConfig.backups.length > 0) {
13123
13176
  console.log(`
13124
- Available Backups (${projectConfig.backups.length}):`);
13125
- for (const backup of projectConfig.backups.slice(0, 3)) {
13177
+ Available Backups (${rulesConfig.backups.length}):`);
13178
+ for (const backup of rulesConfig.backups.slice(0, 3)) {
13126
13179
  const timestamp = new Date(backup.timestamp).toLocaleString();
13127
13180
  console.log(` \u2022 ${timestamp} - IDEs: ${backup.ides.join(", ")}`);
13128
13181
  }
13129
- if (projectConfig.backups.length > 3) {
13130
- console.log(` ... and ${projectConfig.backups.length - 3} more`);
13182
+ if (rulesConfig.backups.length > 3) {
13183
+ console.log(` ... and ${rulesConfig.backups.length - 3} more`);
13131
13184
  }
13132
13185
  }
13133
13186
  console.log("\n\u2139\uFE0F Commands:");
@@ -13136,9 +13189,9 @@ Available Backups (${projectConfig.backups.length}):`);
13136
13189
  console.log(' \u2022 "jai1 rules apply" - Apply a different preset (replaces current)');
13137
13190
  });
13138
13191
  }
13139
- async function checkPathExists2(path13) {
13192
+ async function checkPathExists(path13) {
13140
13193
  try {
13141
- await fs23.access(join14(process.cwd(), path13));
13194
+ await fs24.access(join15(process.cwd(), path13));
13142
13195
  return true;
13143
13196
  } catch {
13144
13197
  return false;
@@ -13147,11 +13200,11 @@ async function checkPathExists2(path13) {
13147
13200
  async function checkIdeFilesExist(ideId, format) {
13148
13201
  try {
13149
13202
  if (ideId === "agentsmd") {
13150
- return await checkPathExists2("AGENTS.md");
13203
+ return await checkPathExists("AGENTS.md");
13151
13204
  } else if (ideId === "gemini") {
13152
- return await checkPathExists2("GEMINI.md");
13205
+ return await checkPathExists("GEMINI.md");
13153
13206
  } else {
13154
- return await checkPathExists2(format.rulesPath);
13207
+ return await checkPathExists(format.rulesPath);
13155
13208
  }
13156
13209
  } catch {
13157
13210
  return false;
@@ -13167,7 +13220,7 @@ function showRulesHelp() {
13167
13220
  console.log(` ${chalk17.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t preset`);
13168
13221
  console.log(` ${chalk17.cyan("init")} Kh\u1EDFi t\u1EA1o rules t\u1EEB preset`);
13169
13222
  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`);
13223
+ console.log(` ${chalk17.cyan("sync")} \u0110\u1ED3ng b\u1ED9 rules sang c\xE1c \u0111\u1ECBnh d\u1EA1ng IDE`);
13171
13224
  console.log(` ${chalk17.cyan("restore")} Kh\xF4i ph\u1EE5c rules t\u1EEB backup`);
13172
13225
  console.log();
13173
13226
  console.log(chalk17.bold("V\xED d\u1EE5:"));
@@ -13354,7 +13407,7 @@ function getInstallCommand(packageManager2) {
13354
13407
  // src/commands/clean.ts
13355
13408
  import { Command as Command51 } from "commander";
13356
13409
  import { confirm as confirm12, select as select7 } from "@inquirer/prompts";
13357
- import { join as join15 } from "path";
13410
+ import { join as join16 } from "path";
13358
13411
  function createCleanCommand() {
13359
13412
  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
13413
  await handleClean(options);
@@ -13367,7 +13420,7 @@ async function handleClean(options) {
13367
13420
  {
13368
13421
  name: "Backups",
13369
13422
  description: "Component backup files (.jai1_backup/)",
13370
- path: join15(cwd, ".jai1_backup"),
13423
+ path: join16(cwd, ".jai1_backup"),
13371
13424
  check: async () => {
13372
13425
  const backups = await service.listBackups(cwd);
13373
13426
  return { exists: backups.length > 0, count: backups.length };
@@ -13426,7 +13479,8 @@ async function handleClean(options) {
13426
13479
  })),
13427
13480
  { name: "\u{1F9F9} Clean all", value: "all" },
13428
13481
  { name: "\u274C Cancel", value: "cancel" }
13429
- ]
13482
+ ],
13483
+ theme: selectTheme
13430
13484
  });
13431
13485
  if (action === "cancel") {
13432
13486
  console.log("\n\u274C Operation cancelled.");
@@ -14358,8 +14412,8 @@ async function handleSyncProject(options) {
14358
14412
 
14359
14413
  // src/commands/framework/info.ts
14360
14414
  import { Command as Command55 } from "commander";
14361
- import { promises as fs24 } from "fs";
14362
- import { join as join16 } from "path";
14415
+ import { promises as fs25 } from "fs";
14416
+ import { join as join17 } from "path";
14363
14417
  import { homedir as homedir5 } from "os";
14364
14418
  function createInfoCommand() {
14365
14419
  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 +14427,7 @@ async function handleInfo(options) {
14373
14427
  if (!config) {
14374
14428
  throw new ValidationError('Not initialized. Run "jai1 auth" first.');
14375
14429
  }
14376
- const frameworkPath = join16(homedir5(), ".jai1", "framework");
14430
+ const frameworkPath = join17(homedir5(), ".jai1", "framework");
14377
14431
  const projectStatus = await getProjectStatus2();
14378
14432
  const info = {
14379
14433
  configPath: configService.getConfigPath(),
@@ -14408,9 +14462,9 @@ function maskKey3(key) {
14408
14462
  return "****" + key.slice(-4);
14409
14463
  }
14410
14464
  async function getProjectStatus2() {
14411
- const projectJai1 = join16(process.cwd(), ".jai1");
14465
+ const projectJai1 = join17(process.cwd(), ".jai1");
14412
14466
  try {
14413
- await fs24.access(projectJai1);
14467
+ await fs25.access(projectJai1);
14414
14468
  return { exists: true, version: "Synced" };
14415
14469
  } catch {
14416
14470
  return { exists: false };
@@ -14600,7 +14654,7 @@ function createClearBackupsCommand() {
14600
14654
  // src/commands/vscode/index.ts
14601
14655
  import { Command as Command58 } from "commander";
14602
14656
  import { checkbox as checkbox7, confirm as confirm15, select as select8 } from "@inquirer/prompts";
14603
- import fs25 from "fs/promises";
14657
+ import fs26 from "fs/promises";
14604
14658
  import path12 from "path";
14605
14659
  import { existsSync as existsSync3 } from "fs";
14606
14660
  var PERFORMANCE_GROUPS2 = {
@@ -14789,7 +14843,8 @@ async function interactiveMode2() {
14789
14843
  { name: "\u274C Disable c\xE1c nh\xF3m t\u1ED1i \u01B0u", value: "disable" },
14790
14844
  { name: "\u{1F680} Max Performance (enable t\u1EA5t c\u1EA3)", value: "max" },
14791
14845
  { name: "\u{1F504} Reset v\u1EC1 m\u1EB7c \u0111\u1ECBnh", value: "reset" }
14792
- ]
14846
+ ],
14847
+ theme: selectTheme
14793
14848
  });
14794
14849
  if (action === "max") {
14795
14850
  const allGroups = Object.keys(PERFORMANCE_GROUPS2);
@@ -14808,7 +14863,8 @@ async function selectGroupsToApply2(action) {
14808
14863
  try {
14809
14864
  const selectedGroups = await checkbox7({
14810
14865
  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
14866
+ choices,
14867
+ theme: checkboxTheme
14812
14868
  });
14813
14869
  if (selectedGroups.length === 0) {
14814
14870
  console.log("\n\u26A0\uFE0F B\u1EA1n ch\u01B0a ch\u1ECDn nh\xF3m n\xE0o!");
@@ -14831,13 +14887,13 @@ async function applyGroups2(groupKeys, action) {
14831
14887
  return;
14832
14888
  }
14833
14889
  if (!existsSync3(vscodeDir)) {
14834
- await fs25.mkdir(vscodeDir, { recursive: true });
14890
+ await fs26.mkdir(vscodeDir, { recursive: true });
14835
14891
  console.log("\u{1F4C1} \u0110\xE3 t\u1EA1o th\u01B0 m\u1EE5c .vscode/");
14836
14892
  }
14837
14893
  let currentSettings = {};
14838
14894
  if (existsSync3(settingsPath)) {
14839
14895
  try {
14840
- const content = await fs25.readFile(settingsPath, "utf-8");
14896
+ const content = await fs26.readFile(settingsPath, "utf-8");
14841
14897
  currentSettings = JSON.parse(content);
14842
14898
  console.log("\u{1F4C4} \u0110\xE3 \u0111\u1ECDc c\xE0i \u0111\u1EB7t hi\u1EC7n t\u1EA1i t\u1EEB settings.json");
14843
14899
  } catch {
@@ -14877,7 +14933,7 @@ async function applyGroups2(groupKeys, action) {
14877
14933
  }
14878
14934
  }
14879
14935
  }
14880
- await fs25.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
14936
+ await fs26.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
14881
14937
  console.log(`
14882
14938
  \u2705 \u0110\xE3 c\u1EADp nh\u1EADt c\xE0i \u0111\u1EB7t VSCode t\u1EA1i: ${settingsPath}`);
14883
14939
  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 +14954,7 @@ async function resetSettings2(groupKeys) {
14898
14954
  return;
14899
14955
  }
14900
14956
  if (groupKeys.length === 0) {
14901
- await fs25.unlink(settingsPath);
14957
+ await fs26.unlink(settingsPath);
14902
14958
  console.log("\n\u2705 \u0110\xE3 x\xF3a file settings.json");
14903
14959
  } else {
14904
14960
  await applyGroups2(groupKeys, "disable");
@@ -15021,7 +15077,8 @@ async function runMigrateIde(options) {
15021
15077
  });
15022
15078
  selectedIdes = await checkbox8({
15023
15079
  message: "Ch\u1ECDn IDE(s) \u0111\u1EC3 migrate (SPACE \u0111\u1EC3 ch\u1ECDn, ENTER \u0111\u1EC3 x\xE1c nh\u1EADn):",
15024
- choices: ideChoices
15080
+ choices: ideChoices,
15081
+ theme: checkboxTheme
15025
15082
  });
15026
15083
  if (selectedIdes.length === 0) {
15027
15084
  console.log("\n\u26A0\uFE0F B\u1EA1n ch\u01B0a ch\u1ECDn IDE n\xE0o!");
@@ -15040,7 +15097,8 @@ async function runMigrateIde(options) {
15040
15097
  ];
15041
15098
  selectedTypes = await checkbox8({
15042
15099
  message: "Ch\u1ECDn content types \u0111\u1EC3 migrate:",
15043
- choices: typeChoices
15100
+ choices: typeChoices,
15101
+ theme: checkboxTheme
15044
15102
  });
15045
15103
  if (selectedTypes.length === 0) {
15046
15104
  console.log("\n\u26A0\uFE0F B\u1EA1n ch\u01B0a ch\u1ECDn content type n\xE0o!");