@jvittechs/jai1-cli 0.1.86 → 0.1.88

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.
Files changed (3) hide show
  1. package/dist/cli.js +1793 -368
  2. package/dist/cli.js.map +1 -1
  3. 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 Command52 } from "commander";
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.86",
36
+ version: "0.1.88",
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((input3, key) => {
898
+ useInput((input4, key) => {
899
899
  if (viewState === "summary") {
900
- if (key.return || input3 === "q" || key.escape) {
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 || input3 === "q") {
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 (input3 === " ") {
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 (input3 === "a") {
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 (input3 === "c") {
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 (input3 === " " || key.return) {
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 Command9 } from "commander";
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((input3, key) => {
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((input3, key) => {
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(input3, 10);
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((input3, key) => {
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((input3, key) => {
1557
- if (key.upArrow || input3 === "k") {
1556
+ useInput5((input4, key) => {
1557
+ if (key.upArrow || input4 === "k") {
1558
1558
  setScrollPosition((prev) => Math.max(0, prev - 1));
1559
- } else if (key.downArrow || input3 === "j") {
1559
+ } else if (key.downArrow || input4 === "j") {
1560
1560
  setScrollPosition((prev) => Math.min(maxScroll, prev + 1));
1561
- } else if (key.pageDown || input3 === "d") {
1561
+ } else if (key.pageDown || input4 === "d") {
1562
1562
  setScrollPosition((prev) => Math.min(maxScroll, prev + 5));
1563
- } else if (key.pageUp || input3 === "u") {
1563
+ } else if (key.pageUp || input4 === "u") {
1564
1564
  setScrollPosition((prev) => Math.max(0, prev - 5));
1565
- } else if (input3 === "g") {
1565
+ } else if (input4 === "g") {
1566
1566
  setScrollPosition(0);
1567
- } else if (input3 === "G") {
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((input3, key) => {
1964
- if (input3 === "q") {
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 Command9("ide").description("IDE integration and configuration commands");
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 Command10 } from "commander";
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((input3, key) => {
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((input3, key) => {
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((input3, key) => {
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 (input3 === "q") {
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 Command10("learn").description("Interactive Agentic Coding learning center").option("--topic <topic>", "Open specific topic (intro, rules, workflows, prompts, skills)").action(async (options) => {
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 Command11 } from "commander";
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((input3, key) => {
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((input3, key) => {
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 || input3 === "r")) {
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 Command11("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) => {
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 Command12 } from "commander";
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 Command12("openai-keys").description("Show OpenAI-compatible API credentials and info").option("--full", "Show full API key (unmasked)").action(async (options) => {
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 Command13 } from "commander";
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 Command13("stats").description("Hi\u1EC3n th\u1ECB th\u1ED1ng k\xEA s\u1EED d\u1EE5ng LLM v\xE0 gi\u1EDBi h\u1EA1n").action(async () => {
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 Command14 } from "commander";
4993
+ import { Command as Command15 } from "commander";
4535
4994
 
4536
4995
  // src/services/translation.service.ts
4537
- import { promises as fs7 } from "fs";
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(input3) {
5013
+ async detectInputType(input4) {
4555
5014
  try {
4556
- const stat = await fs7.stat(input3);
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 fs7.readFile(filePath, "utf-8");
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 fs7.writeFile(outputPath, translatedContent, "utf-8");
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 fs7.readdir(folderPath, { withFileTypes: true });
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(input3, options) {
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(input3, 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(input3);
5255
+ const inputType = await translationService.detectInputType(input4);
4797
5256
  switch (inputType) {
4798
5257
  case "text":
4799
- await handleTextTranslation(translationService, input3, options);
5258
+ await handleTextTranslation(translationService, input4, options);
4800
5259
  break;
4801
5260
  case "file":
4802
- await handleFileTranslation(translationService, input3, options);
5261
+ await handleFileTranslation(translationService, input4, options);
4803
5262
  break;
4804
5263
  case "folder":
4805
- await handleFolderTranslation(translationService, input3, options);
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 Command14("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 (input3, options) => {
4883
- await handleTranslate(input3, options);
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 Command28 } from "commander";
5348
+ import { Command as Command29 } from "commander";
4890
5349
 
4891
5350
  // src/commands/utils/password.ts
4892
- import { Command as Command15 } from "commander";
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(input3, algorithm) {
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(input3);
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(input3, rounds = 10) {
4962
- return bcrypt.hash(input3, rounds);
5420
+ async hashBcrypt(input4, rounds = 10) {
5421
+ return bcrypt.hash(input4, rounds);
4963
5422
  }
4964
5423
  /**
4965
5424
  * Base64 encode
4966
5425
  */
4967
- base64Encode(input3, urlSafe = false) {
4968
- const buffer = typeof input3 === "string" ? Buffer.from(input3, "utf-8") : input3;
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(input3) {
4979
- let normalized = input3.replace(/-/g, "+").replace(/_/g, "/");
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(input3, full = false) {
5109
- return full ? encodeURI(input3) : encodeURIComponent(input3);
5567
+ urlEncode(input4, full = false) {
5568
+ return full ? encodeURI(input4) : encodeURIComponent(input4);
5110
5569
  }
5111
5570
  /**
5112
5571
  * URL decode
5113
5572
  */
5114
- urlDecode(input3, full = false) {
5115
- return full ? decodeURI(input3) : decodeURIComponent(input3);
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 Command15("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", `
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 Command16 } from "commander";
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 Command16("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", `
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 Command17 } from "commander";
5225
- async function handleHashGeneration(input3, options) {
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(input3, options) {
5233
5692
  }
5234
5693
  hash = await service.hashFile(options.file, options.algorithm);
5235
5694
  } else {
5236
- if (!input3) {
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(input3, options.rounds);
5700
+ hash = await service.hashBcrypt(input4, options.rounds);
5242
5701
  } else {
5243
- hash = await service.hash(input3, options.algorithm);
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(input3, options) {
5261
5720
  }
5262
5721
  }
5263
5722
  function createHashCommand() {
5264
- const cmd = new Command17("hash").description("Generate hash (MD5, SHA, bcrypt)").argument("[input]", "Text to hash").option(
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 (input3, options) => {
5277
- await handleHashGeneration(input3, {
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 Command18 } from "commander";
5745
+ import { Command as Command19 } from "commander";
5287
5746
  import { readFile as readFile2 } from "fs/promises";
5288
- async function handleBase64Encode(input3, options) {
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(input3, options) {
5293
5752
  const content = await readFile2(options.file);
5294
5753
  encoded = service.base64Encode(content, options.urlSafe);
5295
5754
  } else {
5296
- if (!input3) {
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(input3, options.urlSafe);
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(input3, options) {
5316
5775
  }
5317
5776
  }
5318
5777
  function createBase64EncodeCommand() {
5319
- const cmd = new Command18("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", `
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 (input3, options) => {
5326
- await handleBase64Encode(input3, options);
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 Command19 } from "commander";
5791
+ import { Command as Command20 } from "commander";
5333
5792
  import { readFile as readFile3, writeFile } from "fs/promises";
5334
- async function handleBase64Decode(input3, options) {
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 (!input3) {
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 = input3;
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(input3, options) {
5361
5820
  }
5362
5821
  }
5363
5822
  function createBase64DecodeCommand() {
5364
- const cmd = new Command19("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", `
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 (input3, options) => {
5371
- await handleBase64Decode(input3, options);
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 Command20 } from "commander";
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 Command20("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", `
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 Command21 } from "commander";
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 Command21("jwt").description("Decode and encode JWT tokens");
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 Command22 } from "commander";
5522
- async function handleUnixTime(input3, options) {
5980
+ import { Command as Command23 } from "commander";
5981
+ async function handleUnixTime(input4, options) {
5523
5982
  const service = new UtilsService();
5524
5983
  try {
5525
- if (!input3) {
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(input3, options) {
5532
5991
  console.log(` ISO: ${now.toISOString()}`);
5533
5992
  console.log(` Local: ${now.toLocaleString()}`);
5534
5993
  console.log();
5535
- } else if (/^\d+$/.test(input3)) {
5536
- const timestamp = parseInt(input3, 10);
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(input3, options) {
5546
6005
  }
5547
6006
  console.log();
5548
6007
  } else {
5549
- const date = new Date(input3);
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(input3, 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: ${input3}`);
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(input3, options) {
5566
6025
  }
5567
6026
  }
5568
6027
  function createUnixTimeCommand() {
5569
- const cmd = new Command22("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", `
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 (input3, options) => {
5578
- await handleUnixTime(input3, options);
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 Command23 } from "commander";
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 Command23("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", `
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 Command24 } from "commander";
5635
- async function handleUrlEncode(input3, options) {
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(input3, options.full);
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(input3, options) {
5652
6111
  }
5653
6112
  }
5654
6113
  function createUrlEncodeCommand() {
5655
- const cmd = new Command24("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", `
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 (input3, options) => {
5662
- await handleUrlEncode(input3, options);
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 Command25 } from "commander";
5669
- async function handleUrlDecode(input3, options) {
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(input3, options.full);
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(input3, options) {
5686
6145
  }
5687
6146
  }
5688
6147
  function createUrlDecodeCommand() {
5689
- const cmd = new Command25("url-decode").description("Decode URL-encoded string").argument("<input>", "Text to decode").option("--full", "Decode full URL (use decodeURI instead of decodeURIComponent)").addHelpText("after", `
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 (input3, options) => {
5696
- await handleUrlDecode(input3, options);
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 Command26 } from "commander";
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 Command26("cron").description("Parse and explain cron expression").argument("<expression>", 'Cron expression (e.g., "0 5 * * *")').addHelpText("after", `
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 Command27 } from "commander";
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 Command27("markdown-preview").description("Preview markdown file in terminal").argument("<file>", "Markdown file path").option("--raw", "Show raw markdown instead of rendered").addHelpText("after", `
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((input3, key) => {
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 (input3 === "c" && passwords.length > 0) {
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((input3, key) => {
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 (input3 === "c" && uuids.length > 0) {
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((input3, key) => {
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 (input3 === "c" && hash) {
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 [input3, setInput] = useState17("");
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((input4, key) => {
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 (input4 === "c" && result) {
6571
+ } else if (input5 === "c" && result) {
6113
6572
  handleCopy();
6114
6573
  }
6115
6574
  });
6116
6575
  const handleConvert = () => {
6117
- if (!input3) {
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(input3, urlSafe);
6583
+ const encoded = service.base64Encode(input4, urlSafe);
6125
6584
  setResult(encoded);
6126
6585
  } else {
6127
- const decoded = service.base64Decode(input3);
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: input3, onChange: setInput, placeholder: "Enter text..." }) : /* @__PURE__ */ React30.createElement(Text21, { dimColor: !input3 }, input3 || "(empty)"))),
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 [input3, setInput] = useState18("");
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((input4, key) => {
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 (input4 === "c" && result) {
6681
+ } else if (input5 === "c" && result) {
6223
6682
  handleCopy();
6224
6683
  }
6225
6684
  });
6226
6685
  const handleConvert = () => {
6227
- if (!input3) return;
6686
+ if (!input4) return;
6228
6687
  if (mode === "encode") {
6229
- const encoded = fullUrl ? service.urlEncode(input3, true) : service.urlEncode(input3, false);
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(input3, true) : service.urlDecode(input3, false);
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: input3, onChange: setInput, placeholder: "Enter text or URL..." }) : /* @__PURE__ */ React31.createElement(Text22, { dimColor: !input3 }, input3 || "(empty)"))),
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 [input3, setInput] = useState19("");
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((input4, key) => {
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 (input4 === "c" && result) {
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 (!input3) {
6791
+ if (!input4) {
6333
6792
  setError("Please enter a timestamp");
6334
6793
  return;
6335
6794
  }
6336
- const human = service.unixTimeToHuman(parseInt(input3, 10), format);
6795
+ const human = service.unixTimeToHuman(parseInt(input4, 10), format);
6337
6796
  setResult(human);
6338
6797
  } else {
6339
- if (!input3) {
6798
+ if (!input4) {
6340
6799
  setError("Please enter a date string");
6341
6800
  return;
6342
6801
  }
6343
- const timestamp = service.unixTimeFromString(input3);
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: input3,
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: !input3 }, input3 || "(empty)"))),
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((input3, key) => {
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 (input3 === "c" && result.token) {
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((input3, key) => {
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 (input3 === "c" && result) {
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((input3, key) => {
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 (input3 === "c" && result) {
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((input3, key) => {
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 (input3 === "c" && response) {
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 fs8 from "fs";
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((input3, key) => {
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 (!fs8.existsSync(resolvedPath)) {
7423
+ if (!fs9.existsSync(resolvedPath)) {
6965
7424
  setError(`File not found: ${resolvedPath}`);
6966
7425
  return;
6967
7426
  }
6968
- let fileContent = fs8.readFileSync(resolvedPath, "utf-8");
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((input3, key) => {
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 || input3 === "k") {
7530
+ if (key.upArrow || input4 === "k") {
7072
7531
  setSelectedIndex((prev) => prev > 0 ? prev - 1 : MENU_ITEMS2.length - 1);
7073
- } else if (key.downArrow || input3 === "j") {
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 (input3 === "q" || key.escape) {
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 Command28("utils").description("Developer utilities for common tasks").option("-i, --interactive", "Run in interactive mode").addHelpText("after", `
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 Command30 } from "commander";
7701
+ import { Command as Command31 } from "commander";
7243
7702
 
7244
7703
  // src/commands/deps/upgrade.ts
7245
- import { Command as Command29 } from "commander";
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 fs9 } from "fs";
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 fs9.readFile(pkgPath, "utf-8");
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 fs9.access(path6.join(cwd, file));
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 Command29("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) => {
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 Command30("deps").description("Qu\u1EA3n l\xFD dependencies trong project");
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 Command34 } from "commander";
8104
+ import { Command as Command35 } from "commander";
7646
8105
 
7647
8106
  // src/commands/kit/list.ts
7648
- import { Command as Command31 } from "commander";
8107
+ import { Command as Command32 } from "commander";
7649
8108
 
7650
8109
  // src/services/starter-kit.service.ts
7651
- import { promises as fs10 } from "fs";
7652
- import { join as join4 } from "path";
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 = join4(process.env.TMPDIR || "/tmp", "jai1-kits");
7700
- await fs10.mkdir(tmpDir, { recursive: true });
7701
- const tmpFile = join4(tmpDir, `${slug}.zip`);
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 fs10.writeFile(tmpFile, Buffer.from(buffer));
8162
+ await fs11.writeFile(tmpFile, Buffer.from(buffer));
7704
8163
  if (onProgress) onProgress(60);
7705
8164
  const zip = new AdmZip(tmpFile);
7706
- await fs10.mkdir(targetDir, { recursive: true });
8165
+ await fs11.mkdir(targetDir, { recursive: true });
7707
8166
  zip.extractAllTo(targetDir, true);
7708
8167
  if (onProgress) onProgress(100);
7709
- await fs10.unlink(tmpFile);
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 Command31("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) => {
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 Command32 } from "commander";
8211
+ import { Command as Command33 } from "commander";
7753
8212
  function createKitInfoCommand() {
7754
- return new Command32("info").description("Show detailed information about a starter kit").argument("<slug>", "Starter kit slug").action(async (slug) => {
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 Command33 } from "commander";
7804
- import { promises as fs11 } from "fs";
7805
- import { join as join5 } from "path";
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 Command33("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) => {
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 || join5(process.cwd(), options.name || slug);
8327
+ const targetDir = directory || join6(process.cwd(), options.name || slug);
7869
8328
  try {
7870
- await fs11.access(targetDir);
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 fs11.readFile(file, "utf-8");
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 fs11.writeFile(file, content, "utf-8");
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 fs11.readdir(dir, { withFileTypes: true });
8463
+ const entries = await fs12.readdir(dir, { withFileTypes: true });
8005
8464
  for (const entry of entries) {
8006
- const fullPath = join5(dir, entry.name);
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 Command34("kit").description("Manage starter kits for new projects").action(() => {
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());
@@ -8027,12 +8486,12 @@ function createKitCommand() {
8027
8486
  }
8028
8487
 
8029
8488
  // src/commands/rules/index.ts
8030
- import { Command as Command39 } from "commander";
8489
+ import { Command as Command42 } from "commander";
8031
8490
 
8032
8491
  // src/commands/rules/list.ts
8033
- import { Command as Command35 } from "commander";
8492
+ import { Command as Command36 } from "commander";
8034
8493
  function createRulesListCommand() {
8035
- return new Command35("list").description("List available rule presets").option("--json", "Output as JSON").action(async (options) => {
8494
+ return new Command36("list").description("List available rule presets").option("--json", "Output as JSON").action(async (options) => {
8036
8495
  const configService = new ConfigService();
8037
8496
  const config = await configService.load();
8038
8497
  if (!config) {
@@ -8087,12 +8546,12 @@ function createRulesListCommand() {
8087
8546
  }
8088
8547
 
8089
8548
  // src/commands/rules/init.ts
8090
- import { Command as Command36 } from "commander";
8091
- import { promises as fs12 } from "fs";
8092
- import { join as join6 } from "path";
8549
+ import { Command as Command37 } from "commander";
8550
+ import { promises as fs13 } from "fs";
8551
+ import { join as join7 } from "path";
8093
8552
  import { select as select3, confirm as confirm5 } from "@inquirer/prompts";
8094
8553
  function createRulesInitCommand() {
8095
- return new Command36("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) => {
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) => {
8096
8555
  const configService = new ConfigService();
8097
8556
  const config = await configService.load();
8098
8557
  if (!config) {
@@ -8174,7 +8633,7 @@ function createRulesInitCommand() {
8174
8633
  appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
8175
8634
  customContext: "09-custom.mdc"
8176
8635
  };
8177
- await fs12.writeFile("jai1-rules.json", JSON.stringify(projectConfig, null, 2));
8636
+ await fs13.writeFile("jai1-rules.json", JSON.stringify(projectConfig, null, 2));
8178
8637
  console.log("\u2713 Created jai1-rules.json");
8179
8638
  console.log("\n\u2705 Preset applied successfully!\n");
8180
8639
  console.log("Next steps:");
@@ -8184,11 +8643,11 @@ function createRulesInitCommand() {
8184
8643
  });
8185
8644
  }
8186
8645
  async function applyCursorFormat(bundle) {
8187
- const rulesDir = join6(process.cwd(), ".cursor", "rules");
8188
- await fs12.mkdir(rulesDir, { recursive: true });
8646
+ const rulesDir = join7(process.cwd(), ".cursor", "rules");
8647
+ await fs13.mkdir(rulesDir, { recursive: true });
8189
8648
  for (const [filename, content] of Object.entries(bundle.files)) {
8190
- const filePath = join6(rulesDir, filename);
8191
- await fs12.writeFile(filePath, content, "utf-8");
8649
+ const filePath = join7(rulesDir, filename);
8650
+ await fs13.writeFile(filePath, content, "utf-8");
8192
8651
  console.log(`\u2713 Created .cursor/rules/${filename}`);
8193
8652
  }
8194
8653
  }
@@ -8215,112 +8674,1049 @@ async function applyAgentsMdFormat(bundle) {
8215
8674
  }
8216
8675
  }
8217
8676
  const agentsMd = sections.join("\n");
8218
- await fs12.writeFile("AGENTS.md", agentsMd, "utf-8");
8677
+ await fs13.writeFile("AGENTS.md", agentsMd, "utf-8");
8219
8678
  console.log("\u2713 Created AGENTS.md");
8220
8679
  }
8221
8680
 
8222
- // src/commands/rules/sync.ts
8223
- import { Command as Command37 } from "commander";
8224
- import { promises as fs13 } from "fs";
8225
- import { join as join7 } from "path";
8226
- function createRulesSyncCommand() {
8227
- return new Command37("sync").description("Regenerate rule outputs after editing custom context").action(async () => {
8228
- const configPath = join7(process.cwd(), "jai1-rules.json");
8229
- let projectConfig;
8230
- try {
8231
- const configContent = await fs13.readFile(configPath, "utf-8");
8232
- projectConfig = JSON.parse(configContent);
8233
- } catch {
8234
- throw new ValidationError(
8235
- 'No jai1-rules.json found. Run "jai1 rules init" first.'
8236
- );
8237
- }
8238
- console.log("\u{1F504} Syncing rules...\n");
8239
- console.log(`Preset: ${projectConfig.preset} v${projectConfig.version}`);
8240
- const hasCursorRules = await checkPathExists(".cursor/rules");
8241
- const hasAgentsMd = await checkPathExists("AGENTS.md");
8242
- if (!hasCursorRules && !hasAgentsMd) {
8243
- throw new ValidationError(
8244
- 'No rule outputs found. Run "jai1 rules init" first.'
8245
- );
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}`);
8246
8712
  }
8247
- if (hasCursorRules) {
8248
- await syncCursorFormat(projectConfig);
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
+ });
8249
8731
  }
8250
- if (hasAgentsMd) {
8251
- await syncAgentsMdFormat(projectConfig);
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
+ });
8252
8764
  }
8253
- console.log("\n\u2705 Rules synced successfully!\n");
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');
8254
9489
  });
8255
9490
  }
8256
- async function checkPathExists(path8) {
8257
- try {
8258
- await fs13.access(join7(process.cwd(), path8));
8259
- return true;
8260
- } catch {
8261
- return false;
8262
- }
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
+ });
8263
9549
  }
8264
- async function syncCursorFormat(config) {
8265
- const customPath = join7(process.cwd(), ".cursor", "rules", config.customContext);
8266
- try {
8267
- await fs13.access(customPath);
8268
- console.log(`\u2713 Custom context found: ${config.customContext}`);
8269
- } catch {
8270
- console.log(`\u26A0\uFE0F Warning: ${config.customContext} not found in .cursor/rules/`);
8271
- }
8272
- console.log("\u2713 Cursor rules (.cursor/rules/) - no changes needed");
8273
- console.log(" (Files are already in place, edit them directly)");
9550
+ function formatBackupInfo(backup) {
9551
+ const timestamp = formatTimestamp(backup.timestamp);
9552
+ const preset = backup.preset ? ` [${backup.preset}]` : "";
9553
+ return `${timestamp}${preset}`;
8274
9554
  }
8275
- async function syncAgentsMdFormat(config) {
8276
- const rulesDir = join7(process.cwd(), ".cursor", "rules");
9555
+ function formatTimestamp(timestamp) {
8277
9556
  try {
8278
- await fs13.access(rulesDir);
9557
+ const date = new Date(timestamp);
9558
+ return date.toLocaleString();
8279
9559
  } catch {
8280
- console.log("\u26A0\uFE0F Warning: .cursor/rules/ not found, cannot regenerate AGENTS.md");
8281
- return;
9560
+ return timestamp;
8282
9561
  }
8283
- const sections = [];
8284
- sections.push("# AGENTS.md\n");
8285
- sections.push(`<!-- Generated by jai1 rules - Preset: ${config.preset} v${config.version} -->
8286
- `);
8287
- sections.push(`<!-- Last synced: ${(/* @__PURE__ */ new Date()).toISOString()} -->
8288
- `);
8289
- const fileOrder = [
8290
- "01-project.mdc",
8291
- "02-standards.mdc",
8292
- "03-frontend.mdc",
8293
- "04-backend.mdc",
8294
- "05-testing.mdc",
8295
- "06-workflow.mdc",
8296
- "09-custom.mdc"
8297
- ];
8298
- for (const filename of fileOrder) {
8299
- const filePath = join7(rulesDir, filename);
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;
8300
9573
  try {
8301
- const content = await fs13.readFile(filePath, "utf-8");
8302
- const withoutFrontmatter = content.replace(/^---\n[\s\S]*?\n---\n/, "");
8303
- sections.push(withoutFrontmatter.trim());
8304
- sections.push("\n");
9574
+ const configContent = await fs16.readFile(configPath, "utf-8");
9575
+ projectConfig = JSON.parse(configContent);
8305
9576
  } catch {
8306
- continue;
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
+ }
8307
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;
8308
9707
  }
8309
- const agentsMd = sections.join("\n");
8310
- await fs13.writeFile("AGENTS.md", agentsMd, "utf-8");
8311
- console.log("\u2713 Regenerated AGENTS.md");
8312
9708
  }
8313
9709
 
8314
9710
  // src/commands/rules/info.ts
8315
- import { Command as Command38 } from "commander";
8316
- import { promises as fs14 } from "fs";
8317
- import { join as join8 } from "path";
9711
+ import { Command as Command41 } from "commander";
9712
+ import { promises as fs17 } from "fs";
9713
+ import { join as join12 } from "path";
8318
9714
  function createRulesInfoCommand() {
8319
- return new Command38("info").description("Show current preset information").option("--json", "Output as JSON").action(async (options) => {
8320
- const configPath = join8(process.cwd(), "jai1-rules.json");
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");
8321
9717
  let projectConfig;
8322
9718
  try {
8323
- const configContent = await fs14.readFile(configPath, "utf-8");
9719
+ const configContent = await fs17.readFile(configPath, "utf-8");
8324
9720
  projectConfig = JSON.parse(configContent);
8325
9721
  } catch {
8326
9722
  throw new ValidationError(
@@ -8336,43 +9732,72 @@ function createRulesInfoCommand() {
8336
9732
  console.log(`Version: ${projectConfig.version}`);
8337
9733
  console.log(`Applied at: ${new Date(projectConfig.appliedAt).toLocaleString()}`);
8338
9734
  console.log(`Custom rules: ${projectConfig.customContext}`);
8339
- console.log("\nOutput files:");
8340
- const cursorRulesExists = await checkPathExists2(".cursor/rules");
8341
- const agentsMdExists = await checkPathExists2("AGENTS.md");
8342
- if (cursorRulesExists) {
8343
- console.log(" \u2713 .cursor/rules/");
8344
- }
8345
- if (agentsMdExists) {
8346
- console.log(" \u2713 AGENTS.md");
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
+ }
8347
9746
  }
8348
- if (!cursorRulesExists && !agentsMdExists) {
8349
- console.log(" (none found)");
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
+ }
8350
9757
  }
8351
- console.log('\n\u2139\uFE0F Use "jai1 rules sync" to regenerate outputs after editing');
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');
8352
9762
  });
8353
9763
  }
8354
9764
  async function checkPathExists2(path8) {
8355
9765
  try {
8356
- await fs14.access(join8(process.cwd(), path8));
9766
+ await fs17.access(join12(process.cwd(), path8));
8357
9767
  return true;
8358
9768
  } catch {
8359
9769
  return false;
8360
9770
  }
8361
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
+ }
8362
9785
 
8363
9786
  // src/commands/rules/index.ts
8364
9787
  function createRulesCommand() {
8365
- const rulesCommand = new Command39("rules").description("Manage rule presets for AI agents");
9788
+ const rulesCommand = new Command42("rules").description("Manage rule presets for AI agents");
8366
9789
  rulesCommand.addCommand(createRulesListCommand());
8367
9790
  rulesCommand.addCommand(createRulesInitCommand());
9791
+ rulesCommand.addCommand(createRulesApplyCommand());
9792
+ rulesCommand.addCommand(createRulesRestoreCommand());
8368
9793
  rulesCommand.addCommand(createRulesSyncCommand());
8369
9794
  rulesCommand.addCommand(createRulesInfoCommand());
8370
9795
  return rulesCommand;
8371
9796
  }
8372
9797
 
8373
9798
  // src/commands/upgrade.ts
8374
- import { Command as Command40 } from "commander";
8375
- import { confirm as confirm6 } from "@inquirer/prompts";
9799
+ import { Command as Command43 } from "commander";
9800
+ import { confirm as confirm9 } from "@inquirer/prompts";
8376
9801
  import { execSync as execSync2 } from "child_process";
8377
9802
  var colors3 = {
8378
9803
  yellow: "\x1B[33m",
@@ -8383,7 +9808,7 @@ var colors3 = {
8383
9808
  bold: "\x1B[1m"
8384
9809
  };
8385
9810
  function createUpgradeCommand() {
8386
- return new Command40("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) => {
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) => {
8387
9812
  await handleUpgrade(options);
8388
9813
  });
8389
9814
  }
@@ -8427,7 +9852,7 @@ ${colors3.bold}Current version:${colors3.reset} ${currentVersion}`);
8427
9852
  return;
8428
9853
  }
8429
9854
  if (!options.force) {
8430
- const shouldUpdate = await confirm6({
9855
+ const shouldUpdate = await confirm9({
8431
9856
  message: "Update to the latest version now?",
8432
9857
  default: true
8433
9858
  });
@@ -8531,11 +9956,11 @@ function getInstallCommand(packageManager2) {
8531
9956
  }
8532
9957
 
8533
9958
  // src/commands/clean.ts
8534
- import { Command as Command41 } from "commander";
8535
- import { confirm as confirm7, select as select4 } from "@inquirer/prompts";
8536
- import { join as join9 } from "path";
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";
8537
9962
  function createCleanCommand() {
8538
- return new Command41("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) => {
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) => {
8539
9964
  await handleClean(options);
8540
9965
  });
8541
9966
  }
@@ -8546,7 +9971,7 @@ async function handleClean(options) {
8546
9971
  {
8547
9972
  name: "Backups",
8548
9973
  description: "Component backup files (.jai1_backup/)",
8549
- path: join9(cwd, ".jai1_backup"),
9974
+ path: join13(cwd, ".jai1_backup"),
8550
9975
  check: async () => {
8551
9976
  const backups = await service.listBackups(cwd);
8552
9977
  return { exists: backups.length > 0, count: backups.length };
@@ -8596,7 +10021,7 @@ async function handleClean(options) {
8596
10021
  console.log(` Path: ${target.path}
8597
10022
  `);
8598
10023
  }
8599
- const action = await select4({
10024
+ const action = await select6({
8600
10025
  message: "What do you want to clean?",
8601
10026
  choices: [
8602
10027
  ...availableTargets.map(({ target, info }) => ({
@@ -8630,7 +10055,7 @@ async function cleanTarget(target, skipConfirm) {
8630
10055
  }
8631
10056
  const countStr = info.count ? ` (${info.count} items)` : "";
8632
10057
  if (!skipConfirm) {
8633
- const confirmed = await confirm7({
10058
+ const confirmed = await confirm10({
8634
10059
  message: `Delete ${target.name}${countStr}?`,
8635
10060
  default: false
8636
10061
  });
@@ -8648,7 +10073,7 @@ async function cleanTarget(target, skipConfirm) {
8648
10073
  }
8649
10074
 
8650
10075
  // src/commands/redmine/check.ts
8651
- import { Command as Command42 } from "commander";
10076
+ import { Command as Command45 } from "commander";
8652
10077
 
8653
10078
  // src/services/redmine-config.service.ts
8654
10079
  import { readFile as readFile6 } from "fs/promises";
@@ -8955,7 +10380,7 @@ async function checkConnectivity(config) {
8955
10380
 
8956
10381
  // src/commands/redmine/check.ts
8957
10382
  function createRedmineCheckCommand() {
8958
- const cmd = new Command42("check").description("Check Redmine connectivity").option("-c, --config <path>", "Config file path", "redmine.config.yaml").option("--json", "Output as JSON").action(async (options) => {
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) => {
8959
10384
  await handleRedmineCheck(options);
8960
10385
  });
8961
10386
  return cmd;
@@ -8983,7 +10408,7 @@ async function handleRedmineCheck(options) {
8983
10408
  }
8984
10409
 
8985
10410
  // src/commands/redmine/sync-issue.ts
8986
- import { Command as Command43 } from "commander";
10411
+ import { Command as Command46 } from "commander";
8987
10412
 
8988
10413
  // src/sync-issue.ts
8989
10414
  import { resolve as resolve3, relative } from "path";
@@ -9126,10 +10551,10 @@ function generateFilename(issueId, title, config, existingSlugs = /* @__PURE__ *
9126
10551
 
9127
10552
  // src/file.util.ts
9128
10553
  import { readFile as readFile7, writeFile as writeFile2, mkdir } from "fs/promises";
9129
- import { dirname } from "path";
10554
+ import { dirname as dirname2 } from "path";
9130
10555
  import matter3 from "gray-matter";
9131
10556
  async function ensureDir(filePath) {
9132
- const dir = dirname(filePath);
10557
+ const dir = dirname2(filePath);
9133
10558
  await mkdir(dir, { recursive: true });
9134
10559
  }
9135
10560
  async function readMarkdownFile(filePath) {
@@ -9359,7 +10784,7 @@ function extractIssueIdFromUrl(url) {
9359
10784
 
9360
10785
  // src/commands/redmine/sync-issue.ts
9361
10786
  function createSyncIssueCommand() {
9362
- const cmd = new Command43("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) => {
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) => {
9363
10788
  await handleSyncIssue(options);
9364
10789
  });
9365
10790
  return cmd;
@@ -9403,7 +10828,7 @@ async function handleSyncIssue(options) {
9403
10828
  }
9404
10829
 
9405
10830
  // src/commands/redmine/sync-project.ts
9406
- import { Command as Command44 } from "commander";
10831
+ import { Command as Command47 } from "commander";
9407
10832
 
9408
10833
  // src/sync-project.ts
9409
10834
  async function syncProject(config, options = {}) {
@@ -9473,7 +10898,7 @@ async function syncProject(config, options = {}) {
9473
10898
 
9474
10899
  // src/commands/redmine/sync-project.ts
9475
10900
  function createSyncProjectCommand() {
9476
- const cmd = new Command44("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) => {
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) => {
9477
10902
  await handleSyncProject(options);
9478
10903
  });
9479
10904
  return cmd;
@@ -9528,12 +10953,12 @@ async function handleSyncProject(options) {
9528
10953
  }
9529
10954
 
9530
10955
  // src/commands/framework/info.ts
9531
- import { Command as Command45 } from "commander";
9532
- import { promises as fs15 } from "fs";
9533
- import { join as join10 } from "path";
10956
+ import { Command as Command48 } from "commander";
10957
+ import { promises as fs18 } from "fs";
10958
+ import { join as join14 } from "path";
9534
10959
  import { homedir as homedir5 } from "os";
9535
10960
  function createInfoCommand() {
9536
- const cmd = new Command45("info").description("Show jai1-client configuration and status").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
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) => {
9537
10962
  await handleInfo(options);
9538
10963
  });
9539
10964
  return cmd;
@@ -9544,7 +10969,7 @@ async function handleInfo(options) {
9544
10969
  if (!config) {
9545
10970
  throw new ValidationError('Not initialized. Run "jai1 auth" first.');
9546
10971
  }
9547
- const frameworkPath = join10(homedir5(), ".jai1", "framework");
10972
+ const frameworkPath = join14(homedir5(), ".jai1", "framework");
9548
10973
  const projectStatus = await getProjectStatus2();
9549
10974
  const info = {
9550
10975
  configPath: configService.getConfigPath(),
@@ -9579,9 +11004,9 @@ function maskKey3(key) {
9579
11004
  return "****" + key.slice(-4);
9580
11005
  }
9581
11006
  async function getProjectStatus2() {
9582
- const projectJai1 = join10(process.cwd(), ".jai1");
11007
+ const projectJai1 = join14(process.cwd(), ".jai1");
9583
11008
  try {
9584
- await fs15.access(projectJai1);
11009
+ await fs18.access(projectJai1);
9585
11010
  return { exists: true, version: "Synced" };
9586
11011
  } catch {
9587
11012
  return { exists: false };
@@ -9589,8 +11014,8 @@ async function getProjectStatus2() {
9589
11014
  }
9590
11015
 
9591
11016
  // src/commands/self-update.ts
9592
- import { Command as Command46 } from "commander";
9593
- import { confirm as confirm8 } from "@inquirer/prompts";
11017
+ import { Command as Command49 } from "commander";
11018
+ import { confirm as confirm11 } from "@inquirer/prompts";
9594
11019
  import { execSync as execSync3 } from "child_process";
9595
11020
  var colors4 = {
9596
11021
  yellow: "\x1B[33m",
@@ -9601,7 +11026,7 @@ var colors4 = {
9601
11026
  bold: "\x1B[1m"
9602
11027
  };
9603
11028
  function createSelfUpdateCommand() {
9604
- return new Command46("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) => {
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) => {
9605
11030
  await handleSelfUpdate(options);
9606
11031
  });
9607
11032
  }
@@ -9645,7 +11070,7 @@ ${colors4.bold}Current version:${colors4.reset} ${currentVersion}`);
9645
11070
  return;
9646
11071
  }
9647
11072
  if (!options.force) {
9648
- const shouldUpdate = await confirm8({
11073
+ const shouldUpdate = await confirm11({
9649
11074
  message: "Update to the latest version now?",
9650
11075
  default: true
9651
11076
  });
@@ -9741,10 +11166,10 @@ function getInstallCommand2(packageManager2) {
9741
11166
  }
9742
11167
 
9743
11168
  // src/commands/clear-backups.ts
9744
- import { Command as Command47 } from "commander";
9745
- import { confirm as confirm9 } from "@inquirer/prompts";
11169
+ import { Command as Command50 } from "commander";
11170
+ import { confirm as confirm12 } from "@inquirer/prompts";
9746
11171
  function createClearBackupsCommand() {
9747
- return new Command47("clear-backups").description("Clear backup files").option("-y, --yes", "Skip confirmation").action(async (options) => {
11172
+ return new Command50("clear-backups").description("Clear backup files").option("-y, --yes", "Skip confirmation").action(async (options) => {
9748
11173
  const service = new ComponentsService();
9749
11174
  const backups = await service.listBackups(process.cwd());
9750
11175
  if (backups.length === 0) {
@@ -9760,7 +11185,7 @@ function createClearBackupsCommand() {
9760
11185
  }
9761
11186
  console.log();
9762
11187
  if (!options.yes) {
9763
- const ok = await confirm9({ message: "Delete all backups?", default: false });
11188
+ const ok = await confirm12({ message: "Delete all backups?", default: false });
9764
11189
  if (!ok) return;
9765
11190
  }
9766
11191
  await service.clearBackups(process.cwd());
@@ -9769,9 +11194,9 @@ function createClearBackupsCommand() {
9769
11194
  }
9770
11195
 
9771
11196
  // src/commands/vscode/index.ts
9772
- import { Command as Command48 } from "commander";
9773
- import { checkbox as checkbox5, confirm as confirm10, select as select5 } from "@inquirer/prompts";
9774
- import fs16 from "fs/promises";
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";
9775
11200
  import path7 from "path";
9776
11201
  import { existsSync as existsSync3 } from "fs";
9777
11202
  var PERFORMANCE_GROUPS2 = {
@@ -9909,7 +11334,7 @@ var PERFORMANCE_GROUPS2 = {
9909
11334
  }
9910
11335
  };
9911
11336
  function createVSCodeCommand() {
9912
- const vscodeCommand = new Command48("vscode").description("Qu\u1EA3n l\xFD c\xE0i \u0111\u1EB7t VSCode cho d\u1EF1 \xE1n hi\u1EC7n t\u1EA1i");
11337
+ const vscodeCommand = new Command51("vscode").description("Qu\u1EA3n l\xFD c\xE0i \u0111\u1EB7t VSCode cho d\u1EF1 \xE1n hi\u1EC7n t\u1EA1i");
9913
11338
  vscodeCommand.action(async () => {
9914
11339
  await interactiveMode2();
9915
11340
  });
@@ -9953,7 +11378,7 @@ async function interactiveMode2() {
9953
11378
  console.log("\u2502 \u2022 Nh\u1EA5n ENTER \u0111\u1EC3 x\xE1c nh\u1EADn v\xE0 \xE1p d\u1EE5ng \u2502");
9954
11379
  console.log("\u2502 \u2022 Nh\u1EA5n Ctrl+C \u0111\u1EC3 h\u1EE7y \u2502");
9955
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");
9956
- const action = await select5({
11381
+ const action = await select7({
9957
11382
  message: "B\u1EA1n mu\u1ED1n l\xE0m g\xEC?",
9958
11383
  choices: [
9959
11384
  { name: "\u2705 Enable c\xE1c nh\xF3m t\u1ED1i \u01B0u", value: "enable" },
@@ -9977,7 +11402,7 @@ async function selectGroupsToApply2(action) {
9977
11402
  value: key
9978
11403
  }));
9979
11404
  try {
9980
- const selectedGroups = await checkbox5({
11405
+ const selectedGroups = await checkbox7({
9981
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):`,
9982
11407
  choices
9983
11408
  });
@@ -10002,18 +11427,18 @@ async function applyGroups2(groupKeys, action) {
10002
11427
  return;
10003
11428
  }
10004
11429
  if (!existsSync3(vscodeDir)) {
10005
- await fs16.mkdir(vscodeDir, { recursive: true });
11430
+ await fs19.mkdir(vscodeDir, { recursive: true });
10006
11431
  console.log("\u{1F4C1} \u0110\xE3 t\u1EA1o th\u01B0 m\u1EE5c .vscode/");
10007
11432
  }
10008
11433
  let currentSettings = {};
10009
11434
  if (existsSync3(settingsPath)) {
10010
11435
  try {
10011
- const content = await fs16.readFile(settingsPath, "utf-8");
11436
+ const content = await fs19.readFile(settingsPath, "utf-8");
10012
11437
  currentSettings = JSON.parse(content);
10013
11438
  console.log("\u{1F4C4} \u0110\xE3 \u0111\u1ECDc c\xE0i \u0111\u1EB7t hi\u1EC7n t\u1EA1i t\u1EEB settings.json");
10014
11439
  } catch {
10015
11440
  console.warn("\u26A0\uFE0F Kh\xF4ng th\u1EC3 \u0111\u1ECDc settings.json (c\xF3 th\u1EC3 ch\u1EE9a comments).");
10016
- const confirmOverwrite = await confirm10({
11441
+ const confirmOverwrite = await confirm13({
10017
11442
  message: "Ghi \u0111\xE8 file settings.json hi\u1EC7n t\u1EA1i?",
10018
11443
  default: false
10019
11444
  });
@@ -10048,7 +11473,7 @@ async function applyGroups2(groupKeys, action) {
10048
11473
  }
10049
11474
  }
10050
11475
  }
10051
- await fs16.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
11476
+ await fs19.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
10052
11477
  console.log(`
10053
11478
  \u2705 \u0110\xE3 c\u1EADp nh\u1EADt c\xE0i \u0111\u1EB7t VSCode t\u1EA1i: ${settingsPath}`);
10054
11479
  console.log("\u{1F4A1} M\u1EB9o: Kh\u1EDFi \u0111\u1ED9ng l\u1EA1i VSCode \u0111\u1EC3 \xE1p d\u1EE5ng c\xE1c thay \u0111\u1ED5i.");
@@ -10060,7 +11485,7 @@ async function resetSettings2(groupKeys) {
10060
11485
  console.log("\n\u26A0\uFE0F Kh\xF4ng t\xECm th\u1EA5y file settings.json");
10061
11486
  return;
10062
11487
  }
10063
- const confirmReset = await confirm10({
11488
+ const confirmReset = await confirm13({
10064
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(", ")}?`,
10065
11490
  default: false
10066
11491
  });
@@ -10069,7 +11494,7 @@ async function resetSettings2(groupKeys) {
10069
11494
  return;
10070
11495
  }
10071
11496
  if (groupKeys.length === 0) {
10072
- await fs16.unlink(settingsPath);
11497
+ await fs19.unlink(settingsPath);
10073
11498
  console.log("\n\u2705 \u0110\xE3 x\xF3a file settings.json");
10074
11499
  } else {
10075
11500
  await applyGroups2(groupKeys, "disable");
@@ -10080,9 +11505,9 @@ async function resetSettings2(groupKeys) {
10080
11505
  // src/commands/guide.ts
10081
11506
  import React40 from "react";
10082
11507
  import { render as render6 } from "ink";
10083
- import { Command as Command49 } from "commander";
11508
+ import { Command as Command52 } from "commander";
10084
11509
  function createGuideCommand() {
10085
- const cmd = new Command49("guide").description("Interactive guide center for Agentic Coding").option("--topic <topic>", "Open specific topic (intro, rules, workflows, prompts, skills)").action(async (options) => {
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) => {
10086
11511
  const { waitUntilExit } = render6(
10087
11512
  React40.createElement(GuideApp, {
10088
11513
  initialTopic: options.topic,
@@ -10099,9 +11524,9 @@ function createGuideCommand() {
10099
11524
  // src/commands/context.ts
10100
11525
  import React41 from "react";
10101
11526
  import { render as render7 } from "ink";
10102
- import { Command as Command50 } from "commander";
11527
+ import { Command as Command53 } from "commander";
10103
11528
  function createContextCommand() {
10104
- const cmd = new Command50("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) => {
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) => {
10105
11530
  let initialIDE;
10106
11531
  if (options.ide) {
10107
11532
  const validIDEs = ["cursor", "windsurf", "antigravity", "jai1"];
@@ -10178,10 +11603,10 @@ async function printStats2() {
10178
11603
  }
10179
11604
 
10180
11605
  // src/commands/migrate-ide.ts
10181
- import { Command as Command51 } from "commander";
10182
- import { checkbox as checkbox6, confirm as confirm11 } from "@inquirer/prompts";
11606
+ import { Command as Command54 } from "commander";
11607
+ import { checkbox as checkbox8, confirm as confirm14 } from "@inquirer/prompts";
10183
11608
  function createMigrateIdeCommand() {
10184
- const cmd = new Command51("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) => {
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) => {
10185
11610
  await runMigrateIde(options);
10186
11611
  });
10187
11612
  return cmd;
@@ -10209,7 +11634,7 @@ async function runMigrateIde(options) {
10209
11634
  value: ide
10210
11635
  };
10211
11636
  });
10212
- selectedIdes = await checkbox6({
11637
+ selectedIdes = await checkbox8({
10213
11638
  message: "Ch\u1ECDn IDE(s) \u0111\u1EC3 migrate (SPACE \u0111\u1EC3 ch\u1ECDn, ENTER \u0111\u1EC3 x\xE1c nh\u1EADn):",
10214
11639
  choices: ideChoices
10215
11640
  });
@@ -10228,7 +11653,7 @@ async function runMigrateIde(options) {
10228
11653
  { name: `Workflows (${content.workflows.length} files)`, value: "workflows" },
10229
11654
  { name: `Commands (${content.commands.length} files)`, value: "commands" }
10230
11655
  ];
10231
- selectedTypes = await checkbox6({
11656
+ selectedTypes = await checkbox8({
10232
11657
  message: "Ch\u1ECDn content types \u0111\u1EC3 migrate:",
10233
11658
  choices: typeChoices
10234
11659
  });
@@ -10250,7 +11675,7 @@ async function runMigrateIde(options) {
10250
11675
  if (options.dryRun) {
10251
11676
  console.log("\u{1F50D} DRY RUN - No files will be written\n");
10252
11677
  }
10253
- const confirmed = await confirm11({
11678
+ const confirmed = await confirm14({
10254
11679
  message: "Proceed with migration?",
10255
11680
  default: true
10256
11681
  });
@@ -10287,7 +11712,7 @@ async function runMigrateIde(options) {
10287
11712
  }
10288
11713
 
10289
11714
  // src/cli.ts
10290
- var program = new Command52();
11715
+ var program = new Command55();
10291
11716
  if (process.argv.includes("-v") || process.argv.includes("--version")) {
10292
11717
  console.log(package_default.version);
10293
11718
  if (!process.argv.includes("--skip-update-check")) {
@@ -10314,9 +11739,9 @@ program.addCommand(createKitCommand());
10314
11739
  program.addCommand(createRulesCommand());
10315
11740
  program.addCommand(createUpgradeCommand());
10316
11741
  program.addCommand(createCleanCommand());
10317
- var redmineCommand = new Command52("redmine").description("Redmine context sync commands");
11742
+ var redmineCommand = new Command55("redmine").description("Redmine context sync commands");
10318
11743
  redmineCommand.addCommand(createRedmineCheckCommand());
10319
- var syncCommand = new Command52("sync").description("Sync Redmine issues to markdown files");
11744
+ var syncCommand = new Command55("sync").description("Sync Redmine issues to markdown files");
10320
11745
  syncCommand.addCommand(createSyncIssueCommand());
10321
11746
  syncCommand.addCommand(createSyncProjectCommand());
10322
11747
  redmineCommand.addCommand(syncCommand);