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