@jvittechs/jai1-cli 0.1.85 → 0.1.87
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 +2050 -274
- package/dist/cli.js.map +1 -1
- package/package.json +1 -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 Command55 } 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.87",
|
|
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: {
|
|
@@ -895,9 +895,9 @@ var UnifiedApplyApp = ({
|
|
|
895
895
|
(c) => c.filepath.toLowerCase().includes(query) || c.name.toLowerCase().includes(query) || c.tags && c.tags.some((t) => t.toLowerCase().includes(query))
|
|
896
896
|
);
|
|
897
897
|
}, [components, searchQuery]);
|
|
898
|
-
useInput((
|
|
898
|
+
useInput((input4, key) => {
|
|
899
899
|
if (viewState === "summary") {
|
|
900
|
-
if (key.return ||
|
|
900
|
+
if (key.return || input4 === "q" || key.escape) {
|
|
901
901
|
onExit();
|
|
902
902
|
}
|
|
903
903
|
return;
|
|
@@ -911,7 +911,7 @@ var UnifiedApplyApp = ({
|
|
|
911
911
|
else setFocusArea("search");
|
|
912
912
|
return;
|
|
913
913
|
}
|
|
914
|
-
if (key.escape ||
|
|
914
|
+
if (key.escape || input4 === "q") {
|
|
915
915
|
onExit();
|
|
916
916
|
return;
|
|
917
917
|
}
|
|
@@ -926,7 +926,7 @@ var UnifiedApplyApp = ({
|
|
|
926
926
|
setCursorIndex((prev) => Math.max(0, prev - 1));
|
|
927
927
|
} else if (key.downArrow) {
|
|
928
928
|
setCursorIndex((prev) => Math.min(filteredComponents.length - 1, prev + 1));
|
|
929
|
-
} else if (
|
|
929
|
+
} else if (input4 === " ") {
|
|
930
930
|
const current = filteredComponents[cursorIndex];
|
|
931
931
|
if (current) {
|
|
932
932
|
setSelectedPaths((prev) => {
|
|
@@ -939,13 +939,13 @@ var UnifiedApplyApp = ({
|
|
|
939
939
|
return next;
|
|
940
940
|
});
|
|
941
941
|
}
|
|
942
|
-
} else if (
|
|
942
|
+
} else if (input4 === "a") {
|
|
943
943
|
setSelectedPaths((prev) => {
|
|
944
944
|
const next = new Set(prev);
|
|
945
945
|
filteredComponents.forEach((c) => next.add(c.filepath));
|
|
946
946
|
return next;
|
|
947
947
|
});
|
|
948
|
-
} else if (
|
|
948
|
+
} else if (input4 === "c") {
|
|
949
949
|
setSelectedPaths(/* @__PURE__ */ new Set());
|
|
950
950
|
}
|
|
951
951
|
}
|
|
@@ -954,7 +954,7 @@ var UnifiedApplyApp = ({
|
|
|
954
954
|
setSelectedPackageIndex((prev) => Math.max(0, prev - 1));
|
|
955
955
|
} else if (key.rightArrow) {
|
|
956
956
|
setSelectedPackageIndex((prev) => Math.min(tags.length - 1, prev + 1));
|
|
957
|
-
} else if (
|
|
957
|
+
} else if (input4 === " " || key.return) {
|
|
958
958
|
const tag = tags[selectedPackageIndex];
|
|
959
959
|
if (tag) {
|
|
960
960
|
const packageComponents = components.filter((c) => c.tags?.includes(tag.tag));
|
|
@@ -1286,7 +1286,7 @@ async function handleCheck(options) {
|
|
|
1286
1286
|
}
|
|
1287
1287
|
|
|
1288
1288
|
// src/commands/ide/index.ts
|
|
1289
|
-
import { Command as
|
|
1289
|
+
import { Command as Command10 } from "commander";
|
|
1290
1290
|
|
|
1291
1291
|
// src/commands/ide/context.ts
|
|
1292
1292
|
import React10 from "react";
|
|
@@ -1332,7 +1332,7 @@ var MainMenuView = ({ ideContexts, onSelect }) => {
|
|
|
1332
1332
|
available: ideContexts.some((ctx) => ctx.ide === "jai1")
|
|
1333
1333
|
}
|
|
1334
1334
|
];
|
|
1335
|
-
useInput2((
|
|
1335
|
+
useInput2((input4, key) => {
|
|
1336
1336
|
if (key.upArrow) {
|
|
1337
1337
|
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
1338
1338
|
} else if (key.downArrow) {
|
|
@@ -1434,13 +1434,13 @@ var IDEOverviewView = ({
|
|
|
1434
1434
|
count: ideContext.stats.byType.context
|
|
1435
1435
|
});
|
|
1436
1436
|
}
|
|
1437
|
-
useInput3((
|
|
1437
|
+
useInput3((input4, key) => {
|
|
1438
1438
|
if (key.tab || key.rightArrow) {
|
|
1439
1439
|
setSelectedTabIndex((prev) => Math.min(tabs.length - 1, prev + 1));
|
|
1440
1440
|
} else if (key.leftArrow) {
|
|
1441
1441
|
setSelectedTabIndex((prev) => Math.max(0, prev - 1));
|
|
1442
1442
|
}
|
|
1443
|
-
const num = parseInt(
|
|
1443
|
+
const num = parseInt(input4, 10);
|
|
1444
1444
|
if (!isNaN(num) && num >= 1 && num <= tabs.length) {
|
|
1445
1445
|
setSelectedTabIndex(num - 1);
|
|
1446
1446
|
}
|
|
@@ -1502,7 +1502,7 @@ import React7, { useState as useState4 } from "react";
|
|
|
1502
1502
|
import { Box as Box5, Text as Text6, useInput as useInput4 } from "ink";
|
|
1503
1503
|
var ListView = ({ items, contentType, onSelect, onBack }) => {
|
|
1504
1504
|
const [selectedIndex, setSelectedIndex] = useState4(0);
|
|
1505
|
-
useInput4((
|
|
1505
|
+
useInput4((input4, key) => {
|
|
1506
1506
|
if (key.upArrow) {
|
|
1507
1507
|
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
1508
1508
|
} else if (key.downArrow) {
|
|
@@ -1553,18 +1553,18 @@ var DetailView = ({ item, scrollPosition: initialScroll, onBack }) => {
|
|
|
1553
1553
|
const [scrollPosition, setScrollPosition] = useState5(initialScroll);
|
|
1554
1554
|
const maxVisibleLines = 25;
|
|
1555
1555
|
const maxScroll = Math.max(0, item.previewLines.length - maxVisibleLines);
|
|
1556
|
-
useInput5((
|
|
1557
|
-
if (key.upArrow ||
|
|
1556
|
+
useInput5((input4, key) => {
|
|
1557
|
+
if (key.upArrow || input4 === "k") {
|
|
1558
1558
|
setScrollPosition((prev) => Math.max(0, prev - 1));
|
|
1559
|
-
} else if (key.downArrow ||
|
|
1559
|
+
} else if (key.downArrow || input4 === "j") {
|
|
1560
1560
|
setScrollPosition((prev) => Math.min(maxScroll, prev + 1));
|
|
1561
|
-
} else if (key.pageDown ||
|
|
1561
|
+
} else if (key.pageDown || input4 === "d") {
|
|
1562
1562
|
setScrollPosition((prev) => Math.min(maxScroll, prev + 5));
|
|
1563
|
-
} else if (key.pageUp ||
|
|
1563
|
+
} else if (key.pageUp || input4 === "u") {
|
|
1564
1564
|
setScrollPosition((prev) => Math.max(0, prev - 5));
|
|
1565
|
-
} else if (
|
|
1565
|
+
} else if (input4 === "g") {
|
|
1566
1566
|
setScrollPosition(0);
|
|
1567
|
-
} else if (
|
|
1567
|
+
} else if (input4 === "G") {
|
|
1568
1568
|
setScrollPosition(maxScroll);
|
|
1569
1569
|
} else if (key.escape || key.backspace) {
|
|
1570
1570
|
onBack();
|
|
@@ -1960,8 +1960,8 @@ var ContextApp = ({ initialIDE, initialType, onExit }) => {
|
|
|
1960
1960
|
setLoading(false);
|
|
1961
1961
|
});
|
|
1962
1962
|
}, [initialIDE]);
|
|
1963
|
-
useInput6((
|
|
1964
|
-
if (
|
|
1963
|
+
useInput6((input4, key) => {
|
|
1964
|
+
if (input4 === "q") {
|
|
1965
1965
|
onExit();
|
|
1966
1966
|
return;
|
|
1967
1967
|
}
|
|
@@ -2823,12 +2823,471 @@ async function runSync(options) {
|
|
|
2823
2823
|
console.log("");
|
|
2824
2824
|
}
|
|
2825
2825
|
|
|
2826
|
+
// src/commands/ide/status.ts
|
|
2827
|
+
import { Command as Command9 } from "commander";
|
|
2828
|
+
|
|
2829
|
+
// src/services/ide-detection.service.ts
|
|
2830
|
+
import { promises as fs7 } from "fs";
|
|
2831
|
+
import { join as join4 } from "path";
|
|
2832
|
+
|
|
2833
|
+
// src/config/ide-formats.ts
|
|
2834
|
+
var IDE_FORMATS = {
|
|
2835
|
+
cursor: {
|
|
2836
|
+
id: "cursor",
|
|
2837
|
+
name: "Cursor",
|
|
2838
|
+
description: "Cursor IDE (.cursor/rules/)",
|
|
2839
|
+
rulesPath: ".cursor/rules",
|
|
2840
|
+
workflowsPath: ".cursor/commands",
|
|
2841
|
+
fileExtension: ".mdc",
|
|
2842
|
+
metadataFormat: "yaml-frontmatter",
|
|
2843
|
+
supportsReference: true
|
|
2844
|
+
},
|
|
2845
|
+
windsurf: {
|
|
2846
|
+
id: "windsurf",
|
|
2847
|
+
name: "Windsurf",
|
|
2848
|
+
description: "Windsurf IDE (.windsurf/rules/)",
|
|
2849
|
+
rulesPath: ".windsurf/rules",
|
|
2850
|
+
workflowsPath: ".windsurf/workflows",
|
|
2851
|
+
fileExtension: ".md",
|
|
2852
|
+
metadataFormat: "yaml-frontmatter",
|
|
2853
|
+
supportsReference: true
|
|
2854
|
+
},
|
|
2855
|
+
antigravity: {
|
|
2856
|
+
id: "antigravity",
|
|
2857
|
+
name: "Antigravity",
|
|
2858
|
+
description: "Antigravity IDE (.agent/rules/)",
|
|
2859
|
+
rulesPath: ".agent/rules",
|
|
2860
|
+
workflowsPath: ".agent/workflows",
|
|
2861
|
+
fileExtension: ".md",
|
|
2862
|
+
metadataFormat: "yaml-frontmatter",
|
|
2863
|
+
supportsReference: true
|
|
2864
|
+
},
|
|
2865
|
+
claude: {
|
|
2866
|
+
id: "claude",
|
|
2867
|
+
name: "Claude Code",
|
|
2868
|
+
description: "Claude Code (.claude/rules/)",
|
|
2869
|
+
rulesPath: ".claude/rules",
|
|
2870
|
+
fileExtension: ".md",
|
|
2871
|
+
metadataFormat: "yaml-frontmatter",
|
|
2872
|
+
supportsReference: true
|
|
2873
|
+
},
|
|
2874
|
+
agentsmd: {
|
|
2875
|
+
id: "agentsmd",
|
|
2876
|
+
name: "AGENTS.md",
|
|
2877
|
+
description: "Single AGENTS.md file",
|
|
2878
|
+
rulesPath: ".",
|
|
2879
|
+
workflowsPath: void 0,
|
|
2880
|
+
fileExtension: ".md",
|
|
2881
|
+
metadataFormat: "none",
|
|
2882
|
+
supportsReference: false
|
|
2883
|
+
},
|
|
2884
|
+
gemini: {
|
|
2885
|
+
id: "gemini",
|
|
2886
|
+
name: "Gemini CLI",
|
|
2887
|
+
description: "Gemini CLI (GEMINI.md)",
|
|
2888
|
+
rulesPath: ".",
|
|
2889
|
+
fileExtension: ".md",
|
|
2890
|
+
metadataFormat: "none",
|
|
2891
|
+
supportsReference: false,
|
|
2892
|
+
dependencies: ["agentsmd"]
|
|
2893
|
+
// Gemini requires AGENTS.md
|
|
2894
|
+
}
|
|
2895
|
+
};
|
|
2896
|
+
var JAI1_BASE_RULE = `# Jai1 Framework Agent
|
|
2897
|
+
|
|
2898
|
+
You are an AI coding assistant integrated with the Jai1 Framework.
|
|
2899
|
+
|
|
2900
|
+
## Skills Reference
|
|
2901
|
+
|
|
2902
|
+
The Jai1 Framework provides specialized skills that you can reference and apply:
|
|
2903
|
+
|
|
2904
|
+
### Pattern Detection
|
|
2905
|
+
|
|
2906
|
+
When you encounter:
|
|
2907
|
+
- **Component creation**: Reference \`@jai1-component-patterns\`
|
|
2908
|
+
- **API development**: Reference \`@jai1-api-patterns\`
|
|
2909
|
+
- **Database operations**: Reference \`@jai1-database-patterns\`
|
|
2910
|
+
- **Testing**: Reference \`@jai1-testing-patterns\`
|
|
2911
|
+
- **Deployment**: Reference \`@jai1-deployment-patterns\`
|
|
2912
|
+
|
|
2913
|
+
### Skill Loading Procedure
|
|
2914
|
+
|
|
2915
|
+
1. **Detect the task type** from user request
|
|
2916
|
+
2. **Load relevant skill** using \`@jai1-[skill-name]\` syntax
|
|
2917
|
+
3. **Apply patterns** from the skill to your response
|
|
2918
|
+
4. **Follow conventions** defined in the skill
|
|
2919
|
+
|
|
2920
|
+
Example:
|
|
2921
|
+
\`\`\`
|
|
2922
|
+
User: "Create a new API endpoint for users"
|
|
2923
|
+
Assistant:
|
|
2924
|
+
1. Loading @jai1-api-patterns
|
|
2925
|
+
2. Applying RESTful conventions
|
|
2926
|
+
3. Following project structure from @jai1-component-patterns
|
|
2927
|
+
4. Implementing validation using @jai1-testing-patterns
|
|
2928
|
+
\`\`\`
|
|
2929
|
+
|
|
2930
|
+
### Application
|
|
2931
|
+
|
|
2932
|
+
- **Always reference skills** before implementing patterns
|
|
2933
|
+
- **Follow naming conventions** from loaded skills
|
|
2934
|
+
- **Apply best practices** defined in skills
|
|
2935
|
+
- **Maintain consistency** with existing codebase patterns
|
|
2936
|
+
|
|
2937
|
+
### Naming Convention
|
|
2938
|
+
|
|
2939
|
+
Skills are named using the pattern: \`@jai1-[domain]-patterns\`
|
|
2940
|
+
|
|
2941
|
+
Examples:
|
|
2942
|
+
- \`@jai1-component-patterns\`
|
|
2943
|
+
- \`@jai1-api-patterns\`
|
|
2944
|
+
- \`@jai1-database-patterns\`
|
|
2945
|
+
- \`@jai1-testing-patterns\`
|
|
2946
|
+
- \`@jai1-deployment-patterns\`
|
|
2947
|
+
|
|
2948
|
+
## Best Practices
|
|
2949
|
+
|
|
2950
|
+
1. **Load before applying**: Always reference the appropriate skill before implementing
|
|
2951
|
+
2. **Follow project structure**: Use patterns from \`@jai1-component-patterns\`
|
|
2952
|
+
3. **Maintain consistency**: Apply conventions consistently across the codebase
|
|
2953
|
+
4. **Document decisions**: Explain why specific patterns are used
|
|
2954
|
+
|
|
2955
|
+
## Integration
|
|
2956
|
+
|
|
2957
|
+
This base rule works in conjunction with:
|
|
2958
|
+
- Project-specific rules (e.g., 01-project.md)
|
|
2959
|
+
- Technology-specific rules (e.g., 03-frontend.md)
|
|
2960
|
+
- Custom rules (e.g., 09-custom.md)
|
|
2961
|
+
- AGENTS.md (if present)
|
|
2962
|
+
|
|
2963
|
+
When conflicts arise, prioritize:
|
|
2964
|
+
1. Custom rules (09-custom.*)
|
|
2965
|
+
2. Project rules (01-project.*)
|
|
2966
|
+
3. This base rule (00-jai1.*)
|
|
2967
|
+
`;
|
|
2968
|
+
|
|
2969
|
+
// src/services/ide-detection.service.ts
|
|
2970
|
+
var IdeDetectionService = class {
|
|
2971
|
+
projectPath;
|
|
2972
|
+
constructor(projectPath = process.cwd()) {
|
|
2973
|
+
this.projectPath = projectPath;
|
|
2974
|
+
}
|
|
2975
|
+
/**
|
|
2976
|
+
* Detect all active IDEs in the project
|
|
2977
|
+
*/
|
|
2978
|
+
async detectActiveIdes() {
|
|
2979
|
+
const detected = [];
|
|
2980
|
+
for (const [ideId, format] of Object.entries(IDE_FORMATS)) {
|
|
2981
|
+
const detection = await this.detectIde(ideId, format);
|
|
2982
|
+
detected.push(detection);
|
|
2983
|
+
}
|
|
2984
|
+
return detected.filter((d) => d.detected);
|
|
2985
|
+
}
|
|
2986
|
+
/**
|
|
2987
|
+
* Detect a specific IDE
|
|
2988
|
+
*/
|
|
2989
|
+
async detectIde(ideId, format) {
|
|
2990
|
+
const detection = {
|
|
2991
|
+
id: ideId,
|
|
2992
|
+
name: format.name,
|
|
2993
|
+
detected: false,
|
|
2994
|
+
hasRules: false,
|
|
2995
|
+
hasWorkflows: false,
|
|
2996
|
+
ruleCount: 0,
|
|
2997
|
+
workflowCount: 0,
|
|
2998
|
+
confidence: "low"
|
|
2999
|
+
};
|
|
3000
|
+
if (ideId === "agentsmd") {
|
|
3001
|
+
const exists = await this.pathExists("AGENTS.md");
|
|
3002
|
+
detection.detected = exists;
|
|
3003
|
+
detection.hasRules = exists;
|
|
3004
|
+
detection.ruleCount = exists ? 1 : 0;
|
|
3005
|
+
detection.confidence = exists ? "high" : "low";
|
|
3006
|
+
return detection;
|
|
3007
|
+
}
|
|
3008
|
+
if (ideId === "gemini") {
|
|
3009
|
+
const exists = await this.pathExists("GEMINI.md");
|
|
3010
|
+
detection.detected = exists;
|
|
3011
|
+
detection.hasRules = exists;
|
|
3012
|
+
detection.ruleCount = exists ? 1 : 0;
|
|
3013
|
+
detection.confidence = exists ? "high" : "low";
|
|
3014
|
+
return detection;
|
|
3015
|
+
}
|
|
3016
|
+
const rulesPath = join4(this.projectPath, format.rulesPath);
|
|
3017
|
+
const rulesExist = await this.pathExists(rulesPath);
|
|
3018
|
+
if (rulesExist) {
|
|
3019
|
+
detection.hasRules = true;
|
|
3020
|
+
detection.ruleCount = await this.countFiles(rulesPath, format.fileExtension);
|
|
3021
|
+
if (detection.ruleCount > 0) {
|
|
3022
|
+
detection.detected = true;
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
3025
|
+
if (format.workflowsPath) {
|
|
3026
|
+
const workflowsPath = join4(this.projectPath, format.workflowsPath);
|
|
3027
|
+
const workflowsExist = await this.pathExists(workflowsPath);
|
|
3028
|
+
if (workflowsExist) {
|
|
3029
|
+
detection.hasWorkflows = true;
|
|
3030
|
+
detection.workflowCount = await this.countFiles(workflowsPath, format.fileExtension);
|
|
3031
|
+
if (detection.workflowCount > 0) {
|
|
3032
|
+
detection.detected = true;
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
if (detection.ruleCount > 3 || detection.workflowCount > 0) {
|
|
3037
|
+
detection.confidence = "high";
|
|
3038
|
+
} else if (detection.ruleCount > 0) {
|
|
3039
|
+
detection.confidence = "medium";
|
|
3040
|
+
}
|
|
3041
|
+
return detection;
|
|
3042
|
+
}
|
|
3043
|
+
/**
|
|
3044
|
+
* Get the most likely active IDE (highest confidence)
|
|
3045
|
+
*/
|
|
3046
|
+
async getPrimaryIde() {
|
|
3047
|
+
const active = await this.detectActiveIdes();
|
|
3048
|
+
if (active.length === 0) {
|
|
3049
|
+
return null;
|
|
3050
|
+
}
|
|
3051
|
+
active.sort((a, b) => {
|
|
3052
|
+
const confidenceScore = { high: 3, medium: 2, low: 1 };
|
|
3053
|
+
const aScore = confidenceScore[a.confidence] * 10 + a.ruleCount;
|
|
3054
|
+
const bScore = confidenceScore[b.confidence] * 10 + b.ruleCount;
|
|
3055
|
+
return bScore - aScore;
|
|
3056
|
+
});
|
|
3057
|
+
return active[0];
|
|
3058
|
+
}
|
|
3059
|
+
/**
|
|
3060
|
+
* Get IDE IDs that are active
|
|
3061
|
+
*/
|
|
3062
|
+
async getActiveIdeIds() {
|
|
3063
|
+
const active = await this.detectActiveIdes();
|
|
3064
|
+
return active.map((d) => d.id);
|
|
3065
|
+
}
|
|
3066
|
+
/**
|
|
3067
|
+
* Check if a specific IDE is active
|
|
3068
|
+
*/
|
|
3069
|
+
async isIdeActive(ideId) {
|
|
3070
|
+
const format = IDE_FORMATS[ideId];
|
|
3071
|
+
if (!format) {
|
|
3072
|
+
return false;
|
|
3073
|
+
}
|
|
3074
|
+
const detection = await this.detectIde(ideId, format);
|
|
3075
|
+
return detection.detected;
|
|
3076
|
+
}
|
|
3077
|
+
/**
|
|
3078
|
+
* Detect IDEs with existing rule files (for backward compatibility)
|
|
3079
|
+
*/
|
|
3080
|
+
async detectExistingIdes() {
|
|
3081
|
+
const detected = [];
|
|
3082
|
+
for (const [ideId, format] of Object.entries(IDE_FORMATS)) {
|
|
3083
|
+
if (ideId === "agentsmd") {
|
|
3084
|
+
if (await this.pathExists("AGENTS.md")) {
|
|
3085
|
+
detected.push(ideId);
|
|
3086
|
+
}
|
|
3087
|
+
} else if (ideId === "gemini") {
|
|
3088
|
+
if (await this.pathExists("GEMINI.md")) {
|
|
3089
|
+
detected.push(ideId);
|
|
3090
|
+
}
|
|
3091
|
+
} else {
|
|
3092
|
+
const rulesPath = join4(this.projectPath, format.rulesPath);
|
|
3093
|
+
if (await this.pathExists(rulesPath)) {
|
|
3094
|
+
const count = await this.countFiles(rulesPath, format.fileExtension);
|
|
3095
|
+
if (count > 0) {
|
|
3096
|
+
detected.push(ideId);
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
return detected;
|
|
3102
|
+
}
|
|
3103
|
+
/**
|
|
3104
|
+
* Get detection summary
|
|
3105
|
+
*/
|
|
3106
|
+
async getSummary() {
|
|
3107
|
+
const all = await Promise.all(
|
|
3108
|
+
Object.entries(IDE_FORMATS).map(([ideId, format]) => this.detectIde(ideId, format))
|
|
3109
|
+
);
|
|
3110
|
+
const active = all.filter((d) => d.detected);
|
|
3111
|
+
const inactive = all.filter((d) => !d.detected).map((d) => d.id);
|
|
3112
|
+
const primary = await this.getPrimaryIde();
|
|
3113
|
+
return {
|
|
3114
|
+
total: all.length,
|
|
3115
|
+
active,
|
|
3116
|
+
inactive,
|
|
3117
|
+
primary
|
|
3118
|
+
};
|
|
3119
|
+
}
|
|
3120
|
+
/**
|
|
3121
|
+
* Check if a path exists
|
|
3122
|
+
*/
|
|
3123
|
+
async pathExists(path8) {
|
|
3124
|
+
try {
|
|
3125
|
+
await fs7.access(path8);
|
|
3126
|
+
return true;
|
|
3127
|
+
} catch {
|
|
3128
|
+
return false;
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
/**
|
|
3132
|
+
* Count files with specific extension in a directory
|
|
3133
|
+
*/
|
|
3134
|
+
async countFiles(dirPath, extension) {
|
|
3135
|
+
try {
|
|
3136
|
+
const entries = await fs7.readdir(dirPath, { withFileTypes: true });
|
|
3137
|
+
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(extension)).length;
|
|
3138
|
+
} catch {
|
|
3139
|
+
return 0;
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
3142
|
+
/**
|
|
3143
|
+
* Get IDE format by ID
|
|
3144
|
+
*/
|
|
3145
|
+
getIdeFormat(ideId) {
|
|
3146
|
+
return IDE_FORMATS[ideId];
|
|
3147
|
+
}
|
|
3148
|
+
/**
|
|
3149
|
+
* Suggest IDEs to configure based on project structure
|
|
3150
|
+
*/
|
|
3151
|
+
async suggestIdes() {
|
|
3152
|
+
const suggestions = [];
|
|
3153
|
+
if (await this.pathExists(join4(this.projectPath, ".vscode"))) {
|
|
3154
|
+
suggestions.push({
|
|
3155
|
+
ideId: "cursor",
|
|
3156
|
+
name: "Cursor",
|
|
3157
|
+
reason: "VSCode configuration detected",
|
|
3158
|
+
priority: "high"
|
|
3159
|
+
});
|
|
3160
|
+
}
|
|
3161
|
+
if (await this.pathExists(join4(this.projectPath, "package.json"))) {
|
|
3162
|
+
suggestions.push({
|
|
3163
|
+
ideId: "windsurf",
|
|
3164
|
+
name: "Windsurf",
|
|
3165
|
+
reason: "JavaScript/TypeScript project detected",
|
|
3166
|
+
priority: "medium"
|
|
3167
|
+
});
|
|
3168
|
+
}
|
|
3169
|
+
suggestions.push({
|
|
3170
|
+
ideId: "agentsmd",
|
|
3171
|
+
name: "AGENTS.md",
|
|
3172
|
+
reason: "Universal format, works with all IDEs",
|
|
3173
|
+
priority: "high"
|
|
3174
|
+
});
|
|
3175
|
+
const hasClaudeConfig = await this.pathExists(join4(this.projectPath, ".claude"));
|
|
3176
|
+
if (hasClaudeConfig) {
|
|
3177
|
+
suggestions.push({
|
|
3178
|
+
ideId: "claude",
|
|
3179
|
+
name: "Claude Code",
|
|
3180
|
+
reason: ".claude directory detected",
|
|
3181
|
+
priority: "high"
|
|
3182
|
+
});
|
|
3183
|
+
}
|
|
3184
|
+
return suggestions;
|
|
3185
|
+
}
|
|
3186
|
+
};
|
|
3187
|
+
|
|
3188
|
+
// src/commands/ide/status.ts
|
|
3189
|
+
function createStatusSubcommand() {
|
|
3190
|
+
const cmd = new Command9("status").description("Show detected IDEs and their configuration status").option("--json", "Output as JSON").option("--suggestions", "Show IDE suggestions based on project structure").action(async (options) => {
|
|
3191
|
+
await runStatus(options);
|
|
3192
|
+
});
|
|
3193
|
+
return cmd;
|
|
3194
|
+
}
|
|
3195
|
+
async function runStatus(options) {
|
|
3196
|
+
const service = new IdeDetectionService();
|
|
3197
|
+
if (options.json) {
|
|
3198
|
+
const summary2 = await service.getSummary();
|
|
3199
|
+
console.log(JSON.stringify(summary2, null, 2));
|
|
3200
|
+
return;
|
|
3201
|
+
}
|
|
3202
|
+
console.log("\n\u{1F50D} IDE Detection Status\n");
|
|
3203
|
+
const summary = await service.getSummary();
|
|
3204
|
+
if (summary.active.length === 0) {
|
|
3205
|
+
console.log("\u26A0\uFE0F No active IDEs detected\n");
|
|
3206
|
+
console.log("\u{1F4A1} Tips:");
|
|
3207
|
+
console.log(' \u2022 Run "jai1 rules apply" to configure IDE rules');
|
|
3208
|
+
console.log(" \u2022 Create rules in .cursor/rules/, .windsurf/rules/, etc.");
|
|
3209
|
+
console.log(" \u2022 Create AGENTS.md for universal IDE support\n");
|
|
3210
|
+
} else {
|
|
3211
|
+
console.log(`\u{1F4CA} Found ${summary.active.length} active IDE(s):
|
|
3212
|
+
`);
|
|
3213
|
+
if (summary.primary) {
|
|
3214
|
+
const confidence = getConfidenceEmoji(summary.primary.confidence);
|
|
3215
|
+
console.log(`\u{1F3AF} Primary IDE: ${summary.primary.name} ${confidence}
|
|
3216
|
+
`);
|
|
3217
|
+
}
|
|
3218
|
+
for (const ide of summary.active) {
|
|
3219
|
+
const confidence = getConfidenceEmoji(ide.confidence);
|
|
3220
|
+
const isPrimary = summary.primary?.id === ide.id;
|
|
3221
|
+
const marker = isPrimary ? "\u{1F3AF}" : " ";
|
|
3222
|
+
console.log(`${marker} ${ide.name} ${confidence}`);
|
|
3223
|
+
if (ide.hasRules) {
|
|
3224
|
+
console.log(` Rules: ${ide.ruleCount} file(s)`);
|
|
3225
|
+
}
|
|
3226
|
+
if (ide.hasWorkflows) {
|
|
3227
|
+
console.log(` Workflows: ${ide.workflowCount} file(s)`);
|
|
3228
|
+
}
|
|
3229
|
+
const format = service.getIdeFormat(ide.id);
|
|
3230
|
+
if (format) {
|
|
3231
|
+
if (ide.id === "agentsmd") {
|
|
3232
|
+
console.log(` Location: AGENTS.md`);
|
|
3233
|
+
} else if (ide.id === "gemini") {
|
|
3234
|
+
console.log(` Location: GEMINI.md`);
|
|
3235
|
+
} else {
|
|
3236
|
+
console.log(` Location: ${format.rulesPath}/`);
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
console.log("");
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
3242
|
+
if (summary.inactive.length > 0) {
|
|
3243
|
+
console.log(`\u{1F4CB} Available IDEs (not configured):
|
|
3244
|
+
`);
|
|
3245
|
+
for (const ideId of summary.inactive) {
|
|
3246
|
+
const format = service.getIdeFormat(ideId);
|
|
3247
|
+
if (format) {
|
|
3248
|
+
console.log(` \u2022 ${format.name} - ${format.description}`);
|
|
3249
|
+
}
|
|
3250
|
+
}
|
|
3251
|
+
console.log("");
|
|
3252
|
+
}
|
|
3253
|
+
if (options.suggestions) {
|
|
3254
|
+
const suggestions = await service.suggestIdes();
|
|
3255
|
+
if (suggestions.length > 0) {
|
|
3256
|
+
console.log("\u{1F4A1} Suggested IDEs for this project:\n");
|
|
3257
|
+
for (const suggestion of suggestions) {
|
|
3258
|
+
const priorityEmoji = suggestion.priority === "high" ? "\u2B50" : suggestion.priority === "medium" ? "\u{1F538}" : "\u25AB\uFE0F";
|
|
3259
|
+
console.log(`${priorityEmoji} ${suggestion.name}`);
|
|
3260
|
+
console.log(` Reason: ${suggestion.reason}
|
|
3261
|
+
`);
|
|
3262
|
+
}
|
|
3263
|
+
}
|
|
3264
|
+
}
|
|
3265
|
+
console.log("\u{1F4DD} Commands:\n");
|
|
3266
|
+
console.log(' \u2022 "jai1 rules apply" - Configure rules for IDEs');
|
|
3267
|
+
console.log(' \u2022 "jai1 rules sync" - Regenerate IDE outputs');
|
|
3268
|
+
console.log(' \u2022 "jai1 ide sync" - Sync .jai1/ content to IDEs');
|
|
3269
|
+
console.log(' \u2022 "jai1 ide status --suggestions" - Get IDE recommendations\n');
|
|
3270
|
+
}
|
|
3271
|
+
function getConfidenceEmoji(confidence) {
|
|
3272
|
+
switch (confidence) {
|
|
3273
|
+
case "high":
|
|
3274
|
+
return "\u{1F7E2}";
|
|
3275
|
+
case "medium":
|
|
3276
|
+
return "\u{1F7E1}";
|
|
3277
|
+
case "low":
|
|
3278
|
+
return "\u{1F534}";
|
|
3279
|
+
default:
|
|
3280
|
+
return "\u26AA";
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
|
|
2826
3284
|
// src/commands/ide/index.ts
|
|
2827
3285
|
function createIdeCommand() {
|
|
2828
|
-
const ideCommand = new
|
|
3286
|
+
const ideCommand = new Command10("ide").description("IDE integration and configuration commands");
|
|
2829
3287
|
ideCommand.addCommand(createContextSubcommand());
|
|
2830
3288
|
ideCommand.addCommand(createSetupSubcommand());
|
|
2831
3289
|
ideCommand.addCommand(createSyncSubcommand());
|
|
3290
|
+
ideCommand.addCommand(createStatusSubcommand());
|
|
2832
3291
|
ideCommand.action(() => {
|
|
2833
3292
|
ideCommand.help();
|
|
2834
3293
|
});
|
|
@@ -2838,7 +3297,7 @@ function createIdeCommand() {
|
|
|
2838
3297
|
// src/commands/learn.ts
|
|
2839
3298
|
import React20 from "react";
|
|
2840
3299
|
import { render as render3 } from "ink";
|
|
2841
|
-
import { Command as
|
|
3300
|
+
import { Command as Command11 } from "commander";
|
|
2842
3301
|
|
|
2843
3302
|
// src/ui/guide/GuideApp.tsx
|
|
2844
3303
|
import React19, { useState as useState9, useEffect as useEffect3 } from "react";
|
|
@@ -2888,7 +3347,7 @@ var MENU_ITEMS = [
|
|
|
2888
3347
|
];
|
|
2889
3348
|
var MenuView = ({ onSelect }) => {
|
|
2890
3349
|
const [selectedIndex, setSelectedIndex] = useState7(0);
|
|
2891
|
-
useInput7((
|
|
3350
|
+
useInput7((input4, key) => {
|
|
2892
3351
|
if (key.upArrow) {
|
|
2893
3352
|
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
2894
3353
|
} else if (key.downArrow) {
|
|
@@ -3501,7 +3960,7 @@ var AgenticGuideView = ({ onBack }) => {
|
|
|
3501
3960
|
const [isTyping, setIsTyping] = useState8(false);
|
|
3502
3961
|
const [selectedSuggestion, setSelectedSuggestion] = useState8(0);
|
|
3503
3962
|
const [focusArea, setFocusArea] = useState8("input");
|
|
3504
|
-
useInput8((
|
|
3963
|
+
useInput8((input4, key) => {
|
|
3505
3964
|
if (key.escape) {
|
|
3506
3965
|
onBack();
|
|
3507
3966
|
return;
|
|
@@ -3633,7 +4092,7 @@ var GuideApp = ({ initialTopic, onExit }) => {
|
|
|
3633
4092
|
setCurrentTopic(TOPIC_MAP[initialTopic]);
|
|
3634
4093
|
}
|
|
3635
4094
|
}, [initialTopic]);
|
|
3636
|
-
useInput9((
|
|
4095
|
+
useInput9((input4, key) => {
|
|
3637
4096
|
if (key.escape || currentTopic !== "menu" && key.backspace) {
|
|
3638
4097
|
if (currentTopic === "menu") {
|
|
3639
4098
|
onExit();
|
|
@@ -3643,7 +4102,7 @@ var GuideApp = ({ initialTopic, onExit }) => {
|
|
|
3643
4102
|
}
|
|
3644
4103
|
return;
|
|
3645
4104
|
}
|
|
3646
|
-
if (
|
|
4105
|
+
if (input4 === "q") {
|
|
3647
4106
|
onExit();
|
|
3648
4107
|
return;
|
|
3649
4108
|
}
|
|
@@ -3692,7 +4151,7 @@ var GuideApp = ({ initialTopic, onExit }) => {
|
|
|
3692
4151
|
|
|
3693
4152
|
// src/commands/learn.ts
|
|
3694
4153
|
function createLearnCommand() {
|
|
3695
|
-
const cmd = new
|
|
4154
|
+
const cmd = new Command11("learn").description("Interactive Agentic Coding learning center").option("--topic <topic>", "Open specific topic (intro, rules, workflows, prompts, skills)").action(async (options) => {
|
|
3696
4155
|
const { waitUntilExit } = render3(
|
|
3697
4156
|
React20.createElement(GuideApp, {
|
|
3698
4157
|
initialTopic: options.topic,
|
|
@@ -3707,7 +4166,7 @@ function createLearnCommand() {
|
|
|
3707
4166
|
}
|
|
3708
4167
|
|
|
3709
4168
|
// src/commands/chat.ts
|
|
3710
|
-
import { Command as
|
|
4169
|
+
import { Command as Command12 } from "commander";
|
|
3711
4170
|
import React26 from "react";
|
|
3712
4171
|
import { render as render4 } from "ink";
|
|
3713
4172
|
|
|
@@ -4142,7 +4601,7 @@ var ModelSelector = ({
|
|
|
4142
4601
|
const allowedModels = models.filter((m) => m.allowed);
|
|
4143
4602
|
const currentIndex = allowedModels.findIndex((m) => m.id === currentModel);
|
|
4144
4603
|
const [selectedIndex, setSelectedIndex] = useState12(Math.max(0, currentIndex));
|
|
4145
|
-
useInput10((
|
|
4604
|
+
useInput10((input4, key) => {
|
|
4146
4605
|
if (key.escape) {
|
|
4147
4606
|
onCancel();
|
|
4148
4607
|
return;
|
|
@@ -4268,7 +4727,7 @@ var ChatApp = ({ service, initialModel }) => {
|
|
|
4268
4727
|
}
|
|
4269
4728
|
return false;
|
|
4270
4729
|
}, [refetch, exit]);
|
|
4271
|
-
useInput11((
|
|
4730
|
+
useInput11((input4, key) => {
|
|
4272
4731
|
if (showSlashMenu) {
|
|
4273
4732
|
if (key.upArrow) {
|
|
4274
4733
|
setSlashMenuIndex((i) => Math.max(0, i - 1));
|
|
@@ -4296,7 +4755,7 @@ var ChatApp = ({ service, initialModel }) => {
|
|
|
4296
4755
|
}
|
|
4297
4756
|
return;
|
|
4298
4757
|
}
|
|
4299
|
-
if (currentView === "error" && (key.return ||
|
|
4758
|
+
if (currentView === "error" && (key.return || input4 === "r")) {
|
|
4300
4759
|
setCurrentView("loading");
|
|
4301
4760
|
refetch();
|
|
4302
4761
|
return;
|
|
@@ -4386,14 +4845,14 @@ async function handleChatCommand(options) {
|
|
|
4386
4845
|
clear();
|
|
4387
4846
|
}
|
|
4388
4847
|
function createChatCommand() {
|
|
4389
|
-
const cmd = new
|
|
4848
|
+
const cmd = new Command12("chat").description("Interactive AI chat with Jai1 LLM Proxy").option("--model <model>", "Initial model to use (e.g., gpt-4o, claude-3-opus)").action(async (options) => {
|
|
4390
4849
|
await handleChatCommand(options);
|
|
4391
4850
|
});
|
|
4392
4851
|
return cmd;
|
|
4393
4852
|
}
|
|
4394
4853
|
|
|
4395
4854
|
// src/commands/openai-keys.ts
|
|
4396
|
-
import { Command as
|
|
4855
|
+
import { Command as Command13 } from "commander";
|
|
4397
4856
|
function maskKey2(key) {
|
|
4398
4857
|
if (key.length <= 8) return "****";
|
|
4399
4858
|
return key.slice(0, 8) + "****" + key.slice(-4);
|
|
@@ -4447,14 +4906,14 @@ async function handleOpenAiKeysCommand(options) {
|
|
|
4447
4906
|
}
|
|
4448
4907
|
}
|
|
4449
4908
|
function createOpenAiKeysCommand() {
|
|
4450
|
-
const cmd = new
|
|
4909
|
+
const cmd = new Command13("openai-keys").description("Show OpenAI-compatible API credentials and info").option("--full", "Show full API key (unmasked)").action(async (options) => {
|
|
4451
4910
|
await handleOpenAiKeysCommand(options);
|
|
4452
4911
|
});
|
|
4453
4912
|
return cmd;
|
|
4454
4913
|
}
|
|
4455
4914
|
|
|
4456
4915
|
// src/commands/stats.ts
|
|
4457
|
-
import { Command as
|
|
4916
|
+
import { Command as Command14 } from "commander";
|
|
4458
4917
|
async function handleStatsCommand() {
|
|
4459
4918
|
const configService = new ConfigService();
|
|
4460
4919
|
const config = await configService.load();
|
|
@@ -4524,17 +4983,17 @@ async function handleStatsCommand() {
|
|
|
4524
4983
|
}
|
|
4525
4984
|
}
|
|
4526
4985
|
function createStatsCommand() {
|
|
4527
|
-
const cmd = new
|
|
4986
|
+
const cmd = new Command14("stats").description("Hi\u1EC3n th\u1ECB th\u1ED1ng k\xEA s\u1EED d\u1EE5ng LLM v\xE0 gi\u1EDBi h\u1EA1n").action(async () => {
|
|
4528
4987
|
await handleStatsCommand();
|
|
4529
4988
|
});
|
|
4530
4989
|
return cmd;
|
|
4531
4990
|
}
|
|
4532
4991
|
|
|
4533
4992
|
// src/commands/translate.ts
|
|
4534
|
-
import { Command as
|
|
4993
|
+
import { Command as Command15 } from "commander";
|
|
4535
4994
|
|
|
4536
4995
|
// src/services/translation.service.ts
|
|
4537
|
-
import { promises as
|
|
4996
|
+
import { promises as fs8 } from "fs";
|
|
4538
4997
|
import path4 from "path";
|
|
4539
4998
|
import pLimit from "p-limit";
|
|
4540
4999
|
import pRetry from "p-retry";
|
|
@@ -4551,9 +5010,9 @@ var TranslationService = class {
|
|
|
4551
5010
|
/**
|
|
4552
5011
|
* Detect input type
|
|
4553
5012
|
*/
|
|
4554
|
-
async detectInputType(
|
|
5013
|
+
async detectInputType(input4) {
|
|
4555
5014
|
try {
|
|
4556
|
-
const stat = await
|
|
5015
|
+
const stat = await fs8.stat(input4);
|
|
4557
5016
|
if (stat.isDirectory()) return "folder";
|
|
4558
5017
|
if (stat.isFile()) return "file";
|
|
4559
5018
|
} catch {
|
|
@@ -4590,13 +5049,13 @@ var TranslationService = class {
|
|
|
4590
5049
|
*/
|
|
4591
5050
|
async translateFile(filePath) {
|
|
4592
5051
|
try {
|
|
4593
|
-
const content = await
|
|
5052
|
+
const content = await fs8.readFile(filePath, "utf-8");
|
|
4594
5053
|
const ext = path4.extname(filePath).toLowerCase();
|
|
4595
5054
|
const fileType = this.getFileType(ext);
|
|
4596
5055
|
const translatedContent = await this.translateWithRetry(content, fileType);
|
|
4597
5056
|
const outputPath = this.generateOutputPath(filePath);
|
|
4598
5057
|
if (!this.options.dryRun) {
|
|
4599
|
-
await
|
|
5058
|
+
await fs8.writeFile(outputPath, translatedContent, "utf-8");
|
|
4600
5059
|
}
|
|
4601
5060
|
return {
|
|
4602
5061
|
inputPath: filePath,
|
|
@@ -4661,7 +5120,7 @@ var TranslationService = class {
|
|
|
4661
5120
|
* Discover translatable files in folder recursively
|
|
4662
5121
|
*/
|
|
4663
5122
|
async discoverFiles(folderPath) {
|
|
4664
|
-
const entries = await
|
|
5123
|
+
const entries = await fs8.readdir(folderPath, { withFileTypes: true });
|
|
4665
5124
|
const files = [];
|
|
4666
5125
|
for (const entry of entries) {
|
|
4667
5126
|
const fullPath = path4.join(folderPath, entry.name);
|
|
@@ -4785,7 +5244,7 @@ Return ONLY the translated content, no explanations.`;
|
|
|
4785
5244
|
};
|
|
4786
5245
|
|
|
4787
5246
|
// src/commands/translate.ts
|
|
4788
|
-
async function handleTranslate(
|
|
5247
|
+
async function handleTranslate(input4, options) {
|
|
4789
5248
|
const configService = new ConfigService();
|
|
4790
5249
|
const config = await configService.load();
|
|
4791
5250
|
if (!config) {
|
|
@@ -4793,16 +5252,16 @@ async function handleTranslate(input2, options) {
|
|
|
4793
5252
|
}
|
|
4794
5253
|
const llmService = new LlmProxyService(config);
|
|
4795
5254
|
const translationService = new TranslationService(llmService, options, options.model);
|
|
4796
|
-
const inputType = await translationService.detectInputType(
|
|
5255
|
+
const inputType = await translationService.detectInputType(input4);
|
|
4797
5256
|
switch (inputType) {
|
|
4798
5257
|
case "text":
|
|
4799
|
-
await handleTextTranslation(translationService,
|
|
5258
|
+
await handleTextTranslation(translationService, input4, options);
|
|
4800
5259
|
break;
|
|
4801
5260
|
case "file":
|
|
4802
|
-
await handleFileTranslation(translationService,
|
|
5261
|
+
await handleFileTranslation(translationService, input4, options);
|
|
4803
5262
|
break;
|
|
4804
5263
|
case "folder":
|
|
4805
|
-
await handleFolderTranslation(translationService,
|
|
5264
|
+
await handleFolderTranslation(translationService, input4, options);
|
|
4806
5265
|
break;
|
|
4807
5266
|
}
|
|
4808
5267
|
}
|
|
@@ -4879,17 +5338,17 @@ async function handleFolderTranslation(service, folderPath, options) {
|
|
|
4879
5338
|
}
|
|
4880
5339
|
}
|
|
4881
5340
|
function createTranslateCommand() {
|
|
4882
|
-
const cmd = new
|
|
4883
|
-
await handleTranslate(
|
|
5341
|
+
const cmd = new Command15("translate").description("D\u1ECBch v\u0103n b\u1EA3n, file ho\u1EB7c th\u01B0 m\u1EE5c b\u1EB1ng AI").argument("<input>", "Chu\u1ED7i v\u0103n b\u1EA3n, \u0111\u01B0\u1EDDng d\u1EABn file ho\u1EB7c th\u01B0 m\u1EE5c").option("--to <language>", "M\xE3 ng\xF4n ng\u1EEF \u0111\xEDch (m\u1EB7c \u0111\u1ECBnh: vi)", "vi").option("-o, --output <path>", "\u0110\u01B0\u1EDDng d\u1EABn output (file/th\u01B0 m\u1EE5c)").option("--dry-run", "Xem tr\u01B0\u1EDBc kh\xF4ng l\u01B0u file").option("--concurrency <number>", "S\u1ED1 file x\u1EED l\xFD song song (m\u1EB7c \u0111\u1ECBnh: 3)", "3").option("--model <model>", "Model LLM (m\u1EB7c \u0111\u1ECBnh: gpt-5.1-codex-mini)").option("--json", "Xu\u1EA5t k\u1EBFt qu\u1EA3 d\u1EA1ng JSON").action(async (input4, options) => {
|
|
5342
|
+
await handleTranslate(input4, options);
|
|
4884
5343
|
});
|
|
4885
5344
|
return cmd;
|
|
4886
5345
|
}
|
|
4887
5346
|
|
|
4888
5347
|
// src/commands/utils/index.ts
|
|
4889
|
-
import { Command as
|
|
5348
|
+
import { Command as Command29 } from "commander";
|
|
4890
5349
|
|
|
4891
5350
|
// src/commands/utils/password.ts
|
|
4892
|
-
import { Command as
|
|
5351
|
+
import { Command as Command16 } from "commander";
|
|
4893
5352
|
|
|
4894
5353
|
// src/services/utils.service.ts
|
|
4895
5354
|
import crypto from "crypto";
|
|
@@ -4940,12 +5399,12 @@ var UtilsService = class {
|
|
|
4940
5399
|
/**
|
|
4941
5400
|
* Hash text or file using specified algorithm
|
|
4942
5401
|
*/
|
|
4943
|
-
async hash(
|
|
5402
|
+
async hash(input4, algorithm) {
|
|
4944
5403
|
if (algorithm === "bcrypt") {
|
|
4945
5404
|
throw new Error("Use hashBcrypt for bcrypt algorithm");
|
|
4946
5405
|
}
|
|
4947
5406
|
const hash = crypto.createHash(algorithm);
|
|
4948
|
-
hash.update(
|
|
5407
|
+
hash.update(input4);
|
|
4949
5408
|
return hash.digest("hex");
|
|
4950
5409
|
}
|
|
4951
5410
|
/**
|
|
@@ -4958,14 +5417,14 @@ var UtilsService = class {
|
|
|
4958
5417
|
/**
|
|
4959
5418
|
* Hash using bcrypt
|
|
4960
5419
|
*/
|
|
4961
|
-
async hashBcrypt(
|
|
4962
|
-
return bcrypt.hash(
|
|
5420
|
+
async hashBcrypt(input4, rounds = 10) {
|
|
5421
|
+
return bcrypt.hash(input4, rounds);
|
|
4963
5422
|
}
|
|
4964
5423
|
/**
|
|
4965
5424
|
* Base64 encode
|
|
4966
5425
|
*/
|
|
4967
|
-
base64Encode(
|
|
4968
|
-
const buffer = typeof
|
|
5426
|
+
base64Encode(input4, urlSafe = false) {
|
|
5427
|
+
const buffer = typeof input4 === "string" ? Buffer.from(input4, "utf-8") : input4;
|
|
4969
5428
|
let encoded = buffer.toString("base64");
|
|
4970
5429
|
if (urlSafe) {
|
|
4971
5430
|
encoded = encoded.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
@@ -4975,8 +5434,8 @@ var UtilsService = class {
|
|
|
4975
5434
|
/**
|
|
4976
5435
|
* Base64 decode
|
|
4977
5436
|
*/
|
|
4978
|
-
base64Decode(
|
|
4979
|
-
let normalized =
|
|
5437
|
+
base64Decode(input4) {
|
|
5438
|
+
let normalized = input4.replace(/-/g, "+").replace(/_/g, "/");
|
|
4980
5439
|
while (normalized.length % 4) {
|
|
4981
5440
|
normalized += "=";
|
|
4982
5441
|
}
|
|
@@ -5105,14 +5564,14 @@ var UtilsService = class {
|
|
|
5105
5564
|
/**
|
|
5106
5565
|
* URL encode
|
|
5107
5566
|
*/
|
|
5108
|
-
urlEncode(
|
|
5109
|
-
return full ? encodeURI(
|
|
5567
|
+
urlEncode(input4, full = false) {
|
|
5568
|
+
return full ? encodeURI(input4) : encodeURIComponent(input4);
|
|
5110
5569
|
}
|
|
5111
5570
|
/**
|
|
5112
5571
|
* URL decode
|
|
5113
5572
|
*/
|
|
5114
|
-
urlDecode(
|
|
5115
|
-
return full ? decodeURI(
|
|
5573
|
+
urlDecode(input4, full = false) {
|
|
5574
|
+
return full ? decodeURI(input4) : decodeURIComponent(input4);
|
|
5116
5575
|
}
|
|
5117
5576
|
/**
|
|
5118
5577
|
* Format bytes to human-readable size
|
|
@@ -5158,7 +5617,7 @@ async function handlePasswordGeneration(options) {
|
|
|
5158
5617
|
}
|
|
5159
5618
|
}
|
|
5160
5619
|
function createPasswordCommand() {
|
|
5161
|
-
const cmd = new
|
|
5620
|
+
const cmd = new Command16("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", `
|
|
5162
5621
|
Examples:
|
|
5163
5622
|
$ jai1 utils password
|
|
5164
5623
|
$ jai1 utils password --length 24
|
|
@@ -5176,7 +5635,7 @@ Examples:
|
|
|
5176
5635
|
}
|
|
5177
5636
|
|
|
5178
5637
|
// src/commands/utils/uuid.ts
|
|
5179
|
-
import { Command as
|
|
5638
|
+
import { Command as Command17 } from "commander";
|
|
5180
5639
|
async function handleUuidGeneration(options) {
|
|
5181
5640
|
const service = new UtilsService();
|
|
5182
5641
|
try {
|
|
@@ -5204,7 +5663,7 @@ async function handleUuidGeneration(options) {
|
|
|
5204
5663
|
}
|
|
5205
5664
|
}
|
|
5206
5665
|
function createUuidCommand() {
|
|
5207
|
-
const cmd = new
|
|
5666
|
+
const cmd = new Command17("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", `
|
|
5208
5667
|
Examples:
|
|
5209
5668
|
$ jai1 utils uuid
|
|
5210
5669
|
$ jai1 utils uuid --count 10
|
|
@@ -5221,8 +5680,8 @@ Examples:
|
|
|
5221
5680
|
}
|
|
5222
5681
|
|
|
5223
5682
|
// src/commands/utils/hash.ts
|
|
5224
|
-
import { Command as
|
|
5225
|
-
async function handleHashGeneration(
|
|
5683
|
+
import { Command as Command18 } from "commander";
|
|
5684
|
+
async function handleHashGeneration(input4, options) {
|
|
5226
5685
|
const service = new UtilsService();
|
|
5227
5686
|
try {
|
|
5228
5687
|
let hash;
|
|
@@ -5233,14 +5692,14 @@ async function handleHashGeneration(input2, options) {
|
|
|
5233
5692
|
}
|
|
5234
5693
|
hash = await service.hashFile(options.file, options.algorithm);
|
|
5235
5694
|
} else {
|
|
5236
|
-
if (!
|
|
5695
|
+
if (!input4) {
|
|
5237
5696
|
console.error("\u274C Please provide input text or use --file option");
|
|
5238
5697
|
process.exit(1);
|
|
5239
5698
|
}
|
|
5240
5699
|
if (options.algorithm === "bcrypt") {
|
|
5241
|
-
hash = await service.hashBcrypt(
|
|
5700
|
+
hash = await service.hashBcrypt(input4, options.rounds);
|
|
5242
5701
|
} else {
|
|
5243
|
-
hash = await service.hash(
|
|
5702
|
+
hash = await service.hash(input4, options.algorithm);
|
|
5244
5703
|
}
|
|
5245
5704
|
}
|
|
5246
5705
|
console.log("\u{1F512} Hash Result:");
|
|
@@ -5261,7 +5720,7 @@ async function handleHashGeneration(input2, options) {
|
|
|
5261
5720
|
}
|
|
5262
5721
|
}
|
|
5263
5722
|
function createHashCommand() {
|
|
5264
|
-
const cmd = new
|
|
5723
|
+
const cmd = new Command18("hash").description("Generate hash (MD5, SHA, bcrypt)").argument("[input]", "Text to hash").option(
|
|
5265
5724
|
"-a, --algorithm <algorithm>",
|
|
5266
5725
|
"Hash algorithm (md5, sha1, sha256, sha512, bcrypt)",
|
|
5267
5726
|
"sha256"
|
|
@@ -5273,8 +5732,8 @@ Examples:
|
|
|
5273
5732
|
$ jai1 utils hash "password" --algorithm bcrypt --rounds 12
|
|
5274
5733
|
$ jai1 utils hash --file ./myfile.txt
|
|
5275
5734
|
$ jai1 utils hash --file ./image.png --algorithm sha512
|
|
5276
|
-
`).action(async (
|
|
5277
|
-
await handleHashGeneration(
|
|
5735
|
+
`).action(async (input4, options) => {
|
|
5736
|
+
await handleHashGeneration(input4, {
|
|
5278
5737
|
...options,
|
|
5279
5738
|
rounds: parseInt(options.rounds, 10)
|
|
5280
5739
|
});
|
|
@@ -5283,9 +5742,9 @@ Examples:
|
|
|
5283
5742
|
}
|
|
5284
5743
|
|
|
5285
5744
|
// src/commands/utils/base64-encode.ts
|
|
5286
|
-
import { Command as
|
|
5745
|
+
import { Command as Command19 } from "commander";
|
|
5287
5746
|
import { readFile as readFile2 } from "fs/promises";
|
|
5288
|
-
async function handleBase64Encode(
|
|
5747
|
+
async function handleBase64Encode(input4, options) {
|
|
5289
5748
|
const service = new UtilsService();
|
|
5290
5749
|
try {
|
|
5291
5750
|
let encoded;
|
|
@@ -5293,11 +5752,11 @@ async function handleBase64Encode(input2, options) {
|
|
|
5293
5752
|
const content = await readFile2(options.file);
|
|
5294
5753
|
encoded = service.base64Encode(content, options.urlSafe);
|
|
5295
5754
|
} else {
|
|
5296
|
-
if (!
|
|
5755
|
+
if (!input4) {
|
|
5297
5756
|
console.error("\u274C Please provide input text or use --file option");
|
|
5298
5757
|
process.exit(1);
|
|
5299
5758
|
}
|
|
5300
|
-
encoded = service.base64Encode(
|
|
5759
|
+
encoded = service.base64Encode(input4, options.urlSafe);
|
|
5301
5760
|
}
|
|
5302
5761
|
console.log("\u{1F4DD} Base64 Encoded:");
|
|
5303
5762
|
console.log("\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");
|
|
@@ -5316,33 +5775,33 @@ async function handleBase64Encode(input2, options) {
|
|
|
5316
5775
|
}
|
|
5317
5776
|
}
|
|
5318
5777
|
function createBase64EncodeCommand() {
|
|
5319
|
-
const cmd = new
|
|
5778
|
+
const cmd = new Command19("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", `
|
|
5320
5779
|
Examples:
|
|
5321
5780
|
$ jai1 utils base64-encode "hello world"
|
|
5322
5781
|
$ jai1 utils base64-encode "hello world" --url-safe
|
|
5323
5782
|
$ jai1 utils base64-encode --file ./document.txt
|
|
5324
5783
|
$ jai1 utils base64-encode --file ./image.png
|
|
5325
|
-
`).action(async (
|
|
5326
|
-
await handleBase64Encode(
|
|
5784
|
+
`).action(async (input4, options) => {
|
|
5785
|
+
await handleBase64Encode(input4, options);
|
|
5327
5786
|
});
|
|
5328
5787
|
return cmd;
|
|
5329
5788
|
}
|
|
5330
5789
|
|
|
5331
5790
|
// src/commands/utils/base64-decode.ts
|
|
5332
|
-
import { Command as
|
|
5791
|
+
import { Command as Command20 } from "commander";
|
|
5333
5792
|
import { readFile as readFile3, writeFile } from "fs/promises";
|
|
5334
|
-
async function handleBase64Decode(
|
|
5793
|
+
async function handleBase64Decode(input4, options) {
|
|
5335
5794
|
const service = new UtilsService();
|
|
5336
5795
|
try {
|
|
5337
5796
|
let encodedInput;
|
|
5338
5797
|
if (options.file) {
|
|
5339
5798
|
encodedInput = await readFile3(options.file, "utf-8");
|
|
5340
5799
|
} else {
|
|
5341
|
-
if (!
|
|
5800
|
+
if (!input4) {
|
|
5342
5801
|
console.error("\u274C Please provide Base64 input or use --file option");
|
|
5343
5802
|
process.exit(1);
|
|
5344
5803
|
}
|
|
5345
|
-
encodedInput =
|
|
5804
|
+
encodedInput = input4;
|
|
5346
5805
|
}
|
|
5347
5806
|
const decoded = service.base64Decode(encodedInput.trim());
|
|
5348
5807
|
if (options.output) {
|
|
@@ -5361,20 +5820,20 @@ async function handleBase64Decode(input2, options) {
|
|
|
5361
5820
|
}
|
|
5362
5821
|
}
|
|
5363
5822
|
function createBase64DecodeCommand() {
|
|
5364
|
-
const cmd = new
|
|
5823
|
+
const cmd = new Command20("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", `
|
|
5365
5824
|
Examples:
|
|
5366
5825
|
$ jai1 utils base64-decode "aGVsbG8gd29ybGQ="
|
|
5367
5826
|
$ jai1 utils base64-decode --file ./encoded.txt
|
|
5368
5827
|
$ jai1 utils base64-decode "aGVsbG8=" --output ./decoded.txt
|
|
5369
5828
|
$ jai1 utils base64-decode --file ./encoded.txt --output ./image.png
|
|
5370
|
-
`).action(async (
|
|
5371
|
-
await handleBase64Decode(
|
|
5829
|
+
`).action(async (input4, options) => {
|
|
5830
|
+
await handleBase64Decode(input4, options);
|
|
5372
5831
|
});
|
|
5373
5832
|
return cmd;
|
|
5374
5833
|
}
|
|
5375
5834
|
|
|
5376
5835
|
// src/commands/utils/http.ts
|
|
5377
|
-
import { Command as
|
|
5836
|
+
import { Command as Command21 } from "commander";
|
|
5378
5837
|
import { readFile as readFile4 } from "fs/promises";
|
|
5379
5838
|
async function handleHttpRequest(url, options) {
|
|
5380
5839
|
const service = new UtilsService();
|
|
@@ -5440,7 +5899,7 @@ async function handleHttpRequest(url, options) {
|
|
|
5440
5899
|
}
|
|
5441
5900
|
}
|
|
5442
5901
|
function createHttpCommand() {
|
|
5443
|
-
const cmd = new
|
|
5902
|
+
const cmd = new Command21("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", `
|
|
5444
5903
|
Examples:
|
|
5445
5904
|
$ jai1 utils http https://api.example.com/users
|
|
5446
5905
|
$ jai1 utils http https://api.example.com/users -X POST -d '{"name":"John"}'
|
|
@@ -5458,7 +5917,7 @@ Examples:
|
|
|
5458
5917
|
}
|
|
5459
5918
|
|
|
5460
5919
|
// src/commands/utils/jwt.ts
|
|
5461
|
-
import { Command as
|
|
5920
|
+
import { Command as Command22 } from "commander";
|
|
5462
5921
|
async function handleJwtDecode(token) {
|
|
5463
5922
|
const service = new UtilsService();
|
|
5464
5923
|
try {
|
|
@@ -5497,7 +5956,7 @@ async function handleJwtEncode(options) {
|
|
|
5497
5956
|
}
|
|
5498
5957
|
}
|
|
5499
5958
|
function createJwtCommand() {
|
|
5500
|
-
const jwtCommand = new
|
|
5959
|
+
const jwtCommand = new Command22("jwt").description("Decode and encode JWT tokens");
|
|
5501
5960
|
jwtCommand.command("decode").description("Decode JWT token").argument("<token>", "JWT token to decode").addHelpText("after", `
|
|
5502
5961
|
Examples:
|
|
5503
5962
|
$ jai1 utils jwt decode "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
|
|
@@ -5518,11 +5977,11 @@ Examples:
|
|
|
5518
5977
|
}
|
|
5519
5978
|
|
|
5520
5979
|
// src/commands/utils/unix-time.ts
|
|
5521
|
-
import { Command as
|
|
5522
|
-
async function handleUnixTime(
|
|
5980
|
+
import { Command as Command23 } from "commander";
|
|
5981
|
+
async function handleUnixTime(input4, options) {
|
|
5523
5982
|
const service = new UtilsService();
|
|
5524
5983
|
try {
|
|
5525
|
-
if (!
|
|
5984
|
+
if (!input4) {
|
|
5526
5985
|
const now = /* @__PURE__ */ new Date();
|
|
5527
5986
|
const timestamp = service.dateToUnix(now, options.ms);
|
|
5528
5987
|
console.log("\u{1F552} Current Unix Timestamp:");
|
|
@@ -5532,8 +5991,8 @@ async function handleUnixTime(input2, options) {
|
|
|
5532
5991
|
console.log(` ISO: ${now.toISOString()}`);
|
|
5533
5992
|
console.log(` Local: ${now.toLocaleString()}`);
|
|
5534
5993
|
console.log();
|
|
5535
|
-
} else if (/^\d+$/.test(
|
|
5536
|
-
const timestamp = parseInt(
|
|
5994
|
+
} else if (/^\d+$/.test(input4)) {
|
|
5995
|
+
const timestamp = parseInt(input4, 10);
|
|
5537
5996
|
const date = service.unixToDate(timestamp, options.ms);
|
|
5538
5997
|
console.log("\u{1F552} Unix Timestamp to Date:");
|
|
5539
5998
|
console.log("\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");
|
|
@@ -5546,7 +6005,7 @@ async function handleUnixTime(input2, options) {
|
|
|
5546
6005
|
}
|
|
5547
6006
|
console.log();
|
|
5548
6007
|
} else {
|
|
5549
|
-
const date = new Date(
|
|
6008
|
+
const date = new Date(input4);
|
|
5550
6009
|
if (isNaN(date.getTime())) {
|
|
5551
6010
|
console.error("\u274C Invalid date format");
|
|
5552
6011
|
process.exit(1);
|
|
@@ -5554,7 +6013,7 @@ async function handleUnixTime(input2, options) {
|
|
|
5554
6013
|
const timestamp = service.dateToUnix(date, options.ms);
|
|
5555
6014
|
console.log("\u{1F552} Date to Unix Timestamp:");
|
|
5556
6015
|
console.log("\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");
|
|
5557
|
-
console.log(` Input: ${
|
|
6016
|
+
console.log(` Input: ${input4}`);
|
|
5558
6017
|
console.log(` Parsed: ${date.toISOString()}`);
|
|
5559
6018
|
console.log();
|
|
5560
6019
|
console.log(` ${timestamp}${options.ms ? " (ms)" : ""}`);
|
|
@@ -5566,7 +6025,7 @@ async function handleUnixTime(input2, options) {
|
|
|
5566
6025
|
}
|
|
5567
6026
|
}
|
|
5568
6027
|
function createUnixTimeCommand() {
|
|
5569
|
-
const cmd = new
|
|
6028
|
+
const cmd = new Command23("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", `
|
|
5570
6029
|
Examples:
|
|
5571
6030
|
$ jai1 utils unix-time
|
|
5572
6031
|
$ jai1 utils unix-time 1702550400
|
|
@@ -5574,14 +6033,14 @@ Examples:
|
|
|
5574
6033
|
$ jai1 utils unix-time "2024-01-15 10:30:00"
|
|
5575
6034
|
$ jai1 utils unix-time "2024-01-15" --format local
|
|
5576
6035
|
$ jai1 utils unix-time --ms
|
|
5577
|
-
`).action(async (
|
|
5578
|
-
await handleUnixTime(
|
|
6036
|
+
`).action(async (input4, options) => {
|
|
6037
|
+
await handleUnixTime(input4, options);
|
|
5579
6038
|
});
|
|
5580
6039
|
return cmd;
|
|
5581
6040
|
}
|
|
5582
6041
|
|
|
5583
6042
|
// src/commands/utils/timezone.ts
|
|
5584
|
-
import { Command as
|
|
6043
|
+
import { Command as Command24 } from "commander";
|
|
5585
6044
|
async function handleTimezoneConversion(time, options) {
|
|
5586
6045
|
const service = new UtilsService();
|
|
5587
6046
|
try {
|
|
@@ -5618,7 +6077,7 @@ async function handleTimezoneConversion(time, options) {
|
|
|
5618
6077
|
}
|
|
5619
6078
|
}
|
|
5620
6079
|
function createTimezoneCommand() {
|
|
5621
|
-
const cmd = new
|
|
6080
|
+
const cmd = new Command24("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", `
|
|
5622
6081
|
Examples:
|
|
5623
6082
|
$ jai1 utils timezone --list
|
|
5624
6083
|
$ jai1 utils timezone "2024-01-15 10:00" --from "America/New_York" --to "Asia/Tokyo"
|
|
@@ -5631,11 +6090,11 @@ Examples:
|
|
|
5631
6090
|
}
|
|
5632
6091
|
|
|
5633
6092
|
// src/commands/utils/url-encode.ts
|
|
5634
|
-
import { Command as
|
|
5635
|
-
async function handleUrlEncode(
|
|
6093
|
+
import { Command as Command25 } from "commander";
|
|
6094
|
+
async function handleUrlEncode(input4, options) {
|
|
5636
6095
|
const service = new UtilsService();
|
|
5637
6096
|
try {
|
|
5638
|
-
const encoded = service.urlEncode(
|
|
6097
|
+
const encoded = service.urlEncode(input4, options.full);
|
|
5639
6098
|
console.log("\u{1F517} URL Encoded:");
|
|
5640
6099
|
console.log("\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");
|
|
5641
6100
|
if (options.full) {
|
|
@@ -5652,24 +6111,24 @@ async function handleUrlEncode(input2, options) {
|
|
|
5652
6111
|
}
|
|
5653
6112
|
}
|
|
5654
6113
|
function createUrlEncodeCommand() {
|
|
5655
|
-
const cmd = new
|
|
6114
|
+
const cmd = new Command25("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", `
|
|
5656
6115
|
Examples:
|
|
5657
6116
|
$ jai1 utils url-encode "hello world"
|
|
5658
6117
|
$ jai1 utils url-encode "hello world & test"
|
|
5659
6118
|
$ jai1 utils url-encode "name=John Doe&age=30"
|
|
5660
6119
|
$ jai1 utils url-encode "https://example.com/path with spaces" --full
|
|
5661
|
-
`).showHelpAfterError("(add --help for additional examples)").action(async (
|
|
5662
|
-
await handleUrlEncode(
|
|
6120
|
+
`).showHelpAfterError("(add --help for additional examples)").action(async (input4, options) => {
|
|
6121
|
+
await handleUrlEncode(input4, options);
|
|
5663
6122
|
});
|
|
5664
6123
|
return cmd;
|
|
5665
6124
|
}
|
|
5666
6125
|
|
|
5667
6126
|
// src/commands/utils/url-decode.ts
|
|
5668
|
-
import { Command as
|
|
5669
|
-
async function handleUrlDecode(
|
|
6127
|
+
import { Command as Command26 } from "commander";
|
|
6128
|
+
async function handleUrlDecode(input4, options) {
|
|
5670
6129
|
const service = new UtilsService();
|
|
5671
6130
|
try {
|
|
5672
|
-
const decoded = service.urlDecode(
|
|
6131
|
+
const decoded = service.urlDecode(input4, options.full);
|
|
5673
6132
|
console.log("\u{1F517} URL Decoded:");
|
|
5674
6133
|
console.log("\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");
|
|
5675
6134
|
if (options.full) {
|
|
@@ -5686,20 +6145,20 @@ async function handleUrlDecode(input2, options) {
|
|
|
5686
6145
|
}
|
|
5687
6146
|
}
|
|
5688
6147
|
function createUrlDecodeCommand() {
|
|
5689
|
-
const cmd = new
|
|
6148
|
+
const cmd = new Command26("url-decode").description("Decode URL-encoded string").argument("<input>", "Text to decode").option("--full", "Decode full URL (use decodeURI instead of decodeURIComponent)").addHelpText("after", `
|
|
5690
6149
|
Examples:
|
|
5691
6150
|
$ jai1 utils url-decode "hello%20world"
|
|
5692
6151
|
$ jai1 utils url-decode "hello%20world%20%26%20test"
|
|
5693
6152
|
$ jai1 utils url-decode "name%3DJohn%20Doe%26age%3D30"
|
|
5694
6153
|
$ jai1 utils url-decode "https://example.com/path%20with%20spaces" --full
|
|
5695
|
-
`).showHelpAfterError("(add --help for additional examples)").action(async (
|
|
5696
|
-
await handleUrlDecode(
|
|
6154
|
+
`).showHelpAfterError("(add --help for additional examples)").action(async (input4, options) => {
|
|
6155
|
+
await handleUrlDecode(input4, options);
|
|
5697
6156
|
});
|
|
5698
6157
|
return cmd;
|
|
5699
6158
|
}
|
|
5700
6159
|
|
|
5701
6160
|
// src/commands/utils/cron.ts
|
|
5702
|
-
import { Command as
|
|
6161
|
+
import { Command as Command27 } from "commander";
|
|
5703
6162
|
import cronstrue from "cronstrue";
|
|
5704
6163
|
async function handleCronParse(expression) {
|
|
5705
6164
|
try {
|
|
@@ -5742,7 +6201,7 @@ async function handleCronParse(expression) {
|
|
|
5742
6201
|
}
|
|
5743
6202
|
}
|
|
5744
6203
|
function createCronCommand() {
|
|
5745
|
-
const cmd = new
|
|
6204
|
+
const cmd = new Command27("cron").description("Parse and explain cron expression").argument("<expression>", 'Cron expression (e.g., "0 5 * * *")').addHelpText("after", `
|
|
5746
6205
|
Examples:
|
|
5747
6206
|
$ jai1 utils cron "0 5 * * *"
|
|
5748
6207
|
$ jai1 utils cron "*/15 * * * *"
|
|
@@ -5756,7 +6215,7 @@ Examples:
|
|
|
5756
6215
|
}
|
|
5757
6216
|
|
|
5758
6217
|
// src/commands/utils/markdown-preview.ts
|
|
5759
|
-
import { Command as
|
|
6218
|
+
import { Command as Command28 } from "commander";
|
|
5760
6219
|
import { readFile as readFile5 } from "fs/promises";
|
|
5761
6220
|
import { marked } from "marked";
|
|
5762
6221
|
import TerminalRenderer from "marked-terminal";
|
|
@@ -5795,7 +6254,7 @@ ${code.trim()}
|
|
|
5795
6254
|
}
|
|
5796
6255
|
}
|
|
5797
6256
|
function createMarkdownPreviewCommand() {
|
|
5798
|
-
const cmd = new
|
|
6257
|
+
const cmd = new Command28("markdown-preview").description("Preview markdown file in terminal").argument("<file>", "Markdown file path").option("--raw", "Show raw markdown instead of rendered").addHelpText("after", `
|
|
5799
6258
|
Examples:
|
|
5800
6259
|
$ jai1 utils markdown-preview README.md
|
|
5801
6260
|
$ jai1 utils markdown-preview ./docs/guide.md
|
|
@@ -5829,14 +6288,14 @@ var PasswordView = () => {
|
|
|
5829
6288
|
React27.useEffect(() => {
|
|
5830
6289
|
handleGenerate();
|
|
5831
6290
|
}, []);
|
|
5832
|
-
useInput12((
|
|
6291
|
+
useInput12((input4, key) => {
|
|
5833
6292
|
if (key.tab) {
|
|
5834
6293
|
if (focusedField === "length") setFocusedField("count");
|
|
5835
6294
|
else if (focusedField === "count") setFocusedField("generate");
|
|
5836
6295
|
else setFocusedField("length");
|
|
5837
6296
|
} else if (key.return) {
|
|
5838
6297
|
handleGenerate();
|
|
5839
|
-
} else if (
|
|
6298
|
+
} else if (input4 === "c" && passwords.length > 0) {
|
|
5840
6299
|
handleCopy(0);
|
|
5841
6300
|
}
|
|
5842
6301
|
});
|
|
@@ -5919,7 +6378,7 @@ var UuidView = () => {
|
|
|
5919
6378
|
React28.useEffect(() => {
|
|
5920
6379
|
handleGenerate();
|
|
5921
6380
|
}, []);
|
|
5922
|
-
useInput13((
|
|
6381
|
+
useInput13((input4, key) => {
|
|
5923
6382
|
if (key.tab) {
|
|
5924
6383
|
const fields = ["count", "uppercase", "hyphens", "generate"];
|
|
5925
6384
|
const currentIndex = fields.indexOf(focusedField);
|
|
@@ -5932,7 +6391,7 @@ var UuidView = () => {
|
|
|
5932
6391
|
} else {
|
|
5933
6392
|
handleGenerate();
|
|
5934
6393
|
}
|
|
5935
|
-
} else if (
|
|
6394
|
+
} else if (input4 === "c" && uuids.length > 0) {
|
|
5936
6395
|
handleCopy(0);
|
|
5937
6396
|
}
|
|
5938
6397
|
});
|
|
@@ -6007,7 +6466,7 @@ var HashView = () => {
|
|
|
6007
6466
|
React29.useEffect(() => {
|
|
6008
6467
|
handleGenerate();
|
|
6009
6468
|
}, []);
|
|
6010
|
-
useInput14((
|
|
6469
|
+
useInput14((input4, key) => {
|
|
6011
6470
|
if (key.tab) {
|
|
6012
6471
|
const fields = ["text", "algorithm", "generate"];
|
|
6013
6472
|
const currentIndex = fields.indexOf(focusedField);
|
|
@@ -6020,7 +6479,7 @@ var HashView = () => {
|
|
|
6020
6479
|
} else if (key.rightArrow && focusedField === "algorithm") {
|
|
6021
6480
|
const currentIndex = ALGORITHMS.indexOf(algorithm);
|
|
6022
6481
|
setAlgorithm(ALGORITHMS[(currentIndex + 1) % ALGORITHMS.length]);
|
|
6023
|
-
} else if (
|
|
6482
|
+
} else if (input4 === "c" && hash) {
|
|
6024
6483
|
handleCopy();
|
|
6025
6484
|
}
|
|
6026
6485
|
});
|
|
@@ -6086,7 +6545,7 @@ import React30, { useState as useState17 } from "react";
|
|
|
6086
6545
|
import { Box as Box20, Text as Text21, useInput as useInput15 } from "ink";
|
|
6087
6546
|
import TextInput7 from "ink-text-input";
|
|
6088
6547
|
var Base64View = () => {
|
|
6089
|
-
const [
|
|
6548
|
+
const [input4, setInput] = useState17("");
|
|
6090
6549
|
const [mode, setMode] = useState17("encode");
|
|
6091
6550
|
const [urlSafe, setUrlSafe] = useState17(false);
|
|
6092
6551
|
const [result, setResult] = useState17("");
|
|
@@ -6094,7 +6553,7 @@ var Base64View = () => {
|
|
|
6094
6553
|
const [focusedField, setFocusedField] = useState17("input");
|
|
6095
6554
|
const [copied, setCopied] = useState17(false);
|
|
6096
6555
|
const service = new UtilsService();
|
|
6097
|
-
useInput15((
|
|
6556
|
+
useInput15((input5, key) => {
|
|
6098
6557
|
if (key.tab) {
|
|
6099
6558
|
const fields = ["input", "mode", "urlsafe", "convert"];
|
|
6100
6559
|
const currentIndex = fields.indexOf(focusedField);
|
|
@@ -6109,22 +6568,22 @@ var Base64View = () => {
|
|
|
6109
6568
|
} else {
|
|
6110
6569
|
handleConvert();
|
|
6111
6570
|
}
|
|
6112
|
-
} else if (
|
|
6571
|
+
} else if (input5 === "c" && result) {
|
|
6113
6572
|
handleCopy();
|
|
6114
6573
|
}
|
|
6115
6574
|
});
|
|
6116
6575
|
const handleConvert = () => {
|
|
6117
|
-
if (!
|
|
6576
|
+
if (!input4) {
|
|
6118
6577
|
setError("Input cannot be empty");
|
|
6119
6578
|
return;
|
|
6120
6579
|
}
|
|
6121
6580
|
try {
|
|
6122
6581
|
setError("");
|
|
6123
6582
|
if (mode === "encode") {
|
|
6124
|
-
const encoded = service.base64Encode(
|
|
6583
|
+
const encoded = service.base64Encode(input4, urlSafe);
|
|
6125
6584
|
setResult(encoded);
|
|
6126
6585
|
} else {
|
|
6127
|
-
const decoded = service.base64Decode(
|
|
6586
|
+
const decoded = service.base64Decode(input4);
|
|
6128
6587
|
setResult(decoded.toString("utf-8"));
|
|
6129
6588
|
}
|
|
6130
6589
|
setCopied(false);
|
|
@@ -6153,7 +6612,7 @@ var Base64View = () => {
|
|
|
6153
6612
|
marginBottom: 1
|
|
6154
6613
|
},
|
|
6155
6614
|
/* @__PURE__ */ React30.createElement(Text21, { bold: true, color: "yellow", marginBottom: 1 }, "Options:"),
|
|
6156
|
-
/* @__PURE__ */ React30.createElement(Box20, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React30.createElement(Box20, { marginBottom: 0 }, /* @__PURE__ */ React30.createElement(Text21, { color: focusedField === "input" ? "green" : void 0 }, focusedField === "input" ? "\u25B6 " : " ", "Input text:")), /* @__PURE__ */ React30.createElement(Box20, { marginLeft: 2, width: 60 }, focusedField === "input" ? /* @__PURE__ */ React30.createElement(TextInput7, { value:
|
|
6615
|
+
/* @__PURE__ */ React30.createElement(Box20, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React30.createElement(Box20, { marginBottom: 0 }, /* @__PURE__ */ React30.createElement(Text21, { color: focusedField === "input" ? "green" : void 0 }, focusedField === "input" ? "\u25B6 " : " ", "Input text:")), /* @__PURE__ */ React30.createElement(Box20, { marginLeft: 2, width: 60 }, focusedField === "input" ? /* @__PURE__ */ React30.createElement(TextInput7, { value: input4, onChange: setInput, placeholder: "Enter text..." }) : /* @__PURE__ */ React30.createElement(Text21, { dimColor: !input4 }, input4 || "(empty)"))),
|
|
6157
6616
|
/* @__PURE__ */ React30.createElement(Box20, { marginBottom: 1 }, /* @__PURE__ */ React30.createElement(Box20, { width: 20 }, /* @__PURE__ */ React30.createElement(Text21, { color: focusedField === "mode" ? "green" : void 0 }, focusedField === "mode" ? "\u25B6 " : " ", "Mode:")), /* @__PURE__ */ React30.createElement(Text21, { bold: true, color: focusedField === "mode" ? "yellow" : void 0 }, mode === "encode" ? "ENCODE" : "DECODE"), focusedField === "mode" && /* @__PURE__ */ React30.createElement(Text21, { dimColor: true }, " (Enter to toggle)")),
|
|
6158
6617
|
mode === "encode" && /* @__PURE__ */ React30.createElement(Box20, { marginBottom: 1 }, /* @__PURE__ */ React30.createElement(Text21, { color: focusedField === "urlsafe" ? "green" : void 0 }, focusedField === "urlsafe" ? "\u25B6 " : " ", "[", urlSafe ? "\u2713" : " ", "] URL-Safe (+ \u2192 -, / \u2192 _)")),
|
|
6159
6618
|
/* @__PURE__ */ React30.createElement(Box20, { marginTop: 1 }, /* @__PURE__ */ React30.createElement(
|
|
@@ -6198,14 +6657,14 @@ import React31, { useState as useState18 } from "react";
|
|
|
6198
6657
|
import { Box as Box21, Text as Text22, useInput as useInput16 } from "ink";
|
|
6199
6658
|
import TextInput8 from "ink-text-input";
|
|
6200
6659
|
var UrlView = () => {
|
|
6201
|
-
const [
|
|
6660
|
+
const [input4, setInput] = useState18("");
|
|
6202
6661
|
const [mode, setMode] = useState18("encode");
|
|
6203
6662
|
const [fullUrl, setFullUrl] = useState18(false);
|
|
6204
6663
|
const [result, setResult] = useState18("");
|
|
6205
6664
|
const [focusedField, setFocusedField] = useState18("input");
|
|
6206
6665
|
const [copied, setCopied] = useState18(false);
|
|
6207
6666
|
const service = new UtilsService();
|
|
6208
|
-
useInput16((
|
|
6667
|
+
useInput16((input5, key) => {
|
|
6209
6668
|
if (key.tab) {
|
|
6210
6669
|
const fields = ["input", "mode", "full", "convert"];
|
|
6211
6670
|
const currentIndex = fields.indexOf(focusedField);
|
|
@@ -6219,17 +6678,17 @@ var UrlView = () => {
|
|
|
6219
6678
|
} else {
|
|
6220
6679
|
handleConvert();
|
|
6221
6680
|
}
|
|
6222
|
-
} else if (
|
|
6681
|
+
} else if (input5 === "c" && result) {
|
|
6223
6682
|
handleCopy();
|
|
6224
6683
|
}
|
|
6225
6684
|
});
|
|
6226
6685
|
const handleConvert = () => {
|
|
6227
|
-
if (!
|
|
6686
|
+
if (!input4) return;
|
|
6228
6687
|
if (mode === "encode") {
|
|
6229
|
-
const encoded = fullUrl ? service.urlEncode(
|
|
6688
|
+
const encoded = fullUrl ? service.urlEncode(input4, true) : service.urlEncode(input4, false);
|
|
6230
6689
|
setResult(encoded);
|
|
6231
6690
|
} else {
|
|
6232
|
-
const decoded = fullUrl ? service.urlDecode(
|
|
6691
|
+
const decoded = fullUrl ? service.urlDecode(input4, true) : service.urlDecode(input4, false);
|
|
6233
6692
|
setResult(decoded);
|
|
6234
6693
|
}
|
|
6235
6694
|
setCopied(false);
|
|
@@ -6254,7 +6713,7 @@ var UrlView = () => {
|
|
|
6254
6713
|
marginBottom: 1
|
|
6255
6714
|
},
|
|
6256
6715
|
/* @__PURE__ */ React31.createElement(Text22, { bold: true, color: "yellow", marginBottom: 1 }, "Options:"),
|
|
6257
|
-
/* @__PURE__ */ React31.createElement(Box21, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React31.createElement(Box21, { marginBottom: 0 }, /* @__PURE__ */ React31.createElement(Text22, { color: focusedField === "input" ? "green" : void 0 }, focusedField === "input" ? "\u25B6 " : " ", "Input text:")), /* @__PURE__ */ React31.createElement(Box21, { marginLeft: 2, width: 60 }, focusedField === "input" ? /* @__PURE__ */ React31.createElement(TextInput8, { value:
|
|
6716
|
+
/* @__PURE__ */ React31.createElement(Box21, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React31.createElement(Box21, { marginBottom: 0 }, /* @__PURE__ */ React31.createElement(Text22, { color: focusedField === "input" ? "green" : void 0 }, focusedField === "input" ? "\u25B6 " : " ", "Input text:")), /* @__PURE__ */ React31.createElement(Box21, { marginLeft: 2, width: 60 }, focusedField === "input" ? /* @__PURE__ */ React31.createElement(TextInput8, { value: input4, onChange: setInput, placeholder: "Enter text or URL..." }) : /* @__PURE__ */ React31.createElement(Text22, { dimColor: !input4 }, input4 || "(empty)"))),
|
|
6258
6717
|
/* @__PURE__ */ React31.createElement(Box21, { marginBottom: 1 }, /* @__PURE__ */ React31.createElement(Box21, { width: 20 }, /* @__PURE__ */ React31.createElement(Text22, { color: focusedField === "mode" ? "green" : void 0 }, focusedField === "mode" ? "\u25B6 " : " ", "Mode:")), /* @__PURE__ */ React31.createElement(Text22, { bold: true, color: focusedField === "mode" ? "yellow" : void 0 }, mode === "encode" ? "ENCODE" : "DECODE"), focusedField === "mode" && /* @__PURE__ */ React31.createElement(Text22, { dimColor: true }, " (Enter to toggle)")),
|
|
6259
6718
|
/* @__PURE__ */ React31.createElement(Box21, { marginBottom: 1 }, /* @__PURE__ */ React31.createElement(Text22, { color: focusedField === "full" ? "green" : void 0 }, focusedField === "full" ? "\u25B6 " : " ", "[", fullUrl ? "\u2713" : " ", "] Full URL (encode/decode entire URL)")),
|
|
6260
6719
|
/* @__PURE__ */ React31.createElement(Box21, { marginTop: 1 }, /* @__PURE__ */ React31.createElement(
|
|
@@ -6288,7 +6747,7 @@ import React32, { useState as useState19 } from "react";
|
|
|
6288
6747
|
import { Box as Box22, Text as Text23, useInput as useInput17 } from "ink";
|
|
6289
6748
|
import TextInput9 from "ink-text-input";
|
|
6290
6749
|
var UnixTimeView = () => {
|
|
6291
|
-
const [
|
|
6750
|
+
const [input4, setInput] = useState19("");
|
|
6292
6751
|
const [mode, setMode] = useState19("now");
|
|
6293
6752
|
const [format, setFormat] = useState19("iso");
|
|
6294
6753
|
const [useMs, setUseMs] = useState19(false);
|
|
@@ -6297,7 +6756,7 @@ var UnixTimeView = () => {
|
|
|
6297
6756
|
const [focusedField, setFocusedField] = useState19("mode");
|
|
6298
6757
|
const [copied, setCopied] = useState19(false);
|
|
6299
6758
|
const service = new UtilsService();
|
|
6300
|
-
useInput17((
|
|
6759
|
+
useInput17((input5, key) => {
|
|
6301
6760
|
if (key.tab) {
|
|
6302
6761
|
const fields = mode === "now" ? ["mode", "ms", "convert"] : mode === "to-human" ? ["mode", "input", "format", "convert"] : ["mode", "input", "convert"];
|
|
6303
6762
|
const currentIndex = fields.indexOf(focusedField);
|
|
@@ -6318,7 +6777,7 @@ var UnixTimeView = () => {
|
|
|
6318
6777
|
} else {
|
|
6319
6778
|
handleConvert();
|
|
6320
6779
|
}
|
|
6321
|
-
} else if (
|
|
6780
|
+
} else if (input5 === "c" && result) {
|
|
6322
6781
|
handleCopy();
|
|
6323
6782
|
}
|
|
6324
6783
|
});
|
|
@@ -6329,18 +6788,18 @@ var UnixTimeView = () => {
|
|
|
6329
6788
|
const timestamp = service.unixTimeCurrent(useMs);
|
|
6330
6789
|
setResult(timestamp.toString());
|
|
6331
6790
|
} else if (mode === "to-human") {
|
|
6332
|
-
if (!
|
|
6791
|
+
if (!input4) {
|
|
6333
6792
|
setError("Please enter a timestamp");
|
|
6334
6793
|
return;
|
|
6335
6794
|
}
|
|
6336
|
-
const human = service.unixTimeToHuman(parseInt(
|
|
6795
|
+
const human = service.unixTimeToHuman(parseInt(input4, 10), format);
|
|
6337
6796
|
setResult(human);
|
|
6338
6797
|
} else {
|
|
6339
|
-
if (!
|
|
6798
|
+
if (!input4) {
|
|
6340
6799
|
setError("Please enter a date string");
|
|
6341
6800
|
return;
|
|
6342
6801
|
}
|
|
6343
|
-
const timestamp = service.unixTimeFromString(
|
|
6802
|
+
const timestamp = service.unixTimeFromString(input4);
|
|
6344
6803
|
setResult(timestamp.toString());
|
|
6345
6804
|
}
|
|
6346
6805
|
setCopied(false);
|
|
@@ -6373,11 +6832,11 @@ var UnixTimeView = () => {
|
|
|
6373
6832
|
mode !== "now" && /* @__PURE__ */ React32.createElement(Box22, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React32.createElement(Box22, { marginBottom: 0 }, /* @__PURE__ */ React32.createElement(Text23, { color: focusedField === "input" ? "green" : void 0 }, focusedField === "input" ? "\u25B6 " : " ", mode === "to-human" ? "Timestamp:" : "Date string:")), /* @__PURE__ */ React32.createElement(Box22, { marginLeft: 2, width: 50 }, focusedField === "input" ? /* @__PURE__ */ React32.createElement(
|
|
6374
6833
|
TextInput9,
|
|
6375
6834
|
{
|
|
6376
|
-
value:
|
|
6835
|
+
value: input4,
|
|
6377
6836
|
onChange: setInput,
|
|
6378
6837
|
placeholder: mode === "to-human" ? "1702550400" : "2024-01-15 10:30:00"
|
|
6379
6838
|
}
|
|
6380
|
-
) : /* @__PURE__ */ React32.createElement(Text23, { dimColor: !
|
|
6839
|
+
) : /* @__PURE__ */ React32.createElement(Text23, { dimColor: !input4 }, input4 || "(empty)"))),
|
|
6381
6840
|
mode === "to-human" && /* @__PURE__ */ React32.createElement(Box22, { marginBottom: 1 }, /* @__PURE__ */ React32.createElement(Box22, { width: 20 }, /* @__PURE__ */ React32.createElement(Text23, { color: focusedField === "format" ? "green" : void 0 }, focusedField === "format" ? "\u25B6 " : " ", "Format:")), /* @__PURE__ */ React32.createElement(Text23, { bold: true, color: focusedField === "format" ? "yellow" : void 0 }, format.toUpperCase()), focusedField === "format" && /* @__PURE__ */ React32.createElement(Text23, { dimColor: true }, " (Enter to cycle)")),
|
|
6382
6841
|
mode === "now" && /* @__PURE__ */ React32.createElement(Box22, { marginBottom: 1 }, /* @__PURE__ */ React32.createElement(Text23, { color: focusedField === "ms" ? "green" : void 0 }, focusedField === "ms" ? "\u25B6 " : " ", "[", useMs ? "\u2713" : " ", "] Milliseconds")),
|
|
6383
6842
|
/* @__PURE__ */ React32.createElement(Box22, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(
|
|
@@ -6431,7 +6890,7 @@ var JwtView = () => {
|
|
|
6431
6890
|
const [focusedField, setFocusedField] = useState20("mode");
|
|
6432
6891
|
const [copied, setCopied] = useState20(false);
|
|
6433
6892
|
const service = new UtilsService();
|
|
6434
|
-
useInput18((
|
|
6893
|
+
useInput18((input4, key) => {
|
|
6435
6894
|
if (key.tab) {
|
|
6436
6895
|
const fields = mode === "decode" ? ["mode", "token", "process"] : ["mode", "payload", "secret", "process"];
|
|
6437
6896
|
const currentIndex = fields.indexOf(focusedField);
|
|
@@ -6444,7 +6903,7 @@ var JwtView = () => {
|
|
|
6444
6903
|
} else {
|
|
6445
6904
|
handleProcess();
|
|
6446
6905
|
}
|
|
6447
|
-
} else if (
|
|
6906
|
+
} else if (input4 === "c" && result.token) {
|
|
6448
6907
|
handleCopy();
|
|
6449
6908
|
}
|
|
6450
6909
|
});
|
|
@@ -6569,12 +7028,12 @@ var CronView = () => {
|
|
|
6569
7028
|
const [focusedField, setFocusedField] = useState21("expression");
|
|
6570
7029
|
const [copied, setCopied] = useState21(false);
|
|
6571
7030
|
const service = new UtilsService();
|
|
6572
|
-
useInput19((
|
|
7031
|
+
useInput19((input4, key) => {
|
|
6573
7032
|
if (key.tab) {
|
|
6574
7033
|
setFocusedField(focusedField === "expression" ? "parse" : "expression");
|
|
6575
7034
|
} else if (key.return) {
|
|
6576
7035
|
handleParse();
|
|
6577
|
-
} else if (
|
|
7036
|
+
} else if (input4 === "c" && result) {
|
|
6578
7037
|
handleCopy();
|
|
6579
7038
|
}
|
|
6580
7039
|
});
|
|
@@ -6675,14 +7134,14 @@ var TimezoneView = () => {
|
|
|
6675
7134
|
const [focusedField, setFocusedField] = useState22("time");
|
|
6676
7135
|
const [copied, setCopied] = useState22(false);
|
|
6677
7136
|
const service = new UtilsService();
|
|
6678
|
-
useInput20((
|
|
7137
|
+
useInput20((input4, key) => {
|
|
6679
7138
|
if (key.tab) {
|
|
6680
7139
|
const fields = ["time", "from", "to", "convert"];
|
|
6681
7140
|
const currentIndex = fields.indexOf(focusedField);
|
|
6682
7141
|
setFocusedField(fields[(currentIndex + 1) % fields.length]);
|
|
6683
7142
|
} else if (key.return) {
|
|
6684
7143
|
handleConvert();
|
|
6685
|
-
} else if (
|
|
7144
|
+
} else if (input4 === "c" && result) {
|
|
6686
7145
|
handleCopy();
|
|
6687
7146
|
}
|
|
6688
7147
|
});
|
|
@@ -6801,7 +7260,7 @@ var HttpView = () => {
|
|
|
6801
7260
|
const [focusedField, setFocusedField] = useState23("url");
|
|
6802
7261
|
const [copied, setCopied] = useState23(false);
|
|
6803
7262
|
const service = new UtilsService();
|
|
6804
|
-
useInput21((
|
|
7263
|
+
useInput21((input4, key) => {
|
|
6805
7264
|
if (loading) return;
|
|
6806
7265
|
if (key.tab) {
|
|
6807
7266
|
const fields = ["url", "method", "headers", "body", "send"];
|
|
@@ -6815,7 +7274,7 @@ var HttpView = () => {
|
|
|
6815
7274
|
setMethod(METHODS[(currentIndex + 1) % METHODS.length]);
|
|
6816
7275
|
} else if (key.return && focusedField === "send") {
|
|
6817
7276
|
handleSend();
|
|
6818
|
-
} else if (
|
|
7277
|
+
} else if (input4 === "c" && response) {
|
|
6819
7278
|
handleCopy();
|
|
6820
7279
|
}
|
|
6821
7280
|
});
|
|
@@ -6939,14 +7398,14 @@ var HttpView = () => {
|
|
|
6939
7398
|
import React37, { useState as useState24 } from "react";
|
|
6940
7399
|
import { Box as Box27, Text as Text28, useInput as useInput22 } from "ink";
|
|
6941
7400
|
import TextInput14 from "ink-text-input";
|
|
6942
|
-
import * as
|
|
7401
|
+
import * as fs9 from "fs";
|
|
6943
7402
|
import * as path5 from "path";
|
|
6944
7403
|
var MarkdownView = () => {
|
|
6945
7404
|
const [filePath, setFilePath] = useState24("");
|
|
6946
7405
|
const [content, setContent] = useState24("");
|
|
6947
7406
|
const [error, setError] = useState24("");
|
|
6948
7407
|
const [focusedField, setFocusedField] = useState24("file");
|
|
6949
|
-
useInput22((
|
|
7408
|
+
useInput22((input4, key) => {
|
|
6950
7409
|
if (key.tab) {
|
|
6951
7410
|
setFocusedField(focusedField === "file" ? "preview" : "file");
|
|
6952
7411
|
} else if (key.return) {
|
|
@@ -6961,11 +7420,11 @@ var MarkdownView = () => {
|
|
|
6961
7420
|
try {
|
|
6962
7421
|
setError("");
|
|
6963
7422
|
const resolvedPath = path5.resolve(filePath);
|
|
6964
|
-
if (!
|
|
7423
|
+
if (!fs9.existsSync(resolvedPath)) {
|
|
6965
7424
|
setError(`File not found: ${resolvedPath}`);
|
|
6966
7425
|
return;
|
|
6967
7426
|
}
|
|
6968
|
-
let fileContent =
|
|
7427
|
+
let fileContent = fs9.readFileSync(resolvedPath, "utf-8");
|
|
6969
7428
|
fileContent = fileContent.replace(/```mermaid\n([\s\S]*?)```/g, (match, code) => {
|
|
6970
7429
|
return `
|
|
6971
7430
|
\u{1F3A8} **Mermaid Diagram**
|
|
@@ -7061,20 +7520,20 @@ var UtilsApp = ({ onExit }) => {
|
|
|
7061
7520
|
const [selectedIndex, setSelectedIndex] = useState25(0);
|
|
7062
7521
|
const [activeView, setActiveView] = useState25(null);
|
|
7063
7522
|
const { exit } = useApp5();
|
|
7064
|
-
useInput23((
|
|
7523
|
+
useInput23((input4, key) => {
|
|
7065
7524
|
if (activeView) {
|
|
7066
7525
|
if (key.escape) {
|
|
7067
7526
|
setActiveView(null);
|
|
7068
7527
|
}
|
|
7069
7528
|
return;
|
|
7070
7529
|
}
|
|
7071
|
-
if (key.upArrow ||
|
|
7530
|
+
if (key.upArrow || input4 === "k") {
|
|
7072
7531
|
setSelectedIndex((prev) => prev > 0 ? prev - 1 : MENU_ITEMS2.length - 1);
|
|
7073
|
-
} else if (key.downArrow ||
|
|
7532
|
+
} else if (key.downArrow || input4 === "j") {
|
|
7074
7533
|
setSelectedIndex((prev) => prev < MENU_ITEMS2.length - 1 ? prev + 1 : 0);
|
|
7075
7534
|
} else if (key.return) {
|
|
7076
7535
|
setActiveView(MENU_ITEMS2[selectedIndex].id);
|
|
7077
|
-
} else if (
|
|
7536
|
+
} else if (input4 === "q" || key.escape) {
|
|
7078
7537
|
onExit();
|
|
7079
7538
|
exit();
|
|
7080
7539
|
}
|
|
@@ -7204,7 +7663,7 @@ async function runInteractiveMode() {
|
|
|
7204
7663
|
|
|
7205
7664
|
// src/commands/utils/index.ts
|
|
7206
7665
|
function createUtilsCommand() {
|
|
7207
|
-
const utilsCommand = new
|
|
7666
|
+
const utilsCommand = new Command29("utils").description("Developer utilities for common tasks").option("-i, --interactive", "Run in interactive mode").addHelpText("after", `
|
|
7208
7667
|
Interactive Mode:
|
|
7209
7668
|
$ jai1 utils --interactive
|
|
7210
7669
|
$ jai1 utils -i
|
|
@@ -7239,14 +7698,14 @@ Quick Usage:
|
|
|
7239
7698
|
}
|
|
7240
7699
|
|
|
7241
7700
|
// src/commands/deps/index.ts
|
|
7242
|
-
import { Command as
|
|
7701
|
+
import { Command as Command31 } from "commander";
|
|
7243
7702
|
|
|
7244
7703
|
// src/commands/deps/upgrade.ts
|
|
7245
|
-
import { Command as
|
|
7704
|
+
import { Command as Command30 } from "commander";
|
|
7246
7705
|
import { checkbox as checkbox3, confirm as confirm4 } from "@inquirer/prompts";
|
|
7247
7706
|
|
|
7248
7707
|
// src/services/deps.service.ts
|
|
7249
|
-
import { promises as
|
|
7708
|
+
import { promises as fs10 } from "fs";
|
|
7250
7709
|
import path6 from "path";
|
|
7251
7710
|
import { execSync } from "child_process";
|
|
7252
7711
|
import pLimit2 from "p-limit";
|
|
@@ -7258,7 +7717,7 @@ var DepsService = class {
|
|
|
7258
7717
|
async readPackageJson(cwd) {
|
|
7259
7718
|
const pkgPath = path6.join(cwd, "package.json");
|
|
7260
7719
|
try {
|
|
7261
|
-
const content = await
|
|
7720
|
+
const content = await fs10.readFile(pkgPath, "utf-8");
|
|
7262
7721
|
return JSON.parse(content);
|
|
7263
7722
|
} catch (error) {
|
|
7264
7723
|
if (error.code === "ENOENT") {
|
|
@@ -7364,7 +7823,7 @@ var DepsService = class {
|
|
|
7364
7823
|
];
|
|
7365
7824
|
for (const { file, pm } of lockFiles) {
|
|
7366
7825
|
try {
|
|
7367
|
-
await
|
|
7826
|
+
await fs10.access(path6.join(cwd, file));
|
|
7368
7827
|
return pm;
|
|
7369
7828
|
} catch {
|
|
7370
7829
|
}
|
|
@@ -7456,7 +7915,7 @@ var colors2 = {
|
|
|
7456
7915
|
dim: "\x1B[2m"
|
|
7457
7916
|
};
|
|
7458
7917
|
function createDepsUpgradeCommand() {
|
|
7459
|
-
return new
|
|
7918
|
+
return new Command30("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) => {
|
|
7460
7919
|
await handleDepsUpgrade(options);
|
|
7461
7920
|
});
|
|
7462
7921
|
}
|
|
@@ -7636,20 +8095,20 @@ function displayUpgradeTable(packages) {
|
|
|
7636
8095
|
|
|
7637
8096
|
// src/commands/deps/index.ts
|
|
7638
8097
|
function createDepsCommand() {
|
|
7639
|
-
const depsCommand = new
|
|
8098
|
+
const depsCommand = new Command31("deps").description("Qu\u1EA3n l\xFD dependencies trong project");
|
|
7640
8099
|
depsCommand.addCommand(createDepsUpgradeCommand());
|
|
7641
8100
|
return depsCommand;
|
|
7642
8101
|
}
|
|
7643
8102
|
|
|
7644
8103
|
// src/commands/kit/index.ts
|
|
7645
|
-
import { Command as
|
|
8104
|
+
import { Command as Command35 } from "commander";
|
|
7646
8105
|
|
|
7647
8106
|
// src/commands/kit/list.ts
|
|
7648
|
-
import { Command as
|
|
8107
|
+
import { Command as Command32 } from "commander";
|
|
7649
8108
|
|
|
7650
8109
|
// src/services/starter-kit.service.ts
|
|
7651
|
-
import { promises as
|
|
7652
|
-
import { join as
|
|
8110
|
+
import { promises as fs11 } from "fs";
|
|
8111
|
+
import { join as join5 } from "path";
|
|
7653
8112
|
import AdmZip from "adm-zip";
|
|
7654
8113
|
var StarterKitService = class {
|
|
7655
8114
|
/**
|
|
@@ -7696,23 +8155,23 @@ var StarterKitService = class {
|
|
|
7696
8155
|
throw new NetworkError(`Failed to download kit: HTTP ${response.status}`);
|
|
7697
8156
|
}
|
|
7698
8157
|
if (onProgress) onProgress(30);
|
|
7699
|
-
const tmpDir =
|
|
7700
|
-
await
|
|
7701
|
-
const tmpFile =
|
|
8158
|
+
const tmpDir = join5(process.env.TMPDIR || "/tmp", "jai1-kits");
|
|
8159
|
+
await fs11.mkdir(tmpDir, { recursive: true });
|
|
8160
|
+
const tmpFile = join5(tmpDir, `${slug}.zip`);
|
|
7702
8161
|
const buffer = await response.arrayBuffer();
|
|
7703
|
-
await
|
|
8162
|
+
await fs11.writeFile(tmpFile, Buffer.from(buffer));
|
|
7704
8163
|
if (onProgress) onProgress(60);
|
|
7705
8164
|
const zip = new AdmZip(tmpFile);
|
|
7706
|
-
await
|
|
8165
|
+
await fs11.mkdir(targetDir, { recursive: true });
|
|
7707
8166
|
zip.extractAllTo(targetDir, true);
|
|
7708
8167
|
if (onProgress) onProgress(100);
|
|
7709
|
-
await
|
|
8168
|
+
await fs11.unlink(tmpFile);
|
|
7710
8169
|
}
|
|
7711
8170
|
};
|
|
7712
8171
|
|
|
7713
8172
|
// src/commands/kit/list.ts
|
|
7714
8173
|
function createKitListCommand() {
|
|
7715
|
-
return new
|
|
8174
|
+
return new Command32("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) => {
|
|
7716
8175
|
const configService = new ConfigService();
|
|
7717
8176
|
const config = await configService.load();
|
|
7718
8177
|
if (!config) {
|
|
@@ -7749,9 +8208,9 @@ function createKitListCommand() {
|
|
|
7749
8208
|
}
|
|
7750
8209
|
|
|
7751
8210
|
// src/commands/kit/info.ts
|
|
7752
|
-
import { Command as
|
|
8211
|
+
import { Command as Command33 } from "commander";
|
|
7753
8212
|
function createKitInfoCommand() {
|
|
7754
|
-
return new
|
|
8213
|
+
return new Command33("info").description("Show detailed information about a starter kit").argument("<slug>", "Starter kit slug").action(async (slug) => {
|
|
7755
8214
|
const configService = new ConfigService();
|
|
7756
8215
|
const config = await configService.load();
|
|
7757
8216
|
if (!config) {
|
|
@@ -7800,9 +8259,9 @@ Post-Init Commands:`);
|
|
|
7800
8259
|
}
|
|
7801
8260
|
|
|
7802
8261
|
// src/commands/kit/create.ts
|
|
7803
|
-
import { Command as
|
|
7804
|
-
import { promises as
|
|
7805
|
-
import { join as
|
|
8262
|
+
import { Command as Command34 } from "commander";
|
|
8263
|
+
import { promises as fs12 } from "fs";
|
|
8264
|
+
import { join as join6 } from "path";
|
|
7806
8265
|
import { select as select2, input, checkbox as checkbox4 } from "@inquirer/prompts";
|
|
7807
8266
|
import { execa as execa2 } from "execa";
|
|
7808
8267
|
|
|
@@ -7845,7 +8304,7 @@ var HookExecutor = class {
|
|
|
7845
8304
|
|
|
7846
8305
|
// src/commands/kit/create.ts
|
|
7847
8306
|
function createKitCreateCommand() {
|
|
7848
|
-
return new
|
|
8307
|
+
return new Command34("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) => {
|
|
7849
8308
|
const configService = new ConfigService();
|
|
7850
8309
|
const config = await configService.load();
|
|
7851
8310
|
if (!config) {
|
|
@@ -7865,9 +8324,9 @@ function createKitCreateCommand() {
|
|
|
7865
8324
|
}
|
|
7866
8325
|
}
|
|
7867
8326
|
}
|
|
7868
|
-
const targetDir = directory ||
|
|
8327
|
+
const targetDir = directory || join6(process.cwd(), options.name || slug);
|
|
7869
8328
|
try {
|
|
7870
|
-
await
|
|
8329
|
+
await fs12.access(targetDir);
|
|
7871
8330
|
throw new Error(`Directory already exists: ${targetDir}`);
|
|
7872
8331
|
} catch (error) {
|
|
7873
8332
|
if (error.code !== "ENOENT") {
|
|
@@ -7985,7 +8444,7 @@ function createKitCreateCommand() {
|
|
|
7985
8444
|
async function applyVariableSubstitution(dir, variables) {
|
|
7986
8445
|
const files = await getAllFiles(dir);
|
|
7987
8446
|
for (const file of files) {
|
|
7988
|
-
let content = await
|
|
8447
|
+
let content = await fs12.readFile(file, "utf-8");
|
|
7989
8448
|
let modified = false;
|
|
7990
8449
|
for (const [key, value] of Object.entries(variables)) {
|
|
7991
8450
|
const regex = new RegExp(`\\{\\{${key}\\}\\}`, "g");
|
|
@@ -7995,15 +8454,15 @@ async function applyVariableSubstitution(dir, variables) {
|
|
|
7995
8454
|
}
|
|
7996
8455
|
}
|
|
7997
8456
|
if (modified) {
|
|
7998
|
-
await
|
|
8457
|
+
await fs12.writeFile(file, content, "utf-8");
|
|
7999
8458
|
}
|
|
8000
8459
|
}
|
|
8001
8460
|
}
|
|
8002
8461
|
async function getAllFiles(dir) {
|
|
8003
8462
|
const files = [];
|
|
8004
|
-
const entries = await
|
|
8463
|
+
const entries = await fs12.readdir(dir, { withFileTypes: true });
|
|
8005
8464
|
for (const entry of entries) {
|
|
8006
|
-
const fullPath =
|
|
8465
|
+
const fullPath = join6(dir, entry.name);
|
|
8007
8466
|
if (entry.isDirectory()) {
|
|
8008
8467
|
if (!entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
8009
8468
|
files.push(...await getAllFiles(fullPath));
|
|
@@ -8017,7 +8476,7 @@ async function getAllFiles(dir) {
|
|
|
8017
8476
|
|
|
8018
8477
|
// src/commands/kit/index.ts
|
|
8019
8478
|
function createKitCommand() {
|
|
8020
|
-
const cmd = new
|
|
8479
|
+
const cmd = new Command35("kit").description("Manage starter kits for new projects").action(() => {
|
|
8021
8480
|
cmd.help();
|
|
8022
8481
|
});
|
|
8023
8482
|
cmd.addCommand(createKitListCommand());
|
|
@@ -8026,25 +8485,1335 @@ function createKitCommand() {
|
|
|
8026
8485
|
return cmd;
|
|
8027
8486
|
}
|
|
8028
8487
|
|
|
8029
|
-
// src/commands/
|
|
8030
|
-
import { Command as
|
|
8031
|
-
|
|
8032
|
-
|
|
8033
|
-
|
|
8034
|
-
|
|
8035
|
-
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
}
|
|
8041
|
-
|
|
8042
|
-
|
|
8043
|
-
|
|
8488
|
+
// src/commands/rules/index.ts
|
|
8489
|
+
import { Command as Command42 } from "commander";
|
|
8490
|
+
|
|
8491
|
+
// src/commands/rules/list.ts
|
|
8492
|
+
import { Command as Command36 } from "commander";
|
|
8493
|
+
function createRulesListCommand() {
|
|
8494
|
+
return new Command36("list").description("List available rule presets").option("--json", "Output as JSON").action(async (options) => {
|
|
8495
|
+
const configService = new ConfigService();
|
|
8496
|
+
const config = await configService.load();
|
|
8497
|
+
if (!config) {
|
|
8498
|
+
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
8499
|
+
}
|
|
8500
|
+
console.log("\u{1F4CB} Fetching available rule presets...\n");
|
|
8501
|
+
try {
|
|
8502
|
+
const response = await fetch(`${config.apiUrl}/api/rules/presets`, {
|
|
8503
|
+
headers: {
|
|
8504
|
+
"JAI1-Access-Key": config.accessKey
|
|
8505
|
+
}
|
|
8506
|
+
});
|
|
8507
|
+
if (!response.ok) {
|
|
8508
|
+
throw new Error(`Failed to fetch presets: ${response.statusText}`);
|
|
8509
|
+
}
|
|
8510
|
+
const data = await response.json();
|
|
8511
|
+
if (options.json) {
|
|
8512
|
+
console.log(JSON.stringify(data, null, 2));
|
|
8513
|
+
return;
|
|
8514
|
+
}
|
|
8515
|
+
if (data.total === 0) {
|
|
8516
|
+
console.log("No presets available.");
|
|
8517
|
+
return;
|
|
8518
|
+
}
|
|
8519
|
+
console.log(`Found ${data.total} preset${data.total > 1 ? "s" : ""}:
|
|
8520
|
+
`);
|
|
8521
|
+
for (const preset of data.presets) {
|
|
8522
|
+
console.log(`\u{1F4E6} ${preset.slug}`);
|
|
8523
|
+
console.log(` Name: ${preset.name}`);
|
|
8524
|
+
console.log(` Description: ${preset.description}`);
|
|
8525
|
+
console.log(` Version: ${preset.version}`);
|
|
8526
|
+
const stackParts = [];
|
|
8527
|
+
if (preset.stack.frontend) stackParts.push(preset.stack.frontend);
|
|
8528
|
+
if (preset.stack.backend) stackParts.push(preset.stack.backend);
|
|
8529
|
+
if (preset.stack.css) stackParts.push(preset.stack.css);
|
|
8530
|
+
if (preset.stack.database) stackParts.push(preset.stack.database);
|
|
8531
|
+
if (stackParts.length > 0) {
|
|
8532
|
+
console.log(` Stack: ${stackParts.join(" + ")}`);
|
|
8533
|
+
}
|
|
8534
|
+
console.log(` Tags: ${preset.tags.join(", ")}`);
|
|
8535
|
+
console.log(` Downloads: ${preset.downloads}`);
|
|
8536
|
+
console.log("");
|
|
8537
|
+
}
|
|
8538
|
+
console.log(`
|
|
8539
|
+
\u2139\uFE0F Use 'jai1 rules init --preset=<slug>' to apply a preset`);
|
|
8540
|
+
} catch (error) {
|
|
8541
|
+
throw new Error(
|
|
8542
|
+
`Failed to list presets: ${error instanceof Error ? error.message : String(error)}`
|
|
8543
|
+
);
|
|
8544
|
+
}
|
|
8044
8545
|
});
|
|
8045
8546
|
}
|
|
8046
|
-
|
|
8047
|
-
|
|
8547
|
+
|
|
8548
|
+
// src/commands/rules/init.ts
|
|
8549
|
+
import { Command as Command37 } from "commander";
|
|
8550
|
+
import { promises as fs13 } from "fs";
|
|
8551
|
+
import { join as join7 } from "path";
|
|
8552
|
+
import { select as select3, confirm as confirm5 } from "@inquirer/prompts";
|
|
8553
|
+
function createRulesInitCommand() {
|
|
8554
|
+
return new Command37("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) => {
|
|
8555
|
+
const configService = new ConfigService();
|
|
8556
|
+
const config = await configService.load();
|
|
8557
|
+
if (!config) {
|
|
8558
|
+
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
8559
|
+
}
|
|
8560
|
+
let presetSlug = options.preset;
|
|
8561
|
+
if (!presetSlug) {
|
|
8562
|
+
console.log("\u{1F4CB} Fetching available presets...\n");
|
|
8563
|
+
const response = await fetch(`${config.apiUrl}/api/rules/presets`, {
|
|
8564
|
+
headers: {
|
|
8565
|
+
"JAI1-Access-Key": config.accessKey
|
|
8566
|
+
}
|
|
8567
|
+
});
|
|
8568
|
+
if (!response.ok) {
|
|
8569
|
+
throw new Error(`Failed to fetch presets: ${response.statusText}`);
|
|
8570
|
+
}
|
|
8571
|
+
const data = await response.json();
|
|
8572
|
+
if (data.total === 0) {
|
|
8573
|
+
console.log("No presets available.");
|
|
8574
|
+
return;
|
|
8575
|
+
}
|
|
8576
|
+
presetSlug = await select3({
|
|
8577
|
+
message: "Select a preset:",
|
|
8578
|
+
choices: data.presets.map((p) => ({
|
|
8579
|
+
name: `${p.name} - ${p.description}`,
|
|
8580
|
+
value: p.slug,
|
|
8581
|
+
description: `v${p.version} | ${p.tags.join(", ")}`
|
|
8582
|
+
}))
|
|
8583
|
+
});
|
|
8584
|
+
}
|
|
8585
|
+
console.log(`
|
|
8586
|
+
\u{1F4E6} Fetching preset: ${presetSlug}...
|
|
8587
|
+
`);
|
|
8588
|
+
const presetResponse = await fetch(`${config.apiUrl}/api/rules/presets/${presetSlug}`, {
|
|
8589
|
+
headers: {
|
|
8590
|
+
"JAI1-Access-Key": config.accessKey
|
|
8591
|
+
}
|
|
8592
|
+
});
|
|
8593
|
+
if (!presetResponse.ok) {
|
|
8594
|
+
if (presetResponse.status === 404) {
|
|
8595
|
+
throw new Error(`Preset '${presetSlug}' not found`);
|
|
8596
|
+
}
|
|
8597
|
+
throw new Error(`Failed to fetch preset: ${presetResponse.statusText}`);
|
|
8598
|
+
}
|
|
8599
|
+
const bundle = await presetResponse.json();
|
|
8600
|
+
console.log(`\u2713 Preset: ${bundle.preset.name} v${bundle.preset.version}`);
|
|
8601
|
+
console.log(` Files: ${Object.keys(bundle.files).length}`);
|
|
8602
|
+
let outputFormat = options.output;
|
|
8603
|
+
if (!["cursor", "agents-md", "both"].includes(outputFormat)) {
|
|
8604
|
+
outputFormat = await select3({
|
|
8605
|
+
message: "Select output format:",
|
|
8606
|
+
choices: [
|
|
8607
|
+
{ name: "Cursor (.cursor/rules/)", value: "cursor" },
|
|
8608
|
+
{ name: "AGENTS.md (single file)", value: "agents-md" },
|
|
8609
|
+
{ name: "Both", value: "both" }
|
|
8610
|
+
]
|
|
8611
|
+
});
|
|
8612
|
+
}
|
|
8613
|
+
if (!options.yes) {
|
|
8614
|
+
const proceed = await confirm5({
|
|
8615
|
+
message: `Apply preset '${bundle.preset.name}' to current directory?`,
|
|
8616
|
+
default: true
|
|
8617
|
+
});
|
|
8618
|
+
if (!proceed) {
|
|
8619
|
+
console.log("Cancelled.");
|
|
8620
|
+
return;
|
|
8621
|
+
}
|
|
8622
|
+
}
|
|
8623
|
+
console.log("\n\u{1F4DD} Applying preset...\n");
|
|
8624
|
+
if (outputFormat === "cursor" || outputFormat === "both") {
|
|
8625
|
+
await applyCursorFormat(bundle);
|
|
8626
|
+
}
|
|
8627
|
+
if (outputFormat === "agents-md" || outputFormat === "both") {
|
|
8628
|
+
await applyAgentsMdFormat(bundle);
|
|
8629
|
+
}
|
|
8630
|
+
const projectConfig = {
|
|
8631
|
+
preset: bundle.preset.slug,
|
|
8632
|
+
version: bundle.preset.version,
|
|
8633
|
+
appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8634
|
+
customContext: "09-custom.mdc"
|
|
8635
|
+
};
|
|
8636
|
+
await fs13.writeFile("jai1-rules.json", JSON.stringify(projectConfig, null, 2));
|
|
8637
|
+
console.log("\u2713 Created jai1-rules.json");
|
|
8638
|
+
console.log("\n\u2705 Preset applied successfully!\n");
|
|
8639
|
+
console.log("Next steps:");
|
|
8640
|
+
console.log(" 1. Edit 09-custom.mdc to add project-specific rules");
|
|
8641
|
+
console.log(" 2. Commit the rules to git");
|
|
8642
|
+
console.log(' 3. Use "jai1 rules sync" to regenerate outputs after editing\n');
|
|
8643
|
+
});
|
|
8644
|
+
}
|
|
8645
|
+
async function applyCursorFormat(bundle) {
|
|
8646
|
+
const rulesDir = join7(process.cwd(), ".cursor", "rules");
|
|
8647
|
+
await fs13.mkdir(rulesDir, { recursive: true });
|
|
8648
|
+
for (const [filename, content] of Object.entries(bundle.files)) {
|
|
8649
|
+
const filePath = join7(rulesDir, filename);
|
|
8650
|
+
await fs13.writeFile(filePath, content, "utf-8");
|
|
8651
|
+
console.log(`\u2713 Created .cursor/rules/${filename}`);
|
|
8652
|
+
}
|
|
8653
|
+
}
|
|
8654
|
+
async function applyAgentsMdFormat(bundle) {
|
|
8655
|
+
const sections = [];
|
|
8656
|
+
sections.push("# AGENTS.md\n");
|
|
8657
|
+
sections.push(`<!-- Generated by jai1 rules - Preset: ${bundle.preset.slug} v${bundle.preset.version} -->
|
|
8658
|
+
`);
|
|
8659
|
+
const fileOrder = [
|
|
8660
|
+
"01-project.mdc",
|
|
8661
|
+
"02-standards.mdc",
|
|
8662
|
+
"03-frontend.mdc",
|
|
8663
|
+
"04-backend.mdc",
|
|
8664
|
+
"05-testing.mdc",
|
|
8665
|
+
"06-workflow.mdc",
|
|
8666
|
+
"09-custom.mdc"
|
|
8667
|
+
];
|
|
8668
|
+
for (const filename of fileOrder) {
|
|
8669
|
+
if (bundle.files[filename]) {
|
|
8670
|
+
const content = bundle.files[filename];
|
|
8671
|
+
const withoutFrontmatter = content.replace(/^---\n[\s\S]*?\n---\n/, "");
|
|
8672
|
+
sections.push(withoutFrontmatter.trim());
|
|
8673
|
+
sections.push("\n");
|
|
8674
|
+
}
|
|
8675
|
+
}
|
|
8676
|
+
const agentsMd = sections.join("\n");
|
|
8677
|
+
await fs13.writeFile("AGENTS.md", agentsMd, "utf-8");
|
|
8678
|
+
console.log("\u2713 Created AGENTS.md");
|
|
8679
|
+
}
|
|
8680
|
+
|
|
8681
|
+
// src/commands/rules/apply.ts
|
|
8682
|
+
import { Command as Command38 } from "commander";
|
|
8683
|
+
import { promises as fs15 } from "fs";
|
|
8684
|
+
import { join as join9 } from "path";
|
|
8685
|
+
import { select as select4, confirm as confirm6, checkbox as checkbox5 } from "@inquirer/prompts";
|
|
8686
|
+
|
|
8687
|
+
// src/services/rules-generator.service.ts
|
|
8688
|
+
var RulesGeneratorService = class {
|
|
8689
|
+
/**
|
|
8690
|
+
* Generate files for a specific IDE format
|
|
8691
|
+
*/
|
|
8692
|
+
generateForIde(bundle, ideId) {
|
|
8693
|
+
const format = IDE_FORMATS[ideId];
|
|
8694
|
+
if (!format) {
|
|
8695
|
+
throw new Error(`Unknown IDE format: ${ideId}`);
|
|
8696
|
+
}
|
|
8697
|
+
switch (ideId) {
|
|
8698
|
+
case "cursor":
|
|
8699
|
+
return this.generateCursorFiles(bundle, format);
|
|
8700
|
+
case "windsurf":
|
|
8701
|
+
return this.generateWindsurfFiles(bundle, format);
|
|
8702
|
+
case "antigravity":
|
|
8703
|
+
return this.generateAntigravityFiles(bundle, format);
|
|
8704
|
+
case "claude":
|
|
8705
|
+
return this.generateClaudeFiles(bundle, format);
|
|
8706
|
+
case "agentsmd":
|
|
8707
|
+
return this.generateAgentsMd(bundle, format);
|
|
8708
|
+
case "gemini":
|
|
8709
|
+
return this.generateGeminiMd(bundle, format);
|
|
8710
|
+
default:
|
|
8711
|
+
throw new Error(`Unsupported IDE format: ${ideId}`);
|
|
8712
|
+
}
|
|
8713
|
+
}
|
|
8714
|
+
/**
|
|
8715
|
+
* Generate Cursor format (.mdc files)
|
|
8716
|
+
*/
|
|
8717
|
+
generateCursorFiles(bundle, format) {
|
|
8718
|
+
const files = [];
|
|
8719
|
+
files.push({
|
|
8720
|
+
path: `${format.rulesPath}/00-jai1${format.fileExtension}`,
|
|
8721
|
+
content: this.generateCursorJai1Rule(),
|
|
8722
|
+
description: "Jai1 Framework base rule"
|
|
8723
|
+
});
|
|
8724
|
+
for (const [filename, content] of Object.entries(bundle.files)) {
|
|
8725
|
+
files.push({
|
|
8726
|
+
path: `${format.rulesPath}/${filename}`,
|
|
8727
|
+
content,
|
|
8728
|
+
// Already in .mdc format
|
|
8729
|
+
description: `Preset rule: ${filename}`
|
|
8730
|
+
});
|
|
8731
|
+
}
|
|
8732
|
+
return files;
|
|
8733
|
+
}
|
|
8734
|
+
/**
|
|
8735
|
+
* Generate Cursor jai1 base rule
|
|
8736
|
+
*/
|
|
8737
|
+
generateCursorJai1Rule() {
|
|
8738
|
+
return `---
|
|
8739
|
+
description: Jai1 Framework integration and skill loading
|
|
8740
|
+
globs: ["**/*"]
|
|
8741
|
+
alwaysApply: true
|
|
8742
|
+
---
|
|
8743
|
+
|
|
8744
|
+
${JAI1_BASE_RULE}`;
|
|
8745
|
+
}
|
|
8746
|
+
/**
|
|
8747
|
+
* Generate Windsurf format (.md files with trigger metadata)
|
|
8748
|
+
*/
|
|
8749
|
+
generateWindsurfFiles(bundle, format) {
|
|
8750
|
+
const files = [];
|
|
8751
|
+
files.push({
|
|
8752
|
+
path: `${format.rulesPath}/00-jai1${format.fileExtension}`,
|
|
8753
|
+
content: this.generateWindsurfJai1Rule(),
|
|
8754
|
+
description: "Jai1 Framework base rule"
|
|
8755
|
+
});
|
|
8756
|
+
for (const [filename, content] of Object.entries(bundle.files)) {
|
|
8757
|
+
const converted = this.convertToWindsurf(content, filename);
|
|
8758
|
+
const newFilename = filename.replace(/\.mdc$/, ".md");
|
|
8759
|
+
files.push({
|
|
8760
|
+
path: `${format.rulesPath}/${newFilename}`,
|
|
8761
|
+
content: converted,
|
|
8762
|
+
description: `Preset rule: ${newFilename}`
|
|
8763
|
+
});
|
|
8764
|
+
}
|
|
8765
|
+
return files;
|
|
8766
|
+
}
|
|
8767
|
+
/**
|
|
8768
|
+
* Convert Cursor format to Windsurf format
|
|
8769
|
+
*/
|
|
8770
|
+
convertToWindsurf(content, filename) {
|
|
8771
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
8772
|
+
if (!frontmatterMatch) {
|
|
8773
|
+
return content;
|
|
8774
|
+
}
|
|
8775
|
+
const [, frontmatter, body] = frontmatterMatch;
|
|
8776
|
+
const parsed = this.parseFrontmatter(frontmatter);
|
|
8777
|
+
let trigger = "always";
|
|
8778
|
+
if (parsed.alwaysApply === "true" || parsed.alwaysApply === true) {
|
|
8779
|
+
trigger = "always";
|
|
8780
|
+
} else if (parsed.globs && parsed.globs.length > 0) {
|
|
8781
|
+
trigger = parsed.globs.join(", ");
|
|
8782
|
+
}
|
|
8783
|
+
const windsurfFrontmatter = `---
|
|
8784
|
+
trigger: ${trigger}
|
|
8785
|
+
---`;
|
|
8786
|
+
return `${windsurfFrontmatter}
|
|
8787
|
+
|
|
8788
|
+
${body.trim()}
|
|
8789
|
+
`;
|
|
8790
|
+
}
|
|
8791
|
+
/**
|
|
8792
|
+
* Generate Windsurf jai1 base rule
|
|
8793
|
+
*/
|
|
8794
|
+
generateWindsurfJai1Rule() {
|
|
8795
|
+
return `---
|
|
8796
|
+
trigger: always
|
|
8797
|
+
---
|
|
8798
|
+
|
|
8799
|
+
${JAI1_BASE_RULE}`;
|
|
8800
|
+
}
|
|
8801
|
+
/**
|
|
8802
|
+
* Generate Antigravity format (.md files with trigger metadata + @AGENTS.md reference)
|
|
8803
|
+
*/
|
|
8804
|
+
generateAntigravityFiles(bundle, format) {
|
|
8805
|
+
const files = [];
|
|
8806
|
+
files.push({
|
|
8807
|
+
path: `${format.rulesPath}/00-jai1${format.fileExtension}`,
|
|
8808
|
+
content: this.generateAntigravityJai1Rule(),
|
|
8809
|
+
description: "Jai1 Framework base rule"
|
|
8810
|
+
});
|
|
8811
|
+
for (const [filename, content] of Object.entries(bundle.files)) {
|
|
8812
|
+
const converted = this.convertToAntigravity(content, filename);
|
|
8813
|
+
const newFilename = filename.replace(/\.mdc$/, ".md");
|
|
8814
|
+
files.push({
|
|
8815
|
+
path: `${format.rulesPath}/${newFilename}`,
|
|
8816
|
+
content: converted,
|
|
8817
|
+
description: `Preset rule: ${newFilename}`
|
|
8818
|
+
});
|
|
8819
|
+
}
|
|
8820
|
+
return files;
|
|
8821
|
+
}
|
|
8822
|
+
/**
|
|
8823
|
+
* Convert Cursor format to Antigravity format
|
|
8824
|
+
*/
|
|
8825
|
+
convertToAntigravity(content, filename) {
|
|
8826
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
8827
|
+
if (!frontmatterMatch) {
|
|
8828
|
+
return content;
|
|
8829
|
+
}
|
|
8830
|
+
const [, frontmatter, body] = frontmatterMatch;
|
|
8831
|
+
const parsed = this.parseFrontmatter(frontmatter);
|
|
8832
|
+
let trigger = "always_on";
|
|
8833
|
+
if (parsed.alwaysApply === "true" || parsed.alwaysApply === true) {
|
|
8834
|
+
trigger = "always_on";
|
|
8835
|
+
}
|
|
8836
|
+
const antigravityFrontmatter = `---
|
|
8837
|
+
trigger: ${trigger}
|
|
8838
|
+
---
|
|
8839
|
+
|
|
8840
|
+
@AGENTS.md`;
|
|
8841
|
+
return `${antigravityFrontmatter}
|
|
8842
|
+
|
|
8843
|
+
${body.trim()}
|
|
8844
|
+
`;
|
|
8845
|
+
}
|
|
8846
|
+
/**
|
|
8847
|
+
* Generate Antigravity jai1 base rule
|
|
8848
|
+
*/
|
|
8849
|
+
generateAntigravityJai1Rule() {
|
|
8850
|
+
return `---
|
|
8851
|
+
trigger: always_on
|
|
8852
|
+
---
|
|
8853
|
+
|
|
8854
|
+
@AGENTS.md
|
|
8855
|
+
|
|
8856
|
+
${JAI1_BASE_RULE}`;
|
|
8857
|
+
}
|
|
8858
|
+
/**
|
|
8859
|
+
* Generate Claude Code format (.md files with paths metadata)
|
|
8860
|
+
*/
|
|
8861
|
+
generateClaudeFiles(bundle, format) {
|
|
8862
|
+
const files = [];
|
|
8863
|
+
files.push({
|
|
8864
|
+
path: `${format.rulesPath}/00-jai1${format.fileExtension}`,
|
|
8865
|
+
content: this.generateClaudeJai1Rule(),
|
|
8866
|
+
description: "Jai1 Framework base rule"
|
|
8867
|
+
});
|
|
8868
|
+
for (const [filename, content] of Object.entries(bundle.files)) {
|
|
8869
|
+
const converted = this.convertToClaude(content, filename);
|
|
8870
|
+
const newFilename = filename.replace(/\.mdc$/, ".md");
|
|
8871
|
+
files.push({
|
|
8872
|
+
path: `${format.rulesPath}/${newFilename}`,
|
|
8873
|
+
content: converted,
|
|
8874
|
+
description: `Preset rule: ${newFilename}`
|
|
8875
|
+
});
|
|
8876
|
+
}
|
|
8877
|
+
return files;
|
|
8878
|
+
}
|
|
8879
|
+
/**
|
|
8880
|
+
* Convert Cursor format to Claude format
|
|
8881
|
+
*/
|
|
8882
|
+
convertToClaude(content, filename) {
|
|
8883
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
8884
|
+
if (!frontmatterMatch) {
|
|
8885
|
+
return content;
|
|
8886
|
+
}
|
|
8887
|
+
const [, frontmatter, body] = frontmatterMatch;
|
|
8888
|
+
const parsed = this.parseFrontmatter(frontmatter);
|
|
8889
|
+
let metadata = `---
|
|
8890
|
+
description: ${parsed.description || "Rule"}`;
|
|
8891
|
+
if (parsed.globs && parsed.globs.length > 0) {
|
|
8892
|
+
const paths = parsed.globs.map((g) => g.replace(/[\[\]"]/g, "")).join(", ");
|
|
8893
|
+
metadata += `
|
|
8894
|
+
paths: ${paths}`;
|
|
8895
|
+
}
|
|
8896
|
+
metadata += "\n---";
|
|
8897
|
+
return `${metadata}
|
|
8898
|
+
|
|
8899
|
+
${body.trim()}
|
|
8900
|
+
`;
|
|
8901
|
+
}
|
|
8902
|
+
/**
|
|
8903
|
+
* Generate Claude jai1 base rule
|
|
8904
|
+
*/
|
|
8905
|
+
generateClaudeJai1Rule() {
|
|
8906
|
+
return `---
|
|
8907
|
+
description: Jai1 Framework integration and skill loading
|
|
8908
|
+
paths: **/*
|
|
8909
|
+
---
|
|
8910
|
+
|
|
8911
|
+
${JAI1_BASE_RULE}`;
|
|
8912
|
+
}
|
|
8913
|
+
/**
|
|
8914
|
+
* Generate AGENTS.md file (single merged file)
|
|
8915
|
+
*/
|
|
8916
|
+
generateAgentsMd(bundle, format) {
|
|
8917
|
+
const sections = [];
|
|
8918
|
+
sections.push("# AGENTS.md\n");
|
|
8919
|
+
sections.push(`<!-- Generated by jai1 rules - Preset: ${bundle.preset.slug} v${bundle.preset.version} -->
|
|
8920
|
+
`);
|
|
8921
|
+
sections.push("## Jai1 Framework Integration\n");
|
|
8922
|
+
sections.push(JAI1_BASE_RULE.trim());
|
|
8923
|
+
sections.push("\n---\n");
|
|
8924
|
+
const fileOrder = [
|
|
8925
|
+
"01-project.mdc",
|
|
8926
|
+
"02-standards.mdc",
|
|
8927
|
+
"03-frontend.mdc",
|
|
8928
|
+
"04-backend.mdc",
|
|
8929
|
+
"05-testing.mdc",
|
|
8930
|
+
"06-workflow.mdc",
|
|
8931
|
+
"07-deployment.mdc",
|
|
8932
|
+
"08-database.mdc",
|
|
8933
|
+
"09-custom.mdc"
|
|
8934
|
+
];
|
|
8935
|
+
for (const filename of fileOrder) {
|
|
8936
|
+
if (bundle.files[filename]) {
|
|
8937
|
+
const content = bundle.files[filename];
|
|
8938
|
+
const withoutFrontmatter = this.removeFrontmatter(content);
|
|
8939
|
+
if (withoutFrontmatter.trim()) {
|
|
8940
|
+
sections.push(withoutFrontmatter.trim());
|
|
8941
|
+
sections.push("\n");
|
|
8942
|
+
}
|
|
8943
|
+
}
|
|
8944
|
+
}
|
|
8945
|
+
const agentsMd = sections.join("\n");
|
|
8946
|
+
return [
|
|
8947
|
+
{
|
|
8948
|
+
path: "AGENTS.md",
|
|
8949
|
+
content: agentsMd,
|
|
8950
|
+
description: "Single AGENTS.md file with all rules"
|
|
8951
|
+
}
|
|
8952
|
+
];
|
|
8953
|
+
}
|
|
8954
|
+
/**
|
|
8955
|
+
* Generate Gemini CLI format (GEMINI.md that references AGENTS.md)
|
|
8956
|
+
*/
|
|
8957
|
+
generateGeminiMd(bundle, format) {
|
|
8958
|
+
const content = `# Gemini Instructions
|
|
8959
|
+
|
|
8960
|
+
@AGENTS.md
|
|
8961
|
+
|
|
8962
|
+
You are an AI coding assistant integrated with the Jai1 Framework.
|
|
8963
|
+
Follow all instructions and patterns defined in AGENTS.md above.
|
|
8964
|
+
|
|
8965
|
+
## Additional Gemini-Specific Guidelines
|
|
8966
|
+
|
|
8967
|
+
- Use the context from AGENTS.md for all coding decisions
|
|
8968
|
+
- Apply the Jai1 Framework patterns consistently
|
|
8969
|
+
- Reference skills using the @jai1-[skill-name] syntax
|
|
8970
|
+
- Maintain consistency with project standards
|
|
8971
|
+
|
|
8972
|
+
<!-- Generated by jai1 rules - Preset: ${bundle.preset.slug} v${bundle.preset.version} -->
|
|
8973
|
+
`;
|
|
8974
|
+
return [
|
|
8975
|
+
{
|
|
8976
|
+
path: "GEMINI.md",
|
|
8977
|
+
content,
|
|
8978
|
+
description: "Gemini CLI instructions file"
|
|
8979
|
+
}
|
|
8980
|
+
];
|
|
8981
|
+
}
|
|
8982
|
+
/**
|
|
8983
|
+
* Remove frontmatter from content
|
|
8984
|
+
*/
|
|
8985
|
+
removeFrontmatter(content) {
|
|
8986
|
+
return content.replace(/^---\n[\s\S]*?\n---\n/, "");
|
|
8987
|
+
}
|
|
8988
|
+
/**
|
|
8989
|
+
* Parse YAML frontmatter into object
|
|
8990
|
+
*/
|
|
8991
|
+
parseFrontmatter(frontmatter) {
|
|
8992
|
+
const result = {};
|
|
8993
|
+
const lines = frontmatter.split("\n");
|
|
8994
|
+
for (const line of lines) {
|
|
8995
|
+
const match = line.match(/^(\w+):\s*(.+)$/);
|
|
8996
|
+
if (match) {
|
|
8997
|
+
const [, key, value] = match;
|
|
8998
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
8999
|
+
result[key] = value.slice(1, -1).split(",").map((v) => v.trim().replace(/^["']|["']$/g, ""));
|
|
9000
|
+
} else if (value === "true" || value === "false") {
|
|
9001
|
+
result[key] = value === "true";
|
|
9002
|
+
} else {
|
|
9003
|
+
result[key] = value.replace(/^["']|["']$/g, "");
|
|
9004
|
+
}
|
|
9005
|
+
}
|
|
9006
|
+
}
|
|
9007
|
+
return result;
|
|
9008
|
+
}
|
|
9009
|
+
/**
|
|
9010
|
+
* Get all IDE IDs that can be generated
|
|
9011
|
+
*/
|
|
9012
|
+
getAvailableIdes() {
|
|
9013
|
+
return Object.keys(IDE_FORMATS);
|
|
9014
|
+
}
|
|
9015
|
+
/**
|
|
9016
|
+
* Check if IDE format exists
|
|
9017
|
+
*/
|
|
9018
|
+
isValidIde(ideId) {
|
|
9019
|
+
return ideId in IDE_FORMATS;
|
|
9020
|
+
}
|
|
9021
|
+
/**
|
|
9022
|
+
* Get IDE format details
|
|
9023
|
+
*/
|
|
9024
|
+
getIdeFormat(ideId) {
|
|
9025
|
+
return IDE_FORMATS[ideId];
|
|
9026
|
+
}
|
|
9027
|
+
/**
|
|
9028
|
+
* Resolve IDE dependencies
|
|
9029
|
+
* For example, Gemini requires AGENTS.md
|
|
9030
|
+
*/
|
|
9031
|
+
resolveIdeDependencies(ides) {
|
|
9032
|
+
const resolved = new Set(ides);
|
|
9033
|
+
for (const ide of ides) {
|
|
9034
|
+
const format = IDE_FORMATS[ide];
|
|
9035
|
+
if (format?.dependencies) {
|
|
9036
|
+
format.dependencies.forEach((dep) => resolved.add(dep));
|
|
9037
|
+
}
|
|
9038
|
+
}
|
|
9039
|
+
return Array.from(resolved);
|
|
9040
|
+
}
|
|
9041
|
+
};
|
|
9042
|
+
|
|
9043
|
+
// src/services/backup.service.ts
|
|
9044
|
+
import { promises as fs14 } from "fs";
|
|
9045
|
+
import { join as join8, dirname } from "path";
|
|
9046
|
+
var BackupService = class {
|
|
9047
|
+
backupDir = ".jai1/backups";
|
|
9048
|
+
/**
|
|
9049
|
+
* Create backup of existing rules for specified IDEs
|
|
9050
|
+
*/
|
|
9051
|
+
async createBackup(ides, presetSlug) {
|
|
9052
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
9053
|
+
const backupPath = join8(this.backupDir, timestamp);
|
|
9054
|
+
const backedUpFiles = [];
|
|
9055
|
+
let hasContent = false;
|
|
9056
|
+
for (const ideId of ides) {
|
|
9057
|
+
const format = IDE_FORMATS[ideId];
|
|
9058
|
+
if (!format) {
|
|
9059
|
+
console.warn(`Unknown IDE format: ${ideId}, skipping backup`);
|
|
9060
|
+
continue;
|
|
9061
|
+
}
|
|
9062
|
+
const rulesPath = format.rulesPath === "." ? process.cwd() : join8(process.cwd(), format.rulesPath);
|
|
9063
|
+
try {
|
|
9064
|
+
const exists = await this.pathExists(rulesPath);
|
|
9065
|
+
if (!exists) {
|
|
9066
|
+
continue;
|
|
9067
|
+
}
|
|
9068
|
+
if (ideId === "agentsmd") {
|
|
9069
|
+
await this.backupSingleFile("AGENTS.md", backupPath, ideId, backedUpFiles);
|
|
9070
|
+
hasContent = true;
|
|
9071
|
+
} else if (ideId === "gemini") {
|
|
9072
|
+
await this.backupSingleFile("GEMINI.md", backupPath, ideId, backedUpFiles);
|
|
9073
|
+
hasContent = true;
|
|
9074
|
+
} else {
|
|
9075
|
+
const stats = await fs14.stat(rulesPath);
|
|
9076
|
+
if (stats.isDirectory()) {
|
|
9077
|
+
const files = await fs14.readdir(rulesPath);
|
|
9078
|
+
for (const file of files) {
|
|
9079
|
+
if (file.endsWith(format.fileExtension)) {
|
|
9080
|
+
const originalPath = join8(rulesPath, file);
|
|
9081
|
+
const relativePath = join8(format.rulesPath, file);
|
|
9082
|
+
const destPath = join8(backupPath, ideId, file);
|
|
9083
|
+
await fs14.mkdir(dirname(destPath), { recursive: true });
|
|
9084
|
+
await fs14.copyFile(originalPath, destPath);
|
|
9085
|
+
backedUpFiles.push({
|
|
9086
|
+
originalPath: relativePath,
|
|
9087
|
+
backupPath: join8(ideId, file),
|
|
9088
|
+
ide: ideId
|
|
9089
|
+
});
|
|
9090
|
+
hasContent = true;
|
|
9091
|
+
}
|
|
9092
|
+
}
|
|
9093
|
+
}
|
|
9094
|
+
}
|
|
9095
|
+
} catch (error) {
|
|
9096
|
+
continue;
|
|
9097
|
+
}
|
|
9098
|
+
}
|
|
9099
|
+
if (!hasContent) {
|
|
9100
|
+
return null;
|
|
9101
|
+
}
|
|
9102
|
+
const metadata = {
|
|
9103
|
+
timestamp,
|
|
9104
|
+
preset: presetSlug,
|
|
9105
|
+
ides,
|
|
9106
|
+
files: backedUpFiles
|
|
9107
|
+
};
|
|
9108
|
+
await fs14.mkdir(backupPath, { recursive: true });
|
|
9109
|
+
await fs14.writeFile(
|
|
9110
|
+
join8(backupPath, "metadata.json"),
|
|
9111
|
+
JSON.stringify(metadata, null, 2),
|
|
9112
|
+
"utf-8"
|
|
9113
|
+
);
|
|
9114
|
+
return backupPath;
|
|
9115
|
+
}
|
|
9116
|
+
/**
|
|
9117
|
+
* Backup a single file (for AGENTS.md, GEMINI.md)
|
|
9118
|
+
*/
|
|
9119
|
+
async backupSingleFile(filename, backupPath, ideId, backedUpFiles) {
|
|
9120
|
+
const originalPath = join8(process.cwd(), filename);
|
|
9121
|
+
try {
|
|
9122
|
+
const exists = await this.pathExists(originalPath);
|
|
9123
|
+
if (!exists) {
|
|
9124
|
+
return;
|
|
9125
|
+
}
|
|
9126
|
+
const destPath = join8(backupPath, ideId, filename);
|
|
9127
|
+
await fs14.mkdir(dirname(destPath), { recursive: true });
|
|
9128
|
+
await fs14.copyFile(originalPath, destPath);
|
|
9129
|
+
backedUpFiles.push({
|
|
9130
|
+
originalPath: filename,
|
|
9131
|
+
backupPath: join8(ideId, filename),
|
|
9132
|
+
ide: ideId
|
|
9133
|
+
});
|
|
9134
|
+
} catch (error) {
|
|
9135
|
+
}
|
|
9136
|
+
}
|
|
9137
|
+
/**
|
|
9138
|
+
* Restore files from a backup
|
|
9139
|
+
*/
|
|
9140
|
+
async restoreBackup(backupPath) {
|
|
9141
|
+
const metadataPath = join8(backupPath, "metadata.json");
|
|
9142
|
+
const metadataContent = await fs14.readFile(metadataPath, "utf-8");
|
|
9143
|
+
const metadata = JSON.parse(metadataContent);
|
|
9144
|
+
console.log(`
|
|
9145
|
+
Restoring backup from ${metadata.timestamp}...`);
|
|
9146
|
+
for (const file of metadata.files) {
|
|
9147
|
+
const sourcePath = join8(backupPath, file.backupPath);
|
|
9148
|
+
const destPath = join8(process.cwd(), file.originalPath);
|
|
9149
|
+
await fs14.mkdir(dirname(destPath), { recursive: true });
|
|
9150
|
+
await fs14.copyFile(sourcePath, destPath);
|
|
9151
|
+
console.log(`\u2713 Restored ${file.originalPath}`);
|
|
9152
|
+
}
|
|
9153
|
+
console.log("\n\u2705 Backup restored successfully!");
|
|
9154
|
+
}
|
|
9155
|
+
/**
|
|
9156
|
+
* List all available backups
|
|
9157
|
+
*/
|
|
9158
|
+
async listBackups() {
|
|
9159
|
+
try {
|
|
9160
|
+
const backupDirPath = join8(process.cwd(), this.backupDir);
|
|
9161
|
+
const exists = await this.pathExists(backupDirPath);
|
|
9162
|
+
if (!exists) {
|
|
9163
|
+
return [];
|
|
9164
|
+
}
|
|
9165
|
+
const entries = await fs14.readdir(backupDirPath, { withFileTypes: true });
|
|
9166
|
+
const backups = [];
|
|
9167
|
+
for (const entry of entries) {
|
|
9168
|
+
if (entry.isDirectory()) {
|
|
9169
|
+
const metadataPath = join8(backupDirPath, entry.name, "metadata.json");
|
|
9170
|
+
try {
|
|
9171
|
+
const metadataContent = await fs14.readFile(metadataPath, "utf-8");
|
|
9172
|
+
const metadata = JSON.parse(metadataContent);
|
|
9173
|
+
backups.push(metadata);
|
|
9174
|
+
} catch {
|
|
9175
|
+
continue;
|
|
9176
|
+
}
|
|
9177
|
+
}
|
|
9178
|
+
}
|
|
9179
|
+
return backups.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
9180
|
+
} catch {
|
|
9181
|
+
return [];
|
|
9182
|
+
}
|
|
9183
|
+
}
|
|
9184
|
+
/**
|
|
9185
|
+
* Delete a specific backup
|
|
9186
|
+
*/
|
|
9187
|
+
async deleteBackup(timestamp) {
|
|
9188
|
+
const backupPath = join8(process.cwd(), this.backupDir, timestamp);
|
|
9189
|
+
await this.deleteDirectory(backupPath);
|
|
9190
|
+
}
|
|
9191
|
+
/**
|
|
9192
|
+
* Delete all backups older than specified days
|
|
9193
|
+
*/
|
|
9194
|
+
async cleanupOldBackups(daysToKeep = 30) {
|
|
9195
|
+
const backups = await this.listBackups();
|
|
9196
|
+
const cutoffDate = /* @__PURE__ */ new Date();
|
|
9197
|
+
cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);
|
|
9198
|
+
let deletedCount = 0;
|
|
9199
|
+
for (const backup of backups) {
|
|
9200
|
+
const backupDate = new Date(backup.timestamp);
|
|
9201
|
+
if (backupDate < cutoffDate) {
|
|
9202
|
+
await this.deleteBackup(backup.timestamp);
|
|
9203
|
+
deletedCount++;
|
|
9204
|
+
}
|
|
9205
|
+
}
|
|
9206
|
+
return deletedCount;
|
|
9207
|
+
}
|
|
9208
|
+
/**
|
|
9209
|
+
* Get the most recent backup
|
|
9210
|
+
*/
|
|
9211
|
+
async getLatestBackup() {
|
|
9212
|
+
const backups = await this.listBackups();
|
|
9213
|
+
return backups.length > 0 ? backups[0] : null;
|
|
9214
|
+
}
|
|
9215
|
+
/**
|
|
9216
|
+
* Check if a path exists
|
|
9217
|
+
*/
|
|
9218
|
+
async pathExists(path8) {
|
|
9219
|
+
try {
|
|
9220
|
+
await fs14.access(path8);
|
|
9221
|
+
return true;
|
|
9222
|
+
} catch {
|
|
9223
|
+
return false;
|
|
9224
|
+
}
|
|
9225
|
+
}
|
|
9226
|
+
/**
|
|
9227
|
+
* Recursively delete a directory
|
|
9228
|
+
*/
|
|
9229
|
+
async deleteDirectory(path8) {
|
|
9230
|
+
try {
|
|
9231
|
+
const exists = await this.pathExists(path8);
|
|
9232
|
+
if (!exists) {
|
|
9233
|
+
return;
|
|
9234
|
+
}
|
|
9235
|
+
const entries = await fs14.readdir(path8, { withFileTypes: true });
|
|
9236
|
+
for (const entry of entries) {
|
|
9237
|
+
const fullPath = join8(path8, entry.name);
|
|
9238
|
+
if (entry.isDirectory()) {
|
|
9239
|
+
await this.deleteDirectory(fullPath);
|
|
9240
|
+
} else {
|
|
9241
|
+
await fs14.unlink(fullPath);
|
|
9242
|
+
}
|
|
9243
|
+
}
|
|
9244
|
+
await fs14.rmdir(path8);
|
|
9245
|
+
} catch (error) {
|
|
9246
|
+
}
|
|
9247
|
+
}
|
|
9248
|
+
/**
|
|
9249
|
+
* Get backup directory path
|
|
9250
|
+
*/
|
|
9251
|
+
getBackupDir() {
|
|
9252
|
+
return join8(process.cwd(), this.backupDir);
|
|
9253
|
+
}
|
|
9254
|
+
/**
|
|
9255
|
+
* Ensure backup directory exists
|
|
9256
|
+
*/
|
|
9257
|
+
async ensureBackupDir() {
|
|
9258
|
+
const backupDirPath = join8(process.cwd(), this.backupDir);
|
|
9259
|
+
await fs14.mkdir(backupDirPath, { recursive: true });
|
|
9260
|
+
}
|
|
9261
|
+
};
|
|
9262
|
+
|
|
9263
|
+
// src/commands/rules/apply.ts
|
|
9264
|
+
function createRulesApplyCommand() {
|
|
9265
|
+
return new Command38("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) => {
|
|
9266
|
+
const configService = new ConfigService();
|
|
9267
|
+
const config = await configService.load();
|
|
9268
|
+
if (!config) {
|
|
9269
|
+
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
9270
|
+
}
|
|
9271
|
+
const generatorService = new RulesGeneratorService();
|
|
9272
|
+
const backupService = new BackupService();
|
|
9273
|
+
const autoMode = options.yes === true;
|
|
9274
|
+
const skipBackup = options.skipBackup === true;
|
|
9275
|
+
let selectedIdes = [];
|
|
9276
|
+
if (options.ides) {
|
|
9277
|
+
selectedIdes = options.ides.split(",").map((ide) => ide.trim()).filter((ide) => generatorService.isValidIde(ide));
|
|
9278
|
+
if (selectedIdes.length === 0) {
|
|
9279
|
+
throw new ValidationError("No valid IDE formats specified");
|
|
9280
|
+
}
|
|
9281
|
+
}
|
|
9282
|
+
if (!presetSlug) {
|
|
9283
|
+
console.log("\u{1F4CB} Fetching available presets...\n");
|
|
9284
|
+
const response = await fetch(`${config.apiUrl}/api/rules/presets`, {
|
|
9285
|
+
headers: {
|
|
9286
|
+
"JAI1-Access-Key": config.accessKey
|
|
9287
|
+
}
|
|
9288
|
+
});
|
|
9289
|
+
if (!response.ok) {
|
|
9290
|
+
throw new Error(`Failed to fetch presets: ${response.statusText}`);
|
|
9291
|
+
}
|
|
9292
|
+
const data = await response.json();
|
|
9293
|
+
if (data.total === 0) {
|
|
9294
|
+
console.log("No presets available.");
|
|
9295
|
+
return;
|
|
9296
|
+
}
|
|
9297
|
+
presetSlug = await select4({
|
|
9298
|
+
message: "Select a preset:",
|
|
9299
|
+
choices: data.presets.map((p) => ({
|
|
9300
|
+
name: `${p.name} - ${p.description}`,
|
|
9301
|
+
value: p.slug,
|
|
9302
|
+
description: `v${p.version} | ${p.tags.join(", ")}`
|
|
9303
|
+
}))
|
|
9304
|
+
});
|
|
9305
|
+
}
|
|
9306
|
+
console.log(`
|
|
9307
|
+
\u{1F4E6} Fetching preset: ${presetSlug}...
|
|
9308
|
+
`);
|
|
9309
|
+
const presetResponse = await fetch(`${config.apiUrl}/api/rules/presets/${presetSlug}`, {
|
|
9310
|
+
headers: {
|
|
9311
|
+
"JAI1-Access-Key": config.accessKey
|
|
9312
|
+
}
|
|
9313
|
+
});
|
|
9314
|
+
if (!presetResponse.ok) {
|
|
9315
|
+
if (presetResponse.status === 404) {
|
|
9316
|
+
throw new Error(`Preset '${presetSlug}' not found`);
|
|
9317
|
+
}
|
|
9318
|
+
throw new Error(`Failed to fetch preset: ${presetResponse.statusText}`);
|
|
9319
|
+
}
|
|
9320
|
+
const bundle = await presetResponse.json();
|
|
9321
|
+
console.log(`\u2713 Preset: ${bundle.preset.name} v${bundle.preset.version}`);
|
|
9322
|
+
console.log(` Files: ${Object.keys(bundle.files).length}`);
|
|
9323
|
+
if (selectedIdes.length === 0) {
|
|
9324
|
+
if (autoMode) {
|
|
9325
|
+
selectedIdes = ["cursor"];
|
|
9326
|
+
} else {
|
|
9327
|
+
const detectionService = new IdeDetectionService();
|
|
9328
|
+
const suggestions = await detectionService.suggestIdes();
|
|
9329
|
+
const detected = await detectionService.detectActiveIdes();
|
|
9330
|
+
if (suggestions.length > 0) {
|
|
9331
|
+
console.log("\n\u{1F4A1} Smart suggestions based on your project:\n");
|
|
9332
|
+
suggestions.slice(0, 3).forEach((s) => {
|
|
9333
|
+
const priority = s.priority === "high" ? "\u2B50" : s.priority === "medium" ? "\u{1F538}" : "\u25AB\uFE0F";
|
|
9334
|
+
console.log(`${priority} ${s.name} - ${s.reason}`);
|
|
9335
|
+
});
|
|
9336
|
+
console.log("");
|
|
9337
|
+
}
|
|
9338
|
+
const choices = [
|
|
9339
|
+
{
|
|
9340
|
+
name: "Cursor (.cursor/rules/)",
|
|
9341
|
+
value: "cursor",
|
|
9342
|
+
checked: suggestions.some((s) => s.ideId === "cursor") || detected.some((d) => d.id === "cursor")
|
|
9343
|
+
},
|
|
9344
|
+
{
|
|
9345
|
+
name: "Windsurf (.windsurf/rules/)",
|
|
9346
|
+
value: "windsurf",
|
|
9347
|
+
checked: detected.some((d) => d.id === "windsurf")
|
|
9348
|
+
},
|
|
9349
|
+
{
|
|
9350
|
+
name: "Antigravity (.agent/rules/)",
|
|
9351
|
+
value: "antigravity",
|
|
9352
|
+
checked: detected.some((d) => d.id === "antigravity")
|
|
9353
|
+
},
|
|
9354
|
+
{
|
|
9355
|
+
name: "Claude Code (.claude/rules/)",
|
|
9356
|
+
value: "claude",
|
|
9357
|
+
checked: suggestions.some((s) => s.ideId === "claude") || detected.some((d) => d.id === "claude")
|
|
9358
|
+
},
|
|
9359
|
+
{
|
|
9360
|
+
name: "AGENTS.md (single file)",
|
|
9361
|
+
value: "agentsmd",
|
|
9362
|
+
checked: suggestions.some((s) => s.ideId === "agentsmd")
|
|
9363
|
+
},
|
|
9364
|
+
{
|
|
9365
|
+
name: "Gemini CLI (GEMINI.md)",
|
|
9366
|
+
value: "gemini",
|
|
9367
|
+
checked: detected.some((d) => d.id === "gemini")
|
|
9368
|
+
}
|
|
9369
|
+
];
|
|
9370
|
+
selectedIdes = await checkbox5({
|
|
9371
|
+
message: "Select IDE formats to generate (pre-selected are recommended):",
|
|
9372
|
+
choices,
|
|
9373
|
+
required: true
|
|
9374
|
+
});
|
|
9375
|
+
}
|
|
9376
|
+
}
|
|
9377
|
+
const resolvedIdes = generatorService.resolveIdeDependencies(selectedIdes);
|
|
9378
|
+
if (resolvedIdes.length > selectedIdes.length) {
|
|
9379
|
+
const addedIdes = resolvedIdes.filter((ide) => !selectedIdes.includes(ide));
|
|
9380
|
+
console.log(`
|
|
9381
|
+
\u{1F4CC} Auto-added dependencies: ${addedIdes.join(", ")}`);
|
|
9382
|
+
}
|
|
9383
|
+
let backupPath = null;
|
|
9384
|
+
if (!skipBackup) {
|
|
9385
|
+
console.log("\n\u{1F4BE} Creating backup of existing rules...");
|
|
9386
|
+
try {
|
|
9387
|
+
backupPath = await backupService.createBackup(resolvedIdes, presetSlug);
|
|
9388
|
+
if (backupPath) {
|
|
9389
|
+
console.log(`\u2713 Backup created: ${backupPath}`);
|
|
9390
|
+
} else {
|
|
9391
|
+
console.log("\u2713 No existing files to backup");
|
|
9392
|
+
}
|
|
9393
|
+
} catch (error) {
|
|
9394
|
+
console.warn("\u26A0\uFE0F Backup failed, but continuing...");
|
|
9395
|
+
}
|
|
9396
|
+
}
|
|
9397
|
+
if (!autoMode) {
|
|
9398
|
+
console.log("\n\u{1F4CB} Summary:");
|
|
9399
|
+
console.log(` Preset: ${bundle.preset.name} v${bundle.preset.version}`);
|
|
9400
|
+
console.log(` IDEs: ${resolvedIdes.join(", ")}`);
|
|
9401
|
+
console.log(` Files: ${Object.keys(bundle.files).length} rule files`);
|
|
9402
|
+
if (backupPath) {
|
|
9403
|
+
console.log(` Backup: ${backupPath}`);
|
|
9404
|
+
}
|
|
9405
|
+
const proceed = await confirm6({
|
|
9406
|
+
message: "Apply these rules to the current directory?",
|
|
9407
|
+
default: true
|
|
9408
|
+
});
|
|
9409
|
+
if (!proceed) {
|
|
9410
|
+
console.log("Cancelled.");
|
|
9411
|
+
return;
|
|
9412
|
+
}
|
|
9413
|
+
}
|
|
9414
|
+
console.log("\n\u{1F4DD} Applying preset...\n");
|
|
9415
|
+
const allGeneratedFiles = [];
|
|
9416
|
+
for (const ideId of resolvedIdes) {
|
|
9417
|
+
try {
|
|
9418
|
+
const files = generatorService.generateForIde(bundle, ideId);
|
|
9419
|
+
for (const file of files) {
|
|
9420
|
+
const fullPath = join9(process.cwd(), file.path);
|
|
9421
|
+
await fs15.mkdir(join9(fullPath, ".."), { recursive: true });
|
|
9422
|
+
await fs15.writeFile(fullPath, file.content, "utf-8");
|
|
9423
|
+
console.log(`\u2713 [${ideId}] ${file.path}`);
|
|
9424
|
+
allGeneratedFiles.push({
|
|
9425
|
+
ide: ideId,
|
|
9426
|
+
path: file.path,
|
|
9427
|
+
description: file.description
|
|
9428
|
+
});
|
|
9429
|
+
}
|
|
9430
|
+
} catch (error) {
|
|
9431
|
+
console.error(`\u2717 Failed to generate ${ideId} files:`, error);
|
|
9432
|
+
}
|
|
9433
|
+
}
|
|
9434
|
+
const projectConfig = {
|
|
9435
|
+
preset: bundle.preset.slug,
|
|
9436
|
+
version: bundle.preset.version,
|
|
9437
|
+
appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9438
|
+
ides: resolvedIdes,
|
|
9439
|
+
customContext: "09-custom.mdc",
|
|
9440
|
+
backups: backupPath ? [
|
|
9441
|
+
{
|
|
9442
|
+
path: backupPath,
|
|
9443
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9444
|
+
ides: resolvedIdes
|
|
9445
|
+
}
|
|
9446
|
+
] : []
|
|
9447
|
+
};
|
|
9448
|
+
try {
|
|
9449
|
+
const existingConfigPath = join9(process.cwd(), "jai1-rules.json");
|
|
9450
|
+
const existingConfigContent = await fs15.readFile(existingConfigPath, "utf-8");
|
|
9451
|
+
const existingConfig = JSON.parse(existingConfigContent);
|
|
9452
|
+
if (existingConfig.backups && existingConfig.backups.length > 0) {
|
|
9453
|
+
projectConfig.backups = [
|
|
9454
|
+
...projectConfig.backups,
|
|
9455
|
+
...existingConfig.backups.slice(0, 5)
|
|
9456
|
+
// Keep last 5 backups
|
|
9457
|
+
];
|
|
9458
|
+
}
|
|
9459
|
+
} catch {
|
|
9460
|
+
}
|
|
9461
|
+
await fs15.writeFile(
|
|
9462
|
+
join9(process.cwd(), "jai1-rules.json"),
|
|
9463
|
+
JSON.stringify(projectConfig, null, 2),
|
|
9464
|
+
"utf-8"
|
|
9465
|
+
);
|
|
9466
|
+
console.log("\n\u2713 Updated jai1-rules.json");
|
|
9467
|
+
console.log("\n\u2705 Preset applied successfully!\n");
|
|
9468
|
+
console.log("\u{1F4CA} Summary:");
|
|
9469
|
+
console.log(` Generated ${allGeneratedFiles.length} files across ${resolvedIdes.length} IDE(s)`);
|
|
9470
|
+
const filesByIde = resolvedIdes.reduce((acc, ide) => {
|
|
9471
|
+
acc[ide] = allGeneratedFiles.filter((f) => f.ide === ide).length;
|
|
9472
|
+
return acc;
|
|
9473
|
+
}, {});
|
|
9474
|
+
for (const [ide, count] of Object.entries(filesByIde)) {
|
|
9475
|
+
const format = IDE_FORMATS[ide];
|
|
9476
|
+
console.log(` - ${format?.name}: ${count} files`);
|
|
9477
|
+
}
|
|
9478
|
+
if (backupPath) {
|
|
9479
|
+
console.log(`
|
|
9480
|
+
\u{1F4BE} Backup saved to: ${backupPath}`);
|
|
9481
|
+
console.log(' Use "jai1 rules restore" to restore if needed');
|
|
9482
|
+
}
|
|
9483
|
+
console.log("\n\u{1F4DD} Next steps:");
|
|
9484
|
+
console.log(" 1. Review generated files in your IDE");
|
|
9485
|
+
console.log(" 2. Edit custom rules if needed (look for 09-custom.*)");
|
|
9486
|
+
console.log(" 3. Commit the rules to git");
|
|
9487
|
+
console.log(' 4. Use "jai1 rules sync" to regenerate after editing');
|
|
9488
|
+
console.log(' 5. Use "jai1 ide status" to check IDE configuration\n');
|
|
9489
|
+
});
|
|
9490
|
+
}
|
|
9491
|
+
|
|
9492
|
+
// src/commands/rules/restore.ts
|
|
9493
|
+
import { Command as Command39 } from "commander";
|
|
9494
|
+
import { join as join10 } from "path";
|
|
9495
|
+
import { select as select5, confirm as confirm7 } from "@inquirer/prompts";
|
|
9496
|
+
function createRulesRestoreCommand() {
|
|
9497
|
+
return new Command39("restore").description("Restore rules from a backup").option("--latest", "Restore the most recent backup").option("-y, --yes", "Skip confirmation").action(async (options) => {
|
|
9498
|
+
const backupService = new BackupService();
|
|
9499
|
+
const backups = await backupService.listBackups();
|
|
9500
|
+
if (backups.length === 0) {
|
|
9501
|
+
console.log("No backups available.");
|
|
9502
|
+
return;
|
|
9503
|
+
}
|
|
9504
|
+
let selectedBackup;
|
|
9505
|
+
if (options.latest) {
|
|
9506
|
+
selectedBackup = backups[0];
|
|
9507
|
+
console.log(`\u{1F4E6} Selected latest backup: ${selectedBackup.timestamp}`);
|
|
9508
|
+
} else {
|
|
9509
|
+
console.log("\u{1F4CB} Available backups:\n");
|
|
9510
|
+
const backupTimestamp = await select5({
|
|
9511
|
+
message: "Select a backup to restore:",
|
|
9512
|
+
choices: backups.map((backup) => ({
|
|
9513
|
+
name: formatBackupInfo(backup),
|
|
9514
|
+
value: backup.timestamp,
|
|
9515
|
+
description: `${backup.files.length} files | IDEs: ${backup.ides.join(", ")}`
|
|
9516
|
+
}))
|
|
9517
|
+
});
|
|
9518
|
+
selectedBackup = backups.find((b) => b.timestamp === backupTimestamp);
|
|
9519
|
+
if (!selectedBackup) {
|
|
9520
|
+
throw new ValidationError("Backup not found");
|
|
9521
|
+
}
|
|
9522
|
+
}
|
|
9523
|
+
console.log("\n\u{1F4CA} Backup Details:");
|
|
9524
|
+
console.log(` Timestamp: ${formatTimestamp(selectedBackup.timestamp)}`);
|
|
9525
|
+
console.log(` Preset: ${selectedBackup.preset || "N/A"}`);
|
|
9526
|
+
console.log(` IDEs: ${selectedBackup.ides.join(", ")}`);
|
|
9527
|
+
console.log(` Files: ${selectedBackup.files.length}`);
|
|
9528
|
+
if (!options.yes) {
|
|
9529
|
+
const proceed = await confirm7({
|
|
9530
|
+
message: "This will overwrite current rules. Continue?",
|
|
9531
|
+
default: false
|
|
9532
|
+
});
|
|
9533
|
+
if (!proceed) {
|
|
9534
|
+
console.log("Cancelled.");
|
|
9535
|
+
return;
|
|
9536
|
+
}
|
|
9537
|
+
}
|
|
9538
|
+
console.log("\n\u{1F504} Restoring backup...\n");
|
|
9539
|
+
try {
|
|
9540
|
+
const backupPath = join10(backupService.getBackupDir(), selectedBackup.timestamp);
|
|
9541
|
+
await backupService.restoreBackup(backupPath);
|
|
9542
|
+
console.log("\n\u2705 Backup restored successfully!\n");
|
|
9543
|
+
console.log("\u{1F4A1} Tip: Your IDE may need to be restarted to pick up the changes.");
|
|
9544
|
+
} catch (error) {
|
|
9545
|
+
console.error("\n\u274C Failed to restore backup:", error);
|
|
9546
|
+
throw error;
|
|
9547
|
+
}
|
|
9548
|
+
});
|
|
9549
|
+
}
|
|
9550
|
+
function formatBackupInfo(backup) {
|
|
9551
|
+
const timestamp = formatTimestamp(backup.timestamp);
|
|
9552
|
+
const preset = backup.preset ? ` [${backup.preset}]` : "";
|
|
9553
|
+
return `${timestamp}${preset}`;
|
|
9554
|
+
}
|
|
9555
|
+
function formatTimestamp(timestamp) {
|
|
9556
|
+
try {
|
|
9557
|
+
const date = new Date(timestamp);
|
|
9558
|
+
return date.toLocaleString();
|
|
9559
|
+
} catch {
|
|
9560
|
+
return timestamp;
|
|
9561
|
+
}
|
|
9562
|
+
}
|
|
9563
|
+
|
|
9564
|
+
// src/commands/rules/sync.ts
|
|
9565
|
+
import { Command as Command40 } from "commander";
|
|
9566
|
+
import { promises as fs16 } from "fs";
|
|
9567
|
+
import { join as join11 } from "path";
|
|
9568
|
+
import { confirm as confirm8 } from "@inquirer/prompts";
|
|
9569
|
+
function createRulesSyncCommand() {
|
|
9570
|
+
return new Command40("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) => {
|
|
9571
|
+
const configPath = join11(process.cwd(), "jai1-rules.json");
|
|
9572
|
+
let projectConfig;
|
|
9573
|
+
try {
|
|
9574
|
+
const configContent = await fs16.readFile(configPath, "utf-8");
|
|
9575
|
+
projectConfig = JSON.parse(configContent);
|
|
9576
|
+
} catch {
|
|
9577
|
+
throw new ValidationError(
|
|
9578
|
+
'No jai1-rules.json found. Run "jai1 rules apply" first.'
|
|
9579
|
+
);
|
|
9580
|
+
}
|
|
9581
|
+
console.log("\u{1F504} Syncing rules...\n");
|
|
9582
|
+
console.log(`Preset: ${projectConfig.preset} v${projectConfig.version}`);
|
|
9583
|
+
let idesToSync = [];
|
|
9584
|
+
if (options.detect) {
|
|
9585
|
+
console.log("\n\u{1F50D} Auto-detecting active IDEs...");
|
|
9586
|
+
const detectionService = new IdeDetectionService();
|
|
9587
|
+
const detected = await detectionService.detectActiveIdes();
|
|
9588
|
+
if (detected.length === 0) {
|
|
9589
|
+
throw new ValidationError(
|
|
9590
|
+
'No active IDEs detected. Run "jai1 rules apply" first or use "jai1 ide status" to check.'
|
|
9591
|
+
);
|
|
9592
|
+
}
|
|
9593
|
+
console.log(`
|
|
9594
|
+
Detected ${detected.length} active IDE(s):
|
|
9595
|
+
`);
|
|
9596
|
+
detected.forEach((d) => {
|
|
9597
|
+
const confidence = d.confidence === "high" ? "\u{1F7E2}" : d.confidence === "medium" ? "\u{1F7E1}" : "\u{1F534}";
|
|
9598
|
+
console.log(` ${confidence} ${d.name} - ${d.ruleCount} rules`);
|
|
9599
|
+
});
|
|
9600
|
+
if (!options.yes) {
|
|
9601
|
+
const proceed = await confirm8({
|
|
9602
|
+
message: "\nSync these detected IDEs?",
|
|
9603
|
+
default: true
|
|
9604
|
+
});
|
|
9605
|
+
if (!proceed) {
|
|
9606
|
+
console.log("Cancelled.");
|
|
9607
|
+
return;
|
|
9608
|
+
}
|
|
9609
|
+
}
|
|
9610
|
+
idesToSync = detected.map((d) => d.id);
|
|
9611
|
+
} else if (options.ides) {
|
|
9612
|
+
const requested = options.ides.split(",").map((ide) => ide.trim());
|
|
9613
|
+
const configured = projectConfig.ides || [];
|
|
9614
|
+
idesToSync = requested;
|
|
9615
|
+
const notConfigured = requested.filter((ide) => !configured.includes(ide));
|
|
9616
|
+
if (notConfigured.length > 0) {
|
|
9617
|
+
console.log(`
|
|
9618
|
+
\u26A0\uFE0F IDEs not in config: ${notConfigured.join(", ")}`);
|
|
9619
|
+
console.log(" They will still be synced, but may not be in jai1-rules.json");
|
|
9620
|
+
}
|
|
9621
|
+
} else {
|
|
9622
|
+
idesToSync = projectConfig.ides || [];
|
|
9623
|
+
if (idesToSync.length === 0) {
|
|
9624
|
+
console.log("\n\u26A0\uFE0F No IDEs configured in jai1-rules.json");
|
|
9625
|
+
console.log(" Detecting from existing files...\n");
|
|
9626
|
+
const detectionService = new IdeDetectionService();
|
|
9627
|
+
idesToSync = await detectionService.detectExistingIdes();
|
|
9628
|
+
if (idesToSync.length === 0) {
|
|
9629
|
+
throw new ValidationError(
|
|
9630
|
+
'No rule outputs found. Run "jai1 rules apply" first.'
|
|
9631
|
+
);
|
|
9632
|
+
}
|
|
9633
|
+
console.log(` Found: ${idesToSync.join(", ")}
|
|
9634
|
+
`);
|
|
9635
|
+
}
|
|
9636
|
+
}
|
|
9637
|
+
console.log(`
|
|
9638
|
+
\u{1F4DD} Syncing ${idesToSync.length} IDE(s): ${idesToSync.join(", ")}
|
|
9639
|
+
`);
|
|
9640
|
+
const configService = new ConfigService();
|
|
9641
|
+
const config = await configService.load();
|
|
9642
|
+
if (!config) {
|
|
9643
|
+
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
9644
|
+
}
|
|
9645
|
+
const presetResponse = await fetch(`${config.apiUrl}/api/rules/presets/${projectConfig.preset}`, {
|
|
9646
|
+
headers: {
|
|
9647
|
+
"JAI1-Access-Key": config.accessKey
|
|
9648
|
+
}
|
|
9649
|
+
});
|
|
9650
|
+
if (!presetResponse.ok) {
|
|
9651
|
+
throw new Error(`Failed to fetch preset: ${presetResponse.statusText}`);
|
|
9652
|
+
}
|
|
9653
|
+
const bundle = await presetResponse.json();
|
|
9654
|
+
const sourceDir = join11(process.cwd(), ".cursor", "rules");
|
|
9655
|
+
const sourceExists = await checkPathExists(sourceDir);
|
|
9656
|
+
if (sourceExists) {
|
|
9657
|
+
const files = await fs16.readdir(sourceDir);
|
|
9658
|
+
for (const file of files) {
|
|
9659
|
+
if (file.endsWith(".mdc")) {
|
|
9660
|
+
const filePath = join11(sourceDir, file);
|
|
9661
|
+
const content = await fs16.readFile(filePath, "utf-8");
|
|
9662
|
+
bundle.files[file] = content;
|
|
9663
|
+
}
|
|
9664
|
+
}
|
|
9665
|
+
}
|
|
9666
|
+
const generatorService = new RulesGeneratorService();
|
|
9667
|
+
for (const ideId of idesToSync) {
|
|
9668
|
+
try {
|
|
9669
|
+
const format = IDE_FORMATS[ideId];
|
|
9670
|
+
if (!format) {
|
|
9671
|
+
console.log(`\u26A0\uFE0F Unknown IDE format: ${ideId}, skipping`);
|
|
9672
|
+
continue;
|
|
9673
|
+
}
|
|
9674
|
+
const files = generatorService.generateForIde(bundle, ideId);
|
|
9675
|
+
for (const file of files) {
|
|
9676
|
+
const fullPath = join11(process.cwd(), file.path);
|
|
9677
|
+
await fs16.mkdir(join11(fullPath, ".."), { recursive: true });
|
|
9678
|
+
await fs16.writeFile(fullPath, file.content, "utf-8");
|
|
9679
|
+
}
|
|
9680
|
+
console.log(`\u2713 ${format.name} - ${files.length} files regenerated`);
|
|
9681
|
+
} catch (error) {
|
|
9682
|
+
console.error(`\u2717 Failed to sync ${ideId}:`, error);
|
|
9683
|
+
}
|
|
9684
|
+
}
|
|
9685
|
+
if (JSON.stringify(projectConfig.ides) !== JSON.stringify(idesToSync)) {
|
|
9686
|
+
projectConfig.ides = idesToSync;
|
|
9687
|
+
await fs16.writeFile(
|
|
9688
|
+
join11(process.cwd(), "jai1-rules.json"),
|
|
9689
|
+
JSON.stringify(projectConfig, null, 2),
|
|
9690
|
+
"utf-8"
|
|
9691
|
+
);
|
|
9692
|
+
console.log("\n\u2713 Updated jai1-rules.json with synced IDEs");
|
|
9693
|
+
}
|
|
9694
|
+
console.log("\n\u2705 Rules synced successfully!\n");
|
|
9695
|
+
console.log("\u{1F4A1} Next steps:");
|
|
9696
|
+
console.log(" \u2022 Your IDE may need to be restarted to pick up changes");
|
|
9697
|
+
console.log(' \u2022 Use "jai1 ide status" to verify IDE configuration');
|
|
9698
|
+
console.log(" \u2022 Edit source files in .cursor/rules/ and sync again\n");
|
|
9699
|
+
});
|
|
9700
|
+
}
|
|
9701
|
+
async function checkPathExists(path8) {
|
|
9702
|
+
try {
|
|
9703
|
+
await fs16.access(join11(process.cwd(), path8));
|
|
9704
|
+
return true;
|
|
9705
|
+
} catch {
|
|
9706
|
+
return false;
|
|
9707
|
+
}
|
|
9708
|
+
}
|
|
9709
|
+
|
|
9710
|
+
// src/commands/rules/info.ts
|
|
9711
|
+
import { Command as Command41 } from "commander";
|
|
9712
|
+
import { promises as fs17 } from "fs";
|
|
9713
|
+
import { join as join12 } from "path";
|
|
9714
|
+
function createRulesInfoCommand() {
|
|
9715
|
+
return new Command41("info").description("Show current preset information").option("--json", "Output as JSON").action(async (options) => {
|
|
9716
|
+
const configPath = join12(process.cwd(), "jai1-rules.json");
|
|
9717
|
+
let projectConfig;
|
|
9718
|
+
try {
|
|
9719
|
+
const configContent = await fs17.readFile(configPath, "utf-8");
|
|
9720
|
+
projectConfig = JSON.parse(configContent);
|
|
9721
|
+
} catch {
|
|
9722
|
+
throw new ValidationError(
|
|
9723
|
+
'No jai1-rules.json found. Run "jai1 rules init" first.'
|
|
9724
|
+
);
|
|
9725
|
+
}
|
|
9726
|
+
if (options.json) {
|
|
9727
|
+
console.log(JSON.stringify(projectConfig, null, 2));
|
|
9728
|
+
return;
|
|
9729
|
+
}
|
|
9730
|
+
console.log("\u{1F4CB} Current Preset Information\n");
|
|
9731
|
+
console.log(`Preset: ${projectConfig.preset}`);
|
|
9732
|
+
console.log(`Version: ${projectConfig.version}`);
|
|
9733
|
+
console.log(`Applied at: ${new Date(projectConfig.appliedAt).toLocaleString()}`);
|
|
9734
|
+
console.log(`Custom rules: ${projectConfig.customContext}`);
|
|
9735
|
+
if (projectConfig.ides && projectConfig.ides.length > 0) {
|
|
9736
|
+
console.log(`
|
|
9737
|
+
Configured IDEs (${projectConfig.ides.length}):`);
|
|
9738
|
+
for (const ideId of projectConfig.ides) {
|
|
9739
|
+
const format = IDE_FORMATS[ideId];
|
|
9740
|
+
if (format) {
|
|
9741
|
+
const exists = await checkIdeFilesExist(ideId, format);
|
|
9742
|
+
const status = exists ? "\u2713" : "\u2717";
|
|
9743
|
+
console.log(` ${status} ${format.name} (${format.description})`);
|
|
9744
|
+
}
|
|
9745
|
+
}
|
|
9746
|
+
}
|
|
9747
|
+
if (projectConfig.backups && projectConfig.backups.length > 0) {
|
|
9748
|
+
console.log(`
|
|
9749
|
+
Available Backups (${projectConfig.backups.length}):`);
|
|
9750
|
+
for (const backup of projectConfig.backups.slice(0, 3)) {
|
|
9751
|
+
const timestamp = new Date(backup.timestamp).toLocaleString();
|
|
9752
|
+
console.log(` \u2022 ${timestamp} - IDEs: ${backup.ides.join(", ")}`);
|
|
9753
|
+
}
|
|
9754
|
+
if (projectConfig.backups.length > 3) {
|
|
9755
|
+
console.log(` ... and ${projectConfig.backups.length - 3} more`);
|
|
9756
|
+
}
|
|
9757
|
+
}
|
|
9758
|
+
console.log("\n\u2139\uFE0F Commands:");
|
|
9759
|
+
console.log(' \u2022 "jai1 rules sync" - Regenerate outputs after editing');
|
|
9760
|
+
console.log(' \u2022 "jai1 rules restore" - Restore from backup');
|
|
9761
|
+
console.log(' \u2022 "jai1 rules apply" - Apply a different preset');
|
|
9762
|
+
});
|
|
9763
|
+
}
|
|
9764
|
+
async function checkPathExists2(path8) {
|
|
9765
|
+
try {
|
|
9766
|
+
await fs17.access(join12(process.cwd(), path8));
|
|
9767
|
+
return true;
|
|
9768
|
+
} catch {
|
|
9769
|
+
return false;
|
|
9770
|
+
}
|
|
9771
|
+
}
|
|
9772
|
+
async function checkIdeFilesExist(ideId, format) {
|
|
9773
|
+
try {
|
|
9774
|
+
if (ideId === "agentsmd") {
|
|
9775
|
+
return await checkPathExists2("AGENTS.md");
|
|
9776
|
+
} else if (ideId === "gemini") {
|
|
9777
|
+
return await checkPathExists2("GEMINI.md");
|
|
9778
|
+
} else {
|
|
9779
|
+
return await checkPathExists2(format.rulesPath);
|
|
9780
|
+
}
|
|
9781
|
+
} catch {
|
|
9782
|
+
return false;
|
|
9783
|
+
}
|
|
9784
|
+
}
|
|
9785
|
+
|
|
9786
|
+
// src/commands/rules/index.ts
|
|
9787
|
+
function createRulesCommand() {
|
|
9788
|
+
const rulesCommand = new Command42("rules").description("Manage rule presets for AI agents");
|
|
9789
|
+
rulesCommand.addCommand(createRulesListCommand());
|
|
9790
|
+
rulesCommand.addCommand(createRulesInitCommand());
|
|
9791
|
+
rulesCommand.addCommand(createRulesApplyCommand());
|
|
9792
|
+
rulesCommand.addCommand(createRulesRestoreCommand());
|
|
9793
|
+
rulesCommand.addCommand(createRulesSyncCommand());
|
|
9794
|
+
rulesCommand.addCommand(createRulesInfoCommand());
|
|
9795
|
+
return rulesCommand;
|
|
9796
|
+
}
|
|
9797
|
+
|
|
9798
|
+
// src/commands/upgrade.ts
|
|
9799
|
+
import { Command as Command43 } from "commander";
|
|
9800
|
+
import { confirm as confirm9 } from "@inquirer/prompts";
|
|
9801
|
+
import { execSync as execSync2 } from "child_process";
|
|
9802
|
+
var colors3 = {
|
|
9803
|
+
yellow: "\x1B[33m",
|
|
9804
|
+
green: "\x1B[32m",
|
|
9805
|
+
cyan: "\x1B[36m",
|
|
9806
|
+
red: "\x1B[31m",
|
|
9807
|
+
reset: "\x1B[0m",
|
|
9808
|
+
bold: "\x1B[1m"
|
|
9809
|
+
};
|
|
9810
|
+
function createUpgradeCommand() {
|
|
9811
|
+
return new Command43("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) => {
|
|
9812
|
+
await handleUpgrade(options);
|
|
9813
|
+
});
|
|
9814
|
+
}
|
|
9815
|
+
async function handleUpgrade(options) {
|
|
9816
|
+
const configService = new ConfigService();
|
|
8048
9817
|
const config = await configService.load();
|
|
8049
9818
|
if (!config) {
|
|
8050
9819
|
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
@@ -8083,7 +9852,7 @@ ${colors3.bold}Current version:${colors3.reset} ${currentVersion}`);
|
|
|
8083
9852
|
return;
|
|
8084
9853
|
}
|
|
8085
9854
|
if (!options.force) {
|
|
8086
|
-
const shouldUpdate = await
|
|
9855
|
+
const shouldUpdate = await confirm9({
|
|
8087
9856
|
message: "Update to the latest version now?",
|
|
8088
9857
|
default: true
|
|
8089
9858
|
});
|
|
@@ -8187,11 +9956,11 @@ function getInstallCommand(packageManager2) {
|
|
|
8187
9956
|
}
|
|
8188
9957
|
|
|
8189
9958
|
// src/commands/clean.ts
|
|
8190
|
-
import { Command as
|
|
8191
|
-
import { confirm as
|
|
8192
|
-
import { join as
|
|
9959
|
+
import { Command as Command44 } from "commander";
|
|
9960
|
+
import { confirm as confirm10, select as select6 } from "@inquirer/prompts";
|
|
9961
|
+
import { join as join13 } from "path";
|
|
8193
9962
|
function createCleanCommand() {
|
|
8194
|
-
return new
|
|
9963
|
+
return new Command44("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) => {
|
|
8195
9964
|
await handleClean(options);
|
|
8196
9965
|
});
|
|
8197
9966
|
}
|
|
@@ -8202,7 +9971,7 @@ async function handleClean(options) {
|
|
|
8202
9971
|
{
|
|
8203
9972
|
name: "Backups",
|
|
8204
9973
|
description: "Component backup files (.jai1_backup/)",
|
|
8205
|
-
path:
|
|
9974
|
+
path: join13(cwd, ".jai1_backup"),
|
|
8206
9975
|
check: async () => {
|
|
8207
9976
|
const backups = await service.listBackups(cwd);
|
|
8208
9977
|
return { exists: backups.length > 0, count: backups.length };
|
|
@@ -8252,7 +10021,7 @@ async function handleClean(options) {
|
|
|
8252
10021
|
console.log(` Path: ${target.path}
|
|
8253
10022
|
`);
|
|
8254
10023
|
}
|
|
8255
|
-
const action = await
|
|
10024
|
+
const action = await select6({
|
|
8256
10025
|
message: "What do you want to clean?",
|
|
8257
10026
|
choices: [
|
|
8258
10027
|
...availableTargets.map(({ target, info }) => ({
|
|
@@ -8286,7 +10055,7 @@ async function cleanTarget(target, skipConfirm) {
|
|
|
8286
10055
|
}
|
|
8287
10056
|
const countStr = info.count ? ` (${info.count} items)` : "";
|
|
8288
10057
|
if (!skipConfirm) {
|
|
8289
|
-
const confirmed = await
|
|
10058
|
+
const confirmed = await confirm10({
|
|
8290
10059
|
message: `Delete ${target.name}${countStr}?`,
|
|
8291
10060
|
default: false
|
|
8292
10061
|
});
|
|
@@ -8304,7 +10073,7 @@ async function cleanTarget(target, skipConfirm) {
|
|
|
8304
10073
|
}
|
|
8305
10074
|
|
|
8306
10075
|
// src/commands/redmine/check.ts
|
|
8307
|
-
import { Command as
|
|
10076
|
+
import { Command as Command45 } from "commander";
|
|
8308
10077
|
|
|
8309
10078
|
// src/services/redmine-config.service.ts
|
|
8310
10079
|
import { readFile as readFile6 } from "fs/promises";
|
|
@@ -8611,7 +10380,7 @@ async function checkConnectivity(config) {
|
|
|
8611
10380
|
|
|
8612
10381
|
// src/commands/redmine/check.ts
|
|
8613
10382
|
function createRedmineCheckCommand() {
|
|
8614
|
-
const cmd = new
|
|
10383
|
+
const cmd = new Command45("check").description("Check Redmine connectivity").option("-c, --config <path>", "Config file path", "redmine.config.yaml").option("--json", "Output as JSON").action(async (options) => {
|
|
8615
10384
|
await handleRedmineCheck(options);
|
|
8616
10385
|
});
|
|
8617
10386
|
return cmd;
|
|
@@ -8639,7 +10408,7 @@ async function handleRedmineCheck(options) {
|
|
|
8639
10408
|
}
|
|
8640
10409
|
|
|
8641
10410
|
// src/commands/redmine/sync-issue.ts
|
|
8642
|
-
import { Command as
|
|
10411
|
+
import { Command as Command46 } from "commander";
|
|
8643
10412
|
|
|
8644
10413
|
// src/sync-issue.ts
|
|
8645
10414
|
import { resolve as resolve3, relative } from "path";
|
|
@@ -8782,10 +10551,10 @@ function generateFilename(issueId, title, config, existingSlugs = /* @__PURE__ *
|
|
|
8782
10551
|
|
|
8783
10552
|
// src/file.util.ts
|
|
8784
10553
|
import { readFile as readFile7, writeFile as writeFile2, mkdir } from "fs/promises";
|
|
8785
|
-
import { dirname } from "path";
|
|
10554
|
+
import { dirname as dirname2 } from "path";
|
|
8786
10555
|
import matter3 from "gray-matter";
|
|
8787
10556
|
async function ensureDir(filePath) {
|
|
8788
|
-
const dir =
|
|
10557
|
+
const dir = dirname2(filePath);
|
|
8789
10558
|
await mkdir(dir, { recursive: true });
|
|
8790
10559
|
}
|
|
8791
10560
|
async function readMarkdownFile(filePath) {
|
|
@@ -9015,7 +10784,7 @@ function extractIssueIdFromUrl(url) {
|
|
|
9015
10784
|
|
|
9016
10785
|
// src/commands/redmine/sync-issue.ts
|
|
9017
10786
|
function createSyncIssueCommand() {
|
|
9018
|
-
const cmd = new
|
|
10787
|
+
const cmd = new Command46("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) => {
|
|
9019
10788
|
await handleSyncIssue(options);
|
|
9020
10789
|
});
|
|
9021
10790
|
return cmd;
|
|
@@ -9059,7 +10828,7 @@ async function handleSyncIssue(options) {
|
|
|
9059
10828
|
}
|
|
9060
10829
|
|
|
9061
10830
|
// src/commands/redmine/sync-project.ts
|
|
9062
|
-
import { Command as
|
|
10831
|
+
import { Command as Command47 } from "commander";
|
|
9063
10832
|
|
|
9064
10833
|
// src/sync-project.ts
|
|
9065
10834
|
async function syncProject(config, options = {}) {
|
|
@@ -9129,7 +10898,7 @@ async function syncProject(config, options = {}) {
|
|
|
9129
10898
|
|
|
9130
10899
|
// src/commands/redmine/sync-project.ts
|
|
9131
10900
|
function createSyncProjectCommand() {
|
|
9132
|
-
const cmd = new
|
|
10901
|
+
const cmd = new Command47("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) => {
|
|
9133
10902
|
await handleSyncProject(options);
|
|
9134
10903
|
});
|
|
9135
10904
|
return cmd;
|
|
@@ -9184,12 +10953,12 @@ async function handleSyncProject(options) {
|
|
|
9184
10953
|
}
|
|
9185
10954
|
|
|
9186
10955
|
// src/commands/framework/info.ts
|
|
9187
|
-
import { Command as
|
|
9188
|
-
import { promises as
|
|
9189
|
-
import { join as
|
|
10956
|
+
import { Command as Command48 } from "commander";
|
|
10957
|
+
import { promises as fs18 } from "fs";
|
|
10958
|
+
import { join as join14 } from "path";
|
|
9190
10959
|
import { homedir as homedir5 } from "os";
|
|
9191
10960
|
function createInfoCommand() {
|
|
9192
|
-
const cmd = new
|
|
10961
|
+
const cmd = new Command48("info").description("Show jai1-client configuration and status").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
|
|
9193
10962
|
await handleInfo(options);
|
|
9194
10963
|
});
|
|
9195
10964
|
return cmd;
|
|
@@ -9200,7 +10969,7 @@ async function handleInfo(options) {
|
|
|
9200
10969
|
if (!config) {
|
|
9201
10970
|
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
9202
10971
|
}
|
|
9203
|
-
const frameworkPath =
|
|
10972
|
+
const frameworkPath = join14(homedir5(), ".jai1", "framework");
|
|
9204
10973
|
const projectStatus = await getProjectStatus2();
|
|
9205
10974
|
const info = {
|
|
9206
10975
|
configPath: configService.getConfigPath(),
|
|
@@ -9235,9 +11004,9 @@ function maskKey3(key) {
|
|
|
9235
11004
|
return "****" + key.slice(-4);
|
|
9236
11005
|
}
|
|
9237
11006
|
async function getProjectStatus2() {
|
|
9238
|
-
const projectJai1 =
|
|
11007
|
+
const projectJai1 = join14(process.cwd(), ".jai1");
|
|
9239
11008
|
try {
|
|
9240
|
-
await
|
|
11009
|
+
await fs18.access(projectJai1);
|
|
9241
11010
|
return { exists: true, version: "Synced" };
|
|
9242
11011
|
} catch {
|
|
9243
11012
|
return { exists: false };
|
|
@@ -9245,8 +11014,8 @@ async function getProjectStatus2() {
|
|
|
9245
11014
|
}
|
|
9246
11015
|
|
|
9247
11016
|
// src/commands/self-update.ts
|
|
9248
|
-
import { Command as
|
|
9249
|
-
import { confirm as
|
|
11017
|
+
import { Command as Command49 } from "commander";
|
|
11018
|
+
import { confirm as confirm11 } from "@inquirer/prompts";
|
|
9250
11019
|
import { execSync as execSync3 } from "child_process";
|
|
9251
11020
|
var colors4 = {
|
|
9252
11021
|
yellow: "\x1B[33m",
|
|
@@ -9257,7 +11026,7 @@ var colors4 = {
|
|
|
9257
11026
|
bold: "\x1B[1m"
|
|
9258
11027
|
};
|
|
9259
11028
|
function createSelfUpdateCommand() {
|
|
9260
|
-
return new
|
|
11029
|
+
return new Command49("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) => {
|
|
9261
11030
|
await handleSelfUpdate(options);
|
|
9262
11031
|
});
|
|
9263
11032
|
}
|
|
@@ -9301,7 +11070,7 @@ ${colors4.bold}Current version:${colors4.reset} ${currentVersion}`);
|
|
|
9301
11070
|
return;
|
|
9302
11071
|
}
|
|
9303
11072
|
if (!options.force) {
|
|
9304
|
-
const shouldUpdate = await
|
|
11073
|
+
const shouldUpdate = await confirm11({
|
|
9305
11074
|
message: "Update to the latest version now?",
|
|
9306
11075
|
default: true
|
|
9307
11076
|
});
|
|
@@ -9397,10 +11166,10 @@ function getInstallCommand2(packageManager2) {
|
|
|
9397
11166
|
}
|
|
9398
11167
|
|
|
9399
11168
|
// src/commands/clear-backups.ts
|
|
9400
|
-
import { Command as
|
|
9401
|
-
import { confirm as
|
|
11169
|
+
import { Command as Command50 } from "commander";
|
|
11170
|
+
import { confirm as confirm12 } from "@inquirer/prompts";
|
|
9402
11171
|
function createClearBackupsCommand() {
|
|
9403
|
-
return new
|
|
11172
|
+
return new Command50("clear-backups").description("Clear backup files").option("-y, --yes", "Skip confirmation").action(async (options) => {
|
|
9404
11173
|
const service = new ComponentsService();
|
|
9405
11174
|
const backups = await service.listBackups(process.cwd());
|
|
9406
11175
|
if (backups.length === 0) {
|
|
@@ -9416,7 +11185,7 @@ function createClearBackupsCommand() {
|
|
|
9416
11185
|
}
|
|
9417
11186
|
console.log();
|
|
9418
11187
|
if (!options.yes) {
|
|
9419
|
-
const ok = await
|
|
11188
|
+
const ok = await confirm12({ message: "Delete all backups?", default: false });
|
|
9420
11189
|
if (!ok) return;
|
|
9421
11190
|
}
|
|
9422
11191
|
await service.clearBackups(process.cwd());
|
|
@@ -9425,9 +11194,9 @@ function createClearBackupsCommand() {
|
|
|
9425
11194
|
}
|
|
9426
11195
|
|
|
9427
11196
|
// src/commands/vscode/index.ts
|
|
9428
|
-
import { Command as
|
|
9429
|
-
import { checkbox as
|
|
9430
|
-
import
|
|
11197
|
+
import { Command as Command51 } from "commander";
|
|
11198
|
+
import { checkbox as checkbox7, confirm as confirm13, select as select7 } from "@inquirer/prompts";
|
|
11199
|
+
import fs19 from "fs/promises";
|
|
9431
11200
|
import path7 from "path";
|
|
9432
11201
|
import { existsSync as existsSync3 } from "fs";
|
|
9433
11202
|
var PERFORMANCE_GROUPS2 = {
|
|
@@ -9565,7 +11334,7 @@ var PERFORMANCE_GROUPS2 = {
|
|
|
9565
11334
|
}
|
|
9566
11335
|
};
|
|
9567
11336
|
function createVSCodeCommand() {
|
|
9568
|
-
const vscodeCommand = new
|
|
11337
|
+
const vscodeCommand = new Command51("vscode").description("Qu\u1EA3n l\xFD c\xE0i \u0111\u1EB7t VSCode cho d\u1EF1 \xE1n hi\u1EC7n t\u1EA1i");
|
|
9569
11338
|
vscodeCommand.action(async () => {
|
|
9570
11339
|
await interactiveMode2();
|
|
9571
11340
|
});
|
|
@@ -9609,7 +11378,7 @@ async function interactiveMode2() {
|
|
|
9609
11378
|
console.log("\u2502 \u2022 Nh\u1EA5n ENTER \u0111\u1EC3 x\xE1c nh\u1EADn v\xE0 \xE1p d\u1EE5ng \u2502");
|
|
9610
11379
|
console.log("\u2502 \u2022 Nh\u1EA5n Ctrl+C \u0111\u1EC3 h\u1EE7y \u2502");
|
|
9611
11380
|
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");
|
|
9612
|
-
const action = await
|
|
11381
|
+
const action = await select7({
|
|
9613
11382
|
message: "B\u1EA1n mu\u1ED1n l\xE0m g\xEC?",
|
|
9614
11383
|
choices: [
|
|
9615
11384
|
{ name: "\u2705 Enable c\xE1c nh\xF3m t\u1ED1i \u01B0u", value: "enable" },
|
|
@@ -9633,7 +11402,7 @@ async function selectGroupsToApply2(action) {
|
|
|
9633
11402
|
value: key
|
|
9634
11403
|
}));
|
|
9635
11404
|
try {
|
|
9636
|
-
const selectedGroups = await
|
|
11405
|
+
const selectedGroups = await checkbox7({
|
|
9637
11406
|
message: `Ch\u1ECDn c\xE1c nh\xF3m \u0111\u1EC3 ${action === "enable" ? "enable" : "disable"} (SPACE \u0111\u1EC3 ch\u1ECDn, ENTER \u0111\u1EC3 x\xE1c nh\u1EADn):`,
|
|
9638
11407
|
choices
|
|
9639
11408
|
});
|
|
@@ -9658,18 +11427,18 @@ async function applyGroups2(groupKeys, action) {
|
|
|
9658
11427
|
return;
|
|
9659
11428
|
}
|
|
9660
11429
|
if (!existsSync3(vscodeDir)) {
|
|
9661
|
-
await
|
|
11430
|
+
await fs19.mkdir(vscodeDir, { recursive: true });
|
|
9662
11431
|
console.log("\u{1F4C1} \u0110\xE3 t\u1EA1o th\u01B0 m\u1EE5c .vscode/");
|
|
9663
11432
|
}
|
|
9664
11433
|
let currentSettings = {};
|
|
9665
11434
|
if (existsSync3(settingsPath)) {
|
|
9666
11435
|
try {
|
|
9667
|
-
const content = await
|
|
11436
|
+
const content = await fs19.readFile(settingsPath, "utf-8");
|
|
9668
11437
|
currentSettings = JSON.parse(content);
|
|
9669
11438
|
console.log("\u{1F4C4} \u0110\xE3 \u0111\u1ECDc c\xE0i \u0111\u1EB7t hi\u1EC7n t\u1EA1i t\u1EEB settings.json");
|
|
9670
11439
|
} catch {
|
|
9671
11440
|
console.warn("\u26A0\uFE0F Kh\xF4ng th\u1EC3 \u0111\u1ECDc settings.json (c\xF3 th\u1EC3 ch\u1EE9a comments).");
|
|
9672
|
-
const confirmOverwrite = await
|
|
11441
|
+
const confirmOverwrite = await confirm13({
|
|
9673
11442
|
message: "Ghi \u0111\xE8 file settings.json hi\u1EC7n t\u1EA1i?",
|
|
9674
11443
|
default: false
|
|
9675
11444
|
});
|
|
@@ -9704,7 +11473,7 @@ async function applyGroups2(groupKeys, action) {
|
|
|
9704
11473
|
}
|
|
9705
11474
|
}
|
|
9706
11475
|
}
|
|
9707
|
-
await
|
|
11476
|
+
await fs19.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
|
|
9708
11477
|
console.log(`
|
|
9709
11478
|
\u2705 \u0110\xE3 c\u1EADp nh\u1EADt c\xE0i \u0111\u1EB7t VSCode t\u1EA1i: ${settingsPath}`);
|
|
9710
11479
|
console.log("\u{1F4A1} M\u1EB9o: Kh\u1EDFi \u0111\u1ED9ng l\u1EA1i VSCode \u0111\u1EC3 \xE1p d\u1EE5ng c\xE1c thay \u0111\u1ED5i.");
|
|
@@ -9716,7 +11485,7 @@ async function resetSettings2(groupKeys) {
|
|
|
9716
11485
|
console.log("\n\u26A0\uFE0F Kh\xF4ng t\xECm th\u1EA5y file settings.json");
|
|
9717
11486
|
return;
|
|
9718
11487
|
}
|
|
9719
|
-
const confirmReset = await
|
|
11488
|
+
const confirmReset = await confirm13({
|
|
9720
11489
|
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(", ")}?`,
|
|
9721
11490
|
default: false
|
|
9722
11491
|
});
|
|
@@ -9725,7 +11494,7 @@ async function resetSettings2(groupKeys) {
|
|
|
9725
11494
|
return;
|
|
9726
11495
|
}
|
|
9727
11496
|
if (groupKeys.length === 0) {
|
|
9728
|
-
await
|
|
11497
|
+
await fs19.unlink(settingsPath);
|
|
9729
11498
|
console.log("\n\u2705 \u0110\xE3 x\xF3a file settings.json");
|
|
9730
11499
|
} else {
|
|
9731
11500
|
await applyGroups2(groupKeys, "disable");
|
|
@@ -9736,9 +11505,9 @@ async function resetSettings2(groupKeys) {
|
|
|
9736
11505
|
// src/commands/guide.ts
|
|
9737
11506
|
import React40 from "react";
|
|
9738
11507
|
import { render as render6 } from "ink";
|
|
9739
|
-
import { Command as
|
|
11508
|
+
import { Command as Command52 } from "commander";
|
|
9740
11509
|
function createGuideCommand() {
|
|
9741
|
-
const cmd = new
|
|
11510
|
+
const cmd = new Command52("guide").description("Interactive guide center for Agentic Coding").option("--topic <topic>", "Open specific topic (intro, rules, workflows, prompts, skills)").action(async (options) => {
|
|
9742
11511
|
const { waitUntilExit } = render6(
|
|
9743
11512
|
React40.createElement(GuideApp, {
|
|
9744
11513
|
initialTopic: options.topic,
|
|
@@ -9755,9 +11524,9 @@ function createGuideCommand() {
|
|
|
9755
11524
|
// src/commands/context.ts
|
|
9756
11525
|
import React41 from "react";
|
|
9757
11526
|
import { render as render7 } from "ink";
|
|
9758
|
-
import { Command as
|
|
11527
|
+
import { Command as Command53 } from "commander";
|
|
9759
11528
|
function createContextCommand() {
|
|
9760
|
-
const cmd = new
|
|
11529
|
+
const cmd = new Command53("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) => {
|
|
9761
11530
|
let initialIDE;
|
|
9762
11531
|
if (options.ide) {
|
|
9763
11532
|
const validIDEs = ["cursor", "windsurf", "antigravity", "jai1"];
|
|
@@ -9834,10 +11603,10 @@ async function printStats2() {
|
|
|
9834
11603
|
}
|
|
9835
11604
|
|
|
9836
11605
|
// src/commands/migrate-ide.ts
|
|
9837
|
-
import { Command as
|
|
9838
|
-
import { checkbox as
|
|
11606
|
+
import { Command as Command54 } from "commander";
|
|
11607
|
+
import { checkbox as checkbox8, confirm as confirm14 } from "@inquirer/prompts";
|
|
9839
11608
|
function createMigrateIdeCommand() {
|
|
9840
|
-
const cmd = new
|
|
11609
|
+
const cmd = new Command54("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) => {
|
|
9841
11610
|
await runMigrateIde(options);
|
|
9842
11611
|
});
|
|
9843
11612
|
return cmd;
|
|
@@ -9865,7 +11634,7 @@ async function runMigrateIde(options) {
|
|
|
9865
11634
|
value: ide
|
|
9866
11635
|
};
|
|
9867
11636
|
});
|
|
9868
|
-
selectedIdes = await
|
|
11637
|
+
selectedIdes = await checkbox8({
|
|
9869
11638
|
message: "Ch\u1ECDn IDE(s) \u0111\u1EC3 migrate (SPACE \u0111\u1EC3 ch\u1ECDn, ENTER \u0111\u1EC3 x\xE1c nh\u1EADn):",
|
|
9870
11639
|
choices: ideChoices
|
|
9871
11640
|
});
|
|
@@ -9884,7 +11653,7 @@ async function runMigrateIde(options) {
|
|
|
9884
11653
|
{ name: `Workflows (${content.workflows.length} files)`, value: "workflows" },
|
|
9885
11654
|
{ name: `Commands (${content.commands.length} files)`, value: "commands" }
|
|
9886
11655
|
];
|
|
9887
|
-
selectedTypes = await
|
|
11656
|
+
selectedTypes = await checkbox8({
|
|
9888
11657
|
message: "Ch\u1ECDn content types \u0111\u1EC3 migrate:",
|
|
9889
11658
|
choices: typeChoices
|
|
9890
11659
|
});
|
|
@@ -9906,7 +11675,7 @@ async function runMigrateIde(options) {
|
|
|
9906
11675
|
if (options.dryRun) {
|
|
9907
11676
|
console.log("\u{1F50D} DRY RUN - No files will be written\n");
|
|
9908
11677
|
}
|
|
9909
|
-
const confirmed = await
|
|
11678
|
+
const confirmed = await confirm14({
|
|
9910
11679
|
message: "Proceed with migration?",
|
|
9911
11680
|
default: true
|
|
9912
11681
|
});
|
|
@@ -9943,7 +11712,7 @@ async function runMigrateIde(options) {
|
|
|
9943
11712
|
}
|
|
9944
11713
|
|
|
9945
11714
|
// src/cli.ts
|
|
9946
|
-
var program = new
|
|
11715
|
+
var program = new Command55();
|
|
9947
11716
|
if (process.argv.includes("-v") || process.argv.includes("--version")) {
|
|
9948
11717
|
console.log(package_default.version);
|
|
9949
11718
|
if (!process.argv.includes("--skip-update-check")) {
|
|
@@ -9967,11 +11736,12 @@ program.addCommand(createTranslateCommand());
|
|
|
9967
11736
|
program.addCommand(createUtilsCommand());
|
|
9968
11737
|
program.addCommand(createDepsCommand());
|
|
9969
11738
|
program.addCommand(createKitCommand());
|
|
11739
|
+
program.addCommand(createRulesCommand());
|
|
9970
11740
|
program.addCommand(createUpgradeCommand());
|
|
9971
11741
|
program.addCommand(createCleanCommand());
|
|
9972
|
-
var redmineCommand = new
|
|
11742
|
+
var redmineCommand = new Command55("redmine").description("Redmine context sync commands");
|
|
9973
11743
|
redmineCommand.addCommand(createRedmineCheckCommand());
|
|
9974
|
-
var syncCommand = new
|
|
11744
|
+
var syncCommand = new Command55("sync").description("Sync Redmine issues to markdown files");
|
|
9975
11745
|
syncCommand.addCommand(createSyncIssueCommand());
|
|
9976
11746
|
syncCommand.addCommand(createSyncProjectCommand());
|
|
9977
11747
|
redmineCommand.addCommand(syncCommand);
|
|
@@ -10014,6 +11784,12 @@ program.on("command:*", (operands) => {
|
|
|
10014
11784
|
console.error(" deps Manage project dependencies");
|
|
10015
11785
|
console.error(" utils Developer tools (password, uuid, hash, jwt, etc.)");
|
|
10016
11786
|
console.error("");
|
|
11787
|
+
console.error(" \u{1F4CB} Rule Presets");
|
|
11788
|
+
console.error(" rules list List available rule presets");
|
|
11789
|
+
console.error(" rules init Apply preset to project");
|
|
11790
|
+
console.error(" rules sync Regenerate outputs after editing");
|
|
11791
|
+
console.error(" rules info Show current preset information");
|
|
11792
|
+
console.error("");
|
|
10017
11793
|
console.error(" \u{1F527} Maintenance");
|
|
10018
11794
|
console.error(" upgrade Upgrade jai1-client to latest version");
|
|
10019
11795
|
console.error(" clean Clean up backups and cache");
|