@jvittechs/j 1.0.31 → 1.0.33

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
@@ -1,4 +1,11 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ ComponentsService,
4
+ Jai1Error,
5
+ NetworkError,
6
+ NotFoundError,
7
+ ValidationError
8
+ } from "./chunk-XZ7VS36G.js";
2
9
  import {
3
10
  BLOCKED_ICON,
4
11
  PRIORITY_ICONS,
@@ -42,34 +49,7 @@ function checkNodeVersion() {
42
49
  }
43
50
 
44
51
  // src/cli.ts
45
- import { Command as Command79 } from "commander";
46
-
47
- // src/errors/index.ts
48
- var Jai1Error = class extends Error {
49
- constructor(message, exitCode) {
50
- super(message);
51
- this.exitCode = exitCode;
52
- this.name = "Jai1Error";
53
- }
54
- };
55
- var ValidationError = class extends Jai1Error {
56
- constructor(message) {
57
- super(message, 2);
58
- this.name = "ValidationError";
59
- }
60
- };
61
- var NotFoundError = class extends Jai1Error {
62
- constructor(message) {
63
- super(message, 4);
64
- this.name = "NotFoundError";
65
- }
66
- };
67
- var NetworkError = class extends Jai1Error {
68
- constructor(message) {
69
- super(message, 5);
70
- this.name = "NetworkError";
71
- }
72
- };
52
+ import { Command as Command85 } from "commander";
73
53
 
74
54
  // src/services/error-log.service.ts
75
55
  import { promises as fs } from "fs";
@@ -164,12 +144,12 @@ var ErrorLogService = class {
164
144
  };
165
145
 
166
146
  // src/cli.ts
167
- import { basename as basename4 } from "path";
147
+ import { basename as basename5 } from "path";
168
148
 
169
149
  // package.json
170
150
  var package_default = {
171
151
  name: "@jvittechs/j",
172
- version: "1.0.31",
152
+ version: "1.0.33",
173
153
  description: "A unified CLI tool for JV-IT TECHS developers to manage Jai1 Framework. Supports both `j` and `jai1` commands. Please contact TeamAI for usage instructions.",
174
154
  type: "module",
175
155
  bin: {
@@ -612,295 +592,9 @@ async function handleAuth(options) {
612
592
  import { Command as Command2 } from "commander";
613
593
  import chalk3 from "chalk";
614
594
  import boxen2 from "boxen";
615
-
616
- // src/services/components.service.ts
617
595
  import { promises as fs3 } from "fs";
618
596
  import { join as join3 } from "path";
619
597
  import { homedir as homedir4 } from "os";
620
- import { gunzipSync } from "zlib";
621
- import { createHash } from "crypto";
622
- var ComponentsService = class {
623
- cacheDir;
624
- manifestFile;
625
- constructor(projectRoot = process.cwd()) {
626
- this.cacheDir = join3(homedir4(), ".jai1", "cache");
627
- this.manifestFile = join3(projectRoot, ".jai1", "manifest.json");
628
- }
629
- /**
630
- * Expand component paths with special prefixes
631
- *
632
- * Supported formats:
633
- * - Standard paths: "rule-presets/react-spa-zustand", "rules/jai1.md", "workflows/commit-it.md"
634
- * - Package prefix: "package:core" -> expands to all components in that package
635
- *
636
- * @param config - Jai1 config
637
- * @param paths - Array of component paths (may include special prefixes)
638
- * @returns Array of expanded component paths
639
- */
640
- async expandPaths(config, paths) {
641
- const expandedPaths = [];
642
- for (const path13 of paths) {
643
- if (path13.startsWith("package:")) {
644
- const packageName = path13.substring("package:".length);
645
- const components = await this.list(config);
646
- if (packageName === "core") {
647
- expandedPaths.push(...components.map((c) => c.filepath));
648
- } else {
649
- console.warn(`Warning: Unknown package '${packageName}', skipping`);
650
- }
651
- } else {
652
- expandedPaths.push(path13);
653
- }
654
- }
655
- return expandedPaths;
656
- }
657
- /**
658
- * List components from API
659
- */
660
- async list(config, options) {
661
- const params = new URLSearchParams();
662
- if (options?.tag) params.set("tag", options.tag);
663
- if (options?.search) params.set("search", options.search);
664
- const url = `${config.apiUrl}/api/components${params.toString() ? "?" + params.toString() : ""}`;
665
- const response = await fetch(url, {
666
- headers: { "JAI1-Access-Key": config.accessKey }
667
- });
668
- if (!response.ok) {
669
- throw new NetworkError(`Failed to list components: HTTP ${response.status}`);
670
- }
671
- const data = await response.json();
672
- return data.components;
673
- }
674
- /**
675
- * List available tags
676
- */
677
- async listTags(config) {
678
- const response = await fetch(`${config.apiUrl}/api/tags`, {
679
- headers: { "JAI1-Access-Key": config.accessKey }
680
- });
681
- if (!response.ok) {
682
- throw new NetworkError(`Failed to list tags: HTTP ${response.status}`);
683
- }
684
- const data = await response.json();
685
- return data.tags;
686
- }
687
- /**
688
- * Get single component
689
- */
690
- async get(config, filepath) {
691
- const encodedPath = encodeURIComponent(filepath);
692
- const response = await fetch(`${config.apiUrl}/api/components/${encodedPath}`, {
693
- headers: { "JAI1-Access-Key": config.accessKey }
694
- });
695
- if (!response.ok) {
696
- if (response.status === 404) {
697
- throw new Error(`Component not found: ${filepath}`);
698
- }
699
- throw new NetworkError(`Failed to get component: HTTP ${response.status}`);
700
- }
701
- return await response.json();
702
- }
703
- /**
704
- * Get core components
705
- */
706
- async getCore(config) {
707
- const response = await fetch(`${config.apiUrl}/api/components/core`, {
708
- headers: { "JAI1-Access-Key": config.accessKey }
709
- });
710
- if (!response.ok) {
711
- throw new NetworkError(`Failed to get core components: HTTP ${response.status}`);
712
- }
713
- const data = await response.json();
714
- return data.components;
715
- }
716
- /**
717
- * Download and install a component
718
- */
719
- async install(config, filepath, targetDir) {
720
- const component = await this.get(config, filepath);
721
- if (!component.content) {
722
- throw new Error(`Component ${filepath} has no content`);
723
- }
724
- let checksumContent = component.content;
725
- if (component.contentType === "bundle" || component.contentType === "zip") {
726
- let bundleJson;
727
- if (component.contentType === "bundle") {
728
- const compressed = Buffer.from(component.content, "base64");
729
- bundleJson = gunzipSync(compressed).toString("utf-8");
730
- checksumContent = bundleJson;
731
- } else {
732
- bundleJson = component.content;
733
- }
734
- const bundle = JSON.parse(bundleJson);
735
- const skillDir = join3(targetDir, filepath);
736
- await fs3.mkdir(skillDir, { recursive: true });
737
- await fs3.writeFile(join3(skillDir, "SKILL.md"), bundle.main);
738
- for (const [assetPath, content] of Object.entries(bundle.assets)) {
739
- if (assetPath === "SKILL.md") continue;
740
- const assetFullPath = join3(skillDir, assetPath);
741
- await fs3.mkdir(join3(assetFullPath, ".."), { recursive: true });
742
- await fs3.writeFile(assetFullPath, content);
743
- }
744
- } else if (component.contentType === "rule-preset") {
745
- const compressed = Buffer.from(component.content, "base64");
746
- const bundleJson = gunzipSync(compressed).toString("utf-8");
747
- checksumContent = bundleJson;
748
- const bundle = JSON.parse(bundleJson);
749
- const presetDir = join3(targetDir, "rule-preset");
750
- try {
751
- await fs3.rm(presetDir, { recursive: true, force: true });
752
- } catch {
753
- }
754
- await fs3.mkdir(presetDir, { recursive: true });
755
- await fs3.writeFile(
756
- join3(presetDir, "preset.json"),
757
- JSON.stringify(bundle.preset, null, 2)
758
- );
759
- for (const [filename, content] of Object.entries(bundle.files)) {
760
- const filePath = join3(presetDir, filename);
761
- await fs3.mkdir(join3(filePath, ".."), { recursive: true });
762
- await fs3.writeFile(filePath, content);
763
- }
764
- } else {
765
- if (component.contentType === "markdown") {
766
- checksumContent = component.content;
767
- }
768
- const targetPath = join3(targetDir, filepath);
769
- const targetFolder = join3(targetPath, "..");
770
- await fs3.mkdir(targetFolder, { recursive: true });
771
- await fs3.writeFile(targetPath, component.content);
772
- }
773
- await this.markInstalled(filepath, component.version, this.calculateChecksum(checksumContent));
774
- }
775
- /**
776
- * Get installed components
777
- */
778
- async getInstalled() {
779
- try {
780
- const content = await fs3.readFile(this.manifestFile, "utf-8");
781
- return JSON.parse(content);
782
- } catch {
783
- return {};
784
- }
785
- }
786
- /**
787
- * Mark component as installed
788
- */
789
- async markInstalled(filepath, version, checksum) {
790
- const installed = await this.getInstalled();
791
- installed[filepath] = {
792
- filepath,
793
- version,
794
- checksum,
795
- installedAt: (/* @__PURE__ */ new Date()).toISOString(),
796
- modified: false
797
- };
798
- await fs3.mkdir(join3(this.manifestFile, ".."), { recursive: true });
799
- await fs3.writeFile(this.manifestFile, JSON.stringify(installed, null, 2));
800
- }
801
- /**
802
- * Get checksums for multiple components
803
- */
804
- async getChecksums(config, filepaths) {
805
- const response = await fetch(`${config.apiUrl}/api/components/checksums`, {
806
- method: "POST",
807
- headers: {
808
- "JAI1-Access-Key": config.accessKey,
809
- "Content-Type": "application/json"
810
- },
811
- body: JSON.stringify({ filepaths })
812
- });
813
- if (!response.ok) {
814
- throw new NetworkError(`Failed to get checksums: HTTP ${response.status}`);
815
- }
816
- const data = await response.json();
817
- return data.checksums;
818
- }
819
- /**
820
- * Resolve dependencies recursively
821
- */
822
- async resolveWithDependencies(config, filepaths) {
823
- const resolved = /* @__PURE__ */ new Set();
824
- const queue = [...filepaths];
825
- const seen = /* @__PURE__ */ new Set();
826
- while (queue.length > 0) {
827
- const current = queue.shift();
828
- if (seen.has(current)) continue;
829
- seen.add(current);
830
- resolved.add(current);
831
- try {
832
- const component = await this.get(config, current);
833
- if (component.dependencies) {
834
- for (const dep of component.dependencies) {
835
- if (!dep.filepath) continue;
836
- const depPath = typeof dep === "string" ? dep : dep.filepath;
837
- if (!seen.has(depPath)) {
838
- queue.push(depPath);
839
- }
840
- }
841
- }
842
- } catch (error) {
843
- console.warn(`Warning: Could not resolve dependencies for ${current}: ${error}`);
844
- }
845
- }
846
- return Array.from(resolved);
847
- }
848
- /**
849
- * Backup component file before update
850
- */
851
- async backupFile(filepath, targetDir) {
852
- const sourcePath = join3(targetDir, filepath);
853
- try {
854
- await fs3.access(sourcePath);
855
- } catch {
856
- return null;
857
- }
858
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
859
- const backupDir = join3(targetDir, "..", ".jai1_backup", timestamp);
860
- const backupPath = join3(backupDir, filepath);
861
- const stats = await fs3.stat(sourcePath);
862
- await fs3.mkdir(join3(backupPath, ".."), { recursive: true });
863
- if (stats.isDirectory()) {
864
- await fs3.cp(sourcePath, backupPath, { recursive: true });
865
- } else {
866
- await fs3.copyFile(sourcePath, backupPath);
867
- }
868
- return backupPath;
869
- }
870
- /**
871
- * List all backup directories
872
- */
873
- async listBackups(projectRoot) {
874
- const backupRoot = join3(projectRoot, ".jai1_backup");
875
- try {
876
- const entries = await fs3.readdir(backupRoot, { withFileTypes: true });
877
- return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort().reverse();
878
- } catch {
879
- return [];
880
- }
881
- }
882
- /**
883
- * Clear all backups
884
- */
885
- async clearBackups(projectRoot) {
886
- const backupRoot = join3(projectRoot, ".jai1_backup");
887
- try {
888
- await fs3.rm(backupRoot, { recursive: true, force: true });
889
- } catch (error) {
890
- }
891
- }
892
- /**
893
- * Calculate SHA256 checksum (matches server logic)
894
- */
895
- calculateChecksum(content) {
896
- return createHash("sha256").update(content).digest("hex").substring(0, 16);
897
- }
898
- };
899
-
900
- // src/commands/status.ts
901
- import { promises as fs4 } from "fs";
902
- import { join as join4 } from "path";
903
- import { homedir as homedir5 } from "os";
904
598
  function createStatusCommand() {
905
599
  const cmd = new Command2("status").description("Hi\u1EC3n th\u1ECB tr\u1EA1ng th\xE1i c\u1EA5u h\xECnh v\xE0 components").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
906
600
  await handleStatus(options);
@@ -914,7 +608,7 @@ async function handleStatus(options) {
914
608
  throw new ValidationError(`Not initialized. Run "${getCliName()} auth" first.`);
915
609
  }
916
610
  const componentsService = new ComponentsService();
917
- const frameworkPath = join4(homedir5(), ".jai1", "framework");
611
+ const frameworkPath = join3(homedir4(), ".jai1", "framework");
918
612
  const projectStatus = await getProjectStatus();
919
613
  const installedComponents = await componentsService.getInstalled();
920
614
  const componentCount = Object.keys(installedComponents).length;
@@ -971,9 +665,9 @@ function maskKey(key) {
971
665
  return "****" + key.slice(-4);
972
666
  }
973
667
  async function getProjectStatus() {
974
- const projectJai1 = join4(process.cwd(), ".jai1");
668
+ const projectJai1 = join3(process.cwd(), ".jai1");
975
669
  try {
976
- await fs4.access(projectJai1);
670
+ await fs3.access(projectJai1);
977
671
  return { exists: true, version: "Synced" };
978
672
  } catch {
979
673
  return { exists: false };
@@ -992,7 +686,7 @@ import Spinner from "ink-spinner";
992
686
  import TextInput from "ink-text-input";
993
687
 
994
688
  // src/services/migrate-ide.service.ts
995
- import { promises as fs5 } from "fs";
689
+ import { promises as fs4 } from "fs";
996
690
  import path from "path";
997
691
  import matter from "gray-matter";
998
692
 
@@ -1180,18 +874,18 @@ var MigrateIdeService = class {
1180
874
  const items = [];
1181
875
  const presetDir = path.join(this.jai1Path, "rule-preset");
1182
876
  try {
1183
- await fs5.access(presetDir);
877
+ await fs4.access(presetDir);
1184
878
  } catch {
1185
879
  return items;
1186
880
  }
1187
- const files = await fs5.readdir(presetDir);
881
+ const files = await fs4.readdir(presetDir);
1188
882
  for (const file of files) {
1189
883
  if (!file.endsWith(".md") && !file.endsWith(".mdc")) continue;
1190
884
  if (file === "preset.json") continue;
1191
885
  const filepath = path.join(presetDir, file);
1192
- const stat = await fs5.stat(filepath);
886
+ const stat = await fs4.stat(filepath);
1193
887
  if (!stat.isFile()) continue;
1194
- const content = await fs5.readFile(filepath, "utf-8");
888
+ const content = await fs4.readFile(filepath, "utf-8");
1195
889
  let frontmatter = {};
1196
890
  try {
1197
891
  const { data } = matter(content);
@@ -1221,17 +915,17 @@ var MigrateIdeService = class {
1221
915
  const items = [];
1222
916
  const dirPath = path.join(this.jai1Path, type);
1223
917
  try {
1224
- await fs5.access(dirPath);
918
+ await fs4.access(dirPath);
1225
919
  } catch {
1226
920
  return items;
1227
921
  }
1228
- const files = await fs5.readdir(dirPath);
922
+ const files = await fs4.readdir(dirPath);
1229
923
  for (const file of files) {
1230
924
  if (!file.endsWith(".md")) continue;
1231
925
  const filepath = path.join(dirPath, file);
1232
- const stat = await fs5.stat(filepath);
926
+ const stat = await fs4.stat(filepath);
1233
927
  if (!stat.isFile()) continue;
1234
- const content = await fs5.readFile(filepath, "utf-8");
928
+ const content = await fs4.readFile(filepath, "utf-8");
1235
929
  let frontmatter = {};
1236
930
  try {
1237
931
  const { data } = matter(content);
@@ -1270,7 +964,7 @@ var MigrateIdeService = class {
1270
964
  alwaysApply: sourceItem.alwaysApply,
1271
965
  sourceFile: sourceItem.relativePath
1272
966
  });
1273
- const sourceContent = await fs5.readFile(sourceItem.filepath, "utf-8");
967
+ const sourceContent = await fs4.readFile(sourceItem.filepath, "utf-8");
1274
968
  const bodyContent = extractBody(sourceContent);
1275
969
  if (frontmatter) {
1276
970
  return `${frontmatter}
@@ -1316,7 +1010,7 @@ ${bodyContent}
1316
1010
  try {
1317
1011
  let status = "created";
1318
1012
  try {
1319
- await fs5.access(agentsPath);
1013
+ await fs4.access(agentsPath);
1320
1014
  status = "updated";
1321
1015
  } catch {
1322
1016
  }
@@ -1325,7 +1019,7 @@ ${bodyContent}
1325
1019
  ""
1326
1020
  ];
1327
1021
  for (const rule of rules) {
1328
- const content = await fs5.readFile(rule.filepath, "utf-8");
1022
+ const content = await fs4.readFile(rule.filepath, "utf-8");
1329
1023
  const body = extractBody(content);
1330
1024
  if (body.trim()) {
1331
1025
  lines.push(downshiftHeadings(body));
@@ -1333,7 +1027,7 @@ ${bodyContent}
1333
1027
  }
1334
1028
  }
1335
1029
  lines.push("");
1336
- await fs5.writeFile(agentsPath, lines.join("\n"), "utf-8");
1030
+ await fs4.writeFile(agentsPath, lines.join("\n"), "utf-8");
1337
1031
  return {
1338
1032
  source: {
1339
1033
  type: "rules",
@@ -1379,15 +1073,15 @@ ${bodyContent}
1379
1073
  };
1380
1074
  }
1381
1075
  const targetDir = path.dirname(targetPath);
1382
- await fs5.mkdir(targetDir, { recursive: true });
1076
+ await fs4.mkdir(targetDir, { recursive: true });
1383
1077
  let status = "created";
1384
1078
  try {
1385
- await fs5.access(targetPath);
1079
+ await fs4.access(targetPath);
1386
1080
  status = "updated";
1387
1081
  } catch {
1388
1082
  }
1389
1083
  const copiedContent = await this.generateCopiedContent(ide, item);
1390
- await fs5.writeFile(targetPath, copiedContent, "utf-8");
1084
+ await fs4.writeFile(targetPath, copiedContent, "utf-8");
1391
1085
  return {
1392
1086
  source: item,
1393
1087
  targetIDE: ide,
@@ -2629,7 +2323,7 @@ import { Command as Command7 } from "commander";
2629
2323
  import chalk6 from "chalk";
2630
2324
 
2631
2325
  // src/services/context-scanner.service.ts
2632
- import { promises as fs6 } from "fs";
2326
+ import { promises as fs5 } from "fs";
2633
2327
  import path2 from "path";
2634
2328
  import matter2 from "gray-matter";
2635
2329
 
@@ -2850,7 +2544,7 @@ var ContextScannerService = class {
2850
2544
  } else {
2851
2545
  dirPath = path2.join(this.projectPath, config.basePath, relativePath);
2852
2546
  try {
2853
- await fs6.access(dirPath);
2547
+ await fs5.access(dirPath);
2854
2548
  } catch {
2855
2549
  return [];
2856
2550
  }
@@ -2861,10 +2555,10 @@ var ContextScannerService = class {
2861
2555
  const skillItems = await this.scanSkills(dirPath, ide);
2862
2556
  items.push(...skillItems);
2863
2557
  } else {
2864
- const files = await fs6.readdir(dirPath);
2558
+ const files = await fs5.readdir(dirPath);
2865
2559
  for (const file of files) {
2866
2560
  const filepath = path2.join(dirPath, file);
2867
- const stat = await fs6.stat(filepath);
2561
+ const stat = await fs5.stat(filepath);
2868
2562
  if (!stat.isFile()) continue;
2869
2563
  const matchesExtension = extensions.some((ext) => file.endsWith(ext));
2870
2564
  if (!matchesExtension) continue;
@@ -2884,13 +2578,13 @@ var ContextScannerService = class {
2884
2578
  async scanSkills(skillsDir, ide) {
2885
2579
  const items = [];
2886
2580
  try {
2887
- const entries = await fs6.readdir(skillsDir, { withFileTypes: true });
2581
+ const entries = await fs5.readdir(skillsDir, { withFileTypes: true });
2888
2582
  for (const entry of entries) {
2889
2583
  if (!entry.isDirectory()) continue;
2890
2584
  const skillPath = path2.join(skillsDir, entry.name);
2891
2585
  const skillFilePath = path2.join(skillPath, "SKILL.md");
2892
2586
  try {
2893
- await fs6.access(skillFilePath);
2587
+ await fs5.access(skillFilePath);
2894
2588
  } catch {
2895
2589
  continue;
2896
2590
  }
@@ -2925,8 +2619,8 @@ var ContextScannerService = class {
2925
2619
  * Parse a context item from file
2926
2620
  */
2927
2621
  async parseContextItem(filepath, ide, type) {
2928
- const content = await fs6.readFile(filepath, "utf-8");
2929
- const stat = await fs6.stat(filepath);
2622
+ const content = await fs5.readFile(filepath, "utf-8");
2623
+ const stat = await fs5.stat(filepath);
2930
2624
  let frontmatter = {};
2931
2625
  let bodyContent = content;
2932
2626
  try {
@@ -3014,7 +2708,7 @@ var ContextScannerService = class {
3014
2708
  */
3015
2709
  async pathExists(filepath) {
3016
2710
  try {
3017
- await fs6.access(filepath);
2711
+ await fs5.access(filepath);
3018
2712
  return true;
3019
2713
  } catch {
3020
2714
  return false;
@@ -3179,7 +2873,7 @@ function createContextSubcommand() {
3179
2873
  // src/commands/ide/setup.ts
3180
2874
  import { Command as Command8 } from "commander";
3181
2875
  import { checkbox, confirm as confirm2, select } from "@inquirer/prompts";
3182
- import fs7 from "fs/promises";
2876
+ import fs6 from "fs/promises";
3183
2877
  import path3 from "path";
3184
2878
  import { existsSync } from "fs";
3185
2879
 
@@ -3431,13 +3125,13 @@ async function applyGroups(groupKeys, action) {
3431
3125
  return;
3432
3126
  }
3433
3127
  if (!existsSync(vscodeDir)) {
3434
- await fs7.mkdir(vscodeDir, { recursive: true });
3128
+ await fs6.mkdir(vscodeDir, { recursive: true });
3435
3129
  console.log("\u{1F4C1} Created .vscode/ directory");
3436
3130
  }
3437
3131
  let currentSettings = {};
3438
3132
  if (existsSync(settingsPath)) {
3439
3133
  try {
3440
- const content = await fs7.readFile(settingsPath, "utf-8");
3134
+ const content = await fs6.readFile(settingsPath, "utf-8");
3441
3135
  currentSettings = JSON.parse(content);
3442
3136
  console.log("\u{1F4C4} Read current settings from settings.json");
3443
3137
  } catch {
@@ -3477,7 +3171,7 @@ async function applyGroups(groupKeys, action) {
3477
3171
  }
3478
3172
  }
3479
3173
  }
3480
- await fs7.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
3174
+ await fs6.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
3481
3175
  console.log(`
3482
3176
  \u2705 Updated IDE settings at: ${settingsPath}`);
3483
3177
  console.log("\u{1F4A1} Tip: Restart your IDE to apply changes.");
@@ -3498,7 +3192,7 @@ async function resetSettings(groupKeys) {
3498
3192
  return;
3499
3193
  }
3500
3194
  if (groupKeys.length === 0) {
3501
- await fs7.unlink(settingsPath);
3195
+ await fs6.unlink(settingsPath);
3502
3196
  console.log("\n\u2705 Deleted settings.json file");
3503
3197
  } else {
3504
3198
  await applyGroups(groupKeys, "disable");
@@ -3636,8 +3330,8 @@ async function runSync(options) {
3636
3330
  import { Command as Command10 } from "commander";
3637
3331
 
3638
3332
  // src/services/ide-detection.service.ts
3639
- import { promises as fs8 } from "fs";
3640
- import { join as join5 } from "path";
3333
+ import { promises as fs7 } from "fs";
3334
+ import { join as join4 } from "path";
3641
3335
 
3642
3336
  // src/config/ide-formats.ts
3643
3337
  var IDE_FORMATS = {
@@ -3749,7 +3443,7 @@ var IdeDetectionService = class {
3749
3443
  confidence: "low"
3750
3444
  };
3751
3445
  if (ideId === "agentsmd") {
3752
- const agentsPath = join5(this.projectPath, "AGENTS.md");
3446
+ const agentsPath = join4(this.projectPath, "AGENTS.md");
3753
3447
  const exists = await this.pathExists(agentsPath);
3754
3448
  detection.detected = exists;
3755
3449
  if (exists) {
@@ -3761,11 +3455,11 @@ var IdeDetectionService = class {
3761
3455
  return detection;
3762
3456
  }
3763
3457
  if (ideId === "gemini") {
3764
- const geminiPath = join5(this.projectPath, "GEMINI.md");
3458
+ const geminiPath = join4(this.projectPath, "GEMINI.md");
3765
3459
  const exists = await this.pathExists(geminiPath);
3766
3460
  detection.detected = exists;
3767
3461
  if (exists) {
3768
- const agentsPath = join5(this.projectPath, "AGENTS.md");
3462
+ const agentsPath = join4(this.projectPath, "AGENTS.md");
3769
3463
  const agentsExists = await this.pathExists(agentsPath);
3770
3464
  if (agentsExists) {
3771
3465
  const customCount = await this.countAgentsMdCustomSections(agentsPath);
@@ -3777,9 +3471,9 @@ var IdeDetectionService = class {
3777
3471
  return detection;
3778
3472
  }
3779
3473
  if (ideId === "opencode") {
3780
- const agentsPath = join5(this.projectPath, "AGENTS.md");
3474
+ const agentsPath = join4(this.projectPath, "AGENTS.md");
3781
3475
  const hasAgents = await this.pathExists(agentsPath);
3782
- const commandPath = join5(this.projectPath, ".opencode/command");
3476
+ const commandPath = join4(this.projectPath, ".opencode/command");
3783
3477
  const hasCommands = await this.pathExists(commandPath);
3784
3478
  if (hasAgents) {
3785
3479
  const customCount = await this.countAgentsMdCustomSections(agentsPath);
@@ -3794,7 +3488,7 @@ var IdeDetectionService = class {
3794
3488
  detection.confidence = detection.detected ? hasAgents && hasCommands ? "high" : "medium" : "low";
3795
3489
  return detection;
3796
3490
  }
3797
- const rulesPath = join5(this.projectPath, format.rulesPath);
3491
+ const rulesPath = join4(this.projectPath, format.rulesPath);
3798
3492
  const rulesExist = await this.pathExists(rulesPath);
3799
3493
  if (rulesExist) {
3800
3494
  const totalRules = await this.countFiles(rulesPath, format.fileExtension);
@@ -3806,7 +3500,7 @@ var IdeDetectionService = class {
3806
3500
  }
3807
3501
  }
3808
3502
  if (format.workflowsPath) {
3809
- const workflowsPath = join5(this.projectPath, format.workflowsPath);
3503
+ const workflowsPath = join4(this.projectPath, format.workflowsPath);
3810
3504
  const workflowsExist = await this.pathExists(workflowsPath);
3811
3505
  if (workflowsExist) {
3812
3506
  detection.hasWorkflows = true;
@@ -3873,13 +3567,13 @@ var IdeDetectionService = class {
3873
3567
  }
3874
3568
  } else if (ideId === "opencode") {
3875
3569
  const hasAgents = await this.pathExists("AGENTS.md");
3876
- const commandPath = join5(this.projectPath, ".opencode/command");
3570
+ const commandPath = join4(this.projectPath, ".opencode/command");
3877
3571
  const hasCommands = await this.pathExists(commandPath);
3878
3572
  if (hasAgents || hasCommands) {
3879
3573
  detected.push(ideId);
3880
3574
  }
3881
3575
  } else {
3882
- const rulesPath = join5(this.projectPath, format.rulesPath);
3576
+ const rulesPath = join4(this.projectPath, format.rulesPath);
3883
3577
  if (await this.pathExists(rulesPath)) {
3884
3578
  const count = await this.countFiles(rulesPath, format.fileExtension);
3885
3579
  if (count > 0) {
@@ -3912,7 +3606,7 @@ var IdeDetectionService = class {
3912
3606
  */
3913
3607
  async pathExists(path13) {
3914
3608
  try {
3915
- await fs8.access(path13);
3609
+ await fs7.access(path13);
3916
3610
  return true;
3917
3611
  } catch {
3918
3612
  return false;
@@ -3923,7 +3617,7 @@ var IdeDetectionService = class {
3923
3617
  */
3924
3618
  async countFiles(dirPath, extension, excludeKeywords = []) {
3925
3619
  try {
3926
- const entries = await fs8.readdir(dirPath, { withFileTypes: true });
3620
+ const entries = await fs7.readdir(dirPath, { withFileTypes: true });
3927
3621
  return entries.filter((entry) => {
3928
3622
  if (!entry.isFile() || !entry.name.endsWith(extension)) return false;
3929
3623
  if (excludeKeywords.length > 0) {
@@ -3942,7 +3636,7 @@ var IdeDetectionService = class {
3942
3636
  */
3943
3637
  async countAgentsMdCustomSections(filePath) {
3944
3638
  try {
3945
- const content = await fs8.readFile(filePath, "utf-8");
3639
+ const content = await fs7.readFile(filePath, "utf-8");
3946
3640
  const h2Headings = content.match(/^## .+$/gm) || [];
3947
3641
  return h2Headings.filter(
3948
3642
  (h) => !h.toLowerCase().includes(DEFAULT_RULE_KEYWORD)
@@ -3962,7 +3656,7 @@ var IdeDetectionService = class {
3962
3656
  */
3963
3657
  async suggestIdes() {
3964
3658
  const suggestions = [];
3965
- if (await this.pathExists(join5(this.projectPath, ".vscode"))) {
3659
+ if (await this.pathExists(join4(this.projectPath, ".vscode"))) {
3966
3660
  suggestions.push({
3967
3661
  ideId: "cursor",
3968
3662
  name: "Cursor",
@@ -3970,7 +3664,7 @@ var IdeDetectionService = class {
3970
3664
  priority: "high"
3971
3665
  });
3972
3666
  }
3973
- if (await this.pathExists(join5(this.projectPath, "package.json"))) {
3667
+ if (await this.pathExists(join4(this.projectPath, "package.json"))) {
3974
3668
  suggestions.push({
3975
3669
  ideId: "windsurf",
3976
3670
  name: "Windsurf",
@@ -3984,7 +3678,7 @@ var IdeDetectionService = class {
3984
3678
  reason: "Universal format, works with all IDEs",
3985
3679
  priority: "high"
3986
3680
  });
3987
- const hasClaudeConfig = await this.pathExists(join5(this.projectPath, ".claude"));
3681
+ const hasClaudeConfig = await this.pathExists(join4(this.projectPath, ".claude"));
3988
3682
  if (hasClaudeConfig) {
3989
3683
  suggestions.push({
3990
3684
  ideId: "claude",
@@ -3993,7 +3687,7 @@ var IdeDetectionService = class {
3993
3687
  priority: "high"
3994
3688
  });
3995
3689
  }
3996
- const hasOpenCodeConfig = await this.pathExists(join5(this.projectPath, ".opencode"));
3690
+ const hasOpenCodeConfig = await this.pathExists(join4(this.projectPath, ".opencode"));
3997
3691
  if (hasOpenCodeConfig) {
3998
3692
  suggestions.push({
3999
3693
  ideId: "opencode",
@@ -4258,8 +3952,8 @@ function showQuickstart(name) {
4258
3952
  // src/commands/doctor.ts
4259
3953
  import { Command as Command14 } from "commander";
4260
3954
  import chalk10 from "chalk";
4261
- import { promises as fs9 } from "fs";
4262
- import { join as join6 } from "path";
3955
+ import { promises as fs8 } from "fs";
3956
+ import { join as join5 } from "path";
4263
3957
  var CORE_FILES = [
4264
3958
  "workflows/gen-project-overview.md",
4265
3959
  "context/jv-it-context.md",
@@ -4378,9 +4072,9 @@ async function checkAuth(cliName) {
4378
4072
  }
4379
4073
  }
4380
4074
  async function checkCoreFiles(cliName) {
4381
- const jai1Dir = join6(process.cwd(), ".jai1");
4075
+ const jai1Dir = join5(process.cwd(), ".jai1");
4382
4076
  try {
4383
- await fs9.access(jai1Dir);
4077
+ await fs8.access(jai1Dir);
4384
4078
  } catch {
4385
4079
  return {
4386
4080
  name: "Core Package",
@@ -4392,9 +4086,9 @@ async function checkCoreFiles(cliName) {
4392
4086
  const missing = [];
4393
4087
  const found = [];
4394
4088
  for (const file of CORE_FILES) {
4395
- const filePath = join6(jai1Dir, file);
4089
+ const filePath = join5(jai1Dir, file);
4396
4090
  try {
4397
- await fs9.access(filePath);
4091
+ await fs8.access(filePath);
4398
4092
  found.push(file);
4399
4093
  } catch {
4400
4094
  missing.push(file);
@@ -5157,7 +4851,7 @@ var ChatApp = ({ service, initialModel }) => {
5157
4851
 
5158
4852
  // src/server/web-chat-server.ts
5159
4853
  import http from "http";
5160
- import fs11 from "fs";
4854
+ import fs10 from "fs";
5161
4855
  import path5 from "path";
5162
4856
  import { fileURLToPath } from "url";
5163
4857
 
@@ -5291,7 +4985,7 @@ var SessionManager = class {
5291
4985
  };
5292
4986
 
5293
4987
  // src/server/file-service.ts
5294
- import fs10 from "fs";
4988
+ import fs9 from "fs";
5295
4989
  import path4 from "path";
5296
4990
  import ignore from "ignore";
5297
4991
  var ALWAYS_EXCLUDED = [
@@ -5500,8 +5194,8 @@ var FileService = class {
5500
5194
  this.ignoreFilter.add(ALWAYS_EXCLUDED);
5501
5195
  const gitignorePath = path4.join(this.workingDir, ".gitignore");
5502
5196
  try {
5503
- if (fs10.existsSync(gitignorePath)) {
5504
- const content = fs10.readFileSync(gitignorePath, "utf-8");
5197
+ if (fs9.existsSync(gitignorePath)) {
5198
+ const content = fs9.readFileSync(gitignorePath, "utf-8");
5505
5199
  this.ignoreFilter.add(content);
5506
5200
  }
5507
5201
  } catch {
@@ -5542,7 +5236,7 @@ var FileService = class {
5542
5236
  const searchDir = async (dirPath, depth = 0) => {
5543
5237
  if (depth > 10 || results.length >= maxResults) return;
5544
5238
  try {
5545
- const entries = await fs10.promises.readdir(dirPath, { withFileTypes: true });
5239
+ const entries = await fs9.promises.readdir(dirPath, { withFileTypes: true });
5546
5240
  for (const entry of entries) {
5547
5241
  if (results.length >= maxResults) break;
5548
5242
  const entryPath = path4.join(dirPath, entry.name);
@@ -5552,7 +5246,7 @@ var FileService = class {
5552
5246
  if (normalizedRelPath.includes(normalizedQuery)) {
5553
5247
  if (entry.isFile() && isTextFile(entry.name)) {
5554
5248
  try {
5555
- const stat = await fs10.promises.stat(entryPath);
5249
+ const stat = await fs9.promises.stat(entryPath);
5556
5250
  results.push({
5557
5251
  path: relativePath,
5558
5252
  name: entry.name,
@@ -5592,14 +5286,14 @@ var FileService = class {
5592
5286
  if (!isTextFile(absolutePath)) {
5593
5287
  throw new Error("Only text files can be read");
5594
5288
  }
5595
- const stat = await fs10.promises.stat(absolutePath);
5289
+ const stat = await fs9.promises.stat(absolutePath);
5596
5290
  if (!stat.isFile()) {
5597
5291
  throw new Error("Path is not a file");
5598
5292
  }
5599
5293
  if (stat.size > this.maxFileSize) {
5600
5294
  throw new Error(`File exceeds maximum size of ${Math.round(this.maxFileSize / 1024)}KB`);
5601
5295
  }
5602
- const content = await fs10.promises.readFile(absolutePath, "utf-8");
5296
+ const content = await fs9.promises.readFile(absolutePath, "utf-8");
5603
5297
  return {
5604
5298
  path: normalizedRelPath,
5605
5299
  content,
@@ -5729,14 +5423,14 @@ function createWebChatServer(options) {
5729
5423
  return;
5730
5424
  }
5731
5425
  try {
5732
- const stat = await fs11.promises.stat(fullPath);
5426
+ const stat = await fs10.promises.stat(fullPath);
5733
5427
  if (!stat.isFile()) {
5734
5428
  sendError(res, 404, "ERR-WC-007", "Not found");
5735
5429
  return;
5736
5430
  }
5737
5431
  const ext = path5.extname(fullPath);
5738
5432
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
5739
- const content = await fs11.promises.readFile(fullPath);
5433
+ const content = await fs10.promises.readFile(fullPath);
5740
5434
  res.writeHead(200, { "Content-Type": contentType });
5741
5435
  res.end(content);
5742
5436
  } catch (error) {
@@ -6310,7 +6004,7 @@ function createStatsCommand() {
6310
6004
  import { Command as Command18 } from "commander";
6311
6005
 
6312
6006
  // src/services/translation.service.ts
6313
- import { promises as fs12 } from "fs";
6007
+ import { promises as fs11 } from "fs";
6314
6008
  import path6 from "path";
6315
6009
  import pLimit from "p-limit";
6316
6010
  import pRetry from "p-retry";
@@ -6329,7 +6023,7 @@ var TranslationService = class {
6329
6023
  */
6330
6024
  async detectInputType(input5) {
6331
6025
  try {
6332
- const stat = await fs12.stat(input5);
6026
+ const stat = await fs11.stat(input5);
6333
6027
  if (stat.isDirectory()) return "folder";
6334
6028
  if (stat.isFile()) return "file";
6335
6029
  } catch {
@@ -6366,13 +6060,13 @@ var TranslationService = class {
6366
6060
  */
6367
6061
  async translateFile(filePath) {
6368
6062
  try {
6369
- const content = await fs12.readFile(filePath, "utf-8");
6063
+ const content = await fs11.readFile(filePath, "utf-8");
6370
6064
  const ext = path6.extname(filePath).toLowerCase();
6371
6065
  const fileType = this.getFileType(ext);
6372
6066
  const translatedContent = await this.translateWithRetry(content, fileType);
6373
6067
  const outputPath = this.generateOutputPath(filePath);
6374
6068
  if (!this.options.dryRun) {
6375
- await fs12.writeFile(outputPath, translatedContent, "utf-8");
6069
+ await fs11.writeFile(outputPath, translatedContent, "utf-8");
6376
6070
  }
6377
6071
  return {
6378
6072
  inputPath: filePath,
@@ -6437,7 +6131,7 @@ var TranslationService = class {
6437
6131
  * Discover translatable files in folder recursively
6438
6132
  */
6439
6133
  async discoverFiles(folderPath) {
6440
- const entries = await fs12.readdir(folderPath, { withFileTypes: true });
6134
+ const entries = await fs11.readdir(folderPath, { withFileTypes: true });
6441
6135
  const files = [];
6442
6136
  for (const entry of entries) {
6443
6137
  const fullPath = path6.join(folderPath, entry.name);
@@ -6970,20 +6664,20 @@ function createImageCommand() {
6970
6664
  import { Command as Command24 } from "commander";
6971
6665
  import { select as select2, input, confirm as confirm5 } from "@inquirer/prompts";
6972
6666
  import os from "os";
6973
- import { promises as fs13 } from "fs";
6974
- import { join as join7 } from "path";
6667
+ import { promises as fs12 } from "fs";
6668
+ import { join as join6 } from "path";
6975
6669
  async function collectContext() {
6976
6670
  const context = {
6977
6671
  os: `${os.platform()} ${os.release()}`
6978
6672
  };
6979
6673
  try {
6980
6674
  const packageJsonPath = new URL("../../package.json", import.meta.url);
6981
- const packageJson = JSON.parse(await fs13.readFile(packageJsonPath, "utf-8"));
6675
+ const packageJson = JSON.parse(await fs12.readFile(packageJsonPath, "utf-8"));
6982
6676
  context.cli_version = packageJson.version;
6983
6677
  } catch {
6984
6678
  }
6985
6679
  try {
6986
- const projectPackageJson = await fs13.readFile(join7(process.cwd(), "package.json"), "utf-8");
6680
+ const projectPackageJson = await fs12.readFile(join6(process.cwd(), "package.json"), "utf-8");
6987
6681
  const projectData = JSON.parse(projectPackageJson);
6988
6682
  context.project_name = projectData.name;
6989
6683
  } catch {
@@ -7186,8 +6880,8 @@ function createFeedbackCommand() {
7186
6880
  import { Command as Command25 } from "commander";
7187
6881
  import { confirm as confirm6 } from "@inquirer/prompts";
7188
6882
  import os2 from "os";
7189
- import { promises as fs14 } from "fs";
7190
- import { basename as basename2, join as join8 } from "path";
6883
+ import { promises as fs13 } from "fs";
6884
+ import { basename as basename2, join as join7 } from "path";
7191
6885
  import { version as nodeVersion2 } from "process";
7192
6886
  function createClientInfoCommand() {
7193
6887
  const cmd = new Command25("client-info").description("T\u1EA1o th\xF4ng tin client \u0111\u1EC3 g\u1EEDi \u0111\u1ED9i ph\xE1t tri\u1EC3n jai1").option("--json", "Output as JSON").option("--full", "Include component list (top 20)").option("--submit", "Submit client info to Jai1 feedback endpoint").option("--message <text>", "Additional message when submitting").option("--show-paths", "Include full project path").action(async (options) => {
@@ -7302,9 +6996,9 @@ async function collectClientInfo(config, options) {
7302
6996
  return payload;
7303
6997
  }
7304
6998
  async function hasProjectJai1() {
7305
- const projectJai1 = join8(process.cwd(), ".jai1");
6999
+ const projectJai1 = join7(process.cwd(), ".jai1");
7306
7000
  try {
7307
- await fs14.access(projectJai1);
7001
+ await fs13.access(projectJai1);
7308
7002
  return true;
7309
7003
  } catch {
7310
7004
  return false;
@@ -9653,7 +9347,7 @@ var HttpView = () => {
9653
9347
  import React23, { useState as useState17 } from "react";
9654
9348
  import { Box as Box19, Text as Text20, useInput as useInput15 } from "ink";
9655
9349
  import TextInput14 from "ink-text-input";
9656
- import * as fs15 from "fs";
9350
+ import * as fs14 from "fs";
9657
9351
  import * as path7 from "path";
9658
9352
  var MarkdownView = () => {
9659
9353
  const [filePath, setFilePath] = useState17("");
@@ -9675,11 +9369,11 @@ var MarkdownView = () => {
9675
9369
  try {
9676
9370
  setError("");
9677
9371
  const resolvedPath = path7.resolve(filePath);
9678
- if (!fs15.existsSync(resolvedPath)) {
9372
+ if (!fs14.existsSync(resolvedPath)) {
9679
9373
  setError(`File not found: ${resolvedPath}`);
9680
9374
  return;
9681
9375
  }
9682
- let fileContent = fs15.readFileSync(resolvedPath, "utf-8");
9376
+ let fileContent = fs14.readFileSync(resolvedPath, "utf-8");
9683
9377
  fileContent = fileContent.replace(/```mermaid\n([\s\S]*?)```/g, (match, code) => {
9684
9378
  return `
9685
9379
  \u{1F3A8} **Mermaid Diagram**
@@ -9989,7 +9683,7 @@ import Table4 from "cli-table3";
9989
9683
  import ora from "ora";
9990
9684
 
9991
9685
  // src/services/deps-detector.service.ts
9992
- import * as fs16 from "fs/promises";
9686
+ import * as fs15 from "fs/promises";
9993
9687
  import * as path8 from "path";
9994
9688
  var DepsDetectorService = class {
9995
9689
  /**
@@ -10057,7 +9751,7 @@ var DepsDetectorService = class {
10057
9751
  async detectLaravel(cwd) {
10058
9752
  try {
10059
9753
  const composerPath = path8.join(cwd, "composer.json");
10060
- const content = await fs16.readFile(composerPath, "utf-8");
9754
+ const content = await fs15.readFile(composerPath, "utf-8");
10061
9755
  const composerJson = JSON.parse(content);
10062
9756
  const deps = {
10063
9757
  ...composerJson.require,
@@ -10073,7 +9767,7 @@ var DepsDetectorService = class {
10073
9767
  */
10074
9768
  async fileExists(cwd, filename) {
10075
9769
  try {
10076
- await fs16.access(path8.join(cwd, filename));
9770
+ await fs15.access(path8.join(cwd, filename));
10077
9771
  return true;
10078
9772
  } catch {
10079
9773
  return false;
@@ -10082,7 +9776,7 @@ var DepsDetectorService = class {
10082
9776
  };
10083
9777
 
10084
9778
  // src/services/deps.service.ts
10085
- import { promises as fs17 } from "fs";
9779
+ import { promises as fs16 } from "fs";
10086
9780
  import path9 from "path";
10087
9781
  import { execSync } from "child_process";
10088
9782
  import pLimit2 from "p-limit";
@@ -10119,7 +9813,7 @@ var DepsService = class {
10119
9813
  async readPackageJson(cwd) {
10120
9814
  const pkgPath = path9.join(cwd, "package.json");
10121
9815
  try {
10122
- const content = await fs17.readFile(pkgPath, "utf-8");
9816
+ const content = await fs16.readFile(pkgPath, "utf-8");
10123
9817
  return JSON.parse(content);
10124
9818
  } catch (error) {
10125
9819
  if (error.code === "ENOENT") {
@@ -10210,7 +9904,7 @@ var DepsService = class {
10210
9904
  ];
10211
9905
  for (const { file, pm } of lockFiles) {
10212
9906
  try {
10213
- await fs17.access(path9.join(cwd, file));
9907
+ await fs16.access(path9.join(cwd, file));
10214
9908
  return pm;
10215
9909
  } catch {
10216
9910
  }
@@ -10281,7 +9975,7 @@ var DepsService = class {
10281
9975
 
10282
9976
  // src/services/deps-php.service.ts
10283
9977
  import { execSync as execSync2 } from "child_process";
10284
- import { promises as fs18 } from "fs";
9978
+ import { promises as fs17 } from "fs";
10285
9979
  import path10 from "path";
10286
9980
  var LARAVEL_PROTECTED_PACKAGES = /^laravel\//;
10287
9981
  var DepsPhpService = class {
@@ -10341,7 +10035,7 @@ var DepsPhpService = class {
10341
10035
  async readComposerJson(cwd) {
10342
10036
  const composerPath = path10.join(cwd, "composer.json");
10343
10037
  try {
10344
- const content = await fs18.readFile(composerPath, "utf-8");
10038
+ const content = await fs17.readFile(composerPath, "utf-8");
10345
10039
  return JSON.parse(content);
10346
10040
  } catch (error) {
10347
10041
  if (error.code === "ENOENT") {
@@ -10403,7 +10097,7 @@ var DepsPhpService = class {
10403
10097
 
10404
10098
  // src/services/deps-python.service.ts
10405
10099
  import { execSync as execSync3 } from "child_process";
10406
- import { promises as fs19 } from "fs";
10100
+ import { promises as fs18 } from "fs";
10407
10101
  import path11 from "path";
10408
10102
  import pLimit3 from "p-limit";
10409
10103
  var DepsPythonService = class {
@@ -10454,7 +10148,7 @@ var DepsPythonService = class {
10454
10148
  async checkPip(cwd, onProgress) {
10455
10149
  const requirementsPath = path11.join(cwd, "requirements.txt");
10456
10150
  try {
10457
- const content = await fs19.readFile(requirementsPath, "utf-8");
10151
+ const content = await fs18.readFile(requirementsPath, "utf-8");
10458
10152
  const packages = this.parseRequirementsTxt(content);
10459
10153
  return await this.fetchBulkVersions(packages, onProgress);
10460
10154
  } catch (error) {
@@ -10566,7 +10260,7 @@ var DepsPythonService = class {
10566
10260
  }
10567
10261
  async fileExists(cwd, filename) {
10568
10262
  try {
10569
- await fs19.access(path11.join(cwd, filename));
10263
+ await fs18.access(path11.join(cwd, filename));
10570
10264
  return true;
10571
10265
  } catch {
10572
10266
  return false;
@@ -11414,8 +11108,8 @@ import chalk30 from "chalk";
11414
11108
  import Table6 from "cli-table3";
11415
11109
 
11416
11110
  // src/services/starter-kit.service.ts
11417
- import { promises as fs20 } from "fs";
11418
- import { join as join10 } from "path";
11111
+ import { promises as fs19 } from "fs";
11112
+ import { join as join9 } from "path";
11419
11113
  import AdmZip from "adm-zip";
11420
11114
  var StarterKitService = class {
11421
11115
  /**
@@ -11462,17 +11156,17 @@ var StarterKitService = class {
11462
11156
  throw new NetworkError(`Failed to download kit: HTTP ${response.status}`);
11463
11157
  }
11464
11158
  if (onProgress) onProgress(30);
11465
- const tmpDir = join10(process.env.TMPDIR || "/tmp", "jai1-kits");
11466
- await fs20.mkdir(tmpDir, { recursive: true });
11467
- const tmpFile = join10(tmpDir, `${slug}.zip`);
11159
+ const tmpDir = join9(process.env.TMPDIR || "/tmp", "jai1-kits");
11160
+ await fs19.mkdir(tmpDir, { recursive: true });
11161
+ const tmpFile = join9(tmpDir, `${slug}.zip`);
11468
11162
  const buffer = await response.arrayBuffer();
11469
- await fs20.writeFile(tmpFile, Buffer.from(buffer));
11163
+ await fs19.writeFile(tmpFile, Buffer.from(buffer));
11470
11164
  if (onProgress) onProgress(60);
11471
11165
  const zip = new AdmZip(tmpFile);
11472
- await fs20.mkdir(targetDir, { recursive: true });
11166
+ await fs19.mkdir(targetDir, { recursive: true });
11473
11167
  zip.extractAllTo(targetDir, true);
11474
11168
  if (onProgress) onProgress(100);
11475
- await fs20.unlink(tmpFile);
11169
+ await fs19.unlink(tmpFile);
11476
11170
  }
11477
11171
  };
11478
11172
 
@@ -11596,8 +11290,8 @@ Post-Init Commands:`);
11596
11290
 
11597
11291
  // src/commands/kit/create.ts
11598
11292
  import { Command as Command60 } from "commander";
11599
- import { promises as fs21 } from "fs";
11600
- import { join as join11 } from "path";
11293
+ import { promises as fs20 } from "fs";
11294
+ import { join as join10 } from "path";
11601
11295
  import { select as select3, input as input2, checkbox as checkbox4 } from "@inquirer/prompts";
11602
11296
  import { execa as execa2 } from "execa";
11603
11297
 
@@ -11660,9 +11354,9 @@ function createKitCreateCommand() {
11660
11354
  }
11661
11355
  }
11662
11356
  }
11663
- const targetDir = directory || join11(process.cwd(), options.name || slug);
11357
+ const targetDir = directory || join10(process.cwd(), options.name || slug);
11664
11358
  try {
11665
- await fs21.access(targetDir);
11359
+ await fs20.access(targetDir);
11666
11360
  throw new Error(`Directory already exists: ${targetDir}`);
11667
11361
  } catch (error) {
11668
11362
  if (error.code !== "ENOENT") {
@@ -11725,7 +11419,7 @@ function createKitCreateCommand() {
11725
11419
  );
11726
11420
  for (const filepath of expandedPaths) {
11727
11421
  console.log(` \u{1F4E5} Installing ${filepath}...`);
11728
- await componentsService.install(config, filepath, join11(targetDir, ".jai1"));
11422
+ await componentsService.install(config, filepath, join10(targetDir, ".jai1"));
11729
11423
  }
11730
11424
  console.log(" \u2713 Framework components applied");
11731
11425
  }
@@ -11787,7 +11481,7 @@ function createKitCreateCommand() {
11787
11481
  async function applyVariableSubstitution(dir, variables) {
11788
11482
  const files = await getAllFiles(dir);
11789
11483
  for (const file of files) {
11790
- let content = await fs21.readFile(file, "utf-8");
11484
+ let content = await fs20.readFile(file, "utf-8");
11791
11485
  let modified = false;
11792
11486
  for (const [key, value] of Object.entries(variables)) {
11793
11487
  const regex = new RegExp(`\\{\\{${key}\\}\\}`, "g");
@@ -11797,15 +11491,15 @@ async function applyVariableSubstitution(dir, variables) {
11797
11491
  }
11798
11492
  }
11799
11493
  if (modified) {
11800
- await fs21.writeFile(file, content, "utf-8");
11494
+ await fs20.writeFile(file, content, "utf-8");
11801
11495
  }
11802
11496
  }
11803
11497
  }
11804
11498
  async function getAllFiles(dir) {
11805
11499
  const files = [];
11806
- const entries = await fs21.readdir(dir, { withFileTypes: true });
11500
+ const entries = await fs20.readdir(dir, { withFileTypes: true });
11807
11501
  for (const entry of entries) {
11808
- const fullPath = join11(dir, entry.name);
11502
+ const fullPath = join10(dir, entry.name);
11809
11503
  if (entry.isDirectory()) {
11810
11504
  if (!entry.name.startsWith(".") && entry.name !== "node_modules") {
11811
11505
  files.push(...await getAllFiles(fullPath));
@@ -11920,28 +11614,28 @@ function createRulesListCommand() {
11920
11614
 
11921
11615
  // src/commands/rules/init.ts
11922
11616
  import { Command as Command63 } from "commander";
11923
- import { promises as fs23 } from "fs";
11924
- import { join as join13 } from "path";
11617
+ import { promises as fs22 } from "fs";
11618
+ import { join as join12 } from "path";
11925
11619
  import { select as select4, confirm as confirm10 } from "@inquirer/prompts";
11926
11620
 
11927
11621
  // src/services/project-config.service.ts
11928
- import { promises as fs22 } from "fs";
11929
- import { join as join12 } from "path";
11622
+ import { promises as fs21 } from "fs";
11623
+ import { join as join11 } from "path";
11930
11624
  var ProjectConfigService = class {
11931
11625
  projectRoot;
11932
11626
  configDir;
11933
11627
  configPath;
11934
11628
  constructor(projectRoot = process.cwd()) {
11935
11629
  this.projectRoot = projectRoot;
11936
- this.configDir = join12(this.projectRoot, ".jai1");
11937
- this.configPath = join12(this.configDir, "project.json");
11630
+ this.configDir = join11(this.projectRoot, ".jai1");
11631
+ this.configPath = join11(this.configDir, "project.json");
11938
11632
  }
11939
11633
  /**
11940
11634
  * Check if config file exists
11941
11635
  */
11942
11636
  async exists() {
11943
11637
  try {
11944
- await fs22.access(this.configPath);
11638
+ await fs21.access(this.configPath);
11945
11639
  return true;
11946
11640
  } catch {
11947
11641
  return false;
@@ -11956,7 +11650,7 @@ var ProjectConfigService = class {
11956
11650
  return null;
11957
11651
  }
11958
11652
  try {
11959
- const content = await fs22.readFile(this.configPath, "utf-8");
11653
+ const content = await fs21.readFile(this.configPath, "utf-8");
11960
11654
  return JSON.parse(content);
11961
11655
  } catch (error) {
11962
11656
  throw new Error(
@@ -11970,8 +11664,8 @@ var ProjectConfigService = class {
11970
11664
  */
11971
11665
  async save(config) {
11972
11666
  try {
11973
- await fs22.mkdir(this.configDir, { recursive: true });
11974
- await fs22.writeFile(this.configPath, JSON.stringify(config, null, 2), "utf-8");
11667
+ await fs21.mkdir(this.configDir, { recursive: true });
11668
+ await fs21.writeFile(this.configPath, JSON.stringify(config, null, 2), "utf-8");
11975
11669
  } catch (error) {
11976
11670
  throw new Error(
11977
11671
  `Failed to save project config: ${error instanceof Error ? error.message : String(error)}`
@@ -12140,11 +11834,11 @@ function createRulesInitCommand() {
12140
11834
  });
12141
11835
  }
12142
11836
  async function applyCursorFormat(bundle) {
12143
- const rulesDir = join13(process.cwd(), ".cursor", "rules");
12144
- await fs23.mkdir(rulesDir, { recursive: true });
11837
+ const rulesDir = join12(process.cwd(), ".cursor", "rules");
11838
+ await fs22.mkdir(rulesDir, { recursive: true });
12145
11839
  for (const [filename, content] of Object.entries(bundle.files)) {
12146
- const filePath = join13(rulesDir, filename);
12147
- await fs23.writeFile(filePath, content, "utf-8");
11840
+ const filePath = join12(rulesDir, filename);
11841
+ await fs22.writeFile(filePath, content, "utf-8");
12148
11842
  console.log(`\u2713 Created .cursor/rules/${filename}`);
12149
11843
  }
12150
11844
  }
@@ -12171,14 +11865,14 @@ async function applyAgentsMdFormat(bundle) {
12171
11865
  }
12172
11866
  }
12173
11867
  const agentsMd = sections.join("\n");
12174
- await fs23.writeFile("AGENTS.md", agentsMd, "utf-8");
11868
+ await fs22.writeFile("AGENTS.md", agentsMd, "utf-8");
12175
11869
  console.log("\u2713 Created AGENTS.md");
12176
11870
  }
12177
11871
 
12178
11872
  // src/commands/rules/apply.ts
12179
11873
  import { Command as Command64 } from "commander";
12180
- import { promises as fs25 } from "fs";
12181
- import { join as join15 } from "path";
11874
+ import { promises as fs24 } from "fs";
11875
+ import { join as join14 } from "path";
12182
11876
  import { search, confirm as confirm11, checkbox as checkbox5 } from "@inquirer/prompts";
12183
11877
 
12184
11878
  // src/services/rules-generator.service.ts
@@ -12469,8 +12163,8 @@ Follow all instructions and patterns defined in AGENTS.md above.
12469
12163
  };
12470
12164
 
12471
12165
  // src/services/backup.service.ts
12472
- import { promises as fs24 } from "fs";
12473
- import { join as join14, dirname } from "path";
12166
+ import { promises as fs23 } from "fs";
12167
+ import { join as join13, dirname } from "path";
12474
12168
  var BackupService = class {
12475
12169
  backupDir = ".jai1/backups";
12476
12170
  /**
@@ -12478,7 +12172,7 @@ var BackupService = class {
12478
12172
  */
12479
12173
  async createBackup(ides, presetSlug) {
12480
12174
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
12481
- const backupPath = join14(this.backupDir, timestamp);
12175
+ const backupPath = join13(this.backupDir, timestamp);
12482
12176
  const backedUpFiles = [];
12483
12177
  let hasContent = false;
12484
12178
  for (const ideId of ides) {
@@ -12487,7 +12181,7 @@ var BackupService = class {
12487
12181
  console.warn(`Unknown IDE format: ${ideId}, skipping backup`);
12488
12182
  continue;
12489
12183
  }
12490
- const rulesPath = format.rulesPath === "." ? process.cwd() : join14(process.cwd(), format.rulesPath);
12184
+ const rulesPath = format.rulesPath === "." ? process.cwd() : join13(process.cwd(), format.rulesPath);
12491
12185
  try {
12492
12186
  const exists = await this.pathExists(rulesPath);
12493
12187
  if (!exists) {
@@ -12500,19 +12194,19 @@ var BackupService = class {
12500
12194
  await this.backupSingleFile("GEMINI.md", backupPath, ideId, backedUpFiles);
12501
12195
  hasContent = true;
12502
12196
  } else {
12503
- const stats = await fs24.stat(rulesPath);
12197
+ const stats = await fs23.stat(rulesPath);
12504
12198
  if (stats.isDirectory()) {
12505
- const files = await fs24.readdir(rulesPath);
12199
+ const files = await fs23.readdir(rulesPath);
12506
12200
  for (const file of files) {
12507
12201
  if (file.endsWith(format.fileExtension)) {
12508
- const originalPath = join14(rulesPath, file);
12509
- const relativePath = join14(format.rulesPath, file);
12510
- const destPath = join14(backupPath, ideId, file);
12511
- await fs24.mkdir(dirname(destPath), { recursive: true });
12512
- await fs24.copyFile(originalPath, destPath);
12202
+ const originalPath = join13(rulesPath, file);
12203
+ const relativePath = join13(format.rulesPath, file);
12204
+ const destPath = join13(backupPath, ideId, file);
12205
+ await fs23.mkdir(dirname(destPath), { recursive: true });
12206
+ await fs23.copyFile(originalPath, destPath);
12513
12207
  backedUpFiles.push({
12514
12208
  originalPath: relativePath,
12515
- backupPath: join14(ideId, file),
12209
+ backupPath: join13(ideId, file),
12516
12210
  ide: ideId
12517
12211
  });
12518
12212
  hasContent = true;
@@ -12533,9 +12227,9 @@ var BackupService = class {
12533
12227
  ides,
12534
12228
  files: backedUpFiles
12535
12229
  };
12536
- await fs24.mkdir(backupPath, { recursive: true });
12537
- await fs24.writeFile(
12538
- join14(backupPath, "metadata.json"),
12230
+ await fs23.mkdir(backupPath, { recursive: true });
12231
+ await fs23.writeFile(
12232
+ join13(backupPath, "metadata.json"),
12539
12233
  JSON.stringify(metadata, null, 2),
12540
12234
  "utf-8"
12541
12235
  );
@@ -12545,18 +12239,18 @@ var BackupService = class {
12545
12239
  * Backup a single file (for AGENTS.md, GEMINI.md)
12546
12240
  */
12547
12241
  async backupSingleFile(filename, backupPath, ideId, backedUpFiles) {
12548
- const originalPath = join14(process.cwd(), filename);
12242
+ const originalPath = join13(process.cwd(), filename);
12549
12243
  try {
12550
12244
  const exists = await this.pathExists(originalPath);
12551
12245
  if (!exists) {
12552
12246
  return;
12553
12247
  }
12554
- const destPath = join14(backupPath, ideId, filename);
12555
- await fs24.mkdir(dirname(destPath), { recursive: true });
12556
- await fs24.copyFile(originalPath, destPath);
12248
+ const destPath = join13(backupPath, ideId, filename);
12249
+ await fs23.mkdir(dirname(destPath), { recursive: true });
12250
+ await fs23.copyFile(originalPath, destPath);
12557
12251
  backedUpFiles.push({
12558
12252
  originalPath: filename,
12559
- backupPath: join14(ideId, filename),
12253
+ backupPath: join13(ideId, filename),
12560
12254
  ide: ideId
12561
12255
  });
12562
12256
  } catch (error) {
@@ -12566,16 +12260,16 @@ var BackupService = class {
12566
12260
  * Restore files from a backup
12567
12261
  */
12568
12262
  async restoreBackup(backupPath) {
12569
- const metadataPath = join14(backupPath, "metadata.json");
12570
- const metadataContent = await fs24.readFile(metadataPath, "utf-8");
12263
+ const metadataPath = join13(backupPath, "metadata.json");
12264
+ const metadataContent = await fs23.readFile(metadataPath, "utf-8");
12571
12265
  const metadata = JSON.parse(metadataContent);
12572
12266
  console.log(`
12573
12267
  Restoring backup from ${metadata.timestamp}...`);
12574
12268
  for (const file of metadata.files) {
12575
- const sourcePath = join14(backupPath, file.backupPath);
12576
- const destPath = join14(process.cwd(), file.originalPath);
12577
- await fs24.mkdir(dirname(destPath), { recursive: true });
12578
- await fs24.copyFile(sourcePath, destPath);
12269
+ const sourcePath = join13(backupPath, file.backupPath);
12270
+ const destPath = join13(process.cwd(), file.originalPath);
12271
+ await fs23.mkdir(dirname(destPath), { recursive: true });
12272
+ await fs23.copyFile(sourcePath, destPath);
12579
12273
  console.log(`\u2713 Restored ${file.originalPath}`);
12580
12274
  }
12581
12275
  console.log("\n\u2705 Backup restored successfully!");
@@ -12585,18 +12279,18 @@ Restoring backup from ${metadata.timestamp}...`);
12585
12279
  */
12586
12280
  async listBackups() {
12587
12281
  try {
12588
- const backupDirPath = join14(process.cwd(), this.backupDir);
12282
+ const backupDirPath = join13(process.cwd(), this.backupDir);
12589
12283
  const exists = await this.pathExists(backupDirPath);
12590
12284
  if (!exists) {
12591
12285
  return [];
12592
12286
  }
12593
- const entries = await fs24.readdir(backupDirPath, { withFileTypes: true });
12287
+ const entries = await fs23.readdir(backupDirPath, { withFileTypes: true });
12594
12288
  const backups = [];
12595
12289
  for (const entry of entries) {
12596
12290
  if (entry.isDirectory()) {
12597
- const metadataPath = join14(backupDirPath, entry.name, "metadata.json");
12291
+ const metadataPath = join13(backupDirPath, entry.name, "metadata.json");
12598
12292
  try {
12599
- const metadataContent = await fs24.readFile(metadataPath, "utf-8");
12293
+ const metadataContent = await fs23.readFile(metadataPath, "utf-8");
12600
12294
  const metadata = JSON.parse(metadataContent);
12601
12295
  backups.push(metadata);
12602
12296
  } catch {
@@ -12613,7 +12307,7 @@ Restoring backup from ${metadata.timestamp}...`);
12613
12307
  * Delete a specific backup
12614
12308
  */
12615
12309
  async deleteBackup(timestamp) {
12616
- const backupPath = join14(process.cwd(), this.backupDir, timestamp);
12310
+ const backupPath = join13(process.cwd(), this.backupDir, timestamp);
12617
12311
  await this.deleteDirectory(backupPath);
12618
12312
  }
12619
12313
  /**
@@ -12645,7 +12339,7 @@ Restoring backup from ${metadata.timestamp}...`);
12645
12339
  */
12646
12340
  async pathExists(path13) {
12647
12341
  try {
12648
- await fs24.access(path13);
12342
+ await fs23.access(path13);
12649
12343
  return true;
12650
12344
  } catch {
12651
12345
  return false;
@@ -12660,16 +12354,16 @@ Restoring backup from ${metadata.timestamp}...`);
12660
12354
  if (!exists) {
12661
12355
  return;
12662
12356
  }
12663
- const entries = await fs24.readdir(path13, { withFileTypes: true });
12357
+ const entries = await fs23.readdir(path13, { withFileTypes: true });
12664
12358
  for (const entry of entries) {
12665
- const fullPath = join14(path13, entry.name);
12359
+ const fullPath = join13(path13, entry.name);
12666
12360
  if (entry.isDirectory()) {
12667
12361
  await this.deleteDirectory(fullPath);
12668
12362
  } else {
12669
- await fs24.unlink(fullPath);
12363
+ await fs23.unlink(fullPath);
12670
12364
  }
12671
12365
  }
12672
- await fs24.rmdir(path13);
12366
+ await fs23.rmdir(path13);
12673
12367
  } catch (error) {
12674
12368
  }
12675
12369
  }
@@ -12677,14 +12371,14 @@ Restoring backup from ${metadata.timestamp}...`);
12677
12371
  * Get backup directory path
12678
12372
  */
12679
12373
  getBackupDir() {
12680
- return join14(process.cwd(), this.backupDir);
12374
+ return join13(process.cwd(), this.backupDir);
12681
12375
  }
12682
12376
  /**
12683
12377
  * Ensure backup directory exists
12684
12378
  */
12685
12379
  async ensureBackupDir() {
12686
- const backupDirPath = join14(process.cwd(), this.backupDir);
12687
- await fs24.mkdir(backupDirPath, { recursive: true });
12380
+ const backupDirPath = join13(process.cwd(), this.backupDir);
12381
+ await fs23.mkdir(backupDirPath, { recursive: true });
12688
12382
  }
12689
12383
  };
12690
12384
 
@@ -12856,21 +12550,21 @@ function createRulesApplyCommand() {
12856
12550
  }
12857
12551
  }
12858
12552
  console.log("\n\u{1F4DD} Applying preset...\n");
12859
- const rulePresetDir = join15(process.cwd(), ".jai1", "rule-preset");
12553
+ const rulePresetDir = join14(process.cwd(), ".jai1", "rule-preset");
12860
12554
  try {
12861
- await fs25.rm(rulePresetDir, { recursive: true, force: true });
12555
+ await fs24.rm(rulePresetDir, { recursive: true, force: true });
12862
12556
  } catch {
12863
12557
  }
12864
- await fs25.mkdir(rulePresetDir, { recursive: true });
12865
- await fs25.writeFile(
12866
- join15(rulePresetDir, "preset.json"),
12558
+ await fs24.mkdir(rulePresetDir, { recursive: true });
12559
+ await fs24.writeFile(
12560
+ join14(rulePresetDir, "preset.json"),
12867
12561
  JSON.stringify(bundle.preset, null, 2),
12868
12562
  "utf-8"
12869
12563
  );
12870
12564
  for (const [filename, content] of Object.entries(bundle.files)) {
12871
- const filePath = join15(rulePresetDir, filename);
12872
- await fs25.mkdir(join15(filePath, ".."), { recursive: true });
12873
- await fs25.writeFile(filePath, content, "utf-8");
12565
+ const filePath = join14(rulePresetDir, filename);
12566
+ await fs24.mkdir(join14(filePath, ".."), { recursive: true });
12567
+ await fs24.writeFile(filePath, content, "utf-8");
12874
12568
  }
12875
12569
  console.log(`\u2713 Saved preset to .jai1/rule-preset/`);
12876
12570
  const allGeneratedFiles = [];
@@ -12878,9 +12572,9 @@ function createRulesApplyCommand() {
12878
12572
  try {
12879
12573
  const files = generatorService.generateForIde(bundle, ideId);
12880
12574
  for (const file of files) {
12881
- const fullPath = join15(process.cwd(), file.path);
12882
- await fs25.mkdir(join15(fullPath, ".."), { recursive: true });
12883
- await fs25.writeFile(fullPath, file.content, "utf-8");
12575
+ const fullPath = join14(process.cwd(), file.path);
12576
+ await fs24.mkdir(join14(fullPath, ".."), { recursive: true });
12577
+ await fs24.writeFile(fullPath, file.content, "utf-8");
12884
12578
  console.log(`\u2713 [${ideId}] ${file.path}`);
12885
12579
  allGeneratedFiles.push({
12886
12580
  ide: ideId,
@@ -12986,7 +12680,7 @@ function createRulesApplyCommand() {
12986
12680
 
12987
12681
  // src/commands/rules/restore.ts
12988
12682
  import { Command as Command65 } from "commander";
12989
- import { join as join16 } from "path";
12683
+ import { join as join15 } from "path";
12990
12684
  import { select as select5, confirm as confirm12 } from "@inquirer/prompts";
12991
12685
  function createRulesRestoreCommand() {
12992
12686
  return new Command65("restore").description("Restore rules from a backup").option("--latest", "Restore the most recent backup").option("-y, --yes", "Skip confirmation").action(async (options) => {
@@ -13033,7 +12727,7 @@ function createRulesRestoreCommand() {
13033
12727
  }
13034
12728
  console.log("\n\u{1F504} Restoring backup...\n");
13035
12729
  try {
13036
- const backupPath = join16(backupService.getBackupDir(), selectedBackup.timestamp);
12730
+ const backupPath = join15(backupService.getBackupDir(), selectedBackup.timestamp);
13037
12731
  await backupService.restoreBackup(backupPath);
13038
12732
  console.log("\n\u2705 Backup restored successfully!\n");
13039
12733
  console.log("\u{1F4A1} Tip: Your IDE may need to be restarted to pick up the changes.");
@@ -13059,17 +12753,17 @@ function formatTimestamp(timestamp) {
13059
12753
 
13060
12754
  // src/commands/rules/sync.ts
13061
12755
  import { Command as Command66 } from "commander";
13062
- import { promises as fs26 } from "fs";
13063
- import { join as join17 } from "path";
12756
+ import { promises as fs25 } from "fs";
12757
+ import { join as join16 } from "path";
13064
12758
  import { checkbox as checkbox6, confirm as confirm13, Separator } from "@inquirer/prompts";
13065
12759
  function createRulesSyncCommand() {
13066
12760
  return new Command66("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) => {
13067
- const rulePresetDir = join17(process.cwd(), ".jai1", "rule-preset");
13068
- const presetJsonPath = join17(rulePresetDir, "preset.json");
12761
+ const rulePresetDir = join16(process.cwd(), ".jai1", "rule-preset");
12762
+ const presetJsonPath = join16(rulePresetDir, "preset.json");
13069
12763
  let presetExists = false;
13070
12764
  let presetData = null;
13071
12765
  try {
13072
- const presetContent = await fs26.readFile(presetJsonPath, "utf-8");
12766
+ const presetContent = await fs25.readFile(presetJsonPath, "utf-8");
13073
12767
  presetData = JSON.parse(presetContent);
13074
12768
  presetExists = true;
13075
12769
  } catch {
@@ -13199,11 +12893,11 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
13199
12893
  throw new Error(`Failed to fetch preset: ${presetResponse.statusText}`);
13200
12894
  }
13201
12895
  const bundle = await presetResponse.json();
13202
- const files = await fs26.readdir(rulePresetDir);
12896
+ const files = await fs25.readdir(rulePresetDir);
13203
12897
  for (const file of files) {
13204
12898
  if (file.endsWith(".mdc") || file.endsWith(".md")) {
13205
- const filePath = join17(rulePresetDir, file);
13206
- const content = await fs26.readFile(filePath, "utf-8");
12899
+ const filePath = join16(rulePresetDir, file);
12900
+ const content = await fs25.readFile(filePath, "utf-8");
13207
12901
  bundle.files[file] = content;
13208
12902
  }
13209
12903
  }
@@ -13217,9 +12911,9 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
13217
12911
  }
13218
12912
  const files2 = generatorService.generateForIde(bundle, ideId);
13219
12913
  for (const file of files2) {
13220
- const fullPath = join17(process.cwd(), file.path);
13221
- await fs26.mkdir(join17(fullPath, ".."), { recursive: true });
13222
- await fs26.writeFile(fullPath, file.content, "utf-8");
12914
+ const fullPath = join16(process.cwd(), file.path);
12915
+ await fs25.mkdir(join16(fullPath, ".."), { recursive: true });
12916
+ await fs25.writeFile(fullPath, file.content, "utf-8");
13223
12917
  }
13224
12918
  console.log(`\u2713 ${format.name} - ${files2.length} files regenerated`);
13225
12919
  } catch (error) {
@@ -13282,8 +12976,8 @@ function buildIdeChoices(currentIdes, detected, suggestions) {
13282
12976
 
13283
12977
  // src/commands/rules/info.ts
13284
12978
  import { Command as Command67 } from "commander";
13285
- import { promises as fs27 } from "fs";
13286
- import { join as join18 } from "path";
12979
+ import { promises as fs26 } from "fs";
12980
+ import { join as join17 } from "path";
13287
12981
  function createRulesInfoCommand() {
13288
12982
  return new Command67("info").description("Show current preset information").option("--json", "Output as JSON").action(async (options) => {
13289
12983
  const projectConfigService = new ProjectConfigService();
@@ -13298,14 +12992,14 @@ function createRulesInfoCommand() {
13298
12992
  return;
13299
12993
  }
13300
12994
  console.log("\u{1F4CB} Current Preset Information\n");
13301
- const rulePresetDir = join18(process.cwd(), ".jai1", "rule-preset");
13302
- const presetJsonPath = join18(rulePresetDir, "preset.json");
12995
+ const rulePresetDir = join17(process.cwd(), ".jai1", "rule-preset");
12996
+ const presetJsonPath = join17(rulePresetDir, "preset.json");
13303
12997
  let presetMetadata = null;
13304
12998
  let presetFiles = [];
13305
12999
  try {
13306
- const presetContent = await fs27.readFile(presetJsonPath, "utf-8");
13000
+ const presetContent = await fs26.readFile(presetJsonPath, "utf-8");
13307
13001
  presetMetadata = JSON.parse(presetContent);
13308
- const files = await fs27.readdir(rulePresetDir);
13002
+ const files = await fs26.readdir(rulePresetDir);
13309
13003
  presetFiles = files.filter((f) => f.endsWith(".mdc"));
13310
13004
  } catch {
13311
13005
  }
@@ -13366,7 +13060,7 @@ Available Backups (${rulesConfig.backups.length}):`);
13366
13060
  }
13367
13061
  async function checkPathExists(path13) {
13368
13062
  try {
13369
- await fs27.access(join18(process.cwd(), path13));
13063
+ await fs26.access(join17(process.cwd(), path13));
13370
13064
  return true;
13371
13065
  } catch {
13372
13066
  return false;
@@ -13419,9 +13113,669 @@ function createRulesCommand() {
13419
13113
  return rulesCommand;
13420
13114
  }
13421
13115
 
13422
- // src/commands/upgrade.ts
13116
+ // src/commands/skills/index.ts
13117
+ import { Command as Command74 } from "commander";
13118
+ import chalk39 from "chalk";
13119
+
13120
+ // src/commands/skills/find.ts
13423
13121
  import { Command as Command69 } from "commander";
13424
- import { confirm as confirm14 } from "@inquirer/prompts";
13122
+ import chalk34 from "chalk";
13123
+ import Table8 from "cli-table3";
13124
+
13125
+ // src/services/skills.service.ts
13126
+ import { promises as fs27 } from "fs";
13127
+ import { join as join18 } from "path";
13128
+ import { execFile } from "child_process";
13129
+ import { promisify } from "util";
13130
+ var execFileAsync = promisify(execFile);
13131
+ var IDE_SKILL_TARGETS = [
13132
+ { id: "cursor", name: "Cursor", icon: "\u{1F52E}", skillsPath: ".cursor/skills" },
13133
+ { id: "windsurf", name: "Windsurf", icon: "\u{1F3C4}", skillsPath: ".windsurf/skills" },
13134
+ { id: "antigravity", name: "Antigravity", icon: "\u{1F680}", skillsPath: ".agents/skills" },
13135
+ { id: "claudecode", name: "Claude Code", icon: "\u{1F916}", skillsPath: ".claude/skills" },
13136
+ { id: "opencode", name: "OpenCode", icon: "\u{1F4BB}", skillsPath: ".opencode/skills" }
13137
+ ];
13138
+ var SkillsService = class {
13139
+ componentsService;
13140
+ constructor(projectRoot = process.cwd()) {
13141
+ this.componentsService = new ComponentsService(projectRoot);
13142
+ }
13143
+ /**
13144
+ * Search skills on Jai1 server
13145
+ */
13146
+ async searchFromServer(config, query) {
13147
+ const options = {};
13148
+ if (query) options.search = query;
13149
+ const components = await this.componentsService.list(config, options);
13150
+ return components.filter((c) => c.filepath.startsWith("skills/"));
13151
+ }
13152
+ /**
13153
+ * Install skill from Jai1 server to .jai1/skills/
13154
+ */
13155
+ async installFromServer(config, skillName, targetDir) {
13156
+ const filepath = skillName.startsWith("skills/") ? skillName : `skills/${skillName}`;
13157
+ await this.componentsService.install(config, filepath, targetDir);
13158
+ }
13159
+ /**
13160
+ * List locally installed skills from .jai1/skills/
13161
+ */
13162
+ async listLocal(projectRoot) {
13163
+ const skillsDir = join18(projectRoot, ".jai1", "skills");
13164
+ const skills = [];
13165
+ try {
13166
+ const entries = await fs27.readdir(skillsDir, { withFileTypes: true });
13167
+ for (const entry of entries) {
13168
+ if (!entry.isDirectory()) continue;
13169
+ const skillPath = join18(skillsDir, entry.name);
13170
+ const skillMd = join18(skillPath, "SKILL.md");
13171
+ try {
13172
+ await fs27.access(skillMd);
13173
+ } catch {
13174
+ continue;
13175
+ }
13176
+ const content = await fs27.readFile(skillMd, "utf-8");
13177
+ const { name, description } = this.parseFrontmatter(content);
13178
+ const fileCount = await this.countFiles(skillPath);
13179
+ skills.push({
13180
+ name: name || entry.name,
13181
+ slug: entry.name,
13182
+ description: description || "",
13183
+ path: skillPath,
13184
+ fileCount
13185
+ });
13186
+ }
13187
+ } catch {
13188
+ }
13189
+ return skills.sort((a, b) => a.slug.localeCompare(b.slug));
13190
+ }
13191
+ /**
13192
+ * Get detailed info for a single skill
13193
+ */
13194
+ async getSkillInfo(projectRoot, skillName) {
13195
+ const skillPath = join18(projectRoot, ".jai1", "skills", skillName);
13196
+ const skillMd = join18(skillPath, "SKILL.md");
13197
+ try {
13198
+ await fs27.access(skillMd);
13199
+ } catch {
13200
+ return null;
13201
+ }
13202
+ const content = await fs27.readFile(skillMd, "utf-8");
13203
+ const { name, description } = this.parseFrontmatter(content);
13204
+ const fileCount = await this.countFiles(skillPath);
13205
+ return {
13206
+ name: name || skillName,
13207
+ slug: skillName,
13208
+ description: description || "",
13209
+ path: skillPath,
13210
+ fileCount
13211
+ };
13212
+ }
13213
+ /**
13214
+ * Search skills via npm `skills` package (wrapper)
13215
+ */
13216
+ async npmSkillsFind(query) {
13217
+ try {
13218
+ const { stdout } = await execFileAsync("npx", ["-y", "skills", "find", query], {
13219
+ timeout: 3e4
13220
+ });
13221
+ return stdout;
13222
+ } catch (error) {
13223
+ throw new Error(
13224
+ `npm skills find failed: ${error instanceof Error ? error.message : String(error)}`
13225
+ );
13226
+ }
13227
+ }
13228
+ /**
13229
+ * Install skill via npm `skills` package (wrapper)
13230
+ * Installs to .jai1/skills/ directory
13231
+ */
13232
+ async npmSkillsAdd(source, projectRoot) {
13233
+ const targetDir = join18(projectRoot, ".jai1", "skills");
13234
+ await fs27.mkdir(targetDir, { recursive: true });
13235
+ try {
13236
+ const { stdout } = await execFileAsync("npx", [
13237
+ "-y",
13238
+ "skills",
13239
+ "add",
13240
+ source,
13241
+ "--path",
13242
+ targetDir
13243
+ ], {
13244
+ timeout: 6e4,
13245
+ cwd: projectRoot
13246
+ });
13247
+ return stdout;
13248
+ } catch (error) {
13249
+ throw new Error(
13250
+ `npm skills add failed: ${error instanceof Error ? error.message : String(error)}`
13251
+ );
13252
+ }
13253
+ }
13254
+ /**
13255
+ * Get available IDE skill targets
13256
+ */
13257
+ getIDETargets() {
13258
+ return IDE_SKILL_TARGETS;
13259
+ }
13260
+ /**
13261
+ * Get IDE skill target by ID
13262
+ */
13263
+ getIDETarget(id) {
13264
+ return IDE_SKILL_TARGETS.find((t) => t.id === id);
13265
+ }
13266
+ /**
13267
+ * Sync skills from .jai1/skills/ to IDE directories
13268
+ */
13269
+ async syncToIdes(projectRoot, ides, skillSlugs, onProgress) {
13270
+ const localSkills = await this.listLocal(projectRoot);
13271
+ const skillsToSync = skillSlugs ? localSkills.filter((s) => skillSlugs.includes(s.slug)) : localSkills;
13272
+ if (skillsToSync.length === 0) {
13273
+ return { created: 0, updated: 0, errors: 0 };
13274
+ }
13275
+ let created = 0;
13276
+ let updated = 0;
13277
+ let errors = 0;
13278
+ for (const ide of ides) {
13279
+ const target = this.getIDETarget(ide);
13280
+ if (!target) continue;
13281
+ for (const skill of skillsToSync) {
13282
+ const targetPath = join18(projectRoot, target.skillsPath, skill.slug);
13283
+ try {
13284
+ let status = "created";
13285
+ try {
13286
+ await fs27.access(targetPath);
13287
+ status = "updated";
13288
+ await fs27.rm(targetPath, { recursive: true, force: true });
13289
+ } catch {
13290
+ }
13291
+ await fs27.mkdir(targetPath, { recursive: true });
13292
+ await this.copyDir(skill.path, targetPath);
13293
+ if (status === "created") created++;
13294
+ else updated++;
13295
+ onProgress?.({
13296
+ ide: target.name,
13297
+ skill: skill.slug,
13298
+ status,
13299
+ path: targetPath
13300
+ });
13301
+ } catch (error) {
13302
+ errors++;
13303
+ onProgress?.({
13304
+ ide: target.name,
13305
+ skill: skill.slug,
13306
+ status: "error",
13307
+ path: targetPath,
13308
+ error: error instanceof Error ? error.message : String(error)
13309
+ });
13310
+ }
13311
+ }
13312
+ }
13313
+ return { created, updated, errors };
13314
+ }
13315
+ /**
13316
+ * Parse YAML frontmatter from SKILL.md
13317
+ */
13318
+ parseFrontmatter(content) {
13319
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
13320
+ if (!match) {
13321
+ const headingMatch = content.match(/^#\s+(.+)/m);
13322
+ return {
13323
+ name: headingMatch?.[1] || "",
13324
+ description: ""
13325
+ };
13326
+ }
13327
+ const frontmatter = match[1] ?? "";
13328
+ let name = "";
13329
+ let description = "";
13330
+ for (const line of frontmatter.split("\n")) {
13331
+ const nameMatch = line.match(/^name:\s*(.+)/);
13332
+ if (nameMatch?.[1]) name = nameMatch[1].trim().replace(/^["']|["']$/g, "");
13333
+ const descMatch = line.match(/^description:\s*(.+)/);
13334
+ if (descMatch?.[1]) description = descMatch[1].trim().replace(/^["']|["']$/g, "");
13335
+ }
13336
+ return { name, description };
13337
+ }
13338
+ /**
13339
+ * Count files in a directory recursively
13340
+ */
13341
+ async countFiles(dirPath) {
13342
+ let count = 0;
13343
+ const entries = await fs27.readdir(dirPath, { withFileTypes: true });
13344
+ for (const entry of entries) {
13345
+ if (entry.isDirectory()) {
13346
+ count += await this.countFiles(join18(dirPath, entry.name));
13347
+ } else {
13348
+ count++;
13349
+ }
13350
+ }
13351
+ return count;
13352
+ }
13353
+ /**
13354
+ * Copy directory recursively
13355
+ */
13356
+ async copyDir(source, target) {
13357
+ const entries = await fs27.readdir(source, { withFileTypes: true });
13358
+ for (const entry of entries) {
13359
+ const srcPath = join18(source, entry.name);
13360
+ const tgtPath = join18(target, entry.name);
13361
+ if (entry.isDirectory()) {
13362
+ await fs27.mkdir(tgtPath, { recursive: true });
13363
+ await this.copyDir(srcPath, tgtPath);
13364
+ } else {
13365
+ await fs27.copyFile(srcPath, tgtPath);
13366
+ }
13367
+ }
13368
+ }
13369
+ };
13370
+
13371
+ // src/commands/skills/find.ts
13372
+ function createSkillsFindCommand() {
13373
+ return new Command69("find").description("Search for skills on server or npm").argument("<query>", "Search query").option("--skillsh", "Search on npm skills registry instead of Jai1 server").option("--all", "Search on both Jai1 server and npm").action(async (query, options) => {
13374
+ const searchNpm = options.skillsh || options.all;
13375
+ const searchServer = !options.skillsh || options.all;
13376
+ if (searchServer) {
13377
+ const configService = new ConfigService();
13378
+ const config = await configService.load();
13379
+ if (!config) {
13380
+ throw new ValidationError('Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.');
13381
+ }
13382
+ console.log(chalk34.cyan("\u{1F50D} \u0110ang t\xECm ki\u1EBFm tr\xEAn Jai1 server..."));
13383
+ console.log();
13384
+ const skillsService = new SkillsService();
13385
+ const results = await skillsService.searchFromServer(config, query);
13386
+ if (results.length === 0) {
13387
+ console.log(chalk34.yellow("Kh\xF4ng t\xECm th\u1EA5y skills n\xE0o tr\xEAn server."));
13388
+ } else {
13389
+ const table = new Table8({
13390
+ head: [
13391
+ chalk34.cyan("T\xEAn"),
13392
+ chalk34.cyan("M\xF4 t\u1EA3"),
13393
+ chalk34.cyan("Version"),
13394
+ chalk34.cyan("Downloads")
13395
+ ],
13396
+ style: { head: [], border: ["gray"] },
13397
+ colWidths: [25, 40, 10, 12]
13398
+ });
13399
+ for (const skill of results) {
13400
+ const name = skill.filepath.replace("skills/", "");
13401
+ table.push([
13402
+ chalk34.white(name),
13403
+ chalk34.dim((skill.description || "").slice(0, 38)),
13404
+ chalk34.green(skill.version || "-"),
13405
+ chalk34.dim(String(skill.downloads || 0))
13406
+ ]);
13407
+ }
13408
+ console.log(chalk34.bold(`\u{1F4E6} Jai1 Server (${results.length} k\u1EBFt qu\u1EA3)`));
13409
+ console.log(table.toString());
13410
+ console.log();
13411
+ }
13412
+ }
13413
+ if (searchNpm) {
13414
+ console.log(chalk34.cyan("\u{1F50D} \u0110ang t\xECm ki\u1EBFm tr\xEAn npm skills..."));
13415
+ console.log();
13416
+ const skillsService = new SkillsService();
13417
+ try {
13418
+ const output = await skillsService.npmSkillsFind(query);
13419
+ console.log(chalk34.bold("\u{1F310} npm Skills Registry"));
13420
+ console.log(output);
13421
+ } catch (error) {
13422
+ console.log(chalk34.yellow(
13423
+ `Kh\xF4ng th\u1EC3 t\xECm ki\u1EBFm tr\xEAn npm: ${error instanceof Error ? error.message : String(error)}`
13424
+ ));
13425
+ }
13426
+ }
13427
+ console.log(chalk34.dim('\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13428
+ });
13429
+ }
13430
+
13431
+ // src/commands/skills/add.ts
13432
+ import { Command as Command70 } from "commander";
13433
+ import { join as join19 } from "path";
13434
+ import chalk35 from "chalk";
13435
+ import { checkbox as checkbox7 } from "@inquirer/prompts";
13436
+ function createSkillsAddCommand() {
13437
+ return new Command70("add").description("Install a skill to .jai1/skills/").argument("<name>", "Skill name or source (npm: GitHub shorthand, URL)").option("--skillsh", "Install from npm skills registry instead of Jai1 server").option("--sync", "Auto-sync to IDE(s) after install").option("--ides <ides...>", "Target IDEs for sync (cursor, windsurf, antigravity, claudecode, opencode)").option("--all", "Sync to all available IDEs").option("-y, --yes", "Headless mode (skip all prompts)").action(async (name, options) => {
13438
+ const skillsService = new SkillsService();
13439
+ const projectRoot = process.cwd();
13440
+ const headless = options.yes === true;
13441
+ if (options.skillsh) {
13442
+ console.log(chalk35.cyan(`\u{1F310} \u0110ang c\xE0i \u0111\u1EB7t skill t\u1EEB npm: ${name}...`));
13443
+ console.log();
13444
+ const output = await skillsService.npmSkillsAdd(name, projectRoot);
13445
+ console.log(output);
13446
+ console.log(chalk35.green("\u2705 C\xE0i \u0111\u1EB7t t\u1EEB npm th\xE0nh c\xF4ng!"));
13447
+ } else {
13448
+ const configService = new ConfigService();
13449
+ const config = await configService.load();
13450
+ if (!config) {
13451
+ throw new ValidationError('Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.');
13452
+ }
13453
+ console.log(chalk35.cyan(`\u{1F4E6} \u0110ang c\xE0i \u0111\u1EB7t skill: ${name}...`));
13454
+ console.log();
13455
+ const targetDir = join19(projectRoot, ".jai1");
13456
+ await skillsService.installFromServer(config, name, targetDir);
13457
+ console.log(chalk35.green(`\u2705 \u0110\xE3 c\xE0i \u0111\u1EB7t skill "${name}" v\xE0o .jai1/skills/${name}/`));
13458
+ }
13459
+ console.log();
13460
+ if (options.sync) {
13461
+ let selectedIdes;
13462
+ if (options.all) {
13463
+ selectedIdes = skillsService.getIDETargets().map((t) => t.id);
13464
+ } else if (options.ides && options.ides.length > 0) {
13465
+ selectedIdes = options.ides;
13466
+ } else if (headless) {
13467
+ throw new Error(
13468
+ "Headless mode (-y) v\u1EDBi --sync y\xEAu c\u1EA7u --ides ho\u1EB7c --all.\nV\xED d\u1EE5:\n j skills add brainstorming --sync --ides cursor windsurf -y\n j skills add brainstorming --sync --all -y"
13469
+ );
13470
+ } else {
13471
+ const targets = skillsService.getIDETargets();
13472
+ selectedIdes = await checkbox7({
13473
+ message: "Sync sang IDE(s) n\xE0o?",
13474
+ choices: targets.map((t) => ({
13475
+ name: `${t.icon} ${t.name}`,
13476
+ value: t.id
13477
+ })),
13478
+ theme: checkboxTheme
13479
+ });
13480
+ }
13481
+ if (selectedIdes.length > 0) {
13482
+ console.log(chalk35.cyan("\u{1F504} \u0110ang sync sang IDE(s)..."));
13483
+ console.log();
13484
+ const slug = name.includes("/") ? name.split("/").pop() : name;
13485
+ const result = await skillsService.syncToIdes(
13486
+ projectRoot,
13487
+ selectedIdes,
13488
+ [slug],
13489
+ (res) => {
13490
+ const icon = res.status === "created" ? "\u2713" : res.status === "updated" ? "\u21BB" : "\u2717";
13491
+ console.log(` ${icon} ${res.ide}: ${res.skill} \u2192 ${res.path}`);
13492
+ }
13493
+ );
13494
+ console.log();
13495
+ console.log(chalk35.green(`\u2705 Sync ho\xE0n t\u1EA5t! Created: ${result.created}, Updated: ${result.updated}`));
13496
+ if (result.errors > 0) {
13497
+ console.log(chalk35.yellow(`\u26A0\uFE0F Errors: ${result.errors}`));
13498
+ }
13499
+ }
13500
+ } else {
13501
+ console.log(chalk35.dim('\u{1F4A1} Ch\u1EA1y "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
13502
+ console.log(chalk35.dim(' ho\u1EB7c "j ide sync" \u0111\u1EC3 sync to\xE0n b\u1ED9 .jai1/'));
13503
+ }
13504
+ });
13505
+ }
13506
+
13507
+ // src/commands/skills/list.ts
13508
+ import { Command as Command71 } from "commander";
13509
+ import chalk36 from "chalk";
13510
+ import Table9 from "cli-table3";
13511
+ function createSkillsListCommand() {
13512
+ return new Command71("list").description("List installed skills or available skills on server").option("--available", "List all skills available on Jai1 server").option("-s, --search <term>", "Search skills by name or description").action(async (options) => {
13513
+ const skillsService = new SkillsService();
13514
+ if (options.available) {
13515
+ const configService = new ConfigService();
13516
+ const config = await configService.load();
13517
+ if (!config) {
13518
+ throw new ValidationError('Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.');
13519
+ }
13520
+ console.log(chalk36.cyan("\u{1F4E6} \u0110ang t\u1EA3i danh s\xE1ch skills t\u1EEB server..."));
13521
+ console.log();
13522
+ const results = await skillsService.searchFromServer(config, options.search);
13523
+ if (results.length === 0) {
13524
+ console.log(chalk36.yellow("Kh\xF4ng t\xECm th\u1EA5y skills n\xE0o."));
13525
+ return;
13526
+ }
13527
+ const table = new Table9({
13528
+ head: [
13529
+ chalk36.cyan("T\xEAn"),
13530
+ chalk36.cyan("M\xF4 t\u1EA3"),
13531
+ chalk36.cyan("Version"),
13532
+ chalk36.cyan("Downloads")
13533
+ ],
13534
+ style: { head: [], border: ["gray"] },
13535
+ colWidths: [28, 40, 10, 12]
13536
+ });
13537
+ for (const skill of results) {
13538
+ const name = skill.filepath.replace("skills/", "");
13539
+ table.push([
13540
+ chalk36.white(name),
13541
+ chalk36.dim((skill.description || "").slice(0, 38)),
13542
+ chalk36.green(skill.version || "-"),
13543
+ chalk36.dim(String(skill.downloads || 0))
13544
+ ]);
13545
+ }
13546
+ console.log(table.toString());
13547
+ console.log();
13548
+ console.log(chalk36.dim(`T\u1ED5ng c\u1ED9ng: ${results.length} skill(s)`));
13549
+ console.log(chalk36.dim('\n\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13550
+ } else {
13551
+ const projectRoot = process.cwd();
13552
+ const skills = await skillsService.listLocal(projectRoot);
13553
+ if (skills.length === 0) {
13554
+ console.log(chalk36.yellow("Ch\u01B0a c\xF3 skills n\xE0o \u0111\u01B0\u1EE3c c\xE0i \u0111\u1EB7t."));
13555
+ console.log();
13556
+ console.log(chalk36.dim('\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13557
+ console.log(chalk36.dim(' ho\u1EB7c "j skills list --available" \u0111\u1EC3 xem skills c\xF3 s\u1EB5n'));
13558
+ return;
13559
+ }
13560
+ console.log(chalk36.bold.cyan("\u{1F6E0} Skills \u0111\xE3 c\xE0i \u0111\u1EB7t"));
13561
+ console.log();
13562
+ const table = new Table9({
13563
+ head: [
13564
+ chalk36.cyan("T\xEAn"),
13565
+ chalk36.cyan("M\xF4 t\u1EA3"),
13566
+ chalk36.cyan("Files")
13567
+ ],
13568
+ style: { head: [], border: ["gray"] },
13569
+ colWidths: [28, 45, 8]
13570
+ });
13571
+ for (const skill of skills) {
13572
+ table.push([
13573
+ chalk36.white(skill.slug),
13574
+ chalk36.dim(skill.description.slice(0, 43)),
13575
+ chalk36.dim(String(skill.fileCount))
13576
+ ]);
13577
+ }
13578
+ console.log(table.toString());
13579
+ console.log();
13580
+ console.log(chalk36.dim(`T\u1ED5ng c\u1ED9ng: ${skills.length} skill(s)`));
13581
+ console.log(chalk36.dim('\n\u{1F4A1} D\xF9ng "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
13582
+ }
13583
+ });
13584
+ }
13585
+
13586
+ // src/commands/skills/info.ts
13587
+ import { Command as Command72 } from "commander";
13588
+ import chalk37 from "chalk";
13589
+ function createSkillsInfoCommand() {
13590
+ return new Command72("info").description("Show detailed information about a skill").argument("<name>", "Skill name").option("--server", "Show info from Jai1 server instead of local").action(async (name, options) => {
13591
+ const skillsService = new SkillsService();
13592
+ if (options.server) {
13593
+ const configService = new ConfigService();
13594
+ const config = await configService.load();
13595
+ if (!config) {
13596
+ console.log(chalk37.red('\u274C Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.'));
13597
+ process.exit(1);
13598
+ }
13599
+ const filepath = name.startsWith("skills/") ? name : `skills/${name}`;
13600
+ const { ComponentsService: ComponentsService2 } = await import("./components.service-NWAWKII3.js");
13601
+ const componentsService = new ComponentsService2();
13602
+ try {
13603
+ const component = await componentsService.get(config, filepath);
13604
+ console.log(`
13605
+ \u{1F6E0} ${chalk37.bold(component.name || name)}
13606
+ `);
13607
+ console.log(`Filepath: ${component.filepath}`);
13608
+ console.log(`Version: ${component.version}`);
13609
+ console.log(`Downloads: ${component.downloads}`);
13610
+ if (component.description) {
13611
+ console.log(`Description: ${component.description}`);
13612
+ }
13613
+ if (component.tags && component.tags.length > 0) {
13614
+ console.log(`Tags: ${component.tags.join(", ")}`);
13615
+ }
13616
+ console.log(`Type: ${component.contentType}`);
13617
+ console.log();
13618
+ console.log(chalk37.dim('\u{1F4A1} D\xF9ng "j skills add ' + name + '" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13619
+ } catch (error) {
13620
+ console.log(chalk37.red(`\u274C Kh\xF4ng t\xECm th\u1EA5y skill "${name}" tr\xEAn server.`));
13621
+ process.exit(1);
13622
+ }
13623
+ } else {
13624
+ const projectRoot = process.cwd();
13625
+ const skill = await skillsService.getSkillInfo(projectRoot, name);
13626
+ if (!skill) {
13627
+ console.log(chalk37.red(`\u274C Skill "${name}" ch\u01B0a \u0111\u01B0\u1EE3c c\xE0i \u0111\u1EB7t.`));
13628
+ console.log(chalk37.dim('\u{1F4A1} D\xF9ng "j skills info ' + name + ' --server" \u0111\u1EC3 xem tr\xEAn server'));
13629
+ console.log(chalk37.dim(' ho\u1EB7c "j skills add ' + name + '" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13630
+ process.exit(1);
13631
+ }
13632
+ console.log(`
13633
+ \u{1F6E0} ${chalk37.bold(skill.name)}
13634
+ `);
13635
+ console.log(`Slug: ${skill.slug}`);
13636
+ console.log(`Description: ${skill.description || chalk37.dim("(none)")}`);
13637
+ console.log(`Path: ${skill.path}`);
13638
+ console.log(`Files: ${skill.fileCount}`);
13639
+ console.log();
13640
+ console.log(chalk37.dim('\u{1F4A1} D\xF9ng "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
13641
+ }
13642
+ });
13643
+ }
13644
+
13645
+ // src/commands/skills/sync.ts
13646
+ import { Command as Command73 } from "commander";
13647
+ import chalk38 from "chalk";
13648
+ import { confirm as confirm15, checkbox as checkbox8 } from "@inquirer/prompts";
13649
+ function createSkillsSyncCommand() {
13650
+ return new Command73("sync").description("Sync skills from .jai1/skills/ to IDE directories").option("--ides <ides...>", "Target IDEs (cursor, windsurf, antigravity, claudecode, opencode)").option("--skills <skills...>", "Specific skill slugs to sync (default: all)").option("--all", "Select all available IDEs").option("--dry-run", "Preview changes without writing files").option("-y, --yes", "Headless mode (skip all prompts)").action(async (options) => {
13651
+ const skillsService = new SkillsService();
13652
+ const projectRoot = process.cwd();
13653
+ const headless = options.yes === true;
13654
+ console.log(chalk38.bold.cyan("\n\u{1F504} Sync skills sang IDE(s)\n"));
13655
+ const localSkills = await skillsService.listLocal(projectRoot);
13656
+ if (localSkills.length === 0) {
13657
+ console.log(chalk38.yellow("\u26A0\uFE0F Kh\xF4ng c\xF3 skills n\xE0o trong .jai1/skills/"));
13658
+ console.log(chalk38.dim('\u{1F4A1} Ch\u1EA1y "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t skills tr\u01B0\u1EDBc'));
13659
+ process.exit(1);
13660
+ }
13661
+ console.log(`\u{1F4C1} T\xECm th\u1EA5y ${localSkills.length} skill(s) trong .jai1/skills/`);
13662
+ console.log();
13663
+ let selectedSlugs;
13664
+ if (options.skills && options.skills.length > 0) {
13665
+ selectedSlugs = options.skills;
13666
+ console.log(`\u{1F6E0} Skills: ${selectedSlugs.join(", ")}`);
13667
+ } else {
13668
+ console.log(`\u{1F6E0} Skills: t\u1EA5t c\u1EA3 (${localSkills.length})`);
13669
+ }
13670
+ let selectedIdes;
13671
+ const targets = skillsService.getIDETargets();
13672
+ if (options.all) {
13673
+ selectedIdes = targets.map((t) => t.id);
13674
+ } else if (options.ides && options.ides.length > 0) {
13675
+ selectedIdes = options.ides;
13676
+ } else if (headless) {
13677
+ throw new Error(
13678
+ "Headless mode (-y) y\xEAu c\u1EA7u --ides ho\u1EB7c --all.\nV\xED d\u1EE5:\n j skills sync --ides cursor windsurf -y\n j skills sync --all -y"
13679
+ );
13680
+ } else {
13681
+ selectedIdes = await checkbox8({
13682
+ message: "Ch\u1ECDn IDE(s) \u0111\u1EC3 sync (SPACE \u0111\u1EC3 ch\u1ECDn, ENTER \u0111\u1EC3 x\xE1c nh\u1EADn):",
13683
+ choices: targets.map((t) => ({
13684
+ name: `${t.icon} ${t.name}`,
13685
+ value: t.id
13686
+ })),
13687
+ theme: checkboxTheme
13688
+ });
13689
+ if (selectedIdes.length === 0) {
13690
+ console.log(chalk38.yellow("\n\u26A0\uFE0F Ch\u01B0a ch\u1ECDn IDE n\xE0o!"));
13691
+ process.exit(0);
13692
+ }
13693
+ }
13694
+ const ideNames = selectedIdes.map((id) => targets.find((t) => t.id === id)).filter(Boolean).map((t) => t.name);
13695
+ const skillCount = selectedSlugs ? selectedSlugs.length : localSkills.length;
13696
+ const totalFiles = skillCount * selectedIdes.length;
13697
+ console.log(`
13698
+ \u{1F4CA} Preview:
13699
+ `);
13700
+ console.log(` IDEs: ${ideNames.join(", ")}`);
13701
+ console.log(` Skills: ${skillCount}`);
13702
+ console.log(` Total: ${totalFiles} skill folder(s) s\u1EBD \u0111\u01B0\u1EE3c sync
13703
+ `);
13704
+ if (options.dryRun) {
13705
+ console.log(chalk38.dim("\u{1F50D} DRY RUN - Kh\xF4ng c\xF3 file n\xE0o \u0111\u01B0\u1EE3c ghi\n"));
13706
+ return;
13707
+ }
13708
+ if (!headless) {
13709
+ const confirmed = await confirm15({
13710
+ message: "Ti\u1EBFp t\u1EE5c sync?",
13711
+ default: true
13712
+ });
13713
+ if (!confirmed) {
13714
+ console.log(chalk38.yellow("\n\u274C \u0110\xE3 h\u1EE7y sync.\n"));
13715
+ process.exit(0);
13716
+ }
13717
+ }
13718
+ console.log(chalk38.cyan("\n\u{1F504} \u0110ang sync...\n"));
13719
+ const result = await skillsService.syncToIdes(
13720
+ projectRoot,
13721
+ selectedIdes,
13722
+ selectedSlugs,
13723
+ (res) => {
13724
+ const icon = res.status === "created" ? "\u2713" : res.status === "updated" ? "\u21BB" : "\u2717";
13725
+ const statusColor = res.status === "error" ? chalk38.red : chalk38.green;
13726
+ console.log(` ${statusColor(icon)} ${res.ide}: ${res.skill} \u2192 ${chalk38.dim(res.path)}`);
13727
+ if (res.status === "error" && res.error) {
13728
+ console.log(` ${chalk38.red("Error:")} ${res.error}`);
13729
+ }
13730
+ }
13731
+ );
13732
+ console.log(chalk38.green("\n\u2705 Sync ho\xE0n t\u1EA5t!\n"));
13733
+ console.log(` Created: ${result.created}`);
13734
+ console.log(` Updated: ${result.updated}`);
13735
+ if (result.errors > 0) {
13736
+ console.log(` Errors: ${result.errors}`);
13737
+ }
13738
+ console.log();
13739
+ });
13740
+ }
13741
+
13742
+ // src/commands/skills/index.ts
13743
+ function showSkillsHelp() {
13744
+ const cli = getCliName();
13745
+ console.log(chalk39.bold.cyan("\u{1F6E0} " + cli + " skills") + chalk39.dim(" - Qu\u1EA3n l\xFD agent skills"));
13746
+ console.log();
13747
+ console.log(chalk39.bold("C\xE1c l\u1EC7nh:"));
13748
+ console.log(` ${chalk39.cyan("find")} T\xECm ki\u1EBFm skills tr\xEAn server ho\u1EB7c npm`);
13749
+ console.log(` ${chalk39.cyan("add")} C\xE0i \u0111\u1EB7t skill v\xE0o .jai1/skills/`);
13750
+ console.log(` ${chalk39.cyan("list")} Li\u1EC7t k\xEA skills \u0111\xE3 c\xE0i ho\u1EB7c c\xF3 s\u1EB5n`);
13751
+ console.log(` ${chalk39.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t skill`);
13752
+ console.log(` ${chalk39.cyan("sync")} \u0110\u1ED3ng b\u1ED9 skills sang c\xE1c IDE`);
13753
+ console.log();
13754
+ console.log(chalk39.bold("V\xED d\u1EE5:"));
13755
+ console.log(chalk39.dim(` $ ${cli} skills find audit`));
13756
+ console.log(chalk39.dim(` $ ${cli} skills find "react" --skillsh`));
13757
+ console.log(chalk39.dim(` $ ${cli} skills add brainstorming`));
13758
+ console.log(chalk39.dim(` $ ${cli} skills add vercel/next-skills --skillsh`));
13759
+ console.log(chalk39.dim(` $ ${cli} skills list`));
13760
+ console.log(chalk39.dim(` $ ${cli} skills sync --all -y`));
13761
+ console.log();
13762
+ console.log(chalk39.dim(`Ch\u1EA1y "${cli} skills <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt`));
13763
+ }
13764
+ function createSkillsCommand() {
13765
+ const cmd = new Command74("skills").alias("s").description("Manage agent skills (search, install, sync to IDEs)").action(() => {
13766
+ showSkillsHelp();
13767
+ });
13768
+ cmd.addCommand(createSkillsFindCommand());
13769
+ cmd.addCommand(createSkillsAddCommand());
13770
+ cmd.addCommand(createSkillsListCommand());
13771
+ cmd.addCommand(createSkillsInfoCommand());
13772
+ cmd.addCommand(createSkillsSyncCommand());
13773
+ return cmd;
13774
+ }
13775
+
13776
+ // src/commands/upgrade.ts
13777
+ import { Command as Command75 } from "commander";
13778
+ import { confirm as confirm16 } from "@inquirer/prompts";
13425
13779
  import { execSync as execSync4 } from "child_process";
13426
13780
  var colors2 = {
13427
13781
  yellow: "\x1B[33m",
@@ -13432,7 +13786,7 @@ var colors2 = {
13432
13786
  bold: "\x1B[1m"
13433
13787
  };
13434
13788
  function createUpgradeCommand() {
13435
- return new Command69("upgrade").description("Upgrade CLI client to the latest version").option("--check", "Only check for updates without installing").option("--force", "Force upgrade without confirmation").action(async (options) => {
13789
+ return new Command75("upgrade").description("Upgrade CLI client to the latest version").option("--check", "Only check for updates without installing").option("--force", "Force upgrade without confirmation").action(async (options) => {
13436
13790
  await handleUpgrade(options);
13437
13791
  });
13438
13792
  }
@@ -13476,7 +13830,7 @@ ${colors2.bold}Current version:${colors2.reset} ${currentVersion}`);
13476
13830
  return;
13477
13831
  }
13478
13832
  if (!options.force) {
13479
- const shouldUpdate = await confirm14({
13833
+ const shouldUpdate = await confirm16({
13480
13834
  message: "Update to the latest version now?",
13481
13835
  default: true
13482
13836
  });
@@ -13580,11 +13934,11 @@ function getInstallCommand(packageManager2) {
13580
13934
  }
13581
13935
 
13582
13936
  // src/commands/clean.ts
13583
- import { Command as Command70 } from "commander";
13584
- import { confirm as confirm15, select as select6 } from "@inquirer/prompts";
13585
- import { join as join19 } from "path";
13937
+ import { Command as Command76 } from "commander";
13938
+ import { confirm as confirm17, select as select6 } from "@inquirer/prompts";
13939
+ import { join as join20 } from "path";
13586
13940
  function createCleanCommand() {
13587
- return new Command70("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) => {
13941
+ return new Command76("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) => {
13588
13942
  await handleClean(options);
13589
13943
  });
13590
13944
  }
@@ -13595,7 +13949,7 @@ async function handleClean(options) {
13595
13949
  {
13596
13950
  name: "Backups",
13597
13951
  description: "Component backup files (.jai1_backup/)",
13598
- path: join19(cwd, ".jai1_backup"),
13952
+ path: join20(cwd, ".jai1_backup"),
13599
13953
  check: async () => {
13600
13954
  const backups = await service.listBackups(cwd);
13601
13955
  return { exists: backups.length > 0, count: backups.length };
@@ -13680,7 +14034,7 @@ async function cleanTarget(target, skipConfirm) {
13680
14034
  }
13681
14035
  const countStr = info.count ? ` (${info.count} items)` : "";
13682
14036
  if (!skipConfirm) {
13683
- const confirmed = await confirm15({
14037
+ const confirmed = await confirm17({
13684
14038
  message: `Delete ${target.name}${countStr}?`,
13685
14039
  default: false
13686
14040
  });
@@ -13698,7 +14052,7 @@ async function cleanTarget(target, skipConfirm) {
13698
14052
  }
13699
14053
 
13700
14054
  // src/commands/redmine/check.ts
13701
- import { Command as Command71 } from "commander";
14055
+ import { Command as Command77 } from "commander";
13702
14056
 
13703
14057
  // src/services/redmine-config.service.ts
13704
14058
  import { readFile as readFile7 } from "fs/promises";
@@ -14005,7 +14359,7 @@ async function checkConnectivity(config) {
14005
14359
 
14006
14360
  // src/commands/redmine/check.ts
14007
14361
  function createRedmineCheckCommand() {
14008
- const cmd = new Command71("check").description("Check Redmine connectivity").option("-c, --config <path>", "Config file path", "redmine.config.yaml").option("--json", "Output as JSON").action(async (options) => {
14362
+ const cmd = new Command77("check").description("Check Redmine connectivity").option("-c, --config <path>", "Config file path", "redmine.config.yaml").option("--json", "Output as JSON").action(async (options) => {
14009
14363
  await handleRedmineCheck(options);
14010
14364
  });
14011
14365
  return cmd;
@@ -14033,7 +14387,7 @@ async function handleRedmineCheck(options) {
14033
14387
  }
14034
14388
 
14035
14389
  // src/commands/redmine/sync-issue.ts
14036
- import { Command as Command72 } from "commander";
14390
+ import { Command as Command78 } from "commander";
14037
14391
 
14038
14392
  // src/sync-issue.ts
14039
14393
  import { resolve as resolve3, relative } from "path";
@@ -14417,7 +14771,7 @@ function extractIssueIdFromUrl(url) {
14417
14771
 
14418
14772
  // src/commands/redmine/sync-issue.ts
14419
14773
  function createSyncIssueCommand() {
14420
- const cmd = new Command72("issue").description("Sync a single issue").option("-i, --id <number>", "Issue ID").option("-u, --url <url>", "Issue URL").option("--dry-run", "Preview without making changes").option("-c, --config <path>", "Config file path").option("-o, --output-dir <path>", "Output directory").option("--json", "Output as JSON").action(async (options) => {
14774
+ const cmd = new Command78("issue").description("Sync a single issue").option("-i, --id <number>", "Issue ID").option("-u, --url <url>", "Issue URL").option("--dry-run", "Preview without making changes").option("-c, --config <path>", "Config file path").option("-o, --output-dir <path>", "Output directory").option("--json", "Output as JSON").action(async (options) => {
14421
14775
  await handleSyncIssue(options);
14422
14776
  });
14423
14777
  return cmd;
@@ -14461,7 +14815,7 @@ async function handleSyncIssue(options) {
14461
14815
  }
14462
14816
 
14463
14817
  // src/commands/redmine/sync-project.ts
14464
- import { Command as Command73 } from "commander";
14818
+ import { Command as Command79 } from "commander";
14465
14819
 
14466
14820
  // src/sync-project.ts
14467
14821
  async function syncProject(config, options = {}) {
@@ -14531,7 +14885,7 @@ async function syncProject(config, options = {}) {
14531
14885
 
14532
14886
  // src/commands/redmine/sync-project.ts
14533
14887
  function createSyncProjectCommand() {
14534
- const cmd = new Command73("project").description("Sync all issues in a project").option("-s, --status <status>", "Filter by status (default: *)", "*").option("--updated-since <date>", "Only sync issues updated since YYYY-MM-DD").option("--concurrency <number>", "Number of concurrent requests").option("--page-size <number>", "Page size for API requests").option("--dry-run", "Preview without making changes").option("-c, --config <path>", "Config file path").option("-o, --output-dir <path>", "Output directory").option("--json", "Output as JSON").action(async (options) => {
14888
+ const cmd = new Command79("project").description("Sync all issues in a project").option("-s, --status <status>", "Filter by status (default: *)", "*").option("--updated-since <date>", "Only sync issues updated since YYYY-MM-DD").option("--concurrency <number>", "Number of concurrent requests").option("--page-size <number>", "Page size for API requests").option("--dry-run", "Preview without making changes").option("-c, --config <path>", "Config file path").option("-o, --output-dir <path>", "Output directory").option("--json", "Output as JSON").action(async (options) => {
14535
14889
  await handleSyncProject(options);
14536
14890
  });
14537
14891
  return cmd;
@@ -14586,12 +14940,12 @@ async function handleSyncProject(options) {
14586
14940
  }
14587
14941
 
14588
14942
  // src/commands/framework/info.ts
14589
- import { Command as Command74 } from "commander";
14943
+ import { Command as Command80 } from "commander";
14590
14944
  import { promises as fs28 } from "fs";
14591
- import { join as join20 } from "path";
14592
- import { homedir as homedir6 } from "os";
14945
+ import { join as join21 } from "path";
14946
+ import { homedir as homedir5 } from "os";
14593
14947
  function createInfoCommand() {
14594
- const cmd = new Command74("info").description("Show client configuration and status").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
14948
+ const cmd = new Command80("info").description("Show client configuration and status").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
14595
14949
  await handleInfo(options);
14596
14950
  });
14597
14951
  return cmd;
@@ -14602,7 +14956,7 @@ async function handleInfo(options) {
14602
14956
  if (!config) {
14603
14957
  throw new ValidationError(`Not initialized. Run "${getCliName()} auth" first.`);
14604
14958
  }
14605
- const frameworkPath = join20(homedir6(), ".jai1", "framework");
14959
+ const frameworkPath = join21(homedir5(), ".jai1", "framework");
14606
14960
  const projectStatus = await getProjectStatus2();
14607
14961
  const info = {
14608
14962
  configPath: configService.getConfigPath(),
@@ -14637,7 +14991,7 @@ function maskKey4(key) {
14637
14991
  return "****" + key.slice(-4);
14638
14992
  }
14639
14993
  async function getProjectStatus2() {
14640
- const projectJai1 = join20(process.cwd(), ".jai1");
14994
+ const projectJai1 = join21(process.cwd(), ".jai1");
14641
14995
  try {
14642
14996
  await fs28.access(projectJai1);
14643
14997
  return { exists: true, version: "Synced" };
@@ -14647,8 +15001,8 @@ async function getProjectStatus2() {
14647
15001
  }
14648
15002
 
14649
15003
  // src/commands/self-update.ts
14650
- import { Command as Command75 } from "commander";
14651
- import { confirm as confirm16 } from "@inquirer/prompts";
15004
+ import { Command as Command81 } from "commander";
15005
+ import { confirm as confirm18 } from "@inquirer/prompts";
14652
15006
  import { execSync as execSync5 } from "child_process";
14653
15007
  var colors3 = {
14654
15008
  yellow: "\x1B[33m",
@@ -14659,7 +15013,7 @@ var colors3 = {
14659
15013
  bold: "\x1B[1m"
14660
15014
  };
14661
15015
  function createSelfUpdateCommand() {
14662
- return new Command75("self-update").description("Update CLI client to the latest version").option("--check", "Only check for updates without installing").option("--force", "Force update without confirmation").action(async (options) => {
15016
+ return new Command81("self-update").description("Update CLI client to the latest version").option("--check", "Only check for updates without installing").option("--force", "Force update without confirmation").action(async (options) => {
14663
15017
  await handleSelfUpdate(options);
14664
15018
  });
14665
15019
  }
@@ -14703,7 +15057,7 @@ ${colors3.bold}Current version:${colors3.reset} ${currentVersion}`);
14703
15057
  return;
14704
15058
  }
14705
15059
  if (!options.force) {
14706
- const shouldUpdate = await confirm16({
15060
+ const shouldUpdate = await confirm18({
14707
15061
  message: "Update to the latest version now?",
14708
15062
  default: true
14709
15063
  });
@@ -14799,10 +15153,10 @@ function getInstallCommand2(packageManager2) {
14799
15153
  }
14800
15154
 
14801
15155
  // src/commands/clear-backups.ts
14802
- import { Command as Command76 } from "commander";
14803
- import { confirm as confirm17 } from "@inquirer/prompts";
15156
+ import { Command as Command82 } from "commander";
15157
+ import { confirm as confirm19 } from "@inquirer/prompts";
14804
15158
  function createClearBackupsCommand() {
14805
- return new Command76("clear-backups").description("Clear backup files").option("-y, --yes", "Skip confirmation").action(async (options) => {
15159
+ return new Command82("clear-backups").description("Clear backup files").option("-y, --yes", "Skip confirmation").action(async (options) => {
14806
15160
  const service = new ComponentsService();
14807
15161
  const backups = await service.listBackups(process.cwd());
14808
15162
  if (backups.length === 0) {
@@ -14818,7 +15172,7 @@ function createClearBackupsCommand() {
14818
15172
  }
14819
15173
  console.log();
14820
15174
  if (!options.yes) {
14821
- const ok = await confirm17({ message: "Delete all backups?", default: false });
15175
+ const ok = await confirm19({ message: "Delete all backups?", default: false });
14822
15176
  if (!ok) return;
14823
15177
  }
14824
15178
  await service.clearBackups(process.cwd());
@@ -14827,8 +15181,8 @@ function createClearBackupsCommand() {
14827
15181
  }
14828
15182
 
14829
15183
  // src/commands/vscode/index.ts
14830
- import { Command as Command77 } from "commander";
14831
- import { checkbox as checkbox7, confirm as confirm18, select as select7 } from "@inquirer/prompts";
15184
+ import { Command as Command83 } from "commander";
15185
+ import { checkbox as checkbox9, confirm as confirm20, select as select7 } from "@inquirer/prompts";
14832
15186
  import fs29 from "fs/promises";
14833
15187
  import path12 from "path";
14834
15188
  import { existsSync as existsSync3 } from "fs";
@@ -14967,7 +15321,7 @@ var PERFORMANCE_GROUPS2 = {
14967
15321
  }
14968
15322
  };
14969
15323
  function createVSCodeCommand() {
14970
- const vscodeCommand = new Command77("vscode").description("Qu\u1EA3n l\xFD c\xE0i \u0111\u1EB7t VSCode cho d\u1EF1 \xE1n hi\u1EC7n t\u1EA1i");
15324
+ const vscodeCommand = new Command83("vscode").description("Qu\u1EA3n l\xFD c\xE0i \u0111\u1EB7t VSCode cho d\u1EF1 \xE1n hi\u1EC7n t\u1EA1i");
14971
15325
  vscodeCommand.action(async () => {
14972
15326
  await interactiveMode2();
14973
15327
  });
@@ -15036,7 +15390,7 @@ async function selectGroupsToApply2(action) {
15036
15390
  value: key
15037
15391
  }));
15038
15392
  try {
15039
- const selectedGroups = await checkbox7({
15393
+ const selectedGroups = await checkbox9({
15040
15394
  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):`,
15041
15395
  choices,
15042
15396
  theme: checkboxTheme
@@ -15073,7 +15427,7 @@ async function applyGroups2(groupKeys, action) {
15073
15427
  console.log("\u{1F4C4} \u0110\xE3 \u0111\u1ECDc c\xE0i \u0111\u1EB7t hi\u1EC7n t\u1EA1i t\u1EEB settings.json");
15074
15428
  } catch {
15075
15429
  console.warn("\u26A0\uFE0F Kh\xF4ng th\u1EC3 \u0111\u1ECDc settings.json (c\xF3 th\u1EC3 ch\u1EE9a comments).");
15076
- const confirmOverwrite = await confirm18({
15430
+ const confirmOverwrite = await confirm20({
15077
15431
  message: "Ghi \u0111\xE8 file settings.json hi\u1EC7n t\u1EA1i?",
15078
15432
  default: false
15079
15433
  });
@@ -15120,7 +15474,7 @@ async function resetSettings2(groupKeys) {
15120
15474
  console.log("\n\u26A0\uFE0F Kh\xF4ng t\xECm th\u1EA5y file settings.json");
15121
15475
  return;
15122
15476
  }
15123
- const confirmReset = await confirm18({
15477
+ const confirmReset = await confirm20({
15124
15478
  message: groupKeys.length === 0 ? "Reset T\u1EA4T C\u1EA2 settings v\u1EC1 m\u1EB7c \u0111\u1ECBnh (x\xF3a to\xE0n b\u1ED9 file)?" : `Reset c\xE1c nh\xF3m: ${groupKeys.join(", ")}?`,
15125
15479
  default: false
15126
15480
  });
@@ -15138,10 +15492,10 @@ async function resetSettings2(groupKeys) {
15138
15492
  }
15139
15493
 
15140
15494
  // src/commands/migrate-ide.ts
15141
- import { Command as Command78 } from "commander";
15142
- import { checkbox as checkbox8, confirm as confirm19 } from "@inquirer/prompts";
15495
+ import { Command as Command84 } from "commander";
15496
+ import { checkbox as checkbox10, confirm as confirm21 } from "@inquirer/prompts";
15143
15497
  function createMigrateIdeCommand() {
15144
- const cmd = new Command78("migrate-ide").description("Migrate .jai1 rules v\xE0 workflows sang IDEs (Cursor, Windsurf, Claude Code, etc.)").option("--ide <ides...>", "Target IDEs (cursor, windsurf, antigravity, claudecode, opencode)").option("--type <types...>", "Content types (rules, workflows, commands)").option("--dry-run", "Preview changes without writing files").action(async (options) => {
15498
+ const cmd = new Command84("migrate-ide").description("Migrate .jai1 rules v\xE0 workflows sang IDEs (Cursor, Windsurf, Claude Code, etc.)").option("--ide <ides...>", "Target IDEs (cursor, windsurf, antigravity, claudecode, opencode)").option("--type <types...>", "Content types (rules, workflows, commands)").option("--dry-run", "Preview changes without writing files").action(async (options) => {
15145
15499
  await runMigrateIde(options);
15146
15500
  });
15147
15501
  return cmd;
@@ -15169,7 +15523,7 @@ async function runMigrateIde(options) {
15169
15523
  value: ide
15170
15524
  };
15171
15525
  });
15172
- selectedIdes = await checkbox8({
15526
+ selectedIdes = await checkbox10({
15173
15527
  message: "Ch\u1ECDn IDE(s) \u0111\u1EC3 migrate (SPACE \u0111\u1EC3 ch\u1ECDn, ENTER \u0111\u1EC3 x\xE1c nh\u1EADn):",
15174
15528
  choices: ideChoices,
15175
15529
  theme: checkboxTheme
@@ -15189,7 +15543,7 @@ async function runMigrateIde(options) {
15189
15543
  { name: `Workflows (${content.workflows.length} files)`, value: "workflows" },
15190
15544
  { name: `Commands (${content.commands.length} files)`, value: "commands" }
15191
15545
  ];
15192
- selectedTypes = await checkbox8({
15546
+ selectedTypes = await checkbox10({
15193
15547
  message: "Ch\u1ECDn content types \u0111\u1EC3 migrate:",
15194
15548
  choices: typeChoices,
15195
15549
  theme: checkboxTheme
@@ -15212,7 +15566,7 @@ async function runMigrateIde(options) {
15212
15566
  if (options.dryRun) {
15213
15567
  console.log("\u{1F50D} DRY RUN - No files will be written\n");
15214
15568
  }
15215
- const confirmed = await confirm19({
15569
+ const confirmed = await confirm21({
15216
15570
  message: "Proceed with migration?",
15217
15571
  default: true
15218
15572
  });
@@ -15250,20 +15604,20 @@ async function runMigrateIde(options) {
15250
15604
 
15251
15605
  // src/utils/help-formatter.ts
15252
15606
  import boxen4 from "boxen";
15253
- import chalk34 from "chalk";
15607
+ import chalk40 from "chalk";
15254
15608
  import gradient from "gradient-string";
15255
15609
  import figlet from "figlet";
15256
15610
  function showCustomHelp(version) {
15257
15611
  const title = figlet.textSync("JAI1", { font: "Small" });
15258
15612
  console.log(gradient.pastel(title));
15259
15613
  console.log(
15260
- boxen4(chalk34.cyan(`Agentic Coding CLI v${version}`), {
15614
+ boxen4(chalk40.cyan(`Agentic Coding CLI v${version}`), {
15261
15615
  padding: { left: 1, right: 1, top: 0, bottom: 0 },
15262
15616
  borderStyle: "round",
15263
15617
  borderColor: "cyan"
15264
15618
  })
15265
15619
  );
15266
- console.log(chalk34.bold("\n\u{1F527} Thi\u1EBFt l\u1EADp & Th\xF4ng tin"));
15620
+ console.log(chalk40.bold("\n\u{1F527} Thi\u1EBFt l\u1EADp & Th\xF4ng tin"));
15267
15621
  console.log(" auth X\xE1c th\u1EF1c v\xE0 c\u1EA5u h\xECnh client");
15268
15622
  console.log(" status Hi\u1EC3n th\u1ECB tr\u1EA1ng th\xE1i c\u1EA5u h\xECnh");
15269
15623
  console.log(" client-info T\u1EA1o th\xF4ng tin client \u0111\u1EC3 g\u1EEDi \u0111\u1ED9i ph\xE1t tri\u1EC3n");
@@ -15271,43 +15625,43 @@ function showCustomHelp(version) {
15271
15625
  console.log(" guide H\u01B0\u1EDBng d\u1EABn s\u1EED d\u1EE5ng nhanh");
15272
15626
  console.log(" quickstart B\u1EAFt \u0111\u1EA7u t\u1EEB \u0111\xE2u? (theo t\xECnh hu\u1ED1ng)");
15273
15627
  console.log(" doctor Chu\u1EA9n \u0111o\xE1n project hi\u1EC7n t\u1EA1i");
15274
- console.log(chalk34.bold("\n\u{1F4E6} Qu\u1EA3n l\xFD Components"));
15628
+ console.log(chalk40.bold("\n\u{1F4E6} Qu\u1EA3n l\xFD Components"));
15275
15629
  console.log(" apply C\xE0i \u0111\u1EB7t components (interactive)");
15276
15630
  console.log(" update C\u1EADp nh\u1EADt components \u0111\xE3 c\xE0i");
15277
15631
  console.log(" check Ki\u1EC3m tra c\u1EADp nh\u1EADt t\u1EEB server");
15278
- console.log(chalk34.bold("\n\u{1F5A5}\uFE0F IDE & T\xEDch h\u1EE3p"));
15632
+ console.log(chalk40.bold("\n\u{1F5A5}\uFE0F IDE & T\xEDch h\u1EE3p"));
15279
15633
  console.log(" ide L\u1EC7nh c\u1EA5u h\xECnh IDE");
15280
15634
  console.log(" chat Chat AI v\u1EDBi Jai1 LLM Proxy");
15281
15635
  console.log(" openai-keys Th\xF4ng tin API credentials");
15282
- console.log(chalk34.bold("\n\u{1F916} AI Tools"));
15636
+ console.log(chalk40.bold("\n\u{1F916} AI Tools"));
15283
15637
  console.log(" translate D\u1ECBch v\u0103n b\u1EA3n/file b\u1EB1ng AI");
15284
15638
  console.log(" image T\u1EA1o \u1EA3nh (Coming Soon)");
15285
15639
  console.log(" stats Th\u1ED1ng k\xEA s\u1EED d\u1EE5ng LLM");
15286
15640
  console.log(" feedback G\u1EEDi b\xE1o c\xE1o/\u0111\u1EC1 xu\u1EA5t");
15287
- console.log(chalk34.bold("\n\u{1F4C1} Project"));
15641
+ console.log(chalk40.bold("\n\u{1F4C1} Project"));
15288
15642
  console.log(" kit Qu\u1EA3n l\xFD starter kits");
15289
15643
  console.log(" tasks (t) Qu\u1EA3n l\xFD tasks ph\xE1t tri\u1EC3n");
15290
15644
  console.log(" rules Qu\u1EA3n l\xFD rule presets");
15291
15645
  console.log(" deps Qu\u1EA3n l\xFD dependencies");
15292
15646
  console.log(" redmine Redmine context sync");
15293
- console.log(chalk34.bold("\n\u2699\uFE0F B\u1EA3o tr\xEC"));
15647
+ console.log(chalk40.bold("\n\u2699\uFE0F B\u1EA3o tr\xEC"));
15294
15648
  console.log(" upgrade C\u1EADp nh\u1EADt CLI client");
15295
15649
  console.log(" clean D\u1ECDn d\u1EB9p cache/backup");
15296
15650
  console.log(" utils Developer utilities");
15297
15651
  const name = getCliName();
15298
- console.log(chalk34.dim(`
15652
+ console.log(chalk40.dim(`
15299
15653
  S\u1EED d\u1EE5ng: ${name} [l\u1EC7nh] --help \u0111\u1EC3 xem chi ti\u1EBFt`));
15300
15654
  }
15301
15655
  function showUnknownCommand(commandName) {
15302
- console.error(chalk34.red(`\u274C L\u1EC7nh kh\xF4ng t\u1ED3n t\u1EA1i: ${commandName}`));
15656
+ console.error(chalk40.red(`\u274C L\u1EC7nh kh\xF4ng t\u1ED3n t\u1EA1i: ${commandName}`));
15303
15657
  const name = getCliName();
15304
- console.error(chalk34.dim(`
15658
+ console.error(chalk40.dim(`
15305
15659
  G\u1EE3i \xFD: Ch\u1EA1y ${name} --help \u0111\u1EC3 xem danh s\xE1ch l\u1EC7nh`));
15306
15660
  }
15307
15661
 
15308
15662
  // src/cli.ts
15309
15663
  checkNodeVersion();
15310
- var program = new Command79();
15664
+ var program = new Command85();
15311
15665
  if (process.argv.includes("-v") || process.argv.includes("--version")) {
15312
15666
  console.log(package_default.version);
15313
15667
  if (!process.argv.includes("--skip-update-check")) {
@@ -15345,11 +15699,12 @@ program.addCommand(createDepsCommand());
15345
15699
  program.addCommand(createTasksCommand());
15346
15700
  program.addCommand(createKitCommand());
15347
15701
  program.addCommand(createRulesCommand());
15702
+ program.addCommand(createSkillsCommand());
15348
15703
  program.addCommand(createUpgradeCommand());
15349
15704
  program.addCommand(createCleanCommand());
15350
- var redmineCommand = new Command79("redmine").description("Redmine context sync commands");
15705
+ var redmineCommand = new Command85("redmine").description("Redmine context sync commands");
15351
15706
  redmineCommand.addCommand(createRedmineCheckCommand());
15352
- var syncCommand = new Command79("sync").description("Sync Redmine issues to markdown files");
15707
+ var syncCommand = new Command85("sync").description("Sync Redmine issues to markdown files");
15353
15708
  syncCommand.addCommand(createSyncIssueCommand());
15354
15709
  syncCommand.addCommand(createSyncProjectCommand());
15355
15710
  redmineCommand.addCommand(syncCommand);
@@ -15367,7 +15722,7 @@ program.on("command:*", (operands) => {
15367
15722
  process.on("unhandledRejection", (error) => {
15368
15723
  void new ErrorLogService().logError(error, {
15369
15724
  command: process.argv.slice(2).join(" "),
15370
- cwdBase: basename4(process.cwd())
15725
+ cwdBase: basename5(process.cwd())
15371
15726
  });
15372
15727
  if (error instanceof Jai1Error) {
15373
15728
  console.error("\u274C", error.message);
@@ -15388,7 +15743,7 @@ program.parseAsync(process.argv).then(async () => {
15388
15743
  }).catch((error) => {
15389
15744
  void new ErrorLogService().logError(error, {
15390
15745
  command: process.argv.slice(2).join(" "),
15391
- cwdBase: basename4(process.cwd())
15746
+ cwdBase: basename5(process.cwd())
15392
15747
  });
15393
15748
  console.error("\u274C", error.message);
15394
15749
  process.exit(1);