@jvittechs/j 1.0.38 → 1.0.40

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
@@ -13,7 +13,7 @@ import {
13
13
  STATUS_ICONS,
14
14
  TaskService,
15
15
  createTaskSummaryCommand
16
- } from "./chunk-KOEMGOAP.js";
16
+ } from "./chunk-ZG3KLHJ4.js";
17
17
 
18
18
  // src/utils/node-version-check.ts
19
19
  import chalk from "chalk";
@@ -49,7 +49,7 @@ function checkNodeVersion() {
49
49
  }
50
50
 
51
51
  // src/cli.ts
52
- import { Command as Command85 } from "commander";
52
+ import { Command as Command87 } from "commander";
53
53
 
54
54
  // src/services/error-log.service.ts
55
55
  import { promises as fs } from "fs";
@@ -149,7 +149,7 @@ import { basename as basename5 } from "path";
149
149
  // package.json
150
150
  var package_default = {
151
151
  name: "@jvittechs/j",
152
- version: "1.0.38",
152
+ version: "1.0.40",
153
153
  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.",
154
154
  type: "module",
155
155
  bin: {
@@ -10614,18 +10614,204 @@ function createDepsCommand() {
10614
10614
  return depsCommand;
10615
10615
  }
10616
10616
 
10617
+ // src/commands/hooks/index.ts
10618
+ import { Command as Command47 } from "commander";
10619
+ import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync, unlinkSync, chmodSync, mkdirSync } from "fs";
10620
+ import { join as join9 } from "path";
10621
+ import { execSync as execSync4 } from "child_process";
10622
+ import chalk20 from "chalk";
10623
+ var MARKER_START = "# >>> jai1-hooks";
10624
+ var MARKER_END = "# <<< jai1-hooks";
10625
+ var SHEBANG = "#!/bin/sh";
10626
+ var HOOK_DEFINITIONS = [
10627
+ {
10628
+ hookName: "post-merge",
10629
+ description: "Auto-pull tasks after git pull",
10630
+ script: [
10631
+ "# task sync: pull after merge",
10632
+ "if command -v j &>/dev/null; then j t sync --pull 2>/dev/null",
10633
+ "elif command -v jai1 &>/dev/null; then jai1 t sync --pull 2>/dev/null",
10634
+ "fi || true"
10635
+ ].join("\n")
10636
+ },
10637
+ {
10638
+ hookName: "pre-push",
10639
+ description: "Auto-push tasks before git push",
10640
+ script: [
10641
+ "# task sync: push before push",
10642
+ "if command -v j &>/dev/null; then j t sync --push 2>/dev/null",
10643
+ "elif command -v jai1 &>/dev/null; then jai1 t sync --push 2>/dev/null",
10644
+ "fi || true"
10645
+ ].join("\n")
10646
+ }
10647
+ ];
10648
+ function isGitInstalled() {
10649
+ try {
10650
+ execSync4("git --version", { stdio: ["pipe", "pipe", "pipe"] });
10651
+ return true;
10652
+ } catch {
10653
+ return false;
10654
+ }
10655
+ }
10656
+ function getGitHooksDir() {
10657
+ if (!isGitInstalled()) {
10658
+ throw new Error("Git ch\u01B0a \u0111\u01B0\u1EE3c c\xE0i \u0111\u1EB7t. H\xE3y c\xE0i git tr\u01B0\u1EDBc khi s\u1EED d\u1EE5ng hooks.");
10659
+ }
10660
+ try {
10661
+ const gitDir = execSync4("git rev-parse --git-common-dir", {
10662
+ encoding: "utf-8",
10663
+ stdio: ["pipe", "pipe", "pipe"]
10664
+ }).trim();
10665
+ return join9(gitDir, "hooks");
10666
+ } catch {
10667
+ throw new Error("Th\u01B0 m\u1EE5c hi\u1EC7n t\u1EA1i kh\xF4ng ph\u1EA3i git repository. H\xE3y ch\u1EA1y l\u1EC7nh n\xE0y trong m\u1ED9t git repo.");
10668
+ }
10669
+ }
10670
+ function buildSection(script) {
10671
+ return `${MARKER_START}
10672
+ ${script}
10673
+ ${MARKER_END}`;
10674
+ }
10675
+ function hasJai1Section(content) {
10676
+ return content.includes(MARKER_START);
10677
+ }
10678
+ function removeJai1Section(content) {
10679
+ const lines = content.split("\n");
10680
+ const result = [];
10681
+ let inSection = false;
10682
+ for (const line of lines) {
10683
+ if (line.trim() === MARKER_START) {
10684
+ inSection = true;
10685
+ continue;
10686
+ }
10687
+ if (line.trim() === MARKER_END) {
10688
+ inSection = false;
10689
+ continue;
10690
+ }
10691
+ if (!inSection) {
10692
+ result.push(line);
10693
+ }
10694
+ }
10695
+ return result.join("\n");
10696
+ }
10697
+ function isEffectivelyEmpty(content) {
10698
+ const stripped = content.split("\n").filter((line) => line.trim() !== "" && line.trim() !== SHEBANG).join("");
10699
+ return stripped.length === 0;
10700
+ }
10701
+ function setupHooks() {
10702
+ const hooksDir = getGitHooksDir();
10703
+ if (!existsSync3(hooksDir)) {
10704
+ mkdirSync(hooksDir, { recursive: true });
10705
+ }
10706
+ let installed = 0;
10707
+ let skipped = 0;
10708
+ for (const def of HOOK_DEFINITIONS) {
10709
+ const hookPath = join9(hooksDir, def.hookName);
10710
+ const section = buildSection(def.script);
10711
+ try {
10712
+ if (existsSync3(hookPath)) {
10713
+ const existing = readFileSync2(hookPath, "utf-8");
10714
+ if (hasJai1Section(existing)) {
10715
+ console.log(chalk20.dim(` \u23ED ${def.hookName} \u2014 \u0111\xE3 c\xF3, skip`));
10716
+ skipped++;
10717
+ continue;
10718
+ }
10719
+ const updated = existing.trimEnd() + "\n\n" + section + "\n";
10720
+ writeFileSync(hookPath, updated);
10721
+ } else {
10722
+ writeFileSync(hookPath, `${SHEBANG}
10723
+
10724
+ ${section}
10725
+ `);
10726
+ }
10727
+ chmodSync(hookPath, 493);
10728
+ console.log(chalk20.green(` \u2705 ${def.hookName} \u2014 ${def.description}`));
10729
+ installed++;
10730
+ } catch (err) {
10731
+ const msg = err instanceof Error ? err.message : String(err);
10732
+ if (msg.includes("EACCES") || msg.includes("permission")) {
10733
+ console.log(chalk20.red(` \u274C ${def.hookName} \u2014 kh\xF4ng c\xF3 quy\u1EC1n ghi. Th\u1EED ch\u1EA1y v\u1EDBi sudo.`));
10734
+ } else {
10735
+ console.log(chalk20.red(` \u274C ${def.hookName} \u2014 l\u1ED7i: ${msg}`));
10736
+ }
10737
+ }
10738
+ }
10739
+ console.log("");
10740
+ if (installed > 0) {
10741
+ console.log(chalk20.green(`\u{1F389} \u0110\xE3 c\xE0i ${installed} hook(s).`));
10742
+ console.log(chalk20.dim(" Tasks s\u1EBD t\u1EF1 \u0111\u1ED9ng sync khi b\u1EA1n push/pull."));
10743
+ } else {
10744
+ console.log(chalk20.dim("\u2139\uFE0F T\u1EA5t c\u1EA3 hooks \u0111\xE3 \u0111\u01B0\u1EE3c c\xE0i s\u1EB5n."));
10745
+ }
10746
+ }
10747
+ function removeHooks() {
10748
+ const hooksDir = getGitHooksDir();
10749
+ let removed = 0;
10750
+ for (const def of HOOK_DEFINITIONS) {
10751
+ const hookPath = join9(hooksDir, def.hookName);
10752
+ if (!existsSync3(hookPath)) {
10753
+ continue;
10754
+ }
10755
+ try {
10756
+ const content = readFileSync2(hookPath, "utf-8");
10757
+ if (!hasJai1Section(content)) {
10758
+ continue;
10759
+ }
10760
+ const cleaned = removeJai1Section(content);
10761
+ if (isEffectivelyEmpty(cleaned)) {
10762
+ unlinkSync(hookPath);
10763
+ console.log(chalk20.yellow(` \u{1F5D1} ${def.hookName} \u2014 xo\xE1 file (ch\u1EC9 c\xF3 jai1 hooks)`));
10764
+ } else {
10765
+ writeFileSync(hookPath, cleaned);
10766
+ console.log(chalk20.yellow(` \u2702\uFE0F ${def.hookName} \u2014 g\u1EE1 ph\u1EA7n jai1`));
10767
+ }
10768
+ removed++;
10769
+ } catch (err) {
10770
+ const msg = err instanceof Error ? err.message : String(err);
10771
+ if (msg.includes("EACCES") || msg.includes("permission")) {
10772
+ console.log(chalk20.red(` \u274C ${def.hookName} \u2014 kh\xF4ng c\xF3 quy\u1EC1n ghi/xo\xE1. Th\u1EED ch\u1EA1y v\u1EDBi sudo.`));
10773
+ } else {
10774
+ console.log(chalk20.red(` \u274C ${def.hookName} \u2014 l\u1ED7i: ${msg}`));
10775
+ }
10776
+ }
10777
+ }
10778
+ console.log("");
10779
+ if (removed > 0) {
10780
+ console.log(chalk20.green(`\u2705 \u0110\xE3 g\u1EE1 ${removed} hook(s).`));
10781
+ } else {
10782
+ console.log(chalk20.dim("\u2139\uFE0F Kh\xF4ng c\xF3 jai1 hooks n\xE0o \u0111\u1EC3 g\u1EE1."));
10783
+ }
10784
+ }
10785
+ function createHooksCommand() {
10786
+ const cmd = new Command47("hooks").description("Qu\u1EA3n l\xFD Git hooks t\xEDch h\u1EE3p cho jai1");
10787
+ cmd.command("setup").description("C\xE0i \u0111\u1EB7t Git hooks (auto task sync on push/pull)").action(() => {
10788
+ console.log(chalk20.bold("\n\u{1F517} C\xE0i \u0111\u1EB7t jai1 Git hooks...\n"));
10789
+ setupHooks();
10790
+ console.log("");
10791
+ });
10792
+ cmd.command("remove").description("G\u1EE1 b\u1ECF jai1 Git hooks").action(() => {
10793
+ console.log(chalk20.bold("\n\u{1F517} G\u1EE1 b\u1ECF jai1 Git hooks...\n"));
10794
+ removeHooks();
10795
+ console.log("");
10796
+ });
10797
+ cmd.action(() => {
10798
+ cmd.help();
10799
+ });
10800
+ return cmd;
10801
+ }
10802
+
10617
10803
  // src/commands/tasks/index.ts
10618
- import { Command as Command57 } from "commander";
10804
+ import { Command as Command59 } from "commander";
10619
10805
 
10620
10806
  // src/commands/tasks/add.ts
10621
- import { Command as Command47 } from "commander";
10622
- import chalk20 from "chalk";
10807
+ import { Command as Command48 } from "commander";
10808
+ import chalk21 from "chalk";
10623
10809
  function createTaskAddCommand() {
10624
- return new Command47("add").description("Add a new task").argument("<title>", "Task title").option("-p, --priority <n>", "Priority: 0=critical, 1=high, 2=medium, 3=low", "2").option("-P, --parent <parent>", "Parent: feature/xxx, plan/xxx, bug/xxx").option("-t, --tags <tags>", "Comma-separated tags").option("-j, --json", "Output JSON").action(async (title, options) => {
10810
+ return new Command48("add").description("Add a new task").argument("<title>", "Task title").option("-p, --priority <n>", "Priority: 0=critical, 1=high, 2=medium, 3=low", "2").option("-P, --parent <parent>", "Parent: feature/xxx, plan/xxx, bug/xxx").option("-t, --tags <tags>", "Comma-separated tags").option("-j, --json", "Output JSON").action(async (title, options) => {
10625
10811
  const service = new TaskService();
10626
10812
  const priority = Number(options.priority ?? 2);
10627
10813
  if (priority < 0 || priority > 3) {
10628
- console.error(chalk20.red("\u274C Priority must be 0-3"));
10814
+ console.error(chalk21.red("\u274C Priority must be 0-3"));
10629
10815
  process.exit(1);
10630
10816
  }
10631
10817
  const tags = options.tags ? options.tags.split(",").map((t) => t.trim()) : [];
@@ -10641,23 +10827,23 @@ function createTaskAddCommand() {
10641
10827
  }
10642
10828
  const icon = PRIORITY_ICONS[task.priority] || "\u{1F7E1}";
10643
10829
  const label = PRIORITY_LABELS[task.priority] || "Medium";
10644
- console.log(chalk20.green(`\u2705 Task added: ${chalk20.bold(task.id)}`));
10645
- console.log(` ${chalk20.dim("Title:")} ${task.title}`);
10646
- console.log(` ${chalk20.dim("Priority:")} ${icon} ${label}`);
10830
+ console.log(chalk21.green(`\u2705 Task added: ${chalk21.bold(task.id)}`));
10831
+ console.log(` ${chalk21.dim("Title:")} ${task.title}`);
10832
+ console.log(` ${chalk21.dim("Priority:")} ${icon} ${label}`);
10647
10833
  if (task.parent) {
10648
- console.log(` ${chalk20.dim("Parent:")} ${task.parent}`);
10834
+ console.log(` ${chalk21.dim("Parent:")} ${task.parent}`);
10649
10835
  }
10650
10836
  if (task.tags.length > 0) {
10651
- console.log(` ${chalk20.dim("Tags:")} ${task.tags.join(", ")}`);
10837
+ console.log(` ${chalk21.dim("Tags:")} ${task.tags.join(", ")}`);
10652
10838
  }
10653
10839
  });
10654
10840
  }
10655
10841
 
10656
10842
  // src/commands/tasks/list.ts
10657
- import { Command as Command48 } from "commander";
10658
- import chalk21 from "chalk";
10843
+ import { Command as Command49 } from "commander";
10844
+ import chalk22 from "chalk";
10659
10845
  function createTaskListCommand() {
10660
- return new Command48("list").alias("ls").description("List tasks").option("-s, --status <status>", "Filter by status: todo, in_progress, done, cancelled").option("-P, --parent <parent>", "Filter by parent: feature/xxx, plan/xxx").option("-j, --json", "Output JSON").action(async (options) => {
10846
+ return new Command49("list").alias("ls").description("List tasks").option("-s, --status <status>", "Filter by status: todo, in_progress, done, cancelled").option("-P, --parent <parent>", "Filter by parent: feature/xxx, plan/xxx").option("-j, --json", "Output JSON").action(async (options) => {
10661
10847
  await handleTaskList(options);
10662
10848
  });
10663
10849
  }
@@ -10676,12 +10862,12 @@ async function handleTaskList(options) {
10676
10862
  return;
10677
10863
  }
10678
10864
  if (tasks.length === 0) {
10679
- console.log(chalk21.dim("No tasks found."));
10865
+ console.log(chalk22.dim("No tasks found."));
10680
10866
  return;
10681
10867
  }
10682
10868
  const resolvedIds = new Set(allTasks.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id));
10683
10869
  const header = options.parent ? `\u{1F4CB} ${options.parent} (${tasks.length} tasks)` : `\u{1F4CB} All tasks (${tasks.length})`;
10684
- console.log(chalk21.bold(header));
10870
+ console.log(chalk22.bold(header));
10685
10871
  console.log();
10686
10872
  for (const task of tasks) {
10687
10873
  printTaskLine(task, resolvedIds);
@@ -10691,25 +10877,25 @@ function printTaskLine(task, resolvedIds) {
10691
10877
  const isBlocked = task.status === "todo" && task.depends_on.length > 0 && !task.depends_on.every((id) => resolvedIds.has(id));
10692
10878
  const statusIcon = isBlocked ? BLOCKED_ICON : STATUS_ICONS[task.status] || "\u{1F4CB}";
10693
10879
  const priorityIcon = PRIORITY_ICONS[task.priority] || "\u{1F7E1}";
10694
- let line = ` ${statusIcon} ${chalk21.dim(task.id)} P${task.priority}${priorityIcon} ${task.title}`;
10880
+ let line = ` ${statusIcon} ${chalk22.dim(task.id)} P${task.priority}${priorityIcon} ${task.title}`;
10695
10881
  if (task.status === "in_progress" && task.assigned_to) {
10696
- line += chalk21.cyan(` @${task.assigned_to}`);
10882
+ line += chalk22.cyan(` @${task.assigned_to}`);
10697
10883
  }
10698
10884
  if (isBlocked) {
10699
10885
  const blockedBy = task.depends_on.filter((id) => !resolvedIds.has(id));
10700
- line += chalk21.red(` (blocked: ${blockedBy.join(", ")})`);
10886
+ line += chalk22.red(` (blocked: ${blockedBy.join(", ")})`);
10701
10887
  }
10702
10888
  if (task.parent) {
10703
- line += chalk21.dim(` [${task.parent}]`);
10889
+ line += chalk22.dim(` [${task.parent}]`);
10704
10890
  }
10705
10891
  console.log(line);
10706
10892
  }
10707
10893
 
10708
10894
  // src/commands/tasks/ready.ts
10709
- import { Command as Command49 } from "commander";
10710
- import chalk22 from "chalk";
10895
+ import { Command as Command50 } from "commander";
10896
+ import chalk23 from "chalk";
10711
10897
  function createTaskReadyCommand() {
10712
- return new Command49("ready").description("Show tasks ready to pick (not blocked, not assigned)").option("-P, --parent <parent>", "Filter by parent").option("-j, --json", "Output JSON").action(async (options) => {
10898
+ return new Command50("ready").description("Show tasks ready to pick (not blocked, not assigned)").option("-P, --parent <parent>", "Filter by parent").option("-j, --json", "Output JSON").action(async (options) => {
10713
10899
  const service = new TaskService();
10714
10900
  const tasks = await service.getReady(options.parent);
10715
10901
  if (options.json) {
@@ -10717,37 +10903,37 @@ function createTaskReadyCommand() {
10717
10903
  return;
10718
10904
  }
10719
10905
  if (tasks.length === 0) {
10720
- console.log(chalk22.dim("No tasks ready to pick."));
10721
- console.log(chalk22.dim("\u{1F4A1} Check blocked tasks: jai1 t list -s todo"));
10906
+ console.log(chalk23.dim("No tasks ready to pick."));
10907
+ console.log(chalk23.dim("\u{1F4A1} Check blocked tasks: jai1 t list -s todo"));
10722
10908
  return;
10723
10909
  }
10724
- console.log(chalk22.bold(`\u{1F4CB} Ready to pick (${tasks.length} tasks):`));
10910
+ console.log(chalk23.bold(`\u{1F4CB} Ready to pick (${tasks.length} tasks):`));
10725
10911
  console.log();
10726
10912
  for (const task of tasks) {
10727
10913
  const icon = PRIORITY_ICONS[task.priority] || "\u{1F7E1}";
10728
- let line = ` P${task.priority}${icon} ${chalk22.dim(task.id)} ${task.title}`;
10914
+ let line = ` P${task.priority}${icon} ${chalk23.dim(task.id)} ${task.title}`;
10729
10915
  if (task.parent) {
10730
- line += chalk22.dim(` [${task.parent}]`);
10916
+ line += chalk23.dim(` [${task.parent}]`);
10731
10917
  }
10732
10918
  console.log(line);
10733
10919
  }
10734
10920
  console.log();
10735
- console.log(chalk22.dim("\u{1F4A1} Run: jai1 t pick"));
10921
+ console.log(chalk23.dim("\u{1F4A1} Run: jai1 t pick"));
10736
10922
  });
10737
10923
  }
10738
10924
 
10739
10925
  // src/commands/tasks/update.ts
10740
- import { Command as Command50 } from "commander";
10741
- import chalk23 from "chalk";
10926
+ import { Command as Command51 } from "commander";
10927
+ import chalk24 from "chalk";
10742
10928
  var VALID_STATUSES = ["todo", "in_progress", "done", "cancelled"];
10743
10929
  function createTaskUpdateCommand() {
10744
- return new Command50("update").description("Update task status and/or notes").argument("<id>", "Task ID (e.g. T-001)").option("-s, --status <status>", "New status: todo, in_progress, done, cancelled").option("-n, --notes <notes>", 'Task notes (e.g. "files: a.ts, b.ts")').option("-j, --json", "Output JSON").action(async (id, options) => {
10930
+ return new Command51("update").description("Update task status and/or notes").argument("<id>", "Task ID (e.g. T-001)").option("-s, --status <status>", "New status: todo, in_progress, done, cancelled").option("-n, --notes <notes>", 'Task notes (e.g. "files: a.ts, b.ts")').option("-j, --json", "Output JSON").action(async (id, options) => {
10745
10931
  if (!options.status && !options.notes) {
10746
- console.error(chalk23.red("\u274C At least one of --status or --notes is required"));
10932
+ console.error(chalk24.red("\u274C At least one of --status or --notes is required"));
10747
10933
  process.exit(1);
10748
10934
  }
10749
10935
  if (options.status && !VALID_STATUSES.includes(options.status)) {
10750
- console.error(chalk23.red(`\u274C Invalid status. Must be: ${VALID_STATUSES.join(", ")}`));
10936
+ console.error(chalk24.red(`\u274C Invalid status. Must be: ${VALID_STATUSES.join(", ")}`));
10751
10937
  process.exit(1);
10752
10938
  }
10753
10939
  const service = new TaskService();
@@ -10757,8 +10943,8 @@ function createTaskUpdateCommand() {
10757
10943
  if (existingTask) {
10758
10944
  const { blocked, blockedBy } = await service.isBlocked(existingTask);
10759
10945
  if (blocked) {
10760
- console.log(chalk23.yellow(`\u26A0\uFE0F Task ${id} is blocked by: ${blockedBy.join(", ")}`));
10761
- console.log(chalk23.yellow(` Dependencies ch\u01B0a done. Ti\u1EBFp t\u1EE5c update...`));
10946
+ console.log(chalk24.yellow(`\u26A0\uFE0F Task ${id} is blocked by: ${blockedBy.join(", ")}`));
10947
+ console.log(chalk24.yellow(` Dependencies ch\u01B0a done. Ti\u1EBFp t\u1EE5c update...`));
10762
10948
  }
10763
10949
  }
10764
10950
  }
@@ -10778,24 +10964,24 @@ function createTaskUpdateCommand() {
10778
10964
  if (options.notes) {
10779
10965
  parts.push(`\u{1F4DD} notes updated`);
10780
10966
  }
10781
- console.log(chalk23.green(`\u2705 ${task.id} \u2192 ${parts.join(" | ")}`));
10967
+ console.log(chalk24.green(`\u2705 ${task.id} \u2192 ${parts.join(" | ")}`));
10782
10968
  } catch (error) {
10783
- console.error(chalk23.red(`\u274C ${error instanceof Error ? error.message : String(error)}`));
10969
+ console.error(chalk24.red(`\u274C ${error instanceof Error ? error.message : String(error)}`));
10784
10970
  process.exit(1);
10785
10971
  }
10786
10972
  });
10787
10973
  }
10788
10974
 
10789
10975
  // src/commands/tasks/show.ts
10790
- import { Command as Command51 } from "commander";
10791
- import chalk24 from "chalk";
10976
+ import { Command as Command52 } from "commander";
10977
+ import chalk25 from "chalk";
10792
10978
  function createTaskShowCommand() {
10793
- return new Command51("show").description("Show task detail or all tasks under a parent").argument("<query>", "Task ID (T-001) or parent (feature/xxx)").option("-j, --json", "Output JSON").action(async (query, options) => {
10979
+ return new Command52("show").description("Show task detail or all tasks under a parent").argument("<query>", "Task ID (T-001) or parent (feature/xxx)").option("-j, --json", "Output JSON").action(async (query, options) => {
10794
10980
  const service = new TaskService();
10795
10981
  if (query.startsWith("T-")) {
10796
10982
  const task = await service.findById(query);
10797
10983
  if (!task) {
10798
- console.error(chalk24.red(`\u274C Task ${query} not found`));
10984
+ console.error(chalk25.red(`\u274C Task ${query} not found`));
10799
10985
  process.exit(1);
10800
10986
  }
10801
10987
  if (options.json) {
@@ -10806,34 +10992,34 @@ function createTaskShowCommand() {
10806
10992
  const statusIcon = blocked ? BLOCKED_ICON : STATUS_ICONS[task.status] || "\u{1F4CB}";
10807
10993
  const priIcon = PRIORITY_ICONS[task.priority] || "\u{1F7E1}";
10808
10994
  const priLabel = PRIORITY_LABELS[task.priority] || "Medium";
10809
- console.log(chalk24.bold(`
10995
+ console.log(chalk25.bold(`
10810
10996
  \u{1F4CC} ${task.id}: ${task.title}
10811
10997
  `));
10812
- console.log(` ${chalk24.dim("Status:")} ${statusIcon} ${task.status}${blocked ? chalk24.red(" (BLOCKED)") : ""}`);
10813
- console.log(` ${chalk24.dim("Priority:")} ${priIcon} P${task.priority} ${priLabel}`);
10998
+ console.log(` ${chalk25.dim("Status:")} ${statusIcon} ${task.status}${blocked ? chalk25.red(" (BLOCKED)") : ""}`);
10999
+ console.log(` ${chalk25.dim("Priority:")} ${priIcon} P${task.priority} ${priLabel}`);
10814
11000
  if (task.parent) {
10815
- console.log(` ${chalk24.dim("Parent:")} ${task.parent}`);
11001
+ console.log(` ${chalk25.dim("Parent:")} ${task.parent}`);
10816
11002
  }
10817
11003
  if (task.assigned_to) {
10818
- console.log(` ${chalk24.dim("Assigned:")} @${task.assigned_to} (${task.claimed_at})`);
11004
+ console.log(` ${chalk25.dim("Assigned:")} @${task.assigned_to} (${task.claimed_at})`);
10819
11005
  }
10820
11006
  if (task.depends_on.length > 0) {
10821
- console.log(` ${chalk24.dim("Depends on:")} ${task.depends_on.join(", ")}`);
11007
+ console.log(` ${chalk25.dim("Depends on:")} ${task.depends_on.join(", ")}`);
10822
11008
  if (blocked) {
10823
- console.log(` ${chalk24.dim("Blocked by:")} ${chalk24.red(blockedBy.join(", "))}`);
11009
+ console.log(` ${chalk25.dim("Blocked by:")} ${chalk25.red(blockedBy.join(", "))}`);
10824
11010
  }
10825
11011
  }
10826
11012
  if (task.tags.length > 0) {
10827
- console.log(` ${chalk24.dim("Tags:")} ${task.tags.join(", ")}`);
11013
+ console.log(` ${chalk25.dim("Tags:")} ${task.tags.join(", ")}`);
10828
11014
  }
10829
11015
  if (task.branch) {
10830
- console.log(` ${chalk24.dim("Branch:")} ${task.branch}`);
11016
+ console.log(` ${chalk25.dim("Branch:")} ${task.branch}`);
10831
11017
  }
10832
11018
  if (task.notes) {
10833
- console.log(` ${chalk24.dim("Notes:")} ${task.notes}`);
11019
+ console.log(` ${chalk25.dim("Notes:")} ${task.notes}`);
10834
11020
  }
10835
- console.log(` ${chalk24.dim("Created:")} ${task.created}`);
10836
- console.log(` ${chalk24.dim("Updated:")} ${task.updated}`);
11021
+ console.log(` ${chalk25.dim("Created:")} ${task.created}`);
11022
+ console.log(` ${chalk25.dim("Updated:")} ${task.updated}`);
10837
11023
  console.log();
10838
11024
  } else {
10839
11025
  const tasks = await service.filter({ parent: query });
@@ -10842,10 +11028,10 @@ function createTaskShowCommand() {
10842
11028
  return;
10843
11029
  }
10844
11030
  if (tasks.length === 0) {
10845
- console.log(chalk24.dim(`No tasks for parent: ${query}`));
11031
+ console.log(chalk25.dim(`No tasks for parent: ${query}`));
10846
11032
  return;
10847
11033
  }
10848
- console.log(chalk24.bold(`
11034
+ console.log(chalk25.bold(`
10849
11035
  \u{1F4CB} ${query} (${tasks.length} tasks)
10850
11036
  `));
10851
11037
  const allTasks = await service.readAll();
@@ -10853,11 +11039,11 @@ function createTaskShowCommand() {
10853
11039
  for (const task of tasks) {
10854
11040
  const isBlocked = task.status === "todo" && task.depends_on.length > 0 && !task.depends_on.every((id) => doneIds.has(id));
10855
11041
  const icon = isBlocked ? BLOCKED_ICON : STATUS_ICONS[task.status] || "\u{1F4CB}";
10856
- let line = ` ${icon} ${chalk24.dim(task.id)} P${task.priority} ${task.title}`;
10857
- if (task.assigned_to) line += chalk24.cyan(` @${task.assigned_to}`);
11042
+ let line = ` ${icon} ${chalk25.dim(task.id)} P${task.priority} ${task.title}`;
11043
+ if (task.assigned_to) line += chalk25.cyan(` @${task.assigned_to}`);
10858
11044
  if (isBlocked) {
10859
11045
  const bb = task.depends_on.filter((id) => !doneIds.has(id));
10860
- line += chalk24.red(` (blocked: ${bb.join(", ")})`);
11046
+ line += chalk25.red(` (blocked: ${bb.join(", ")})`);
10861
11047
  }
10862
11048
  console.log(line);
10863
11049
  }
@@ -10867,11 +11053,11 @@ function createTaskShowCommand() {
10867
11053
  }
10868
11054
 
10869
11055
  // src/commands/tasks/pick.ts
10870
- import { Command as Command52 } from "commander";
10871
- import chalk25 from "chalk";
11056
+ import { Command as Command53 } from "commander";
11057
+ import chalk26 from "chalk";
10872
11058
  import { confirm as confirm9 } from "@inquirer/prompts";
10873
11059
  function createTaskPickCommand() {
10874
- return new Command52("pick").description("Claim the next available task").option("-j, --json", "Output JSON").action(async (options) => {
11060
+ return new Command53("pick").description("Claim the next available task").option("-j, --json", "Output JSON").action(async (options) => {
10875
11061
  const service = new TaskService();
10876
11062
  const ready = await service.getReady();
10877
11063
  if (ready.length === 0) {
@@ -10879,8 +11065,8 @@ function createTaskPickCommand() {
10879
11065
  console.log(JSON.stringify({ picked: null, message: "No tasks ready" }));
10880
11066
  return;
10881
11067
  }
10882
- console.log(chalk25.dim("No tasks ready to pick."));
10883
- console.log(chalk25.dim('\u{1F4A1} Add tasks first: jai1 t add "..."'));
11068
+ console.log(chalk26.dim("No tasks ready to pick."));
11069
+ console.log(chalk26.dim('\u{1F4A1} Add tasks first: jai1 t add "..."'));
10884
11070
  return;
10885
11071
  }
10886
11072
  const top = ready[0];
@@ -10890,13 +11076,13 @@ function createTaskPickCommand() {
10890
11076
  console.log(JSON.stringify(picked2, null, 2));
10891
11077
  return;
10892
11078
  }
10893
- console.log(chalk25.bold("\n\u{1F4CC} Next available task:"));
10894
- console.log(` ${chalk25.bold(top.id)} P${top.priority}${icon} ${top.title}`);
11079
+ console.log(chalk26.bold("\n\u{1F4CC} Next available task:"));
11080
+ console.log(` ${chalk26.bold(top.id)} P${top.priority}${icon} ${top.title}`);
10895
11081
  if (top.parent) {
10896
- console.log(` ${chalk25.dim("Parent:")} ${top.parent}`);
11082
+ console.log(` ${chalk26.dim("Parent:")} ${top.parent}`);
10897
11083
  }
10898
11084
  if (ready.length > 1) {
10899
- console.log(chalk25.dim(`
11085
+ console.log(chalk26.dim(`
10900
11086
  +${ready.length - 1} more tasks ready`));
10901
11087
  }
10902
11088
  const proceed = await confirm9({
@@ -10904,20 +11090,20 @@ function createTaskPickCommand() {
10904
11090
  default: true
10905
11091
  });
10906
11092
  if (!proceed) {
10907
- console.log(chalk25.dim("\nCancelled."));
11093
+ console.log(chalk26.dim("\nCancelled."));
10908
11094
  return;
10909
11095
  }
10910
11096
  const picked = await service.pick(top.id);
10911
- console.log(chalk25.green(`
11097
+ console.log(chalk26.green(`
10912
11098
  \u2705 ${picked.id} assigned to @${picked.assigned_to}, status \u2192 in_progress`));
10913
11099
  });
10914
11100
  }
10915
11101
 
10916
11102
  // src/commands/tasks/done.ts
10917
- import { Command as Command53 } from "commander";
10918
- import chalk26 from "chalk";
11103
+ import { Command as Command54 } from "commander";
11104
+ import chalk27 from "chalk";
10919
11105
  function createTaskDoneCommand() {
10920
- return new Command53("done").description("Mark task as done").argument("<id>", "Task ID (e.g. T-001)").option("-j, --json", "Output JSON").action(async (id, options) => {
11106
+ return new Command54("done").description("Mark task as done").argument("<id>", "Task ID (e.g. T-001)").option("-j, --json", "Output JSON").action(async (id, options) => {
10921
11107
  const service = new TaskService();
10922
11108
  try {
10923
11109
  const task = await service.markDone(id);
@@ -10925,19 +11111,19 @@ function createTaskDoneCommand() {
10925
11111
  console.log(JSON.stringify(task, null, 2));
10926
11112
  return;
10927
11113
  }
10928
- console.log(chalk26.green(`\u2705 ${task.id}: ${task.title} \u2192 done`));
11114
+ console.log(chalk27.green(`\u2705 ${task.id}: ${task.title} \u2192 done`));
10929
11115
  } catch (error) {
10930
- console.error(chalk26.red(`\u274C ${error instanceof Error ? error.message : String(error)}`));
11116
+ console.error(chalk27.red(`\u274C ${error instanceof Error ? error.message : String(error)}`));
10931
11117
  process.exit(1);
10932
11118
  }
10933
11119
  });
10934
11120
  }
10935
11121
 
10936
11122
  // src/commands/tasks/dep.ts
10937
- import { Command as Command54 } from "commander";
10938
- import chalk27 from "chalk";
11123
+ import { Command as Command55 } from "commander";
11124
+ import chalk28 from "chalk";
10939
11125
  function createTaskDepCommand() {
10940
- return new Command54("dep").description("Add dependency: child depends on parent").argument("<childId>", "Child task ID (the one that waits)").argument("<parentId>", "Parent task ID (must be done first)").option("-j, --json", "Output JSON").action(async (childId, parentId, options) => {
11126
+ return new Command55("dep").description("Add dependency: child depends on parent").argument("<childId>", "Child task ID (the one that waits)").argument("<parentId>", "Parent task ID (must be done first)").option("-j, --json", "Output JSON").action(async (childId, parentId, options) => {
10941
11127
  const service = new TaskService();
10942
11128
  try {
10943
11129
  const task = await service.addDependency(childId, parentId);
@@ -10945,165 +11131,216 @@ function createTaskDepCommand() {
10945
11131
  console.log(JSON.stringify(task, null, 2));
10946
11132
  return;
10947
11133
  }
10948
- console.log(chalk27.green(`\u2705 ${childId} now depends on ${parentId}`));
10949
- console.log(chalk27.dim(` ${task.title} \u2192 waits for ${parentId}`));
11134
+ console.log(chalk28.green(`\u2705 ${childId} now depends on ${parentId}`));
11135
+ console.log(chalk28.dim(` ${task.title} \u2192 waits for ${parentId}`));
10950
11136
  } catch (error) {
10951
- console.error(chalk27.red(`\u274C ${error instanceof Error ? error.message : String(error)}`));
11137
+ console.error(chalk28.red(`\u274C ${error instanceof Error ? error.message : String(error)}`));
10952
11138
  process.exit(1);
10953
11139
  }
10954
11140
  });
10955
11141
  }
10956
11142
 
10957
11143
  // src/commands/tasks/sync.ts
10958
- import { Command as Command55 } from "commander";
10959
- import chalk28 from "chalk";
11144
+ import { Command as Command56 } from "commander";
11145
+ import chalk29 from "chalk";
10960
11146
  function createTaskSyncCommand() {
10961
- return new Command55("sync").description("Sync tasks via git (default: pull \u2192 push)").option("--pull", "Pull only: merge tasks from origin/jai1").option("--push", "Push only: commit and push tasks to origin/jai1").action(async (options) => {
11147
+ return new Command56("sync").description("Sync tasks via git (default: pull \u2192 push)").option("--pull", "Pull only: merge tasks from origin/jai1").option("--push", "Push only: commit and push tasks to origin/jai1").action(async (options) => {
10962
11148
  const service = new TaskService();
10963
11149
  const branch = service.getSyncBranch();
10964
11150
  const doPull = options.pull || !options.pull && !options.push;
10965
11151
  const doPush = options.push || !options.pull && !options.push;
10966
11152
  if (doPull) {
10967
- console.log(chalk28.dim(`\u23F3 Pulling tasks from origin/${branch}...`));
11153
+ console.log(chalk29.dim(`\u23F3 Pulling tasks from origin/${branch}...`));
10968
11154
  try {
10969
11155
  const result = await service.syncPull();
10970
11156
  if (result.merged > 0) {
10971
- console.log(chalk28.green(` \u2193 ${result.merged} tasks merged`));
11157
+ console.log(chalk29.green(` \u2193 ${result.merged} tasks merged`));
10972
11158
  } else {
10973
- console.log(chalk28.dim(` \u2193 Already up to date`));
11159
+ console.log(chalk29.dim(` \u2193 Already up to date`));
10974
11160
  }
10975
11161
  } catch (error) {
10976
- console.error(chalk28.red(`\u274C Pull failed: ${error instanceof Error ? error.message : String(error)}`));
11162
+ console.error(chalk29.red(`\u274C Pull failed: ${error instanceof Error ? error.message : String(error)}`));
10977
11163
  process.exit(1);
10978
11164
  }
10979
11165
  }
10980
11166
  if (doPush) {
10981
- console.log(chalk28.dim(`\u23F3 Pushing tasks to origin/${branch}...`));
11167
+ console.log(chalk29.dim(`\u23F3 Pushing tasks to origin/${branch}...`));
10982
11168
  try {
10983
11169
  await service.syncPush();
10984
- console.log(chalk28.green(` \u2191 Tasks pushed to origin/${branch}`));
11170
+ console.log(chalk29.green(` \u2191 Tasks pushed to origin/${branch}`));
10985
11171
  } catch (error) {
10986
- console.error(chalk28.red(`\u274C Push failed: ${error instanceof Error ? error.message : String(error)}`));
11172
+ console.error(chalk29.red(`\u274C Push failed: ${error instanceof Error ? error.message : String(error)}`));
10987
11173
  process.exit(1);
10988
11174
  }
10989
11175
  }
10990
- console.log(chalk28.green("\u2705 Sync complete"));
11176
+ console.log(chalk29.green("\u2705 Sync complete"));
10991
11177
  });
10992
11178
  }
10993
11179
 
10994
11180
  // src/commands/tasks/guide.ts
10995
- import { Command as Command56 } from "commander";
10996
- import chalk29 from "chalk";
11181
+ import { Command as Command57 } from "commander";
11182
+ import chalk30 from "chalk";
10997
11183
  var GUIDE_TEXT = `
10998
- ${chalk29.cyan.bold("\u{1F4D6} Jai1 Task Management Guide")}
10999
-
11000
- ${chalk29.bold("\u2501\u2501\u2501 STATUSES \u2501\u2501\u2501")}
11001
- ${chalk29.dim("todo")} \u{1F4CB} Ch\u01B0a b\u1EAFt \u0111\u1EA7u
11002
- ${chalk29.dim("in_progress")} \u{1F535} \u0110ang l\xE0m (bao g\u1ED3m review)
11003
- ${chalk29.dim("done")} \u2705 Ho\xE0n th\xE0nh
11004
- ${chalk29.dim("cancelled")} \u26AB Hu\u1EF7
11005
- ${chalk29.dim("(blocked)")} \u{1F534} Computed: depends_on ch\u01B0a done
11006
-
11007
- ${chalk29.bold("\u2501\u2501\u2501 PRIORITY \u2501\u2501\u2501")}
11008
- ${chalk29.dim("0")} = \u{1F525} Critical \u2014 Prod down, security, block c\u1EA3 team
11009
- ${chalk29.dim("1")} = \u{1F534} High \u2014 Feature ch\xEDnh, deadline g\u1EA7n
11010
- ${chalk29.dim("2")} = \u{1F7E1} Medium \u2014 B\xECnh th\u01B0\u1EDDng (default)
11011
- ${chalk29.dim("3")} = \u{1F7E2} Low \u2014 Nice-to-have, docs, refactor
11012
-
11013
- ${chalk29.bold("\u2501\u2501\u2501 QUICK START \u2501\u2501\u2501")}
11014
- ${chalk29.cyan("jai1 t add")} "Fix login bug" -p 1 -P bug/login
11015
- ${chalk29.cyan("jai1 t ready")} Show tasks s\u1EB5n s\xE0ng
11016
- ${chalk29.cyan("jai1 t pick")} Claim & start working
11017
- ${chalk29.cyan("jai1 t done")} T-003 Mark complete
11018
-
11019
- ${chalk29.bold("\u2501\u2501\u2501 DAILY WORKFLOW \u2501\u2501\u2501")}
11020
- ${chalk29.cyan("jai1 t sync --pull")} Pull latest tasks
11021
- ${chalk29.cyan("jai1 t summary")} Dashboard t\u1ED5ng quan
11022
- ${chalk29.cyan("jai1 t ready")} Xem tasks s\u1EB5n s\xE0ng
11023
- ${chalk29.cyan("jai1 t pick")} Claim task m\u1EDBi
11024
- ${chalk29.cyan("jai1 t done")} T-xxx Ho\xE0n th\xE0nh task
11025
- ${chalk29.cyan("jai1 t sync --push")} Push l\xEAn git
11026
-
11027
- ${chalk29.bold("\u2501\u2501\u2501 ADDING TASKS \u2501\u2501\u2501")}
11028
- ${chalk29.yellow("\u26A0 Lu\xF4n ki\u1EC3m tra duplicate tr\u01B0\u1EDBc khi add:")}
11029
- ${chalk29.cyan("jai1 t list -P")} feature/xxx
11030
-
11031
- ${chalk29.dim("Add cho feature:")}
11032
- ${chalk29.cyan("jai1 t add")} "Setup DB schema" -p 1 -P feature/xxx
11033
- ${chalk29.cyan("jai1 t add")} "Create API" -p 1 -P feature/xxx
11034
- ${chalk29.cyan("jai1 t add")} "Build UI" -p 2 -P feature/xxx
11035
-
11036
- ${chalk29.dim("Add cho plan:")}
11037
- ${chalk29.cyan("jai1 t add")} "Refactor middleware" -p 2 -P plan/xxx
11038
-
11039
- ${chalk29.dim("Add standalone:")}
11040
- ${chalk29.cyan("jai1 t add")} "Fix README typo" -p 3
11041
-
11042
- ${chalk29.dim("Add bug fix:")}
11043
- ${chalk29.cyan("jai1 t add")} "Fix login redirect" -p 1 -P bug/xxx
11044
-
11045
- ${chalk29.bold("\u2501\u2501\u2501 DEPENDENCY \u2501\u2501\u2501")}
11046
- ${chalk29.dim("Task dependency:")}
11047
- ${chalk29.cyan("jai1 t dep")} T-002 T-001 T-002 ch\u1EDD T-001 done
11048
- ${chalk29.cyan("jai1 t dep")} T-003 T-002 T-003 ch\u1EDD T-002 done
11049
-
11050
- ${chalk29.dim("Feature-level dependency:")}
11051
- ${chalk29.dim("# N\u1EBFu feature/auth ph\u1EE5 thu\u1ED9c feature/user-model:")}
11052
- ${chalk29.cyan("jai1 t add")} "[DEP] Wait for feature/user-model" -p 1 -P feature/auth
11053
- ${chalk29.dim("# R\u1ED3i dep n\xF3 v\u1EDBi tasks cu\u1ED1i c\u1EE7a user-model")}
11054
-
11055
- ${chalk29.dim("View deps:")}
11056
- ${chalk29.cyan("jai1 t show")} T-002 Hi\u1EC7n depends_on
11057
-
11058
- ${chalk29.bold("\u2501\u2501\u2501 TEAM COLLABORATION \u2501\u2501\u2501")}
11059
- ${chalk29.yellow("\u26A0 Assignment ch\u1EC9 qua pick \u2014 kh\xF4ng set th\u1EE7 c\xF4ng.")}
11060
- ${chalk29.dim("Khi b\u1EA1n pick \u2192 team th\u1EA5y task \u0111\xE3 c\xF3 ng\u01B0\u1EDDi nh\u1EADn.")}
11061
-
11062
- ${chalk29.dim("Sync morning:")} ${chalk29.cyan("jai1 t sync --pull")}
11063
- ${chalk29.dim("Sync evening:")} ${chalk29.cyan("jai1 t sync --push")}
11064
-
11065
- ${chalk29.bold("\u2501\u2501\u2501 FOR AI AGENTS (Workflow Integration) \u2501\u2501\u2501")}
11066
- ${chalk29.dim("Khi t\u1EA1o tasks t\u1EEB feature/plan:")}
11067
- 1. ${chalk29.cyan("jai1 t list -P")} <parent> Check existing (tr\xE1nh duplicate)
11068
- 2. ${chalk29.cyan("jai1 t add")} "..." -p <0-3> -P <parent> Add t\u1EEBng task
11069
- 3. ${chalk29.cyan("jai1 t dep")} <child> <parent> Set dependencies
11070
- 4. ${chalk29.cyan("jai1 t done")} <id> Mark complete
11071
-
11072
- ${chalk29.dim("Khi implement task ti\u1EBFp theo:")}
11073
- 1. ${chalk29.cyan("jai1 t pick")} (ho\u1EB7c ${chalk29.cyan("jai1 t ready -P")} <parent>)
11184
+ ${chalk30.cyan.bold("\u{1F4D6} Jai1 Task Management Guide")}
11185
+
11186
+ ${chalk30.bold("\u2501\u2501\u2501 STATUSES \u2501\u2501\u2501")}
11187
+ ${chalk30.dim("todo")} \u{1F4CB} Ch\u01B0a b\u1EAFt \u0111\u1EA7u
11188
+ ${chalk30.dim("in_progress")} \u{1F535} \u0110ang l\xE0m (bao g\u1ED3m review)
11189
+ ${chalk30.dim("done")} \u2705 Ho\xE0n th\xE0nh
11190
+ ${chalk30.dim("cancelled")} \u26AB Hu\u1EF7
11191
+ ${chalk30.dim("(blocked)")} \u{1F534} Computed: depends_on ch\u01B0a done
11192
+
11193
+ ${chalk30.bold("\u2501\u2501\u2501 PRIORITY \u2501\u2501\u2501")}
11194
+ ${chalk30.dim("0")} = \u{1F525} Critical \u2014 Prod down, security, block c\u1EA3 team
11195
+ ${chalk30.dim("1")} = \u{1F534} High \u2014 Feature ch\xEDnh, deadline g\u1EA7n
11196
+ ${chalk30.dim("2")} = \u{1F7E1} Medium \u2014 B\xECnh th\u01B0\u1EDDng (default)
11197
+ ${chalk30.dim("3")} = \u{1F7E2} Low \u2014 Nice-to-have, docs, refactor
11198
+
11199
+ ${chalk30.bold("\u2501\u2501\u2501 QUICK START \u2501\u2501\u2501")}
11200
+ ${chalk30.cyan("jai1 t add")} "Fix login bug" -p 1 -P bug/login
11201
+ ${chalk30.cyan("jai1 t ready")} Show tasks s\u1EB5n s\xE0ng
11202
+ ${chalk30.cyan("jai1 t pick")} Claim & start working
11203
+ ${chalk30.cyan("jai1 t done")} T-003 Mark complete
11204
+
11205
+ ${chalk30.bold("\u2501\u2501\u2501 DAILY WORKFLOW \u2501\u2501\u2501")}
11206
+ ${chalk30.cyan("jai1 t sync --pull")} Pull latest tasks
11207
+ ${chalk30.cyan("jai1 t summary")} Dashboard t\u1ED5ng quan
11208
+ ${chalk30.cyan("jai1 t ready")} Xem tasks s\u1EB5n s\xE0ng
11209
+ ${chalk30.cyan("jai1 t pick")} Claim task m\u1EDBi
11210
+ ${chalk30.cyan("jai1 t done")} T-xxx Ho\xE0n th\xE0nh task
11211
+ ${chalk30.cyan("jai1 t sync --push")} Push l\xEAn git
11212
+
11213
+ ${chalk30.bold("\u2501\u2501\u2501 ADDING TASKS \u2501\u2501\u2501")}
11214
+ ${chalk30.yellow("\u26A0 Lu\xF4n ki\u1EC3m tra duplicate tr\u01B0\u1EDBc khi add:")}
11215
+ ${chalk30.cyan("jai1 t list -P")} feature/xxx
11216
+
11217
+ ${chalk30.dim("Add cho feature:")}
11218
+ ${chalk30.cyan("jai1 t add")} "Setup DB schema" -p 1 -P feature/xxx
11219
+ ${chalk30.cyan("jai1 t add")} "Create API" -p 1 -P feature/xxx
11220
+ ${chalk30.cyan("jai1 t add")} "Build UI" -p 2 -P feature/xxx
11221
+
11222
+ ${chalk30.dim("Add cho plan:")}
11223
+ ${chalk30.cyan("jai1 t add")} "Refactor middleware" -p 2 -P plan/xxx
11224
+
11225
+ ${chalk30.dim("Add standalone:")}
11226
+ ${chalk30.cyan("jai1 t add")} "Fix README typo" -p 3
11227
+
11228
+ ${chalk30.dim("Add bug fix:")}
11229
+ ${chalk30.cyan("jai1 t add")} "Fix login redirect" -p 1 -P bug/xxx
11230
+
11231
+ ${chalk30.bold("\u2501\u2501\u2501 DEPENDENCY \u2501\u2501\u2501")}
11232
+ ${chalk30.dim("Task dependency:")}
11233
+ ${chalk30.cyan("jai1 t dep")} T-002 T-001 T-002 ch\u1EDD T-001 done
11234
+ ${chalk30.cyan("jai1 t dep")} T-003 T-002 T-003 ch\u1EDD T-002 done
11235
+
11236
+ ${chalk30.dim("Feature-level dependency:")}
11237
+ ${chalk30.dim("# N\u1EBFu feature/auth ph\u1EE5 thu\u1ED9c feature/user-model:")}
11238
+ ${chalk30.cyan("jai1 t add")} "[DEP] Wait for feature/user-model" -p 1 -P feature/auth
11239
+ ${chalk30.dim("# R\u1ED3i dep n\xF3 v\u1EDBi tasks cu\u1ED1i c\u1EE7a user-model")}
11240
+
11241
+ ${chalk30.dim("View deps:")}
11242
+ ${chalk30.cyan("jai1 t show")} T-002 Hi\u1EC7n depends_on
11243
+
11244
+ ${chalk30.bold("\u2501\u2501\u2501 TEAM COLLABORATION \u2501\u2501\u2501")}
11245
+ ${chalk30.yellow("\u26A0 Assignment ch\u1EC9 qua pick \u2014 kh\xF4ng set th\u1EE7 c\xF4ng.")}
11246
+ ${chalk30.dim("Khi b\u1EA1n pick \u2192 team th\u1EA5y task \u0111\xE3 c\xF3 ng\u01B0\u1EDDi nh\u1EADn.")}
11247
+
11248
+ ${chalk30.dim("Sync morning:")} ${chalk30.cyan("jai1 t sync --pull")}
11249
+ ${chalk30.dim("Sync evening:")} ${chalk30.cyan("jai1 t sync --push")}
11250
+
11251
+ ${chalk30.bold("\u2501\u2501\u2501 FOR AI AGENTS (Workflow Integration) \u2501\u2501\u2501")}
11252
+ ${chalk30.dim("Khi t\u1EA1o tasks t\u1EEB feature/plan:")}
11253
+ 1. ${chalk30.cyan("jai1 t list -P")} <parent> Check existing (tr\xE1nh duplicate)
11254
+ 2. ${chalk30.cyan("jai1 t add")} "..." -p <0-3> -P <parent> Add t\u1EEBng task
11255
+ 3. ${chalk30.cyan("jai1 t dep")} <child> <parent> Set dependencies
11256
+ 4. ${chalk30.cyan("jai1 t done")} <id> Mark complete
11257
+
11258
+ ${chalk30.dim("Khi implement task ti\u1EBFp theo:")}
11259
+ 1. ${chalk30.cyan("jai1 t pick")} (ho\u1EB7c ${chalk30.cyan("jai1 t ready -P")} <parent>)
11074
11260
  2. Implement task
11075
- 3. ${chalk29.cyan("jai1 t done")} <id>
11261
+ 3. ${chalk30.cyan("jai1 t done")} <id>
11076
11262
 
11077
- ${chalk29.dim("Status transitions:")}
11263
+ ${chalk30.dim("Status transitions:")}
11078
11264
  add \u2192 todo (default)
11079
11265
  pick \u2192 in_progress (auto assign)
11080
11266
  done \u2192 done
11081
11267
  update -s \u2192 any valid status
11082
11268
 
11083
- ${chalk29.bold("\u2501\u2501\u2501 ALL COMMANDS \u2501\u2501\u2501")}
11084
- ${chalk29.cyan("jai1 t list")} [-s status] [-P parent] [-j]
11085
- ${chalk29.cyan("jai1 t ready")} [-P parent] [-j]
11086
- ${chalk29.cyan("jai1 t add")} <title> [-p 0-3] [-P parent] [-t tags] [-j]
11087
- ${chalk29.cyan("jai1 t update")} <id> [-s <status>] [-n <notes>] [-j]
11088
- ${chalk29.cyan("jai1 t show")} <id|parent> [-j]
11089
- ${chalk29.cyan("jai1 t pick")} [-j]
11090
- ${chalk29.cyan("jai1 t done")} <id> [-j]
11091
- ${chalk29.cyan("jai1 t dep")} <childId> <parentId> [-j]
11092
- ${chalk29.cyan("jai1 t sync")} [--pull] [--push]
11093
- ${chalk29.cyan("jai1 t summary")} [-j]
11094
- ${chalk29.cyan("jai1 t guide")}
11095
-
11096
- ${chalk29.dim("-j / --json available on all commands (except guide, sync)")}
11269
+ ${chalk30.bold("\u2501\u2501\u2501 ALL COMMANDS \u2501\u2501\u2501")}
11270
+ ${chalk30.cyan("jai1 t list")} [-s status] [-P parent] [-j]
11271
+ ${chalk30.cyan("jai1 t ready")} [-P parent] [-j]
11272
+ ${chalk30.cyan("jai1 t add")} <title> [-p 0-3] [-P parent] [-t tags] [-j]
11273
+ ${chalk30.cyan("jai1 t update")} <id> [-s <status>] [-n <notes>] [-j]
11274
+ ${chalk30.cyan("jai1 t show")} <id|parent> [-j]
11275
+ ${chalk30.cyan("jai1 t pick")} [-j]
11276
+ ${chalk30.cyan("jai1 t done")} <id> [-j]
11277
+ ${chalk30.cyan("jai1 t dep")} <childId> <parentId> [-j]
11278
+ ${chalk30.cyan("jai1 t sync")} [--pull] [--push]
11279
+ ${chalk30.cyan("jai1 t summary")} [-j]
11280
+ ${chalk30.cyan("jai1 t groups")} [-s status] [-j] ${chalk30.dim("(alias: parents)")}
11281
+ ${chalk30.cyan("jai1 t guide")}
11282
+
11283
+ ${chalk30.dim("-j / --json available on all commands (except guide, sync)")}
11097
11284
  `;
11098
11285
  function createTaskGuideCommand() {
11099
- return new Command56("guide").description("Show full task management guide").action(() => {
11286
+ return new Command57("guide").description("Show full task management guide").action(() => {
11100
11287
  console.log(GUIDE_TEXT);
11101
11288
  });
11102
11289
  }
11103
11290
 
11291
+ // src/commands/tasks/parents.ts
11292
+ import { Command as Command58 } from "commander";
11293
+ import chalk31 from "chalk";
11294
+ var PARENT_STATUS_ICONS = {
11295
+ done: "\u2705",
11296
+ in_progress: "\u{1F535}",
11297
+ ready: "\u{1F4CB}",
11298
+ todo: "\u{1F534}"
11299
+ };
11300
+ function formatProgress(p) {
11301
+ const completed = p.done + p.cancelled;
11302
+ if (p.status === "done") {
11303
+ return chalk31.green(`${completed}/${p.total} done`);
11304
+ }
11305
+ const parts = [];
11306
+ if (p.in_progress > 0) parts.push(`${p.in_progress} in_progress`);
11307
+ if (p.ready > 0) parts.push(`${p.ready} ready`);
11308
+ if (p.blocked > 0) parts.push(chalk31.red(`${p.blocked} blocked`));
11309
+ if (p.done > 0) parts.push(`${p.done} done`);
11310
+ return `${completed}/${p.total} tasks` + (parts.length > 0 ? ` (${parts.join(", ")})` : "");
11311
+ }
11312
+ function createTaskParentsCommand() {
11313
+ return new Command58("groups").aliases(["parents", "pg"]).description("List task groups with computed status").option("-s, --status <status>", "Filter by computed status: done, in_progress, ready, todo").option("-j, --json", "Output JSON").action(async (options) => {
11314
+ const service = new TaskService();
11315
+ const parents = await service.getParents(options.status);
11316
+ if (options.json) {
11317
+ console.log(JSON.stringify(parents, null, 2));
11318
+ return;
11319
+ }
11320
+ if (parents.length === 0) {
11321
+ if (options.status) {
11322
+ console.log(chalk31.dim(`No groups with status: ${options.status}`));
11323
+ } else {
11324
+ console.log(chalk31.dim("No task groups found."));
11325
+ console.log(chalk31.dim('\u{1F4A1} Add tasks with parent: jai1 t add "..." -P feature/xxx'));
11326
+ }
11327
+ return;
11328
+ }
11329
+ const header = options.status ? `\u{1F4E6} Task Groups \u2014 ${options.status} (${parents.length})` : `\u{1F4E6} Task Groups (${parents.length})`;
11330
+ console.log(chalk31.bold(header));
11331
+ console.log();
11332
+ for (const p of parents) {
11333
+ const icon = PARENT_STATUS_ICONS[p.status] || "\u{1F4CB}";
11334
+ const progress = formatProgress(p);
11335
+ console.log(` ${icon} ${chalk31.bold(p.name)} ${chalk31.dim("\u2014")} ${progress}`);
11336
+ }
11337
+ console.log();
11338
+ });
11339
+ }
11340
+
11104
11341
  // src/commands/tasks/index.ts
11105
11342
  function createTasksCommand() {
11106
- const cmd = new Command57("tasks").alias("t").description("Task management \u2014 track, assign, and manage development tasks").hook("preAction", (thisCommand, actionCommand) => {
11343
+ const cmd = new Command59("tasks").alias("t").description("Task management \u2014 track, assign, and manage development tasks").hook("preAction", (thisCommand, actionCommand) => {
11107
11344
  if (actionCommand.name() !== "guide") {
11108
11345
  TaskService.ensureJai1Dir();
11109
11346
  }
@@ -11118,26 +11355,27 @@ function createTasksCommand() {
11118
11355
  cmd.addCommand(createTaskDepCommand());
11119
11356
  cmd.addCommand(createTaskSyncCommand());
11120
11357
  cmd.addCommand(createTaskSummaryCommand());
11358
+ cmd.addCommand(createTaskParentsCommand());
11121
11359
  cmd.addCommand(createTaskGuideCommand());
11122
11360
  cmd.action(async () => {
11123
- const { handleTaskSummary } = await import("./summary-O75DZKM2.js");
11361
+ const { handleTaskSummary } = await import("./summary-AEJ34P4Q.js");
11124
11362
  await handleTaskSummary({ json: false });
11125
11363
  });
11126
11364
  return cmd;
11127
11365
  }
11128
11366
 
11129
11367
  // src/commands/kit/index.ts
11130
- import { Command as Command61 } from "commander";
11131
- import chalk31 from "chalk";
11368
+ import { Command as Command63 } from "commander";
11369
+ import chalk33 from "chalk";
11132
11370
 
11133
11371
  // src/commands/kit/list.ts
11134
- import { Command as Command58 } from "commander";
11135
- import chalk30 from "chalk";
11372
+ import { Command as Command60 } from "commander";
11373
+ import chalk32 from "chalk";
11136
11374
  import Table6 from "cli-table3";
11137
11375
 
11138
11376
  // src/services/starter-kit.service.ts
11139
11377
  import { promises as fs19 } from "fs";
11140
- import { join as join9 } from "path";
11378
+ import { join as join10 } from "path";
11141
11379
  import AdmZip from "adm-zip";
11142
11380
  var StarterKitService = class {
11143
11381
  /**
@@ -11184,9 +11422,9 @@ var StarterKitService = class {
11184
11422
  throw new NetworkError(`Failed to download kit: HTTP ${response.status}`);
11185
11423
  }
11186
11424
  if (onProgress) onProgress(30);
11187
- const tmpDir = join9(process.env.TMPDIR || "/tmp", "jai1-kits");
11425
+ const tmpDir = join10(process.env.TMPDIR || "/tmp", "jai1-kits");
11188
11426
  await fs19.mkdir(tmpDir, { recursive: true });
11189
- const tmpFile = join9(tmpDir, `${slug}.zip`);
11427
+ const tmpFile = join10(tmpDir, `${slug}.zip`);
11190
11428
  const buffer = await response.arrayBuffer();
11191
11429
  await fs19.writeFile(tmpFile, Buffer.from(buffer));
11192
11430
  if (onProgress) onProgress(60);
@@ -11200,13 +11438,13 @@ var StarterKitService = class {
11200
11438
 
11201
11439
  // src/commands/kit/list.ts
11202
11440
  function createKitListCommand() {
11203
- return new Command58("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) => {
11441
+ return new Command60("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) => {
11204
11442
  const configService = new ConfigService();
11205
11443
  const config = await configService.load();
11206
11444
  if (!config) {
11207
11445
  throw new ValidationError('Not initialized. Run "jai1 auth" first.');
11208
11446
  }
11209
- console.log(chalk30.cyan("\u{1F4E6} \u0110ang t\u1EA3i danh s\xE1ch starter kits..."));
11447
+ console.log(chalk32.cyan("\u{1F4E6} \u0110ang t\u1EA3i danh s\xE1ch starter kits..."));
11210
11448
  console.log();
11211
11449
  const kitService = new StarterKitService();
11212
11450
  const kits = await kitService.list(config, {
@@ -11214,9 +11452,9 @@ function createKitListCommand() {
11214
11452
  search: options.search
11215
11453
  });
11216
11454
  if (kits.length === 0) {
11217
- console.log(chalk30.yellow("Kh\xF4ng t\xECm th\u1EA5y starter kits n\xE0o."));
11455
+ console.log(chalk32.yellow("Kh\xF4ng t\xECm th\u1EA5y starter kits n\xE0o."));
11218
11456
  if (options.category || options.search) {
11219
- console.log(chalk30.dim("Th\u1EED b\u1ECF filter \u0111\u1EC3 xem t\u1EA5t c\u1EA3."));
11457
+ console.log(chalk32.dim("Th\u1EED b\u1ECF filter \u0111\u1EC3 xem t\u1EA5t c\u1EA3."));
11220
11458
  }
11221
11459
  return;
11222
11460
  }
@@ -11240,35 +11478,35 @@ function createKitListCommand() {
11240
11478
  const categoryKits = byCategory[category];
11241
11479
  const categoryIcon = category === "frontend" ? "\u{1F3A8}" : category === "backend" ? "\u2699\uFE0F" : category === "fullstack" ? "\u{1F680}" : "\u{1F4E6}";
11242
11480
  console.log(
11243
- chalk30.bold(`${categoryIcon} ${category.charAt(0).toUpperCase() + category.slice(1)}`)
11481
+ chalk32.bold(`${categoryIcon} ${category.charAt(0).toUpperCase() + category.slice(1)}`)
11244
11482
  );
11245
11483
  const table = new Table6({
11246
11484
  head: [
11247
- chalk30.cyan("Slug"),
11248
- chalk30.cyan("M\xF4 t\u1EA3"),
11249
- chalk30.cyan("Version")
11485
+ chalk32.cyan("Slug"),
11486
+ chalk32.cyan("M\xF4 t\u1EA3"),
11487
+ chalk32.cyan("Version")
11250
11488
  ],
11251
11489
  style: { head: [], border: ["gray"] }
11252
11490
  });
11253
11491
  for (const kit of categoryKits) {
11254
11492
  table.push([
11255
- chalk30.white(kit.slug),
11256
- chalk30.dim(kit.description.slice(0, 50)),
11257
- chalk30.green(`v${kit.version}`)
11493
+ chalk32.white(kit.slug),
11494
+ chalk32.dim(kit.description.slice(0, 50)),
11495
+ chalk32.green(`v${kit.version}`)
11258
11496
  ]);
11259
11497
  }
11260
11498
  console.log(table.toString());
11261
11499
  console.log();
11262
11500
  }
11263
- console.log(chalk30.dim(`T\u1ED5ng c\u1ED9ng: ${kits.length} starter kit(s)`));
11264
- console.log(chalk30.dim('\n\u{1F4A1} Ch\u1EA1y "jai1 kit create <slug>" \u0111\u1EC3 t\u1EA1o project m\u1EDBi'));
11501
+ console.log(chalk32.dim(`T\u1ED5ng c\u1ED9ng: ${kits.length} starter kit(s)`));
11502
+ console.log(chalk32.dim('\n\u{1F4A1} Ch\u1EA1y "jai1 kit create <slug>" \u0111\u1EC3 t\u1EA1o project m\u1EDBi'));
11265
11503
  });
11266
11504
  }
11267
11505
 
11268
11506
  // src/commands/kit/info.ts
11269
- import { Command as Command59 } from "commander";
11507
+ import { Command as Command61 } from "commander";
11270
11508
  function createKitInfoCommand() {
11271
- return new Command59("info").description("Show detailed information about a starter kit").argument("<slug>", "Starter kit slug").action(async (slug) => {
11509
+ return new Command61("info").description("Show detailed information about a starter kit").argument("<slug>", "Starter kit slug").action(async (slug) => {
11272
11510
  const configService = new ConfigService();
11273
11511
  const config = await configService.load();
11274
11512
  if (!config) {
@@ -11317,9 +11555,9 @@ Post-Init Commands:`);
11317
11555
  }
11318
11556
 
11319
11557
  // src/commands/kit/create.ts
11320
- import { Command as Command60 } from "commander";
11558
+ import { Command as Command62 } from "commander";
11321
11559
  import { promises as fs20 } from "fs";
11322
- import { join as join10 } from "path";
11560
+ import { join as join11 } from "path";
11323
11561
  import { select as select3, input as input2, checkbox as checkbox4 } from "@inquirer/prompts";
11324
11562
  import { execa as execa2 } from "execa";
11325
11563
 
@@ -11362,7 +11600,7 @@ var HookExecutor = class {
11362
11600
 
11363
11601
  // src/commands/kit/create.ts
11364
11602
  function createKitCreateCommand() {
11365
- return new Command60("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) => {
11603
+ return new Command62("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) => {
11366
11604
  const configService = new ConfigService();
11367
11605
  const config = await configService.load();
11368
11606
  if (!config) {
@@ -11382,7 +11620,7 @@ function createKitCreateCommand() {
11382
11620
  }
11383
11621
  }
11384
11622
  }
11385
- const targetDir = directory || join10(process.cwd(), options.name || slug);
11623
+ const targetDir = directory || join11(process.cwd(), options.name || slug);
11386
11624
  try {
11387
11625
  await fs20.access(targetDir);
11388
11626
  throw new Error(`Directory already exists: ${targetDir}`);
@@ -11447,7 +11685,7 @@ function createKitCreateCommand() {
11447
11685
  );
11448
11686
  for (const filepath of expandedPaths) {
11449
11687
  console.log(` \u{1F4E5} Installing ${filepath}...`);
11450
- await componentsService.install(config, filepath, join10(targetDir, ".jai1"));
11688
+ await componentsService.install(config, filepath, join11(targetDir, ".jai1"));
11451
11689
  }
11452
11690
  console.log(" \u2713 Framework components applied");
11453
11691
  }
@@ -11527,7 +11765,7 @@ async function getAllFiles(dir) {
11527
11765
  const files = [];
11528
11766
  const entries = await fs20.readdir(dir, { withFileTypes: true });
11529
11767
  for (const entry of entries) {
11530
- const fullPath = join10(dir, entry.name);
11768
+ const fullPath = join11(dir, entry.name);
11531
11769
  if (entry.isDirectory()) {
11532
11770
  if (!entry.name.startsWith(".") && entry.name !== "node_modules") {
11533
11771
  files.push(...await getAllFiles(fullPath));
@@ -11541,23 +11779,23 @@ async function getAllFiles(dir) {
11541
11779
 
11542
11780
  // src/commands/kit/index.ts
11543
11781
  function showKitHelp() {
11544
- console.log(chalk31.bold.cyan("\u{1F4E6} jai1 kit") + chalk31.dim(" - Qu\u1EA3n l\xFD starter kits"));
11782
+ console.log(chalk33.bold.cyan("\u{1F4E6} jai1 kit") + chalk33.dim(" - Qu\u1EA3n l\xFD starter kits"));
11545
11783
  console.log();
11546
- console.log(chalk31.bold("C\xE1c l\u1EC7nh:"));
11547
- console.log(` ${chalk31.cyan("list")} Li\u1EC7t k\xEA c\xE1c starter kits c\xF3 s\u1EB5n`);
11548
- console.log(` ${chalk31.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t starter kit`);
11549
- console.log(` ${chalk31.cyan("create")} T\u1EA1o project m\u1EDBi t\u1EEB starter kit`);
11784
+ console.log(chalk33.bold("C\xE1c l\u1EC7nh:"));
11785
+ console.log(` ${chalk33.cyan("list")} Li\u1EC7t k\xEA c\xE1c starter kits c\xF3 s\u1EB5n`);
11786
+ console.log(` ${chalk33.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t starter kit`);
11787
+ console.log(` ${chalk33.cyan("create")} T\u1EA1o project m\u1EDBi t\u1EEB starter kit`);
11550
11788
  console.log();
11551
- console.log(chalk31.bold("V\xED d\u1EE5:"));
11552
- console.log(chalk31.dim(" $ jai1 kit list"));
11553
- console.log(chalk31.dim(" $ jai1 kit list --category frontend"));
11554
- console.log(chalk31.dim(" $ jai1 kit info next-tw4-shadcn"));
11555
- console.log(chalk31.dim(" $ jai1 kit create next-tw4-shadcn my-project"));
11789
+ console.log(chalk33.bold("V\xED d\u1EE5:"));
11790
+ console.log(chalk33.dim(" $ jai1 kit list"));
11791
+ console.log(chalk33.dim(" $ jai1 kit list --category frontend"));
11792
+ console.log(chalk33.dim(" $ jai1 kit info next-tw4-shadcn"));
11793
+ console.log(chalk33.dim(" $ jai1 kit create next-tw4-shadcn my-project"));
11556
11794
  console.log();
11557
- console.log(chalk31.dim('Ch\u1EA1y "jai1 kit <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
11795
+ console.log(chalk33.dim('Ch\u1EA1y "jai1 kit <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
11558
11796
  }
11559
11797
  function createKitCommand() {
11560
- const cmd = new Command61("kit").description("Manage starter kits for new projects").action(() => {
11798
+ const cmd = new Command63("kit").description("Manage starter kits for new projects").action(() => {
11561
11799
  showKitHelp();
11562
11800
  });
11563
11801
  cmd.addCommand(createKitListCommand());
@@ -11567,21 +11805,21 @@ function createKitCommand() {
11567
11805
  }
11568
11806
 
11569
11807
  // src/commands/rules/index.ts
11570
- import { Command as Command68 } from "commander";
11571
- import chalk33 from "chalk";
11808
+ import { Command as Command70 } from "commander";
11809
+ import chalk35 from "chalk";
11572
11810
 
11573
11811
  // src/commands/rules/list.ts
11574
- import { Command as Command62 } from "commander";
11575
- import chalk32 from "chalk";
11812
+ import { Command as Command64 } from "commander";
11813
+ import chalk34 from "chalk";
11576
11814
  import Table7 from "cli-table3";
11577
11815
  function createRulesListCommand() {
11578
- return new Command62("list").description("List available rule presets").option("--json", "Output as JSON").action(async (options) => {
11816
+ return new Command64("list").description("List available rule presets").option("--json", "Output as JSON").action(async (options) => {
11579
11817
  const configService = new ConfigService();
11580
11818
  const config = await configService.load();
11581
11819
  if (!config) {
11582
11820
  throw new ValidationError('Not initialized. Run "jai1 auth" first.');
11583
11821
  }
11584
- console.log(chalk32.cyan("\u{1F4CB} \u0110ang t\u1EA3i danh s\xE1ch rule presets..."));
11822
+ console.log(chalk34.cyan("\u{1F4CB} \u0110ang t\u1EA3i danh s\xE1ch rule presets..."));
11585
11823
  console.log();
11586
11824
  try {
11587
11825
  const response = await fetch(`${config.apiUrl}/api/rules/presets`, {
@@ -11598,23 +11836,23 @@ function createRulesListCommand() {
11598
11836
  return;
11599
11837
  }
11600
11838
  if (data.total === 0) {
11601
- console.log(chalk32.yellow("Kh\xF4ng c\xF3 presets n\xE0o."));
11839
+ console.log(chalk34.yellow("Kh\xF4ng c\xF3 presets n\xE0o."));
11602
11840
  return;
11603
11841
  }
11604
11842
  console.log(
11605
- chalk32.green(`\u2713 T\xECm th\u1EA5y ${chalk32.bold(data.total)} preset${data.total > 1 ? "s" : ""}`)
11843
+ chalk34.green(`\u2713 T\xECm th\u1EA5y ${chalk34.bold(data.total)} preset${data.total > 1 ? "s" : ""}`)
11606
11844
  );
11607
11845
  console.log();
11608
11846
  for (const preset of data.presets) {
11609
- console.log(chalk32.bold.cyan(`\u{1F4E6} ${preset.slug}`));
11847
+ console.log(chalk34.bold.cyan(`\u{1F4E6} ${preset.slug}`));
11610
11848
  const table = new Table7({
11611
11849
  style: { head: [], border: ["gray"], compact: true },
11612
11850
  colWidths: [15, 55]
11613
11851
  });
11614
11852
  table.push(
11615
- [chalk32.dim("T\xEAn"), chalk32.white(preset.name)],
11616
- [chalk32.dim("M\xF4 t\u1EA3"), chalk32.white(preset.description)],
11617
- [chalk32.dim("Version"), chalk32.green(`v${preset.version}`)]
11853
+ [chalk34.dim("T\xEAn"), chalk34.white(preset.name)],
11854
+ [chalk34.dim("M\xF4 t\u1EA3"), chalk34.white(preset.description)],
11855
+ [chalk34.dim("Version"), chalk34.green(`v${preset.version}`)]
11618
11856
  );
11619
11857
  const stackParts = [];
11620
11858
  if (preset.stack.frontend) stackParts.push(preset.stack.frontend);
@@ -11622,16 +11860,16 @@ function createRulesListCommand() {
11622
11860
  if (preset.stack.css) stackParts.push(preset.stack.css);
11623
11861
  if (preset.stack.database) stackParts.push(preset.stack.database);
11624
11862
  if (stackParts.length > 0) {
11625
- table.push([chalk32.dim("Stack"), chalk32.yellow(stackParts.join(" + "))]);
11863
+ table.push([chalk34.dim("Stack"), chalk34.yellow(stackParts.join(" + "))]);
11626
11864
  }
11627
11865
  table.push(
11628
- [chalk32.dim("Tags"), chalk32.dim(preset.tags.join(", ") || "-")],
11629
- [chalk32.dim("Downloads"), chalk32.white(preset.downloads.toString())]
11866
+ [chalk34.dim("Tags"), chalk34.dim(preset.tags.join(", ") || "-")],
11867
+ [chalk34.dim("Downloads"), chalk34.white(preset.downloads.toString())]
11630
11868
  );
11631
11869
  console.log(table.toString());
11632
11870
  console.log();
11633
11871
  }
11634
- console.log(chalk32.dim('\u{1F4A1} Ch\u1EA1y "jai1 rules apply <name>" \u0111\u1EC3 \xE1p d\u1EE5ng preset'));
11872
+ console.log(chalk34.dim('\u{1F4A1} Ch\u1EA1y "jai1 rules apply <name>" \u0111\u1EC3 \xE1p d\u1EE5ng preset'));
11635
11873
  } catch (error) {
11636
11874
  throw new Error(
11637
11875
  `L\u1ED7i khi t\u1EA3i presets: ${error instanceof Error ? error.message : String(error)}`
@@ -11641,22 +11879,22 @@ function createRulesListCommand() {
11641
11879
  }
11642
11880
 
11643
11881
  // src/commands/rules/init.ts
11644
- import { Command as Command63 } from "commander";
11882
+ import { Command as Command65 } from "commander";
11645
11883
  import { promises as fs22 } from "fs";
11646
- import { join as join12 } from "path";
11884
+ import { join as join13 } from "path";
11647
11885
  import { select as select4, confirm as confirm10 } from "@inquirer/prompts";
11648
11886
 
11649
11887
  // src/services/project-config.service.ts
11650
11888
  import { promises as fs21 } from "fs";
11651
- import { join as join11 } from "path";
11889
+ import { join as join12 } from "path";
11652
11890
  var ProjectConfigService = class {
11653
11891
  projectRoot;
11654
11892
  configDir;
11655
11893
  configPath;
11656
11894
  constructor(projectRoot = process.cwd()) {
11657
11895
  this.projectRoot = projectRoot;
11658
- this.configDir = join11(this.projectRoot, ".jai1");
11659
- this.configPath = join11(this.configDir, "project.json");
11896
+ this.configDir = join12(this.projectRoot, ".jai1");
11897
+ this.configPath = join12(this.configDir, "project.json");
11660
11898
  }
11661
11899
  /**
11662
11900
  * Check if config file exists
@@ -11766,7 +12004,7 @@ var ProjectConfigService = class {
11766
12004
 
11767
12005
  // src/commands/rules/init.ts
11768
12006
  function createRulesInitCommand() {
11769
- return new Command63("init").description("Apply rule preset to project").option("--preset <slug>", "Preset slug to apply").option("--output <format>", "Output format: cursor, agents-md, both (default: cursor)", "cursor").option("-y, --yes", "Skip confirmations").action(async (options) => {
12007
+ return new Command65("init").description("Apply rule preset to project").option("--preset <slug>", "Preset slug to apply").option("--output <format>", "Output format: cursor, agents-md, both (default: cursor)", "cursor").option("-y, --yes", "Skip confirmations").action(async (options) => {
11770
12008
  const configService = new ConfigService();
11771
12009
  const config = await configService.load();
11772
12010
  if (!config) {
@@ -11862,10 +12100,10 @@ function createRulesInitCommand() {
11862
12100
  });
11863
12101
  }
11864
12102
  async function applyCursorFormat(bundle) {
11865
- const rulesDir = join12(process.cwd(), ".cursor", "rules");
12103
+ const rulesDir = join13(process.cwd(), ".cursor", "rules");
11866
12104
  await fs22.mkdir(rulesDir, { recursive: true });
11867
12105
  for (const [filename, content] of Object.entries(bundle.files)) {
11868
- const filePath = join12(rulesDir, filename);
12106
+ const filePath = join13(rulesDir, filename);
11869
12107
  await fs22.writeFile(filePath, content, "utf-8");
11870
12108
  console.log(`\u2713 Created .cursor/rules/${filename}`);
11871
12109
  }
@@ -11898,9 +12136,9 @@ async function applyAgentsMdFormat(bundle) {
11898
12136
  }
11899
12137
 
11900
12138
  // src/commands/rules/apply.ts
11901
- import { Command as Command64 } from "commander";
12139
+ import { Command as Command66 } from "commander";
11902
12140
  import { promises as fs24 } from "fs";
11903
- import { join as join14 } from "path";
12141
+ import { join as join15 } from "path";
11904
12142
  import { search, confirm as confirm11, checkbox as checkbox5 } from "@inquirer/prompts";
11905
12143
 
11906
12144
  // src/services/rules-generator.service.ts
@@ -12192,7 +12430,7 @@ Follow all instructions and patterns defined in AGENTS.md above.
12192
12430
 
12193
12431
  // src/services/backup.service.ts
12194
12432
  import { promises as fs23 } from "fs";
12195
- import { join as join13, dirname } from "path";
12433
+ import { join as join14, dirname } from "path";
12196
12434
  var BackupService = class {
12197
12435
  backupDir = ".jai1/backups";
12198
12436
  /**
@@ -12200,7 +12438,7 @@ var BackupService = class {
12200
12438
  */
12201
12439
  async createBackup(ides, presetSlug) {
12202
12440
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
12203
- const backupPath = join13(this.backupDir, timestamp);
12441
+ const backupPath = join14(this.backupDir, timestamp);
12204
12442
  const backedUpFiles = [];
12205
12443
  let hasContent = false;
12206
12444
  for (const ideId of ides) {
@@ -12209,7 +12447,7 @@ var BackupService = class {
12209
12447
  console.warn(`Unknown IDE format: ${ideId}, skipping backup`);
12210
12448
  continue;
12211
12449
  }
12212
- const rulesPath = format.rulesPath === "." ? process.cwd() : join13(process.cwd(), format.rulesPath);
12450
+ const rulesPath = format.rulesPath === "." ? process.cwd() : join14(process.cwd(), format.rulesPath);
12213
12451
  try {
12214
12452
  const exists = await this.pathExists(rulesPath);
12215
12453
  if (!exists) {
@@ -12227,14 +12465,14 @@ var BackupService = class {
12227
12465
  const files = await fs23.readdir(rulesPath);
12228
12466
  for (const file of files) {
12229
12467
  if (file.endsWith(format.fileExtension)) {
12230
- const originalPath = join13(rulesPath, file);
12231
- const relativePath = join13(format.rulesPath, file);
12232
- const destPath = join13(backupPath, ideId, file);
12468
+ const originalPath = join14(rulesPath, file);
12469
+ const relativePath = join14(format.rulesPath, file);
12470
+ const destPath = join14(backupPath, ideId, file);
12233
12471
  await fs23.mkdir(dirname(destPath), { recursive: true });
12234
12472
  await fs23.copyFile(originalPath, destPath);
12235
12473
  backedUpFiles.push({
12236
12474
  originalPath: relativePath,
12237
- backupPath: join13(ideId, file),
12475
+ backupPath: join14(ideId, file),
12238
12476
  ide: ideId
12239
12477
  });
12240
12478
  hasContent = true;
@@ -12257,7 +12495,7 @@ var BackupService = class {
12257
12495
  };
12258
12496
  await fs23.mkdir(backupPath, { recursive: true });
12259
12497
  await fs23.writeFile(
12260
- join13(backupPath, "metadata.json"),
12498
+ join14(backupPath, "metadata.json"),
12261
12499
  JSON.stringify(metadata, null, 2),
12262
12500
  "utf-8"
12263
12501
  );
@@ -12267,18 +12505,18 @@ var BackupService = class {
12267
12505
  * Backup a single file (for AGENTS.md, GEMINI.md)
12268
12506
  */
12269
12507
  async backupSingleFile(filename, backupPath, ideId, backedUpFiles) {
12270
- const originalPath = join13(process.cwd(), filename);
12508
+ const originalPath = join14(process.cwd(), filename);
12271
12509
  try {
12272
12510
  const exists = await this.pathExists(originalPath);
12273
12511
  if (!exists) {
12274
12512
  return;
12275
12513
  }
12276
- const destPath = join13(backupPath, ideId, filename);
12514
+ const destPath = join14(backupPath, ideId, filename);
12277
12515
  await fs23.mkdir(dirname(destPath), { recursive: true });
12278
12516
  await fs23.copyFile(originalPath, destPath);
12279
12517
  backedUpFiles.push({
12280
12518
  originalPath: filename,
12281
- backupPath: join13(ideId, filename),
12519
+ backupPath: join14(ideId, filename),
12282
12520
  ide: ideId
12283
12521
  });
12284
12522
  } catch (error) {
@@ -12288,14 +12526,14 @@ var BackupService = class {
12288
12526
  * Restore files from a backup
12289
12527
  */
12290
12528
  async restoreBackup(backupPath) {
12291
- const metadataPath = join13(backupPath, "metadata.json");
12529
+ const metadataPath = join14(backupPath, "metadata.json");
12292
12530
  const metadataContent = await fs23.readFile(metadataPath, "utf-8");
12293
12531
  const metadata = JSON.parse(metadataContent);
12294
12532
  console.log(`
12295
12533
  Restoring backup from ${metadata.timestamp}...`);
12296
12534
  for (const file of metadata.files) {
12297
- const sourcePath = join13(backupPath, file.backupPath);
12298
- const destPath = join13(process.cwd(), file.originalPath);
12535
+ const sourcePath = join14(backupPath, file.backupPath);
12536
+ const destPath = join14(process.cwd(), file.originalPath);
12299
12537
  await fs23.mkdir(dirname(destPath), { recursive: true });
12300
12538
  await fs23.copyFile(sourcePath, destPath);
12301
12539
  console.log(`\u2713 Restored ${file.originalPath}`);
@@ -12307,7 +12545,7 @@ Restoring backup from ${metadata.timestamp}...`);
12307
12545
  */
12308
12546
  async listBackups() {
12309
12547
  try {
12310
- const backupDirPath = join13(process.cwd(), this.backupDir);
12548
+ const backupDirPath = join14(process.cwd(), this.backupDir);
12311
12549
  const exists = await this.pathExists(backupDirPath);
12312
12550
  if (!exists) {
12313
12551
  return [];
@@ -12316,7 +12554,7 @@ Restoring backup from ${metadata.timestamp}...`);
12316
12554
  const backups = [];
12317
12555
  for (const entry of entries) {
12318
12556
  if (entry.isDirectory()) {
12319
- const metadataPath = join13(backupDirPath, entry.name, "metadata.json");
12557
+ const metadataPath = join14(backupDirPath, entry.name, "metadata.json");
12320
12558
  try {
12321
12559
  const metadataContent = await fs23.readFile(metadataPath, "utf-8");
12322
12560
  const metadata = JSON.parse(metadataContent);
@@ -12335,7 +12573,7 @@ Restoring backup from ${metadata.timestamp}...`);
12335
12573
  * Delete a specific backup
12336
12574
  */
12337
12575
  async deleteBackup(timestamp) {
12338
- const backupPath = join13(process.cwd(), this.backupDir, timestamp);
12576
+ const backupPath = join14(process.cwd(), this.backupDir, timestamp);
12339
12577
  await this.deleteDirectory(backupPath);
12340
12578
  }
12341
12579
  /**
@@ -12384,7 +12622,7 @@ Restoring backup from ${metadata.timestamp}...`);
12384
12622
  }
12385
12623
  const entries = await fs23.readdir(path13, { withFileTypes: true });
12386
12624
  for (const entry of entries) {
12387
- const fullPath = join13(path13, entry.name);
12625
+ const fullPath = join14(path13, entry.name);
12388
12626
  if (entry.isDirectory()) {
12389
12627
  await this.deleteDirectory(fullPath);
12390
12628
  } else {
@@ -12399,20 +12637,20 @@ Restoring backup from ${metadata.timestamp}...`);
12399
12637
  * Get backup directory path
12400
12638
  */
12401
12639
  getBackupDir() {
12402
- return join13(process.cwd(), this.backupDir);
12640
+ return join14(process.cwd(), this.backupDir);
12403
12641
  }
12404
12642
  /**
12405
12643
  * Ensure backup directory exists
12406
12644
  */
12407
12645
  async ensureBackupDir() {
12408
- const backupDirPath = join13(process.cwd(), this.backupDir);
12646
+ const backupDirPath = join14(process.cwd(), this.backupDir);
12409
12647
  await fs23.mkdir(backupDirPath, { recursive: true });
12410
12648
  }
12411
12649
  };
12412
12650
 
12413
12651
  // src/commands/rules/apply.ts
12414
12652
  function createRulesApplyCommand() {
12415
- return new Command64("apply").description("Apply rule preset to project with multi-IDE support").argument("[preset]", "Preset slug to apply (optional)").option("--ides <ides>", 'Comma-separated list of IDE formats (cursor,windsurf,antigravity,claude,agentsmd,gemini) or "all"').option("--skip-backup", "Skip backup creation").option("-y, --yes", "Skip all confirmations (auto mode)").action(async (presetSlug, options) => {
12653
+ return new Command66("apply").description("Apply rule preset to project with multi-IDE support").argument("[preset]", "Preset slug to apply (optional)").option("--ides <ides>", 'Comma-separated list of IDE formats (cursor,windsurf,antigravity,claude,agentsmd,gemini) or "all"').option("--skip-backup", "Skip backup creation").option("-y, --yes", "Skip all confirmations (auto mode)").action(async (presetSlug, options) => {
12416
12654
  const configService = new ConfigService();
12417
12655
  const config = await configService.load();
12418
12656
  if (!config) {
@@ -12578,20 +12816,20 @@ function createRulesApplyCommand() {
12578
12816
  }
12579
12817
  }
12580
12818
  console.log("\n\u{1F4DD} Applying preset...\n");
12581
- const rulePresetDir = join14(process.cwd(), ".jai1", "rule-preset");
12819
+ const rulePresetDir = join15(process.cwd(), ".jai1", "rule-preset");
12582
12820
  try {
12583
12821
  await fs24.rm(rulePresetDir, { recursive: true, force: true });
12584
12822
  } catch {
12585
12823
  }
12586
12824
  await fs24.mkdir(rulePresetDir, { recursive: true });
12587
12825
  await fs24.writeFile(
12588
- join14(rulePresetDir, "preset.json"),
12826
+ join15(rulePresetDir, "preset.json"),
12589
12827
  JSON.stringify(bundle.preset, null, 2),
12590
12828
  "utf-8"
12591
12829
  );
12592
12830
  for (const [filename, content] of Object.entries(bundle.files)) {
12593
- const filePath = join14(rulePresetDir, filename);
12594
- await fs24.mkdir(join14(filePath, ".."), { recursive: true });
12831
+ const filePath = join15(rulePresetDir, filename);
12832
+ await fs24.mkdir(join15(filePath, ".."), { recursive: true });
12595
12833
  await fs24.writeFile(filePath, content, "utf-8");
12596
12834
  }
12597
12835
  console.log(`\u2713 Saved preset to .jai1/rule-preset/`);
@@ -12600,8 +12838,8 @@ function createRulesApplyCommand() {
12600
12838
  try {
12601
12839
  const files = generatorService.generateForIde(bundle, ideId);
12602
12840
  for (const file of files) {
12603
- const fullPath = join14(process.cwd(), file.path);
12604
- await fs24.mkdir(join14(fullPath, ".."), { recursive: true });
12841
+ const fullPath = join15(process.cwd(), file.path);
12842
+ await fs24.mkdir(join15(fullPath, ".."), { recursive: true });
12605
12843
  await fs24.writeFile(fullPath, file.content, "utf-8");
12606
12844
  console.log(`\u2713 [${ideId}] ${file.path}`);
12607
12845
  allGeneratedFiles.push({
@@ -12707,11 +12945,11 @@ function createRulesApplyCommand() {
12707
12945
  }
12708
12946
 
12709
12947
  // src/commands/rules/restore.ts
12710
- import { Command as Command65 } from "commander";
12711
- import { join as join15 } from "path";
12948
+ import { Command as Command67 } from "commander";
12949
+ import { join as join16 } from "path";
12712
12950
  import { select as select5, confirm as confirm12 } from "@inquirer/prompts";
12713
12951
  function createRulesRestoreCommand() {
12714
- return new Command65("restore").description("Restore rules from a backup").option("--latest", "Restore the most recent backup").option("-y, --yes", "Skip confirmation").action(async (options) => {
12952
+ return new Command67("restore").description("Restore rules from a backup").option("--latest", "Restore the most recent backup").option("-y, --yes", "Skip confirmation").action(async (options) => {
12715
12953
  const backupService = new BackupService();
12716
12954
  const backups = await backupService.listBackups();
12717
12955
  if (backups.length === 0) {
@@ -12755,7 +12993,7 @@ function createRulesRestoreCommand() {
12755
12993
  }
12756
12994
  console.log("\n\u{1F504} Restoring backup...\n");
12757
12995
  try {
12758
- const backupPath = join15(backupService.getBackupDir(), selectedBackup.timestamp);
12996
+ const backupPath = join16(backupService.getBackupDir(), selectedBackup.timestamp);
12759
12997
  await backupService.restoreBackup(backupPath);
12760
12998
  console.log("\n\u2705 Backup restored successfully!\n");
12761
12999
  console.log("\u{1F4A1} Tip: Your IDE may need to be restarted to pick up the changes.");
@@ -12780,14 +13018,14 @@ function formatTimestamp(timestamp) {
12780
13018
  }
12781
13019
 
12782
13020
  // src/commands/rules/sync.ts
12783
- import { Command as Command66 } from "commander";
13021
+ import { Command as Command68 } from "commander";
12784
13022
  import { promises as fs25 } from "fs";
12785
- import { join as join16 } from "path";
13023
+ import { join as join17 } from "path";
12786
13024
  import { checkbox as checkbox6, confirm as confirm13, Separator } from "@inquirer/prompts";
12787
13025
  function createRulesSyncCommand() {
12788
- return new Command66("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) => {
12789
- const rulePresetDir = join16(process.cwd(), ".jai1", "rule-preset");
12790
- const presetJsonPath = join16(rulePresetDir, "preset.json");
13026
+ return new Command68("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) => {
13027
+ const rulePresetDir = join17(process.cwd(), ".jai1", "rule-preset");
13028
+ const presetJsonPath = join17(rulePresetDir, "preset.json");
12791
13029
  let presetExists = false;
12792
13030
  let presetData = null;
12793
13031
  try {
@@ -12924,7 +13162,7 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
12924
13162
  const files = await fs25.readdir(rulePresetDir);
12925
13163
  for (const file of files) {
12926
13164
  if (file.endsWith(".mdc") || file.endsWith(".md")) {
12927
- const filePath = join16(rulePresetDir, file);
13165
+ const filePath = join17(rulePresetDir, file);
12928
13166
  const content = await fs25.readFile(filePath, "utf-8");
12929
13167
  bundle.files[file] = content;
12930
13168
  }
@@ -12939,8 +13177,8 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
12939
13177
  }
12940
13178
  const files2 = generatorService.generateForIde(bundle, ideId);
12941
13179
  for (const file of files2) {
12942
- const fullPath = join16(process.cwd(), file.path);
12943
- await fs25.mkdir(join16(fullPath, ".."), { recursive: true });
13180
+ const fullPath = join17(process.cwd(), file.path);
13181
+ await fs25.mkdir(join17(fullPath, ".."), { recursive: true });
12944
13182
  await fs25.writeFile(fullPath, file.content, "utf-8");
12945
13183
  }
12946
13184
  console.log(`\u2713 ${format.name} - ${files2.length} files regenerated`);
@@ -13003,11 +13241,11 @@ function buildIdeChoices(currentIdes, detected, suggestions) {
13003
13241
  }
13004
13242
 
13005
13243
  // src/commands/rules/info.ts
13006
- import { Command as Command67 } from "commander";
13244
+ import { Command as Command69 } from "commander";
13007
13245
  import { promises as fs26 } from "fs";
13008
- import { join as join17 } from "path";
13246
+ import { join as join18 } from "path";
13009
13247
  function createRulesInfoCommand() {
13010
- return new Command67("info").description("Show current preset information").option("--json", "Output as JSON").action(async (options) => {
13248
+ return new Command69("info").description("Show current preset information").option("--json", "Output as JSON").action(async (options) => {
13011
13249
  const projectConfigService = new ProjectConfigService();
13012
13250
  const rulesConfig = await projectConfigService.loadRules();
13013
13251
  if (!rulesConfig) {
@@ -13020,8 +13258,8 @@ function createRulesInfoCommand() {
13020
13258
  return;
13021
13259
  }
13022
13260
  console.log("\u{1F4CB} Current Preset Information\n");
13023
- const rulePresetDir = join17(process.cwd(), ".jai1", "rule-preset");
13024
- const presetJsonPath = join17(rulePresetDir, "preset.json");
13261
+ const rulePresetDir = join18(process.cwd(), ".jai1", "rule-preset");
13262
+ const presetJsonPath = join18(rulePresetDir, "preset.json");
13025
13263
  let presetMetadata = null;
13026
13264
  let presetFiles = [];
13027
13265
  try {
@@ -13088,7 +13326,7 @@ Available Backups (${rulesConfig.backups.length}):`);
13088
13326
  }
13089
13327
  async function checkPathExists(path13) {
13090
13328
  try {
13091
- await fs26.access(join17(process.cwd(), path13));
13329
+ await fs26.access(join18(process.cwd(), path13));
13092
13330
  return true;
13093
13331
  } catch {
13094
13332
  return false;
@@ -13110,26 +13348,26 @@ async function checkIdeFilesExist(ideId, format) {
13110
13348
 
13111
13349
  // src/commands/rules/index.ts
13112
13350
  function showRulesHelp() {
13113
- console.log(chalk33.bold.cyan("\u{1F4CB} jai1 rules") + chalk33.dim(" - Qu\u1EA3n l\xFD rule presets cho AI agents"));
13351
+ console.log(chalk35.bold.cyan("\u{1F4CB} jai1 rules") + chalk35.dim(" - Qu\u1EA3n l\xFD rule presets cho AI agents"));
13114
13352
  console.log();
13115
- console.log(chalk33.bold("C\xE1c l\u1EC7nh:"));
13116
- console.log(` ${chalk33.cyan("list")} Li\u1EC7t k\xEA c\xE1c presets c\xF3 s\u1EB5n`);
13117
- console.log(` ${chalk33.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t preset`);
13118
- console.log(` ${chalk33.cyan("init")} Kh\u1EDFi t\u1EA1o rules t\u1EEB preset`);
13119
- console.log(` ${chalk33.cyan("apply")} \xC1p d\u1EE5ng preset v\xE0o project`);
13120
- console.log(` ${chalk33.cyan("sync")} \u0110\u1ED3ng b\u1ED9 rules sang c\xE1c \u0111\u1ECBnh d\u1EA1ng IDE`);
13121
- console.log(` ${chalk33.cyan("restore")} Kh\xF4i ph\u1EE5c rules t\u1EEB backup`);
13353
+ console.log(chalk35.bold("C\xE1c l\u1EC7nh:"));
13354
+ console.log(` ${chalk35.cyan("list")} Li\u1EC7t k\xEA c\xE1c presets c\xF3 s\u1EB5n`);
13355
+ console.log(` ${chalk35.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t preset`);
13356
+ console.log(` ${chalk35.cyan("init")} Kh\u1EDFi t\u1EA1o rules t\u1EEB preset`);
13357
+ console.log(` ${chalk35.cyan("apply")} \xC1p d\u1EE5ng preset v\xE0o project`);
13358
+ console.log(` ${chalk35.cyan("sync")} \u0110\u1ED3ng b\u1ED9 rules sang c\xE1c \u0111\u1ECBnh d\u1EA1ng IDE`);
13359
+ console.log(` ${chalk35.cyan("restore")} Kh\xF4i ph\u1EE5c rules t\u1EEB backup`);
13122
13360
  console.log();
13123
- console.log(chalk33.bold("V\xED d\u1EE5:"));
13124
- console.log(chalk33.dim(" $ jai1 rules list"));
13125
- console.log(chalk33.dim(" $ jai1 rules info react-typescript"));
13126
- console.log(chalk33.dim(" $ jai1 rules init --preset=react-typescript"));
13127
- console.log(chalk33.dim(" $ jai1 rules apply react-typescript"));
13361
+ console.log(chalk35.bold("V\xED d\u1EE5:"));
13362
+ console.log(chalk35.dim(" $ jai1 rules list"));
13363
+ console.log(chalk35.dim(" $ jai1 rules info react-typescript"));
13364
+ console.log(chalk35.dim(" $ jai1 rules init --preset=react-typescript"));
13365
+ console.log(chalk35.dim(" $ jai1 rules apply react-typescript"));
13128
13366
  console.log();
13129
- console.log(chalk33.dim('Ch\u1EA1y "jai1 rules <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
13367
+ console.log(chalk35.dim('Ch\u1EA1y "jai1 rules <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
13130
13368
  }
13131
13369
  function createRulesCommand() {
13132
- const rulesCommand = new Command68("rules").description("Manage rule presets for AI agents").action(() => {
13370
+ const rulesCommand = new Command70("rules").description("Manage rule presets for AI agents").action(() => {
13133
13371
  showRulesHelp();
13134
13372
  });
13135
13373
  rulesCommand.addCommand(createRulesListCommand());
@@ -13142,17 +13380,17 @@ function createRulesCommand() {
13142
13380
  }
13143
13381
 
13144
13382
  // src/commands/skills/index.ts
13145
- import { Command as Command74 } from "commander";
13146
- import chalk39 from "chalk";
13383
+ import { Command as Command76 } from "commander";
13384
+ import chalk41 from "chalk";
13147
13385
 
13148
13386
  // src/commands/skills/find.ts
13149
- import { Command as Command69 } from "commander";
13150
- import chalk34 from "chalk";
13387
+ import { Command as Command71 } from "commander";
13388
+ import chalk36 from "chalk";
13151
13389
  import Table8 from "cli-table3";
13152
13390
 
13153
13391
  // src/services/skills.service.ts
13154
13392
  import { promises as fs27 } from "fs";
13155
- import { join as join18 } from "path";
13393
+ import { join as join19 } from "path";
13156
13394
  import { execFile } from "child_process";
13157
13395
  import { promisify } from "util";
13158
13396
  var execFileAsync = promisify(execFile);
@@ -13188,14 +13426,14 @@ var SkillsService = class {
13188
13426
  * List locally installed skills from .jai1/skills/
13189
13427
  */
13190
13428
  async listLocal(projectRoot) {
13191
- const skillsDir = join18(projectRoot, ".jai1", "skills");
13429
+ const skillsDir = join19(projectRoot, ".jai1", "skills");
13192
13430
  const skills = [];
13193
13431
  try {
13194
13432
  const entries = await fs27.readdir(skillsDir, { withFileTypes: true });
13195
13433
  for (const entry of entries) {
13196
13434
  if (!entry.isDirectory()) continue;
13197
- const skillPath = join18(skillsDir, entry.name);
13198
- const skillMd = join18(skillPath, "SKILL.md");
13435
+ const skillPath = join19(skillsDir, entry.name);
13436
+ const skillMd = join19(skillPath, "SKILL.md");
13199
13437
  try {
13200
13438
  await fs27.access(skillMd);
13201
13439
  } catch {
@@ -13220,8 +13458,8 @@ var SkillsService = class {
13220
13458
  * Get detailed info for a single skill
13221
13459
  */
13222
13460
  async getSkillInfo(projectRoot, skillName) {
13223
- const skillPath = join18(projectRoot, ".jai1", "skills", skillName);
13224
- const skillMd = join18(skillPath, "SKILL.md");
13461
+ const skillPath = join19(projectRoot, ".jai1", "skills", skillName);
13462
+ const skillMd = join19(skillPath, "SKILL.md");
13225
13463
  try {
13226
13464
  await fs27.access(skillMd);
13227
13465
  } catch {
@@ -13296,22 +13534,22 @@ var SkillsService = class {
13296
13534
  * After npx skills add, copy newly installed skills from .agents/skills/ into .jai1/skills/
13297
13535
  */
13298
13536
  async copySkillshResultsToJai1(projectRoot, specificSkill) {
13299
- const jai1SkillsDir = join18(projectRoot, ".jai1", "skills");
13537
+ const jai1SkillsDir = join19(projectRoot, ".jai1", "skills");
13300
13538
  await fs27.mkdir(jai1SkillsDir, { recursive: true });
13301
- const universalDir = join18(projectRoot, ".agents", "skills");
13539
+ const universalDir = join19(projectRoot, ".agents", "skills");
13302
13540
  try {
13303
13541
  const entries = await fs27.readdir(universalDir, { withFileTypes: true });
13304
13542
  for (const entry of entries) {
13305
13543
  if (!entry.isDirectory()) continue;
13306
13544
  if (specificSkill && entry.name !== specificSkill) continue;
13307
- const srcSkill = join18(universalDir, entry.name);
13308
- const skillMd = join18(srcSkill, "SKILL.md");
13545
+ const srcSkill = join19(universalDir, entry.name);
13546
+ const skillMd = join19(srcSkill, "SKILL.md");
13309
13547
  try {
13310
13548
  await fs27.access(skillMd);
13311
13549
  } catch {
13312
13550
  continue;
13313
13551
  }
13314
- const targetSkill = join18(jai1SkillsDir, entry.name);
13552
+ const targetSkill = join19(jai1SkillsDir, entry.name);
13315
13553
  try {
13316
13554
  await fs27.access(targetSkill);
13317
13555
  } catch {
@@ -13350,7 +13588,7 @@ var SkillsService = class {
13350
13588
  const target = this.getIDETarget(ide);
13351
13589
  if (!target) continue;
13352
13590
  for (const skill of skillsToSync) {
13353
- const targetPath = join18(projectRoot, target.skillsPath, skill.slug);
13591
+ const targetPath = join19(projectRoot, target.skillsPath, skill.slug);
13354
13592
  try {
13355
13593
  let status = "created";
13356
13594
  try {
@@ -13414,7 +13652,7 @@ var SkillsService = class {
13414
13652
  const entries = await fs27.readdir(dirPath, { withFileTypes: true });
13415
13653
  for (const entry of entries) {
13416
13654
  if (entry.isDirectory()) {
13417
- count += await this.countFiles(join18(dirPath, entry.name));
13655
+ count += await this.countFiles(join19(dirPath, entry.name));
13418
13656
  } else {
13419
13657
  count++;
13420
13658
  }
@@ -13427,8 +13665,8 @@ var SkillsService = class {
13427
13665
  async copyDir(source, target) {
13428
13666
  const entries = await fs27.readdir(source, { withFileTypes: true });
13429
13667
  for (const entry of entries) {
13430
- const srcPath = join18(source, entry.name);
13431
- const tgtPath = join18(target, entry.name);
13668
+ const srcPath = join19(source, entry.name);
13669
+ const tgtPath = join19(target, entry.name);
13432
13670
  if (entry.isDirectory()) {
13433
13671
  await fs27.mkdir(tgtPath, { recursive: true });
13434
13672
  await this.copyDir(srcPath, tgtPath);
@@ -13441,7 +13679,7 @@ var SkillsService = class {
13441
13679
 
13442
13680
  // src/commands/skills/find.ts
13443
13681
  function createSkillsFindCommand() {
13444
- return new Command69("find").description("Search for skills on server or npm").argument("<query>", "Search query").option("--skillsh", "Search on npm skills registry instead of Jai1 server").option("--all", "Search on both Jai1 server and npm").action(async (query, options) => {
13682
+ return new Command71("find").description("Search for skills on server or npm").argument("<query>", "Search query").option("--skillsh", "Search on npm skills registry instead of Jai1 server").option("--all", "Search on both Jai1 server and npm").action(async (query, options) => {
13445
13683
  const searchNpm = options.skillsh || options.all;
13446
13684
  const searchServer = !options.skillsh || options.all;
13447
13685
  if (searchServer) {
@@ -13450,19 +13688,19 @@ function createSkillsFindCommand() {
13450
13688
  if (!config) {
13451
13689
  throw new ValidationError('Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.');
13452
13690
  }
13453
- console.log(chalk34.cyan("\u{1F50D} \u0110ang t\xECm ki\u1EBFm tr\xEAn Jai1 server..."));
13691
+ console.log(chalk36.cyan("\u{1F50D} \u0110ang t\xECm ki\u1EBFm tr\xEAn Jai1 server..."));
13454
13692
  console.log();
13455
13693
  const skillsService = new SkillsService();
13456
13694
  const results = await skillsService.searchFromServer(config, query);
13457
13695
  if (results.length === 0) {
13458
- console.log(chalk34.yellow("Kh\xF4ng t\xECm th\u1EA5y skills n\xE0o tr\xEAn server."));
13696
+ console.log(chalk36.yellow("Kh\xF4ng t\xECm th\u1EA5y skills n\xE0o tr\xEAn server."));
13459
13697
  } else {
13460
13698
  const table = new Table8({
13461
13699
  head: [
13462
- chalk34.cyan("T\xEAn"),
13463
- chalk34.cyan("M\xF4 t\u1EA3"),
13464
- chalk34.cyan("Version"),
13465
- chalk34.cyan("Downloads")
13700
+ chalk36.cyan("T\xEAn"),
13701
+ chalk36.cyan("M\xF4 t\u1EA3"),
13702
+ chalk36.cyan("Version"),
13703
+ chalk36.cyan("Downloads")
13466
13704
  ],
13467
13705
  style: { head: [], border: ["gray"] },
13468
13706
  colWidths: [25, 40, 10, 12]
@@ -13470,70 +13708,70 @@ function createSkillsFindCommand() {
13470
13708
  for (const skill of results) {
13471
13709
  const name = skill.filepath.replace("skills/", "");
13472
13710
  table.push([
13473
- chalk34.white(name),
13474
- chalk34.dim((skill.description || "").slice(0, 38)),
13475
- chalk34.green(skill.version || "-"),
13476
- chalk34.dim(String(skill.downloads || 0))
13711
+ chalk36.white(name),
13712
+ chalk36.dim((skill.description || "").slice(0, 38)),
13713
+ chalk36.green(skill.version || "-"),
13714
+ chalk36.dim(String(skill.downloads || 0))
13477
13715
  ]);
13478
13716
  }
13479
- console.log(chalk34.bold(`\u{1F4E6} Jai1 Server (${results.length} k\u1EBFt qu\u1EA3)`));
13717
+ console.log(chalk36.bold(`\u{1F4E6} Jai1 Server (${results.length} k\u1EBFt qu\u1EA3)`));
13480
13718
  console.log(table.toString());
13481
13719
  console.log();
13482
13720
  }
13483
13721
  }
13484
13722
  if (searchNpm) {
13485
- console.log(chalk34.cyan("\u{1F50D} \u0110ang t\xECm ki\u1EBFm tr\xEAn npm skills..."));
13723
+ console.log(chalk36.cyan("\u{1F50D} \u0110ang t\xECm ki\u1EBFm tr\xEAn npm skills..."));
13486
13724
  console.log();
13487
13725
  const skillsService = new SkillsService();
13488
13726
  try {
13489
13727
  const output = await skillsService.npmSkillsFind(query);
13490
- console.log(chalk34.bold("\u{1F310} npm Skills Registry"));
13728
+ console.log(chalk36.bold("\u{1F310} npm Skills Registry"));
13491
13729
  console.log(output);
13492
13730
  } catch (error) {
13493
- console.log(chalk34.yellow(
13731
+ console.log(chalk36.yellow(
13494
13732
  `Kh\xF4ng th\u1EC3 t\xECm ki\u1EBFm tr\xEAn npm: ${error instanceof Error ? error.message : String(error)}`
13495
13733
  ));
13496
13734
  }
13497
13735
  }
13498
13736
  if (searchServer && !searchNpm) {
13499
- console.log(chalk34.dim('\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t t\u1EEB server'));
13737
+ console.log(chalk36.dim('\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t t\u1EEB server'));
13500
13738
  } else if (searchNpm && !searchServer) {
13501
- console.log(chalk34.dim('\u{1F4A1} D\xF9ng "j skills add <owner/repo@skill> --skillsh" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13739
+ console.log(chalk36.dim('\u{1F4A1} D\xF9ng "j skills add <owner/repo@skill> --skillsh" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13502
13740
  } else {
13503
- console.log(chalk34.dim("\u{1F4A1} C\xE0i \u0111\u1EB7t:"));
13504
- console.log(chalk34.dim(" Server: j skills add <t\xEAn>"));
13505
- console.log(chalk34.dim(" Skills.sh: j skills add <owner/repo@skill> --skillsh"));
13741
+ console.log(chalk36.dim("\u{1F4A1} C\xE0i \u0111\u1EB7t:"));
13742
+ console.log(chalk36.dim(" Server: j skills add <t\xEAn>"));
13743
+ console.log(chalk36.dim(" Skills.sh: j skills add <owner/repo@skill> --skillsh"));
13506
13744
  }
13507
13745
  });
13508
13746
  }
13509
13747
 
13510
13748
  // src/commands/skills/add.ts
13511
- import { Command as Command70 } from "commander";
13512
- import { join as join19 } from "path";
13513
- import chalk35 from "chalk";
13749
+ import { Command as Command72 } from "commander";
13750
+ import { join as join20 } from "path";
13751
+ import chalk37 from "chalk";
13514
13752
  import { checkbox as checkbox7 } from "@inquirer/prompts";
13515
13753
  function createSkillsAddCommand() {
13516
- return new Command70("add").description("Install a skill to .jai1/skills/").argument("<name>", "Skill name or source (npm: GitHub shorthand, URL)").option("--skillsh", "Install from npm skills registry instead of Jai1 server").option("--sync", "Auto-sync to IDE(s) after install").option("--ides <ides...>", "Target IDEs for sync (cursor, windsurf, antigravity, claudecode, opencode)").option("--all", "Sync to all available IDEs").option("-y, --yes", "Headless mode (skip all prompts)").action(async (name, options) => {
13754
+ return new Command72("add").description("Install a skill to .jai1/skills/").argument("<name>", "Skill name or source (npm: GitHub shorthand, URL)").option("--skillsh", "Install from npm skills registry instead of Jai1 server").option("--sync", "Auto-sync to IDE(s) after install").option("--ides <ides...>", "Target IDEs for sync (cursor, windsurf, antigravity, claudecode, opencode)").option("--all", "Sync to all available IDEs").option("-y, --yes", "Headless mode (skip all prompts)").action(async (name, options) => {
13517
13755
  const skillsService = new SkillsService();
13518
13756
  const projectRoot = process.cwd();
13519
13757
  const headless = options.yes === true;
13520
13758
  if (options.skillsh) {
13521
- console.log(chalk35.cyan(`\u{1F310} \u0110ang c\xE0i \u0111\u1EB7t skill t\u1EEB npm: ${name}...`));
13759
+ console.log(chalk37.cyan(`\u{1F310} \u0110ang c\xE0i \u0111\u1EB7t skill t\u1EEB npm: ${name}...`));
13522
13760
  console.log();
13523
13761
  const output = await skillsService.npmSkillsAdd(name, projectRoot);
13524
13762
  console.log(output);
13525
- console.log(chalk35.green("\u2705 C\xE0i \u0111\u1EB7t t\u1EEB npm th\xE0nh c\xF4ng!"));
13763
+ console.log(chalk37.green("\u2705 C\xE0i \u0111\u1EB7t t\u1EEB npm th\xE0nh c\xF4ng!"));
13526
13764
  } else {
13527
13765
  const configService = new ConfigService();
13528
13766
  const config = await configService.load();
13529
13767
  if (!config) {
13530
13768
  throw new ValidationError('Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.');
13531
13769
  }
13532
- console.log(chalk35.cyan(`\u{1F4E6} \u0110ang c\xE0i \u0111\u1EB7t skill: ${name}...`));
13770
+ console.log(chalk37.cyan(`\u{1F4E6} \u0110ang c\xE0i \u0111\u1EB7t skill: ${name}...`));
13533
13771
  console.log();
13534
- const targetDir = join19(projectRoot, ".jai1");
13772
+ const targetDir = join20(projectRoot, ".jai1");
13535
13773
  await skillsService.installFromServer(config, name, targetDir);
13536
- console.log(chalk35.green(`\u2705 \u0110\xE3 c\xE0i \u0111\u1EB7t skill "${name}" v\xE0o .jai1/skills/${name}/`));
13774
+ console.log(chalk37.green(`\u2705 \u0110\xE3 c\xE0i \u0111\u1EB7t skill "${name}" v\xE0o .jai1/skills/${name}/`));
13537
13775
  }
13538
13776
  console.log();
13539
13777
  if (options.sync) {
@@ -13558,7 +13796,7 @@ function createSkillsAddCommand() {
13558
13796
  });
13559
13797
  }
13560
13798
  if (selectedIdes.length > 0) {
13561
- console.log(chalk35.cyan("\u{1F504} \u0110ang sync sang IDE(s)..."));
13799
+ console.log(chalk37.cyan("\u{1F504} \u0110ang sync sang IDE(s)..."));
13562
13800
  console.log();
13563
13801
  const slug = name.includes("/") ? name.split("/").pop() : name;
13564
13802
  const result = await skillsService.syncToIdes(
@@ -13571,24 +13809,24 @@ function createSkillsAddCommand() {
13571
13809
  }
13572
13810
  );
13573
13811
  console.log();
13574
- console.log(chalk35.green(`\u2705 Sync ho\xE0n t\u1EA5t! Created: ${result.created}, Updated: ${result.updated}`));
13812
+ console.log(chalk37.green(`\u2705 Sync ho\xE0n t\u1EA5t! Created: ${result.created}, Updated: ${result.updated}`));
13575
13813
  if (result.errors > 0) {
13576
- console.log(chalk35.yellow(`\u26A0\uFE0F Errors: ${result.errors}`));
13814
+ console.log(chalk37.yellow(`\u26A0\uFE0F Errors: ${result.errors}`));
13577
13815
  }
13578
13816
  }
13579
13817
  } else {
13580
- console.log(chalk35.dim('\u{1F4A1} Ch\u1EA1y "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
13581
- console.log(chalk35.dim(' ho\u1EB7c "j ide sync" \u0111\u1EC3 sync to\xE0n b\u1ED9 .jai1/'));
13818
+ console.log(chalk37.dim('\u{1F4A1} Ch\u1EA1y "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
13819
+ console.log(chalk37.dim(' ho\u1EB7c "j ide sync" \u0111\u1EC3 sync to\xE0n b\u1ED9 .jai1/'));
13582
13820
  }
13583
13821
  });
13584
13822
  }
13585
13823
 
13586
13824
  // src/commands/skills/list.ts
13587
- import { Command as Command71 } from "commander";
13588
- import chalk36 from "chalk";
13825
+ import { Command as Command73 } from "commander";
13826
+ import chalk38 from "chalk";
13589
13827
  import Table9 from "cli-table3";
13590
13828
  function createSkillsListCommand() {
13591
- return new Command71("list").description("List installed skills or available skills on server").option("--available", "List all skills available on Jai1 server").option("-s, --search <term>", "Search skills by name or description").action(async (options) => {
13829
+ return new Command73("list").description("List installed skills or available skills on server").option("--available", "List all skills available on Jai1 server").option("-s, --search <term>", "Search skills by name or description").action(async (options) => {
13592
13830
  const skillsService = new SkillsService();
13593
13831
  if (options.available) {
13594
13832
  const configService = new ConfigService();
@@ -13596,19 +13834,19 @@ function createSkillsListCommand() {
13596
13834
  if (!config) {
13597
13835
  throw new ValidationError('Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.');
13598
13836
  }
13599
- console.log(chalk36.cyan("\u{1F4E6} \u0110ang t\u1EA3i danh s\xE1ch skills t\u1EEB server..."));
13837
+ console.log(chalk38.cyan("\u{1F4E6} \u0110ang t\u1EA3i danh s\xE1ch skills t\u1EEB server..."));
13600
13838
  console.log();
13601
13839
  const results = await skillsService.searchFromServer(config, options.search);
13602
13840
  if (results.length === 0) {
13603
- console.log(chalk36.yellow("Kh\xF4ng t\xECm th\u1EA5y skills n\xE0o."));
13841
+ console.log(chalk38.yellow("Kh\xF4ng t\xECm th\u1EA5y skills n\xE0o."));
13604
13842
  return;
13605
13843
  }
13606
13844
  const table = new Table9({
13607
13845
  head: [
13608
- chalk36.cyan("T\xEAn"),
13609
- chalk36.cyan("M\xF4 t\u1EA3"),
13610
- chalk36.cyan("Version"),
13611
- chalk36.cyan("Downloads")
13846
+ chalk38.cyan("T\xEAn"),
13847
+ chalk38.cyan("M\xF4 t\u1EA3"),
13848
+ chalk38.cyan("Version"),
13849
+ chalk38.cyan("Downloads")
13612
13850
  ],
13613
13851
  style: { head: [], border: ["gray"] },
13614
13852
  colWidths: [28, 40, 10, 12]
@@ -13616,63 +13854,63 @@ function createSkillsListCommand() {
13616
13854
  for (const skill of results) {
13617
13855
  const name = skill.filepath.replace("skills/", "");
13618
13856
  table.push([
13619
- chalk36.white(name),
13620
- chalk36.dim((skill.description || "").slice(0, 38)),
13621
- chalk36.green(skill.version || "-"),
13622
- chalk36.dim(String(skill.downloads || 0))
13857
+ chalk38.white(name),
13858
+ chalk38.dim((skill.description || "").slice(0, 38)),
13859
+ chalk38.green(skill.version || "-"),
13860
+ chalk38.dim(String(skill.downloads || 0))
13623
13861
  ]);
13624
13862
  }
13625
13863
  console.log(table.toString());
13626
13864
  console.log();
13627
- console.log(chalk36.dim(`T\u1ED5ng c\u1ED9ng: ${results.length} skill(s)`));
13628
- console.log(chalk36.dim('\n\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13865
+ console.log(chalk38.dim(`T\u1ED5ng c\u1ED9ng: ${results.length} skill(s)`));
13866
+ console.log(chalk38.dim('\n\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13629
13867
  } else {
13630
13868
  const projectRoot = process.cwd();
13631
13869
  const skills = await skillsService.listLocal(projectRoot);
13632
13870
  if (skills.length === 0) {
13633
- console.log(chalk36.yellow("Ch\u01B0a c\xF3 skills n\xE0o \u0111\u01B0\u1EE3c c\xE0i \u0111\u1EB7t."));
13871
+ console.log(chalk38.yellow("Ch\u01B0a c\xF3 skills n\xE0o \u0111\u01B0\u1EE3c c\xE0i \u0111\u1EB7t."));
13634
13872
  console.log();
13635
- console.log(chalk36.dim('\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13636
- console.log(chalk36.dim(' ho\u1EB7c "j skills list --available" \u0111\u1EC3 xem skills c\xF3 s\u1EB5n'));
13873
+ console.log(chalk38.dim('\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13874
+ console.log(chalk38.dim(' ho\u1EB7c "j skills list --available" \u0111\u1EC3 xem skills c\xF3 s\u1EB5n'));
13637
13875
  return;
13638
13876
  }
13639
- console.log(chalk36.bold.cyan("\u{1F6E0} Skills \u0111\xE3 c\xE0i \u0111\u1EB7t"));
13877
+ console.log(chalk38.bold.cyan("\u{1F6E0} Skills \u0111\xE3 c\xE0i \u0111\u1EB7t"));
13640
13878
  console.log();
13641
13879
  const table = new Table9({
13642
13880
  head: [
13643
- chalk36.cyan("T\xEAn"),
13644
- chalk36.cyan("M\xF4 t\u1EA3"),
13645
- chalk36.cyan("Files")
13881
+ chalk38.cyan("T\xEAn"),
13882
+ chalk38.cyan("M\xF4 t\u1EA3"),
13883
+ chalk38.cyan("Files")
13646
13884
  ],
13647
13885
  style: { head: [], border: ["gray"] },
13648
13886
  colWidths: [28, 45, 8]
13649
13887
  });
13650
13888
  for (const skill of skills) {
13651
13889
  table.push([
13652
- chalk36.white(skill.slug),
13653
- chalk36.dim(skill.description.slice(0, 43)),
13654
- chalk36.dim(String(skill.fileCount))
13890
+ chalk38.white(skill.slug),
13891
+ chalk38.dim(skill.description.slice(0, 43)),
13892
+ chalk38.dim(String(skill.fileCount))
13655
13893
  ]);
13656
13894
  }
13657
13895
  console.log(table.toString());
13658
13896
  console.log();
13659
- console.log(chalk36.dim(`T\u1ED5ng c\u1ED9ng: ${skills.length} skill(s)`));
13660
- console.log(chalk36.dim('\n\u{1F4A1} D\xF9ng "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
13897
+ console.log(chalk38.dim(`T\u1ED5ng c\u1ED9ng: ${skills.length} skill(s)`));
13898
+ console.log(chalk38.dim('\n\u{1F4A1} D\xF9ng "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
13661
13899
  }
13662
13900
  });
13663
13901
  }
13664
13902
 
13665
13903
  // src/commands/skills/info.ts
13666
- import { Command as Command72 } from "commander";
13667
- import chalk37 from "chalk";
13904
+ import { Command as Command74 } from "commander";
13905
+ import chalk39 from "chalk";
13668
13906
  function createSkillsInfoCommand() {
13669
- return new Command72("info").description("Show detailed information about a skill").argument("<name>", "Skill name").option("--server", "Show info from Jai1 server instead of local").action(async (name, options) => {
13907
+ return new Command74("info").description("Show detailed information about a skill").argument("<name>", "Skill name").option("--server", "Show info from Jai1 server instead of local").action(async (name, options) => {
13670
13908
  const skillsService = new SkillsService();
13671
13909
  if (options.server) {
13672
13910
  const configService = new ConfigService();
13673
13911
  const config = await configService.load();
13674
13912
  if (!config) {
13675
- console.log(chalk37.red('\u274C Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.'));
13913
+ console.log(chalk39.red('\u274C Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.'));
13676
13914
  process.exit(1);
13677
13915
  }
13678
13916
  const filepath = name.startsWith("skills/") ? name : `skills/${name}`;
@@ -13681,7 +13919,7 @@ function createSkillsInfoCommand() {
13681
13919
  try {
13682
13920
  const component = await componentsService.get(config, filepath);
13683
13921
  console.log(`
13684
- \u{1F6E0} ${chalk37.bold(component.name || name)}
13922
+ \u{1F6E0} ${chalk39.bold(component.name || name)}
13685
13923
  `);
13686
13924
  console.log(`Filepath: ${component.filepath}`);
13687
13925
  console.log(`Version: ${component.version}`);
@@ -13694,47 +13932,47 @@ function createSkillsInfoCommand() {
13694
13932
  }
13695
13933
  console.log(`Type: ${component.contentType}`);
13696
13934
  console.log();
13697
- console.log(chalk37.dim('\u{1F4A1} D\xF9ng "j skills add ' + name + '" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13935
+ console.log(chalk39.dim('\u{1F4A1} D\xF9ng "j skills add ' + name + '" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13698
13936
  } catch (error) {
13699
- console.log(chalk37.red(`\u274C Kh\xF4ng t\xECm th\u1EA5y skill "${name}" tr\xEAn server.`));
13937
+ console.log(chalk39.red(`\u274C Kh\xF4ng t\xECm th\u1EA5y skill "${name}" tr\xEAn server.`));
13700
13938
  process.exit(1);
13701
13939
  }
13702
13940
  } else {
13703
13941
  const projectRoot = process.cwd();
13704
13942
  const skill = await skillsService.getSkillInfo(projectRoot, name);
13705
13943
  if (!skill) {
13706
- console.log(chalk37.red(`\u274C Skill "${name}" ch\u01B0a \u0111\u01B0\u1EE3c c\xE0i \u0111\u1EB7t.`));
13707
- console.log(chalk37.dim('\u{1F4A1} D\xF9ng "j skills info ' + name + ' --server" \u0111\u1EC3 xem tr\xEAn server'));
13708
- console.log(chalk37.dim(' ho\u1EB7c "j skills add ' + name + '" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13944
+ console.log(chalk39.red(`\u274C Skill "${name}" ch\u01B0a \u0111\u01B0\u1EE3c c\xE0i \u0111\u1EB7t.`));
13945
+ console.log(chalk39.dim('\u{1F4A1} D\xF9ng "j skills info ' + name + ' --server" \u0111\u1EC3 xem tr\xEAn server'));
13946
+ console.log(chalk39.dim(' ho\u1EB7c "j skills add ' + name + '" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13709
13947
  process.exit(1);
13710
13948
  }
13711
13949
  console.log(`
13712
- \u{1F6E0} ${chalk37.bold(skill.name)}
13950
+ \u{1F6E0} ${chalk39.bold(skill.name)}
13713
13951
  `);
13714
13952
  console.log(`Slug: ${skill.slug}`);
13715
- console.log(`Description: ${skill.description || chalk37.dim("(none)")}`);
13953
+ console.log(`Description: ${skill.description || chalk39.dim("(none)")}`);
13716
13954
  console.log(`Path: ${skill.path}`);
13717
13955
  console.log(`Files: ${skill.fileCount}`);
13718
13956
  console.log();
13719
- console.log(chalk37.dim('\u{1F4A1} D\xF9ng "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
13957
+ console.log(chalk39.dim('\u{1F4A1} D\xF9ng "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
13720
13958
  }
13721
13959
  });
13722
13960
  }
13723
13961
 
13724
13962
  // src/commands/skills/sync.ts
13725
- import { Command as Command73 } from "commander";
13726
- import chalk38 from "chalk";
13963
+ import { Command as Command75 } from "commander";
13964
+ import chalk40 from "chalk";
13727
13965
  import { confirm as confirm15, checkbox as checkbox8 } from "@inquirer/prompts";
13728
13966
  function createSkillsSyncCommand() {
13729
- return new Command73("sync").description("Sync skills from .jai1/skills/ to IDE directories").option("--ides <ides...>", "Target IDEs (cursor, windsurf, antigravity, claudecode, opencode)").option("--skills <skills...>", "Specific skill slugs to sync (default: all)").option("--all", "Select all available IDEs").option("--dry-run", "Preview changes without writing files").option("-y, --yes", "Headless mode (skip all prompts)").action(async (options) => {
13967
+ return new Command75("sync").description("Sync skills from .jai1/skills/ to IDE directories").option("--ides <ides...>", "Target IDEs (cursor, windsurf, antigravity, claudecode, opencode)").option("--skills <skills...>", "Specific skill slugs to sync (default: all)").option("--all", "Select all available IDEs").option("--dry-run", "Preview changes without writing files").option("-y, --yes", "Headless mode (skip all prompts)").action(async (options) => {
13730
13968
  const skillsService = new SkillsService();
13731
13969
  const projectRoot = process.cwd();
13732
13970
  const headless = options.yes === true;
13733
- console.log(chalk38.bold.cyan("\n\u{1F504} Sync skills sang IDE(s)\n"));
13971
+ console.log(chalk40.bold.cyan("\n\u{1F504} Sync skills sang IDE(s)\n"));
13734
13972
  const localSkills = await skillsService.listLocal(projectRoot);
13735
13973
  if (localSkills.length === 0) {
13736
- console.log(chalk38.yellow("\u26A0\uFE0F Kh\xF4ng c\xF3 skills n\xE0o trong .jai1/skills/"));
13737
- console.log(chalk38.dim('\u{1F4A1} Ch\u1EA1y "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t skills tr\u01B0\u1EDBc'));
13974
+ console.log(chalk40.yellow("\u26A0\uFE0F Kh\xF4ng c\xF3 skills n\xE0o trong .jai1/skills/"));
13975
+ console.log(chalk40.dim('\u{1F4A1} Ch\u1EA1y "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t skills tr\u01B0\u1EDBc'));
13738
13976
  process.exit(1);
13739
13977
  }
13740
13978
  console.log(`\u{1F4C1} T\xECm th\u1EA5y ${localSkills.length} skill(s) trong .jai1/skills/`);
@@ -13766,7 +14004,7 @@ function createSkillsSyncCommand() {
13766
14004
  theme: checkboxTheme
13767
14005
  });
13768
14006
  if (selectedIdes.length === 0) {
13769
- console.log(chalk38.yellow("\n\u26A0\uFE0F Ch\u01B0a ch\u1ECDn IDE n\xE0o!"));
14007
+ console.log(chalk40.yellow("\n\u26A0\uFE0F Ch\u01B0a ch\u1ECDn IDE n\xE0o!"));
13770
14008
  process.exit(0);
13771
14009
  }
13772
14010
  }
@@ -13781,7 +14019,7 @@ function createSkillsSyncCommand() {
13781
14019
  console.log(` Total: ${totalFiles} skill folder(s) s\u1EBD \u0111\u01B0\u1EE3c sync
13782
14020
  `);
13783
14021
  if (options.dryRun) {
13784
- console.log(chalk38.dim("\u{1F50D} DRY RUN - Kh\xF4ng c\xF3 file n\xE0o \u0111\u01B0\u1EE3c ghi\n"));
14022
+ console.log(chalk40.dim("\u{1F50D} DRY RUN - Kh\xF4ng c\xF3 file n\xE0o \u0111\u01B0\u1EE3c ghi\n"));
13785
14023
  return;
13786
14024
  }
13787
14025
  if (!headless) {
@@ -13790,25 +14028,25 @@ function createSkillsSyncCommand() {
13790
14028
  default: true
13791
14029
  });
13792
14030
  if (!confirmed) {
13793
- console.log(chalk38.yellow("\n\u274C \u0110\xE3 h\u1EE7y sync.\n"));
14031
+ console.log(chalk40.yellow("\n\u274C \u0110\xE3 h\u1EE7y sync.\n"));
13794
14032
  process.exit(0);
13795
14033
  }
13796
14034
  }
13797
- console.log(chalk38.cyan("\n\u{1F504} \u0110ang sync...\n"));
14035
+ console.log(chalk40.cyan("\n\u{1F504} \u0110ang sync...\n"));
13798
14036
  const result = await skillsService.syncToIdes(
13799
14037
  projectRoot,
13800
14038
  selectedIdes,
13801
14039
  selectedSlugs,
13802
14040
  (res) => {
13803
14041
  const icon = res.status === "created" ? "\u2713" : res.status === "updated" ? "\u21BB" : "\u2717";
13804
- const statusColor = res.status === "error" ? chalk38.red : chalk38.green;
13805
- console.log(` ${statusColor(icon)} ${res.ide}: ${res.skill} \u2192 ${chalk38.dim(res.path)}`);
14042
+ const statusColor = res.status === "error" ? chalk40.red : chalk40.green;
14043
+ console.log(` ${statusColor(icon)} ${res.ide}: ${res.skill} \u2192 ${chalk40.dim(res.path)}`);
13806
14044
  if (res.status === "error" && res.error) {
13807
- console.log(` ${chalk38.red("Error:")} ${res.error}`);
14045
+ console.log(` ${chalk40.red("Error:")} ${res.error}`);
13808
14046
  }
13809
14047
  }
13810
14048
  );
13811
- console.log(chalk38.green("\n\u2705 Sync ho\xE0n t\u1EA5t!\n"));
14049
+ console.log(chalk40.green("\n\u2705 Sync ho\xE0n t\u1EA5t!\n"));
13812
14050
  console.log(` Created: ${result.created}`);
13813
14051
  console.log(` Updated: ${result.updated}`);
13814
14052
  if (result.errors > 0) {
@@ -13821,27 +14059,27 @@ function createSkillsSyncCommand() {
13821
14059
  // src/commands/skills/index.ts
13822
14060
  function showSkillsHelp() {
13823
14061
  const cli = getCliName();
13824
- console.log(chalk39.bold.cyan("\u{1F6E0} " + cli + " skills") + chalk39.dim(" - Qu\u1EA3n l\xFD agent skills"));
14062
+ console.log(chalk41.bold.cyan("\u{1F6E0} " + cli + " skills") + chalk41.dim(" - Qu\u1EA3n l\xFD agent skills"));
13825
14063
  console.log();
13826
- console.log(chalk39.bold("C\xE1c l\u1EC7nh:"));
13827
- console.log(` ${chalk39.cyan("find")} T\xECm ki\u1EBFm skills tr\xEAn server ho\u1EB7c npm`);
13828
- console.log(` ${chalk39.cyan("add")} C\xE0i \u0111\u1EB7t skill v\xE0o .jai1/skills/`);
13829
- console.log(` ${chalk39.cyan("list")} Li\u1EC7t k\xEA skills \u0111\xE3 c\xE0i ho\u1EB7c c\xF3 s\u1EB5n`);
13830
- console.log(` ${chalk39.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t skill`);
13831
- console.log(` ${chalk39.cyan("sync")} \u0110\u1ED3ng b\u1ED9 skills sang c\xE1c IDE`);
14064
+ console.log(chalk41.bold("C\xE1c l\u1EC7nh:"));
14065
+ console.log(` ${chalk41.cyan("find")} T\xECm ki\u1EBFm skills tr\xEAn server ho\u1EB7c npm`);
14066
+ console.log(` ${chalk41.cyan("add")} C\xE0i \u0111\u1EB7t skill v\xE0o .jai1/skills/`);
14067
+ console.log(` ${chalk41.cyan("list")} Li\u1EC7t k\xEA skills \u0111\xE3 c\xE0i ho\u1EB7c c\xF3 s\u1EB5n`);
14068
+ console.log(` ${chalk41.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t skill`);
14069
+ console.log(` ${chalk41.cyan("sync")} \u0110\u1ED3ng b\u1ED9 skills sang c\xE1c IDE`);
13832
14070
  console.log();
13833
- console.log(chalk39.bold("V\xED d\u1EE5:"));
13834
- console.log(chalk39.dim(` $ ${cli} skills find audit`));
13835
- console.log(chalk39.dim(` $ ${cli} skills find "react" --skillsh`));
13836
- console.log(chalk39.dim(` $ ${cli} skills add brainstorming`));
13837
- console.log(chalk39.dim(` $ ${cli} skills add vercel/next-skills --skillsh`));
13838
- console.log(chalk39.dim(` $ ${cli} skills list`));
13839
- console.log(chalk39.dim(` $ ${cli} skills sync --all -y`));
14071
+ console.log(chalk41.bold("V\xED d\u1EE5:"));
14072
+ console.log(chalk41.dim(` $ ${cli} skills find audit`));
14073
+ console.log(chalk41.dim(` $ ${cli} skills find "react" --skillsh`));
14074
+ console.log(chalk41.dim(` $ ${cli} skills add brainstorming`));
14075
+ console.log(chalk41.dim(` $ ${cli} skills add vercel/next-skills --skillsh`));
14076
+ console.log(chalk41.dim(` $ ${cli} skills list`));
14077
+ console.log(chalk41.dim(` $ ${cli} skills sync --all -y`));
13840
14078
  console.log();
13841
- console.log(chalk39.dim(`Ch\u1EA1y "${cli} skills <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt`));
14079
+ console.log(chalk41.dim(`Ch\u1EA1y "${cli} skills <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt`));
13842
14080
  }
13843
14081
  function createSkillsCommand() {
13844
- const cmd = new Command74("skills").alias("s").description("Manage agent skills (search, install, sync to IDEs)").action(() => {
14082
+ const cmd = new Command76("skills").alias("s").description("Manage agent skills (search, install, sync to IDEs)").action(() => {
13845
14083
  showSkillsHelp();
13846
14084
  });
13847
14085
  cmd.addCommand(createSkillsFindCommand());
@@ -13853,9 +14091,9 @@ function createSkillsCommand() {
13853
14091
  }
13854
14092
 
13855
14093
  // src/commands/upgrade.ts
13856
- import { Command as Command75 } from "commander";
14094
+ import { Command as Command77 } from "commander";
13857
14095
  import { confirm as confirm16 } from "@inquirer/prompts";
13858
- import { execSync as execSync4 } from "child_process";
14096
+ import { execSync as execSync5 } from "child_process";
13859
14097
  var colors2 = {
13860
14098
  yellow: "\x1B[33m",
13861
14099
  green: "\x1B[32m",
@@ -13865,7 +14103,7 @@ var colors2 = {
13865
14103
  bold: "\x1B[1m"
13866
14104
  };
13867
14105
  function createUpgradeCommand() {
13868
- return new Command75("upgrade").description("Upgrade CLI client to the latest version").option("--check", "Only check for updates without installing").option("--force", "Force upgrade without confirmation").action(async (options) => {
14106
+ return new Command77("upgrade").description("Upgrade CLI client to the latest version").option("--check", "Only check for updates without installing").option("--force", "Force upgrade without confirmation").action(async (options) => {
13869
14107
  await handleUpgrade(options);
13870
14108
  });
13871
14109
  }
@@ -13926,7 +14164,7 @@ ${colors2.cyan}\u{1F4E5} Installing latest version...${colors2.reset}
13926
14164
  const packageManager2 = detectPackageManager();
13927
14165
  const installCommand = getInstallCommand(packageManager2);
13928
14166
  console.log(`${colors2.cyan}Using ${packageManager2}...${colors2.reset}`);
13929
- execSync4(installCommand, {
14167
+ execSync5(installCommand, {
13930
14168
  stdio: "inherit",
13931
14169
  env: { ...process.env, FORCE_COLOR: "1" }
13932
14170
  });
@@ -13975,7 +14213,7 @@ function isNewerVersion2(remote, local) {
13975
14213
  }
13976
14214
  function detectPackageManager() {
13977
14215
  try {
13978
- const jai1Path = execSync4("which j || which jai1 || where j || where jai1", {
14216
+ const jai1Path = execSync5("which j || which jai1 || where j || where jai1", {
13979
14217
  encoding: "utf-8",
13980
14218
  stdio: ["pipe", "pipe", "ignore"]
13981
14219
  }).trim();
@@ -14013,11 +14251,11 @@ function getInstallCommand(packageManager2) {
14013
14251
  }
14014
14252
 
14015
14253
  // src/commands/clean.ts
14016
- import { Command as Command76 } from "commander";
14254
+ import { Command as Command78 } from "commander";
14017
14255
  import { confirm as confirm17, select as select6 } from "@inquirer/prompts";
14018
- import { join as join20 } from "path";
14256
+ import { join as join21 } from "path";
14019
14257
  function createCleanCommand() {
14020
- return new Command76("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) => {
14258
+ return new Command78("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) => {
14021
14259
  await handleClean(options);
14022
14260
  });
14023
14261
  }
@@ -14028,7 +14266,7 @@ async function handleClean(options) {
14028
14266
  {
14029
14267
  name: "Backups",
14030
14268
  description: "Component backup files (.jai1_backup/)",
14031
- path: join20(cwd, ".jai1_backup"),
14269
+ path: join21(cwd, ".jai1_backup"),
14032
14270
  check: async () => {
14033
14271
  const backups = await service.listBackups(cwd);
14034
14272
  return { exists: backups.length > 0, count: backups.length };
@@ -14131,7 +14369,7 @@ async function cleanTarget(target, skipConfirm) {
14131
14369
  }
14132
14370
 
14133
14371
  // src/commands/redmine/check.ts
14134
- import { Command as Command77 } from "commander";
14372
+ import { Command as Command79 } from "commander";
14135
14373
 
14136
14374
  // src/services/redmine-config.service.ts
14137
14375
  import { readFile as readFile7 } from "fs/promises";
@@ -14438,7 +14676,7 @@ async function checkConnectivity(config) {
14438
14676
 
14439
14677
  // src/commands/redmine/check.ts
14440
14678
  function createRedmineCheckCommand() {
14441
- const cmd = new Command77("check").description("Check Redmine connectivity").option("-c, --config <path>", "Config file path", "redmine.config.yaml").option("--json", "Output as JSON").action(async (options) => {
14679
+ const cmd = new Command79("check").description("Check Redmine connectivity").option("-c, --config <path>", "Config file path", "redmine.config.yaml").option("--json", "Output as JSON").action(async (options) => {
14442
14680
  await handleRedmineCheck(options);
14443
14681
  });
14444
14682
  return cmd;
@@ -14466,7 +14704,7 @@ async function handleRedmineCheck(options) {
14466
14704
  }
14467
14705
 
14468
14706
  // src/commands/redmine/sync-issue.ts
14469
- import { Command as Command78 } from "commander";
14707
+ import { Command as Command80 } from "commander";
14470
14708
 
14471
14709
  // src/sync-issue.ts
14472
14710
  import { resolve as resolve3, relative } from "path";
@@ -14850,7 +15088,7 @@ function extractIssueIdFromUrl(url) {
14850
15088
 
14851
15089
  // src/commands/redmine/sync-issue.ts
14852
15090
  function createSyncIssueCommand() {
14853
- const cmd = new Command78("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) => {
15091
+ const cmd = new Command80("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) => {
14854
15092
  await handleSyncIssue(options);
14855
15093
  });
14856
15094
  return cmd;
@@ -14894,7 +15132,7 @@ async function handleSyncIssue(options) {
14894
15132
  }
14895
15133
 
14896
15134
  // src/commands/redmine/sync-project.ts
14897
- import { Command as Command79 } from "commander";
15135
+ import { Command as Command81 } from "commander";
14898
15136
 
14899
15137
  // src/sync-project.ts
14900
15138
  async function syncProject(config, options = {}) {
@@ -14964,7 +15202,7 @@ async function syncProject(config, options = {}) {
14964
15202
 
14965
15203
  // src/commands/redmine/sync-project.ts
14966
15204
  function createSyncProjectCommand() {
14967
- const cmd = new Command79("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) => {
15205
+ const cmd = new Command81("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) => {
14968
15206
  await handleSyncProject(options);
14969
15207
  });
14970
15208
  return cmd;
@@ -15019,12 +15257,12 @@ async function handleSyncProject(options) {
15019
15257
  }
15020
15258
 
15021
15259
  // src/commands/framework/info.ts
15022
- import { Command as Command80 } from "commander";
15260
+ import { Command as Command82 } from "commander";
15023
15261
  import { promises as fs28 } from "fs";
15024
- import { join as join21 } from "path";
15262
+ import { join as join22 } from "path";
15025
15263
  import { homedir as homedir5 } from "os";
15026
15264
  function createInfoCommand() {
15027
- const cmd = new Command80("info").description("Show client configuration and status").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
15265
+ const cmd = new Command82("info").description("Show client configuration and status").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
15028
15266
  await handleInfo(options);
15029
15267
  });
15030
15268
  return cmd;
@@ -15035,7 +15273,7 @@ async function handleInfo(options) {
15035
15273
  if (!config) {
15036
15274
  throw new ValidationError(`Not initialized. Run "${getCliName()} auth" first.`);
15037
15275
  }
15038
- const frameworkPath = join21(homedir5(), ".jai1", "framework");
15276
+ const frameworkPath = join22(homedir5(), ".jai1", "framework");
15039
15277
  const projectStatus = await getProjectStatus2();
15040
15278
  const info = {
15041
15279
  configPath: configService.getConfigPath(),
@@ -15070,7 +15308,7 @@ function maskKey4(key) {
15070
15308
  return "****" + key.slice(-4);
15071
15309
  }
15072
15310
  async function getProjectStatus2() {
15073
- const projectJai1 = join21(process.cwd(), ".jai1");
15311
+ const projectJai1 = join22(process.cwd(), ".jai1");
15074
15312
  try {
15075
15313
  await fs28.access(projectJai1);
15076
15314
  return { exists: true, version: "Synced" };
@@ -15080,9 +15318,9 @@ async function getProjectStatus2() {
15080
15318
  }
15081
15319
 
15082
15320
  // src/commands/self-update.ts
15083
- import { Command as Command81 } from "commander";
15321
+ import { Command as Command83 } from "commander";
15084
15322
  import { confirm as confirm18 } from "@inquirer/prompts";
15085
- import { execSync as execSync5 } from "child_process";
15323
+ import { execSync as execSync6 } from "child_process";
15086
15324
  var colors3 = {
15087
15325
  yellow: "\x1B[33m",
15088
15326
  green: "\x1B[32m",
@@ -15092,7 +15330,7 @@ var colors3 = {
15092
15330
  bold: "\x1B[1m"
15093
15331
  };
15094
15332
  function createSelfUpdateCommand() {
15095
- return new Command81("self-update").description("Update CLI client to the latest version").option("--check", "Only check for updates without installing").option("--force", "Force update without confirmation").action(async (options) => {
15333
+ return new Command83("self-update").description("Update CLI client to the latest version").option("--check", "Only check for updates without installing").option("--force", "Force update without confirmation").action(async (options) => {
15096
15334
  await handleSelfUpdate(options);
15097
15335
  });
15098
15336
  }
@@ -15153,7 +15391,7 @@ ${colors3.cyan}\u{1F4E5} Installing latest version...${colors3.reset}
15153
15391
  const packageManager2 = detectPackageManager2();
15154
15392
  const installCommand = getInstallCommand2(packageManager2);
15155
15393
  console.log(`${colors3.cyan}Using ${packageManager2}...${colors3.reset}`);
15156
- execSync5(installCommand, {
15394
+ execSync6(installCommand, {
15157
15395
  stdio: "inherit",
15158
15396
  env: { ...process.env, FORCE_COLOR: "1" }
15159
15397
  });
@@ -15200,17 +15438,17 @@ function detectPackageManager2() {
15200
15438
  if (userAgent.includes("yarn")) return "yarn";
15201
15439
  if (userAgent.includes("bun")) return "bun";
15202
15440
  try {
15203
- execSync5("pnpm --version", { stdio: "ignore" });
15441
+ execSync6("pnpm --version", { stdio: "ignore" });
15204
15442
  return "pnpm";
15205
15443
  } catch {
15206
15444
  }
15207
15445
  try {
15208
- execSync5("yarn --version", { stdio: "ignore" });
15446
+ execSync6("yarn --version", { stdio: "ignore" });
15209
15447
  return "yarn";
15210
15448
  } catch {
15211
15449
  }
15212
15450
  try {
15213
- execSync5("bun --version", { stdio: "ignore" });
15451
+ execSync6("bun --version", { stdio: "ignore" });
15214
15452
  return "bun";
15215
15453
  } catch {
15216
15454
  }
@@ -15232,10 +15470,10 @@ function getInstallCommand2(packageManager2) {
15232
15470
  }
15233
15471
 
15234
15472
  // src/commands/clear-backups.ts
15235
- import { Command as Command82 } from "commander";
15473
+ import { Command as Command84 } from "commander";
15236
15474
  import { confirm as confirm19 } from "@inquirer/prompts";
15237
15475
  function createClearBackupsCommand() {
15238
- return new Command82("clear-backups").description("Clear backup files").option("-y, --yes", "Skip confirmation").action(async (options) => {
15476
+ return new Command84("clear-backups").description("Clear backup files").option("-y, --yes", "Skip confirmation").action(async (options) => {
15239
15477
  const service = new ComponentsService();
15240
15478
  const backups = await service.listBackups(process.cwd());
15241
15479
  if (backups.length === 0) {
@@ -15260,11 +15498,11 @@ function createClearBackupsCommand() {
15260
15498
  }
15261
15499
 
15262
15500
  // src/commands/vscode/index.ts
15263
- import { Command as Command83 } from "commander";
15501
+ import { Command as Command85 } from "commander";
15264
15502
  import { checkbox as checkbox9, confirm as confirm20, select as select7 } from "@inquirer/prompts";
15265
15503
  import fs29 from "fs/promises";
15266
15504
  import path12 from "path";
15267
- import { existsSync as existsSync3 } from "fs";
15505
+ import { existsSync as existsSync4 } from "fs";
15268
15506
  var PERFORMANCE_GROUPS2 = {
15269
15507
  telemetry: {
15270
15508
  name: "Telemetry",
@@ -15400,7 +15638,7 @@ var PERFORMANCE_GROUPS2 = {
15400
15638
  }
15401
15639
  };
15402
15640
  function createVSCodeCommand() {
15403
- const vscodeCommand = new Command83("vscode").description("Qu\u1EA3n l\xFD c\xE0i \u0111\u1EB7t VSCode cho d\u1EF1 \xE1n hi\u1EC7n t\u1EA1i");
15641
+ const vscodeCommand = new Command85("vscode").description("Qu\u1EA3n l\xFD c\xE0i \u0111\u1EB7t VSCode cho d\u1EF1 \xE1n hi\u1EC7n t\u1EA1i");
15404
15642
  vscodeCommand.action(async () => {
15405
15643
  await interactiveMode2();
15406
15644
  });
@@ -15494,12 +15732,12 @@ async function applyGroups2(groupKeys, action) {
15494
15732
  console.log(' \u{1F4A1} Ch\u1EA1y "jai1 vscode list" \u0111\u1EC3 xem danh s\xE1ch nh\xF3m c\xF3 s\u1EB5n.');
15495
15733
  return;
15496
15734
  }
15497
- if (!existsSync3(vscodeDir)) {
15735
+ if (!existsSync4(vscodeDir)) {
15498
15736
  await fs29.mkdir(vscodeDir, { recursive: true });
15499
15737
  console.log("\u{1F4C1} \u0110\xE3 t\u1EA1o th\u01B0 m\u1EE5c .vscode/");
15500
15738
  }
15501
15739
  let currentSettings = {};
15502
- if (existsSync3(settingsPath)) {
15740
+ if (existsSync4(settingsPath)) {
15503
15741
  try {
15504
15742
  const content = await fs29.readFile(settingsPath, "utf-8");
15505
15743
  currentSettings = JSON.parse(content);
@@ -15549,7 +15787,7 @@ async function applyGroups2(groupKeys, action) {
15549
15787
  async function resetSettings2(groupKeys) {
15550
15788
  const vscodeDir = path12.join(process.cwd(), ".vscode");
15551
15789
  const settingsPath = path12.join(vscodeDir, "settings.json");
15552
- if (!existsSync3(settingsPath)) {
15790
+ if (!existsSync4(settingsPath)) {
15553
15791
  console.log("\n\u26A0\uFE0F Kh\xF4ng t\xECm th\u1EA5y file settings.json");
15554
15792
  return;
15555
15793
  }
@@ -15571,10 +15809,10 @@ async function resetSettings2(groupKeys) {
15571
15809
  }
15572
15810
 
15573
15811
  // src/commands/migrate-ide.ts
15574
- import { Command as Command84 } from "commander";
15812
+ import { Command as Command86 } from "commander";
15575
15813
  import { checkbox as checkbox10, confirm as confirm21 } from "@inquirer/prompts";
15576
15814
  function createMigrateIdeCommand() {
15577
- const cmd = new Command84("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) => {
15815
+ const cmd = new Command86("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) => {
15578
15816
  await runMigrateIde(options);
15579
15817
  });
15580
15818
  return cmd;
@@ -15683,20 +15921,20 @@ async function runMigrateIde(options) {
15683
15921
 
15684
15922
  // src/utils/help-formatter.ts
15685
15923
  import boxen4 from "boxen";
15686
- import chalk40 from "chalk";
15924
+ import chalk42 from "chalk";
15687
15925
  import gradient from "gradient-string";
15688
15926
  import figlet from "figlet";
15689
15927
  function showCustomHelp(version) {
15690
15928
  const title = figlet.textSync("JAI1", { font: "Small" });
15691
15929
  console.log(gradient.pastel(title));
15692
15930
  console.log(
15693
- boxen4(chalk40.cyan(`Agentic Coding CLI v${version}`), {
15931
+ boxen4(chalk42.cyan(`Agentic Coding CLI v${version}`), {
15694
15932
  padding: { left: 1, right: 1, top: 0, bottom: 0 },
15695
15933
  borderStyle: "round",
15696
15934
  borderColor: "cyan"
15697
15935
  })
15698
15936
  );
15699
- console.log(chalk40.bold("\n\u{1F527} Thi\u1EBFt l\u1EADp & Th\xF4ng tin"));
15937
+ console.log(chalk42.bold("\n\u{1F527} Thi\u1EBFt l\u1EADp & Th\xF4ng tin"));
15700
15938
  console.log(" auth X\xE1c th\u1EF1c v\xE0 c\u1EA5u h\xECnh client");
15701
15939
  console.log(" status Hi\u1EC3n th\u1ECB tr\u1EA1ng th\xE1i c\u1EA5u h\xECnh");
15702
15940
  console.log(" client-info T\u1EA1o th\xF4ng tin client \u0111\u1EC3 g\u1EEDi \u0111\u1ED9i ph\xE1t tri\u1EC3n");
@@ -15704,43 +15942,43 @@ function showCustomHelp(version) {
15704
15942
  console.log(" guide H\u01B0\u1EDBng d\u1EABn s\u1EED d\u1EE5ng nhanh");
15705
15943
  console.log(" quickstart B\u1EAFt \u0111\u1EA7u t\u1EEB \u0111\xE2u? (theo t\xECnh hu\u1ED1ng)");
15706
15944
  console.log(" doctor Chu\u1EA9n \u0111o\xE1n project hi\u1EC7n t\u1EA1i");
15707
- console.log(chalk40.bold("\n\u{1F4E6} Qu\u1EA3n l\xFD Components"));
15945
+ console.log(chalk42.bold("\n\u{1F4E6} Qu\u1EA3n l\xFD Components"));
15708
15946
  console.log(" apply C\xE0i \u0111\u1EB7t components (interactive)");
15709
15947
  console.log(" update C\u1EADp nh\u1EADt components \u0111\xE3 c\xE0i");
15710
15948
  console.log(" check Ki\u1EC3m tra c\u1EADp nh\u1EADt t\u1EEB server");
15711
- console.log(chalk40.bold("\n\u{1F5A5}\uFE0F IDE & T\xEDch h\u1EE3p"));
15949
+ console.log(chalk42.bold("\n\u{1F5A5}\uFE0F IDE & T\xEDch h\u1EE3p"));
15712
15950
  console.log(" ide L\u1EC7nh c\u1EA5u h\xECnh IDE");
15713
15951
  console.log(" chat Chat AI v\u1EDBi Jai1 LLM Proxy");
15714
15952
  console.log(" openai-keys Th\xF4ng tin API credentials");
15715
- console.log(chalk40.bold("\n\u{1F916} AI Tools"));
15953
+ console.log(chalk42.bold("\n\u{1F916} AI Tools"));
15716
15954
  console.log(" translate D\u1ECBch v\u0103n b\u1EA3n/file b\u1EB1ng AI");
15717
15955
  console.log(" image T\u1EA1o \u1EA3nh (Coming Soon)");
15718
15956
  console.log(" stats Th\u1ED1ng k\xEA s\u1EED d\u1EE5ng LLM");
15719
15957
  console.log(" feedback G\u1EEDi b\xE1o c\xE1o/\u0111\u1EC1 xu\u1EA5t");
15720
- console.log(chalk40.bold("\n\u{1F4C1} Project"));
15958
+ console.log(chalk42.bold("\n\u{1F4C1} Project"));
15721
15959
  console.log(" kit Qu\u1EA3n l\xFD starter kits");
15722
15960
  console.log(" tasks (t) Qu\u1EA3n l\xFD tasks ph\xE1t tri\u1EC3n");
15723
15961
  console.log(" rules Qu\u1EA3n l\xFD rule presets");
15724
15962
  console.log(" deps Qu\u1EA3n l\xFD dependencies");
15725
15963
  console.log(" redmine Redmine context sync");
15726
- console.log(chalk40.bold("\n\u2699\uFE0F B\u1EA3o tr\xEC"));
15964
+ console.log(chalk42.bold("\n\u2699\uFE0F B\u1EA3o tr\xEC"));
15727
15965
  console.log(" upgrade C\u1EADp nh\u1EADt CLI client");
15728
15966
  console.log(" clean D\u1ECDn d\u1EB9p cache/backup");
15729
15967
  console.log(" utils Developer utilities");
15730
15968
  const name = getCliName();
15731
- console.log(chalk40.dim(`
15969
+ console.log(chalk42.dim(`
15732
15970
  S\u1EED d\u1EE5ng: ${name} [l\u1EC7nh] --help \u0111\u1EC3 xem chi ti\u1EBFt`));
15733
15971
  }
15734
15972
  function showUnknownCommand(commandName) {
15735
- console.error(chalk40.red(`\u274C L\u1EC7nh kh\xF4ng t\u1ED3n t\u1EA1i: ${commandName}`));
15973
+ console.error(chalk42.red(`\u274C L\u1EC7nh kh\xF4ng t\u1ED3n t\u1EA1i: ${commandName}`));
15736
15974
  const name = getCliName();
15737
- console.error(chalk40.dim(`
15975
+ console.error(chalk42.dim(`
15738
15976
  G\u1EE3i \xFD: Ch\u1EA1y ${name} --help \u0111\u1EC3 xem danh s\xE1ch l\u1EC7nh`));
15739
15977
  }
15740
15978
 
15741
15979
  // src/cli.ts
15742
15980
  checkNodeVersion();
15743
- var program = new Command85();
15981
+ var program = new Command87();
15744
15982
  if (process.argv.includes("-v") || process.argv.includes("--version")) {
15745
15983
  console.log(package_default.version);
15746
15984
  if (!process.argv.includes("--skip-update-check")) {
@@ -15779,11 +16017,12 @@ program.addCommand(createTasksCommand());
15779
16017
  program.addCommand(createKitCommand());
15780
16018
  program.addCommand(createRulesCommand());
15781
16019
  program.addCommand(createSkillsCommand());
16020
+ program.addCommand(createHooksCommand());
15782
16021
  program.addCommand(createUpgradeCommand());
15783
16022
  program.addCommand(createCleanCommand());
15784
- var redmineCommand = new Command85("redmine").description("Redmine context sync commands");
16023
+ var redmineCommand = new Command87("redmine").description("Redmine context sync commands");
15785
16024
  redmineCommand.addCommand(createRedmineCheckCommand());
15786
- var syncCommand = new Command85("sync").description("Sync Redmine issues to markdown files");
16025
+ var syncCommand = new Command87("sync").description("Sync Redmine issues to markdown files");
15787
16026
  syncCommand.addCommand(createSyncIssueCommand());
15788
16027
  syncCommand.addCommand(createSyncProjectCommand());
15789
16028
  redmineCommand.addCommand(syncCommand);