@jvittechs/jai1-cli 0.1.91 → 0.1.93
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 +1821 -1465
- package/dist/cli.js.map +1 -1
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command60 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/errors/index.ts
|
|
7
7
|
var Jai1Error = class extends Error {
|
|
@@ -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.93",
|
|
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: {
|
|
@@ -97,6 +97,7 @@ var package_default = {
|
|
|
97
97
|
"p-retry": "^6.2.0",
|
|
98
98
|
react: "^18.3.1",
|
|
99
99
|
slugify: "^1.6.6",
|
|
100
|
+
"terminal-image": "^4.1.0",
|
|
100
101
|
undici: "^6.19.5",
|
|
101
102
|
yaml: "^2.5.0",
|
|
102
103
|
zod: "^3.23.8"
|
|
@@ -771,554 +772,965 @@ import { Box as Box2, Text as Text3, useInput, useApp } from "ink";
|
|
|
771
772
|
import Spinner from "ink-spinner";
|
|
772
773
|
import TextInput from "ink-text-input";
|
|
773
774
|
|
|
774
|
-
// src/
|
|
775
|
-
import
|
|
776
|
-
import
|
|
777
|
-
|
|
778
|
-
current,
|
|
779
|
-
total,
|
|
780
|
-
width = 40,
|
|
781
|
-
showPercentage = true,
|
|
782
|
-
color = "cyan"
|
|
783
|
-
}) => {
|
|
784
|
-
const percentage = total > 0 ? Math.round(current / total * 100) : 0;
|
|
785
|
-
const filled = Math.round(percentage / 100 * width);
|
|
786
|
-
const empty = width - filled;
|
|
787
|
-
const bar = "\u2588".repeat(filled) + "\u2591".repeat(empty);
|
|
788
|
-
return /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color }, bar), showPercentage && /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " ", current, "/", total, " (", percentage, "%)"));
|
|
789
|
-
};
|
|
790
|
-
|
|
791
|
-
// src/ui/shared/StatusIcon.tsx
|
|
792
|
-
import React2 from "react";
|
|
793
|
-
import { Text as Text2 } from "ink";
|
|
794
|
-
|
|
795
|
-
// src/ui/shared/theme.ts
|
|
796
|
-
var theme = {
|
|
797
|
-
colors: {
|
|
798
|
-
primary: "#6366f1",
|
|
799
|
-
// Indigo
|
|
800
|
-
success: "#22c55e",
|
|
801
|
-
// Green
|
|
802
|
-
warning: "#eab308",
|
|
803
|
-
// Yellow
|
|
804
|
-
error: "#ef4444",
|
|
805
|
-
// Red
|
|
806
|
-
muted: "#6b7280",
|
|
807
|
-
// Gray
|
|
808
|
-
border: "#374151",
|
|
809
|
-
// Dark gray
|
|
810
|
-
text: "#f3f4f6",
|
|
811
|
-
// Light gray
|
|
812
|
-
background: "#1a1a2e"
|
|
813
|
-
// Dark background
|
|
814
|
-
},
|
|
815
|
-
icons: {
|
|
816
|
-
success: "\u2705",
|
|
817
|
-
error: "\u274C",
|
|
818
|
-
pending: "\u25CB",
|
|
819
|
-
loading: "\u23F3",
|
|
820
|
-
package: "\u{1F4E6}",
|
|
821
|
-
folder: "\u{1F4C1}",
|
|
822
|
-
file: "\u{1F4C4}",
|
|
823
|
-
tip: "\u{1F4A1}",
|
|
824
|
-
warning: "\u26A0\uFE0F",
|
|
825
|
-
checkmark: "\u2713",
|
|
826
|
-
arrow: "\u2192",
|
|
827
|
-
bullet: "\u2022"
|
|
828
|
-
},
|
|
829
|
-
borders: {
|
|
830
|
-
single: {
|
|
831
|
-
topLeft: "\u250C",
|
|
832
|
-
topRight: "\u2510",
|
|
833
|
-
bottomLeft: "\u2514",
|
|
834
|
-
bottomRight: "\u2518",
|
|
835
|
-
horizontal: "\u2500",
|
|
836
|
-
vertical: "\u2502"
|
|
837
|
-
},
|
|
838
|
-
double: {
|
|
839
|
-
topLeft: "\u2554",
|
|
840
|
-
topRight: "\u2557",
|
|
841
|
-
bottomLeft: "\u255A",
|
|
842
|
-
bottomRight: "\u255D",
|
|
843
|
-
horizontal: "\u2550",
|
|
844
|
-
vertical: "\u2551"
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
};
|
|
848
|
-
|
|
849
|
-
// src/ui/shared/StatusIcon.tsx
|
|
850
|
-
var StatusIcon = ({ status, text }) => {
|
|
851
|
-
const iconMap = {
|
|
852
|
-
success: theme.icons.success,
|
|
853
|
-
error: theme.icons.error,
|
|
854
|
-
pending: theme.icons.pending,
|
|
855
|
-
loading: theme.icons.loading,
|
|
856
|
-
warning: theme.icons.warning
|
|
857
|
-
};
|
|
858
|
-
const colorMap = {
|
|
859
|
-
success: "green",
|
|
860
|
-
error: "red",
|
|
861
|
-
pending: "gray",
|
|
862
|
-
loading: "yellow",
|
|
863
|
-
warning: "yellow"
|
|
864
|
-
};
|
|
865
|
-
const icon = iconMap[status];
|
|
866
|
-
const color = colorMap[status];
|
|
867
|
-
return /* @__PURE__ */ React2.createElement(Text2, { color }, icon, " ", text);
|
|
868
|
-
};
|
|
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";
|
|
869
779
|
|
|
870
|
-
// src/
|
|
871
|
-
var
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
const [cursorIndex, setCursorIndex] = useState(0);
|
|
886
|
-
const [focusArea, setFocusArea] = useState("packages");
|
|
887
|
-
const [selectedPackageIndex, setSelectedPackageIndex] = useState(0);
|
|
888
|
-
const [installProgress, setInstallProgress] = useState([]);
|
|
889
|
-
const [installStats, setInstallStats] = useState({ total: 0, completed: 0, added: 0, updated: 0, failed: 0 });
|
|
890
|
-
const service = new ComponentsService();
|
|
891
|
-
useEffect(() => {
|
|
892
|
-
const loadData = async () => {
|
|
893
|
-
try {
|
|
894
|
-
setLoading(true);
|
|
895
|
-
const [comps, tagList, installed] = await Promise.all([
|
|
896
|
-
service.list(config),
|
|
897
|
-
service.listTags(config),
|
|
898
|
-
service.getInstalled()
|
|
899
|
-
]);
|
|
900
|
-
setComponents(comps);
|
|
901
|
-
setTags(tagList);
|
|
902
|
-
setInstalledPaths(new Set(Object.keys(installed)));
|
|
903
|
-
} catch (err) {
|
|
904
|
-
setError(err instanceof Error ? err.message : "Failed to load data");
|
|
905
|
-
} finally {
|
|
906
|
-
setLoading(false);
|
|
780
|
+
// src/constants/ide-migration-configs.ts
|
|
781
|
+
var IDE_MIGRATION_CONFIGS = {
|
|
782
|
+
cursor: {
|
|
783
|
+
id: "cursor",
|
|
784
|
+
name: "Cursor",
|
|
785
|
+
icon: "\u{1F52E}",
|
|
786
|
+
basePath: ".cursor",
|
|
787
|
+
rulesPath: "rules",
|
|
788
|
+
workflowsPath: null,
|
|
789
|
+
commandsPath: "commands",
|
|
790
|
+
fileExtension: ".mdc",
|
|
791
|
+
generateFrontmatter: (opts) => {
|
|
792
|
+
const lines = ["---"];
|
|
793
|
+
if (opts.description) {
|
|
794
|
+
lines.push(`description: ${opts.description}`);
|
|
907
795
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
}, []);
|
|
911
|
-
const filteredComponents = useMemo(() => {
|
|
912
|
-
if (!searchQuery.trim()) return components;
|
|
913
|
-
const query = searchQuery.toLowerCase();
|
|
914
|
-
return components.filter(
|
|
915
|
-
(c) => c.filepath.toLowerCase().includes(query) || c.name.toLowerCase().includes(query) || c.tags && c.tags.some((t) => t.toLowerCase().includes(query))
|
|
916
|
-
);
|
|
917
|
-
}, [components, searchQuery]);
|
|
918
|
-
useInput((input4, key) => {
|
|
919
|
-
if (viewState === "summary") {
|
|
920
|
-
if (key.return || input4 === "q" || key.escape) {
|
|
921
|
-
onExit();
|
|
796
|
+
if (opts.globs && opts.globs.length > 0) {
|
|
797
|
+
lines.push(`globs: ${opts.globs.join(", ")}`);
|
|
922
798
|
}
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
return;
|
|
799
|
+
lines.push(`alwaysApply: ${opts.alwaysApply}`);
|
|
800
|
+
lines.push("---");
|
|
801
|
+
return lines.join("\n");
|
|
927
802
|
}
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
803
|
+
},
|
|
804
|
+
windsurf: {
|
|
805
|
+
id: "windsurf",
|
|
806
|
+
name: "Windsurf",
|
|
807
|
+
icon: "\u{1F3C4}",
|
|
808
|
+
basePath: ".windsurf",
|
|
809
|
+
rulesPath: "rules",
|
|
810
|
+
workflowsPath: "workflows",
|
|
811
|
+
commandsPath: null,
|
|
812
|
+
fileExtension: ".md",
|
|
813
|
+
generateFrontmatter: (opts) => {
|
|
814
|
+
const trigger = opts.alwaysApply ? "always" : "glob";
|
|
815
|
+
return `---
|
|
816
|
+
trigger: ${trigger}
|
|
817
|
+
---`;
|
|
933
818
|
}
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
819
|
+
},
|
|
820
|
+
antigravity: {
|
|
821
|
+
id: "antigravity",
|
|
822
|
+
name: "Antigravity",
|
|
823
|
+
icon: "\u{1F680}",
|
|
824
|
+
basePath: ".agent",
|
|
825
|
+
rulesPath: "rules",
|
|
826
|
+
workflowsPath: "workflows",
|
|
827
|
+
commandsPath: null,
|
|
828
|
+
fileExtension: ".md",
|
|
829
|
+
generateFrontmatter: (opts) => {
|
|
830
|
+
const trigger = opts.alwaysApply ? "always" : "glob";
|
|
831
|
+
return `---
|
|
832
|
+
trigger: ${trigger}
|
|
833
|
+
---`;
|
|
937
834
|
}
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
835
|
+
},
|
|
836
|
+
claudecode: {
|
|
837
|
+
id: "claudecode",
|
|
838
|
+
name: "Claude Code",
|
|
839
|
+
icon: "\u{1F916}",
|
|
840
|
+
basePath: ".claude",
|
|
841
|
+
rulesPath: "rules",
|
|
842
|
+
workflowsPath: null,
|
|
843
|
+
commandsPath: "commands",
|
|
844
|
+
fileExtension: ".md",
|
|
845
|
+
generateFrontmatter: (opts) => {
|
|
846
|
+
const lines = ["---"];
|
|
847
|
+
if (opts.description) {
|
|
848
|
+
lines.push(`description: ${opts.description}`);
|
|
941
849
|
}
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
if (focusArea !== "search") {
|
|
945
|
-
if (input4 === "a") {
|
|
946
|
-
setSelectedPaths((prev) => {
|
|
947
|
-
const next = new Set(prev);
|
|
948
|
-
filteredComponents.forEach((c) => next.add(c.filepath));
|
|
949
|
-
return next;
|
|
950
|
-
});
|
|
951
|
-
return;
|
|
952
|
-
} else if (input4 === "c") {
|
|
953
|
-
setSelectedPaths(/* @__PURE__ */ new Set());
|
|
954
|
-
return;
|
|
850
|
+
if (opts.globs && opts.globs.length > 0) {
|
|
851
|
+
lines.push(`paths: ${opts.globs.join(", ")}`);
|
|
955
852
|
}
|
|
853
|
+
lines.push("---");
|
|
854
|
+
return lines.join("\n");
|
|
956
855
|
}
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
return next;
|
|
973
|
-
});
|
|
974
|
-
}
|
|
975
|
-
}
|
|
856
|
+
},
|
|
857
|
+
opencode: {
|
|
858
|
+
id: "opencode",
|
|
859
|
+
name: "OpenCode",
|
|
860
|
+
icon: "\u{1F4BB}",
|
|
861
|
+
basePath: ".opencode",
|
|
862
|
+
rulesPath: "rules",
|
|
863
|
+
workflowsPath: "workflows",
|
|
864
|
+
commandsPath: null,
|
|
865
|
+
fileExtension: ".md",
|
|
866
|
+
generateFrontmatter: (opts) => {
|
|
867
|
+
const trigger = opts.alwaysApply ? "always" : "glob";
|
|
868
|
+
return `---
|
|
869
|
+
trigger: ${trigger}
|
|
870
|
+
---`;
|
|
976
871
|
}
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
872
|
+
}
|
|
873
|
+
};
|
|
874
|
+
function getMigrationIDEs() {
|
|
875
|
+
return Object.keys(IDE_MIGRATION_CONFIGS);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
// src/services/migrate-ide.service.ts
|
|
879
|
+
var MigrateIdeService = class {
|
|
880
|
+
projectPath;
|
|
881
|
+
jai1Path;
|
|
882
|
+
constructor(projectPath = process.cwd()) {
|
|
883
|
+
this.projectPath = projectPath;
|
|
884
|
+
this.jai1Path = path.join(projectPath, ".jai1");
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Scan .jai1/ directory for content
|
|
888
|
+
*/
|
|
889
|
+
async scanJai1Content() {
|
|
890
|
+
const manualRules = await this.scanContentType("rules");
|
|
891
|
+
const presetRules = await this.scanRulePreset();
|
|
892
|
+
const rules = [...presetRules, ...manualRules];
|
|
893
|
+
const workflows = await this.scanContentType("workflows");
|
|
894
|
+
const commands = [];
|
|
895
|
+
return {
|
|
896
|
+
rules,
|
|
897
|
+
workflows,
|
|
898
|
+
commands,
|
|
899
|
+
totalCount: rules.length + workflows.length + commands.length
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Scan .jai1/rule-preset/ directory for rule preset files
|
|
904
|
+
*/
|
|
905
|
+
async scanRulePreset() {
|
|
906
|
+
const items = [];
|
|
907
|
+
const presetDir = path.join(this.jai1Path, "rule-preset");
|
|
908
|
+
try {
|
|
909
|
+
await fs4.access(presetDir);
|
|
910
|
+
} catch {
|
|
911
|
+
return items;
|
|
994
912
|
}
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
filepath: fp,
|
|
1004
|
-
status: "pending"
|
|
1005
|
-
}));
|
|
1006
|
-
setInstallProgress(initialProgress);
|
|
1007
|
-
setInstallStats({ total: resolvedPaths.length, completed: 0, added: 0, updated: 0, failed: 0 });
|
|
1008
|
-
let added = 0, updated = 0, failed = 0;
|
|
1009
|
-
for (let i = 0; i < resolvedPaths.length; i++) {
|
|
1010
|
-
const fp = resolvedPaths[i];
|
|
1011
|
-
setInstallProgress((prev) => prev.map(
|
|
1012
|
-
(p) => p.filepath === fp ? { ...p, status: "downloading" } : p
|
|
1013
|
-
));
|
|
913
|
+
const files = await fs4.readdir(presetDir);
|
|
914
|
+
for (const file of files) {
|
|
915
|
+
if (!file.endsWith(".mdc")) continue;
|
|
916
|
+
const filepath = path.join(presetDir, file);
|
|
917
|
+
const stat = await fs4.stat(filepath);
|
|
918
|
+
if (!stat.isFile()) continue;
|
|
919
|
+
const content = await fs4.readFile(filepath, "utf-8");
|
|
920
|
+
let frontmatter = {};
|
|
1014
921
|
try {
|
|
1015
|
-
const
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
(p) => p.filepath === fp ? { ...p, status: "error", error: "Modified locally" } : p
|
|
1019
|
-
));
|
|
1020
|
-
failed++;
|
|
1021
|
-
} else {
|
|
1022
|
-
await service.install(config, fp, targetDir);
|
|
1023
|
-
setInstallProgress((prev) => prev.map(
|
|
1024
|
-
(p) => p.filepath === fp ? { ...p, status: "success" } : p
|
|
1025
|
-
));
|
|
1026
|
-
if (info) updated++;
|
|
1027
|
-
else added++;
|
|
1028
|
-
}
|
|
1029
|
-
} catch (err) {
|
|
1030
|
-
setInstallProgress((prev) => prev.map(
|
|
1031
|
-
(p) => p.filepath === fp ? { ...p, status: "error", error: err instanceof Error ? err.message : "Error" } : p
|
|
1032
|
-
));
|
|
1033
|
-
failed++;
|
|
922
|
+
const { data } = matter(content);
|
|
923
|
+
frontmatter = data;
|
|
924
|
+
} catch {
|
|
1034
925
|
}
|
|
1035
|
-
|
|
926
|
+
items.push({
|
|
927
|
+
type: "rules",
|
|
928
|
+
name: path.basename(file, ".mdc"),
|
|
929
|
+
filepath,
|
|
930
|
+
relativePath: path.relative(this.projectPath, filepath),
|
|
931
|
+
description: frontmatter.description,
|
|
932
|
+
globs: this.extractGlobs(frontmatter),
|
|
933
|
+
alwaysApply: this.extractAlwaysApply(frontmatter)
|
|
934
|
+
});
|
|
1036
935
|
}
|
|
1037
|
-
|
|
1038
|
-
};
|
|
1039
|
-
if (loading) {
|
|
1040
|
-
return /* @__PURE__ */ React3.createElement(Box2, { padding: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: "cyan" }, /* @__PURE__ */ React3.createElement(Spinner, { type: "dots" }), " \u0110ang t\u1EA3i components..."));
|
|
1041
|
-
}
|
|
1042
|
-
if (error) {
|
|
1043
|
-
return /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: "red" }, "\u274C Error: ", error), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "Press q to exit"));
|
|
1044
|
-
}
|
|
1045
|
-
if (viewState === "installing") {
|
|
1046
|
-
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"))));
|
|
1047
|
-
}
|
|
1048
|
-
if (viewState === "summary") {
|
|
1049
|
-
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 }, "Press Enter or q to exit")));
|
|
936
|
+
return items;
|
|
1050
937
|
}
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
938
|
+
/**
|
|
939
|
+
* Scan a specific content type
|
|
940
|
+
*/
|
|
941
|
+
async scanContentType(type) {
|
|
942
|
+
const items = [];
|
|
943
|
+
const dirPath = path.join(this.jai1Path, type);
|
|
944
|
+
try {
|
|
945
|
+
await fs4.access(dirPath);
|
|
946
|
+
} catch {
|
|
947
|
+
return items;
|
|
1059
948
|
}
|
|
1060
|
-
|
|
1061
|
-
const
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
const isInstalled = installedPaths.has(comp.filepath);
|
|
1078
|
-
return /* @__PURE__ */ React3.createElement(Box2, { key: comp.filepath }, /* @__PURE__ */ React3.createElement(Text3, { color: isCursor ? "cyan" : "white" }, isCursor ? "\u276F " : " ", isChecked ? "[\u2713]" : "[ ]", " ", comp.filepath), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, " ", isInstalled ? "\u2713 installed" : "\u25CB new"));
|
|
1079
|
-
}), filteredComponents.length === 0 && /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "No components match your search")), selectedPaths.size > 0 && /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Text3, { bold: true }, "Selected: ", selectedPaths.size, " components"), /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", marginLeft: 2 }, Array.from(selectedPaths).slice(0, 4).map((fp) => /* @__PURE__ */ React3.createElement(Text3, { key: fp, dimColor: true }, "\u{1F4CC} ", fp)), selectedPaths.size > 4 && /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, " ... and ", selectedPaths.size - 4, " more"))), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "[Tab] Switch area \xB7 [\u2191\u2193/\u2190\u2192] Navigate \xB7 [\u2423] Toggle \xB7 [A] Select all \xB7 [C] Clear \xB7 [Enter] Apply \xB7 [Q] Quit")));
|
|
1080
|
-
};
|
|
1081
|
-
|
|
1082
|
-
// src/commands/apply.ts
|
|
1083
|
-
function createApplyCommand() {
|
|
1084
|
-
const cmd = new Command3("apply").description("Apply components to your project (interactive UI)").argument("[items...]", "Package name or component filepaths (non-interactive mode)").option("--force", "Force overwrite even if modified locally").option("--no-interactive", "Disable interactive UI (for CI/CD)").action(async (items, options) => {
|
|
1085
|
-
const configService = new ConfigService();
|
|
1086
|
-
const config = await configService.load();
|
|
1087
|
-
if (!config) {
|
|
1088
|
-
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
949
|
+
const files = await fs4.readdir(dirPath);
|
|
950
|
+
for (const file of files) {
|
|
951
|
+
if (!file.endsWith(".md")) continue;
|
|
952
|
+
const filepath = path.join(dirPath, file);
|
|
953
|
+
const stat = await fs4.stat(filepath);
|
|
954
|
+
if (!stat.isFile()) continue;
|
|
955
|
+
const content = await fs4.readFile(filepath, "utf-8");
|
|
956
|
+
const { data: frontmatter } = matter(content);
|
|
957
|
+
items.push({
|
|
958
|
+
type,
|
|
959
|
+
name: path.basename(file, ".md"),
|
|
960
|
+
filepath,
|
|
961
|
+
relativePath: path.relative(this.projectPath, filepath),
|
|
962
|
+
description: frontmatter.description,
|
|
963
|
+
globs: this.extractGlobs(frontmatter),
|
|
964
|
+
alwaysApply: this.extractAlwaysApply(frontmatter)
|
|
965
|
+
});
|
|
1089
966
|
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
967
|
+
return items;
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Generate stub file content with @ reference
|
|
971
|
+
*/
|
|
972
|
+
generateStubContent(ide, sourceItem) {
|
|
973
|
+
const config = IDE_MIGRATION_CONFIGS[ide];
|
|
974
|
+
if (!config) throw new Error(`Unknown IDE: ${ide}`);
|
|
975
|
+
const frontmatter = config.generateFrontmatter({
|
|
976
|
+
description: sourceItem.description,
|
|
977
|
+
globs: sourceItem.globs,
|
|
978
|
+
alwaysApply: sourceItem.alwaysApply,
|
|
979
|
+
sourceFile: sourceItem.relativePath
|
|
980
|
+
});
|
|
981
|
+
const reference = `@${sourceItem.relativePath}`;
|
|
982
|
+
if (frontmatter) {
|
|
983
|
+
return `${frontmatter}
|
|
984
|
+
|
|
985
|
+
${reference}
|
|
986
|
+
`;
|
|
1093
987
|
}
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
988
|
+
return `${reference}
|
|
989
|
+
`;
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* Migrate content to specific IDEs
|
|
993
|
+
*/
|
|
994
|
+
async migrate(ides, contentTypes, content, onProgress) {
|
|
995
|
+
const results = [];
|
|
996
|
+
for (const ide of ides) {
|
|
997
|
+
const config = IDE_MIGRATION_CONFIGS[ide];
|
|
998
|
+
if (!config) continue;
|
|
999
|
+
for (const type of contentTypes) {
|
|
1000
|
+
const items = type === "rules" ? content.rules : type === "workflows" ? content.workflows : content.commands;
|
|
1001
|
+
for (const item of items) {
|
|
1002
|
+
const result = await this.migrateItem(ide, config, item);
|
|
1003
|
+
results.push(result);
|
|
1004
|
+
onProgress?.(result);
|
|
1100
1005
|
}
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
});
|
|
1105
|
-
return cmd;
|
|
1106
|
-
}
|
|
1107
|
-
async function nonInteractiveApply(config, items, options) {
|
|
1108
|
-
const componentsService = new ComponentsService();
|
|
1109
|
-
if (items.length === 0) {
|
|
1110
|
-
console.log("\u274C No items specified. Use interactive mode or provide package/component names.");
|
|
1111
|
-
console.log("\u{1F4A1} Usage: jai1 apply <package> or jai1 apply <filepath...>");
|
|
1112
|
-
return;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
return results;
|
|
1113
1009
|
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
console.log(`\u{1F4E6} Applying package '${items[0]}'...`);
|
|
1119
|
-
const components = await componentsService.list(config, { tag: items[0] });
|
|
1120
|
-
filepaths = components.map((c) => c.filepath);
|
|
1121
|
-
} else {
|
|
1122
|
-
filepaths = items;
|
|
1123
|
-
}
|
|
1124
|
-
console.log("\u{1F50D} Resolving dependencies...");
|
|
1125
|
-
const resolvedPaths = await componentsService.resolveWithDependencies(config, filepaths);
|
|
1126
|
-
console.log(`\u{1F4E6} Will apply ${resolvedPaths.length} component(s):`);
|
|
1127
|
-
for (const fp of resolvedPaths) {
|
|
1128
|
-
const isDep = !filepaths.includes(fp);
|
|
1129
|
-
const suffix = isDep ? " (dependency)" : "";
|
|
1130
|
-
console.log(` - ${fp}${suffix}`);
|
|
1131
|
-
}
|
|
1132
|
-
const targetDir = process.cwd() + "/.jai1";
|
|
1133
|
-
const installed = await componentsService.getInstalled();
|
|
1134
|
-
let added = 0;
|
|
1135
|
-
let updated = 0;
|
|
1136
|
-
let skipped = 0;
|
|
1137
|
-
for (const fp of resolvedPaths) {
|
|
1010
|
+
/**
|
|
1011
|
+
* Migrate a single item
|
|
1012
|
+
*/
|
|
1013
|
+
async migrateItem(ide, config, item) {
|
|
1138
1014
|
try {
|
|
1139
|
-
const
|
|
1140
|
-
if (
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1015
|
+
const targetPath = this.getTargetPath(config, item);
|
|
1016
|
+
if (!targetPath) {
|
|
1017
|
+
return {
|
|
1018
|
+
source: item,
|
|
1019
|
+
targetIDE: ide,
|
|
1020
|
+
targetPath: "",
|
|
1021
|
+
status: "skipped",
|
|
1022
|
+
error: `IDE ${config.id} does not support ${item.type}`
|
|
1023
|
+
};
|
|
1144
1024
|
}
|
|
1145
|
-
|
|
1146
|
-
await
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1025
|
+
const targetDir = path.dirname(targetPath);
|
|
1026
|
+
await fs4.mkdir(targetDir, { recursive: true });
|
|
1027
|
+
let status = "created";
|
|
1028
|
+
try {
|
|
1029
|
+
await fs4.access(targetPath);
|
|
1030
|
+
status = "updated";
|
|
1031
|
+
} catch {
|
|
1032
|
+
}
|
|
1033
|
+
const stubContent = this.generateStubContent(ide, item);
|
|
1034
|
+
await fs4.writeFile(targetPath, stubContent, "utf-8");
|
|
1035
|
+
return {
|
|
1036
|
+
source: item,
|
|
1037
|
+
targetIDE: ide,
|
|
1038
|
+
targetPath,
|
|
1039
|
+
status
|
|
1040
|
+
};
|
|
1150
1041
|
} catch (error) {
|
|
1151
|
-
|
|
1042
|
+
return {
|
|
1043
|
+
source: item,
|
|
1044
|
+
targetIDE: ide,
|
|
1045
|
+
targetPath: "",
|
|
1046
|
+
status: "error",
|
|
1047
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
1048
|
+
};
|
|
1152
1049
|
}
|
|
1153
1050
|
}
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
const config = await configService.load();
|
|
1170
|
-
if (!config) {
|
|
1171
|
-
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
1172
|
-
}
|
|
1173
|
-
const componentsService = new ComponentsService();
|
|
1174
|
-
const installed = await componentsService.getInstalled();
|
|
1175
|
-
const filepaths = Object.keys(installed);
|
|
1176
|
-
if (filepaths.length === 0) {
|
|
1177
|
-
console.log('No components installed. Run "jai1 apply" to add components.');
|
|
1178
|
-
return;
|
|
1179
|
-
}
|
|
1180
|
-
console.log("\u{1F50D} Checking for updates...");
|
|
1181
|
-
try {
|
|
1182
|
-
const checksums = await componentsService.getChecksums(config, filepaths);
|
|
1183
|
-
const updates = [];
|
|
1184
|
-
const upToDate = [];
|
|
1185
|
-
for (const fp of filepaths) {
|
|
1186
|
-
const current = installed[fp];
|
|
1187
|
-
const latest = checksums[fp];
|
|
1188
|
-
if (!latest) {
|
|
1189
|
-
console.log(`\u26A0\uFE0F ${fp}: Component not found on server (deprecated?)`);
|
|
1190
|
-
continue;
|
|
1191
|
-
}
|
|
1192
|
-
if (current.checksum !== latest.checksum) {
|
|
1193
|
-
updates.push(fp);
|
|
1194
|
-
} else {
|
|
1195
|
-
upToDate.push(fp);
|
|
1196
|
-
}
|
|
1197
|
-
}
|
|
1198
|
-
if (updates.length === 0) {
|
|
1199
|
-
console.log("\u2705 All components are up to date.");
|
|
1200
|
-
return;
|
|
1201
|
-
}
|
|
1202
|
-
console.log(`
|
|
1203
|
-
\u{1F4E6} Found ${updates.length} updates:`);
|
|
1204
|
-
for (const fp of updates) {
|
|
1205
|
-
const current = installed[fp];
|
|
1206
|
-
const latest = checksums[fp];
|
|
1207
|
-
console.log(` - ${fp} (v${current.version} \u2192 v${latest.version})`);
|
|
1208
|
-
}
|
|
1209
|
-
console.log();
|
|
1210
|
-
if (!options.force) {
|
|
1211
|
-
const shouldUpdate = await confirm({ message: "Update now?", default: true });
|
|
1212
|
-
if (!shouldUpdate) return;
|
|
1213
|
-
}
|
|
1214
|
-
const targetDir = process.cwd() + "/.jai1";
|
|
1215
|
-
let updatedCount = 0;
|
|
1216
|
-
const backupPaths = [];
|
|
1217
|
-
for (const fp of updates) {
|
|
1218
|
-
try {
|
|
1219
|
-
console.log(`\u{1F4E5} Updating ${fp}...`);
|
|
1220
|
-
const backupPath = await componentsService.backupFile(fp, targetDir);
|
|
1221
|
-
if (backupPath) {
|
|
1222
|
-
backupPaths.push(backupPath);
|
|
1223
|
-
const displayPath = backupPath.split(".jai1_backup/")[1];
|
|
1224
|
-
console.log(` Backed up: .../${displayPath}`);
|
|
1225
|
-
}
|
|
1226
|
-
await componentsService.install(config, fp, targetDir);
|
|
1227
|
-
console.log(`\u2705 Updated`);
|
|
1228
|
-
updatedCount++;
|
|
1229
|
-
} catch (error) {
|
|
1230
|
-
console.log(`\u274C Failed to update ${fp}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1231
|
-
}
|
|
1051
|
+
/**
|
|
1052
|
+
* Get target file path for an item
|
|
1053
|
+
*/
|
|
1054
|
+
getTargetPath(config, item) {
|
|
1055
|
+
let contentPath = null;
|
|
1056
|
+
switch (item.type) {
|
|
1057
|
+
case "rules":
|
|
1058
|
+
contentPath = config.rulesPath;
|
|
1059
|
+
break;
|
|
1060
|
+
case "workflows":
|
|
1061
|
+
contentPath = config.workflowsPath ?? config.commandsPath;
|
|
1062
|
+
break;
|
|
1063
|
+
case "commands":
|
|
1064
|
+
contentPath = config.commandsPath;
|
|
1065
|
+
break;
|
|
1232
1066
|
}
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
if (backupPaths.length > 0) {
|
|
1236
|
-
console.log(`
|
|
1237
|
-
\u{1F4C1} Backups created in .jai1_backup/`);
|
|
1238
|
-
const cleanBackups = await confirm({ message: "Clear these backups?", default: false });
|
|
1239
|
-
if (cleanBackups) {
|
|
1240
|
-
await componentsService.clearBackups(process.cwd());
|
|
1241
|
-
console.log("\u{1F5D1}\uFE0F Backups cleared.");
|
|
1242
|
-
} else {
|
|
1243
|
-
console.log('\u{1F4A1} Run "jai1 clean" to remove them later.');
|
|
1244
|
-
}
|
|
1067
|
+
if (!contentPath) {
|
|
1068
|
+
return null;
|
|
1245
1069
|
}
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
throw new Error(`Update check failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1249
|
-
}
|
|
1250
|
-
}
|
|
1251
|
-
|
|
1252
|
-
// src/commands/framework/check.ts
|
|
1253
|
-
import { Command as Command5 } from "commander";
|
|
1254
|
-
function createCheckCommand() {
|
|
1255
|
-
const cmd = new Command5("check").description("Check for framework updates").option("--json", "Output as JSON").action(async (options) => {
|
|
1256
|
-
await handleCheck(options);
|
|
1257
|
-
});
|
|
1258
|
-
return cmd;
|
|
1259
|
-
}
|
|
1260
|
-
async function handleCheck(options) {
|
|
1261
|
-
const configService = new ConfigService();
|
|
1262
|
-
const config = await configService.load();
|
|
1263
|
-
if (!config) {
|
|
1264
|
-
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
1070
|
+
const filename = `${item.name}${config.fileExtension}`;
|
|
1071
|
+
return path.join(this.projectPath, config.basePath, contentPath, filename);
|
|
1265
1072
|
}
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
return;
|
|
1073
|
+
// Helper methods
|
|
1074
|
+
extractGlobs(frontmatter) {
|
|
1075
|
+
const globs = frontmatter.globs;
|
|
1076
|
+
if (typeof globs === "string") return [globs];
|
|
1077
|
+
if (Array.isArray(globs)) return globs;
|
|
1078
|
+
return void 0;
|
|
1272
1079
|
}
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
const checksums = await componentsService.getChecksums(config, filepaths);
|
|
1276
|
-
const updates = [];
|
|
1277
|
-
const upToDate = [];
|
|
1278
|
-
for (const fp of filepaths) {
|
|
1279
|
-
const current = installed[fp];
|
|
1280
|
-
const latest = checksums[fp];
|
|
1281
|
-
if (!latest) {
|
|
1282
|
-
continue;
|
|
1283
|
-
}
|
|
1284
|
-
if (current.checksum !== latest.checksum) {
|
|
1285
|
-
updates.push(fp);
|
|
1286
|
-
} else {
|
|
1287
|
-
upToDate.push(fp);
|
|
1288
|
-
}
|
|
1289
|
-
}
|
|
1290
|
-
console.log(`\u{1F4CC} Components status:`);
|
|
1291
|
-
for (const fp of updates) {
|
|
1292
|
-
const current = installed[fp];
|
|
1293
|
-
const latest = checksums[fp];
|
|
1294
|
-
console.log(` ${fp} v${current.version} \u2192 v${latest.version} \u26A0\uFE0F UPDATE`);
|
|
1295
|
-
}
|
|
1296
|
-
if (updates.length === 0 && upToDate.length > 0) {
|
|
1297
|
-
console.log(` \u2713 All ${upToDate.length} components up to date.`);
|
|
1298
|
-
} else if (upToDate.length > 0) {
|
|
1299
|
-
console.log(` \u2713 ${upToDate.length} components up to date.`);
|
|
1300
|
-
}
|
|
1301
|
-
console.log();
|
|
1302
|
-
if (updates.length > 0) {
|
|
1303
|
-
console.log(`Status: ${updates.length} component(s) update available!`);
|
|
1304
|
-
console.log(`Run 'jai1 update' to update.`);
|
|
1305
|
-
} else {
|
|
1306
|
-
console.log(`Status: \u2705 All good!`);
|
|
1307
|
-
}
|
|
1308
|
-
} catch (error) {
|
|
1309
|
-
console.error("Error checking updates:", error instanceof Error ? error.message : error);
|
|
1080
|
+
extractAlwaysApply(frontmatter) {
|
|
1081
|
+
return frontmatter.alwaysApply === true || frontmatter.trigger === "always" || frontmatter.trigger === "always_on";
|
|
1310
1082
|
}
|
|
1311
|
-
}
|
|
1312
|
-
|
|
1313
|
-
// src/commands/ide/index.ts
|
|
1314
|
-
import { Command as Command10 } from "commander";
|
|
1315
|
-
|
|
1316
|
-
// src/commands/ide/context.ts
|
|
1317
|
-
import React10 from "react";
|
|
1318
|
-
import { render as render2 } from "ink";
|
|
1319
|
-
import { Command as Command6 } from "commander";
|
|
1083
|
+
};
|
|
1320
1084
|
|
|
1321
|
-
// src/ui/
|
|
1085
|
+
// src/ui/shared/ProgressBar.tsx
|
|
1086
|
+
import React from "react";
|
|
1087
|
+
import { Box, Text } from "ink";
|
|
1088
|
+
var ProgressBar = ({
|
|
1089
|
+
current,
|
|
1090
|
+
total,
|
|
1091
|
+
width = 40,
|
|
1092
|
+
showPercentage = true,
|
|
1093
|
+
color = "cyan"
|
|
1094
|
+
}) => {
|
|
1095
|
+
const percentage = total > 0 ? Math.round(current / total * 100) : 0;
|
|
1096
|
+
const filled = Math.round(percentage / 100 * width);
|
|
1097
|
+
const empty = width - filled;
|
|
1098
|
+
const bar = "\u2588".repeat(filled) + "\u2591".repeat(empty);
|
|
1099
|
+
return /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color }, bar), showPercentage && /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " ", current, "/", total, " (", percentage, "%)"));
|
|
1100
|
+
};
|
|
1101
|
+
|
|
1102
|
+
// src/ui/shared/StatusIcon.tsx
|
|
1103
|
+
import React2 from "react";
|
|
1104
|
+
import { Text as Text2 } from "ink";
|
|
1105
|
+
|
|
1106
|
+
// src/ui/shared/theme.ts
|
|
1107
|
+
var theme = {
|
|
1108
|
+
colors: {
|
|
1109
|
+
primary: "#6366f1",
|
|
1110
|
+
// Indigo
|
|
1111
|
+
success: "#22c55e",
|
|
1112
|
+
// Green
|
|
1113
|
+
warning: "#eab308",
|
|
1114
|
+
// Yellow
|
|
1115
|
+
error: "#ef4444",
|
|
1116
|
+
// Red
|
|
1117
|
+
muted: "#6b7280",
|
|
1118
|
+
// Gray
|
|
1119
|
+
border: "#374151",
|
|
1120
|
+
// Dark gray
|
|
1121
|
+
text: "#f3f4f6",
|
|
1122
|
+
// Light gray
|
|
1123
|
+
background: "#1a1a2e"
|
|
1124
|
+
// Dark background
|
|
1125
|
+
},
|
|
1126
|
+
icons: {
|
|
1127
|
+
success: "\u2705",
|
|
1128
|
+
error: "\u274C",
|
|
1129
|
+
pending: "\u25CB",
|
|
1130
|
+
loading: "\u23F3",
|
|
1131
|
+
package: "\u{1F4E6}",
|
|
1132
|
+
folder: "\u{1F4C1}",
|
|
1133
|
+
file: "\u{1F4C4}",
|
|
1134
|
+
tip: "\u{1F4A1}",
|
|
1135
|
+
warning: "\u26A0\uFE0F",
|
|
1136
|
+
checkmark: "\u2713",
|
|
1137
|
+
arrow: "\u2192",
|
|
1138
|
+
bullet: "\u2022"
|
|
1139
|
+
},
|
|
1140
|
+
borders: {
|
|
1141
|
+
single: {
|
|
1142
|
+
topLeft: "\u250C",
|
|
1143
|
+
topRight: "\u2510",
|
|
1144
|
+
bottomLeft: "\u2514",
|
|
1145
|
+
bottomRight: "\u2518",
|
|
1146
|
+
horizontal: "\u2500",
|
|
1147
|
+
vertical: "\u2502"
|
|
1148
|
+
},
|
|
1149
|
+
double: {
|
|
1150
|
+
topLeft: "\u2554",
|
|
1151
|
+
topRight: "\u2557",
|
|
1152
|
+
bottomLeft: "\u255A",
|
|
1153
|
+
bottomRight: "\u255D",
|
|
1154
|
+
horizontal: "\u2550",
|
|
1155
|
+
vertical: "\u2551"
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
};
|
|
1159
|
+
|
|
1160
|
+
// src/ui/shared/StatusIcon.tsx
|
|
1161
|
+
var StatusIcon = ({ status, text }) => {
|
|
1162
|
+
const iconMap = {
|
|
1163
|
+
success: theme.icons.success,
|
|
1164
|
+
error: theme.icons.error,
|
|
1165
|
+
pending: theme.icons.pending,
|
|
1166
|
+
loading: theme.icons.loading,
|
|
1167
|
+
warning: theme.icons.warning
|
|
1168
|
+
};
|
|
1169
|
+
const colorMap = {
|
|
1170
|
+
success: "green",
|
|
1171
|
+
error: "red",
|
|
1172
|
+
pending: "gray",
|
|
1173
|
+
loading: "yellow",
|
|
1174
|
+
warning: "yellow"
|
|
1175
|
+
};
|
|
1176
|
+
const icon = iconMap[status];
|
|
1177
|
+
const color = colorMap[status];
|
|
1178
|
+
return /* @__PURE__ */ React2.createElement(Text2, { color }, icon, " ", text);
|
|
1179
|
+
};
|
|
1180
|
+
|
|
1181
|
+
// src/ui/apply/UnifiedApplyApp.tsx
|
|
1182
|
+
var UnifiedApplyApp = ({
|
|
1183
|
+
config,
|
|
1184
|
+
force = false,
|
|
1185
|
+
onExit
|
|
1186
|
+
}) => {
|
|
1187
|
+
const { exit } = useApp();
|
|
1188
|
+
const [viewState, setViewState] = useState("browse");
|
|
1189
|
+
const [components, setComponents] = useState([]);
|
|
1190
|
+
const [tags, setTags] = useState([]);
|
|
1191
|
+
const [installedPaths, setInstalledPaths] = useState(/* @__PURE__ */ new Set());
|
|
1192
|
+
const [loading, setLoading] = useState(true);
|
|
1193
|
+
const [error, setError] = useState(null);
|
|
1194
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
1195
|
+
const [selectedPaths, setSelectedPaths] = useState(/* @__PURE__ */ new Set());
|
|
1196
|
+
const [cursorIndex, setCursorIndex] = useState(0);
|
|
1197
|
+
const [focusArea, setFocusArea] = useState("packages");
|
|
1198
|
+
const [selectedPackageIndex, setSelectedPackageIndex] = useState(0);
|
|
1199
|
+
const [installProgress, setInstallProgress] = useState([]);
|
|
1200
|
+
const [installStats, setInstallStats] = useState({ total: 0, completed: 0, added: 0, updated: 0, failed: 0 });
|
|
1201
|
+
const [availableIdes, setAvailableIdes] = useState([]);
|
|
1202
|
+
const [selectedIdes, setSelectedIdes] = useState(/* @__PURE__ */ new Set());
|
|
1203
|
+
const [ideCursorIndex, setIdeCursorIndex] = useState(0);
|
|
1204
|
+
const [syncProgress, setSyncProgress] = useState([]);
|
|
1205
|
+
const [syncStats, setSyncStats] = useState({ total: 0, completed: 0, created: 0, updated: 0, skipped: 0, errors: 0 });
|
|
1206
|
+
const service = new ComponentsService();
|
|
1207
|
+
useEffect(() => {
|
|
1208
|
+
const loadData = async () => {
|
|
1209
|
+
try {
|
|
1210
|
+
const [comps, tagList, installed] = await Promise.all([
|
|
1211
|
+
service.list(config),
|
|
1212
|
+
service.listTags(config),
|
|
1213
|
+
service.getInstalled()
|
|
1214
|
+
]);
|
|
1215
|
+
setComponents(comps);
|
|
1216
|
+
setTags(tagList);
|
|
1217
|
+
setInstalledPaths(new Set(Object.keys(installed)));
|
|
1218
|
+
setLoading(false);
|
|
1219
|
+
} catch (err) {
|
|
1220
|
+
setError(err instanceof Error ? err.message : "Failed to load");
|
|
1221
|
+
setLoading(false);
|
|
1222
|
+
}
|
|
1223
|
+
};
|
|
1224
|
+
loadData();
|
|
1225
|
+
setAvailableIdes(getMigrationIDEs());
|
|
1226
|
+
}, []);
|
|
1227
|
+
const filteredComponents = useMemo(() => {
|
|
1228
|
+
if (!searchQuery.trim()) return components;
|
|
1229
|
+
const query = searchQuery.toLowerCase();
|
|
1230
|
+
return components.filter(
|
|
1231
|
+
(c) => c.filepath.toLowerCase().includes(query) || c.tags?.some((t) => t.toLowerCase().includes(query))
|
|
1232
|
+
);
|
|
1233
|
+
}, [components, searchQuery]);
|
|
1234
|
+
useInput((input4, key) => {
|
|
1235
|
+
if (viewState === "done") {
|
|
1236
|
+
if (key.return || input4 === "q" || key.escape) {
|
|
1237
|
+
onExit();
|
|
1238
|
+
}
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
1241
|
+
if (viewState === "summary") {
|
|
1242
|
+
if (key.return) {
|
|
1243
|
+
setViewState("ide-sync");
|
|
1244
|
+
} else if (input4 === "s" || input4 === "q" || key.escape) {
|
|
1245
|
+
onExit();
|
|
1246
|
+
}
|
|
1247
|
+
return;
|
|
1248
|
+
}
|
|
1249
|
+
if (viewState === "ide-sync") {
|
|
1250
|
+
if (key.upArrow) {
|
|
1251
|
+
setIdeCursorIndex((prev) => Math.max(0, prev - 1));
|
|
1252
|
+
} else if (key.downArrow) {
|
|
1253
|
+
setIdeCursorIndex((prev) => Math.min(availableIdes.length - 1, prev + 1));
|
|
1254
|
+
} else if (input4 === " ") {
|
|
1255
|
+
const ide = availableIdes[ideCursorIndex];
|
|
1256
|
+
if (ide) {
|
|
1257
|
+
setSelectedIdes((prev) => {
|
|
1258
|
+
const next = new Set(prev);
|
|
1259
|
+
if (next.has(ide)) {
|
|
1260
|
+
next.delete(ide);
|
|
1261
|
+
} else {
|
|
1262
|
+
next.add(ide);
|
|
1263
|
+
}
|
|
1264
|
+
return next;
|
|
1265
|
+
});
|
|
1266
|
+
}
|
|
1267
|
+
} else if (input4 === "a") {
|
|
1268
|
+
setSelectedIdes(new Set(availableIdes));
|
|
1269
|
+
} else if (input4 === "c") {
|
|
1270
|
+
setSelectedIdes(/* @__PURE__ */ new Set());
|
|
1271
|
+
} else if (key.return) {
|
|
1272
|
+
if (selectedIdes.size > 0) {
|
|
1273
|
+
handleIdeSync();
|
|
1274
|
+
}
|
|
1275
|
+
} else if (input4 === "s" || input4 === "q" || key.escape) {
|
|
1276
|
+
onExit();
|
|
1277
|
+
}
|
|
1278
|
+
return;
|
|
1279
|
+
}
|
|
1280
|
+
if (viewState === "syncing") {
|
|
1281
|
+
return;
|
|
1282
|
+
}
|
|
1283
|
+
if (viewState === "installing") {
|
|
1284
|
+
return;
|
|
1285
|
+
}
|
|
1286
|
+
if (key.tab) {
|
|
1287
|
+
if (focusArea === "packages") setFocusArea("components");
|
|
1288
|
+
else if (focusArea === "components") setFocusArea("search");
|
|
1289
|
+
else setFocusArea("packages");
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
if (key.escape || input4 === "q") {
|
|
1293
|
+
onExit();
|
|
1294
|
+
return;
|
|
1295
|
+
}
|
|
1296
|
+
if (key.return && focusArea !== "search") {
|
|
1297
|
+
if (selectedPaths.size > 0) {
|
|
1298
|
+
handleApply();
|
|
1299
|
+
}
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
if (focusArea !== "search") {
|
|
1303
|
+
if (input4 === "a") {
|
|
1304
|
+
setSelectedPaths((prev) => {
|
|
1305
|
+
const next = new Set(prev);
|
|
1306
|
+
filteredComponents.forEach((c) => next.add(c.filepath));
|
|
1307
|
+
return next;
|
|
1308
|
+
});
|
|
1309
|
+
return;
|
|
1310
|
+
} else if (input4 === "c") {
|
|
1311
|
+
setSelectedPaths(/* @__PURE__ */ new Set());
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
if (focusArea === "components") {
|
|
1316
|
+
if (key.upArrow) {
|
|
1317
|
+
setCursorIndex((prev) => Math.max(0, prev - 1));
|
|
1318
|
+
} else if (key.downArrow) {
|
|
1319
|
+
setCursorIndex((prev) => Math.min(filteredComponents.length - 1, prev + 1));
|
|
1320
|
+
} else if (input4 === " ") {
|
|
1321
|
+
const current = filteredComponents[cursorIndex];
|
|
1322
|
+
if (current) {
|
|
1323
|
+
setSelectedPaths((prev) => {
|
|
1324
|
+
const next = new Set(prev);
|
|
1325
|
+
if (next.has(current.filepath)) {
|
|
1326
|
+
next.delete(current.filepath);
|
|
1327
|
+
} else {
|
|
1328
|
+
next.add(current.filepath);
|
|
1329
|
+
}
|
|
1330
|
+
return next;
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
if (focusArea === "packages") {
|
|
1336
|
+
if (key.leftArrow) {
|
|
1337
|
+
setSelectedPackageIndex((prev) => Math.max(0, prev - 1));
|
|
1338
|
+
} else if (key.rightArrow) {
|
|
1339
|
+
setSelectedPackageIndex((prev) => Math.min(tags.length - 1, prev + 1));
|
|
1340
|
+
} else if (input4 === " " || key.return) {
|
|
1341
|
+
const tag = tags[selectedPackageIndex];
|
|
1342
|
+
if (tag) {
|
|
1343
|
+
const packageComponents = components.filter((c) => c.tags?.includes(tag.tag));
|
|
1344
|
+
setSelectedPaths((prev) => {
|
|
1345
|
+
const next = new Set(prev);
|
|
1346
|
+
packageComponents.forEach((c) => next.add(c.filepath));
|
|
1347
|
+
return next;
|
|
1348
|
+
});
|
|
1349
|
+
setSearchQuery(tag.tag);
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
});
|
|
1354
|
+
const handleApply = async () => {
|
|
1355
|
+
setViewState("installing");
|
|
1356
|
+
const targetDir = process.cwd() + "/.jai1";
|
|
1357
|
+
const pathsToInstall = Array.from(selectedPaths);
|
|
1358
|
+
const resolvedPaths = await service.resolveWithDependencies(config, pathsToInstall);
|
|
1359
|
+
const installed = await service.getInstalled();
|
|
1360
|
+
const initialProgress = resolvedPaths.map((fp) => ({
|
|
1361
|
+
filepath: fp,
|
|
1362
|
+
status: "pending"
|
|
1363
|
+
}));
|
|
1364
|
+
setInstallProgress(initialProgress);
|
|
1365
|
+
setInstallStats({ total: resolvedPaths.length, completed: 0, added: 0, updated: 0, failed: 0 });
|
|
1366
|
+
let added = 0, updated = 0, failed = 0;
|
|
1367
|
+
for (let i = 0; i < resolvedPaths.length; i++) {
|
|
1368
|
+
const fp = resolvedPaths[i];
|
|
1369
|
+
setInstallProgress((prev) => prev.map(
|
|
1370
|
+
(p) => p.filepath === fp ? { ...p, status: "downloading" } : p
|
|
1371
|
+
));
|
|
1372
|
+
try {
|
|
1373
|
+
const info = installed[fp];
|
|
1374
|
+
if (info && !force && info.modified) {
|
|
1375
|
+
setInstallProgress((prev) => prev.map(
|
|
1376
|
+
(p) => p.filepath === fp ? { ...p, status: "error", error: "Modified locally" } : p
|
|
1377
|
+
));
|
|
1378
|
+
failed++;
|
|
1379
|
+
} else {
|
|
1380
|
+
await service.install(config, fp, targetDir);
|
|
1381
|
+
setInstallProgress((prev) => prev.map(
|
|
1382
|
+
(p) => p.filepath === fp ? { ...p, status: "success" } : p
|
|
1383
|
+
));
|
|
1384
|
+
if (info) updated++;
|
|
1385
|
+
else added++;
|
|
1386
|
+
}
|
|
1387
|
+
} catch (err) {
|
|
1388
|
+
setInstallProgress((prev) => prev.map(
|
|
1389
|
+
(p) => p.filepath === fp ? { ...p, status: "error", error: err instanceof Error ? err.message : "Error" } : p
|
|
1390
|
+
));
|
|
1391
|
+
failed++;
|
|
1392
|
+
}
|
|
1393
|
+
setInstallStats({ total: resolvedPaths.length, completed: i + 1, added, updated, failed });
|
|
1394
|
+
}
|
|
1395
|
+
setViewState("summary");
|
|
1396
|
+
};
|
|
1397
|
+
const handleIdeSync = async () => {
|
|
1398
|
+
setViewState("syncing");
|
|
1399
|
+
const migrateService = new MigrateIdeService();
|
|
1400
|
+
const content = await migrateService.scanJai1Content();
|
|
1401
|
+
if (content.totalCount === 0) {
|
|
1402
|
+
setSyncStats({ total: 0, completed: 0, created: 0, updated: 0, skipped: 0, errors: 0 });
|
|
1403
|
+
setViewState("done");
|
|
1404
|
+
return;
|
|
1405
|
+
}
|
|
1406
|
+
const selectedIdeList = Array.from(selectedIdes);
|
|
1407
|
+
const contentTypes = ["rules", "workflows"];
|
|
1408
|
+
const totalItems = contentTypes.reduce((sum, type) => {
|
|
1409
|
+
return sum + (type === "rules" ? content.rules.length : type === "workflows" ? content.workflows.length : content.commands.length);
|
|
1410
|
+
}, 0);
|
|
1411
|
+
const totalFiles = totalItems * selectedIdeList.length;
|
|
1412
|
+
setSyncStats({ total: totalFiles, completed: 0, created: 0, updated: 0, skipped: 0, errors: 0 });
|
|
1413
|
+
let created = 0, updated = 0, skipped = 0, errors = 0;
|
|
1414
|
+
let completed = 0;
|
|
1415
|
+
const results = await migrateService.migrate(
|
|
1416
|
+
selectedIdeList,
|
|
1417
|
+
contentTypes,
|
|
1418
|
+
content,
|
|
1419
|
+
(result) => {
|
|
1420
|
+
completed++;
|
|
1421
|
+
if (result.status === "created") created++;
|
|
1422
|
+
else if (result.status === "updated") updated++;
|
|
1423
|
+
else if (result.status === "skipped") skipped++;
|
|
1424
|
+
else if (result.status === "error") errors++;
|
|
1425
|
+
setSyncProgress((prev) => [...prev.slice(-7), {
|
|
1426
|
+
targetPath: result.targetPath || result.source.relativePath,
|
|
1427
|
+
status: result.status === "error" ? "error" : "success",
|
|
1428
|
+
error: result.error
|
|
1429
|
+
}]);
|
|
1430
|
+
setSyncStats({ total: totalFiles, completed, created, updated, skipped, errors });
|
|
1431
|
+
}
|
|
1432
|
+
);
|
|
1433
|
+
setViewState("done");
|
|
1434
|
+
};
|
|
1435
|
+
if (loading) {
|
|
1436
|
+
return /* @__PURE__ */ React3.createElement(Box2, { padding: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: "cyan" }, /* @__PURE__ */ React3.createElement(Spinner, { type: "dots" }), " \u0110ang t\u1EA3i components..."));
|
|
1437
|
+
}
|
|
1438
|
+
if (error) {
|
|
1439
|
+
return /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: "red" }, "\u274C Error: ", error), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "Press q to exit"));
|
|
1440
|
+
}
|
|
1441
|
+
if (viewState === "installing") {
|
|
1442
|
+
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"))));
|
|
1443
|
+
}
|
|
1444
|
+
if (viewState === "summary") {
|
|
1445
|
+
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")));
|
|
1446
|
+
}
|
|
1447
|
+
if (viewState === "ide-sync") {
|
|
1448
|
+
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) => {
|
|
1449
|
+
const ideConfig = IDE_MIGRATION_CONFIGS[ide];
|
|
1450
|
+
const isCursor = i === ideCursorIndex;
|
|
1451
|
+
const isChecked = selectedIdes.has(ide);
|
|
1452
|
+
return /* @__PURE__ */ React3.createElement(Box2, { key: ide }, /* @__PURE__ */ React3.createElement(Text3, { color: isCursor ? "cyan" : "white" }, isCursor ? "\u276F " : " ", isChecked ? "[\u2713]" : "[ ]", " ", ideConfig.icon, " ", ideConfig.name));
|
|
1453
|
+
})), 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")));
|
|
1454
|
+
}
|
|
1455
|
+
if (viewState === "syncing") {
|
|
1456
|
+
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")))));
|
|
1457
|
+
}
|
|
1458
|
+
if (viewState === "done") {
|
|
1459
|
+
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")));
|
|
1460
|
+
}
|
|
1461
|
+
const visibleComponents = filteredComponents.slice(0, 12);
|
|
1462
|
+
const hasMore = filteredComponents.length > 12;
|
|
1463
|
+
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} Jai1 Apply"), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, " - Select components to install")), /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: focusArea === "search" ? "cyan" : "gray" }, "\u{1F50D} Search: "), focusArea === "search" ? /* @__PURE__ */ React3.createElement(
|
|
1464
|
+
TextInput,
|
|
1465
|
+
{
|
|
1466
|
+
value: searchQuery,
|
|
1467
|
+
onChange: setSearchQuery,
|
|
1468
|
+
placeholder: "Type to filter..."
|
|
1469
|
+
}
|
|
1470
|
+
) : /* @__PURE__ */ React3.createElement(Text3, null, searchQuery || /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "Press Tab to search"))), /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Text3, { bold: true, dimColor: true }, "Quick Apply (Packages):"), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, flexWrap: "wrap" }, tags.slice(0, 6).map((tag, i) => {
|
|
1471
|
+
const isSelected = focusArea === "packages" && i === selectedPackageIndex;
|
|
1472
|
+
return /* @__PURE__ */ React3.createElement(Box2, { key: tag.tag, marginRight: 1 }, /* @__PURE__ */ React3.createElement(
|
|
1473
|
+
Text3,
|
|
1474
|
+
{
|
|
1475
|
+
backgroundColor: isSelected ? "cyan" : void 0,
|
|
1476
|
+
color: isSelected ? "black" : "gray"
|
|
1477
|
+
},
|
|
1478
|
+
"[",
|
|
1479
|
+
tag.tag,
|
|
1480
|
+
":",
|
|
1481
|
+
tag.count,
|
|
1482
|
+
"]"
|
|
1483
|
+
));
|
|
1484
|
+
}))), /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", borderStyle: "round", borderColor: focusArea === "components" ? "cyan" : "gray", padding: 1 }, /* @__PURE__ */ React3.createElement(Box2, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Text3, { bold: true }, "Components "), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "(", filteredComponents.length, " shown", hasMore ? `, scroll for more` : "", ")")), visibleComponents.map((comp, i) => {
|
|
1485
|
+
const isCursor = i === cursorIndex && focusArea === "components";
|
|
1486
|
+
const isChecked = selectedPaths.has(comp.filepath);
|
|
1487
|
+
const isInstalled = installedPaths.has(comp.filepath);
|
|
1488
|
+
return /* @__PURE__ */ React3.createElement(Box2, { key: comp.filepath }, /* @__PURE__ */ React3.createElement(Text3, { color: isCursor ? "cyan" : "white" }, isCursor ? "\u276F " : " ", isChecked ? "[\u2713]" : "[ ]", " ", comp.filepath), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, " ", isInstalled ? "\u2713 installed" : "\u25CB new"));
|
|
1489
|
+
}), filteredComponents.length === 0 && /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "No components match your search")), selectedPaths.size > 0 && /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Text3, { bold: true }, "Selected: ", selectedPaths.size, " components"), /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column", marginLeft: 2 }, Array.from(selectedPaths).slice(0, 4).map((fp) => /* @__PURE__ */ React3.createElement(Text3, { key: fp, dimColor: true }, "\u{1F4CC} ", fp)), selectedPaths.size > 4 && /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, " ... and ", selectedPaths.size - 4, " more"))), /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "[Tab] Switch area \xB7 [\u2191\u2193/\u2190\u2192] Navigate \xB7 [\u2423] Toggle \xB7 [A] Select all \xB7 [C] Clear \xB7 [Enter] Apply \xB7 [Q] Quit")));
|
|
1490
|
+
};
|
|
1491
|
+
|
|
1492
|
+
// src/commands/apply.ts
|
|
1493
|
+
function createApplyCommand() {
|
|
1494
|
+
const cmd = new Command3("apply").description("Apply components to your project (interactive UI)").argument("[items...]", "Package name or component filepaths (non-interactive mode)").option("--force", "Force overwrite even if modified locally").option("--no-interactive", "Disable interactive UI (for CI/CD)").action(async (items, options) => {
|
|
1495
|
+
const configService = new ConfigService();
|
|
1496
|
+
const config = await configService.load();
|
|
1497
|
+
if (!config) {
|
|
1498
|
+
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
1499
|
+
}
|
|
1500
|
+
if (options.interactive === false || items.length > 0) {
|
|
1501
|
+
await nonInteractiveApply(config, items, options);
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
const { waitUntilExit } = render(
|
|
1505
|
+
React4.createElement(UnifiedApplyApp, {
|
|
1506
|
+
config,
|
|
1507
|
+
force: options.force,
|
|
1508
|
+
onExit: () => {
|
|
1509
|
+
process.exit(0);
|
|
1510
|
+
}
|
|
1511
|
+
})
|
|
1512
|
+
);
|
|
1513
|
+
await waitUntilExit();
|
|
1514
|
+
});
|
|
1515
|
+
return cmd;
|
|
1516
|
+
}
|
|
1517
|
+
async function nonInteractiveApply(config, items, options) {
|
|
1518
|
+
const componentsService = new ComponentsService();
|
|
1519
|
+
if (items.length === 0) {
|
|
1520
|
+
console.log("\u274C No items specified. Use interactive mode or provide package/component names.");
|
|
1521
|
+
console.log("\u{1F4A1} Usage: jai1 apply <package> or jai1 apply <filepath...>");
|
|
1522
|
+
return;
|
|
1523
|
+
}
|
|
1524
|
+
const tags = await componentsService.listTags(config);
|
|
1525
|
+
const packageNames = tags.map((t) => t.tag);
|
|
1526
|
+
let filepaths;
|
|
1527
|
+
if (items.length === 1 && packageNames.includes(items[0])) {
|
|
1528
|
+
console.log(`\u{1F4E6} Applying package '${items[0]}'...`);
|
|
1529
|
+
const components = await componentsService.list(config, { tag: items[0] });
|
|
1530
|
+
filepaths = components.map((c) => c.filepath);
|
|
1531
|
+
} else {
|
|
1532
|
+
filepaths = items;
|
|
1533
|
+
}
|
|
1534
|
+
console.log("\u{1F50D} Resolving dependencies...");
|
|
1535
|
+
const resolvedPaths = await componentsService.resolveWithDependencies(config, filepaths);
|
|
1536
|
+
console.log(`\u{1F4E6} Will apply ${resolvedPaths.length} component(s):`);
|
|
1537
|
+
for (const fp of resolvedPaths) {
|
|
1538
|
+
const isDep = !filepaths.includes(fp);
|
|
1539
|
+
const suffix = isDep ? " (dependency)" : "";
|
|
1540
|
+
console.log(` - ${fp}${suffix}`);
|
|
1541
|
+
}
|
|
1542
|
+
const targetDir = process.cwd() + "/.jai1";
|
|
1543
|
+
const installed = await componentsService.getInstalled();
|
|
1544
|
+
let added = 0;
|
|
1545
|
+
let updated = 0;
|
|
1546
|
+
let skipped = 0;
|
|
1547
|
+
for (const fp of resolvedPaths) {
|
|
1548
|
+
try {
|
|
1549
|
+
const info = installed[fp];
|
|
1550
|
+
if (info && !options.force && info.modified) {
|
|
1551
|
+
console.log(`\u26A0\uFE0F ${fp}: modified locally. Use --force to overwrite.`);
|
|
1552
|
+
skipped++;
|
|
1553
|
+
continue;
|
|
1554
|
+
}
|
|
1555
|
+
console.log(`\u{1F4E5} Downloading ${fp}...`);
|
|
1556
|
+
await componentsService.install(config, fp, targetDir);
|
|
1557
|
+
if (info) updated++;
|
|
1558
|
+
else added++;
|
|
1559
|
+
console.log(`\u2705 ${fp}`);
|
|
1560
|
+
} catch (error) {
|
|
1561
|
+
console.log(`\u274C ${fp}: ${error instanceof Error ? error.message : "Error"}`);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
console.log(`
|
|
1565
|
+
\u2705 Complete: ${added} added, ${updated} updated, ${skipped} skipped`);
|
|
1566
|
+
console.log(`\u{1F4C1} Location: ${targetDir}`);
|
|
1567
|
+
console.log(`
|
|
1568
|
+
\u{1F4A1} Next step: Run "jai1 ide sync" to sync content to your IDE(s)`);
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
// src/commands/update.ts
|
|
1572
|
+
import { Command as Command4 } from "commander";
|
|
1573
|
+
import { confirm } from "@inquirer/prompts";
|
|
1574
|
+
function createUpdateCommand() {
|
|
1575
|
+
return new Command4("update").description("Update installed components to latest versions").option("--force", "Force update even if files are modified locally").action(async (options) => {
|
|
1576
|
+
await handleUpdate(options);
|
|
1577
|
+
});
|
|
1578
|
+
}
|
|
1579
|
+
async function handleUpdate(options) {
|
|
1580
|
+
const configService = new ConfigService();
|
|
1581
|
+
const config = await configService.load();
|
|
1582
|
+
if (!config) {
|
|
1583
|
+
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
1584
|
+
}
|
|
1585
|
+
const componentsService = new ComponentsService();
|
|
1586
|
+
const installed = await componentsService.getInstalled();
|
|
1587
|
+
const filepaths = Object.keys(installed);
|
|
1588
|
+
if (filepaths.length === 0) {
|
|
1589
|
+
console.log('No components installed. Run "jai1 apply" to add components.');
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
console.log("\u{1F50D} Checking for updates...");
|
|
1593
|
+
try {
|
|
1594
|
+
const checksums = await componentsService.getChecksums(config, filepaths);
|
|
1595
|
+
const updates = [];
|
|
1596
|
+
const upToDate = [];
|
|
1597
|
+
for (const fp of filepaths) {
|
|
1598
|
+
const current = installed[fp];
|
|
1599
|
+
const latest = checksums[fp];
|
|
1600
|
+
if (!latest) {
|
|
1601
|
+
console.log(`\u26A0\uFE0F ${fp}: Component not found on server (deprecated?)`);
|
|
1602
|
+
continue;
|
|
1603
|
+
}
|
|
1604
|
+
if (current.checksum !== latest.checksum) {
|
|
1605
|
+
updates.push(fp);
|
|
1606
|
+
} else {
|
|
1607
|
+
upToDate.push(fp);
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
if (updates.length === 0) {
|
|
1611
|
+
console.log("\u2705 All components are up to date.");
|
|
1612
|
+
return;
|
|
1613
|
+
}
|
|
1614
|
+
console.log(`
|
|
1615
|
+
\u{1F4E6} Found ${updates.length} updates:`);
|
|
1616
|
+
for (const fp of updates) {
|
|
1617
|
+
const current = installed[fp];
|
|
1618
|
+
const latest = checksums[fp];
|
|
1619
|
+
console.log(` - ${fp} (v${current.version} \u2192 v${latest.version})`);
|
|
1620
|
+
}
|
|
1621
|
+
console.log();
|
|
1622
|
+
if (!options.force) {
|
|
1623
|
+
const shouldUpdate = await confirm({ message: "Update now?", default: true });
|
|
1624
|
+
if (!shouldUpdate) return;
|
|
1625
|
+
}
|
|
1626
|
+
const targetDir = process.cwd() + "/.jai1";
|
|
1627
|
+
let updatedCount = 0;
|
|
1628
|
+
const backupPaths = [];
|
|
1629
|
+
for (const fp of updates) {
|
|
1630
|
+
try {
|
|
1631
|
+
console.log(`\u{1F4E5} Updating ${fp}...`);
|
|
1632
|
+
const backupPath = await componentsService.backupFile(fp, targetDir);
|
|
1633
|
+
if (backupPath) {
|
|
1634
|
+
backupPaths.push(backupPath);
|
|
1635
|
+
const displayPath = backupPath.split(".jai1_backup/")[1];
|
|
1636
|
+
console.log(` Backed up: .../${displayPath}`);
|
|
1637
|
+
}
|
|
1638
|
+
await componentsService.install(config, fp, targetDir);
|
|
1639
|
+
console.log(`\u2705 Updated`);
|
|
1640
|
+
updatedCount++;
|
|
1641
|
+
} catch (error) {
|
|
1642
|
+
console.log(`\u274C Failed to update ${fp}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
console.log(`
|
|
1646
|
+
\u2705 Update complete: ${updatedCount}/${updates.length} updated.`);
|
|
1647
|
+
if (backupPaths.length > 0) {
|
|
1648
|
+
console.log(`
|
|
1649
|
+
\u{1F4C1} Backups created in .jai1_backup/`);
|
|
1650
|
+
const cleanBackups = await confirm({ message: "Clear these backups?", default: false });
|
|
1651
|
+
if (cleanBackups) {
|
|
1652
|
+
await componentsService.clearBackups(process.cwd());
|
|
1653
|
+
console.log("\u{1F5D1}\uFE0F Backups cleared.");
|
|
1654
|
+
} else {
|
|
1655
|
+
console.log('\u{1F4A1} Run "jai1 clean" to remove them later.');
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
trackAction("components_update", { count: updatedCount });
|
|
1659
|
+
} catch (error) {
|
|
1660
|
+
throw new Error(`Update check failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
// src/commands/framework/check.ts
|
|
1665
|
+
import { Command as Command5 } from "commander";
|
|
1666
|
+
function createCheckCommand() {
|
|
1667
|
+
const cmd = new Command5("check").description("Check for framework updates").option("--json", "Output as JSON").action(async (options) => {
|
|
1668
|
+
await handleCheck(options);
|
|
1669
|
+
});
|
|
1670
|
+
return cmd;
|
|
1671
|
+
}
|
|
1672
|
+
async function handleCheck(options) {
|
|
1673
|
+
const configService = new ConfigService();
|
|
1674
|
+
const config = await configService.load();
|
|
1675
|
+
if (!config) {
|
|
1676
|
+
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
1677
|
+
}
|
|
1678
|
+
const componentsService = new ComponentsService();
|
|
1679
|
+
const installed = await componentsService.getInstalled();
|
|
1680
|
+
const filepaths = Object.keys(installed);
|
|
1681
|
+
if (filepaths.length === 0) {
|
|
1682
|
+
console.log('No components installed. Run "jai1 apply" to get started.');
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
console.log("Checking for updates...\n");
|
|
1686
|
+
try {
|
|
1687
|
+
const checksums = await componentsService.getChecksums(config, filepaths);
|
|
1688
|
+
const updates = [];
|
|
1689
|
+
const upToDate = [];
|
|
1690
|
+
for (const fp of filepaths) {
|
|
1691
|
+
const current = installed[fp];
|
|
1692
|
+
const latest = checksums[fp];
|
|
1693
|
+
if (!latest) {
|
|
1694
|
+
continue;
|
|
1695
|
+
}
|
|
1696
|
+
if (current.checksum !== latest.checksum) {
|
|
1697
|
+
updates.push(fp);
|
|
1698
|
+
} else {
|
|
1699
|
+
upToDate.push(fp);
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
console.log(`\u{1F4CC} Components status:`);
|
|
1703
|
+
for (const fp of updates) {
|
|
1704
|
+
const current = installed[fp];
|
|
1705
|
+
const latest = checksums[fp];
|
|
1706
|
+
console.log(` ${fp} v${current.version} \u2192 v${latest.version} \u26A0\uFE0F UPDATE`);
|
|
1707
|
+
}
|
|
1708
|
+
if (updates.length === 0 && upToDate.length > 0) {
|
|
1709
|
+
console.log(` \u2713 All ${upToDate.length} components up to date.`);
|
|
1710
|
+
} else if (upToDate.length > 0) {
|
|
1711
|
+
console.log(` \u2713 ${upToDate.length} components up to date.`);
|
|
1712
|
+
}
|
|
1713
|
+
console.log();
|
|
1714
|
+
if (updates.length > 0) {
|
|
1715
|
+
console.log(`Status: ${updates.length} component(s) update available!`);
|
|
1716
|
+
console.log(`Run 'jai1 update' to update.`);
|
|
1717
|
+
} else {
|
|
1718
|
+
console.log(`Status: \u2705 All good!`);
|
|
1719
|
+
}
|
|
1720
|
+
} catch (error) {
|
|
1721
|
+
console.error("Error checking updates:", error instanceof Error ? error.message : error);
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
// src/commands/ide/index.ts
|
|
1726
|
+
import { Command as Command10 } from "commander";
|
|
1727
|
+
|
|
1728
|
+
// src/commands/ide/context.ts
|
|
1729
|
+
import React10 from "react";
|
|
1730
|
+
import { render as render2 } from "ink";
|
|
1731
|
+
import { Command as Command6 } from "commander";
|
|
1732
|
+
|
|
1733
|
+
// src/ui/context/ContextApp.tsx
|
|
1322
1734
|
import React9, { useState as useState6, useEffect as useEffect2 } from "react";
|
|
1323
1735
|
import { Box as Box7, Text as Text8, useInput as useInput6, useApp as useApp2 } from "ink";
|
|
1324
1736
|
|
|
@@ -1623,9 +2035,9 @@ var DetailView = ({ item, scrollPosition: initialScroll, onBack }) => {
|
|
|
1623
2035
|
};
|
|
1624
2036
|
|
|
1625
2037
|
// src/services/context-scanner.service.ts
|
|
1626
|
-
import { promises as
|
|
1627
|
-
import
|
|
1628
|
-
import
|
|
2038
|
+
import { promises as fs5 } from "fs";
|
|
2039
|
+
import path2 from "path";
|
|
2040
|
+
import matter2 from "gray-matter";
|
|
1629
2041
|
|
|
1630
2042
|
// src/constants/ide-configs.ts
|
|
1631
2043
|
var IDE_CONFIGS = {
|
|
@@ -1788,8 +2200,8 @@ var ContextScannerService = class {
|
|
|
1788
2200
|
if (!relativePath) return [];
|
|
1789
2201
|
let dirPath;
|
|
1790
2202
|
if (ide === "jai1") {
|
|
1791
|
-
const jai1Path =
|
|
1792
|
-
const frameworkPath =
|
|
2203
|
+
const jai1Path = path2.join(this.projectPath, ".jai1", relativePath);
|
|
2204
|
+
const frameworkPath = path2.join(this.projectPath, "packages/jai1-framework", relativePath);
|
|
1793
2205
|
if (await this.pathExists(jai1Path)) {
|
|
1794
2206
|
dirPath = jai1Path;
|
|
1795
2207
|
} else if (await this.pathExists(frameworkPath)) {
|
|
@@ -1798,9 +2210,9 @@ var ContextScannerService = class {
|
|
|
1798
2210
|
return [];
|
|
1799
2211
|
}
|
|
1800
2212
|
} else {
|
|
1801
|
-
dirPath =
|
|
2213
|
+
dirPath = path2.join(this.projectPath, config.basePath, relativePath);
|
|
1802
2214
|
try {
|
|
1803
|
-
await
|
|
2215
|
+
await fs5.access(dirPath);
|
|
1804
2216
|
} catch {
|
|
1805
2217
|
return [];
|
|
1806
2218
|
}
|
|
@@ -1811,10 +2223,10 @@ var ContextScannerService = class {
|
|
|
1811
2223
|
const skillItems = await this.scanSkills(dirPath, ide);
|
|
1812
2224
|
items.push(...skillItems);
|
|
1813
2225
|
} else {
|
|
1814
|
-
const files = await
|
|
2226
|
+
const files = await fs5.readdir(dirPath);
|
|
1815
2227
|
for (const file of files) {
|
|
1816
|
-
const filepath =
|
|
1817
|
-
const stat = await
|
|
2228
|
+
const filepath = path2.join(dirPath, file);
|
|
2229
|
+
const stat = await fs5.stat(filepath);
|
|
1818
2230
|
if (!stat.isFile()) continue;
|
|
1819
2231
|
const matchesExtension = extensions.some((ext) => file.endsWith(ext));
|
|
1820
2232
|
if (!matchesExtension) continue;
|
|
@@ -1834,20 +2246,20 @@ var ContextScannerService = class {
|
|
|
1834
2246
|
async scanSkills(skillsDir, ide) {
|
|
1835
2247
|
const items = [];
|
|
1836
2248
|
try {
|
|
1837
|
-
const entries = await
|
|
2249
|
+
const entries = await fs5.readdir(skillsDir, { withFileTypes: true });
|
|
1838
2250
|
for (const entry of entries) {
|
|
1839
2251
|
if (!entry.isDirectory()) continue;
|
|
1840
|
-
const skillPath =
|
|
1841
|
-
const skillFilePath =
|
|
2252
|
+
const skillPath = path2.join(skillsDir, entry.name);
|
|
2253
|
+
const skillFilePath = path2.join(skillPath, "SKILL.md");
|
|
1842
2254
|
try {
|
|
1843
|
-
await
|
|
2255
|
+
await fs5.access(skillFilePath);
|
|
1844
2256
|
} catch {
|
|
1845
2257
|
continue;
|
|
1846
2258
|
}
|
|
1847
2259
|
const item = await this.parseContextItem(skillFilePath, ide, "skills");
|
|
1848
|
-
item.hasScripts = await this.pathExists(
|
|
1849
|
-
item.hasReferences = await this.pathExists(
|
|
1850
|
-
item.hasAssets = await this.pathExists(
|
|
2260
|
+
item.hasScripts = await this.pathExists(path2.join(skillPath, "scripts"));
|
|
2261
|
+
item.hasReferences = await this.pathExists(path2.join(skillPath, "references"));
|
|
2262
|
+
item.hasAssets = await this.pathExists(path2.join(skillPath, "assets"));
|
|
1851
2263
|
items.push(item);
|
|
1852
2264
|
}
|
|
1853
2265
|
} catch (error) {
|
|
@@ -1859,9 +2271,9 @@ var ContextScannerService = class {
|
|
|
1859
2271
|
* Parse a context item from file
|
|
1860
2272
|
*/
|
|
1861
2273
|
async parseContextItem(filepath, ide, type) {
|
|
1862
|
-
const content = await
|
|
1863
|
-
const stat = await
|
|
1864
|
-
const { data: frontmatter, content: bodyContent } =
|
|
2274
|
+
const content = await fs5.readFile(filepath, "utf-8");
|
|
2275
|
+
const stat = await fs5.stat(filepath);
|
|
2276
|
+
const { data: frontmatter, content: bodyContent } = matter2(content);
|
|
1865
2277
|
const config = IDE_CONFIGS[ide];
|
|
1866
2278
|
const description = frontmatter[config.frontmatterSchema.descriptionField];
|
|
1867
2279
|
let globs;
|
|
@@ -1877,9 +2289,9 @@ var ContextScannerService = class {
|
|
|
1877
2289
|
globs = triggerValue;
|
|
1878
2290
|
}
|
|
1879
2291
|
const { previewLines, lineCount } = this.extractPreview(bodyContent, 20);
|
|
1880
|
-
const relativePath =
|
|
1881
|
-
const id = `${ide}-${type}-${
|
|
1882
|
-
const name = frontmatter.name ||
|
|
2292
|
+
const relativePath = path2.relative(this.projectPath, filepath);
|
|
2293
|
+
const id = `${ide}-${type}-${path2.basename(filepath, path2.extname(filepath))}`;
|
|
2294
|
+
const name = frontmatter.name || path2.basename(filepath, path2.extname(filepath));
|
|
1883
2295
|
return {
|
|
1884
2296
|
id,
|
|
1885
2297
|
ide,
|
|
@@ -1907,882 +2319,570 @@ var ContextScannerService = class {
|
|
|
1907
2319
|
const lineCount = lines.length;
|
|
1908
2320
|
return { previewLines, lineCount };
|
|
1909
2321
|
}
|
|
1910
|
-
/**
|
|
1911
|
-
* Detect available IDEs in project
|
|
1912
|
-
*/
|
|
1913
|
-
async detectIDEs() {
|
|
1914
|
-
const ides = [];
|
|
1915
|
-
for (const ide of getAllIDEs()) {
|
|
1916
|
-
const config = IDE_CONFIGS[ide];
|
|
1917
|
-
if (ide === "jai1") {
|
|
1918
|
-
const jai1Path =
|
|
1919
|
-
const frameworkPath =
|
|
1920
|
-
if (await this.pathExists(jai1Path) || await this.pathExists(frameworkPath)) {
|
|
1921
|
-
ides.push(ide);
|
|
1922
|
-
}
|
|
1923
|
-
} else {
|
|
1924
|
-
const idePath =
|
|
1925
|
-
if (await this.pathExists(idePath)) {
|
|
1926
|
-
ides.push(ide);
|
|
1927
|
-
}
|
|
1928
|
-
}
|
|
1929
|
-
}
|
|
1930
|
-
return ides;
|
|
1931
|
-
}
|
|
1932
|
-
/**
|
|
1933
|
-
* Check if path exists
|
|
1934
|
-
*/
|
|
1935
|
-
async pathExists(filepath) {
|
|
1936
|
-
try {
|
|
1937
|
-
await fs4.access(filepath);
|
|
1938
|
-
return true;
|
|
1939
|
-
} catch {
|
|
1940
|
-
return false;
|
|
1941
|
-
}
|
|
1942
|
-
}
|
|
1943
|
-
/**
|
|
1944
|
-
* Calculate statistics from items
|
|
1945
|
-
*/
|
|
1946
|
-
calculateStats(items) {
|
|
1947
|
-
const byType = {};
|
|
1948
|
-
for (const item of items) {
|
|
1949
|
-
byType[item.type] = (byType[item.type] || 0) + 1;
|
|
1950
|
-
}
|
|
1951
|
-
const totalSize = items.reduce((sum, item) => sum + item.fileSize, 0);
|
|
1952
|
-
const lastModified = items.length > 0 ? items.reduce(
|
|
1953
|
-
(latest, item) => item.modifiedAt > latest ? item.modifiedAt : latest,
|
|
1954
|
-
items[0].modifiedAt
|
|
1955
|
-
) : null;
|
|
1956
|
-
return {
|
|
1957
|
-
totalItems: items.length,
|
|
1958
|
-
byType,
|
|
1959
|
-
totalSize,
|
|
1960
|
-
lastModified
|
|
1961
|
-
};
|
|
1962
|
-
}
|
|
1963
|
-
};
|
|
1964
|
-
|
|
1965
|
-
// src/ui/context/ContextApp.tsx
|
|
1966
|
-
var ContextApp = ({ initialIDE, initialType, onExit }) => {
|
|
1967
|
-
const { exit } = useApp2();
|
|
1968
|
-
const [screen, setScreen] = useState6("menu");
|
|
1969
|
-
const [selectedIDE, setSelectedIDE] = useState6(initialIDE || null);
|
|
1970
|
-
const [selectedType, setSelectedType] = useState6(initialType || null);
|
|
1971
|
-
const [selectedItem, setSelectedItem] = useState6(null);
|
|
1972
|
-
const [ideContexts, setIdeContexts] = useState6([]);
|
|
1973
|
-
const [loading, setLoading] = useState6(true);
|
|
1974
|
-
const [scrollPosition, setScrollPosition] = useState6(0);
|
|
1975
|
-
useEffect2(() => {
|
|
1976
|
-
const scanner = new ContextScannerService();
|
|
1977
|
-
scanner.scanAll().then((result) => {
|
|
1978
|
-
setIdeContexts(result.ides);
|
|
1979
|
-
setLoading(false);
|
|
1980
|
-
if (initialIDE) {
|
|
1981
|
-
setScreen("overview");
|
|
1982
|
-
}
|
|
1983
|
-
}).catch((error) => {
|
|
1984
|
-
console.error("Failed to scan context:", error);
|
|
1985
|
-
setLoading(false);
|
|
1986
|
-
});
|
|
1987
|
-
}, [initialIDE]);
|
|
1988
|
-
useInput6((input4, key) => {
|
|
1989
|
-
if (input4 === "q") {
|
|
1990
|
-
onExit();
|
|
1991
|
-
return;
|
|
1992
|
-
}
|
|
1993
|
-
if (screen !== "detail" && (key.escape || key.backspace)) {
|
|
1994
|
-
handleBack();
|
|
1995
|
-
return;
|
|
1996
|
-
}
|
|
1997
|
-
});
|
|
1998
|
-
const handleBack = () => {
|
|
1999
|
-
if (screen === "detail") {
|
|
2000
|
-
setScreen("list");
|
|
2001
|
-
setSelectedItem(null);
|
|
2002
|
-
setScrollPosition(0);
|
|
2003
|
-
} else if (screen === "list") {
|
|
2004
|
-
setScreen("overview");
|
|
2005
|
-
setSelectedType(null);
|
|
2006
|
-
} else if (screen === "overview") {
|
|
2007
|
-
setScreen("menu");
|
|
2008
|
-
setSelectedIDE(null);
|
|
2009
|
-
} else {
|
|
2010
|
-
onExit();
|
|
2011
|
-
}
|
|
2012
|
-
};
|
|
2013
|
-
const handleSelectIDE = (ide) => {
|
|
2014
|
-
setSelectedIDE(ide);
|
|
2015
|
-
setScreen("overview");
|
|
2016
|
-
};
|
|
2017
|
-
const handleSelectType = (type) => {
|
|
2018
|
-
setSelectedType(type);
|
|
2019
|
-
setScreen("list");
|
|
2020
|
-
};
|
|
2021
|
-
const handleSelectItem = (item) => {
|
|
2022
|
-
setSelectedItem(item);
|
|
2023
|
-
setScreen("detail");
|
|
2024
|
-
setScrollPosition(0);
|
|
2025
|
-
};
|
|
2026
|
-
const currentIDEContext = ideContexts.find((ctx) => ctx.ide === selectedIDE);
|
|
2027
|
-
const currentItems = currentIDEContext?.items?.filter((item) => item.type === selectedType) ?? [];
|
|
2028
|
-
const renderContent = () => {
|
|
2029
|
-
if (loading) {
|
|
2030
|
-
return /* @__PURE__ */ React9.createElement(Box7, { padding: 1 }, /* @__PURE__ */ React9.createElement(Text8, null, "\u0110ang qu\xE9t context..."));
|
|
2031
|
-
}
|
|
2032
|
-
switch (screen) {
|
|
2033
|
-
case "menu":
|
|
2034
|
-
return /* @__PURE__ */ React9.createElement(MainMenuView, { ideContexts, onSelect: handleSelectIDE });
|
|
2035
|
-
case "overview":
|
|
2036
|
-
return currentIDEContext ? /* @__PURE__ */ React9.createElement(
|
|
2037
|
-
IDEOverviewView,
|
|
2038
|
-
{
|
|
2039
|
-
ideContext: currentIDEContext,
|
|
2040
|
-
onSelectType: handleSelectType,
|
|
2041
|
-
onBack: handleBack
|
|
2042
|
-
}
|
|
2043
|
-
) : null;
|
|
2044
|
-
case "list":
|
|
2045
|
-
return /* @__PURE__ */ React9.createElement(
|
|
2046
|
-
ListView,
|
|
2047
|
-
{
|
|
2048
|
-
items: currentItems,
|
|
2049
|
-
contentType: selectedType,
|
|
2050
|
-
onSelect: handleSelectItem,
|
|
2051
|
-
onBack: handleBack
|
|
2052
|
-
}
|
|
2053
|
-
);
|
|
2054
|
-
case "detail":
|
|
2055
|
-
return selectedItem ? /* @__PURE__ */ React9.createElement(
|
|
2056
|
-
DetailView,
|
|
2057
|
-
{
|
|
2058
|
-
item: selectedItem,
|
|
2059
|
-
scrollPosition,
|
|
2060
|
-
onBack: handleBack
|
|
2061
|
-
}
|
|
2062
|
-
) : null;
|
|
2063
|
-
default:
|
|
2064
|
-
return /* @__PURE__ */ React9.createElement(MainMenuView, { ideContexts, onSelect: handleSelectIDE });
|
|
2065
|
-
}
|
|
2066
|
-
};
|
|
2067
|
-
const getFooterHints = () => {
|
|
2068
|
-
switch (screen) {
|
|
2069
|
-
case "menu":
|
|
2070
|
-
return "[\u2191\u2193] Ch\u1ECDn \xB7 [Enter] M\u1EDF \xB7 [q] Tho\xE1t";
|
|
2071
|
-
case "overview":
|
|
2072
|
-
return "[Tab/1-6] Chuy\u1EC3n tab \xB7 [Enter] Xem danh s\xE1ch \xB7 [Esc/\u2190] Quay l\u1EA1i \xB7 [q] Tho\xE1t";
|
|
2073
|
-
case "list":
|
|
2074
|
-
return "[\u2191\u2193] Ch\u1ECDn \xB7 [Enter] Chi ti\u1EBFt \xB7 [Esc/\u2190] Quay l\u1EA1i \xB7 [q] Tho\xE1t";
|
|
2075
|
-
case "detail":
|
|
2076
|
-
return "[\u2191\u2193/j/k] Cu\u1ED9n \xB7 [PgUp/u PgDn/d] Cu\u1ED9n nhanh \xB7 [g/G] \u0110\u1EA7u/Cu\u1ED1i \xB7 [Esc/\u2190] Quay l\u1EA1i \xB7 [q] Tho\xE1t";
|
|
2077
|
-
default:
|
|
2078
|
-
return "[q] Tho\xE1t";
|
|
2079
|
-
}
|
|
2080
|
-
};
|
|
2081
|
-
return /* @__PURE__ */ React9.createElement(Box7, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React9.createElement(Box7, { marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { bold: true, color: "cyan" }, "\u{1F4CB} Jai1 Context"), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, " - Kh\xE1m ph\xE1 context d\u1EF1 \xE1n")), renderContent(), /* @__PURE__ */ React9.createElement(Box7, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, getFooterHints())));
|
|
2082
|
-
};
|
|
2083
|
-
|
|
2084
|
-
// src/commands/ide/context.ts
|
|
2085
|
-
function createContextSubcommand() {
|
|
2086
|
-
const cmd = new Command6("context").description("Browse and explore IDE context (rules, workflows, skills)").option("--target <ide>", "Open specific IDE context (cursor, windsurf, antigravity, jai1)").option("--type <type>", "Show specific content type (rules, workflows, skills, agents, prompts)").option("--stats", "Show context statistics (non-interactive)").action(async (options) => {
|
|
2087
|
-
let initialIDE;
|
|
2088
|
-
if (options.target) {
|
|
2089
|
-
const validIDEs = ["cursor", "windsurf", "antigravity", "jai1"];
|
|
2090
|
-
if (!validIDEs.includes(options.target)) {
|
|
2091
|
-
console.error(`\u274C Invalid IDE: ${options.target}`);
|
|
2092
|
-
console.error(` Valid IDEs: ${validIDEs.join(", ")}`);
|
|
2093
|
-
process.exit(1);
|
|
2094
|
-
}
|
|
2095
|
-
initialIDE = options.target;
|
|
2096
|
-
}
|
|
2097
|
-
let initialType;
|
|
2098
|
-
if (options.type) {
|
|
2099
|
-
const validTypes = ["rules", "workflows", "skills", "agents", "prompts", "context"];
|
|
2100
|
-
if (!validTypes.includes(options.type)) {
|
|
2101
|
-
console.error(`\u274C Invalid content type: ${options.type}`);
|
|
2102
|
-
console.error(` Valid types: ${validTypes.join(", ")}`);
|
|
2103
|
-
process.exit(1);
|
|
2104
|
-
}
|
|
2105
|
-
initialType = options.type;
|
|
2106
|
-
}
|
|
2107
|
-
if (options.stats) {
|
|
2108
|
-
await printStats();
|
|
2109
|
-
return;
|
|
2110
|
-
}
|
|
2111
|
-
const { waitUntilExit } = render2(
|
|
2112
|
-
React10.createElement(ContextApp, {
|
|
2113
|
-
initialIDE,
|
|
2114
|
-
initialType,
|
|
2115
|
-
onExit: () => {
|
|
2116
|
-
process.exit(0);
|
|
2322
|
+
/**
|
|
2323
|
+
* Detect available IDEs in project
|
|
2324
|
+
*/
|
|
2325
|
+
async detectIDEs() {
|
|
2326
|
+
const ides = [];
|
|
2327
|
+
for (const ide of getAllIDEs()) {
|
|
2328
|
+
const config = IDE_CONFIGS[ide];
|
|
2329
|
+
if (ide === "jai1") {
|
|
2330
|
+
const jai1Path = path2.join(this.projectPath, ".jai1");
|
|
2331
|
+
const frameworkPath = path2.join(this.projectPath, "packages/jai1-framework");
|
|
2332
|
+
if (await this.pathExists(jai1Path) || await this.pathExists(frameworkPath)) {
|
|
2333
|
+
ides.push(ide);
|
|
2334
|
+
}
|
|
2335
|
+
} else {
|
|
2336
|
+
const idePath = path2.join(this.projectPath, config.basePath);
|
|
2337
|
+
if (await this.pathExists(idePath)) {
|
|
2338
|
+
ides.push(ide);
|
|
2117
2339
|
}
|
|
2118
|
-
})
|
|
2119
|
-
);
|
|
2120
|
-
await waitUntilExit();
|
|
2121
|
-
});
|
|
2122
|
-
return cmd;
|
|
2123
|
-
}
|
|
2124
|
-
async function printStats() {
|
|
2125
|
-
const scanner = new ContextScannerService();
|
|
2126
|
-
console.log("\u{1F50D} Scanning context...\n");
|
|
2127
|
-
try {
|
|
2128
|
-
const context = await scanner.scanAll();
|
|
2129
|
-
if (context.ides.length === 0) {
|
|
2130
|
-
console.log("\u26A0\uFE0F No context found");
|
|
2131
|
-
console.log(" Run `jai1 apply` to install context for your IDE.\n");
|
|
2132
|
-
return;
|
|
2133
|
-
}
|
|
2134
|
-
console.log("\u{1F4CA} Context Statistics\n");
|
|
2135
|
-
console.log(`\u{1F4C1} Project: ${context.projectPath}`);
|
|
2136
|
-
console.log(`\u{1F550} Scan time: ${context.scanTime.toLocaleString("en-US")}
|
|
2137
|
-
`);
|
|
2138
|
-
for (const ideContext of context.ides) {
|
|
2139
|
-
console.log(`${ideContext.config.icon} ${ideContext.config.name}`);
|
|
2140
|
-
console.log(` Path: ${ideContext.config.basePath}`);
|
|
2141
|
-
console.log(` Items: ${ideContext.stats.totalItems}`);
|
|
2142
|
-
const types = [];
|
|
2143
|
-
if (ideContext.stats.byType.rules) types.push(`${ideContext.stats.byType.rules} rules`);
|
|
2144
|
-
if (ideContext.stats.byType.workflows) types.push(`${ideContext.stats.byType.workflows} workflows`);
|
|
2145
|
-
if (ideContext.stats.byType.skills) types.push(`${ideContext.stats.byType.skills} skills`);
|
|
2146
|
-
if (ideContext.stats.byType.agents) types.push(`${ideContext.stats.byType.agents} agents`);
|
|
2147
|
-
if (ideContext.stats.byType.prompts) types.push(`${ideContext.stats.byType.prompts} prompts`);
|
|
2148
|
-
if (ideContext.stats.byType.context) types.push(`${ideContext.stats.byType.context} context`);
|
|
2149
|
-
if (types.length > 0) {
|
|
2150
|
-
console.log(` Breakdown: ${types.join(", ")}`);
|
|
2151
2340
|
}
|
|
2152
|
-
console.log("");
|
|
2153
2341
|
}
|
|
2154
|
-
|
|
2155
|
-
`);
|
|
2156
|
-
} catch (error) {
|
|
2157
|
-
console.error("\u274C Error scanning context:", error);
|
|
2158
|
-
process.exit(1);
|
|
2342
|
+
return ides;
|
|
2159
2343
|
}
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
telemetry: {
|
|
2170
|
-
name: "Telemetry",
|
|
2171
|
-
description: "Disable all telemetry and data collection",
|
|
2172
|
-
settings: {
|
|
2173
|
-
"telemetry.telemetryLevel": "off",
|
|
2174
|
-
"telemetry.feedback.enabled": false,
|
|
2175
|
-
"workbench.enableExperiments": false,
|
|
2176
|
-
"workbench.settings.enableNaturalLanguageSearch": false
|
|
2177
|
-
}
|
|
2178
|
-
},
|
|
2179
|
-
languageServers: {
|
|
2180
|
-
name: "Language Servers",
|
|
2181
|
-
description: "Disable/reduce language server load (tsserver, eslint...)",
|
|
2182
|
-
settings: {
|
|
2183
|
-
// TypeScript
|
|
2184
|
-
"typescript.tsserver.experimental.enableProjectDiagnostics": false,
|
|
2185
|
-
"typescript.disableAutomaticTypeAcquisition": true,
|
|
2186
|
-
"typescript.tsserver.maxTsServerMemory": 2048,
|
|
2187
|
-
"typescript.surveys.enabled": false,
|
|
2188
|
-
// JavaScript
|
|
2189
|
-
"js/ts.implicitProjectConfig.strictNullChecks": false,
|
|
2190
|
-
// ESLint
|
|
2191
|
-
"eslint.enable": false,
|
|
2192
|
-
// Prettier
|
|
2193
|
-
"prettier.enable": false,
|
|
2194
|
-
// CSS/HTML
|
|
2195
|
-
"css.validate": false,
|
|
2196
|
-
"html.validate.scripts": false,
|
|
2197
|
-
"html.validate.styles": false,
|
|
2198
|
-
// JSON
|
|
2199
|
-
"json.schemaDownload.enable": false
|
|
2344
|
+
/**
|
|
2345
|
+
* Check if path exists
|
|
2346
|
+
*/
|
|
2347
|
+
async pathExists(filepath) {
|
|
2348
|
+
try {
|
|
2349
|
+
await fs5.access(filepath);
|
|
2350
|
+
return true;
|
|
2351
|
+
} catch {
|
|
2352
|
+
return false;
|
|
2200
2353
|
}
|
|
2201
|
-
}
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
"git.path": null,
|
|
2210
|
-
"scm.diffDecorations": "none"
|
|
2354
|
+
}
|
|
2355
|
+
/**
|
|
2356
|
+
* Calculate statistics from items
|
|
2357
|
+
*/
|
|
2358
|
+
calculateStats(items) {
|
|
2359
|
+
const byType = {};
|
|
2360
|
+
for (const item of items) {
|
|
2361
|
+
byType[item.type] = (byType[item.type] || 0) + 1;
|
|
2211
2362
|
}
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2363
|
+
const totalSize = items.reduce((sum, item) => sum + item.fileSize, 0);
|
|
2364
|
+
const lastModified = items.length > 0 ? items.reduce(
|
|
2365
|
+
(latest, item) => item.modifiedAt > latest ? item.modifiedAt : latest,
|
|
2366
|
+
items[0].modifiedAt
|
|
2367
|
+
) : null;
|
|
2368
|
+
return {
|
|
2369
|
+
totalItems: items.length,
|
|
2370
|
+
byType,
|
|
2371
|
+
totalSize,
|
|
2372
|
+
lastModified
|
|
2373
|
+
};
|
|
2374
|
+
}
|
|
2375
|
+
};
|
|
2376
|
+
|
|
2377
|
+
// src/ui/context/ContextApp.tsx
|
|
2378
|
+
var ContextApp = ({ initialIDE, initialType, onExit }) => {
|
|
2379
|
+
const { exit } = useApp2();
|
|
2380
|
+
const [screen, setScreen] = useState6("menu");
|
|
2381
|
+
const [selectedIDE, setSelectedIDE] = useState6(initialIDE || null);
|
|
2382
|
+
const [selectedType, setSelectedType] = useState6(initialType || null);
|
|
2383
|
+
const [selectedItem, setSelectedItem] = useState6(null);
|
|
2384
|
+
const [ideContexts, setIdeContexts] = useState6([]);
|
|
2385
|
+
const [loading, setLoading] = useState6(true);
|
|
2386
|
+
const [scrollPosition, setScrollPosition] = useState6(0);
|
|
2387
|
+
useEffect2(() => {
|
|
2388
|
+
const scanner = new ContextScannerService();
|
|
2389
|
+
scanner.scanAll().then((result) => {
|
|
2390
|
+
setIdeContexts(result.ides);
|
|
2391
|
+
setLoading(false);
|
|
2392
|
+
if (initialIDE) {
|
|
2393
|
+
setScreen("overview");
|
|
2234
2394
|
}
|
|
2395
|
+
}).catch((error) => {
|
|
2396
|
+
console.error("Failed to scan context:", error);
|
|
2397
|
+
setLoading(false);
|
|
2398
|
+
});
|
|
2399
|
+
}, [initialIDE]);
|
|
2400
|
+
useInput6((input4, key) => {
|
|
2401
|
+
if (input4 === "q") {
|
|
2402
|
+
onExit();
|
|
2403
|
+
return;
|
|
2235
2404
|
}
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
description: "Exclude directories from search",
|
|
2240
|
-
settings: {
|
|
2241
|
-
"search.exclude": {
|
|
2242
|
-
"**/node_modules": true,
|
|
2243
|
-
"**/bower_components": true,
|
|
2244
|
-
"**/*.code-search": true,
|
|
2245
|
-
"**/dist": true,
|
|
2246
|
-
"**/build": true,
|
|
2247
|
-
"**/out": true,
|
|
2248
|
-
"**/.next": true,
|
|
2249
|
-
"**/.nuxt": true,
|
|
2250
|
-
"**/coverage": true,
|
|
2251
|
-
"**/.cache": true
|
|
2252
|
-
},
|
|
2253
|
-
"search.followSymlinks": false
|
|
2405
|
+
if (screen !== "detail" && (key.escape || key.backspace)) {
|
|
2406
|
+
handleBack();
|
|
2407
|
+
return;
|
|
2254
2408
|
}
|
|
2255
|
-
}
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
"
|
|
2263
|
-
|
|
2409
|
+
});
|
|
2410
|
+
const handleBack = () => {
|
|
2411
|
+
if (screen === "detail") {
|
|
2412
|
+
setScreen("list");
|
|
2413
|
+
setSelectedItem(null);
|
|
2414
|
+
setScrollPosition(0);
|
|
2415
|
+
} else if (screen === "list") {
|
|
2416
|
+
setScreen("overview");
|
|
2417
|
+
setSelectedType(null);
|
|
2418
|
+
} else if (screen === "overview") {
|
|
2419
|
+
setScreen("menu");
|
|
2420
|
+
setSelectedIDE(null);
|
|
2421
|
+
} else {
|
|
2422
|
+
onExit();
|
|
2264
2423
|
}
|
|
2265
|
-
}
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
"editor.hover.delay": 500,
|
|
2285
|
-
"editor.matchBrackets": "never"
|
|
2424
|
+
};
|
|
2425
|
+
const handleSelectIDE = (ide) => {
|
|
2426
|
+
setSelectedIDE(ide);
|
|
2427
|
+
setScreen("overview");
|
|
2428
|
+
};
|
|
2429
|
+
const handleSelectType = (type) => {
|
|
2430
|
+
setSelectedType(type);
|
|
2431
|
+
setScreen("list");
|
|
2432
|
+
};
|
|
2433
|
+
const handleSelectItem = (item) => {
|
|
2434
|
+
setSelectedItem(item);
|
|
2435
|
+
setScreen("detail");
|
|
2436
|
+
setScrollPosition(0);
|
|
2437
|
+
};
|
|
2438
|
+
const currentIDEContext = ideContexts.find((ctx) => ctx.ide === selectedIDE);
|
|
2439
|
+
const currentItems = currentIDEContext?.items?.filter((item) => item.type === selectedType) ?? [];
|
|
2440
|
+
const renderContent = () => {
|
|
2441
|
+
if (loading) {
|
|
2442
|
+
return /* @__PURE__ */ React9.createElement(Box7, { padding: 1 }, /* @__PURE__ */ React9.createElement(Text8, null, "\u0110ang qu\xE9t context..."));
|
|
2286
2443
|
}
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2444
|
+
switch (screen) {
|
|
2445
|
+
case "menu":
|
|
2446
|
+
return /* @__PURE__ */ React9.createElement(MainMenuView, { ideContexts, onSelect: handleSelectIDE });
|
|
2447
|
+
case "overview":
|
|
2448
|
+
return currentIDEContext ? /* @__PURE__ */ React9.createElement(
|
|
2449
|
+
IDEOverviewView,
|
|
2450
|
+
{
|
|
2451
|
+
ideContext: currentIDEContext,
|
|
2452
|
+
onSelectType: handleSelectType,
|
|
2453
|
+
onBack: handleBack
|
|
2454
|
+
}
|
|
2455
|
+
) : null;
|
|
2456
|
+
case "list":
|
|
2457
|
+
return /* @__PURE__ */ React9.createElement(
|
|
2458
|
+
ListView,
|
|
2459
|
+
{
|
|
2460
|
+
items: currentItems,
|
|
2461
|
+
contentType: selectedType,
|
|
2462
|
+
onSelect: handleSelectItem,
|
|
2463
|
+
onBack: handleBack
|
|
2464
|
+
}
|
|
2465
|
+
);
|
|
2466
|
+
case "detail":
|
|
2467
|
+
return selectedItem ? /* @__PURE__ */ React9.createElement(
|
|
2468
|
+
DetailView,
|
|
2469
|
+
{
|
|
2470
|
+
item: selectedItem,
|
|
2471
|
+
scrollPosition,
|
|
2472
|
+
onBack: handleBack
|
|
2473
|
+
}
|
|
2474
|
+
) : null;
|
|
2475
|
+
default:
|
|
2476
|
+
return /* @__PURE__ */ React9.createElement(MainMenuView, { ideContexts, onSelect: handleSelectIDE });
|
|
2299
2477
|
}
|
|
2300
|
-
}
|
|
2478
|
+
};
|
|
2479
|
+
const getFooterHints = () => {
|
|
2480
|
+
switch (screen) {
|
|
2481
|
+
case "menu":
|
|
2482
|
+
return "[\u2191\u2193] Ch\u1ECDn \xB7 [Enter] M\u1EDF \xB7 [q] Tho\xE1t";
|
|
2483
|
+
case "overview":
|
|
2484
|
+
return "[Tab/1-6] Chuy\u1EC3n tab \xB7 [Enter] Xem danh s\xE1ch \xB7 [Esc/\u2190] Quay l\u1EA1i \xB7 [q] Tho\xE1t";
|
|
2485
|
+
case "list":
|
|
2486
|
+
return "[\u2191\u2193] Ch\u1ECDn \xB7 [Enter] Chi ti\u1EBFt \xB7 [Esc/\u2190] Quay l\u1EA1i \xB7 [q] Tho\xE1t";
|
|
2487
|
+
case "detail":
|
|
2488
|
+
return "[\u2191\u2193/j/k] Cu\u1ED9n \xB7 [PgUp/u PgDn/d] Cu\u1ED9n nhanh \xB7 [g/G] \u0110\u1EA7u/Cu\u1ED1i \xB7 [Esc/\u2190] Quay l\u1EA1i \xB7 [q] Tho\xE1t";
|
|
2489
|
+
default:
|
|
2490
|
+
return "[q] Tho\xE1t";
|
|
2491
|
+
}
|
|
2492
|
+
};
|
|
2493
|
+
return /* @__PURE__ */ React9.createElement(Box7, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React9.createElement(Box7, { marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { bold: true, color: "cyan" }, "\u{1F4CB} Jai1 Context"), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, " - Kh\xE1m ph\xE1 context d\u1EF1 \xE1n")), renderContent(), /* @__PURE__ */ React9.createElement(Box7, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, getFooterHints())));
|
|
2301
2494
|
};
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2495
|
+
|
|
2496
|
+
// src/commands/ide/context.ts
|
|
2497
|
+
function createContextSubcommand() {
|
|
2498
|
+
const cmd = new Command6("context").description("Browse and explore IDE context (rules, workflows, skills)").option("--target <ide>", "Open specific IDE context (cursor, windsurf, antigravity, jai1)").option("--type <type>", "Show specific content type (rules, workflows, skills, agents, prompts)").option("--stats", "Show context statistics (non-interactive)").action(async (options) => {
|
|
2499
|
+
let initialIDE;
|
|
2500
|
+
if (options.target) {
|
|
2501
|
+
const validIDEs = ["cursor", "windsurf", "antigravity", "jai1"];
|
|
2502
|
+
if (!validIDEs.includes(options.target)) {
|
|
2503
|
+
console.error(`\u274C Invalid IDE: ${options.target}`);
|
|
2504
|
+
console.error(` Valid IDEs: ${validIDEs.join(", ")}`);
|
|
2505
|
+
process.exit(1);
|
|
2506
|
+
}
|
|
2507
|
+
initialIDE = options.target;
|
|
2312
2508
|
}
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2509
|
+
let initialType;
|
|
2510
|
+
if (options.type) {
|
|
2511
|
+
const validTypes = ["rules", "workflows", "skills", "agents", "prompts", "context"];
|
|
2512
|
+
if (!validTypes.includes(options.type)) {
|
|
2513
|
+
console.error(`\u274C Invalid content type: ${options.type}`);
|
|
2514
|
+
console.error(` Valid types: ${validTypes.join(", ")}`);
|
|
2515
|
+
process.exit(1);
|
|
2516
|
+
}
|
|
2517
|
+
initialType = options.type;
|
|
2319
2518
|
}
|
|
2519
|
+
if (options.stats) {
|
|
2520
|
+
await printStats();
|
|
2521
|
+
return;
|
|
2522
|
+
}
|
|
2523
|
+
const { waitUntilExit } = render2(
|
|
2524
|
+
React10.createElement(ContextApp, {
|
|
2525
|
+
initialIDE,
|
|
2526
|
+
initialType,
|
|
2527
|
+
onExit: () => {
|
|
2528
|
+
process.exit(0);
|
|
2529
|
+
}
|
|
2530
|
+
})
|
|
2531
|
+
);
|
|
2532
|
+
await waitUntilExit();
|
|
2320
2533
|
});
|
|
2321
|
-
|
|
2322
|
-
const allGroups = Object.keys(PERFORMANCE_GROUPS);
|
|
2323
|
-
await applyGroups(allGroups, "enable");
|
|
2324
|
-
});
|
|
2325
|
-
setupCommand.command("reset").description("Reset to default settings (remove all optimizations)").argument("[groups...]", "Group names to reset (empty = reset all)").action(async (groups) => {
|
|
2326
|
-
await resetSettings(groups);
|
|
2327
|
-
});
|
|
2328
|
-
setupCommand.command("list").description("List all available optimization groups").action(() => {
|
|
2329
|
-
console.log("\u{1F527} Available optimization groups:\n");
|
|
2330
|
-
Object.entries(PERFORMANCE_GROUPS).forEach(([key, group]) => {
|
|
2331
|
-
console.log(` ${key.padEnd(20)} - ${group.name}`);
|
|
2332
|
-
console.log(` ${" ".repeat(20)} ${group.description}`);
|
|
2333
|
-
console.log();
|
|
2334
|
-
});
|
|
2335
|
-
});
|
|
2336
|
-
return setupCommand;
|
|
2337
|
-
}
|
|
2338
|
-
async function interactiveMode() {
|
|
2339
|
-
console.log("\u{1F527} IDE Settings Manager\n");
|
|
2340
|
-
console.log("\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E");
|
|
2341
|
-
console.log("\u2502 \u{1F4CC} Usage: \u2502");
|
|
2342
|
-
console.log("\u2502 \u2022 Use \u2191\u2193 keys to navigate \u2502");
|
|
2343
|
-
console.log("\u2502 \u2022 Press SPACE to select/deselect group \u2502");
|
|
2344
|
-
console.log("\u2502 \u2022 Press ENTER to confirm and apply \u2502");
|
|
2345
|
-
console.log("\u2502 \u2022 Press Ctrl+C to cancel \u2502");
|
|
2346
|
-
console.log("\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F\n");
|
|
2347
|
-
const action = await select({
|
|
2348
|
-
message: "What do you want to do?",
|
|
2349
|
-
choices: [
|
|
2350
|
-
{ name: "\u2705 Enable optimization groups", value: "enable" },
|
|
2351
|
-
{ name: "\u274C Disable optimization groups", value: "disable" },
|
|
2352
|
-
{ name: "\u{1F680} Max Performance (enable all)", value: "max" },
|
|
2353
|
-
{ name: "\u{1F504} Reset to defaults", value: "reset" }
|
|
2354
|
-
]
|
|
2355
|
-
});
|
|
2356
|
-
if (action === "max") {
|
|
2357
|
-
const allGroups = Object.keys(PERFORMANCE_GROUPS);
|
|
2358
|
-
await applyGroups(allGroups, "enable");
|
|
2359
|
-
} else if (action === "reset") {
|
|
2360
|
-
await resetSettings([]);
|
|
2361
|
-
} else {
|
|
2362
|
-
await selectGroupsToApply(action);
|
|
2363
|
-
}
|
|
2534
|
+
return cmd;
|
|
2364
2535
|
}
|
|
2365
|
-
async function
|
|
2366
|
-
const
|
|
2367
|
-
|
|
2368
|
-
value: key
|
|
2369
|
-
}));
|
|
2536
|
+
async function printStats() {
|
|
2537
|
+
const scanner = new ContextScannerService();
|
|
2538
|
+
console.log("\u{1F50D} Scanning context...\n");
|
|
2370
2539
|
try {
|
|
2371
|
-
const
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
if (selectedGroups.length === 0) {
|
|
2376
|
-
console.log("\n\u26A0\uFE0F No groups selected!");
|
|
2377
|
-
console.log(" \u{1F4A1} Tip: Press SPACE to select at least 1 group before pressing ENTER.");
|
|
2540
|
+
const context = await scanner.scanAll();
|
|
2541
|
+
if (context.ides.length === 0) {
|
|
2542
|
+
console.log("\u26A0\uFE0F No context found");
|
|
2543
|
+
console.log(" Run `jai1 apply` to install context for your IDE.\n");
|
|
2378
2544
|
return;
|
|
2379
2545
|
}
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
console.log(
|
|
2383
|
-
}
|
|
2384
|
-
}
|
|
2385
|
-
async function applyGroups(groupKeys, action) {
|
|
2386
|
-
const vscodeDir = path2.join(process.cwd(), ".vscode");
|
|
2387
|
-
const settingsPath = path2.join(vscodeDir, "settings.json");
|
|
2388
|
-
const invalidGroups = groupKeys.filter((key) => !PERFORMANCE_GROUPS[key]);
|
|
2389
|
-
if (invalidGroups.length > 0) {
|
|
2390
|
-
console.log(`
|
|
2391
|
-
\u274C Invalid groups: ${invalidGroups.join(", ")}`);
|
|
2392
|
-
console.log(' \u{1F4A1} Run "jai1 ide setup list" to see available groups.');
|
|
2393
|
-
return;
|
|
2394
|
-
}
|
|
2395
|
-
if (!existsSync(vscodeDir)) {
|
|
2396
|
-
await fs5.mkdir(vscodeDir, { recursive: true });
|
|
2397
|
-
console.log("\u{1F4C1} Created .vscode/ directory");
|
|
2398
|
-
}
|
|
2399
|
-
let currentSettings = {};
|
|
2400
|
-
if (existsSync(settingsPath)) {
|
|
2401
|
-
try {
|
|
2402
|
-
const content = await fs5.readFile(settingsPath, "utf-8");
|
|
2403
|
-
currentSettings = JSON.parse(content);
|
|
2404
|
-
console.log("\u{1F4C4} Read current settings from settings.json");
|
|
2405
|
-
} catch {
|
|
2406
|
-
console.warn("\u26A0\uFE0F Cannot read settings.json (may contain comments).");
|
|
2407
|
-
const confirmOverwrite = await confirm2({
|
|
2408
|
-
message: "Overwrite current settings.json file?",
|
|
2409
|
-
default: false
|
|
2410
|
-
});
|
|
2411
|
-
if (!confirmOverwrite) {
|
|
2412
|
-
console.log("\u274C Operation cancelled.");
|
|
2413
|
-
return;
|
|
2414
|
-
}
|
|
2415
|
-
currentSettings = {};
|
|
2416
|
-
}
|
|
2417
|
-
}
|
|
2418
|
-
const newSettings = { ...currentSettings };
|
|
2419
|
-
console.log(`
|
|
2420
|
-
\u{1F4DD} ${action === "enable" ? "Enabling" : "Disabling"} groups:
|
|
2546
|
+
console.log("\u{1F4CA} Context Statistics\n");
|
|
2547
|
+
console.log(`\u{1F4C1} Project: ${context.projectPath}`);
|
|
2548
|
+
console.log(`\u{1F550} Scan time: ${context.scanTime.toLocaleString("en-US")}
|
|
2421
2549
|
`);
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
}
|
|
2435
|
-
}
|
|
2436
|
-
} else {
|
|
2437
|
-
for (const settingKey of Object.keys(group.settings)) {
|
|
2438
|
-
delete newSettings[settingKey];
|
|
2550
|
+
for (const ideContext of context.ides) {
|
|
2551
|
+
console.log(`${ideContext.config.icon} ${ideContext.config.name}`);
|
|
2552
|
+
console.log(` Path: ${ideContext.config.basePath}`);
|
|
2553
|
+
console.log(` Items: ${ideContext.stats.totalItems}`);
|
|
2554
|
+
const types = [];
|
|
2555
|
+
if (ideContext.stats.byType.rules) types.push(`${ideContext.stats.byType.rules} rules`);
|
|
2556
|
+
if (ideContext.stats.byType.workflows) types.push(`${ideContext.stats.byType.workflows} workflows`);
|
|
2557
|
+
if (ideContext.stats.byType.skills) types.push(`${ideContext.stats.byType.skills} skills`);
|
|
2558
|
+
if (ideContext.stats.byType.agents) types.push(`${ideContext.stats.byType.agents} agents`);
|
|
2559
|
+
if (ideContext.stats.byType.prompts) types.push(`${ideContext.stats.byType.prompts} prompts`);
|
|
2560
|
+
if (ideContext.stats.byType.context) types.push(`${ideContext.stats.byType.context} context`);
|
|
2561
|
+
if (types.length > 0) {
|
|
2562
|
+
console.log(` Breakdown: ${types.join(", ")}`);
|
|
2439
2563
|
}
|
|
2564
|
+
console.log("");
|
|
2440
2565
|
}
|
|
2566
|
+
console.log(`\u2705 Total: ${context.totalItems} items
|
|
2567
|
+
`);
|
|
2568
|
+
} catch (error) {
|
|
2569
|
+
console.error("\u274C Error scanning context:", error);
|
|
2570
|
+
process.exit(1);
|
|
2441
2571
|
}
|
|
2442
|
-
await fs5.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
|
|
2443
|
-
console.log(`
|
|
2444
|
-
\u2705 Updated IDE settings at: ${settingsPath}`);
|
|
2445
|
-
console.log("\u{1F4A1} Tip: Restart your IDE to apply changes.");
|
|
2446
|
-
}
|
|
2447
|
-
async function resetSettings(groupKeys) {
|
|
2448
|
-
const vscodeDir = path2.join(process.cwd(), ".vscode");
|
|
2449
|
-
const settingsPath = path2.join(vscodeDir, "settings.json");
|
|
2450
|
-
if (!existsSync(settingsPath)) {
|
|
2451
|
-
console.log("\n\u26A0\uFE0F No settings.json file found");
|
|
2452
|
-
return;
|
|
2453
|
-
}
|
|
2454
|
-
const confirmReset = await confirm2({
|
|
2455
|
-
message: groupKeys.length === 0 ? "Reset ALL settings to default (delete entire file)?" : `Reset groups: ${groupKeys.join(", ")}?`,
|
|
2456
|
-
default: false
|
|
2457
|
-
});
|
|
2458
|
-
if (!confirmReset) {
|
|
2459
|
-
console.log("\u274C Operation cancelled.");
|
|
2460
|
-
return;
|
|
2461
|
-
}
|
|
2462
|
-
if (groupKeys.length === 0) {
|
|
2463
|
-
await fs5.unlink(settingsPath);
|
|
2464
|
-
console.log("\n\u2705 Deleted settings.json file");
|
|
2465
|
-
} else {
|
|
2466
|
-
await applyGroups(groupKeys, "disable");
|
|
2467
|
-
}
|
|
2468
|
-
console.log("\u{1F4A1} Tip: Restart your IDE to apply changes.");
|
|
2469
2572
|
}
|
|
2470
2573
|
|
|
2471
|
-
// src/commands/ide/
|
|
2472
|
-
import { Command as
|
|
2473
|
-
import { checkbox
|
|
2474
|
-
|
|
2475
|
-
// src/services/migrate-ide.service.ts
|
|
2476
|
-
import { promises as fs6 } from "fs";
|
|
2574
|
+
// src/commands/ide/setup.ts
|
|
2575
|
+
import { Command as Command7 } from "commander";
|
|
2576
|
+
import { checkbox, confirm as confirm2, select } from "@inquirer/prompts";
|
|
2577
|
+
import fs6 from "fs/promises";
|
|
2477
2578
|
import path3 from "path";
|
|
2478
|
-
import
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
workflowsPath: null,
|
|
2489
|
-
commandsPath: "commands",
|
|
2490
|
-
fileExtension: ".mdc",
|
|
2491
|
-
generateFrontmatter: (opts) => {
|
|
2492
|
-
const lines = ["---"];
|
|
2493
|
-
if (opts.description) {
|
|
2494
|
-
lines.push(`description: ${opts.description}`);
|
|
2495
|
-
}
|
|
2496
|
-
if (opts.globs && opts.globs.length > 0) {
|
|
2497
|
-
lines.push(`globs: ${opts.globs.join(", ")}`);
|
|
2498
|
-
}
|
|
2499
|
-
lines.push(`alwaysApply: ${opts.alwaysApply}`);
|
|
2500
|
-
lines.push("---");
|
|
2501
|
-
return lines.join("\n");
|
|
2579
|
+
import { existsSync } from "fs";
|
|
2580
|
+
var PERFORMANCE_GROUPS = {
|
|
2581
|
+
telemetry: {
|
|
2582
|
+
name: "Telemetry",
|
|
2583
|
+
description: "Disable all telemetry and data collection",
|
|
2584
|
+
settings: {
|
|
2585
|
+
"telemetry.telemetryLevel": "off",
|
|
2586
|
+
"telemetry.feedback.enabled": false,
|
|
2587
|
+
"workbench.enableExperiments": false,
|
|
2588
|
+
"workbench.settings.enableNaturalLanguageSearch": false
|
|
2502
2589
|
}
|
|
2503
2590
|
},
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2591
|
+
languageServers: {
|
|
2592
|
+
name: "Language Servers",
|
|
2593
|
+
description: "Disable/reduce language server load (tsserver, eslint...)",
|
|
2594
|
+
settings: {
|
|
2595
|
+
// TypeScript
|
|
2596
|
+
"typescript.tsserver.experimental.enableProjectDiagnostics": false,
|
|
2597
|
+
"typescript.disableAutomaticTypeAcquisition": true,
|
|
2598
|
+
"typescript.tsserver.maxTsServerMemory": 2048,
|
|
2599
|
+
"typescript.surveys.enabled": false,
|
|
2600
|
+
// JavaScript
|
|
2601
|
+
"js/ts.implicitProjectConfig.strictNullChecks": false,
|
|
2602
|
+
// ESLint
|
|
2603
|
+
"eslint.enable": false,
|
|
2604
|
+
// Prettier
|
|
2605
|
+
"prettier.enable": false,
|
|
2606
|
+
// CSS/HTML
|
|
2607
|
+
"css.validate": false,
|
|
2608
|
+
"html.validate.scripts": false,
|
|
2609
|
+
"html.validate.styles": false,
|
|
2610
|
+
// JSON
|
|
2611
|
+
"json.schemaDownload.enable": false
|
|
2518
2612
|
}
|
|
2519
2613
|
},
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
generateFrontmatter: (opts) => {
|
|
2530
|
-
const trigger = opts.alwaysApply ? "always" : "glob";
|
|
2531
|
-
return `---
|
|
2532
|
-
trigger: ${trigger}
|
|
2533
|
-
---`;
|
|
2614
|
+
git: {
|
|
2615
|
+
name: "Git Integration",
|
|
2616
|
+
description: "Disable built-in Git for better performance",
|
|
2617
|
+
settings: {
|
|
2618
|
+
"git.enabled": false,
|
|
2619
|
+
"git.autoRepositoryDetection": false,
|
|
2620
|
+
"git.decorations.enabled": false,
|
|
2621
|
+
"git.path": null,
|
|
2622
|
+
"scm.diffDecorations": "none"
|
|
2534
2623
|
}
|
|
2535
2624
|
},
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2625
|
+
fileWatcher: {
|
|
2626
|
+
name: "File Watcher",
|
|
2627
|
+
description: "Exclude directories from file watcher",
|
|
2628
|
+
settings: {
|
|
2629
|
+
"files.watcherExclude": {
|
|
2630
|
+
"**/node_modules/**": true,
|
|
2631
|
+
"**/.git/**": true,
|
|
2632
|
+
"**/vendor/**": true,
|
|
2633
|
+
"**/dist/**": true,
|
|
2634
|
+
"**/build/**": true,
|
|
2635
|
+
"**/.next/**": true,
|
|
2636
|
+
"**/out/**": true,
|
|
2637
|
+
"**/.nuxt/**": true,
|
|
2638
|
+
"**/.cache/**": true,
|
|
2639
|
+
"**/coverage/**": true
|
|
2640
|
+
},
|
|
2641
|
+
"files.exclude": {
|
|
2642
|
+
"**/.git": true,
|
|
2643
|
+
"**/.DS_Store": true,
|
|
2644
|
+
"**/node_modules": false
|
|
2645
|
+
// Keep visible but don't watch
|
|
2552
2646
|
}
|
|
2553
|
-
lines.push("---");
|
|
2554
|
-
return lines.join("\n");
|
|
2555
2647
|
}
|
|
2556
2648
|
},
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2649
|
+
search: {
|
|
2650
|
+
name: "Search",
|
|
2651
|
+
description: "Exclude directories from search",
|
|
2652
|
+
settings: {
|
|
2653
|
+
"search.exclude": {
|
|
2654
|
+
"**/node_modules": true,
|
|
2655
|
+
"**/bower_components": true,
|
|
2656
|
+
"**/*.code-search": true,
|
|
2657
|
+
"**/dist": true,
|
|
2658
|
+
"**/build": true,
|
|
2659
|
+
"**/out": true,
|
|
2660
|
+
"**/.next": true,
|
|
2661
|
+
"**/.nuxt": true,
|
|
2662
|
+
"**/coverage": true,
|
|
2663
|
+
"**/.cache": true
|
|
2664
|
+
},
|
|
2665
|
+
"search.followSymlinks": false
|
|
2571
2666
|
}
|
|
2572
|
-
}
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
* Scan .jai1/rule-preset/ directory for rule preset files
|
|
2604
|
-
*/
|
|
2605
|
-
async scanRulePreset() {
|
|
2606
|
-
const items = [];
|
|
2607
|
-
const presetDir = path3.join(this.jai1Path, "rule-preset");
|
|
2608
|
-
try {
|
|
2609
|
-
await fs6.access(presetDir);
|
|
2610
|
-
} catch {
|
|
2611
|
-
return items;
|
|
2667
|
+
},
|
|
2668
|
+
extensions: {
|
|
2669
|
+
name: "Extensions",
|
|
2670
|
+
description: "Disable auto-update extensions",
|
|
2671
|
+
settings: {
|
|
2672
|
+
"extensions.autoUpdate": false,
|
|
2673
|
+
"extensions.autoCheckUpdates": false,
|
|
2674
|
+
"update.mode": "manual",
|
|
2675
|
+
"extensions.ignoreRecommendations": true
|
|
2676
|
+
}
|
|
2677
|
+
},
|
|
2678
|
+
editorRendering: {
|
|
2679
|
+
name: "Editor Rendering",
|
|
2680
|
+
description: "Optimize rendering (minimap, whitespace, highlights...)",
|
|
2681
|
+
settings: {
|
|
2682
|
+
"editor.minimap.enabled": false,
|
|
2683
|
+
"editor.renderWhitespace": "none",
|
|
2684
|
+
"editor.renderControlCharacters": false,
|
|
2685
|
+
"editor.renderLineHighlight": "none",
|
|
2686
|
+
"editor.cursorBlinking": "solid",
|
|
2687
|
+
"editor.smoothScrolling": false,
|
|
2688
|
+
"editor.largeFileOptimizations": true,
|
|
2689
|
+
"editor.maxTokenizationLineLength": 2e3,
|
|
2690
|
+
"editor.quickSuggestions": {
|
|
2691
|
+
"other": true,
|
|
2692
|
+
"comments": false,
|
|
2693
|
+
"strings": false
|
|
2694
|
+
},
|
|
2695
|
+
"editor.suggest.showStatusBar": false,
|
|
2696
|
+
"editor.hover.delay": 500,
|
|
2697
|
+
"editor.matchBrackets": "never"
|
|
2612
2698
|
}
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
}
|
|
2626
|
-
items.push({
|
|
2627
|
-
type: "rules",
|
|
2628
|
-
name: path3.basename(file, ".mdc"),
|
|
2629
|
-
filepath,
|
|
2630
|
-
relativePath: path3.relative(this.projectPath, filepath),
|
|
2631
|
-
description: frontmatter.description,
|
|
2632
|
-
globs: this.extractGlobs(frontmatter),
|
|
2633
|
-
alwaysApply: this.extractAlwaysApply(frontmatter)
|
|
2634
|
-
});
|
|
2699
|
+
},
|
|
2700
|
+
uiElements: {
|
|
2701
|
+
name: "UI Elements",
|
|
2702
|
+
description: "Hide unnecessary UI elements",
|
|
2703
|
+
settings: {
|
|
2704
|
+
"workbench.activityBar.visible": false,
|
|
2705
|
+
"workbench.statusBar.visible": false,
|
|
2706
|
+
"breadcrumbs.enabled": false,
|
|
2707
|
+
"window.menuBarVisibility": "compact",
|
|
2708
|
+
"workbench.editor.showTabs": true,
|
|
2709
|
+
// Keep tabs for navigation
|
|
2710
|
+
"workbench.startupEditor": "none"
|
|
2635
2711
|
}
|
|
2636
|
-
return items;
|
|
2637
2712
|
}
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
async
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2713
|
+
};
|
|
2714
|
+
function createSetupSubcommand() {
|
|
2715
|
+
const setupCommand = new Command7("setup").description("Configure IDE settings and optimizations");
|
|
2716
|
+
setupCommand.action(async () => {
|
|
2717
|
+
await interactiveMode();
|
|
2718
|
+
});
|
|
2719
|
+
setupCommand.command("enable").description("Enable specific optimization groups").argument("[groups...]", "Group names to enable (telemetry, languageServers, git, ...)").action(async (groups) => {
|
|
2720
|
+
if (groups.length === 0) {
|
|
2721
|
+
await selectGroupsToApply("enable");
|
|
2722
|
+
} else {
|
|
2723
|
+
await applyGroups(groups, "enable");
|
|
2648
2724
|
}
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
const content = await fs6.readFile(filepath, "utf-8");
|
|
2656
|
-
const { data: frontmatter } = matter2(content);
|
|
2657
|
-
items.push({
|
|
2658
|
-
type,
|
|
2659
|
-
name: path3.basename(file, ".md"),
|
|
2660
|
-
filepath,
|
|
2661
|
-
relativePath: path3.relative(this.projectPath, filepath),
|
|
2662
|
-
description: frontmatter.description,
|
|
2663
|
-
globs: this.extractGlobs(frontmatter),
|
|
2664
|
-
alwaysApply: this.extractAlwaysApply(frontmatter)
|
|
2665
|
-
});
|
|
2725
|
+
});
|
|
2726
|
+
setupCommand.command("disable").description("Disable specific optimization groups (restore to default)").argument("[groups...]", "Group names to disable").action(async (groups) => {
|
|
2727
|
+
if (groups.length === 0) {
|
|
2728
|
+
await selectGroupsToApply("disable");
|
|
2729
|
+
} else {
|
|
2730
|
+
await applyGroups(groups, "disable");
|
|
2666
2731
|
}
|
|
2667
|
-
|
|
2732
|
+
});
|
|
2733
|
+
setupCommand.command("max-performance").description("Enable all optimizations for maximum performance").action(async () => {
|
|
2734
|
+
const allGroups = Object.keys(PERFORMANCE_GROUPS);
|
|
2735
|
+
await applyGroups(allGroups, "enable");
|
|
2736
|
+
});
|
|
2737
|
+
setupCommand.command("reset").description("Reset to default settings (remove all optimizations)").argument("[groups...]", "Group names to reset (empty = reset all)").action(async (groups) => {
|
|
2738
|
+
await resetSettings(groups);
|
|
2739
|
+
});
|
|
2740
|
+
setupCommand.command("list").description("List all available optimization groups").action(() => {
|
|
2741
|
+
console.log("\u{1F527} Available optimization groups:\n");
|
|
2742
|
+
Object.entries(PERFORMANCE_GROUPS).forEach(([key, group]) => {
|
|
2743
|
+
console.log(` ${key.padEnd(20)} - ${group.name}`);
|
|
2744
|
+
console.log(` ${" ".repeat(20)} ${group.description}`);
|
|
2745
|
+
console.log();
|
|
2746
|
+
});
|
|
2747
|
+
});
|
|
2748
|
+
return setupCommand;
|
|
2749
|
+
}
|
|
2750
|
+
async function interactiveMode() {
|
|
2751
|
+
console.log("\u{1F527} IDE Settings Manager\n");
|
|
2752
|
+
console.log("\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E");
|
|
2753
|
+
console.log("\u2502 \u{1F4CC} Usage: \u2502");
|
|
2754
|
+
console.log("\u2502 \u2022 Use \u2191\u2193 keys to navigate \u2502");
|
|
2755
|
+
console.log("\u2502 \u2022 Press SPACE to select/deselect group \u2502");
|
|
2756
|
+
console.log("\u2502 \u2022 Press ENTER to confirm and apply \u2502");
|
|
2757
|
+
console.log("\u2502 \u2022 Press Ctrl+C to cancel \u2502");
|
|
2758
|
+
console.log("\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F\n");
|
|
2759
|
+
const action = await select({
|
|
2760
|
+
message: "What do you want to do?",
|
|
2761
|
+
choices: [
|
|
2762
|
+
{ name: "\u2705 Enable optimization groups", value: "enable" },
|
|
2763
|
+
{ name: "\u274C Disable optimization groups", value: "disable" },
|
|
2764
|
+
{ name: "\u{1F680} Max Performance (enable all)", value: "max" },
|
|
2765
|
+
{ name: "\u{1F504} Reset to defaults", value: "reset" }
|
|
2766
|
+
]
|
|
2767
|
+
});
|
|
2768
|
+
if (action === "max") {
|
|
2769
|
+
const allGroups = Object.keys(PERFORMANCE_GROUPS);
|
|
2770
|
+
await applyGroups(allGroups, "enable");
|
|
2771
|
+
} else if (action === "reset") {
|
|
2772
|
+
await resetSettings([]);
|
|
2773
|
+
} else {
|
|
2774
|
+
await selectGroupsToApply(action);
|
|
2668
2775
|
}
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
sourceFile: sourceItem.relativePath
|
|
2776
|
+
}
|
|
2777
|
+
async function selectGroupsToApply(action) {
|
|
2778
|
+
const choices = Object.entries(PERFORMANCE_GROUPS).map(([key, group]) => ({
|
|
2779
|
+
name: `${group.name} - ${group.description}`,
|
|
2780
|
+
value: key
|
|
2781
|
+
}));
|
|
2782
|
+
try {
|
|
2783
|
+
const selectedGroups = await checkbox({
|
|
2784
|
+
message: `Select groups to ${action} (SPACE to select, ENTER to confirm):`,
|
|
2785
|
+
choices
|
|
2680
2786
|
});
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
${reference}
|
|
2686
|
-
`;
|
|
2787
|
+
if (selectedGroups.length === 0) {
|
|
2788
|
+
console.log("\n\u26A0\uFE0F No groups selected!");
|
|
2789
|
+
console.log(" \u{1F4A1} Tip: Press SPACE to select at least 1 group before pressing ENTER.");
|
|
2790
|
+
return;
|
|
2687
2791
|
}
|
|
2688
|
-
|
|
2689
|
-
|
|
2792
|
+
await applyGroups(selectedGroups, action);
|
|
2793
|
+
} catch {
|
|
2794
|
+
console.log("\n\u274C Operation cancelled.");
|
|
2690
2795
|
}
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
for (const item of items) {
|
|
2702
|
-
const result = await this.migrateItem(ide, config, item);
|
|
2703
|
-
results.push(result);
|
|
2704
|
-
onProgress?.(result);
|
|
2705
|
-
}
|
|
2706
|
-
}
|
|
2707
|
-
}
|
|
2708
|
-
return results;
|
|
2796
|
+
}
|
|
2797
|
+
async function applyGroups(groupKeys, action) {
|
|
2798
|
+
const vscodeDir = path3.join(process.cwd(), ".vscode");
|
|
2799
|
+
const settingsPath = path3.join(vscodeDir, "settings.json");
|
|
2800
|
+
const invalidGroups = groupKeys.filter((key) => !PERFORMANCE_GROUPS[key]);
|
|
2801
|
+
if (invalidGroups.length > 0) {
|
|
2802
|
+
console.log(`
|
|
2803
|
+
\u274C Invalid groups: ${invalidGroups.join(", ")}`);
|
|
2804
|
+
console.log(' \u{1F4A1} Run "jai1 ide setup list" to see available groups.');
|
|
2805
|
+
return;
|
|
2709
2806
|
}
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2807
|
+
if (!existsSync(vscodeDir)) {
|
|
2808
|
+
await fs6.mkdir(vscodeDir, { recursive: true });
|
|
2809
|
+
console.log("\u{1F4C1} Created .vscode/ directory");
|
|
2810
|
+
}
|
|
2811
|
+
let currentSettings = {};
|
|
2812
|
+
if (existsSync(settingsPath)) {
|
|
2714
2813
|
try {
|
|
2715
|
-
const
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
let status = "created";
|
|
2728
|
-
try {
|
|
2729
|
-
await fs6.access(targetPath);
|
|
2730
|
-
status = "updated";
|
|
2731
|
-
} catch {
|
|
2814
|
+
const content = await fs6.readFile(settingsPath, "utf-8");
|
|
2815
|
+
currentSettings = JSON.parse(content);
|
|
2816
|
+
console.log("\u{1F4C4} Read current settings from settings.json");
|
|
2817
|
+
} catch {
|
|
2818
|
+
console.warn("\u26A0\uFE0F Cannot read settings.json (may contain comments).");
|
|
2819
|
+
const confirmOverwrite = await confirm2({
|
|
2820
|
+
message: "Overwrite current settings.json file?",
|
|
2821
|
+
default: false
|
|
2822
|
+
});
|
|
2823
|
+
if (!confirmOverwrite) {
|
|
2824
|
+
console.log("\u274C Operation cancelled.");
|
|
2825
|
+
return;
|
|
2732
2826
|
}
|
|
2733
|
-
|
|
2734
|
-
await fs6.writeFile(targetPath, stubContent, "utf-8");
|
|
2735
|
-
return {
|
|
2736
|
-
source: item,
|
|
2737
|
-
targetIDE: ide,
|
|
2738
|
-
targetPath,
|
|
2739
|
-
status
|
|
2740
|
-
};
|
|
2741
|
-
} catch (error) {
|
|
2742
|
-
return {
|
|
2743
|
-
source: item,
|
|
2744
|
-
targetIDE: ide,
|
|
2745
|
-
targetPath: "",
|
|
2746
|
-
status: "error",
|
|
2747
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
2748
|
-
};
|
|
2827
|
+
currentSettings = {};
|
|
2749
2828
|
}
|
|
2750
2829
|
}
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2830
|
+
const newSettings = { ...currentSettings };
|
|
2831
|
+
console.log(`
|
|
2832
|
+
\u{1F4DD} ${action === "enable" ? "Enabling" : "Disabling"} groups:
|
|
2833
|
+
`);
|
|
2834
|
+
for (const key of groupKeys) {
|
|
2835
|
+
const group = PERFORMANCE_GROUPS[key];
|
|
2836
|
+
console.log(` ${action === "enable" ? "\u2713" : "\u2717"} ${group.name}`);
|
|
2837
|
+
if (action === "enable") {
|
|
2838
|
+
for (const [settingKey, settingValue] of Object.entries(group.settings)) {
|
|
2839
|
+
if (typeof settingValue === "object" && settingValue !== null && !Array.isArray(settingValue) && typeof newSettings[settingKey] === "object" && newSettings[settingKey] !== null) {
|
|
2840
|
+
newSettings[settingKey] = {
|
|
2841
|
+
...newSettings[settingKey],
|
|
2842
|
+
...settingValue
|
|
2843
|
+
};
|
|
2844
|
+
} else {
|
|
2845
|
+
newSettings[settingKey] = settingValue;
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
} else {
|
|
2849
|
+
for (const settingKey of Object.keys(group.settings)) {
|
|
2850
|
+
delete newSettings[settingKey];
|
|
2851
|
+
}
|
|
2769
2852
|
}
|
|
2770
|
-
const filename = `${item.name}${config.fileExtension}`;
|
|
2771
|
-
return path3.join(this.projectPath, config.basePath, contentPath, filename);
|
|
2772
2853
|
}
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2854
|
+
await fs6.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
|
|
2855
|
+
console.log(`
|
|
2856
|
+
\u2705 Updated IDE settings at: ${settingsPath}`);
|
|
2857
|
+
console.log("\u{1F4A1} Tip: Restart your IDE to apply changes.");
|
|
2858
|
+
}
|
|
2859
|
+
async function resetSettings(groupKeys) {
|
|
2860
|
+
const vscodeDir = path3.join(process.cwd(), ".vscode");
|
|
2861
|
+
const settingsPath = path3.join(vscodeDir, "settings.json");
|
|
2862
|
+
if (!existsSync(settingsPath)) {
|
|
2863
|
+
console.log("\n\u26A0\uFE0F No settings.json file found");
|
|
2864
|
+
return;
|
|
2779
2865
|
}
|
|
2780
|
-
|
|
2781
|
-
|
|
2866
|
+
const confirmReset = await confirm2({
|
|
2867
|
+
message: groupKeys.length === 0 ? "Reset ALL settings to default (delete entire file)?" : `Reset groups: ${groupKeys.join(", ")}?`,
|
|
2868
|
+
default: false
|
|
2869
|
+
});
|
|
2870
|
+
if (!confirmReset) {
|
|
2871
|
+
console.log("\u274C Operation cancelled.");
|
|
2872
|
+
return;
|
|
2782
2873
|
}
|
|
2783
|
-
|
|
2874
|
+
if (groupKeys.length === 0) {
|
|
2875
|
+
await fs6.unlink(settingsPath);
|
|
2876
|
+
console.log("\n\u2705 Deleted settings.json file");
|
|
2877
|
+
} else {
|
|
2878
|
+
await applyGroups(groupKeys, "disable");
|
|
2879
|
+
}
|
|
2880
|
+
console.log("\u{1F4A1} Tip: Restart your IDE to apply changes.");
|
|
2881
|
+
}
|
|
2784
2882
|
|
|
2785
2883
|
// src/commands/ide/sync.ts
|
|
2884
|
+
import { Command as Command8 } from "commander";
|
|
2885
|
+
import { checkbox as checkbox2, confirm as confirm3 } from "@inquirer/prompts";
|
|
2786
2886
|
function createSyncSubcommand() {
|
|
2787
2887
|
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) => {
|
|
2788
2888
|
await runSync(options);
|
|
@@ -5414,11 +5514,266 @@ function createTranslateCommand() {
|
|
|
5414
5514
|
return cmd;
|
|
5415
5515
|
}
|
|
5416
5516
|
|
|
5517
|
+
// src/commands/image/index.ts
|
|
5518
|
+
import { Command as Command20 } from "commander";
|
|
5519
|
+
|
|
5520
|
+
// src/commands/image/gen.ts
|
|
5521
|
+
import { Command as Command16 } from "commander";
|
|
5522
|
+
|
|
5523
|
+
// src/services/image.service.ts
|
|
5524
|
+
var ImageService = class {
|
|
5525
|
+
/**
|
|
5526
|
+
* Generate image from prompt
|
|
5527
|
+
*/
|
|
5528
|
+
async generate(config, options) {
|
|
5529
|
+
const response = await fetch(`${config.apiUrl}/api/image/generate`, {
|
|
5530
|
+
method: "POST",
|
|
5531
|
+
headers: {
|
|
5532
|
+
"Content-Type": "application/json",
|
|
5533
|
+
"JAI1-Access-Key": config.accessKey
|
|
5534
|
+
},
|
|
5535
|
+
body: JSON.stringify(options)
|
|
5536
|
+
});
|
|
5537
|
+
if (!response.ok) {
|
|
5538
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
5539
|
+
try {
|
|
5540
|
+
const error = await response.json();
|
|
5541
|
+
errorMessage = error.message || errorMessage;
|
|
5542
|
+
} catch (e) {
|
|
5543
|
+
}
|
|
5544
|
+
throw new NetworkError(errorMessage);
|
|
5545
|
+
}
|
|
5546
|
+
const data = await response.json();
|
|
5547
|
+
return data.data;
|
|
5548
|
+
}
|
|
5549
|
+
/**
|
|
5550
|
+
* List generated images
|
|
5551
|
+
*/
|
|
5552
|
+
async list(config, options) {
|
|
5553
|
+
const params = new URLSearchParams();
|
|
5554
|
+
if (options.model) params.set("model", options.model);
|
|
5555
|
+
if (options.limit) params.set("limit", options.limit.toString());
|
|
5556
|
+
if (options.offset) params.set("offset", options.offset.toString());
|
|
5557
|
+
const url = `${config.apiUrl}/api/image/list?${params}`;
|
|
5558
|
+
const response = await fetch(url, {
|
|
5559
|
+
headers: {
|
|
5560
|
+
"JAI1-Access-Key": config.accessKey
|
|
5561
|
+
}
|
|
5562
|
+
});
|
|
5563
|
+
if (!response.ok) {
|
|
5564
|
+
throw new NetworkError(`Failed to list images: HTTP ${response.status}`);
|
|
5565
|
+
}
|
|
5566
|
+
const data = await response.json();
|
|
5567
|
+
return data.data;
|
|
5568
|
+
}
|
|
5569
|
+
/**
|
|
5570
|
+
* Get image info
|
|
5571
|
+
*/
|
|
5572
|
+
async getInfo(config, id) {
|
|
5573
|
+
const response = await fetch(`${config.apiUrl}/api/image/${id}`, {
|
|
5574
|
+
headers: {
|
|
5575
|
+
"JAI1-Access-Key": config.accessKey
|
|
5576
|
+
}
|
|
5577
|
+
});
|
|
5578
|
+
if (!response.ok) {
|
|
5579
|
+
if (response.status === 404) {
|
|
5580
|
+
throw new Error("Image not found");
|
|
5581
|
+
}
|
|
5582
|
+
throw new NetworkError(`Failed to get image: HTTP ${response.status}`);
|
|
5583
|
+
}
|
|
5584
|
+
const data = await response.json();
|
|
5585
|
+
return data.data;
|
|
5586
|
+
}
|
|
5587
|
+
/**
|
|
5588
|
+
* Delete image
|
|
5589
|
+
*/
|
|
5590
|
+
async delete(config, id) {
|
|
5591
|
+
const response = await fetch(`${config.apiUrl}/api/image/${id}`, {
|
|
5592
|
+
method: "DELETE",
|
|
5593
|
+
headers: {
|
|
5594
|
+
"JAI1-Access-Key": config.accessKey
|
|
5595
|
+
}
|
|
5596
|
+
});
|
|
5597
|
+
if (!response.ok) {
|
|
5598
|
+
if (response.status === 404) {
|
|
5599
|
+
throw new Error("Image not found");
|
|
5600
|
+
}
|
|
5601
|
+
throw new NetworkError(`Failed to delete image: HTTP ${response.status}`);
|
|
5602
|
+
}
|
|
5603
|
+
}
|
|
5604
|
+
};
|
|
5605
|
+
|
|
5606
|
+
// src/commands/image/gen.ts
|
|
5607
|
+
import terminalImage from "terminal-image";
|
|
5608
|
+
function createImageGenCommand() {
|
|
5609
|
+
return new Command16("gen").description("Generate an image from text prompt").argument("<prompt>", "Image description prompt").option("-m, --model <model>", "Model to use", "gemini-3-flash-preview").option("-w, --width <number>", "Image width").option("-h, --height <number>", "Image height").option("-r, --ratio <ratio>", "Aspect ratio (1:1, 16:9, 9:16, 4:3, 3:4)").option("--negative <prompt>", "Negative prompt").option("--seed <number>", "Random seed").option("--no-display", "Don't display image in terminal").action(async (prompt, options) => {
|
|
5610
|
+
const configService = new ConfigService();
|
|
5611
|
+
const config = await configService.load();
|
|
5612
|
+
if (!config) {
|
|
5613
|
+
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
5614
|
+
}
|
|
5615
|
+
const imageService = new ImageService();
|
|
5616
|
+
console.log("\u{1F3A8} Generating image...");
|
|
5617
|
+
console.log(` Prompt: ${prompt}`);
|
|
5618
|
+
console.log(` Model: ${options.model}`);
|
|
5619
|
+
try {
|
|
5620
|
+
const result = await imageService.generate(config, {
|
|
5621
|
+
prompt,
|
|
5622
|
+
model: options.model,
|
|
5623
|
+
...options.width && { width: parseInt(options.width) },
|
|
5624
|
+
...options.height && { height: parseInt(options.height) },
|
|
5625
|
+
...options.ratio && { aspectRatio: options.ratio },
|
|
5626
|
+
...options.negative && { negativePrompt: options.negative },
|
|
5627
|
+
...options.seed && { seed: parseInt(options.seed) }
|
|
5628
|
+
});
|
|
5629
|
+
console.log("\n\u2705 Image generated successfully!");
|
|
5630
|
+
console.log(` URL: ${result.url}`);
|
|
5631
|
+
console.log(` Size: ${(result.contentSize / 1024).toFixed(2)} KB`);
|
|
5632
|
+
console.log(` ID: ${result.id}`);
|
|
5633
|
+
if (options.display !== false) {
|
|
5634
|
+
try {
|
|
5635
|
+
const imageResponse = await fetch(result.url);
|
|
5636
|
+
const arrayBuffer = await imageResponse.arrayBuffer();
|
|
5637
|
+
const imageBuffer = Buffer.from(arrayBuffer);
|
|
5638
|
+
const rendered = await terminalImage.buffer(imageBuffer, {
|
|
5639
|
+
width: "50%",
|
|
5640
|
+
height: "50%"
|
|
5641
|
+
});
|
|
5642
|
+
console.log("\n" + rendered);
|
|
5643
|
+
} catch (displayError) {
|
|
5644
|
+
}
|
|
5645
|
+
}
|
|
5646
|
+
console.log('\n\u{1F4A1} Tip: Use "jai1 image list" to view all your images');
|
|
5647
|
+
console.log(' Use "jai1 image info <id>" for details');
|
|
5648
|
+
} catch (error) {
|
|
5649
|
+
if (error instanceof Error) {
|
|
5650
|
+
if (error.message.includes("Rate limit")) {
|
|
5651
|
+
console.error("\n\u26A0\uFE0F Rate limit exceeded");
|
|
5652
|
+
console.error(' Use "jai1 image limits" to check your quota');
|
|
5653
|
+
} else {
|
|
5654
|
+
throw error;
|
|
5655
|
+
}
|
|
5656
|
+
}
|
|
5657
|
+
throw error;
|
|
5658
|
+
}
|
|
5659
|
+
});
|
|
5660
|
+
}
|
|
5661
|
+
|
|
5662
|
+
// src/commands/image/list.ts
|
|
5663
|
+
import { Command as Command17 } from "commander";
|
|
5664
|
+
function createImageListCommand() {
|
|
5665
|
+
return new Command17("list").description("List generated images").option("-m, --model <model>", "Filter by model").option("-l, --limit <number>", "Number of results", "20").option("-o, --offset <number>", "Offset for pagination", "0").action(async (options) => {
|
|
5666
|
+
const configService = new ConfigService();
|
|
5667
|
+
const config = await configService.load();
|
|
5668
|
+
if (!config) {
|
|
5669
|
+
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
5670
|
+
}
|
|
5671
|
+
const imageService = new ImageService();
|
|
5672
|
+
const result = await imageService.list(config, {
|
|
5673
|
+
model: options.model,
|
|
5674
|
+
limit: parseInt(options.limit),
|
|
5675
|
+
offset: parseInt(options.offset)
|
|
5676
|
+
});
|
|
5677
|
+
if (result.images.length === 0) {
|
|
5678
|
+
console.log("No images found.");
|
|
5679
|
+
return;
|
|
5680
|
+
}
|
|
5681
|
+
console.log(`
|
|
5682
|
+
\u{1F4F8} Your Generated Images (${result.total} total):
|
|
5683
|
+
`);
|
|
5684
|
+
for (const img of result.images) {
|
|
5685
|
+
const date = new Date(img.createdAt).toLocaleString();
|
|
5686
|
+
const promptPreview = img.prompt.length > 50 ? img.prompt.slice(0, 50) + "..." : img.prompt;
|
|
5687
|
+
console.log(`ID: ${img.id.toString().padEnd(6)} | ${date}`);
|
|
5688
|
+
console.log(` Model: ${img.model}`);
|
|
5689
|
+
console.log(` Prompt: ${promptPreview}`);
|
|
5690
|
+
console.log(` URL: ${img.url}`);
|
|
5691
|
+
console.log();
|
|
5692
|
+
}
|
|
5693
|
+
if (result.total > result.offset + result.limit) {
|
|
5694
|
+
const nextOffset = result.offset + result.limit;
|
|
5695
|
+
console.log(`\u{1F4A1} Show more: jai1 image list -o ${nextOffset}`);
|
|
5696
|
+
}
|
|
5697
|
+
});
|
|
5698
|
+
}
|
|
5699
|
+
|
|
5700
|
+
// src/commands/image/info.ts
|
|
5701
|
+
import { Command as Command18 } from "commander";
|
|
5702
|
+
function createImageInfoCommand() {
|
|
5703
|
+
return new Command18("info").description("Get detailed info about an image").argument("<id>", "Image ID").action(async (id) => {
|
|
5704
|
+
const configService = new ConfigService();
|
|
5705
|
+
const config = await configService.load();
|
|
5706
|
+
if (!config) {
|
|
5707
|
+
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
5708
|
+
}
|
|
5709
|
+
const imageService = new ImageService();
|
|
5710
|
+
const image = await imageService.getInfo(config, parseInt(id));
|
|
5711
|
+
console.log("\n\u{1F4F8} Image Details:\n");
|
|
5712
|
+
console.log(`ID: ${image.id}`);
|
|
5713
|
+
console.log(`Model: ${image.model}`);
|
|
5714
|
+
console.log(`Prompt: ${image.prompt}`);
|
|
5715
|
+
console.log(`URL: ${image.url}`);
|
|
5716
|
+
console.log(`Size: ${(image.contentSize / 1024).toFixed(2)} KB`);
|
|
5717
|
+
if (image.width && image.height) {
|
|
5718
|
+
console.log(`Dimensions: ${image.width}x${image.height}`);
|
|
5719
|
+
}
|
|
5720
|
+
console.log(`Created: ${new Date(image.createdAt).toLocaleString()}`);
|
|
5721
|
+
if (image.metadata) {
|
|
5722
|
+
const meta = typeof image.metadata === "string" ? JSON.parse(image.metadata) : image.metadata;
|
|
5723
|
+
if (meta.aspectRatio) {
|
|
5724
|
+
console.log(`Ratio: ${meta.aspectRatio}`);
|
|
5725
|
+
}
|
|
5726
|
+
if (meta.negativePrompt) {
|
|
5727
|
+
console.log(`Negative: ${meta.negativePrompt}`);
|
|
5728
|
+
}
|
|
5729
|
+
if (meta.seed) {
|
|
5730
|
+
console.log(`Seed: ${meta.seed}`);
|
|
5731
|
+
}
|
|
5732
|
+
}
|
|
5733
|
+
});
|
|
5734
|
+
}
|
|
5735
|
+
|
|
5736
|
+
// src/commands/image/delete.ts
|
|
5737
|
+
import { Command as Command19 } from "commander";
|
|
5738
|
+
import { confirm as confirm4 } from "@inquirer/prompts";
|
|
5739
|
+
function createImageDeleteCommand() {
|
|
5740
|
+
return new Command19("delete").description("Delete a generated image").argument("<id>", "Image ID").option("-y, --yes", "Skip confirmation").action(async (id, options) => {
|
|
5741
|
+
const configService = new ConfigService();
|
|
5742
|
+
const config = await configService.load();
|
|
5743
|
+
if (!config) {
|
|
5744
|
+
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
5745
|
+
}
|
|
5746
|
+
if (!options.yes) {
|
|
5747
|
+
const confirmed = await confirm4({
|
|
5748
|
+
message: `Are you sure you want to delete image #${id}?`,
|
|
5749
|
+
default: false
|
|
5750
|
+
});
|
|
5751
|
+
if (!confirmed) {
|
|
5752
|
+
console.log("Cancelled.");
|
|
5753
|
+
return;
|
|
5754
|
+
}
|
|
5755
|
+
}
|
|
5756
|
+
const imageService = new ImageService();
|
|
5757
|
+
await imageService.delete(config, parseInt(id));
|
|
5758
|
+
console.log(`\u2705 Image #${id} deleted successfully.`);
|
|
5759
|
+
});
|
|
5760
|
+
}
|
|
5761
|
+
|
|
5762
|
+
// src/commands/image/index.ts
|
|
5763
|
+
function createImageCommand() {
|
|
5764
|
+
const cmd = new Command20("image").description("Image generation commands (Coming Soon)");
|
|
5765
|
+
cmd.addCommand(createImageGenCommand());
|
|
5766
|
+
cmd.addCommand(createImageListCommand());
|
|
5767
|
+
cmd.addCommand(createImageInfoCommand());
|
|
5768
|
+
cmd.addCommand(createImageDeleteCommand());
|
|
5769
|
+
return cmd;
|
|
5770
|
+
}
|
|
5771
|
+
|
|
5417
5772
|
// src/commands/utils/index.ts
|
|
5418
|
-
import { Command as
|
|
5773
|
+
import { Command as Command34 } from "commander";
|
|
5419
5774
|
|
|
5420
5775
|
// src/commands/utils/password.ts
|
|
5421
|
-
import { Command as
|
|
5776
|
+
import { Command as Command21 } from "commander";
|
|
5422
5777
|
|
|
5423
5778
|
// src/services/utils.service.ts
|
|
5424
5779
|
import crypto from "crypto";
|
|
@@ -5687,7 +6042,7 @@ async function handlePasswordGeneration(options) {
|
|
|
5687
6042
|
}
|
|
5688
6043
|
}
|
|
5689
6044
|
function createPasswordCommand() {
|
|
5690
|
-
const cmd = new
|
|
6045
|
+
const cmd = new Command21("password").description("Generate secure random password").option("-l, --length <number>", "Password length", "16").option("--no-lowercase", "Exclude lowercase letters").option("--no-uppercase", "Exclude uppercase letters").option("--no-digits", "Exclude digits").option("--no-symbols", "Exclude symbols").option("--symbol-chars <chars>", "Custom symbol characters").option("-c, --count <number>", "Number of passwords to generate", "1").addHelpText("after", `
|
|
5691
6046
|
Examples:
|
|
5692
6047
|
$ jai1 utils password
|
|
5693
6048
|
$ jai1 utils password --length 24
|
|
@@ -5705,7 +6060,7 @@ Examples:
|
|
|
5705
6060
|
}
|
|
5706
6061
|
|
|
5707
6062
|
// src/commands/utils/uuid.ts
|
|
5708
|
-
import { Command as
|
|
6063
|
+
import { Command as Command22 } from "commander";
|
|
5709
6064
|
async function handleUuidGeneration(options) {
|
|
5710
6065
|
const service = new UtilsService();
|
|
5711
6066
|
try {
|
|
@@ -5733,7 +6088,7 @@ async function handleUuidGeneration(options) {
|
|
|
5733
6088
|
}
|
|
5734
6089
|
}
|
|
5735
6090
|
function createUuidCommand() {
|
|
5736
|
-
const cmd = new
|
|
6091
|
+
const cmd = new Command22("uuid").description("Generate UUID v4 identifier").option("-c, --count <number>", "Number of UUIDs to generate", "1").option("--uppercase", "Output in uppercase").option("--no-hyphens", "Remove hyphens from output").addHelpText("after", `
|
|
5737
6092
|
Examples:
|
|
5738
6093
|
$ jai1 utils uuid
|
|
5739
6094
|
$ jai1 utils uuid --count 10
|
|
@@ -5750,7 +6105,7 @@ Examples:
|
|
|
5750
6105
|
}
|
|
5751
6106
|
|
|
5752
6107
|
// src/commands/utils/hash.ts
|
|
5753
|
-
import { Command as
|
|
6108
|
+
import { Command as Command23 } from "commander";
|
|
5754
6109
|
async function handleHashGeneration(input4, options) {
|
|
5755
6110
|
const service = new UtilsService();
|
|
5756
6111
|
try {
|
|
@@ -5790,7 +6145,7 @@ async function handleHashGeneration(input4, options) {
|
|
|
5790
6145
|
}
|
|
5791
6146
|
}
|
|
5792
6147
|
function createHashCommand() {
|
|
5793
|
-
const cmd = new
|
|
6148
|
+
const cmd = new Command23("hash").description("Generate hash (MD5, SHA, bcrypt)").argument("[input]", "Text to hash").option(
|
|
5794
6149
|
"-a, --algorithm <algorithm>",
|
|
5795
6150
|
"Hash algorithm (md5, sha1, sha256, sha512, bcrypt)",
|
|
5796
6151
|
"sha256"
|
|
@@ -5812,7 +6167,7 @@ Examples:
|
|
|
5812
6167
|
}
|
|
5813
6168
|
|
|
5814
6169
|
// src/commands/utils/base64-encode.ts
|
|
5815
|
-
import { Command as
|
|
6170
|
+
import { Command as Command24 } from "commander";
|
|
5816
6171
|
import { readFile as readFile2 } from "fs/promises";
|
|
5817
6172
|
async function handleBase64Encode(input4, options) {
|
|
5818
6173
|
const service = new UtilsService();
|
|
@@ -5845,7 +6200,7 @@ async function handleBase64Encode(input4, options) {
|
|
|
5845
6200
|
}
|
|
5846
6201
|
}
|
|
5847
6202
|
function createBase64EncodeCommand() {
|
|
5848
|
-
const cmd = new
|
|
6203
|
+
const cmd = new Command24("base64-encode").description("Encode text or file to Base64").argument("[input]", "Text to encode").option("-f, --file <path>", "Encode file contents").option("--url-safe", "Use URL-safe Base64 encoding").addHelpText("after", `
|
|
5849
6204
|
Examples:
|
|
5850
6205
|
$ jai1 utils base64-encode "hello world"
|
|
5851
6206
|
$ jai1 utils base64-encode "hello world" --url-safe
|
|
@@ -5858,7 +6213,7 @@ Examples:
|
|
|
5858
6213
|
}
|
|
5859
6214
|
|
|
5860
6215
|
// src/commands/utils/base64-decode.ts
|
|
5861
|
-
import { Command as
|
|
6216
|
+
import { Command as Command25 } from "commander";
|
|
5862
6217
|
import { readFile as readFile3, writeFile } from "fs/promises";
|
|
5863
6218
|
async function handleBase64Decode(input4, options) {
|
|
5864
6219
|
const service = new UtilsService();
|
|
@@ -5890,7 +6245,7 @@ async function handleBase64Decode(input4, options) {
|
|
|
5890
6245
|
}
|
|
5891
6246
|
}
|
|
5892
6247
|
function createBase64DecodeCommand() {
|
|
5893
|
-
const cmd = new
|
|
6248
|
+
const cmd = new Command25("base64-decode").description("Decode Base64 string").argument("[input]", "Base64 string to decode").option("-f, --file <path>", "Decode from file").option("-o, --output <path>", "Write decoded output to file").addHelpText("after", `
|
|
5894
6249
|
Examples:
|
|
5895
6250
|
$ jai1 utils base64-decode "aGVsbG8gd29ybGQ="
|
|
5896
6251
|
$ jai1 utils base64-decode --file ./encoded.txt
|
|
@@ -5903,7 +6258,7 @@ Examples:
|
|
|
5903
6258
|
}
|
|
5904
6259
|
|
|
5905
6260
|
// src/commands/utils/http.ts
|
|
5906
|
-
import { Command as
|
|
6261
|
+
import { Command as Command26 } from "commander";
|
|
5907
6262
|
import { readFile as readFile4 } from "fs/promises";
|
|
5908
6263
|
async function handleHttpRequest(url, options) {
|
|
5909
6264
|
const service = new UtilsService();
|
|
@@ -5969,7 +6324,7 @@ async function handleHttpRequest(url, options) {
|
|
|
5969
6324
|
}
|
|
5970
6325
|
}
|
|
5971
6326
|
function createHttpCommand() {
|
|
5972
|
-
const cmd = new
|
|
6327
|
+
const cmd = new Command26("http").description("Make HTTP request with formatted output").argument("<url>", "Target URL").option("-X, --method <method>", "HTTP method", "GET").option("-H, --header <header...>", "Request headers (repeatable)").option("-d, --data <data>", "Request body (JSON string)").option("--file <path>", "Read body from file").option("--timeout <ms>", "Request timeout in milliseconds", "30000").option("--no-follow", "Do not follow redirects").option("--only-headers", "Show only response headers").option("--only-body", "Show only response body").addHelpText("after", `
|
|
5973
6328
|
Examples:
|
|
5974
6329
|
$ jai1 utils http https://api.example.com/users
|
|
5975
6330
|
$ jai1 utils http https://api.example.com/users -X POST -d '{"name":"John"}'
|
|
@@ -5987,7 +6342,7 @@ Examples:
|
|
|
5987
6342
|
}
|
|
5988
6343
|
|
|
5989
6344
|
// src/commands/utils/jwt.ts
|
|
5990
|
-
import { Command as
|
|
6345
|
+
import { Command as Command27 } from "commander";
|
|
5991
6346
|
async function handleJwtDecode(token) {
|
|
5992
6347
|
const service = new UtilsService();
|
|
5993
6348
|
try {
|
|
@@ -6026,7 +6381,7 @@ async function handleJwtEncode(options) {
|
|
|
6026
6381
|
}
|
|
6027
6382
|
}
|
|
6028
6383
|
function createJwtCommand() {
|
|
6029
|
-
const jwtCommand = new
|
|
6384
|
+
const jwtCommand = new Command27("jwt").description("Decode and encode JWT tokens");
|
|
6030
6385
|
jwtCommand.command("decode").description("Decode JWT token").argument("<token>", "JWT token to decode").addHelpText("after", `
|
|
6031
6386
|
Examples:
|
|
6032
6387
|
$ jai1 utils jwt decode "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
|
|
@@ -6047,7 +6402,7 @@ Examples:
|
|
|
6047
6402
|
}
|
|
6048
6403
|
|
|
6049
6404
|
// src/commands/utils/unix-time.ts
|
|
6050
|
-
import { Command as
|
|
6405
|
+
import { Command as Command28 } from "commander";
|
|
6051
6406
|
async function handleUnixTime(input4, options) {
|
|
6052
6407
|
const service = new UtilsService();
|
|
6053
6408
|
try {
|
|
@@ -6095,7 +6450,7 @@ async function handleUnixTime(input4, options) {
|
|
|
6095
6450
|
}
|
|
6096
6451
|
}
|
|
6097
6452
|
function createUnixTimeCommand() {
|
|
6098
|
-
const cmd = new
|
|
6453
|
+
const cmd = new Command28("unix-time").description("Convert unix timestamps to/from dates").argument("[input]", "Unix timestamp or date string").option("-f, --format <format>", "Output format (iso, local, utc)", "iso").option("--ms", "Use milliseconds instead of seconds").addHelpText("after", `
|
|
6099
6454
|
Examples:
|
|
6100
6455
|
$ jai1 utils unix-time
|
|
6101
6456
|
$ jai1 utils unix-time 1702550400
|
|
@@ -6110,7 +6465,7 @@ Examples:
|
|
|
6110
6465
|
}
|
|
6111
6466
|
|
|
6112
6467
|
// src/commands/utils/timezone.ts
|
|
6113
|
-
import { Command as
|
|
6468
|
+
import { Command as Command29 } from "commander";
|
|
6114
6469
|
async function handleTimezoneConversion(time, options) {
|
|
6115
6470
|
const service = new UtilsService();
|
|
6116
6471
|
try {
|
|
@@ -6147,7 +6502,7 @@ async function handleTimezoneConversion(time, options) {
|
|
|
6147
6502
|
}
|
|
6148
6503
|
}
|
|
6149
6504
|
function createTimezoneCommand() {
|
|
6150
|
-
const cmd = new
|
|
6505
|
+
const cmd = new Command29("timezone").description("Convert time between timezones").argument("[time]", 'Time to convert (e.g., "2024-01-15 10:00")').option("--from <timezone>", "Source timezone").option("--to <timezone>", "Target timezone").option("--list", "Show available timezones").addHelpText("after", `
|
|
6151
6506
|
Examples:
|
|
6152
6507
|
$ jai1 utils timezone --list
|
|
6153
6508
|
$ jai1 utils timezone "2024-01-15 10:00" --from "America/New_York" --to "Asia/Tokyo"
|
|
@@ -6160,7 +6515,7 @@ Examples:
|
|
|
6160
6515
|
}
|
|
6161
6516
|
|
|
6162
6517
|
// src/commands/utils/url-encode.ts
|
|
6163
|
-
import { Command as
|
|
6518
|
+
import { Command as Command30 } from "commander";
|
|
6164
6519
|
async function handleUrlEncode(input4, options) {
|
|
6165
6520
|
const service = new UtilsService();
|
|
6166
6521
|
try {
|
|
@@ -6181,7 +6536,7 @@ async function handleUrlEncode(input4, options) {
|
|
|
6181
6536
|
}
|
|
6182
6537
|
}
|
|
6183
6538
|
function createUrlEncodeCommand() {
|
|
6184
|
-
const cmd = new
|
|
6539
|
+
const cmd = new Command30("url-encode").description("Encode URL component or full URL").argument("<input>", "Text to encode").option("--full", "Encode full URL (use encodeURI instead of encodeURIComponent)").addHelpText("after", `
|
|
6185
6540
|
Examples:
|
|
6186
6541
|
$ jai1 utils url-encode "hello world"
|
|
6187
6542
|
$ jai1 utils url-encode "hello world & test"
|
|
@@ -6194,7 +6549,7 @@ Examples:
|
|
|
6194
6549
|
}
|
|
6195
6550
|
|
|
6196
6551
|
// src/commands/utils/url-decode.ts
|
|
6197
|
-
import { Command as
|
|
6552
|
+
import { Command as Command31 } from "commander";
|
|
6198
6553
|
async function handleUrlDecode(input4, options) {
|
|
6199
6554
|
const service = new UtilsService();
|
|
6200
6555
|
try {
|
|
@@ -6215,7 +6570,7 @@ async function handleUrlDecode(input4, options) {
|
|
|
6215
6570
|
}
|
|
6216
6571
|
}
|
|
6217
6572
|
function createUrlDecodeCommand() {
|
|
6218
|
-
const cmd = new
|
|
6573
|
+
const cmd = new Command31("url-decode").description("Decode URL-encoded string").argument("<input>", "Text to decode").option("--full", "Decode full URL (use decodeURI instead of decodeURIComponent)").addHelpText("after", `
|
|
6219
6574
|
Examples:
|
|
6220
6575
|
$ jai1 utils url-decode "hello%20world"
|
|
6221
6576
|
$ jai1 utils url-decode "hello%20world%20%26%20test"
|
|
@@ -6228,7 +6583,7 @@ Examples:
|
|
|
6228
6583
|
}
|
|
6229
6584
|
|
|
6230
6585
|
// src/commands/utils/cron.ts
|
|
6231
|
-
import { Command as
|
|
6586
|
+
import { Command as Command32 } from "commander";
|
|
6232
6587
|
import cronstrue from "cronstrue";
|
|
6233
6588
|
async function handleCronParse(expression) {
|
|
6234
6589
|
try {
|
|
@@ -6271,7 +6626,7 @@ async function handleCronParse(expression) {
|
|
|
6271
6626
|
}
|
|
6272
6627
|
}
|
|
6273
6628
|
function createCronCommand() {
|
|
6274
|
-
const cmd = new
|
|
6629
|
+
const cmd = new Command32("cron").description("Parse and explain cron expression").argument("<expression>", 'Cron expression (e.g., "0 5 * * *")').addHelpText("after", `
|
|
6275
6630
|
Examples:
|
|
6276
6631
|
$ jai1 utils cron "0 5 * * *"
|
|
6277
6632
|
$ jai1 utils cron "*/15 * * * *"
|
|
@@ -6285,7 +6640,7 @@ Examples:
|
|
|
6285
6640
|
}
|
|
6286
6641
|
|
|
6287
6642
|
// src/commands/utils/markdown-preview.ts
|
|
6288
|
-
import { Command as
|
|
6643
|
+
import { Command as Command33 } from "commander";
|
|
6289
6644
|
import { readFile as readFile5 } from "fs/promises";
|
|
6290
6645
|
import { marked } from "marked";
|
|
6291
6646
|
import TerminalRenderer from "marked-terminal";
|
|
@@ -6324,7 +6679,7 @@ ${code.trim()}
|
|
|
6324
6679
|
}
|
|
6325
6680
|
}
|
|
6326
6681
|
function createMarkdownPreviewCommand() {
|
|
6327
|
-
const cmd = new
|
|
6682
|
+
const cmd = new Command33("markdown-preview").description("Preview markdown file in terminal").argument("<file>", "Markdown file path").option("--raw", "Show raw markdown instead of rendered").addHelpText("after", `
|
|
6328
6683
|
Examples:
|
|
6329
6684
|
$ jai1 utils markdown-preview README.md
|
|
6330
6685
|
$ jai1 utils markdown-preview ./docs/guide.md
|
|
@@ -7733,7 +8088,7 @@ async function runInteractiveMode() {
|
|
|
7733
8088
|
|
|
7734
8089
|
// src/commands/utils/index.ts
|
|
7735
8090
|
function createUtilsCommand() {
|
|
7736
|
-
const utilsCommand = new
|
|
8091
|
+
const utilsCommand = new Command34("utils").description("Developer utilities for common tasks").option("-i, --interactive", "Run in interactive mode").addHelpText("after", `
|
|
7737
8092
|
Interactive Mode:
|
|
7738
8093
|
$ jai1 utils --interactive
|
|
7739
8094
|
$ jai1 utils -i
|
|
@@ -7768,11 +8123,11 @@ Quick Usage:
|
|
|
7768
8123
|
}
|
|
7769
8124
|
|
|
7770
8125
|
// src/commands/deps/index.ts
|
|
7771
|
-
import { Command as
|
|
8126
|
+
import { Command as Command36 } from "commander";
|
|
7772
8127
|
|
|
7773
8128
|
// src/commands/deps/upgrade.ts
|
|
7774
|
-
import { Command as
|
|
7775
|
-
import { checkbox as checkbox3, confirm as
|
|
8129
|
+
import { Command as Command35 } from "commander";
|
|
8130
|
+
import { checkbox as checkbox3, confirm as confirm5 } from "@inquirer/prompts";
|
|
7776
8131
|
|
|
7777
8132
|
// src/services/deps.service.ts
|
|
7778
8133
|
import { promises as fs10 } from "fs";
|
|
@@ -7985,7 +8340,7 @@ var colors2 = {
|
|
|
7985
8340
|
dim: "\x1B[2m"
|
|
7986
8341
|
};
|
|
7987
8342
|
function createDepsUpgradeCommand() {
|
|
7988
|
-
return new
|
|
8343
|
+
return new Command35("upgrade").description("Upgrade npm packages l\xEAn version m\u1EDBi nh\u1EA5t").option("--check", "Ch\u1EC9 ki\u1EC3m tra, kh\xF4ng upgrade").option("--all", "Upgrade t\u1EA5t c\u1EA3 kh\xF4ng c\u1EA7n ch\u1ECDn").option("--deps-only", "Ch\u1EC9 upgrade dependencies").option("--dev-only", "Ch\u1EC9 upgrade devDependencies").action(async (options) => {
|
|
7989
8344
|
await handleDepsUpgrade(options);
|
|
7990
8345
|
});
|
|
7991
8346
|
}
|
|
@@ -8077,7 +8432,7 @@ ${colors2.yellow}\u23F8\uFE0F \u0110\xE3 h\u1EE7y${colors2.reset}
|
|
|
8077
8432
|
}
|
|
8078
8433
|
let shouldProceed;
|
|
8079
8434
|
try {
|
|
8080
|
-
shouldProceed = await
|
|
8435
|
+
shouldProceed = await confirm5({
|
|
8081
8436
|
message: `Ti\u1EBFn h\xE0nh upgrade ${selectedPackages.length} packages?`,
|
|
8082
8437
|
default: true
|
|
8083
8438
|
});
|
|
@@ -8165,16 +8520,16 @@ function displayUpgradeTable(packages) {
|
|
|
8165
8520
|
|
|
8166
8521
|
// src/commands/deps/index.ts
|
|
8167
8522
|
function createDepsCommand() {
|
|
8168
|
-
const depsCommand = new
|
|
8523
|
+
const depsCommand = new Command36("deps").description("Qu\u1EA3n l\xFD dependencies trong project");
|
|
8169
8524
|
depsCommand.addCommand(createDepsUpgradeCommand());
|
|
8170
8525
|
return depsCommand;
|
|
8171
8526
|
}
|
|
8172
8527
|
|
|
8173
8528
|
// src/commands/kit/index.ts
|
|
8174
|
-
import { Command as
|
|
8529
|
+
import { Command as Command40 } from "commander";
|
|
8175
8530
|
|
|
8176
8531
|
// src/commands/kit/list.ts
|
|
8177
|
-
import { Command as
|
|
8532
|
+
import { Command as Command37 } from "commander";
|
|
8178
8533
|
|
|
8179
8534
|
// src/services/starter-kit.service.ts
|
|
8180
8535
|
import { promises as fs11 } from "fs";
|
|
@@ -8241,7 +8596,7 @@ var StarterKitService = class {
|
|
|
8241
8596
|
|
|
8242
8597
|
// src/commands/kit/list.ts
|
|
8243
8598
|
function createKitListCommand() {
|
|
8244
|
-
return new
|
|
8599
|
+
return new Command37("list").description("List available starter kits").option("-c, --category <category>", "Filter by category (backend, frontend, fullstack)").option("-s, --search <term>", "Search kits by name or description").action(async (options) => {
|
|
8245
8600
|
const configService = new ConfigService();
|
|
8246
8601
|
const config = await configService.load();
|
|
8247
8602
|
if (!config) {
|
|
@@ -8278,9 +8633,9 @@ function createKitListCommand() {
|
|
|
8278
8633
|
}
|
|
8279
8634
|
|
|
8280
8635
|
// src/commands/kit/info.ts
|
|
8281
|
-
import { Command as
|
|
8636
|
+
import { Command as Command38 } from "commander";
|
|
8282
8637
|
function createKitInfoCommand() {
|
|
8283
|
-
return new
|
|
8638
|
+
return new Command38("info").description("Show detailed information about a starter kit").argument("<slug>", "Starter kit slug").action(async (slug) => {
|
|
8284
8639
|
const configService = new ConfigService();
|
|
8285
8640
|
const config = await configService.load();
|
|
8286
8641
|
if (!config) {
|
|
@@ -8329,7 +8684,7 @@ Post-Init Commands:`);
|
|
|
8329
8684
|
}
|
|
8330
8685
|
|
|
8331
8686
|
// src/commands/kit/create.ts
|
|
8332
|
-
import { Command as
|
|
8687
|
+
import { Command as Command39 } from "commander";
|
|
8333
8688
|
import { promises as fs12 } from "fs";
|
|
8334
8689
|
import { join as join6 } from "path";
|
|
8335
8690
|
import { select as select2, input, checkbox as checkbox4 } from "@inquirer/prompts";
|
|
@@ -8374,7 +8729,7 @@ var HookExecutor = class {
|
|
|
8374
8729
|
|
|
8375
8730
|
// src/commands/kit/create.ts
|
|
8376
8731
|
function createKitCreateCommand() {
|
|
8377
|
-
return new
|
|
8732
|
+
return new Command39("create").description("Create a new project from a starter kit").argument("<slug>", "Starter kit slug").argument("[directory]", "Project directory (default: ./<slug>)").option("-y, --yes", "Auto mode - use defaults, no prompts").option("--name <name>", "Project name").option("--skip-install", "Skip dependency installation").option("--skip-git", "Skip git initialization").option("--skip-framework", "Skip framework apply").option("--skip-ide", "Skip IDE sync").action(async (slug, directory, options) => {
|
|
8378
8733
|
const configService = new ConfigService();
|
|
8379
8734
|
const config = await configService.load();
|
|
8380
8735
|
if (!config) {
|
|
@@ -8546,7 +8901,7 @@ async function getAllFiles(dir) {
|
|
|
8546
8901
|
|
|
8547
8902
|
// src/commands/kit/index.ts
|
|
8548
8903
|
function createKitCommand() {
|
|
8549
|
-
const cmd = new
|
|
8904
|
+
const cmd = new Command40("kit").description("Manage starter kits for new projects").action(() => {
|
|
8550
8905
|
cmd.help();
|
|
8551
8906
|
});
|
|
8552
8907
|
cmd.addCommand(createKitListCommand());
|
|
@@ -8556,12 +8911,12 @@ function createKitCommand() {
|
|
|
8556
8911
|
}
|
|
8557
8912
|
|
|
8558
8913
|
// src/commands/rules/index.ts
|
|
8559
|
-
import { Command as
|
|
8914
|
+
import { Command as Command47 } from "commander";
|
|
8560
8915
|
|
|
8561
8916
|
// src/commands/rules/list.ts
|
|
8562
|
-
import { Command as
|
|
8917
|
+
import { Command as Command41 } from "commander";
|
|
8563
8918
|
function createRulesListCommand() {
|
|
8564
|
-
return new
|
|
8919
|
+
return new Command41("list").description("List available rule presets").option("--json", "Output as JSON").action(async (options) => {
|
|
8565
8920
|
const configService = new ConfigService();
|
|
8566
8921
|
const config = await configService.load();
|
|
8567
8922
|
if (!config) {
|
|
@@ -8616,12 +8971,12 @@ function createRulesListCommand() {
|
|
|
8616
8971
|
}
|
|
8617
8972
|
|
|
8618
8973
|
// src/commands/rules/init.ts
|
|
8619
|
-
import { Command as
|
|
8974
|
+
import { Command as Command42 } from "commander";
|
|
8620
8975
|
import { promises as fs13 } from "fs";
|
|
8621
8976
|
import { join as join7 } from "path";
|
|
8622
|
-
import { select as select3, confirm as
|
|
8977
|
+
import { select as select3, confirm as confirm6 } from "@inquirer/prompts";
|
|
8623
8978
|
function createRulesInitCommand() {
|
|
8624
|
-
return new
|
|
8979
|
+
return new Command42("init").description("Apply rule preset to project").option("--preset <slug>", "Preset slug to apply").option("--output <format>", "Output format: cursor, agents-md, both (default: cursor)", "cursor").option("-y, --yes", "Skip confirmations").action(async (options) => {
|
|
8625
8980
|
const configService = new ConfigService();
|
|
8626
8981
|
const config = await configService.load();
|
|
8627
8982
|
if (!config) {
|
|
@@ -8681,7 +9036,7 @@ function createRulesInitCommand() {
|
|
|
8681
9036
|
});
|
|
8682
9037
|
}
|
|
8683
9038
|
if (!options.yes) {
|
|
8684
|
-
const proceed = await
|
|
9039
|
+
const proceed = await confirm6({
|
|
8685
9040
|
message: `Apply preset '${bundle.preset.name}' to current directory?`,
|
|
8686
9041
|
default: true
|
|
8687
9042
|
});
|
|
@@ -8749,10 +9104,10 @@ async function applyAgentsMdFormat(bundle) {
|
|
|
8749
9104
|
}
|
|
8750
9105
|
|
|
8751
9106
|
// src/commands/rules/apply.ts
|
|
8752
|
-
import { Command as
|
|
9107
|
+
import { Command as Command43 } from "commander";
|
|
8753
9108
|
import { promises as fs15 } from "fs";
|
|
8754
9109
|
import { join as join9 } from "path";
|
|
8755
|
-
import { select as select4, confirm as
|
|
9110
|
+
import { select as select4, confirm as confirm7, checkbox as checkbox5 } from "@inquirer/prompts";
|
|
8756
9111
|
|
|
8757
9112
|
// src/services/rules-generator.service.ts
|
|
8758
9113
|
var RulesGeneratorService = class {
|
|
@@ -9332,7 +9687,7 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
9332
9687
|
|
|
9333
9688
|
// src/commands/rules/apply.ts
|
|
9334
9689
|
function createRulesApplyCommand() {
|
|
9335
|
-
return new
|
|
9690
|
+
return new Command43("apply").description("Apply rule preset to project with multi-IDE support").argument("[preset]", "Preset slug to apply (optional)").option("--ides <ides>", "Comma-separated list of IDE formats (cursor,windsurf,antigravity,claude,agentsmd,gemini)").option("--skip-backup", "Skip backup creation").option("-y, --yes", "Skip all confirmations (auto mode)").action(async (presetSlug, options) => {
|
|
9336
9691
|
const configService = new ConfigService();
|
|
9337
9692
|
const config = await configService.load();
|
|
9338
9693
|
if (!config) {
|
|
@@ -9472,7 +9827,7 @@ function createRulesApplyCommand() {
|
|
|
9472
9827
|
if (backupPath) {
|
|
9473
9828
|
console.log(` Backup: ${backupPath}`);
|
|
9474
9829
|
}
|
|
9475
|
-
const proceed = await
|
|
9830
|
+
const proceed = await confirm7({
|
|
9476
9831
|
message: "Apply these rules to the current directory?",
|
|
9477
9832
|
default: true
|
|
9478
9833
|
});
|
|
@@ -9577,11 +9932,11 @@ function createRulesApplyCommand() {
|
|
|
9577
9932
|
}
|
|
9578
9933
|
|
|
9579
9934
|
// src/commands/rules/restore.ts
|
|
9580
|
-
import { Command as
|
|
9935
|
+
import { Command as Command44 } from "commander";
|
|
9581
9936
|
import { join as join10 } from "path";
|
|
9582
|
-
import { select as select5, confirm as
|
|
9937
|
+
import { select as select5, confirm as confirm8 } from "@inquirer/prompts";
|
|
9583
9938
|
function createRulesRestoreCommand() {
|
|
9584
|
-
return new
|
|
9939
|
+
return new Command44("restore").description("Restore rules from a backup").option("--latest", "Restore the most recent backup").option("-y, --yes", "Skip confirmation").action(async (options) => {
|
|
9585
9940
|
const backupService = new BackupService();
|
|
9586
9941
|
const backups = await backupService.listBackups();
|
|
9587
9942
|
if (backups.length === 0) {
|
|
@@ -9613,7 +9968,7 @@ function createRulesRestoreCommand() {
|
|
|
9613
9968
|
console.log(` IDEs: ${selectedBackup.ides.join(", ")}`);
|
|
9614
9969
|
console.log(` Files: ${selectedBackup.files.length}`);
|
|
9615
9970
|
if (!options.yes) {
|
|
9616
|
-
const proceed = await
|
|
9971
|
+
const proceed = await confirm8({
|
|
9617
9972
|
message: "This will overwrite current rules. Continue?",
|
|
9618
9973
|
default: false
|
|
9619
9974
|
});
|
|
@@ -9649,12 +10004,12 @@ function formatTimestamp(timestamp) {
|
|
|
9649
10004
|
}
|
|
9650
10005
|
|
|
9651
10006
|
// src/commands/rules/sync.ts
|
|
9652
|
-
import { Command as
|
|
10007
|
+
import { Command as Command45 } from "commander";
|
|
9653
10008
|
import { promises as fs16 } from "fs";
|
|
9654
10009
|
import { join as join11 } from "path";
|
|
9655
|
-
import { confirm as
|
|
10010
|
+
import { confirm as confirm9 } from "@inquirer/prompts";
|
|
9656
10011
|
function createRulesSyncCommand() {
|
|
9657
|
-
return new
|
|
10012
|
+
return new Command45("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) => {
|
|
9658
10013
|
const configPath = join11(process.cwd(), "jai1-rules.json");
|
|
9659
10014
|
let projectConfig;
|
|
9660
10015
|
try {
|
|
@@ -9685,7 +10040,7 @@ Detected ${detected.length} active IDE(s):
|
|
|
9685
10040
|
console.log(` ${confidence} ${d.name} - ${d.ruleCount} rules`);
|
|
9686
10041
|
});
|
|
9687
10042
|
if (!options.yes) {
|
|
9688
|
-
const proceed = await
|
|
10043
|
+
const proceed = await confirm9({
|
|
9689
10044
|
message: "\nSync these detected IDEs?",
|
|
9690
10045
|
default: true
|
|
9691
10046
|
});
|
|
@@ -9799,11 +10154,11 @@ async function checkPathExists(absolutePath) {
|
|
|
9799
10154
|
}
|
|
9800
10155
|
|
|
9801
10156
|
// src/commands/rules/info.ts
|
|
9802
|
-
import { Command as
|
|
10157
|
+
import { Command as Command46 } from "commander";
|
|
9803
10158
|
import { promises as fs17 } from "fs";
|
|
9804
10159
|
import { join as join12 } from "path";
|
|
9805
10160
|
function createRulesInfoCommand() {
|
|
9806
|
-
return new
|
|
10161
|
+
return new Command46("info").description("Show current preset information").option("--json", "Output as JSON").action(async (options) => {
|
|
9807
10162
|
const configPath = join12(process.cwd(), "jai1-rules.json");
|
|
9808
10163
|
let projectConfig;
|
|
9809
10164
|
try {
|
|
@@ -9909,7 +10264,7 @@ async function checkIdeFilesExist(ideId, format) {
|
|
|
9909
10264
|
|
|
9910
10265
|
// src/commands/rules/index.ts
|
|
9911
10266
|
function createRulesCommand() {
|
|
9912
|
-
const rulesCommand = new
|
|
10267
|
+
const rulesCommand = new Command47("rules").description("Manage rule presets for AI agents");
|
|
9913
10268
|
rulesCommand.addCommand(createRulesListCommand());
|
|
9914
10269
|
rulesCommand.addCommand(createRulesInitCommand());
|
|
9915
10270
|
rulesCommand.addCommand(createRulesApplyCommand());
|
|
@@ -9920,8 +10275,8 @@ function createRulesCommand() {
|
|
|
9920
10275
|
}
|
|
9921
10276
|
|
|
9922
10277
|
// src/commands/upgrade.ts
|
|
9923
|
-
import { Command as
|
|
9924
|
-
import { confirm as
|
|
10278
|
+
import { Command as Command48 } from "commander";
|
|
10279
|
+
import { confirm as confirm10 } from "@inquirer/prompts";
|
|
9925
10280
|
import { execSync as execSync2 } from "child_process";
|
|
9926
10281
|
var colors3 = {
|
|
9927
10282
|
yellow: "\x1B[33m",
|
|
@@ -9932,7 +10287,7 @@ var colors3 = {
|
|
|
9932
10287
|
bold: "\x1B[1m"
|
|
9933
10288
|
};
|
|
9934
10289
|
function createUpgradeCommand() {
|
|
9935
|
-
return new
|
|
10290
|
+
return new Command48("upgrade").description("Upgrade jai1-client to the latest version").option("--check", "Only check for updates without installing").option("--force", "Force upgrade without confirmation").action(async (options) => {
|
|
9936
10291
|
await handleUpgrade(options);
|
|
9937
10292
|
});
|
|
9938
10293
|
}
|
|
@@ -9976,7 +10331,7 @@ ${colors3.bold}Current version:${colors3.reset} ${currentVersion}`);
|
|
|
9976
10331
|
return;
|
|
9977
10332
|
}
|
|
9978
10333
|
if (!options.force) {
|
|
9979
|
-
const shouldUpdate = await
|
|
10334
|
+
const shouldUpdate = await confirm10({
|
|
9980
10335
|
message: "Update to the latest version now?",
|
|
9981
10336
|
default: true
|
|
9982
10337
|
});
|
|
@@ -10080,11 +10435,11 @@ function getInstallCommand(packageManager2) {
|
|
|
10080
10435
|
}
|
|
10081
10436
|
|
|
10082
10437
|
// src/commands/clean.ts
|
|
10083
|
-
import { Command as
|
|
10084
|
-
import { confirm as
|
|
10438
|
+
import { Command as Command49 } from "commander";
|
|
10439
|
+
import { confirm as confirm11, select as select6 } from "@inquirer/prompts";
|
|
10085
10440
|
import { join as join13 } from "path";
|
|
10086
10441
|
function createCleanCommand() {
|
|
10087
|
-
return new
|
|
10442
|
+
return new Command49("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) => {
|
|
10088
10443
|
await handleClean(options);
|
|
10089
10444
|
});
|
|
10090
10445
|
}
|
|
@@ -10179,7 +10534,7 @@ async function cleanTarget(target, skipConfirm) {
|
|
|
10179
10534
|
}
|
|
10180
10535
|
const countStr = info.count ? ` (${info.count} items)` : "";
|
|
10181
10536
|
if (!skipConfirm) {
|
|
10182
|
-
const confirmed = await
|
|
10537
|
+
const confirmed = await confirm11({
|
|
10183
10538
|
message: `Delete ${target.name}${countStr}?`,
|
|
10184
10539
|
default: false
|
|
10185
10540
|
});
|
|
@@ -10197,7 +10552,7 @@ async function cleanTarget(target, skipConfirm) {
|
|
|
10197
10552
|
}
|
|
10198
10553
|
|
|
10199
10554
|
// src/commands/redmine/check.ts
|
|
10200
|
-
import { Command as
|
|
10555
|
+
import { Command as Command50 } from "commander";
|
|
10201
10556
|
|
|
10202
10557
|
// src/services/redmine-config.service.ts
|
|
10203
10558
|
import { readFile as readFile6 } from "fs/promises";
|
|
@@ -10504,7 +10859,7 @@ async function checkConnectivity(config) {
|
|
|
10504
10859
|
|
|
10505
10860
|
// src/commands/redmine/check.ts
|
|
10506
10861
|
function createRedmineCheckCommand() {
|
|
10507
|
-
const cmd = new
|
|
10862
|
+
const cmd = new Command50("check").description("Check Redmine connectivity").option("-c, --config <path>", "Config file path", "redmine.config.yaml").option("--json", "Output as JSON").action(async (options) => {
|
|
10508
10863
|
await handleRedmineCheck(options);
|
|
10509
10864
|
});
|
|
10510
10865
|
return cmd;
|
|
@@ -10532,7 +10887,7 @@ async function handleRedmineCheck(options) {
|
|
|
10532
10887
|
}
|
|
10533
10888
|
|
|
10534
10889
|
// src/commands/redmine/sync-issue.ts
|
|
10535
|
-
import { Command as
|
|
10890
|
+
import { Command as Command51 } from "commander";
|
|
10536
10891
|
|
|
10537
10892
|
// src/sync-issue.ts
|
|
10538
10893
|
import { resolve as resolve3, relative } from "path";
|
|
@@ -10908,7 +11263,7 @@ function extractIssueIdFromUrl(url) {
|
|
|
10908
11263
|
|
|
10909
11264
|
// src/commands/redmine/sync-issue.ts
|
|
10910
11265
|
function createSyncIssueCommand() {
|
|
10911
|
-
const cmd = new
|
|
11266
|
+
const cmd = new Command51("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) => {
|
|
10912
11267
|
await handleSyncIssue(options);
|
|
10913
11268
|
});
|
|
10914
11269
|
return cmd;
|
|
@@ -10952,7 +11307,7 @@ async function handleSyncIssue(options) {
|
|
|
10952
11307
|
}
|
|
10953
11308
|
|
|
10954
11309
|
// src/commands/redmine/sync-project.ts
|
|
10955
|
-
import { Command as
|
|
11310
|
+
import { Command as Command52 } from "commander";
|
|
10956
11311
|
|
|
10957
11312
|
// src/sync-project.ts
|
|
10958
11313
|
async function syncProject(config, options = {}) {
|
|
@@ -11022,7 +11377,7 @@ async function syncProject(config, options = {}) {
|
|
|
11022
11377
|
|
|
11023
11378
|
// src/commands/redmine/sync-project.ts
|
|
11024
11379
|
function createSyncProjectCommand() {
|
|
11025
|
-
const cmd = new
|
|
11380
|
+
const cmd = new Command52("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) => {
|
|
11026
11381
|
await handleSyncProject(options);
|
|
11027
11382
|
});
|
|
11028
11383
|
return cmd;
|
|
@@ -11077,12 +11432,12 @@ async function handleSyncProject(options) {
|
|
|
11077
11432
|
}
|
|
11078
11433
|
|
|
11079
11434
|
// src/commands/framework/info.ts
|
|
11080
|
-
import { Command as
|
|
11435
|
+
import { Command as Command53 } from "commander";
|
|
11081
11436
|
import { promises as fs18 } from "fs";
|
|
11082
11437
|
import { join as join14 } from "path";
|
|
11083
11438
|
import { homedir as homedir5 } from "os";
|
|
11084
11439
|
function createInfoCommand() {
|
|
11085
|
-
const cmd = new
|
|
11440
|
+
const cmd = new Command53("info").description("Show jai1-client configuration and status").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
|
|
11086
11441
|
await handleInfo(options);
|
|
11087
11442
|
});
|
|
11088
11443
|
return cmd;
|
|
@@ -11138,8 +11493,8 @@ async function getProjectStatus2() {
|
|
|
11138
11493
|
}
|
|
11139
11494
|
|
|
11140
11495
|
// src/commands/self-update.ts
|
|
11141
|
-
import { Command as
|
|
11142
|
-
import { confirm as
|
|
11496
|
+
import { Command as Command54 } from "commander";
|
|
11497
|
+
import { confirm as confirm12 } from "@inquirer/prompts";
|
|
11143
11498
|
import { execSync as execSync3 } from "child_process";
|
|
11144
11499
|
var colors4 = {
|
|
11145
11500
|
yellow: "\x1B[33m",
|
|
@@ -11150,7 +11505,7 @@ var colors4 = {
|
|
|
11150
11505
|
bold: "\x1B[1m"
|
|
11151
11506
|
};
|
|
11152
11507
|
function createSelfUpdateCommand() {
|
|
11153
|
-
return new
|
|
11508
|
+
return new Command54("self-update").description("Update jai1-client to the latest version").option("--check", "Only check for updates without installing").option("--force", "Force update without confirmation").action(async (options) => {
|
|
11154
11509
|
await handleSelfUpdate(options);
|
|
11155
11510
|
});
|
|
11156
11511
|
}
|
|
@@ -11194,7 +11549,7 @@ ${colors4.bold}Current version:${colors4.reset} ${currentVersion}`);
|
|
|
11194
11549
|
return;
|
|
11195
11550
|
}
|
|
11196
11551
|
if (!options.force) {
|
|
11197
|
-
const shouldUpdate = await
|
|
11552
|
+
const shouldUpdate = await confirm12({
|
|
11198
11553
|
message: "Update to the latest version now?",
|
|
11199
11554
|
default: true
|
|
11200
11555
|
});
|
|
@@ -11290,10 +11645,10 @@ function getInstallCommand2(packageManager2) {
|
|
|
11290
11645
|
}
|
|
11291
11646
|
|
|
11292
11647
|
// src/commands/clear-backups.ts
|
|
11293
|
-
import { Command as
|
|
11294
|
-
import { confirm as
|
|
11648
|
+
import { Command as Command55 } from "commander";
|
|
11649
|
+
import { confirm as confirm13 } from "@inquirer/prompts";
|
|
11295
11650
|
function createClearBackupsCommand() {
|
|
11296
|
-
return new
|
|
11651
|
+
return new Command55("clear-backups").description("Clear backup files").option("-y, --yes", "Skip confirmation").action(async (options) => {
|
|
11297
11652
|
const service = new ComponentsService();
|
|
11298
11653
|
const backups = await service.listBackups(process.cwd());
|
|
11299
11654
|
if (backups.length === 0) {
|
|
@@ -11309,7 +11664,7 @@ function createClearBackupsCommand() {
|
|
|
11309
11664
|
}
|
|
11310
11665
|
console.log();
|
|
11311
11666
|
if (!options.yes) {
|
|
11312
|
-
const ok = await
|
|
11667
|
+
const ok = await confirm13({ message: "Delete all backups?", default: false });
|
|
11313
11668
|
if (!ok) return;
|
|
11314
11669
|
}
|
|
11315
11670
|
await service.clearBackups(process.cwd());
|
|
@@ -11318,8 +11673,8 @@ function createClearBackupsCommand() {
|
|
|
11318
11673
|
}
|
|
11319
11674
|
|
|
11320
11675
|
// src/commands/vscode/index.ts
|
|
11321
|
-
import { Command as
|
|
11322
|
-
import { checkbox as checkbox7, confirm as
|
|
11676
|
+
import { Command as Command56 } from "commander";
|
|
11677
|
+
import { checkbox as checkbox7, confirm as confirm14, select as select7 } from "@inquirer/prompts";
|
|
11323
11678
|
import fs19 from "fs/promises";
|
|
11324
11679
|
import path7 from "path";
|
|
11325
11680
|
import { existsSync as existsSync3 } from "fs";
|
|
@@ -11458,7 +11813,7 @@ var PERFORMANCE_GROUPS2 = {
|
|
|
11458
11813
|
}
|
|
11459
11814
|
};
|
|
11460
11815
|
function createVSCodeCommand() {
|
|
11461
|
-
const vscodeCommand = new
|
|
11816
|
+
const vscodeCommand = new Command56("vscode").description("Qu\u1EA3n l\xFD c\xE0i \u0111\u1EB7t VSCode cho d\u1EF1 \xE1n hi\u1EC7n t\u1EA1i");
|
|
11462
11817
|
vscodeCommand.action(async () => {
|
|
11463
11818
|
await interactiveMode2();
|
|
11464
11819
|
});
|
|
@@ -11562,7 +11917,7 @@ async function applyGroups2(groupKeys, action) {
|
|
|
11562
11917
|
console.log("\u{1F4C4} \u0110\xE3 \u0111\u1ECDc c\xE0i \u0111\u1EB7t hi\u1EC7n t\u1EA1i t\u1EEB settings.json");
|
|
11563
11918
|
} catch {
|
|
11564
11919
|
console.warn("\u26A0\uFE0F Kh\xF4ng th\u1EC3 \u0111\u1ECDc settings.json (c\xF3 th\u1EC3 ch\u1EE9a comments).");
|
|
11565
|
-
const confirmOverwrite = await
|
|
11920
|
+
const confirmOverwrite = await confirm14({
|
|
11566
11921
|
message: "Ghi \u0111\xE8 file settings.json hi\u1EC7n t\u1EA1i?",
|
|
11567
11922
|
default: false
|
|
11568
11923
|
});
|
|
@@ -11609,7 +11964,7 @@ async function resetSettings2(groupKeys) {
|
|
|
11609
11964
|
console.log("\n\u26A0\uFE0F Kh\xF4ng t\xECm th\u1EA5y file settings.json");
|
|
11610
11965
|
return;
|
|
11611
11966
|
}
|
|
11612
|
-
const confirmReset = await
|
|
11967
|
+
const confirmReset = await confirm14({
|
|
11613
11968
|
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(", ")}?`,
|
|
11614
11969
|
default: false
|
|
11615
11970
|
});
|
|
@@ -11629,9 +11984,9 @@ async function resetSettings2(groupKeys) {
|
|
|
11629
11984
|
// src/commands/guide.ts
|
|
11630
11985
|
import React40 from "react";
|
|
11631
11986
|
import { render as render6 } from "ink";
|
|
11632
|
-
import { Command as
|
|
11987
|
+
import { Command as Command57 } from "commander";
|
|
11633
11988
|
function createGuideCommand() {
|
|
11634
|
-
const cmd = new
|
|
11989
|
+
const cmd = new Command57("guide").description("Interactive guide center for Agentic Coding").option("--topic <topic>", "Open specific topic (intro, rules, workflows, prompts, skills)").action(async (options) => {
|
|
11635
11990
|
const { waitUntilExit } = render6(
|
|
11636
11991
|
React40.createElement(GuideApp, {
|
|
11637
11992
|
initialTopic: options.topic,
|
|
@@ -11648,9 +12003,9 @@ function createGuideCommand() {
|
|
|
11648
12003
|
// src/commands/context.ts
|
|
11649
12004
|
import React41 from "react";
|
|
11650
12005
|
import { render as render7 } from "ink";
|
|
11651
|
-
import { Command as
|
|
12006
|
+
import { Command as Command58 } from "commander";
|
|
11652
12007
|
function createContextCommand() {
|
|
11653
|
-
const cmd = new
|
|
12008
|
+
const cmd = new Command58("context").description("Kh\xE1m ph\xE1 v\xE0 qu\u1EA3n l\xFD context d\u1EF1 \xE1n cho c\xE1c IDE").option("--ide <ide>", "M\u1EDF tr\u1EF1c ti\u1EBFp IDE c\u1EE5 th\u1EC3 (cursor, windsurf, antigravity, jai1)").option("--type <type>", "Hi\u1EC3n th\u1ECB lo\u1EA1i context c\u1EE5 th\u1EC3 (rules, workflows, skills, agents, prompts)").option("--stats", "Hi\u1EC3n th\u1ECB th\u1ED1ng k\xEA context (non-interactive)").action(async (options) => {
|
|
11654
12009
|
let initialIDE;
|
|
11655
12010
|
if (options.ide) {
|
|
11656
12011
|
const validIDEs = ["cursor", "windsurf", "antigravity", "jai1"];
|
|
@@ -11727,10 +12082,10 @@ async function printStats2() {
|
|
|
11727
12082
|
}
|
|
11728
12083
|
|
|
11729
12084
|
// src/commands/migrate-ide.ts
|
|
11730
|
-
import { Command as
|
|
11731
|
-
import { checkbox as checkbox8, confirm as
|
|
12085
|
+
import { Command as Command59 } from "commander";
|
|
12086
|
+
import { checkbox as checkbox8, confirm as confirm15 } from "@inquirer/prompts";
|
|
11732
12087
|
function createMigrateIdeCommand() {
|
|
11733
|
-
const cmd = new
|
|
12088
|
+
const cmd = new Command59("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) => {
|
|
11734
12089
|
await runMigrateIde(options);
|
|
11735
12090
|
});
|
|
11736
12091
|
return cmd;
|
|
@@ -11799,7 +12154,7 @@ async function runMigrateIde(options) {
|
|
|
11799
12154
|
if (options.dryRun) {
|
|
11800
12155
|
console.log("\u{1F50D} DRY RUN - No files will be written\n");
|
|
11801
12156
|
}
|
|
11802
|
-
const confirmed = await
|
|
12157
|
+
const confirmed = await confirm15({
|
|
11803
12158
|
message: "Proceed with migration?",
|
|
11804
12159
|
default: true
|
|
11805
12160
|
});
|
|
@@ -11836,7 +12191,7 @@ async function runMigrateIde(options) {
|
|
|
11836
12191
|
}
|
|
11837
12192
|
|
|
11838
12193
|
// src/cli.ts
|
|
11839
|
-
var program = new
|
|
12194
|
+
var program = new Command60();
|
|
11840
12195
|
if (process.argv.includes("-v") || process.argv.includes("--version")) {
|
|
11841
12196
|
console.log(package_default.version);
|
|
11842
12197
|
if (!process.argv.includes("--skip-update-check")) {
|
|
@@ -11857,15 +12212,16 @@ program.addCommand(createChatCommand());
|
|
|
11857
12212
|
program.addCommand(createOpenAiKeysCommand());
|
|
11858
12213
|
program.addCommand(createStatsCommand());
|
|
11859
12214
|
program.addCommand(createTranslateCommand());
|
|
12215
|
+
program.addCommand(createImageCommand());
|
|
11860
12216
|
program.addCommand(createUtilsCommand());
|
|
11861
12217
|
program.addCommand(createDepsCommand());
|
|
11862
12218
|
program.addCommand(createKitCommand());
|
|
11863
12219
|
program.addCommand(createRulesCommand());
|
|
11864
12220
|
program.addCommand(createUpgradeCommand());
|
|
11865
12221
|
program.addCommand(createCleanCommand());
|
|
11866
|
-
var redmineCommand = new
|
|
12222
|
+
var redmineCommand = new Command60("redmine").description("Redmine context sync commands");
|
|
11867
12223
|
redmineCommand.addCommand(createRedmineCheckCommand());
|
|
11868
|
-
var syncCommand = new
|
|
12224
|
+
var syncCommand = new Command60("sync").description("Sync Redmine issues to markdown files");
|
|
11869
12225
|
syncCommand.addCommand(createSyncIssueCommand());
|
|
11870
12226
|
syncCommand.addCommand(createSyncProjectCommand());
|
|
11871
12227
|
redmineCommand.addCommand(syncCommand);
|