@jvittechs/j 1.0.32 → 1.0.34

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.32",
152
+ version: "1.0.34",
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",
@@ -4173,6 +3867,15 @@ function showGuide(name) {
4173
3867
  console.log(` ${chalk8.cyan(`${name} ide sync`)} \u0110\u1ED3ng b\u1ED9 .jai1 content \u0111\u1EBFn IDE`);
4174
3868
  console.log(` ${chalk8.cyan(`${name} rules apply`)} C\u1EA5u h\xECnh rules cho IDE`);
4175
3869
  console.log();
3870
+ console.log(chalk8.bold("\u2501\u2501\u2501 SKILLS \u2501\u2501\u2501"));
3871
+ console.log(` ${chalk8.cyan(`${name} s find`)} ${chalk8.dim("<query>")} T\xECm skills tr\xEAn server`);
3872
+ console.log(` ${chalk8.cyan(`${name} s find`)} ${chalk8.dim("<query> --skillsh")} T\xECm tr\xEAn npm skills`);
3873
+ console.log(` ${chalk8.cyan(`${name} s add`)} ${chalk8.dim("<name>")} C\xE0i skill v\xE0o .jai1/skills/`);
3874
+ console.log(` ${chalk8.cyan(`${name} s list`)} Skills \u0111\xE3 c\xE0i`);
3875
+ console.log(` ${chalk8.cyan(`${name} s list`)} ${chalk8.dim("--available")} Skills tr\xEAn server`);
3876
+ console.log(` ${chalk8.cyan(`${name} s info`)} ${chalk8.dim("<name>")} Chi ti\u1EBFt skill`);
3877
+ console.log(` ${chalk8.cyan(`${name} s sync`)} ${chalk8.dim("--all -y")} Sync sang t\u1EA5t c\u1EA3 IDEs`);
3878
+ console.log();
4176
3879
  console.log(chalk8.bold("\u2501\u2501\u2501 TASK MANAGEMENT \u2501\u2501\u2501"));
4177
3880
  console.log(` ${chalk8.cyan(`${name} t add`)} ${chalk8.dim('"title"')} ${chalk8.dim("[-p 0-3] [-P parent]")} T\u1EA1o task`);
4178
3881
  console.log(` ${chalk8.cyan(`${name} t list`)} ${chalk8.dim("[-s status] [-P parent]")} Li\u1EC7t k\xEA tasks`);
@@ -4258,8 +3961,8 @@ function showQuickstart(name) {
4258
3961
  // src/commands/doctor.ts
4259
3962
  import { Command as Command14 } from "commander";
4260
3963
  import chalk10 from "chalk";
4261
- import { promises as fs9 } from "fs";
4262
- import { join as join6 } from "path";
3964
+ import { promises as fs8 } from "fs";
3965
+ import { join as join5 } from "path";
4263
3966
  var CORE_FILES = [
4264
3967
  "workflows/gen-project-overview.md",
4265
3968
  "context/jv-it-context.md",
@@ -4378,9 +4081,9 @@ async function checkAuth(cliName) {
4378
4081
  }
4379
4082
  }
4380
4083
  async function checkCoreFiles(cliName) {
4381
- const jai1Dir = join6(process.cwd(), ".jai1");
4084
+ const jai1Dir = join5(process.cwd(), ".jai1");
4382
4085
  try {
4383
- await fs9.access(jai1Dir);
4086
+ await fs8.access(jai1Dir);
4384
4087
  } catch {
4385
4088
  return {
4386
4089
  name: "Core Package",
@@ -4392,9 +4095,9 @@ async function checkCoreFiles(cliName) {
4392
4095
  const missing = [];
4393
4096
  const found = [];
4394
4097
  for (const file of CORE_FILES) {
4395
- const filePath = join6(jai1Dir, file);
4098
+ const filePath = join5(jai1Dir, file);
4396
4099
  try {
4397
- await fs9.access(filePath);
4100
+ await fs8.access(filePath);
4398
4101
  found.push(file);
4399
4102
  } catch {
4400
4103
  missing.push(file);
@@ -5157,7 +4860,7 @@ var ChatApp = ({ service, initialModel }) => {
5157
4860
 
5158
4861
  // src/server/web-chat-server.ts
5159
4862
  import http from "http";
5160
- import fs11 from "fs";
4863
+ import fs10 from "fs";
5161
4864
  import path5 from "path";
5162
4865
  import { fileURLToPath } from "url";
5163
4866
 
@@ -5291,7 +4994,7 @@ var SessionManager = class {
5291
4994
  };
5292
4995
 
5293
4996
  // src/server/file-service.ts
5294
- import fs10 from "fs";
4997
+ import fs9 from "fs";
5295
4998
  import path4 from "path";
5296
4999
  import ignore from "ignore";
5297
5000
  var ALWAYS_EXCLUDED = [
@@ -5500,8 +5203,8 @@ var FileService = class {
5500
5203
  this.ignoreFilter.add(ALWAYS_EXCLUDED);
5501
5204
  const gitignorePath = path4.join(this.workingDir, ".gitignore");
5502
5205
  try {
5503
- if (fs10.existsSync(gitignorePath)) {
5504
- const content = fs10.readFileSync(gitignorePath, "utf-8");
5206
+ if (fs9.existsSync(gitignorePath)) {
5207
+ const content = fs9.readFileSync(gitignorePath, "utf-8");
5505
5208
  this.ignoreFilter.add(content);
5506
5209
  }
5507
5210
  } catch {
@@ -5542,7 +5245,7 @@ var FileService = class {
5542
5245
  const searchDir = async (dirPath, depth = 0) => {
5543
5246
  if (depth > 10 || results.length >= maxResults) return;
5544
5247
  try {
5545
- const entries = await fs10.promises.readdir(dirPath, { withFileTypes: true });
5248
+ const entries = await fs9.promises.readdir(dirPath, { withFileTypes: true });
5546
5249
  for (const entry of entries) {
5547
5250
  if (results.length >= maxResults) break;
5548
5251
  const entryPath = path4.join(dirPath, entry.name);
@@ -5552,7 +5255,7 @@ var FileService = class {
5552
5255
  if (normalizedRelPath.includes(normalizedQuery)) {
5553
5256
  if (entry.isFile() && isTextFile(entry.name)) {
5554
5257
  try {
5555
- const stat = await fs10.promises.stat(entryPath);
5258
+ const stat = await fs9.promises.stat(entryPath);
5556
5259
  results.push({
5557
5260
  path: relativePath,
5558
5261
  name: entry.name,
@@ -5592,14 +5295,14 @@ var FileService = class {
5592
5295
  if (!isTextFile(absolutePath)) {
5593
5296
  throw new Error("Only text files can be read");
5594
5297
  }
5595
- const stat = await fs10.promises.stat(absolutePath);
5298
+ const stat = await fs9.promises.stat(absolutePath);
5596
5299
  if (!stat.isFile()) {
5597
5300
  throw new Error("Path is not a file");
5598
5301
  }
5599
5302
  if (stat.size > this.maxFileSize) {
5600
5303
  throw new Error(`File exceeds maximum size of ${Math.round(this.maxFileSize / 1024)}KB`);
5601
5304
  }
5602
- const content = await fs10.promises.readFile(absolutePath, "utf-8");
5305
+ const content = await fs9.promises.readFile(absolutePath, "utf-8");
5603
5306
  return {
5604
5307
  path: normalizedRelPath,
5605
5308
  content,
@@ -5729,14 +5432,14 @@ function createWebChatServer(options) {
5729
5432
  return;
5730
5433
  }
5731
5434
  try {
5732
- const stat = await fs11.promises.stat(fullPath);
5435
+ const stat = await fs10.promises.stat(fullPath);
5733
5436
  if (!stat.isFile()) {
5734
5437
  sendError(res, 404, "ERR-WC-007", "Not found");
5735
5438
  return;
5736
5439
  }
5737
5440
  const ext = path5.extname(fullPath);
5738
5441
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
5739
- const content = await fs11.promises.readFile(fullPath);
5442
+ const content = await fs10.promises.readFile(fullPath);
5740
5443
  res.writeHead(200, { "Content-Type": contentType });
5741
5444
  res.end(content);
5742
5445
  } catch (error) {
@@ -6310,7 +6013,7 @@ function createStatsCommand() {
6310
6013
  import { Command as Command18 } from "commander";
6311
6014
 
6312
6015
  // src/services/translation.service.ts
6313
- import { promises as fs12 } from "fs";
6016
+ import { promises as fs11 } from "fs";
6314
6017
  import path6 from "path";
6315
6018
  import pLimit from "p-limit";
6316
6019
  import pRetry from "p-retry";
@@ -6329,7 +6032,7 @@ var TranslationService = class {
6329
6032
  */
6330
6033
  async detectInputType(input5) {
6331
6034
  try {
6332
- const stat = await fs12.stat(input5);
6035
+ const stat = await fs11.stat(input5);
6333
6036
  if (stat.isDirectory()) return "folder";
6334
6037
  if (stat.isFile()) return "file";
6335
6038
  } catch {
@@ -6366,13 +6069,13 @@ var TranslationService = class {
6366
6069
  */
6367
6070
  async translateFile(filePath) {
6368
6071
  try {
6369
- const content = await fs12.readFile(filePath, "utf-8");
6072
+ const content = await fs11.readFile(filePath, "utf-8");
6370
6073
  const ext = path6.extname(filePath).toLowerCase();
6371
6074
  const fileType = this.getFileType(ext);
6372
6075
  const translatedContent = await this.translateWithRetry(content, fileType);
6373
6076
  const outputPath = this.generateOutputPath(filePath);
6374
6077
  if (!this.options.dryRun) {
6375
- await fs12.writeFile(outputPath, translatedContent, "utf-8");
6078
+ await fs11.writeFile(outputPath, translatedContent, "utf-8");
6376
6079
  }
6377
6080
  return {
6378
6081
  inputPath: filePath,
@@ -6437,7 +6140,7 @@ var TranslationService = class {
6437
6140
  * Discover translatable files in folder recursively
6438
6141
  */
6439
6142
  async discoverFiles(folderPath) {
6440
- const entries = await fs12.readdir(folderPath, { withFileTypes: true });
6143
+ const entries = await fs11.readdir(folderPath, { withFileTypes: true });
6441
6144
  const files = [];
6442
6145
  for (const entry of entries) {
6443
6146
  const fullPath = path6.join(folderPath, entry.name);
@@ -6970,20 +6673,20 @@ function createImageCommand() {
6970
6673
  import { Command as Command24 } from "commander";
6971
6674
  import { select as select2, input, confirm as confirm5 } from "@inquirer/prompts";
6972
6675
  import os from "os";
6973
- import { promises as fs13 } from "fs";
6974
- import { join as join7 } from "path";
6676
+ import { promises as fs12 } from "fs";
6677
+ import { join as join6 } from "path";
6975
6678
  async function collectContext() {
6976
6679
  const context = {
6977
6680
  os: `${os.platform()} ${os.release()}`
6978
6681
  };
6979
6682
  try {
6980
6683
  const packageJsonPath = new URL("../../package.json", import.meta.url);
6981
- const packageJson = JSON.parse(await fs13.readFile(packageJsonPath, "utf-8"));
6684
+ const packageJson = JSON.parse(await fs12.readFile(packageJsonPath, "utf-8"));
6982
6685
  context.cli_version = packageJson.version;
6983
6686
  } catch {
6984
6687
  }
6985
6688
  try {
6986
- const projectPackageJson = await fs13.readFile(join7(process.cwd(), "package.json"), "utf-8");
6689
+ const projectPackageJson = await fs12.readFile(join6(process.cwd(), "package.json"), "utf-8");
6987
6690
  const projectData = JSON.parse(projectPackageJson);
6988
6691
  context.project_name = projectData.name;
6989
6692
  } catch {
@@ -7186,8 +6889,8 @@ function createFeedbackCommand() {
7186
6889
  import { Command as Command25 } from "commander";
7187
6890
  import { confirm as confirm6 } from "@inquirer/prompts";
7188
6891
  import os2 from "os";
7189
- import { promises as fs14 } from "fs";
7190
- import { basename as basename2, join as join8 } from "path";
6892
+ import { promises as fs13 } from "fs";
6893
+ import { basename as basename2, join as join7 } from "path";
7191
6894
  import { version as nodeVersion2 } from "process";
7192
6895
  function createClientInfoCommand() {
7193
6896
  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 +7005,9 @@ async function collectClientInfo(config, options) {
7302
7005
  return payload;
7303
7006
  }
7304
7007
  async function hasProjectJai1() {
7305
- const projectJai1 = join8(process.cwd(), ".jai1");
7008
+ const projectJai1 = join7(process.cwd(), ".jai1");
7306
7009
  try {
7307
- await fs14.access(projectJai1);
7010
+ await fs13.access(projectJai1);
7308
7011
  return true;
7309
7012
  } catch {
7310
7013
  return false;
@@ -9653,7 +9356,7 @@ var HttpView = () => {
9653
9356
  import React23, { useState as useState17 } from "react";
9654
9357
  import { Box as Box19, Text as Text20, useInput as useInput15 } from "ink";
9655
9358
  import TextInput14 from "ink-text-input";
9656
- import * as fs15 from "fs";
9359
+ import * as fs14 from "fs";
9657
9360
  import * as path7 from "path";
9658
9361
  var MarkdownView = () => {
9659
9362
  const [filePath, setFilePath] = useState17("");
@@ -9675,11 +9378,11 @@ var MarkdownView = () => {
9675
9378
  try {
9676
9379
  setError("");
9677
9380
  const resolvedPath = path7.resolve(filePath);
9678
- if (!fs15.existsSync(resolvedPath)) {
9381
+ if (!fs14.existsSync(resolvedPath)) {
9679
9382
  setError(`File not found: ${resolvedPath}`);
9680
9383
  return;
9681
9384
  }
9682
- let fileContent = fs15.readFileSync(resolvedPath, "utf-8");
9385
+ let fileContent = fs14.readFileSync(resolvedPath, "utf-8");
9683
9386
  fileContent = fileContent.replace(/```mermaid\n([\s\S]*?)```/g, (match, code) => {
9684
9387
  return `
9685
9388
  \u{1F3A8} **Mermaid Diagram**
@@ -9989,7 +9692,7 @@ import Table4 from "cli-table3";
9989
9692
  import ora from "ora";
9990
9693
 
9991
9694
  // src/services/deps-detector.service.ts
9992
- import * as fs16 from "fs/promises";
9695
+ import * as fs15 from "fs/promises";
9993
9696
  import * as path8 from "path";
9994
9697
  var DepsDetectorService = class {
9995
9698
  /**
@@ -10057,7 +9760,7 @@ var DepsDetectorService = class {
10057
9760
  async detectLaravel(cwd) {
10058
9761
  try {
10059
9762
  const composerPath = path8.join(cwd, "composer.json");
10060
- const content = await fs16.readFile(composerPath, "utf-8");
9763
+ const content = await fs15.readFile(composerPath, "utf-8");
10061
9764
  const composerJson = JSON.parse(content);
10062
9765
  const deps = {
10063
9766
  ...composerJson.require,
@@ -10073,7 +9776,7 @@ var DepsDetectorService = class {
10073
9776
  */
10074
9777
  async fileExists(cwd, filename) {
10075
9778
  try {
10076
- await fs16.access(path8.join(cwd, filename));
9779
+ await fs15.access(path8.join(cwd, filename));
10077
9780
  return true;
10078
9781
  } catch {
10079
9782
  return false;
@@ -10082,7 +9785,7 @@ var DepsDetectorService = class {
10082
9785
  };
10083
9786
 
10084
9787
  // src/services/deps.service.ts
10085
- import { promises as fs17 } from "fs";
9788
+ import { promises as fs16 } from "fs";
10086
9789
  import path9 from "path";
10087
9790
  import { execSync } from "child_process";
10088
9791
  import pLimit2 from "p-limit";
@@ -10119,7 +9822,7 @@ var DepsService = class {
10119
9822
  async readPackageJson(cwd) {
10120
9823
  const pkgPath = path9.join(cwd, "package.json");
10121
9824
  try {
10122
- const content = await fs17.readFile(pkgPath, "utf-8");
9825
+ const content = await fs16.readFile(pkgPath, "utf-8");
10123
9826
  return JSON.parse(content);
10124
9827
  } catch (error) {
10125
9828
  if (error.code === "ENOENT") {
@@ -10210,7 +9913,7 @@ var DepsService = class {
10210
9913
  ];
10211
9914
  for (const { file, pm } of lockFiles) {
10212
9915
  try {
10213
- await fs17.access(path9.join(cwd, file));
9916
+ await fs16.access(path9.join(cwd, file));
10214
9917
  return pm;
10215
9918
  } catch {
10216
9919
  }
@@ -10281,7 +9984,7 @@ var DepsService = class {
10281
9984
 
10282
9985
  // src/services/deps-php.service.ts
10283
9986
  import { execSync as execSync2 } from "child_process";
10284
- import { promises as fs18 } from "fs";
9987
+ import { promises as fs17 } from "fs";
10285
9988
  import path10 from "path";
10286
9989
  var LARAVEL_PROTECTED_PACKAGES = /^laravel\//;
10287
9990
  var DepsPhpService = class {
@@ -10341,7 +10044,7 @@ var DepsPhpService = class {
10341
10044
  async readComposerJson(cwd) {
10342
10045
  const composerPath = path10.join(cwd, "composer.json");
10343
10046
  try {
10344
- const content = await fs18.readFile(composerPath, "utf-8");
10047
+ const content = await fs17.readFile(composerPath, "utf-8");
10345
10048
  return JSON.parse(content);
10346
10049
  } catch (error) {
10347
10050
  if (error.code === "ENOENT") {
@@ -10403,7 +10106,7 @@ var DepsPhpService = class {
10403
10106
 
10404
10107
  // src/services/deps-python.service.ts
10405
10108
  import { execSync as execSync3 } from "child_process";
10406
- import { promises as fs19 } from "fs";
10109
+ import { promises as fs18 } from "fs";
10407
10110
  import path11 from "path";
10408
10111
  import pLimit3 from "p-limit";
10409
10112
  var DepsPythonService = class {
@@ -10454,7 +10157,7 @@ var DepsPythonService = class {
10454
10157
  async checkPip(cwd, onProgress) {
10455
10158
  const requirementsPath = path11.join(cwd, "requirements.txt");
10456
10159
  try {
10457
- const content = await fs19.readFile(requirementsPath, "utf-8");
10160
+ const content = await fs18.readFile(requirementsPath, "utf-8");
10458
10161
  const packages = this.parseRequirementsTxt(content);
10459
10162
  return await this.fetchBulkVersions(packages, onProgress);
10460
10163
  } catch (error) {
@@ -10566,7 +10269,7 @@ var DepsPythonService = class {
10566
10269
  }
10567
10270
  async fileExists(cwd, filename) {
10568
10271
  try {
10569
- await fs19.access(path11.join(cwd, filename));
10272
+ await fs18.access(path11.join(cwd, filename));
10570
10273
  return true;
10571
10274
  } catch {
10572
10275
  return false;
@@ -11414,8 +11117,8 @@ import chalk30 from "chalk";
11414
11117
  import Table6 from "cli-table3";
11415
11118
 
11416
11119
  // src/services/starter-kit.service.ts
11417
- import { promises as fs20 } from "fs";
11418
- import { join as join10 } from "path";
11120
+ import { promises as fs19 } from "fs";
11121
+ import { join as join9 } from "path";
11419
11122
  import AdmZip from "adm-zip";
11420
11123
  var StarterKitService = class {
11421
11124
  /**
@@ -11462,17 +11165,17 @@ var StarterKitService = class {
11462
11165
  throw new NetworkError(`Failed to download kit: HTTP ${response.status}`);
11463
11166
  }
11464
11167
  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`);
11168
+ const tmpDir = join9(process.env.TMPDIR || "/tmp", "jai1-kits");
11169
+ await fs19.mkdir(tmpDir, { recursive: true });
11170
+ const tmpFile = join9(tmpDir, `${slug}.zip`);
11468
11171
  const buffer = await response.arrayBuffer();
11469
- await fs20.writeFile(tmpFile, Buffer.from(buffer));
11172
+ await fs19.writeFile(tmpFile, Buffer.from(buffer));
11470
11173
  if (onProgress) onProgress(60);
11471
11174
  const zip = new AdmZip(tmpFile);
11472
- await fs20.mkdir(targetDir, { recursive: true });
11175
+ await fs19.mkdir(targetDir, { recursive: true });
11473
11176
  zip.extractAllTo(targetDir, true);
11474
11177
  if (onProgress) onProgress(100);
11475
- await fs20.unlink(tmpFile);
11178
+ await fs19.unlink(tmpFile);
11476
11179
  }
11477
11180
  };
11478
11181
 
@@ -11596,8 +11299,8 @@ Post-Init Commands:`);
11596
11299
 
11597
11300
  // src/commands/kit/create.ts
11598
11301
  import { Command as Command60 } from "commander";
11599
- import { promises as fs21 } from "fs";
11600
- import { join as join11 } from "path";
11302
+ import { promises as fs20 } from "fs";
11303
+ import { join as join10 } from "path";
11601
11304
  import { select as select3, input as input2, checkbox as checkbox4 } from "@inquirer/prompts";
11602
11305
  import { execa as execa2 } from "execa";
11603
11306
 
@@ -11660,9 +11363,9 @@ function createKitCreateCommand() {
11660
11363
  }
11661
11364
  }
11662
11365
  }
11663
- const targetDir = directory || join11(process.cwd(), options.name || slug);
11366
+ const targetDir = directory || join10(process.cwd(), options.name || slug);
11664
11367
  try {
11665
- await fs21.access(targetDir);
11368
+ await fs20.access(targetDir);
11666
11369
  throw new Error(`Directory already exists: ${targetDir}`);
11667
11370
  } catch (error) {
11668
11371
  if (error.code !== "ENOENT") {
@@ -11725,7 +11428,7 @@ function createKitCreateCommand() {
11725
11428
  );
11726
11429
  for (const filepath of expandedPaths) {
11727
11430
  console.log(` \u{1F4E5} Installing ${filepath}...`);
11728
- await componentsService.install(config, filepath, join11(targetDir, ".jai1"));
11431
+ await componentsService.install(config, filepath, join10(targetDir, ".jai1"));
11729
11432
  }
11730
11433
  console.log(" \u2713 Framework components applied");
11731
11434
  }
@@ -11787,7 +11490,7 @@ function createKitCreateCommand() {
11787
11490
  async function applyVariableSubstitution(dir, variables) {
11788
11491
  const files = await getAllFiles(dir);
11789
11492
  for (const file of files) {
11790
- let content = await fs21.readFile(file, "utf-8");
11493
+ let content = await fs20.readFile(file, "utf-8");
11791
11494
  let modified = false;
11792
11495
  for (const [key, value] of Object.entries(variables)) {
11793
11496
  const regex = new RegExp(`\\{\\{${key}\\}\\}`, "g");
@@ -11797,15 +11500,15 @@ async function applyVariableSubstitution(dir, variables) {
11797
11500
  }
11798
11501
  }
11799
11502
  if (modified) {
11800
- await fs21.writeFile(file, content, "utf-8");
11503
+ await fs20.writeFile(file, content, "utf-8");
11801
11504
  }
11802
11505
  }
11803
11506
  }
11804
11507
  async function getAllFiles(dir) {
11805
11508
  const files = [];
11806
- const entries = await fs21.readdir(dir, { withFileTypes: true });
11509
+ const entries = await fs20.readdir(dir, { withFileTypes: true });
11807
11510
  for (const entry of entries) {
11808
- const fullPath = join11(dir, entry.name);
11511
+ const fullPath = join10(dir, entry.name);
11809
11512
  if (entry.isDirectory()) {
11810
11513
  if (!entry.name.startsWith(".") && entry.name !== "node_modules") {
11811
11514
  files.push(...await getAllFiles(fullPath));
@@ -11920,28 +11623,28 @@ function createRulesListCommand() {
11920
11623
 
11921
11624
  // src/commands/rules/init.ts
11922
11625
  import { Command as Command63 } from "commander";
11923
- import { promises as fs23 } from "fs";
11924
- import { join as join13 } from "path";
11626
+ import { promises as fs22 } from "fs";
11627
+ import { join as join12 } from "path";
11925
11628
  import { select as select4, confirm as confirm10 } from "@inquirer/prompts";
11926
11629
 
11927
11630
  // src/services/project-config.service.ts
11928
- import { promises as fs22 } from "fs";
11929
- import { join as join12 } from "path";
11631
+ import { promises as fs21 } from "fs";
11632
+ import { join as join11 } from "path";
11930
11633
  var ProjectConfigService = class {
11931
11634
  projectRoot;
11932
11635
  configDir;
11933
11636
  configPath;
11934
11637
  constructor(projectRoot = process.cwd()) {
11935
11638
  this.projectRoot = projectRoot;
11936
- this.configDir = join12(this.projectRoot, ".jai1");
11937
- this.configPath = join12(this.configDir, "project.json");
11639
+ this.configDir = join11(this.projectRoot, ".jai1");
11640
+ this.configPath = join11(this.configDir, "project.json");
11938
11641
  }
11939
11642
  /**
11940
11643
  * Check if config file exists
11941
11644
  */
11942
11645
  async exists() {
11943
11646
  try {
11944
- await fs22.access(this.configPath);
11647
+ await fs21.access(this.configPath);
11945
11648
  return true;
11946
11649
  } catch {
11947
11650
  return false;
@@ -11956,7 +11659,7 @@ var ProjectConfigService = class {
11956
11659
  return null;
11957
11660
  }
11958
11661
  try {
11959
- const content = await fs22.readFile(this.configPath, "utf-8");
11662
+ const content = await fs21.readFile(this.configPath, "utf-8");
11960
11663
  return JSON.parse(content);
11961
11664
  } catch (error) {
11962
11665
  throw new Error(
@@ -11970,8 +11673,8 @@ var ProjectConfigService = class {
11970
11673
  */
11971
11674
  async save(config) {
11972
11675
  try {
11973
- await fs22.mkdir(this.configDir, { recursive: true });
11974
- await fs22.writeFile(this.configPath, JSON.stringify(config, null, 2), "utf-8");
11676
+ await fs21.mkdir(this.configDir, { recursive: true });
11677
+ await fs21.writeFile(this.configPath, JSON.stringify(config, null, 2), "utf-8");
11975
11678
  } catch (error) {
11976
11679
  throw new Error(
11977
11680
  `Failed to save project config: ${error instanceof Error ? error.message : String(error)}`
@@ -12140,11 +11843,11 @@ function createRulesInitCommand() {
12140
11843
  });
12141
11844
  }
12142
11845
  async function applyCursorFormat(bundle) {
12143
- const rulesDir = join13(process.cwd(), ".cursor", "rules");
12144
- await fs23.mkdir(rulesDir, { recursive: true });
11846
+ const rulesDir = join12(process.cwd(), ".cursor", "rules");
11847
+ await fs22.mkdir(rulesDir, { recursive: true });
12145
11848
  for (const [filename, content] of Object.entries(bundle.files)) {
12146
- const filePath = join13(rulesDir, filename);
12147
- await fs23.writeFile(filePath, content, "utf-8");
11849
+ const filePath = join12(rulesDir, filename);
11850
+ await fs22.writeFile(filePath, content, "utf-8");
12148
11851
  console.log(`\u2713 Created .cursor/rules/${filename}`);
12149
11852
  }
12150
11853
  }
@@ -12171,14 +11874,14 @@ async function applyAgentsMdFormat(bundle) {
12171
11874
  }
12172
11875
  }
12173
11876
  const agentsMd = sections.join("\n");
12174
- await fs23.writeFile("AGENTS.md", agentsMd, "utf-8");
11877
+ await fs22.writeFile("AGENTS.md", agentsMd, "utf-8");
12175
11878
  console.log("\u2713 Created AGENTS.md");
12176
11879
  }
12177
11880
 
12178
11881
  // src/commands/rules/apply.ts
12179
11882
  import { Command as Command64 } from "commander";
12180
- import { promises as fs25 } from "fs";
12181
- import { join as join15 } from "path";
11883
+ import { promises as fs24 } from "fs";
11884
+ import { join as join14 } from "path";
12182
11885
  import { search, confirm as confirm11, checkbox as checkbox5 } from "@inquirer/prompts";
12183
11886
 
12184
11887
  // src/services/rules-generator.service.ts
@@ -12469,8 +12172,8 @@ Follow all instructions and patterns defined in AGENTS.md above.
12469
12172
  };
12470
12173
 
12471
12174
  // src/services/backup.service.ts
12472
- import { promises as fs24 } from "fs";
12473
- import { join as join14, dirname } from "path";
12175
+ import { promises as fs23 } from "fs";
12176
+ import { join as join13, dirname } from "path";
12474
12177
  var BackupService = class {
12475
12178
  backupDir = ".jai1/backups";
12476
12179
  /**
@@ -12478,7 +12181,7 @@ var BackupService = class {
12478
12181
  */
12479
12182
  async createBackup(ides, presetSlug) {
12480
12183
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
12481
- const backupPath = join14(this.backupDir, timestamp);
12184
+ const backupPath = join13(this.backupDir, timestamp);
12482
12185
  const backedUpFiles = [];
12483
12186
  let hasContent = false;
12484
12187
  for (const ideId of ides) {
@@ -12487,7 +12190,7 @@ var BackupService = class {
12487
12190
  console.warn(`Unknown IDE format: ${ideId}, skipping backup`);
12488
12191
  continue;
12489
12192
  }
12490
- const rulesPath = format.rulesPath === "." ? process.cwd() : join14(process.cwd(), format.rulesPath);
12193
+ const rulesPath = format.rulesPath === "." ? process.cwd() : join13(process.cwd(), format.rulesPath);
12491
12194
  try {
12492
12195
  const exists = await this.pathExists(rulesPath);
12493
12196
  if (!exists) {
@@ -12500,19 +12203,19 @@ var BackupService = class {
12500
12203
  await this.backupSingleFile("GEMINI.md", backupPath, ideId, backedUpFiles);
12501
12204
  hasContent = true;
12502
12205
  } else {
12503
- const stats = await fs24.stat(rulesPath);
12206
+ const stats = await fs23.stat(rulesPath);
12504
12207
  if (stats.isDirectory()) {
12505
- const files = await fs24.readdir(rulesPath);
12208
+ const files = await fs23.readdir(rulesPath);
12506
12209
  for (const file of files) {
12507
12210
  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);
12211
+ const originalPath = join13(rulesPath, file);
12212
+ const relativePath = join13(format.rulesPath, file);
12213
+ const destPath = join13(backupPath, ideId, file);
12214
+ await fs23.mkdir(dirname(destPath), { recursive: true });
12215
+ await fs23.copyFile(originalPath, destPath);
12513
12216
  backedUpFiles.push({
12514
12217
  originalPath: relativePath,
12515
- backupPath: join14(ideId, file),
12218
+ backupPath: join13(ideId, file),
12516
12219
  ide: ideId
12517
12220
  });
12518
12221
  hasContent = true;
@@ -12533,9 +12236,9 @@ var BackupService = class {
12533
12236
  ides,
12534
12237
  files: backedUpFiles
12535
12238
  };
12536
- await fs24.mkdir(backupPath, { recursive: true });
12537
- await fs24.writeFile(
12538
- join14(backupPath, "metadata.json"),
12239
+ await fs23.mkdir(backupPath, { recursive: true });
12240
+ await fs23.writeFile(
12241
+ join13(backupPath, "metadata.json"),
12539
12242
  JSON.stringify(metadata, null, 2),
12540
12243
  "utf-8"
12541
12244
  );
@@ -12545,18 +12248,18 @@ var BackupService = class {
12545
12248
  * Backup a single file (for AGENTS.md, GEMINI.md)
12546
12249
  */
12547
12250
  async backupSingleFile(filename, backupPath, ideId, backedUpFiles) {
12548
- const originalPath = join14(process.cwd(), filename);
12251
+ const originalPath = join13(process.cwd(), filename);
12549
12252
  try {
12550
12253
  const exists = await this.pathExists(originalPath);
12551
12254
  if (!exists) {
12552
12255
  return;
12553
12256
  }
12554
- const destPath = join14(backupPath, ideId, filename);
12555
- await fs24.mkdir(dirname(destPath), { recursive: true });
12556
- await fs24.copyFile(originalPath, destPath);
12257
+ const destPath = join13(backupPath, ideId, filename);
12258
+ await fs23.mkdir(dirname(destPath), { recursive: true });
12259
+ await fs23.copyFile(originalPath, destPath);
12557
12260
  backedUpFiles.push({
12558
12261
  originalPath: filename,
12559
- backupPath: join14(ideId, filename),
12262
+ backupPath: join13(ideId, filename),
12560
12263
  ide: ideId
12561
12264
  });
12562
12265
  } catch (error) {
@@ -12566,16 +12269,16 @@ var BackupService = class {
12566
12269
  * Restore files from a backup
12567
12270
  */
12568
12271
  async restoreBackup(backupPath) {
12569
- const metadataPath = join14(backupPath, "metadata.json");
12570
- const metadataContent = await fs24.readFile(metadataPath, "utf-8");
12272
+ const metadataPath = join13(backupPath, "metadata.json");
12273
+ const metadataContent = await fs23.readFile(metadataPath, "utf-8");
12571
12274
  const metadata = JSON.parse(metadataContent);
12572
12275
  console.log(`
12573
12276
  Restoring backup from ${metadata.timestamp}...`);
12574
12277
  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);
12278
+ const sourcePath = join13(backupPath, file.backupPath);
12279
+ const destPath = join13(process.cwd(), file.originalPath);
12280
+ await fs23.mkdir(dirname(destPath), { recursive: true });
12281
+ await fs23.copyFile(sourcePath, destPath);
12579
12282
  console.log(`\u2713 Restored ${file.originalPath}`);
12580
12283
  }
12581
12284
  console.log("\n\u2705 Backup restored successfully!");
@@ -12585,18 +12288,18 @@ Restoring backup from ${metadata.timestamp}...`);
12585
12288
  */
12586
12289
  async listBackups() {
12587
12290
  try {
12588
- const backupDirPath = join14(process.cwd(), this.backupDir);
12291
+ const backupDirPath = join13(process.cwd(), this.backupDir);
12589
12292
  const exists = await this.pathExists(backupDirPath);
12590
12293
  if (!exists) {
12591
12294
  return [];
12592
12295
  }
12593
- const entries = await fs24.readdir(backupDirPath, { withFileTypes: true });
12296
+ const entries = await fs23.readdir(backupDirPath, { withFileTypes: true });
12594
12297
  const backups = [];
12595
12298
  for (const entry of entries) {
12596
12299
  if (entry.isDirectory()) {
12597
- const metadataPath = join14(backupDirPath, entry.name, "metadata.json");
12300
+ const metadataPath = join13(backupDirPath, entry.name, "metadata.json");
12598
12301
  try {
12599
- const metadataContent = await fs24.readFile(metadataPath, "utf-8");
12302
+ const metadataContent = await fs23.readFile(metadataPath, "utf-8");
12600
12303
  const metadata = JSON.parse(metadataContent);
12601
12304
  backups.push(metadata);
12602
12305
  } catch {
@@ -12613,7 +12316,7 @@ Restoring backup from ${metadata.timestamp}...`);
12613
12316
  * Delete a specific backup
12614
12317
  */
12615
12318
  async deleteBackup(timestamp) {
12616
- const backupPath = join14(process.cwd(), this.backupDir, timestamp);
12319
+ const backupPath = join13(process.cwd(), this.backupDir, timestamp);
12617
12320
  await this.deleteDirectory(backupPath);
12618
12321
  }
12619
12322
  /**
@@ -12645,7 +12348,7 @@ Restoring backup from ${metadata.timestamp}...`);
12645
12348
  */
12646
12349
  async pathExists(path13) {
12647
12350
  try {
12648
- await fs24.access(path13);
12351
+ await fs23.access(path13);
12649
12352
  return true;
12650
12353
  } catch {
12651
12354
  return false;
@@ -12660,16 +12363,16 @@ Restoring backup from ${metadata.timestamp}...`);
12660
12363
  if (!exists) {
12661
12364
  return;
12662
12365
  }
12663
- const entries = await fs24.readdir(path13, { withFileTypes: true });
12366
+ const entries = await fs23.readdir(path13, { withFileTypes: true });
12664
12367
  for (const entry of entries) {
12665
- const fullPath = join14(path13, entry.name);
12368
+ const fullPath = join13(path13, entry.name);
12666
12369
  if (entry.isDirectory()) {
12667
12370
  await this.deleteDirectory(fullPath);
12668
12371
  } else {
12669
- await fs24.unlink(fullPath);
12372
+ await fs23.unlink(fullPath);
12670
12373
  }
12671
12374
  }
12672
- await fs24.rmdir(path13);
12375
+ await fs23.rmdir(path13);
12673
12376
  } catch (error) {
12674
12377
  }
12675
12378
  }
@@ -12677,14 +12380,14 @@ Restoring backup from ${metadata.timestamp}...`);
12677
12380
  * Get backup directory path
12678
12381
  */
12679
12382
  getBackupDir() {
12680
- return join14(process.cwd(), this.backupDir);
12383
+ return join13(process.cwd(), this.backupDir);
12681
12384
  }
12682
12385
  /**
12683
12386
  * Ensure backup directory exists
12684
12387
  */
12685
12388
  async ensureBackupDir() {
12686
- const backupDirPath = join14(process.cwd(), this.backupDir);
12687
- await fs24.mkdir(backupDirPath, { recursive: true });
12389
+ const backupDirPath = join13(process.cwd(), this.backupDir);
12390
+ await fs23.mkdir(backupDirPath, { recursive: true });
12688
12391
  }
12689
12392
  };
12690
12393
 
@@ -12856,21 +12559,21 @@ function createRulesApplyCommand() {
12856
12559
  }
12857
12560
  }
12858
12561
  console.log("\n\u{1F4DD} Applying preset...\n");
12859
- const rulePresetDir = join15(process.cwd(), ".jai1", "rule-preset");
12562
+ const rulePresetDir = join14(process.cwd(), ".jai1", "rule-preset");
12860
12563
  try {
12861
- await fs25.rm(rulePresetDir, { recursive: true, force: true });
12564
+ await fs24.rm(rulePresetDir, { recursive: true, force: true });
12862
12565
  } catch {
12863
12566
  }
12864
- await fs25.mkdir(rulePresetDir, { recursive: true });
12865
- await fs25.writeFile(
12866
- join15(rulePresetDir, "preset.json"),
12567
+ await fs24.mkdir(rulePresetDir, { recursive: true });
12568
+ await fs24.writeFile(
12569
+ join14(rulePresetDir, "preset.json"),
12867
12570
  JSON.stringify(bundle.preset, null, 2),
12868
12571
  "utf-8"
12869
12572
  );
12870
12573
  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");
12574
+ const filePath = join14(rulePresetDir, filename);
12575
+ await fs24.mkdir(join14(filePath, ".."), { recursive: true });
12576
+ await fs24.writeFile(filePath, content, "utf-8");
12874
12577
  }
12875
12578
  console.log(`\u2713 Saved preset to .jai1/rule-preset/`);
12876
12579
  const allGeneratedFiles = [];
@@ -12878,9 +12581,9 @@ function createRulesApplyCommand() {
12878
12581
  try {
12879
12582
  const files = generatorService.generateForIde(bundle, ideId);
12880
12583
  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");
12584
+ const fullPath = join14(process.cwd(), file.path);
12585
+ await fs24.mkdir(join14(fullPath, ".."), { recursive: true });
12586
+ await fs24.writeFile(fullPath, file.content, "utf-8");
12884
12587
  console.log(`\u2713 [${ideId}] ${file.path}`);
12885
12588
  allGeneratedFiles.push({
12886
12589
  ide: ideId,
@@ -12986,7 +12689,7 @@ function createRulesApplyCommand() {
12986
12689
 
12987
12690
  // src/commands/rules/restore.ts
12988
12691
  import { Command as Command65 } from "commander";
12989
- import { join as join16 } from "path";
12692
+ import { join as join15 } from "path";
12990
12693
  import { select as select5, confirm as confirm12 } from "@inquirer/prompts";
12991
12694
  function createRulesRestoreCommand() {
12992
12695
  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 +12736,7 @@ function createRulesRestoreCommand() {
13033
12736
  }
13034
12737
  console.log("\n\u{1F504} Restoring backup...\n");
13035
12738
  try {
13036
- const backupPath = join16(backupService.getBackupDir(), selectedBackup.timestamp);
12739
+ const backupPath = join15(backupService.getBackupDir(), selectedBackup.timestamp);
13037
12740
  await backupService.restoreBackup(backupPath);
13038
12741
  console.log("\n\u2705 Backup restored successfully!\n");
13039
12742
  console.log("\u{1F4A1} Tip: Your IDE may need to be restarted to pick up the changes.");
@@ -13059,17 +12762,17 @@ function formatTimestamp(timestamp) {
13059
12762
 
13060
12763
  // src/commands/rules/sync.ts
13061
12764
  import { Command as Command66 } from "commander";
13062
- import { promises as fs26 } from "fs";
13063
- import { join as join17 } from "path";
12765
+ import { promises as fs25 } from "fs";
12766
+ import { join as join16 } from "path";
13064
12767
  import { checkbox as checkbox6, confirm as confirm13, Separator } from "@inquirer/prompts";
13065
12768
  function createRulesSyncCommand() {
13066
12769
  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");
12770
+ const rulePresetDir = join16(process.cwd(), ".jai1", "rule-preset");
12771
+ const presetJsonPath = join16(rulePresetDir, "preset.json");
13069
12772
  let presetExists = false;
13070
12773
  let presetData = null;
13071
12774
  try {
13072
- const presetContent = await fs26.readFile(presetJsonPath, "utf-8");
12775
+ const presetContent = await fs25.readFile(presetJsonPath, "utf-8");
13073
12776
  presetData = JSON.parse(presetContent);
13074
12777
  presetExists = true;
13075
12778
  } catch {
@@ -13199,11 +12902,11 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
13199
12902
  throw new Error(`Failed to fetch preset: ${presetResponse.statusText}`);
13200
12903
  }
13201
12904
  const bundle = await presetResponse.json();
13202
- const files = await fs26.readdir(rulePresetDir);
12905
+ const files = await fs25.readdir(rulePresetDir);
13203
12906
  for (const file of files) {
13204
12907
  if (file.endsWith(".mdc") || file.endsWith(".md")) {
13205
- const filePath = join17(rulePresetDir, file);
13206
- const content = await fs26.readFile(filePath, "utf-8");
12908
+ const filePath = join16(rulePresetDir, file);
12909
+ const content = await fs25.readFile(filePath, "utf-8");
13207
12910
  bundle.files[file] = content;
13208
12911
  }
13209
12912
  }
@@ -13217,9 +12920,9 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
13217
12920
  }
13218
12921
  const files2 = generatorService.generateForIde(bundle, ideId);
13219
12922
  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");
12923
+ const fullPath = join16(process.cwd(), file.path);
12924
+ await fs25.mkdir(join16(fullPath, ".."), { recursive: true });
12925
+ await fs25.writeFile(fullPath, file.content, "utf-8");
13223
12926
  }
13224
12927
  console.log(`\u2713 ${format.name} - ${files2.length} files regenerated`);
13225
12928
  } catch (error) {
@@ -13282,8 +12985,8 @@ function buildIdeChoices(currentIdes, detected, suggestions) {
13282
12985
 
13283
12986
  // src/commands/rules/info.ts
13284
12987
  import { Command as Command67 } from "commander";
13285
- import { promises as fs27 } from "fs";
13286
- import { join as join18 } from "path";
12988
+ import { promises as fs26 } from "fs";
12989
+ import { join as join17 } from "path";
13287
12990
  function createRulesInfoCommand() {
13288
12991
  return new Command67("info").description("Show current preset information").option("--json", "Output as JSON").action(async (options) => {
13289
12992
  const projectConfigService = new ProjectConfigService();
@@ -13298,14 +13001,14 @@ function createRulesInfoCommand() {
13298
13001
  return;
13299
13002
  }
13300
13003
  console.log("\u{1F4CB} Current Preset Information\n");
13301
- const rulePresetDir = join18(process.cwd(), ".jai1", "rule-preset");
13302
- const presetJsonPath = join18(rulePresetDir, "preset.json");
13004
+ const rulePresetDir = join17(process.cwd(), ".jai1", "rule-preset");
13005
+ const presetJsonPath = join17(rulePresetDir, "preset.json");
13303
13006
  let presetMetadata = null;
13304
13007
  let presetFiles = [];
13305
13008
  try {
13306
- const presetContent = await fs27.readFile(presetJsonPath, "utf-8");
13009
+ const presetContent = await fs26.readFile(presetJsonPath, "utf-8");
13307
13010
  presetMetadata = JSON.parse(presetContent);
13308
- const files = await fs27.readdir(rulePresetDir);
13011
+ const files = await fs26.readdir(rulePresetDir);
13309
13012
  presetFiles = files.filter((f) => f.endsWith(".mdc"));
13310
13013
  } catch {
13311
13014
  }
@@ -13366,7 +13069,7 @@ Available Backups (${rulesConfig.backups.length}):`);
13366
13069
  }
13367
13070
  async function checkPathExists(path13) {
13368
13071
  try {
13369
- await fs27.access(join18(process.cwd(), path13));
13072
+ await fs26.access(join17(process.cwd(), path13));
13370
13073
  return true;
13371
13074
  } catch {
13372
13075
  return false;
@@ -13419,9 +13122,723 @@ function createRulesCommand() {
13419
13122
  return rulesCommand;
13420
13123
  }
13421
13124
 
13422
- // src/commands/upgrade.ts
13125
+ // src/commands/skills/index.ts
13126
+ import { Command as Command74 } from "commander";
13127
+ import chalk39 from "chalk";
13128
+
13129
+ // src/commands/skills/find.ts
13423
13130
  import { Command as Command69 } from "commander";
13424
- import { confirm as confirm14 } from "@inquirer/prompts";
13131
+ import chalk34 from "chalk";
13132
+ import Table8 from "cli-table3";
13133
+
13134
+ // src/services/skills.service.ts
13135
+ import { promises as fs27 } from "fs";
13136
+ import { join as join18 } from "path";
13137
+ import { execFile } from "child_process";
13138
+ import { promisify } from "util";
13139
+ var execFileAsync = promisify(execFile);
13140
+ var IDE_SKILL_TARGETS = [
13141
+ { id: "cursor", name: "Cursor", icon: "\u{1F52E}", skillsPath: ".agents/skills" },
13142
+ { id: "windsurf", name: "Windsurf", icon: "\u{1F3C4}", skillsPath: ".windsurf/skills" },
13143
+ { id: "antigravity", name: "Antigravity", icon: "\u{1F680}", skillsPath: ".agent/skills" },
13144
+ { id: "claudecode", name: "Claude Code", icon: "\u{1F916}", skillsPath: ".claude/skills" },
13145
+ { id: "opencode", name: "OpenCode", icon: "\u{1F4BB}", skillsPath: ".agents/skills" }
13146
+ ];
13147
+ var SkillsService = class {
13148
+ componentsService;
13149
+ constructor(projectRoot = process.cwd()) {
13150
+ this.componentsService = new ComponentsService(projectRoot);
13151
+ }
13152
+ /**
13153
+ * Search skills on Jai1 server
13154
+ */
13155
+ async searchFromServer(config, query) {
13156
+ const options = {};
13157
+ if (query) options.search = query;
13158
+ const components = await this.componentsService.list(config, options);
13159
+ return components.filter((c) => c.filepath.startsWith("skills/"));
13160
+ }
13161
+ /**
13162
+ * Install skill from Jai1 server to .jai1/skills/
13163
+ */
13164
+ async installFromServer(config, skillName, targetDir) {
13165
+ const filepath = skillName.startsWith("skills/") ? skillName : `skills/${skillName}`;
13166
+ await this.componentsService.install(config, filepath, targetDir);
13167
+ }
13168
+ /**
13169
+ * List locally installed skills from .jai1/skills/
13170
+ */
13171
+ async listLocal(projectRoot) {
13172
+ const skillsDir = join18(projectRoot, ".jai1", "skills");
13173
+ const skills = [];
13174
+ try {
13175
+ const entries = await fs27.readdir(skillsDir, { withFileTypes: true });
13176
+ for (const entry of entries) {
13177
+ if (!entry.isDirectory()) continue;
13178
+ const skillPath = join18(skillsDir, entry.name);
13179
+ const skillMd = join18(skillPath, "SKILL.md");
13180
+ try {
13181
+ await fs27.access(skillMd);
13182
+ } catch {
13183
+ continue;
13184
+ }
13185
+ const content = await fs27.readFile(skillMd, "utf-8");
13186
+ const { name, description } = this.parseFrontmatter(content);
13187
+ const fileCount = await this.countFiles(skillPath);
13188
+ skills.push({
13189
+ name: name || entry.name,
13190
+ slug: entry.name,
13191
+ description: description || "",
13192
+ path: skillPath,
13193
+ fileCount
13194
+ });
13195
+ }
13196
+ } catch {
13197
+ }
13198
+ return skills.sort((a, b) => a.slug.localeCompare(b.slug));
13199
+ }
13200
+ /**
13201
+ * Get detailed info for a single skill
13202
+ */
13203
+ async getSkillInfo(projectRoot, skillName) {
13204
+ const skillPath = join18(projectRoot, ".jai1", "skills", skillName);
13205
+ const skillMd = join18(skillPath, "SKILL.md");
13206
+ try {
13207
+ await fs27.access(skillMd);
13208
+ } catch {
13209
+ return null;
13210
+ }
13211
+ const content = await fs27.readFile(skillMd, "utf-8");
13212
+ const { name, description } = this.parseFrontmatter(content);
13213
+ const fileCount = await this.countFiles(skillPath);
13214
+ return {
13215
+ name: name || skillName,
13216
+ slug: skillName,
13217
+ description: description || "",
13218
+ path: skillPath,
13219
+ fileCount
13220
+ };
13221
+ }
13222
+ /**
13223
+ * Search skills via npm `skills` package (wrapper)
13224
+ */
13225
+ async npmSkillsFind(query) {
13226
+ try {
13227
+ const { stdout } = await execFileAsync("npx", ["-y", "skills", "find", query], {
13228
+ timeout: 3e4
13229
+ });
13230
+ return stdout;
13231
+ } catch (error) {
13232
+ throw new Error(
13233
+ `npm skills find failed: ${error instanceof Error ? error.message : String(error)}`
13234
+ );
13235
+ }
13236
+ }
13237
+ /**
13238
+ * Install skill via npm `skills` package (wrapper)
13239
+ *
13240
+ * Source format: "owner/repo" or "owner/repo@skill"
13241
+ * Runs: npx skills add owner/repo [--skill name] --copy --yes
13242
+ * Then copies installed skills into .jai1/skills/
13243
+ */
13244
+ async npmSkillsAdd(source, projectRoot) {
13245
+ const { repo, skill } = this.parseSkillshSource(source);
13246
+ const args = ["-y", "skills", "add", repo, "--copy", "--yes"];
13247
+ if (skill) {
13248
+ args.push("--skill", skill);
13249
+ }
13250
+ try {
13251
+ const { stdout } = await execFileAsync("npx", args, {
13252
+ timeout: 6e4,
13253
+ cwd: projectRoot
13254
+ });
13255
+ await this.copySkillshResultsToJai1(projectRoot, skill);
13256
+ return stdout;
13257
+ } catch (error) {
13258
+ throw new Error(
13259
+ `skills.sh add failed: ${error instanceof Error ? error.message : String(error)}`
13260
+ );
13261
+ }
13262
+ }
13263
+ /**
13264
+ * Parse skills.sh source format: "owner/repo@skill" → { repo, skill }
13265
+ */
13266
+ parseSkillshSource(source) {
13267
+ const atIndex = source.lastIndexOf("@");
13268
+ if (atIndex > 0 && !source.substring(atIndex + 1).includes("/")) {
13269
+ return {
13270
+ repo: source.substring(0, atIndex),
13271
+ skill: source.substring(atIndex + 1)
13272
+ };
13273
+ }
13274
+ return { repo: source };
13275
+ }
13276
+ /**
13277
+ * After npx skills add, copy newly installed skills from agent dirs into .jai1/skills/
13278
+ */
13279
+ async copySkillshResultsToJai1(projectRoot, specificSkill) {
13280
+ const jai1SkillsDir = join18(projectRoot, ".jai1", "skills");
13281
+ await fs27.mkdir(jai1SkillsDir, { recursive: true });
13282
+ const agentDirs = [".agents/skills", ".agent/skills", ".claude/skills", ".windsurf/skills", ".cursor/skills"];
13283
+ for (const agentDir of agentDirs) {
13284
+ const fullDir = join18(projectRoot, agentDir);
13285
+ try {
13286
+ const entries = await fs27.readdir(fullDir, { withFileTypes: true });
13287
+ for (const entry of entries) {
13288
+ if (!entry.isDirectory()) continue;
13289
+ if (specificSkill && entry.name !== specificSkill) continue;
13290
+ const srcSkill = join18(fullDir, entry.name);
13291
+ const skillMd = join18(srcSkill, "SKILL.md");
13292
+ try {
13293
+ await fs27.access(skillMd);
13294
+ } catch {
13295
+ continue;
13296
+ }
13297
+ const targetSkill = join18(jai1SkillsDir, entry.name);
13298
+ try {
13299
+ await fs27.access(targetSkill);
13300
+ } catch {
13301
+ await fs27.mkdir(targetSkill, { recursive: true });
13302
+ await this.copyDir(srcSkill, targetSkill);
13303
+ }
13304
+ }
13305
+ } catch {
13306
+ }
13307
+ }
13308
+ }
13309
+ /**
13310
+ * Get available IDE skill targets
13311
+ */
13312
+ getIDETargets() {
13313
+ return IDE_SKILL_TARGETS;
13314
+ }
13315
+ /**
13316
+ * Get IDE skill target by ID
13317
+ */
13318
+ getIDETarget(id) {
13319
+ return IDE_SKILL_TARGETS.find((t) => t.id === id);
13320
+ }
13321
+ /**
13322
+ * Sync skills from .jai1/skills/ to IDE directories
13323
+ */
13324
+ async syncToIdes(projectRoot, ides, skillSlugs, onProgress) {
13325
+ const localSkills = await this.listLocal(projectRoot);
13326
+ const skillsToSync = skillSlugs ? localSkills.filter((s) => skillSlugs.includes(s.slug)) : localSkills;
13327
+ if (skillsToSync.length === 0) {
13328
+ return { created: 0, updated: 0, errors: 0 };
13329
+ }
13330
+ let created = 0;
13331
+ let updated = 0;
13332
+ let errors = 0;
13333
+ for (const ide of ides) {
13334
+ const target = this.getIDETarget(ide);
13335
+ if (!target) continue;
13336
+ for (const skill of skillsToSync) {
13337
+ const targetPath = join18(projectRoot, target.skillsPath, skill.slug);
13338
+ try {
13339
+ let status = "created";
13340
+ try {
13341
+ await fs27.access(targetPath);
13342
+ status = "updated";
13343
+ await fs27.rm(targetPath, { recursive: true, force: true });
13344
+ } catch {
13345
+ }
13346
+ await fs27.mkdir(targetPath, { recursive: true });
13347
+ await this.copyDir(skill.path, targetPath);
13348
+ if (status === "created") created++;
13349
+ else updated++;
13350
+ onProgress?.({
13351
+ ide: target.name,
13352
+ skill: skill.slug,
13353
+ status,
13354
+ path: targetPath
13355
+ });
13356
+ } catch (error) {
13357
+ errors++;
13358
+ onProgress?.({
13359
+ ide: target.name,
13360
+ skill: skill.slug,
13361
+ status: "error",
13362
+ path: targetPath,
13363
+ error: error instanceof Error ? error.message : String(error)
13364
+ });
13365
+ }
13366
+ }
13367
+ }
13368
+ return { created, updated, errors };
13369
+ }
13370
+ /**
13371
+ * Parse YAML frontmatter from SKILL.md
13372
+ */
13373
+ parseFrontmatter(content) {
13374
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
13375
+ if (!match) {
13376
+ const headingMatch = content.match(/^#\s+(.+)/m);
13377
+ return {
13378
+ name: headingMatch?.[1] || "",
13379
+ description: ""
13380
+ };
13381
+ }
13382
+ const frontmatter = match[1] ?? "";
13383
+ let name = "";
13384
+ let description = "";
13385
+ for (const line of frontmatter.split("\n")) {
13386
+ const nameMatch = line.match(/^name:\s*(.+)/);
13387
+ if (nameMatch?.[1]) name = nameMatch[1].trim().replace(/^["']|["']$/g, "");
13388
+ const descMatch = line.match(/^description:\s*(.+)/);
13389
+ if (descMatch?.[1]) description = descMatch[1].trim().replace(/^["']|["']$/g, "");
13390
+ }
13391
+ return { name, description };
13392
+ }
13393
+ /**
13394
+ * Count files in a directory recursively
13395
+ */
13396
+ async countFiles(dirPath) {
13397
+ let count = 0;
13398
+ const entries = await fs27.readdir(dirPath, { withFileTypes: true });
13399
+ for (const entry of entries) {
13400
+ if (entry.isDirectory()) {
13401
+ count += await this.countFiles(join18(dirPath, entry.name));
13402
+ } else {
13403
+ count++;
13404
+ }
13405
+ }
13406
+ return count;
13407
+ }
13408
+ /**
13409
+ * Copy directory recursively
13410
+ */
13411
+ async copyDir(source, target) {
13412
+ const entries = await fs27.readdir(source, { withFileTypes: true });
13413
+ for (const entry of entries) {
13414
+ const srcPath = join18(source, entry.name);
13415
+ const tgtPath = join18(target, entry.name);
13416
+ if (entry.isDirectory()) {
13417
+ await fs27.mkdir(tgtPath, { recursive: true });
13418
+ await this.copyDir(srcPath, tgtPath);
13419
+ } else {
13420
+ await fs27.copyFile(srcPath, tgtPath);
13421
+ }
13422
+ }
13423
+ }
13424
+ };
13425
+
13426
+ // src/commands/skills/find.ts
13427
+ function createSkillsFindCommand() {
13428
+ 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) => {
13429
+ const searchNpm = options.skillsh || options.all;
13430
+ const searchServer = !options.skillsh || options.all;
13431
+ if (searchServer) {
13432
+ const configService = new ConfigService();
13433
+ const config = await configService.load();
13434
+ if (!config) {
13435
+ throw new ValidationError('Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.');
13436
+ }
13437
+ console.log(chalk34.cyan("\u{1F50D} \u0110ang t\xECm ki\u1EBFm tr\xEAn Jai1 server..."));
13438
+ console.log();
13439
+ const skillsService = new SkillsService();
13440
+ const results = await skillsService.searchFromServer(config, query);
13441
+ if (results.length === 0) {
13442
+ console.log(chalk34.yellow("Kh\xF4ng t\xECm th\u1EA5y skills n\xE0o tr\xEAn server."));
13443
+ } else {
13444
+ const table = new Table8({
13445
+ head: [
13446
+ chalk34.cyan("T\xEAn"),
13447
+ chalk34.cyan("M\xF4 t\u1EA3"),
13448
+ chalk34.cyan("Version"),
13449
+ chalk34.cyan("Downloads")
13450
+ ],
13451
+ style: { head: [], border: ["gray"] },
13452
+ colWidths: [25, 40, 10, 12]
13453
+ });
13454
+ for (const skill of results) {
13455
+ const name = skill.filepath.replace("skills/", "");
13456
+ table.push([
13457
+ chalk34.white(name),
13458
+ chalk34.dim((skill.description || "").slice(0, 38)),
13459
+ chalk34.green(skill.version || "-"),
13460
+ chalk34.dim(String(skill.downloads || 0))
13461
+ ]);
13462
+ }
13463
+ console.log(chalk34.bold(`\u{1F4E6} Jai1 Server (${results.length} k\u1EBFt qu\u1EA3)`));
13464
+ console.log(table.toString());
13465
+ console.log();
13466
+ }
13467
+ }
13468
+ if (searchNpm) {
13469
+ console.log(chalk34.cyan("\u{1F50D} \u0110ang t\xECm ki\u1EBFm tr\xEAn npm skills..."));
13470
+ console.log();
13471
+ const skillsService = new SkillsService();
13472
+ try {
13473
+ const output = await skillsService.npmSkillsFind(query);
13474
+ console.log(chalk34.bold("\u{1F310} npm Skills Registry"));
13475
+ console.log(output);
13476
+ } catch (error) {
13477
+ console.log(chalk34.yellow(
13478
+ `Kh\xF4ng th\u1EC3 t\xECm ki\u1EBFm tr\xEAn npm: ${error instanceof Error ? error.message : String(error)}`
13479
+ ));
13480
+ }
13481
+ }
13482
+ if (searchServer && !searchNpm) {
13483
+ console.log(chalk34.dim('\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t t\u1EEB server'));
13484
+ } else if (searchNpm && !searchServer) {
13485
+ console.log(chalk34.dim('\u{1F4A1} D\xF9ng "j skills add <owner/repo@skill> --skillsh" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13486
+ } else {
13487
+ console.log(chalk34.dim("\u{1F4A1} C\xE0i \u0111\u1EB7t:"));
13488
+ console.log(chalk34.dim(" Server: j skills add <t\xEAn>"));
13489
+ console.log(chalk34.dim(" Skills.sh: j skills add <owner/repo@skill> --skillsh"));
13490
+ }
13491
+ });
13492
+ }
13493
+
13494
+ // src/commands/skills/add.ts
13495
+ import { Command as Command70 } from "commander";
13496
+ import { join as join19 } from "path";
13497
+ import chalk35 from "chalk";
13498
+ import { checkbox as checkbox7 } from "@inquirer/prompts";
13499
+ function createSkillsAddCommand() {
13500
+ 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) => {
13501
+ const skillsService = new SkillsService();
13502
+ const projectRoot = process.cwd();
13503
+ const headless = options.yes === true;
13504
+ if (options.skillsh) {
13505
+ console.log(chalk35.cyan(`\u{1F310} \u0110ang c\xE0i \u0111\u1EB7t skill t\u1EEB npm: ${name}...`));
13506
+ console.log();
13507
+ const output = await skillsService.npmSkillsAdd(name, projectRoot);
13508
+ console.log(output);
13509
+ console.log(chalk35.green("\u2705 C\xE0i \u0111\u1EB7t t\u1EEB npm th\xE0nh c\xF4ng!"));
13510
+ } else {
13511
+ const configService = new ConfigService();
13512
+ const config = await configService.load();
13513
+ if (!config) {
13514
+ throw new ValidationError('Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.');
13515
+ }
13516
+ console.log(chalk35.cyan(`\u{1F4E6} \u0110ang c\xE0i \u0111\u1EB7t skill: ${name}...`));
13517
+ console.log();
13518
+ const targetDir = join19(projectRoot, ".jai1");
13519
+ await skillsService.installFromServer(config, name, targetDir);
13520
+ console.log(chalk35.green(`\u2705 \u0110\xE3 c\xE0i \u0111\u1EB7t skill "${name}" v\xE0o .jai1/skills/${name}/`));
13521
+ }
13522
+ console.log();
13523
+ if (options.sync) {
13524
+ let selectedIdes;
13525
+ if (options.all) {
13526
+ selectedIdes = skillsService.getIDETargets().map((t) => t.id);
13527
+ } else if (options.ides && options.ides.length > 0) {
13528
+ selectedIdes = options.ides;
13529
+ } else if (headless) {
13530
+ throw new Error(
13531
+ "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"
13532
+ );
13533
+ } else {
13534
+ const targets = skillsService.getIDETargets();
13535
+ selectedIdes = await checkbox7({
13536
+ message: "Sync sang IDE(s) n\xE0o?",
13537
+ choices: targets.map((t) => ({
13538
+ name: `${t.icon} ${t.name}`,
13539
+ value: t.id
13540
+ })),
13541
+ theme: checkboxTheme
13542
+ });
13543
+ }
13544
+ if (selectedIdes.length > 0) {
13545
+ console.log(chalk35.cyan("\u{1F504} \u0110ang sync sang IDE(s)..."));
13546
+ console.log();
13547
+ const slug = name.includes("/") ? name.split("/").pop() : name;
13548
+ const result = await skillsService.syncToIdes(
13549
+ projectRoot,
13550
+ selectedIdes,
13551
+ [slug],
13552
+ (res) => {
13553
+ const icon = res.status === "created" ? "\u2713" : res.status === "updated" ? "\u21BB" : "\u2717";
13554
+ console.log(` ${icon} ${res.ide}: ${res.skill} \u2192 ${res.path}`);
13555
+ }
13556
+ );
13557
+ console.log();
13558
+ console.log(chalk35.green(`\u2705 Sync ho\xE0n t\u1EA5t! Created: ${result.created}, Updated: ${result.updated}`));
13559
+ if (result.errors > 0) {
13560
+ console.log(chalk35.yellow(`\u26A0\uFE0F Errors: ${result.errors}`));
13561
+ }
13562
+ }
13563
+ } else {
13564
+ console.log(chalk35.dim('\u{1F4A1} Ch\u1EA1y "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
13565
+ console.log(chalk35.dim(' ho\u1EB7c "j ide sync" \u0111\u1EC3 sync to\xE0n b\u1ED9 .jai1/'));
13566
+ }
13567
+ });
13568
+ }
13569
+
13570
+ // src/commands/skills/list.ts
13571
+ import { Command as Command71 } from "commander";
13572
+ import chalk36 from "chalk";
13573
+ import Table9 from "cli-table3";
13574
+ function createSkillsListCommand() {
13575
+ 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) => {
13576
+ const skillsService = new SkillsService();
13577
+ if (options.available) {
13578
+ const configService = new ConfigService();
13579
+ const config = await configService.load();
13580
+ if (!config) {
13581
+ throw new ValidationError('Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.');
13582
+ }
13583
+ console.log(chalk36.cyan("\u{1F4E6} \u0110ang t\u1EA3i danh s\xE1ch skills t\u1EEB server..."));
13584
+ console.log();
13585
+ const results = await skillsService.searchFromServer(config, options.search);
13586
+ if (results.length === 0) {
13587
+ console.log(chalk36.yellow("Kh\xF4ng t\xECm th\u1EA5y skills n\xE0o."));
13588
+ return;
13589
+ }
13590
+ const table = new Table9({
13591
+ head: [
13592
+ chalk36.cyan("T\xEAn"),
13593
+ chalk36.cyan("M\xF4 t\u1EA3"),
13594
+ chalk36.cyan("Version"),
13595
+ chalk36.cyan("Downloads")
13596
+ ],
13597
+ style: { head: [], border: ["gray"] },
13598
+ colWidths: [28, 40, 10, 12]
13599
+ });
13600
+ for (const skill of results) {
13601
+ const name = skill.filepath.replace("skills/", "");
13602
+ table.push([
13603
+ chalk36.white(name),
13604
+ chalk36.dim((skill.description || "").slice(0, 38)),
13605
+ chalk36.green(skill.version || "-"),
13606
+ chalk36.dim(String(skill.downloads || 0))
13607
+ ]);
13608
+ }
13609
+ console.log(table.toString());
13610
+ console.log();
13611
+ console.log(chalk36.dim(`T\u1ED5ng c\u1ED9ng: ${results.length} skill(s)`));
13612
+ console.log(chalk36.dim('\n\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13613
+ } else {
13614
+ const projectRoot = process.cwd();
13615
+ const skills = await skillsService.listLocal(projectRoot);
13616
+ if (skills.length === 0) {
13617
+ console.log(chalk36.yellow("Ch\u01B0a c\xF3 skills n\xE0o \u0111\u01B0\u1EE3c c\xE0i \u0111\u1EB7t."));
13618
+ console.log();
13619
+ console.log(chalk36.dim('\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13620
+ console.log(chalk36.dim(' ho\u1EB7c "j skills list --available" \u0111\u1EC3 xem skills c\xF3 s\u1EB5n'));
13621
+ return;
13622
+ }
13623
+ console.log(chalk36.bold.cyan("\u{1F6E0} Skills \u0111\xE3 c\xE0i \u0111\u1EB7t"));
13624
+ console.log();
13625
+ const table = new Table9({
13626
+ head: [
13627
+ chalk36.cyan("T\xEAn"),
13628
+ chalk36.cyan("M\xF4 t\u1EA3"),
13629
+ chalk36.cyan("Files")
13630
+ ],
13631
+ style: { head: [], border: ["gray"] },
13632
+ colWidths: [28, 45, 8]
13633
+ });
13634
+ for (const skill of skills) {
13635
+ table.push([
13636
+ chalk36.white(skill.slug),
13637
+ chalk36.dim(skill.description.slice(0, 43)),
13638
+ chalk36.dim(String(skill.fileCount))
13639
+ ]);
13640
+ }
13641
+ console.log(table.toString());
13642
+ console.log();
13643
+ console.log(chalk36.dim(`T\u1ED5ng c\u1ED9ng: ${skills.length} skill(s)`));
13644
+ console.log(chalk36.dim('\n\u{1F4A1} D\xF9ng "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
13645
+ }
13646
+ });
13647
+ }
13648
+
13649
+ // src/commands/skills/info.ts
13650
+ import { Command as Command72 } from "commander";
13651
+ import chalk37 from "chalk";
13652
+ function createSkillsInfoCommand() {
13653
+ 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) => {
13654
+ const skillsService = new SkillsService();
13655
+ if (options.server) {
13656
+ const configService = new ConfigService();
13657
+ const config = await configService.load();
13658
+ if (!config) {
13659
+ console.log(chalk37.red('\u274C Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.'));
13660
+ process.exit(1);
13661
+ }
13662
+ const filepath = name.startsWith("skills/") ? name : `skills/${name}`;
13663
+ const { ComponentsService: ComponentsService2 } = await import("./components.service-NWAWKII3.js");
13664
+ const componentsService = new ComponentsService2();
13665
+ try {
13666
+ const component = await componentsService.get(config, filepath);
13667
+ console.log(`
13668
+ \u{1F6E0} ${chalk37.bold(component.name || name)}
13669
+ `);
13670
+ console.log(`Filepath: ${component.filepath}`);
13671
+ console.log(`Version: ${component.version}`);
13672
+ console.log(`Downloads: ${component.downloads}`);
13673
+ if (component.description) {
13674
+ console.log(`Description: ${component.description}`);
13675
+ }
13676
+ if (component.tags && component.tags.length > 0) {
13677
+ console.log(`Tags: ${component.tags.join(", ")}`);
13678
+ }
13679
+ console.log(`Type: ${component.contentType}`);
13680
+ console.log();
13681
+ console.log(chalk37.dim('\u{1F4A1} D\xF9ng "j skills add ' + name + '" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13682
+ } catch (error) {
13683
+ console.log(chalk37.red(`\u274C Kh\xF4ng t\xECm th\u1EA5y skill "${name}" tr\xEAn server.`));
13684
+ process.exit(1);
13685
+ }
13686
+ } else {
13687
+ const projectRoot = process.cwd();
13688
+ const skill = await skillsService.getSkillInfo(projectRoot, name);
13689
+ if (!skill) {
13690
+ console.log(chalk37.red(`\u274C Skill "${name}" ch\u01B0a \u0111\u01B0\u1EE3c c\xE0i \u0111\u1EB7t.`));
13691
+ console.log(chalk37.dim('\u{1F4A1} D\xF9ng "j skills info ' + name + ' --server" \u0111\u1EC3 xem tr\xEAn server'));
13692
+ console.log(chalk37.dim(' ho\u1EB7c "j skills add ' + name + '" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13693
+ process.exit(1);
13694
+ }
13695
+ console.log(`
13696
+ \u{1F6E0} ${chalk37.bold(skill.name)}
13697
+ `);
13698
+ console.log(`Slug: ${skill.slug}`);
13699
+ console.log(`Description: ${skill.description || chalk37.dim("(none)")}`);
13700
+ console.log(`Path: ${skill.path}`);
13701
+ console.log(`Files: ${skill.fileCount}`);
13702
+ console.log();
13703
+ console.log(chalk37.dim('\u{1F4A1} D\xF9ng "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
13704
+ }
13705
+ });
13706
+ }
13707
+
13708
+ // src/commands/skills/sync.ts
13709
+ import { Command as Command73 } from "commander";
13710
+ import chalk38 from "chalk";
13711
+ import { confirm as confirm15, checkbox as checkbox8 } from "@inquirer/prompts";
13712
+ function createSkillsSyncCommand() {
13713
+ 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) => {
13714
+ const skillsService = new SkillsService();
13715
+ const projectRoot = process.cwd();
13716
+ const headless = options.yes === true;
13717
+ console.log(chalk38.bold.cyan("\n\u{1F504} Sync skills sang IDE(s)\n"));
13718
+ const localSkills = await skillsService.listLocal(projectRoot);
13719
+ if (localSkills.length === 0) {
13720
+ console.log(chalk38.yellow("\u26A0\uFE0F Kh\xF4ng c\xF3 skills n\xE0o trong .jai1/skills/"));
13721
+ console.log(chalk38.dim('\u{1F4A1} Ch\u1EA1y "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t skills tr\u01B0\u1EDBc'));
13722
+ process.exit(1);
13723
+ }
13724
+ console.log(`\u{1F4C1} T\xECm th\u1EA5y ${localSkills.length} skill(s) trong .jai1/skills/`);
13725
+ console.log();
13726
+ let selectedSlugs;
13727
+ if (options.skills && options.skills.length > 0) {
13728
+ selectedSlugs = options.skills;
13729
+ console.log(`\u{1F6E0} Skills: ${selectedSlugs.join(", ")}`);
13730
+ } else {
13731
+ console.log(`\u{1F6E0} Skills: t\u1EA5t c\u1EA3 (${localSkills.length})`);
13732
+ }
13733
+ let selectedIdes;
13734
+ const targets = skillsService.getIDETargets();
13735
+ if (options.all) {
13736
+ selectedIdes = targets.map((t) => t.id);
13737
+ } else if (options.ides && options.ides.length > 0) {
13738
+ selectedIdes = options.ides;
13739
+ } else if (headless) {
13740
+ throw new Error(
13741
+ "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"
13742
+ );
13743
+ } else {
13744
+ selectedIdes = await checkbox8({
13745
+ message: "Ch\u1ECDn IDE(s) \u0111\u1EC3 sync (SPACE \u0111\u1EC3 ch\u1ECDn, ENTER \u0111\u1EC3 x\xE1c nh\u1EADn):",
13746
+ choices: targets.map((t) => ({
13747
+ name: `${t.icon} ${t.name}`,
13748
+ value: t.id
13749
+ })),
13750
+ theme: checkboxTheme
13751
+ });
13752
+ if (selectedIdes.length === 0) {
13753
+ console.log(chalk38.yellow("\n\u26A0\uFE0F Ch\u01B0a ch\u1ECDn IDE n\xE0o!"));
13754
+ process.exit(0);
13755
+ }
13756
+ }
13757
+ const ideNames = selectedIdes.map((id) => targets.find((t) => t.id === id)).filter(Boolean).map((t) => t.name);
13758
+ const skillCount = selectedSlugs ? selectedSlugs.length : localSkills.length;
13759
+ const totalFiles = skillCount * selectedIdes.length;
13760
+ console.log(`
13761
+ \u{1F4CA} Preview:
13762
+ `);
13763
+ console.log(` IDEs: ${ideNames.join(", ")}`);
13764
+ console.log(` Skills: ${skillCount}`);
13765
+ console.log(` Total: ${totalFiles} skill folder(s) s\u1EBD \u0111\u01B0\u1EE3c sync
13766
+ `);
13767
+ if (options.dryRun) {
13768
+ console.log(chalk38.dim("\u{1F50D} DRY RUN - Kh\xF4ng c\xF3 file n\xE0o \u0111\u01B0\u1EE3c ghi\n"));
13769
+ return;
13770
+ }
13771
+ if (!headless) {
13772
+ const confirmed = await confirm15({
13773
+ message: "Ti\u1EBFp t\u1EE5c sync?",
13774
+ default: true
13775
+ });
13776
+ if (!confirmed) {
13777
+ console.log(chalk38.yellow("\n\u274C \u0110\xE3 h\u1EE7y sync.\n"));
13778
+ process.exit(0);
13779
+ }
13780
+ }
13781
+ console.log(chalk38.cyan("\n\u{1F504} \u0110ang sync...\n"));
13782
+ const result = await skillsService.syncToIdes(
13783
+ projectRoot,
13784
+ selectedIdes,
13785
+ selectedSlugs,
13786
+ (res) => {
13787
+ const icon = res.status === "created" ? "\u2713" : res.status === "updated" ? "\u21BB" : "\u2717";
13788
+ const statusColor = res.status === "error" ? chalk38.red : chalk38.green;
13789
+ console.log(` ${statusColor(icon)} ${res.ide}: ${res.skill} \u2192 ${chalk38.dim(res.path)}`);
13790
+ if (res.status === "error" && res.error) {
13791
+ console.log(` ${chalk38.red("Error:")} ${res.error}`);
13792
+ }
13793
+ }
13794
+ );
13795
+ console.log(chalk38.green("\n\u2705 Sync ho\xE0n t\u1EA5t!\n"));
13796
+ console.log(` Created: ${result.created}`);
13797
+ console.log(` Updated: ${result.updated}`);
13798
+ if (result.errors > 0) {
13799
+ console.log(` Errors: ${result.errors}`);
13800
+ }
13801
+ console.log();
13802
+ });
13803
+ }
13804
+
13805
+ // src/commands/skills/index.ts
13806
+ function showSkillsHelp() {
13807
+ const cli = getCliName();
13808
+ console.log(chalk39.bold.cyan("\u{1F6E0} " + cli + " skills") + chalk39.dim(" - Qu\u1EA3n l\xFD agent skills"));
13809
+ console.log();
13810
+ console.log(chalk39.bold("C\xE1c l\u1EC7nh:"));
13811
+ console.log(` ${chalk39.cyan("find")} T\xECm ki\u1EBFm skills tr\xEAn server ho\u1EB7c npm`);
13812
+ console.log(` ${chalk39.cyan("add")} C\xE0i \u0111\u1EB7t skill v\xE0o .jai1/skills/`);
13813
+ console.log(` ${chalk39.cyan("list")} Li\u1EC7t k\xEA skills \u0111\xE3 c\xE0i ho\u1EB7c c\xF3 s\u1EB5n`);
13814
+ console.log(` ${chalk39.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t skill`);
13815
+ console.log(` ${chalk39.cyan("sync")} \u0110\u1ED3ng b\u1ED9 skills sang c\xE1c IDE`);
13816
+ console.log();
13817
+ console.log(chalk39.bold("V\xED d\u1EE5:"));
13818
+ console.log(chalk39.dim(` $ ${cli} skills find audit`));
13819
+ console.log(chalk39.dim(` $ ${cli} skills find "react" --skillsh`));
13820
+ console.log(chalk39.dim(` $ ${cli} skills add brainstorming`));
13821
+ console.log(chalk39.dim(` $ ${cli} skills add vercel/next-skills --skillsh`));
13822
+ console.log(chalk39.dim(` $ ${cli} skills list`));
13823
+ console.log(chalk39.dim(` $ ${cli} skills sync --all -y`));
13824
+ console.log();
13825
+ console.log(chalk39.dim(`Ch\u1EA1y "${cli} skills <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt`));
13826
+ }
13827
+ function createSkillsCommand() {
13828
+ const cmd = new Command74("skills").alias("s").description("Manage agent skills (search, install, sync to IDEs)").action(() => {
13829
+ showSkillsHelp();
13830
+ });
13831
+ cmd.addCommand(createSkillsFindCommand());
13832
+ cmd.addCommand(createSkillsAddCommand());
13833
+ cmd.addCommand(createSkillsListCommand());
13834
+ cmd.addCommand(createSkillsInfoCommand());
13835
+ cmd.addCommand(createSkillsSyncCommand());
13836
+ return cmd;
13837
+ }
13838
+
13839
+ // src/commands/upgrade.ts
13840
+ import { Command as Command75 } from "commander";
13841
+ import { confirm as confirm16 } from "@inquirer/prompts";
13425
13842
  import { execSync as execSync4 } from "child_process";
13426
13843
  var colors2 = {
13427
13844
  yellow: "\x1B[33m",
@@ -13432,7 +13849,7 @@ var colors2 = {
13432
13849
  bold: "\x1B[1m"
13433
13850
  };
13434
13851
  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) => {
13852
+ 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
13853
  await handleUpgrade(options);
13437
13854
  });
13438
13855
  }
@@ -13476,7 +13893,7 @@ ${colors2.bold}Current version:${colors2.reset} ${currentVersion}`);
13476
13893
  return;
13477
13894
  }
13478
13895
  if (!options.force) {
13479
- const shouldUpdate = await confirm14({
13896
+ const shouldUpdate = await confirm16({
13480
13897
  message: "Update to the latest version now?",
13481
13898
  default: true
13482
13899
  });
@@ -13580,11 +13997,11 @@ function getInstallCommand(packageManager2) {
13580
13997
  }
13581
13998
 
13582
13999
  // 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";
14000
+ import { Command as Command76 } from "commander";
14001
+ import { confirm as confirm17, select as select6 } from "@inquirer/prompts";
14002
+ import { join as join20 } from "path";
13586
14003
  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) => {
14004
+ 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
14005
  await handleClean(options);
13589
14006
  });
13590
14007
  }
@@ -13595,7 +14012,7 @@ async function handleClean(options) {
13595
14012
  {
13596
14013
  name: "Backups",
13597
14014
  description: "Component backup files (.jai1_backup/)",
13598
- path: join19(cwd, ".jai1_backup"),
14015
+ path: join20(cwd, ".jai1_backup"),
13599
14016
  check: async () => {
13600
14017
  const backups = await service.listBackups(cwd);
13601
14018
  return { exists: backups.length > 0, count: backups.length };
@@ -13680,7 +14097,7 @@ async function cleanTarget(target, skipConfirm) {
13680
14097
  }
13681
14098
  const countStr = info.count ? ` (${info.count} items)` : "";
13682
14099
  if (!skipConfirm) {
13683
- const confirmed = await confirm15({
14100
+ const confirmed = await confirm17({
13684
14101
  message: `Delete ${target.name}${countStr}?`,
13685
14102
  default: false
13686
14103
  });
@@ -13698,7 +14115,7 @@ async function cleanTarget(target, skipConfirm) {
13698
14115
  }
13699
14116
 
13700
14117
  // src/commands/redmine/check.ts
13701
- import { Command as Command71 } from "commander";
14118
+ import { Command as Command77 } from "commander";
13702
14119
 
13703
14120
  // src/services/redmine-config.service.ts
13704
14121
  import { readFile as readFile7 } from "fs/promises";
@@ -14005,7 +14422,7 @@ async function checkConnectivity(config) {
14005
14422
 
14006
14423
  // src/commands/redmine/check.ts
14007
14424
  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) => {
14425
+ 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
14426
  await handleRedmineCheck(options);
14010
14427
  });
14011
14428
  return cmd;
@@ -14033,7 +14450,7 @@ async function handleRedmineCheck(options) {
14033
14450
  }
14034
14451
 
14035
14452
  // src/commands/redmine/sync-issue.ts
14036
- import { Command as Command72 } from "commander";
14453
+ import { Command as Command78 } from "commander";
14037
14454
 
14038
14455
  // src/sync-issue.ts
14039
14456
  import { resolve as resolve3, relative } from "path";
@@ -14417,7 +14834,7 @@ function extractIssueIdFromUrl(url) {
14417
14834
 
14418
14835
  // src/commands/redmine/sync-issue.ts
14419
14836
  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) => {
14837
+ 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
14838
  await handleSyncIssue(options);
14422
14839
  });
14423
14840
  return cmd;
@@ -14461,7 +14878,7 @@ async function handleSyncIssue(options) {
14461
14878
  }
14462
14879
 
14463
14880
  // src/commands/redmine/sync-project.ts
14464
- import { Command as Command73 } from "commander";
14881
+ import { Command as Command79 } from "commander";
14465
14882
 
14466
14883
  // src/sync-project.ts
14467
14884
  async function syncProject(config, options = {}) {
@@ -14531,7 +14948,7 @@ async function syncProject(config, options = {}) {
14531
14948
 
14532
14949
  // src/commands/redmine/sync-project.ts
14533
14950
  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) => {
14951
+ 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
14952
  await handleSyncProject(options);
14536
14953
  });
14537
14954
  return cmd;
@@ -14586,12 +15003,12 @@ async function handleSyncProject(options) {
14586
15003
  }
14587
15004
 
14588
15005
  // src/commands/framework/info.ts
14589
- import { Command as Command74 } from "commander";
15006
+ import { Command as Command80 } from "commander";
14590
15007
  import { promises as fs28 } from "fs";
14591
- import { join as join20 } from "path";
14592
- import { homedir as homedir6 } from "os";
15008
+ import { join as join21 } from "path";
15009
+ import { homedir as homedir5 } from "os";
14593
15010
  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) => {
15011
+ 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
15012
  await handleInfo(options);
14596
15013
  });
14597
15014
  return cmd;
@@ -14602,7 +15019,7 @@ async function handleInfo(options) {
14602
15019
  if (!config) {
14603
15020
  throw new ValidationError(`Not initialized. Run "${getCliName()} auth" first.`);
14604
15021
  }
14605
- const frameworkPath = join20(homedir6(), ".jai1", "framework");
15022
+ const frameworkPath = join21(homedir5(), ".jai1", "framework");
14606
15023
  const projectStatus = await getProjectStatus2();
14607
15024
  const info = {
14608
15025
  configPath: configService.getConfigPath(),
@@ -14637,7 +15054,7 @@ function maskKey4(key) {
14637
15054
  return "****" + key.slice(-4);
14638
15055
  }
14639
15056
  async function getProjectStatus2() {
14640
- const projectJai1 = join20(process.cwd(), ".jai1");
15057
+ const projectJai1 = join21(process.cwd(), ".jai1");
14641
15058
  try {
14642
15059
  await fs28.access(projectJai1);
14643
15060
  return { exists: true, version: "Synced" };
@@ -14647,8 +15064,8 @@ async function getProjectStatus2() {
14647
15064
  }
14648
15065
 
14649
15066
  // src/commands/self-update.ts
14650
- import { Command as Command75 } from "commander";
14651
- import { confirm as confirm16 } from "@inquirer/prompts";
15067
+ import { Command as Command81 } from "commander";
15068
+ import { confirm as confirm18 } from "@inquirer/prompts";
14652
15069
  import { execSync as execSync5 } from "child_process";
14653
15070
  var colors3 = {
14654
15071
  yellow: "\x1B[33m",
@@ -14659,7 +15076,7 @@ var colors3 = {
14659
15076
  bold: "\x1B[1m"
14660
15077
  };
14661
15078
  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) => {
15079
+ 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
15080
  await handleSelfUpdate(options);
14664
15081
  });
14665
15082
  }
@@ -14703,7 +15120,7 @@ ${colors3.bold}Current version:${colors3.reset} ${currentVersion}`);
14703
15120
  return;
14704
15121
  }
14705
15122
  if (!options.force) {
14706
- const shouldUpdate = await confirm16({
15123
+ const shouldUpdate = await confirm18({
14707
15124
  message: "Update to the latest version now?",
14708
15125
  default: true
14709
15126
  });
@@ -14799,10 +15216,10 @@ function getInstallCommand2(packageManager2) {
14799
15216
  }
14800
15217
 
14801
15218
  // src/commands/clear-backups.ts
14802
- import { Command as Command76 } from "commander";
14803
- import { confirm as confirm17 } from "@inquirer/prompts";
15219
+ import { Command as Command82 } from "commander";
15220
+ import { confirm as confirm19 } from "@inquirer/prompts";
14804
15221
  function createClearBackupsCommand() {
14805
- return new Command76("clear-backups").description("Clear backup files").option("-y, --yes", "Skip confirmation").action(async (options) => {
15222
+ return new Command82("clear-backups").description("Clear backup files").option("-y, --yes", "Skip confirmation").action(async (options) => {
14806
15223
  const service = new ComponentsService();
14807
15224
  const backups = await service.listBackups(process.cwd());
14808
15225
  if (backups.length === 0) {
@@ -14818,7 +15235,7 @@ function createClearBackupsCommand() {
14818
15235
  }
14819
15236
  console.log();
14820
15237
  if (!options.yes) {
14821
- const ok = await confirm17({ message: "Delete all backups?", default: false });
15238
+ const ok = await confirm19({ message: "Delete all backups?", default: false });
14822
15239
  if (!ok) return;
14823
15240
  }
14824
15241
  await service.clearBackups(process.cwd());
@@ -14827,8 +15244,8 @@ function createClearBackupsCommand() {
14827
15244
  }
14828
15245
 
14829
15246
  // 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";
15247
+ import { Command as Command83 } from "commander";
15248
+ import { checkbox as checkbox9, confirm as confirm20, select as select7 } from "@inquirer/prompts";
14832
15249
  import fs29 from "fs/promises";
14833
15250
  import path12 from "path";
14834
15251
  import { existsSync as existsSync3 } from "fs";
@@ -14967,7 +15384,7 @@ var PERFORMANCE_GROUPS2 = {
14967
15384
  }
14968
15385
  };
14969
15386
  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");
15387
+ const vscodeCommand = new Command83("vscode").description("Qu\u1EA3n l\xFD c\xE0i \u0111\u1EB7t VSCode cho d\u1EF1 \xE1n hi\u1EC7n t\u1EA1i");
14971
15388
  vscodeCommand.action(async () => {
14972
15389
  await interactiveMode2();
14973
15390
  });
@@ -15036,7 +15453,7 @@ async function selectGroupsToApply2(action) {
15036
15453
  value: key
15037
15454
  }));
15038
15455
  try {
15039
- const selectedGroups = await checkbox7({
15456
+ const selectedGroups = await checkbox9({
15040
15457
  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
15458
  choices,
15042
15459
  theme: checkboxTheme
@@ -15073,7 +15490,7 @@ async function applyGroups2(groupKeys, action) {
15073
15490
  console.log("\u{1F4C4} \u0110\xE3 \u0111\u1ECDc c\xE0i \u0111\u1EB7t hi\u1EC7n t\u1EA1i t\u1EEB settings.json");
15074
15491
  } catch {
15075
15492
  console.warn("\u26A0\uFE0F Kh\xF4ng th\u1EC3 \u0111\u1ECDc settings.json (c\xF3 th\u1EC3 ch\u1EE9a comments).");
15076
- const confirmOverwrite = await confirm18({
15493
+ const confirmOverwrite = await confirm20({
15077
15494
  message: "Ghi \u0111\xE8 file settings.json hi\u1EC7n t\u1EA1i?",
15078
15495
  default: false
15079
15496
  });
@@ -15120,7 +15537,7 @@ async function resetSettings2(groupKeys) {
15120
15537
  console.log("\n\u26A0\uFE0F Kh\xF4ng t\xECm th\u1EA5y file settings.json");
15121
15538
  return;
15122
15539
  }
15123
- const confirmReset = await confirm18({
15540
+ const confirmReset = await confirm20({
15124
15541
  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
15542
  default: false
15126
15543
  });
@@ -15138,10 +15555,10 @@ async function resetSettings2(groupKeys) {
15138
15555
  }
15139
15556
 
15140
15557
  // src/commands/migrate-ide.ts
15141
- import { Command as Command78 } from "commander";
15142
- import { checkbox as checkbox8, confirm as confirm19 } from "@inquirer/prompts";
15558
+ import { Command as Command84 } from "commander";
15559
+ import { checkbox as checkbox10, confirm as confirm21 } from "@inquirer/prompts";
15143
15560
  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) => {
15561
+ 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
15562
  await runMigrateIde(options);
15146
15563
  });
15147
15564
  return cmd;
@@ -15169,7 +15586,7 @@ async function runMigrateIde(options) {
15169
15586
  value: ide
15170
15587
  };
15171
15588
  });
15172
- selectedIdes = await checkbox8({
15589
+ selectedIdes = await checkbox10({
15173
15590
  message: "Ch\u1ECDn IDE(s) \u0111\u1EC3 migrate (SPACE \u0111\u1EC3 ch\u1ECDn, ENTER \u0111\u1EC3 x\xE1c nh\u1EADn):",
15174
15591
  choices: ideChoices,
15175
15592
  theme: checkboxTheme
@@ -15189,7 +15606,7 @@ async function runMigrateIde(options) {
15189
15606
  { name: `Workflows (${content.workflows.length} files)`, value: "workflows" },
15190
15607
  { name: `Commands (${content.commands.length} files)`, value: "commands" }
15191
15608
  ];
15192
- selectedTypes = await checkbox8({
15609
+ selectedTypes = await checkbox10({
15193
15610
  message: "Ch\u1ECDn content types \u0111\u1EC3 migrate:",
15194
15611
  choices: typeChoices,
15195
15612
  theme: checkboxTheme
@@ -15212,7 +15629,7 @@ async function runMigrateIde(options) {
15212
15629
  if (options.dryRun) {
15213
15630
  console.log("\u{1F50D} DRY RUN - No files will be written\n");
15214
15631
  }
15215
- const confirmed = await confirm19({
15632
+ const confirmed = await confirm21({
15216
15633
  message: "Proceed with migration?",
15217
15634
  default: true
15218
15635
  });
@@ -15250,20 +15667,20 @@ async function runMigrateIde(options) {
15250
15667
 
15251
15668
  // src/utils/help-formatter.ts
15252
15669
  import boxen4 from "boxen";
15253
- import chalk34 from "chalk";
15670
+ import chalk40 from "chalk";
15254
15671
  import gradient from "gradient-string";
15255
15672
  import figlet from "figlet";
15256
15673
  function showCustomHelp(version) {
15257
15674
  const title = figlet.textSync("JAI1", { font: "Small" });
15258
15675
  console.log(gradient.pastel(title));
15259
15676
  console.log(
15260
- boxen4(chalk34.cyan(`Agentic Coding CLI v${version}`), {
15677
+ boxen4(chalk40.cyan(`Agentic Coding CLI v${version}`), {
15261
15678
  padding: { left: 1, right: 1, top: 0, bottom: 0 },
15262
15679
  borderStyle: "round",
15263
15680
  borderColor: "cyan"
15264
15681
  })
15265
15682
  );
15266
- console.log(chalk34.bold("\n\u{1F527} Thi\u1EBFt l\u1EADp & Th\xF4ng tin"));
15683
+ console.log(chalk40.bold("\n\u{1F527} Thi\u1EBFt l\u1EADp & Th\xF4ng tin"));
15267
15684
  console.log(" auth X\xE1c th\u1EF1c v\xE0 c\u1EA5u h\xECnh client");
15268
15685
  console.log(" status Hi\u1EC3n th\u1ECB tr\u1EA1ng th\xE1i c\u1EA5u h\xECnh");
15269
15686
  console.log(" client-info T\u1EA1o th\xF4ng tin client \u0111\u1EC3 g\u1EEDi \u0111\u1ED9i ph\xE1t tri\u1EC3n");
@@ -15271,43 +15688,43 @@ function showCustomHelp(version) {
15271
15688
  console.log(" guide H\u01B0\u1EDBng d\u1EABn s\u1EED d\u1EE5ng nhanh");
15272
15689
  console.log(" quickstart B\u1EAFt \u0111\u1EA7u t\u1EEB \u0111\xE2u? (theo t\xECnh hu\u1ED1ng)");
15273
15690
  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"));
15691
+ console.log(chalk40.bold("\n\u{1F4E6} Qu\u1EA3n l\xFD Components"));
15275
15692
  console.log(" apply C\xE0i \u0111\u1EB7t components (interactive)");
15276
15693
  console.log(" update C\u1EADp nh\u1EADt components \u0111\xE3 c\xE0i");
15277
15694
  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"));
15695
+ console.log(chalk40.bold("\n\u{1F5A5}\uFE0F IDE & T\xEDch h\u1EE3p"));
15279
15696
  console.log(" ide L\u1EC7nh c\u1EA5u h\xECnh IDE");
15280
15697
  console.log(" chat Chat AI v\u1EDBi Jai1 LLM Proxy");
15281
15698
  console.log(" openai-keys Th\xF4ng tin API credentials");
15282
- console.log(chalk34.bold("\n\u{1F916} AI Tools"));
15699
+ console.log(chalk40.bold("\n\u{1F916} AI Tools"));
15283
15700
  console.log(" translate D\u1ECBch v\u0103n b\u1EA3n/file b\u1EB1ng AI");
15284
15701
  console.log(" image T\u1EA1o \u1EA3nh (Coming Soon)");
15285
15702
  console.log(" stats Th\u1ED1ng k\xEA s\u1EED d\u1EE5ng LLM");
15286
15703
  console.log(" feedback G\u1EEDi b\xE1o c\xE1o/\u0111\u1EC1 xu\u1EA5t");
15287
- console.log(chalk34.bold("\n\u{1F4C1} Project"));
15704
+ console.log(chalk40.bold("\n\u{1F4C1} Project"));
15288
15705
  console.log(" kit Qu\u1EA3n l\xFD starter kits");
15289
15706
  console.log(" tasks (t) Qu\u1EA3n l\xFD tasks ph\xE1t tri\u1EC3n");
15290
15707
  console.log(" rules Qu\u1EA3n l\xFD rule presets");
15291
15708
  console.log(" deps Qu\u1EA3n l\xFD dependencies");
15292
15709
  console.log(" redmine Redmine context sync");
15293
- console.log(chalk34.bold("\n\u2699\uFE0F B\u1EA3o tr\xEC"));
15710
+ console.log(chalk40.bold("\n\u2699\uFE0F B\u1EA3o tr\xEC"));
15294
15711
  console.log(" upgrade C\u1EADp nh\u1EADt CLI client");
15295
15712
  console.log(" clean D\u1ECDn d\u1EB9p cache/backup");
15296
15713
  console.log(" utils Developer utilities");
15297
15714
  const name = getCliName();
15298
- console.log(chalk34.dim(`
15715
+ console.log(chalk40.dim(`
15299
15716
  S\u1EED d\u1EE5ng: ${name} [l\u1EC7nh] --help \u0111\u1EC3 xem chi ti\u1EBFt`));
15300
15717
  }
15301
15718
  function showUnknownCommand(commandName) {
15302
- console.error(chalk34.red(`\u274C L\u1EC7nh kh\xF4ng t\u1ED3n t\u1EA1i: ${commandName}`));
15719
+ console.error(chalk40.red(`\u274C L\u1EC7nh kh\xF4ng t\u1ED3n t\u1EA1i: ${commandName}`));
15303
15720
  const name = getCliName();
15304
- console.error(chalk34.dim(`
15721
+ console.error(chalk40.dim(`
15305
15722
  G\u1EE3i \xFD: Ch\u1EA1y ${name} --help \u0111\u1EC3 xem danh s\xE1ch l\u1EC7nh`));
15306
15723
  }
15307
15724
 
15308
15725
  // src/cli.ts
15309
15726
  checkNodeVersion();
15310
- var program = new Command79();
15727
+ var program = new Command85();
15311
15728
  if (process.argv.includes("-v") || process.argv.includes("--version")) {
15312
15729
  console.log(package_default.version);
15313
15730
  if (!process.argv.includes("--skip-update-check")) {
@@ -15345,11 +15762,12 @@ program.addCommand(createDepsCommand());
15345
15762
  program.addCommand(createTasksCommand());
15346
15763
  program.addCommand(createKitCommand());
15347
15764
  program.addCommand(createRulesCommand());
15765
+ program.addCommand(createSkillsCommand());
15348
15766
  program.addCommand(createUpgradeCommand());
15349
15767
  program.addCommand(createCleanCommand());
15350
- var redmineCommand = new Command79("redmine").description("Redmine context sync commands");
15768
+ var redmineCommand = new Command85("redmine").description("Redmine context sync commands");
15351
15769
  redmineCommand.addCommand(createRedmineCheckCommand());
15352
- var syncCommand = new Command79("sync").description("Sync Redmine issues to markdown files");
15770
+ var syncCommand = new Command85("sync").description("Sync Redmine issues to markdown files");
15353
15771
  syncCommand.addCommand(createSyncIssueCommand());
15354
15772
  syncCommand.addCommand(createSyncProjectCommand());
15355
15773
  redmineCommand.addCommand(syncCommand);
@@ -15367,7 +15785,7 @@ program.on("command:*", (operands) => {
15367
15785
  process.on("unhandledRejection", (error) => {
15368
15786
  void new ErrorLogService().logError(error, {
15369
15787
  command: process.argv.slice(2).join(" "),
15370
- cwdBase: basename4(process.cwd())
15788
+ cwdBase: basename5(process.cwd())
15371
15789
  });
15372
15790
  if (error instanceof Jai1Error) {
15373
15791
  console.error("\u274C", error.message);
@@ -15388,7 +15806,7 @@ program.parseAsync(process.argv).then(async () => {
15388
15806
  }).catch((error) => {
15389
15807
  void new ErrorLogService().logError(error, {
15390
15808
  command: process.argv.slice(2).join(" "),
15391
- cwdBase: basename4(process.cwd())
15809
+ cwdBase: basename5(process.cwd())
15392
15810
  });
15393
15811
  console.error("\u274C", error.message);
15394
15812
  process.exit(1);