@jvittechs/j 1.0.61 → 1.0.62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-F6GY6L6O.js → chunk-TEVFTK65.js} +10 -44
- package/dist/chunk-TEVFTK65.js.map +1 -0
- package/dist/chunk-WOAXYUZ6.js +62 -0
- package/dist/chunk-WOAXYUZ6.js.map +1 -0
- package/dist/cli.js +148 -93
- package/dist/cli.js.map +1 -1
- package/dist/{summary-KFXQDFG6.js → summary-7EHQYYJU.js} +3 -2
- package/dist/task.types-M4GWGDMH.js +21 -0
- package/dist/task.types-M4GWGDMH.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-F6GY6L6O.js.map +0 -1
- /package/dist/{summary-KFXQDFG6.js.map → summary-7EHQYYJU.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -10,14 +10,17 @@ import {
|
|
|
10
10
|
createSettingsShowCommand
|
|
11
11
|
} from "./chunk-5RDBJYO2.js";
|
|
12
12
|
import {
|
|
13
|
-
BLOCKED_ICON,
|
|
14
13
|
CloudTaskProvider,
|
|
14
|
+
TaskService,
|
|
15
|
+
createTaskSummaryCommand
|
|
16
|
+
} from "./chunk-TEVFTK65.js";
|
|
17
|
+
import {
|
|
18
|
+
BLOCKED_ICON,
|
|
15
19
|
PRIORITY_ICONS,
|
|
16
20
|
PRIORITY_LABELS,
|
|
17
21
|
STATUS_ICONS,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
} from "./chunk-F6GY6L6O.js";
|
|
22
|
+
TaskSchema
|
|
23
|
+
} from "./chunk-WOAXYUZ6.js";
|
|
21
24
|
import {
|
|
22
25
|
ConfigService,
|
|
23
26
|
SettingsService
|
|
@@ -157,7 +160,7 @@ import { basename as basename5 } from "path";
|
|
|
157
160
|
// package.json
|
|
158
161
|
var package_default = {
|
|
159
162
|
name: "@jvittechs/j",
|
|
160
|
-
version: "1.0.
|
|
163
|
+
version: "1.0.62",
|
|
161
164
|
description: "A unified CLI tool for JV-IT TECHS developers to manage Jai1 Framework. Supports both `j` and `jai1` commands. Please contact TeamAI for usage instructions.",
|
|
162
165
|
type: "module",
|
|
163
166
|
bin: {
|
|
@@ -11391,6 +11394,30 @@ async function registerProject(config, projectId, repoUrl, silent) {
|
|
|
11391
11394
|
return null;
|
|
11392
11395
|
}
|
|
11393
11396
|
}
|
|
11397
|
+
async function syncLocalTasksToCloud(config, projectId, tasksPath, silent) {
|
|
11398
|
+
try {
|
|
11399
|
+
const { existsSync: existsSync7, readFileSync: readFileSync4 } = await import("fs");
|
|
11400
|
+
if (!existsSync7(tasksPath)) return;
|
|
11401
|
+
const content = readFileSync4(tasksPath, "utf-8").trim();
|
|
11402
|
+
if (!content) return;
|
|
11403
|
+
const { TaskSchema: TaskSchema2 } = await import("./task.types-M4GWGDMH.js");
|
|
11404
|
+
const lines = content.split("\n").filter(Boolean);
|
|
11405
|
+
const tasks = lines.map((line) => TaskSchema2.parse(JSON.parse(line)));
|
|
11406
|
+
if (tasks.length === 0) return;
|
|
11407
|
+
if (!silent) {
|
|
11408
|
+
console.log(chalk31.dim(` \u2191 Syncing ${tasks.length} local tasks to cloud...`));
|
|
11409
|
+
}
|
|
11410
|
+
const provider = new CloudTaskProvider(config, projectId);
|
|
11411
|
+
await provider.push(tasks);
|
|
11412
|
+
if (!silent) {
|
|
11413
|
+
console.log(chalk31.green(` \u2713 ${tasks.length} tasks synced to cloud`));
|
|
11414
|
+
}
|
|
11415
|
+
} catch {
|
|
11416
|
+
if (!silent) {
|
|
11417
|
+
console.log(chalk31.dim(" \u26A0\uFE0F Could not sync local tasks. Use j t sync --push later."));
|
|
11418
|
+
}
|
|
11419
|
+
}
|
|
11420
|
+
}
|
|
11394
11421
|
function createSettingsInitCommand() {
|
|
11395
11422
|
return new Command58("init").description("Kh\u1EDFi t\u1EA1o .jai1/settings.yaml").option("-f, --force", "Ghi \u0111\xE8 file c\u0169 n\u1EBFu \u0111\xE3 t\u1ED3n t\u1EA1i").option("--cloud", "Enable cloud task sync (non-interactive)").option("--no-interactive", "Kh\xF4ng h\u1ECFi, d\xF9ng flags/defaults").option("-j, --json", "Output JSON").action(async (options) => {
|
|
11396
11423
|
try {
|
|
@@ -11449,9 +11476,18 @@ function createSettingsInitCommand() {
|
|
|
11449
11476
|
repoUrl,
|
|
11450
11477
|
!!options.json
|
|
11451
11478
|
);
|
|
11479
|
+
const finalProjectId = registeredProjectId && registeredProjectId !== projectId ? registeredProjectId : projectId;
|
|
11452
11480
|
if (registeredProjectId && registeredProjectId !== projectId) {
|
|
11453
11481
|
await service.set("tasks.projectId", registeredProjectId);
|
|
11454
11482
|
}
|
|
11483
|
+
if (registeredProjectId) {
|
|
11484
|
+
await syncLocalTasksToCloud(
|
|
11485
|
+
authResult.config,
|
|
11486
|
+
finalProjectId,
|
|
11487
|
+
service.getSettingsPath().replace(/settings\.yaml$/, "tasks.jsonl"),
|
|
11488
|
+
!!options.json
|
|
11489
|
+
);
|
|
11490
|
+
}
|
|
11455
11491
|
}
|
|
11456
11492
|
}
|
|
11457
11493
|
const finalSettings = service.load();
|
|
@@ -11488,6 +11524,8 @@ function createSettingsInitCommand() {
|
|
|
11488
11524
|
// src/commands/settings/set.ts
|
|
11489
11525
|
import { Command as Command59 } from "commander";
|
|
11490
11526
|
import chalk32 from "chalk";
|
|
11527
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
11528
|
+
import { join as join9 } from "path";
|
|
11491
11529
|
function createSettingsSetCommand() {
|
|
11492
11530
|
return new Command59("set").description("Set a setting value (dot-notation)").argument("<key>", "Setting key (e.g., tasks.cloud)").argument("<value>", "Setting value").option("-j, --json", "Output JSON").action(async (key, value, options) => {
|
|
11493
11531
|
try {
|
|
@@ -11513,15 +11551,29 @@ function createSettingsSetCommand() {
|
|
|
11513
11551
|
const repoUrl = service.resolveGitRepoUrl();
|
|
11514
11552
|
const projectId = service.getProjectId();
|
|
11515
11553
|
if (repoUrl && projectId) {
|
|
11554
|
+
let finalProjectId = projectId;
|
|
11516
11555
|
try {
|
|
11517
11556
|
const provider = new CloudTaskProvider(config, projectId);
|
|
11518
11557
|
const registeredId = await provider.ensureProjectRegistered(repoUrl);
|
|
11519
11558
|
console.log(chalk32.green(" \u2713 Project registered on server"));
|
|
11520
11559
|
if (registeredId && registeredId !== projectId) {
|
|
11521
11560
|
await service.set("tasks.projectId", registeredId);
|
|
11522
|
-
|
|
11523
|
-
}
|
|
11524
|
-
|
|
11561
|
+
finalProjectId = registeredId;
|
|
11562
|
+
}
|
|
11563
|
+
console.log(chalk32.dim(` Project: ${finalProjectId}`));
|
|
11564
|
+
const tasksPath = join9(process.cwd(), ".jai1", "tasks.jsonl");
|
|
11565
|
+
if (existsSync4(tasksPath)) {
|
|
11566
|
+
const content = readFileSync3(tasksPath, "utf-8").trim();
|
|
11567
|
+
if (content) {
|
|
11568
|
+
const lines = content.split("\n").filter(Boolean);
|
|
11569
|
+
const tasks = lines.map((l) => TaskSchema.parse(JSON.parse(l)));
|
|
11570
|
+
if (tasks.length > 0) {
|
|
11571
|
+
console.log(chalk32.dim(` \u2191 Syncing ${tasks.length} local tasks to cloud...`));
|
|
11572
|
+
const p = new CloudTaskProvider(config, finalProjectId);
|
|
11573
|
+
await p.push(tasks);
|
|
11574
|
+
console.log(chalk32.green(` \u2713 ${tasks.length} tasks synced to cloud`));
|
|
11575
|
+
}
|
|
11576
|
+
}
|
|
11525
11577
|
}
|
|
11526
11578
|
} catch (error) {
|
|
11527
11579
|
const msg = error instanceof Error ? error.message : String(error);
|
|
@@ -11673,6 +11725,9 @@ function printTaskLine(task, resolvedIds) {
|
|
|
11673
11725
|
const blockedBy = task.depends_on.filter((id) => !resolvedIds.has(id));
|
|
11674
11726
|
line += chalk35.red(` (blocked: ${blockedBy.join(", ")})`);
|
|
11675
11727
|
}
|
|
11728
|
+
if (task.type && task.type !== "task") {
|
|
11729
|
+
line += chalk35.magenta(` {${task.type}}`);
|
|
11730
|
+
}
|
|
11676
11731
|
if (task.parent) {
|
|
11677
11732
|
line += chalk35.dim(` [${task.parent}]`);
|
|
11678
11733
|
}
|
|
@@ -12396,7 +12451,7 @@ function createTasksCommand() {
|
|
|
12396
12451
|
cmd.addCommand(createTaskDeleteCommand());
|
|
12397
12452
|
cmd.addCommand(createTaskGuideCommand());
|
|
12398
12453
|
cmd.action(async () => {
|
|
12399
|
-
const { handleTaskSummary } = await import("./summary-
|
|
12454
|
+
const { handleTaskSummary } = await import("./summary-7EHQYYJU.js");
|
|
12400
12455
|
await handleTaskSummary({ json: false });
|
|
12401
12456
|
});
|
|
12402
12457
|
return cmd;
|
|
@@ -12413,7 +12468,7 @@ import Table6 from "cli-table3";
|
|
|
12413
12468
|
|
|
12414
12469
|
// src/services/starter-kit.service.ts
|
|
12415
12470
|
import { promises as fs18 } from "fs";
|
|
12416
|
-
import { join as
|
|
12471
|
+
import { join as join10 } from "path";
|
|
12417
12472
|
import AdmZip from "adm-zip";
|
|
12418
12473
|
var StarterKitService = class {
|
|
12419
12474
|
/**
|
|
@@ -12460,9 +12515,9 @@ var StarterKitService = class {
|
|
|
12460
12515
|
throw new NetworkError(`Failed to download kit: HTTP ${response.status}`);
|
|
12461
12516
|
}
|
|
12462
12517
|
if (onProgress) onProgress(30);
|
|
12463
|
-
const tmpDir =
|
|
12518
|
+
const tmpDir = join10(process.env.TMPDIR || "/tmp", "jai1-kits");
|
|
12464
12519
|
await fs18.mkdir(tmpDir, { recursive: true });
|
|
12465
|
-
const tmpFile =
|
|
12520
|
+
const tmpFile = join10(tmpDir, `${slug}.zip`);
|
|
12466
12521
|
const buffer = await response.arrayBuffer();
|
|
12467
12522
|
await fs18.writeFile(tmpFile, Buffer.from(buffer));
|
|
12468
12523
|
if (onProgress) onProgress(60);
|
|
@@ -12595,7 +12650,7 @@ Post-Init Commands:`);
|
|
|
12595
12650
|
// src/commands/kit/create.ts
|
|
12596
12651
|
import { Command as Command78 } from "commander";
|
|
12597
12652
|
import { promises as fs19 } from "fs";
|
|
12598
|
-
import { join as
|
|
12653
|
+
import { join as join11 } from "path";
|
|
12599
12654
|
import { select as select3, input as input2, checkbox as checkbox4 } from "@inquirer/prompts";
|
|
12600
12655
|
import { execa as execa2 } from "execa";
|
|
12601
12656
|
|
|
@@ -12658,7 +12713,7 @@ function createKitCreateCommand() {
|
|
|
12658
12713
|
}
|
|
12659
12714
|
}
|
|
12660
12715
|
}
|
|
12661
|
-
const targetDir = directory ||
|
|
12716
|
+
const targetDir = directory || join11(process.cwd(), options.name || slug);
|
|
12662
12717
|
try {
|
|
12663
12718
|
await fs19.access(targetDir);
|
|
12664
12719
|
throw new Error(`Directory already exists: ${targetDir}`);
|
|
@@ -12723,7 +12778,7 @@ function createKitCreateCommand() {
|
|
|
12723
12778
|
);
|
|
12724
12779
|
for (const filepath of expandedPaths) {
|
|
12725
12780
|
console.log(` \u{1F4E5} Installing ${filepath}...`);
|
|
12726
|
-
await componentsService.install(config, filepath,
|
|
12781
|
+
await componentsService.install(config, filepath, join11(targetDir, ".jai1"));
|
|
12727
12782
|
}
|
|
12728
12783
|
console.log(" \u2713 Framework components applied");
|
|
12729
12784
|
}
|
|
@@ -12803,7 +12858,7 @@ async function getAllFiles(dir) {
|
|
|
12803
12858
|
const files = [];
|
|
12804
12859
|
const entries = await fs19.readdir(dir, { withFileTypes: true });
|
|
12805
12860
|
for (const entry of entries) {
|
|
12806
|
-
const fullPath =
|
|
12861
|
+
const fullPath = join11(dir, entry.name);
|
|
12807
12862
|
if (entry.isDirectory()) {
|
|
12808
12863
|
if (!entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
12809
12864
|
files.push(...await getAllFiles(fullPath));
|
|
@@ -12919,20 +12974,20 @@ function createRulesListCommand() {
|
|
|
12919
12974
|
// src/commands/rules/init.ts
|
|
12920
12975
|
import { Command as Command81 } from "commander";
|
|
12921
12976
|
import { promises as fs21 } from "fs";
|
|
12922
|
-
import { join as
|
|
12977
|
+
import { join as join13 } from "path";
|
|
12923
12978
|
import { select as select4, confirm as confirm15 } from "@inquirer/prompts";
|
|
12924
12979
|
|
|
12925
12980
|
// src/services/project-config.service.ts
|
|
12926
12981
|
import { promises as fs20 } from "fs";
|
|
12927
|
-
import { join as
|
|
12982
|
+
import { join as join12 } from "path";
|
|
12928
12983
|
var ProjectConfigService = class {
|
|
12929
12984
|
projectRoot;
|
|
12930
12985
|
configDir;
|
|
12931
12986
|
configPath;
|
|
12932
12987
|
constructor(projectRoot = process.cwd()) {
|
|
12933
12988
|
this.projectRoot = projectRoot;
|
|
12934
|
-
this.configDir =
|
|
12935
|
-
this.configPath =
|
|
12989
|
+
this.configDir = join12(this.projectRoot, ".jai1");
|
|
12990
|
+
this.configPath = join12(this.configDir, "project.json");
|
|
12936
12991
|
}
|
|
12937
12992
|
/**
|
|
12938
12993
|
* Check if config file exists
|
|
@@ -13138,10 +13193,10 @@ function createRulesInitCommand() {
|
|
|
13138
13193
|
});
|
|
13139
13194
|
}
|
|
13140
13195
|
async function applyCursorFormat(bundle) {
|
|
13141
|
-
const rulesDir =
|
|
13196
|
+
const rulesDir = join13(process.cwd(), ".cursor", "rules");
|
|
13142
13197
|
await fs21.mkdir(rulesDir, { recursive: true });
|
|
13143
13198
|
for (const [filename, content] of Object.entries(bundle.files)) {
|
|
13144
|
-
const filePath =
|
|
13199
|
+
const filePath = join13(rulesDir, filename);
|
|
13145
13200
|
await fs21.writeFile(filePath, content, "utf-8");
|
|
13146
13201
|
console.log(`\u2713 Created .cursor/rules/${filename}`);
|
|
13147
13202
|
}
|
|
@@ -13176,7 +13231,7 @@ async function applyAgentsMdFormat(bundle) {
|
|
|
13176
13231
|
// src/commands/rules/apply.ts
|
|
13177
13232
|
import { Command as Command82 } from "commander";
|
|
13178
13233
|
import { promises as fs23 } from "fs";
|
|
13179
|
-
import { join as
|
|
13234
|
+
import { join as join15 } from "path";
|
|
13180
13235
|
import { search, confirm as confirm16, checkbox as checkbox5 } from "@inquirer/prompts";
|
|
13181
13236
|
|
|
13182
13237
|
// src/services/rules-generator.service.ts
|
|
@@ -13468,7 +13523,7 @@ Follow all instructions and patterns defined in AGENTS.md above.
|
|
|
13468
13523
|
|
|
13469
13524
|
// src/services/backup.service.ts
|
|
13470
13525
|
import { promises as fs22 } from "fs";
|
|
13471
|
-
import { join as
|
|
13526
|
+
import { join as join14, dirname } from "path";
|
|
13472
13527
|
var BackupService = class {
|
|
13473
13528
|
backupDir = ".jai1/backups";
|
|
13474
13529
|
/**
|
|
@@ -13476,7 +13531,7 @@ var BackupService = class {
|
|
|
13476
13531
|
*/
|
|
13477
13532
|
async createBackup(ides, presetSlug) {
|
|
13478
13533
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
13479
|
-
const backupPath =
|
|
13534
|
+
const backupPath = join14(this.backupDir, timestamp);
|
|
13480
13535
|
const backedUpFiles = [];
|
|
13481
13536
|
let hasContent = false;
|
|
13482
13537
|
for (const ideId of ides) {
|
|
@@ -13485,7 +13540,7 @@ var BackupService = class {
|
|
|
13485
13540
|
console.warn(`Unknown IDE format: ${ideId}, skipping backup`);
|
|
13486
13541
|
continue;
|
|
13487
13542
|
}
|
|
13488
|
-
const rulesPath = format.rulesPath === "." ? process.cwd() :
|
|
13543
|
+
const rulesPath = format.rulesPath === "." ? process.cwd() : join14(process.cwd(), format.rulesPath);
|
|
13489
13544
|
try {
|
|
13490
13545
|
const exists = await this.pathExists(rulesPath);
|
|
13491
13546
|
if (!exists) {
|
|
@@ -13503,14 +13558,14 @@ var BackupService = class {
|
|
|
13503
13558
|
const files = await fs22.readdir(rulesPath);
|
|
13504
13559
|
for (const file of files) {
|
|
13505
13560
|
if (file.endsWith(format.fileExtension)) {
|
|
13506
|
-
const originalPath =
|
|
13507
|
-
const relativePath =
|
|
13508
|
-
const destPath =
|
|
13561
|
+
const originalPath = join14(rulesPath, file);
|
|
13562
|
+
const relativePath = join14(format.rulesPath, file);
|
|
13563
|
+
const destPath = join14(backupPath, ideId, file);
|
|
13509
13564
|
await fs22.mkdir(dirname(destPath), { recursive: true });
|
|
13510
13565
|
await fs22.copyFile(originalPath, destPath);
|
|
13511
13566
|
backedUpFiles.push({
|
|
13512
13567
|
originalPath: relativePath,
|
|
13513
|
-
backupPath:
|
|
13568
|
+
backupPath: join14(ideId, file),
|
|
13514
13569
|
ide: ideId
|
|
13515
13570
|
});
|
|
13516
13571
|
hasContent = true;
|
|
@@ -13533,7 +13588,7 @@ var BackupService = class {
|
|
|
13533
13588
|
};
|
|
13534
13589
|
await fs22.mkdir(backupPath, { recursive: true });
|
|
13535
13590
|
await fs22.writeFile(
|
|
13536
|
-
|
|
13591
|
+
join14(backupPath, "metadata.json"),
|
|
13537
13592
|
JSON.stringify(metadata, null, 2),
|
|
13538
13593
|
"utf-8"
|
|
13539
13594
|
);
|
|
@@ -13543,18 +13598,18 @@ var BackupService = class {
|
|
|
13543
13598
|
* Backup a single file (for AGENTS.md, GEMINI.md)
|
|
13544
13599
|
*/
|
|
13545
13600
|
async backupSingleFile(filename, backupPath, ideId, backedUpFiles) {
|
|
13546
|
-
const originalPath =
|
|
13601
|
+
const originalPath = join14(process.cwd(), filename);
|
|
13547
13602
|
try {
|
|
13548
13603
|
const exists = await this.pathExists(originalPath);
|
|
13549
13604
|
if (!exists) {
|
|
13550
13605
|
return;
|
|
13551
13606
|
}
|
|
13552
|
-
const destPath =
|
|
13607
|
+
const destPath = join14(backupPath, ideId, filename);
|
|
13553
13608
|
await fs22.mkdir(dirname(destPath), { recursive: true });
|
|
13554
13609
|
await fs22.copyFile(originalPath, destPath);
|
|
13555
13610
|
backedUpFiles.push({
|
|
13556
13611
|
originalPath: filename,
|
|
13557
|
-
backupPath:
|
|
13612
|
+
backupPath: join14(ideId, filename),
|
|
13558
13613
|
ide: ideId
|
|
13559
13614
|
});
|
|
13560
13615
|
} catch (error) {
|
|
@@ -13564,14 +13619,14 @@ var BackupService = class {
|
|
|
13564
13619
|
* Restore files from a backup
|
|
13565
13620
|
*/
|
|
13566
13621
|
async restoreBackup(backupPath) {
|
|
13567
|
-
const metadataPath =
|
|
13622
|
+
const metadataPath = join14(backupPath, "metadata.json");
|
|
13568
13623
|
const metadataContent = await fs22.readFile(metadataPath, "utf-8");
|
|
13569
13624
|
const metadata = JSON.parse(metadataContent);
|
|
13570
13625
|
console.log(`
|
|
13571
13626
|
Restoring backup from ${metadata.timestamp}...`);
|
|
13572
13627
|
for (const file of metadata.files) {
|
|
13573
|
-
const sourcePath =
|
|
13574
|
-
const destPath =
|
|
13628
|
+
const sourcePath = join14(backupPath, file.backupPath);
|
|
13629
|
+
const destPath = join14(process.cwd(), file.originalPath);
|
|
13575
13630
|
await fs22.mkdir(dirname(destPath), { recursive: true });
|
|
13576
13631
|
await fs22.copyFile(sourcePath, destPath);
|
|
13577
13632
|
console.log(`\u2713 Restored ${file.originalPath}`);
|
|
@@ -13583,7 +13638,7 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
13583
13638
|
*/
|
|
13584
13639
|
async listBackups() {
|
|
13585
13640
|
try {
|
|
13586
|
-
const backupDirPath =
|
|
13641
|
+
const backupDirPath = join14(process.cwd(), this.backupDir);
|
|
13587
13642
|
const exists = await this.pathExists(backupDirPath);
|
|
13588
13643
|
if (!exists) {
|
|
13589
13644
|
return [];
|
|
@@ -13592,7 +13647,7 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
13592
13647
|
const backups = [];
|
|
13593
13648
|
for (const entry of entries) {
|
|
13594
13649
|
if (entry.isDirectory()) {
|
|
13595
|
-
const metadataPath =
|
|
13650
|
+
const metadataPath = join14(backupDirPath, entry.name, "metadata.json");
|
|
13596
13651
|
try {
|
|
13597
13652
|
const metadataContent = await fs22.readFile(metadataPath, "utf-8");
|
|
13598
13653
|
const metadata = JSON.parse(metadataContent);
|
|
@@ -13611,7 +13666,7 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
13611
13666
|
* Delete a specific backup
|
|
13612
13667
|
*/
|
|
13613
13668
|
async deleteBackup(timestamp) {
|
|
13614
|
-
const backupPath =
|
|
13669
|
+
const backupPath = join14(process.cwd(), this.backupDir, timestamp);
|
|
13615
13670
|
await this.deleteDirectory(backupPath);
|
|
13616
13671
|
}
|
|
13617
13672
|
/**
|
|
@@ -13660,7 +13715,7 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
13660
13715
|
}
|
|
13661
13716
|
const entries = await fs22.readdir(path13, { withFileTypes: true });
|
|
13662
13717
|
for (const entry of entries) {
|
|
13663
|
-
const fullPath =
|
|
13718
|
+
const fullPath = join14(path13, entry.name);
|
|
13664
13719
|
if (entry.isDirectory()) {
|
|
13665
13720
|
await this.deleteDirectory(fullPath);
|
|
13666
13721
|
} else {
|
|
@@ -13675,13 +13730,13 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
13675
13730
|
* Get backup directory path
|
|
13676
13731
|
*/
|
|
13677
13732
|
getBackupDir() {
|
|
13678
|
-
return
|
|
13733
|
+
return join14(process.cwd(), this.backupDir);
|
|
13679
13734
|
}
|
|
13680
13735
|
/**
|
|
13681
13736
|
* Ensure backup directory exists
|
|
13682
13737
|
*/
|
|
13683
13738
|
async ensureBackupDir() {
|
|
13684
|
-
const backupDirPath =
|
|
13739
|
+
const backupDirPath = join14(process.cwd(), this.backupDir);
|
|
13685
13740
|
await fs22.mkdir(backupDirPath, { recursive: true });
|
|
13686
13741
|
}
|
|
13687
13742
|
};
|
|
@@ -13854,20 +13909,20 @@ function createRulesApplyCommand() {
|
|
|
13854
13909
|
}
|
|
13855
13910
|
}
|
|
13856
13911
|
console.log("\n\u{1F4DD} Applying preset...\n");
|
|
13857
|
-
const rulePresetDir =
|
|
13912
|
+
const rulePresetDir = join15(process.cwd(), ".jai1", "rule-preset");
|
|
13858
13913
|
try {
|
|
13859
13914
|
await fs23.rm(rulePresetDir, { recursive: true, force: true });
|
|
13860
13915
|
} catch {
|
|
13861
13916
|
}
|
|
13862
13917
|
await fs23.mkdir(rulePresetDir, { recursive: true });
|
|
13863
13918
|
await fs23.writeFile(
|
|
13864
|
-
|
|
13919
|
+
join15(rulePresetDir, "preset.json"),
|
|
13865
13920
|
JSON.stringify(bundle.preset, null, 2),
|
|
13866
13921
|
"utf-8"
|
|
13867
13922
|
);
|
|
13868
13923
|
for (const [filename, content] of Object.entries(bundle.files)) {
|
|
13869
|
-
const filePath =
|
|
13870
|
-
await fs23.mkdir(
|
|
13924
|
+
const filePath = join15(rulePresetDir, filename);
|
|
13925
|
+
await fs23.mkdir(join15(filePath, ".."), { recursive: true });
|
|
13871
13926
|
await fs23.writeFile(filePath, content, "utf-8");
|
|
13872
13927
|
}
|
|
13873
13928
|
console.log(`\u2713 Saved preset to .jai1/rule-preset/`);
|
|
@@ -13876,8 +13931,8 @@ function createRulesApplyCommand() {
|
|
|
13876
13931
|
try {
|
|
13877
13932
|
const files = generatorService.generateForIde(bundle, ideId);
|
|
13878
13933
|
for (const file of files) {
|
|
13879
|
-
const fullPath =
|
|
13880
|
-
await fs23.mkdir(
|
|
13934
|
+
const fullPath = join15(process.cwd(), file.path);
|
|
13935
|
+
await fs23.mkdir(join15(fullPath, ".."), { recursive: true });
|
|
13881
13936
|
await fs23.writeFile(fullPath, file.content, "utf-8");
|
|
13882
13937
|
console.log(`\u2713 [${ideId}] ${file.path}`);
|
|
13883
13938
|
allGeneratedFiles.push({
|
|
@@ -13984,7 +14039,7 @@ function createRulesApplyCommand() {
|
|
|
13984
14039
|
|
|
13985
14040
|
// src/commands/rules/restore.ts
|
|
13986
14041
|
import { Command as Command83 } from "commander";
|
|
13987
|
-
import { join as
|
|
14042
|
+
import { join as join16 } from "path";
|
|
13988
14043
|
import { select as select5, confirm as confirm17 } from "@inquirer/prompts";
|
|
13989
14044
|
function createRulesRestoreCommand() {
|
|
13990
14045
|
return new Command83("restore").description("Restore rules from a backup").option("--latest", "Restore the most recent backup").option("-y, --yes", "Skip confirmation").action(async (options) => {
|
|
@@ -14031,7 +14086,7 @@ function createRulesRestoreCommand() {
|
|
|
14031
14086
|
}
|
|
14032
14087
|
console.log("\n\u{1F504} Restoring backup...\n");
|
|
14033
14088
|
try {
|
|
14034
|
-
const backupPath =
|
|
14089
|
+
const backupPath = join16(backupService.getBackupDir(), selectedBackup.timestamp);
|
|
14035
14090
|
await backupService.restoreBackup(backupPath);
|
|
14036
14091
|
console.log("\n\u2705 Backup restored successfully!\n");
|
|
14037
14092
|
console.log("\u{1F4A1} Tip: Your IDE may need to be restarted to pick up the changes.");
|
|
@@ -14058,12 +14113,12 @@ function formatTimestamp(timestamp) {
|
|
|
14058
14113
|
// src/commands/rules/sync.ts
|
|
14059
14114
|
import { Command as Command84 } from "commander";
|
|
14060
14115
|
import { promises as fs24 } from "fs";
|
|
14061
|
-
import { join as
|
|
14116
|
+
import { join as join17 } from "path";
|
|
14062
14117
|
import { checkbox as checkbox6, confirm as confirm18, Separator } from "@inquirer/prompts";
|
|
14063
14118
|
function createRulesSyncCommand() {
|
|
14064
14119
|
return new Command84("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) => {
|
|
14065
|
-
const rulePresetDir =
|
|
14066
|
-
const presetJsonPath =
|
|
14120
|
+
const rulePresetDir = join17(process.cwd(), ".jai1", "rule-preset");
|
|
14121
|
+
const presetJsonPath = join17(rulePresetDir, "preset.json");
|
|
14067
14122
|
let presetExists = false;
|
|
14068
14123
|
let presetData = null;
|
|
14069
14124
|
try {
|
|
@@ -14200,7 +14255,7 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
|
|
|
14200
14255
|
const files = await fs24.readdir(rulePresetDir);
|
|
14201
14256
|
for (const file of files) {
|
|
14202
14257
|
if (file.endsWith(".mdc") || file.endsWith(".md")) {
|
|
14203
|
-
const filePath =
|
|
14258
|
+
const filePath = join17(rulePresetDir, file);
|
|
14204
14259
|
const content = await fs24.readFile(filePath, "utf-8");
|
|
14205
14260
|
bundle.files[file] = content;
|
|
14206
14261
|
}
|
|
@@ -14215,8 +14270,8 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
|
|
|
14215
14270
|
}
|
|
14216
14271
|
const files2 = generatorService.generateForIde(bundle, ideId);
|
|
14217
14272
|
for (const file of files2) {
|
|
14218
|
-
const fullPath =
|
|
14219
|
-
await fs24.mkdir(
|
|
14273
|
+
const fullPath = join17(process.cwd(), file.path);
|
|
14274
|
+
await fs24.mkdir(join17(fullPath, ".."), { recursive: true });
|
|
14220
14275
|
await fs24.writeFile(fullPath, file.content, "utf-8");
|
|
14221
14276
|
}
|
|
14222
14277
|
console.log(`\u2713 ${format.name} - ${files2.length} files regenerated`);
|
|
@@ -14281,7 +14336,7 @@ function buildIdeChoices(currentIdes, detected, suggestions) {
|
|
|
14281
14336
|
// src/commands/rules/info.ts
|
|
14282
14337
|
import { Command as Command85 } from "commander";
|
|
14283
14338
|
import { promises as fs25 } from "fs";
|
|
14284
|
-
import { join as
|
|
14339
|
+
import { join as join18 } from "path";
|
|
14285
14340
|
function createRulesInfoCommand() {
|
|
14286
14341
|
return new Command85("info").description("Show current preset information").option("--json", "Output as JSON").action(async (options) => {
|
|
14287
14342
|
const projectConfigService = new ProjectConfigService();
|
|
@@ -14296,8 +14351,8 @@ function createRulesInfoCommand() {
|
|
|
14296
14351
|
return;
|
|
14297
14352
|
}
|
|
14298
14353
|
console.log("\u{1F4CB} Current Preset Information\n");
|
|
14299
|
-
const rulePresetDir =
|
|
14300
|
-
const presetJsonPath =
|
|
14354
|
+
const rulePresetDir = join18(process.cwd(), ".jai1", "rule-preset");
|
|
14355
|
+
const presetJsonPath = join18(rulePresetDir, "preset.json");
|
|
14301
14356
|
let presetMetadata = null;
|
|
14302
14357
|
let presetFiles = [];
|
|
14303
14358
|
try {
|
|
@@ -14364,7 +14419,7 @@ Available Backups (${rulesConfig.backups.length}):`);
|
|
|
14364
14419
|
}
|
|
14365
14420
|
async function checkPathExists(path13) {
|
|
14366
14421
|
try {
|
|
14367
|
-
await fs25.access(
|
|
14422
|
+
await fs25.access(join18(process.cwd(), path13));
|
|
14368
14423
|
return true;
|
|
14369
14424
|
} catch {
|
|
14370
14425
|
return false;
|
|
@@ -14427,7 +14482,7 @@ import chalk51 from "chalk";
|
|
|
14427
14482
|
|
|
14428
14483
|
// src/services/skills.service.ts
|
|
14429
14484
|
import { promises as fs26 } from "fs";
|
|
14430
|
-
import { join as
|
|
14485
|
+
import { join as join19 } from "path";
|
|
14431
14486
|
import { execFile } from "child_process";
|
|
14432
14487
|
import { promisify } from "util";
|
|
14433
14488
|
var execFileAsync = promisify(execFile);
|
|
@@ -14463,14 +14518,14 @@ var SkillsService = class {
|
|
|
14463
14518
|
* List locally installed skills from .jai1/skills/
|
|
14464
14519
|
*/
|
|
14465
14520
|
async listLocal(projectRoot) {
|
|
14466
|
-
const skillsDir =
|
|
14521
|
+
const skillsDir = join19(projectRoot, ".jai1", "skills");
|
|
14467
14522
|
const skills = [];
|
|
14468
14523
|
try {
|
|
14469
14524
|
const entries = await fs26.readdir(skillsDir, { withFileTypes: true });
|
|
14470
14525
|
for (const entry of entries) {
|
|
14471
14526
|
if (!entry.isDirectory()) continue;
|
|
14472
|
-
const skillPath =
|
|
14473
|
-
const skillMd =
|
|
14527
|
+
const skillPath = join19(skillsDir, entry.name);
|
|
14528
|
+
const skillMd = join19(skillPath, "SKILL.md");
|
|
14474
14529
|
try {
|
|
14475
14530
|
await fs26.access(skillMd);
|
|
14476
14531
|
} catch {
|
|
@@ -14495,8 +14550,8 @@ var SkillsService = class {
|
|
|
14495
14550
|
* Get detailed info for a single skill
|
|
14496
14551
|
*/
|
|
14497
14552
|
async getSkillInfo(projectRoot, skillName) {
|
|
14498
|
-
const skillPath =
|
|
14499
|
-
const skillMd =
|
|
14553
|
+
const skillPath = join19(projectRoot, ".jai1", "skills", skillName);
|
|
14554
|
+
const skillMd = join19(skillPath, "SKILL.md");
|
|
14500
14555
|
try {
|
|
14501
14556
|
await fs26.access(skillMd);
|
|
14502
14557
|
} catch {
|
|
@@ -14571,22 +14626,22 @@ var SkillsService = class {
|
|
|
14571
14626
|
* After npx skills add, copy newly installed skills from .agents/skills/ into .jai1/skills/
|
|
14572
14627
|
*/
|
|
14573
14628
|
async copySkillshResultsToJai1(projectRoot, specificSkill) {
|
|
14574
|
-
const jai1SkillsDir =
|
|
14629
|
+
const jai1SkillsDir = join19(projectRoot, ".jai1", "skills");
|
|
14575
14630
|
await fs26.mkdir(jai1SkillsDir, { recursive: true });
|
|
14576
|
-
const universalDir =
|
|
14631
|
+
const universalDir = join19(projectRoot, ".agents", "skills");
|
|
14577
14632
|
try {
|
|
14578
14633
|
const entries = await fs26.readdir(universalDir, { withFileTypes: true });
|
|
14579
14634
|
for (const entry of entries) {
|
|
14580
14635
|
if (!entry.isDirectory()) continue;
|
|
14581
14636
|
if (specificSkill && entry.name !== specificSkill) continue;
|
|
14582
|
-
const srcSkill =
|
|
14583
|
-
const skillMd =
|
|
14637
|
+
const srcSkill = join19(universalDir, entry.name);
|
|
14638
|
+
const skillMd = join19(srcSkill, "SKILL.md");
|
|
14584
14639
|
try {
|
|
14585
14640
|
await fs26.access(skillMd);
|
|
14586
14641
|
} catch {
|
|
14587
14642
|
continue;
|
|
14588
14643
|
}
|
|
14589
|
-
const targetSkill =
|
|
14644
|
+
const targetSkill = join19(jai1SkillsDir, entry.name);
|
|
14590
14645
|
try {
|
|
14591
14646
|
await fs26.access(targetSkill);
|
|
14592
14647
|
} catch {
|
|
@@ -14625,7 +14680,7 @@ var SkillsService = class {
|
|
|
14625
14680
|
const target = this.getIDETarget(ide);
|
|
14626
14681
|
if (!target) continue;
|
|
14627
14682
|
for (const skill of skillsToSync) {
|
|
14628
|
-
const targetPath =
|
|
14683
|
+
const targetPath = join19(projectRoot, target.skillsPath, skill.slug);
|
|
14629
14684
|
try {
|
|
14630
14685
|
let status = "created";
|
|
14631
14686
|
try {
|
|
@@ -14689,7 +14744,7 @@ var SkillsService = class {
|
|
|
14689
14744
|
const entries = await fs26.readdir(dirPath, { withFileTypes: true });
|
|
14690
14745
|
for (const entry of entries) {
|
|
14691
14746
|
if (entry.isDirectory()) {
|
|
14692
|
-
count += await this.countFiles(
|
|
14747
|
+
count += await this.countFiles(join19(dirPath, entry.name));
|
|
14693
14748
|
} else {
|
|
14694
14749
|
count++;
|
|
14695
14750
|
}
|
|
@@ -14702,8 +14757,8 @@ var SkillsService = class {
|
|
|
14702
14757
|
async copyDir(source, target) {
|
|
14703
14758
|
const entries = await fs26.readdir(source, { withFileTypes: true });
|
|
14704
14759
|
for (const entry of entries) {
|
|
14705
|
-
const srcPath =
|
|
14706
|
-
const tgtPath =
|
|
14760
|
+
const srcPath = join19(source, entry.name);
|
|
14761
|
+
const tgtPath = join19(target, entry.name);
|
|
14707
14762
|
if (entry.isDirectory()) {
|
|
14708
14763
|
await fs26.mkdir(tgtPath, { recursive: true });
|
|
14709
14764
|
await this.copyDir(srcPath, tgtPath);
|
|
@@ -14777,7 +14832,7 @@ function createSkillsFindCommand() {
|
|
|
14777
14832
|
|
|
14778
14833
|
// src/commands/skills/add.ts
|
|
14779
14834
|
import { Command as Command88 } from "commander";
|
|
14780
|
-
import { join as
|
|
14835
|
+
import { join as join20 } from "path";
|
|
14781
14836
|
import chalk52 from "chalk";
|
|
14782
14837
|
import { checkbox as checkbox7 } from "@inquirer/prompts";
|
|
14783
14838
|
function createSkillsAddCommand() {
|
|
@@ -14799,7 +14854,7 @@ function createSkillsAddCommand() {
|
|
|
14799
14854
|
}
|
|
14800
14855
|
console.log(chalk52.cyan(`\u{1F4E6} \u0110ang c\xE0i \u0111\u1EB7t skill: ${name}...`));
|
|
14801
14856
|
console.log();
|
|
14802
|
-
const targetDir =
|
|
14857
|
+
const targetDir = join20(projectRoot, ".jai1");
|
|
14803
14858
|
await skillsService.installFromServer(config, name, targetDir);
|
|
14804
14859
|
console.log(chalk52.green(`\u2705 \u0110\xE3 c\xE0i \u0111\u1EB7t skill "${name}" v\xE0o .jai1/skills/${name}/`));
|
|
14805
14860
|
}
|
|
@@ -15289,8 +15344,8 @@ function getInstallCommand(packageManager2) {
|
|
|
15289
15344
|
import { Command as Command94 } from "commander";
|
|
15290
15345
|
import { confirm as confirm22, select as select6 } from "@inquirer/prompts";
|
|
15291
15346
|
import { promises as fs27 } from "fs";
|
|
15292
|
-
import { join as
|
|
15293
|
-
import { existsSync as
|
|
15347
|
+
import { join as join21 } from "path";
|
|
15348
|
+
import { existsSync as existsSync5 } from "fs";
|
|
15294
15349
|
function createCleanCommand() {
|
|
15295
15350
|
return new Command94("clean").description("Clean up backups, cache, IDE configs, and .jai1 directory").option("-y, --yes", "Skip confirmation").option("--backups", "Clean only backup files").option("--jai1", "Clean only .jai1/ directory").option("--ide", "Clean only IDE directories (.cursor, .windsurf, .agent, .claude, .opencode)").option("--all", "Clean all (backups + .jai1 + IDE dirs)").action(async (options) => {
|
|
15296
15351
|
await handleClean(options);
|
|
@@ -15303,7 +15358,7 @@ async function handleClean(options) {
|
|
|
15303
15358
|
{
|
|
15304
15359
|
name: "Backups",
|
|
15305
15360
|
description: "Component backup files (.jai1_backup/)",
|
|
15306
|
-
path:
|
|
15361
|
+
path: join21(cwd, ".jai1_backup"),
|
|
15307
15362
|
check: async () => {
|
|
15308
15363
|
const backups = await service.listBackups(cwd);
|
|
15309
15364
|
return { exists: backups.length > 0, count: backups.length };
|
|
@@ -15315,13 +15370,13 @@ async function handleClean(options) {
|
|
|
15315
15370
|
{
|
|
15316
15371
|
name: "Jai1 Config",
|
|
15317
15372
|
description: "Jai1 framework config (.jai1/)",
|
|
15318
|
-
path:
|
|
15373
|
+
path: join21(cwd, ".jai1"),
|
|
15319
15374
|
check: async () => {
|
|
15320
|
-
const exists =
|
|
15375
|
+
const exists = existsSync5(join21(cwd, ".jai1"));
|
|
15321
15376
|
return { exists };
|
|
15322
15377
|
},
|
|
15323
15378
|
clean: async () => {
|
|
15324
|
-
await fs27.rm(
|
|
15379
|
+
await fs27.rm(join21(cwd, ".jai1"), { recursive: true, force: true });
|
|
15325
15380
|
}
|
|
15326
15381
|
}
|
|
15327
15382
|
];
|
|
@@ -15333,8 +15388,8 @@ async function handleClean(options) {
|
|
|
15333
15388
|
{ name: "OpenCode", dir: ".opencode" }
|
|
15334
15389
|
];
|
|
15335
15390
|
for (const ide of ideDirectories) {
|
|
15336
|
-
const idePath =
|
|
15337
|
-
if (
|
|
15391
|
+
const idePath = join21(cwd, ide.dir);
|
|
15392
|
+
if (existsSync5(idePath)) {
|
|
15338
15393
|
targets.push({
|
|
15339
15394
|
name: `IDE: ${ide.name}`,
|
|
15340
15395
|
description: `${ide.name} IDE config (${ide.dir}/)`,
|
|
@@ -16342,7 +16397,7 @@ async function handleSyncProject(options) {
|
|
|
16342
16397
|
// src/commands/framework/info.ts
|
|
16343
16398
|
import { Command as Command98 } from "commander";
|
|
16344
16399
|
import { promises as fs28 } from "fs";
|
|
16345
|
-
import { join as
|
|
16400
|
+
import { join as join22 } from "path";
|
|
16346
16401
|
import { homedir as homedir4 } from "os";
|
|
16347
16402
|
function createInfoCommand() {
|
|
16348
16403
|
const cmd = new Command98("info").description("Show client configuration and status").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
|
|
@@ -16356,7 +16411,7 @@ async function handleInfo(options) {
|
|
|
16356
16411
|
if (!config) {
|
|
16357
16412
|
throw new ValidationError(`Not initialized. Run "${getCliName()} auth" first.`);
|
|
16358
16413
|
}
|
|
16359
|
-
const frameworkPath =
|
|
16414
|
+
const frameworkPath = join22(homedir4(), ".jai1", "framework");
|
|
16360
16415
|
const projectStatus = await getProjectStatus2();
|
|
16361
16416
|
const info = {
|
|
16362
16417
|
configPath: configService.getConfigPath(),
|
|
@@ -16391,7 +16446,7 @@ function maskKey4(key) {
|
|
|
16391
16446
|
return "****" + key.slice(-4);
|
|
16392
16447
|
}
|
|
16393
16448
|
async function getProjectStatus2() {
|
|
16394
|
-
const projectJai1 =
|
|
16449
|
+
const projectJai1 = join22(process.cwd(), ".jai1");
|
|
16395
16450
|
try {
|
|
16396
16451
|
await fs28.access(projectJai1);
|
|
16397
16452
|
return { exists: true, version: "Synced" };
|
|
@@ -16585,7 +16640,7 @@ import { Command as Command101 } from "commander";
|
|
|
16585
16640
|
import { checkbox as checkbox9, confirm as confirm25, select as select7 } from "@inquirer/prompts";
|
|
16586
16641
|
import fs29 from "fs/promises";
|
|
16587
16642
|
import path12 from "path";
|
|
16588
|
-
import { existsSync as
|
|
16643
|
+
import { existsSync as existsSync6 } from "fs";
|
|
16589
16644
|
var PERFORMANCE_GROUPS2 = {
|
|
16590
16645
|
telemetry: {
|
|
16591
16646
|
name: "Telemetry",
|
|
@@ -16815,12 +16870,12 @@ async function applyGroups2(groupKeys, action) {
|
|
|
16815
16870
|
console.log(' \u{1F4A1} Ch\u1EA1y "jai1 vscode list" \u0111\u1EC3 xem danh s\xE1ch nh\xF3m c\xF3 s\u1EB5n.');
|
|
16816
16871
|
return;
|
|
16817
16872
|
}
|
|
16818
|
-
if (!
|
|
16873
|
+
if (!existsSync6(vscodeDir)) {
|
|
16819
16874
|
await fs29.mkdir(vscodeDir, { recursive: true });
|
|
16820
16875
|
console.log("\u{1F4C1} \u0110\xE3 t\u1EA1o th\u01B0 m\u1EE5c .vscode/");
|
|
16821
16876
|
}
|
|
16822
16877
|
let currentSettings = {};
|
|
16823
|
-
if (
|
|
16878
|
+
if (existsSync6(settingsPath)) {
|
|
16824
16879
|
try {
|
|
16825
16880
|
const content = await fs29.readFile(settingsPath, "utf-8");
|
|
16826
16881
|
currentSettings = JSON.parse(content);
|
|
@@ -16870,7 +16925,7 @@ async function applyGroups2(groupKeys, action) {
|
|
|
16870
16925
|
async function resetSettings2(groupKeys) {
|
|
16871
16926
|
const vscodeDir = path12.join(process.cwd(), ".vscode");
|
|
16872
16927
|
const settingsPath = path12.join(vscodeDir, "settings.json");
|
|
16873
|
-
if (!
|
|
16928
|
+
if (!existsSync6(settingsPath)) {
|
|
16874
16929
|
console.log("\n\u26A0\uFE0F Kh\xF4ng t\xECm th\u1EA5y file settings.json");
|
|
16875
16930
|
return;
|
|
16876
16931
|
}
|