@jvittechs/jai1-cli 0.1.104 → 0.1.106

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +1201 -552
  2. package/dist/cli.js.map +1 -1
  3. package/package.json +2 -1
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { Command as Command60 } from "commander";
4
+ import { Command as Command61 } 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.104",
36
+ version: "0.1.106",
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: {
@@ -101,6 +101,7 @@ var package_default = {
101
101
  marked: "^12.0.0",
102
102
  "marked-terminal": "^7.0.0",
103
103
  open: "^10.1.0",
104
+ ora: "^9.0.0",
104
105
  "p-limit": "^5.0.0",
105
106
  "p-queue": "^7.4.1",
106
107
  "p-retry": "^6.2.0",
@@ -3507,9 +3508,9 @@ var IdeDetectionService = class {
3507
3508
  /**
3508
3509
  * Check if a path exists
3509
3510
  */
3510
- async pathExists(path10) {
3511
+ async pathExists(path13) {
3511
3512
  try {
3512
- await fs7.access(path10);
3513
+ await fs7.access(path13);
3513
3514
  return true;
3514
3515
  } catch {
3515
3516
  return false;
@@ -4401,6 +4402,27 @@ AI: \u0110\u1ECDc rules \u2192 Bi\u1EBFt d\xF9ng HonoJS, c\u1EA5u tr\xFAc th\u01
4401
4402
 
4402
4403
  > \u{1F4A1} **Tip:** Context l\xE0 ch\xECa kh\xF3a \u0111\u1EC3 AI l\xE0m vi\u1EC7c hi\u1EC7u qu\u1EA3!
4403
4404
 
4405
+ ## Y\xEAu c\u1EA7u m\xF4i tr\u01B0\u1EDDng
4406
+
4407
+ ### Windows
4408
+
4409
+ \u26A0\uFE0F **B\u1EAFt bu\u1ED9c:** C\xE0i \u0111\u1EB7t **Git for Windows** (bao g\u1ED3m Git Bash)
4410
+
4411
+ Workflows c\u1EE7a Jai1 s\u1EED d\u1EE5ng c\xE1c Unix commands (\`grep\`, \`wc\`, \`xargs\`...) kh\xF4ng c\xF3 s\u1EB5n tr\xEAn PowerShell/CMD.
4412
+
4413
+ 1. Download: https://git-scm.com/download/win
4414
+ 2. Khi c\xE0i \u0111\u1EB7t, ch\u1ECDn **"Git Bash Here"** context menu
4415
+ 3. Trong VSCode, c\u1EA5u h\xECnh terminal m\u1EB7c \u0111\u1ECBnh:
4416
+ \`\`\`json
4417
+ {
4418
+ "terminal.integrated.defaultProfile.windows": "Git Bash"
4419
+ }
4420
+ \`\`\`
4421
+
4422
+ ### macOS / Linux
4423
+
4424
+ \u2705 S\u1EB5n s\xE0ng - C\xE1c Unix commands \u0111\xE3 c\xF3 s\u1EB5n trong terminal.
4425
+
4404
4426
  ## B\u1EAFt \u0111\u1EA7u nhanh
4405
4427
 
4406
4428
  1. **Kh\u1EDFi t\u1EA1o Jai1** trong d\u1EF1 \xE1n:
@@ -4598,6 +4620,52 @@ V\xED d\u1EE5 minh h\u1ECDa (n\u1EBFu c\u1EA7n)
4598
4620
  - G\u1ECDi khi c\u1EA7n b\u1EB1ng \`@ruleName\`
4599
4621
  - V\xED d\u1EE5: \`@database-migration\`, \`@api-design\`
4600
4622
 
4623
+ ## Qu\u1EA3n l\xFD Rules v\u1EDBi CLI
4624
+
4625
+ ### Apply Rules Preset
4626
+
4627
+ \`\`\`bash
4628
+ # Apply preset c\xF3 s\u1EB5n
4629
+ jai1 rules apply nextjs-saas
4630
+
4631
+ # Apply cho nhi\u1EC1u IDE c\xF9ng l\xFAc
4632
+ jai1 rules apply nextjs-saas --ides cursor,windsurf,agentsmd
4633
+ \`\`\`
4634
+
4635
+ ### Sync Rules (NEW!)
4636
+
4637
+ Sau khi apply rules, b\u1EA1n c\xF3 th\u1EC3 sync sang IDE kh\xE1c ho\u1EB7c regenerate:
4638
+
4639
+ \`\`\`bash
4640
+ # Interactive mode - ch\u1ECDn IDE t\u1EEB menu
4641
+ jai1 rules sync
4642
+
4643
+ # Auto-detect active IDEs
4644
+ jai1 rules sync --detect
4645
+
4646
+ # Sync specific IDEs
4647
+ jai1 rules sync --ides cursor,windsurf
4648
+
4649
+ # Auto mode (no prompts)
4650
+ jai1 rules sync -y
4651
+ \`\`\`
4652
+
4653
+ **Interactive Mode Flow:**
4654
+ 1. Hi\u1EC3n th\u1ECB current IDE configuration
4655
+ 2. Smart suggestions d\u1EF1a tr\xEAn project structure
4656
+ 3. Checkbox \u0111\u1EC3 ch\u1ECDn IDE (pre-selected current + suggested)
4657
+ 4. Regenerate rules cho c\xE1c IDE \u0111\xE3 ch\u1ECDn
4658
+
4659
+ ### Restore Rules
4660
+
4661
+ \`\`\`bash
4662
+ # List available backups
4663
+ jai1 rules restore --list
4664
+
4665
+ # Restore from backup
4666
+ jai1 rules restore
4667
+ \`\`\`
4668
+
4601
4669
  ## Best Practices
4602
4670
 
4603
4671
  ### \u2705 N\xCAN l\xE0m:
@@ -4606,6 +4674,8 @@ V\xED d\u1EE5 minh h\u1ECDa (n\u1EBFu c\u1EA7n)
4606
4674
  - Focus v\xE0o "what to do", kh\xF4ng ph\u1EA3i "how to do"
4607
4675
  - C\u1EADp nh\u1EADt rules khi d\u1EF1 \xE1n thay \u0111\u1ED5i
4608
4676
  - Chia nh\u1ECF rules theo domain/feature
4677
+ - Edit rules trong \`.jai1/rule-preset/\` (source of truth)
4678
+ - Ch\u1EA1y \`jai1 rules sync\` sau khi edit \u0111\u1EC3 regenerate
4609
4679
 
4610
4680
  ### \u274C KH\xD4NG N\xCAN:
4611
4681
 
@@ -4613,8 +4683,9 @@ V\xED d\u1EE5 minh h\u1ECDa (n\u1EBFu c\u1EA7n)
4613
4683
  - Copy-paste to\xE0n b\u1ED9 documentation
4614
4684
  - Include code examples qu\xE1 chi ti\u1EBFt
4615
4685
  - Hardcode paths ho\u1EB7c values
4686
+ - Edit tr\u1EF1c ti\u1EBFp trong \`.cursor/rules/\` (s\u1EBD b\u1ECB overwrite khi sync)
4616
4687
 
4617
- > \u{1F4A1} **Tip:** D\xF9ng \`jai1 apply rules/...\` \u0111\u1EC3 c\xE0i \u0111\u1EB7t rules m\u1EABu cho tech stack c\u1EE7a b\u1EA1n!
4688
+ > \u{1F4A1} **Tip:** D\xF9ng \`jai1 rules apply\` \u0111\u1EC3 c\xE0i \u0111\u1EB7t rules m\u1EABu cho tech stack c\u1EE7a b\u1EA1n!
4618
4689
  `,
4619
4690
  "skills": `---
4620
4691
  title: Skills
@@ -4743,7 +4814,7 @@ D\xF9ng workflow \`skill-creator\`:
4743
4814
  "workflows": `---
4744
4815
  title: Workflows
4745
4816
  icon: \u{1F504}
4746
- description: Quy tr\xECnh l\xE0m vi\u1EC7c step-by-step (Cursor: Commands)
4817
+ description: "Quy tr\xECnh l\xE0m vi\u1EC7c step-by-step (Cursor: Commands)"
4747
4818
  ---
4748
4819
 
4749
4820
  # Workflows - Quy tr\xECnh l\xE0m vi\u1EC7c
@@ -10293,27 +10364,147 @@ function createUtilsCommand() {
10293
10364
  }
10294
10365
 
10295
10366
  // src/commands/deps/index.ts
10296
- import { Command as Command37 } from "commander";
10297
- import chalk11 from "chalk";
10367
+ import { Command as Command38 } from "commander";
10368
+ import chalk13 from "chalk";
10298
10369
 
10299
- // src/commands/deps/upgrade.ts
10370
+ // src/commands/deps/check.ts
10300
10371
  import { Command as Command36 } from "commander";
10301
- import { checkbox as checkbox3, confirm as confirm6 } from "@inquirer/prompts";
10372
+ import chalk11 from "chalk";
10373
+ import Table3 from "cli-table3";
10374
+ import ora from "ora";
10375
+
10376
+ // src/services/deps-detector.service.ts
10377
+ import * as fs13 from "fs/promises";
10378
+ import * as path8 from "path";
10379
+ var DepsDetectorService = class {
10380
+ /**
10381
+ * Detect all project types in the given directory
10382
+ */
10383
+ async detectProjects(cwd) {
10384
+ const projects = [];
10385
+ if (await this.fileExists(cwd, "package.json")) {
10386
+ const manager = await this.detectNodePackageManager(cwd);
10387
+ projects.push({
10388
+ ecosystem: "node",
10389
+ manager,
10390
+ configFile: "package.json"
10391
+ });
10392
+ }
10393
+ if (await this.fileExists(cwd, "composer.json") || await this.fileExists(cwd, "composer.lock")) {
10394
+ const isLaravel = await this.detectLaravel(cwd);
10395
+ projects.push({
10396
+ ecosystem: "php",
10397
+ manager: "composer",
10398
+ isLaravel,
10399
+ configFile: "composer.json"
10400
+ });
10401
+ }
10402
+ if (await this.fileExists(cwd, "Pipfile")) {
10403
+ projects.push({
10404
+ ecosystem: "python",
10405
+ manager: "pipenv",
10406
+ configFile: "Pipfile"
10407
+ });
10408
+ } else if (await this.fileExists(cwd, "requirements.txt")) {
10409
+ projects.push({
10410
+ ecosystem: "python",
10411
+ manager: "pip",
10412
+ configFile: "requirements.txt"
10413
+ });
10414
+ }
10415
+ return projects;
10416
+ }
10417
+ /**
10418
+ * Detect Node.js package manager
10419
+ */
10420
+ async detectNodePackageManager(cwd) {
10421
+ if (await this.fileExists(cwd, "pnpm-lock.yaml")) {
10422
+ return "pnpm";
10423
+ }
10424
+ if (await this.fileExists(cwd, "yarn.lock")) {
10425
+ return "yarn";
10426
+ }
10427
+ if (await this.fileExists(cwd, "bun.lockb")) {
10428
+ return "bun";
10429
+ }
10430
+ if (await this.fileExists(cwd, "package-lock.json")) {
10431
+ return "npm";
10432
+ }
10433
+ const userAgent = process.env.npm_config_user_agent || "";
10434
+ if (userAgent.includes("pnpm")) return "pnpm";
10435
+ if (userAgent.includes("yarn")) return "yarn";
10436
+ if (userAgent.includes("bun")) return "bun";
10437
+ return "npm";
10438
+ }
10439
+ /**
10440
+ * Detect if project is Laravel
10441
+ */
10442
+ async detectLaravel(cwd) {
10443
+ try {
10444
+ const composerPath = path8.join(cwd, "composer.json");
10445
+ const content = await fs13.readFile(composerPath, "utf-8");
10446
+ const composerJson = JSON.parse(content);
10447
+ const deps = {
10448
+ ...composerJson.require,
10449
+ ...composerJson["require-dev"]
10450
+ };
10451
+ return "laravel/framework" in deps;
10452
+ } catch {
10453
+ return false;
10454
+ }
10455
+ }
10456
+ /**
10457
+ * Check if file exists
10458
+ */
10459
+ async fileExists(cwd, filename) {
10460
+ try {
10461
+ await fs13.access(path8.join(cwd, filename));
10462
+ return true;
10463
+ } catch {
10464
+ return false;
10465
+ }
10466
+ }
10467
+ };
10302
10468
 
10303
10469
  // src/services/deps.service.ts
10304
- import { promises as fs13 } from "fs";
10305
- import path8 from "path";
10470
+ import { promises as fs14 } from "fs";
10471
+ import path9 from "path";
10306
10472
  import { execSync } from "child_process";
10307
10473
  import pLimit2 from "p-limit";
10308
10474
  var DepsService = class {
10475
+ ecosystem = "node";
10309
10476
  versionCache = /* @__PURE__ */ new Map();
10310
- /**
10311
- * Đọc package.json từ thư mục
10312
- */
10477
+ async check(cwd, onProgress) {
10478
+ const pkgJson = await this.readPackageJson(cwd);
10479
+ const manager = await this.detectPackageManager(cwd);
10480
+ const packages = [];
10481
+ if (pkgJson.dependencies) {
10482
+ for (const [name, version] of Object.entries(pkgJson.dependencies)) {
10483
+ if (this.isValidNpmDependency(version)) {
10484
+ packages.push({ name, current: version, type: "dep" });
10485
+ }
10486
+ }
10487
+ }
10488
+ if (pkgJson.devDependencies) {
10489
+ for (const [name, version] of Object.entries(pkgJson.devDependencies)) {
10490
+ if (this.isValidNpmDependency(version)) {
10491
+ packages.push({ name, current: version, type: "dev" });
10492
+ }
10493
+ }
10494
+ }
10495
+ const upgradablePackages = await this.fetchBulkVersions(packages, onProgress);
10496
+ return { ecosystem: this.ecosystem, manager, packages: upgradablePackages };
10497
+ }
10498
+ async upgrade(cwd, options) {
10499
+ const manager = await this.detectPackageManager(cwd);
10500
+ const commands = this.buildUpgradeCommands(options.packages, manager);
10501
+ if (commands.deps) this.executeUpgrade(commands.deps);
10502
+ if (commands.devDeps) this.executeUpgrade(commands.devDeps);
10503
+ }
10313
10504
  async readPackageJson(cwd) {
10314
- const pkgPath = path8.join(cwd, "package.json");
10505
+ const pkgPath = path9.join(cwd, "package.json");
10315
10506
  try {
10316
- const content = await fs13.readFile(pkgPath, "utf-8");
10507
+ const content = await fs14.readFile(pkgPath, "utf-8");
10317
10508
  return JSON.parse(content);
10318
10509
  } catch (error) {
10319
10510
  if (error.code === "ENOENT") {
@@ -10322,9 +10513,6 @@ var DepsService = class {
10322
10513
  throw new Error(`L\u1ED7i \u0111\u1ECDc package.json: ${error.message}`);
10323
10514
  }
10324
10515
  }
10325
- /**
10326
- * Lấy version mới nhất từ npm registry
10327
- */
10328
10516
  async fetchLatestVersion(packageName) {
10329
10517
  if (this.versionCache.has(packageName)) {
10330
10518
  return this.versionCache.get(packageName);
@@ -10334,9 +10522,7 @@ var DepsService = class {
10334
10522
  `https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`,
10335
10523
  { signal: AbortSignal.timeout(1e4) }
10336
10524
  );
10337
- if (!response.ok) {
10338
- return null;
10339
- }
10525
+ if (!response.ok) return null;
10340
10526
  const data = await response.json();
10341
10527
  this.versionCache.set(packageName, data.version);
10342
10528
  return data.version;
@@ -10344,54 +10530,51 @@ var DepsService = class {
10344
10530
  return null;
10345
10531
  }
10346
10532
  }
10347
- /**
10348
- * Fetch nhiều packages cùng lúc với giới hạn concurrent
10349
- */
10350
10533
  async fetchBulkVersions(packages, onProgress) {
10351
10534
  const limit = pLimit2(10);
10352
10535
  const total = packages.length;
10353
10536
  let completed = 0;
10354
10537
  const results = await Promise.all(
10355
- packages.map(
10356
- (pkg) => limit(async () => {
10357
- const latest = await this.fetchLatestVersion(pkg.name);
10358
- completed++;
10359
- onProgress?.(completed, total);
10360
- if (!latest) {
10361
- return null;
10362
- }
10363
- const currentClean = this.stripSemverPrefix(pkg.current);
10364
- const canUpgrade = this.isNewerVersion(latest, currentClean);
10365
- return {
10366
- name: pkg.name,
10367
- current: pkg.current,
10368
- latest,
10369
- latestWithPrefix: this.preserveSemverPrefix(pkg.current, latest),
10370
- type: pkg.type,
10371
- canUpgrade
10372
- };
10373
- })
10374
- )
10538
+ packages.map((pkg) => limit(async () => {
10539
+ const latest = await this.fetchLatestVersion(pkg.name);
10540
+ completed++;
10541
+ onProgress?.(completed, total);
10542
+ if (!latest) return null;
10543
+ const currentClean = this.stripSemverPrefix(pkg.current);
10544
+ const canUpgrade = this.isNewerVersion(latest, currentClean);
10545
+ const upgradeType = this.getUpgradeType(currentClean, latest);
10546
+ return {
10547
+ name: pkg.name,
10548
+ current: pkg.current,
10549
+ latest,
10550
+ latestWithPrefix: this.preserveSemverPrefix(pkg.current, latest),
10551
+ type: pkg.type,
10552
+ canUpgrade,
10553
+ upgradeType
10554
+ };
10555
+ }))
10375
10556
  );
10376
10557
  return results.filter((r) => r !== null && r.canUpgrade);
10377
10558
  }
10378
- /**
10379
- * Loại bỏ semver prefix (^, ~, >=, etc.)
10380
- */
10559
+ isValidNpmDependency(version) {
10560
+ const invalidPrefixes = ["git+", "git:", "file:", "link:", "workspace:"];
10561
+ return !invalidPrefixes.some((prefix) => version.startsWith(prefix));
10562
+ }
10563
+ getUpgradeType(current, latest) {
10564
+ const currentParts = current.split(".").map(Number);
10565
+ const latestParts = latest.split(".").map(Number);
10566
+ if ((latestParts[0] || 0) > (currentParts[0] || 0)) return "major";
10567
+ if ((latestParts[1] || 0) > (currentParts[1] || 0)) return "minor";
10568
+ return "patch";
10569
+ }
10381
10570
  stripSemverPrefix(version) {
10382
10571
  return version.replace(/^[~^>=<]+/, "");
10383
10572
  }
10384
- /**
10385
- * Giữ nguyên semver prefix khi upgrade
10386
- */
10387
10573
  preserveSemverPrefix(current, latest) {
10388
10574
  const match = current.match(/^([~^>=<]*)/);
10389
10575
  const prefix = match?.[1] || "";
10390
10576
  return prefix + latest;
10391
10577
  }
10392
- /**
10393
- * So sánh version để xác định có cần upgrade không
10394
- */
10395
10578
  isNewerVersion(remote, local) {
10396
10579
  const remoteParts = remote.split(".").map(Number);
10397
10580
  const localParts = local.split(".").map(Number);
@@ -10403,13 +10586,6 @@ var DepsService = class {
10403
10586
  }
10404
10587
  return false;
10405
10588
  }
10406
- /**
10407
- * Detect package manager từ lock files
10408
- * Priority:
10409
- * 1. Lock file trong project
10410
- * 2. pnpm nếu có cài đặt trong máy
10411
- * 3. npm (fallback)
10412
- */
10413
10589
  async detectPackageManager(cwd) {
10414
10590
  const lockFiles = [
10415
10591
  { file: "pnpm-lock.yaml", pm: "pnpm" },
@@ -10419,7 +10595,7 @@ var DepsService = class {
10419
10595
  ];
10420
10596
  for (const { file, pm } of lockFiles) {
10421
10597
  try {
10422
- await fs13.access(path8.join(cwd, file));
10598
+ await fs14.access(path9.join(cwd, file));
10423
10599
  return pm;
10424
10600
  } catch {
10425
10601
  }
@@ -10428,27 +10604,22 @@ var DepsService = class {
10428
10604
  if (userAgent.includes("pnpm")) return "pnpm";
10429
10605
  if (userAgent.includes("yarn")) return "yarn";
10430
10606
  if (userAgent.includes("bun")) return "bun";
10431
- if (userAgent.includes("npm")) return "npm";
10432
10607
  if (this.isCommandAvailable("pnpm")) return "pnpm";
10433
10608
  return "npm";
10434
10609
  }
10435
- /**
10436
- * Kiểm tra command có sẵn trong hệ thống không
10437
- */
10438
10610
  isCommandAvailable(command) {
10439
10611
  try {
10440
- execSync(`${command} --version`, {
10441
- stdio: ["pipe", "pipe", "pipe"]
10442
- });
10612
+ execSync(`${command} --version`, { stdio: ["pipe", "pipe", "pipe"] });
10443
10613
  return true;
10444
10614
  } catch {
10445
10615
  return false;
10446
10616
  }
10447
10617
  }
10448
- /**
10449
- * Tạo command để upgrade packages
10450
- */
10451
- getUpgradeCommands(packages, pm) {
10618
+ getUpgradeCommands(packages) {
10619
+ const pm = process.env.npm_config_user_agent?.includes("pnpm") ? "pnpm" : "npm";
10620
+ return this.buildUpgradeCommands(packages, pm);
10621
+ }
10622
+ buildUpgradeCommands(packages, pm) {
10452
10623
  const deps = packages.filter((p) => p.type === "dep");
10453
10624
  const devDeps = packages.filter((p) => p.type === "dev");
10454
10625
  const formatPackages = (pkgs) => pkgs.map((p) => `${p.name}@${p.latestWithPrefix}`).join(" ");
@@ -10488,240 +10659,654 @@ var DepsService = class {
10488
10659
  }
10489
10660
  return { deps: depsCmd, devDeps: devDepsCmd };
10490
10661
  }
10491
- /**
10492
- * Thực thi upgrade command
10493
- */
10494
10662
  executeUpgrade(command) {
10495
- execSync(command, {
10496
- stdio: "inherit",
10497
- env: { ...process.env, FORCE_COLOR: "1" }
10498
- });
10663
+ execSync(command, { stdio: "inherit", env: { ...process.env, FORCE_COLOR: "1" } });
10499
10664
  }
10500
10665
  };
10501
10666
 
10502
- // src/commands/deps/upgrade.ts
10503
- var colors3 = {
10504
- yellow: "\x1B[33m",
10505
- green: "\x1B[32m",
10506
- cyan: "\x1B[36m",
10507
- red: "\x1B[31m",
10508
- gray: "\x1B[90m",
10509
- reset: "\x1B[0m",
10510
- bold: "\x1B[1m",
10511
- dim: "\x1B[2m"
10512
- };
10513
- function createDepsUpgradeCommand() {
10514
- return new Command36("upgrade").description("Upgrade npm packages l\xEAn version m\u1EDBi nh\u1EA5t").option("--check", "Ch\u1EC9 ki\u1EC3m tra, kh\xF4ng upgrade").option("--all", "Upgrade t\u1EA5t c\u1EA3 kh\xF4ng c\u1EA7n ch\u1ECDn").option("--deps-only", "Ch\u1EC9 upgrade dependencies").option("--dev-only", "Ch\u1EC9 upgrade devDependencies").action(async (options) => {
10515
- await handleDepsUpgrade(options);
10516
- });
10517
- }
10518
- async function handleDepsUpgrade(options) {
10519
- const depsService = new DepsService();
10520
- const cwd = process.cwd();
10521
- try {
10522
- console.log(`
10523
- ${colors3.cyan}\u{1F50D} \u0110ang \u0111\u1ECDc package.json...${colors3.reset}`);
10524
- const pkg = await depsService.readPackageJson(cwd);
10525
- const packagesToCheck = [];
10526
- if (!options.devOnly && pkg.dependencies) {
10527
- for (const [name, version] of Object.entries(pkg.dependencies)) {
10528
- if (isNpmVersion(version)) {
10529
- packagesToCheck.push({ name, current: version, type: "dep" });
10530
- }
10531
- }
10667
+ // src/services/deps-php.service.ts
10668
+ import { execSync as execSync2 } from "child_process";
10669
+ import { promises as fs15 } from "fs";
10670
+ import path10 from "path";
10671
+ var LARAVEL_PROTECTED_PACKAGES = /^laravel\//;
10672
+ var DepsPhpService = class {
10673
+ ecosystem = "php";
10674
+ isLaravel = false;
10675
+ async check(cwd, onProgress) {
10676
+ this.isLaravel = await this.detectLaravel(cwd);
10677
+ const outdated = await this.fetchOutdatedPackages(cwd);
10678
+ const composerJson = await this.readComposerJson(cwd);
10679
+ const packages = [];
10680
+ let processed = 0;
10681
+ for (const pkg of outdated) {
10682
+ const isDev = pkg.name in (composerJson["require-dev"] || {});
10683
+ const upgradeType = this.getUpgradeType(pkg.version, pkg.latest);
10684
+ const canUpgrade = this.canUpgradePackage(pkg.name, upgradeType);
10685
+ const blockedReason = !canUpgrade ? "Laravel major version blocked" : void 0;
10686
+ packages.push({
10687
+ name: pkg.name,
10688
+ current: pkg.version,
10689
+ latest: pkg.latest,
10690
+ latestWithPrefix: pkg.latest,
10691
+ type: isDev ? "dev" : "dep",
10692
+ upgradeType,
10693
+ canUpgrade,
10694
+ blockedReason
10695
+ });
10696
+ processed++;
10697
+ onProgress?.(processed, outdated.length);
10532
10698
  }
10533
- if (!options.depsOnly && pkg.devDependencies) {
10534
- for (const [name, version] of Object.entries(pkg.devDependencies)) {
10535
- if (isNpmVersion(version)) {
10536
- packagesToCheck.push({ name, current: version, type: "dev" });
10537
- }
10699
+ return {
10700
+ ecosystem: this.ecosystem,
10701
+ manager: "composer",
10702
+ packages: packages.filter((p) => p.canUpgrade),
10703
+ isLaravel: this.isLaravel
10704
+ };
10705
+ }
10706
+ async upgrade(cwd, options) {
10707
+ const commands = this.getUpgradeCommands(options.packages);
10708
+ if (commands.deps) this.executeUpgrade(commands.deps, cwd);
10709
+ if (commands.devDeps) this.executeUpgrade(commands.devDeps, cwd);
10710
+ }
10711
+ getUpgradeCommands(packages) {
10712
+ const deps = packages.filter((p) => p.type === "dep");
10713
+ const devDeps = packages.filter((p) => p.type === "dev");
10714
+ let depsCmd = null;
10715
+ let devDepsCmd = null;
10716
+ if (deps.length > 0) {
10717
+ const pkgList = deps.map((p) => `${p.name}:${p.latest}`).join(" ");
10718
+ depsCmd = `composer require -W ${pkgList}`;
10719
+ }
10720
+ if (devDeps.length > 0) {
10721
+ const pkgList = devDeps.map((p) => `${p.name}:${p.latest}`).join(" ");
10722
+ devDepsCmd = `composer require --dev -W ${pkgList}`;
10723
+ }
10724
+ return { deps: depsCmd, devDeps: devDepsCmd };
10725
+ }
10726
+ async readComposerJson(cwd) {
10727
+ const composerPath = path10.join(cwd, "composer.json");
10728
+ try {
10729
+ const content = await fs15.readFile(composerPath, "utf-8");
10730
+ return JSON.parse(content);
10731
+ } catch (error) {
10732
+ if (error.code === "ENOENT") {
10733
+ throw new Error("Kh\xF4ng t\xECm th\u1EA5y composer.json trong th\u01B0 m\u1EE5c hi\u1EC7n t\u1EA1i");
10538
10734
  }
10735
+ throw new Error(`L\u1ED7i \u0111\u1ECDc composer.json: ${error.message}`);
10539
10736
  }
10540
- if (packagesToCheck.length === 0) {
10541
- console.log(`${colors3.yellow}\u26A0\uFE0F Kh\xF4ng t\xECm th\u1EA5y packages \u0111\u1EC3 ki\u1EC3m tra${colors3.reset}
10542
- `);
10543
- return;
10737
+ }
10738
+ async detectLaravel(cwd) {
10739
+ try {
10740
+ const composerJson = await this.readComposerJson(cwd);
10741
+ const deps = { ...composerJson.require, ...composerJson["require-dev"] };
10742
+ return "laravel/framework" in deps;
10743
+ } catch {
10744
+ return false;
10544
10745
  }
10545
- const depsCount = packagesToCheck.filter((p) => p.type === "dep").length;
10546
- const devCount = packagesToCheck.filter((p) => p.type === "dev").length;
10547
- console.log(
10548
- `${colors3.bold}\u{1F4E6} T\xECm th\u1EA5y ${depsCount} dependencies v\xE0 ${devCount} devDependencies${colors3.reset}
10549
- `
10550
- );
10551
- console.log(`${colors3.cyan}\u23F3 Ki\u1EC3m tra phi\xEAn b\u1EA3n m\u1EDBi nh\u1EA5t...${colors3.reset}`);
10552
- const upgradablePackages = await depsService.fetchBulkVersions(
10553
- packagesToCheck,
10554
- (completed, total) => {
10555
- const progress = Math.round(completed / total * 100);
10556
- const bar = "\u2588".repeat(Math.floor(progress / 5)) + "\u2591".repeat(20 - Math.floor(progress / 5));
10557
- process.stdout.write(`\r[${bar}] ${completed}/${total} packages`);
10746
+ }
10747
+ async fetchOutdatedPackages(cwd) {
10748
+ try {
10749
+ const output = execSync2("composer outdated --format=json --direct", {
10750
+ cwd,
10751
+ encoding: "utf-8",
10752
+ stdio: ["pipe", "pipe", "pipe"]
10753
+ });
10754
+ const result = JSON.parse(output);
10755
+ return result.installed || [];
10756
+ } catch (error) {
10757
+ if (error.stdout) {
10758
+ try {
10759
+ const result = JSON.parse(error.stdout);
10760
+ return result.installed || [];
10761
+ } catch {
10762
+ }
10558
10763
  }
10559
- );
10560
- console.log("\n");
10561
- if (upgradablePackages.length === 0) {
10562
- console.log(`${colors3.green}\u2705 T\u1EA5t c\u1EA3 packages \u0111\xE3 l\xE0 phi\xEAn b\u1EA3n m\u1EDBi nh\u1EA5t!${colors3.reset}
10563
- `);
10564
- return;
10764
+ throw new Error(`L\u1ED7i ki\u1EC3m tra composer packages: ${error.message}`);
10565
10765
  }
10566
- displayUpgradeTable(upgradablePackages);
10567
- if (options.check) {
10568
- console.log(
10569
- `${colors3.cyan}\u{1F4A1} Ch\u1EA1y "jai1 deps upgrade" \u0111\u1EC3 ti\u1EBFn h\xE0nh upgrade.${colors3.reset}
10570
- `
10571
- );
10572
- return;
10766
+ }
10767
+ getUpgradeType(current, latest) {
10768
+ const currentParts = current.split(".").map(Number);
10769
+ const latestParts = latest.split(".").map(Number);
10770
+ if ((latestParts[0] || 0) > (currentParts[0] || 0)) return "major";
10771
+ if ((latestParts[1] || 0) > (currentParts[1] || 0)) return "minor";
10772
+ return "patch";
10773
+ }
10774
+ canUpgradePackage(packageName, upgradeType) {
10775
+ if (this.isLaravel && LARAVEL_PROTECTED_PACKAGES.test(packageName) && upgradeType === "major") {
10776
+ return false;
10573
10777
  }
10574
- let selectedPackages;
10575
- if (options.all) {
10576
- selectedPackages = upgradablePackages;
10577
- console.log(`${colors3.cyan}\u{1F4CB} \u0110\xE3 ch\u1ECDn t\u1EA5t c\u1EA3 ${selectedPackages.length} packages${colors3.reset}
10578
- `);
10778
+ return true;
10779
+ }
10780
+ executeUpgrade(command, cwd) {
10781
+ execSync2(command, {
10782
+ cwd,
10783
+ stdio: "inherit",
10784
+ env: { ...process.env, FORCE_COLOR: "1" }
10785
+ });
10786
+ }
10787
+ };
10788
+
10789
+ // src/services/deps-python.service.ts
10790
+ import { execSync as execSync3 } from "child_process";
10791
+ import { promises as fs16 } from "fs";
10792
+ import path11 from "path";
10793
+ import pLimit3 from "p-limit";
10794
+ var DepsPythonService = class {
10795
+ ecosystem = "python";
10796
+ versionCache = /* @__PURE__ */ new Map();
10797
+ async check(cwd, onProgress) {
10798
+ const manager = await this.detectPackageManager(cwd);
10799
+ let packages;
10800
+ if (manager === "pipenv") {
10801
+ packages = await this.checkPipenv(cwd, onProgress);
10579
10802
  } else {
10580
- try {
10581
- const selected = await checkbox3({
10582
- message: "Ch\u1ECDn packages \u0111\u1EC3 upgrade (ESC \u0111\u1EC3 h\u1EE7y):",
10583
- choices: upgradablePackages.map((pkg2) => ({
10584
- name: `${pkg2.name} (${pkg2.current} \u2192 ${pkg2.latestWithPrefix}) [${pkg2.type}]`,
10585
- value: pkg2.name,
10586
- checked: true
10587
- // Mặc định chọn tất cả
10588
- })),
10589
- instructions: "\u2191\u2193 navigate \u2022 space select \u2022 a all \u2022 i invert \u2022 \u23CE submit \u2022 esc cancel"
10590
- });
10591
- if (selected.length === 0) {
10592
- console.log(`${colors3.yellow}\u23F8\uFE0F Kh\xF4ng c\xF3 packages n\xE0o \u0111\u01B0\u1EE3c ch\u1ECDn${colors3.reset}
10593
- `);
10594
- return;
10595
- }
10596
- selectedPackages = upgradablePackages.filter((pkg2) => selected.includes(pkg2.name));
10597
- } catch {
10598
- console.log(`
10599
- ${colors3.yellow}\u23F8\uFE0F \u0110\xE3 h\u1EE7y${colors3.reset}
10600
- `);
10601
- return;
10803
+ packages = await this.checkPip(cwd, onProgress);
10804
+ }
10805
+ return { ecosystem: this.ecosystem, manager, packages };
10806
+ }
10807
+ async upgrade(cwd, options) {
10808
+ const manager = await this.detectPackageManager(cwd);
10809
+ const commands = this.getUpgradeCommands(options.packages);
10810
+ if (manager === "pipenv") {
10811
+ if (commands.deps || commands.devDeps) {
10812
+ const allPackages = options.packages.map((p) => p.name).join(" ");
10813
+ this.executeUpgrade(`pipenv update ${allPackages}`, cwd);
10602
10814
  }
10815
+ } else {
10816
+ if (commands.deps) this.executeUpgrade(commands.deps, cwd);
10817
+ if (commands.devDeps) this.executeUpgrade(commands.devDeps, cwd);
10603
10818
  }
10604
- let shouldProceed;
10605
- try {
10606
- shouldProceed = await confirm6({
10607
- message: `Ti\u1EBFn h\xE0nh upgrade ${selectedPackages.length} packages?`,
10608
- default: true
10609
- });
10610
- } catch {
10611
- console.log(`
10612
- ${colors3.yellow}\u23F8\uFE0F \u0110\xE3 h\u1EE7y${colors3.reset}
10613
- `);
10614
- return;
10819
+ }
10820
+ getUpgradeCommands(packages) {
10821
+ const deps = packages.filter((p) => p.type === "dep");
10822
+ const devDeps = packages.filter((p) => p.type === "dev");
10823
+ let depsCmd = null;
10824
+ let devDepsCmd = null;
10825
+ if (deps.length > 0) {
10826
+ const pkgList = deps.map((p) => `${p.name}==${p.latest}`).join(" ");
10827
+ depsCmd = `pip install --upgrade ${pkgList}`;
10615
10828
  }
10616
- if (!shouldProceed) {
10617
- console.log(`${colors3.yellow}\u23F8\uFE0F Upgrade \u0111\xE3 h\u1EE7y${colors3.reset}
10618
- `);
10619
- return;
10829
+ if (devDeps.length > 0) {
10830
+ const pkgList = devDeps.map((p) => `${p.name}==${p.latest}`).join(" ");
10831
+ devDepsCmd = `pip install --upgrade ${pkgList}`;
10620
10832
  }
10621
- const pm = await depsService.detectPackageManager(cwd);
10622
- console.log(`
10623
- ${colors3.cyan}\u{1F527} Package manager: ${pm}${colors3.reset}`);
10624
- console.log(`${colors3.cyan}\u{1F4E5} \u0110ang upgrade...${colors3.reset}
10625
- `);
10626
- const commands = depsService.getUpgradeCommands(selectedPackages, pm);
10833
+ return { deps: depsCmd, devDeps: devDepsCmd };
10834
+ }
10835
+ async detectPackageManager(cwd) {
10836
+ if (await this.fileExists(cwd, "Pipfile")) return "pipenv";
10837
+ return "pip";
10838
+ }
10839
+ async checkPip(cwd, onProgress) {
10840
+ const requirementsPath = path11.join(cwd, "requirements.txt");
10627
10841
  try {
10628
- if (commands.deps) {
10629
- console.log(`${colors3.dim}$ ${commands.deps}${colors3.reset}
10630
- `);
10631
- depsService.executeUpgrade(commands.deps);
10632
- }
10633
- if (commands.devDeps) {
10634
- console.log(`
10635
- ${colors3.dim}$ ${commands.devDeps}${colors3.reset}
10636
- `);
10637
- depsService.executeUpgrade(commands.devDeps);
10638
- }
10639
- console.log(
10640
- `
10641
- ${colors3.green}\u2705 \u0110\xE3 upgrade ${selectedPackages.length} packages th\xE0nh c\xF4ng!${colors3.reset}
10642
- `
10643
- );
10842
+ const content = await fs16.readFile(requirementsPath, "utf-8");
10843
+ const packages = this.parseRequirementsTxt(content);
10844
+ return await this.fetchBulkVersions(packages, onProgress);
10644
10845
  } catch (error) {
10645
- console.error(`
10646
- ${colors3.red}\u274C L\u1ED7i khi upgrade:${colors3.reset}`);
10647
- console.error(`${colors3.red}${error.message}${colors3.reset}
10648
- `);
10649
- console.log(`${colors3.yellow}\u{1F4A1} B\u1EA1n c\xF3 th\u1EC3 th\u1EED upgrade th\u1EE7 c\xF4ng:${colors3.reset}`);
10650
- if (commands.deps) console.log(` ${colors3.cyan}${commands.deps}${colors3.reset}`);
10651
- if (commands.devDeps) console.log(` ${colors3.cyan}${commands.devDeps}${colors3.reset}`);
10652
- console.log("");
10653
- process.exit(1);
10846
+ if (error.code === "ENOENT") {
10847
+ throw new Error("Kh\xF4ng t\xECm th\u1EA5y requirements.txt trong th\u01B0 m\u1EE5c hi\u1EC7n t\u1EA1i");
10848
+ }
10849
+ throw new Error(`L\u1ED7i \u0111\u1ECDc requirements.txt: ${error.message}`);
10654
10850
  }
10655
- } catch (error) {
10656
- console.error(`
10657
- ${colors3.red}\u274C ${error.message}${colors3.reset}
10658
- `);
10659
- process.exit(1);
10660
- }
10661
- }
10662
- function isNpmVersion(version) {
10663
- if (version.startsWith("git") || version.startsWith("github") || version.startsWith("file:") || version.startsWith("link:") || version.startsWith("workspace:") || version.includes("://")) {
10664
- return false;
10665
10851
  }
10666
- return true;
10852
+ async checkPipenv(cwd, onProgress) {
10853
+ try {
10854
+ const output = execSync3("pipenv run pip list --outdated --format=json", {
10855
+ cwd,
10856
+ encoding: "utf-8",
10857
+ stdio: ["pipe", "pipe", "pipe"]
10858
+ });
10859
+ const outdated = JSON.parse(output);
10860
+ const total = outdated.length;
10861
+ let completed = 0;
10862
+ const packages = outdated.map((pkg) => {
10863
+ completed++;
10864
+ onProgress?.(completed, total);
10865
+ return {
10866
+ name: pkg.name,
10867
+ current: pkg.version,
10868
+ latest: pkg.latest_version,
10869
+ latestWithPrefix: pkg.latest_version,
10870
+ type: "dep",
10871
+ upgradeType: this.getUpgradeType(pkg.version, pkg.latest_version),
10872
+ canUpgrade: true
10873
+ };
10874
+ });
10875
+ return packages;
10876
+ } catch (error) {
10877
+ throw new Error(`L\u1ED7i ki\u1EC3m tra pipenv packages: ${error.message}`);
10878
+ }
10879
+ }
10880
+ parseRequirementsTxt(content) {
10881
+ const packages = [];
10882
+ const lines = content.split("\n");
10883
+ for (const line of lines) {
10884
+ const trimmed = line.trim();
10885
+ if (!trimmed || trimmed.startsWith("#")) continue;
10886
+ const match = trimmed.match(/^([a-zA-Z0-9_-]+)\s*([=><]=?)\s*([0-9.]+)/);
10887
+ if (match) {
10888
+ const [, name, , version] = match;
10889
+ packages.push({ name: name.toLowerCase(), current: version, type: "dep" });
10890
+ }
10891
+ }
10892
+ return packages;
10893
+ }
10894
+ async fetchBulkVersions(packages, onProgress) {
10895
+ const limit = pLimit3(10);
10896
+ const total = packages.length;
10897
+ let completed = 0;
10898
+ const results = await Promise.all(
10899
+ packages.map((pkg) => limit(async () => {
10900
+ const latest = await this.fetchLatestVersion(pkg.name);
10901
+ completed++;
10902
+ onProgress?.(completed, total);
10903
+ if (!latest) return null;
10904
+ const canUpgrade = this.isNewerVersion(latest, pkg.current);
10905
+ if (!canUpgrade) return null;
10906
+ return {
10907
+ name: pkg.name,
10908
+ current: pkg.current,
10909
+ latest,
10910
+ latestWithPrefix: latest,
10911
+ type: pkg.type,
10912
+ upgradeType: this.getUpgradeType(pkg.current, latest),
10913
+ canUpgrade: true
10914
+ };
10915
+ }))
10916
+ );
10917
+ return results.filter((r) => r !== null);
10918
+ }
10919
+ async fetchLatestVersion(packageName) {
10920
+ if (this.versionCache.has(packageName)) return this.versionCache.get(packageName);
10921
+ try {
10922
+ const response = await fetch(
10923
+ `https://pypi.org/pypi/${encodeURIComponent(packageName)}/json`,
10924
+ { signal: AbortSignal.timeout(1e4) }
10925
+ );
10926
+ if (!response.ok) return null;
10927
+ const data = await response.json();
10928
+ this.versionCache.set(packageName, data.info.version);
10929
+ return data.info.version;
10930
+ } catch {
10931
+ return null;
10932
+ }
10933
+ }
10934
+ getUpgradeType(current, latest) {
10935
+ const currentParts = current.split(".").map(Number);
10936
+ const latestParts = latest.split(".").map(Number);
10937
+ if ((latestParts[0] || 0) > (currentParts[0] || 0)) return "major";
10938
+ if ((latestParts[1] || 0) > (currentParts[1] || 0)) return "minor";
10939
+ return "patch";
10940
+ }
10941
+ isNewerVersion(remote, local) {
10942
+ const remoteParts = remote.split(".").map(Number);
10943
+ const localParts = local.split(".").map(Number);
10944
+ for (let i = 0; i < 3; i++) {
10945
+ const r = remoteParts[i] || 0;
10946
+ const l = localParts[i] || 0;
10947
+ if (r > l) return true;
10948
+ if (r < l) return false;
10949
+ }
10950
+ return false;
10951
+ }
10952
+ async fileExists(cwd, filename) {
10953
+ try {
10954
+ await fs16.access(path11.join(cwd, filename));
10955
+ return true;
10956
+ } catch {
10957
+ return false;
10958
+ }
10959
+ }
10960
+ executeUpgrade(command, cwd) {
10961
+ execSync3(command, { cwd, stdio: "inherit", env: { ...process.env, FORCE_COLOR: "1" } });
10962
+ }
10963
+ };
10964
+
10965
+ // src/commands/deps/check.ts
10966
+ function createDepsCheckCommand() {
10967
+ const checkCommand = new Command36("check").description("Ki\u1EC3m tra c\xE1c packages c\u1EA7n upgrade").action(async () => {
10968
+ const cwd = process.cwd();
10969
+ const detector = new DepsDetectorService();
10970
+ const spinner = ora("\u0110ang ph\xE1t hi\u1EC7n lo\u1EA1i project...").start();
10971
+ const projects = await detector.detectProjects(cwd);
10972
+ if (projects.length === 0) {
10973
+ spinner.fail("Kh\xF4ng t\xECm th\u1EA5y project n\xE0o \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3");
10974
+ console.log();
10975
+ console.log(chalk11.dim("H\u1ED7 tr\u1EE3: Node.js (package.json), PHP (composer.json), Python (requirements.txt/Pipfile)"));
10976
+ process.exit(1);
10977
+ }
10978
+ spinner.succeed(`Ph\xE1t hi\u1EC7n ${projects.length} project:`);
10979
+ for (const project of projects) {
10980
+ const icon = project.ecosystem === "node" ? "\u{1F4E6}" : project.ecosystem === "php" ? "\u{1F418}" : "\u{1F40D}";
10981
+ const label = project.ecosystem === "node" ? "Node.js" : project.ecosystem === "php" ? "PHP/Composer" : "Python";
10982
+ console.log(` ${icon} ${label} (${project.manager})`);
10983
+ }
10984
+ console.log();
10985
+ for (const project of projects) {
10986
+ await checkEcosystem(project.ecosystem, project.manager, cwd);
10987
+ }
10988
+ console.log();
10989
+ console.log(chalk11.dim('\u{1F4A1} Ch\u1EA1y "jai1 deps upgrade" \u0111\u1EC3 n\xE2ng c\u1EA5p packages.'));
10990
+ });
10991
+ return checkCommand;
10992
+ }
10993
+ async function checkEcosystem(ecosystem, manager, cwd) {
10994
+ let service;
10995
+ let icon;
10996
+ let label;
10997
+ switch (ecosystem) {
10998
+ case "node":
10999
+ service = new DepsService();
11000
+ icon = "\u{1F4E6}";
11001
+ label = `Node.js (${manager})`;
11002
+ break;
11003
+ case "php":
11004
+ service = new DepsPhpService();
11005
+ icon = "\u{1F418}";
11006
+ label = "PHP/Composer";
11007
+ break;
11008
+ case "python":
11009
+ service = new DepsPythonService();
11010
+ icon = "\u{1F40D}";
11011
+ label = `Python (${manager})`;
11012
+ break;
11013
+ }
11014
+ const spinner = ora(`\u0110ang ki\u1EC3m tra ${label}...`).start();
11015
+ let result;
11016
+ try {
11017
+ result = await service.check(cwd, (completed, total) => {
11018
+ spinner.text = `\u0110ang ki\u1EC3m tra ${label}... (${completed}/${total})`;
11019
+ });
11020
+ } catch (error) {
11021
+ spinner.fail(`L\u1ED7i ki\u1EC3m tra ${label}`);
11022
+ console.log(chalk11.red(error.message));
11023
+ console.log();
11024
+ return;
11025
+ }
11026
+ if (result.packages.length === 0) {
11027
+ spinner.succeed(`${label} - T\u1EA5t c\u1EA3 packages \u0111\xE3 c\u1EADp nh\u1EADt`);
11028
+ console.log();
11029
+ return;
11030
+ }
11031
+ spinner.succeed(`${label} - ${result.packages.length} packages c\xF3 th\u1EC3 n\xE2ng c\u1EA5p`);
11032
+ console.log();
11033
+ const table = new Table3({
11034
+ head: [
11035
+ chalk11.cyan("Package"),
11036
+ chalk11.cyan("Hi\u1EC7n t\u1EA1i"),
11037
+ chalk11.cyan("M\u1EDBi nh\u1EA5t"),
11038
+ chalk11.cyan("Lo\u1EA1i")
11039
+ ],
11040
+ style: {
11041
+ head: [],
11042
+ border: ["dim"]
11043
+ }
11044
+ });
11045
+ for (const pkg of result.packages) {
11046
+ const upgradeIcon = pkg.upgradeType === "major" ? "\u{1F534}" : pkg.upgradeType === "minor" ? "\u{1F7E1}" : "\u{1F7E2}";
11047
+ table.push([
11048
+ `${upgradeIcon} ${pkg.name}`,
11049
+ chalk11.dim(pkg.current),
11050
+ chalk11.green(pkg.latest),
11051
+ pkg.type === "dev" ? chalk11.dim("dev") : "dep"
11052
+ ]);
11053
+ }
11054
+ console.log(table.toString());
11055
+ console.log();
11056
+ if (result.isLaravel) {
11057
+ const blockedPackages = result.packages.filter((p) => p.blockedReason);
11058
+ if (blockedPackages.length > 0) {
11059
+ console.log(chalk11.yellow("\u26A0\uFE0F Laravel major version upgrades blocked (nguy hi\u1EC3m):"));
11060
+ for (const pkg of blockedPackages) {
11061
+ console.log(chalk11.dim(` - ${pkg.name}: ${pkg.current} \u2192 ${pkg.latest}`));
11062
+ }
11063
+ console.log();
11064
+ console.log(chalk11.dim("\u{1F4A1} \u0110\u1EC3 n\xE2ng c\u1EA5p Laravel major version, ch\u1EA1y th\u1EE7 c\xF4ng:"));
11065
+ console.log(chalk11.dim(` composer require ${blockedPackages[0].name}:^${blockedPackages[0].latest}`));
11066
+ console.log();
11067
+ }
11068
+ }
11069
+ }
11070
+
11071
+ // src/commands/deps/upgrade.ts
11072
+ import { Command as Command37 } from "commander";
11073
+ import { checkbox as checkbox3, confirm as confirm6 } from "@inquirer/prompts";
11074
+ import chalk12 from "chalk";
11075
+ import ora2 from "ora";
11076
+ import Table4 from "cli-table3";
11077
+ function createDepsUpgradeCommand() {
11078
+ return new Command37("upgrade").description("Upgrade packages l\xEAn version m\u1EDBi nh\u1EA5t").option("--all", "Upgrade t\u1EA5t c\u1EA3 kh\xF4ng c\u1EA7n ch\u1ECDn").action(async (options) => {
11079
+ await handleDepsUpgrade(options);
11080
+ });
11081
+ }
11082
+ async function handleDepsUpgrade(options) {
11083
+ const cwd = process.cwd();
11084
+ const detector = new DepsDetectorService();
11085
+ try {
11086
+ const spinner = ora2("\u0110ang ph\xE1t hi\u1EC7n lo\u1EA1i project...").start();
11087
+ const projects = await detector.detectProjects(cwd);
11088
+ if (projects.length === 0) {
11089
+ spinner.fail("Kh\xF4ng t\xECm th\u1EA5y project n\xE0o \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3");
11090
+ console.log();
11091
+ console.log(chalk12.dim("H\u1ED7 tr\u1EE3: Node.js (package.json), PHP (composer.json), Python (requirements.txt/Pipfile)"));
11092
+ process.exit(1);
11093
+ }
11094
+ spinner.succeed(`Ph\xE1t hi\u1EC7n ${projects.length} project:`);
11095
+ for (const project of projects) {
11096
+ const icon = getEcosystemIcon(project.ecosystem);
11097
+ const label = getEcosystemLabel(project.ecosystem);
11098
+ console.log(` ${icon} ${label} (${project.manager})`);
11099
+ }
11100
+ console.log();
11101
+ for (const project of projects) {
11102
+ await upgradeEcosystem(project, cwd, options);
11103
+ }
11104
+ console.log();
11105
+ console.log(chalk12.green("\u2705 Ho\xE0n th\xE0nh!"));
11106
+ } catch (error) {
11107
+ console.error(chalk12.red(`
11108
+ \u274C ${error.message}
11109
+ `));
11110
+ process.exit(1);
11111
+ }
11112
+ }
11113
+ async function upgradeEcosystem(project, cwd, options) {
11114
+ const service = getService(project.ecosystem);
11115
+ const label = `${getEcosystemIcon(project.ecosystem)} ${getEcosystemLabel(project.ecosystem)}`;
11116
+ console.log(chalk12.bold.cyan(`
11117
+ ${"\u2501".repeat(80)}`));
11118
+ console.log(chalk12.bold.cyan(`${label}`));
11119
+ console.log(chalk12.bold.cyan("\u2501".repeat(80)));
11120
+ console.log();
11121
+ const spinner = ora2("\u0110ang ki\u1EC3m tra packages...").start();
11122
+ let packages;
11123
+ try {
11124
+ const result = await service.check(cwd, (completed, total) => {
11125
+ spinner.text = `\u0110ang ki\u1EC3m tra packages... (${completed}/${total})`;
11126
+ });
11127
+ packages = result.packages;
11128
+ } catch (error) {
11129
+ spinner.fail("L\u1ED7i ki\u1EC3m tra packages");
11130
+ console.log(chalk12.red(error.message));
11131
+ return;
11132
+ }
11133
+ if (packages.length === 0) {
11134
+ spinner.succeed("T\u1EA5t c\u1EA3 packages \u0111\xE3 c\u1EADp nh\u1EADt");
11135
+ return;
11136
+ }
11137
+ spinner.succeed(`T\xECm th\u1EA5y ${packages.length} packages c\xF3 th\u1EC3 n\xE2ng c\u1EA5p`);
11138
+ console.log();
11139
+ displayUpgradeTable(packages);
11140
+ let selectedPackages;
11141
+ if (options.all) {
11142
+ selectedPackages = packages;
11143
+ console.log(chalk12.cyan(`\u{1F4CB} \u0110\xE3 ch\u1ECDn t\u1EA5t c\u1EA3 ${selectedPackages.length} packages
11144
+ `));
11145
+ } else {
11146
+ try {
11147
+ const selected = await checkbox3({
11148
+ message: "Ch\u1ECDn packages \u0111\u1EC3 upgrade (\u2191\u2193 di chuy\u1EC3n \u2022 space ch\u1ECDn \u2022 a t\u1EA5t c\u1EA3 \u2022 i \u0111\u1EA3o \u2022 \u23CE x\xE1c nh\u1EADn \u2022 esc tho\xE1t):",
11149
+ choices: packages.map((pkg) => {
11150
+ const icon = pkg.upgradeType === "major" ? "\u{1F534}" : pkg.upgradeType === "minor" ? "\u{1F7E1}" : "\u{1F7E2}";
11151
+ return {
11152
+ name: `${icon} ${pkg.name} (${pkg.current} \u2192 ${pkg.latest}) [${pkg.type}]`,
11153
+ value: pkg.name,
11154
+ checked: true
11155
+ };
11156
+ }),
11157
+ pageSize: 15
11158
+ });
11159
+ if (selected.length === 0) {
11160
+ console.log(chalk12.yellow("\u23F8\uFE0F Kh\xF4ng c\xF3 packages n\xE0o \u0111\u01B0\u1EE3c ch\u1ECDn\n"));
11161
+ return;
11162
+ }
11163
+ selectedPackages = packages.filter((p) => selected.includes(p.name));
11164
+ } catch {
11165
+ console.log(chalk12.yellow("\n\u23F8\uFE0F \u0110\xE3 h\u1EE7y\n"));
11166
+ return;
11167
+ }
11168
+ }
11169
+ let shouldProceed;
11170
+ try {
11171
+ shouldProceed = await confirm6({
11172
+ message: `Ti\u1EBFn h\xE0nh upgrade ${selectedPackages.length} packages? (ESC \u0111\u1EC3 h\u1EE7y)`,
11173
+ default: true
11174
+ });
11175
+ } catch {
11176
+ console.log(chalk12.yellow("\n\u23F8\uFE0F \u0110\xE3 h\u1EE7y\n"));
11177
+ return;
11178
+ }
11179
+ if (!shouldProceed) {
11180
+ console.log(chalk12.yellow("\u23F8\uFE0F Upgrade \u0111\xE3 h\u1EE7y\n"));
11181
+ return;
11182
+ }
11183
+ console.log();
11184
+ console.log(chalk12.cyan(`\u{1F527} Package manager: ${project.manager}`));
11185
+ console.log(chalk12.cyan("\u{1F4E5} \u0110ang upgrade...\n"));
11186
+ const commands = service.getUpgradeCommands(selectedPackages);
11187
+ try {
11188
+ if (commands.deps) {
11189
+ console.log(chalk12.dim(`$ ${commands.deps}
11190
+ `));
11191
+ }
11192
+ if (commands.devDeps) {
11193
+ console.log(chalk12.dim(`$ ${commands.devDeps}
11194
+ `));
11195
+ }
11196
+ await service.upgrade(cwd, { packages: selectedPackages });
11197
+ console.log(chalk12.green(`
11198
+ \u2705 \u0110\xE3 upgrade ${selectedPackages.length} packages th\xE0nh c\xF4ng!`));
11199
+ } catch (error) {
11200
+ console.error(chalk12.red("\n\u274C L\u1ED7i khi upgrade:"));
11201
+ console.error(chalk12.red(error.message));
11202
+ console.log(chalk12.yellow("\n\u{1F4A1} B\u1EA1n c\xF3 th\u1EC3 th\u1EED upgrade th\u1EE7 c\xF4ng:"));
11203
+ if (commands.deps) console.log(chalk12.cyan(` ${commands.deps}`));
11204
+ if (commands.devDeps) console.log(chalk12.cyan(` ${commands.devDeps}`));
11205
+ console.log();
11206
+ throw error;
11207
+ }
10667
11208
  }
10668
11209
  function displayUpgradeTable(packages) {
10669
- console.log(`${colors3.bold}\u{1F4CA} C\xF3 th\u1EC3 upgrade:${colors3.reset}
10670
- `);
10671
- const nameWidth = Math.max(7, ...packages.map((p) => p.name.length));
10672
- const currentWidth = Math.max(8, ...packages.map((p) => p.current.length));
10673
- const latestWidth = Math.max(8, ...packages.map((p) => p.latestWithPrefix.length));
10674
- const header = `\u2502 ${"Package".padEnd(nameWidth)} \u2502 ${"Hi\u1EC7n t\u1EA1i".padEnd(currentWidth)} \u2502 ${"M\u1EDBi nh\u1EA5t".padEnd(latestWidth)} \u2502 Lo\u1EA1i \u2502`;
10675
- const separator = `\u251C${"\u2500".repeat(nameWidth + 2)}\u253C${"\u2500".repeat(currentWidth + 2)}\u253C${"\u2500".repeat(latestWidth + 2)}\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2524`;
10676
- const topBorder = `\u250C${"\u2500".repeat(nameWidth + 2)}\u252C${"\u2500".repeat(currentWidth + 2)}\u252C${"\u2500".repeat(latestWidth + 2)}\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2510`;
10677
- const bottomBorder = `\u2514${"\u2500".repeat(nameWidth + 2)}\u2534${"\u2500".repeat(currentWidth + 2)}\u2534${"\u2500".repeat(latestWidth + 2)}\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2518`;
10678
- console.log(topBorder);
10679
- console.log(header);
10680
- console.log(separator);
11210
+ const table = new Table4({
11211
+ head: [
11212
+ chalk12.cyan("Package"),
11213
+ chalk12.cyan("Hi\u1EC7n t\u1EA1i"),
11214
+ chalk12.cyan("M\u1EDBi nh\u1EA5t"),
11215
+ chalk12.cyan("Lo\u1EA1i")
11216
+ ],
11217
+ style: {
11218
+ head: [],
11219
+ border: ["dim"]
11220
+ }
11221
+ });
10681
11222
  for (const pkg of packages) {
10682
- const typeLabel = pkg.type === "dep" ? "dep " : "dev ";
10683
- const typeColor = pkg.type === "dep" ? colors3.cyan : colors3.yellow;
10684
- console.log(
10685
- `\u2502 ${pkg.name.padEnd(nameWidth)} \u2502 ${colors3.gray}${pkg.current.padEnd(currentWidth)}${colors3.reset} \u2502 ${colors3.green}${pkg.latestWithPrefix.padEnd(latestWidth)}${colors3.reset} \u2502 ${typeColor}${typeLabel}${colors3.reset}\u2502`
10686
- );
11223
+ const upgradeIcon = pkg.upgradeType === "major" ? "\u{1F534}" : pkg.upgradeType === "minor" ? "\u{1F7E1}" : "\u{1F7E2}";
11224
+ table.push([
11225
+ `${upgradeIcon} ${pkg.name}`,
11226
+ chalk12.dim(pkg.current),
11227
+ chalk12.green(pkg.latest),
11228
+ pkg.type === "dev" ? chalk12.dim("dev") : "dep"
11229
+ ]);
11230
+ }
11231
+ console.log(table.toString());
11232
+ console.log();
11233
+ }
11234
+ function getService(ecosystem) {
11235
+ switch (ecosystem) {
11236
+ case "node":
11237
+ return new DepsService();
11238
+ case "php":
11239
+ return new DepsPhpService();
11240
+ case "python":
11241
+ return new DepsPythonService();
11242
+ }
11243
+ }
11244
+ function getEcosystemIcon(ecosystem) {
11245
+ switch (ecosystem) {
11246
+ case "node":
11247
+ return "\u{1F4E6}";
11248
+ case "php":
11249
+ return "\u{1F418}";
11250
+ case "python":
11251
+ return "\u{1F40D}";
11252
+ default:
11253
+ return "\u{1F4E6}";
11254
+ }
11255
+ }
11256
+ function getEcosystemLabel(ecosystem) {
11257
+ switch (ecosystem) {
11258
+ case "node":
11259
+ return "Node.js";
11260
+ case "php":
11261
+ return "PHP/Composer";
11262
+ case "python":
11263
+ return "Python";
11264
+ default:
11265
+ return ecosystem;
10687
11266
  }
10688
- console.log(bottomBorder);
10689
- console.log("");
10690
11267
  }
10691
11268
 
10692
11269
  // src/commands/deps/index.ts
10693
11270
  function showDepsHelp() {
10694
- console.log(chalk11.bold.cyan("\u{1F4E6} jai1 deps") + chalk11.dim(" - Qu\u1EA3n l\xFD dependencies trong project"));
11271
+ console.log(chalk13.bold.cyan("\u{1F4E6} jai1 deps") + chalk13.dim(" - Qu\u1EA3n l\xFD dependencies trong project"));
10695
11272
  console.log();
10696
- console.log(chalk11.bold("C\xE1c l\u1EC7nh:"));
10697
- console.log(` ${chalk11.cyan("upgrade")} N\xE2ng c\u1EA5p dependencies l\xEAn phi\xEAn b\u1EA3n m\u1EDBi nh\u1EA5t`);
11273
+ console.log(chalk13.bold("C\xE1c l\u1EC7nh:"));
11274
+ console.log(` ${chalk13.cyan("check")} Ki\u1EC3m tra c\xE1c packages c\u1EA7n upgrade`);
11275
+ console.log(` ${chalk13.cyan("upgrade")} N\xE2ng c\u1EA5p dependencies l\xEAn phi\xEAn b\u1EA3n m\u1EDBi nh\u1EA5t`);
11276
+ console.log();
11277
+ console.log(chalk13.bold("H\u1ED7 tr\u1EE3:"));
11278
+ console.log(chalk13.dim(" \u2022 Node.js (npm, pnpm, yarn, bun)"));
11279
+ console.log(chalk13.dim(" \u2022 PHP/Composer (v\u1EDBi b\u1EA3o v\u1EC7 Laravel major version)"));
11280
+ console.log(chalk13.dim(" \u2022 Python (pip, pipenv)"));
10698
11281
  console.log();
10699
- console.log(chalk11.bold("V\xED d\u1EE5:"));
10700
- console.log(chalk11.dim(" $ jai1 deps upgrade"));
10701
- console.log(chalk11.dim(" $ jai1 deps upgrade --dry-run"));
11282
+ console.log(chalk13.bold("V\xED d\u1EE5:"));
11283
+ console.log(chalk13.dim(" $ jai1 deps check"));
11284
+ console.log(chalk13.dim(" $ jai1 deps upgrade"));
11285
+ console.log(chalk13.dim(" $ jai1 deps upgrade --all"));
10702
11286
  console.log();
10703
- console.log(chalk11.dim('Ch\u1EA1y "jai1 deps <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
11287
+ console.log(chalk13.dim('Ch\u1EA1y "jai1 deps <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
10704
11288
  }
10705
11289
  function createDepsCommand() {
10706
- const depsCommand = new Command37("deps").description("Qu\u1EA3n l\xFD dependencies trong project").action(() => {
11290
+ const depsCommand = new Command38("deps").description("Qu\u1EA3n l\xFD dependencies trong project").action(() => {
10707
11291
  showDepsHelp();
10708
11292
  });
11293
+ depsCommand.addCommand(createDepsCheckCommand());
10709
11294
  depsCommand.addCommand(createDepsUpgradeCommand());
10710
11295
  return depsCommand;
10711
11296
  }
10712
11297
 
10713
11298
  // src/commands/kit/index.ts
10714
- import { Command as Command41 } from "commander";
10715
- import chalk13 from "chalk";
11299
+ import { Command as Command42 } from "commander";
11300
+ import chalk15 from "chalk";
10716
11301
 
10717
11302
  // src/commands/kit/list.ts
10718
- import { Command as Command38 } from "commander";
10719
- import chalk12 from "chalk";
10720
- import Table3 from "cli-table3";
11303
+ import { Command as Command39 } from "commander";
11304
+ import chalk14 from "chalk";
11305
+ import Table5 from "cli-table3";
10721
11306
 
10722
11307
  // src/services/starter-kit.service.ts
10723
- import { promises as fs14 } from "fs";
10724
- import { join as join6 } from "path";
11308
+ import { promises as fs17 } from "fs";
11309
+ import { join as join7 } from "path";
10725
11310
  import AdmZip from "adm-zip";
10726
11311
  var StarterKitService = class {
10727
11312
  /**
@@ -10768,29 +11353,29 @@ var StarterKitService = class {
10768
11353
  throw new NetworkError(`Failed to download kit: HTTP ${response.status}`);
10769
11354
  }
10770
11355
  if (onProgress) onProgress(30);
10771
- const tmpDir = join6(process.env.TMPDIR || "/tmp", "jai1-kits");
10772
- await fs14.mkdir(tmpDir, { recursive: true });
10773
- const tmpFile = join6(tmpDir, `${slug}.zip`);
11356
+ const tmpDir = join7(process.env.TMPDIR || "/tmp", "jai1-kits");
11357
+ await fs17.mkdir(tmpDir, { recursive: true });
11358
+ const tmpFile = join7(tmpDir, `${slug}.zip`);
10774
11359
  const buffer = await response.arrayBuffer();
10775
- await fs14.writeFile(tmpFile, Buffer.from(buffer));
11360
+ await fs17.writeFile(tmpFile, Buffer.from(buffer));
10776
11361
  if (onProgress) onProgress(60);
10777
11362
  const zip = new AdmZip(tmpFile);
10778
- await fs14.mkdir(targetDir, { recursive: true });
11363
+ await fs17.mkdir(targetDir, { recursive: true });
10779
11364
  zip.extractAllTo(targetDir, true);
10780
11365
  if (onProgress) onProgress(100);
10781
- await fs14.unlink(tmpFile);
11366
+ await fs17.unlink(tmpFile);
10782
11367
  }
10783
11368
  };
10784
11369
 
10785
11370
  // src/commands/kit/list.ts
10786
11371
  function createKitListCommand() {
10787
- return new Command38("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) => {
11372
+ return new Command39("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) => {
10788
11373
  const configService = new ConfigService();
10789
11374
  const config = await configService.load();
10790
11375
  if (!config) {
10791
11376
  throw new ValidationError('Not initialized. Run "jai1 auth" first.');
10792
11377
  }
10793
- console.log(chalk12.cyan("\u{1F4E6} \u0110ang t\u1EA3i danh s\xE1ch starter kits..."));
11378
+ console.log(chalk14.cyan("\u{1F4E6} \u0110ang t\u1EA3i danh s\xE1ch starter kits..."));
10794
11379
  console.log();
10795
11380
  const kitService = new StarterKitService();
10796
11381
  const kits = await kitService.list(config, {
@@ -10798,9 +11383,9 @@ function createKitListCommand() {
10798
11383
  search: options.search
10799
11384
  });
10800
11385
  if (kits.length === 0) {
10801
- console.log(chalk12.yellow("Kh\xF4ng t\xECm th\u1EA5y starter kits n\xE0o."));
11386
+ console.log(chalk14.yellow("Kh\xF4ng t\xECm th\u1EA5y starter kits n\xE0o."));
10802
11387
  if (options.category || options.search) {
10803
- console.log(chalk12.dim("Th\u1EED b\u1ECF filter \u0111\u1EC3 xem t\u1EA5t c\u1EA3."));
11388
+ console.log(chalk14.dim("Th\u1EED b\u1ECF filter \u0111\u1EC3 xem t\u1EA5t c\u1EA3."));
10804
11389
  }
10805
11390
  return;
10806
11391
  }
@@ -10824,35 +11409,35 @@ function createKitListCommand() {
10824
11409
  const categoryKits = byCategory[category];
10825
11410
  const categoryIcon = category === "frontend" ? "\u{1F3A8}" : category === "backend" ? "\u2699\uFE0F" : category === "fullstack" ? "\u{1F680}" : "\u{1F4E6}";
10826
11411
  console.log(
10827
- chalk12.bold(`${categoryIcon} ${category.charAt(0).toUpperCase() + category.slice(1)}`)
11412
+ chalk14.bold(`${categoryIcon} ${category.charAt(0).toUpperCase() + category.slice(1)}`)
10828
11413
  );
10829
- const table = new Table3({
11414
+ const table = new Table5({
10830
11415
  head: [
10831
- chalk12.cyan("Slug"),
10832
- chalk12.cyan("M\xF4 t\u1EA3"),
10833
- chalk12.cyan("Version")
11416
+ chalk14.cyan("Slug"),
11417
+ chalk14.cyan("M\xF4 t\u1EA3"),
11418
+ chalk14.cyan("Version")
10834
11419
  ],
10835
11420
  style: { head: [], border: ["gray"] }
10836
11421
  });
10837
11422
  for (const kit of categoryKits) {
10838
11423
  table.push([
10839
- chalk12.white(kit.slug),
10840
- chalk12.dim(kit.description.slice(0, 50)),
10841
- chalk12.green(`v${kit.version}`)
11424
+ chalk14.white(kit.slug),
11425
+ chalk14.dim(kit.description.slice(0, 50)),
11426
+ chalk14.green(`v${kit.version}`)
10842
11427
  ]);
10843
11428
  }
10844
11429
  console.log(table.toString());
10845
11430
  console.log();
10846
11431
  }
10847
- console.log(chalk12.dim(`T\u1ED5ng c\u1ED9ng: ${kits.length} starter kit(s)`));
10848
- console.log(chalk12.dim('\n\u{1F4A1} Ch\u1EA1y "jai1 kit create <slug>" \u0111\u1EC3 t\u1EA1o project m\u1EDBi'));
11432
+ console.log(chalk14.dim(`T\u1ED5ng c\u1ED9ng: ${kits.length} starter kit(s)`));
11433
+ console.log(chalk14.dim('\n\u{1F4A1} Ch\u1EA1y "jai1 kit create <slug>" \u0111\u1EC3 t\u1EA1o project m\u1EDBi'));
10849
11434
  });
10850
11435
  }
10851
11436
 
10852
11437
  // src/commands/kit/info.ts
10853
- import { Command as Command39 } from "commander";
11438
+ import { Command as Command40 } from "commander";
10854
11439
  function createKitInfoCommand() {
10855
- return new Command39("info").description("Show detailed information about a starter kit").argument("<slug>", "Starter kit slug").action(async (slug) => {
11440
+ return new Command40("info").description("Show detailed information about a starter kit").argument("<slug>", "Starter kit slug").action(async (slug) => {
10856
11441
  const configService = new ConfigService();
10857
11442
  const config = await configService.load();
10858
11443
  if (!config) {
@@ -10901,9 +11486,9 @@ Post-Init Commands:`);
10901
11486
  }
10902
11487
 
10903
11488
  // src/commands/kit/create.ts
10904
- import { Command as Command40 } from "commander";
10905
- import { promises as fs15 } from "fs";
10906
- import { join as join7 } from "path";
11489
+ import { Command as Command41 } from "commander";
11490
+ import { promises as fs18 } from "fs";
11491
+ import { join as join8 } from "path";
10907
11492
  import { select as select3, input as input2, checkbox as checkbox4 } from "@inquirer/prompts";
10908
11493
  import { execa as execa2 } from "execa";
10909
11494
 
@@ -10946,7 +11531,7 @@ var HookExecutor = class {
10946
11531
 
10947
11532
  // src/commands/kit/create.ts
10948
11533
  function createKitCreateCommand() {
10949
- return new Command40("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) => {
11534
+ return new Command41("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) => {
10950
11535
  const configService = new ConfigService();
10951
11536
  const config = await configService.load();
10952
11537
  if (!config) {
@@ -10966,9 +11551,9 @@ function createKitCreateCommand() {
10966
11551
  }
10967
11552
  }
10968
11553
  }
10969
- const targetDir = directory || join7(process.cwd(), options.name || slug);
11554
+ const targetDir = directory || join8(process.cwd(), options.name || slug);
10970
11555
  try {
10971
- await fs15.access(targetDir);
11556
+ await fs18.access(targetDir);
10972
11557
  throw new Error(`Directory already exists: ${targetDir}`);
10973
11558
  } catch (error) {
10974
11559
  if (error.code !== "ENOENT") {
@@ -11086,7 +11671,7 @@ function createKitCreateCommand() {
11086
11671
  async function applyVariableSubstitution(dir, variables) {
11087
11672
  const files = await getAllFiles(dir);
11088
11673
  for (const file of files) {
11089
- let content = await fs15.readFile(file, "utf-8");
11674
+ let content = await fs18.readFile(file, "utf-8");
11090
11675
  let modified = false;
11091
11676
  for (const [key, value] of Object.entries(variables)) {
11092
11677
  const regex = new RegExp(`\\{\\{${key}\\}\\}`, "g");
@@ -11096,15 +11681,15 @@ async function applyVariableSubstitution(dir, variables) {
11096
11681
  }
11097
11682
  }
11098
11683
  if (modified) {
11099
- await fs15.writeFile(file, content, "utf-8");
11684
+ await fs18.writeFile(file, content, "utf-8");
11100
11685
  }
11101
11686
  }
11102
11687
  }
11103
11688
  async function getAllFiles(dir) {
11104
11689
  const files = [];
11105
- const entries = await fs15.readdir(dir, { withFileTypes: true });
11690
+ const entries = await fs18.readdir(dir, { withFileTypes: true });
11106
11691
  for (const entry of entries) {
11107
- const fullPath = join7(dir, entry.name);
11692
+ const fullPath = join8(dir, entry.name);
11108
11693
  if (entry.isDirectory()) {
11109
11694
  if (!entry.name.startsWith(".") && entry.name !== "node_modules") {
11110
11695
  files.push(...await getAllFiles(fullPath));
@@ -11118,23 +11703,23 @@ async function getAllFiles(dir) {
11118
11703
 
11119
11704
  // src/commands/kit/index.ts
11120
11705
  function showKitHelp() {
11121
- console.log(chalk13.bold.cyan("\u{1F4E6} jai1 kit") + chalk13.dim(" - Qu\u1EA3n l\xFD starter kits"));
11706
+ console.log(chalk15.bold.cyan("\u{1F4E6} jai1 kit") + chalk15.dim(" - Qu\u1EA3n l\xFD starter kits"));
11122
11707
  console.log();
11123
- console.log(chalk13.bold("C\xE1c l\u1EC7nh:"));
11124
- console.log(` ${chalk13.cyan("list")} Li\u1EC7t k\xEA c\xE1c starter kits c\xF3 s\u1EB5n`);
11125
- console.log(` ${chalk13.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t starter kit`);
11126
- console.log(` ${chalk13.cyan("create")} T\u1EA1o project m\u1EDBi t\u1EEB starter kit`);
11708
+ console.log(chalk15.bold("C\xE1c l\u1EC7nh:"));
11709
+ console.log(` ${chalk15.cyan("list")} Li\u1EC7t k\xEA c\xE1c starter kits c\xF3 s\u1EB5n`);
11710
+ console.log(` ${chalk15.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t starter kit`);
11711
+ console.log(` ${chalk15.cyan("create")} T\u1EA1o project m\u1EDBi t\u1EEB starter kit`);
11127
11712
  console.log();
11128
- console.log(chalk13.bold("V\xED d\u1EE5:"));
11129
- console.log(chalk13.dim(" $ jai1 kit list"));
11130
- console.log(chalk13.dim(" $ jai1 kit list --category frontend"));
11131
- console.log(chalk13.dim(" $ jai1 kit info next-tw4-shadcn"));
11132
- console.log(chalk13.dim(" $ jai1 kit create next-tw4-shadcn my-project"));
11713
+ console.log(chalk15.bold("V\xED d\u1EE5:"));
11714
+ console.log(chalk15.dim(" $ jai1 kit list"));
11715
+ console.log(chalk15.dim(" $ jai1 kit list --category frontend"));
11716
+ console.log(chalk15.dim(" $ jai1 kit info next-tw4-shadcn"));
11717
+ console.log(chalk15.dim(" $ jai1 kit create next-tw4-shadcn my-project"));
11133
11718
  console.log();
11134
- console.log(chalk13.dim('Ch\u1EA1y "jai1 kit <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
11719
+ console.log(chalk15.dim('Ch\u1EA1y "jai1 kit <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
11135
11720
  }
11136
11721
  function createKitCommand() {
11137
- const cmd = new Command41("kit").description("Manage starter kits for new projects").action(() => {
11722
+ const cmd = new Command42("kit").description("Manage starter kits for new projects").action(() => {
11138
11723
  showKitHelp();
11139
11724
  });
11140
11725
  cmd.addCommand(createKitListCommand());
@@ -11144,21 +11729,21 @@ function createKitCommand() {
11144
11729
  }
11145
11730
 
11146
11731
  // src/commands/rules/index.ts
11147
- import { Command as Command48 } from "commander";
11148
- import chalk15 from "chalk";
11732
+ import { Command as Command49 } from "commander";
11733
+ import chalk17 from "chalk";
11149
11734
 
11150
11735
  // src/commands/rules/list.ts
11151
- import { Command as Command42 } from "commander";
11152
- import chalk14 from "chalk";
11153
- import Table4 from "cli-table3";
11736
+ import { Command as Command43 } from "commander";
11737
+ import chalk16 from "chalk";
11738
+ import Table6 from "cli-table3";
11154
11739
  function createRulesListCommand() {
11155
- return new Command42("list").description("List available rule presets").option("--json", "Output as JSON").action(async (options) => {
11740
+ return new Command43("list").description("List available rule presets").option("--json", "Output as JSON").action(async (options) => {
11156
11741
  const configService = new ConfigService();
11157
11742
  const config = await configService.load();
11158
11743
  if (!config) {
11159
11744
  throw new ValidationError('Not initialized. Run "jai1 auth" first.');
11160
11745
  }
11161
- console.log(chalk14.cyan("\u{1F4CB} \u0110ang t\u1EA3i danh s\xE1ch rule presets..."));
11746
+ console.log(chalk16.cyan("\u{1F4CB} \u0110ang t\u1EA3i danh s\xE1ch rule presets..."));
11162
11747
  console.log();
11163
11748
  try {
11164
11749
  const response = await fetch(`${config.apiUrl}/api/rules/presets`, {
@@ -11175,23 +11760,23 @@ function createRulesListCommand() {
11175
11760
  return;
11176
11761
  }
11177
11762
  if (data.total === 0) {
11178
- console.log(chalk14.yellow("Kh\xF4ng c\xF3 presets n\xE0o."));
11763
+ console.log(chalk16.yellow("Kh\xF4ng c\xF3 presets n\xE0o."));
11179
11764
  return;
11180
11765
  }
11181
11766
  console.log(
11182
- chalk14.green(`\u2713 T\xECm th\u1EA5y ${chalk14.bold(data.total)} preset${data.total > 1 ? "s" : ""}`)
11767
+ chalk16.green(`\u2713 T\xECm th\u1EA5y ${chalk16.bold(data.total)} preset${data.total > 1 ? "s" : ""}`)
11183
11768
  );
11184
11769
  console.log();
11185
11770
  for (const preset of data.presets) {
11186
- console.log(chalk14.bold.cyan(`\u{1F4E6} ${preset.slug}`));
11187
- const table = new Table4({
11771
+ console.log(chalk16.bold.cyan(`\u{1F4E6} ${preset.slug}`));
11772
+ const table = new Table6({
11188
11773
  style: { head: [], border: ["gray"], compact: true },
11189
11774
  colWidths: [15, 55]
11190
11775
  });
11191
11776
  table.push(
11192
- [chalk14.dim("T\xEAn"), chalk14.white(preset.name)],
11193
- [chalk14.dim("M\xF4 t\u1EA3"), chalk14.white(preset.description)],
11194
- [chalk14.dim("Version"), chalk14.green(`v${preset.version}`)]
11777
+ [chalk16.dim("T\xEAn"), chalk16.white(preset.name)],
11778
+ [chalk16.dim("M\xF4 t\u1EA3"), chalk16.white(preset.description)],
11779
+ [chalk16.dim("Version"), chalk16.green(`v${preset.version}`)]
11195
11780
  );
11196
11781
  const stackParts = [];
11197
11782
  if (preset.stack.frontend) stackParts.push(preset.stack.frontend);
@@ -11199,16 +11784,16 @@ function createRulesListCommand() {
11199
11784
  if (preset.stack.css) stackParts.push(preset.stack.css);
11200
11785
  if (preset.stack.database) stackParts.push(preset.stack.database);
11201
11786
  if (stackParts.length > 0) {
11202
- table.push([chalk14.dim("Stack"), chalk14.yellow(stackParts.join(" + "))]);
11787
+ table.push([chalk16.dim("Stack"), chalk16.yellow(stackParts.join(" + "))]);
11203
11788
  }
11204
11789
  table.push(
11205
- [chalk14.dim("Tags"), chalk14.dim(preset.tags.join(", ") || "-")],
11206
- [chalk14.dim("Downloads"), chalk14.white(preset.downloads.toString())]
11790
+ [chalk16.dim("Tags"), chalk16.dim(preset.tags.join(", ") || "-")],
11791
+ [chalk16.dim("Downloads"), chalk16.white(preset.downloads.toString())]
11207
11792
  );
11208
11793
  console.log(table.toString());
11209
11794
  console.log();
11210
11795
  }
11211
- console.log(chalk14.dim('\u{1F4A1} Ch\u1EA1y "jai1 rules init --preset=<slug>" \u0111\u1EC3 \xE1p d\u1EE5ng preset'));
11796
+ console.log(chalk16.dim('\u{1F4A1} Ch\u1EA1y "jai1 rules init --preset=<slug>" \u0111\u1EC3 \xE1p d\u1EE5ng preset'));
11212
11797
  } catch (error) {
11213
11798
  throw new Error(
11214
11799
  `L\u1ED7i khi t\u1EA3i presets: ${error instanceof Error ? error.message : String(error)}`
@@ -11218,12 +11803,12 @@ function createRulesListCommand() {
11218
11803
  }
11219
11804
 
11220
11805
  // src/commands/rules/init.ts
11221
- import { Command as Command43 } from "commander";
11222
- import { promises as fs16 } from "fs";
11223
- import { join as join8 } from "path";
11806
+ import { Command as Command44 } from "commander";
11807
+ import { promises as fs19 } from "fs";
11808
+ import { join as join9 } from "path";
11224
11809
  import { select as select4, confirm as confirm7 } from "@inquirer/prompts";
11225
11810
  function createRulesInitCommand() {
11226
- return new Command43("init").description("Apply rule preset to project").option("--preset <slug>", "Preset slug to apply").option("--output <format>", "Output format: cursor, agents-md, both (default: cursor)", "cursor").option("-y, --yes", "Skip confirmations").action(async (options) => {
11811
+ return new Command44("init").description("Apply rule preset to project").option("--preset <slug>", "Preset slug to apply").option("--output <format>", "Output format: cursor, agents-md, both (default: cursor)", "cursor").option("-y, --yes", "Skip confirmations").action(async (options) => {
11227
11812
  const configService = new ConfigService();
11228
11813
  const config = await configService.load();
11229
11814
  if (!config) {
@@ -11305,7 +11890,7 @@ function createRulesInitCommand() {
11305
11890
  appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
11306
11891
  customContext: "09-custom.mdc"
11307
11892
  };
11308
- await fs16.writeFile("jai1-rules.json", JSON.stringify(projectConfig, null, 2));
11893
+ await fs19.writeFile("jai1-rules.json", JSON.stringify(projectConfig, null, 2));
11309
11894
  console.log("\u2713 Created jai1-rules.json");
11310
11895
  console.log("\n\u2705 Preset applied successfully!\n");
11311
11896
  console.log("Next steps:");
@@ -11315,11 +11900,11 @@ function createRulesInitCommand() {
11315
11900
  });
11316
11901
  }
11317
11902
  async function applyCursorFormat(bundle) {
11318
- const rulesDir = join8(process.cwd(), ".cursor", "rules");
11319
- await fs16.mkdir(rulesDir, { recursive: true });
11903
+ const rulesDir = join9(process.cwd(), ".cursor", "rules");
11904
+ await fs19.mkdir(rulesDir, { recursive: true });
11320
11905
  for (const [filename, content] of Object.entries(bundle.files)) {
11321
- const filePath = join8(rulesDir, filename);
11322
- await fs16.writeFile(filePath, content, "utf-8");
11906
+ const filePath = join9(rulesDir, filename);
11907
+ await fs19.writeFile(filePath, content, "utf-8");
11323
11908
  console.log(`\u2713 Created .cursor/rules/${filename}`);
11324
11909
  }
11325
11910
  }
@@ -11346,14 +11931,14 @@ async function applyAgentsMdFormat(bundle) {
11346
11931
  }
11347
11932
  }
11348
11933
  const agentsMd = sections.join("\n");
11349
- await fs16.writeFile("AGENTS.md", agentsMd, "utf-8");
11934
+ await fs19.writeFile("AGENTS.md", agentsMd, "utf-8");
11350
11935
  console.log("\u2713 Created AGENTS.md");
11351
11936
  }
11352
11937
 
11353
11938
  // src/commands/rules/apply.ts
11354
- import { Command as Command44 } from "commander";
11355
- import { promises as fs18 } from "fs";
11356
- import { join as join10 } from "path";
11939
+ import { Command as Command45 } from "commander";
11940
+ import { promises as fs21 } from "fs";
11941
+ import { join as join11 } from "path";
11357
11942
  import { select as select5, confirm as confirm8, checkbox as checkbox5 } from "@inquirer/prompts";
11358
11943
 
11359
11944
  // src/services/rules-generator.service.ts
@@ -11713,8 +12298,8 @@ Follow all instructions and patterns defined in AGENTS.md above.
11713
12298
  };
11714
12299
 
11715
12300
  // src/services/backup.service.ts
11716
- import { promises as fs17 } from "fs";
11717
- import { join as join9, dirname } from "path";
12301
+ import { promises as fs20 } from "fs";
12302
+ import { join as join10, dirname } from "path";
11718
12303
  var BackupService = class {
11719
12304
  backupDir = ".jai1/backups";
11720
12305
  /**
@@ -11722,7 +12307,7 @@ var BackupService = class {
11722
12307
  */
11723
12308
  async createBackup(ides, presetSlug) {
11724
12309
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
11725
- const backupPath = join9(this.backupDir, timestamp);
12310
+ const backupPath = join10(this.backupDir, timestamp);
11726
12311
  const backedUpFiles = [];
11727
12312
  let hasContent = false;
11728
12313
  for (const ideId of ides) {
@@ -11731,7 +12316,7 @@ var BackupService = class {
11731
12316
  console.warn(`Unknown IDE format: ${ideId}, skipping backup`);
11732
12317
  continue;
11733
12318
  }
11734
- const rulesPath = format.rulesPath === "." ? process.cwd() : join9(process.cwd(), format.rulesPath);
12319
+ const rulesPath = format.rulesPath === "." ? process.cwd() : join10(process.cwd(), format.rulesPath);
11735
12320
  try {
11736
12321
  const exists = await this.pathExists(rulesPath);
11737
12322
  if (!exists) {
@@ -11744,19 +12329,19 @@ var BackupService = class {
11744
12329
  await this.backupSingleFile("GEMINI.md", backupPath, ideId, backedUpFiles);
11745
12330
  hasContent = true;
11746
12331
  } else {
11747
- const stats = await fs17.stat(rulesPath);
12332
+ const stats = await fs20.stat(rulesPath);
11748
12333
  if (stats.isDirectory()) {
11749
- const files = await fs17.readdir(rulesPath);
12334
+ const files = await fs20.readdir(rulesPath);
11750
12335
  for (const file of files) {
11751
12336
  if (file.endsWith(format.fileExtension)) {
11752
- const originalPath = join9(rulesPath, file);
11753
- const relativePath = join9(format.rulesPath, file);
11754
- const destPath = join9(backupPath, ideId, file);
11755
- await fs17.mkdir(dirname(destPath), { recursive: true });
11756
- await fs17.copyFile(originalPath, destPath);
12337
+ const originalPath = join10(rulesPath, file);
12338
+ const relativePath = join10(format.rulesPath, file);
12339
+ const destPath = join10(backupPath, ideId, file);
12340
+ await fs20.mkdir(dirname(destPath), { recursive: true });
12341
+ await fs20.copyFile(originalPath, destPath);
11757
12342
  backedUpFiles.push({
11758
12343
  originalPath: relativePath,
11759
- backupPath: join9(ideId, file),
12344
+ backupPath: join10(ideId, file),
11760
12345
  ide: ideId
11761
12346
  });
11762
12347
  hasContent = true;
@@ -11777,9 +12362,9 @@ var BackupService = class {
11777
12362
  ides,
11778
12363
  files: backedUpFiles
11779
12364
  };
11780
- await fs17.mkdir(backupPath, { recursive: true });
11781
- await fs17.writeFile(
11782
- join9(backupPath, "metadata.json"),
12365
+ await fs20.mkdir(backupPath, { recursive: true });
12366
+ await fs20.writeFile(
12367
+ join10(backupPath, "metadata.json"),
11783
12368
  JSON.stringify(metadata, null, 2),
11784
12369
  "utf-8"
11785
12370
  );
@@ -11789,18 +12374,18 @@ var BackupService = class {
11789
12374
  * Backup a single file (for AGENTS.md, GEMINI.md)
11790
12375
  */
11791
12376
  async backupSingleFile(filename, backupPath, ideId, backedUpFiles) {
11792
- const originalPath = join9(process.cwd(), filename);
12377
+ const originalPath = join10(process.cwd(), filename);
11793
12378
  try {
11794
12379
  const exists = await this.pathExists(originalPath);
11795
12380
  if (!exists) {
11796
12381
  return;
11797
12382
  }
11798
- const destPath = join9(backupPath, ideId, filename);
11799
- await fs17.mkdir(dirname(destPath), { recursive: true });
11800
- await fs17.copyFile(originalPath, destPath);
12383
+ const destPath = join10(backupPath, ideId, filename);
12384
+ await fs20.mkdir(dirname(destPath), { recursive: true });
12385
+ await fs20.copyFile(originalPath, destPath);
11801
12386
  backedUpFiles.push({
11802
12387
  originalPath: filename,
11803
- backupPath: join9(ideId, filename),
12388
+ backupPath: join10(ideId, filename),
11804
12389
  ide: ideId
11805
12390
  });
11806
12391
  } catch (error) {
@@ -11810,16 +12395,16 @@ var BackupService = class {
11810
12395
  * Restore files from a backup
11811
12396
  */
11812
12397
  async restoreBackup(backupPath) {
11813
- const metadataPath = join9(backupPath, "metadata.json");
11814
- const metadataContent = await fs17.readFile(metadataPath, "utf-8");
12398
+ const metadataPath = join10(backupPath, "metadata.json");
12399
+ const metadataContent = await fs20.readFile(metadataPath, "utf-8");
11815
12400
  const metadata = JSON.parse(metadataContent);
11816
12401
  console.log(`
11817
12402
  Restoring backup from ${metadata.timestamp}...`);
11818
12403
  for (const file of metadata.files) {
11819
- const sourcePath = join9(backupPath, file.backupPath);
11820
- const destPath = join9(process.cwd(), file.originalPath);
11821
- await fs17.mkdir(dirname(destPath), { recursive: true });
11822
- await fs17.copyFile(sourcePath, destPath);
12404
+ const sourcePath = join10(backupPath, file.backupPath);
12405
+ const destPath = join10(process.cwd(), file.originalPath);
12406
+ await fs20.mkdir(dirname(destPath), { recursive: true });
12407
+ await fs20.copyFile(sourcePath, destPath);
11823
12408
  console.log(`\u2713 Restored ${file.originalPath}`);
11824
12409
  }
11825
12410
  console.log("\n\u2705 Backup restored successfully!");
@@ -11829,18 +12414,18 @@ Restoring backup from ${metadata.timestamp}...`);
11829
12414
  */
11830
12415
  async listBackups() {
11831
12416
  try {
11832
- const backupDirPath = join9(process.cwd(), this.backupDir);
12417
+ const backupDirPath = join10(process.cwd(), this.backupDir);
11833
12418
  const exists = await this.pathExists(backupDirPath);
11834
12419
  if (!exists) {
11835
12420
  return [];
11836
12421
  }
11837
- const entries = await fs17.readdir(backupDirPath, { withFileTypes: true });
12422
+ const entries = await fs20.readdir(backupDirPath, { withFileTypes: true });
11838
12423
  const backups = [];
11839
12424
  for (const entry of entries) {
11840
12425
  if (entry.isDirectory()) {
11841
- const metadataPath = join9(backupDirPath, entry.name, "metadata.json");
12426
+ const metadataPath = join10(backupDirPath, entry.name, "metadata.json");
11842
12427
  try {
11843
- const metadataContent = await fs17.readFile(metadataPath, "utf-8");
12428
+ const metadataContent = await fs20.readFile(metadataPath, "utf-8");
11844
12429
  const metadata = JSON.parse(metadataContent);
11845
12430
  backups.push(metadata);
11846
12431
  } catch {
@@ -11857,7 +12442,7 @@ Restoring backup from ${metadata.timestamp}...`);
11857
12442
  * Delete a specific backup
11858
12443
  */
11859
12444
  async deleteBackup(timestamp) {
11860
- const backupPath = join9(process.cwd(), this.backupDir, timestamp);
12445
+ const backupPath = join10(process.cwd(), this.backupDir, timestamp);
11861
12446
  await this.deleteDirectory(backupPath);
11862
12447
  }
11863
12448
  /**
@@ -11887,9 +12472,9 @@ Restoring backup from ${metadata.timestamp}...`);
11887
12472
  /**
11888
12473
  * Check if a path exists
11889
12474
  */
11890
- async pathExists(path10) {
12475
+ async pathExists(path13) {
11891
12476
  try {
11892
- await fs17.access(path10);
12477
+ await fs20.access(path13);
11893
12478
  return true;
11894
12479
  } catch {
11895
12480
  return false;
@@ -11898,22 +12483,22 @@ Restoring backup from ${metadata.timestamp}...`);
11898
12483
  /**
11899
12484
  * Recursively delete a directory
11900
12485
  */
11901
- async deleteDirectory(path10) {
12486
+ async deleteDirectory(path13) {
11902
12487
  try {
11903
- const exists = await this.pathExists(path10);
12488
+ const exists = await this.pathExists(path13);
11904
12489
  if (!exists) {
11905
12490
  return;
11906
12491
  }
11907
- const entries = await fs17.readdir(path10, { withFileTypes: true });
12492
+ const entries = await fs20.readdir(path13, { withFileTypes: true });
11908
12493
  for (const entry of entries) {
11909
- const fullPath = join9(path10, entry.name);
12494
+ const fullPath = join10(path13, entry.name);
11910
12495
  if (entry.isDirectory()) {
11911
12496
  await this.deleteDirectory(fullPath);
11912
12497
  } else {
11913
- await fs17.unlink(fullPath);
12498
+ await fs20.unlink(fullPath);
11914
12499
  }
11915
12500
  }
11916
- await fs17.rmdir(path10);
12501
+ await fs20.rmdir(path13);
11917
12502
  } catch (error) {
11918
12503
  }
11919
12504
  }
@@ -11921,20 +12506,20 @@ Restoring backup from ${metadata.timestamp}...`);
11921
12506
  * Get backup directory path
11922
12507
  */
11923
12508
  getBackupDir() {
11924
- return join9(process.cwd(), this.backupDir);
12509
+ return join10(process.cwd(), this.backupDir);
11925
12510
  }
11926
12511
  /**
11927
12512
  * Ensure backup directory exists
11928
12513
  */
11929
12514
  async ensureBackupDir() {
11930
- const backupDirPath = join9(process.cwd(), this.backupDir);
11931
- await fs17.mkdir(backupDirPath, { recursive: true });
12515
+ const backupDirPath = join10(process.cwd(), this.backupDir);
12516
+ await fs20.mkdir(backupDirPath, { recursive: true });
11932
12517
  }
11933
12518
  };
11934
12519
 
11935
12520
  // src/commands/rules/apply.ts
11936
12521
  function createRulesApplyCommand() {
11937
- return new Command44("apply").description("Apply rule preset to project with multi-IDE support").argument("[preset]", "Preset slug to apply (optional)").option("--ides <ides>", "Comma-separated list of IDE formats (cursor,windsurf,antigravity,claude,agentsmd,gemini)").option("--skip-backup", "Skip backup creation").option("-y, --yes", "Skip all confirmations (auto mode)").action(async (presetSlug, options) => {
12522
+ return new Command45("apply").description("Apply rule preset to project with multi-IDE support").argument("[preset]", "Preset slug to apply (optional)").option("--ides <ides>", "Comma-separated list of IDE formats (cursor,windsurf,antigravity,claude,agentsmd,gemini)").option("--skip-backup", "Skip backup creation").option("-y, --yes", "Skip all confirmations (auto mode)").action(async (presetSlug, options) => {
11938
12523
  const configService = new ConfigService();
11939
12524
  const config = await configService.load();
11940
12525
  if (!config) {
@@ -12084,21 +12669,21 @@ function createRulesApplyCommand() {
12084
12669
  }
12085
12670
  }
12086
12671
  console.log("\n\u{1F4DD} Applying preset...\n");
12087
- const rulePresetDir = join10(process.cwd(), ".jai1", "rule-preset");
12672
+ const rulePresetDir = join11(process.cwd(), ".jai1", "rule-preset");
12088
12673
  try {
12089
- await fs18.rm(rulePresetDir, { recursive: true, force: true });
12674
+ await fs21.rm(rulePresetDir, { recursive: true, force: true });
12090
12675
  } catch {
12091
12676
  }
12092
- await fs18.mkdir(rulePresetDir, { recursive: true });
12093
- await fs18.writeFile(
12094
- join10(rulePresetDir, "preset.json"),
12677
+ await fs21.mkdir(rulePresetDir, { recursive: true });
12678
+ await fs21.writeFile(
12679
+ join11(rulePresetDir, "preset.json"),
12095
12680
  JSON.stringify(bundle.preset, null, 2),
12096
12681
  "utf-8"
12097
12682
  );
12098
12683
  for (const [filename, content] of Object.entries(bundle.files)) {
12099
- const filePath = join10(rulePresetDir, filename);
12100
- await fs18.mkdir(join10(filePath, ".."), { recursive: true });
12101
- await fs18.writeFile(filePath, content, "utf-8");
12684
+ const filePath = join11(rulePresetDir, filename);
12685
+ await fs21.mkdir(join11(filePath, ".."), { recursive: true });
12686
+ await fs21.writeFile(filePath, content, "utf-8");
12102
12687
  }
12103
12688
  console.log(`\u2713 Saved preset to .jai1/rule-preset/`);
12104
12689
  const allGeneratedFiles = [];
@@ -12106,9 +12691,9 @@ function createRulesApplyCommand() {
12106
12691
  try {
12107
12692
  const files = generatorService.generateForIde(bundle, ideId);
12108
12693
  for (const file of files) {
12109
- const fullPath = join10(process.cwd(), file.path);
12110
- await fs18.mkdir(join10(fullPath, ".."), { recursive: true });
12111
- await fs18.writeFile(fullPath, file.content, "utf-8");
12694
+ const fullPath = join11(process.cwd(), file.path);
12695
+ await fs21.mkdir(join11(fullPath, ".."), { recursive: true });
12696
+ await fs21.writeFile(fullPath, file.content, "utf-8");
12112
12697
  console.log(`\u2713 [${ideId}] ${file.path}`);
12113
12698
  allGeneratedFiles.push({
12114
12699
  ide: ideId,
@@ -12135,8 +12720,8 @@ function createRulesApplyCommand() {
12135
12720
  ] : []
12136
12721
  };
12137
12722
  try {
12138
- const existingConfigPath = join10(process.cwd(), "jai1-rules.json");
12139
- const existingConfigContent = await fs18.readFile(existingConfigPath, "utf-8");
12723
+ const existingConfigPath = join11(process.cwd(), "jai1-rules.json");
12724
+ const existingConfigContent = await fs21.readFile(existingConfigPath, "utf-8");
12140
12725
  const existingConfig = JSON.parse(existingConfigContent);
12141
12726
  if (existingConfig.backups && existingConfig.backups.length > 0) {
12142
12727
  projectConfig.backups = [
@@ -12147,8 +12732,8 @@ function createRulesApplyCommand() {
12147
12732
  }
12148
12733
  } catch {
12149
12734
  }
12150
- await fs18.writeFile(
12151
- join10(process.cwd(), "jai1-rules.json"),
12735
+ await fs21.writeFile(
12736
+ join11(process.cwd(), "jai1-rules.json"),
12152
12737
  JSON.stringify(projectConfig, null, 2),
12153
12738
  "utf-8"
12154
12739
  );
@@ -12179,11 +12764,11 @@ function createRulesApplyCommand() {
12179
12764
  }
12180
12765
 
12181
12766
  // src/commands/rules/restore.ts
12182
- import { Command as Command45 } from "commander";
12183
- import { join as join11 } from "path";
12767
+ import { Command as Command46 } from "commander";
12768
+ import { join as join12 } from "path";
12184
12769
  import { select as select6, confirm as confirm9 } from "@inquirer/prompts";
12185
12770
  function createRulesRestoreCommand() {
12186
- return new Command45("restore").description("Restore rules from a backup").option("--latest", "Restore the most recent backup").option("-y, --yes", "Skip confirmation").action(async (options) => {
12771
+ return new Command46("restore").description("Restore rules from a backup").option("--latest", "Restore the most recent backup").option("-y, --yes", "Skip confirmation").action(async (options) => {
12187
12772
  const backupService = new BackupService();
12188
12773
  const backups = await backupService.listBackups();
12189
12774
  if (backups.length === 0) {
@@ -12226,7 +12811,7 @@ function createRulesRestoreCommand() {
12226
12811
  }
12227
12812
  console.log("\n\u{1F504} Restoring backup...\n");
12228
12813
  try {
12229
- const backupPath = join11(backupService.getBackupDir(), selectedBackup.timestamp);
12814
+ const backupPath = join12(backupService.getBackupDir(), selectedBackup.timestamp);
12230
12815
  await backupService.restoreBackup(backupPath);
12231
12816
  console.log("\n\u2705 Backup restored successfully!\n");
12232
12817
  console.log("\u{1F4A1} Tip: Your IDE may need to be restarted to pick up the changes.");
@@ -12251,16 +12836,16 @@ function formatTimestamp(timestamp) {
12251
12836
  }
12252
12837
 
12253
12838
  // src/commands/rules/sync.ts
12254
- import { Command as Command46 } from "commander";
12255
- import { promises as fs19 } from "fs";
12256
- import { join as join12 } from "path";
12257
- import { confirm as confirm10 } from "@inquirer/prompts";
12839
+ import { Command as Command47 } from "commander";
12840
+ import { promises as fs22 } from "fs";
12841
+ import { join as join13 } from "path";
12842
+ import { checkbox as checkbox6, confirm as confirm10 } from "@inquirer/prompts";
12258
12843
  function createRulesSyncCommand() {
12259
- return new Command46("sync").description("Regenerate rule outputs for all configured IDEs").option("--ides <ides>", "Comma-separated list of IDEs to sync (default: all configured)").option("--detect", "Auto-detect active IDEs instead of using config").option("-y, --yes", "Skip confirmations").action(async (options) => {
12260
- const configPath = join12(process.cwd(), "jai1-rules.json");
12844
+ return new Command47("sync").description("Regenerate rule outputs for all configured IDEs").option("--ides <ides>", "Comma-separated list of IDEs to sync (default: all configured)").option("--detect", "Auto-detect active IDEs instead of using config").option("-y, --yes", "Skip confirmations").action(async (options) => {
12845
+ const configPath = join13(process.cwd(), "jai1-rules.json");
12261
12846
  let projectConfig;
12262
12847
  try {
12263
- const configContent = await fs19.readFile(configPath, "utf-8");
12848
+ const configContent = await fs22.readFile(configPath, "utf-8");
12264
12849
  projectConfig = JSON.parse(configContent);
12265
12850
  } catch {
12266
12851
  throw new ValidationError(
@@ -12307,6 +12892,35 @@ Detected ${detected.length} active IDE(s):
12307
12892
  \u26A0\uFE0F IDEs not in config: ${notConfigured.join(", ")}`);
12308
12893
  console.log(" They will still be synced, but may not be in jai1-rules.json");
12309
12894
  }
12895
+ } else if (!options.yes) {
12896
+ const currentIdes = projectConfig.ides || [];
12897
+ console.log(`
12898
+ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
12899
+ const detectionService = new IdeDetectionService();
12900
+ const detected = await detectionService.detectActiveIdes();
12901
+ const suggestions = await detectionService.suggestIdes();
12902
+ if (detected.length > 0) {
12903
+ const detectedCount = detected.reduce((sum, d) => sum + d.ruleCount + d.workflowCount, 0);
12904
+ console.log(` (${detectedCount} files detected)`);
12905
+ }
12906
+ if (suggestions.length > 0) {
12907
+ console.log("\n\u{1F4A1} Smart suggestions based on your project:\n");
12908
+ suggestions.slice(0, 3).forEach((s) => {
12909
+ const priority = s.priority === "high" ? "\u2B50" : s.priority === "medium" ? "\u{1F538}" : "\u25AB\uFE0F";
12910
+ console.log(`${priority} ${s.name} - ${s.reason}`);
12911
+ });
12912
+ console.log("");
12913
+ }
12914
+ const choices = buildIdeChoices(currentIdes, detected, suggestions);
12915
+ idesToSync = await checkbox6({
12916
+ message: "Select IDE formats to sync (pre-selected are recommended):",
12917
+ choices,
12918
+ required: true
12919
+ });
12920
+ if (idesToSync.length === 0) {
12921
+ console.log("Cancelled.");
12922
+ return;
12923
+ }
12310
12924
  } else {
12311
12925
  idesToSync = projectConfig.ides || [];
12312
12926
  if (idesToSync.length === 0) {
@@ -12340,14 +12954,14 @@ Detected ${detected.length} active IDE(s):
12340
12954
  throw new Error(`Failed to fetch preset: ${presetResponse.statusText}`);
12341
12955
  }
12342
12956
  const bundle = await presetResponse.json();
12343
- const rulePresetDir = join12(process.cwd(), ".jai1", "rule-preset");
12957
+ const rulePresetDir = join13(process.cwd(), ".jai1", "rule-preset");
12344
12958
  const presetExists = await checkPathExists(rulePresetDir);
12345
12959
  if (presetExists) {
12346
- const files = await fs19.readdir(rulePresetDir);
12960
+ const files = await fs22.readdir(rulePresetDir);
12347
12961
  for (const file of files) {
12348
12962
  if (file.endsWith(".mdc")) {
12349
- const filePath = join12(rulePresetDir, file);
12350
- const content = await fs19.readFile(filePath, "utf-8");
12963
+ const filePath = join13(rulePresetDir, file);
12964
+ const content = await fs22.readFile(filePath, "utf-8");
12351
12965
  bundle.files[file] = content;
12352
12966
  }
12353
12967
  }
@@ -12366,9 +12980,9 @@ Detected ${detected.length} active IDE(s):
12366
12980
  }
12367
12981
  const files = generatorService.generateForIde(bundle, ideId);
12368
12982
  for (const file of files) {
12369
- const fullPath = join12(process.cwd(), file.path);
12370
- await fs19.mkdir(join12(fullPath, ".."), { recursive: true });
12371
- await fs19.writeFile(fullPath, file.content, "utf-8");
12983
+ const fullPath = join13(process.cwd(), file.path);
12984
+ await fs22.mkdir(join13(fullPath, ".."), { recursive: true });
12985
+ await fs22.writeFile(fullPath, file.content, "utf-8");
12372
12986
  }
12373
12987
  console.log(`\u2713 ${format.name} - ${files.length} files regenerated`);
12374
12988
  } catch (error) {
@@ -12377,8 +12991,8 @@ Detected ${detected.length} active IDE(s):
12377
12991
  }
12378
12992
  if (JSON.stringify(projectConfig.ides) !== JSON.stringify(idesToSync)) {
12379
12993
  projectConfig.ides = idesToSync;
12380
- await fs19.writeFile(
12381
- join12(process.cwd(), "jai1-rules.json"),
12994
+ await fs22.writeFile(
12995
+ join13(process.cwd(), "jai1-rules.json"),
12382
12996
  JSON.stringify(projectConfig, null, 2),
12383
12997
  "utf-8"
12384
12998
  );
@@ -12391,9 +13005,44 @@ Detected ${detected.length} active IDE(s):
12391
13005
  console.log(" \u2022 Edit source files in .jai1/rule-preset/ and sync again\n");
12392
13006
  });
12393
13007
  }
13008
+ function buildIdeChoices(currentIdes, detected, suggestions) {
13009
+ const choices = [
13010
+ {
13011
+ name: "Cursor (.cursor/rules/)",
13012
+ value: "cursor",
13013
+ checked: currentIdes.includes("cursor") || detected.some((d) => d.id === "cursor")
13014
+ },
13015
+ {
13016
+ name: "Windsurf (.windsurf/rules/)",
13017
+ value: "windsurf",
13018
+ checked: currentIdes.includes("windsurf") || detected.some((d) => d.id === "windsurf")
13019
+ },
13020
+ {
13021
+ name: "Antigravity (.agent/rules/)",
13022
+ value: "antigravity",
13023
+ checked: currentIdes.includes("antigravity") || detected.some((d) => d.id === "antigravity")
13024
+ },
13025
+ {
13026
+ name: "Claude Code (.claude/rules/)",
13027
+ value: "claude",
13028
+ checked: currentIdes.includes("claude") || detected.some((d) => d.id === "claude")
13029
+ },
13030
+ {
13031
+ name: "AGENTS.md (single file)",
13032
+ value: "agentsmd",
13033
+ checked: currentIdes.includes("agentsmd") || detected.some((d) => d.id === "agentsmd")
13034
+ },
13035
+ {
13036
+ name: "Gemini CLI (GEMINI.md)",
13037
+ value: "gemini",
13038
+ checked: currentIdes.includes("gemini") || detected.some((d) => d.id === "gemini")
13039
+ }
13040
+ ];
13041
+ return choices;
13042
+ }
12394
13043
  async function checkPathExists(absolutePath) {
12395
13044
  try {
12396
- await fs19.access(absolutePath);
13045
+ await fs22.access(absolutePath);
12397
13046
  return true;
12398
13047
  } catch {
12399
13048
  return false;
@@ -12401,15 +13050,15 @@ async function checkPathExists(absolutePath) {
12401
13050
  }
12402
13051
 
12403
13052
  // src/commands/rules/info.ts
12404
- import { Command as Command47 } from "commander";
12405
- import { promises as fs20 } from "fs";
12406
- import { join as join13 } from "path";
13053
+ import { Command as Command48 } from "commander";
13054
+ import { promises as fs23 } from "fs";
13055
+ import { join as join14 } from "path";
12407
13056
  function createRulesInfoCommand() {
12408
- return new Command47("info").description("Show current preset information").option("--json", "Output as JSON").action(async (options) => {
12409
- const configPath = join13(process.cwd(), "jai1-rules.json");
13057
+ return new Command48("info").description("Show current preset information").option("--json", "Output as JSON").action(async (options) => {
13058
+ const configPath = join14(process.cwd(), "jai1-rules.json");
12410
13059
  let projectConfig;
12411
13060
  try {
12412
- const configContent = await fs20.readFile(configPath, "utf-8");
13061
+ const configContent = await fs23.readFile(configPath, "utf-8");
12413
13062
  projectConfig = JSON.parse(configContent);
12414
13063
  } catch {
12415
13064
  throw new ValidationError(
@@ -12421,14 +13070,14 @@ function createRulesInfoCommand() {
12421
13070
  return;
12422
13071
  }
12423
13072
  console.log("\u{1F4CB} Current Preset Information\n");
12424
- const rulePresetDir = join13(process.cwd(), ".jai1", "rule-preset");
12425
- const presetJsonPath = join13(rulePresetDir, "preset.json");
13073
+ const rulePresetDir = join14(process.cwd(), ".jai1", "rule-preset");
13074
+ const presetJsonPath = join14(rulePresetDir, "preset.json");
12426
13075
  let presetMetadata = null;
12427
13076
  let presetFiles = [];
12428
13077
  try {
12429
- const presetContent = await fs20.readFile(presetJsonPath, "utf-8");
13078
+ const presetContent = await fs23.readFile(presetJsonPath, "utf-8");
12430
13079
  presetMetadata = JSON.parse(presetContent);
12431
- const files = await fs20.readdir(rulePresetDir);
13080
+ const files = await fs23.readdir(rulePresetDir);
12432
13081
  presetFiles = files.filter((f) => f.endsWith(".mdc"));
12433
13082
  } catch {
12434
13083
  }
@@ -12487,9 +13136,9 @@ Available Backups (${projectConfig.backups.length}):`);
12487
13136
  console.log(' \u2022 "jai1 rules apply" - Apply a different preset (replaces current)');
12488
13137
  });
12489
13138
  }
12490
- async function checkPathExists2(path10) {
13139
+ async function checkPathExists2(path13) {
12491
13140
  try {
12492
- await fs20.access(join13(process.cwd(), path10));
13141
+ await fs23.access(join14(process.cwd(), path13));
12493
13142
  return true;
12494
13143
  } catch {
12495
13144
  return false;
@@ -12511,26 +13160,26 @@ async function checkIdeFilesExist(ideId, format) {
12511
13160
 
12512
13161
  // src/commands/rules/index.ts
12513
13162
  function showRulesHelp() {
12514
- console.log(chalk15.bold.cyan("\u{1F4CB} jai1 rules") + chalk15.dim(" - Qu\u1EA3n l\xFD rule presets cho AI agents"));
13163
+ console.log(chalk17.bold.cyan("\u{1F4CB} jai1 rules") + chalk17.dim(" - Qu\u1EA3n l\xFD rule presets cho AI agents"));
12515
13164
  console.log();
12516
- console.log(chalk15.bold("C\xE1c l\u1EC7nh:"));
12517
- console.log(` ${chalk15.cyan("list")} Li\u1EC7t k\xEA c\xE1c presets c\xF3 s\u1EB5n`);
12518
- console.log(` ${chalk15.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t preset`);
12519
- console.log(` ${chalk15.cyan("init")} Kh\u1EDFi t\u1EA1o rules t\u1EEB preset`);
12520
- console.log(` ${chalk15.cyan("apply")} \xC1p d\u1EE5ng preset v\xE0o project`);
12521
- console.log(` ${chalk15.cyan("sync")} \u0110\u1ED3ng b\u1ED9 rules v\u1EDBi server`);
12522
- console.log(` ${chalk15.cyan("restore")} Kh\xF4i ph\u1EE5c rules t\u1EEB backup`);
13165
+ console.log(chalk17.bold("C\xE1c l\u1EC7nh:"));
13166
+ console.log(` ${chalk17.cyan("list")} Li\u1EC7t k\xEA c\xE1c presets c\xF3 s\u1EB5n`);
13167
+ console.log(` ${chalk17.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t preset`);
13168
+ console.log(` ${chalk17.cyan("init")} Kh\u1EDFi t\u1EA1o rules t\u1EEB preset`);
13169
+ console.log(` ${chalk17.cyan("apply")} \xC1p d\u1EE5ng preset v\xE0o project`);
13170
+ console.log(` ${chalk17.cyan("sync")} \u0110\u1ED3ng b\u1ED9 rules v\u1EDBi server`);
13171
+ console.log(` ${chalk17.cyan("restore")} Kh\xF4i ph\u1EE5c rules t\u1EEB backup`);
12523
13172
  console.log();
12524
- console.log(chalk15.bold("V\xED d\u1EE5:"));
12525
- console.log(chalk15.dim(" $ jai1 rules list"));
12526
- console.log(chalk15.dim(" $ jai1 rules info react-typescript"));
12527
- console.log(chalk15.dim(" $ jai1 rules init --preset=react-typescript"));
12528
- console.log(chalk15.dim(" $ jai1 rules apply react-typescript"));
13173
+ console.log(chalk17.bold("V\xED d\u1EE5:"));
13174
+ console.log(chalk17.dim(" $ jai1 rules list"));
13175
+ console.log(chalk17.dim(" $ jai1 rules info react-typescript"));
13176
+ console.log(chalk17.dim(" $ jai1 rules init --preset=react-typescript"));
13177
+ console.log(chalk17.dim(" $ jai1 rules apply react-typescript"));
12529
13178
  console.log();
12530
- console.log(chalk15.dim('Ch\u1EA1y "jai1 rules <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
13179
+ console.log(chalk17.dim('Ch\u1EA1y "jai1 rules <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
12531
13180
  }
12532
13181
  function createRulesCommand() {
12533
- const rulesCommand = new Command48("rules").description("Manage rule presets for AI agents").action(() => {
13182
+ const rulesCommand = new Command49("rules").description("Manage rule presets for AI agents").action(() => {
12534
13183
  showRulesHelp();
12535
13184
  });
12536
13185
  rulesCommand.addCommand(createRulesListCommand());
@@ -12543,10 +13192,10 @@ function createRulesCommand() {
12543
13192
  }
12544
13193
 
12545
13194
  // src/commands/upgrade.ts
12546
- import { Command as Command49 } from "commander";
13195
+ import { Command as Command50 } from "commander";
12547
13196
  import { confirm as confirm11 } from "@inquirer/prompts";
12548
- import { execSync as execSync2 } from "child_process";
12549
- var colors4 = {
13197
+ import { execSync as execSync4 } from "child_process";
13198
+ var colors3 = {
12550
13199
  yellow: "\x1B[33m",
12551
13200
  green: "\x1B[32m",
12552
13201
  cyan: "\x1B[36m",
@@ -12555,7 +13204,7 @@ var colors4 = {
12555
13204
  bold: "\x1B[1m"
12556
13205
  };
12557
13206
  function createUpgradeCommand() {
12558
- return new Command49("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) => {
13207
+ return new Command50("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) => {
12559
13208
  await handleUpgrade(options);
12560
13209
  });
12561
13210
  }
@@ -12566,7 +13215,7 @@ async function handleUpgrade(options) {
12566
13215
  throw new ValidationError('Not initialized. Run "jai1 auth" first.');
12567
13216
  }
12568
13217
  try {
12569
- console.log(`${colors4.cyan}\u{1F50D} Checking for updates...${colors4.reset}`);
13218
+ console.log(`${colors3.cyan}\u{1F50D} Checking for updates...${colors3.reset}`);
12570
13219
  const response = await fetch(`${config.apiUrl}/api/versions/client`, {
12571
13220
  headers: {
12572
13221
  "JAI1-Access-Key": config.accessKey
@@ -12581,20 +13230,20 @@ async function handleUpgrade(options) {
12581
13230
  const latestVersion = data.version;
12582
13231
  const currentVersion = package_default.version;
12583
13232
  console.log(`
12584
- ${colors4.bold}Current version:${colors4.reset} ${currentVersion}`);
12585
- console.log(`${colors4.bold}Latest version:${colors4.reset} ${latestVersion}
13233
+ ${colors3.bold}Current version:${colors3.reset} ${currentVersion}`);
13234
+ console.log(`${colors3.bold}Latest version:${colors3.reset} ${latestVersion}
12586
13235
  `);
12587
13236
  if (!isNewerVersion2(latestVersion, currentVersion)) {
12588
- console.log(`${colors4.green}\u2705 You're already on the latest version!${colors4.reset}
13237
+ console.log(`${colors3.green}\u2705 You're already on the latest version!${colors3.reset}
12589
13238
  `);
12590
13239
  return;
12591
13240
  }
12592
- console.log(`${colors4.yellow}\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E${colors4.reset}`);
12593
- console.log(`${colors4.yellow}\u2502${colors4.reset} ${colors4.bold}\u2B06\uFE0F Update available!${colors4.reset} ${currentVersion} \u2192 ${colors4.cyan}${latestVersion}${colors4.reset} ${colors4.yellow}\u2502${colors4.reset}`);
12594
- console.log(`${colors4.yellow}\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F${colors4.reset}
13241
+ console.log(`${colors3.yellow}\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E${colors3.reset}`);
13242
+ console.log(`${colors3.yellow}\u2502${colors3.reset} ${colors3.bold}\u2B06\uFE0F Update available!${colors3.reset} ${currentVersion} \u2192 ${colors3.cyan}${latestVersion}${colors3.reset} ${colors3.yellow}\u2502${colors3.reset}`);
13243
+ console.log(`${colors3.yellow}\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F${colors3.reset}
12595
13244
  `);
12596
13245
  if (options.check) {
12597
- console.log(`${colors4.cyan}Run "jai1 upgrade" to install the latest version.${colors4.reset}
13246
+ console.log(`${colors3.cyan}Run "jai1 upgrade" to install the latest version.${colors3.reset}
12598
13247
  `);
12599
13248
  return;
12600
13249
  }
@@ -12604,24 +13253,24 @@ ${colors4.bold}Current version:${colors4.reset} ${currentVersion}`);
12604
13253
  default: true
12605
13254
  });
12606
13255
  if (!shouldUpdate) {
12607
- console.log(`${colors4.yellow}\u23F8\uFE0F Upgrade cancelled.${colors4.reset}
13256
+ console.log(`${colors3.yellow}\u23F8\uFE0F Upgrade cancelled.${colors3.reset}
12608
13257
  `);
12609
13258
  return;
12610
13259
  }
12611
13260
  }
12612
13261
  console.log(`
12613
- ${colors4.cyan}\u{1F4E5} Installing latest version...${colors4.reset}
13262
+ ${colors3.cyan}\u{1F4E5} Installing latest version...${colors3.reset}
12614
13263
  `);
12615
13264
  try {
12616
13265
  const packageManager2 = detectPackageManager();
12617
13266
  const installCommand = getInstallCommand(packageManager2);
12618
- console.log(`${colors4.cyan}Using ${packageManager2}...${colors4.reset}`);
12619
- execSync2(installCommand, {
13267
+ console.log(`${colors3.cyan}Using ${packageManager2}...${colors3.reset}`);
13268
+ execSync4(installCommand, {
12620
13269
  stdio: "inherit",
12621
13270
  env: { ...process.env, FORCE_COLOR: "1" }
12622
13271
  });
12623
13272
  console.log(`
12624
- ${colors4.green}\u2705 Successfully upgraded to version ${latestVersion}!${colors4.reset}
13273
+ ${colors3.green}\u2705 Successfully upgraded to version ${latestVersion}!${colors3.reset}
12625
13274
  `);
12626
13275
  trackAction("upgrade", {
12627
13276
  from_version: currentVersion,
@@ -12631,17 +13280,17 @@ ${colors4.green}\u2705 Successfully upgraded to version ${latestVersion}!${color
12631
13280
  disableUpdateCheck();
12632
13281
  } catch (error) {
12633
13282
  console.error(`
12634
- ${colors4.red}\u274C Upgrade failed!${colors4.reset}`);
12635
- console.error(`${colors4.red}Error: ${error instanceof Error ? error.message : "Unknown error"}${colors4.reset}
13283
+ ${colors3.red}\u274C Upgrade failed!${colors3.reset}`);
13284
+ console.error(`${colors3.red}Error: ${error instanceof Error ? error.message : "Unknown error"}${colors3.reset}
12636
13285
  `);
12637
- console.error(`${colors4.yellow}\u{1F4A1} You can try manually upgrading with:${colors4.reset}`);
13286
+ console.error(`${colors3.yellow}\u{1F4A1} You can try manually upgrading with:${colors3.reset}`);
12638
13287
  const manualCommands = {
12639
13288
  npm: `npm install -g @jvittechs/jai1-cli@latest`,
12640
13289
  pnpm: `pnpm add -g @jvittechs/jai1-cli@latest`,
12641
13290
  yarn: `yarn global add @jvittechs/jai1-cli@latest`,
12642
13291
  bun: `bun add -g @jvittechs/jai1-cli@latest`
12643
13292
  };
12644
- console.error(` ${colors4.cyan}${manualCommands[packageManager]}${colors4.reset}
13293
+ console.error(` ${colors3.cyan}${manualCommands[packageManager]}${colors3.reset}
12645
13294
  `);
12646
13295
  throw error;
12647
13296
  }
@@ -12665,7 +13314,7 @@ function isNewerVersion2(remote, local) {
12665
13314
  }
12666
13315
  function detectPackageManager() {
12667
13316
  try {
12668
- const jai1Path = execSync2("which jai1 || where jai1", {
13317
+ const jai1Path = execSync4("which jai1 || where jai1", {
12669
13318
  encoding: "utf-8",
12670
13319
  stdio: ["pipe", "pipe", "ignore"]
12671
13320
  }).trim();
@@ -12703,11 +13352,11 @@ function getInstallCommand(packageManager2) {
12703
13352
  }
12704
13353
 
12705
13354
  // src/commands/clean.ts
12706
- import { Command as Command50 } from "commander";
13355
+ import { Command as Command51 } from "commander";
12707
13356
  import { confirm as confirm12, select as select7 } from "@inquirer/prompts";
12708
- import { join as join14 } from "path";
13357
+ import { join as join15 } from "path";
12709
13358
  function createCleanCommand() {
12710
- return new Command50("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) => {
13359
+ return new Command51("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) => {
12711
13360
  await handleClean(options);
12712
13361
  });
12713
13362
  }
@@ -12718,7 +13367,7 @@ async function handleClean(options) {
12718
13367
  {
12719
13368
  name: "Backups",
12720
13369
  description: "Component backup files (.jai1_backup/)",
12721
- path: join14(cwd, ".jai1_backup"),
13370
+ path: join15(cwd, ".jai1_backup"),
12722
13371
  check: async () => {
12723
13372
  const backups = await service.listBackups(cwd);
12724
13373
  return { exists: backups.length > 0, count: backups.length };
@@ -12820,10 +13469,10 @@ async function cleanTarget(target, skipConfirm) {
12820
13469
  }
12821
13470
 
12822
13471
  // src/commands/redmine/check.ts
12823
- import { Command as Command51 } from "commander";
13472
+ import { Command as Command52 } from "commander";
12824
13473
 
12825
13474
  // src/services/redmine-config.service.ts
12826
- import { readFile as readFile6 } from "fs/promises";
13475
+ import { readFile as readFile7 } from "fs/promises";
12827
13476
  import { resolve as resolve2 } from "path";
12828
13477
 
12829
13478
  // src/types/redmine.types.ts
@@ -12882,7 +13531,7 @@ var RedmineConfigService = class {
12882
13531
  */
12883
13532
  async load() {
12884
13533
  try {
12885
- const content = await readFile6(this.configPath, "utf-8");
13534
+ const content = await readFile7(this.configPath, "utf-8");
12886
13535
  const rawConfig = parse(content);
12887
13536
  return RedmineConfigSchema.parse(rawConfig);
12888
13537
  } catch (error) {
@@ -12921,7 +13570,7 @@ var RedmineConfigService = class {
12921
13570
  // src/api.ts
12922
13571
  import { fetch as fetch2 } from "undici";
12923
13572
  import pRetry2 from "p-retry";
12924
- import pLimit3 from "p-limit";
13573
+ import pLimit4 from "p-limit";
12925
13574
  var RedmineApiError = class extends Error {
12926
13575
  constructor(message, status, response) {
12927
13576
  super(message);
@@ -12939,10 +13588,10 @@ var RedmineApiClient = class {
12939
13588
  this.baseUrl = config.baseUrl.replace(/\/$/, "");
12940
13589
  this.apiAccessToken = config.apiAccessToken;
12941
13590
  this.retryConfig = config.defaults.retry;
12942
- this.concurrencyLimit = pLimit3(config.defaults.concurrency);
13591
+ this.concurrencyLimit = pLimit4(config.defaults.concurrency);
12943
13592
  }
12944
- async request(path10, options = {}) {
12945
- const url = `${this.baseUrl}${path10}`;
13593
+ async request(path13, options = {}) {
13594
+ const url = `${this.baseUrl}${path13}`;
12946
13595
  const headers = {
12947
13596
  "X-Redmine-API-Key": this.apiAccessToken,
12948
13597
  "Content-Type": "application/json",
@@ -13003,8 +13652,8 @@ var RedmineApiClient = class {
13003
13652
  if (include && include.length > 0) {
13004
13653
  params.append("include", include.join(","));
13005
13654
  }
13006
- const path10 = `/issues/${issueId}.json${params.toString() ? `?${params.toString()}` : ""}`;
13007
- return this.request(path10);
13655
+ const path13 = `/issues/${issueId}.json${params.toString() ? `?${params.toString()}` : ""}`;
13656
+ return this.request(path13);
13008
13657
  }
13009
13658
  async getIssues(projectId, options = {}) {
13010
13659
  const params = new URLSearchParams();
@@ -13024,8 +13673,8 @@ var RedmineApiClient = class {
13024
13673
  if (options.updatedSince) {
13025
13674
  params.append("updated_on", `>=${options.updatedSince}`);
13026
13675
  }
13027
- const path10 = `/issues.json?${params.toString()}`;
13028
- return this.request(path10);
13676
+ const path13 = `/issues.json?${params.toString()}`;
13677
+ return this.request(path13);
13029
13678
  }
13030
13679
  async getAllIssues(projectId, options = {}) {
13031
13680
  const pageSize = options.pageSize || 100;
@@ -13127,7 +13776,7 @@ async function checkConnectivity(config) {
13127
13776
 
13128
13777
  // src/commands/redmine/check.ts
13129
13778
  function createRedmineCheckCommand() {
13130
- const cmd = new Command51("check").description("Check Redmine connectivity").option("-c, --config <path>", "Config file path", "redmine.config.yaml").option("--json", "Output as JSON").action(async (options) => {
13779
+ const cmd = new Command52("check").description("Check Redmine connectivity").option("-c, --config <path>", "Config file path", "redmine.config.yaml").option("--json", "Output as JSON").action(async (options) => {
13131
13780
  await handleRedmineCheck(options);
13132
13781
  });
13133
13782
  return cmd;
@@ -13155,7 +13804,7 @@ async function handleRedmineCheck(options) {
13155
13804
  }
13156
13805
 
13157
13806
  // src/commands/redmine/sync-issue.ts
13158
- import { Command as Command52 } from "commander";
13807
+ import { Command as Command53 } from "commander";
13159
13808
 
13160
13809
  // src/sync-issue.ts
13161
13810
  import { resolve as resolve3, relative } from "path";
@@ -13297,7 +13946,7 @@ function generateFilename(issueId, title, config, existingSlugs = /* @__PURE__ *
13297
13946
  }
13298
13947
 
13299
13948
  // src/file.util.ts
13300
- import { readFile as readFile7, writeFile as writeFile2, mkdir } from "fs/promises";
13949
+ import { readFile as readFile8, writeFile as writeFile2, mkdir } from "fs/promises";
13301
13950
  import { dirname as dirname2 } from "path";
13302
13951
  import matter4 from "gray-matter";
13303
13952
  async function ensureDir(filePath) {
@@ -13306,7 +13955,7 @@ async function ensureDir(filePath) {
13306
13955
  }
13307
13956
  async function readMarkdownFile(filePath) {
13308
13957
  try {
13309
- const content = await readFile7(filePath, "utf-8");
13958
+ const content = await readFile8(filePath, "utf-8");
13310
13959
  return parseMarkdownContent(content);
13311
13960
  } catch (error) {
13312
13961
  if (error.code === "ENOENT") {
@@ -13539,7 +14188,7 @@ function extractIssueIdFromUrl(url) {
13539
14188
 
13540
14189
  // src/commands/redmine/sync-issue.ts
13541
14190
  function createSyncIssueCommand() {
13542
- const cmd = new Command52("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) => {
14191
+ const cmd = new Command53("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) => {
13543
14192
  await handleSyncIssue(options);
13544
14193
  });
13545
14194
  return cmd;
@@ -13583,7 +14232,7 @@ async function handleSyncIssue(options) {
13583
14232
  }
13584
14233
 
13585
14234
  // src/commands/redmine/sync-project.ts
13586
- import { Command as Command53 } from "commander";
14235
+ import { Command as Command54 } from "commander";
13587
14236
 
13588
14237
  // src/sync-project.ts
13589
14238
  async function syncProject(config, options = {}) {
@@ -13653,7 +14302,7 @@ async function syncProject(config, options = {}) {
13653
14302
 
13654
14303
  // src/commands/redmine/sync-project.ts
13655
14304
  function createSyncProjectCommand() {
13656
- const cmd = new Command53("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) => {
14305
+ const cmd = new Command54("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) => {
13657
14306
  await handleSyncProject(options);
13658
14307
  });
13659
14308
  return cmd;
@@ -13708,12 +14357,12 @@ async function handleSyncProject(options) {
13708
14357
  }
13709
14358
 
13710
14359
  // src/commands/framework/info.ts
13711
- import { Command as Command54 } from "commander";
13712
- import { promises as fs21 } from "fs";
13713
- import { join as join15 } from "path";
14360
+ import { Command as Command55 } from "commander";
14361
+ import { promises as fs24 } from "fs";
14362
+ import { join as join16 } from "path";
13714
14363
  import { homedir as homedir5 } from "os";
13715
14364
  function createInfoCommand() {
13716
- const cmd = new Command54("info").description("Show jai1-client configuration and status").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
14365
+ const cmd = new Command55("info").description("Show jai1-client configuration and status").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
13717
14366
  await handleInfo(options);
13718
14367
  });
13719
14368
  return cmd;
@@ -13724,7 +14373,7 @@ async function handleInfo(options) {
13724
14373
  if (!config) {
13725
14374
  throw new ValidationError('Not initialized. Run "jai1 auth" first.');
13726
14375
  }
13727
- const frameworkPath = join15(homedir5(), ".jai1", "framework");
14376
+ const frameworkPath = join16(homedir5(), ".jai1", "framework");
13728
14377
  const projectStatus = await getProjectStatus2();
13729
14378
  const info = {
13730
14379
  configPath: configService.getConfigPath(),
@@ -13759,9 +14408,9 @@ function maskKey3(key) {
13759
14408
  return "****" + key.slice(-4);
13760
14409
  }
13761
14410
  async function getProjectStatus2() {
13762
- const projectJai1 = join15(process.cwd(), ".jai1");
14411
+ const projectJai1 = join16(process.cwd(), ".jai1");
13763
14412
  try {
13764
- await fs21.access(projectJai1);
14413
+ await fs24.access(projectJai1);
13765
14414
  return { exists: true, version: "Synced" };
13766
14415
  } catch {
13767
14416
  return { exists: false };
@@ -13769,10 +14418,10 @@ async function getProjectStatus2() {
13769
14418
  }
13770
14419
 
13771
14420
  // src/commands/self-update.ts
13772
- import { Command as Command55 } from "commander";
14421
+ import { Command as Command56 } from "commander";
13773
14422
  import { confirm as confirm13 } from "@inquirer/prompts";
13774
- import { execSync as execSync3 } from "child_process";
13775
- var colors5 = {
14423
+ import { execSync as execSync5 } from "child_process";
14424
+ var colors4 = {
13776
14425
  yellow: "\x1B[33m",
13777
14426
  green: "\x1B[32m",
13778
14427
  cyan: "\x1B[36m",
@@ -13781,7 +14430,7 @@ var colors5 = {
13781
14430
  bold: "\x1B[1m"
13782
14431
  };
13783
14432
  function createSelfUpdateCommand() {
13784
- return new Command55("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) => {
14433
+ return new Command56("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) => {
13785
14434
  await handleSelfUpdate(options);
13786
14435
  });
13787
14436
  }
@@ -13792,7 +14441,7 @@ async function handleSelfUpdate(options) {
13792
14441
  throw new ValidationError('Not initialized. Run "jai1 auth" first.');
13793
14442
  }
13794
14443
  try {
13795
- console.log(`${colors5.cyan}\u{1F50D} Checking for updates...${colors5.reset}`);
14444
+ console.log(`${colors4.cyan}\u{1F50D} Checking for updates...${colors4.reset}`);
13796
14445
  const response = await fetch(`${config.apiUrl}/api/versions/client`, {
13797
14446
  headers: {
13798
14447
  "JAI1-Access-Key": config.accessKey
@@ -13807,20 +14456,20 @@ async function handleSelfUpdate(options) {
13807
14456
  const latestVersion = data.version;
13808
14457
  const currentVersion = package_default.version;
13809
14458
  console.log(`
13810
- ${colors5.bold}Current version:${colors5.reset} ${currentVersion}`);
13811
- console.log(`${colors5.bold}Latest version:${colors5.reset} ${latestVersion}
14459
+ ${colors4.bold}Current version:${colors4.reset} ${currentVersion}`);
14460
+ console.log(`${colors4.bold}Latest version:${colors4.reset} ${latestVersion}
13812
14461
  `);
13813
14462
  if (!isNewerVersion3(latestVersion, currentVersion)) {
13814
- console.log(`${colors5.green}\u2705 You're already on the latest version!${colors5.reset}
14463
+ console.log(`${colors4.green}\u2705 You're already on the latest version!${colors4.reset}
13815
14464
  `);
13816
14465
  return;
13817
14466
  }
13818
- console.log(`${colors5.yellow}\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E${colors5.reset}`);
13819
- console.log(`${colors5.yellow}\u2502${colors5.reset} ${colors5.bold}\u2B06\uFE0F Update available!${colors5.reset} ${currentVersion} \u2192 ${colors5.cyan}${latestVersion}${colors5.reset} ${colors5.yellow}\u2502${colors5.reset}`);
13820
- console.log(`${colors5.yellow}\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F${colors5.reset}
14467
+ console.log(`${colors4.yellow}\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E${colors4.reset}`);
14468
+ console.log(`${colors4.yellow}\u2502${colors4.reset} ${colors4.bold}\u2B06\uFE0F Update available!${colors4.reset} ${currentVersion} \u2192 ${colors4.cyan}${latestVersion}${colors4.reset} ${colors4.yellow}\u2502${colors4.reset}`);
14469
+ console.log(`${colors4.yellow}\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F${colors4.reset}
13821
14470
  `);
13822
14471
  if (options.check) {
13823
- console.log(`${colors5.cyan}Run "jai1 self-update" to install the latest version.${colors5.reset}
14472
+ console.log(`${colors4.cyan}Run "jai1 self-update" to install the latest version.${colors4.reset}
13824
14473
  `);
13825
14474
  return;
13826
14475
  }
@@ -13830,24 +14479,24 @@ ${colors5.bold}Current version:${colors5.reset} ${currentVersion}`);
13830
14479
  default: true
13831
14480
  });
13832
14481
  if (!shouldUpdate) {
13833
- console.log(`${colors5.yellow}\u23F8\uFE0F Update cancelled.${colors5.reset}
14482
+ console.log(`${colors4.yellow}\u23F8\uFE0F Update cancelled.${colors4.reset}
13834
14483
  `);
13835
14484
  return;
13836
14485
  }
13837
14486
  }
13838
14487
  console.log(`
13839
- ${colors5.cyan}\u{1F4E5} Installing latest version...${colors5.reset}
14488
+ ${colors4.cyan}\u{1F4E5} Installing latest version...${colors4.reset}
13840
14489
  `);
13841
14490
  try {
13842
14491
  const packageManager2 = detectPackageManager2();
13843
14492
  const installCommand = getInstallCommand2(packageManager2);
13844
- console.log(`${colors5.cyan}Using ${packageManager2}...${colors5.reset}`);
13845
- execSync3(installCommand, {
14493
+ console.log(`${colors4.cyan}Using ${packageManager2}...${colors4.reset}`);
14494
+ execSync5(installCommand, {
13846
14495
  stdio: "inherit",
13847
14496
  env: { ...process.env, FORCE_COLOR: "1" }
13848
14497
  });
13849
14498
  console.log(`
13850
- ${colors5.green}\u2705 Successfully updated to version ${latestVersion}!${colors5.reset}
14499
+ ${colors4.green}\u2705 Successfully updated to version ${latestVersion}!${colors4.reset}
13851
14500
  `);
13852
14501
  trackAction("self_update", {
13853
14502
  from_version: currentVersion,
@@ -13857,11 +14506,11 @@ ${colors5.green}\u2705 Successfully updated to version ${latestVersion}!${colors
13857
14506
  disableUpdateCheck();
13858
14507
  } catch (error) {
13859
14508
  console.error(`
13860
- ${colors5.red}\u274C Update failed!${colors5.reset}`);
13861
- console.error(`${colors5.red}Error: ${error instanceof Error ? error.message : "Unknown error"}${colors5.reset}
14509
+ ${colors4.red}\u274C Update failed!${colors4.reset}`);
14510
+ console.error(`${colors4.red}Error: ${error instanceof Error ? error.message : "Unknown error"}${colors4.reset}
13862
14511
  `);
13863
- console.error(`${colors5.yellow}\u{1F4A1} You can try manually updating with:${colors5.reset}`);
13864
- console.error(` ${colors5.cyan}npm install -g @jvittechs/jai1-cli@latest${colors5.reset}
14512
+ console.error(`${colors4.yellow}\u{1F4A1} You can try manually updating with:${colors4.reset}`);
14513
+ console.error(` ${colors4.cyan}npm install -g @jvittechs/jai1-cli@latest${colors4.reset}
13865
14514
  `);
13866
14515
  throw error;
13867
14516
  }
@@ -13889,17 +14538,17 @@ function detectPackageManager2() {
13889
14538
  if (userAgent.includes("yarn")) return "yarn";
13890
14539
  if (userAgent.includes("bun")) return "bun";
13891
14540
  try {
13892
- execSync3("pnpm --version", { stdio: "ignore" });
14541
+ execSync5("pnpm --version", { stdio: "ignore" });
13893
14542
  return "pnpm";
13894
14543
  } catch {
13895
14544
  }
13896
14545
  try {
13897
- execSync3("yarn --version", { stdio: "ignore" });
14546
+ execSync5("yarn --version", { stdio: "ignore" });
13898
14547
  return "yarn";
13899
14548
  } catch {
13900
14549
  }
13901
14550
  try {
13902
- execSync3("bun --version", { stdio: "ignore" });
14551
+ execSync5("bun --version", { stdio: "ignore" });
13903
14552
  return "bun";
13904
14553
  } catch {
13905
14554
  }
@@ -13921,10 +14570,10 @@ function getInstallCommand2(packageManager2) {
13921
14570
  }
13922
14571
 
13923
14572
  // src/commands/clear-backups.ts
13924
- import { Command as Command56 } from "commander";
14573
+ import { Command as Command57 } from "commander";
13925
14574
  import { confirm as confirm14 } from "@inquirer/prompts";
13926
14575
  function createClearBackupsCommand() {
13927
- return new Command56("clear-backups").description("Clear backup files").option("-y, --yes", "Skip confirmation").action(async (options) => {
14576
+ return new Command57("clear-backups").description("Clear backup files").option("-y, --yes", "Skip confirmation").action(async (options) => {
13928
14577
  const service = new ComponentsService();
13929
14578
  const backups = await service.listBackups(process.cwd());
13930
14579
  if (backups.length === 0) {
@@ -13949,10 +14598,10 @@ function createClearBackupsCommand() {
13949
14598
  }
13950
14599
 
13951
14600
  // src/commands/vscode/index.ts
13952
- import { Command as Command57 } from "commander";
14601
+ import { Command as Command58 } from "commander";
13953
14602
  import { checkbox as checkbox7, confirm as confirm15, select as select8 } from "@inquirer/prompts";
13954
- import fs22 from "fs/promises";
13955
- import path9 from "path";
14603
+ import fs25 from "fs/promises";
14604
+ import path12 from "path";
13956
14605
  import { existsSync as existsSync3 } from "fs";
13957
14606
  var PERFORMANCE_GROUPS2 = {
13958
14607
  telemetry: {
@@ -14089,7 +14738,7 @@ var PERFORMANCE_GROUPS2 = {
14089
14738
  }
14090
14739
  };
14091
14740
  function createVSCodeCommand() {
14092
- const vscodeCommand = new Command57("vscode").description("Qu\u1EA3n l\xFD c\xE0i \u0111\u1EB7t VSCode cho d\u1EF1 \xE1n hi\u1EC7n t\u1EA1i");
14741
+ const vscodeCommand = new Command58("vscode").description("Qu\u1EA3n l\xFD c\xE0i \u0111\u1EB7t VSCode cho d\u1EF1 \xE1n hi\u1EC7n t\u1EA1i");
14093
14742
  vscodeCommand.action(async () => {
14094
14743
  await interactiveMode2();
14095
14744
  });
@@ -14172,8 +14821,8 @@ async function selectGroupsToApply2(action) {
14172
14821
  }
14173
14822
  }
14174
14823
  async function applyGroups2(groupKeys, action) {
14175
- const vscodeDir = path9.join(process.cwd(), ".vscode");
14176
- const settingsPath = path9.join(vscodeDir, "settings.json");
14824
+ const vscodeDir = path12.join(process.cwd(), ".vscode");
14825
+ const settingsPath = path12.join(vscodeDir, "settings.json");
14177
14826
  const invalidGroups = groupKeys.filter((key) => !PERFORMANCE_GROUPS2[key]);
14178
14827
  if (invalidGroups.length > 0) {
14179
14828
  console.log(`
@@ -14182,13 +14831,13 @@ async function applyGroups2(groupKeys, action) {
14182
14831
  return;
14183
14832
  }
14184
14833
  if (!existsSync3(vscodeDir)) {
14185
- await fs22.mkdir(vscodeDir, { recursive: true });
14834
+ await fs25.mkdir(vscodeDir, { recursive: true });
14186
14835
  console.log("\u{1F4C1} \u0110\xE3 t\u1EA1o th\u01B0 m\u1EE5c .vscode/");
14187
14836
  }
14188
14837
  let currentSettings = {};
14189
14838
  if (existsSync3(settingsPath)) {
14190
14839
  try {
14191
- const content = await fs22.readFile(settingsPath, "utf-8");
14840
+ const content = await fs25.readFile(settingsPath, "utf-8");
14192
14841
  currentSettings = JSON.parse(content);
14193
14842
  console.log("\u{1F4C4} \u0110\xE3 \u0111\u1ECDc c\xE0i \u0111\u1EB7t hi\u1EC7n t\u1EA1i t\u1EEB settings.json");
14194
14843
  } catch {
@@ -14228,14 +14877,14 @@ async function applyGroups2(groupKeys, action) {
14228
14877
  }
14229
14878
  }
14230
14879
  }
14231
- await fs22.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
14880
+ await fs25.writeFile(settingsPath, JSON.stringify(newSettings, null, 2));
14232
14881
  console.log(`
14233
14882
  \u2705 \u0110\xE3 c\u1EADp nh\u1EADt c\xE0i \u0111\u1EB7t VSCode t\u1EA1i: ${settingsPath}`);
14234
14883
  console.log("\u{1F4A1} M\u1EB9o: Kh\u1EDFi \u0111\u1ED9ng l\u1EA1i VSCode \u0111\u1EC3 \xE1p d\u1EE5ng c\xE1c thay \u0111\u1ED5i.");
14235
14884
  }
14236
14885
  async function resetSettings2(groupKeys) {
14237
- const vscodeDir = path9.join(process.cwd(), ".vscode");
14238
- const settingsPath = path9.join(vscodeDir, "settings.json");
14886
+ const vscodeDir = path12.join(process.cwd(), ".vscode");
14887
+ const settingsPath = path12.join(vscodeDir, "settings.json");
14239
14888
  if (!existsSync3(settingsPath)) {
14240
14889
  console.log("\n\u26A0\uFE0F Kh\xF4ng t\xECm th\u1EA5y file settings.json");
14241
14890
  return;
@@ -14249,7 +14898,7 @@ async function resetSettings2(groupKeys) {
14249
14898
  return;
14250
14899
  }
14251
14900
  if (groupKeys.length === 0) {
14252
- await fs22.unlink(settingsPath);
14901
+ await fs25.unlink(settingsPath);
14253
14902
  console.log("\n\u2705 \u0110\xE3 x\xF3a file settings.json");
14254
14903
  } else {
14255
14904
  await applyGroups2(groupKeys, "disable");
@@ -14260,9 +14909,9 @@ async function resetSettings2(groupKeys) {
14260
14909
  // src/commands/context.ts
14261
14910
  import React43 from "react";
14262
14911
  import { render as render6 } from "ink";
14263
- import { Command as Command58 } from "commander";
14912
+ import { Command as Command59 } from "commander";
14264
14913
  function createContextCommand() {
14265
- const cmd = new Command58("context").description("Kh\xE1m ph\xE1 v\xE0 qu\u1EA3n l\xFD context d\u1EF1 \xE1n cho c\xE1c IDE").option("--ide <ide>", "M\u1EDF tr\u1EF1c ti\u1EBFp IDE c\u1EE5 th\u1EC3 (cursor, windsurf, antigravity, jai1)").option("--type <type>", "Hi\u1EC3n th\u1ECB lo\u1EA1i context c\u1EE5 th\u1EC3 (rules, workflows, skills, agents, prompts)").option("--stats", "Hi\u1EC3n th\u1ECB th\u1ED1ng k\xEA context (non-interactive)").action(async (options) => {
14914
+ const cmd = new Command59("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) => {
14266
14915
  let initialIDE;
14267
14916
  if (options.ide) {
14268
14917
  const validIDEs = ["cursor", "windsurf", "antigravity", "jai1"];
@@ -14339,10 +14988,10 @@ async function printStats2() {
14339
14988
  }
14340
14989
 
14341
14990
  // src/commands/migrate-ide.ts
14342
- import { Command as Command59 } from "commander";
14991
+ import { Command as Command60 } from "commander";
14343
14992
  import { checkbox as checkbox8, confirm as confirm16 } from "@inquirer/prompts";
14344
14993
  function createMigrateIdeCommand() {
14345
- const cmd = new Command59("migrate-ide").description("Migrate .jai1 rules v\xE0 workflows sang IDEs (Cursor, Windsurf, Claude Code, etc.)").option("--ide <ides...>", "Target IDEs (cursor, windsurf, antigravity, claudecode, opencode)").option("--type <types...>", "Content types (rules, workflows, commands)").option("--dry-run", "Preview changes without writing files").action(async (options) => {
14994
+ const cmd = new Command60("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) => {
14346
14995
  await runMigrateIde(options);
14347
14996
  });
14348
14997
  return cmd;
@@ -14449,54 +15098,54 @@ async function runMigrateIde(options) {
14449
15098
 
14450
15099
  // src/utils/help-formatter.ts
14451
15100
  import boxen4 from "boxen";
14452
- import chalk16 from "chalk";
15101
+ import chalk18 from "chalk";
14453
15102
  import gradient from "gradient-string";
14454
15103
  import figlet from "figlet";
14455
15104
  function showCustomHelp(version) {
14456
15105
  const title = figlet.textSync("JAI1", { font: "Small" });
14457
15106
  console.log(gradient.pastel(title));
14458
15107
  console.log(
14459
- boxen4(chalk16.cyan(`Agentic Coding CLI v${version}`), {
15108
+ boxen4(chalk18.cyan(`Agentic Coding CLI v${version}`), {
14460
15109
  padding: { left: 1, right: 1, top: 0, bottom: 0 },
14461
15110
  borderStyle: "round",
14462
15111
  borderColor: "cyan"
14463
15112
  })
14464
15113
  );
14465
- console.log(chalk16.bold("\n\u{1F527} Thi\u1EBFt l\u1EADp & Th\xF4ng tin"));
15114
+ console.log(chalk18.bold("\n\u{1F527} Thi\u1EBFt l\u1EADp & Th\xF4ng tin"));
14466
15115
  console.log(" auth X\xE1c th\u1EF1c v\xE0 c\u1EA5u h\xECnh jai1-client");
14467
15116
  console.log(" status Hi\u1EC3n th\u1ECB tr\u1EA1ng th\xE1i c\u1EA5u h\xECnh");
14468
15117
  console.log(" guide Trung t\xE2m h\u1ECDc Agentic Coding");
14469
- console.log(chalk16.bold("\n\u{1F4E6} Qu\u1EA3n l\xFD Components"));
15118
+ console.log(chalk18.bold("\n\u{1F4E6} Qu\u1EA3n l\xFD Components"));
14470
15119
  console.log(" apply C\xE0i \u0111\u1EB7t components (interactive)");
14471
15120
  console.log(" update C\u1EADp nh\u1EADt components \u0111\xE3 c\xE0i");
14472
15121
  console.log(" check Ki\u1EC3m tra c\u1EADp nh\u1EADt t\u1EEB server");
14473
- console.log(chalk16.bold("\n\u{1F5A5}\uFE0F IDE & T\xEDch h\u1EE3p"));
15122
+ console.log(chalk18.bold("\n\u{1F5A5}\uFE0F IDE & T\xEDch h\u1EE3p"));
14474
15123
  console.log(" ide L\u1EC7nh c\u1EA5u h\xECnh IDE");
14475
15124
  console.log(" chat Chat AI v\u1EDBi Jai1 LLM Proxy");
14476
15125
  console.log(" openai-keys Th\xF4ng tin API credentials");
14477
- console.log(chalk16.bold("\n\u{1F916} AI Tools"));
15126
+ console.log(chalk18.bold("\n\u{1F916} AI Tools"));
14478
15127
  console.log(" translate D\u1ECBch v\u0103n b\u1EA3n/file b\u1EB1ng AI");
14479
15128
  console.log(" image T\u1EA1o \u1EA3nh (Coming Soon)");
14480
15129
  console.log(" stats Th\u1ED1ng k\xEA s\u1EED d\u1EE5ng LLM");
14481
15130
  console.log(" feedback G\u1EEDi b\xE1o c\xE1o/\u0111\u1EC1 xu\u1EA5t");
14482
- console.log(chalk16.bold("\n\u{1F4C1} Project"));
15131
+ console.log(chalk18.bold("\n\u{1F4C1} Project"));
14483
15132
  console.log(" kit Qu\u1EA3n l\xFD starter kits");
14484
15133
  console.log(" rules Qu\u1EA3n l\xFD rule presets");
14485
15134
  console.log(" deps Qu\u1EA3n l\xFD dependencies");
14486
15135
  console.log(" redmine Redmine context sync");
14487
- console.log(chalk16.bold("\n\u2699\uFE0F B\u1EA3o tr\xEC"));
15136
+ console.log(chalk18.bold("\n\u2699\uFE0F B\u1EA3o tr\xEC"));
14488
15137
  console.log(" upgrade C\u1EADp nh\u1EADt jai1-client");
14489
15138
  console.log(" clean D\u1ECDn d\u1EB9p cache/backup");
14490
15139
  console.log(" utils Developer utilities");
14491
- console.log(chalk16.dim("\nS\u1EED d\u1EE5ng: jai1 [l\u1EC7nh] --help \u0111\u1EC3 xem chi ti\u1EBFt"));
15140
+ console.log(chalk18.dim("\nS\u1EED d\u1EE5ng: jai1 [l\u1EC7nh] --help \u0111\u1EC3 xem chi ti\u1EBFt"));
14492
15141
  }
14493
15142
  function showUnknownCommand(commandName) {
14494
- console.error(chalk16.red(`\u274C L\u1EC7nh kh\xF4ng t\u1ED3n t\u1EA1i: ${commandName}`));
14495
- console.error(chalk16.dim("\nG\u1EE3i \xFD: Ch\u1EA1y jai1 --help \u0111\u1EC3 xem danh s\xE1ch l\u1EC7nh"));
15143
+ console.error(chalk18.red(`\u274C L\u1EC7nh kh\xF4ng t\u1ED3n t\u1EA1i: ${commandName}`));
15144
+ console.error(chalk18.dim("\nG\u1EE3i \xFD: Ch\u1EA1y jai1 --help \u0111\u1EC3 xem danh s\xE1ch l\u1EC7nh"));
14496
15145
  }
14497
15146
 
14498
15147
  // src/cli.ts
14499
- var program = new Command60();
15148
+ var program = new Command61();
14500
15149
  if (process.argv.includes("-v") || process.argv.includes("--version")) {
14501
15150
  console.log(package_default.version);
14502
15151
  if (!process.argv.includes("--skip-update-check")) {
@@ -14530,9 +15179,9 @@ program.addCommand(createKitCommand());
14530
15179
  program.addCommand(createRulesCommand());
14531
15180
  program.addCommand(createUpgradeCommand());
14532
15181
  program.addCommand(createCleanCommand());
14533
- var redmineCommand = new Command60("redmine").description("Redmine context sync commands");
15182
+ var redmineCommand = new Command61("redmine").description("Redmine context sync commands");
14534
15183
  redmineCommand.addCommand(createRedmineCheckCommand());
14535
- var syncCommand = new Command60("sync").description("Sync Redmine issues to markdown files");
15184
+ var syncCommand = new Command61("sync").description("Sync Redmine issues to markdown files");
14536
15185
  syncCommand.addCommand(createSyncIssueCommand());
14537
15186
  syncCommand.addCommand(createSyncProjectCommand());
14538
15187
  redmineCommand.addCommand(syncCommand);