@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.
- package/dist/cli.js +1201 -552
- package/dist/cli.js.map +1 -1
- 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
|
|
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.
|
|
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(
|
|
3511
|
+
async pathExists(path13) {
|
|
3511
3512
|
try {
|
|
3512
|
-
await fs7.access(
|
|
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
|
|
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
|
|
10297
|
-
import
|
|
10367
|
+
import { Command as Command38 } from "commander";
|
|
10368
|
+
import chalk13 from "chalk";
|
|
10298
10369
|
|
|
10299
|
-
// src/commands/deps/
|
|
10370
|
+
// src/commands/deps/check.ts
|
|
10300
10371
|
import { Command as Command36 } from "commander";
|
|
10301
|
-
import
|
|
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
|
|
10305
|
-
import
|
|
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
|
-
|
|
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 =
|
|
10505
|
+
const pkgPath = path9.join(cwd, "package.json");
|
|
10315
10506
|
try {
|
|
10316
|
-
const content = await
|
|
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
|
-
|
|
10357
|
-
|
|
10358
|
-
|
|
10359
|
-
|
|
10360
|
-
|
|
10361
|
-
|
|
10362
|
-
|
|
10363
|
-
|
|
10364
|
-
|
|
10365
|
-
|
|
10366
|
-
|
|
10367
|
-
|
|
10368
|
-
|
|
10369
|
-
|
|
10370
|
-
|
|
10371
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
10450
|
-
|
|
10451
|
-
|
|
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/
|
|
10503
|
-
|
|
10504
|
-
|
|
10505
|
-
|
|
10506
|
-
|
|
10507
|
-
|
|
10508
|
-
|
|
10509
|
-
|
|
10510
|
-
|
|
10511
|
-
|
|
10512
|
-
|
|
10513
|
-
|
|
10514
|
-
|
|
10515
|
-
|
|
10516
|
-
|
|
10517
|
-
}
|
|
10518
|
-
|
|
10519
|
-
|
|
10520
|
-
|
|
10521
|
-
|
|
10522
|
-
|
|
10523
|
-
|
|
10524
|
-
|
|
10525
|
-
|
|
10526
|
-
|
|
10527
|
-
|
|
10528
|
-
|
|
10529
|
-
|
|
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
|
-
|
|
10534
|
-
|
|
10535
|
-
|
|
10536
|
-
|
|
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
|
-
|
|
10541
|
-
|
|
10542
|
-
|
|
10543
|
-
|
|
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
|
-
|
|
10546
|
-
|
|
10547
|
-
|
|
10548
|
-
|
|
10549
|
-
|
|
10550
|
-
|
|
10551
|
-
|
|
10552
|
-
|
|
10553
|
-
|
|
10554
|
-
|
|
10555
|
-
|
|
10556
|
-
|
|
10557
|
-
|
|
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
|
-
|
|
10567
|
-
|
|
10568
|
-
|
|
10569
|
-
|
|
10570
|
-
|
|
10571
|
-
|
|
10572
|
-
|
|
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
|
-
|
|
10575
|
-
|
|
10576
|
-
|
|
10577
|
-
|
|
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
|
-
|
|
10581
|
-
|
|
10582
|
-
|
|
10583
|
-
|
|
10584
|
-
|
|
10585
|
-
|
|
10586
|
-
|
|
10587
|
-
|
|
10588
|
-
|
|
10589
|
-
|
|
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
|
-
|
|
10605
|
-
|
|
10606
|
-
|
|
10607
|
-
|
|
10608
|
-
|
|
10609
|
-
|
|
10610
|
-
|
|
10611
|
-
|
|
10612
|
-
|
|
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 (
|
|
10617
|
-
|
|
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
|
-
|
|
10622
|
-
|
|
10623
|
-
|
|
10624
|
-
|
|
10625
|
-
|
|
10626
|
-
|
|
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
|
-
|
|
10629
|
-
|
|
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
|
-
|
|
10646
|
-
|
|
10647
|
-
|
|
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
|
-
|
|
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
|
-
|
|
10670
|
-
|
|
10671
|
-
|
|
10672
|
-
|
|
10673
|
-
|
|
10674
|
-
|
|
10675
|
-
|
|
10676
|
-
|
|
10677
|
-
|
|
10678
|
-
|
|
10679
|
-
|
|
10680
|
-
|
|
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
|
|
10683
|
-
|
|
10684
|
-
|
|
10685
|
-
|
|
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(
|
|
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(
|
|
10697
|
-
console.log(` ${
|
|
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(
|
|
10700
|
-
console.log(
|
|
10701
|
-
console.log(
|
|
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(
|
|
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
|
|
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
|
|
10715
|
-
import
|
|
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
|
|
10719
|
-
import
|
|
10720
|
-
import
|
|
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
|
|
10724
|
-
import { join as
|
|
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 =
|
|
10772
|
-
await
|
|
10773
|
-
const tmpFile =
|
|
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
|
|
11360
|
+
await fs17.writeFile(tmpFile, Buffer.from(buffer));
|
|
10776
11361
|
if (onProgress) onProgress(60);
|
|
10777
11362
|
const zip = new AdmZip(tmpFile);
|
|
10778
|
-
await
|
|
11363
|
+
await fs17.mkdir(targetDir, { recursive: true });
|
|
10779
11364
|
zip.extractAllTo(targetDir, true);
|
|
10780
11365
|
if (onProgress) onProgress(100);
|
|
10781
|
-
await
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
11412
|
+
chalk14.bold(`${categoryIcon} ${category.charAt(0).toUpperCase() + category.slice(1)}`)
|
|
10828
11413
|
);
|
|
10829
|
-
const table = new
|
|
11414
|
+
const table = new Table5({
|
|
10830
11415
|
head: [
|
|
10831
|
-
|
|
10832
|
-
|
|
10833
|
-
|
|
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
|
-
|
|
10840
|
-
|
|
10841
|
-
|
|
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(
|
|
10848
|
-
console.log(
|
|
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
|
|
11438
|
+
import { Command as Command40 } from "commander";
|
|
10854
11439
|
function createKitInfoCommand() {
|
|
10855
|
-
return new
|
|
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
|
|
10905
|
-
import { promises as
|
|
10906
|
-
import { join as
|
|
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
|
|
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 ||
|
|
11554
|
+
const targetDir = directory || join8(process.cwd(), options.name || slug);
|
|
10970
11555
|
try {
|
|
10971
|
-
await
|
|
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
|
|
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
|
|
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
|
|
11690
|
+
const entries = await fs18.readdir(dir, { withFileTypes: true });
|
|
11106
11691
|
for (const entry of entries) {
|
|
11107
|
-
const fullPath =
|
|
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(
|
|
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(
|
|
11124
|
-
console.log(` ${
|
|
11125
|
-
console.log(` ${
|
|
11126
|
-
console.log(` ${
|
|
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(
|
|
11129
|
-
console.log(
|
|
11130
|
-
console.log(
|
|
11131
|
-
console.log(
|
|
11132
|
-
console.log(
|
|
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(
|
|
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
|
|
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
|
|
11148
|
-
import
|
|
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
|
|
11152
|
-
import
|
|
11153
|
-
import
|
|
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
|
|
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(
|
|
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(
|
|
11763
|
+
console.log(chalk16.yellow("Kh\xF4ng c\xF3 presets n\xE0o."));
|
|
11179
11764
|
return;
|
|
11180
11765
|
}
|
|
11181
11766
|
console.log(
|
|
11182
|
-
|
|
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(
|
|
11187
|
-
const table = new
|
|
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
|
-
[
|
|
11193
|
-
[
|
|
11194
|
-
[
|
|
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([
|
|
11787
|
+
table.push([chalk16.dim("Stack"), chalk16.yellow(stackParts.join(" + "))]);
|
|
11203
11788
|
}
|
|
11204
11789
|
table.push(
|
|
11205
|
-
[
|
|
11206
|
-
[
|
|
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(
|
|
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
|
|
11222
|
-
import { promises as
|
|
11223
|
-
import { join as
|
|
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
|
|
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
|
|
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 =
|
|
11319
|
-
await
|
|
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 =
|
|
11322
|
-
await
|
|
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
|
|
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
|
|
11355
|
-
import { promises as
|
|
11356
|
-
import { join as
|
|
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
|
|
11717
|
-
import { join as
|
|
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 =
|
|
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() :
|
|
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
|
|
12332
|
+
const stats = await fs20.stat(rulesPath);
|
|
11748
12333
|
if (stats.isDirectory()) {
|
|
11749
|
-
const files = await
|
|
12334
|
+
const files = await fs20.readdir(rulesPath);
|
|
11750
12335
|
for (const file of files) {
|
|
11751
12336
|
if (file.endsWith(format.fileExtension)) {
|
|
11752
|
-
const originalPath =
|
|
11753
|
-
const relativePath =
|
|
11754
|
-
const destPath =
|
|
11755
|
-
await
|
|
11756
|
-
await
|
|
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:
|
|
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
|
|
11781
|
-
await
|
|
11782
|
-
|
|
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 =
|
|
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 =
|
|
11799
|
-
await
|
|
11800
|
-
await
|
|
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:
|
|
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 =
|
|
11814
|
-
const metadataContent = await
|
|
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 =
|
|
11820
|
-
const destPath =
|
|
11821
|
-
await
|
|
11822
|
-
await
|
|
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 =
|
|
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
|
|
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 =
|
|
12426
|
+
const metadataPath = join10(backupDirPath, entry.name, "metadata.json");
|
|
11842
12427
|
try {
|
|
11843
|
-
const metadataContent = await
|
|
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 =
|
|
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(
|
|
12475
|
+
async pathExists(path13) {
|
|
11891
12476
|
try {
|
|
11892
|
-
await
|
|
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(
|
|
12486
|
+
async deleteDirectory(path13) {
|
|
11902
12487
|
try {
|
|
11903
|
-
const exists = await this.pathExists(
|
|
12488
|
+
const exists = await this.pathExists(path13);
|
|
11904
12489
|
if (!exists) {
|
|
11905
12490
|
return;
|
|
11906
12491
|
}
|
|
11907
|
-
const entries = await
|
|
12492
|
+
const entries = await fs20.readdir(path13, { withFileTypes: true });
|
|
11908
12493
|
for (const entry of entries) {
|
|
11909
|
-
const fullPath =
|
|
12494
|
+
const fullPath = join10(path13, entry.name);
|
|
11910
12495
|
if (entry.isDirectory()) {
|
|
11911
12496
|
await this.deleteDirectory(fullPath);
|
|
11912
12497
|
} else {
|
|
11913
|
-
await
|
|
12498
|
+
await fs20.unlink(fullPath);
|
|
11914
12499
|
}
|
|
11915
12500
|
}
|
|
11916
|
-
await
|
|
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
|
|
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 =
|
|
11931
|
-
await
|
|
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
|
|
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 =
|
|
12672
|
+
const rulePresetDir = join11(process.cwd(), ".jai1", "rule-preset");
|
|
12088
12673
|
try {
|
|
12089
|
-
await
|
|
12674
|
+
await fs21.rm(rulePresetDir, { recursive: true, force: true });
|
|
12090
12675
|
} catch {
|
|
12091
12676
|
}
|
|
12092
|
-
await
|
|
12093
|
-
await
|
|
12094
|
-
|
|
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 =
|
|
12100
|
-
await
|
|
12101
|
-
await
|
|
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 =
|
|
12110
|
-
await
|
|
12111
|
-
await
|
|
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 =
|
|
12139
|
-
const existingConfigContent = await
|
|
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
|
|
12151
|
-
|
|
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
|
|
12183
|
-
import { join as
|
|
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
|
|
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 =
|
|
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
|
|
12255
|
-
import { promises as
|
|
12256
|
-
import { join as
|
|
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
|
|
12260
|
-
const configPath =
|
|
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
|
|
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 =
|
|
12957
|
+
const rulePresetDir = join13(process.cwd(), ".jai1", "rule-preset");
|
|
12344
12958
|
const presetExists = await checkPathExists(rulePresetDir);
|
|
12345
12959
|
if (presetExists) {
|
|
12346
|
-
const files = await
|
|
12960
|
+
const files = await fs22.readdir(rulePresetDir);
|
|
12347
12961
|
for (const file of files) {
|
|
12348
12962
|
if (file.endsWith(".mdc")) {
|
|
12349
|
-
const filePath =
|
|
12350
|
-
const content = await
|
|
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 =
|
|
12370
|
-
await
|
|
12371
|
-
await
|
|
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
|
|
12381
|
-
|
|
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
|
|
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
|
|
12405
|
-
import { promises as
|
|
12406
|
-
import { join as
|
|
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
|
|
12409
|
-
const configPath =
|
|
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
|
|
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 =
|
|
12425
|
-
const presetJsonPath =
|
|
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
|
|
13078
|
+
const presetContent = await fs23.readFile(presetJsonPath, "utf-8");
|
|
12430
13079
|
presetMetadata = JSON.parse(presetContent);
|
|
12431
|
-
const files = await
|
|
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(
|
|
13139
|
+
async function checkPathExists2(path13) {
|
|
12491
13140
|
try {
|
|
12492
|
-
await
|
|
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(
|
|
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(
|
|
12517
|
-
console.log(` ${
|
|
12518
|
-
console.log(` ${
|
|
12519
|
-
console.log(` ${
|
|
12520
|
-
console.log(` ${
|
|
12521
|
-
console.log(` ${
|
|
12522
|
-
console.log(` ${
|
|
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(
|
|
12525
|
-
console.log(
|
|
12526
|
-
console.log(
|
|
12527
|
-
console.log(
|
|
12528
|
-
console.log(
|
|
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(
|
|
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
|
|
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
|
|
13195
|
+
import { Command as Command50 } from "commander";
|
|
12547
13196
|
import { confirm as confirm11 } from "@inquirer/prompts";
|
|
12548
|
-
import { execSync as
|
|
12549
|
-
var
|
|
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
|
|
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(`${
|
|
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
|
-
${
|
|
12585
|
-
console.log(`${
|
|
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(`${
|
|
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(`${
|
|
12593
|
-
console.log(`${
|
|
12594
|
-
console.log(`${
|
|
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(`${
|
|
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(`${
|
|
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
|
-
${
|
|
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(`${
|
|
12619
|
-
|
|
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
|
-
${
|
|
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
|
-
${
|
|
12635
|
-
console.error(`${
|
|
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(`${
|
|
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(` ${
|
|
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 =
|
|
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
|
|
13355
|
+
import { Command as Command51 } from "commander";
|
|
12707
13356
|
import { confirm as confirm12, select as select7 } from "@inquirer/prompts";
|
|
12708
|
-
import { join as
|
|
13357
|
+
import { join as join15 } from "path";
|
|
12709
13358
|
function createCleanCommand() {
|
|
12710
|
-
return new
|
|
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:
|
|
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
|
|
13472
|
+
import { Command as Command52 } from "commander";
|
|
12824
13473
|
|
|
12825
13474
|
// src/services/redmine-config.service.ts
|
|
12826
|
-
import { readFile as
|
|
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
|
|
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
|
|
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 =
|
|
13591
|
+
this.concurrencyLimit = pLimit4(config.defaults.concurrency);
|
|
12943
13592
|
}
|
|
12944
|
-
async request(
|
|
12945
|
-
const url = `${this.baseUrl}${
|
|
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
|
|
13007
|
-
return this.request(
|
|
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
|
|
13028
|
-
return this.request(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
13712
|
-
import { promises as
|
|
13713
|
-
import { join as
|
|
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
|
|
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 =
|
|
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 =
|
|
14411
|
+
const projectJai1 = join16(process.cwd(), ".jai1");
|
|
13763
14412
|
try {
|
|
13764
|
-
await
|
|
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
|
|
14421
|
+
import { Command as Command56 } from "commander";
|
|
13773
14422
|
import { confirm as confirm13 } from "@inquirer/prompts";
|
|
13774
|
-
import { execSync as
|
|
13775
|
-
var
|
|
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
|
|
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(`${
|
|
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
|
-
${
|
|
13811
|
-
console.log(`${
|
|
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(`${
|
|
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(`${
|
|
13819
|
-
console.log(`${
|
|
13820
|
-
console.log(`${
|
|
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(`${
|
|
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(`${
|
|
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
|
-
${
|
|
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(`${
|
|
13845
|
-
|
|
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
|
-
${
|
|
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
|
-
${
|
|
13861
|
-
console.error(`${
|
|
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(`${
|
|
13864
|
-
console.error(` ${
|
|
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
|
-
|
|
14541
|
+
execSync5("pnpm --version", { stdio: "ignore" });
|
|
13893
14542
|
return "pnpm";
|
|
13894
14543
|
} catch {
|
|
13895
14544
|
}
|
|
13896
14545
|
try {
|
|
13897
|
-
|
|
14546
|
+
execSync5("yarn --version", { stdio: "ignore" });
|
|
13898
14547
|
return "yarn";
|
|
13899
14548
|
} catch {
|
|
13900
14549
|
}
|
|
13901
14550
|
try {
|
|
13902
|
-
|
|
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
|
|
14573
|
+
import { Command as Command57 } from "commander";
|
|
13925
14574
|
import { confirm as confirm14 } from "@inquirer/prompts";
|
|
13926
14575
|
function createClearBackupsCommand() {
|
|
13927
|
-
return new
|
|
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
|
|
14601
|
+
import { Command as Command58 } from "commander";
|
|
13953
14602
|
import { checkbox as checkbox7, confirm as confirm15, select as select8 } from "@inquirer/prompts";
|
|
13954
|
-
import
|
|
13955
|
-
import
|
|
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
|
|
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 =
|
|
14176
|
-
const settingsPath =
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
14238
|
-
const settingsPath =
|
|
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
|
|
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
|
|
14912
|
+
import { Command as Command59 } from "commander";
|
|
14264
14913
|
function createContextCommand() {
|
|
14265
|
-
const cmd = new
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
14495
|
-
console.error(
|
|
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
|
|
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
|
|
15182
|
+
var redmineCommand = new Command61("redmine").description("Redmine context sync commands");
|
|
14534
15183
|
redmineCommand.addCommand(createRedmineCheckCommand());
|
|
14535
|
-
var syncCommand = new
|
|
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);
|