@jvittechs/jai1-cli 0.1.77 → 0.1.79

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 Command41 } from "commander";
4
+ import { Command as Command45 } 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.77",
36
+ version: "0.1.79",
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: {
@@ -85,6 +85,7 @@ var package_default = {
85
85
  clipboardy: "^4.0.0",
86
86
  commander: "^12.1.0",
87
87
  cronstrue: "^2.50.0",
88
+ execa: "^9.6.1",
88
89
  "gray-matter": "^4.0.3",
89
90
  ink: "^5.0.1",
90
91
  "ink-spinner": "^5.0.0",
@@ -4391,13 +4392,13 @@ function createChatCommand() {
4391
4392
  return cmd;
4392
4393
  }
4393
4394
 
4394
- // src/commands/api-keys.ts
4395
+ // src/commands/openai-keys.ts
4395
4396
  import { Command as Command12 } from "commander";
4396
4397
  function maskKey2(key) {
4397
4398
  if (key.length <= 8) return "****";
4398
4399
  return key.slice(0, 8) + "****" + key.slice(-4);
4399
4400
  }
4400
- async function handleApiKeysCommand(options) {
4401
+ async function handleOpenAiKeysCommand(options) {
4401
4402
  const configService = new ConfigService();
4402
4403
  const config = await configService.load();
4403
4404
  if (!config) {
@@ -4436,7 +4437,7 @@ async function handleApiKeysCommand(options) {
4436
4437
  console.log(" - Use as drop-in replacement for OpenAI API");
4437
4438
  console.log(" - Compatible with: OpenAI SDK, LangChain, LlamaIndex, etc.");
4438
4439
  console.log(' - Run "jai1 chat" for interactive mode');
4439
- console.log(' - Run "jai1 api-keys --full" to show full API key');
4440
+ console.log(' - Run "jai1 openai-keys --full" to show full API key');
4440
4441
  } catch (error) {
4441
4442
  console.error(
4442
4443
  "\n\u274C Failed to fetch API info:",
@@ -4445,9 +4446,9 @@ async function handleApiKeysCommand(options) {
4445
4446
  console.log('\n\u{1F4A1} Check your API URL and access key with "jai1 status"');
4446
4447
  }
4447
4448
  }
4448
- function createApiKeysCommand() {
4449
- const cmd = new Command12("api-keys").description("Show OpenAI-compatible API credentials and info").option("--full", "Show full API key (unmasked)").action(async (options) => {
4450
- await handleApiKeysCommand(options);
4449
+ 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) => {
4451
+ await handleOpenAiKeysCommand(options);
4451
4452
  });
4452
4453
  return cmd;
4453
4454
  }
@@ -4513,7 +4514,7 @@ async function handleStatsCommand() {
4513
4514
  });
4514
4515
  }
4515
4516
  console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
4516
- console.log('\n\u{1F4A1} M\u1EB9o: Ch\u1EA1y "jai1 api-keys" \u0111\u1EC3 xem danh s\xE1ch model kh\u1EA3 d\u1EE5ng');
4517
+ console.log('\n\u{1F4A1} M\u1EB9o: Ch\u1EA1y "jai1 openai-keys" \u0111\u1EC3 xem danh s\xE1ch model kh\u1EA3 d\u1EE5ng');
4517
4518
  } catch (error) {
4518
4519
  console.error(
4519
4520
  "\n\u274C Kh\xF4ng th\u1EC3 l\u1EA5y th\u1ED1ng k\xEA:",
@@ -7237,8 +7238,398 @@ Quick Usage:
7237
7238
  return utilsCommand;
7238
7239
  }
7239
7240
 
7240
- // src/commands/upgrade.ts
7241
+ // src/commands/kit/index.ts
7242
+ import { Command as Command32 } from "commander";
7243
+
7244
+ // src/commands/kit/list.ts
7241
7245
  import { Command as Command29 } from "commander";
7246
+
7247
+ // src/services/starter-kit.service.ts
7248
+ import { promises as fs9 } from "fs";
7249
+ import { join as join4 } from "path";
7250
+ import AdmZip from "adm-zip";
7251
+ var StarterKitService = class {
7252
+ /**
7253
+ * List available starter kits
7254
+ */
7255
+ async list(config, options) {
7256
+ const params = new URLSearchParams();
7257
+ if (options?.category) params.set("category", options.category);
7258
+ if (options?.search) params.set("search", options.search);
7259
+ const url = `${config.apiUrl}/api/starter-kits${params.toString() ? "?" + params : ""}`;
7260
+ const response = await fetch(url, {
7261
+ headers: { "JAI1-Access-Key": config.accessKey }
7262
+ });
7263
+ if (!response.ok) {
7264
+ throw new NetworkError(`Failed to list starter kits: HTTP ${response.status}`);
7265
+ }
7266
+ const data = await response.json();
7267
+ return data.kits;
7268
+ }
7269
+ /**
7270
+ * Get single starter kit with full config
7271
+ */
7272
+ async get(config, slug) {
7273
+ const response = await fetch(`${config.apiUrl}/api/starter-kits/${slug}`, {
7274
+ headers: { "JAI1-Access-Key": config.accessKey }
7275
+ });
7276
+ if (!response.ok) {
7277
+ if (response.status === 404) {
7278
+ throw new Error(`Starter kit not found: ${slug}`);
7279
+ }
7280
+ throw new NetworkError(`Failed to get starter kit: HTTP ${response.status}`);
7281
+ }
7282
+ return await response.json();
7283
+ }
7284
+ /**
7285
+ * Download and extract starter kit
7286
+ */
7287
+ async downloadAndExtract(config, slug, targetDir, onProgress) {
7288
+ if (onProgress) onProgress(10);
7289
+ const response = await fetch(`${config.apiUrl}/api/starter-kits/${slug}/download`, {
7290
+ headers: { "JAI1-Access-Key": config.accessKey }
7291
+ });
7292
+ if (!response.ok) {
7293
+ throw new NetworkError(`Failed to get download URL: HTTP ${response.status}`);
7294
+ }
7295
+ const data = await response.json();
7296
+ if (onProgress) onProgress(30);
7297
+ const downloadResponse = await fetch(data.downloadUrl);
7298
+ if (!downloadResponse.ok) {
7299
+ throw new NetworkError(`Failed to download kit: HTTP ${downloadResponse.status}`);
7300
+ }
7301
+ if (onProgress) onProgress(60);
7302
+ const tmpDir = join4(process.env.TMPDIR || "/tmp", "jai1-kits");
7303
+ await fs9.mkdir(tmpDir, { recursive: true });
7304
+ const tmpFile = join4(tmpDir, `${slug}.zip`);
7305
+ const buffer = await downloadResponse.arrayBuffer();
7306
+ await fs9.writeFile(tmpFile, Buffer.from(buffer));
7307
+ if (onProgress) onProgress(80);
7308
+ const zip = new AdmZip(tmpFile);
7309
+ await fs9.mkdir(targetDir, { recursive: true });
7310
+ zip.extractAllTo(targetDir, true);
7311
+ if (onProgress) onProgress(100);
7312
+ await fs9.unlink(tmpFile);
7313
+ }
7314
+ };
7315
+
7316
+ // src/commands/kit/list.ts
7317
+ function createKitListCommand() {
7318
+ return new Command29("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) => {
7319
+ const configService = new ConfigService();
7320
+ const config = await configService.load();
7321
+ if (!config) {
7322
+ throw new ValidationError('Not initialized. Run "jai1 auth" first.');
7323
+ }
7324
+ const kitService = new StarterKitService();
7325
+ const kits = await kitService.list(config, {
7326
+ category: options.category,
7327
+ search: options.search
7328
+ });
7329
+ if (kits.length === 0) {
7330
+ console.log("No starter kits found.");
7331
+ return;
7332
+ }
7333
+ const byCategory = {};
7334
+ for (const kit of kits) {
7335
+ if (!byCategory[kit.category]) {
7336
+ byCategory[kit.category] = [];
7337
+ }
7338
+ byCategory[kit.category].push(kit);
7339
+ }
7340
+ console.log("\u{1F4E6} Available Starter Kits:\n");
7341
+ for (const [category, categoryKits] of Object.entries(byCategory)) {
7342
+ console.log(`${category.charAt(0).toUpperCase() + category.slice(1)}:`);
7343
+ for (const kit of categoryKits) {
7344
+ const tags = kit.tags.length > 0 ? ` [${kit.tags.join(", ")}]` : "";
7345
+ console.log(
7346
+ ` ${kit.slug.padEnd(20)} ${kit.description.slice(0, 50).padEnd(52)} v${kit.version}${tags}`
7347
+ );
7348
+ }
7349
+ console.log();
7350
+ }
7351
+ });
7352
+ }
7353
+
7354
+ // src/commands/kit/info.ts
7355
+ import { Command as Command30 } from "commander";
7356
+ function createKitInfoCommand() {
7357
+ return new Command30("info").description("Show detailed information about a starter kit").argument("<slug>", "Starter kit slug").action(async (slug) => {
7358
+ const configService = new ConfigService();
7359
+ const config = await configService.load();
7360
+ if (!config) {
7361
+ throw new ValidationError('Not initialized. Run "jai1 auth" first.');
7362
+ }
7363
+ const kitService = new StarterKitService();
7364
+ const kit = await kitService.get(config, slug);
7365
+ console.log(`\u{1F4E6} ${kit.name}
7366
+ `);
7367
+ console.log(`Description: ${kit.description}`);
7368
+ console.log(`Category: ${kit.category}`);
7369
+ console.log(`Version: ${kit.version}`);
7370
+ console.log(`Downloads: ${kit.downloads}`);
7371
+ if (kit.tags.length > 0) {
7372
+ console.log(`Tags: ${kit.tags.join(", ")}`);
7373
+ }
7374
+ if (kit.config.requiredTools && kit.config.requiredTools.length > 0) {
7375
+ console.log(`
7376
+ Required Tools:`);
7377
+ kit.config.requiredTools.forEach((tool) => {
7378
+ console.log(` - ${tool}`);
7379
+ });
7380
+ }
7381
+ if (kit.config.framework?.apply && kit.config.framework.components.length > 0) {
7382
+ console.log(`
7383
+ Included Framework Components:`);
7384
+ kit.config.framework.components.forEach((comp) => {
7385
+ console.log(` - ${comp}`);
7386
+ });
7387
+ }
7388
+ if (kit.config.variables && kit.config.variables.length > 0) {
7389
+ console.log(`
7390
+ Variables (prompted during creation):`);
7391
+ kit.config.variables.forEach((v) => {
7392
+ console.log(` - ${v.name}: ${v.prompt}${v.default ? ` (default: ${v.default})` : ""}`);
7393
+ });
7394
+ }
7395
+ if (kit.config.hooks?.postInit && kit.config.hooks.postInit.length > 0) {
7396
+ console.log(`
7397
+ Post-Init Commands:`);
7398
+ kit.config.hooks.postInit.forEach((cmd) => {
7399
+ console.log(` - ${cmd}`);
7400
+ });
7401
+ }
7402
+ });
7403
+ }
7404
+
7405
+ // src/commands/kit/create.ts
7406
+ import { Command as Command31 } from "commander";
7407
+ import { promises as fs10 } from "fs";
7408
+ import { join as join5 } from "path";
7409
+ import prompts from "@inquirer/prompts";
7410
+ import { execa as execa2 } from "execa";
7411
+
7412
+ // src/services/hook-executor.service.ts
7413
+ import { execa } from "execa";
7414
+ var HookExecutor = class {
7415
+ constructor(cwd, variables) {
7416
+ this.cwd = cwd;
7417
+ this.variables = variables;
7418
+ }
7419
+ /**
7420
+ * Run hooks sequentially
7421
+ */
7422
+ async run(hooks) {
7423
+ for (const hook of hooks) {
7424
+ const cmd = this.replaceVariables(hook);
7425
+ console.log(` $ ${cmd}`);
7426
+ try {
7427
+ await execa(cmd, {
7428
+ cwd: this.cwd,
7429
+ shell: true,
7430
+ stdio: "inherit"
7431
+ });
7432
+ } catch (error) {
7433
+ throw new Error(`Hook failed: ${cmd}`);
7434
+ }
7435
+ }
7436
+ }
7437
+ /**
7438
+ * Replace variables in command
7439
+ */
7440
+ replaceVariables(cmd) {
7441
+ let result = cmd;
7442
+ for (const [key, value] of Object.entries(this.variables)) {
7443
+ result = result.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), value);
7444
+ }
7445
+ return result;
7446
+ }
7447
+ };
7448
+
7449
+ // src/commands/kit/create.ts
7450
+ function createKitCreateCommand() {
7451
+ return new Command31("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) => {
7452
+ const configService = new ConfigService();
7453
+ const config = await configService.load();
7454
+ if (!config) {
7455
+ throw new ValidationError('Not initialized. Run "jai1 auth" first.');
7456
+ }
7457
+ const isAutoMode = options.yes === true;
7458
+ const kitService = new StarterKitService();
7459
+ console.log(`\u{1F4E6} Fetching starter kit: ${slug}...`);
7460
+ const kit = await kitService.get(config, slug);
7461
+ if (kit.config.requiredTools && kit.config.requiredTools.length > 0) {
7462
+ console.log("\u{1F50D} Checking required tools...");
7463
+ for (const tool of kit.config.requiredTools) {
7464
+ try {
7465
+ await execa2("which", [tool]);
7466
+ } catch {
7467
+ console.warn(`\u26A0\uFE0F Warning: Required tool '${tool}' not found`);
7468
+ }
7469
+ }
7470
+ }
7471
+ const targetDir = directory || join5(process.cwd(), options.name || slug);
7472
+ try {
7473
+ await fs10.access(targetDir);
7474
+ throw new Error(`Directory already exists: ${targetDir}`);
7475
+ } catch (error) {
7476
+ if (error.code !== "ENOENT") {
7477
+ throw error;
7478
+ }
7479
+ }
7480
+ if (kit.config.hooks?.preInit && kit.config.hooks.preInit.length > 0) {
7481
+ console.log("\u{1F527} Running pre-init hooks...");
7482
+ const hookExecutor = new HookExecutor(process.cwd(), {});
7483
+ await hookExecutor.run(kit.config.hooks.preInit);
7484
+ }
7485
+ console.log("\u{1F4E5} Downloading template...");
7486
+ await kitService.downloadAndExtract(config, slug, targetDir, (percent) => {
7487
+ if (percent === 100) {
7488
+ console.log(" \u2713 Downloaded and extracted");
7489
+ }
7490
+ });
7491
+ if (kit.config.hooks?.postExtract && kit.config.hooks.postExtract.length > 0) {
7492
+ console.log("\u{1F4C4} Setting up environment...");
7493
+ const hookExecutor = new HookExecutor(targetDir, {});
7494
+ await hookExecutor.run(kit.config.hooks.postExtract);
7495
+ }
7496
+ const variables = {};
7497
+ if (kit.config.variables && kit.config.variables.length > 0) {
7498
+ if (isAutoMode) {
7499
+ console.log("\u{1F4DD} Using default values (auto mode)");
7500
+ for (const v of kit.config.variables) {
7501
+ variables[v.name] = options.name && v.name === "PROJECT_NAME" ? options.name : v.default || "";
7502
+ }
7503
+ } else {
7504
+ console.log("\u{1F4DD} Please provide project details:");
7505
+ for (const v of kit.config.variables) {
7506
+ if (v.type === "select" && v.options) {
7507
+ const answer = await prompts.select({
7508
+ message: v.prompt,
7509
+ choices: v.options.map((opt) => ({ name: opt, value: opt })),
7510
+ default: v.default
7511
+ });
7512
+ variables[v.name] = answer;
7513
+ } else {
7514
+ const answer = await prompts.input({
7515
+ message: v.prompt,
7516
+ default: v.default
7517
+ });
7518
+ variables[v.name] = answer;
7519
+ }
7520
+ }
7521
+ }
7522
+ }
7523
+ if (Object.keys(variables).length > 0) {
7524
+ await applyVariableSubstitution(targetDir, variables);
7525
+ }
7526
+ if (!options.skipFramework && kit.config.framework?.apply) {
7527
+ console.log("\u{1F916} Applying jai1 framework...");
7528
+ const componentsService = new ComponentsService(targetDir);
7529
+ for (const filepath of kit.config.framework.components) {
7530
+ console.log(` \u{1F4E5} Installing ${filepath}...`);
7531
+ await componentsService.install(config, filepath);
7532
+ }
7533
+ console.log(" \u2713 Framework components applied");
7534
+ }
7535
+ if (!options.skipGit && kit.config.git?.init) {
7536
+ console.log("\u{1F527} Initializing git repository...");
7537
+ await execa2("git", ["init"], { cwd: targetDir });
7538
+ await execa2("git", ["add", "."], { cwd: targetDir });
7539
+ const commitMsg = kit.config.git.initialCommit || "Initial commit";
7540
+ await execa2("git", ["commit", "-m", commitMsg], { cwd: targetDir });
7541
+ console.log(" \u2713 Git initialized");
7542
+ }
7543
+ if (!options.skipInstall && kit.config.hooks?.postInit) {
7544
+ console.log("\u{1F527} Running setup...");
7545
+ const hookExecutor = new HookExecutor(targetDir, variables);
7546
+ await hookExecutor.run(kit.config.hooks.postInit);
7547
+ }
7548
+ if (!options.skipIde) {
7549
+ const ideChoices = [
7550
+ { name: "Cursor", value: "cursor" },
7551
+ { name: "Windsurf", value: "windsurf" },
7552
+ { name: "VSCode", value: "vscode" },
7553
+ { name: "Claude Code", value: "claude-code" },
7554
+ { name: "Antigravity", value: "antigravity" }
7555
+ ];
7556
+ let selectedIdes = [];
7557
+ if (isAutoMode) {
7558
+ selectedIdes = kit.config.ide?.defaultTargets || [];
7559
+ if (selectedIdes.length > 0) {
7560
+ console.log(`\u{1F5A5}\uFE0F Syncing to IDEs: ${selectedIdes.join(", ")}`);
7561
+ }
7562
+ } else {
7563
+ const defaultTargets = kit.config.ide?.defaultTargets || [];
7564
+ const answer = await prompts.checkbox({
7565
+ message: "Ch\u1ECDn IDE \u0111\u1EC3 sync framework:",
7566
+ choices: ideChoices.map((c) => ({
7567
+ ...c,
7568
+ checked: defaultTargets.includes(c.value)
7569
+ }))
7570
+ });
7571
+ selectedIdes = answer;
7572
+ }
7573
+ if (selectedIdes.length > 0) {
7574
+ await execa2("jai1", ["ide", "sync", ...selectedIdes], {
7575
+ cwd: targetDir,
7576
+ stdio: "inherit"
7577
+ });
7578
+ }
7579
+ }
7580
+ console.log("\n\u2705 Project created successfully!");
7581
+ console.log(`\u{1F4C1} Location: ${targetDir}`);
7582
+ console.log("\n\u{1F4A1} Next steps:");
7583
+ console.log(` cd ${targetDir}`);
7584
+ console.log(" jai1 apply # Add more framework components");
7585
+ });
7586
+ }
7587
+ async function applyVariableSubstitution(dir, variables) {
7588
+ const files = await getAllFiles(dir);
7589
+ for (const file of files) {
7590
+ let content = await fs10.readFile(file, "utf-8");
7591
+ let modified = false;
7592
+ for (const [key, value] of Object.entries(variables)) {
7593
+ const regex = new RegExp(`\\{\\{${key}\\}\\}`, "g");
7594
+ if (regex.test(content)) {
7595
+ content = content.replace(regex, value);
7596
+ modified = true;
7597
+ }
7598
+ }
7599
+ if (modified) {
7600
+ await fs10.writeFile(file, content, "utf-8");
7601
+ }
7602
+ }
7603
+ }
7604
+ async function getAllFiles(dir) {
7605
+ const files = [];
7606
+ const entries = await fs10.readdir(dir, { withFileTypes: true });
7607
+ for (const entry of entries) {
7608
+ const fullPath = join5(dir, entry.name);
7609
+ if (entry.isDirectory()) {
7610
+ if (!entry.name.startsWith(".") && entry.name !== "node_modules") {
7611
+ files.push(...await getAllFiles(fullPath));
7612
+ }
7613
+ } else {
7614
+ files.push(fullPath);
7615
+ }
7616
+ }
7617
+ return files;
7618
+ }
7619
+
7620
+ // src/commands/kit/index.ts
7621
+ function createKitCommand() {
7622
+ const cmd = new Command32("kit").description("Manage starter kits for new projects").action(() => {
7623
+ cmd.help();
7624
+ });
7625
+ cmd.addCommand(createKitListCommand());
7626
+ cmd.addCommand(createKitInfoCommand());
7627
+ cmd.addCommand(createKitCreateCommand());
7628
+ return cmd;
7629
+ }
7630
+
7631
+ // src/commands/upgrade.ts
7632
+ import { Command as Command33 } from "commander";
7242
7633
  import { confirm as confirm4 } from "@inquirer/prompts";
7243
7634
  import { execSync } from "child_process";
7244
7635
  var colors2 = {
@@ -7250,7 +7641,7 @@ var colors2 = {
7250
7641
  bold: "\x1B[1m"
7251
7642
  };
7252
7643
  function createUpgradeCommand() {
7253
- return new Command29("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) => {
7644
+ return new Command33("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) => {
7254
7645
  await handleUpgrade(options);
7255
7646
  });
7256
7647
  }
@@ -7398,11 +7789,11 @@ function getInstallCommand(packageManager2) {
7398
7789
  }
7399
7790
 
7400
7791
  // src/commands/clean.ts
7401
- import { Command as Command30 } from "commander";
7792
+ import { Command as Command34 } from "commander";
7402
7793
  import { confirm as confirm5, select as select2 } from "@inquirer/prompts";
7403
- import { join as join4 } from "path";
7794
+ import { join as join6 } from "path";
7404
7795
  function createCleanCommand() {
7405
- return new Command30("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) => {
7796
+ return new Command34("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) => {
7406
7797
  await handleClean(options);
7407
7798
  });
7408
7799
  }
@@ -7413,7 +7804,7 @@ async function handleClean(options) {
7413
7804
  {
7414
7805
  name: "Backups",
7415
7806
  description: "Component backup files (.jai1_backup/)",
7416
- path: join4(cwd, ".jai1_backup"),
7807
+ path: join6(cwd, ".jai1_backup"),
7417
7808
  check: async () => {
7418
7809
  const backups = await service.listBackups(cwd);
7419
7810
  return { exists: backups.length > 0, count: backups.length };
@@ -7515,7 +7906,7 @@ async function cleanTarget(target, skipConfirm) {
7515
7906
  }
7516
7907
 
7517
7908
  // src/commands/redmine/check.ts
7518
- import { Command as Command31 } from "commander";
7909
+ import { Command as Command35 } from "commander";
7519
7910
 
7520
7911
  // src/services/redmine-config.service.ts
7521
7912
  import { readFile as readFile6 } from "fs/promises";
@@ -7822,7 +8213,7 @@ async function checkConnectivity(config) {
7822
8213
 
7823
8214
  // src/commands/redmine/check.ts
7824
8215
  function createRedmineCheckCommand() {
7825
- const cmd = new Command31("check").description("Check Redmine connectivity").option("-c, --config <path>", "Config file path", "redmine.config.yaml").option("--json", "Output as JSON").action(async (options) => {
8216
+ const cmd = new Command35("check").description("Check Redmine connectivity").option("-c, --config <path>", "Config file path", "redmine.config.yaml").option("--json", "Output as JSON").action(async (options) => {
7826
8217
  await handleRedmineCheck(options);
7827
8218
  });
7828
8219
  return cmd;
@@ -7850,7 +8241,7 @@ async function handleRedmineCheck(options) {
7850
8241
  }
7851
8242
 
7852
8243
  // src/commands/redmine/sync-issue.ts
7853
- import { Command as Command32 } from "commander";
8244
+ import { Command as Command36 } from "commander";
7854
8245
 
7855
8246
  // src/sync-issue.ts
7856
8247
  import { resolve as resolve3, relative } from "path";
@@ -8226,7 +8617,7 @@ function extractIssueIdFromUrl(url) {
8226
8617
 
8227
8618
  // src/commands/redmine/sync-issue.ts
8228
8619
  function createSyncIssueCommand() {
8229
- const cmd = new Command32("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) => {
8620
+ const cmd = new Command36("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) => {
8230
8621
  await handleSyncIssue(options);
8231
8622
  });
8232
8623
  return cmd;
@@ -8270,7 +8661,7 @@ async function handleSyncIssue(options) {
8270
8661
  }
8271
8662
 
8272
8663
  // src/commands/redmine/sync-project.ts
8273
- import { Command as Command33 } from "commander";
8664
+ import { Command as Command37 } from "commander";
8274
8665
 
8275
8666
  // src/sync-project.ts
8276
8667
  async function syncProject(config, options = {}) {
@@ -8340,7 +8731,7 @@ async function syncProject(config, options = {}) {
8340
8731
 
8341
8732
  // src/commands/redmine/sync-project.ts
8342
8733
  function createSyncProjectCommand() {
8343
- const cmd = new Command33("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) => {
8734
+ const cmd = new Command37("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) => {
8344
8735
  await handleSyncProject(options);
8345
8736
  });
8346
8737
  return cmd;
@@ -8395,12 +8786,12 @@ async function handleSyncProject(options) {
8395
8786
  }
8396
8787
 
8397
8788
  // src/commands/framework/info.ts
8398
- import { Command as Command34 } from "commander";
8399
- import { promises as fs9 } from "fs";
8400
- import { join as join5 } from "path";
8789
+ import { Command as Command38 } from "commander";
8790
+ import { promises as fs11 } from "fs";
8791
+ import { join as join7 } from "path";
8401
8792
  import { homedir as homedir5 } from "os";
8402
8793
  function createInfoCommand() {
8403
- const cmd = new Command34("info").description("Show jai1-client configuration and status").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
8794
+ const cmd = new Command38("info").description("Show jai1-client configuration and status").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
8404
8795
  await handleInfo(options);
8405
8796
  });
8406
8797
  return cmd;
@@ -8411,7 +8802,7 @@ async function handleInfo(options) {
8411
8802
  if (!config) {
8412
8803
  throw new ValidationError('Not initialized. Run "jai1 auth" first.');
8413
8804
  }
8414
- const frameworkPath = join5(homedir5(), ".jai1", "framework");
8805
+ const frameworkPath = join7(homedir5(), ".jai1", "framework");
8415
8806
  const projectStatus = await getProjectStatus2();
8416
8807
  const info = {
8417
8808
  configPath: configService.getConfigPath(),
@@ -8446,9 +8837,9 @@ function maskKey3(key) {
8446
8837
  return "****" + key.slice(-4);
8447
8838
  }
8448
8839
  async function getProjectStatus2() {
8449
- const projectJai1 = join5(process.cwd(), ".jai1");
8840
+ const projectJai1 = join7(process.cwd(), ".jai1");
8450
8841
  try {
8451
- await fs9.access(projectJai1);
8842
+ await fs11.access(projectJai1);
8452
8843
  return { exists: true, version: "Synced" };
8453
8844
  } catch {
8454
8845
  return { exists: false };
@@ -8456,7 +8847,7 @@ async function getProjectStatus2() {
8456
8847
  }
8457
8848
 
8458
8849
  // src/commands/self-update.ts
8459
- import { Command as Command35 } from "commander";
8850
+ import { Command as Command39 } from "commander";
8460
8851
  import { confirm as confirm6 } from "@inquirer/prompts";
8461
8852
  import { execSync as execSync2 } from "child_process";
8462
8853
  var colors3 = {
@@ -8468,7 +8859,7 @@ var colors3 = {
8468
8859
  bold: "\x1B[1m"
8469
8860
  };
8470
8861
  function createSelfUpdateCommand() {
8471
- return new Command35("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) => {
8862
+ return new Command39("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) => {
8472
8863
  await handleSelfUpdate(options);
8473
8864
  });
8474
8865
  }
@@ -8608,10 +8999,10 @@ function getInstallCommand2(packageManager2) {
8608
8999
  }
8609
9000
 
8610
9001
  // src/commands/clear-backups.ts
8611
- import { Command as Command36 } from "commander";
9002
+ import { Command as Command40 } from "commander";
8612
9003
  import { confirm as confirm7 } from "@inquirer/prompts";
8613
9004
  function createClearBackupsCommand() {
8614
- return new Command36("clear-backups").description("Clear backup files").option("-y, --yes", "Skip confirmation").action(async (options) => {
9005
+ return new Command40("clear-backups").description("Clear backup files").option("-y, --yes", "Skip confirmation").action(async (options) => {
8615
9006
  const service = new ComponentsService();
8616
9007
  const backups = await service.listBackups(process.cwd());
8617
9008
  if (backups.length === 0) {
@@ -8636,9 +9027,9 @@ function createClearBackupsCommand() {
8636
9027
  }
8637
9028
 
8638
9029
  // src/commands/vscode/index.ts
8639
- import { Command as Command37 } from "commander";
9030
+ import { Command as Command41 } from "commander";
8640
9031
  import { checkbox as checkbox3, confirm as confirm8, select as select3 } from "@inquirer/prompts";
8641
- import fs10 from "fs/promises";
9032
+ import fs12 from "fs/promises";
8642
9033
  import path6 from "path";
8643
9034
  import { existsSync as existsSync3 } from "fs";
8644
9035
  var PERFORMANCE_GROUPS2 = {
@@ -8776,7 +9167,7 @@ var PERFORMANCE_GROUPS2 = {
8776
9167
  }
8777
9168
  };
8778
9169
  function createVSCodeCommand() {
8779
- const vscodeCommand = new Command37("vscode").description("Qu\u1EA3n l\xFD c\xE0i \u0111\u1EB7t VSCode cho d\u1EF1 \xE1n hi\u1EC7n t\u1EA1i");
9170
+ const vscodeCommand = new Command41("vscode").description("Qu\u1EA3n l\xFD c\xE0i \u0111\u1EB7t VSCode cho d\u1EF1 \xE1n hi\u1EC7n t\u1EA1i");
8780
9171
  vscodeCommand.action(async () => {
8781
9172
  await interactiveMode2();
8782
9173
  });
@@ -8869,13 +9260,13 @@ async function applyGroups2(groupKeys, action) {
8869
9260
  return;
8870
9261
  }
8871
9262
  if (!existsSync3(vscodeDir)) {
8872
- await fs10.mkdir(vscodeDir, { recursive: true });
9263
+ await fs12.mkdir(vscodeDir, { recursive: true });
8873
9264
  console.log("\u{1F4C1} \u0110\xE3 t\u1EA1o th\u01B0 m\u1EE5c .vscode/");
8874
9265
  }
8875
9266
  let currentSettings = {};
8876
9267
  if (existsSync3(settingsPath)) {
8877
9268
  try {
8878
- const content = await fs10.readFile(settingsPath, "utf-8");
9269
+ const content = await fs12.readFile(settingsPath, "utf-8");
8879
9270
  currentSettings = JSON.parse(content);
8880
9271
  console.log("\u{1F4C4} \u0110\xE3 \u0111\u1ECDc c\xE0i \u0111\u1EB7t hi\u1EC7n t\u1EA1i t\u1EEB settings.json");
8881
9272
  } catch {
@@ -8915,7 +9306,7 @@ async function applyGroups2(groupKeys, action) {
8915
9306
  }
8916
9307
  }
8917
9308
  }
8918
- await fs10.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
9309
+ await fs12.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
8919
9310
  console.log(`
8920
9311
  \u2705 \u0110\xE3 c\u1EADp nh\u1EADt c\xE0i \u0111\u1EB7t VSCode t\u1EA1i: ${settingsPath}`);
8921
9312
  console.log("\u{1F4A1} M\u1EB9o: Kh\u1EDFi \u0111\u1ED9ng l\u1EA1i VSCode \u0111\u1EC3 \xE1p d\u1EE5ng c\xE1c thay \u0111\u1ED5i.");
@@ -8936,7 +9327,7 @@ async function resetSettings2(groupKeys) {
8936
9327
  return;
8937
9328
  }
8938
9329
  if (groupKeys.length === 0) {
8939
- await fs10.unlink(settingsPath);
9330
+ await fs12.unlink(settingsPath);
8940
9331
  console.log("\n\u2705 \u0110\xE3 x\xF3a file settings.json");
8941
9332
  } else {
8942
9333
  await applyGroups2(groupKeys, "disable");
@@ -8947,9 +9338,9 @@ async function resetSettings2(groupKeys) {
8947
9338
  // src/commands/guide.ts
8948
9339
  import React40 from "react";
8949
9340
  import { render as render6 } from "ink";
8950
- import { Command as Command38 } from "commander";
9341
+ import { Command as Command42 } from "commander";
8951
9342
  function createGuideCommand() {
8952
- const cmd = new Command38("guide").description("Interactive guide center for Agentic Coding").option("--topic <topic>", "Open specific topic (intro, rules, workflows, prompts, skills)").action(async (options) => {
9343
+ const cmd = new Command42("guide").description("Interactive guide center for Agentic Coding").option("--topic <topic>", "Open specific topic (intro, rules, workflows, prompts, skills)").action(async (options) => {
8953
9344
  const { waitUntilExit } = render6(
8954
9345
  React40.createElement(GuideApp, {
8955
9346
  initialTopic: options.topic,
@@ -8966,9 +9357,9 @@ function createGuideCommand() {
8966
9357
  // src/commands/context.ts
8967
9358
  import React41 from "react";
8968
9359
  import { render as render7 } from "ink";
8969
- import { Command as Command39 } from "commander";
9360
+ import { Command as Command43 } from "commander";
8970
9361
  function createContextCommand() {
8971
- const cmd = new Command39("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) => {
9362
+ const cmd = new Command43("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) => {
8972
9363
  let initialIDE;
8973
9364
  if (options.ide) {
8974
9365
  const validIDEs = ["cursor", "windsurf", "antigravity", "jai1"];
@@ -9045,10 +9436,10 @@ async function printStats2() {
9045
9436
  }
9046
9437
 
9047
9438
  // src/commands/migrate-ide.ts
9048
- import { Command as Command40 } from "commander";
9439
+ import { Command as Command44 } from "commander";
9049
9440
  import { checkbox as checkbox4, confirm as confirm9 } from "@inquirer/prompts";
9050
9441
  function createMigrateIdeCommand() {
9051
- const cmd = new Command40("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) => {
9442
+ const cmd = new Command44("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) => {
9052
9443
  await runMigrateIde(options);
9053
9444
  });
9054
9445
  return cmd;
@@ -9154,7 +9545,7 @@ async function runMigrateIde(options) {
9154
9545
  }
9155
9546
 
9156
9547
  // src/cli.ts
9157
- var program = new Command41();
9548
+ var program = new Command45();
9158
9549
  if (process.argv.includes("-v") || process.argv.includes("--version")) {
9159
9550
  console.log(package_default.version);
9160
9551
  if (!process.argv.includes("--skip-update-check")) {
@@ -9172,15 +9563,16 @@ program.addCommand(createCheckCommand());
9172
9563
  program.addCommand(createIdeCommand());
9173
9564
  program.addCommand(createLearnCommand());
9174
9565
  program.addCommand(createChatCommand());
9175
- program.addCommand(createApiKeysCommand());
9566
+ program.addCommand(createOpenAiKeysCommand());
9176
9567
  program.addCommand(createStatsCommand());
9177
9568
  program.addCommand(createTranslateCommand());
9178
9569
  program.addCommand(createUtilsCommand());
9570
+ program.addCommand(createKitCommand());
9179
9571
  program.addCommand(createUpgradeCommand());
9180
9572
  program.addCommand(createCleanCommand());
9181
- var redmineCommand = new Command41("redmine").description("Redmine context sync commands");
9573
+ var redmineCommand = new Command45("redmine").description("Redmine context sync commands");
9182
9574
  redmineCommand.addCommand(createRedmineCheckCommand());
9183
- var syncCommand = new Command41("sync").description("Sync Redmine issues to markdown files");
9575
+ var syncCommand = new Command45("sync").description("Sync Redmine issues to markdown files");
9184
9576
  syncCommand.addCommand(createSyncIssueCommand());
9185
9577
  syncCommand.addCommand(createSyncProjectCommand());
9186
9578
  redmineCommand.addCommand(syncCommand);
@@ -9215,7 +9607,7 @@ program.on("command:*", (operands) => {
9215
9607
  console.error("");
9216
9608
  console.error(" \u{1F916} LLM Proxy");
9217
9609
  console.error(" chat Interactive AI chat");
9218
- console.error(" api-keys Show OpenAI-compatible API credentials");
9610
+ console.error(" openai-keys Show OpenAI-compatible API credentials");
9219
9611
  console.error(" stats Show LLM usage statistics and limits");
9220
9612
  console.error(" translate Translate text, files, or folders using AI");
9221
9613
  console.error("");