@jvittechs/j 1.0.37 → 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/{chunk-NBOT35QM.js → chunk-KOEMGOAP.js} +5 -1
- package/dist/chunk-KOEMGOAP.js.map +1 -0
- package/dist/cli.js +678 -491
- package/dist/cli.js.map +1 -1
- package/dist/{summary-KPCAN522.js → summary-O75DZKM2.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-NBOT35QM.js.map +0 -1
- /package/dist/{summary-KPCAN522.js.map → summary-O75DZKM2.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
STATUS_ICONS,
|
|
14
14
|
TaskService,
|
|
15
15
|
createTaskSummaryCommand
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-KOEMGOAP.js";
|
|
17
17
|
|
|
18
18
|
// src/utils/node-version-check.ts
|
|
19
19
|
import chalk from "chalk";
|
|
@@ -49,7 +49,7 @@ function checkNodeVersion() {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
// src/cli.ts
|
|
52
|
-
import { Command as
|
|
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.
|
|
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
|
|
10804
|
+
import { Command as Command58 } from "commander";
|
|
10619
10805
|
|
|
10620
10806
|
// src/commands/tasks/add.ts
|
|
10621
|
-
import { Command as
|
|
10622
|
-
import
|
|
10807
|
+
import { Command as Command48 } from "commander";
|
|
10808
|
+
import chalk21 from "chalk";
|
|
10623
10809
|
function createTaskAddCommand() {
|
|
10624
|
-
return new
|
|
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(
|
|
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(
|
|
10645
|
-
console.log(` ${
|
|
10646
|
-
console.log(` ${
|
|
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(` ${
|
|
10834
|
+
console.log(` ${chalk21.dim("Parent:")} ${task.parent}`);
|
|
10649
10835
|
}
|
|
10650
10836
|
if (task.tags.length > 0) {
|
|
10651
|
-
console.log(` ${
|
|
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
|
|
10658
|
-
import
|
|
10843
|
+
import { Command as Command49 } from "commander";
|
|
10844
|
+
import chalk22 from "chalk";
|
|
10659
10845
|
function createTaskListCommand() {
|
|
10660
|
-
return new
|
|
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(
|
|
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(
|
|
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} ${
|
|
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 +=
|
|
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 +=
|
|
10886
|
+
line += chalk22.red(` (blocked: ${blockedBy.join(", ")})`);
|
|
10701
10887
|
}
|
|
10702
10888
|
if (task.parent) {
|
|
10703
|
-
line +=
|
|
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
|
|
10710
|
-
import
|
|
10895
|
+
import { Command as Command50 } from "commander";
|
|
10896
|
+
import chalk23 from "chalk";
|
|
10711
10897
|
function createTaskReadyCommand() {
|
|
10712
|
-
return new
|
|
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(
|
|
10721
|
-
console.log(
|
|
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(
|
|
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} ${
|
|
10914
|
+
let line = ` P${task.priority}${icon} ${chalk23.dim(task.id)} ${task.title}`;
|
|
10729
10915
|
if (task.parent) {
|
|
10730
|
-
line +=
|
|
10916
|
+
line += chalk23.dim(` [${task.parent}]`);
|
|
10731
10917
|
}
|
|
10732
10918
|
console.log(line);
|
|
10733
10919
|
}
|
|
10734
10920
|
console.log();
|
|
10735
|
-
console.log(
|
|
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
|
|
10741
|
-
import
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
10761
|
-
console.log(
|
|
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(
|
|
10967
|
+
console.log(chalk24.green(`\u2705 ${task.id} \u2192 ${parts.join(" | ")}`));
|
|
10782
10968
|
} catch (error) {
|
|
10783
|
-
console.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
|
|
10791
|
-
import
|
|
10976
|
+
import { Command as Command52 } from "commander";
|
|
10977
|
+
import chalk25 from "chalk";
|
|
10792
10978
|
function createTaskShowCommand() {
|
|
10793
|
-
return new
|
|
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(
|
|
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(
|
|
10995
|
+
console.log(chalk25.bold(`
|
|
10810
10996
|
\u{1F4CC} ${task.id}: ${task.title}
|
|
10811
10997
|
`));
|
|
10812
|
-
console.log(` ${
|
|
10813
|
-
console.log(` ${
|
|
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(` ${
|
|
11001
|
+
console.log(` ${chalk25.dim("Parent:")} ${task.parent}`);
|
|
10816
11002
|
}
|
|
10817
11003
|
if (task.assigned_to) {
|
|
10818
|
-
console.log(` ${
|
|
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(` ${
|
|
11007
|
+
console.log(` ${chalk25.dim("Depends on:")} ${task.depends_on.join(", ")}`);
|
|
10822
11008
|
if (blocked) {
|
|
10823
|
-
console.log(` ${
|
|
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(` ${
|
|
11013
|
+
console.log(` ${chalk25.dim("Tags:")} ${task.tags.join(", ")}`);
|
|
10828
11014
|
}
|
|
10829
11015
|
if (task.branch) {
|
|
10830
|
-
console.log(` ${
|
|
11016
|
+
console.log(` ${chalk25.dim("Branch:")} ${task.branch}`);
|
|
10831
11017
|
}
|
|
10832
11018
|
if (task.notes) {
|
|
10833
|
-
console.log(` ${
|
|
11019
|
+
console.log(` ${chalk25.dim("Notes:")} ${task.notes}`);
|
|
10834
11020
|
}
|
|
10835
|
-
console.log(` ${
|
|
10836
|
-
console.log(` ${
|
|
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(
|
|
11031
|
+
console.log(chalk25.dim(`No tasks for parent: ${query}`));
|
|
10846
11032
|
return;
|
|
10847
11033
|
}
|
|
10848
|
-
console.log(
|
|
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} ${
|
|
10857
|
-
if (task.assigned_to) line +=
|
|
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 +=
|
|
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
|
|
10871
|
-
import
|
|
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
|
|
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(
|
|
10883
|
-
console.log(
|
|
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(
|
|
10894
|
-
console.log(` ${
|
|
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(` ${
|
|
11082
|
+
console.log(` ${chalk26.dim("Parent:")} ${top.parent}`);
|
|
10897
11083
|
}
|
|
10898
11084
|
if (ready.length > 1) {
|
|
10899
|
-
console.log(
|
|
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(
|
|
11093
|
+
console.log(chalk26.dim("\nCancelled."));
|
|
10908
11094
|
return;
|
|
10909
11095
|
}
|
|
10910
11096
|
const picked = await service.pick(top.id);
|
|
10911
|
-
console.log(
|
|
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
|
|
10918
|
-
import
|
|
11103
|
+
import { Command as Command54 } from "commander";
|
|
11104
|
+
import chalk27 from "chalk";
|
|
10919
11105
|
function createTaskDoneCommand() {
|
|
10920
|
-
return new
|
|
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(
|
|
11114
|
+
console.log(chalk27.green(`\u2705 ${task.id}: ${task.title} \u2192 done`));
|
|
10929
11115
|
} catch (error) {
|
|
10930
|
-
console.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
|
|
10938
|
-
import
|
|
11123
|
+
import { Command as Command55 } from "commander";
|
|
11124
|
+
import chalk28 from "chalk";
|
|
10939
11125
|
function createTaskDepCommand() {
|
|
10940
|
-
return new
|
|
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(
|
|
10949
|
-
console.log(
|
|
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(
|
|
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
|
|
10959
|
-
import
|
|
11144
|
+
import { Command as Command56 } from "commander";
|
|
11145
|
+
import chalk29 from "chalk";
|
|
10960
11146
|
function createTaskSyncCommand() {
|
|
10961
|
-
return new
|
|
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(
|
|
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(
|
|
11157
|
+
console.log(chalk29.green(` \u2193 ${result.merged} tasks merged`));
|
|
10972
11158
|
} else {
|
|
10973
|
-
console.log(
|
|
11159
|
+
console.log(chalk29.dim(` \u2193 Already up to date`));
|
|
10974
11160
|
}
|
|
10975
11161
|
} catch (error) {
|
|
10976
|
-
console.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(
|
|
11167
|
+
console.log(chalk29.dim(`\u23F3 Pushing tasks to origin/${branch}...`));
|
|
10982
11168
|
try {
|
|
10983
11169
|
await service.syncPush();
|
|
10984
|
-
console.log(
|
|
11170
|
+
console.log(chalk29.green(` \u2191 Tasks pushed to origin/${branch}`));
|
|
10985
11171
|
} catch (error) {
|
|
10986
|
-
console.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(
|
|
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
|
|
10996
|
-
import
|
|
11181
|
+
import { Command as Command57 } from "commander";
|
|
11182
|
+
import chalk30 from "chalk";
|
|
10997
11183
|
var GUIDE_TEXT = `
|
|
10998
|
-
${
|
|
10999
|
-
|
|
11000
|
-
${
|
|
11001
|
-
${
|
|
11002
|
-
${
|
|
11003
|
-
${
|
|
11004
|
-
${
|
|
11005
|
-
${
|
|
11006
|
-
|
|
11007
|
-
${
|
|
11008
|
-
${
|
|
11009
|
-
${
|
|
11010
|
-
${
|
|
11011
|
-
${
|
|
11012
|
-
|
|
11013
|
-
${
|
|
11014
|
-
${
|
|
11015
|
-
${
|
|
11016
|
-
${
|
|
11017
|
-
${
|
|
11018
|
-
|
|
11019
|
-
${
|
|
11020
|
-
${
|
|
11021
|
-
${
|
|
11022
|
-
${
|
|
11023
|
-
${
|
|
11024
|
-
${
|
|
11025
|
-
${
|
|
11026
|
-
|
|
11027
|
-
${
|
|
11028
|
-
${
|
|
11029
|
-
${
|
|
11030
|
-
|
|
11031
|
-
${
|
|
11032
|
-
${
|
|
11033
|
-
${
|
|
11034
|
-
${
|
|
11035
|
-
|
|
11036
|
-
${
|
|
11037
|
-
${
|
|
11038
|
-
|
|
11039
|
-
${
|
|
11040
|
-
${
|
|
11041
|
-
|
|
11042
|
-
${
|
|
11043
|
-
${
|
|
11044
|
-
|
|
11045
|
-
${
|
|
11046
|
-
${
|
|
11047
|
-
${
|
|
11048
|
-
${
|
|
11049
|
-
|
|
11050
|
-
${
|
|
11051
|
-
${
|
|
11052
|
-
${
|
|
11053
|
-
${
|
|
11054
|
-
|
|
11055
|
-
${
|
|
11056
|
-
${
|
|
11057
|
-
|
|
11058
|
-
${
|
|
11059
|
-
${
|
|
11060
|
-
${
|
|
11061
|
-
|
|
11062
|
-
${
|
|
11063
|
-
${
|
|
11064
|
-
|
|
11065
|
-
${
|
|
11066
|
-
${
|
|
11067
|
-
1. ${
|
|
11068
|
-
2. ${
|
|
11069
|
-
3. ${
|
|
11070
|
-
4. ${
|
|
11071
|
-
|
|
11072
|
-
${
|
|
11073
|
-
1. ${
|
|
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. ${
|
|
11261
|
+
3. ${chalk30.cyan("jai1 t done")} <id>
|
|
11076
11262
|
|
|
11077
|
-
${
|
|
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
|
-
${
|
|
11084
|
-
${
|
|
11085
|
-
${
|
|
11086
|
-
${
|
|
11087
|
-
${
|
|
11088
|
-
${
|
|
11089
|
-
${
|
|
11090
|
-
${
|
|
11091
|
-
${
|
|
11092
|
-
${
|
|
11093
|
-
${
|
|
11094
|
-
${
|
|
11095
|
-
|
|
11096
|
-
${
|
|
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
|
|
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
|
|
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
|
}
|
|
@@ -11120,24 +11306,24 @@ function createTasksCommand() {
|
|
|
11120
11306
|
cmd.addCommand(createTaskSummaryCommand());
|
|
11121
11307
|
cmd.addCommand(createTaskGuideCommand());
|
|
11122
11308
|
cmd.action(async () => {
|
|
11123
|
-
const { handleTaskSummary } = await import("./summary-
|
|
11309
|
+
const { handleTaskSummary } = await import("./summary-O75DZKM2.js");
|
|
11124
11310
|
await handleTaskSummary({ json: false });
|
|
11125
11311
|
});
|
|
11126
11312
|
return cmd;
|
|
11127
11313
|
}
|
|
11128
11314
|
|
|
11129
11315
|
// src/commands/kit/index.ts
|
|
11130
|
-
import { Command as
|
|
11131
|
-
import
|
|
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
|
|
11135
|
-
import
|
|
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
|
|
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 =
|
|
11373
|
+
const tmpDir = join10(process.env.TMPDIR || "/tmp", "jai1-kits");
|
|
11188
11374
|
await fs19.mkdir(tmpDir, { recursive: true });
|
|
11189
|
-
const tmpFile =
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
11429
|
+
chalk31.bold(`${categoryIcon} ${category.charAt(0).toUpperCase() + category.slice(1)}`)
|
|
11244
11430
|
);
|
|
11245
11431
|
const table = new Table6({
|
|
11246
11432
|
head: [
|
|
11247
|
-
|
|
11248
|
-
|
|
11249
|
-
|
|
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
|
-
|
|
11256
|
-
|
|
11257
|
-
|
|
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(
|
|
11264
|
-
console.log(
|
|
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
|
|
11455
|
+
import { Command as Command60 } from "commander";
|
|
11270
11456
|
function createKitInfoCommand() {
|
|
11271
|
-
return new
|
|
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
|
|
11506
|
+
import { Command as Command61 } from "commander";
|
|
11321
11507
|
import { promises as fs20 } from "fs";
|
|
11322
|
-
import { join as
|
|
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
|
|
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 ||
|
|
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,
|
|
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 =
|
|
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(
|
|
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(
|
|
11547
|
-
console.log(` ${
|
|
11548
|
-
console.log(` ${
|
|
11549
|
-
console.log(` ${
|
|
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(
|
|
11552
|
-
console.log(
|
|
11553
|
-
console.log(
|
|
11554
|
-
console.log(
|
|
11555
|
-
console.log(
|
|
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(
|
|
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
|
|
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
|
|
11571
|
-
import
|
|
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
|
|
11575
|
-
import
|
|
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
|
|
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(
|
|
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(
|
|
11787
|
+
console.log(chalk33.yellow("Kh\xF4ng c\xF3 presets n\xE0o."));
|
|
11602
11788
|
return;
|
|
11603
11789
|
}
|
|
11604
11790
|
console.log(
|
|
11605
|
-
|
|
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(
|
|
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
|
-
[
|
|
11616
|
-
[
|
|
11617
|
-
[
|
|
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([
|
|
11811
|
+
table.push([chalk33.dim("Stack"), chalk33.yellow(stackParts.join(" + "))]);
|
|
11626
11812
|
}
|
|
11627
11813
|
table.push(
|
|
11628
|
-
[
|
|
11629
|
-
[
|
|
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(
|
|
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
|
|
11830
|
+
import { Command as Command64 } from "commander";
|
|
11645
11831
|
import { promises as fs22 } from "fs";
|
|
11646
|
-
import { join as
|
|
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
|
|
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 =
|
|
11659
|
-
this.configPath =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
12087
|
+
import { Command as Command65 } from "commander";
|
|
11902
12088
|
import { promises as fs24 } from "fs";
|
|
11903
|
-
import { join as
|
|
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
|
|
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 =
|
|
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() :
|
|
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 =
|
|
12231
|
-
const relativePath =
|
|
12232
|
-
const destPath =
|
|
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:
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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:
|
|
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 =
|
|
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 =
|
|
12298
|
-
const destPath =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
12594
|
-
await fs24.mkdir(
|
|
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 =
|
|
12604
|
-
await fs24.mkdir(
|
|
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
|
|
12711
|
-
import { join as
|
|
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
|
|
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 =
|
|
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
|
|
12969
|
+
import { Command as Command67 } from "commander";
|
|
12784
12970
|
import { promises as fs25 } from "fs";
|
|
12785
|
-
import { join as
|
|
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
|
|
12789
|
-
const rulePresetDir =
|
|
12790
|
-
const presetJsonPath =
|
|
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 =
|
|
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 =
|
|
12943
|
-
await fs25.mkdir(
|
|
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
|
|
13192
|
+
import { Command as Command68 } from "commander";
|
|
13007
13193
|
import { promises as fs26 } from "fs";
|
|
13008
|
-
import { join as
|
|
13194
|
+
import { join as join18 } from "path";
|
|
13009
13195
|
function createRulesInfoCommand() {
|
|
13010
|
-
return new
|
|
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 =
|
|
13024
|
-
const presetJsonPath =
|
|
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(
|
|
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(
|
|
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(
|
|
13116
|
-
console.log(` ${
|
|
13117
|
-
console.log(` ${
|
|
13118
|
-
console.log(` ${
|
|
13119
|
-
console.log(` ${
|
|
13120
|
-
console.log(` ${
|
|
13121
|
-
console.log(` ${
|
|
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(
|
|
13124
|
-
console.log(
|
|
13125
|
-
console.log(
|
|
13126
|
-
console.log(
|
|
13127
|
-
console.log(
|
|
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(
|
|
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
|
|
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
|
|
13146
|
-
import
|
|
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
|
|
13150
|
-
import
|
|
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
|
|
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 =
|
|
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 =
|
|
13198
|
-
const skillMd =
|
|
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 =
|
|
13224
|
-
const skillMd =
|
|
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 =
|
|
13485
|
+
const jai1SkillsDir = join19(projectRoot, ".jai1", "skills");
|
|
13300
13486
|
await fs27.mkdir(jai1SkillsDir, { recursive: true });
|
|
13301
|
-
const universalDir =
|
|
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 =
|
|
13308
|
-
const skillMd =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
13431
|
-
const tgtPath =
|
|
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
|
|
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(
|
|
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(
|
|
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
|
-
|
|
13463
|
-
|
|
13464
|
-
|
|
13465
|
-
|
|
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
|
-
|
|
13474
|
-
|
|
13475
|
-
|
|
13476
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
13676
|
+
console.log(chalk35.bold("\u{1F310} npm Skills Registry"));
|
|
13491
13677
|
console.log(output);
|
|
13492
13678
|
} catch (error) {
|
|
13493
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
13504
|
-
console.log(
|
|
13505
|
-
console.log(
|
|
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
|
|
13512
|
-
import { join as
|
|
13513
|
-
import
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
13718
|
+
console.log(chalk36.cyan(`\u{1F4E6} \u0110ang c\xE0i \u0111\u1EB7t skill: ${name}...`));
|
|
13533
13719
|
console.log();
|
|
13534
|
-
const targetDir =
|
|
13720
|
+
const targetDir = join20(projectRoot, ".jai1");
|
|
13535
13721
|
await skillsService.installFromServer(config, name, targetDir);
|
|
13536
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
13762
|
+
console.log(chalk36.yellow(`\u26A0\uFE0F Errors: ${result.errors}`));
|
|
13577
13763
|
}
|
|
13578
13764
|
}
|
|
13579
13765
|
} else {
|
|
13580
|
-
console.log(
|
|
13581
|
-
console.log(
|
|
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
|
|
13588
|
-
import
|
|
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
|
|
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(
|
|
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(
|
|
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
|
-
|
|
13609
|
-
|
|
13610
|
-
|
|
13611
|
-
|
|
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
|
-
|
|
13620
|
-
|
|
13621
|
-
|
|
13622
|
-
|
|
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(
|
|
13628
|
-
console.log(
|
|
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(
|
|
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(
|
|
13636
|
-
console.log(
|
|
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(
|
|
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
|
-
|
|
13644
|
-
|
|
13645
|
-
|
|
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
|
-
|
|
13653
|
-
|
|
13654
|
-
|
|
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(
|
|
13660
|
-
console.log(
|
|
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
|
|
13667
|
-
import
|
|
13852
|
+
import { Command as Command73 } from "commander";
|
|
13853
|
+
import chalk38 from "chalk";
|
|
13668
13854
|
function createSkillsInfoCommand() {
|
|
13669
|
-
return new
|
|
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(
|
|
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} ${
|
|
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(
|
|
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(
|
|
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(
|
|
13707
|
-
console.log(
|
|
13708
|
-
console.log(
|
|
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} ${
|
|
13898
|
+
\u{1F6E0} ${chalk38.bold(skill.name)}
|
|
13713
13899
|
`);
|
|
13714
13900
|
console.log(`Slug: ${skill.slug}`);
|
|
13715
|
-
console.log(`Description: ${skill.description ||
|
|
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(
|
|
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
|
|
13726
|
-
import
|
|
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
|
|
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(
|
|
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(
|
|
13737
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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" ?
|
|
13805
|
-
console.log(` ${statusColor(icon)} ${res.ide}: ${res.skill} \u2192 ${
|
|
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(` ${
|
|
13993
|
+
console.log(` ${chalk39.red("Error:")} ${res.error}`);
|
|
13808
13994
|
}
|
|
13809
13995
|
}
|
|
13810
13996
|
);
|
|
13811
|
-
console.log(
|
|
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(
|
|
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(
|
|
13827
|
-
console.log(` ${
|
|
13828
|
-
console.log(` ${
|
|
13829
|
-
console.log(` ${
|
|
13830
|
-
console.log(` ${
|
|
13831
|
-
console.log(` ${
|
|
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(
|
|
13834
|
-
console.log(
|
|
13835
|
-
console.log(
|
|
13836
|
-
console.log(
|
|
13837
|
-
console.log(
|
|
13838
|
-
console.log(
|
|
13839
|
-
console.log(
|
|
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(
|
|
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
|
|
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
|
|
14042
|
+
import { Command as Command76 } from "commander";
|
|
13857
14043
|
import { confirm as confirm16 } from "@inquirer/prompts";
|
|
13858
|
-
import { execSync as
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
|
14202
|
+
import { Command as Command77 } from "commander";
|
|
14017
14203
|
import { confirm as confirm17, select as select6 } from "@inquirer/prompts";
|
|
14018
|
-
import { join as
|
|
14204
|
+
import { join as join21 } from "path";
|
|
14019
14205
|
function createCleanCommand() {
|
|
14020
|
-
return new
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
15208
|
+
import { Command as Command81 } from "commander";
|
|
15023
15209
|
import { promises as fs28 } from "fs";
|
|
15024
|
-
import { join as
|
|
15210
|
+
import { join as join22 } from "path";
|
|
15025
15211
|
import { homedir as homedir5 } from "os";
|
|
15026
15212
|
function createInfoCommand() {
|
|
15027
|
-
const cmd = new
|
|
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 =
|
|
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 =
|
|
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
|
|
15269
|
+
import { Command as Command82 } from "commander";
|
|
15084
15270
|
import { confirm as confirm18 } from "@inquirer/prompts";
|
|
15085
|
-
import { execSync as
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
15389
|
+
execSync6("pnpm --version", { stdio: "ignore" });
|
|
15204
15390
|
return "pnpm";
|
|
15205
15391
|
} catch {
|
|
15206
15392
|
}
|
|
15207
15393
|
try {
|
|
15208
|
-
|
|
15394
|
+
execSync6("yarn --version", { stdio: "ignore" });
|
|
15209
15395
|
return "yarn";
|
|
15210
15396
|
} catch {
|
|
15211
15397
|
}
|
|
15212
15398
|
try {
|
|
15213
|
-
|
|
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
|
|
15421
|
+
import { Command as Command83 } from "commander";
|
|
15236
15422
|
import { confirm as confirm19 } from "@inquirer/prompts";
|
|
15237
15423
|
function createClearBackupsCommand() {
|
|
15238
|
-
return new
|
|
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
|
|
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
|
|
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
|
|
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 (!
|
|
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 (
|
|
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 (!
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
15921
|
+
console.error(chalk41.red(`\u274C L\u1EC7nh kh\xF4ng t\u1ED3n t\u1EA1i: ${commandName}`));
|
|
15736
15922
|
const name = getCliName();
|
|
15737
|
-
console.error(
|
|
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
|
|
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
|
|
15971
|
+
var redmineCommand = new Command86("redmine").description("Redmine context sync commands");
|
|
15785
15972
|
redmineCommand.addCommand(createRedmineCheckCommand());
|
|
15786
|
-
var syncCommand = new
|
|
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);
|