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