@jvittechs/j 1.0.60 → 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/cli.js CHANGED
@@ -9,14 +9,18 @@ import {
9
9
  import {
10
10
  createSettingsShowCommand
11
11
  } from "./chunk-5RDBJYO2.js";
12
+ import {
13
+ CloudTaskProvider,
14
+ TaskService,
15
+ createTaskSummaryCommand
16
+ } from "./chunk-TEVFTK65.js";
12
17
  import {
13
18
  BLOCKED_ICON,
14
19
  PRIORITY_ICONS,
15
20
  PRIORITY_LABELS,
16
21
  STATUS_ICONS,
17
- TaskService,
18
- createTaskSummaryCommand
19
- } from "./chunk-22FXNN76.js";
22
+ TaskSchema
23
+ } from "./chunk-WOAXYUZ6.js";
20
24
  import {
21
25
  ConfigService,
22
26
  SettingsService
@@ -156,7 +160,7 @@ import { basename as basename5 } from "path";
156
160
  // package.json
157
161
  var package_default = {
158
162
  name: "@jvittechs/j",
159
- version: "1.0.60",
163
+ version: "1.0.62",
160
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.",
161
165
  type: "module",
162
166
  bin: {
@@ -11366,13 +11370,54 @@ async function checkAuthStatus() {
11366
11370
  try {
11367
11371
  const config = await new ConfigService().load();
11368
11372
  if (config?.apiUrl && config?.accessKey) {
11369
- return { ok: true, apiUrl: config.apiUrl, accessKey: config.accessKey };
11373
+ return { ok: true, config };
11370
11374
  }
11371
11375
  return { ok: false };
11372
11376
  } catch {
11373
11377
  return { ok: false };
11374
11378
  }
11375
11379
  }
11380
+ async function registerProject(config, projectId, repoUrl, silent) {
11381
+ try {
11382
+ const provider = new CloudTaskProvider(config, projectId);
11383
+ const registeredId = await provider.ensureProjectRegistered(repoUrl);
11384
+ if (!silent) {
11385
+ console.log(chalk31.green(" \u2713 Project registered on server"));
11386
+ }
11387
+ return registeredId;
11388
+ } catch (error) {
11389
+ if (!silent) {
11390
+ const msg = error instanceof Error ? error.message : String(error);
11391
+ console.log(chalk31.yellow(` \u26A0\uFE0F Register failed: ${msg}`));
11392
+ console.log(chalk31.dim(" Project s\u1EBD \u0111\u01B0\u1EE3c \u0111\u0103ng k\xFD l\u1EA1i khi ch\u1EA1y l\u1EC7nh task \u0111\u1EA7u ti\xEAn."));
11393
+ }
11394
+ return null;
11395
+ }
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
+ }
11376
11421
  function createSettingsInitCommand() {
11377
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) => {
11378
11423
  try {
@@ -11381,6 +11426,7 @@ function createSettingsInitCommand() {
11381
11426
  throw new Error("Settings file \u0111\xE3 t\u1ED3n t\u1EA1i. D\xF9ng --force \u0111\u1EC3 ghi \u0111\xE8.");
11382
11427
  }
11383
11428
  let enableCloud = options.cloud || false;
11429
+ let authResult = { ok: false };
11384
11430
  if (options.interactive !== false && !options.json) {
11385
11431
  const repoUrl = service.resolveGitRepoUrl();
11386
11432
  console.log(chalk31.bold("\u{1F4CB} Kh\u1EDFi t\u1EA1o Project Settings"));
@@ -11394,8 +11440,8 @@ function createSettingsInitCommand() {
11394
11440
  default: false
11395
11441
  });
11396
11442
  if (enableCloud) {
11397
- const auth = await checkAuthStatus();
11398
- if (!auth.ok) {
11443
+ authResult = await checkAuthStatus();
11444
+ if (!authResult.ok) {
11399
11445
  console.log("");
11400
11446
  console.log(chalk31.yellow("\u26A0\uFE0F Ch\u01B0a \u0111\u0103ng nh\u1EADp Jai1."));
11401
11447
  console.log(chalk31.dim(" Ch\u1EA1y: j auth"));
@@ -11409,22 +11455,48 @@ function createSettingsInitCommand() {
11409
11455
  console.log(chalk31.green(" \u2713 Auth OK"));
11410
11456
  }
11411
11457
  }
11412
- } else if (enableCloud && !options.json) {
11413
- const auth = await checkAuthStatus();
11414
- if (!auth.ok) {
11458
+ } else if (enableCloud) {
11459
+ authResult = await checkAuthStatus();
11460
+ if (!authResult.ok && !options.json) {
11415
11461
  console.log(chalk31.yellow("\u26A0\uFE0F Ch\u01B0a \u0111\u0103ng nh\u1EADp. Ch\u1EA1y j auth tr\u01B0\u1EDBc khi d\xF9ng cloud tasks."));
11416
11462
  }
11417
11463
  }
11418
- const settings = await service.init(true);
11464
+ await service.init(true);
11419
11465
  if (enableCloud) {
11420
11466
  await service.set("tasks.cloud", true);
11421
11467
  }
11468
+ let registeredProjectId = null;
11469
+ if (enableCloud && authResult.ok && authResult.config) {
11470
+ const repoUrl = service.resolveGitRepoUrl();
11471
+ const projectId = service.getProjectId();
11472
+ if (repoUrl && projectId) {
11473
+ registeredProjectId = await registerProject(
11474
+ authResult.config,
11475
+ projectId,
11476
+ repoUrl,
11477
+ !!options.json
11478
+ );
11479
+ const finalProjectId = registeredProjectId && registeredProjectId !== projectId ? registeredProjectId : projectId;
11480
+ if (registeredProjectId && registeredProjectId !== projectId) {
11481
+ await service.set("tasks.projectId", registeredProjectId);
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
+ }
11491
+ }
11492
+ }
11422
11493
  const finalSettings = service.load();
11423
11494
  if (options.json) {
11424
11495
  console.log(JSON.stringify({
11425
11496
  success: true,
11426
11497
  path: service.getSettingsPath(),
11427
- settings: finalSettings
11498
+ settings: finalSettings,
11499
+ registered: !!registeredProjectId
11428
11500
  }, null, 2));
11429
11501
  return;
11430
11502
  }
@@ -11437,8 +11509,6 @@ function createSettingsInitCommand() {
11437
11509
  if (projectId) {
11438
11510
  console.log(` ${chalk31.dim("Project ID:")} ${projectId}`);
11439
11511
  }
11440
- console.log("");
11441
- console.log(chalk31.dim(" Cloud tasks s\u1EBD t\u1EF1 \u0111\u0103ng k\xFD project khi ch\u1EA1y l\u1EC7nh task \u0111\u1EA7u ti\xEAn."));
11442
11512
  }
11443
11513
  } catch (error) {
11444
11514
  if (options.json) {
@@ -11454,6 +11524,8 @@ function createSettingsInitCommand() {
11454
11524
  // src/commands/settings/set.ts
11455
11525
  import { Command as Command59 } from "commander";
11456
11526
  import chalk32 from "chalk";
11527
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
11528
+ import { join as join9 } from "path";
11457
11529
  function createSettingsSetCommand() {
11458
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) => {
11459
11531
  try {
@@ -11476,9 +11548,38 @@ function createSettingsSetCommand() {
11476
11548
  console.log(chalk32.yellow("\u26A0\uFE0F Ch\u01B0a \u0111\u0103ng nh\u1EADp. Ch\u1EA1y j auth tr\u01B0\u1EDBc khi d\xF9ng cloud tasks."));
11477
11549
  } else {
11478
11550
  console.log(chalk32.dim(" Auth: \u2713 OK"));
11551
+ const repoUrl = service.resolveGitRepoUrl();
11479
11552
  const projectId = service.getProjectId();
11480
- if (projectId) {
11481
- console.log(chalk32.dim(` Project: ${projectId}`));
11553
+ if (repoUrl && projectId) {
11554
+ let finalProjectId = projectId;
11555
+ try {
11556
+ const provider = new CloudTaskProvider(config, projectId);
11557
+ const registeredId = await provider.ensureProjectRegistered(repoUrl);
11558
+ console.log(chalk32.green(" \u2713 Project registered on server"));
11559
+ if (registeredId && registeredId !== projectId) {
11560
+ await service.set("tasks.projectId", registeredId);
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
+ }
11577
+ }
11578
+ } catch (error) {
11579
+ const msg = error instanceof Error ? error.message : String(error);
11580
+ console.log(chalk32.yellow(` \u26A0\uFE0F Register failed: ${msg}`));
11581
+ console.log(chalk32.dim(" Project s\u1EBD \u0111\u01B0\u1EE3c \u0111\u0103ng k\xFD l\u1EA1i khi ch\u1EA1y l\u1EC7nh task \u0111\u1EA7u ti\xEAn."));
11582
+ }
11482
11583
  }
11483
11584
  }
11484
11585
  }
@@ -11624,6 +11725,9 @@ function printTaskLine(task, resolvedIds) {
11624
11725
  const blockedBy = task.depends_on.filter((id) => !resolvedIds.has(id));
11625
11726
  line += chalk35.red(` (blocked: ${blockedBy.join(", ")})`);
11626
11727
  }
11728
+ if (task.type && task.type !== "task") {
11729
+ line += chalk35.magenta(` {${task.type}}`);
11730
+ }
11627
11731
  if (task.parent) {
11628
11732
  line += chalk35.dim(` [${task.parent}]`);
11629
11733
  }
@@ -12347,7 +12451,7 @@ function createTasksCommand() {
12347
12451
  cmd.addCommand(createTaskDeleteCommand());
12348
12452
  cmd.addCommand(createTaskGuideCommand());
12349
12453
  cmd.action(async () => {
12350
- const { handleTaskSummary } = await import("./summary-4ZVFN4XV.js");
12454
+ const { handleTaskSummary } = await import("./summary-7EHQYYJU.js");
12351
12455
  await handleTaskSummary({ json: false });
12352
12456
  });
12353
12457
  return cmd;
@@ -12364,7 +12468,7 @@ import Table6 from "cli-table3";
12364
12468
 
12365
12469
  // src/services/starter-kit.service.ts
12366
12470
  import { promises as fs18 } from "fs";
12367
- import { join as join9 } from "path";
12471
+ import { join as join10 } from "path";
12368
12472
  import AdmZip from "adm-zip";
12369
12473
  var StarterKitService = class {
12370
12474
  /**
@@ -12411,9 +12515,9 @@ var StarterKitService = class {
12411
12515
  throw new NetworkError(`Failed to download kit: HTTP ${response.status}`);
12412
12516
  }
12413
12517
  if (onProgress) onProgress(30);
12414
- const tmpDir = join9(process.env.TMPDIR || "/tmp", "jai1-kits");
12518
+ const tmpDir = join10(process.env.TMPDIR || "/tmp", "jai1-kits");
12415
12519
  await fs18.mkdir(tmpDir, { recursive: true });
12416
- const tmpFile = join9(tmpDir, `${slug}.zip`);
12520
+ const tmpFile = join10(tmpDir, `${slug}.zip`);
12417
12521
  const buffer = await response.arrayBuffer();
12418
12522
  await fs18.writeFile(tmpFile, Buffer.from(buffer));
12419
12523
  if (onProgress) onProgress(60);
@@ -12546,7 +12650,7 @@ Post-Init Commands:`);
12546
12650
  // src/commands/kit/create.ts
12547
12651
  import { Command as Command78 } from "commander";
12548
12652
  import { promises as fs19 } from "fs";
12549
- import { join as join10 } from "path";
12653
+ import { join as join11 } from "path";
12550
12654
  import { select as select3, input as input2, checkbox as checkbox4 } from "@inquirer/prompts";
12551
12655
  import { execa as execa2 } from "execa";
12552
12656
 
@@ -12609,7 +12713,7 @@ function createKitCreateCommand() {
12609
12713
  }
12610
12714
  }
12611
12715
  }
12612
- const targetDir = directory || join10(process.cwd(), options.name || slug);
12716
+ const targetDir = directory || join11(process.cwd(), options.name || slug);
12613
12717
  try {
12614
12718
  await fs19.access(targetDir);
12615
12719
  throw new Error(`Directory already exists: ${targetDir}`);
@@ -12674,7 +12778,7 @@ function createKitCreateCommand() {
12674
12778
  );
12675
12779
  for (const filepath of expandedPaths) {
12676
12780
  console.log(` \u{1F4E5} Installing ${filepath}...`);
12677
- await componentsService.install(config, filepath, join10(targetDir, ".jai1"));
12781
+ await componentsService.install(config, filepath, join11(targetDir, ".jai1"));
12678
12782
  }
12679
12783
  console.log(" \u2713 Framework components applied");
12680
12784
  }
@@ -12754,7 +12858,7 @@ async function getAllFiles(dir) {
12754
12858
  const files = [];
12755
12859
  const entries = await fs19.readdir(dir, { withFileTypes: true });
12756
12860
  for (const entry of entries) {
12757
- const fullPath = join10(dir, entry.name);
12861
+ const fullPath = join11(dir, entry.name);
12758
12862
  if (entry.isDirectory()) {
12759
12863
  if (!entry.name.startsWith(".") && entry.name !== "node_modules") {
12760
12864
  files.push(...await getAllFiles(fullPath));
@@ -12870,20 +12974,20 @@ function createRulesListCommand() {
12870
12974
  // src/commands/rules/init.ts
12871
12975
  import { Command as Command81 } from "commander";
12872
12976
  import { promises as fs21 } from "fs";
12873
- import { join as join12 } from "path";
12977
+ import { join as join13 } from "path";
12874
12978
  import { select as select4, confirm as confirm15 } from "@inquirer/prompts";
12875
12979
 
12876
12980
  // src/services/project-config.service.ts
12877
12981
  import { promises as fs20 } from "fs";
12878
- import { join as join11 } from "path";
12982
+ import { join as join12 } from "path";
12879
12983
  var ProjectConfigService = class {
12880
12984
  projectRoot;
12881
12985
  configDir;
12882
12986
  configPath;
12883
12987
  constructor(projectRoot = process.cwd()) {
12884
12988
  this.projectRoot = projectRoot;
12885
- this.configDir = join11(this.projectRoot, ".jai1");
12886
- this.configPath = join11(this.configDir, "project.json");
12989
+ this.configDir = join12(this.projectRoot, ".jai1");
12990
+ this.configPath = join12(this.configDir, "project.json");
12887
12991
  }
12888
12992
  /**
12889
12993
  * Check if config file exists
@@ -13089,10 +13193,10 @@ function createRulesInitCommand() {
13089
13193
  });
13090
13194
  }
13091
13195
  async function applyCursorFormat(bundle) {
13092
- const rulesDir = join12(process.cwd(), ".cursor", "rules");
13196
+ const rulesDir = join13(process.cwd(), ".cursor", "rules");
13093
13197
  await fs21.mkdir(rulesDir, { recursive: true });
13094
13198
  for (const [filename, content] of Object.entries(bundle.files)) {
13095
- const filePath = join12(rulesDir, filename);
13199
+ const filePath = join13(rulesDir, filename);
13096
13200
  await fs21.writeFile(filePath, content, "utf-8");
13097
13201
  console.log(`\u2713 Created .cursor/rules/${filename}`);
13098
13202
  }
@@ -13127,7 +13231,7 @@ async function applyAgentsMdFormat(bundle) {
13127
13231
  // src/commands/rules/apply.ts
13128
13232
  import { Command as Command82 } from "commander";
13129
13233
  import { promises as fs23 } from "fs";
13130
- import { join as join14 } from "path";
13234
+ import { join as join15 } from "path";
13131
13235
  import { search, confirm as confirm16, checkbox as checkbox5 } from "@inquirer/prompts";
13132
13236
 
13133
13237
  // src/services/rules-generator.service.ts
@@ -13419,7 +13523,7 @@ Follow all instructions and patterns defined in AGENTS.md above.
13419
13523
 
13420
13524
  // src/services/backup.service.ts
13421
13525
  import { promises as fs22 } from "fs";
13422
- import { join as join13, dirname } from "path";
13526
+ import { join as join14, dirname } from "path";
13423
13527
  var BackupService = class {
13424
13528
  backupDir = ".jai1/backups";
13425
13529
  /**
@@ -13427,7 +13531,7 @@ var BackupService = class {
13427
13531
  */
13428
13532
  async createBackup(ides, presetSlug) {
13429
13533
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
13430
- const backupPath = join13(this.backupDir, timestamp);
13534
+ const backupPath = join14(this.backupDir, timestamp);
13431
13535
  const backedUpFiles = [];
13432
13536
  let hasContent = false;
13433
13537
  for (const ideId of ides) {
@@ -13436,7 +13540,7 @@ var BackupService = class {
13436
13540
  console.warn(`Unknown IDE format: ${ideId}, skipping backup`);
13437
13541
  continue;
13438
13542
  }
13439
- const rulesPath = format.rulesPath === "." ? process.cwd() : join13(process.cwd(), format.rulesPath);
13543
+ const rulesPath = format.rulesPath === "." ? process.cwd() : join14(process.cwd(), format.rulesPath);
13440
13544
  try {
13441
13545
  const exists = await this.pathExists(rulesPath);
13442
13546
  if (!exists) {
@@ -13454,14 +13558,14 @@ var BackupService = class {
13454
13558
  const files = await fs22.readdir(rulesPath);
13455
13559
  for (const file of files) {
13456
13560
  if (file.endsWith(format.fileExtension)) {
13457
- const originalPath = join13(rulesPath, file);
13458
- const relativePath = join13(format.rulesPath, file);
13459
- const destPath = join13(backupPath, ideId, file);
13561
+ const originalPath = join14(rulesPath, file);
13562
+ const relativePath = join14(format.rulesPath, file);
13563
+ const destPath = join14(backupPath, ideId, file);
13460
13564
  await fs22.mkdir(dirname(destPath), { recursive: true });
13461
13565
  await fs22.copyFile(originalPath, destPath);
13462
13566
  backedUpFiles.push({
13463
13567
  originalPath: relativePath,
13464
- backupPath: join13(ideId, file),
13568
+ backupPath: join14(ideId, file),
13465
13569
  ide: ideId
13466
13570
  });
13467
13571
  hasContent = true;
@@ -13484,7 +13588,7 @@ var BackupService = class {
13484
13588
  };
13485
13589
  await fs22.mkdir(backupPath, { recursive: true });
13486
13590
  await fs22.writeFile(
13487
- join13(backupPath, "metadata.json"),
13591
+ join14(backupPath, "metadata.json"),
13488
13592
  JSON.stringify(metadata, null, 2),
13489
13593
  "utf-8"
13490
13594
  );
@@ -13494,18 +13598,18 @@ var BackupService = class {
13494
13598
  * Backup a single file (for AGENTS.md, GEMINI.md)
13495
13599
  */
13496
13600
  async backupSingleFile(filename, backupPath, ideId, backedUpFiles) {
13497
- const originalPath = join13(process.cwd(), filename);
13601
+ const originalPath = join14(process.cwd(), filename);
13498
13602
  try {
13499
13603
  const exists = await this.pathExists(originalPath);
13500
13604
  if (!exists) {
13501
13605
  return;
13502
13606
  }
13503
- const destPath = join13(backupPath, ideId, filename);
13607
+ const destPath = join14(backupPath, ideId, filename);
13504
13608
  await fs22.mkdir(dirname(destPath), { recursive: true });
13505
13609
  await fs22.copyFile(originalPath, destPath);
13506
13610
  backedUpFiles.push({
13507
13611
  originalPath: filename,
13508
- backupPath: join13(ideId, filename),
13612
+ backupPath: join14(ideId, filename),
13509
13613
  ide: ideId
13510
13614
  });
13511
13615
  } catch (error) {
@@ -13515,14 +13619,14 @@ var BackupService = class {
13515
13619
  * Restore files from a backup
13516
13620
  */
13517
13621
  async restoreBackup(backupPath) {
13518
- const metadataPath = join13(backupPath, "metadata.json");
13622
+ const metadataPath = join14(backupPath, "metadata.json");
13519
13623
  const metadataContent = await fs22.readFile(metadataPath, "utf-8");
13520
13624
  const metadata = JSON.parse(metadataContent);
13521
13625
  console.log(`
13522
13626
  Restoring backup from ${metadata.timestamp}...`);
13523
13627
  for (const file of metadata.files) {
13524
- const sourcePath = join13(backupPath, file.backupPath);
13525
- const destPath = join13(process.cwd(), file.originalPath);
13628
+ const sourcePath = join14(backupPath, file.backupPath);
13629
+ const destPath = join14(process.cwd(), file.originalPath);
13526
13630
  await fs22.mkdir(dirname(destPath), { recursive: true });
13527
13631
  await fs22.copyFile(sourcePath, destPath);
13528
13632
  console.log(`\u2713 Restored ${file.originalPath}`);
@@ -13534,7 +13638,7 @@ Restoring backup from ${metadata.timestamp}...`);
13534
13638
  */
13535
13639
  async listBackups() {
13536
13640
  try {
13537
- const backupDirPath = join13(process.cwd(), this.backupDir);
13641
+ const backupDirPath = join14(process.cwd(), this.backupDir);
13538
13642
  const exists = await this.pathExists(backupDirPath);
13539
13643
  if (!exists) {
13540
13644
  return [];
@@ -13543,7 +13647,7 @@ Restoring backup from ${metadata.timestamp}...`);
13543
13647
  const backups = [];
13544
13648
  for (const entry of entries) {
13545
13649
  if (entry.isDirectory()) {
13546
- const metadataPath = join13(backupDirPath, entry.name, "metadata.json");
13650
+ const metadataPath = join14(backupDirPath, entry.name, "metadata.json");
13547
13651
  try {
13548
13652
  const metadataContent = await fs22.readFile(metadataPath, "utf-8");
13549
13653
  const metadata = JSON.parse(metadataContent);
@@ -13562,7 +13666,7 @@ Restoring backup from ${metadata.timestamp}...`);
13562
13666
  * Delete a specific backup
13563
13667
  */
13564
13668
  async deleteBackup(timestamp) {
13565
- const backupPath = join13(process.cwd(), this.backupDir, timestamp);
13669
+ const backupPath = join14(process.cwd(), this.backupDir, timestamp);
13566
13670
  await this.deleteDirectory(backupPath);
13567
13671
  }
13568
13672
  /**
@@ -13611,7 +13715,7 @@ Restoring backup from ${metadata.timestamp}...`);
13611
13715
  }
13612
13716
  const entries = await fs22.readdir(path13, { withFileTypes: true });
13613
13717
  for (const entry of entries) {
13614
- const fullPath = join13(path13, entry.name);
13718
+ const fullPath = join14(path13, entry.name);
13615
13719
  if (entry.isDirectory()) {
13616
13720
  await this.deleteDirectory(fullPath);
13617
13721
  } else {
@@ -13626,13 +13730,13 @@ Restoring backup from ${metadata.timestamp}...`);
13626
13730
  * Get backup directory path
13627
13731
  */
13628
13732
  getBackupDir() {
13629
- return join13(process.cwd(), this.backupDir);
13733
+ return join14(process.cwd(), this.backupDir);
13630
13734
  }
13631
13735
  /**
13632
13736
  * Ensure backup directory exists
13633
13737
  */
13634
13738
  async ensureBackupDir() {
13635
- const backupDirPath = join13(process.cwd(), this.backupDir);
13739
+ const backupDirPath = join14(process.cwd(), this.backupDir);
13636
13740
  await fs22.mkdir(backupDirPath, { recursive: true });
13637
13741
  }
13638
13742
  };
@@ -13805,20 +13909,20 @@ function createRulesApplyCommand() {
13805
13909
  }
13806
13910
  }
13807
13911
  console.log("\n\u{1F4DD} Applying preset...\n");
13808
- const rulePresetDir = join14(process.cwd(), ".jai1", "rule-preset");
13912
+ const rulePresetDir = join15(process.cwd(), ".jai1", "rule-preset");
13809
13913
  try {
13810
13914
  await fs23.rm(rulePresetDir, { recursive: true, force: true });
13811
13915
  } catch {
13812
13916
  }
13813
13917
  await fs23.mkdir(rulePresetDir, { recursive: true });
13814
13918
  await fs23.writeFile(
13815
- join14(rulePresetDir, "preset.json"),
13919
+ join15(rulePresetDir, "preset.json"),
13816
13920
  JSON.stringify(bundle.preset, null, 2),
13817
13921
  "utf-8"
13818
13922
  );
13819
13923
  for (const [filename, content] of Object.entries(bundle.files)) {
13820
- const filePath = join14(rulePresetDir, filename);
13821
- await fs23.mkdir(join14(filePath, ".."), { recursive: true });
13924
+ const filePath = join15(rulePresetDir, filename);
13925
+ await fs23.mkdir(join15(filePath, ".."), { recursive: true });
13822
13926
  await fs23.writeFile(filePath, content, "utf-8");
13823
13927
  }
13824
13928
  console.log(`\u2713 Saved preset to .jai1/rule-preset/`);
@@ -13827,8 +13931,8 @@ function createRulesApplyCommand() {
13827
13931
  try {
13828
13932
  const files = generatorService.generateForIde(bundle, ideId);
13829
13933
  for (const file of files) {
13830
- const fullPath = join14(process.cwd(), file.path);
13831
- await fs23.mkdir(join14(fullPath, ".."), { recursive: true });
13934
+ const fullPath = join15(process.cwd(), file.path);
13935
+ await fs23.mkdir(join15(fullPath, ".."), { recursive: true });
13832
13936
  await fs23.writeFile(fullPath, file.content, "utf-8");
13833
13937
  console.log(`\u2713 [${ideId}] ${file.path}`);
13834
13938
  allGeneratedFiles.push({
@@ -13935,7 +14039,7 @@ function createRulesApplyCommand() {
13935
14039
 
13936
14040
  // src/commands/rules/restore.ts
13937
14041
  import { Command as Command83 } from "commander";
13938
- import { join as join15 } from "path";
14042
+ import { join as join16 } from "path";
13939
14043
  import { select as select5, confirm as confirm17 } from "@inquirer/prompts";
13940
14044
  function createRulesRestoreCommand() {
13941
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) => {
@@ -13982,7 +14086,7 @@ function createRulesRestoreCommand() {
13982
14086
  }
13983
14087
  console.log("\n\u{1F504} Restoring backup...\n");
13984
14088
  try {
13985
- const backupPath = join15(backupService.getBackupDir(), selectedBackup.timestamp);
14089
+ const backupPath = join16(backupService.getBackupDir(), selectedBackup.timestamp);
13986
14090
  await backupService.restoreBackup(backupPath);
13987
14091
  console.log("\n\u2705 Backup restored successfully!\n");
13988
14092
  console.log("\u{1F4A1} Tip: Your IDE may need to be restarted to pick up the changes.");
@@ -14009,12 +14113,12 @@ function formatTimestamp(timestamp) {
14009
14113
  // src/commands/rules/sync.ts
14010
14114
  import { Command as Command84 } from "commander";
14011
14115
  import { promises as fs24 } from "fs";
14012
- import { join as join16 } from "path";
14116
+ import { join as join17 } from "path";
14013
14117
  import { checkbox as checkbox6, confirm as confirm18, Separator } from "@inquirer/prompts";
14014
14118
  function createRulesSyncCommand() {
14015
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) => {
14016
- const rulePresetDir = join16(process.cwd(), ".jai1", "rule-preset");
14017
- const presetJsonPath = join16(rulePresetDir, "preset.json");
14120
+ const rulePresetDir = join17(process.cwd(), ".jai1", "rule-preset");
14121
+ const presetJsonPath = join17(rulePresetDir, "preset.json");
14018
14122
  let presetExists = false;
14019
14123
  let presetData = null;
14020
14124
  try {
@@ -14151,7 +14255,7 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
14151
14255
  const files = await fs24.readdir(rulePresetDir);
14152
14256
  for (const file of files) {
14153
14257
  if (file.endsWith(".mdc") || file.endsWith(".md")) {
14154
- const filePath = join16(rulePresetDir, file);
14258
+ const filePath = join17(rulePresetDir, file);
14155
14259
  const content = await fs24.readFile(filePath, "utf-8");
14156
14260
  bundle.files[file] = content;
14157
14261
  }
@@ -14166,8 +14270,8 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
14166
14270
  }
14167
14271
  const files2 = generatorService.generateForIde(bundle, ideId);
14168
14272
  for (const file of files2) {
14169
- const fullPath = join16(process.cwd(), file.path);
14170
- await fs24.mkdir(join16(fullPath, ".."), { recursive: true });
14273
+ const fullPath = join17(process.cwd(), file.path);
14274
+ await fs24.mkdir(join17(fullPath, ".."), { recursive: true });
14171
14275
  await fs24.writeFile(fullPath, file.content, "utf-8");
14172
14276
  }
14173
14277
  console.log(`\u2713 ${format.name} - ${files2.length} files regenerated`);
@@ -14232,7 +14336,7 @@ function buildIdeChoices(currentIdes, detected, suggestions) {
14232
14336
  // src/commands/rules/info.ts
14233
14337
  import { Command as Command85 } from "commander";
14234
14338
  import { promises as fs25 } from "fs";
14235
- import { join as join17 } from "path";
14339
+ import { join as join18 } from "path";
14236
14340
  function createRulesInfoCommand() {
14237
14341
  return new Command85("info").description("Show current preset information").option("--json", "Output as JSON").action(async (options) => {
14238
14342
  const projectConfigService = new ProjectConfigService();
@@ -14247,8 +14351,8 @@ function createRulesInfoCommand() {
14247
14351
  return;
14248
14352
  }
14249
14353
  console.log("\u{1F4CB} Current Preset Information\n");
14250
- const rulePresetDir = join17(process.cwd(), ".jai1", "rule-preset");
14251
- const presetJsonPath = join17(rulePresetDir, "preset.json");
14354
+ const rulePresetDir = join18(process.cwd(), ".jai1", "rule-preset");
14355
+ const presetJsonPath = join18(rulePresetDir, "preset.json");
14252
14356
  let presetMetadata = null;
14253
14357
  let presetFiles = [];
14254
14358
  try {
@@ -14315,7 +14419,7 @@ Available Backups (${rulesConfig.backups.length}):`);
14315
14419
  }
14316
14420
  async function checkPathExists(path13) {
14317
14421
  try {
14318
- await fs25.access(join17(process.cwd(), path13));
14422
+ await fs25.access(join18(process.cwd(), path13));
14319
14423
  return true;
14320
14424
  } catch {
14321
14425
  return false;
@@ -14378,7 +14482,7 @@ import chalk51 from "chalk";
14378
14482
 
14379
14483
  // src/services/skills.service.ts
14380
14484
  import { promises as fs26 } from "fs";
14381
- import { join as join18 } from "path";
14485
+ import { join as join19 } from "path";
14382
14486
  import { execFile } from "child_process";
14383
14487
  import { promisify } from "util";
14384
14488
  var execFileAsync = promisify(execFile);
@@ -14414,14 +14518,14 @@ var SkillsService = class {
14414
14518
  * List locally installed skills from .jai1/skills/
14415
14519
  */
14416
14520
  async listLocal(projectRoot) {
14417
- const skillsDir = join18(projectRoot, ".jai1", "skills");
14521
+ const skillsDir = join19(projectRoot, ".jai1", "skills");
14418
14522
  const skills = [];
14419
14523
  try {
14420
14524
  const entries = await fs26.readdir(skillsDir, { withFileTypes: true });
14421
14525
  for (const entry of entries) {
14422
14526
  if (!entry.isDirectory()) continue;
14423
- const skillPath = join18(skillsDir, entry.name);
14424
- const skillMd = join18(skillPath, "SKILL.md");
14527
+ const skillPath = join19(skillsDir, entry.name);
14528
+ const skillMd = join19(skillPath, "SKILL.md");
14425
14529
  try {
14426
14530
  await fs26.access(skillMd);
14427
14531
  } catch {
@@ -14446,8 +14550,8 @@ var SkillsService = class {
14446
14550
  * Get detailed info for a single skill
14447
14551
  */
14448
14552
  async getSkillInfo(projectRoot, skillName) {
14449
- const skillPath = join18(projectRoot, ".jai1", "skills", skillName);
14450
- const skillMd = join18(skillPath, "SKILL.md");
14553
+ const skillPath = join19(projectRoot, ".jai1", "skills", skillName);
14554
+ const skillMd = join19(skillPath, "SKILL.md");
14451
14555
  try {
14452
14556
  await fs26.access(skillMd);
14453
14557
  } catch {
@@ -14522,22 +14626,22 @@ var SkillsService = class {
14522
14626
  * After npx skills add, copy newly installed skills from .agents/skills/ into .jai1/skills/
14523
14627
  */
14524
14628
  async copySkillshResultsToJai1(projectRoot, specificSkill) {
14525
- const jai1SkillsDir = join18(projectRoot, ".jai1", "skills");
14629
+ const jai1SkillsDir = join19(projectRoot, ".jai1", "skills");
14526
14630
  await fs26.mkdir(jai1SkillsDir, { recursive: true });
14527
- const universalDir = join18(projectRoot, ".agents", "skills");
14631
+ const universalDir = join19(projectRoot, ".agents", "skills");
14528
14632
  try {
14529
14633
  const entries = await fs26.readdir(universalDir, { withFileTypes: true });
14530
14634
  for (const entry of entries) {
14531
14635
  if (!entry.isDirectory()) continue;
14532
14636
  if (specificSkill && entry.name !== specificSkill) continue;
14533
- const srcSkill = join18(universalDir, entry.name);
14534
- const skillMd = join18(srcSkill, "SKILL.md");
14637
+ const srcSkill = join19(universalDir, entry.name);
14638
+ const skillMd = join19(srcSkill, "SKILL.md");
14535
14639
  try {
14536
14640
  await fs26.access(skillMd);
14537
14641
  } catch {
14538
14642
  continue;
14539
14643
  }
14540
- const targetSkill = join18(jai1SkillsDir, entry.name);
14644
+ const targetSkill = join19(jai1SkillsDir, entry.name);
14541
14645
  try {
14542
14646
  await fs26.access(targetSkill);
14543
14647
  } catch {
@@ -14576,7 +14680,7 @@ var SkillsService = class {
14576
14680
  const target = this.getIDETarget(ide);
14577
14681
  if (!target) continue;
14578
14682
  for (const skill of skillsToSync) {
14579
- const targetPath = join18(projectRoot, target.skillsPath, skill.slug);
14683
+ const targetPath = join19(projectRoot, target.skillsPath, skill.slug);
14580
14684
  try {
14581
14685
  let status = "created";
14582
14686
  try {
@@ -14640,7 +14744,7 @@ var SkillsService = class {
14640
14744
  const entries = await fs26.readdir(dirPath, { withFileTypes: true });
14641
14745
  for (const entry of entries) {
14642
14746
  if (entry.isDirectory()) {
14643
- count += await this.countFiles(join18(dirPath, entry.name));
14747
+ count += await this.countFiles(join19(dirPath, entry.name));
14644
14748
  } else {
14645
14749
  count++;
14646
14750
  }
@@ -14653,8 +14757,8 @@ var SkillsService = class {
14653
14757
  async copyDir(source, target) {
14654
14758
  const entries = await fs26.readdir(source, { withFileTypes: true });
14655
14759
  for (const entry of entries) {
14656
- const srcPath = join18(source, entry.name);
14657
- const tgtPath = join18(target, entry.name);
14760
+ const srcPath = join19(source, entry.name);
14761
+ const tgtPath = join19(target, entry.name);
14658
14762
  if (entry.isDirectory()) {
14659
14763
  await fs26.mkdir(tgtPath, { recursive: true });
14660
14764
  await this.copyDir(srcPath, tgtPath);
@@ -14728,7 +14832,7 @@ function createSkillsFindCommand() {
14728
14832
 
14729
14833
  // src/commands/skills/add.ts
14730
14834
  import { Command as Command88 } from "commander";
14731
- import { join as join19 } from "path";
14835
+ import { join as join20 } from "path";
14732
14836
  import chalk52 from "chalk";
14733
14837
  import { checkbox as checkbox7 } from "@inquirer/prompts";
14734
14838
  function createSkillsAddCommand() {
@@ -14750,7 +14854,7 @@ function createSkillsAddCommand() {
14750
14854
  }
14751
14855
  console.log(chalk52.cyan(`\u{1F4E6} \u0110ang c\xE0i \u0111\u1EB7t skill: ${name}...`));
14752
14856
  console.log();
14753
- const targetDir = join19(projectRoot, ".jai1");
14857
+ const targetDir = join20(projectRoot, ".jai1");
14754
14858
  await skillsService.installFromServer(config, name, targetDir);
14755
14859
  console.log(chalk52.green(`\u2705 \u0110\xE3 c\xE0i \u0111\u1EB7t skill "${name}" v\xE0o .jai1/skills/${name}/`));
14756
14860
  }
@@ -15240,8 +15344,8 @@ function getInstallCommand(packageManager2) {
15240
15344
  import { Command as Command94 } from "commander";
15241
15345
  import { confirm as confirm22, select as select6 } from "@inquirer/prompts";
15242
15346
  import { promises as fs27 } from "fs";
15243
- import { join as join20 } from "path";
15244
- import { existsSync as existsSync4 } from "fs";
15347
+ import { join as join21 } from "path";
15348
+ import { existsSync as existsSync5 } from "fs";
15245
15349
  function createCleanCommand() {
15246
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) => {
15247
15351
  await handleClean(options);
@@ -15254,7 +15358,7 @@ async function handleClean(options) {
15254
15358
  {
15255
15359
  name: "Backups",
15256
15360
  description: "Component backup files (.jai1_backup/)",
15257
- path: join20(cwd, ".jai1_backup"),
15361
+ path: join21(cwd, ".jai1_backup"),
15258
15362
  check: async () => {
15259
15363
  const backups = await service.listBackups(cwd);
15260
15364
  return { exists: backups.length > 0, count: backups.length };
@@ -15266,13 +15370,13 @@ async function handleClean(options) {
15266
15370
  {
15267
15371
  name: "Jai1 Config",
15268
15372
  description: "Jai1 framework config (.jai1/)",
15269
- path: join20(cwd, ".jai1"),
15373
+ path: join21(cwd, ".jai1"),
15270
15374
  check: async () => {
15271
- const exists = existsSync4(join20(cwd, ".jai1"));
15375
+ const exists = existsSync5(join21(cwd, ".jai1"));
15272
15376
  return { exists };
15273
15377
  },
15274
15378
  clean: async () => {
15275
- await fs27.rm(join20(cwd, ".jai1"), { recursive: true, force: true });
15379
+ await fs27.rm(join21(cwd, ".jai1"), { recursive: true, force: true });
15276
15380
  }
15277
15381
  }
15278
15382
  ];
@@ -15284,8 +15388,8 @@ async function handleClean(options) {
15284
15388
  { name: "OpenCode", dir: ".opencode" }
15285
15389
  ];
15286
15390
  for (const ide of ideDirectories) {
15287
- const idePath = join20(cwd, ide.dir);
15288
- if (existsSync4(idePath)) {
15391
+ const idePath = join21(cwd, ide.dir);
15392
+ if (existsSync5(idePath)) {
15289
15393
  targets.push({
15290
15394
  name: `IDE: ${ide.name}`,
15291
15395
  description: `${ide.name} IDE config (${ide.dir}/)`,
@@ -16293,7 +16397,7 @@ async function handleSyncProject(options) {
16293
16397
  // src/commands/framework/info.ts
16294
16398
  import { Command as Command98 } from "commander";
16295
16399
  import { promises as fs28 } from "fs";
16296
- import { join as join21 } from "path";
16400
+ import { join as join22 } from "path";
16297
16401
  import { homedir as homedir4 } from "os";
16298
16402
  function createInfoCommand() {
16299
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) => {
@@ -16307,7 +16411,7 @@ async function handleInfo(options) {
16307
16411
  if (!config) {
16308
16412
  throw new ValidationError(`Not initialized. Run "${getCliName()} auth" first.`);
16309
16413
  }
16310
- const frameworkPath = join21(homedir4(), ".jai1", "framework");
16414
+ const frameworkPath = join22(homedir4(), ".jai1", "framework");
16311
16415
  const projectStatus = await getProjectStatus2();
16312
16416
  const info = {
16313
16417
  configPath: configService.getConfigPath(),
@@ -16342,7 +16446,7 @@ function maskKey4(key) {
16342
16446
  return "****" + key.slice(-4);
16343
16447
  }
16344
16448
  async function getProjectStatus2() {
16345
- const projectJai1 = join21(process.cwd(), ".jai1");
16449
+ const projectJai1 = join22(process.cwd(), ".jai1");
16346
16450
  try {
16347
16451
  await fs28.access(projectJai1);
16348
16452
  return { exists: true, version: "Synced" };
@@ -16536,7 +16640,7 @@ import { Command as Command101 } from "commander";
16536
16640
  import { checkbox as checkbox9, confirm as confirm25, select as select7 } from "@inquirer/prompts";
16537
16641
  import fs29 from "fs/promises";
16538
16642
  import path12 from "path";
16539
- import { existsSync as existsSync5 } from "fs";
16643
+ import { existsSync as existsSync6 } from "fs";
16540
16644
  var PERFORMANCE_GROUPS2 = {
16541
16645
  telemetry: {
16542
16646
  name: "Telemetry",
@@ -16766,12 +16870,12 @@ async function applyGroups2(groupKeys, action) {
16766
16870
  console.log(' \u{1F4A1} Ch\u1EA1y "jai1 vscode list" \u0111\u1EC3 xem danh s\xE1ch nh\xF3m c\xF3 s\u1EB5n.');
16767
16871
  return;
16768
16872
  }
16769
- if (!existsSync5(vscodeDir)) {
16873
+ if (!existsSync6(vscodeDir)) {
16770
16874
  await fs29.mkdir(vscodeDir, { recursive: true });
16771
16875
  console.log("\u{1F4C1} \u0110\xE3 t\u1EA1o th\u01B0 m\u1EE5c .vscode/");
16772
16876
  }
16773
16877
  let currentSettings = {};
16774
- if (existsSync5(settingsPath)) {
16878
+ if (existsSync6(settingsPath)) {
16775
16879
  try {
16776
16880
  const content = await fs29.readFile(settingsPath, "utf-8");
16777
16881
  currentSettings = JSON.parse(content);
@@ -16821,7 +16925,7 @@ async function applyGroups2(groupKeys, action) {
16821
16925
  async function resetSettings2(groupKeys) {
16822
16926
  const vscodeDir = path12.join(process.cwd(), ".vscode");
16823
16927
  const settingsPath = path12.join(vscodeDir, "settings.json");
16824
- if (!existsSync5(settingsPath)) {
16928
+ if (!existsSync6(settingsPath)) {
16825
16929
  console.log("\n\u26A0\uFE0F Kh\xF4ng t\xECm th\u1EA5y file settings.json");
16826
16930
  return;
16827
16931
  }