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