@jvittechs/jai1-cli 0.1.92 → 0.1.94
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +622 -447
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -33,7 +33,7 @@ var NetworkError = class extends Jai1Error {
|
|
|
33
33
|
// package.json
|
|
34
34
|
var package_default = {
|
|
35
35
|
name: "@jvittechs/jai1-cli",
|
|
36
|
-
version: "0.1.
|
|
36
|
+
version: "0.1.94",
|
|
37
37
|
description: "A unified CLI tool for JV-IT TECHS developers to manage Jai1 Framework. Please contact TeamAI for usage instructions.",
|
|
38
38
|
type: "module",
|
|
39
39
|
bin: {
|
|
@@ -772,6 +772,392 @@ import { Box as Box2, Text as Text3, useInput, useApp } from "ink";
|
|
|
772
772
|
import Spinner from "ink-spinner";
|
|
773
773
|
import TextInput from "ink-text-input";
|
|
774
774
|
|
|
775
|
+
// src/services/migrate-ide.service.ts
|
|
776
|
+
import { promises as fs4 } from "fs";
|
|
777
|
+
import path from "path";
|
|
778
|
+
import matter from "gray-matter";
|
|
779
|
+
|
|
780
|
+
// src/constants/ide-migration-configs.ts
|
|
781
|
+
function isAlwaysTrigger(trigger) {
|
|
782
|
+
return trigger === "always_on" || trigger === "always";
|
|
783
|
+
}
|
|
784
|
+
function getGlobFromTrigger(trigger, globs) {
|
|
785
|
+
if (globs && globs.length > 0) return globs;
|
|
786
|
+
if (trigger && !isAlwaysTrigger(trigger) && trigger !== "manual") {
|
|
787
|
+
return [trigger];
|
|
788
|
+
}
|
|
789
|
+
return void 0;
|
|
790
|
+
}
|
|
791
|
+
var IDE_MIGRATION_CONFIGS = {
|
|
792
|
+
cursor: {
|
|
793
|
+
id: "cursor",
|
|
794
|
+
name: "Cursor",
|
|
795
|
+
icon: "\u{1F52E}",
|
|
796
|
+
basePath: ".cursor",
|
|
797
|
+
rulesPath: "rules",
|
|
798
|
+
workflowsPath: null,
|
|
799
|
+
commandsPath: "commands",
|
|
800
|
+
fileExtension: ".mdc",
|
|
801
|
+
generateFrontmatter: (opts) => {
|
|
802
|
+
const lines = ["---"];
|
|
803
|
+
if (opts.description) {
|
|
804
|
+
lines.push(`description: ${opts.description}`);
|
|
805
|
+
}
|
|
806
|
+
const alwaysApply = opts.alwaysApply || isAlwaysTrigger(opts.trigger);
|
|
807
|
+
const globs = getGlobFromTrigger(opts.trigger, opts.globs);
|
|
808
|
+
if (globs && globs.length > 0) {
|
|
809
|
+
lines.push(`globs: ${globs.join(", ")}`);
|
|
810
|
+
}
|
|
811
|
+
lines.push(`alwaysApply: ${alwaysApply}`);
|
|
812
|
+
lines.push("---");
|
|
813
|
+
return lines.join("\n");
|
|
814
|
+
}
|
|
815
|
+
},
|
|
816
|
+
windsurf: {
|
|
817
|
+
id: "windsurf",
|
|
818
|
+
name: "Windsurf",
|
|
819
|
+
icon: "\u{1F3C4}",
|
|
820
|
+
basePath: ".windsurf",
|
|
821
|
+
rulesPath: "rules",
|
|
822
|
+
workflowsPath: "workflows",
|
|
823
|
+
commandsPath: null,
|
|
824
|
+
fileExtension: ".md",
|
|
825
|
+
generateFrontmatter: (opts) => {
|
|
826
|
+
let trigger;
|
|
827
|
+
if (opts.trigger) {
|
|
828
|
+
trigger = opts.trigger === "always" ? "always_on" : opts.trigger;
|
|
829
|
+
} else if (opts.alwaysApply) {
|
|
830
|
+
trigger = "always_on";
|
|
831
|
+
} else if (opts.globs && opts.globs.length > 0) {
|
|
832
|
+
trigger = opts.globs[0];
|
|
833
|
+
} else {
|
|
834
|
+
trigger = "always_on";
|
|
835
|
+
}
|
|
836
|
+
return `---
|
|
837
|
+
trigger: ${trigger}
|
|
838
|
+
---`;
|
|
839
|
+
}
|
|
840
|
+
},
|
|
841
|
+
antigravity: {
|
|
842
|
+
id: "antigravity",
|
|
843
|
+
name: "Antigravity",
|
|
844
|
+
icon: "\u{1F680}",
|
|
845
|
+
basePath: ".agent",
|
|
846
|
+
rulesPath: "rules",
|
|
847
|
+
workflowsPath: "workflows",
|
|
848
|
+
commandsPath: null,
|
|
849
|
+
fileExtension: ".md",
|
|
850
|
+
generateFrontmatter: (opts) => {
|
|
851
|
+
let trigger;
|
|
852
|
+
if (opts.trigger) {
|
|
853
|
+
trigger = opts.trigger === "always" ? "always_on" : opts.trigger;
|
|
854
|
+
} else if (opts.alwaysApply) {
|
|
855
|
+
trigger = "always_on";
|
|
856
|
+
} else if (opts.globs && opts.globs.length > 0) {
|
|
857
|
+
trigger = opts.globs[0];
|
|
858
|
+
} else {
|
|
859
|
+
trigger = "always_on";
|
|
860
|
+
}
|
|
861
|
+
return `---
|
|
862
|
+
trigger: ${trigger}
|
|
863
|
+
---`;
|
|
864
|
+
}
|
|
865
|
+
},
|
|
866
|
+
claudecode: {
|
|
867
|
+
id: "claudecode",
|
|
868
|
+
name: "Claude Code",
|
|
869
|
+
icon: "\u{1F916}",
|
|
870
|
+
basePath: ".claude",
|
|
871
|
+
rulesPath: "rules",
|
|
872
|
+
workflowsPath: null,
|
|
873
|
+
commandsPath: "commands",
|
|
874
|
+
fileExtension: ".md",
|
|
875
|
+
generateFrontmatter: (opts) => {
|
|
876
|
+
const lines = ["---"];
|
|
877
|
+
if (opts.description) {
|
|
878
|
+
lines.push(`description: ${opts.description}`);
|
|
879
|
+
}
|
|
880
|
+
const isAlways = opts.alwaysApply || isAlwaysTrigger(opts.trigger);
|
|
881
|
+
if (!isAlways) {
|
|
882
|
+
const paths = getGlobFromTrigger(opts.trigger, opts.globs);
|
|
883
|
+
if (paths && paths.length > 0) {
|
|
884
|
+
lines.push(`paths: ${paths.join(", ")}`);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
lines.push("---");
|
|
888
|
+
return lines.join("\n");
|
|
889
|
+
}
|
|
890
|
+
},
|
|
891
|
+
opencode: {
|
|
892
|
+
id: "opencode",
|
|
893
|
+
name: "OpenCode",
|
|
894
|
+
icon: "\u{1F4BB}",
|
|
895
|
+
basePath: ".opencode",
|
|
896
|
+
rulesPath: "rules",
|
|
897
|
+
workflowsPath: "workflows",
|
|
898
|
+
commandsPath: null,
|
|
899
|
+
fileExtension: ".md",
|
|
900
|
+
generateFrontmatter: (opts) => {
|
|
901
|
+
let trigger;
|
|
902
|
+
if (opts.trigger) {
|
|
903
|
+
trigger = opts.trigger === "always" ? "always_on" : opts.trigger;
|
|
904
|
+
} else if (opts.alwaysApply) {
|
|
905
|
+
trigger = "always_on";
|
|
906
|
+
} else if (opts.globs && opts.globs.length > 0) {
|
|
907
|
+
trigger = opts.globs[0];
|
|
908
|
+
} else {
|
|
909
|
+
trigger = "always_on";
|
|
910
|
+
}
|
|
911
|
+
return `---
|
|
912
|
+
trigger: ${trigger}
|
|
913
|
+
---`;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
};
|
|
917
|
+
function getMigrationIDEs() {
|
|
918
|
+
return Object.keys(IDE_MIGRATION_CONFIGS);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// src/services/migrate-ide.service.ts
|
|
922
|
+
var MigrateIdeService = class {
|
|
923
|
+
projectPath;
|
|
924
|
+
jai1Path;
|
|
925
|
+
constructor(projectPath = process.cwd()) {
|
|
926
|
+
this.projectPath = projectPath;
|
|
927
|
+
this.jai1Path = path.join(projectPath, ".jai1");
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* Scan .jai1/ directory for content
|
|
931
|
+
*/
|
|
932
|
+
async scanJai1Content() {
|
|
933
|
+
const manualRules = await this.scanContentType("rules");
|
|
934
|
+
const presetRules = await this.scanRulePreset();
|
|
935
|
+
const rules = [...presetRules, ...manualRules];
|
|
936
|
+
const workflows = await this.scanContentType("workflows");
|
|
937
|
+
const commands = [];
|
|
938
|
+
return {
|
|
939
|
+
rules,
|
|
940
|
+
workflows,
|
|
941
|
+
commands,
|
|
942
|
+
totalCount: rules.length + workflows.length + commands.length
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
/**
|
|
946
|
+
* Scan .jai1/rule-preset/ directory for rule preset files
|
|
947
|
+
*/
|
|
948
|
+
async scanRulePreset() {
|
|
949
|
+
const items = [];
|
|
950
|
+
const presetDir = path.join(this.jai1Path, "rule-preset");
|
|
951
|
+
try {
|
|
952
|
+
await fs4.access(presetDir);
|
|
953
|
+
} catch {
|
|
954
|
+
return items;
|
|
955
|
+
}
|
|
956
|
+
const files = await fs4.readdir(presetDir);
|
|
957
|
+
for (const file of files) {
|
|
958
|
+
if (!file.endsWith(".mdc")) continue;
|
|
959
|
+
const filepath = path.join(presetDir, file);
|
|
960
|
+
const stat = await fs4.stat(filepath);
|
|
961
|
+
if (!stat.isFile()) continue;
|
|
962
|
+
const content = await fs4.readFile(filepath, "utf-8");
|
|
963
|
+
let frontmatter = {};
|
|
964
|
+
try {
|
|
965
|
+
const { data } = matter(content);
|
|
966
|
+
frontmatter = data;
|
|
967
|
+
} catch {
|
|
968
|
+
}
|
|
969
|
+
const name = path.basename(file, ".mdc");
|
|
970
|
+
const trigger = this.extractTrigger(frontmatter);
|
|
971
|
+
const alwaysApply = this.isAlwaysTrigger(trigger);
|
|
972
|
+
items.push({
|
|
973
|
+
type: "rules",
|
|
974
|
+
name,
|
|
975
|
+
filepath,
|
|
976
|
+
relativePath: path.relative(this.projectPath, filepath),
|
|
977
|
+
description: frontmatter.description,
|
|
978
|
+
globs: this.extractGlobs(frontmatter),
|
|
979
|
+
trigger,
|
|
980
|
+
alwaysApply
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
return items;
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Scan a specific content type
|
|
987
|
+
*/
|
|
988
|
+
async scanContentType(type) {
|
|
989
|
+
const items = [];
|
|
990
|
+
const dirPath = path.join(this.jai1Path, type);
|
|
991
|
+
try {
|
|
992
|
+
await fs4.access(dirPath);
|
|
993
|
+
} catch {
|
|
994
|
+
return items;
|
|
995
|
+
}
|
|
996
|
+
const files = await fs4.readdir(dirPath);
|
|
997
|
+
for (const file of files) {
|
|
998
|
+
if (!file.endsWith(".md")) continue;
|
|
999
|
+
const filepath = path.join(dirPath, file);
|
|
1000
|
+
const stat = await fs4.stat(filepath);
|
|
1001
|
+
if (!stat.isFile()) continue;
|
|
1002
|
+
const content = await fs4.readFile(filepath, "utf-8");
|
|
1003
|
+
const { data: frontmatter } = matter(content);
|
|
1004
|
+
const name = path.basename(file, ".md");
|
|
1005
|
+
const trigger = this.extractTrigger(frontmatter);
|
|
1006
|
+
const alwaysApply = this.isAlwaysTrigger(trigger);
|
|
1007
|
+
items.push({
|
|
1008
|
+
type,
|
|
1009
|
+
name,
|
|
1010
|
+
filepath,
|
|
1011
|
+
relativePath: path.relative(this.projectPath, filepath),
|
|
1012
|
+
description: frontmatter.description,
|
|
1013
|
+
globs: this.extractGlobs(frontmatter),
|
|
1014
|
+
trigger,
|
|
1015
|
+
alwaysApply
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
return items;
|
|
1019
|
+
}
|
|
1020
|
+
/**
|
|
1021
|
+
* Generate stub file content with @ reference
|
|
1022
|
+
*/
|
|
1023
|
+
generateStubContent(ide, sourceItem) {
|
|
1024
|
+
const config = IDE_MIGRATION_CONFIGS[ide];
|
|
1025
|
+
if (!config) throw new Error(`Unknown IDE: ${ide}`);
|
|
1026
|
+
const frontmatter = config.generateFrontmatter({
|
|
1027
|
+
description: sourceItem.description,
|
|
1028
|
+
globs: sourceItem.globs,
|
|
1029
|
+
trigger: sourceItem.trigger,
|
|
1030
|
+
alwaysApply: sourceItem.alwaysApply,
|
|
1031
|
+
sourceFile: sourceItem.relativePath
|
|
1032
|
+
});
|
|
1033
|
+
const reference = `@${sourceItem.relativePath}`;
|
|
1034
|
+
if (frontmatter) {
|
|
1035
|
+
return `${frontmatter}
|
|
1036
|
+
|
|
1037
|
+
${reference}
|
|
1038
|
+
`;
|
|
1039
|
+
}
|
|
1040
|
+
return `${reference}
|
|
1041
|
+
`;
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Migrate content to specific IDEs
|
|
1045
|
+
*/
|
|
1046
|
+
async migrate(ides, contentTypes, content, onProgress) {
|
|
1047
|
+
const results = [];
|
|
1048
|
+
for (const ide of ides) {
|
|
1049
|
+
const config = IDE_MIGRATION_CONFIGS[ide];
|
|
1050
|
+
if (!config) continue;
|
|
1051
|
+
for (const type of contentTypes) {
|
|
1052
|
+
const items = type === "rules" ? content.rules : type === "workflows" ? content.workflows : content.commands;
|
|
1053
|
+
for (const item of items) {
|
|
1054
|
+
const result = await this.migrateItem(ide, config, item);
|
|
1055
|
+
results.push(result);
|
|
1056
|
+
onProgress?.(result);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
return results;
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Migrate a single item
|
|
1064
|
+
*/
|
|
1065
|
+
async migrateItem(ide, config, item) {
|
|
1066
|
+
try {
|
|
1067
|
+
const targetPath = this.getTargetPath(config, item);
|
|
1068
|
+
if (!targetPath) {
|
|
1069
|
+
return {
|
|
1070
|
+
source: item,
|
|
1071
|
+
targetIDE: ide,
|
|
1072
|
+
targetPath: "",
|
|
1073
|
+
status: "skipped",
|
|
1074
|
+
error: `IDE ${config.id} does not support ${item.type}`
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
const targetDir = path.dirname(targetPath);
|
|
1078
|
+
await fs4.mkdir(targetDir, { recursive: true });
|
|
1079
|
+
let status = "created";
|
|
1080
|
+
try {
|
|
1081
|
+
await fs4.access(targetPath);
|
|
1082
|
+
status = "updated";
|
|
1083
|
+
} catch {
|
|
1084
|
+
}
|
|
1085
|
+
const stubContent = this.generateStubContent(ide, item);
|
|
1086
|
+
await fs4.writeFile(targetPath, stubContent, "utf-8");
|
|
1087
|
+
return {
|
|
1088
|
+
source: item,
|
|
1089
|
+
targetIDE: ide,
|
|
1090
|
+
targetPath,
|
|
1091
|
+
status
|
|
1092
|
+
};
|
|
1093
|
+
} catch (error) {
|
|
1094
|
+
return {
|
|
1095
|
+
source: item,
|
|
1096
|
+
targetIDE: ide,
|
|
1097
|
+
targetPath: "",
|
|
1098
|
+
status: "error",
|
|
1099
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Get target file path for an item
|
|
1105
|
+
*/
|
|
1106
|
+
getTargetPath(config, item) {
|
|
1107
|
+
let contentPath = null;
|
|
1108
|
+
switch (item.type) {
|
|
1109
|
+
case "rules":
|
|
1110
|
+
contentPath = config.rulesPath;
|
|
1111
|
+
break;
|
|
1112
|
+
case "workflows":
|
|
1113
|
+
contentPath = config.workflowsPath ?? config.commandsPath;
|
|
1114
|
+
break;
|
|
1115
|
+
case "commands":
|
|
1116
|
+
contentPath = config.commandsPath;
|
|
1117
|
+
break;
|
|
1118
|
+
}
|
|
1119
|
+
if (!contentPath) {
|
|
1120
|
+
return null;
|
|
1121
|
+
}
|
|
1122
|
+
const filename = `${item.name}${config.fileExtension}`;
|
|
1123
|
+
return path.join(this.projectPath, config.basePath, contentPath, filename);
|
|
1124
|
+
}
|
|
1125
|
+
// Helper methods
|
|
1126
|
+
/**
|
|
1127
|
+
* Extract trigger from frontmatter
|
|
1128
|
+
* Jai1 source format uses 'trigger' field (Windsurf/Antigravity compatible)
|
|
1129
|
+
*/
|
|
1130
|
+
extractTrigger(frontmatter) {
|
|
1131
|
+
const trigger = frontmatter.trigger;
|
|
1132
|
+
if (typeof trigger === "string") {
|
|
1133
|
+
return trigger;
|
|
1134
|
+
}
|
|
1135
|
+
if (frontmatter.alwaysApply === true) {
|
|
1136
|
+
return "always_on";
|
|
1137
|
+
}
|
|
1138
|
+
const globs = this.extractGlobs(frontmatter);
|
|
1139
|
+
if (globs && globs.length > 0) {
|
|
1140
|
+
return globs[0];
|
|
1141
|
+
}
|
|
1142
|
+
return void 0;
|
|
1143
|
+
}
|
|
1144
|
+
/**
|
|
1145
|
+
* Extract globs from frontmatter
|
|
1146
|
+
*/
|
|
1147
|
+
extractGlobs(frontmatter) {
|
|
1148
|
+
const globs = frontmatter.globs;
|
|
1149
|
+
if (typeof globs === "string") return [globs];
|
|
1150
|
+
if (Array.isArray(globs)) return globs;
|
|
1151
|
+
return void 0;
|
|
1152
|
+
}
|
|
1153
|
+
/**
|
|
1154
|
+
* Check if trigger indicates "always apply"
|
|
1155
|
+
*/
|
|
1156
|
+
isAlwaysTrigger(trigger) {
|
|
1157
|
+
return trigger === "always" || trigger === "always_on";
|
|
1158
|
+
}
|
|
1159
|
+
};
|
|
1160
|
+
|
|
775
1161
|
// src/ui/shared/ProgressBar.tsx
|
|
776
1162
|
import React from "react";
|
|
777
1163
|
import { Box, Text } from "ink";
|
|
@@ -888,11 +1274,15 @@ var UnifiedApplyApp = ({
|
|
|
888
1274
|
const [selectedPackageIndex, setSelectedPackageIndex] = useState(0);
|
|
889
1275
|
const [installProgress, setInstallProgress] = useState([]);
|
|
890
1276
|
const [installStats, setInstallStats] = useState({ total: 0, completed: 0, added: 0, updated: 0, failed: 0 });
|
|
1277
|
+
const [availableIdes, setAvailableIdes] = useState([]);
|
|
1278
|
+
const [selectedIdes, setSelectedIdes] = useState(/* @__PURE__ */ new Set());
|
|
1279
|
+
const [ideCursorIndex, setIdeCursorIndex] = useState(0);
|
|
1280
|
+
const [syncProgress, setSyncProgress] = useState([]);
|
|
1281
|
+
const [syncStats, setSyncStats] = useState({ total: 0, completed: 0, created: 0, updated: 0, skipped: 0, errors: 0 });
|
|
891
1282
|
const service = new ComponentsService();
|
|
892
1283
|
useEffect(() => {
|
|
893
1284
|
const loadData = async () => {
|
|
894
1285
|
try {
|
|
895
|
-
setLoading(true);
|
|
896
1286
|
const [comps, tagList, installed] = await Promise.all([
|
|
897
1287
|
service.list(config),
|
|
898
1288
|
service.listTags(config),
|
|
@@ -901,28 +1291,71 @@ var UnifiedApplyApp = ({
|
|
|
901
1291
|
setComponents(comps);
|
|
902
1292
|
setTags(tagList);
|
|
903
1293
|
setInstalledPaths(new Set(Object.keys(installed)));
|
|
1294
|
+
setLoading(false);
|
|
904
1295
|
} catch (err) {
|
|
905
|
-
setError(err instanceof Error ? err.message : "Failed to load
|
|
906
|
-
} finally {
|
|
1296
|
+
setError(err instanceof Error ? err.message : "Failed to load");
|
|
907
1297
|
setLoading(false);
|
|
908
1298
|
}
|
|
909
1299
|
};
|
|
910
1300
|
loadData();
|
|
1301
|
+
setAvailableIdes(getMigrationIDEs());
|
|
911
1302
|
}, []);
|
|
912
1303
|
const filteredComponents = useMemo(() => {
|
|
913
1304
|
if (!searchQuery.trim()) return components;
|
|
914
1305
|
const query = searchQuery.toLowerCase();
|
|
915
1306
|
return components.filter(
|
|
916
|
-
(c) => c.filepath.toLowerCase().includes(query) || c.
|
|
1307
|
+
(c) => c.filepath.toLowerCase().includes(query) || c.tags?.some((t) => t.toLowerCase().includes(query))
|
|
917
1308
|
);
|
|
918
1309
|
}, [components, searchQuery]);
|
|
919
1310
|
useInput((input4, key) => {
|
|
920
|
-
if (viewState === "
|
|
1311
|
+
if (viewState === "done") {
|
|
921
1312
|
if (key.return || input4 === "q" || key.escape) {
|
|
922
1313
|
onExit();
|
|
923
1314
|
}
|
|
924
1315
|
return;
|
|
925
1316
|
}
|
|
1317
|
+
if (viewState === "summary") {
|
|
1318
|
+
if (key.return) {
|
|
1319
|
+
setViewState("ide-sync");
|
|
1320
|
+
} else if (input4 === "s" || input4 === "q" || key.escape) {
|
|
1321
|
+
onExit();
|
|
1322
|
+
}
|
|
1323
|
+
return;
|
|
1324
|
+
}
|
|
1325
|
+
if (viewState === "ide-sync") {
|
|
1326
|
+
if (key.upArrow) {
|
|
1327
|
+
setIdeCursorIndex((prev) => Math.max(0, prev - 1));
|
|
1328
|
+
} else if (key.downArrow) {
|
|
1329
|
+
setIdeCursorIndex((prev) => Math.min(availableIdes.length - 1, prev + 1));
|
|
1330
|
+
} else if (input4 === " ") {
|
|
1331
|
+
const ide = availableIdes[ideCursorIndex];
|
|
1332
|
+
if (ide) {
|
|
1333
|
+
setSelectedIdes((prev) => {
|
|
1334
|
+
const next = new Set(prev);
|
|
1335
|
+
if (next.has(ide)) {
|
|
1336
|
+
next.delete(ide);
|
|
1337
|
+
} else {
|
|
1338
|
+
next.add(ide);
|
|
1339
|
+
}
|
|
1340
|
+
return next;
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
} else if (input4 === "a") {
|
|
1344
|
+
setSelectedIdes(new Set(availableIdes));
|
|
1345
|
+
} else if (input4 === "c") {
|
|
1346
|
+
setSelectedIdes(/* @__PURE__ */ new Set());
|
|
1347
|
+
} else if (key.return) {
|
|
1348
|
+
if (selectedIdes.size > 0) {
|
|
1349
|
+
handleIdeSync();
|
|
1350
|
+
}
|
|
1351
|
+
} else if (input4 === "s" || input4 === "q" || key.escape) {
|
|
1352
|
+
onExit();
|
|
1353
|
+
}
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1356
|
+
if (viewState === "syncing") {
|
|
1357
|
+
return;
|
|
1358
|
+
}
|
|
926
1359
|
if (viewState === "installing") {
|
|
927
1360
|
return;
|
|
928
1361
|
}
|
|
@@ -1037,6 +1470,44 @@ var UnifiedApplyApp = ({
|
|
|
1037
1470
|
}
|
|
1038
1471
|
setViewState("summary");
|
|
1039
1472
|
};
|
|
1473
|
+
const handleIdeSync = async () => {
|
|
1474
|
+
setViewState("syncing");
|
|
1475
|
+
const migrateService = new MigrateIdeService();
|
|
1476
|
+
const content = await migrateService.scanJai1Content();
|
|
1477
|
+
if (content.totalCount === 0) {
|
|
1478
|
+
setSyncStats({ total: 0, completed: 0, created: 0, updated: 0, skipped: 0, errors: 0 });
|
|
1479
|
+
setViewState("done");
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
const selectedIdeList = Array.from(selectedIdes);
|
|
1483
|
+
const contentTypes = ["rules", "workflows"];
|
|
1484
|
+
const totalItems = contentTypes.reduce((sum, type) => {
|
|
1485
|
+
return sum + (type === "rules" ? content.rules.length : type === "workflows" ? content.workflows.length : content.commands.length);
|
|
1486
|
+
}, 0);
|
|
1487
|
+
const totalFiles = totalItems * selectedIdeList.length;
|
|
1488
|
+
setSyncStats({ total: totalFiles, completed: 0, created: 0, updated: 0, skipped: 0, errors: 0 });
|
|
1489
|
+
let created = 0, updated = 0, skipped = 0, errors = 0;
|
|
1490
|
+
let completed = 0;
|
|
1491
|
+
const results = await migrateService.migrate(
|
|
1492
|
+
selectedIdeList,
|
|
1493
|
+
contentTypes,
|
|
1494
|
+
content,
|
|
1495
|
+
(result) => {
|
|
1496
|
+
completed++;
|
|
1497
|
+
if (result.status === "created") created++;
|
|
1498
|
+
else if (result.status === "updated") updated++;
|
|
1499
|
+
else if (result.status === "skipped") skipped++;
|
|
1500
|
+
else if (result.status === "error") errors++;
|
|
1501
|
+
setSyncProgress((prev) => [...prev.slice(-7), {
|
|
1502
|
+
targetPath: result.targetPath || result.source.relativePath,
|
|
1503
|
+
status: result.status === "error" ? "error" : "success",
|
|
1504
|
+
error: result.error
|
|
1505
|
+
}]);
|
|
1506
|
+
setSyncStats({ total: totalFiles, completed, created, updated, skipped, errors });
|
|
1507
|
+
}
|
|
1508
|
+
);
|
|
1509
|
+
setViewState("done");
|
|
1510
|
+
};
|
|
1040
1511
|
if (loading) {
|
|
1041
1512
|
return /* @__PURE__ */ React3.createElement(Box2, { padding: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: "cyan" }, /* @__PURE__ */ React3.createElement(Spinner, { type: "dots" }), " \u0110ang t\u1EA3i components..."));
|
|
1042
1513
|
}
|
|
@@ -1047,7 +1518,21 @@ var UnifiedApplyApp = ({
|
|
|
1047
1518
|
return /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Text3, { bold: true, color: "cyan" }, "\u{1F4E6} Installing Components")), /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(ProgressBar, { current: installStats.completed, total: installStats.total, width: 50 })), /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "gray", padding: 1, height: 10 }, installProgress.slice(-8).map((item) => /* @__PURE__ */ React3.createElement(Box2, { key: item.filepath }, /* @__PURE__ */ React3.createElement(StatusIcon, { status: item.status === "success" ? "success" : item.status === "error" ? "error" : item.status === "downloading" ? "loading" : "pending" }), /* @__PURE__ */ React3.createElement(Text3, null, " ", item.filepath), item.error && /* @__PURE__ */ React3.createElement(Text3, { color: "red", dimColor: true }, " (", item.error, ")")))), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, null, "\u{1F4CA} ", /* @__PURE__ */ React3.createElement(Text3, { color: "green" }, installStats.added, " added"), " \xB7 ", /* @__PURE__ */ React3.createElement(Text3, { color: "cyan" }, installStats.updated, " updated"), " \xB7 ", /* @__PURE__ */ React3.createElement(Text3, { color: "red" }, installStats.failed, " failed"))));
|
|
1048
1519
|
}
|
|
1049
1520
|
if (viewState === "summary") {
|
|
1050
|
-
return /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Text3, { bold: true, color: "green" }, "\u2705 Installation Complete!")), /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "gray", padding: 1 }, /* @__PURE__ */ React3.createElement(Text3, null, installStats.total, " components processed:"), /* @__PURE__ */ React3.createElement(Text3, null, " \u2514\u2500 ", /* @__PURE__ */ React3.createElement(Text3, { color: "green" }, installStats.added), " newly added"), /* @__PURE__ */ React3.createElement(Text3, null, " \u2514\u2500 ", /* @__PURE__ */ React3.createElement(Text3, { color: "cyan" }, installStats.updated), " updated"), installStats.failed > 0 && /* @__PURE__ */ React3.createElement(Text3, null, " \u2514\u2500 ", /* @__PURE__ */ React3.createElement(Text3, { color: "red" }, installStats.failed), " failed")), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, null, "\u{1F4C1} Location: ", /* @__PURE__ */ React3.createElement(Text3, { color: "cyan" }, process.cwd(), "/.jai1"))), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "
|
|
1521
|
+
return /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Text3, { bold: true, color: "green" }, "\u2705 Installation Complete!")), /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "gray", padding: 1 }, /* @__PURE__ */ React3.createElement(Text3, null, installStats.total, " components processed:"), /* @__PURE__ */ React3.createElement(Text3, null, " \u2514\u2500 ", /* @__PURE__ */ React3.createElement(Text3, { color: "green" }, installStats.added), " newly added"), /* @__PURE__ */ React3.createElement(Text3, null, " \u2514\u2500 ", /* @__PURE__ */ React3.createElement(Text3, { color: "cyan" }, installStats.updated), " updated"), installStats.failed > 0 && /* @__PURE__ */ React3.createElement(Text3, null, " \u2514\u2500 ", /* @__PURE__ */ React3.createElement(Text3, { color: "red" }, installStats.failed), " failed")), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, null, "\u{1F4C1} Location: ", /* @__PURE__ */ React3.createElement(Text3, { color: "cyan" }, process.cwd(), "/.jai1"))), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Text3, { bold: true, color: "yellow" }, "\u{1F4E6} Next Step: Sync to IDE(s)?"), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "Sync .jai1/ content to your IDE directories (rules, workflows)")), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "[Enter] Select IDE(s) to sync \xB7 [S/Q] Skip and exit")));
|
|
1522
|
+
}
|
|
1523
|
+
if (viewState === "ide-sync") {
|
|
1524
|
+
return /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Text3, { bold: true, color: "cyan" }, "\u{1F504} Select IDE(s) to Sync")), /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "Sync .jai1/ content (rules, workflows) to IDE-specific directories")), /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", padding: 1 }, availableIdes.map((ide, i) => {
|
|
1525
|
+
const ideConfig = IDE_MIGRATION_CONFIGS[ide];
|
|
1526
|
+
const isCursor = i === ideCursorIndex;
|
|
1527
|
+
const isChecked = selectedIdes.has(ide);
|
|
1528
|
+
return /* @__PURE__ */ React3.createElement(Box2, { key: ide }, /* @__PURE__ */ React3.createElement(Text3, { color: isCursor ? "cyan" : "white" }, isCursor ? "\u276F " : " ", isChecked ? "[\u2713]" : "[ ]", " ", ideConfig.icon, " ", ideConfig.name));
|
|
1529
|
+
})), selectedIdes.size > 0 && /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, null, "Selected: ", /* @__PURE__ */ React3.createElement(Text3, { color: "green" }, selectedIdes.size), " IDE(s)")), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "[\u2191\u2193] Navigate \xB7 [\u2423] Toggle \xB7 [A] Select all \xB7 [C] Clear \xB7 [Enter] Sync \xB7 [S/Q] Skip")));
|
|
1530
|
+
}
|
|
1531
|
+
if (viewState === "syncing") {
|
|
1532
|
+
return /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Text3, { bold: true, color: "cyan" }, "\u{1F504} Syncing to IDE(s)")), /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(ProgressBar, { current: syncStats.completed, total: syncStats.total, width: 50 })), /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "gray", padding: 1, height: 10 }, syncProgress.slice(-8).map((item, idx) => /* @__PURE__ */ React3.createElement(Box2, { key: `${item.targetPath}-${idx}` }, /* @__PURE__ */ React3.createElement(StatusIcon, { status: item.status === "success" ? "success" : item.status === "error" ? "error" : item.status === "syncing" ? "loading" : "pending" }), /* @__PURE__ */ React3.createElement(Text3, null, " ", item.targetPath), item.error && /* @__PURE__ */ React3.createElement(Text3, { color: "red", dimColor: true }, " (", item.error, ")")))), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, null, "\u{1F4CA} ", /* @__PURE__ */ React3.createElement(Text3, { color: "green" }, syncStats.created, " created"), " \xB7 ", /* @__PURE__ */ React3.createElement(Text3, { color: "cyan" }, syncStats.updated, " updated"), syncStats.skipped > 0 && /* @__PURE__ */ React3.createElement(React3.Fragment, null, " \xB7 ", /* @__PURE__ */ React3.createElement(Text3, { color: "yellow" }, syncStats.skipped, " skipped")), syncStats.errors > 0 && /* @__PURE__ */ React3.createElement(React3.Fragment, null, " \xB7 ", /* @__PURE__ */ React3.createElement(Text3, { color: "red" }, syncStats.errors, " errors")))));
|
|
1533
|
+
}
|
|
1534
|
+
if (viewState === "done") {
|
|
1535
|
+
return /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Text3, { bold: true, color: "green" }, "\u{1F389} All Done!")), /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "gray", padding: 1 }, /* @__PURE__ */ React3.createElement(Text3, { bold: true }, "\u{1F4E6} Components Applied:"), /* @__PURE__ */ React3.createElement(Text3, null, " \u2514\u2500 ", /* @__PURE__ */ React3.createElement(Text3, { color: "green" }, installStats.added), " added, ", /* @__PURE__ */ React3.createElement(Text3, { color: "cyan" }, installStats.updated), " updated"), syncStats.total > 0 && /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(Text3, null, " "), /* @__PURE__ */ React3.createElement(Text3, { bold: true }, "\u{1F504} IDE Sync:"), /* @__PURE__ */ React3.createElement(Text3, null, " \u2514\u2500 ", /* @__PURE__ */ React3.createElement(Text3, { color: "green" }, syncStats.created), " created, ", /* @__PURE__ */ React3.createElement(Text3, { color: "cyan" }, syncStats.updated), " updated"), /* @__PURE__ */ React3.createElement(Text3, null, " \u2514\u2500 Synced to: ", Array.from(selectedIdes).map((ide) => IDE_MIGRATION_CONFIGS[ide].name).join(", ")))), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, null, "\u{1F4C1} Framework: ", /* @__PURE__ */ React3.createElement(Text3, { color: "cyan" }, process.cwd(), "/.jai1"))), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "Press Enter or Q to exit")));
|
|
1051
1536
|
}
|
|
1052
1537
|
const visibleComponents = filteredComponents.slice(0, 12);
|
|
1053
1538
|
const hasMore = filteredComponents.length > 12;
|
|
@@ -1155,6 +1640,8 @@ async function nonInteractiveApply(config, items, options) {
|
|
|
1155
1640
|
console.log(`
|
|
1156
1641
|
\u2705 Complete: ${added} added, ${updated} updated, ${skipped} skipped`);
|
|
1157
1642
|
console.log(`\u{1F4C1} Location: ${targetDir}`);
|
|
1643
|
+
console.log(`
|
|
1644
|
+
\u{1F4A1} Next step: Run "jai1 ide sync" to sync content to your IDE(s)`);
|
|
1158
1645
|
}
|
|
1159
1646
|
|
|
1160
1647
|
// src/commands/update.ts
|
|
@@ -1624,9 +2111,9 @@ var DetailView = ({ item, scrollPosition: initialScroll, onBack }) => {
|
|
|
1624
2111
|
};
|
|
1625
2112
|
|
|
1626
2113
|
// src/services/context-scanner.service.ts
|
|
1627
|
-
import { promises as
|
|
1628
|
-
import
|
|
1629
|
-
import
|
|
2114
|
+
import { promises as fs5 } from "fs";
|
|
2115
|
+
import path2 from "path";
|
|
2116
|
+
import matter2 from "gray-matter";
|
|
1630
2117
|
|
|
1631
2118
|
// src/constants/ide-configs.ts
|
|
1632
2119
|
var IDE_CONFIGS = {
|
|
@@ -1789,8 +2276,8 @@ var ContextScannerService = class {
|
|
|
1789
2276
|
if (!relativePath) return [];
|
|
1790
2277
|
let dirPath;
|
|
1791
2278
|
if (ide === "jai1") {
|
|
1792
|
-
const jai1Path =
|
|
1793
|
-
const frameworkPath =
|
|
2279
|
+
const jai1Path = path2.join(this.projectPath, ".jai1", relativePath);
|
|
2280
|
+
const frameworkPath = path2.join(this.projectPath, "packages/jai1-framework", relativePath);
|
|
1794
2281
|
if (await this.pathExists(jai1Path)) {
|
|
1795
2282
|
dirPath = jai1Path;
|
|
1796
2283
|
} else if (await this.pathExists(frameworkPath)) {
|
|
@@ -1799,9 +2286,9 @@ var ContextScannerService = class {
|
|
|
1799
2286
|
return [];
|
|
1800
2287
|
}
|
|
1801
2288
|
} else {
|
|
1802
|
-
dirPath =
|
|
2289
|
+
dirPath = path2.join(this.projectPath, config.basePath, relativePath);
|
|
1803
2290
|
try {
|
|
1804
|
-
await
|
|
2291
|
+
await fs5.access(dirPath);
|
|
1805
2292
|
} catch {
|
|
1806
2293
|
return [];
|
|
1807
2294
|
}
|
|
@@ -1812,10 +2299,10 @@ var ContextScannerService = class {
|
|
|
1812
2299
|
const skillItems = await this.scanSkills(dirPath, ide);
|
|
1813
2300
|
items.push(...skillItems);
|
|
1814
2301
|
} else {
|
|
1815
|
-
const files = await
|
|
2302
|
+
const files = await fs5.readdir(dirPath);
|
|
1816
2303
|
for (const file of files) {
|
|
1817
|
-
const filepath =
|
|
1818
|
-
const stat = await
|
|
2304
|
+
const filepath = path2.join(dirPath, file);
|
|
2305
|
+
const stat = await fs5.stat(filepath);
|
|
1819
2306
|
if (!stat.isFile()) continue;
|
|
1820
2307
|
const matchesExtension = extensions.some((ext) => file.endsWith(ext));
|
|
1821
2308
|
if (!matchesExtension) continue;
|
|
@@ -1835,20 +2322,20 @@ var ContextScannerService = class {
|
|
|
1835
2322
|
async scanSkills(skillsDir, ide) {
|
|
1836
2323
|
const items = [];
|
|
1837
2324
|
try {
|
|
1838
|
-
const entries = await
|
|
2325
|
+
const entries = await fs5.readdir(skillsDir, { withFileTypes: true });
|
|
1839
2326
|
for (const entry of entries) {
|
|
1840
2327
|
if (!entry.isDirectory()) continue;
|
|
1841
|
-
const skillPath =
|
|
1842
|
-
const skillFilePath =
|
|
2328
|
+
const skillPath = path2.join(skillsDir, entry.name);
|
|
2329
|
+
const skillFilePath = path2.join(skillPath, "SKILL.md");
|
|
1843
2330
|
try {
|
|
1844
|
-
await
|
|
2331
|
+
await fs5.access(skillFilePath);
|
|
1845
2332
|
} catch {
|
|
1846
2333
|
continue;
|
|
1847
2334
|
}
|
|
1848
2335
|
const item = await this.parseContextItem(skillFilePath, ide, "skills");
|
|
1849
|
-
item.hasScripts = await this.pathExists(
|
|
1850
|
-
item.hasReferences = await this.pathExists(
|
|
1851
|
-
item.hasAssets = await this.pathExists(
|
|
2336
|
+
item.hasScripts = await this.pathExists(path2.join(skillPath, "scripts"));
|
|
2337
|
+
item.hasReferences = await this.pathExists(path2.join(skillPath, "references"));
|
|
2338
|
+
item.hasAssets = await this.pathExists(path2.join(skillPath, "assets"));
|
|
1852
2339
|
items.push(item);
|
|
1853
2340
|
}
|
|
1854
2341
|
} catch (error) {
|
|
@@ -1860,9 +2347,9 @@ var ContextScannerService = class {
|
|
|
1860
2347
|
* Parse a context item from file
|
|
1861
2348
|
*/
|
|
1862
2349
|
async parseContextItem(filepath, ide, type) {
|
|
1863
|
-
const content = await
|
|
1864
|
-
const stat = await
|
|
1865
|
-
const { data: frontmatter, content: bodyContent } =
|
|
2350
|
+
const content = await fs5.readFile(filepath, "utf-8");
|
|
2351
|
+
const stat = await fs5.stat(filepath);
|
|
2352
|
+
const { data: frontmatter, content: bodyContent } = matter2(content);
|
|
1866
2353
|
const config = IDE_CONFIGS[ide];
|
|
1867
2354
|
const description = frontmatter[config.frontmatterSchema.descriptionField];
|
|
1868
2355
|
let globs;
|
|
@@ -1878,9 +2365,9 @@ var ContextScannerService = class {
|
|
|
1878
2365
|
globs = triggerValue;
|
|
1879
2366
|
}
|
|
1880
2367
|
const { previewLines, lineCount } = this.extractPreview(bodyContent, 20);
|
|
1881
|
-
const relativePath =
|
|
1882
|
-
const id = `${ide}-${type}-${
|
|
1883
|
-
const name = frontmatter.name ||
|
|
2368
|
+
const relativePath = path2.relative(this.projectPath, filepath);
|
|
2369
|
+
const id = `${ide}-${type}-${path2.basename(filepath, path2.extname(filepath))}`;
|
|
2370
|
+
const name = frontmatter.name || path2.basename(filepath, path2.extname(filepath));
|
|
1884
2371
|
return {
|
|
1885
2372
|
id,
|
|
1886
2373
|
ide,
|
|
@@ -1916,13 +2403,13 @@ var ContextScannerService = class {
|
|
|
1916
2403
|
for (const ide of getAllIDEs()) {
|
|
1917
2404
|
const config = IDE_CONFIGS[ide];
|
|
1918
2405
|
if (ide === "jai1") {
|
|
1919
|
-
const jai1Path =
|
|
1920
|
-
const frameworkPath =
|
|
2406
|
+
const jai1Path = path2.join(this.projectPath, ".jai1");
|
|
2407
|
+
const frameworkPath = path2.join(this.projectPath, "packages/jai1-framework");
|
|
1921
2408
|
if (await this.pathExists(jai1Path) || await this.pathExists(frameworkPath)) {
|
|
1922
2409
|
ides.push(ide);
|
|
1923
2410
|
}
|
|
1924
2411
|
} else {
|
|
1925
|
-
const idePath =
|
|
2412
|
+
const idePath = path2.join(this.projectPath, config.basePath);
|
|
1926
2413
|
if (await this.pathExists(idePath)) {
|
|
1927
2414
|
ides.push(ide);
|
|
1928
2415
|
}
|
|
@@ -1935,7 +2422,7 @@ var ContextScannerService = class {
|
|
|
1935
2422
|
*/
|
|
1936
2423
|
async pathExists(filepath) {
|
|
1937
2424
|
try {
|
|
1938
|
-
await
|
|
2425
|
+
await fs5.access(filepath);
|
|
1939
2426
|
return true;
|
|
1940
2427
|
} catch {
|
|
1941
2428
|
return false;
|
|
@@ -2163,8 +2650,8 @@ async function printStats() {
|
|
|
2163
2650
|
// src/commands/ide/setup.ts
|
|
2164
2651
|
import { Command as Command7 } from "commander";
|
|
2165
2652
|
import { checkbox, confirm as confirm2, select } from "@inquirer/prompts";
|
|
2166
|
-
import
|
|
2167
|
-
import
|
|
2653
|
+
import fs6 from "fs/promises";
|
|
2654
|
+
import path3 from "path";
|
|
2168
2655
|
import { existsSync } from "fs";
|
|
2169
2656
|
var PERFORMANCE_GROUPS = {
|
|
2170
2657
|
telemetry: {
|
|
@@ -2354,436 +2841,124 @@ async function interactiveMode() {
|
|
|
2354
2841
|
{ name: "\u{1F504} Reset to defaults", value: "reset" }
|
|
2355
2842
|
]
|
|
2356
2843
|
});
|
|
2357
|
-
if (action === "max") {
|
|
2358
|
-
const allGroups = Object.keys(PERFORMANCE_GROUPS);
|
|
2359
|
-
await applyGroups(allGroups, "enable");
|
|
2360
|
-
} else if (action === "reset") {
|
|
2361
|
-
await resetSettings([]);
|
|
2362
|
-
} else {
|
|
2363
|
-
await selectGroupsToApply(action);
|
|
2364
|
-
}
|
|
2365
|
-
}
|
|
2366
|
-
async function selectGroupsToApply(action) {
|
|
2367
|
-
const choices = Object.entries(PERFORMANCE_GROUPS).map(([key, group]) => ({
|
|
2368
|
-
name: `${group.name} - ${group.description}`,
|
|
2369
|
-
value: key
|
|
2370
|
-
}));
|
|
2371
|
-
try {
|
|
2372
|
-
const selectedGroups = await checkbox({
|
|
2373
|
-
message: `Select groups to ${action} (SPACE to select, ENTER to confirm):`,
|
|
2374
|
-
choices
|
|
2375
|
-
});
|
|
2376
|
-
if (selectedGroups.length === 0) {
|
|
2377
|
-
console.log("\n\u26A0\uFE0F No groups selected!");
|
|
2378
|
-
console.log(" \u{1F4A1} Tip: Press SPACE to select at least 1 group before pressing ENTER.");
|
|
2379
|
-
return;
|
|
2380
|
-
}
|
|
2381
|
-
await applyGroups(selectedGroups, action);
|
|
2382
|
-
} catch {
|
|
2383
|
-
console.log("\n\u274C Operation cancelled.");
|
|
2384
|
-
}
|
|
2385
|
-
}
|
|
2386
|
-
async function applyGroups(groupKeys, action) {
|
|
2387
|
-
const vscodeDir = path2.join(process.cwd(), ".vscode");
|
|
2388
|
-
const settingsPath = path2.join(vscodeDir, "settings.json");
|
|
2389
|
-
const invalidGroups = groupKeys.filter((key) => !PERFORMANCE_GROUPS[key]);
|
|
2390
|
-
if (invalidGroups.length > 0) {
|
|
2391
|
-
console.log(`
|
|
2392
|
-
\u274C Invalid groups: ${invalidGroups.join(", ")}`);
|
|
2393
|
-
console.log(' \u{1F4A1} Run "jai1 ide setup list" to see available groups.');
|
|
2394
|
-
return;
|
|
2395
|
-
}
|
|
2396
|
-
if (!existsSync(vscodeDir)) {
|
|
2397
|
-
await fs5.mkdir(vscodeDir, { recursive: true });
|
|
2398
|
-
console.log("\u{1F4C1} Created .vscode/ directory");
|
|
2399
|
-
}
|
|
2400
|
-
let currentSettings = {};
|
|
2401
|
-
if (existsSync(settingsPath)) {
|
|
2402
|
-
try {
|
|
2403
|
-
const content = await fs5.readFile(settingsPath, "utf-8");
|
|
2404
|
-
currentSettings = JSON.parse(content);
|
|
2405
|
-
console.log("\u{1F4C4} Read current settings from settings.json");
|
|
2406
|
-
} catch {
|
|
2407
|
-
console.warn("\u26A0\uFE0F Cannot read settings.json (may contain comments).");
|
|
2408
|
-
const confirmOverwrite = await confirm2({
|
|
2409
|
-
message: "Overwrite current settings.json file?",
|
|
2410
|
-
default: false
|
|
2411
|
-
});
|
|
2412
|
-
if (!confirmOverwrite) {
|
|
2413
|
-
console.log("\u274C Operation cancelled.");
|
|
2414
|
-
return;
|
|
2415
|
-
}
|
|
2416
|
-
currentSettings = {};
|
|
2417
|
-
}
|
|
2418
|
-
}
|
|
2419
|
-
const newSettings = { ...currentSettings };
|
|
2420
|
-
console.log(`
|
|
2421
|
-
\u{1F4DD} ${action === "enable" ? "Enabling" : "Disabling"} groups:
|
|
2422
|
-
`);
|
|
2423
|
-
for (const key of groupKeys) {
|
|
2424
|
-
const group = PERFORMANCE_GROUPS[key];
|
|
2425
|
-
console.log(` ${action === "enable" ? "\u2713" : "\u2717"} ${group.name}`);
|
|
2426
|
-
if (action === "enable") {
|
|
2427
|
-
for (const [settingKey, settingValue] of Object.entries(group.settings)) {
|
|
2428
|
-
if (typeof settingValue === "object" && settingValue !== null && !Array.isArray(settingValue) && typeof newSettings[settingKey] === "object" && newSettings[settingKey] !== null) {
|
|
2429
|
-
newSettings[settingKey] = {
|
|
2430
|
-
...newSettings[settingKey],
|
|
2431
|
-
...settingValue
|
|
2432
|
-
};
|
|
2433
|
-
} else {
|
|
2434
|
-
newSettings[settingKey] = settingValue;
|
|
2435
|
-
}
|
|
2436
|
-
}
|
|
2437
|
-
} else {
|
|
2438
|
-
for (const settingKey of Object.keys(group.settings)) {
|
|
2439
|
-
delete newSettings[settingKey];
|
|
2440
|
-
}
|
|
2441
|
-
}
|
|
2442
|
-
}
|
|
2443
|
-
await fs5.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
|
|
2444
|
-
console.log(`
|
|
2445
|
-
\u2705 Updated IDE settings at: ${settingsPath}`);
|
|
2446
|
-
console.log("\u{1F4A1} Tip: Restart your IDE to apply changes.");
|
|
2447
|
-
}
|
|
2448
|
-
async function resetSettings(groupKeys) {
|
|
2449
|
-
const vscodeDir = path2.join(process.cwd(), ".vscode");
|
|
2450
|
-
const settingsPath = path2.join(vscodeDir, "settings.json");
|
|
2451
|
-
if (!existsSync(settingsPath)) {
|
|
2452
|
-
console.log("\n\u26A0\uFE0F No settings.json file found");
|
|
2453
|
-
return;
|
|
2454
|
-
}
|
|
2455
|
-
const confirmReset = await confirm2({
|
|
2456
|
-
message: groupKeys.length === 0 ? "Reset ALL settings to default (delete entire file)?" : `Reset groups: ${groupKeys.join(", ")}?`,
|
|
2457
|
-
default: false
|
|
2458
|
-
});
|
|
2459
|
-
if (!confirmReset) {
|
|
2460
|
-
console.log("\u274C Operation cancelled.");
|
|
2461
|
-
return;
|
|
2462
|
-
}
|
|
2463
|
-
if (groupKeys.length === 0) {
|
|
2464
|
-
await fs5.unlink(settingsPath);
|
|
2465
|
-
console.log("\n\u2705 Deleted settings.json file");
|
|
2466
|
-
} else {
|
|
2467
|
-
await applyGroups(groupKeys, "disable");
|
|
2468
|
-
}
|
|
2469
|
-
console.log("\u{1F4A1} Tip: Restart your IDE to apply changes.");
|
|
2470
|
-
}
|
|
2471
|
-
|
|
2472
|
-
// src/commands/ide/sync.ts
|
|
2473
|
-
import { Command as Command8 } from "commander";
|
|
2474
|
-
import { checkbox as checkbox2, confirm as confirm3 } from "@inquirer/prompts";
|
|
2475
|
-
|
|
2476
|
-
// src/services/migrate-ide.service.ts
|
|
2477
|
-
import { promises as fs6 } from "fs";
|
|
2478
|
-
import path3 from "path";
|
|
2479
|
-
import matter2 from "gray-matter";
|
|
2480
|
-
|
|
2481
|
-
// src/constants/ide-migration-configs.ts
|
|
2482
|
-
var IDE_MIGRATION_CONFIGS = {
|
|
2483
|
-
cursor: {
|
|
2484
|
-
id: "cursor",
|
|
2485
|
-
name: "Cursor",
|
|
2486
|
-
icon: "\u{1F52E}",
|
|
2487
|
-
basePath: ".cursor",
|
|
2488
|
-
rulesPath: "rules",
|
|
2489
|
-
workflowsPath: null,
|
|
2490
|
-
commandsPath: "commands",
|
|
2491
|
-
fileExtension: ".mdc",
|
|
2492
|
-
generateFrontmatter: (opts) => {
|
|
2493
|
-
const lines = ["---"];
|
|
2494
|
-
if (opts.description) {
|
|
2495
|
-
lines.push(`description: ${opts.description}`);
|
|
2496
|
-
}
|
|
2497
|
-
if (opts.globs && opts.globs.length > 0) {
|
|
2498
|
-
lines.push(`globs: ${opts.globs.join(", ")}`);
|
|
2499
|
-
}
|
|
2500
|
-
lines.push(`alwaysApply: ${opts.alwaysApply}`);
|
|
2501
|
-
lines.push("---");
|
|
2502
|
-
return lines.join("\n");
|
|
2503
|
-
}
|
|
2504
|
-
},
|
|
2505
|
-
windsurf: {
|
|
2506
|
-
id: "windsurf",
|
|
2507
|
-
name: "Windsurf",
|
|
2508
|
-
icon: "\u{1F3C4}",
|
|
2509
|
-
basePath: ".windsurf",
|
|
2510
|
-
rulesPath: "rules",
|
|
2511
|
-
workflowsPath: "workflows",
|
|
2512
|
-
commandsPath: null,
|
|
2513
|
-
fileExtension: ".md",
|
|
2514
|
-
generateFrontmatter: (opts) => {
|
|
2515
|
-
const trigger = opts.alwaysApply ? "always" : "glob";
|
|
2516
|
-
return `---
|
|
2517
|
-
trigger: ${trigger}
|
|
2518
|
-
---`;
|
|
2519
|
-
}
|
|
2520
|
-
},
|
|
2521
|
-
antigravity: {
|
|
2522
|
-
id: "antigravity",
|
|
2523
|
-
name: "Antigravity",
|
|
2524
|
-
icon: "\u{1F680}",
|
|
2525
|
-
basePath: ".agent",
|
|
2526
|
-
rulesPath: "rules",
|
|
2527
|
-
workflowsPath: "workflows",
|
|
2528
|
-
commandsPath: null,
|
|
2529
|
-
fileExtension: ".md",
|
|
2530
|
-
generateFrontmatter: (opts) => {
|
|
2531
|
-
const trigger = opts.alwaysApply ? "always" : "glob";
|
|
2532
|
-
return `---
|
|
2533
|
-
trigger: ${trigger}
|
|
2534
|
-
---`;
|
|
2535
|
-
}
|
|
2536
|
-
},
|
|
2537
|
-
claudecode: {
|
|
2538
|
-
id: "claudecode",
|
|
2539
|
-
name: "Claude Code",
|
|
2540
|
-
icon: "\u{1F916}",
|
|
2541
|
-
basePath: ".claude",
|
|
2542
|
-
rulesPath: "rules",
|
|
2543
|
-
workflowsPath: null,
|
|
2544
|
-
commandsPath: "commands",
|
|
2545
|
-
fileExtension: ".md",
|
|
2546
|
-
generateFrontmatter: (opts) => {
|
|
2547
|
-
const lines = ["---"];
|
|
2548
|
-
if (opts.description) {
|
|
2549
|
-
lines.push(`description: ${opts.description}`);
|
|
2550
|
-
}
|
|
2551
|
-
if (opts.globs && opts.globs.length > 0) {
|
|
2552
|
-
lines.push(`paths: ${opts.globs.join(", ")}`);
|
|
2553
|
-
}
|
|
2554
|
-
lines.push("---");
|
|
2555
|
-
return lines.join("\n");
|
|
2556
|
-
}
|
|
2557
|
-
},
|
|
2558
|
-
opencode: {
|
|
2559
|
-
id: "opencode",
|
|
2560
|
-
name: "OpenCode",
|
|
2561
|
-
icon: "\u{1F4BB}",
|
|
2562
|
-
basePath: ".opencode",
|
|
2563
|
-
rulesPath: "rules",
|
|
2564
|
-
workflowsPath: "workflows",
|
|
2565
|
-
commandsPath: null,
|
|
2566
|
-
fileExtension: ".md",
|
|
2567
|
-
generateFrontmatter: (opts) => {
|
|
2568
|
-
const trigger = opts.alwaysApply ? "always" : "glob";
|
|
2569
|
-
return `---
|
|
2570
|
-
trigger: ${trigger}
|
|
2571
|
-
---`;
|
|
2572
|
-
}
|
|
2844
|
+
if (action === "max") {
|
|
2845
|
+
const allGroups = Object.keys(PERFORMANCE_GROUPS);
|
|
2846
|
+
await applyGroups(allGroups, "enable");
|
|
2847
|
+
} else if (action === "reset") {
|
|
2848
|
+
await resetSettings([]);
|
|
2849
|
+
} else {
|
|
2850
|
+
await selectGroupsToApply(action);
|
|
2573
2851
|
}
|
|
2574
|
-
};
|
|
2575
|
-
function getMigrationIDEs() {
|
|
2576
|
-
return Object.keys(IDE_MIGRATION_CONFIGS);
|
|
2577
2852
|
}
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2853
|
+
async function selectGroupsToApply(action) {
|
|
2854
|
+
const choices = Object.entries(PERFORMANCE_GROUPS).map(([key, group]) => ({
|
|
2855
|
+
name: `${group.name} - ${group.description}`,
|
|
2856
|
+
value: key
|
|
2857
|
+
}));
|
|
2858
|
+
try {
|
|
2859
|
+
const selectedGroups = await checkbox({
|
|
2860
|
+
message: `Select groups to ${action} (SPACE to select, ENTER to confirm):`,
|
|
2861
|
+
choices
|
|
2862
|
+
});
|
|
2863
|
+
if (selectedGroups.length === 0) {
|
|
2864
|
+
console.log("\n\u26A0\uFE0F No groups selected!");
|
|
2865
|
+
console.log(" \u{1F4A1} Tip: Press SPACE to select at least 1 group before pressing ENTER.");
|
|
2866
|
+
return;
|
|
2867
|
+
}
|
|
2868
|
+
await applyGroups(selectedGroups, action);
|
|
2869
|
+
} catch {
|
|
2870
|
+
console.log("\n\u274C Operation cancelled.");
|
|
2586
2871
|
}
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
return
|
|
2597
|
-
rules,
|
|
2598
|
-
workflows,
|
|
2599
|
-
commands,
|
|
2600
|
-
totalCount: rules.length + workflows.length + commands.length
|
|
2601
|
-
};
|
|
2872
|
+
}
|
|
2873
|
+
async function applyGroups(groupKeys, action) {
|
|
2874
|
+
const vscodeDir = path3.join(process.cwd(), ".vscode");
|
|
2875
|
+
const settingsPath = path3.join(vscodeDir, "settings.json");
|
|
2876
|
+
const invalidGroups = groupKeys.filter((key) => !PERFORMANCE_GROUPS[key]);
|
|
2877
|
+
if (invalidGroups.length > 0) {
|
|
2878
|
+
console.log(`
|
|
2879
|
+
\u274C Invalid groups: ${invalidGroups.join(", ")}`);
|
|
2880
|
+
console.log(' \u{1F4A1} Run "jai1 ide setup list" to see available groups.');
|
|
2881
|
+
return;
|
|
2602
2882
|
}
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
async scanRulePreset() {
|
|
2607
|
-
const items = [];
|
|
2608
|
-
const presetDir = path3.join(this.jai1Path, "rule-preset");
|
|
2609
|
-
try {
|
|
2610
|
-
await fs6.access(presetDir);
|
|
2611
|
-
} catch {
|
|
2612
|
-
return items;
|
|
2613
|
-
}
|
|
2614
|
-
const files = await fs6.readdir(presetDir);
|
|
2615
|
-
for (const file of files) {
|
|
2616
|
-
if (!file.endsWith(".mdc")) continue;
|
|
2617
|
-
const filepath = path3.join(presetDir, file);
|
|
2618
|
-
const stat = await fs6.stat(filepath);
|
|
2619
|
-
if (!stat.isFile()) continue;
|
|
2620
|
-
const content = await fs6.readFile(filepath, "utf-8");
|
|
2621
|
-
let frontmatter = {};
|
|
2622
|
-
try {
|
|
2623
|
-
const { data } = matter2(content);
|
|
2624
|
-
frontmatter = data;
|
|
2625
|
-
} catch {
|
|
2626
|
-
}
|
|
2627
|
-
items.push({
|
|
2628
|
-
type: "rules",
|
|
2629
|
-
name: path3.basename(file, ".mdc"),
|
|
2630
|
-
filepath,
|
|
2631
|
-
relativePath: path3.relative(this.projectPath, filepath),
|
|
2632
|
-
description: frontmatter.description,
|
|
2633
|
-
globs: this.extractGlobs(frontmatter),
|
|
2634
|
-
alwaysApply: this.extractAlwaysApply(frontmatter)
|
|
2635
|
-
});
|
|
2636
|
-
}
|
|
2637
|
-
return items;
|
|
2883
|
+
if (!existsSync(vscodeDir)) {
|
|
2884
|
+
await fs6.mkdir(vscodeDir, { recursive: true });
|
|
2885
|
+
console.log("\u{1F4C1} Created .vscode/ directory");
|
|
2638
2886
|
}
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
*/
|
|
2642
|
-
async scanContentType(type) {
|
|
2643
|
-
const items = [];
|
|
2644
|
-
const dirPath = path3.join(this.jai1Path, type);
|
|
2887
|
+
let currentSettings = {};
|
|
2888
|
+
if (existsSync(settingsPath)) {
|
|
2645
2889
|
try {
|
|
2646
|
-
await fs6.
|
|
2890
|
+
const content = await fs6.readFile(settingsPath, "utf-8");
|
|
2891
|
+
currentSettings = JSON.parse(content);
|
|
2892
|
+
console.log("\u{1F4C4} Read current settings from settings.json");
|
|
2647
2893
|
} catch {
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
if (!file.endsWith(".md")) continue;
|
|
2653
|
-
const filepath = path3.join(dirPath, file);
|
|
2654
|
-
const stat = await fs6.stat(filepath);
|
|
2655
|
-
if (!stat.isFile()) continue;
|
|
2656
|
-
const content = await fs6.readFile(filepath, "utf-8");
|
|
2657
|
-
const { data: frontmatter } = matter2(content);
|
|
2658
|
-
items.push({
|
|
2659
|
-
type,
|
|
2660
|
-
name: path3.basename(file, ".md"),
|
|
2661
|
-
filepath,
|
|
2662
|
-
relativePath: path3.relative(this.projectPath, filepath),
|
|
2663
|
-
description: frontmatter.description,
|
|
2664
|
-
globs: this.extractGlobs(frontmatter),
|
|
2665
|
-
alwaysApply: this.extractAlwaysApply(frontmatter)
|
|
2894
|
+
console.warn("\u26A0\uFE0F Cannot read settings.json (may contain comments).");
|
|
2895
|
+
const confirmOverwrite = await confirm2({
|
|
2896
|
+
message: "Overwrite current settings.json file?",
|
|
2897
|
+
default: false
|
|
2666
2898
|
});
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
/**
|
|
2671
|
-
* Generate stub file content with @ reference
|
|
2672
|
-
*/
|
|
2673
|
-
generateStubContent(ide, sourceItem) {
|
|
2674
|
-
const config = IDE_MIGRATION_CONFIGS[ide];
|
|
2675
|
-
if (!config) throw new Error(`Unknown IDE: ${ide}`);
|
|
2676
|
-
const frontmatter = config.generateFrontmatter({
|
|
2677
|
-
description: sourceItem.description,
|
|
2678
|
-
globs: sourceItem.globs,
|
|
2679
|
-
alwaysApply: sourceItem.alwaysApply,
|
|
2680
|
-
sourceFile: sourceItem.relativePath
|
|
2681
|
-
});
|
|
2682
|
-
const reference = `@${sourceItem.relativePath}`;
|
|
2683
|
-
if (frontmatter) {
|
|
2684
|
-
return `${frontmatter}
|
|
2685
|
-
|
|
2686
|
-
${reference}
|
|
2687
|
-
`;
|
|
2688
|
-
}
|
|
2689
|
-
return `${reference}
|
|
2690
|
-
`;
|
|
2691
|
-
}
|
|
2692
|
-
/**
|
|
2693
|
-
* Migrate content to specific IDEs
|
|
2694
|
-
*/
|
|
2695
|
-
async migrate(ides, contentTypes, content, onProgress) {
|
|
2696
|
-
const results = [];
|
|
2697
|
-
for (const ide of ides) {
|
|
2698
|
-
const config = IDE_MIGRATION_CONFIGS[ide];
|
|
2699
|
-
if (!config) continue;
|
|
2700
|
-
for (const type of contentTypes) {
|
|
2701
|
-
const items = type === "rules" ? content.rules : type === "workflows" ? content.workflows : content.commands;
|
|
2702
|
-
for (const item of items) {
|
|
2703
|
-
const result = await this.migrateItem(ide, config, item);
|
|
2704
|
-
results.push(result);
|
|
2705
|
-
onProgress?.(result);
|
|
2706
|
-
}
|
|
2899
|
+
if (!confirmOverwrite) {
|
|
2900
|
+
console.log("\u274C Operation cancelled.");
|
|
2901
|
+
return;
|
|
2707
2902
|
}
|
|
2903
|
+
currentSettings = {};
|
|
2708
2904
|
}
|
|
2709
|
-
return results;
|
|
2710
2905
|
}
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2906
|
+
const newSettings = { ...currentSettings };
|
|
2907
|
+
console.log(`
|
|
2908
|
+
\u{1F4DD} ${action === "enable" ? "Enabling" : "Disabling"} groups:
|
|
2909
|
+
`);
|
|
2910
|
+
for (const key of groupKeys) {
|
|
2911
|
+
const group = PERFORMANCE_GROUPS[key];
|
|
2912
|
+
console.log(` ${action === "enable" ? "\u2713" : "\u2717"} ${group.name}`);
|
|
2913
|
+
if (action === "enable") {
|
|
2914
|
+
for (const [settingKey, settingValue] of Object.entries(group.settings)) {
|
|
2915
|
+
if (typeof settingValue === "object" && settingValue !== null && !Array.isArray(settingValue) && typeof newSettings[settingKey] === "object" && newSettings[settingKey] !== null) {
|
|
2916
|
+
newSettings[settingKey] = {
|
|
2917
|
+
...newSettings[settingKey],
|
|
2918
|
+
...settingValue
|
|
2919
|
+
};
|
|
2920
|
+
} else {
|
|
2921
|
+
newSettings[settingKey] = settingValue;
|
|
2922
|
+
}
|
|
2725
2923
|
}
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
try {
|
|
2730
|
-
await fs6.access(targetPath);
|
|
2731
|
-
status = "updated";
|
|
2732
|
-
} catch {
|
|
2924
|
+
} else {
|
|
2925
|
+
for (const settingKey of Object.keys(group.settings)) {
|
|
2926
|
+
delete newSettings[settingKey];
|
|
2733
2927
|
}
|
|
2734
|
-
const stubContent = this.generateStubContent(ide, item);
|
|
2735
|
-
await fs6.writeFile(targetPath, stubContent, "utf-8");
|
|
2736
|
-
return {
|
|
2737
|
-
source: item,
|
|
2738
|
-
targetIDE: ide,
|
|
2739
|
-
targetPath,
|
|
2740
|
-
status
|
|
2741
|
-
};
|
|
2742
|
-
} catch (error) {
|
|
2743
|
-
return {
|
|
2744
|
-
source: item,
|
|
2745
|
-
targetIDE: ide,
|
|
2746
|
-
targetPath: "",
|
|
2747
|
-
status: "error",
|
|
2748
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
2749
|
-
};
|
|
2750
2928
|
}
|
|
2751
2929
|
}
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
break;
|
|
2764
|
-
case "commands":
|
|
2765
|
-
contentPath = config.commandsPath;
|
|
2766
|
-
break;
|
|
2767
|
-
}
|
|
2768
|
-
if (!contentPath) {
|
|
2769
|
-
return null;
|
|
2770
|
-
}
|
|
2771
|
-
const filename = `${item.name}${config.fileExtension}`;
|
|
2772
|
-
return path3.join(this.projectPath, config.basePath, contentPath, filename);
|
|
2930
|
+
await fs6.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
|
|
2931
|
+
console.log(`
|
|
2932
|
+
\u2705 Updated IDE settings at: ${settingsPath}`);
|
|
2933
|
+
console.log("\u{1F4A1} Tip: Restart your IDE to apply changes.");
|
|
2934
|
+
}
|
|
2935
|
+
async function resetSettings(groupKeys) {
|
|
2936
|
+
const vscodeDir = path3.join(process.cwd(), ".vscode");
|
|
2937
|
+
const settingsPath = path3.join(vscodeDir, "settings.json");
|
|
2938
|
+
if (!existsSync(settingsPath)) {
|
|
2939
|
+
console.log("\n\u26A0\uFE0F No settings.json file found");
|
|
2940
|
+
return;
|
|
2773
2941
|
}
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2942
|
+
const confirmReset = await confirm2({
|
|
2943
|
+
message: groupKeys.length === 0 ? "Reset ALL settings to default (delete entire file)?" : `Reset groups: ${groupKeys.join(", ")}?`,
|
|
2944
|
+
default: false
|
|
2945
|
+
});
|
|
2946
|
+
if (!confirmReset) {
|
|
2947
|
+
console.log("\u274C Operation cancelled.");
|
|
2948
|
+
return;
|
|
2780
2949
|
}
|
|
2781
|
-
|
|
2782
|
-
|
|
2950
|
+
if (groupKeys.length === 0) {
|
|
2951
|
+
await fs6.unlink(settingsPath);
|
|
2952
|
+
console.log("\n\u2705 Deleted settings.json file");
|
|
2953
|
+
} else {
|
|
2954
|
+
await applyGroups(groupKeys, "disable");
|
|
2783
2955
|
}
|
|
2784
|
-
};
|
|
2956
|
+
console.log("\u{1F4A1} Tip: Restart your IDE to apply changes.");
|
|
2957
|
+
}
|
|
2785
2958
|
|
|
2786
2959
|
// src/commands/ide/sync.ts
|
|
2960
|
+
import { Command as Command8 } from "commander";
|
|
2961
|
+
import { checkbox as checkbox2, confirm as confirm3 } from "@inquirer/prompts";
|
|
2787
2962
|
function createSyncSubcommand() {
|
|
2788
2963
|
const cmd = new Command8("sync").description("Sync .jai1 content to IDE directories (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) => {
|
|
2789
2964
|
await runSync(options);
|
|
@@ -5662,7 +5837,7 @@ function createImageDeleteCommand() {
|
|
|
5662
5837
|
|
|
5663
5838
|
// src/commands/image/index.ts
|
|
5664
5839
|
function createImageCommand() {
|
|
5665
|
-
const cmd = new Command20("image").description("Image generation commands");
|
|
5840
|
+
const cmd = new Command20("image").description("Image generation commands (Coming Soon)");
|
|
5666
5841
|
cmd.addCommand(createImageGenCommand());
|
|
5667
5842
|
cmd.addCommand(createImageListCommand());
|
|
5668
5843
|
cmd.addCommand(createImageInfoCommand());
|