@jvittechs/j 1.0.38 → 1.0.39

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
@@ -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 Command86 } 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.39",
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 Command58 } 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,165 @@ 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 guide")}
11281
+
11282
+ ${chalk30.dim("-j / --json available on all commands (except guide, sync)")}
11097
11283
  `;
11098
11284
  function createTaskGuideCommand() {
11099
- return new Command56("guide").description("Show full task management guide").action(() => {
11285
+ return new Command57("guide").description("Show full task management guide").action(() => {
11100
11286
  console.log(GUIDE_TEXT);
11101
11287
  });
11102
11288
  }
11103
11289
 
11104
11290
  // src/commands/tasks/index.ts
11105
11291
  function createTasksCommand() {
11106
- const cmd = new Command57("tasks").alias("t").description("Task management \u2014 track, assign, and manage development tasks").hook("preAction", (thisCommand, actionCommand) => {
11292
+ const cmd = new Command58("tasks").alias("t").description("Task management \u2014 track, assign, and manage development tasks").hook("preAction", (thisCommand, actionCommand) => {
11107
11293
  if (actionCommand.name() !== "guide") {
11108
11294
  TaskService.ensureJai1Dir();
11109
11295
  }
@@ -11127,17 +11313,17 @@ function createTasksCommand() {
11127
11313
  }
11128
11314
 
11129
11315
  // src/commands/kit/index.ts
11130
- import { Command as Command61 } from "commander";
11131
- import chalk31 from "chalk";
11316
+ import { Command as Command62 } from "commander";
11317
+ import chalk32 from "chalk";
11132
11318
 
11133
11319
  // src/commands/kit/list.ts
11134
- import { Command as Command58 } from "commander";
11135
- import chalk30 from "chalk";
11320
+ import { Command as Command59 } from "commander";
11321
+ import chalk31 from "chalk";
11136
11322
  import Table6 from "cli-table3";
11137
11323
 
11138
11324
  // src/services/starter-kit.service.ts
11139
11325
  import { promises as fs19 } from "fs";
11140
- import { join as join9 } from "path";
11326
+ import { join as join10 } from "path";
11141
11327
  import AdmZip from "adm-zip";
11142
11328
  var StarterKitService = class {
11143
11329
  /**
@@ -11184,9 +11370,9 @@ var StarterKitService = class {
11184
11370
  throw new NetworkError(`Failed to download kit: HTTP ${response.status}`);
11185
11371
  }
11186
11372
  if (onProgress) onProgress(30);
11187
- const tmpDir = join9(process.env.TMPDIR || "/tmp", "jai1-kits");
11373
+ const tmpDir = join10(process.env.TMPDIR || "/tmp", "jai1-kits");
11188
11374
  await fs19.mkdir(tmpDir, { recursive: true });
11189
- const tmpFile = join9(tmpDir, `${slug}.zip`);
11375
+ const tmpFile = join10(tmpDir, `${slug}.zip`);
11190
11376
  const buffer = await response.arrayBuffer();
11191
11377
  await fs19.writeFile(tmpFile, Buffer.from(buffer));
11192
11378
  if (onProgress) onProgress(60);
@@ -11200,13 +11386,13 @@ var StarterKitService = class {
11200
11386
 
11201
11387
  // src/commands/kit/list.ts
11202
11388
  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) => {
11389
+ return new Command59("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
11390
  const configService = new ConfigService();
11205
11391
  const config = await configService.load();
11206
11392
  if (!config) {
11207
11393
  throw new ValidationError('Not initialized. Run "jai1 auth" first.');
11208
11394
  }
11209
- console.log(chalk30.cyan("\u{1F4E6} \u0110ang t\u1EA3i danh s\xE1ch starter kits..."));
11395
+ console.log(chalk31.cyan("\u{1F4E6} \u0110ang t\u1EA3i danh s\xE1ch starter kits..."));
11210
11396
  console.log();
11211
11397
  const kitService = new StarterKitService();
11212
11398
  const kits = await kitService.list(config, {
@@ -11214,9 +11400,9 @@ function createKitListCommand() {
11214
11400
  search: options.search
11215
11401
  });
11216
11402
  if (kits.length === 0) {
11217
- console.log(chalk30.yellow("Kh\xF4ng t\xECm th\u1EA5y starter kits n\xE0o."));
11403
+ console.log(chalk31.yellow("Kh\xF4ng t\xECm th\u1EA5y starter kits n\xE0o."));
11218
11404
  if (options.category || options.search) {
11219
- console.log(chalk30.dim("Th\u1EED b\u1ECF filter \u0111\u1EC3 xem t\u1EA5t c\u1EA3."));
11405
+ console.log(chalk31.dim("Th\u1EED b\u1ECF filter \u0111\u1EC3 xem t\u1EA5t c\u1EA3."));
11220
11406
  }
11221
11407
  return;
11222
11408
  }
@@ -11240,35 +11426,35 @@ function createKitListCommand() {
11240
11426
  const categoryKits = byCategory[category];
11241
11427
  const categoryIcon = category === "frontend" ? "\u{1F3A8}" : category === "backend" ? "\u2699\uFE0F" : category === "fullstack" ? "\u{1F680}" : "\u{1F4E6}";
11242
11428
  console.log(
11243
- chalk30.bold(`${categoryIcon} ${category.charAt(0).toUpperCase() + category.slice(1)}`)
11429
+ chalk31.bold(`${categoryIcon} ${category.charAt(0).toUpperCase() + category.slice(1)}`)
11244
11430
  );
11245
11431
  const table = new Table6({
11246
11432
  head: [
11247
- chalk30.cyan("Slug"),
11248
- chalk30.cyan("M\xF4 t\u1EA3"),
11249
- chalk30.cyan("Version")
11433
+ chalk31.cyan("Slug"),
11434
+ chalk31.cyan("M\xF4 t\u1EA3"),
11435
+ chalk31.cyan("Version")
11250
11436
  ],
11251
11437
  style: { head: [], border: ["gray"] }
11252
11438
  });
11253
11439
  for (const kit of categoryKits) {
11254
11440
  table.push([
11255
- chalk30.white(kit.slug),
11256
- chalk30.dim(kit.description.slice(0, 50)),
11257
- chalk30.green(`v${kit.version}`)
11441
+ chalk31.white(kit.slug),
11442
+ chalk31.dim(kit.description.slice(0, 50)),
11443
+ chalk31.green(`v${kit.version}`)
11258
11444
  ]);
11259
11445
  }
11260
11446
  console.log(table.toString());
11261
11447
  console.log();
11262
11448
  }
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'));
11449
+ console.log(chalk31.dim(`T\u1ED5ng c\u1ED9ng: ${kits.length} starter kit(s)`));
11450
+ console.log(chalk31.dim('\n\u{1F4A1} Ch\u1EA1y "jai1 kit create <slug>" \u0111\u1EC3 t\u1EA1o project m\u1EDBi'));
11265
11451
  });
11266
11452
  }
11267
11453
 
11268
11454
  // src/commands/kit/info.ts
11269
- import { Command as Command59 } from "commander";
11455
+ import { Command as Command60 } from "commander";
11270
11456
  function createKitInfoCommand() {
11271
- return new Command59("info").description("Show detailed information about a starter kit").argument("<slug>", "Starter kit slug").action(async (slug) => {
11457
+ return new Command60("info").description("Show detailed information about a starter kit").argument("<slug>", "Starter kit slug").action(async (slug) => {
11272
11458
  const configService = new ConfigService();
11273
11459
  const config = await configService.load();
11274
11460
  if (!config) {
@@ -11317,9 +11503,9 @@ Post-Init Commands:`);
11317
11503
  }
11318
11504
 
11319
11505
  // src/commands/kit/create.ts
11320
- import { Command as Command60 } from "commander";
11506
+ import { Command as Command61 } from "commander";
11321
11507
  import { promises as fs20 } from "fs";
11322
- import { join as join10 } from "path";
11508
+ import { join as join11 } from "path";
11323
11509
  import { select as select3, input as input2, checkbox as checkbox4 } from "@inquirer/prompts";
11324
11510
  import { execa as execa2 } from "execa";
11325
11511
 
@@ -11362,7 +11548,7 @@ var HookExecutor = class {
11362
11548
 
11363
11549
  // src/commands/kit/create.ts
11364
11550
  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) => {
11551
+ return new Command61("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
11552
  const configService = new ConfigService();
11367
11553
  const config = await configService.load();
11368
11554
  if (!config) {
@@ -11382,7 +11568,7 @@ function createKitCreateCommand() {
11382
11568
  }
11383
11569
  }
11384
11570
  }
11385
- const targetDir = directory || join10(process.cwd(), options.name || slug);
11571
+ const targetDir = directory || join11(process.cwd(), options.name || slug);
11386
11572
  try {
11387
11573
  await fs20.access(targetDir);
11388
11574
  throw new Error(`Directory already exists: ${targetDir}`);
@@ -11447,7 +11633,7 @@ function createKitCreateCommand() {
11447
11633
  );
11448
11634
  for (const filepath of expandedPaths) {
11449
11635
  console.log(` \u{1F4E5} Installing ${filepath}...`);
11450
- await componentsService.install(config, filepath, join10(targetDir, ".jai1"));
11636
+ await componentsService.install(config, filepath, join11(targetDir, ".jai1"));
11451
11637
  }
11452
11638
  console.log(" \u2713 Framework components applied");
11453
11639
  }
@@ -11527,7 +11713,7 @@ async function getAllFiles(dir) {
11527
11713
  const files = [];
11528
11714
  const entries = await fs20.readdir(dir, { withFileTypes: true });
11529
11715
  for (const entry of entries) {
11530
- const fullPath = join10(dir, entry.name);
11716
+ const fullPath = join11(dir, entry.name);
11531
11717
  if (entry.isDirectory()) {
11532
11718
  if (!entry.name.startsWith(".") && entry.name !== "node_modules") {
11533
11719
  files.push(...await getAllFiles(fullPath));
@@ -11541,23 +11727,23 @@ async function getAllFiles(dir) {
11541
11727
 
11542
11728
  // src/commands/kit/index.ts
11543
11729
  function showKitHelp() {
11544
- console.log(chalk31.bold.cyan("\u{1F4E6} jai1 kit") + chalk31.dim(" - Qu\u1EA3n l\xFD starter kits"));
11730
+ console.log(chalk32.bold.cyan("\u{1F4E6} jai1 kit") + chalk32.dim(" - Qu\u1EA3n l\xFD starter kits"));
11545
11731
  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`);
11732
+ console.log(chalk32.bold("C\xE1c l\u1EC7nh:"));
11733
+ console.log(` ${chalk32.cyan("list")} Li\u1EC7t k\xEA c\xE1c starter kits c\xF3 s\u1EB5n`);
11734
+ console.log(` ${chalk32.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t starter kit`);
11735
+ console.log(` ${chalk32.cyan("create")} T\u1EA1o project m\u1EDBi t\u1EEB starter kit`);
11550
11736
  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"));
11737
+ console.log(chalk32.bold("V\xED d\u1EE5:"));
11738
+ console.log(chalk32.dim(" $ jai1 kit list"));
11739
+ console.log(chalk32.dim(" $ jai1 kit list --category frontend"));
11740
+ console.log(chalk32.dim(" $ jai1 kit info next-tw4-shadcn"));
11741
+ console.log(chalk32.dim(" $ jai1 kit create next-tw4-shadcn my-project"));
11556
11742
  console.log();
11557
- console.log(chalk31.dim('Ch\u1EA1y "jai1 kit <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
11743
+ console.log(chalk32.dim('Ch\u1EA1y "jai1 kit <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
11558
11744
  }
11559
11745
  function createKitCommand() {
11560
- const cmd = new Command61("kit").description("Manage starter kits for new projects").action(() => {
11746
+ const cmd = new Command62("kit").description("Manage starter kits for new projects").action(() => {
11561
11747
  showKitHelp();
11562
11748
  });
11563
11749
  cmd.addCommand(createKitListCommand());
@@ -11567,21 +11753,21 @@ function createKitCommand() {
11567
11753
  }
11568
11754
 
11569
11755
  // src/commands/rules/index.ts
11570
- import { Command as Command68 } from "commander";
11571
- import chalk33 from "chalk";
11756
+ import { Command as Command69 } from "commander";
11757
+ import chalk34 from "chalk";
11572
11758
 
11573
11759
  // src/commands/rules/list.ts
11574
- import { Command as Command62 } from "commander";
11575
- import chalk32 from "chalk";
11760
+ import { Command as Command63 } from "commander";
11761
+ import chalk33 from "chalk";
11576
11762
  import Table7 from "cli-table3";
11577
11763
  function createRulesListCommand() {
11578
- return new Command62("list").description("List available rule presets").option("--json", "Output as JSON").action(async (options) => {
11764
+ return new Command63("list").description("List available rule presets").option("--json", "Output as JSON").action(async (options) => {
11579
11765
  const configService = new ConfigService();
11580
11766
  const config = await configService.load();
11581
11767
  if (!config) {
11582
11768
  throw new ValidationError('Not initialized. Run "jai1 auth" first.');
11583
11769
  }
11584
- console.log(chalk32.cyan("\u{1F4CB} \u0110ang t\u1EA3i danh s\xE1ch rule presets..."));
11770
+ console.log(chalk33.cyan("\u{1F4CB} \u0110ang t\u1EA3i danh s\xE1ch rule presets..."));
11585
11771
  console.log();
11586
11772
  try {
11587
11773
  const response = await fetch(`${config.apiUrl}/api/rules/presets`, {
@@ -11598,23 +11784,23 @@ function createRulesListCommand() {
11598
11784
  return;
11599
11785
  }
11600
11786
  if (data.total === 0) {
11601
- console.log(chalk32.yellow("Kh\xF4ng c\xF3 presets n\xE0o."));
11787
+ console.log(chalk33.yellow("Kh\xF4ng c\xF3 presets n\xE0o."));
11602
11788
  return;
11603
11789
  }
11604
11790
  console.log(
11605
- chalk32.green(`\u2713 T\xECm th\u1EA5y ${chalk32.bold(data.total)} preset${data.total > 1 ? "s" : ""}`)
11791
+ chalk33.green(`\u2713 T\xECm th\u1EA5y ${chalk33.bold(data.total)} preset${data.total > 1 ? "s" : ""}`)
11606
11792
  );
11607
11793
  console.log();
11608
11794
  for (const preset of data.presets) {
11609
- console.log(chalk32.bold.cyan(`\u{1F4E6} ${preset.slug}`));
11795
+ console.log(chalk33.bold.cyan(`\u{1F4E6} ${preset.slug}`));
11610
11796
  const table = new Table7({
11611
11797
  style: { head: [], border: ["gray"], compact: true },
11612
11798
  colWidths: [15, 55]
11613
11799
  });
11614
11800
  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}`)]
11801
+ [chalk33.dim("T\xEAn"), chalk33.white(preset.name)],
11802
+ [chalk33.dim("M\xF4 t\u1EA3"), chalk33.white(preset.description)],
11803
+ [chalk33.dim("Version"), chalk33.green(`v${preset.version}`)]
11618
11804
  );
11619
11805
  const stackParts = [];
11620
11806
  if (preset.stack.frontend) stackParts.push(preset.stack.frontend);
@@ -11622,16 +11808,16 @@ function createRulesListCommand() {
11622
11808
  if (preset.stack.css) stackParts.push(preset.stack.css);
11623
11809
  if (preset.stack.database) stackParts.push(preset.stack.database);
11624
11810
  if (stackParts.length > 0) {
11625
- table.push([chalk32.dim("Stack"), chalk32.yellow(stackParts.join(" + "))]);
11811
+ table.push([chalk33.dim("Stack"), chalk33.yellow(stackParts.join(" + "))]);
11626
11812
  }
11627
11813
  table.push(
11628
- [chalk32.dim("Tags"), chalk32.dim(preset.tags.join(", ") || "-")],
11629
- [chalk32.dim("Downloads"), chalk32.white(preset.downloads.toString())]
11814
+ [chalk33.dim("Tags"), chalk33.dim(preset.tags.join(", ") || "-")],
11815
+ [chalk33.dim("Downloads"), chalk33.white(preset.downloads.toString())]
11630
11816
  );
11631
11817
  console.log(table.toString());
11632
11818
  console.log();
11633
11819
  }
11634
- console.log(chalk32.dim('\u{1F4A1} Ch\u1EA1y "jai1 rules apply <name>" \u0111\u1EC3 \xE1p d\u1EE5ng preset'));
11820
+ console.log(chalk33.dim('\u{1F4A1} Ch\u1EA1y "jai1 rules apply <name>" \u0111\u1EC3 \xE1p d\u1EE5ng preset'));
11635
11821
  } catch (error) {
11636
11822
  throw new Error(
11637
11823
  `L\u1ED7i khi t\u1EA3i presets: ${error instanceof Error ? error.message : String(error)}`
@@ -11641,22 +11827,22 @@ function createRulesListCommand() {
11641
11827
  }
11642
11828
 
11643
11829
  // src/commands/rules/init.ts
11644
- import { Command as Command63 } from "commander";
11830
+ import { Command as Command64 } from "commander";
11645
11831
  import { promises as fs22 } from "fs";
11646
- import { join as join12 } from "path";
11832
+ import { join as join13 } from "path";
11647
11833
  import { select as select4, confirm as confirm10 } from "@inquirer/prompts";
11648
11834
 
11649
11835
  // src/services/project-config.service.ts
11650
11836
  import { promises as fs21 } from "fs";
11651
- import { join as join11 } from "path";
11837
+ import { join as join12 } from "path";
11652
11838
  var ProjectConfigService = class {
11653
11839
  projectRoot;
11654
11840
  configDir;
11655
11841
  configPath;
11656
11842
  constructor(projectRoot = process.cwd()) {
11657
11843
  this.projectRoot = projectRoot;
11658
- this.configDir = join11(this.projectRoot, ".jai1");
11659
- this.configPath = join11(this.configDir, "project.json");
11844
+ this.configDir = join12(this.projectRoot, ".jai1");
11845
+ this.configPath = join12(this.configDir, "project.json");
11660
11846
  }
11661
11847
  /**
11662
11848
  * Check if config file exists
@@ -11766,7 +11952,7 @@ var ProjectConfigService = class {
11766
11952
 
11767
11953
  // src/commands/rules/init.ts
11768
11954
  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) => {
11955
+ return new Command64("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
11956
  const configService = new ConfigService();
11771
11957
  const config = await configService.load();
11772
11958
  if (!config) {
@@ -11862,10 +12048,10 @@ function createRulesInitCommand() {
11862
12048
  });
11863
12049
  }
11864
12050
  async function applyCursorFormat(bundle) {
11865
- const rulesDir = join12(process.cwd(), ".cursor", "rules");
12051
+ const rulesDir = join13(process.cwd(), ".cursor", "rules");
11866
12052
  await fs22.mkdir(rulesDir, { recursive: true });
11867
12053
  for (const [filename, content] of Object.entries(bundle.files)) {
11868
- const filePath = join12(rulesDir, filename);
12054
+ const filePath = join13(rulesDir, filename);
11869
12055
  await fs22.writeFile(filePath, content, "utf-8");
11870
12056
  console.log(`\u2713 Created .cursor/rules/${filename}`);
11871
12057
  }
@@ -11898,9 +12084,9 @@ async function applyAgentsMdFormat(bundle) {
11898
12084
  }
11899
12085
 
11900
12086
  // src/commands/rules/apply.ts
11901
- import { Command as Command64 } from "commander";
12087
+ import { Command as Command65 } from "commander";
11902
12088
  import { promises as fs24 } from "fs";
11903
- import { join as join14 } from "path";
12089
+ import { join as join15 } from "path";
11904
12090
  import { search, confirm as confirm11, checkbox as checkbox5 } from "@inquirer/prompts";
11905
12091
 
11906
12092
  // src/services/rules-generator.service.ts
@@ -12192,7 +12378,7 @@ Follow all instructions and patterns defined in AGENTS.md above.
12192
12378
 
12193
12379
  // src/services/backup.service.ts
12194
12380
  import { promises as fs23 } from "fs";
12195
- import { join as join13, dirname } from "path";
12381
+ import { join as join14, dirname } from "path";
12196
12382
  var BackupService = class {
12197
12383
  backupDir = ".jai1/backups";
12198
12384
  /**
@@ -12200,7 +12386,7 @@ var BackupService = class {
12200
12386
  */
12201
12387
  async createBackup(ides, presetSlug) {
12202
12388
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
12203
- const backupPath = join13(this.backupDir, timestamp);
12389
+ const backupPath = join14(this.backupDir, timestamp);
12204
12390
  const backedUpFiles = [];
12205
12391
  let hasContent = false;
12206
12392
  for (const ideId of ides) {
@@ -12209,7 +12395,7 @@ var BackupService = class {
12209
12395
  console.warn(`Unknown IDE format: ${ideId}, skipping backup`);
12210
12396
  continue;
12211
12397
  }
12212
- const rulesPath = format.rulesPath === "." ? process.cwd() : join13(process.cwd(), format.rulesPath);
12398
+ const rulesPath = format.rulesPath === "." ? process.cwd() : join14(process.cwd(), format.rulesPath);
12213
12399
  try {
12214
12400
  const exists = await this.pathExists(rulesPath);
12215
12401
  if (!exists) {
@@ -12227,14 +12413,14 @@ var BackupService = class {
12227
12413
  const files = await fs23.readdir(rulesPath);
12228
12414
  for (const file of files) {
12229
12415
  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);
12416
+ const originalPath = join14(rulesPath, file);
12417
+ const relativePath = join14(format.rulesPath, file);
12418
+ const destPath = join14(backupPath, ideId, file);
12233
12419
  await fs23.mkdir(dirname(destPath), { recursive: true });
12234
12420
  await fs23.copyFile(originalPath, destPath);
12235
12421
  backedUpFiles.push({
12236
12422
  originalPath: relativePath,
12237
- backupPath: join13(ideId, file),
12423
+ backupPath: join14(ideId, file),
12238
12424
  ide: ideId
12239
12425
  });
12240
12426
  hasContent = true;
@@ -12257,7 +12443,7 @@ var BackupService = class {
12257
12443
  };
12258
12444
  await fs23.mkdir(backupPath, { recursive: true });
12259
12445
  await fs23.writeFile(
12260
- join13(backupPath, "metadata.json"),
12446
+ join14(backupPath, "metadata.json"),
12261
12447
  JSON.stringify(metadata, null, 2),
12262
12448
  "utf-8"
12263
12449
  );
@@ -12267,18 +12453,18 @@ var BackupService = class {
12267
12453
  * Backup a single file (for AGENTS.md, GEMINI.md)
12268
12454
  */
12269
12455
  async backupSingleFile(filename, backupPath, ideId, backedUpFiles) {
12270
- const originalPath = join13(process.cwd(), filename);
12456
+ const originalPath = join14(process.cwd(), filename);
12271
12457
  try {
12272
12458
  const exists = await this.pathExists(originalPath);
12273
12459
  if (!exists) {
12274
12460
  return;
12275
12461
  }
12276
- const destPath = join13(backupPath, ideId, filename);
12462
+ const destPath = join14(backupPath, ideId, filename);
12277
12463
  await fs23.mkdir(dirname(destPath), { recursive: true });
12278
12464
  await fs23.copyFile(originalPath, destPath);
12279
12465
  backedUpFiles.push({
12280
12466
  originalPath: filename,
12281
- backupPath: join13(ideId, filename),
12467
+ backupPath: join14(ideId, filename),
12282
12468
  ide: ideId
12283
12469
  });
12284
12470
  } catch (error) {
@@ -12288,14 +12474,14 @@ var BackupService = class {
12288
12474
  * Restore files from a backup
12289
12475
  */
12290
12476
  async restoreBackup(backupPath) {
12291
- const metadataPath = join13(backupPath, "metadata.json");
12477
+ const metadataPath = join14(backupPath, "metadata.json");
12292
12478
  const metadataContent = await fs23.readFile(metadataPath, "utf-8");
12293
12479
  const metadata = JSON.parse(metadataContent);
12294
12480
  console.log(`
12295
12481
  Restoring backup from ${metadata.timestamp}...`);
12296
12482
  for (const file of metadata.files) {
12297
- const sourcePath = join13(backupPath, file.backupPath);
12298
- const destPath = join13(process.cwd(), file.originalPath);
12483
+ const sourcePath = join14(backupPath, file.backupPath);
12484
+ const destPath = join14(process.cwd(), file.originalPath);
12299
12485
  await fs23.mkdir(dirname(destPath), { recursive: true });
12300
12486
  await fs23.copyFile(sourcePath, destPath);
12301
12487
  console.log(`\u2713 Restored ${file.originalPath}`);
@@ -12307,7 +12493,7 @@ Restoring backup from ${metadata.timestamp}...`);
12307
12493
  */
12308
12494
  async listBackups() {
12309
12495
  try {
12310
- const backupDirPath = join13(process.cwd(), this.backupDir);
12496
+ const backupDirPath = join14(process.cwd(), this.backupDir);
12311
12497
  const exists = await this.pathExists(backupDirPath);
12312
12498
  if (!exists) {
12313
12499
  return [];
@@ -12316,7 +12502,7 @@ Restoring backup from ${metadata.timestamp}...`);
12316
12502
  const backups = [];
12317
12503
  for (const entry of entries) {
12318
12504
  if (entry.isDirectory()) {
12319
- const metadataPath = join13(backupDirPath, entry.name, "metadata.json");
12505
+ const metadataPath = join14(backupDirPath, entry.name, "metadata.json");
12320
12506
  try {
12321
12507
  const metadataContent = await fs23.readFile(metadataPath, "utf-8");
12322
12508
  const metadata = JSON.parse(metadataContent);
@@ -12335,7 +12521,7 @@ Restoring backup from ${metadata.timestamp}...`);
12335
12521
  * Delete a specific backup
12336
12522
  */
12337
12523
  async deleteBackup(timestamp) {
12338
- const backupPath = join13(process.cwd(), this.backupDir, timestamp);
12524
+ const backupPath = join14(process.cwd(), this.backupDir, timestamp);
12339
12525
  await this.deleteDirectory(backupPath);
12340
12526
  }
12341
12527
  /**
@@ -12384,7 +12570,7 @@ Restoring backup from ${metadata.timestamp}...`);
12384
12570
  }
12385
12571
  const entries = await fs23.readdir(path13, { withFileTypes: true });
12386
12572
  for (const entry of entries) {
12387
- const fullPath = join13(path13, entry.name);
12573
+ const fullPath = join14(path13, entry.name);
12388
12574
  if (entry.isDirectory()) {
12389
12575
  await this.deleteDirectory(fullPath);
12390
12576
  } else {
@@ -12399,20 +12585,20 @@ Restoring backup from ${metadata.timestamp}...`);
12399
12585
  * Get backup directory path
12400
12586
  */
12401
12587
  getBackupDir() {
12402
- return join13(process.cwd(), this.backupDir);
12588
+ return join14(process.cwd(), this.backupDir);
12403
12589
  }
12404
12590
  /**
12405
12591
  * Ensure backup directory exists
12406
12592
  */
12407
12593
  async ensureBackupDir() {
12408
- const backupDirPath = join13(process.cwd(), this.backupDir);
12594
+ const backupDirPath = join14(process.cwd(), this.backupDir);
12409
12595
  await fs23.mkdir(backupDirPath, { recursive: true });
12410
12596
  }
12411
12597
  };
12412
12598
 
12413
12599
  // src/commands/rules/apply.ts
12414
12600
  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) => {
12601
+ return new Command65("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
12602
  const configService = new ConfigService();
12417
12603
  const config = await configService.load();
12418
12604
  if (!config) {
@@ -12578,20 +12764,20 @@ function createRulesApplyCommand() {
12578
12764
  }
12579
12765
  }
12580
12766
  console.log("\n\u{1F4DD} Applying preset...\n");
12581
- const rulePresetDir = join14(process.cwd(), ".jai1", "rule-preset");
12767
+ const rulePresetDir = join15(process.cwd(), ".jai1", "rule-preset");
12582
12768
  try {
12583
12769
  await fs24.rm(rulePresetDir, { recursive: true, force: true });
12584
12770
  } catch {
12585
12771
  }
12586
12772
  await fs24.mkdir(rulePresetDir, { recursive: true });
12587
12773
  await fs24.writeFile(
12588
- join14(rulePresetDir, "preset.json"),
12774
+ join15(rulePresetDir, "preset.json"),
12589
12775
  JSON.stringify(bundle.preset, null, 2),
12590
12776
  "utf-8"
12591
12777
  );
12592
12778
  for (const [filename, content] of Object.entries(bundle.files)) {
12593
- const filePath = join14(rulePresetDir, filename);
12594
- await fs24.mkdir(join14(filePath, ".."), { recursive: true });
12779
+ const filePath = join15(rulePresetDir, filename);
12780
+ await fs24.mkdir(join15(filePath, ".."), { recursive: true });
12595
12781
  await fs24.writeFile(filePath, content, "utf-8");
12596
12782
  }
12597
12783
  console.log(`\u2713 Saved preset to .jai1/rule-preset/`);
@@ -12600,8 +12786,8 @@ function createRulesApplyCommand() {
12600
12786
  try {
12601
12787
  const files = generatorService.generateForIde(bundle, ideId);
12602
12788
  for (const file of files) {
12603
- const fullPath = join14(process.cwd(), file.path);
12604
- await fs24.mkdir(join14(fullPath, ".."), { recursive: true });
12789
+ const fullPath = join15(process.cwd(), file.path);
12790
+ await fs24.mkdir(join15(fullPath, ".."), { recursive: true });
12605
12791
  await fs24.writeFile(fullPath, file.content, "utf-8");
12606
12792
  console.log(`\u2713 [${ideId}] ${file.path}`);
12607
12793
  allGeneratedFiles.push({
@@ -12707,11 +12893,11 @@ function createRulesApplyCommand() {
12707
12893
  }
12708
12894
 
12709
12895
  // src/commands/rules/restore.ts
12710
- import { Command as Command65 } from "commander";
12711
- import { join as join15 } from "path";
12896
+ import { Command as Command66 } from "commander";
12897
+ import { join as join16 } from "path";
12712
12898
  import { select as select5, confirm as confirm12 } from "@inquirer/prompts";
12713
12899
  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) => {
12900
+ return new Command66("restore").description("Restore rules from a backup").option("--latest", "Restore the most recent backup").option("-y, --yes", "Skip confirmation").action(async (options) => {
12715
12901
  const backupService = new BackupService();
12716
12902
  const backups = await backupService.listBackups();
12717
12903
  if (backups.length === 0) {
@@ -12755,7 +12941,7 @@ function createRulesRestoreCommand() {
12755
12941
  }
12756
12942
  console.log("\n\u{1F504} Restoring backup...\n");
12757
12943
  try {
12758
- const backupPath = join15(backupService.getBackupDir(), selectedBackup.timestamp);
12944
+ const backupPath = join16(backupService.getBackupDir(), selectedBackup.timestamp);
12759
12945
  await backupService.restoreBackup(backupPath);
12760
12946
  console.log("\n\u2705 Backup restored successfully!\n");
12761
12947
  console.log("\u{1F4A1} Tip: Your IDE may need to be restarted to pick up the changes.");
@@ -12780,14 +12966,14 @@ function formatTimestamp(timestamp) {
12780
12966
  }
12781
12967
 
12782
12968
  // src/commands/rules/sync.ts
12783
- import { Command as Command66 } from "commander";
12969
+ import { Command as Command67 } from "commander";
12784
12970
  import { promises as fs25 } from "fs";
12785
- import { join as join16 } from "path";
12971
+ import { join as join17 } from "path";
12786
12972
  import { checkbox as checkbox6, confirm as confirm13, Separator } from "@inquirer/prompts";
12787
12973
  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");
12974
+ return new Command67("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) => {
12975
+ const rulePresetDir = join17(process.cwd(), ".jai1", "rule-preset");
12976
+ const presetJsonPath = join17(rulePresetDir, "preset.json");
12791
12977
  let presetExists = false;
12792
12978
  let presetData = null;
12793
12979
  try {
@@ -12924,7 +13110,7 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
12924
13110
  const files = await fs25.readdir(rulePresetDir);
12925
13111
  for (const file of files) {
12926
13112
  if (file.endsWith(".mdc") || file.endsWith(".md")) {
12927
- const filePath = join16(rulePresetDir, file);
13113
+ const filePath = join17(rulePresetDir, file);
12928
13114
  const content = await fs25.readFile(filePath, "utf-8");
12929
13115
  bundle.files[file] = content;
12930
13116
  }
@@ -12939,8 +13125,8 @@ Current IDE(s): ${currentIdes.join(", ") || "none"}`);
12939
13125
  }
12940
13126
  const files2 = generatorService.generateForIde(bundle, ideId);
12941
13127
  for (const file of files2) {
12942
- const fullPath = join16(process.cwd(), file.path);
12943
- await fs25.mkdir(join16(fullPath, ".."), { recursive: true });
13128
+ const fullPath = join17(process.cwd(), file.path);
13129
+ await fs25.mkdir(join17(fullPath, ".."), { recursive: true });
12944
13130
  await fs25.writeFile(fullPath, file.content, "utf-8");
12945
13131
  }
12946
13132
  console.log(`\u2713 ${format.name} - ${files2.length} files regenerated`);
@@ -13003,11 +13189,11 @@ function buildIdeChoices(currentIdes, detected, suggestions) {
13003
13189
  }
13004
13190
 
13005
13191
  // src/commands/rules/info.ts
13006
- import { Command as Command67 } from "commander";
13192
+ import { Command as Command68 } from "commander";
13007
13193
  import { promises as fs26 } from "fs";
13008
- import { join as join17 } from "path";
13194
+ import { join as join18 } from "path";
13009
13195
  function createRulesInfoCommand() {
13010
- return new Command67("info").description("Show current preset information").option("--json", "Output as JSON").action(async (options) => {
13196
+ return new Command68("info").description("Show current preset information").option("--json", "Output as JSON").action(async (options) => {
13011
13197
  const projectConfigService = new ProjectConfigService();
13012
13198
  const rulesConfig = await projectConfigService.loadRules();
13013
13199
  if (!rulesConfig) {
@@ -13020,8 +13206,8 @@ function createRulesInfoCommand() {
13020
13206
  return;
13021
13207
  }
13022
13208
  console.log("\u{1F4CB} Current Preset Information\n");
13023
- const rulePresetDir = join17(process.cwd(), ".jai1", "rule-preset");
13024
- const presetJsonPath = join17(rulePresetDir, "preset.json");
13209
+ const rulePresetDir = join18(process.cwd(), ".jai1", "rule-preset");
13210
+ const presetJsonPath = join18(rulePresetDir, "preset.json");
13025
13211
  let presetMetadata = null;
13026
13212
  let presetFiles = [];
13027
13213
  try {
@@ -13088,7 +13274,7 @@ Available Backups (${rulesConfig.backups.length}):`);
13088
13274
  }
13089
13275
  async function checkPathExists(path13) {
13090
13276
  try {
13091
- await fs26.access(join17(process.cwd(), path13));
13277
+ await fs26.access(join18(process.cwd(), path13));
13092
13278
  return true;
13093
13279
  } catch {
13094
13280
  return false;
@@ -13110,26 +13296,26 @@ async function checkIdeFilesExist(ideId, format) {
13110
13296
 
13111
13297
  // src/commands/rules/index.ts
13112
13298
  function showRulesHelp() {
13113
- console.log(chalk33.bold.cyan("\u{1F4CB} jai1 rules") + chalk33.dim(" - Qu\u1EA3n l\xFD rule presets cho AI agents"));
13299
+ console.log(chalk34.bold.cyan("\u{1F4CB} jai1 rules") + chalk34.dim(" - Qu\u1EA3n l\xFD rule presets cho AI agents"));
13114
13300
  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`);
13301
+ console.log(chalk34.bold("C\xE1c l\u1EC7nh:"));
13302
+ console.log(` ${chalk34.cyan("list")} Li\u1EC7t k\xEA c\xE1c presets c\xF3 s\u1EB5n`);
13303
+ console.log(` ${chalk34.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t preset`);
13304
+ console.log(` ${chalk34.cyan("init")} Kh\u1EDFi t\u1EA1o rules t\u1EEB preset`);
13305
+ console.log(` ${chalk34.cyan("apply")} \xC1p d\u1EE5ng preset v\xE0o project`);
13306
+ console.log(` ${chalk34.cyan("sync")} \u0110\u1ED3ng b\u1ED9 rules sang c\xE1c \u0111\u1ECBnh d\u1EA1ng IDE`);
13307
+ console.log(` ${chalk34.cyan("restore")} Kh\xF4i ph\u1EE5c rules t\u1EEB backup`);
13122
13308
  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"));
13309
+ console.log(chalk34.bold("V\xED d\u1EE5:"));
13310
+ console.log(chalk34.dim(" $ jai1 rules list"));
13311
+ console.log(chalk34.dim(" $ jai1 rules info react-typescript"));
13312
+ console.log(chalk34.dim(" $ jai1 rules init --preset=react-typescript"));
13313
+ console.log(chalk34.dim(" $ jai1 rules apply react-typescript"));
13128
13314
  console.log();
13129
- console.log(chalk33.dim('Ch\u1EA1y "jai1 rules <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
13315
+ console.log(chalk34.dim('Ch\u1EA1y "jai1 rules <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
13130
13316
  }
13131
13317
  function createRulesCommand() {
13132
- const rulesCommand = new Command68("rules").description("Manage rule presets for AI agents").action(() => {
13318
+ const rulesCommand = new Command69("rules").description("Manage rule presets for AI agents").action(() => {
13133
13319
  showRulesHelp();
13134
13320
  });
13135
13321
  rulesCommand.addCommand(createRulesListCommand());
@@ -13142,17 +13328,17 @@ function createRulesCommand() {
13142
13328
  }
13143
13329
 
13144
13330
  // src/commands/skills/index.ts
13145
- import { Command as Command74 } from "commander";
13146
- import chalk39 from "chalk";
13331
+ import { Command as Command75 } from "commander";
13332
+ import chalk40 from "chalk";
13147
13333
 
13148
13334
  // src/commands/skills/find.ts
13149
- import { Command as Command69 } from "commander";
13150
- import chalk34 from "chalk";
13335
+ import { Command as Command70 } from "commander";
13336
+ import chalk35 from "chalk";
13151
13337
  import Table8 from "cli-table3";
13152
13338
 
13153
13339
  // src/services/skills.service.ts
13154
13340
  import { promises as fs27 } from "fs";
13155
- import { join as join18 } from "path";
13341
+ import { join as join19 } from "path";
13156
13342
  import { execFile } from "child_process";
13157
13343
  import { promisify } from "util";
13158
13344
  var execFileAsync = promisify(execFile);
@@ -13188,14 +13374,14 @@ var SkillsService = class {
13188
13374
  * List locally installed skills from .jai1/skills/
13189
13375
  */
13190
13376
  async listLocal(projectRoot) {
13191
- const skillsDir = join18(projectRoot, ".jai1", "skills");
13377
+ const skillsDir = join19(projectRoot, ".jai1", "skills");
13192
13378
  const skills = [];
13193
13379
  try {
13194
13380
  const entries = await fs27.readdir(skillsDir, { withFileTypes: true });
13195
13381
  for (const entry of entries) {
13196
13382
  if (!entry.isDirectory()) continue;
13197
- const skillPath = join18(skillsDir, entry.name);
13198
- const skillMd = join18(skillPath, "SKILL.md");
13383
+ const skillPath = join19(skillsDir, entry.name);
13384
+ const skillMd = join19(skillPath, "SKILL.md");
13199
13385
  try {
13200
13386
  await fs27.access(skillMd);
13201
13387
  } catch {
@@ -13220,8 +13406,8 @@ var SkillsService = class {
13220
13406
  * Get detailed info for a single skill
13221
13407
  */
13222
13408
  async getSkillInfo(projectRoot, skillName) {
13223
- const skillPath = join18(projectRoot, ".jai1", "skills", skillName);
13224
- const skillMd = join18(skillPath, "SKILL.md");
13409
+ const skillPath = join19(projectRoot, ".jai1", "skills", skillName);
13410
+ const skillMd = join19(skillPath, "SKILL.md");
13225
13411
  try {
13226
13412
  await fs27.access(skillMd);
13227
13413
  } catch {
@@ -13296,22 +13482,22 @@ var SkillsService = class {
13296
13482
  * After npx skills add, copy newly installed skills from .agents/skills/ into .jai1/skills/
13297
13483
  */
13298
13484
  async copySkillshResultsToJai1(projectRoot, specificSkill) {
13299
- const jai1SkillsDir = join18(projectRoot, ".jai1", "skills");
13485
+ const jai1SkillsDir = join19(projectRoot, ".jai1", "skills");
13300
13486
  await fs27.mkdir(jai1SkillsDir, { recursive: true });
13301
- const universalDir = join18(projectRoot, ".agents", "skills");
13487
+ const universalDir = join19(projectRoot, ".agents", "skills");
13302
13488
  try {
13303
13489
  const entries = await fs27.readdir(universalDir, { withFileTypes: true });
13304
13490
  for (const entry of entries) {
13305
13491
  if (!entry.isDirectory()) continue;
13306
13492
  if (specificSkill && entry.name !== specificSkill) continue;
13307
- const srcSkill = join18(universalDir, entry.name);
13308
- const skillMd = join18(srcSkill, "SKILL.md");
13493
+ const srcSkill = join19(universalDir, entry.name);
13494
+ const skillMd = join19(srcSkill, "SKILL.md");
13309
13495
  try {
13310
13496
  await fs27.access(skillMd);
13311
13497
  } catch {
13312
13498
  continue;
13313
13499
  }
13314
- const targetSkill = join18(jai1SkillsDir, entry.name);
13500
+ const targetSkill = join19(jai1SkillsDir, entry.name);
13315
13501
  try {
13316
13502
  await fs27.access(targetSkill);
13317
13503
  } catch {
@@ -13350,7 +13536,7 @@ var SkillsService = class {
13350
13536
  const target = this.getIDETarget(ide);
13351
13537
  if (!target) continue;
13352
13538
  for (const skill of skillsToSync) {
13353
- const targetPath = join18(projectRoot, target.skillsPath, skill.slug);
13539
+ const targetPath = join19(projectRoot, target.skillsPath, skill.slug);
13354
13540
  try {
13355
13541
  let status = "created";
13356
13542
  try {
@@ -13414,7 +13600,7 @@ var SkillsService = class {
13414
13600
  const entries = await fs27.readdir(dirPath, { withFileTypes: true });
13415
13601
  for (const entry of entries) {
13416
13602
  if (entry.isDirectory()) {
13417
- count += await this.countFiles(join18(dirPath, entry.name));
13603
+ count += await this.countFiles(join19(dirPath, entry.name));
13418
13604
  } else {
13419
13605
  count++;
13420
13606
  }
@@ -13427,8 +13613,8 @@ var SkillsService = class {
13427
13613
  async copyDir(source, target) {
13428
13614
  const entries = await fs27.readdir(source, { withFileTypes: true });
13429
13615
  for (const entry of entries) {
13430
- const srcPath = join18(source, entry.name);
13431
- const tgtPath = join18(target, entry.name);
13616
+ const srcPath = join19(source, entry.name);
13617
+ const tgtPath = join19(target, entry.name);
13432
13618
  if (entry.isDirectory()) {
13433
13619
  await fs27.mkdir(tgtPath, { recursive: true });
13434
13620
  await this.copyDir(srcPath, tgtPath);
@@ -13441,7 +13627,7 @@ var SkillsService = class {
13441
13627
 
13442
13628
  // src/commands/skills/find.ts
13443
13629
  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) => {
13630
+ return new Command70("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
13631
  const searchNpm = options.skillsh || options.all;
13446
13632
  const searchServer = !options.skillsh || options.all;
13447
13633
  if (searchServer) {
@@ -13450,19 +13636,19 @@ function createSkillsFindCommand() {
13450
13636
  if (!config) {
13451
13637
  throw new ValidationError('Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.');
13452
13638
  }
13453
- console.log(chalk34.cyan("\u{1F50D} \u0110ang t\xECm ki\u1EBFm tr\xEAn Jai1 server..."));
13639
+ console.log(chalk35.cyan("\u{1F50D} \u0110ang t\xECm ki\u1EBFm tr\xEAn Jai1 server..."));
13454
13640
  console.log();
13455
13641
  const skillsService = new SkillsService();
13456
13642
  const results = await skillsService.searchFromServer(config, query);
13457
13643
  if (results.length === 0) {
13458
- console.log(chalk34.yellow("Kh\xF4ng t\xECm th\u1EA5y skills n\xE0o tr\xEAn server."));
13644
+ console.log(chalk35.yellow("Kh\xF4ng t\xECm th\u1EA5y skills n\xE0o tr\xEAn server."));
13459
13645
  } else {
13460
13646
  const table = new Table8({
13461
13647
  head: [
13462
- chalk34.cyan("T\xEAn"),
13463
- chalk34.cyan("M\xF4 t\u1EA3"),
13464
- chalk34.cyan("Version"),
13465
- chalk34.cyan("Downloads")
13648
+ chalk35.cyan("T\xEAn"),
13649
+ chalk35.cyan("M\xF4 t\u1EA3"),
13650
+ chalk35.cyan("Version"),
13651
+ chalk35.cyan("Downloads")
13466
13652
  ],
13467
13653
  style: { head: [], border: ["gray"] },
13468
13654
  colWidths: [25, 40, 10, 12]
@@ -13470,70 +13656,70 @@ function createSkillsFindCommand() {
13470
13656
  for (const skill of results) {
13471
13657
  const name = skill.filepath.replace("skills/", "");
13472
13658
  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))
13659
+ chalk35.white(name),
13660
+ chalk35.dim((skill.description || "").slice(0, 38)),
13661
+ chalk35.green(skill.version || "-"),
13662
+ chalk35.dim(String(skill.downloads || 0))
13477
13663
  ]);
13478
13664
  }
13479
- console.log(chalk34.bold(`\u{1F4E6} Jai1 Server (${results.length} k\u1EBFt qu\u1EA3)`));
13665
+ console.log(chalk35.bold(`\u{1F4E6} Jai1 Server (${results.length} k\u1EBFt qu\u1EA3)`));
13480
13666
  console.log(table.toString());
13481
13667
  console.log();
13482
13668
  }
13483
13669
  }
13484
13670
  if (searchNpm) {
13485
- console.log(chalk34.cyan("\u{1F50D} \u0110ang t\xECm ki\u1EBFm tr\xEAn npm skills..."));
13671
+ console.log(chalk35.cyan("\u{1F50D} \u0110ang t\xECm ki\u1EBFm tr\xEAn npm skills..."));
13486
13672
  console.log();
13487
13673
  const skillsService = new SkillsService();
13488
13674
  try {
13489
13675
  const output = await skillsService.npmSkillsFind(query);
13490
- console.log(chalk34.bold("\u{1F310} npm Skills Registry"));
13676
+ console.log(chalk35.bold("\u{1F310} npm Skills Registry"));
13491
13677
  console.log(output);
13492
13678
  } catch (error) {
13493
- console.log(chalk34.yellow(
13679
+ console.log(chalk35.yellow(
13494
13680
  `Kh\xF4ng th\u1EC3 t\xECm ki\u1EBFm tr\xEAn npm: ${error instanceof Error ? error.message : String(error)}`
13495
13681
  ));
13496
13682
  }
13497
13683
  }
13498
13684
  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'));
13685
+ console.log(chalk35.dim('\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t t\u1EEB server'));
13500
13686
  } 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'));
13687
+ console.log(chalk35.dim('\u{1F4A1} D\xF9ng "j skills add <owner/repo@skill> --skillsh" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13502
13688
  } 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"));
13689
+ console.log(chalk35.dim("\u{1F4A1} C\xE0i \u0111\u1EB7t:"));
13690
+ console.log(chalk35.dim(" Server: j skills add <t\xEAn>"));
13691
+ console.log(chalk35.dim(" Skills.sh: j skills add <owner/repo@skill> --skillsh"));
13506
13692
  }
13507
13693
  });
13508
13694
  }
13509
13695
 
13510
13696
  // src/commands/skills/add.ts
13511
- import { Command as Command70 } from "commander";
13512
- import { join as join19 } from "path";
13513
- import chalk35 from "chalk";
13697
+ import { Command as Command71 } from "commander";
13698
+ import { join as join20 } from "path";
13699
+ import chalk36 from "chalk";
13514
13700
  import { checkbox as checkbox7 } from "@inquirer/prompts";
13515
13701
  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) => {
13702
+ return new Command71("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
13703
  const skillsService = new SkillsService();
13518
13704
  const projectRoot = process.cwd();
13519
13705
  const headless = options.yes === true;
13520
13706
  if (options.skillsh) {
13521
- console.log(chalk35.cyan(`\u{1F310} \u0110ang c\xE0i \u0111\u1EB7t skill t\u1EEB npm: ${name}...`));
13707
+ console.log(chalk36.cyan(`\u{1F310} \u0110ang c\xE0i \u0111\u1EB7t skill t\u1EEB npm: ${name}...`));
13522
13708
  console.log();
13523
13709
  const output = await skillsService.npmSkillsAdd(name, projectRoot);
13524
13710
  console.log(output);
13525
- console.log(chalk35.green("\u2705 C\xE0i \u0111\u1EB7t t\u1EEB npm th\xE0nh c\xF4ng!"));
13711
+ console.log(chalk36.green("\u2705 C\xE0i \u0111\u1EB7t t\u1EEB npm th\xE0nh c\xF4ng!"));
13526
13712
  } else {
13527
13713
  const configService = new ConfigService();
13528
13714
  const config = await configService.load();
13529
13715
  if (!config) {
13530
13716
  throw new ValidationError('Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.');
13531
13717
  }
13532
- console.log(chalk35.cyan(`\u{1F4E6} \u0110ang c\xE0i \u0111\u1EB7t skill: ${name}...`));
13718
+ console.log(chalk36.cyan(`\u{1F4E6} \u0110ang c\xE0i \u0111\u1EB7t skill: ${name}...`));
13533
13719
  console.log();
13534
- const targetDir = join19(projectRoot, ".jai1");
13720
+ const targetDir = join20(projectRoot, ".jai1");
13535
13721
  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}/`));
13722
+ console.log(chalk36.green(`\u2705 \u0110\xE3 c\xE0i \u0111\u1EB7t skill "${name}" v\xE0o .jai1/skills/${name}/`));
13537
13723
  }
13538
13724
  console.log();
13539
13725
  if (options.sync) {
@@ -13558,7 +13744,7 @@ function createSkillsAddCommand() {
13558
13744
  });
13559
13745
  }
13560
13746
  if (selectedIdes.length > 0) {
13561
- console.log(chalk35.cyan("\u{1F504} \u0110ang sync sang IDE(s)..."));
13747
+ console.log(chalk36.cyan("\u{1F504} \u0110ang sync sang IDE(s)..."));
13562
13748
  console.log();
13563
13749
  const slug = name.includes("/") ? name.split("/").pop() : name;
13564
13750
  const result = await skillsService.syncToIdes(
@@ -13571,24 +13757,24 @@ function createSkillsAddCommand() {
13571
13757
  }
13572
13758
  );
13573
13759
  console.log();
13574
- console.log(chalk35.green(`\u2705 Sync ho\xE0n t\u1EA5t! Created: ${result.created}, Updated: ${result.updated}`));
13760
+ console.log(chalk36.green(`\u2705 Sync ho\xE0n t\u1EA5t! Created: ${result.created}, Updated: ${result.updated}`));
13575
13761
  if (result.errors > 0) {
13576
- console.log(chalk35.yellow(`\u26A0\uFE0F Errors: ${result.errors}`));
13762
+ console.log(chalk36.yellow(`\u26A0\uFE0F Errors: ${result.errors}`));
13577
13763
  }
13578
13764
  }
13579
13765
  } 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/'));
13766
+ console.log(chalk36.dim('\u{1F4A1} Ch\u1EA1y "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
13767
+ console.log(chalk36.dim(' ho\u1EB7c "j ide sync" \u0111\u1EC3 sync to\xE0n b\u1ED9 .jai1/'));
13582
13768
  }
13583
13769
  });
13584
13770
  }
13585
13771
 
13586
13772
  // src/commands/skills/list.ts
13587
- import { Command as Command71 } from "commander";
13588
- import chalk36 from "chalk";
13773
+ import { Command as Command72 } from "commander";
13774
+ import chalk37 from "chalk";
13589
13775
  import Table9 from "cli-table3";
13590
13776
  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) => {
13777
+ return new Command72("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
13778
  const skillsService = new SkillsService();
13593
13779
  if (options.available) {
13594
13780
  const configService = new ConfigService();
@@ -13596,19 +13782,19 @@ function createSkillsListCommand() {
13596
13782
  if (!config) {
13597
13783
  throw new ValidationError('Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.');
13598
13784
  }
13599
- console.log(chalk36.cyan("\u{1F4E6} \u0110ang t\u1EA3i danh s\xE1ch skills t\u1EEB server..."));
13785
+ console.log(chalk37.cyan("\u{1F4E6} \u0110ang t\u1EA3i danh s\xE1ch skills t\u1EEB server..."));
13600
13786
  console.log();
13601
13787
  const results = await skillsService.searchFromServer(config, options.search);
13602
13788
  if (results.length === 0) {
13603
- console.log(chalk36.yellow("Kh\xF4ng t\xECm th\u1EA5y skills n\xE0o."));
13789
+ console.log(chalk37.yellow("Kh\xF4ng t\xECm th\u1EA5y skills n\xE0o."));
13604
13790
  return;
13605
13791
  }
13606
13792
  const table = new Table9({
13607
13793
  head: [
13608
- chalk36.cyan("T\xEAn"),
13609
- chalk36.cyan("M\xF4 t\u1EA3"),
13610
- chalk36.cyan("Version"),
13611
- chalk36.cyan("Downloads")
13794
+ chalk37.cyan("T\xEAn"),
13795
+ chalk37.cyan("M\xF4 t\u1EA3"),
13796
+ chalk37.cyan("Version"),
13797
+ chalk37.cyan("Downloads")
13612
13798
  ],
13613
13799
  style: { head: [], border: ["gray"] },
13614
13800
  colWidths: [28, 40, 10, 12]
@@ -13616,63 +13802,63 @@ function createSkillsListCommand() {
13616
13802
  for (const skill of results) {
13617
13803
  const name = skill.filepath.replace("skills/", "");
13618
13804
  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))
13805
+ chalk37.white(name),
13806
+ chalk37.dim((skill.description || "").slice(0, 38)),
13807
+ chalk37.green(skill.version || "-"),
13808
+ chalk37.dim(String(skill.downloads || 0))
13623
13809
  ]);
13624
13810
  }
13625
13811
  console.log(table.toString());
13626
13812
  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'));
13813
+ console.log(chalk37.dim(`T\u1ED5ng c\u1ED9ng: ${results.length} skill(s)`));
13814
+ console.log(chalk37.dim('\n\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13629
13815
  } else {
13630
13816
  const projectRoot = process.cwd();
13631
13817
  const skills = await skillsService.listLocal(projectRoot);
13632
13818
  if (skills.length === 0) {
13633
- console.log(chalk36.yellow("Ch\u01B0a c\xF3 skills n\xE0o \u0111\u01B0\u1EE3c c\xE0i \u0111\u1EB7t."));
13819
+ console.log(chalk37.yellow("Ch\u01B0a c\xF3 skills n\xE0o \u0111\u01B0\u1EE3c c\xE0i \u0111\u1EB7t."));
13634
13820
  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'));
13821
+ console.log(chalk37.dim('\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13822
+ console.log(chalk37.dim(' ho\u1EB7c "j skills list --available" \u0111\u1EC3 xem skills c\xF3 s\u1EB5n'));
13637
13823
  return;
13638
13824
  }
13639
- console.log(chalk36.bold.cyan("\u{1F6E0} Skills \u0111\xE3 c\xE0i \u0111\u1EB7t"));
13825
+ console.log(chalk37.bold.cyan("\u{1F6E0} Skills \u0111\xE3 c\xE0i \u0111\u1EB7t"));
13640
13826
  console.log();
13641
13827
  const table = new Table9({
13642
13828
  head: [
13643
- chalk36.cyan("T\xEAn"),
13644
- chalk36.cyan("M\xF4 t\u1EA3"),
13645
- chalk36.cyan("Files")
13829
+ chalk37.cyan("T\xEAn"),
13830
+ chalk37.cyan("M\xF4 t\u1EA3"),
13831
+ chalk37.cyan("Files")
13646
13832
  ],
13647
13833
  style: { head: [], border: ["gray"] },
13648
13834
  colWidths: [28, 45, 8]
13649
13835
  });
13650
13836
  for (const skill of skills) {
13651
13837
  table.push([
13652
- chalk36.white(skill.slug),
13653
- chalk36.dim(skill.description.slice(0, 43)),
13654
- chalk36.dim(String(skill.fileCount))
13838
+ chalk37.white(skill.slug),
13839
+ chalk37.dim(skill.description.slice(0, 43)),
13840
+ chalk37.dim(String(skill.fileCount))
13655
13841
  ]);
13656
13842
  }
13657
13843
  console.log(table.toString());
13658
13844
  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)'));
13845
+ console.log(chalk37.dim(`T\u1ED5ng c\u1ED9ng: ${skills.length} skill(s)`));
13846
+ console.log(chalk37.dim('\n\u{1F4A1} D\xF9ng "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
13661
13847
  }
13662
13848
  });
13663
13849
  }
13664
13850
 
13665
13851
  // src/commands/skills/info.ts
13666
- import { Command as Command72 } from "commander";
13667
- import chalk37 from "chalk";
13852
+ import { Command as Command73 } from "commander";
13853
+ import chalk38 from "chalk";
13668
13854
  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) => {
13855
+ return new Command73("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
13856
  const skillsService = new SkillsService();
13671
13857
  if (options.server) {
13672
13858
  const configService = new ConfigService();
13673
13859
  const config = await configService.load();
13674
13860
  if (!config) {
13675
- console.log(chalk37.red('\u274C Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.'));
13861
+ console.log(chalk38.red('\u274C Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.'));
13676
13862
  process.exit(1);
13677
13863
  }
13678
13864
  const filepath = name.startsWith("skills/") ? name : `skills/${name}`;
@@ -13681,7 +13867,7 @@ function createSkillsInfoCommand() {
13681
13867
  try {
13682
13868
  const component = await componentsService.get(config, filepath);
13683
13869
  console.log(`
13684
- \u{1F6E0} ${chalk37.bold(component.name || name)}
13870
+ \u{1F6E0} ${chalk38.bold(component.name || name)}
13685
13871
  `);
13686
13872
  console.log(`Filepath: ${component.filepath}`);
13687
13873
  console.log(`Version: ${component.version}`);
@@ -13694,47 +13880,47 @@ function createSkillsInfoCommand() {
13694
13880
  }
13695
13881
  console.log(`Type: ${component.contentType}`);
13696
13882
  console.log();
13697
- console.log(chalk37.dim('\u{1F4A1} D\xF9ng "j skills add ' + name + '" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13883
+ console.log(chalk38.dim('\u{1F4A1} D\xF9ng "j skills add ' + name + '" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13698
13884
  } catch (error) {
13699
- console.log(chalk37.red(`\u274C Kh\xF4ng t\xECm th\u1EA5y skill "${name}" tr\xEAn server.`));
13885
+ console.log(chalk38.red(`\u274C Kh\xF4ng t\xECm th\u1EA5y skill "${name}" tr\xEAn server.`));
13700
13886
  process.exit(1);
13701
13887
  }
13702
13888
  } else {
13703
13889
  const projectRoot = process.cwd();
13704
13890
  const skill = await skillsService.getSkillInfo(projectRoot, name);
13705
13891
  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'));
13892
+ console.log(chalk38.red(`\u274C Skill "${name}" ch\u01B0a \u0111\u01B0\u1EE3c c\xE0i \u0111\u1EB7t.`));
13893
+ console.log(chalk38.dim('\u{1F4A1} D\xF9ng "j skills info ' + name + ' --server" \u0111\u1EC3 xem tr\xEAn server'));
13894
+ console.log(chalk38.dim(' ho\u1EB7c "j skills add ' + name + '" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
13709
13895
  process.exit(1);
13710
13896
  }
13711
13897
  console.log(`
13712
- \u{1F6E0} ${chalk37.bold(skill.name)}
13898
+ \u{1F6E0} ${chalk38.bold(skill.name)}
13713
13899
  `);
13714
13900
  console.log(`Slug: ${skill.slug}`);
13715
- console.log(`Description: ${skill.description || chalk37.dim("(none)")}`);
13901
+ console.log(`Description: ${skill.description || chalk38.dim("(none)")}`);
13716
13902
  console.log(`Path: ${skill.path}`);
13717
13903
  console.log(`Files: ${skill.fileCount}`);
13718
13904
  console.log();
13719
- console.log(chalk37.dim('\u{1F4A1} D\xF9ng "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
13905
+ console.log(chalk38.dim('\u{1F4A1} D\xF9ng "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
13720
13906
  }
13721
13907
  });
13722
13908
  }
13723
13909
 
13724
13910
  // src/commands/skills/sync.ts
13725
- import { Command as Command73 } from "commander";
13726
- import chalk38 from "chalk";
13911
+ import { Command as Command74 } from "commander";
13912
+ import chalk39 from "chalk";
13727
13913
  import { confirm as confirm15, checkbox as checkbox8 } from "@inquirer/prompts";
13728
13914
  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) => {
13915
+ return new Command74("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
13916
  const skillsService = new SkillsService();
13731
13917
  const projectRoot = process.cwd();
13732
13918
  const headless = options.yes === true;
13733
- console.log(chalk38.bold.cyan("\n\u{1F504} Sync skills sang IDE(s)\n"));
13919
+ console.log(chalk39.bold.cyan("\n\u{1F504} Sync skills sang IDE(s)\n"));
13734
13920
  const localSkills = await skillsService.listLocal(projectRoot);
13735
13921
  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'));
13922
+ console.log(chalk39.yellow("\u26A0\uFE0F Kh\xF4ng c\xF3 skills n\xE0o trong .jai1/skills/"));
13923
+ console.log(chalk39.dim('\u{1F4A1} Ch\u1EA1y "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t skills tr\u01B0\u1EDBc'));
13738
13924
  process.exit(1);
13739
13925
  }
13740
13926
  console.log(`\u{1F4C1} T\xECm th\u1EA5y ${localSkills.length} skill(s) trong .jai1/skills/`);
@@ -13766,7 +13952,7 @@ function createSkillsSyncCommand() {
13766
13952
  theme: checkboxTheme
13767
13953
  });
13768
13954
  if (selectedIdes.length === 0) {
13769
- console.log(chalk38.yellow("\n\u26A0\uFE0F Ch\u01B0a ch\u1ECDn IDE n\xE0o!"));
13955
+ console.log(chalk39.yellow("\n\u26A0\uFE0F Ch\u01B0a ch\u1ECDn IDE n\xE0o!"));
13770
13956
  process.exit(0);
13771
13957
  }
13772
13958
  }
@@ -13781,7 +13967,7 @@ function createSkillsSyncCommand() {
13781
13967
  console.log(` Total: ${totalFiles} skill folder(s) s\u1EBD \u0111\u01B0\u1EE3c sync
13782
13968
  `);
13783
13969
  if (options.dryRun) {
13784
- console.log(chalk38.dim("\u{1F50D} DRY RUN - Kh\xF4ng c\xF3 file n\xE0o \u0111\u01B0\u1EE3c ghi\n"));
13970
+ console.log(chalk39.dim("\u{1F50D} DRY RUN - Kh\xF4ng c\xF3 file n\xE0o \u0111\u01B0\u1EE3c ghi\n"));
13785
13971
  return;
13786
13972
  }
13787
13973
  if (!headless) {
@@ -13790,25 +13976,25 @@ function createSkillsSyncCommand() {
13790
13976
  default: true
13791
13977
  });
13792
13978
  if (!confirmed) {
13793
- console.log(chalk38.yellow("\n\u274C \u0110\xE3 h\u1EE7y sync.\n"));
13979
+ console.log(chalk39.yellow("\n\u274C \u0110\xE3 h\u1EE7y sync.\n"));
13794
13980
  process.exit(0);
13795
13981
  }
13796
13982
  }
13797
- console.log(chalk38.cyan("\n\u{1F504} \u0110ang sync...\n"));
13983
+ console.log(chalk39.cyan("\n\u{1F504} \u0110ang sync...\n"));
13798
13984
  const result = await skillsService.syncToIdes(
13799
13985
  projectRoot,
13800
13986
  selectedIdes,
13801
13987
  selectedSlugs,
13802
13988
  (res) => {
13803
13989
  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)}`);
13990
+ const statusColor = res.status === "error" ? chalk39.red : chalk39.green;
13991
+ console.log(` ${statusColor(icon)} ${res.ide}: ${res.skill} \u2192 ${chalk39.dim(res.path)}`);
13806
13992
  if (res.status === "error" && res.error) {
13807
- console.log(` ${chalk38.red("Error:")} ${res.error}`);
13993
+ console.log(` ${chalk39.red("Error:")} ${res.error}`);
13808
13994
  }
13809
13995
  }
13810
13996
  );
13811
- console.log(chalk38.green("\n\u2705 Sync ho\xE0n t\u1EA5t!\n"));
13997
+ console.log(chalk39.green("\n\u2705 Sync ho\xE0n t\u1EA5t!\n"));
13812
13998
  console.log(` Created: ${result.created}`);
13813
13999
  console.log(` Updated: ${result.updated}`);
13814
14000
  if (result.errors > 0) {
@@ -13821,27 +14007,27 @@ function createSkillsSyncCommand() {
13821
14007
  // src/commands/skills/index.ts
13822
14008
  function showSkillsHelp() {
13823
14009
  const cli = getCliName();
13824
- console.log(chalk39.bold.cyan("\u{1F6E0} " + cli + " skills") + chalk39.dim(" - Qu\u1EA3n l\xFD agent skills"));
14010
+ console.log(chalk40.bold.cyan("\u{1F6E0} " + cli + " skills") + chalk40.dim(" - Qu\u1EA3n l\xFD agent skills"));
13825
14011
  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`);
14012
+ console.log(chalk40.bold("C\xE1c l\u1EC7nh:"));
14013
+ console.log(` ${chalk40.cyan("find")} T\xECm ki\u1EBFm skills tr\xEAn server ho\u1EB7c npm`);
14014
+ console.log(` ${chalk40.cyan("add")} C\xE0i \u0111\u1EB7t skill v\xE0o .jai1/skills/`);
14015
+ console.log(` ${chalk40.cyan("list")} Li\u1EC7t k\xEA skills \u0111\xE3 c\xE0i ho\u1EB7c c\xF3 s\u1EB5n`);
14016
+ console.log(` ${chalk40.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t skill`);
14017
+ console.log(` ${chalk40.cyan("sync")} \u0110\u1ED3ng b\u1ED9 skills sang c\xE1c IDE`);
13832
14018
  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`));
14019
+ console.log(chalk40.bold("V\xED d\u1EE5:"));
14020
+ console.log(chalk40.dim(` $ ${cli} skills find audit`));
14021
+ console.log(chalk40.dim(` $ ${cli} skills find "react" --skillsh`));
14022
+ console.log(chalk40.dim(` $ ${cli} skills add brainstorming`));
14023
+ console.log(chalk40.dim(` $ ${cli} skills add vercel/next-skills --skillsh`));
14024
+ console.log(chalk40.dim(` $ ${cli} skills list`));
14025
+ console.log(chalk40.dim(` $ ${cli} skills sync --all -y`));
13840
14026
  console.log();
13841
- console.log(chalk39.dim(`Ch\u1EA1y "${cli} skills <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt`));
14027
+ console.log(chalk40.dim(`Ch\u1EA1y "${cli} skills <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt`));
13842
14028
  }
13843
14029
  function createSkillsCommand() {
13844
- const cmd = new Command74("skills").alias("s").description("Manage agent skills (search, install, sync to IDEs)").action(() => {
14030
+ const cmd = new Command75("skills").alias("s").description("Manage agent skills (search, install, sync to IDEs)").action(() => {
13845
14031
  showSkillsHelp();
13846
14032
  });
13847
14033
  cmd.addCommand(createSkillsFindCommand());
@@ -13853,9 +14039,9 @@ function createSkillsCommand() {
13853
14039
  }
13854
14040
 
13855
14041
  // src/commands/upgrade.ts
13856
- import { Command as Command75 } from "commander";
14042
+ import { Command as Command76 } from "commander";
13857
14043
  import { confirm as confirm16 } from "@inquirer/prompts";
13858
- import { execSync as execSync4 } from "child_process";
14044
+ import { execSync as execSync5 } from "child_process";
13859
14045
  var colors2 = {
13860
14046
  yellow: "\x1B[33m",
13861
14047
  green: "\x1B[32m",
@@ -13865,7 +14051,7 @@ var colors2 = {
13865
14051
  bold: "\x1B[1m"
13866
14052
  };
13867
14053
  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) => {
14054
+ return new Command76("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
14055
  await handleUpgrade(options);
13870
14056
  });
13871
14057
  }
@@ -13926,7 +14112,7 @@ ${colors2.cyan}\u{1F4E5} Installing latest version...${colors2.reset}
13926
14112
  const packageManager2 = detectPackageManager();
13927
14113
  const installCommand = getInstallCommand(packageManager2);
13928
14114
  console.log(`${colors2.cyan}Using ${packageManager2}...${colors2.reset}`);
13929
- execSync4(installCommand, {
14115
+ execSync5(installCommand, {
13930
14116
  stdio: "inherit",
13931
14117
  env: { ...process.env, FORCE_COLOR: "1" }
13932
14118
  });
@@ -13975,7 +14161,7 @@ function isNewerVersion2(remote, local) {
13975
14161
  }
13976
14162
  function detectPackageManager() {
13977
14163
  try {
13978
- const jai1Path = execSync4("which j || which jai1 || where j || where jai1", {
14164
+ const jai1Path = execSync5("which j || which jai1 || where j || where jai1", {
13979
14165
  encoding: "utf-8",
13980
14166
  stdio: ["pipe", "pipe", "ignore"]
13981
14167
  }).trim();
@@ -14013,11 +14199,11 @@ function getInstallCommand(packageManager2) {
14013
14199
  }
14014
14200
 
14015
14201
  // src/commands/clean.ts
14016
- import { Command as Command76 } from "commander";
14202
+ import { Command as Command77 } from "commander";
14017
14203
  import { confirm as confirm17, select as select6 } from "@inquirer/prompts";
14018
- import { join as join20 } from "path";
14204
+ import { join as join21 } from "path";
14019
14205
  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) => {
14206
+ return new Command77("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
14207
  await handleClean(options);
14022
14208
  });
14023
14209
  }
@@ -14028,7 +14214,7 @@ async function handleClean(options) {
14028
14214
  {
14029
14215
  name: "Backups",
14030
14216
  description: "Component backup files (.jai1_backup/)",
14031
- path: join20(cwd, ".jai1_backup"),
14217
+ path: join21(cwd, ".jai1_backup"),
14032
14218
  check: async () => {
14033
14219
  const backups = await service.listBackups(cwd);
14034
14220
  return { exists: backups.length > 0, count: backups.length };
@@ -14131,7 +14317,7 @@ async function cleanTarget(target, skipConfirm) {
14131
14317
  }
14132
14318
 
14133
14319
  // src/commands/redmine/check.ts
14134
- import { Command as Command77 } from "commander";
14320
+ import { Command as Command78 } from "commander";
14135
14321
 
14136
14322
  // src/services/redmine-config.service.ts
14137
14323
  import { readFile as readFile7 } from "fs/promises";
@@ -14438,7 +14624,7 @@ async function checkConnectivity(config) {
14438
14624
 
14439
14625
  // src/commands/redmine/check.ts
14440
14626
  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) => {
14627
+ const cmd = new Command78("check").description("Check Redmine connectivity").option("-c, --config <path>", "Config file path", "redmine.config.yaml").option("--json", "Output as JSON").action(async (options) => {
14442
14628
  await handleRedmineCheck(options);
14443
14629
  });
14444
14630
  return cmd;
@@ -14466,7 +14652,7 @@ async function handleRedmineCheck(options) {
14466
14652
  }
14467
14653
 
14468
14654
  // src/commands/redmine/sync-issue.ts
14469
- import { Command as Command78 } from "commander";
14655
+ import { Command as Command79 } from "commander";
14470
14656
 
14471
14657
  // src/sync-issue.ts
14472
14658
  import { resolve as resolve3, relative } from "path";
@@ -14850,7 +15036,7 @@ function extractIssueIdFromUrl(url) {
14850
15036
 
14851
15037
  // src/commands/redmine/sync-issue.ts
14852
15038
  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) => {
15039
+ const cmd = new Command79("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
15040
  await handleSyncIssue(options);
14855
15041
  });
14856
15042
  return cmd;
@@ -14894,7 +15080,7 @@ async function handleSyncIssue(options) {
14894
15080
  }
14895
15081
 
14896
15082
  // src/commands/redmine/sync-project.ts
14897
- import { Command as Command79 } from "commander";
15083
+ import { Command as Command80 } from "commander";
14898
15084
 
14899
15085
  // src/sync-project.ts
14900
15086
  async function syncProject(config, options = {}) {
@@ -14964,7 +15150,7 @@ async function syncProject(config, options = {}) {
14964
15150
 
14965
15151
  // src/commands/redmine/sync-project.ts
14966
15152
  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) => {
15153
+ const cmd = new Command80("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
15154
  await handleSyncProject(options);
14969
15155
  });
14970
15156
  return cmd;
@@ -15019,12 +15205,12 @@ async function handleSyncProject(options) {
15019
15205
  }
15020
15206
 
15021
15207
  // src/commands/framework/info.ts
15022
- import { Command as Command80 } from "commander";
15208
+ import { Command as Command81 } from "commander";
15023
15209
  import { promises as fs28 } from "fs";
15024
- import { join as join21 } from "path";
15210
+ import { join as join22 } from "path";
15025
15211
  import { homedir as homedir5 } from "os";
15026
15212
  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) => {
15213
+ const cmd = new Command81("info").description("Show client configuration and status").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
15028
15214
  await handleInfo(options);
15029
15215
  });
15030
15216
  return cmd;
@@ -15035,7 +15221,7 @@ async function handleInfo(options) {
15035
15221
  if (!config) {
15036
15222
  throw new ValidationError(`Not initialized. Run "${getCliName()} auth" first.`);
15037
15223
  }
15038
- const frameworkPath = join21(homedir5(), ".jai1", "framework");
15224
+ const frameworkPath = join22(homedir5(), ".jai1", "framework");
15039
15225
  const projectStatus = await getProjectStatus2();
15040
15226
  const info = {
15041
15227
  configPath: configService.getConfigPath(),
@@ -15070,7 +15256,7 @@ function maskKey4(key) {
15070
15256
  return "****" + key.slice(-4);
15071
15257
  }
15072
15258
  async function getProjectStatus2() {
15073
- const projectJai1 = join21(process.cwd(), ".jai1");
15259
+ const projectJai1 = join22(process.cwd(), ".jai1");
15074
15260
  try {
15075
15261
  await fs28.access(projectJai1);
15076
15262
  return { exists: true, version: "Synced" };
@@ -15080,9 +15266,9 @@ async function getProjectStatus2() {
15080
15266
  }
15081
15267
 
15082
15268
  // src/commands/self-update.ts
15083
- import { Command as Command81 } from "commander";
15269
+ import { Command as Command82 } from "commander";
15084
15270
  import { confirm as confirm18 } from "@inquirer/prompts";
15085
- import { execSync as execSync5 } from "child_process";
15271
+ import { execSync as execSync6 } from "child_process";
15086
15272
  var colors3 = {
15087
15273
  yellow: "\x1B[33m",
15088
15274
  green: "\x1B[32m",
@@ -15092,7 +15278,7 @@ var colors3 = {
15092
15278
  bold: "\x1B[1m"
15093
15279
  };
15094
15280
  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) => {
15281
+ return new Command82("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
15282
  await handleSelfUpdate(options);
15097
15283
  });
15098
15284
  }
@@ -15153,7 +15339,7 @@ ${colors3.cyan}\u{1F4E5} Installing latest version...${colors3.reset}
15153
15339
  const packageManager2 = detectPackageManager2();
15154
15340
  const installCommand = getInstallCommand2(packageManager2);
15155
15341
  console.log(`${colors3.cyan}Using ${packageManager2}...${colors3.reset}`);
15156
- execSync5(installCommand, {
15342
+ execSync6(installCommand, {
15157
15343
  stdio: "inherit",
15158
15344
  env: { ...process.env, FORCE_COLOR: "1" }
15159
15345
  });
@@ -15200,17 +15386,17 @@ function detectPackageManager2() {
15200
15386
  if (userAgent.includes("yarn")) return "yarn";
15201
15387
  if (userAgent.includes("bun")) return "bun";
15202
15388
  try {
15203
- execSync5("pnpm --version", { stdio: "ignore" });
15389
+ execSync6("pnpm --version", { stdio: "ignore" });
15204
15390
  return "pnpm";
15205
15391
  } catch {
15206
15392
  }
15207
15393
  try {
15208
- execSync5("yarn --version", { stdio: "ignore" });
15394
+ execSync6("yarn --version", { stdio: "ignore" });
15209
15395
  return "yarn";
15210
15396
  } catch {
15211
15397
  }
15212
15398
  try {
15213
- execSync5("bun --version", { stdio: "ignore" });
15399
+ execSync6("bun --version", { stdio: "ignore" });
15214
15400
  return "bun";
15215
15401
  } catch {
15216
15402
  }
@@ -15232,10 +15418,10 @@ function getInstallCommand2(packageManager2) {
15232
15418
  }
15233
15419
 
15234
15420
  // src/commands/clear-backups.ts
15235
- import { Command as Command82 } from "commander";
15421
+ import { Command as Command83 } from "commander";
15236
15422
  import { confirm as confirm19 } from "@inquirer/prompts";
15237
15423
  function createClearBackupsCommand() {
15238
- return new Command82("clear-backups").description("Clear backup files").option("-y, --yes", "Skip confirmation").action(async (options) => {
15424
+ return new Command83("clear-backups").description("Clear backup files").option("-y, --yes", "Skip confirmation").action(async (options) => {
15239
15425
  const service = new ComponentsService();
15240
15426
  const backups = await service.listBackups(process.cwd());
15241
15427
  if (backups.length === 0) {
@@ -15260,11 +15446,11 @@ function createClearBackupsCommand() {
15260
15446
  }
15261
15447
 
15262
15448
  // src/commands/vscode/index.ts
15263
- import { Command as Command83 } from "commander";
15449
+ import { Command as Command84 } from "commander";
15264
15450
  import { checkbox as checkbox9, confirm as confirm20, select as select7 } from "@inquirer/prompts";
15265
15451
  import fs29 from "fs/promises";
15266
15452
  import path12 from "path";
15267
- import { existsSync as existsSync3 } from "fs";
15453
+ import { existsSync as existsSync4 } from "fs";
15268
15454
  var PERFORMANCE_GROUPS2 = {
15269
15455
  telemetry: {
15270
15456
  name: "Telemetry",
@@ -15400,7 +15586,7 @@ var PERFORMANCE_GROUPS2 = {
15400
15586
  }
15401
15587
  };
15402
15588
  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");
15589
+ const vscodeCommand = new Command84("vscode").description("Qu\u1EA3n l\xFD c\xE0i \u0111\u1EB7t VSCode cho d\u1EF1 \xE1n hi\u1EC7n t\u1EA1i");
15404
15590
  vscodeCommand.action(async () => {
15405
15591
  await interactiveMode2();
15406
15592
  });
@@ -15494,12 +15680,12 @@ async function applyGroups2(groupKeys, action) {
15494
15680
  console.log(' \u{1F4A1} Ch\u1EA1y "jai1 vscode list" \u0111\u1EC3 xem danh s\xE1ch nh\xF3m c\xF3 s\u1EB5n.');
15495
15681
  return;
15496
15682
  }
15497
- if (!existsSync3(vscodeDir)) {
15683
+ if (!existsSync4(vscodeDir)) {
15498
15684
  await fs29.mkdir(vscodeDir, { recursive: true });
15499
15685
  console.log("\u{1F4C1} \u0110\xE3 t\u1EA1o th\u01B0 m\u1EE5c .vscode/");
15500
15686
  }
15501
15687
  let currentSettings = {};
15502
- if (existsSync3(settingsPath)) {
15688
+ if (existsSync4(settingsPath)) {
15503
15689
  try {
15504
15690
  const content = await fs29.readFile(settingsPath, "utf-8");
15505
15691
  currentSettings = JSON.parse(content);
@@ -15549,7 +15735,7 @@ async function applyGroups2(groupKeys, action) {
15549
15735
  async function resetSettings2(groupKeys) {
15550
15736
  const vscodeDir = path12.join(process.cwd(), ".vscode");
15551
15737
  const settingsPath = path12.join(vscodeDir, "settings.json");
15552
- if (!existsSync3(settingsPath)) {
15738
+ if (!existsSync4(settingsPath)) {
15553
15739
  console.log("\n\u26A0\uFE0F Kh\xF4ng t\xECm th\u1EA5y file settings.json");
15554
15740
  return;
15555
15741
  }
@@ -15571,10 +15757,10 @@ async function resetSettings2(groupKeys) {
15571
15757
  }
15572
15758
 
15573
15759
  // src/commands/migrate-ide.ts
15574
- import { Command as Command84 } from "commander";
15760
+ import { Command as Command85 } from "commander";
15575
15761
  import { checkbox as checkbox10, confirm as confirm21 } from "@inquirer/prompts";
15576
15762
  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) => {
15763
+ const cmd = new Command85("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
15764
  await runMigrateIde(options);
15579
15765
  });
15580
15766
  return cmd;
@@ -15683,20 +15869,20 @@ async function runMigrateIde(options) {
15683
15869
 
15684
15870
  // src/utils/help-formatter.ts
15685
15871
  import boxen4 from "boxen";
15686
- import chalk40 from "chalk";
15872
+ import chalk41 from "chalk";
15687
15873
  import gradient from "gradient-string";
15688
15874
  import figlet from "figlet";
15689
15875
  function showCustomHelp(version) {
15690
15876
  const title = figlet.textSync("JAI1", { font: "Small" });
15691
15877
  console.log(gradient.pastel(title));
15692
15878
  console.log(
15693
- boxen4(chalk40.cyan(`Agentic Coding CLI v${version}`), {
15879
+ boxen4(chalk41.cyan(`Agentic Coding CLI v${version}`), {
15694
15880
  padding: { left: 1, right: 1, top: 0, bottom: 0 },
15695
15881
  borderStyle: "round",
15696
15882
  borderColor: "cyan"
15697
15883
  })
15698
15884
  );
15699
- console.log(chalk40.bold("\n\u{1F527} Thi\u1EBFt l\u1EADp & Th\xF4ng tin"));
15885
+ console.log(chalk41.bold("\n\u{1F527} Thi\u1EBFt l\u1EADp & Th\xF4ng tin"));
15700
15886
  console.log(" auth X\xE1c th\u1EF1c v\xE0 c\u1EA5u h\xECnh client");
15701
15887
  console.log(" status Hi\u1EC3n th\u1ECB tr\u1EA1ng th\xE1i c\u1EA5u h\xECnh");
15702
15888
  console.log(" client-info T\u1EA1o th\xF4ng tin client \u0111\u1EC3 g\u1EEDi \u0111\u1ED9i ph\xE1t tri\u1EC3n");
@@ -15704,43 +15890,43 @@ function showCustomHelp(version) {
15704
15890
  console.log(" guide H\u01B0\u1EDBng d\u1EABn s\u1EED d\u1EE5ng nhanh");
15705
15891
  console.log(" quickstart B\u1EAFt \u0111\u1EA7u t\u1EEB \u0111\xE2u? (theo t\xECnh hu\u1ED1ng)");
15706
15892
  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"));
15893
+ console.log(chalk41.bold("\n\u{1F4E6} Qu\u1EA3n l\xFD Components"));
15708
15894
  console.log(" apply C\xE0i \u0111\u1EB7t components (interactive)");
15709
15895
  console.log(" update C\u1EADp nh\u1EADt components \u0111\xE3 c\xE0i");
15710
15896
  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"));
15897
+ console.log(chalk41.bold("\n\u{1F5A5}\uFE0F IDE & T\xEDch h\u1EE3p"));
15712
15898
  console.log(" ide L\u1EC7nh c\u1EA5u h\xECnh IDE");
15713
15899
  console.log(" chat Chat AI v\u1EDBi Jai1 LLM Proxy");
15714
15900
  console.log(" openai-keys Th\xF4ng tin API credentials");
15715
- console.log(chalk40.bold("\n\u{1F916} AI Tools"));
15901
+ console.log(chalk41.bold("\n\u{1F916} AI Tools"));
15716
15902
  console.log(" translate D\u1ECBch v\u0103n b\u1EA3n/file b\u1EB1ng AI");
15717
15903
  console.log(" image T\u1EA1o \u1EA3nh (Coming Soon)");
15718
15904
  console.log(" stats Th\u1ED1ng k\xEA s\u1EED d\u1EE5ng LLM");
15719
15905
  console.log(" feedback G\u1EEDi b\xE1o c\xE1o/\u0111\u1EC1 xu\u1EA5t");
15720
- console.log(chalk40.bold("\n\u{1F4C1} Project"));
15906
+ console.log(chalk41.bold("\n\u{1F4C1} Project"));
15721
15907
  console.log(" kit Qu\u1EA3n l\xFD starter kits");
15722
15908
  console.log(" tasks (t) Qu\u1EA3n l\xFD tasks ph\xE1t tri\u1EC3n");
15723
15909
  console.log(" rules Qu\u1EA3n l\xFD rule presets");
15724
15910
  console.log(" deps Qu\u1EA3n l\xFD dependencies");
15725
15911
  console.log(" redmine Redmine context sync");
15726
- console.log(chalk40.bold("\n\u2699\uFE0F B\u1EA3o tr\xEC"));
15912
+ console.log(chalk41.bold("\n\u2699\uFE0F B\u1EA3o tr\xEC"));
15727
15913
  console.log(" upgrade C\u1EADp nh\u1EADt CLI client");
15728
15914
  console.log(" clean D\u1ECDn d\u1EB9p cache/backup");
15729
15915
  console.log(" utils Developer utilities");
15730
15916
  const name = getCliName();
15731
- console.log(chalk40.dim(`
15917
+ console.log(chalk41.dim(`
15732
15918
  S\u1EED d\u1EE5ng: ${name} [l\u1EC7nh] --help \u0111\u1EC3 xem chi ti\u1EBFt`));
15733
15919
  }
15734
15920
  function showUnknownCommand(commandName) {
15735
- console.error(chalk40.red(`\u274C L\u1EC7nh kh\xF4ng t\u1ED3n t\u1EA1i: ${commandName}`));
15921
+ console.error(chalk41.red(`\u274C L\u1EC7nh kh\xF4ng t\u1ED3n t\u1EA1i: ${commandName}`));
15736
15922
  const name = getCliName();
15737
- console.error(chalk40.dim(`
15923
+ console.error(chalk41.dim(`
15738
15924
  G\u1EE3i \xFD: Ch\u1EA1y ${name} --help \u0111\u1EC3 xem danh s\xE1ch l\u1EC7nh`));
15739
15925
  }
15740
15926
 
15741
15927
  // src/cli.ts
15742
15928
  checkNodeVersion();
15743
- var program = new Command85();
15929
+ var program = new Command86();
15744
15930
  if (process.argv.includes("-v") || process.argv.includes("--version")) {
15745
15931
  console.log(package_default.version);
15746
15932
  if (!process.argv.includes("--skip-update-check")) {
@@ -15779,11 +15965,12 @@ program.addCommand(createTasksCommand());
15779
15965
  program.addCommand(createKitCommand());
15780
15966
  program.addCommand(createRulesCommand());
15781
15967
  program.addCommand(createSkillsCommand());
15968
+ program.addCommand(createHooksCommand());
15782
15969
  program.addCommand(createUpgradeCommand());
15783
15970
  program.addCommand(createCleanCommand());
15784
- var redmineCommand = new Command85("redmine").description("Redmine context sync commands");
15971
+ var redmineCommand = new Command86("redmine").description("Redmine context sync commands");
15785
15972
  redmineCommand.addCommand(createRedmineCheckCommand());
15786
- var syncCommand = new Command85("sync").description("Sync Redmine issues to markdown files");
15973
+ var syncCommand = new Command86("sync").description("Sync Redmine issues to markdown files");
15787
15974
  syncCommand.addCommand(createSyncIssueCommand());
15788
15975
  syncCommand.addCommand(createSyncProjectCommand());
15789
15976
  redmineCommand.addCommand(syncCommand);