@jvittechs/jai1-cli 0.1.60 → 0.1.62

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { Command as Command26 } from "commander";
4
+ import { Command as Command40 } 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.60",
36
+ version: "0.1.62",
37
37
  description: "Unified CLI for Jai1 Framework Management and Redmine Context Sync",
38
38
  type: "module",
39
39
  bin: {
@@ -80,13 +80,17 @@ var package_default = {
80
80
  dependencies: {
81
81
  "@inquirer/prompts": "^8.0.2",
82
82
  "adm-zip": "^0.5.16",
83
+ bcryptjs: "^2.4.3",
83
84
  "cli-progress": "^3.12.0",
84
85
  clipboardy: "^4.0.0",
85
86
  commander: "^12.1.0",
87
+ cronstrue: "^2.50.0",
86
88
  "gray-matter": "^4.0.3",
87
89
  ink: "^5.0.1",
88
90
  "ink-spinner": "^5.0.0",
89
91
  "ink-text-input": "^6.0.0",
92
+ marked: "^12.0.0",
93
+ "marked-terminal": "^7.0.0",
90
94
  "p-limit": "^5.0.0",
91
95
  "p-queue": "^7.4.1",
92
96
  "p-retry": "^6.2.0",
@@ -4790,8 +4794,941 @@ function createTranslateCommand() {
4790
4794
  return cmd;
4791
4795
  }
4792
4796
 
4793
- // src/commands/upgrade.ts
4797
+ // src/commands/utils/index.ts
4798
+ import { Command as Command27 } from "commander";
4799
+
4800
+ // src/commands/utils/password.ts
4794
4801
  import { Command as Command14 } from "commander";
4802
+
4803
+ // src/services/utils.service.ts
4804
+ import crypto from "crypto";
4805
+ import bcrypt from "bcryptjs";
4806
+ import { readFile } from "fs/promises";
4807
+ var UtilsService = class {
4808
+ /**
4809
+ * Generate secure random password
4810
+ */
4811
+ generatePassword(options) {
4812
+ const {
4813
+ length,
4814
+ lowercase,
4815
+ uppercase,
4816
+ digits,
4817
+ symbols,
4818
+ symbolChars = "!@#$%^&*()_+-=[]{}|;:,.<>?"
4819
+ } = options;
4820
+ let charset = "";
4821
+ if (lowercase) charset += "abcdefghijklmnopqrstuvwxyz";
4822
+ if (uppercase) charset += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
4823
+ if (digits) charset += "0123456789";
4824
+ if (symbols) charset += symbolChars;
4825
+ if (charset.length === 0) {
4826
+ throw new Error("At least one character type must be enabled");
4827
+ }
4828
+ const randomBytes = crypto.randomBytes(length);
4829
+ let password = "";
4830
+ for (let i = 0; i < length; i++) {
4831
+ const randomIndex = randomBytes[i] % charset.length;
4832
+ password += charset[randomIndex];
4833
+ }
4834
+ return password;
4835
+ }
4836
+ /**
4837
+ * Generate UUID v4
4838
+ */
4839
+ generateUuid(options) {
4840
+ let uuid = crypto.randomUUID();
4841
+ if (options?.uppercase) {
4842
+ uuid = uuid.toUpperCase();
4843
+ }
4844
+ if (options?.noHyphens) {
4845
+ uuid = uuid.replace(/-/g, "");
4846
+ }
4847
+ return uuid;
4848
+ }
4849
+ /**
4850
+ * Hash text or file using specified algorithm
4851
+ */
4852
+ async hash(input, algorithm) {
4853
+ if (algorithm === "bcrypt") {
4854
+ throw new Error("Use hashBcrypt for bcrypt algorithm");
4855
+ }
4856
+ const hash = crypto.createHash(algorithm);
4857
+ hash.update(input);
4858
+ return hash.digest("hex");
4859
+ }
4860
+ /**
4861
+ * Hash file contents
4862
+ */
4863
+ async hashFile(filePath, algorithm) {
4864
+ const content = await readFile(filePath);
4865
+ return this.hash(content, algorithm);
4866
+ }
4867
+ /**
4868
+ * Hash using bcrypt
4869
+ */
4870
+ async hashBcrypt(input, rounds = 10) {
4871
+ return bcrypt.hash(input, rounds);
4872
+ }
4873
+ /**
4874
+ * Base64 encode
4875
+ */
4876
+ base64Encode(input, urlSafe = false) {
4877
+ const buffer = typeof input === "string" ? Buffer.from(input, "utf-8") : input;
4878
+ let encoded = buffer.toString("base64");
4879
+ if (urlSafe) {
4880
+ encoded = encoded.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
4881
+ }
4882
+ return encoded;
4883
+ }
4884
+ /**
4885
+ * Base64 decode
4886
+ */
4887
+ base64Decode(input) {
4888
+ let normalized = input.replace(/-/g, "+").replace(/_/g, "/");
4889
+ while (normalized.length % 4) {
4890
+ normalized += "=";
4891
+ }
4892
+ return Buffer.from(normalized, "base64");
4893
+ }
4894
+ /**
4895
+ * Make HTTP request with detailed output
4896
+ */
4897
+ async httpRequest(url, options) {
4898
+ const startTime = performance.now();
4899
+ const fetchOptions = {
4900
+ method: options.method,
4901
+ headers: options.headers,
4902
+ body: options.body,
4903
+ signal: options.timeout ? AbortSignal.timeout(options.timeout) : void 0,
4904
+ redirect: options.followRedirects === false ? "manual" : "follow"
4905
+ };
4906
+ const response = await fetch(url, fetchOptions);
4907
+ const body = await response.text();
4908
+ const endTime = performance.now();
4909
+ const headers = {};
4910
+ response.headers.forEach((value, key) => {
4911
+ headers[key] = value;
4912
+ });
4913
+ return {
4914
+ status: response.status,
4915
+ statusText: response.statusText,
4916
+ headers,
4917
+ body,
4918
+ timing: Math.round(endTime - startTime),
4919
+ size: Buffer.byteLength(body, "utf-8")
4920
+ };
4921
+ }
4922
+ /**
4923
+ * Decode JWT (no verification)
4924
+ */
4925
+ jwtDecode(token) {
4926
+ const parts = token.split(".");
4927
+ if (parts.length !== 3) {
4928
+ throw new Error("Invalid JWT format");
4929
+ }
4930
+ const [headerB64, payloadB64, signature] = parts;
4931
+ const header = JSON.parse(this.base64Decode(headerB64).toString("utf-8"));
4932
+ const payload = JSON.parse(this.base64Decode(payloadB64).toString("utf-8"));
4933
+ return { header, payload, signature };
4934
+ }
4935
+ /**
4936
+ * Encode JWT (HS256 only)
4937
+ */
4938
+ jwtEncode(payload, secret, header) {
4939
+ const defaultHeader = { alg: "HS256", typ: "JWT" };
4940
+ const finalHeader = { ...defaultHeader, ...header };
4941
+ const headerB64 = this.base64Encode(JSON.stringify(finalHeader), true);
4942
+ const payloadB64 = this.base64Encode(JSON.stringify(payload), true);
4943
+ const signatureInput = `${headerB64}.${payloadB64}`;
4944
+ const signature = crypto.createHmac("sha256", secret).update(signatureInput).digest("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
4945
+ return `${headerB64}.${payloadB64}.${signature}`;
4946
+ }
4947
+ /**
4948
+ * Convert unix timestamp to date
4949
+ */
4950
+ unixToDate(timestamp, milliseconds = false) {
4951
+ return new Date(milliseconds ? timestamp : timestamp * 1e3);
4952
+ }
4953
+ /**
4954
+ * Convert date to unix timestamp
4955
+ */
4956
+ dateToUnix(date, milliseconds = false) {
4957
+ const timestamp = date.getTime();
4958
+ return milliseconds ? timestamp : Math.floor(timestamp / 1e3);
4959
+ }
4960
+ /**
4961
+ * Convert time between timezones
4962
+ */
4963
+ convertTimezone(time, fromTimezone, toTimezone) {
4964
+ const date = typeof time === "string" ? new Date(time) : time;
4965
+ const formatter = new Intl.DateTimeFormat("en-US", {
4966
+ timeZone: toTimezone,
4967
+ year: "numeric",
4968
+ month: "2-digit",
4969
+ day: "2-digit",
4970
+ hour: "2-digit",
4971
+ minute: "2-digit",
4972
+ second: "2-digit",
4973
+ hour12: false
4974
+ });
4975
+ const parts = formatter.formatToParts(date);
4976
+ const dateParts = parts.reduce((acc, part) => {
4977
+ if (part.type !== "literal") {
4978
+ acc[part.type] = part.value;
4979
+ }
4980
+ return acc;
4981
+ }, {});
4982
+ const time24 = `${dateParts.year}-${dateParts.month}-${dateParts.day} ${dateParts.hour}:${dateParts.minute}:${dateParts.second}`;
4983
+ return {
4984
+ time: time24,
4985
+ iso: date.toISOString()
4986
+ };
4987
+ }
4988
+ /**
4989
+ * Get list of available timezones
4990
+ */
4991
+ getTimezones() {
4992
+ return [
4993
+ "UTC",
4994
+ "America/New_York",
4995
+ "America/Chicago",
4996
+ "America/Denver",
4997
+ "America/Los_Angeles",
4998
+ "America/Sao_Paulo",
4999
+ "Europe/London",
5000
+ "Europe/Paris",
5001
+ "Europe/Berlin",
5002
+ "Europe/Moscow",
5003
+ "Asia/Dubai",
5004
+ "Asia/Kolkata",
5005
+ "Asia/Bangkok",
5006
+ "Asia/Shanghai",
5007
+ "Asia/Tokyo",
5008
+ "Asia/Seoul",
5009
+ "Asia/Ho_Chi_Minh",
5010
+ "Australia/Sydney",
5011
+ "Pacific/Auckland"
5012
+ ];
5013
+ }
5014
+ /**
5015
+ * URL encode
5016
+ */
5017
+ urlEncode(input, full = false) {
5018
+ return full ? encodeURI(input) : encodeURIComponent(input);
5019
+ }
5020
+ /**
5021
+ * URL decode
5022
+ */
5023
+ urlDecode(input, full = false) {
5024
+ return full ? decodeURI(input) : decodeURIComponent(input);
5025
+ }
5026
+ /**
5027
+ * Format bytes to human-readable size
5028
+ */
5029
+ formatBytes(bytes) {
5030
+ if (bytes === 0) return "0 B";
5031
+ const k = 1024;
5032
+ const sizes = ["B", "KB", "MB", "GB"];
5033
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
5034
+ return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;
5035
+ }
5036
+ };
5037
+
5038
+ // src/commands/utils/password.ts
5039
+ async function handlePasswordGeneration(options) {
5040
+ const service = new UtilsService();
5041
+ try {
5042
+ const passwords = [];
5043
+ for (let i = 0; i < options.count; i++) {
5044
+ const password = service.generatePassword({
5045
+ length: options.length,
5046
+ lowercase: options.lowercase,
5047
+ uppercase: options.uppercase,
5048
+ digits: options.digits,
5049
+ symbols: options.symbols,
5050
+ symbolChars: options.symbolChars
5051
+ });
5052
+ passwords.push(password);
5053
+ }
5054
+ console.log("\u{1F510} Generated Password(s):");
5055
+ 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\n");
5056
+ passwords.forEach((password, index) => {
5057
+ if (options.count > 1) {
5058
+ console.log(` ${index + 1}. ${password}`);
5059
+ } else {
5060
+ console.log(` ${password}`);
5061
+ }
5062
+ });
5063
+ console.log();
5064
+ } catch (error) {
5065
+ console.error("\u274C Error:", error instanceof Error ? error.message : String(error));
5066
+ process.exit(1);
5067
+ }
5068
+ }
5069
+ function createPasswordCommand() {
5070
+ const cmd = new Command14("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", `
5071
+ Examples:
5072
+ $ jai1 utils password
5073
+ $ jai1 utils password --length 24
5074
+ $ jai1 utils password --no-symbols
5075
+ $ jai1 utils password --count 5
5076
+ $ jai1 utils password -l 32 --no-digits
5077
+ `).action(async (options) => {
5078
+ await handlePasswordGeneration({
5079
+ ...options,
5080
+ length: parseInt(options.length, 10),
5081
+ count: parseInt(options.count, 10)
5082
+ });
5083
+ });
5084
+ return cmd;
5085
+ }
5086
+
5087
+ // src/commands/utils/uuid.ts
5088
+ import { Command as Command15 } from "commander";
5089
+ async function handleUuidGeneration(options) {
5090
+ const service = new UtilsService();
5091
+ try {
5092
+ const uuids = [];
5093
+ for (let i = 0; i < options.count; i++) {
5094
+ const uuid = service.generateUuid({
5095
+ uppercase: options.uppercase,
5096
+ noHyphens: options.noHyphens
5097
+ });
5098
+ uuids.push(uuid);
5099
+ }
5100
+ console.log("\u{1F194} Generated UUID(s):");
5101
+ 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\n");
5102
+ uuids.forEach((uuid, index) => {
5103
+ if (options.count > 1) {
5104
+ console.log(` ${index + 1}. ${uuid}`);
5105
+ } else {
5106
+ console.log(` ${uuid}`);
5107
+ }
5108
+ });
5109
+ console.log();
5110
+ } catch (error) {
5111
+ console.error("\u274C Error:", error instanceof Error ? error.message : String(error));
5112
+ process.exit(1);
5113
+ }
5114
+ }
5115
+ function createUuidCommand() {
5116
+ const cmd = new Command15("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", `
5117
+ Examples:
5118
+ $ jai1 utils uuid
5119
+ $ jai1 utils uuid --count 10
5120
+ $ jai1 utils uuid --uppercase
5121
+ $ jai1 utils uuid --no-hyphens
5122
+ $ jai1 utils uuid --uppercase --no-hyphens
5123
+ `).action(async (options) => {
5124
+ await handleUuidGeneration({
5125
+ ...options,
5126
+ count: parseInt(options.count, 10)
5127
+ });
5128
+ });
5129
+ return cmd;
5130
+ }
5131
+
5132
+ // src/commands/utils/hash.ts
5133
+ import { Command as Command16 } from "commander";
5134
+ async function handleHashGeneration(input, options) {
5135
+ const service = new UtilsService();
5136
+ try {
5137
+ let hash;
5138
+ if (options.file) {
5139
+ if (options.algorithm === "bcrypt") {
5140
+ console.error("\u274C bcrypt cannot be used with files (it requires string input)");
5141
+ process.exit(1);
5142
+ }
5143
+ hash = await service.hashFile(options.file, options.algorithm);
5144
+ } else {
5145
+ if (!input) {
5146
+ console.error("\u274C Please provide input text or use --file option");
5147
+ process.exit(1);
5148
+ }
5149
+ if (options.algorithm === "bcrypt") {
5150
+ hash = await service.hashBcrypt(input, options.rounds);
5151
+ } else {
5152
+ hash = await service.hash(input, options.algorithm);
5153
+ }
5154
+ }
5155
+ console.log("\u{1F512} Hash Result:");
5156
+ 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");
5157
+ console.log(` Algorithm: ${options.algorithm.toUpperCase()}`);
5158
+ if (options.file) {
5159
+ console.log(` File: ${options.file}`);
5160
+ }
5161
+ if (options.algorithm === "bcrypt") {
5162
+ console.log(` Rounds: ${options.rounds}`);
5163
+ }
5164
+ console.log();
5165
+ console.log(` ${hash}`);
5166
+ console.log();
5167
+ } catch (error) {
5168
+ console.error("\u274C Error:", error instanceof Error ? error.message : String(error));
5169
+ process.exit(1);
5170
+ }
5171
+ }
5172
+ function createHashCommand() {
5173
+ const cmd = new Command16("hash").description("Generate hash (MD5, SHA, bcrypt)").argument("[input]", "Text to hash").option(
5174
+ "-a, --algorithm <algorithm>",
5175
+ "Hash algorithm (md5, sha1, sha256, sha512, bcrypt)",
5176
+ "sha256"
5177
+ ).option("-f, --file <path>", "Hash file contents instead of text").option("-r, --rounds <number>", "Bcrypt rounds (only for bcrypt)", "10").addHelpText("after", `
5178
+ Examples:
5179
+ $ jai1 utils hash "hello world"
5180
+ $ jai1 utils hash "hello" --algorithm md5
5181
+ $ jai1 utils hash "password" --algorithm bcrypt
5182
+ $ jai1 utils hash "password" --algorithm bcrypt --rounds 12
5183
+ $ jai1 utils hash --file ./myfile.txt
5184
+ $ jai1 utils hash --file ./image.png --algorithm sha512
5185
+ `).action(async (input, options) => {
5186
+ await handleHashGeneration(input, {
5187
+ ...options,
5188
+ rounds: parseInt(options.rounds, 10)
5189
+ });
5190
+ });
5191
+ return cmd;
5192
+ }
5193
+
5194
+ // src/commands/utils/base64-encode.ts
5195
+ import { Command as Command17 } from "commander";
5196
+ import { readFile as readFile2 } from "fs/promises";
5197
+ async function handleBase64Encode(input, options) {
5198
+ const service = new UtilsService();
5199
+ try {
5200
+ let encoded;
5201
+ if (options.file) {
5202
+ const content = await readFile2(options.file);
5203
+ encoded = service.base64Encode(content, options.urlSafe);
5204
+ } else {
5205
+ if (!input) {
5206
+ console.error("\u274C Please provide input text or use --file option");
5207
+ process.exit(1);
5208
+ }
5209
+ encoded = service.base64Encode(input, options.urlSafe);
5210
+ }
5211
+ console.log("\u{1F4DD} Base64 Encoded:");
5212
+ 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");
5213
+ if (options.file) {
5214
+ console.log(` File: ${options.file}`);
5215
+ }
5216
+ if (options.urlSafe) {
5217
+ console.log(` Format: URL-safe`);
5218
+ }
5219
+ console.log();
5220
+ console.log(` ${encoded}`);
5221
+ console.log();
5222
+ } catch (error) {
5223
+ console.error("\u274C Error:", error instanceof Error ? error.message : String(error));
5224
+ process.exit(1);
5225
+ }
5226
+ }
5227
+ function createBase64EncodeCommand() {
5228
+ const cmd = new Command17("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", `
5229
+ Examples:
5230
+ $ jai1 utils base64-encode "hello world"
5231
+ $ jai1 utils base64-encode "hello world" --url-safe
5232
+ $ jai1 utils base64-encode --file ./document.txt
5233
+ $ jai1 utils base64-encode --file ./image.png
5234
+ `).action(async (input, options) => {
5235
+ await handleBase64Encode(input, options);
5236
+ });
5237
+ return cmd;
5238
+ }
5239
+
5240
+ // src/commands/utils/base64-decode.ts
5241
+ import { Command as Command18 } from "commander";
5242
+ import { readFile as readFile3, writeFile } from "fs/promises";
5243
+ async function handleBase64Decode(input, options) {
5244
+ const service = new UtilsService();
5245
+ try {
5246
+ let encodedInput;
5247
+ if (options.file) {
5248
+ encodedInput = await readFile3(options.file, "utf-8");
5249
+ } else {
5250
+ if (!input) {
5251
+ console.error("\u274C Please provide Base64 input or use --file option");
5252
+ process.exit(1);
5253
+ }
5254
+ encodedInput = input;
5255
+ }
5256
+ const decoded = service.base64Decode(encodedInput.trim());
5257
+ if (options.output) {
5258
+ await writeFile(options.output, decoded);
5259
+ console.log("\u2705 Decoded and saved to:", options.output);
5260
+ } else {
5261
+ console.log("\u{1F4DD} Base64 Decoded:");
5262
+ 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");
5263
+ console.log();
5264
+ console.log(` ${decoded.toString("utf-8")}`);
5265
+ console.log();
5266
+ }
5267
+ } catch (error) {
5268
+ console.error("\u274C Error:", error instanceof Error ? error.message : String(error));
5269
+ process.exit(1);
5270
+ }
5271
+ }
5272
+ function createBase64DecodeCommand() {
5273
+ const cmd = new Command18("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", `
5274
+ Examples:
5275
+ $ jai1 utils base64-decode "aGVsbG8gd29ybGQ="
5276
+ $ jai1 utils base64-decode --file ./encoded.txt
5277
+ $ jai1 utils base64-decode "aGVsbG8=" --output ./decoded.txt
5278
+ $ jai1 utils base64-decode --file ./encoded.txt --output ./image.png
5279
+ `).action(async (input, options) => {
5280
+ await handleBase64Decode(input, options);
5281
+ });
5282
+ return cmd;
5283
+ }
5284
+
5285
+ // src/commands/utils/http.ts
5286
+ import { Command as Command19 } from "commander";
5287
+ import { readFile as readFile4 } from "fs/promises";
5288
+ async function handleHttpRequest(url, options) {
5289
+ const service = new UtilsService();
5290
+ try {
5291
+ const headers = {};
5292
+ if (options.header) {
5293
+ options.header.forEach((header) => {
5294
+ const [key, ...valueParts] = header.split(":");
5295
+ if (key && valueParts.length > 0) {
5296
+ headers[key.trim()] = valueParts.join(":").trim();
5297
+ }
5298
+ });
5299
+ }
5300
+ let body;
5301
+ if (options.file) {
5302
+ body = await readFile4(options.file, "utf-8");
5303
+ } else if (options.data) {
5304
+ body = options.data;
5305
+ }
5306
+ const response = await service.httpRequest(url, {
5307
+ method: options.method.toUpperCase(),
5308
+ headers,
5309
+ body,
5310
+ timeout: options.timeout,
5311
+ followRedirects: !options.noFollow
5312
+ });
5313
+ if (!options.onlyBody) {
5314
+ console.log(`HTTP/${response.status >= 200 && response.status < 300 ? "1.1" : "1.0"} ${response.status} ${response.statusText}`);
5315
+ 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\u2500\u2500\u2500");
5316
+ console.log(`\u{1F4CA} Status: ${response.status} ${response.statusText}`);
5317
+ console.log(`\u23F1\uFE0F Time: ${response.timing}ms`);
5318
+ console.log(`\u{1F4E6} Size: ${service.formatBytes(response.size)}`);
5319
+ console.log();
5320
+ }
5321
+ if (!options.onlyBody && !options.onlyHeaders) {
5322
+ console.log("\u{1F4CB} Headers:");
5323
+ Object.entries(response.headers).forEach(([key, value]) => {
5324
+ console.log(` ${key}: ${value}`);
5325
+ });
5326
+ console.log();
5327
+ }
5328
+ if (options.onlyHeaders) {
5329
+ Object.entries(response.headers).forEach(([key, value]) => {
5330
+ console.log(`${key}: ${value}`);
5331
+ });
5332
+ return;
5333
+ }
5334
+ if (!options.onlyHeaders) {
5335
+ if (!options.onlyBody) {
5336
+ console.log("\u{1F4C4} Body:");
5337
+ }
5338
+ try {
5339
+ const json = JSON.parse(response.body);
5340
+ console.log(JSON.stringify(json, null, 2));
5341
+ } catch {
5342
+ console.log(response.body);
5343
+ }
5344
+ console.log();
5345
+ }
5346
+ } catch (error) {
5347
+ console.error("\u274C Error:", error instanceof Error ? error.message : String(error));
5348
+ process.exit(1);
5349
+ }
5350
+ }
5351
+ function createHttpCommand() {
5352
+ const cmd = new Command19("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", `
5353
+ Examples:
5354
+ $ jai1 utils http https://api.example.com/users
5355
+ $ jai1 utils http https://api.example.com/users -X POST -d '{"name":"John"}'
5356
+ $ jai1 utils http https://api.example.com -H "Authorization: Bearer token123"
5357
+ $ jai1 utils http https://api.example.com -H "Content-Type: application/json" -H "Accept: application/json"
5358
+ $ jai1 utils http https://api.example.com --timeout 5000
5359
+ $ jai1 utils http https://api.example.com --only-body
5360
+ `).showHelpAfterError("(add --help for additional examples)").action(async (url, options) => {
5361
+ await handleHttpRequest(url, {
5362
+ ...options,
5363
+ timeout: parseInt(options.timeout, 10)
5364
+ });
5365
+ });
5366
+ return cmd;
5367
+ }
5368
+
5369
+ // src/commands/utils/jwt.ts
5370
+ import { Command as Command20 } from "commander";
5371
+ async function handleJwtDecode(token) {
5372
+ const service = new UtilsService();
5373
+ try {
5374
+ const { header, payload, signature } = service.jwtDecode(token);
5375
+ console.log("\u{1F513} JWT Decoded:");
5376
+ 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\n");
5377
+ console.log("\u{1F4CB} Header:");
5378
+ console.log(JSON.stringify(header, null, 2));
5379
+ console.log();
5380
+ console.log("\u{1F4C4} Payload:");
5381
+ console.log(JSON.stringify(payload, null, 2));
5382
+ console.log();
5383
+ console.log("\u{1F50F} Signature:");
5384
+ console.log(` ${signature}`);
5385
+ console.log();
5386
+ console.log("\u26A0\uFE0F Note: Token signature is not verified");
5387
+ console.log();
5388
+ } catch (error) {
5389
+ console.error("\u274C Error:", error instanceof Error ? error.message : String(error));
5390
+ process.exit(1);
5391
+ }
5392
+ }
5393
+ async function handleJwtEncode(options) {
5394
+ const service = new UtilsService();
5395
+ try {
5396
+ const payload = JSON.parse(options.payload);
5397
+ const header = options.header ? JSON.parse(options.header) : void 0;
5398
+ const token = service.jwtEncode(payload, options.secret, header);
5399
+ console.log("\u{1F510} JWT Encoded:");
5400
+ 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\n");
5401
+ console.log(` ${token}`);
5402
+ console.log();
5403
+ } catch (error) {
5404
+ console.error("\u274C Error:", error instanceof Error ? error.message : String(error));
5405
+ process.exit(1);
5406
+ }
5407
+ }
5408
+ function createJwtCommand() {
5409
+ const jwtCommand = new Command20("jwt").description("Decode and encode JWT tokens");
5410
+ jwtCommand.command("decode").description("Decode JWT token").argument("<token>", "JWT token to decode").addHelpText("after", `
5411
+ Examples:
5412
+ $ jai1 utils jwt decode "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
5413
+ $ jai1 utils jwt decode $JWT_TOKEN
5414
+ `).action(async (token) => {
5415
+ await handleJwtDecode(token);
5416
+ });
5417
+ const encodeCommand = jwtCommand.command("encode").description("Encode JWT token (HS256)").requiredOption("-p, --payload <json>", "Payload as JSON string").requiredOption("-s, --secret <secret>", "Secret key for signing").option("-h, --header <json>", "Custom header as JSON string").addHelpText("after", `
5418
+ Examples:
5419
+ $ jai1 utils jwt encode --payload '{"sub":"123","name":"John"}' --secret "mysecret"
5420
+ $ jai1 utils jwt encode -p '{"userId":"456"}' -s "key123"
5421
+ $ jai1 utils jwt encode -p '{"sub":"789"}' -s "secret" -h '{"alg":"HS256","typ":"JWT"}'
5422
+ `).action(async (options) => {
5423
+ await handleJwtEncode(options);
5424
+ });
5425
+ encodeCommand.showHelpAfterError("(add --help for additional examples)");
5426
+ return jwtCommand;
5427
+ }
5428
+
5429
+ // src/commands/utils/unix-time.ts
5430
+ import { Command as Command21 } from "commander";
5431
+ async function handleUnixTime(input, options) {
5432
+ const service = new UtilsService();
5433
+ try {
5434
+ if (!input) {
5435
+ const now = /* @__PURE__ */ new Date();
5436
+ const timestamp = service.dateToUnix(now, options.ms);
5437
+ console.log("\u{1F552} Current Unix Timestamp:");
5438
+ 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");
5439
+ console.log(` ${timestamp}${options.ms ? " (milliseconds)" : ""}`);
5440
+ console.log();
5441
+ console.log(` ISO: ${now.toISOString()}`);
5442
+ console.log(` Local: ${now.toLocaleString()}`);
5443
+ console.log();
5444
+ } else if (/^\d+$/.test(input)) {
5445
+ const timestamp = parseInt(input, 10);
5446
+ const date = service.unixToDate(timestamp, options.ms);
5447
+ console.log("\u{1F552} Unix Timestamp to Date:");
5448
+ 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");
5449
+ console.log(` Input: ${timestamp}${options.ms ? " (ms)" : ""}`);
5450
+ console.log();
5451
+ if (options.format === "iso" || options.format === "utc") {
5452
+ console.log(` ${date.toISOString()}`);
5453
+ } else {
5454
+ console.log(` ${date.toLocaleString()}`);
5455
+ }
5456
+ console.log();
5457
+ } else {
5458
+ const date = new Date(input);
5459
+ if (isNaN(date.getTime())) {
5460
+ console.error("\u274C Invalid date format");
5461
+ process.exit(1);
5462
+ }
5463
+ const timestamp = service.dateToUnix(date, options.ms);
5464
+ console.log("\u{1F552} Date to Unix Timestamp:");
5465
+ 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");
5466
+ console.log(` Input: ${input}`);
5467
+ console.log(` Parsed: ${date.toISOString()}`);
5468
+ console.log();
5469
+ console.log(` ${timestamp}${options.ms ? " (ms)" : ""}`);
5470
+ console.log();
5471
+ }
5472
+ } catch (error) {
5473
+ console.error("\u274C Error:", error instanceof Error ? error.message : String(error));
5474
+ process.exit(1);
5475
+ }
5476
+ }
5477
+ function createUnixTimeCommand() {
5478
+ const cmd = new Command21("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", `
5479
+ Examples:
5480
+ $ jai1 utils unix-time
5481
+ $ jai1 utils unix-time 1702550400
5482
+ $ jai1 utils unix-time 1702550400000 --ms
5483
+ $ jai1 utils unix-time "2024-01-15 10:30:00"
5484
+ $ jai1 utils unix-time "2024-01-15" --format local
5485
+ $ jai1 utils unix-time --ms
5486
+ `).action(async (input, options) => {
5487
+ await handleUnixTime(input, options);
5488
+ });
5489
+ return cmd;
5490
+ }
5491
+
5492
+ // src/commands/utils/timezone.ts
5493
+ import { Command as Command22 } from "commander";
5494
+ async function handleTimezoneConversion(time, options) {
5495
+ const service = new UtilsService();
5496
+ try {
5497
+ if (options.list) {
5498
+ console.log("\u{1F30D} Available Timezones:");
5499
+ 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\n");
5500
+ const timezones = service.getTimezones();
5501
+ timezones.forEach((tz) => {
5502
+ console.log(` ${tz}`);
5503
+ });
5504
+ console.log();
5505
+ return;
5506
+ }
5507
+ if (!time) {
5508
+ console.error("\u274C Please provide time to convert or use --list to see available timezones");
5509
+ process.exit(1);
5510
+ }
5511
+ if (!options.from || !options.to) {
5512
+ console.error("\u274C Please provide both --from and --to timezones");
5513
+ process.exit(1);
5514
+ }
5515
+ const result = service.convertTimezone(time, options.from, options.to);
5516
+ console.log("\u{1F30D} Timezone Conversion:");
5517
+ 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");
5518
+ console.log(` From: ${options.from}`);
5519
+ console.log(` To: ${options.to}`);
5520
+ console.log();
5521
+ console.log(` ${result.time}`);
5522
+ console.log(` ISO: ${result.iso}`);
5523
+ console.log();
5524
+ } catch (error) {
5525
+ console.error("\u274C Error:", error instanceof Error ? error.message : String(error));
5526
+ process.exit(1);
5527
+ }
5528
+ }
5529
+ function createTimezoneCommand() {
5530
+ const cmd = new Command22("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", `
5531
+ Examples:
5532
+ $ jai1 utils timezone --list
5533
+ $ jai1 utils timezone "2024-01-15 10:00" --from "America/New_York" --to "Asia/Tokyo"
5534
+ $ jai1 utils timezone "2024-01-15 14:30" --from "UTC" --to "America/Los_Angeles"
5535
+ $ jai1 utils timezone "2024-12-25 12:00" --from "Europe/London" --to "Asia/Ho_Chi_Minh"
5536
+ `).action(async (time, options) => {
5537
+ await handleTimezoneConversion(time, options);
5538
+ });
5539
+ return cmd;
5540
+ }
5541
+
5542
+ // src/commands/utils/url-encode.ts
5543
+ import { Command as Command23 } from "commander";
5544
+ async function handleUrlEncode(input, options) {
5545
+ const service = new UtilsService();
5546
+ try {
5547
+ const encoded = service.urlEncode(input, options.full);
5548
+ console.log("\u{1F517} URL Encoded:");
5549
+ 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");
5550
+ if (options.full) {
5551
+ console.log(" Mode: Full URL (encodeURI)");
5552
+ } else {
5553
+ console.log(" Mode: Component (encodeURIComponent)");
5554
+ }
5555
+ console.log();
5556
+ console.log(` ${encoded}`);
5557
+ console.log();
5558
+ } catch (error) {
5559
+ console.error("\u274C Error:", error instanceof Error ? error.message : String(error));
5560
+ process.exit(1);
5561
+ }
5562
+ }
5563
+ function createUrlEncodeCommand() {
5564
+ const cmd = new Command23("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", `
5565
+ Examples:
5566
+ $ jai1 utils url-encode "hello world"
5567
+ $ jai1 utils url-encode "hello world & test"
5568
+ $ jai1 utils url-encode "name=John Doe&age=30"
5569
+ $ jai1 utils url-encode "https://example.com/path with spaces" --full
5570
+ `).showHelpAfterError("(add --help for additional examples)").action(async (input, options) => {
5571
+ await handleUrlEncode(input, options);
5572
+ });
5573
+ return cmd;
5574
+ }
5575
+
5576
+ // src/commands/utils/url-decode.ts
5577
+ import { Command as Command24 } from "commander";
5578
+ async function handleUrlDecode(input, options) {
5579
+ const service = new UtilsService();
5580
+ try {
5581
+ const decoded = service.urlDecode(input, options.full);
5582
+ console.log("\u{1F517} URL Decoded:");
5583
+ 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");
5584
+ if (options.full) {
5585
+ console.log(" Mode: Full URL (decodeURI)");
5586
+ } else {
5587
+ console.log(" Mode: Component (decodeURIComponent)");
5588
+ }
5589
+ console.log();
5590
+ console.log(` ${decoded}`);
5591
+ console.log();
5592
+ } catch (error) {
5593
+ console.error("\u274C Error:", error instanceof Error ? error.message : String(error));
5594
+ process.exit(1);
5595
+ }
5596
+ }
5597
+ function createUrlDecodeCommand() {
5598
+ const cmd = new Command24("url-decode").description("Decode URL-encoded string").argument("<input>", "Text to decode").option("--full", "Decode full URL (use decodeURI instead of decodeURIComponent)").addHelpText("after", `
5599
+ Examples:
5600
+ $ jai1 utils url-decode "hello%20world"
5601
+ $ jai1 utils url-decode "hello%20world%20%26%20test"
5602
+ $ jai1 utils url-decode "name%3DJohn%20Doe%26age%3D30"
5603
+ $ jai1 utils url-decode "https://example.com/path%20with%20spaces" --full
5604
+ `).showHelpAfterError("(add --help for additional examples)").action(async (input, options) => {
5605
+ await handleUrlDecode(input, options);
5606
+ });
5607
+ return cmd;
5608
+ }
5609
+
5610
+ // src/commands/utils/cron.ts
5611
+ import { Command as Command25 } from "commander";
5612
+ import cronstrue from "cronstrue";
5613
+ async function handleCronParse(expression) {
5614
+ try {
5615
+ const description = cronstrue.toString(expression);
5616
+ console.log("\u23F0 Cron Expression Parser:");
5617
+ 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");
5618
+ console.log(` Expression: ${expression}`);
5619
+ console.log(` Meaning: ${description}`);
5620
+ console.log();
5621
+ console.log("\u{1F4C5} Next 5 Executions:");
5622
+ const parts = expression.trim().split(/\s+/);
5623
+ if (parts.length < 5 || parts.length > 6) {
5624
+ console.log(" \u26A0\uFE0F Invalid cron expression format");
5625
+ return;
5626
+ }
5627
+ const now = /* @__PURE__ */ new Date();
5628
+ const executions = [];
5629
+ for (let i = 0; i < 5; i++) {
5630
+ const nextTime = new Date(now);
5631
+ nextTime.setDate(nextTime.getDate() + i);
5632
+ const [minute, hour] = parts.slice(parts.length === 6 ? 1 : 0, parts.length === 6 ? 3 : 2);
5633
+ if (minute !== "*" && !minute.includes("/") && !minute.includes("-")) {
5634
+ nextTime.setMinutes(parseInt(minute, 10));
5635
+ }
5636
+ if (hour !== "*" && !hour.includes("/") && !hour.includes("-")) {
5637
+ nextTime.setHours(parseInt(hour, 10));
5638
+ }
5639
+ executions.push(
5640
+ ` ${i + 1}. ${nextTime.toISOString().replace("T", " ").substring(0, 19)}`
5641
+ );
5642
+ }
5643
+ executions.forEach((exec) => console.log(exec));
5644
+ console.log();
5645
+ console.log("\u{1F4A1} Tip: This is an approximate calculation.");
5646
+ console.log(" For exact times, consider your system timezone.");
5647
+ console.log();
5648
+ } catch (error) {
5649
+ console.error("\u274C Error:", error instanceof Error ? error.message : String(error));
5650
+ process.exit(1);
5651
+ }
5652
+ }
5653
+ function createCronCommand() {
5654
+ const cmd = new Command25("cron").description("Parse and explain cron expression").argument("<expression>", 'Cron expression (e.g., "0 5 * * *")').addHelpText("after", `
5655
+ Examples:
5656
+ $ jai1 utils cron "0 5 * * *"
5657
+ $ jai1 utils cron "*/15 * * * *"
5658
+ $ jai1 utils cron "0 0 1 * *"
5659
+ $ jai1 utils cron "0 9-17 * * 1-5"
5660
+ $ jai1 utils cron "30 2 * * 0"
5661
+ `).showHelpAfterError("(add --help for additional examples)").action(async (expression) => {
5662
+ await handleCronParse(expression);
5663
+ });
5664
+ return cmd;
5665
+ }
5666
+
5667
+ // src/commands/utils/markdown-preview.ts
5668
+ import { Command as Command26 } from "commander";
5669
+ import { readFile as readFile5 } from "fs/promises";
5670
+ import { marked } from "marked";
5671
+ import TerminalRenderer from "marked-terminal";
5672
+ async function handleMarkdownPreview(file, options) {
5673
+ try {
5674
+ const content = await readFile5(file, "utf-8");
5675
+ if (options.raw) {
5676
+ console.log("\u{1F4C4} Markdown File (Raw):");
5677
+ 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\n");
5678
+ console.log(content);
5679
+ console.log();
5680
+ } else {
5681
+ marked.setOptions({
5682
+ // @ts-ignore - marked-terminal types may not match perfectly
5683
+ renderer: new TerminalRenderer()
5684
+ });
5685
+ const rendered = marked(content);
5686
+ console.log("\u{1F4C4} Markdown Preview:");
5687
+ 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\n");
5688
+ console.log(rendered);
5689
+ }
5690
+ } catch (error) {
5691
+ console.error("\u274C Error:", error instanceof Error ? error.message : String(error));
5692
+ process.exit(1);
5693
+ }
5694
+ }
5695
+ function createMarkdownPreviewCommand() {
5696
+ const cmd = new Command26("markdown-preview").description("Preview markdown file in terminal").argument("<file>", "Markdown file path").option("--raw", "Show raw markdown instead of rendered").addHelpText("after", `
5697
+ Examples:
5698
+ $ jai1 utils markdown-preview README.md
5699
+ $ jai1 utils markdown-preview ./docs/guide.md
5700
+ $ jai1 utils markdown-preview CHANGELOG.md --raw
5701
+ $ jai1 utils markdown-preview ./notes.md
5702
+ `).showHelpAfterError("(add --help for additional examples)").action(async (file, options) => {
5703
+ await handleMarkdownPreview(file, options);
5704
+ });
5705
+ return cmd;
5706
+ }
5707
+
5708
+ // src/commands/utils/index.ts
5709
+ function createUtilsCommand() {
5710
+ const utilsCommand = new Command27("utils").description("Developer utilities for common tasks");
5711
+ utilsCommand.addCommand(createPasswordCommand());
5712
+ utilsCommand.addCommand(createUuidCommand());
5713
+ utilsCommand.addCommand(createHashCommand());
5714
+ utilsCommand.addCommand(createBase64EncodeCommand());
5715
+ utilsCommand.addCommand(createBase64DecodeCommand());
5716
+ utilsCommand.addCommand(createHttpCommand());
5717
+ utilsCommand.addCommand(createJwtCommand());
5718
+ utilsCommand.addCommand(createUnixTimeCommand());
5719
+ utilsCommand.addCommand(createTimezoneCommand());
5720
+ utilsCommand.addCommand(createUrlEncodeCommand());
5721
+ utilsCommand.addCommand(createUrlDecodeCommand());
5722
+ utilsCommand.addCommand(createCronCommand());
5723
+ utilsCommand.addCommand(createMarkdownPreviewCommand());
5724
+ utilsCommand.action(() => {
5725
+ utilsCommand.help();
5726
+ });
5727
+ return utilsCommand;
5728
+ }
5729
+
5730
+ // src/commands/upgrade.ts
5731
+ import { Command as Command28 } from "commander";
4795
5732
  import { confirm as confirm4 } from "@inquirer/prompts";
4796
5733
  import { execSync } from "child_process";
4797
5734
  var colors2 = {
@@ -4803,7 +5740,7 @@ var colors2 = {
4803
5740
  bold: "\x1B[1m"
4804
5741
  };
4805
5742
  function createUpgradeCommand() {
4806
- return new Command14("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) => {
5743
+ return new Command28("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) => {
4807
5744
  await handleUpgrade(options);
4808
5745
  });
4809
5746
  }
@@ -4951,11 +5888,11 @@ function getInstallCommand(packageManager2) {
4951
5888
  }
4952
5889
 
4953
5890
  // src/commands/clean.ts
4954
- import { Command as Command15 } from "commander";
5891
+ import { Command as Command29 } from "commander";
4955
5892
  import { confirm as confirm5, select as select2 } from "@inquirer/prompts";
4956
5893
  import { join as join4 } from "path";
4957
5894
  function createCleanCommand() {
4958
- return new Command15("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) => {
5895
+ return new Command29("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) => {
4959
5896
  await handleClean(options);
4960
5897
  });
4961
5898
  }
@@ -5068,10 +6005,10 @@ async function cleanTarget(target, skipConfirm) {
5068
6005
  }
5069
6006
 
5070
6007
  // src/commands/redmine/check.ts
5071
- import { Command as Command16 } from "commander";
6008
+ import { Command as Command30 } from "commander";
5072
6009
 
5073
6010
  // src/services/redmine-config.service.ts
5074
- import { readFile } from "fs/promises";
6011
+ import { readFile as readFile6 } from "fs/promises";
5075
6012
  import { resolve } from "path";
5076
6013
 
5077
6014
  // src/types/redmine.types.ts
@@ -5130,7 +6067,7 @@ var RedmineConfigService = class {
5130
6067
  */
5131
6068
  async load() {
5132
6069
  try {
5133
- const content = await readFile(this.configPath, "utf-8");
6070
+ const content = await readFile6(this.configPath, "utf-8");
5134
6071
  const rawConfig = parse(content);
5135
6072
  return RedmineConfigSchema.parse(rawConfig);
5136
6073
  } catch (error) {
@@ -5375,7 +6312,7 @@ async function checkConnectivity(config) {
5375
6312
 
5376
6313
  // src/commands/redmine/check.ts
5377
6314
  function createRedmineCheckCommand() {
5378
- const cmd = new Command16("check").description("Check Redmine connectivity").option("-c, --config <path>", "Config file path", "redmine.config.yaml").option("--json", "Output as JSON").action(async (options) => {
6315
+ const cmd = new Command30("check").description("Check Redmine connectivity").option("-c, --config <path>", "Config file path", "redmine.config.yaml").option("--json", "Output as JSON").action(async (options) => {
5379
6316
  await handleRedmineCheck(options);
5380
6317
  });
5381
6318
  return cmd;
@@ -5403,7 +6340,7 @@ async function handleRedmineCheck(options) {
5403
6340
  }
5404
6341
 
5405
6342
  // src/commands/redmine/sync-issue.ts
5406
- import { Command as Command17 } from "commander";
6343
+ import { Command as Command31 } from "commander";
5407
6344
 
5408
6345
  // src/sync-issue.ts
5409
6346
  import { resolve as resolve2, relative } from "path";
@@ -5545,7 +6482,7 @@ function generateFilename(issueId, title, config, existingSlugs = /* @__PURE__ *
5545
6482
  }
5546
6483
 
5547
6484
  // src/file.util.ts
5548
- import { readFile as readFile2, writeFile, mkdir } from "fs/promises";
6485
+ import { readFile as readFile7, writeFile as writeFile2, mkdir } from "fs/promises";
5549
6486
  import { dirname } from "path";
5550
6487
  import matter3 from "gray-matter";
5551
6488
  async function ensureDir(filePath) {
@@ -5554,7 +6491,7 @@ async function ensureDir(filePath) {
5554
6491
  }
5555
6492
  async function readMarkdownFile(filePath) {
5556
6493
  try {
5557
- const content = await readFile2(filePath, "utf-8");
6494
+ const content = await readFile7(filePath, "utf-8");
5558
6495
  return parseMarkdownContent(content);
5559
6496
  } catch (error) {
5560
6497
  if (error.code === "ENOENT") {
@@ -5632,7 +6569,7 @@ ${config.anchors.end}`;
5632
6569
  async function writeMarkdownFile(filePath, data, config) {
5633
6570
  await ensureDir(filePath);
5634
6571
  const content = buildMarkdownContent(data, config);
5635
- await writeFile(filePath, content, "utf-8");
6572
+ await writeFile2(filePath, content, "utf-8");
5636
6573
  }
5637
6574
  function extractIssueIdFromFrontmatter(frontmatter) {
5638
6575
  const id = frontmatter.id;
@@ -5779,7 +6716,7 @@ function extractIssueIdFromUrl(url) {
5779
6716
 
5780
6717
  // src/commands/redmine/sync-issue.ts
5781
6718
  function createSyncIssueCommand() {
5782
- const cmd = new Command17("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) => {
6719
+ const cmd = new Command31("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) => {
5783
6720
  await handleSyncIssue(options);
5784
6721
  });
5785
6722
  return cmd;
@@ -5823,7 +6760,7 @@ async function handleSyncIssue(options) {
5823
6760
  }
5824
6761
 
5825
6762
  // src/commands/redmine/sync-project.ts
5826
- import { Command as Command18 } from "commander";
6763
+ import { Command as Command32 } from "commander";
5827
6764
 
5828
6765
  // src/sync-project.ts
5829
6766
  async function syncProject(config, options = {}) {
@@ -5893,7 +6830,7 @@ async function syncProject(config, options = {}) {
5893
6830
 
5894
6831
  // src/commands/redmine/sync-project.ts
5895
6832
  function createSyncProjectCommand() {
5896
- const cmd = new Command18("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) => {
6833
+ const cmd = new Command32("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) => {
5897
6834
  await handleSyncProject(options);
5898
6835
  });
5899
6836
  return cmd;
@@ -5948,12 +6885,12 @@ async function handleSyncProject(options) {
5948
6885
  }
5949
6886
 
5950
6887
  // src/commands/framework/info.ts
5951
- import { Command as Command19 } from "commander";
6888
+ import { Command as Command33 } from "commander";
5952
6889
  import { promises as fs8 } from "fs";
5953
6890
  import { join as join5 } from "path";
5954
6891
  import { homedir as homedir5 } from "os";
5955
6892
  function createInfoCommand() {
5956
- const cmd = new Command19("info").description("Show jai1-client configuration and status").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
6893
+ const cmd = new Command33("info").description("Show jai1-client configuration and status").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
5957
6894
  await handleInfo(options);
5958
6895
  });
5959
6896
  return cmd;
@@ -6009,7 +6946,7 @@ async function getProjectStatus2() {
6009
6946
  }
6010
6947
 
6011
6948
  // src/commands/self-update.ts
6012
- import { Command as Command20 } from "commander";
6949
+ import { Command as Command34 } from "commander";
6013
6950
  import { confirm as confirm6 } from "@inquirer/prompts";
6014
6951
  import { execSync as execSync2 } from "child_process";
6015
6952
  var colors3 = {
@@ -6021,7 +6958,7 @@ var colors3 = {
6021
6958
  bold: "\x1B[1m"
6022
6959
  };
6023
6960
  function createSelfUpdateCommand() {
6024
- return new Command20("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) => {
6961
+ return new Command34("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) => {
6025
6962
  await handleSelfUpdate(options);
6026
6963
  });
6027
6964
  }
@@ -6161,10 +7098,10 @@ function getInstallCommand2(packageManager2) {
6161
7098
  }
6162
7099
 
6163
7100
  // src/commands/clear-backups.ts
6164
- import { Command as Command21 } from "commander";
7101
+ import { Command as Command35 } from "commander";
6165
7102
  import { confirm as confirm7 } from "@inquirer/prompts";
6166
7103
  function createClearBackupsCommand() {
6167
- return new Command21("clear-backups").description("Clear backup files").option("-y, --yes", "Skip confirmation").action(async (options) => {
7104
+ return new Command35("clear-backups").description("Clear backup files").option("-y, --yes", "Skip confirmation").action(async (options) => {
6168
7105
  const service = new ComponentsService();
6169
7106
  const backups = await service.listBackups(process.cwd());
6170
7107
  if (backups.length === 0) {
@@ -6189,7 +7126,7 @@ function createClearBackupsCommand() {
6189
7126
  }
6190
7127
 
6191
7128
  // src/commands/vscode/index.ts
6192
- import { Command as Command22 } from "commander";
7129
+ import { Command as Command36 } from "commander";
6193
7130
  import { checkbox as checkbox3, confirm as confirm8, select as select3 } from "@inquirer/prompts";
6194
7131
  import fs9 from "fs/promises";
6195
7132
  import path5 from "path";
@@ -6329,7 +7266,7 @@ var PERFORMANCE_GROUPS2 = {
6329
7266
  }
6330
7267
  };
6331
7268
  function createVSCodeCommand() {
6332
- const vscodeCommand = new Command22("vscode").description("Qu\u1EA3n l\xFD c\xE0i \u0111\u1EB7t VSCode cho d\u1EF1 \xE1n hi\u1EC7n t\u1EA1i");
7269
+ const vscodeCommand = new Command36("vscode").description("Qu\u1EA3n l\xFD c\xE0i \u0111\u1EB7t VSCode cho d\u1EF1 \xE1n hi\u1EC7n t\u1EA1i");
6333
7270
  vscodeCommand.action(async () => {
6334
7271
  await interactiveMode2();
6335
7272
  });
@@ -6500,9 +7437,9 @@ async function resetSettings2(groupKeys) {
6500
7437
  // src/commands/guide.ts
6501
7438
  import React27 from "react";
6502
7439
  import { render as render5 } from "ink";
6503
- import { Command as Command23 } from "commander";
7440
+ import { Command as Command37 } from "commander";
6504
7441
  function createGuideCommand() {
6505
- const cmd = new Command23("guide").description("Interactive guide center for Agentic Coding").option("--topic <topic>", "Open specific topic (intro, rules, workflows, prompts, skills)").action(async (options) => {
7442
+ const cmd = new Command37("guide").description("Interactive guide center for Agentic Coding").option("--topic <topic>", "Open specific topic (intro, rules, workflows, prompts, skills)").action(async (options) => {
6506
7443
  const { waitUntilExit } = render5(
6507
7444
  React27.createElement(GuideApp, {
6508
7445
  initialTopic: options.topic,
@@ -6519,9 +7456,9 @@ function createGuideCommand() {
6519
7456
  // src/commands/context.ts
6520
7457
  import React28 from "react";
6521
7458
  import { render as render6 } from "ink";
6522
- import { Command as Command24 } from "commander";
7459
+ import { Command as Command38 } from "commander";
6523
7460
  function createContextCommand() {
6524
- const cmd = new Command24("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) => {
7461
+ const cmd = new Command38("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) => {
6525
7462
  let initialIDE;
6526
7463
  if (options.ide) {
6527
7464
  const validIDEs = ["cursor", "windsurf", "antigravity", "jai1"];
@@ -6598,10 +7535,10 @@ async function printStats2() {
6598
7535
  }
6599
7536
 
6600
7537
  // src/commands/migrate-ide.ts
6601
- import { Command as Command25 } from "commander";
7538
+ import { Command as Command39 } from "commander";
6602
7539
  import { checkbox as checkbox4, confirm as confirm9 } from "@inquirer/prompts";
6603
7540
  function createMigrateIdeCommand() {
6604
- const cmd = new Command25("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) => {
7541
+ const cmd = new Command39("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) => {
6605
7542
  await runMigrateIde(options);
6606
7543
  });
6607
7544
  return cmd;
@@ -6707,7 +7644,7 @@ async function runMigrateIde(options) {
6707
7644
  }
6708
7645
 
6709
7646
  // src/cli.ts
6710
- var program = new Command26();
7647
+ var program = new Command40();
6711
7648
  if (process.argv.includes("-v") || process.argv.includes("--version")) {
6712
7649
  console.log(package_default.version);
6713
7650
  if (!process.argv.includes("--skip-update-check")) {
@@ -6727,11 +7664,12 @@ program.addCommand(createLearnCommand());
6727
7664
  program.addCommand(createChatCommand());
6728
7665
  program.addCommand(createApiKeysCommand());
6729
7666
  program.addCommand(createTranslateCommand());
7667
+ program.addCommand(createUtilsCommand());
6730
7668
  program.addCommand(createUpgradeCommand());
6731
7669
  program.addCommand(createCleanCommand());
6732
- var redmineCommand = new Command26("redmine").description("Redmine context sync commands");
7670
+ var redmineCommand = new Command40("redmine").description("Redmine context sync commands");
6733
7671
  redmineCommand.addCommand(createRedmineCheckCommand());
6734
- var syncCommand = new Command26("sync").description("Sync Redmine issues to markdown files");
7672
+ var syncCommand = new Command40("sync").description("Sync Redmine issues to markdown files");
6735
7673
  syncCommand.addCommand(createSyncIssueCommand());
6736
7674
  syncCommand.addCommand(createSyncProjectCommand());
6737
7675
  redmineCommand.addCommand(syncCommand);
@@ -6769,6 +7707,9 @@ program.on("command:*", (operands) => {
6769
7707
  console.error(" api-keys Show OpenAI-compatible API credentials");
6770
7708
  console.error(" translate Translate text, files, or folders using AI");
6771
7709
  console.error("");
7710
+ console.error(" \u{1F6E0}\uFE0F Developer Utilities");
7711
+ console.error(" utils Developer tools (password, uuid, hash, jwt, etc.)");
7712
+ console.error("");
6772
7713
  console.error(" \u{1F527} Maintenance");
6773
7714
  console.error(" upgrade Upgrade jai1-client to latest version");
6774
7715
  console.error(" clean Clean up backups and cache");