@jvittechs/j 1.0.45 → 1.0.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1060 -490
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -49,7 +49,7 @@ function checkNodeVersion() {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
// src/cli.ts
|
|
52
|
-
import { Command as
|
|
52
|
+
import { Command as Command99 } 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.47",
|
|
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: {
|
|
@@ -10616,12 +10616,581 @@ function createDepsCommand() {
|
|
|
10616
10616
|
return depsCommand;
|
|
10617
10617
|
}
|
|
10618
10618
|
|
|
10619
|
-
// src/commands/
|
|
10619
|
+
// src/commands/dev/index.ts
|
|
10620
|
+
import { Command as Command56 } from "commander";
|
|
10621
|
+
import chalk29 from "chalk";
|
|
10622
|
+
|
|
10623
|
+
// src/commands/dev/mail/index.ts
|
|
10624
|
+
import { Command as Command55 } from "commander";
|
|
10625
|
+
import chalk28 from "chalk";
|
|
10626
|
+
|
|
10627
|
+
// src/commands/dev/mail/create.ts
|
|
10620
10628
|
import { Command as Command47 } from "commander";
|
|
10629
|
+
import chalk20 from "chalk";
|
|
10630
|
+
|
|
10631
|
+
// src/services/temp-mail.service.ts
|
|
10632
|
+
var TempMailApiService = class {
|
|
10633
|
+
baseUrl;
|
|
10634
|
+
accessKey;
|
|
10635
|
+
constructor(config) {
|
|
10636
|
+
this.baseUrl = config.apiUrl.replace(/\/$/, "");
|
|
10637
|
+
this.accessKey = config.accessKey;
|
|
10638
|
+
}
|
|
10639
|
+
async request(path13, options = {}) {
|
|
10640
|
+
const url = `${this.baseUrl}/api/temp-mail${path13}`;
|
|
10641
|
+
const res = await fetch(url, {
|
|
10642
|
+
...options,
|
|
10643
|
+
headers: {
|
|
10644
|
+
"Content-Type": "application/json",
|
|
10645
|
+
"JAI1-Access-Key": this.accessKey,
|
|
10646
|
+
...options.headers
|
|
10647
|
+
}
|
|
10648
|
+
});
|
|
10649
|
+
const body = await res.json();
|
|
10650
|
+
return body;
|
|
10651
|
+
}
|
|
10652
|
+
async createAccount(name) {
|
|
10653
|
+
return this.request("/accounts", {
|
|
10654
|
+
method: "POST",
|
|
10655
|
+
body: JSON.stringify({ name })
|
|
10656
|
+
});
|
|
10657
|
+
}
|
|
10658
|
+
async listAccounts() {
|
|
10659
|
+
return this.request("/accounts");
|
|
10660
|
+
}
|
|
10661
|
+
async getRandomAccount() {
|
|
10662
|
+
return this.request("/accounts/random");
|
|
10663
|
+
}
|
|
10664
|
+
async getInbox(address, limit = 1) {
|
|
10665
|
+
const enc = encodeURIComponent(address);
|
|
10666
|
+
return this.request(`/accounts/${enc}/inbox?limit=${limit}`);
|
|
10667
|
+
}
|
|
10668
|
+
async getMessage(address, messageId) {
|
|
10669
|
+
const enc = encodeURIComponent(address);
|
|
10670
|
+
return this.request(`/accounts/${enc}/messages/${messageId}`);
|
|
10671
|
+
}
|
|
10672
|
+
async purgeMessages(address) {
|
|
10673
|
+
const enc = encodeURIComponent(address);
|
|
10674
|
+
return this.request(`/accounts/${enc}/messages`, {
|
|
10675
|
+
method: "DELETE"
|
|
10676
|
+
});
|
|
10677
|
+
}
|
|
10678
|
+
async deleteAccount(address) {
|
|
10679
|
+
const enc = encodeURIComponent(address);
|
|
10680
|
+
return this.request(`/accounts/${enc}`, { method: "DELETE" });
|
|
10681
|
+
}
|
|
10682
|
+
};
|
|
10683
|
+
|
|
10684
|
+
// src/commands/dev/mail/create.ts
|
|
10685
|
+
async function handleCreate(options) {
|
|
10686
|
+
const config = await new ConfigService().load();
|
|
10687
|
+
if (!config) {
|
|
10688
|
+
console.error('\u274C Not initialized. Run "jai1 auth" first.');
|
|
10689
|
+
process.exit(1);
|
|
10690
|
+
}
|
|
10691
|
+
const api = new TempMailApiService(config);
|
|
10692
|
+
if (!options.json) {
|
|
10693
|
+
console.log(chalk20.dim("\u23F3 Creating temp email account..."));
|
|
10694
|
+
}
|
|
10695
|
+
const result = await api.createAccount(options.name);
|
|
10696
|
+
if (!result.success) {
|
|
10697
|
+
if (options.json) {
|
|
10698
|
+
console.log(JSON.stringify({ success: false, error: result.error }));
|
|
10699
|
+
} else {
|
|
10700
|
+
console.error(`\u274C ${result.error}`);
|
|
10701
|
+
}
|
|
10702
|
+
process.exit(1);
|
|
10703
|
+
}
|
|
10704
|
+
if (options.json) {
|
|
10705
|
+
console.log(JSON.stringify({ success: true, data: result.data }));
|
|
10706
|
+
return;
|
|
10707
|
+
}
|
|
10708
|
+
console.log();
|
|
10709
|
+
console.log(`\u2705 Temp email created!`);
|
|
10710
|
+
console.log(` ${chalk20.bold.cyan(result.data.address)}`);
|
|
10711
|
+
console.log(chalk20.dim(` Created: ${new Date(result.data.created_at).toLocaleString()}`));
|
|
10712
|
+
console.log();
|
|
10713
|
+
console.log(chalk20.dim(" Run `j dev mail watch <address>` to listen for new emails"));
|
|
10714
|
+
}
|
|
10715
|
+
function createMailCreateCommand() {
|
|
10716
|
+
return new Command47("create").description("T\u1EA1o temp email account m\u1EDBi").option("--name <prefix>", "Custom prefix cho \u0111\u1ECBa ch\u1EC9 email").option("-j, --json", "Output d\u1EA1ng JSON").addHelpText("after", `
|
|
10717
|
+
Examples:
|
|
10718
|
+
$ j dev mail create
|
|
10719
|
+
$ j dev mail create --name test
|
|
10720
|
+
$ j dev mail create -j
|
|
10721
|
+
`).action(async (options) => {
|
|
10722
|
+
await handleCreate(options);
|
|
10723
|
+
});
|
|
10724
|
+
}
|
|
10725
|
+
|
|
10726
|
+
// src/commands/dev/mail/list.ts
|
|
10727
|
+
import { Command as Command48 } from "commander";
|
|
10728
|
+
import chalk21 from "chalk";
|
|
10729
|
+
async function handleList(options) {
|
|
10730
|
+
const config = await new ConfigService().load();
|
|
10731
|
+
if (!config) {
|
|
10732
|
+
console.error('\u274C Not initialized. Run "jai1 auth" first.');
|
|
10733
|
+
process.exit(1);
|
|
10734
|
+
}
|
|
10735
|
+
const api = new TempMailApiService(config);
|
|
10736
|
+
const result = await api.listAccounts();
|
|
10737
|
+
if (!result.success) {
|
|
10738
|
+
if (options.json) {
|
|
10739
|
+
console.log(JSON.stringify({ success: false, error: result.error }));
|
|
10740
|
+
} else {
|
|
10741
|
+
console.error(`\u274C ${result.error}`);
|
|
10742
|
+
}
|
|
10743
|
+
process.exit(1);
|
|
10744
|
+
}
|
|
10745
|
+
const accounts = result.data ?? [];
|
|
10746
|
+
if (options.json) {
|
|
10747
|
+
console.log(JSON.stringify({ success: true, data: accounts }));
|
|
10748
|
+
return;
|
|
10749
|
+
}
|
|
10750
|
+
if (!accounts.length) {
|
|
10751
|
+
console.log(chalk21.dim("Ch\u01B0a c\xF3 temp email n\xE0o. T\u1EA1o m\u1EDBi b\u1EB1ng: j dev mail create"));
|
|
10752
|
+
return;
|
|
10753
|
+
}
|
|
10754
|
+
console.log(chalk21.bold.cyan(`\u{1F4EC} Temp Email Accounts (${accounts.length}/20)
|
|
10755
|
+
`));
|
|
10756
|
+
console.log(
|
|
10757
|
+
chalk21.bold(` ${"#".padEnd(4)} ${"Address".padEnd(40)} Created`)
|
|
10758
|
+
);
|
|
10759
|
+
console.log(" " + "\u2500".repeat(70));
|
|
10760
|
+
accounts.forEach((acc, i) => {
|
|
10761
|
+
const idx = chalk21.dim(String(i + 1).padEnd(4));
|
|
10762
|
+
const addr = chalk21.cyan(acc.address.padEnd(40));
|
|
10763
|
+
const date = chalk21.dim(new Date(acc.created_at).toLocaleDateString("vi-VN"));
|
|
10764
|
+
console.log(` ${idx} ${addr} ${date}`);
|
|
10765
|
+
});
|
|
10766
|
+
console.log();
|
|
10767
|
+
}
|
|
10768
|
+
function createMailListCommand() {
|
|
10769
|
+
return new Command48("list").description("Li\u1EC7t k\xEA t\u1EA5t c\u1EA3 temp email accounts").option("-j, --json", "Output d\u1EA1ng JSON").addHelpText("after", `
|
|
10770
|
+
Examples:
|
|
10771
|
+
$ j dev mail list
|
|
10772
|
+
$ j dev mail list -j
|
|
10773
|
+
`).action(async (options) => {
|
|
10774
|
+
await handleList(options);
|
|
10775
|
+
});
|
|
10776
|
+
}
|
|
10777
|
+
|
|
10778
|
+
// src/commands/dev/mail/random.ts
|
|
10779
|
+
import { Command as Command49 } from "commander";
|
|
10780
|
+
import chalk22 from "chalk";
|
|
10781
|
+
async function handleRandom(options) {
|
|
10782
|
+
const config = await new ConfigService().load();
|
|
10783
|
+
if (!config) {
|
|
10784
|
+
console.error('\u274C Not initialized. Run "jai1 auth" first.');
|
|
10785
|
+
process.exit(1);
|
|
10786
|
+
}
|
|
10787
|
+
const api = new TempMailApiService(config);
|
|
10788
|
+
const result = await api.getRandomAccount();
|
|
10789
|
+
if (!result.success) {
|
|
10790
|
+
if (options.json) {
|
|
10791
|
+
console.log(JSON.stringify({ success: false, error: result.error }));
|
|
10792
|
+
} else {
|
|
10793
|
+
console.error(`\u274C ${result.error}`);
|
|
10794
|
+
}
|
|
10795
|
+
process.exit(1);
|
|
10796
|
+
}
|
|
10797
|
+
if (options.json) {
|
|
10798
|
+
console.log(JSON.stringify({ success: true, data: result.data }));
|
|
10799
|
+
return;
|
|
10800
|
+
}
|
|
10801
|
+
console.log(chalk22.cyan(result.data.address));
|
|
10802
|
+
}
|
|
10803
|
+
function createMailRandomCommand() {
|
|
10804
|
+
return new Command49("random").description("L\u1EA5y ng\u1EABu nhi\xEAn 1 \u0111\u1ECBa ch\u1EC9 email t\u1EEB danh s\xE1ch").option("-j, --json", "Output d\u1EA1ng JSON").addHelpText("after", `
|
|
10805
|
+
Examples:
|
|
10806
|
+
$ j dev mail random
|
|
10807
|
+
$ j dev mail random -j
|
|
10808
|
+
$ ADDRESS=$(j dev mail random) && echo "Using: $ADDRESS"
|
|
10809
|
+
`).action(async (options) => {
|
|
10810
|
+
await handleRandom(options);
|
|
10811
|
+
});
|
|
10812
|
+
}
|
|
10813
|
+
|
|
10814
|
+
// src/commands/dev/mail/inbox.ts
|
|
10815
|
+
import { Command as Command50 } from "commander";
|
|
10816
|
+
import chalk23 from "chalk";
|
|
10817
|
+
async function handleInbox(address, options) {
|
|
10818
|
+
const config = await new ConfigService().load();
|
|
10819
|
+
if (!config) {
|
|
10820
|
+
console.error('\u274C Not initialized. Run "jai1 auth" first.');
|
|
10821
|
+
process.exit(1);
|
|
10822
|
+
}
|
|
10823
|
+
const limit = Math.max(1, Math.min(30, parseInt(options.number, 10) || 1));
|
|
10824
|
+
const api = new TempMailApiService(config);
|
|
10825
|
+
const result = await api.getInbox(address, limit);
|
|
10826
|
+
if (!result.success) {
|
|
10827
|
+
if (options.json) {
|
|
10828
|
+
console.log(JSON.stringify({ success: false, error: result.error }));
|
|
10829
|
+
} else {
|
|
10830
|
+
console.error(`\u274C ${result.error}`);
|
|
10831
|
+
}
|
|
10832
|
+
process.exit(1);
|
|
10833
|
+
}
|
|
10834
|
+
const messages = result.data ?? [];
|
|
10835
|
+
if (options.json) {
|
|
10836
|
+
console.log(JSON.stringify({ success: true, data: messages }));
|
|
10837
|
+
return;
|
|
10838
|
+
}
|
|
10839
|
+
if (!messages.length) {
|
|
10840
|
+
console.log(chalk23.dim(`Inbox tr\u1ED1ng: ${address}`));
|
|
10841
|
+
return;
|
|
10842
|
+
}
|
|
10843
|
+
console.log(chalk23.bold.cyan(`\u{1F4E5} Inbox: ${address}
|
|
10844
|
+
`));
|
|
10845
|
+
console.log(
|
|
10846
|
+
chalk23.bold(` ${"From".padEnd(30)} ${"Subject".padEnd(35)} Date`)
|
|
10847
|
+
);
|
|
10848
|
+
console.log(" " + "\u2500".repeat(85));
|
|
10849
|
+
messages.forEach((msg) => {
|
|
10850
|
+
const from = (msg.from.name || msg.from.address).slice(0, 28).padEnd(30);
|
|
10851
|
+
const subject = (msg.subject || "(no subject)").slice(0, 33).padEnd(35);
|
|
10852
|
+
const date = new Date(msg.createdAt).toLocaleString("vi-VN");
|
|
10853
|
+
const seenDot = msg.seen ? " " : chalk23.blue("\u25CF");
|
|
10854
|
+
console.log(` ${seenDot} ${chalk23.cyan(from)} ${subject} ${chalk23.dim(date)}`);
|
|
10855
|
+
console.log(chalk23.dim(` ID: ${msg.id}`));
|
|
10856
|
+
});
|
|
10857
|
+
console.log();
|
|
10858
|
+
console.log(chalk23.dim(` Run \`j dev mail read ${address} <id>\` to read full email`));
|
|
10859
|
+
}
|
|
10860
|
+
function createMailInboxCommand() {
|
|
10861
|
+
return new Command50("inbox").description("Xem inbox c\u1EE7a m\u1ED9t email address").argument("<address>", "\u0110\u1ECBa ch\u1EC9 email").option("-n, --number <count>", "S\u1ED1 l\u01B0\u1EE3ng email c\u1EA7n l\u1EA5y (m\u1EB7c \u0111\u1ECBnh: 1)", "1").option("-j, --json", "Output d\u1EA1ng JSON").addHelpText("after", `
|
|
10862
|
+
Examples:
|
|
10863
|
+
$ j dev mail inbox user@dollicons.com
|
|
10864
|
+
$ j dev mail inbox user@dollicons.com -n 5
|
|
10865
|
+
$ j dev mail inbox user@dollicons.com -j
|
|
10866
|
+
`).action(async (address, options) => {
|
|
10867
|
+
await handleInbox(address, options);
|
|
10868
|
+
});
|
|
10869
|
+
}
|
|
10870
|
+
|
|
10871
|
+
// src/commands/dev/mail/read.ts
|
|
10872
|
+
import { Command as Command51 } from "commander";
|
|
10873
|
+
import chalk24 from "chalk";
|
|
10874
|
+
function htmlToText(html) {
|
|
10875
|
+
return html.join("\n").replace(/<br\s*\/?>/gi, "\n").replace(/<\/p>/gi, "\n\n").replace(/<[^>]+>/g, "").replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/\n{3,}/g, "\n\n").trim();
|
|
10876
|
+
}
|
|
10877
|
+
async function handleRead(address, messageId, options) {
|
|
10878
|
+
const config = await new ConfigService().load();
|
|
10879
|
+
if (!config) {
|
|
10880
|
+
console.error('\u274C Not initialized. Run "jai1 auth" first.');
|
|
10881
|
+
process.exit(1);
|
|
10882
|
+
}
|
|
10883
|
+
const api = new TempMailApiService(config);
|
|
10884
|
+
const result = await api.getMessage(address, messageId);
|
|
10885
|
+
if (!result.success) {
|
|
10886
|
+
if (options.json) {
|
|
10887
|
+
console.log(JSON.stringify({ success: false, error: result.error }));
|
|
10888
|
+
} else {
|
|
10889
|
+
console.error(`\u274C ${result.error}`);
|
|
10890
|
+
}
|
|
10891
|
+
process.exit(1);
|
|
10892
|
+
}
|
|
10893
|
+
const msg = result.data;
|
|
10894
|
+
if (options.json) {
|
|
10895
|
+
console.log(JSON.stringify({ success: true, data: msg }));
|
|
10896
|
+
return;
|
|
10897
|
+
}
|
|
10898
|
+
const body = msg.text || (msg.html?.length ? htmlToText(msg.html) : "(no content)");
|
|
10899
|
+
console.log(chalk24.bold.cyan("\u{1F4E7} Email Detail\n"));
|
|
10900
|
+
console.log(` ${chalk24.bold("From:")} ${msg.from.name} <${msg.from.address}>`);
|
|
10901
|
+
console.log(` ${chalk24.bold("To:")} ${msg.to.map((t) => t.address).join(", ")}`);
|
|
10902
|
+
if (msg.cc?.length) console.log(` ${chalk24.bold("CC:")} ${msg.cc.join(", ")}`);
|
|
10903
|
+
console.log(` ${chalk24.bold("Subject:")} ${msg.subject || "(no subject)"}`);
|
|
10904
|
+
console.log(` ${chalk24.bold("Date:")} ${new Date(msg.createdAt).toLocaleString("vi-VN")}`);
|
|
10905
|
+
if (msg.hasAttachments) console.log(` ${chalk24.yellow("\u{1F4CE} Has attachments")}`);
|
|
10906
|
+
console.log("\n" + "\u2500".repeat(80) + "\n");
|
|
10907
|
+
console.log(body);
|
|
10908
|
+
console.log("\n" + "\u2500".repeat(80));
|
|
10909
|
+
}
|
|
10910
|
+
function createMailReadCommand() {
|
|
10911
|
+
return new Command51("read").description("\u0110\u1ECDc n\u1ED9i dung \u0111\u1EA7y \u0111\u1EE7 c\u1EE7a m\u1ED9t email").argument("<address>", "\u0110\u1ECBa ch\u1EC9 email").argument("<message-id>", "ID c\u1EE7a email (t\u1EEB l\u1EC7nh inbox)").option("-j, --json", "Output d\u1EA1ng JSON (raw)").addHelpText("after", `
|
|
10912
|
+
Examples:
|
|
10913
|
+
$ j dev mail read user@dollicons.com abc123
|
|
10914
|
+
$ j dev mail read user@dollicons.com abc123 -j
|
|
10915
|
+
`).action(async (address, messageId, options) => {
|
|
10916
|
+
await handleRead(address, messageId, options);
|
|
10917
|
+
});
|
|
10918
|
+
}
|
|
10919
|
+
|
|
10920
|
+
// src/commands/dev/mail/watch.ts
|
|
10921
|
+
import { Command as Command52 } from "commander";
|
|
10922
|
+
import chalk25 from "chalk";
|
|
10923
|
+
function htmlToText2(html) {
|
|
10924
|
+
return html.join("\n").replace(/<br\s*\/?>/gi, "\n").replace(/<\/p>/gi, "\n\n").replace(/<[^>]+>/g, "").replace(/&[a-z]+;/g, " ").replace(/\n{3,}/g, "\n\n").trim();
|
|
10925
|
+
}
|
|
10926
|
+
async function handleWatch(address, options) {
|
|
10927
|
+
const config = await new ConfigService().load();
|
|
10928
|
+
if (!config) {
|
|
10929
|
+
console.error('\u274C Not initialized. Run "jai1 auth" first.');
|
|
10930
|
+
process.exit(1);
|
|
10931
|
+
}
|
|
10932
|
+
const intervalSec = Math.max(5, parseInt(options.interval, 10) || 15);
|
|
10933
|
+
const timeoutSec = options.timeout ? parseInt(options.timeout, 10) : void 0;
|
|
10934
|
+
const api = new TempMailApiService(config);
|
|
10935
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
10936
|
+
let pollCount = 0;
|
|
10937
|
+
const startTime = Date.now();
|
|
10938
|
+
if (!options.json) {
|
|
10939
|
+
console.log(chalk25.bold.cyan(`\u{1F440} Watching inbox: ${address}`));
|
|
10940
|
+
console.log(chalk25.dim(
|
|
10941
|
+
` Polling every ${intervalSec}s` + (timeoutSec ? ` \xB7 timeout ${timeoutSec}s` : "") + (options.keep ? " \xB7 keep watching" : " \xB7 stops after first email") + ` \xB7 Ctrl+C to stop
|
|
10942
|
+
`
|
|
10943
|
+
));
|
|
10944
|
+
}
|
|
10945
|
+
const init = await api.getInbox(address, 30);
|
|
10946
|
+
if (init.success && init.data) {
|
|
10947
|
+
init.data.forEach((m) => seenIds.add(m.id));
|
|
10948
|
+
}
|
|
10949
|
+
const poll = async () => {
|
|
10950
|
+
if (timeoutSec && (Date.now() - startTime) / 1e3 >= timeoutSec) {
|
|
10951
|
+
if (!options.json) console.log(chalk25.dim("\n\u23F1 Timeout reached. Stopped."));
|
|
10952
|
+
process.exit(0);
|
|
10953
|
+
}
|
|
10954
|
+
pollCount++;
|
|
10955
|
+
if (!options.json) {
|
|
10956
|
+
process.stdout.write(chalk25.dim(`\r \u{1F504} Poll #${pollCount} \u2014 ${(/* @__PURE__ */ new Date()).toLocaleTimeString("vi-VN")} `));
|
|
10957
|
+
}
|
|
10958
|
+
const result = await api.getInbox(address, 30);
|
|
10959
|
+
if (!result.success) {
|
|
10960
|
+
if (!options.json) {
|
|
10961
|
+
process.stdout.write(chalk25.red(`\r \u274C Error: ${result.error}
|
|
10962
|
+
`));
|
|
10963
|
+
}
|
|
10964
|
+
return;
|
|
10965
|
+
}
|
|
10966
|
+
const newMessages = (result.data ?? []).filter((m) => !seenIds.has(m.id));
|
|
10967
|
+
if (newMessages.length > 0) {
|
|
10968
|
+
for (const msg of newMessages) {
|
|
10969
|
+
seenIds.add(msg.id);
|
|
10970
|
+
if (options.json) {
|
|
10971
|
+
console.log(JSON.stringify({ event: "new_message", data: msg }));
|
|
10972
|
+
continue;
|
|
10973
|
+
}
|
|
10974
|
+
const detail = await api.getMessage(address, msg.id);
|
|
10975
|
+
process.stdout.write("\r" + " ".repeat(50) + "\r");
|
|
10976
|
+
console.log("\n" + chalk25.bold.green("\u{1F4E8} New Email!\n"));
|
|
10977
|
+
console.log(` ${chalk25.bold("From:")} ${msg.from.name || msg.from.address}`);
|
|
10978
|
+
console.log(` ${chalk25.bold("Subject:")} ${msg.subject || "(no subject)"}`);
|
|
10979
|
+
console.log(` ${chalk25.bold("Date:")} ${new Date(msg.createdAt).toLocaleString("vi-VN")}`);
|
|
10980
|
+
console.log(` ${chalk25.dim("ID:")} ${msg.id}`);
|
|
10981
|
+
if (detail.success && detail.data) {
|
|
10982
|
+
const body = detail.data.text || (detail.data.html?.length ? htmlToText2(detail.data.html) : "");
|
|
10983
|
+
if (body) {
|
|
10984
|
+
const preview = body.slice(0, 600) + (body.length > 600 ? "\n\u2026" : "");
|
|
10985
|
+
console.log("\n" + "\u2500".repeat(60) + "\n");
|
|
10986
|
+
console.log(preview);
|
|
10987
|
+
console.log("\u2500".repeat(60));
|
|
10988
|
+
}
|
|
10989
|
+
}
|
|
10990
|
+
console.log();
|
|
10991
|
+
if (!options.keep) {
|
|
10992
|
+
clearInterval(timer);
|
|
10993
|
+
process.exit(0);
|
|
10994
|
+
}
|
|
10995
|
+
}
|
|
10996
|
+
}
|
|
10997
|
+
};
|
|
10998
|
+
await poll();
|
|
10999
|
+
const timer = setInterval(poll, intervalSec * 1e3);
|
|
11000
|
+
process.on("SIGINT", () => {
|
|
11001
|
+
clearInterval(timer);
|
|
11002
|
+
if (!options.json) {
|
|
11003
|
+
console.log(chalk25.dim("\n\n Stopped watching. Goodbye!"));
|
|
11004
|
+
}
|
|
11005
|
+
process.exit(0);
|
|
11006
|
+
});
|
|
11007
|
+
}
|
|
11008
|
+
function createMailWatchCommand() {
|
|
11009
|
+
return new Command52("watch").description("L\u1EAFng nghe email m\u1EDBi (polling)").argument("<address>", "\u0110\u1ECBa ch\u1EC9 email c\u1EA7n theo d\xF5i").option("-i, --interval <seconds>", "Kho\u1EA3ng th\u1EDDi gian poll (gi\xE2y, m\u1EB7c \u0111\u1ECBnh: 15)", "15").option("-t, --timeout <seconds>", "D\u1EEBng sau N gi\xE2y (m\u1EB7c \u0111\u1ECBnh: ch\u1EA1y m\xE3i)").option("-k, --keep", "Ti\u1EBFp t\u1EE5c watch sau khi nh\u1EADn \u0111\u01B0\u1EE3c email (m\u1EB7c \u0111\u1ECBnh: d\u1EEBng sau email \u0111\u1EA7u ti\xEAn)").option("-j, --json", "Output s\u1EF1 ki\u1EC7n email m\u1EDBi d\u1EA1ng JSON (m\u1ED7i d\xF2ng 1 event)").addHelpText("after", `
|
|
11010
|
+
Examples:
|
|
11011
|
+
$ j dev mail watch user@dollicons.com # D\u1EEBng khi nh\u1EADn \u0111\u01B0\u1EE3c email \u0111\u1EA7u ti\xEAn
|
|
11012
|
+
$ j dev mail watch user@dollicons.com -k # Ti\u1EBFp t\u1EE5c watch
|
|
11013
|
+
$ j dev mail watch user@dollicons.com -i 30 -k
|
|
11014
|
+
$ j dev mail watch user@dollicons.com -t 300
|
|
11015
|
+
$ j dev mail watch user@dollicons.com -j | jq .
|
|
11016
|
+
`).action(async (address, options) => {
|
|
11017
|
+
await handleWatch(address, options);
|
|
11018
|
+
});
|
|
11019
|
+
}
|
|
11020
|
+
|
|
11021
|
+
// src/commands/dev/mail/purge.ts
|
|
11022
|
+
import { Command as Command53 } from "commander";
|
|
11023
|
+
import chalk26 from "chalk";
|
|
11024
|
+
import { confirm as confirm9 } from "@inquirer/prompts";
|
|
11025
|
+
async function handlePurge(address, options) {
|
|
11026
|
+
const config = await new ConfigService().load();
|
|
11027
|
+
if (!config) {
|
|
11028
|
+
console.error('\u274C Not initialized. Run "jai1 auth" first.');
|
|
11029
|
+
process.exit(1);
|
|
11030
|
+
}
|
|
11031
|
+
if (!options.yes && !options.json) {
|
|
11032
|
+
const confirmed = await confirm9({
|
|
11033
|
+
message: `X\xF3a to\xE0n b\u1ED9 emails c\u1EE7a ${chalk26.cyan(address)}?`,
|
|
11034
|
+
default: false
|
|
11035
|
+
});
|
|
11036
|
+
if (!confirmed) {
|
|
11037
|
+
console.log(chalk26.dim("\u274C \u0110\xE3 h\u1EE7y."));
|
|
11038
|
+
return;
|
|
11039
|
+
}
|
|
11040
|
+
}
|
|
11041
|
+
const api = new TempMailApiService(config);
|
|
11042
|
+
if (!options.json) {
|
|
11043
|
+
console.log(chalk26.dim("\u23F3 \u0110ang x\xF3a t\u1EA5t c\u1EA3 emails..."));
|
|
11044
|
+
}
|
|
11045
|
+
const result = await api.purgeMessages(address);
|
|
11046
|
+
if (!result.success) {
|
|
11047
|
+
if (options.json) {
|
|
11048
|
+
console.log(JSON.stringify({ success: false, error: result.error }));
|
|
11049
|
+
} else {
|
|
11050
|
+
console.error(`\u274C ${result.error}`);
|
|
11051
|
+
}
|
|
11052
|
+
process.exit(1);
|
|
11053
|
+
}
|
|
11054
|
+
if (options.json) {
|
|
11055
|
+
console.log(JSON.stringify({ success: true, data: result.data }));
|
|
11056
|
+
return;
|
|
11057
|
+
}
|
|
11058
|
+
const deleted = result.data?.deleted ?? 0;
|
|
11059
|
+
if (deleted === 0) {
|
|
11060
|
+
console.log(chalk26.dim("Inbox \u0111\xE3 tr\u1ED1ng, kh\xF4ng c\xF3 g\xEC \u0111\u1EC3 x\xF3a."));
|
|
11061
|
+
} else {
|
|
11062
|
+
console.log(`\u2705 \u0110\xE3 x\xF3a ${chalk26.bold(deleted)} email.`);
|
|
11063
|
+
}
|
|
11064
|
+
}
|
|
11065
|
+
function createMailPurgeCommand() {
|
|
11066
|
+
return new Command53("purge").description("X\xF3a to\xE0n b\u1ED9 emails c\u1EE7a m\u1ED9t account").argument("<address>", "\u0110\u1ECBa ch\u1EC9 email").option("-y, --yes", "B\u1ECF qua x\xE1c nh\u1EADn").option("-j, --json", "Output d\u1EA1ng JSON").addHelpText("after", `
|
|
11067
|
+
Examples:
|
|
11068
|
+
$ j dev mail purge user@dollicons.com
|
|
11069
|
+
$ j dev mail purge user@dollicons.com -y
|
|
11070
|
+
$ j dev mail purge user@dollicons.com -j
|
|
11071
|
+
`).action(async (address, options) => {
|
|
11072
|
+
await handlePurge(address, options);
|
|
11073
|
+
});
|
|
11074
|
+
}
|
|
11075
|
+
|
|
11076
|
+
// src/commands/dev/mail/delete.ts
|
|
11077
|
+
import { Command as Command54 } from "commander";
|
|
11078
|
+
import chalk27 from "chalk";
|
|
11079
|
+
import { confirm as confirm10 } from "@inquirer/prompts";
|
|
11080
|
+
async function handleDelete(address, options) {
|
|
11081
|
+
const config = await new ConfigService().load();
|
|
11082
|
+
if (!config) {
|
|
11083
|
+
console.error('\u274C Not initialized. Run "jai1 auth" first.');
|
|
11084
|
+
process.exit(1);
|
|
11085
|
+
}
|
|
11086
|
+
if (!options.yes && !options.json) {
|
|
11087
|
+
const confirmed = await confirm10({
|
|
11088
|
+
message: `X\xF3a account ${chalk27.cyan(address)}? H\xE0nh \u0111\u1ED9ng kh\xF4ng th\u1EC3 ho\xE0n t\xE1c.`,
|
|
11089
|
+
default: false
|
|
11090
|
+
});
|
|
11091
|
+
if (!confirmed) {
|
|
11092
|
+
console.log(chalk27.dim("\u274C \u0110\xE3 h\u1EE7y."));
|
|
11093
|
+
return;
|
|
11094
|
+
}
|
|
11095
|
+
}
|
|
11096
|
+
const api = new TempMailApiService(config);
|
|
11097
|
+
if (!options.json) {
|
|
11098
|
+
console.log(chalk27.dim("\u23F3 \u0110ang x\xF3a account..."));
|
|
11099
|
+
}
|
|
11100
|
+
const result = await api.deleteAccount(address);
|
|
11101
|
+
if (!result.success) {
|
|
11102
|
+
if (options.json) {
|
|
11103
|
+
console.log(JSON.stringify({ success: false, error: result.error }));
|
|
11104
|
+
} else {
|
|
11105
|
+
console.error(`\u274C ${result.error}`);
|
|
11106
|
+
}
|
|
11107
|
+
process.exit(1);
|
|
11108
|
+
}
|
|
11109
|
+
if (options.json) {
|
|
11110
|
+
console.log(JSON.stringify({ success: true }));
|
|
11111
|
+
return;
|
|
11112
|
+
}
|
|
11113
|
+
console.log(`\u2705 \u0110\xE3 x\xF3a account: ${chalk27.cyan(address)}`);
|
|
11114
|
+
}
|
|
11115
|
+
function createMailDeleteCommand() {
|
|
11116
|
+
return new Command54("delete").description("X\xF3a m\u1ED9t temp email account").argument("<address>", "\u0110\u1ECBa ch\u1EC9 email c\u1EA7n x\xF3a").option("-y, --yes", "B\u1ECF qua x\xE1c nh\u1EADn").option("-j, --json", "Output d\u1EA1ng JSON").addHelpText("after", `
|
|
11117
|
+
Examples:
|
|
11118
|
+
$ j dev mail delete user@dollicons.com
|
|
11119
|
+
$ j dev mail delete user@dollicons.com -y
|
|
11120
|
+
$ j dev mail delete user@dollicons.com -j
|
|
11121
|
+
`).action(async (address, options) => {
|
|
11122
|
+
await handleDelete(address, options);
|
|
11123
|
+
});
|
|
11124
|
+
}
|
|
11125
|
+
|
|
11126
|
+
// src/commands/dev/mail/index.ts
|
|
11127
|
+
function showMailHelp() {
|
|
11128
|
+
console.log(chalk28.bold.cyan("\u{1F4EC} j dev mail") + chalk28.dim(" - Temp email cho m\xF4i tr\u01B0\u1EDDng dev"));
|
|
11129
|
+
console.log();
|
|
11130
|
+
console.log(chalk28.bold("Qu\u1EA3n l\xFD accounts:"));
|
|
11131
|
+
console.log(` ${chalk28.cyan("create")} T\u1EA1o temp email account m\u1EDBi`);
|
|
11132
|
+
console.log(` ${chalk28.cyan("list")} Li\u1EC7t k\xEA t\u1EA5t c\u1EA3 accounts`);
|
|
11133
|
+
console.log(` ${chalk28.cyan("random")} L\u1EA5y ng\u1EABu nhi\xEAn 1 \u0111\u1ECBa ch\u1EC9`);
|
|
11134
|
+
console.log(` ${chalk28.cyan("delete")} X\xF3a 1 account`);
|
|
11135
|
+
console.log();
|
|
11136
|
+
console.log(chalk28.bold("Email:"));
|
|
11137
|
+
console.log(` ${chalk28.cyan("inbox")} Xem inbox (emails g\u1EA7n nh\u1EA5t)`);
|
|
11138
|
+
console.log(` ${chalk28.cyan("read")} \u0110\u1ECDc n\u1ED9i dung email`);
|
|
11139
|
+
console.log(` ${chalk28.cyan("watch")} L\u1EAFng nghe email m\u1EDBi (polling)`);
|
|
11140
|
+
console.log(` ${chalk28.cyan("purge")} X\xF3a to\xE0n b\u1ED9 emails c\u1EE7a 1 account`);
|
|
11141
|
+
console.log();
|
|
11142
|
+
console.log(chalk28.bold("V\xED d\u1EE5:"));
|
|
11143
|
+
console.log(chalk28.dim(" $ j dev mail create"));
|
|
11144
|
+
console.log(chalk28.dim(" $ j dev mail create --name test"));
|
|
11145
|
+
console.log(chalk28.dim(" $ j dev mail list"));
|
|
11146
|
+
console.log(chalk28.dim(" $ j dev mail watch user@dollicons.com"));
|
|
11147
|
+
console.log();
|
|
11148
|
+
console.log(chalk28.dim(" T\u1EA5t c\u1EA3 l\u1EC7nh \u0111\u1EC1u h\u1ED7 tr\u1EE3 -j / --json"));
|
|
11149
|
+
console.log(chalk28.dim(' Ch\u1EA1y "j dev mail <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
|
|
11150
|
+
}
|
|
11151
|
+
function createMailCommand() {
|
|
11152
|
+
const cmd = new Command55("mail").description("Temp email management cho dev environment").action(() => {
|
|
11153
|
+
showMailHelp();
|
|
11154
|
+
});
|
|
11155
|
+
cmd.addCommand(createMailCreateCommand());
|
|
11156
|
+
cmd.addCommand(createMailListCommand());
|
|
11157
|
+
cmd.addCommand(createMailRandomCommand());
|
|
11158
|
+
cmd.addCommand(createMailInboxCommand());
|
|
11159
|
+
cmd.addCommand(createMailReadCommand());
|
|
11160
|
+
cmd.addCommand(createMailWatchCommand());
|
|
11161
|
+
cmd.addCommand(createMailPurgeCommand());
|
|
11162
|
+
cmd.addCommand(createMailDeleteCommand());
|
|
11163
|
+
return cmd;
|
|
11164
|
+
}
|
|
11165
|
+
|
|
11166
|
+
// src/commands/dev/index.ts
|
|
11167
|
+
function showDevHelp() {
|
|
11168
|
+
console.log(chalk29.bold.cyan("\u{1F9EA} j dev") + chalk29.dim(" - Dev tools cho m\xF4i tr\u01B0\u1EDDng local"));
|
|
11169
|
+
console.log();
|
|
11170
|
+
console.log(chalk29.bold("Tools:"));
|
|
11171
|
+
console.log(` ${chalk29.cyan("mail")} Qu\u1EA3n l\xFD temp email accounts (via mail.tm)`);
|
|
11172
|
+
console.log();
|
|
11173
|
+
console.log(chalk29.bold("V\xED d\u1EE5:"));
|
|
11174
|
+
console.log(chalk29.dim(" $ j dev mail create"));
|
|
11175
|
+
console.log(chalk29.dim(" $ j dev mail list"));
|
|
11176
|
+
console.log(chalk29.dim(" $ j dev mail watch <address>"));
|
|
11177
|
+
console.log();
|
|
11178
|
+
console.log(chalk29.dim('Ch\u1EA1y "j dev <tool> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
|
|
11179
|
+
}
|
|
11180
|
+
function createDevCommand() {
|
|
11181
|
+
const cmd = new Command56("dev").alias("d").description("Developer tools for local development environments").action(() => {
|
|
11182
|
+
showDevHelp();
|
|
11183
|
+
});
|
|
11184
|
+
cmd.addCommand(createMailCommand());
|
|
11185
|
+
return cmd;
|
|
11186
|
+
}
|
|
11187
|
+
|
|
11188
|
+
// src/commands/hooks/index.ts
|
|
11189
|
+
import { Command as Command57 } from "commander";
|
|
10621
11190
|
import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync, unlinkSync, chmodSync, mkdirSync } from "fs";
|
|
10622
11191
|
import { join as join9 } from "path";
|
|
10623
11192
|
import { execSync as execSync4 } from "child_process";
|
|
10624
|
-
import
|
|
11193
|
+
import chalk30 from "chalk";
|
|
10625
11194
|
var MARKER_START = "# >>> jai1-hooks";
|
|
10626
11195
|
var MARKER_END = "# <<< jai1-hooks";
|
|
10627
11196
|
var SHEBANG = "#!/bin/sh";
|
|
@@ -10714,7 +11283,7 @@ function setupHooks() {
|
|
|
10714
11283
|
if (existsSync3(hookPath)) {
|
|
10715
11284
|
const existing = readFileSync2(hookPath, "utf-8");
|
|
10716
11285
|
if (hasJai1Section(existing)) {
|
|
10717
|
-
console.log(
|
|
11286
|
+
console.log(chalk30.dim(` \u23ED ${def.hookName} \u2014 \u0111\xE3 c\xF3, skip`));
|
|
10718
11287
|
skipped++;
|
|
10719
11288
|
continue;
|
|
10720
11289
|
}
|
|
@@ -10727,23 +11296,23 @@ ${section}
|
|
|
10727
11296
|
`);
|
|
10728
11297
|
}
|
|
10729
11298
|
chmodSync(hookPath, 493);
|
|
10730
|
-
console.log(
|
|
11299
|
+
console.log(chalk30.green(` \u2705 ${def.hookName} \u2014 ${def.description}`));
|
|
10731
11300
|
installed++;
|
|
10732
11301
|
} catch (err) {
|
|
10733
11302
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10734
11303
|
if (msg.includes("EACCES") || msg.includes("permission")) {
|
|
10735
|
-
console.log(
|
|
11304
|
+
console.log(chalk30.red(` \u274C ${def.hookName} \u2014 kh\xF4ng c\xF3 quy\u1EC1n ghi. Th\u1EED ch\u1EA1y v\u1EDBi sudo.`));
|
|
10736
11305
|
} else {
|
|
10737
|
-
console.log(
|
|
11306
|
+
console.log(chalk30.red(` \u274C ${def.hookName} \u2014 l\u1ED7i: ${msg}`));
|
|
10738
11307
|
}
|
|
10739
11308
|
}
|
|
10740
11309
|
}
|
|
10741
11310
|
console.log("");
|
|
10742
11311
|
if (installed > 0) {
|
|
10743
|
-
console.log(
|
|
10744
|
-
console.log(
|
|
11312
|
+
console.log(chalk30.green(`\u{1F389} \u0110\xE3 c\xE0i ${installed} hook(s).`));
|
|
11313
|
+
console.log(chalk30.dim(" Tasks s\u1EBD t\u1EF1 \u0111\u1ED9ng sync khi b\u1EA1n push/pull."));
|
|
10745
11314
|
} else {
|
|
10746
|
-
console.log(
|
|
11315
|
+
console.log(chalk30.dim("\u2139\uFE0F T\u1EA5t c\u1EA3 hooks \u0111\xE3 \u0111\u01B0\u1EE3c c\xE0i s\u1EB5n."));
|
|
10747
11316
|
}
|
|
10748
11317
|
}
|
|
10749
11318
|
function removeHooks() {
|
|
@@ -10762,37 +11331,37 @@ function removeHooks() {
|
|
|
10762
11331
|
const cleaned = removeJai1Section(content);
|
|
10763
11332
|
if (isEffectivelyEmpty(cleaned)) {
|
|
10764
11333
|
unlinkSync(hookPath);
|
|
10765
|
-
console.log(
|
|
11334
|
+
console.log(chalk30.yellow(` \u{1F5D1} ${def.hookName} \u2014 xo\xE1 file (ch\u1EC9 c\xF3 jai1 hooks)`));
|
|
10766
11335
|
} else {
|
|
10767
11336
|
writeFileSync(hookPath, cleaned);
|
|
10768
|
-
console.log(
|
|
11337
|
+
console.log(chalk30.yellow(` \u2702\uFE0F ${def.hookName} \u2014 g\u1EE1 ph\u1EA7n jai1`));
|
|
10769
11338
|
}
|
|
10770
11339
|
removed++;
|
|
10771
11340
|
} catch (err) {
|
|
10772
11341
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10773
11342
|
if (msg.includes("EACCES") || msg.includes("permission")) {
|
|
10774
|
-
console.log(
|
|
11343
|
+
console.log(chalk30.red(` \u274C ${def.hookName} \u2014 kh\xF4ng c\xF3 quy\u1EC1n ghi/xo\xE1. Th\u1EED ch\u1EA1y v\u1EDBi sudo.`));
|
|
10775
11344
|
} else {
|
|
10776
|
-
console.log(
|
|
11345
|
+
console.log(chalk30.red(` \u274C ${def.hookName} \u2014 l\u1ED7i: ${msg}`));
|
|
10777
11346
|
}
|
|
10778
11347
|
}
|
|
10779
11348
|
}
|
|
10780
11349
|
console.log("");
|
|
10781
11350
|
if (removed > 0) {
|
|
10782
|
-
console.log(
|
|
11351
|
+
console.log(chalk30.green(`\u2705 \u0110\xE3 g\u1EE1 ${removed} hook(s).`));
|
|
10783
11352
|
} else {
|
|
10784
|
-
console.log(
|
|
11353
|
+
console.log(chalk30.dim("\u2139\uFE0F Kh\xF4ng c\xF3 jai1 hooks n\xE0o \u0111\u1EC3 g\u1EE1."));
|
|
10785
11354
|
}
|
|
10786
11355
|
}
|
|
10787
11356
|
function createHooksCommand() {
|
|
10788
|
-
const cmd = new
|
|
11357
|
+
const cmd = new Command57("hooks").description("Qu\u1EA3n l\xFD Git hooks t\xEDch h\u1EE3p cho jai1");
|
|
10789
11358
|
cmd.command("setup").description("C\xE0i \u0111\u1EB7t Git hooks (auto task sync on push/pull)").action(() => {
|
|
10790
|
-
console.log(
|
|
11359
|
+
console.log(chalk30.bold("\n\u{1F517} C\xE0i \u0111\u1EB7t jai1 Git hooks...\n"));
|
|
10791
11360
|
setupHooks();
|
|
10792
11361
|
console.log("");
|
|
10793
11362
|
});
|
|
10794
11363
|
cmd.command("remove").description("G\u1EE1 b\u1ECF jai1 Git hooks").action(() => {
|
|
10795
|
-
console.log(
|
|
11364
|
+
console.log(chalk30.bold("\n\u{1F517} G\u1EE1 b\u1ECF jai1 Git hooks...\n"));
|
|
10796
11365
|
removeHooks();
|
|
10797
11366
|
console.log("");
|
|
10798
11367
|
});
|
|
@@ -10803,17 +11372,17 @@ function createHooksCommand() {
|
|
|
10803
11372
|
}
|
|
10804
11373
|
|
|
10805
11374
|
// src/commands/tasks/index.ts
|
|
10806
|
-
import { Command as
|
|
11375
|
+
import { Command as Command71 } from "commander";
|
|
10807
11376
|
|
|
10808
11377
|
// src/commands/tasks/add.ts
|
|
10809
|
-
import { Command as
|
|
10810
|
-
import
|
|
11378
|
+
import { Command as Command58 } from "commander";
|
|
11379
|
+
import chalk31 from "chalk";
|
|
10811
11380
|
function createTaskAddCommand() {
|
|
10812
|
-
return new
|
|
11381
|
+
return new Command58("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) => {
|
|
10813
11382
|
const service = new TaskService();
|
|
10814
11383
|
const priority = Number(options.priority ?? 2);
|
|
10815
11384
|
if (priority < 0 || priority > 3) {
|
|
10816
|
-
console.error(
|
|
11385
|
+
console.error(chalk31.red("\u274C Priority must be 0-3"));
|
|
10817
11386
|
process.exit(1);
|
|
10818
11387
|
}
|
|
10819
11388
|
const tags = options.tags ? options.tags.split(",").map((t) => t.trim()) : [];
|
|
@@ -10829,23 +11398,23 @@ function createTaskAddCommand() {
|
|
|
10829
11398
|
}
|
|
10830
11399
|
const icon = PRIORITY_ICONS[task.priority] || "\u{1F7E1}";
|
|
10831
11400
|
const label = PRIORITY_LABELS[task.priority] || "Medium";
|
|
10832
|
-
console.log(
|
|
10833
|
-
console.log(` ${
|
|
10834
|
-
console.log(` ${
|
|
11401
|
+
console.log(chalk31.green(`\u2705 Task added: ${chalk31.bold(task.id)}`));
|
|
11402
|
+
console.log(` ${chalk31.dim("Title:")} ${task.title}`);
|
|
11403
|
+
console.log(` ${chalk31.dim("Priority:")} ${icon} ${label}`);
|
|
10835
11404
|
if (task.parent) {
|
|
10836
|
-
console.log(` ${
|
|
11405
|
+
console.log(` ${chalk31.dim("Parent:")} ${task.parent}`);
|
|
10837
11406
|
}
|
|
10838
11407
|
if (task.tags.length > 0) {
|
|
10839
|
-
console.log(` ${
|
|
11408
|
+
console.log(` ${chalk31.dim("Tags:")} ${task.tags.join(", ")}`);
|
|
10840
11409
|
}
|
|
10841
11410
|
});
|
|
10842
11411
|
}
|
|
10843
11412
|
|
|
10844
11413
|
// src/commands/tasks/list.ts
|
|
10845
|
-
import { Command as
|
|
10846
|
-
import
|
|
11414
|
+
import { Command as Command59 } from "commander";
|
|
11415
|
+
import chalk32 from "chalk";
|
|
10847
11416
|
function createTaskListCommand() {
|
|
10848
|
-
return new
|
|
11417
|
+
return new Command59("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) => {
|
|
10849
11418
|
await handleTaskList(options);
|
|
10850
11419
|
});
|
|
10851
11420
|
}
|
|
@@ -10864,12 +11433,12 @@ async function handleTaskList(options) {
|
|
|
10864
11433
|
return;
|
|
10865
11434
|
}
|
|
10866
11435
|
if (tasks.length === 0) {
|
|
10867
|
-
console.log(
|
|
11436
|
+
console.log(chalk32.dim("No tasks found."));
|
|
10868
11437
|
return;
|
|
10869
11438
|
}
|
|
10870
11439
|
const resolvedIds = new Set(allTasks.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id));
|
|
10871
11440
|
const header = options.parent ? `\u{1F4CB} ${options.parent} (${tasks.length} tasks)` : `\u{1F4CB} All tasks (${tasks.length})`;
|
|
10872
|
-
console.log(
|
|
11441
|
+
console.log(chalk32.bold(header));
|
|
10873
11442
|
console.log();
|
|
10874
11443
|
for (const task of tasks) {
|
|
10875
11444
|
printTaskLine(task, resolvedIds);
|
|
@@ -10879,25 +11448,25 @@ function printTaskLine(task, resolvedIds) {
|
|
|
10879
11448
|
const isBlocked = task.status === "todo" && task.depends_on.length > 0 && !task.depends_on.every((id) => resolvedIds.has(id));
|
|
10880
11449
|
const statusIcon = isBlocked ? BLOCKED_ICON : STATUS_ICONS[task.status] || "\u{1F4CB}";
|
|
10881
11450
|
const priorityIcon = PRIORITY_ICONS[task.priority] || "\u{1F7E1}";
|
|
10882
|
-
let line = ` ${statusIcon} ${
|
|
11451
|
+
let line = ` ${statusIcon} ${chalk32.dim(task.id)} P${task.priority}${priorityIcon} ${task.title}`;
|
|
10883
11452
|
if (task.status === "in_progress" && task.assigned_to) {
|
|
10884
|
-
line +=
|
|
11453
|
+
line += chalk32.cyan(` @${task.assigned_to}`);
|
|
10885
11454
|
}
|
|
10886
11455
|
if (isBlocked) {
|
|
10887
11456
|
const blockedBy = task.depends_on.filter((id) => !resolvedIds.has(id));
|
|
10888
|
-
line +=
|
|
11457
|
+
line += chalk32.red(` (blocked: ${blockedBy.join(", ")})`);
|
|
10889
11458
|
}
|
|
10890
11459
|
if (task.parent) {
|
|
10891
|
-
line +=
|
|
11460
|
+
line += chalk32.dim(` [${task.parent}]`);
|
|
10892
11461
|
}
|
|
10893
11462
|
console.log(line);
|
|
10894
11463
|
}
|
|
10895
11464
|
|
|
10896
11465
|
// src/commands/tasks/ready.ts
|
|
10897
|
-
import { Command as
|
|
10898
|
-
import
|
|
11466
|
+
import { Command as Command60 } from "commander";
|
|
11467
|
+
import chalk33 from "chalk";
|
|
10899
11468
|
function createTaskReadyCommand() {
|
|
10900
|
-
return new
|
|
11469
|
+
return new Command60("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) => {
|
|
10901
11470
|
const service = new TaskService();
|
|
10902
11471
|
const tasks = await service.getReady(options.parent);
|
|
10903
11472
|
if (options.json) {
|
|
@@ -10905,37 +11474,37 @@ function createTaskReadyCommand() {
|
|
|
10905
11474
|
return;
|
|
10906
11475
|
}
|
|
10907
11476
|
if (tasks.length === 0) {
|
|
10908
|
-
console.log(
|
|
10909
|
-
console.log(
|
|
11477
|
+
console.log(chalk33.dim("No tasks ready to pick."));
|
|
11478
|
+
console.log(chalk33.dim("\u{1F4A1} Check blocked tasks: jai1 t list -s todo"));
|
|
10910
11479
|
return;
|
|
10911
11480
|
}
|
|
10912
|
-
console.log(
|
|
11481
|
+
console.log(chalk33.bold(`\u{1F4CB} Ready to pick (${tasks.length} tasks):`));
|
|
10913
11482
|
console.log();
|
|
10914
11483
|
for (const task of tasks) {
|
|
10915
11484
|
const icon = PRIORITY_ICONS[task.priority] || "\u{1F7E1}";
|
|
10916
|
-
let line = ` P${task.priority}${icon} ${
|
|
11485
|
+
let line = ` P${task.priority}${icon} ${chalk33.dim(task.id)} ${task.title}`;
|
|
10917
11486
|
if (task.parent) {
|
|
10918
|
-
line +=
|
|
11487
|
+
line += chalk33.dim(` [${task.parent}]`);
|
|
10919
11488
|
}
|
|
10920
11489
|
console.log(line);
|
|
10921
11490
|
}
|
|
10922
11491
|
console.log();
|
|
10923
|
-
console.log(
|
|
11492
|
+
console.log(chalk33.dim("\u{1F4A1} Run: jai1 t pick"));
|
|
10924
11493
|
});
|
|
10925
11494
|
}
|
|
10926
11495
|
|
|
10927
11496
|
// src/commands/tasks/update.ts
|
|
10928
|
-
import { Command as
|
|
10929
|
-
import
|
|
11497
|
+
import { Command as Command61 } from "commander";
|
|
11498
|
+
import chalk34 from "chalk";
|
|
10930
11499
|
var VALID_STATUSES = ["todo", "in_progress", "done", "cancelled"];
|
|
10931
11500
|
function createTaskUpdateCommand() {
|
|
10932
|
-
return new
|
|
11501
|
+
return new Command61("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) => {
|
|
10933
11502
|
if (!options.status && !options.notes) {
|
|
10934
|
-
console.error(
|
|
11503
|
+
console.error(chalk34.red("\u274C At least one of --status or --notes is required"));
|
|
10935
11504
|
process.exit(1);
|
|
10936
11505
|
}
|
|
10937
11506
|
if (options.status && !VALID_STATUSES.includes(options.status)) {
|
|
10938
|
-
console.error(
|
|
11507
|
+
console.error(chalk34.red(`\u274C Invalid status. Must be: ${VALID_STATUSES.join(", ")}`));
|
|
10939
11508
|
process.exit(1);
|
|
10940
11509
|
}
|
|
10941
11510
|
const service = new TaskService();
|
|
@@ -10945,8 +11514,8 @@ function createTaskUpdateCommand() {
|
|
|
10945
11514
|
if (existingTask) {
|
|
10946
11515
|
const { blocked, blockedBy } = await service.isBlocked(existingTask);
|
|
10947
11516
|
if (blocked) {
|
|
10948
|
-
console.log(
|
|
10949
|
-
console.log(
|
|
11517
|
+
console.log(chalk34.yellow(`\u26A0\uFE0F Task ${id} is blocked by: ${blockedBy.join(", ")}`));
|
|
11518
|
+
console.log(chalk34.yellow(` Dependencies ch\u01B0a done. Ti\u1EBFp t\u1EE5c update...`));
|
|
10950
11519
|
}
|
|
10951
11520
|
}
|
|
10952
11521
|
}
|
|
@@ -10966,24 +11535,24 @@ function createTaskUpdateCommand() {
|
|
|
10966
11535
|
if (options.notes) {
|
|
10967
11536
|
parts.push(`\u{1F4DD} notes updated`);
|
|
10968
11537
|
}
|
|
10969
|
-
console.log(
|
|
11538
|
+
console.log(chalk34.green(`\u2705 ${task.id} \u2192 ${parts.join(" | ")}`));
|
|
10970
11539
|
} catch (error) {
|
|
10971
|
-
console.error(
|
|
11540
|
+
console.error(chalk34.red(`\u274C ${error instanceof Error ? error.message : String(error)}`));
|
|
10972
11541
|
process.exit(1);
|
|
10973
11542
|
}
|
|
10974
11543
|
});
|
|
10975
11544
|
}
|
|
10976
11545
|
|
|
10977
11546
|
// src/commands/tasks/show.ts
|
|
10978
|
-
import { Command as
|
|
10979
|
-
import
|
|
11547
|
+
import { Command as Command62 } from "commander";
|
|
11548
|
+
import chalk35 from "chalk";
|
|
10980
11549
|
function createTaskShowCommand() {
|
|
10981
|
-
return new
|
|
11550
|
+
return new Command62("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) => {
|
|
10982
11551
|
const service = new TaskService();
|
|
10983
11552
|
if (query.startsWith("T-")) {
|
|
10984
11553
|
const task = await service.findById(query);
|
|
10985
11554
|
if (!task) {
|
|
10986
|
-
console.error(
|
|
11555
|
+
console.error(chalk35.red(`\u274C Task ${query} not found`));
|
|
10987
11556
|
process.exit(1);
|
|
10988
11557
|
}
|
|
10989
11558
|
if (options.json) {
|
|
@@ -10994,34 +11563,34 @@ function createTaskShowCommand() {
|
|
|
10994
11563
|
const statusIcon = blocked ? BLOCKED_ICON : STATUS_ICONS[task.status] || "\u{1F4CB}";
|
|
10995
11564
|
const priIcon = PRIORITY_ICONS[task.priority] || "\u{1F7E1}";
|
|
10996
11565
|
const priLabel = PRIORITY_LABELS[task.priority] || "Medium";
|
|
10997
|
-
console.log(
|
|
11566
|
+
console.log(chalk35.bold(`
|
|
10998
11567
|
\u{1F4CC} ${task.id}: ${task.title}
|
|
10999
11568
|
`));
|
|
11000
|
-
console.log(` ${
|
|
11001
|
-
console.log(` ${
|
|
11569
|
+
console.log(` ${chalk35.dim("Status:")} ${statusIcon} ${task.status}${blocked ? chalk35.red(" (BLOCKED)") : ""}`);
|
|
11570
|
+
console.log(` ${chalk35.dim("Priority:")} ${priIcon} P${task.priority} ${priLabel}`);
|
|
11002
11571
|
if (task.parent) {
|
|
11003
|
-
console.log(` ${
|
|
11572
|
+
console.log(` ${chalk35.dim("Parent:")} ${task.parent}`);
|
|
11004
11573
|
}
|
|
11005
11574
|
if (task.assigned_to) {
|
|
11006
|
-
console.log(` ${
|
|
11575
|
+
console.log(` ${chalk35.dim("Assigned:")} @${task.assigned_to} (${task.claimed_at})`);
|
|
11007
11576
|
}
|
|
11008
11577
|
if (task.depends_on.length > 0) {
|
|
11009
|
-
console.log(` ${
|
|
11578
|
+
console.log(` ${chalk35.dim("Depends on:")} ${task.depends_on.join(", ")}`);
|
|
11010
11579
|
if (blocked) {
|
|
11011
|
-
console.log(` ${
|
|
11580
|
+
console.log(` ${chalk35.dim("Blocked by:")} ${chalk35.red(blockedBy.join(", "))}`);
|
|
11012
11581
|
}
|
|
11013
11582
|
}
|
|
11014
11583
|
if (task.tags.length > 0) {
|
|
11015
|
-
console.log(` ${
|
|
11584
|
+
console.log(` ${chalk35.dim("Tags:")} ${task.tags.join(", ")}`);
|
|
11016
11585
|
}
|
|
11017
11586
|
if (task.branch) {
|
|
11018
|
-
console.log(` ${
|
|
11587
|
+
console.log(` ${chalk35.dim("Branch:")} ${task.branch}`);
|
|
11019
11588
|
}
|
|
11020
11589
|
if (task.notes) {
|
|
11021
|
-
console.log(` ${
|
|
11590
|
+
console.log(` ${chalk35.dim("Notes:")} ${task.notes}`);
|
|
11022
11591
|
}
|
|
11023
|
-
console.log(` ${
|
|
11024
|
-
console.log(` ${
|
|
11592
|
+
console.log(` ${chalk35.dim("Created:")} ${task.created}`);
|
|
11593
|
+
console.log(` ${chalk35.dim("Updated:")} ${task.updated}`);
|
|
11025
11594
|
console.log();
|
|
11026
11595
|
} else {
|
|
11027
11596
|
const tasks = await service.filter({ parent: query });
|
|
@@ -11030,10 +11599,10 @@ function createTaskShowCommand() {
|
|
|
11030
11599
|
return;
|
|
11031
11600
|
}
|
|
11032
11601
|
if (tasks.length === 0) {
|
|
11033
|
-
console.log(
|
|
11602
|
+
console.log(chalk35.dim(`No tasks for parent: ${query}`));
|
|
11034
11603
|
return;
|
|
11035
11604
|
}
|
|
11036
|
-
console.log(
|
|
11605
|
+
console.log(chalk35.bold(`
|
|
11037
11606
|
\u{1F4CB} ${query} (${tasks.length} tasks)
|
|
11038
11607
|
`));
|
|
11039
11608
|
const allTasks = await service.readAll();
|
|
@@ -11041,11 +11610,11 @@ function createTaskShowCommand() {
|
|
|
11041
11610
|
for (const task of tasks) {
|
|
11042
11611
|
const isBlocked = task.status === "todo" && task.depends_on.length > 0 && !task.depends_on.every((id) => doneIds.has(id));
|
|
11043
11612
|
const icon = isBlocked ? BLOCKED_ICON : STATUS_ICONS[task.status] || "\u{1F4CB}";
|
|
11044
|
-
let line = ` ${icon} ${
|
|
11045
|
-
if (task.assigned_to) line +=
|
|
11613
|
+
let line = ` ${icon} ${chalk35.dim(task.id)} P${task.priority} ${task.title}`;
|
|
11614
|
+
if (task.assigned_to) line += chalk35.cyan(` @${task.assigned_to}`);
|
|
11046
11615
|
if (isBlocked) {
|
|
11047
11616
|
const bb = task.depends_on.filter((id) => !doneIds.has(id));
|
|
11048
|
-
line +=
|
|
11617
|
+
line += chalk35.red(` (blocked: ${bb.join(", ")})`);
|
|
11049
11618
|
}
|
|
11050
11619
|
console.log(line);
|
|
11051
11620
|
}
|
|
@@ -11055,11 +11624,11 @@ function createTaskShowCommand() {
|
|
|
11055
11624
|
}
|
|
11056
11625
|
|
|
11057
11626
|
// src/commands/tasks/pick.ts
|
|
11058
|
-
import { Command as
|
|
11059
|
-
import
|
|
11060
|
-
import { confirm as
|
|
11627
|
+
import { Command as Command63 } from "commander";
|
|
11628
|
+
import chalk36 from "chalk";
|
|
11629
|
+
import { confirm as confirm11 } from "@inquirer/prompts";
|
|
11061
11630
|
function createTaskPickCommand() {
|
|
11062
|
-
return new
|
|
11631
|
+
return new Command63("pick").description("Claim the next available task").option("-j, --json", "Output JSON").action(async (options) => {
|
|
11063
11632
|
const service = new TaskService();
|
|
11064
11633
|
const ready = await service.getReady();
|
|
11065
11634
|
if (ready.length === 0) {
|
|
@@ -11067,8 +11636,8 @@ function createTaskPickCommand() {
|
|
|
11067
11636
|
console.log(JSON.stringify({ picked: null, message: "No tasks ready" }));
|
|
11068
11637
|
return;
|
|
11069
11638
|
}
|
|
11070
|
-
console.log(
|
|
11071
|
-
console.log(
|
|
11639
|
+
console.log(chalk36.dim("No tasks ready to pick."));
|
|
11640
|
+
console.log(chalk36.dim('\u{1F4A1} Add tasks first: jai1 t add "..."'));
|
|
11072
11641
|
return;
|
|
11073
11642
|
}
|
|
11074
11643
|
const top = ready[0];
|
|
@@ -11078,34 +11647,34 @@ function createTaskPickCommand() {
|
|
|
11078
11647
|
console.log(JSON.stringify(picked2, null, 2));
|
|
11079
11648
|
return;
|
|
11080
11649
|
}
|
|
11081
|
-
console.log(
|
|
11082
|
-
console.log(` ${
|
|
11650
|
+
console.log(chalk36.bold("\n\u{1F4CC} Next available task:"));
|
|
11651
|
+
console.log(` ${chalk36.bold(top.id)} P${top.priority}${icon} ${top.title}`);
|
|
11083
11652
|
if (top.parent) {
|
|
11084
|
-
console.log(` ${
|
|
11653
|
+
console.log(` ${chalk36.dim("Parent:")} ${top.parent}`);
|
|
11085
11654
|
}
|
|
11086
11655
|
if (ready.length > 1) {
|
|
11087
|
-
console.log(
|
|
11656
|
+
console.log(chalk36.dim(`
|
|
11088
11657
|
+${ready.length - 1} more tasks ready`));
|
|
11089
11658
|
}
|
|
11090
|
-
const proceed = await
|
|
11659
|
+
const proceed = await confirm11({
|
|
11091
11660
|
message: "Claim this task?",
|
|
11092
11661
|
default: true
|
|
11093
11662
|
});
|
|
11094
11663
|
if (!proceed) {
|
|
11095
|
-
console.log(
|
|
11664
|
+
console.log(chalk36.dim("\nCancelled."));
|
|
11096
11665
|
return;
|
|
11097
11666
|
}
|
|
11098
11667
|
const picked = await service.pick(top.id);
|
|
11099
|
-
console.log(
|
|
11668
|
+
console.log(chalk36.green(`
|
|
11100
11669
|
\u2705 ${picked.id} assigned to @${picked.assigned_to}, status \u2192 in_progress`));
|
|
11101
11670
|
});
|
|
11102
11671
|
}
|
|
11103
11672
|
|
|
11104
11673
|
// src/commands/tasks/done.ts
|
|
11105
|
-
import { Command as
|
|
11106
|
-
import
|
|
11674
|
+
import { Command as Command64 } from "commander";
|
|
11675
|
+
import chalk37 from "chalk";
|
|
11107
11676
|
function createTaskDoneCommand() {
|
|
11108
|
-
return new
|
|
11677
|
+
return new Command64("done").description("Mark task as done").argument("<id>", "Task ID (e.g. T-001)").option("-j, --json", "Output JSON").action(async (id, options) => {
|
|
11109
11678
|
const service = new TaskService();
|
|
11110
11679
|
try {
|
|
11111
11680
|
const task = await service.markDone(id);
|
|
@@ -11113,19 +11682,19 @@ function createTaskDoneCommand() {
|
|
|
11113
11682
|
console.log(JSON.stringify(task, null, 2));
|
|
11114
11683
|
return;
|
|
11115
11684
|
}
|
|
11116
|
-
console.log(
|
|
11685
|
+
console.log(chalk37.green(`\u2705 ${task.id}: ${task.title} \u2192 done`));
|
|
11117
11686
|
} catch (error) {
|
|
11118
|
-
console.error(
|
|
11687
|
+
console.error(chalk37.red(`\u274C ${error instanceof Error ? error.message : String(error)}`));
|
|
11119
11688
|
process.exit(1);
|
|
11120
11689
|
}
|
|
11121
11690
|
});
|
|
11122
11691
|
}
|
|
11123
11692
|
|
|
11124
11693
|
// src/commands/tasks/dep.ts
|
|
11125
|
-
import { Command as
|
|
11126
|
-
import
|
|
11694
|
+
import { Command as Command65 } from "commander";
|
|
11695
|
+
import chalk38 from "chalk";
|
|
11127
11696
|
function createTaskDepCommand() {
|
|
11128
|
-
return new
|
|
11697
|
+
return new Command65("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) => {
|
|
11129
11698
|
const service = new TaskService();
|
|
11130
11699
|
try {
|
|
11131
11700
|
const task = await service.addDependency(childId, parentId);
|
|
@@ -11133,166 +11702,166 @@ function createTaskDepCommand() {
|
|
|
11133
11702
|
console.log(JSON.stringify(task, null, 2));
|
|
11134
11703
|
return;
|
|
11135
11704
|
}
|
|
11136
|
-
console.log(
|
|
11137
|
-
console.log(
|
|
11705
|
+
console.log(chalk38.green(`\u2705 ${childId} now depends on ${parentId}`));
|
|
11706
|
+
console.log(chalk38.dim(` ${task.title} \u2192 waits for ${parentId}`));
|
|
11138
11707
|
} catch (error) {
|
|
11139
|
-
console.error(
|
|
11708
|
+
console.error(chalk38.red(`\u274C ${error instanceof Error ? error.message : String(error)}`));
|
|
11140
11709
|
process.exit(1);
|
|
11141
11710
|
}
|
|
11142
11711
|
});
|
|
11143
11712
|
}
|
|
11144
11713
|
|
|
11145
11714
|
// src/commands/tasks/sync.ts
|
|
11146
|
-
import { Command as
|
|
11147
|
-
import
|
|
11715
|
+
import { Command as Command66 } from "commander";
|
|
11716
|
+
import chalk39 from "chalk";
|
|
11148
11717
|
function createTaskSyncCommand() {
|
|
11149
|
-
return new
|
|
11718
|
+
return new Command66("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) => {
|
|
11150
11719
|
const service = new TaskService();
|
|
11151
11720
|
const branch = service.getSyncBranch();
|
|
11152
11721
|
const doPull = options.pull || !options.pull && !options.push;
|
|
11153
11722
|
const doPush = options.push || !options.pull && !options.push;
|
|
11154
11723
|
if (doPull) {
|
|
11155
|
-
console.log(
|
|
11724
|
+
console.log(chalk39.dim(`\u23F3 Pulling tasks from origin/${branch}...`));
|
|
11156
11725
|
try {
|
|
11157
11726
|
const result = await service.syncPull();
|
|
11158
11727
|
if (result.merged > 0) {
|
|
11159
|
-
console.log(
|
|
11728
|
+
console.log(chalk39.green(` \u2193 ${result.merged} tasks merged`));
|
|
11160
11729
|
} else {
|
|
11161
|
-
console.log(
|
|
11730
|
+
console.log(chalk39.dim(` \u2193 Already up to date`));
|
|
11162
11731
|
}
|
|
11163
11732
|
} catch (error) {
|
|
11164
|
-
console.error(
|
|
11733
|
+
console.error(chalk39.red(`\u274C Pull failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
11165
11734
|
process.exit(1);
|
|
11166
11735
|
}
|
|
11167
11736
|
}
|
|
11168
11737
|
if (doPush) {
|
|
11169
|
-
console.log(
|
|
11738
|
+
console.log(chalk39.dim(`\u23F3 Pushing tasks to origin/${branch}...`));
|
|
11170
11739
|
try {
|
|
11171
11740
|
await service.syncPush();
|
|
11172
|
-
console.log(
|
|
11741
|
+
console.log(chalk39.green(` \u2191 Tasks pushed to origin/${branch}`));
|
|
11173
11742
|
} catch (error) {
|
|
11174
|
-
console.error(
|
|
11743
|
+
console.error(chalk39.red(`\u274C Push failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
11175
11744
|
process.exit(1);
|
|
11176
11745
|
}
|
|
11177
11746
|
}
|
|
11178
|
-
console.log(
|
|
11747
|
+
console.log(chalk39.green("\u2705 Sync complete"));
|
|
11179
11748
|
});
|
|
11180
11749
|
}
|
|
11181
11750
|
|
|
11182
11751
|
// src/commands/tasks/guide.ts
|
|
11183
|
-
import { Command as
|
|
11184
|
-
import
|
|
11752
|
+
import { Command as Command67 } from "commander";
|
|
11753
|
+
import chalk40 from "chalk";
|
|
11185
11754
|
var GUIDE_TEXT = `
|
|
11186
|
-
${
|
|
11187
|
-
|
|
11188
|
-
${
|
|
11189
|
-
${
|
|
11190
|
-
${
|
|
11191
|
-
${
|
|
11192
|
-
${
|
|
11193
|
-
${
|
|
11194
|
-
|
|
11195
|
-
${
|
|
11196
|
-
${
|
|
11197
|
-
${
|
|
11198
|
-
${
|
|
11199
|
-
${
|
|
11200
|
-
|
|
11201
|
-
${
|
|
11202
|
-
${
|
|
11203
|
-
${
|
|
11204
|
-
${
|
|
11205
|
-
${
|
|
11206
|
-
|
|
11207
|
-
${
|
|
11208
|
-
${
|
|
11209
|
-
${
|
|
11210
|
-
${
|
|
11211
|
-
${
|
|
11212
|
-
${
|
|
11213
|
-
${
|
|
11214
|
-
|
|
11215
|
-
${
|
|
11216
|
-
${
|
|
11217
|
-
${
|
|
11218
|
-
|
|
11219
|
-
${
|
|
11220
|
-
${
|
|
11221
|
-
${
|
|
11222
|
-
${
|
|
11223
|
-
|
|
11224
|
-
${
|
|
11225
|
-
${
|
|
11226
|
-
|
|
11227
|
-
${
|
|
11228
|
-
${
|
|
11229
|
-
|
|
11230
|
-
${
|
|
11231
|
-
${
|
|
11232
|
-
|
|
11233
|
-
${
|
|
11234
|
-
${
|
|
11235
|
-
${
|
|
11236
|
-
${
|
|
11237
|
-
|
|
11238
|
-
${
|
|
11239
|
-
${
|
|
11240
|
-
${
|
|
11241
|
-
${
|
|
11242
|
-
|
|
11243
|
-
${
|
|
11244
|
-
${
|
|
11245
|
-
|
|
11246
|
-
${
|
|
11247
|
-
${
|
|
11248
|
-
${
|
|
11249
|
-
|
|
11250
|
-
${
|
|
11251
|
-
${
|
|
11252
|
-
|
|
11253
|
-
${
|
|
11254
|
-
${
|
|
11255
|
-
1. ${
|
|
11256
|
-
2. ${
|
|
11257
|
-
3. ${
|
|
11258
|
-
4. ${
|
|
11259
|
-
|
|
11260
|
-
${
|
|
11261
|
-
1. ${
|
|
11755
|
+
${chalk40.cyan.bold("\u{1F4D6} Jai1 Task Management Guide")}
|
|
11756
|
+
|
|
11757
|
+
${chalk40.bold("\u2501\u2501\u2501 STATUSES \u2501\u2501\u2501")}
|
|
11758
|
+
${chalk40.dim("todo")} \u{1F4CB} Ch\u01B0a b\u1EAFt \u0111\u1EA7u
|
|
11759
|
+
${chalk40.dim("in_progress")} \u{1F535} \u0110ang l\xE0m (bao g\u1ED3m review)
|
|
11760
|
+
${chalk40.dim("done")} \u2705 Ho\xE0n th\xE0nh
|
|
11761
|
+
${chalk40.dim("cancelled")} \u26AB Hu\u1EF7
|
|
11762
|
+
${chalk40.dim("(blocked)")} \u{1F534} Computed: depends_on ch\u01B0a done
|
|
11763
|
+
|
|
11764
|
+
${chalk40.bold("\u2501\u2501\u2501 PRIORITY \u2501\u2501\u2501")}
|
|
11765
|
+
${chalk40.dim("0")} = \u{1F525} Critical \u2014 Prod down, security, block c\u1EA3 team
|
|
11766
|
+
${chalk40.dim("1")} = \u{1F534} High \u2014 Feature ch\xEDnh, deadline g\u1EA7n
|
|
11767
|
+
${chalk40.dim("2")} = \u{1F7E1} Medium \u2014 B\xECnh th\u01B0\u1EDDng (default)
|
|
11768
|
+
${chalk40.dim("3")} = \u{1F7E2} Low \u2014 Nice-to-have, docs, refactor
|
|
11769
|
+
|
|
11770
|
+
${chalk40.bold("\u2501\u2501\u2501 QUICK START \u2501\u2501\u2501")}
|
|
11771
|
+
${chalk40.cyan("jai1 t add")} "Fix login bug" -p 1 -P bug/login
|
|
11772
|
+
${chalk40.cyan("jai1 t ready")} Show tasks s\u1EB5n s\xE0ng
|
|
11773
|
+
${chalk40.cyan("jai1 t pick")} Claim & start working
|
|
11774
|
+
${chalk40.cyan("jai1 t done")} T-003 Mark complete
|
|
11775
|
+
|
|
11776
|
+
${chalk40.bold("\u2501\u2501\u2501 DAILY WORKFLOW \u2501\u2501\u2501")}
|
|
11777
|
+
${chalk40.cyan("jai1 t sync --pull")} Pull latest tasks
|
|
11778
|
+
${chalk40.cyan("jai1 t summary")} Dashboard t\u1ED5ng quan
|
|
11779
|
+
${chalk40.cyan("jai1 t ready")} Xem tasks s\u1EB5n s\xE0ng
|
|
11780
|
+
${chalk40.cyan("jai1 t pick")} Claim task m\u1EDBi
|
|
11781
|
+
${chalk40.cyan("jai1 t done")} T-xxx Ho\xE0n th\xE0nh task
|
|
11782
|
+
${chalk40.cyan("jai1 t sync --push")} Push l\xEAn git
|
|
11783
|
+
|
|
11784
|
+
${chalk40.bold("\u2501\u2501\u2501 ADDING TASKS \u2501\u2501\u2501")}
|
|
11785
|
+
${chalk40.yellow("\u26A0 Lu\xF4n ki\u1EC3m tra duplicate tr\u01B0\u1EDBc khi add:")}
|
|
11786
|
+
${chalk40.cyan("jai1 t list -P")} feature/xxx
|
|
11787
|
+
|
|
11788
|
+
${chalk40.dim("Add cho feature:")}
|
|
11789
|
+
${chalk40.cyan("jai1 t add")} "Setup DB schema" -p 1 -P feature/xxx
|
|
11790
|
+
${chalk40.cyan("jai1 t add")} "Create API" -p 1 -P feature/xxx
|
|
11791
|
+
${chalk40.cyan("jai1 t add")} "Build UI" -p 2 -P feature/xxx
|
|
11792
|
+
|
|
11793
|
+
${chalk40.dim("Add cho plan:")}
|
|
11794
|
+
${chalk40.cyan("jai1 t add")} "Refactor middleware" -p 2 -P plan/xxx
|
|
11795
|
+
|
|
11796
|
+
${chalk40.dim("Add standalone:")}
|
|
11797
|
+
${chalk40.cyan("jai1 t add")} "Fix README typo" -p 3
|
|
11798
|
+
|
|
11799
|
+
${chalk40.dim("Add bug fix:")}
|
|
11800
|
+
${chalk40.cyan("jai1 t add")} "Fix login redirect" -p 1 -P bug/xxx
|
|
11801
|
+
|
|
11802
|
+
${chalk40.bold("\u2501\u2501\u2501 DEPENDENCY \u2501\u2501\u2501")}
|
|
11803
|
+
${chalk40.dim("Task dependency:")}
|
|
11804
|
+
${chalk40.cyan("jai1 t dep")} T-002 T-001 T-002 ch\u1EDD T-001 done
|
|
11805
|
+
${chalk40.cyan("jai1 t dep")} T-003 T-002 T-003 ch\u1EDD T-002 done
|
|
11806
|
+
|
|
11807
|
+
${chalk40.dim("Feature-level dependency:")}
|
|
11808
|
+
${chalk40.dim("# N\u1EBFu feature/auth ph\u1EE5 thu\u1ED9c feature/user-model:")}
|
|
11809
|
+
${chalk40.cyan("jai1 t add")} "[DEP] Wait for feature/user-model" -p 1 -P feature/auth
|
|
11810
|
+
${chalk40.dim("# R\u1ED3i dep n\xF3 v\u1EDBi tasks cu\u1ED1i c\u1EE7a user-model")}
|
|
11811
|
+
|
|
11812
|
+
${chalk40.dim("View deps:")}
|
|
11813
|
+
${chalk40.cyan("jai1 t show")} T-002 Hi\u1EC7n depends_on
|
|
11814
|
+
|
|
11815
|
+
${chalk40.bold("\u2501\u2501\u2501 TEAM COLLABORATION \u2501\u2501\u2501")}
|
|
11816
|
+
${chalk40.yellow("\u26A0 Assignment ch\u1EC9 qua pick \u2014 kh\xF4ng set th\u1EE7 c\xF4ng.")}
|
|
11817
|
+
${chalk40.dim("Khi b\u1EA1n pick \u2192 team th\u1EA5y task \u0111\xE3 c\xF3 ng\u01B0\u1EDDi nh\u1EADn.")}
|
|
11818
|
+
|
|
11819
|
+
${chalk40.dim("Sync morning:")} ${chalk40.cyan("jai1 t sync --pull")}
|
|
11820
|
+
${chalk40.dim("Sync evening:")} ${chalk40.cyan("jai1 t sync --push")}
|
|
11821
|
+
|
|
11822
|
+
${chalk40.bold("\u2501\u2501\u2501 FOR AI AGENTS (Workflow Integration) \u2501\u2501\u2501")}
|
|
11823
|
+
${chalk40.dim("Khi t\u1EA1o tasks t\u1EEB feature/plan:")}
|
|
11824
|
+
1. ${chalk40.cyan("jai1 t list -P")} <parent> Check existing (tr\xE1nh duplicate)
|
|
11825
|
+
2. ${chalk40.cyan("jai1 t add")} "..." -p <0-3> -P <parent> Add t\u1EEBng task
|
|
11826
|
+
3. ${chalk40.cyan("jai1 t dep")} <child> <parent> Set dependencies
|
|
11827
|
+
4. ${chalk40.cyan("jai1 t done")} <id> Mark complete
|
|
11828
|
+
|
|
11829
|
+
${chalk40.dim("Khi implement task ti\u1EBFp theo:")}
|
|
11830
|
+
1. ${chalk40.cyan("jai1 t pick")} (ho\u1EB7c ${chalk40.cyan("jai1 t ready -P")} <parent>)
|
|
11262
11831
|
2. Implement task
|
|
11263
|
-
3. ${
|
|
11832
|
+
3. ${chalk40.cyan("jai1 t done")} <id>
|
|
11264
11833
|
|
|
11265
|
-
${
|
|
11834
|
+
${chalk40.dim("Status transitions:")}
|
|
11266
11835
|
add \u2192 todo (default)
|
|
11267
11836
|
pick \u2192 in_progress (auto assign)
|
|
11268
11837
|
done \u2192 done
|
|
11269
11838
|
update -s \u2192 any valid status
|
|
11270
11839
|
|
|
11271
|
-
${
|
|
11272
|
-
${
|
|
11273
|
-
${
|
|
11274
|
-
${
|
|
11275
|
-
${
|
|
11276
|
-
${
|
|
11277
|
-
${
|
|
11278
|
-
${
|
|
11279
|
-
${
|
|
11280
|
-
${
|
|
11281
|
-
${
|
|
11282
|
-
${
|
|
11283
|
-
${
|
|
11284
|
-
|
|
11285
|
-
${
|
|
11840
|
+
${chalk40.bold("\u2501\u2501\u2501 ALL COMMANDS \u2501\u2501\u2501")}
|
|
11841
|
+
${chalk40.cyan("jai1 t list")} [-s status] [-P parent] [-j]
|
|
11842
|
+
${chalk40.cyan("jai1 t ready")} [-P parent] [-j]
|
|
11843
|
+
${chalk40.cyan("jai1 t add")} <title> [-p 0-3] [-P parent] [-t tags] [-j]
|
|
11844
|
+
${chalk40.cyan("jai1 t update")} <id> [-s <status>] [-n <notes>] [-j]
|
|
11845
|
+
${chalk40.cyan("jai1 t show")} <id|parent> [-j]
|
|
11846
|
+
${chalk40.cyan("jai1 t pick")} [-j]
|
|
11847
|
+
${chalk40.cyan("jai1 t done")} <id> [-j]
|
|
11848
|
+
${chalk40.cyan("jai1 t dep")} <childId> <parentId> [-j]
|
|
11849
|
+
${chalk40.cyan("jai1 t sync")} [--pull] [--push]
|
|
11850
|
+
${chalk40.cyan("jai1 t summary")} [-j]
|
|
11851
|
+
${chalk40.cyan("jai1 t groups")} [-s status] [-j] ${chalk40.dim("(alias: parents)")}
|
|
11852
|
+
${chalk40.cyan("jai1 t guide")}
|
|
11853
|
+
|
|
11854
|
+
${chalk40.dim("-j / --json available on all commands (except guide, sync)")}
|
|
11286
11855
|
`;
|
|
11287
11856
|
function createTaskGuideCommand() {
|
|
11288
|
-
return new
|
|
11857
|
+
return new Command67("guide").description("Show full task management guide").action(() => {
|
|
11289
11858
|
console.log(GUIDE_TEXT);
|
|
11290
11859
|
});
|
|
11291
11860
|
}
|
|
11292
11861
|
|
|
11293
11862
|
// src/commands/tasks/parents.ts
|
|
11294
|
-
import { Command as
|
|
11295
|
-
import
|
|
11863
|
+
import { Command as Command68 } from "commander";
|
|
11864
|
+
import chalk41 from "chalk";
|
|
11296
11865
|
var PARENT_STATUS_ICONS = {
|
|
11297
11866
|
done: "\u2705",
|
|
11298
11867
|
in_progress: "\u{1F535}",
|
|
@@ -11302,17 +11871,17 @@ var PARENT_STATUS_ICONS = {
|
|
|
11302
11871
|
function formatProgress(p) {
|
|
11303
11872
|
const completed = p.done + p.cancelled;
|
|
11304
11873
|
if (p.status === "done") {
|
|
11305
|
-
return
|
|
11874
|
+
return chalk41.green(`${completed}/${p.total} done`);
|
|
11306
11875
|
}
|
|
11307
11876
|
const parts = [];
|
|
11308
11877
|
if (p.in_progress > 0) parts.push(`${p.in_progress} in_progress`);
|
|
11309
11878
|
if (p.ready > 0) parts.push(`${p.ready} ready`);
|
|
11310
|
-
if (p.blocked > 0) parts.push(
|
|
11879
|
+
if (p.blocked > 0) parts.push(chalk41.red(`${p.blocked} blocked`));
|
|
11311
11880
|
if (p.done > 0) parts.push(`${p.done} done`);
|
|
11312
11881
|
return `${completed}/${p.total} tasks` + (parts.length > 0 ? ` (${parts.join(", ")})` : "");
|
|
11313
11882
|
}
|
|
11314
11883
|
function createTaskParentsCommand() {
|
|
11315
|
-
return new
|
|
11884
|
+
return new Command68("groups").aliases(["parents", "pg"]).description("List task groups with computed status").option("-s, --status <status>", "Filter by computed status: done, in_progress, ready, todo").option("-j, --json", "Output JSON").action(async (options) => {
|
|
11316
11885
|
const service = new TaskService();
|
|
11317
11886
|
const parents = await service.getParents(options.status);
|
|
11318
11887
|
if (options.json) {
|
|
@@ -11321,44 +11890,44 @@ function createTaskParentsCommand() {
|
|
|
11321
11890
|
}
|
|
11322
11891
|
if (parents.length === 0) {
|
|
11323
11892
|
if (options.status) {
|
|
11324
|
-
console.log(
|
|
11893
|
+
console.log(chalk41.dim(`No groups with status: ${options.status}`));
|
|
11325
11894
|
} else {
|
|
11326
|
-
console.log(
|
|
11327
|
-
console.log(
|
|
11895
|
+
console.log(chalk41.dim("No task groups found."));
|
|
11896
|
+
console.log(chalk41.dim('\u{1F4A1} Add tasks with parent: jai1 t add "..." -P feature/xxx'));
|
|
11328
11897
|
}
|
|
11329
11898
|
return;
|
|
11330
11899
|
}
|
|
11331
11900
|
const header = options.status ? `\u{1F4E6} Task Groups \u2014 ${options.status} (${parents.length})` : `\u{1F4E6} Task Groups (${parents.length})`;
|
|
11332
|
-
console.log(
|
|
11901
|
+
console.log(chalk41.bold(header));
|
|
11333
11902
|
console.log();
|
|
11334
11903
|
for (const p of parents) {
|
|
11335
11904
|
const icon = PARENT_STATUS_ICONS[p.status] || "\u{1F4CB}";
|
|
11336
11905
|
const progress = formatProgress(p);
|
|
11337
|
-
console.log(` ${icon} ${
|
|
11906
|
+
console.log(` ${icon} ${chalk41.bold(p.name)} ${chalk41.dim("\u2014")} ${progress}`);
|
|
11338
11907
|
}
|
|
11339
11908
|
console.log();
|
|
11340
11909
|
});
|
|
11341
11910
|
}
|
|
11342
11911
|
|
|
11343
11912
|
// src/commands/tasks/cancel.ts
|
|
11344
|
-
import { Command as
|
|
11345
|
-
import
|
|
11346
|
-
import { confirm as
|
|
11913
|
+
import { Command as Command69 } from "commander";
|
|
11914
|
+
import chalk42 from "chalk";
|
|
11915
|
+
import { confirm as confirm12 } from "@inquirer/prompts";
|
|
11347
11916
|
function createTaskCancelCommand() {
|
|
11348
|
-
return new
|
|
11917
|
+
return new Command69("cancel").description("Cancel a task (with dependency impact check)").argument("<id>", "Task ID (e.g. T-001)").option("-y, --yes", "Skip confirmation").option("-j, --json", "Output JSON").action(async (id, options) => {
|
|
11349
11918
|
const service = new TaskService();
|
|
11350
11919
|
try {
|
|
11351
11920
|
const task = await service.findById(id);
|
|
11352
11921
|
if (!task) {
|
|
11353
|
-
console.error(
|
|
11922
|
+
console.error(chalk42.red(`\u274C Task ${id} not found`));
|
|
11354
11923
|
process.exit(1);
|
|
11355
11924
|
}
|
|
11356
11925
|
if (task.status === "cancelled") {
|
|
11357
|
-
console.error(
|
|
11926
|
+
console.error(chalk42.yellow(`\u26A0\uFE0F Task ${id} is already cancelled`));
|
|
11358
11927
|
process.exit(0);
|
|
11359
11928
|
}
|
|
11360
11929
|
if (task.status === "done") {
|
|
11361
|
-
console.error(
|
|
11930
|
+
console.error(chalk42.red(`\u274C Task ${id} is already done \u2014 cannot cancel`));
|
|
11362
11931
|
process.exit(1);
|
|
11363
11932
|
}
|
|
11364
11933
|
const dependents = await service.getDependents(id);
|
|
@@ -11366,17 +11935,17 @@ function createTaskCancelCommand() {
|
|
|
11366
11935
|
(t) => t.status !== "done" && t.status !== "cancelled"
|
|
11367
11936
|
);
|
|
11368
11937
|
const icon = STATUS_ICONS[task.status] || "\u{1F4CB}";
|
|
11369
|
-
console.log(
|
|
11938
|
+
console.log(chalk42.bold(`
|
|
11370
11939
|
\u26AB Cancel: ${icon} ${task.id} \u2014 ${task.title}
|
|
11371
11940
|
`));
|
|
11372
11941
|
if (activeDependents.length > 0) {
|
|
11373
|
-
console.log(
|
|
11942
|
+
console.log(chalk42.yellow(`\u26A0\uFE0F ${activeDependents.length} task(s) ph\u1EE5 thu\u1ED9c v\xE0o ${id}:`));
|
|
11374
11943
|
for (const dep of activeDependents) {
|
|
11375
11944
|
const depIcon = STATUS_ICONS[dep.status] || "\u{1F4CB}";
|
|
11376
|
-
console.log(` ${depIcon} ${
|
|
11945
|
+
console.log(` ${depIcon} ${chalk42.dim(dep.id)} ${dep.title}`);
|
|
11377
11946
|
}
|
|
11378
11947
|
console.log(
|
|
11379
|
-
|
|
11948
|
+
chalk42.dim(
|
|
11380
11949
|
`
|
|
11381
11950
|
\u2192 Cancel s\u1EBD resolve dependency, c\xE1c task tr\xEAn c\xF3 th\u1EC3 \u0111\u01B0\u1EE3c unblock.`
|
|
11382
11951
|
)
|
|
@@ -11384,12 +11953,12 @@ function createTaskCancelCommand() {
|
|
|
11384
11953
|
console.log();
|
|
11385
11954
|
}
|
|
11386
11955
|
if (!options.yes) {
|
|
11387
|
-
const proceed = await
|
|
11956
|
+
const proceed = await confirm12({
|
|
11388
11957
|
message: `Cancel task ${id}?`,
|
|
11389
11958
|
default: false
|
|
11390
11959
|
});
|
|
11391
11960
|
if (!proceed) {
|
|
11392
|
-
console.log(
|
|
11961
|
+
console.log(chalk42.dim("\u0110\xE3 hu\u1EF7."));
|
|
11393
11962
|
return;
|
|
11394
11963
|
}
|
|
11395
11964
|
}
|
|
@@ -11398,10 +11967,10 @@ function createTaskCancelCommand() {
|
|
|
11398
11967
|
console.log(JSON.stringify(updated, null, 2));
|
|
11399
11968
|
return;
|
|
11400
11969
|
}
|
|
11401
|
-
console.log(
|
|
11970
|
+
console.log(chalk42.green(`\u2705 ${updated.id}: ${updated.title} \u2192 cancelled`));
|
|
11402
11971
|
} catch (error) {
|
|
11403
11972
|
console.error(
|
|
11404
|
-
|
|
11973
|
+
chalk42.red(`\u274C ${error instanceof Error ? error.message : String(error)}`)
|
|
11405
11974
|
);
|
|
11406
11975
|
process.exit(1);
|
|
11407
11976
|
}
|
|
@@ -11409,27 +11978,27 @@ function createTaskCancelCommand() {
|
|
|
11409
11978
|
}
|
|
11410
11979
|
|
|
11411
11980
|
// src/commands/tasks/delete.ts
|
|
11412
|
-
import { Command as
|
|
11413
|
-
import
|
|
11414
|
-
import { confirm as
|
|
11981
|
+
import { Command as Command70 } from "commander";
|
|
11982
|
+
import chalk43 from "chalk";
|
|
11983
|
+
import { confirm as confirm13 } from "@inquirer/prompts";
|
|
11415
11984
|
function createTaskDeleteCommand() {
|
|
11416
|
-
return new
|
|
11985
|
+
return new Command70("delete").description("Delete a task permanently (with dependency impact check)").argument("<id>", "Task ID (e.g. T-001)").option("-y, --yes", "Skip confirmation").option("-j, --json", "Output JSON before deletion").action(async (id, options) => {
|
|
11417
11986
|
const service = new TaskService();
|
|
11418
11987
|
try {
|
|
11419
11988
|
const task = await service.findById(id);
|
|
11420
11989
|
if (!task) {
|
|
11421
|
-
console.error(
|
|
11990
|
+
console.error(chalk43.red(`\u274C Task ${id} not found`));
|
|
11422
11991
|
process.exit(1);
|
|
11423
11992
|
}
|
|
11424
11993
|
const dependents = await service.getDependents(id);
|
|
11425
11994
|
const icon = STATUS_ICONS[task.status] || "\u{1F4CB}";
|
|
11426
|
-
console.log(
|
|
11995
|
+
console.log(chalk43.bold(`
|
|
11427
11996
|
\u{1F5D1}\uFE0F Delete: ${icon} ${task.id} \u2014 ${task.title}
|
|
11428
11997
|
`));
|
|
11429
|
-
console.log(` ${
|
|
11430
|
-
console.log(` ${
|
|
11998
|
+
console.log(` ${chalk43.dim("Status:")} ${task.status}`);
|
|
11999
|
+
console.log(` ${chalk43.dim("Parent:")} ${task.parent || "(none)"}`);
|
|
11431
12000
|
if (task.depends_on.length > 0) {
|
|
11432
|
-
console.log(` ${
|
|
12001
|
+
console.log(` ${chalk43.dim("Depends:")} ${task.depends_on.join(", ")}`);
|
|
11433
12002
|
}
|
|
11434
12003
|
console.log();
|
|
11435
12004
|
if (dependents.length > 0) {
|
|
@@ -11440,23 +12009,23 @@ function createTaskDeleteCommand() {
|
|
|
11440
12009
|
(t) => t.status === "done" || t.status === "cancelled"
|
|
11441
12010
|
);
|
|
11442
12011
|
console.log(
|
|
11443
|
-
|
|
12012
|
+
chalk43.yellow(
|
|
11444
12013
|
`\u26A0\uFE0F ${dependents.length} task(s) c\xF3 depends_on \u2192 ${id}:`
|
|
11445
12014
|
)
|
|
11446
12015
|
);
|
|
11447
12016
|
for (const dep of dependents) {
|
|
11448
12017
|
const depIcon = STATUS_ICONS[dep.status] || "\u{1F4CB}";
|
|
11449
|
-
console.log(` ${depIcon} ${
|
|
12018
|
+
console.log(` ${depIcon} ${chalk43.dim(dep.id)} ${dep.title}`);
|
|
11450
12019
|
}
|
|
11451
12020
|
console.log(
|
|
11452
|
-
|
|
12021
|
+
chalk43.dim(
|
|
11453
12022
|
`
|
|
11454
12023
|
\u2192 Xo\xE1 s\u1EBD remove ${id} kh\u1ECFi depends_on c\u1EE7a c\xE1c task tr\xEAn.`
|
|
11455
12024
|
)
|
|
11456
12025
|
);
|
|
11457
12026
|
if (activeDependents.length > 0) {
|
|
11458
12027
|
console.log(
|
|
11459
|
-
|
|
12028
|
+
chalk43.dim(
|
|
11460
12029
|
` \u2192 ${activeDependents.length} task(s) ch\u01B0a done c\xF3 th\u1EC3 b\u1ECB \u1EA3nh h\u01B0\u1EDFng (unblock).`
|
|
11461
12030
|
)
|
|
11462
12031
|
);
|
|
@@ -11464,12 +12033,12 @@ function createTaskDeleteCommand() {
|
|
|
11464
12033
|
console.log();
|
|
11465
12034
|
}
|
|
11466
12035
|
if (!options.yes) {
|
|
11467
|
-
const proceed = await
|
|
12036
|
+
const proceed = await confirm13({
|
|
11468
12037
|
message: `Xo\xE1 v\u0129nh vi\u1EC5n task ${id}?`,
|
|
11469
12038
|
default: false
|
|
11470
12039
|
});
|
|
11471
12040
|
if (!proceed) {
|
|
11472
|
-
console.log(
|
|
12041
|
+
console.log(chalk43.dim("\u0110\xE3 hu\u1EF7."));
|
|
11473
12042
|
return;
|
|
11474
12043
|
}
|
|
11475
12044
|
}
|
|
@@ -11478,10 +12047,10 @@ function createTaskDeleteCommand() {
|
|
|
11478
12047
|
}
|
|
11479
12048
|
await service.deleteTask(id);
|
|
11480
12049
|
if (!options.json) {
|
|
11481
|
-
console.log(
|
|
12050
|
+
console.log(chalk43.green(`\u2705 \u0110\xE3 xo\xE1 ${task.id}: ${task.title}`));
|
|
11482
12051
|
if (dependents.length > 0) {
|
|
11483
12052
|
console.log(
|
|
11484
|
-
|
|
12053
|
+
chalk43.dim(
|
|
11485
12054
|
` \u0110\xE3 c\u1EADp nh\u1EADt depends_on cho: ${dependents.map((d) => d.id).join(", ")}`
|
|
11486
12055
|
)
|
|
11487
12056
|
);
|
|
@@ -11489,7 +12058,7 @@ function createTaskDeleteCommand() {
|
|
|
11489
12058
|
}
|
|
11490
12059
|
} catch (error) {
|
|
11491
12060
|
console.error(
|
|
11492
|
-
|
|
12061
|
+
chalk43.red(`\u274C ${error instanceof Error ? error.message : String(error)}`)
|
|
11493
12062
|
);
|
|
11494
12063
|
process.exit(1);
|
|
11495
12064
|
}
|
|
@@ -11498,7 +12067,7 @@ function createTaskDeleteCommand() {
|
|
|
11498
12067
|
|
|
11499
12068
|
// src/commands/tasks/index.ts
|
|
11500
12069
|
function createTasksCommand() {
|
|
11501
|
-
const cmd = new
|
|
12070
|
+
const cmd = new Command71("tasks").alias("t").description("Task management \u2014 track, assign, and manage development tasks").hook("preAction", (thisCommand, actionCommand) => {
|
|
11502
12071
|
if (actionCommand.name() !== "guide") {
|
|
11503
12072
|
TaskService.ensureJai1Dir();
|
|
11504
12073
|
}
|
|
@@ -11525,12 +12094,12 @@ function createTasksCommand() {
|
|
|
11525
12094
|
}
|
|
11526
12095
|
|
|
11527
12096
|
// src/commands/kit/index.ts
|
|
11528
|
-
import { Command as
|
|
11529
|
-
import
|
|
12097
|
+
import { Command as Command75 } from "commander";
|
|
12098
|
+
import chalk45 from "chalk";
|
|
11530
12099
|
|
|
11531
12100
|
// src/commands/kit/list.ts
|
|
11532
|
-
import { Command as
|
|
11533
|
-
import
|
|
12101
|
+
import { Command as Command72 } from "commander";
|
|
12102
|
+
import chalk44 from "chalk";
|
|
11534
12103
|
import Table6 from "cli-table3";
|
|
11535
12104
|
|
|
11536
12105
|
// src/services/starter-kit.service.ts
|
|
@@ -11598,13 +12167,13 @@ var StarterKitService = class {
|
|
|
11598
12167
|
|
|
11599
12168
|
// src/commands/kit/list.ts
|
|
11600
12169
|
function createKitListCommand() {
|
|
11601
|
-
return new
|
|
12170
|
+
return new Command72("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) => {
|
|
11602
12171
|
const configService = new ConfigService();
|
|
11603
12172
|
const config = await configService.load();
|
|
11604
12173
|
if (!config) {
|
|
11605
12174
|
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
11606
12175
|
}
|
|
11607
|
-
console.log(
|
|
12176
|
+
console.log(chalk44.cyan("\u{1F4E6} \u0110ang t\u1EA3i danh s\xE1ch starter kits..."));
|
|
11608
12177
|
console.log();
|
|
11609
12178
|
const kitService = new StarterKitService();
|
|
11610
12179
|
const kits = await kitService.list(config, {
|
|
@@ -11612,9 +12181,9 @@ function createKitListCommand() {
|
|
|
11612
12181
|
search: options.search
|
|
11613
12182
|
});
|
|
11614
12183
|
if (kits.length === 0) {
|
|
11615
|
-
console.log(
|
|
12184
|
+
console.log(chalk44.yellow("Kh\xF4ng t\xECm th\u1EA5y starter kits n\xE0o."));
|
|
11616
12185
|
if (options.category || options.search) {
|
|
11617
|
-
console.log(
|
|
12186
|
+
console.log(chalk44.dim("Th\u1EED b\u1ECF filter \u0111\u1EC3 xem t\u1EA5t c\u1EA3."));
|
|
11618
12187
|
}
|
|
11619
12188
|
return;
|
|
11620
12189
|
}
|
|
@@ -11638,35 +12207,35 @@ function createKitListCommand() {
|
|
|
11638
12207
|
const categoryKits = byCategory[category];
|
|
11639
12208
|
const categoryIcon = category === "frontend" ? "\u{1F3A8}" : category === "backend" ? "\u2699\uFE0F" : category === "fullstack" ? "\u{1F680}" : "\u{1F4E6}";
|
|
11640
12209
|
console.log(
|
|
11641
|
-
|
|
12210
|
+
chalk44.bold(`${categoryIcon} ${category.charAt(0).toUpperCase() + category.slice(1)}`)
|
|
11642
12211
|
);
|
|
11643
12212
|
const table = new Table6({
|
|
11644
12213
|
head: [
|
|
11645
|
-
|
|
11646
|
-
|
|
11647
|
-
|
|
12214
|
+
chalk44.cyan("Slug"),
|
|
12215
|
+
chalk44.cyan("M\xF4 t\u1EA3"),
|
|
12216
|
+
chalk44.cyan("Version")
|
|
11648
12217
|
],
|
|
11649
12218
|
style: { head: [], border: ["gray"] }
|
|
11650
12219
|
});
|
|
11651
12220
|
for (const kit of categoryKits) {
|
|
11652
12221
|
table.push([
|
|
11653
|
-
|
|
11654
|
-
|
|
11655
|
-
|
|
12222
|
+
chalk44.white(kit.slug),
|
|
12223
|
+
chalk44.dim(kit.description.slice(0, 50)),
|
|
12224
|
+
chalk44.green(`v${kit.version}`)
|
|
11656
12225
|
]);
|
|
11657
12226
|
}
|
|
11658
12227
|
console.log(table.toString());
|
|
11659
12228
|
console.log();
|
|
11660
12229
|
}
|
|
11661
|
-
console.log(
|
|
11662
|
-
console.log(
|
|
12230
|
+
console.log(chalk44.dim(`T\u1ED5ng c\u1ED9ng: ${kits.length} starter kit(s)`));
|
|
12231
|
+
console.log(chalk44.dim('\n\u{1F4A1} Ch\u1EA1y "jai1 kit create <slug>" \u0111\u1EC3 t\u1EA1o project m\u1EDBi'));
|
|
11663
12232
|
});
|
|
11664
12233
|
}
|
|
11665
12234
|
|
|
11666
12235
|
// src/commands/kit/info.ts
|
|
11667
|
-
import { Command as
|
|
12236
|
+
import { Command as Command73 } from "commander";
|
|
11668
12237
|
function createKitInfoCommand() {
|
|
11669
|
-
return new
|
|
12238
|
+
return new Command73("info").description("Show detailed information about a starter kit").argument("<slug>", "Starter kit slug").action(async (slug) => {
|
|
11670
12239
|
const configService = new ConfigService();
|
|
11671
12240
|
const config = await configService.load();
|
|
11672
12241
|
if (!config) {
|
|
@@ -11715,7 +12284,7 @@ Post-Init Commands:`);
|
|
|
11715
12284
|
}
|
|
11716
12285
|
|
|
11717
12286
|
// src/commands/kit/create.ts
|
|
11718
|
-
import { Command as
|
|
12287
|
+
import { Command as Command74 } from "commander";
|
|
11719
12288
|
import { promises as fs20 } from "fs";
|
|
11720
12289
|
import { join as join11 } from "path";
|
|
11721
12290
|
import { select as select3, input as input2, checkbox as checkbox4 } from "@inquirer/prompts";
|
|
@@ -11760,7 +12329,7 @@ var HookExecutor = class {
|
|
|
11760
12329
|
|
|
11761
12330
|
// src/commands/kit/create.ts
|
|
11762
12331
|
function createKitCreateCommand() {
|
|
11763
|
-
return new
|
|
12332
|
+
return new Command74("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) => {
|
|
11764
12333
|
const configService = new ConfigService();
|
|
11765
12334
|
const config = await configService.load();
|
|
11766
12335
|
if (!config) {
|
|
@@ -11939,23 +12508,23 @@ async function getAllFiles(dir) {
|
|
|
11939
12508
|
|
|
11940
12509
|
// src/commands/kit/index.ts
|
|
11941
12510
|
function showKitHelp() {
|
|
11942
|
-
console.log(
|
|
12511
|
+
console.log(chalk45.bold.cyan("\u{1F4E6} jai1 kit") + chalk45.dim(" - Qu\u1EA3n l\xFD starter kits"));
|
|
11943
12512
|
console.log();
|
|
11944
|
-
console.log(
|
|
11945
|
-
console.log(` ${
|
|
11946
|
-
console.log(` ${
|
|
11947
|
-
console.log(` ${
|
|
12513
|
+
console.log(chalk45.bold("C\xE1c l\u1EC7nh:"));
|
|
12514
|
+
console.log(` ${chalk45.cyan("list")} Li\u1EC7t k\xEA c\xE1c starter kits c\xF3 s\u1EB5n`);
|
|
12515
|
+
console.log(` ${chalk45.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t starter kit`);
|
|
12516
|
+
console.log(` ${chalk45.cyan("create")} T\u1EA1o project m\u1EDBi t\u1EEB starter kit`);
|
|
11948
12517
|
console.log();
|
|
11949
|
-
console.log(
|
|
11950
|
-
console.log(
|
|
11951
|
-
console.log(
|
|
11952
|
-
console.log(
|
|
11953
|
-
console.log(
|
|
12518
|
+
console.log(chalk45.bold("V\xED d\u1EE5:"));
|
|
12519
|
+
console.log(chalk45.dim(" $ jai1 kit list"));
|
|
12520
|
+
console.log(chalk45.dim(" $ jai1 kit list --category frontend"));
|
|
12521
|
+
console.log(chalk45.dim(" $ jai1 kit info next-tw4-shadcn"));
|
|
12522
|
+
console.log(chalk45.dim(" $ jai1 kit create next-tw4-shadcn my-project"));
|
|
11954
12523
|
console.log();
|
|
11955
|
-
console.log(
|
|
12524
|
+
console.log(chalk45.dim('Ch\u1EA1y "jai1 kit <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
|
|
11956
12525
|
}
|
|
11957
12526
|
function createKitCommand() {
|
|
11958
|
-
const cmd = new
|
|
12527
|
+
const cmd = new Command75("kit").description("Manage starter kits for new projects").action(() => {
|
|
11959
12528
|
showKitHelp();
|
|
11960
12529
|
});
|
|
11961
12530
|
cmd.addCommand(createKitListCommand());
|
|
@@ -11965,21 +12534,21 @@ function createKitCommand() {
|
|
|
11965
12534
|
}
|
|
11966
12535
|
|
|
11967
12536
|
// src/commands/rules/index.ts
|
|
11968
|
-
import { Command as
|
|
11969
|
-
import
|
|
12537
|
+
import { Command as Command82 } from "commander";
|
|
12538
|
+
import chalk47 from "chalk";
|
|
11970
12539
|
|
|
11971
12540
|
// src/commands/rules/list.ts
|
|
11972
|
-
import { Command as
|
|
11973
|
-
import
|
|
12541
|
+
import { Command as Command76 } from "commander";
|
|
12542
|
+
import chalk46 from "chalk";
|
|
11974
12543
|
import Table7 from "cli-table3";
|
|
11975
12544
|
function createRulesListCommand() {
|
|
11976
|
-
return new
|
|
12545
|
+
return new Command76("list").description("List available rule presets").option("--json", "Output as JSON").action(async (options) => {
|
|
11977
12546
|
const configService = new ConfigService();
|
|
11978
12547
|
const config = await configService.load();
|
|
11979
12548
|
if (!config) {
|
|
11980
12549
|
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
11981
12550
|
}
|
|
11982
|
-
console.log(
|
|
12551
|
+
console.log(chalk46.cyan("\u{1F4CB} \u0110ang t\u1EA3i danh s\xE1ch rule presets..."));
|
|
11983
12552
|
console.log();
|
|
11984
12553
|
try {
|
|
11985
12554
|
const response = await fetch(`${config.apiUrl}/api/rules/presets`, {
|
|
@@ -11996,23 +12565,23 @@ function createRulesListCommand() {
|
|
|
11996
12565
|
return;
|
|
11997
12566
|
}
|
|
11998
12567
|
if (data.total === 0) {
|
|
11999
|
-
console.log(
|
|
12568
|
+
console.log(chalk46.yellow("Kh\xF4ng c\xF3 presets n\xE0o."));
|
|
12000
12569
|
return;
|
|
12001
12570
|
}
|
|
12002
12571
|
console.log(
|
|
12003
|
-
|
|
12572
|
+
chalk46.green(`\u2713 T\xECm th\u1EA5y ${chalk46.bold(data.total)} preset${data.total > 1 ? "s" : ""}`)
|
|
12004
12573
|
);
|
|
12005
12574
|
console.log();
|
|
12006
12575
|
for (const preset of data.presets) {
|
|
12007
|
-
console.log(
|
|
12576
|
+
console.log(chalk46.bold.cyan(`\u{1F4E6} ${preset.slug}`));
|
|
12008
12577
|
const table = new Table7({
|
|
12009
12578
|
style: { head: [], border: ["gray"], compact: true },
|
|
12010
12579
|
colWidths: [15, 55]
|
|
12011
12580
|
});
|
|
12012
12581
|
table.push(
|
|
12013
|
-
[
|
|
12014
|
-
[
|
|
12015
|
-
[
|
|
12582
|
+
[chalk46.dim("T\xEAn"), chalk46.white(preset.name)],
|
|
12583
|
+
[chalk46.dim("M\xF4 t\u1EA3"), chalk46.white(preset.description)],
|
|
12584
|
+
[chalk46.dim("Version"), chalk46.green(`v${preset.version}`)]
|
|
12016
12585
|
);
|
|
12017
12586
|
const stackParts = [];
|
|
12018
12587
|
if (preset.stack.frontend) stackParts.push(preset.stack.frontend);
|
|
@@ -12020,16 +12589,16 @@ function createRulesListCommand() {
|
|
|
12020
12589
|
if (preset.stack.css) stackParts.push(preset.stack.css);
|
|
12021
12590
|
if (preset.stack.database) stackParts.push(preset.stack.database);
|
|
12022
12591
|
if (stackParts.length > 0) {
|
|
12023
|
-
table.push([
|
|
12592
|
+
table.push([chalk46.dim("Stack"), chalk46.yellow(stackParts.join(" + "))]);
|
|
12024
12593
|
}
|
|
12025
12594
|
table.push(
|
|
12026
|
-
[
|
|
12027
|
-
[
|
|
12595
|
+
[chalk46.dim("Tags"), chalk46.dim(preset.tags.join(", ") || "-")],
|
|
12596
|
+
[chalk46.dim("Downloads"), chalk46.white(preset.downloads.toString())]
|
|
12028
12597
|
);
|
|
12029
12598
|
console.log(table.toString());
|
|
12030
12599
|
console.log();
|
|
12031
12600
|
}
|
|
12032
|
-
console.log(
|
|
12601
|
+
console.log(chalk46.dim('\u{1F4A1} Ch\u1EA1y "jai1 rules apply <name>" \u0111\u1EC3 \xE1p d\u1EE5ng preset'));
|
|
12033
12602
|
} catch (error) {
|
|
12034
12603
|
throw new Error(
|
|
12035
12604
|
`L\u1ED7i khi t\u1EA3i presets: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -12039,10 +12608,10 @@ function createRulesListCommand() {
|
|
|
12039
12608
|
}
|
|
12040
12609
|
|
|
12041
12610
|
// src/commands/rules/init.ts
|
|
12042
|
-
import { Command as
|
|
12611
|
+
import { Command as Command77 } from "commander";
|
|
12043
12612
|
import { promises as fs22 } from "fs";
|
|
12044
12613
|
import { join as join13 } from "path";
|
|
12045
|
-
import { select as select4, confirm as
|
|
12614
|
+
import { select as select4, confirm as confirm14 } from "@inquirer/prompts";
|
|
12046
12615
|
|
|
12047
12616
|
// src/services/project-config.service.ts
|
|
12048
12617
|
import { promises as fs21 } from "fs";
|
|
@@ -12164,7 +12733,7 @@ var ProjectConfigService = class {
|
|
|
12164
12733
|
|
|
12165
12734
|
// src/commands/rules/init.ts
|
|
12166
12735
|
function createRulesInitCommand() {
|
|
12167
|
-
return new
|
|
12736
|
+
return new Command77("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) => {
|
|
12168
12737
|
const configService = new ConfigService();
|
|
12169
12738
|
const config = await configService.load();
|
|
12170
12739
|
if (!config) {
|
|
@@ -12226,7 +12795,7 @@ function createRulesInitCommand() {
|
|
|
12226
12795
|
});
|
|
12227
12796
|
}
|
|
12228
12797
|
if (!options.yes) {
|
|
12229
|
-
const proceed = await
|
|
12798
|
+
const proceed = await confirm14({
|
|
12230
12799
|
message: `Apply preset '${bundle.preset.name}' to current directory?`,
|
|
12231
12800
|
default: true
|
|
12232
12801
|
});
|
|
@@ -12296,10 +12865,10 @@ async function applyAgentsMdFormat(bundle) {
|
|
|
12296
12865
|
}
|
|
12297
12866
|
|
|
12298
12867
|
// src/commands/rules/apply.ts
|
|
12299
|
-
import { Command as
|
|
12868
|
+
import { Command as Command78 } from "commander";
|
|
12300
12869
|
import { promises as fs24 } from "fs";
|
|
12301
12870
|
import { join as join15 } from "path";
|
|
12302
|
-
import { search, confirm as
|
|
12871
|
+
import { search, confirm as confirm15, checkbox as checkbox5 } from "@inquirer/prompts";
|
|
12303
12872
|
|
|
12304
12873
|
// src/services/rules-generator.service.ts
|
|
12305
12874
|
var RulesGeneratorService = class {
|
|
@@ -12810,7 +13379,7 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
12810
13379
|
|
|
12811
13380
|
// src/commands/rules/apply.ts
|
|
12812
13381
|
function createRulesApplyCommand() {
|
|
12813
|
-
return new
|
|
13382
|
+
return new Command78("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) => {
|
|
12814
13383
|
const configService = new ConfigService();
|
|
12815
13384
|
const config = await configService.load();
|
|
12816
13385
|
if (!config) {
|
|
@@ -12966,7 +13535,7 @@ function createRulesApplyCommand() {
|
|
|
12966
13535
|
if (backupPath) {
|
|
12967
13536
|
console.log(` Backup: ${backupPath}`);
|
|
12968
13537
|
}
|
|
12969
|
-
const proceed = await
|
|
13538
|
+
const proceed = await confirm15({
|
|
12970
13539
|
message: "Apply these rules to the current directory?",
|
|
12971
13540
|
default: true
|
|
12972
13541
|
});
|
|
@@ -13105,11 +13674,11 @@ function createRulesApplyCommand() {
|
|
|
13105
13674
|
}
|
|
13106
13675
|
|
|
13107
13676
|
// src/commands/rules/restore.ts
|
|
13108
|
-
import { Command as
|
|
13677
|
+
import { Command as Command79 } from "commander";
|
|
13109
13678
|
import { join as join16 } from "path";
|
|
13110
|
-
import { select as select5, confirm as
|
|
13679
|
+
import { select as select5, confirm as confirm16 } from "@inquirer/prompts";
|
|
13111
13680
|
function createRulesRestoreCommand() {
|
|
13112
|
-
return new
|
|
13681
|
+
return new Command79("restore").description("Restore rules from a backup").option("--latest", "Restore the most recent backup").option("-y, --yes", "Skip confirmation").action(async (options) => {
|
|
13113
13682
|
const backupService = new BackupService();
|
|
13114
13683
|
const backups = await backupService.listBackups();
|
|
13115
13684
|
if (backups.length === 0) {
|
|
@@ -13142,7 +13711,7 @@ function createRulesRestoreCommand() {
|
|
|
13142
13711
|
console.log(` IDEs: ${selectedBackup.ides.join(", ")}`);
|
|
13143
13712
|
console.log(` Files: ${selectedBackup.files.length}`);
|
|
13144
13713
|
if (!options.yes) {
|
|
13145
|
-
const proceed = await
|
|
13714
|
+
const proceed = await confirm16({
|
|
13146
13715
|
message: "This will overwrite current rules. Continue?",
|
|
13147
13716
|
default: false
|
|
13148
13717
|
});
|
|
@@ -13178,12 +13747,12 @@ function formatTimestamp(timestamp) {
|
|
|
13178
13747
|
}
|
|
13179
13748
|
|
|
13180
13749
|
// src/commands/rules/sync.ts
|
|
13181
|
-
import { Command as
|
|
13750
|
+
import { Command as Command80 } from "commander";
|
|
13182
13751
|
import { promises as fs25 } from "fs";
|
|
13183
13752
|
import { join as join17 } from "path";
|
|
13184
|
-
import { checkbox as checkbox6, confirm as
|
|
13753
|
+
import { checkbox as checkbox6, confirm as confirm17, Separator } from "@inquirer/prompts";
|
|
13185
13754
|
function createRulesSyncCommand() {
|
|
13186
|
-
return new
|
|
13755
|
+
return new Command80("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) => {
|
|
13187
13756
|
const rulePresetDir = join17(process.cwd(), ".jai1", "rule-preset");
|
|
13188
13757
|
const presetJsonPath = join17(rulePresetDir, "preset.json");
|
|
13189
13758
|
let presetExists = false;
|
|
@@ -13232,7 +13801,7 @@ Detected ${detected.length} active IDE(s):
|
|
|
13232
13801
|
console.log(` ${confidence} ${d.name} - ${d.ruleCount} rules`);
|
|
13233
13802
|
});
|
|
13234
13803
|
if (!options.yes) {
|
|
13235
|
-
const proceed = await
|
|
13804
|
+
const proceed = await confirm17({
|
|
13236
13805
|
message: "\nSync these detected IDEs?",
|
|
13237
13806
|
default: true
|
|
13238
13807
|
});
|
|
@@ -13401,11 +13970,11 @@ function buildIdeChoices(currentIdes, detected, suggestions) {
|
|
|
13401
13970
|
}
|
|
13402
13971
|
|
|
13403
13972
|
// src/commands/rules/info.ts
|
|
13404
|
-
import { Command as
|
|
13973
|
+
import { Command as Command81 } from "commander";
|
|
13405
13974
|
import { promises as fs26 } from "fs";
|
|
13406
13975
|
import { join as join18 } from "path";
|
|
13407
13976
|
function createRulesInfoCommand() {
|
|
13408
|
-
return new
|
|
13977
|
+
return new Command81("info").description("Show current preset information").option("--json", "Output as JSON").action(async (options) => {
|
|
13409
13978
|
const projectConfigService = new ProjectConfigService();
|
|
13410
13979
|
const rulesConfig = await projectConfigService.loadRules();
|
|
13411
13980
|
if (!rulesConfig) {
|
|
@@ -13508,26 +14077,26 @@ async function checkIdeFilesExist(ideId, format) {
|
|
|
13508
14077
|
|
|
13509
14078
|
// src/commands/rules/index.ts
|
|
13510
14079
|
function showRulesHelp() {
|
|
13511
|
-
console.log(
|
|
14080
|
+
console.log(chalk47.bold.cyan("\u{1F4CB} jai1 rules") + chalk47.dim(" - Qu\u1EA3n l\xFD rule presets cho AI agents"));
|
|
13512
14081
|
console.log();
|
|
13513
|
-
console.log(
|
|
13514
|
-
console.log(` ${
|
|
13515
|
-
console.log(` ${
|
|
13516
|
-
console.log(` ${
|
|
13517
|
-
console.log(` ${
|
|
13518
|
-
console.log(` ${
|
|
13519
|
-
console.log(` ${
|
|
14082
|
+
console.log(chalk47.bold("C\xE1c l\u1EC7nh:"));
|
|
14083
|
+
console.log(` ${chalk47.cyan("list")} Li\u1EC7t k\xEA c\xE1c presets c\xF3 s\u1EB5n`);
|
|
14084
|
+
console.log(` ${chalk47.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t preset`);
|
|
14085
|
+
console.log(` ${chalk47.cyan("init")} Kh\u1EDFi t\u1EA1o rules t\u1EEB preset`);
|
|
14086
|
+
console.log(` ${chalk47.cyan("apply")} \xC1p d\u1EE5ng preset v\xE0o project`);
|
|
14087
|
+
console.log(` ${chalk47.cyan("sync")} \u0110\u1ED3ng b\u1ED9 rules sang c\xE1c \u0111\u1ECBnh d\u1EA1ng IDE`);
|
|
14088
|
+
console.log(` ${chalk47.cyan("restore")} Kh\xF4i ph\u1EE5c rules t\u1EEB backup`);
|
|
13520
14089
|
console.log();
|
|
13521
|
-
console.log(
|
|
13522
|
-
console.log(
|
|
13523
|
-
console.log(
|
|
13524
|
-
console.log(
|
|
13525
|
-
console.log(
|
|
14090
|
+
console.log(chalk47.bold("V\xED d\u1EE5:"));
|
|
14091
|
+
console.log(chalk47.dim(" $ jai1 rules list"));
|
|
14092
|
+
console.log(chalk47.dim(" $ jai1 rules info react-typescript"));
|
|
14093
|
+
console.log(chalk47.dim(" $ jai1 rules init --preset=react-typescript"));
|
|
14094
|
+
console.log(chalk47.dim(" $ jai1 rules apply react-typescript"));
|
|
13526
14095
|
console.log();
|
|
13527
|
-
console.log(
|
|
14096
|
+
console.log(chalk47.dim('Ch\u1EA1y "jai1 rules <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
|
|
13528
14097
|
}
|
|
13529
14098
|
function createRulesCommand() {
|
|
13530
|
-
const rulesCommand = new
|
|
14099
|
+
const rulesCommand = new Command82("rules").description("Manage rule presets for AI agents").action(() => {
|
|
13531
14100
|
showRulesHelp();
|
|
13532
14101
|
});
|
|
13533
14102
|
rulesCommand.addCommand(createRulesListCommand());
|
|
@@ -13540,12 +14109,12 @@ function createRulesCommand() {
|
|
|
13540
14109
|
}
|
|
13541
14110
|
|
|
13542
14111
|
// src/commands/skills/index.ts
|
|
13543
|
-
import { Command as
|
|
13544
|
-
import
|
|
14112
|
+
import { Command as Command88 } from "commander";
|
|
14113
|
+
import chalk53 from "chalk";
|
|
13545
14114
|
|
|
13546
14115
|
// src/commands/skills/find.ts
|
|
13547
|
-
import { Command as
|
|
13548
|
-
import
|
|
14116
|
+
import { Command as Command83 } from "commander";
|
|
14117
|
+
import chalk48 from "chalk";
|
|
13549
14118
|
import Table8 from "cli-table3";
|
|
13550
14119
|
|
|
13551
14120
|
// src/services/skills.service.ts
|
|
@@ -13839,7 +14408,7 @@ var SkillsService = class {
|
|
|
13839
14408
|
|
|
13840
14409
|
// src/commands/skills/find.ts
|
|
13841
14410
|
function createSkillsFindCommand() {
|
|
13842
|
-
return new
|
|
14411
|
+
return new Command83("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) => {
|
|
13843
14412
|
const searchNpm = options.skillsh || options.all;
|
|
13844
14413
|
const searchServer = !options.skillsh || options.all;
|
|
13845
14414
|
if (searchServer) {
|
|
@@ -13848,19 +14417,19 @@ function createSkillsFindCommand() {
|
|
|
13848
14417
|
if (!config) {
|
|
13849
14418
|
throw new ValidationError('Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.');
|
|
13850
14419
|
}
|
|
13851
|
-
console.log(
|
|
14420
|
+
console.log(chalk48.cyan("\u{1F50D} \u0110ang t\xECm ki\u1EBFm tr\xEAn Jai1 server..."));
|
|
13852
14421
|
console.log();
|
|
13853
14422
|
const skillsService = new SkillsService();
|
|
13854
14423
|
const results = await skillsService.searchFromServer(config, query);
|
|
13855
14424
|
if (results.length === 0) {
|
|
13856
|
-
console.log(
|
|
14425
|
+
console.log(chalk48.yellow("Kh\xF4ng t\xECm th\u1EA5y skills n\xE0o tr\xEAn server."));
|
|
13857
14426
|
} else {
|
|
13858
14427
|
const table = new Table8({
|
|
13859
14428
|
head: [
|
|
13860
|
-
|
|
13861
|
-
|
|
13862
|
-
|
|
13863
|
-
|
|
14429
|
+
chalk48.cyan("T\xEAn"),
|
|
14430
|
+
chalk48.cyan("M\xF4 t\u1EA3"),
|
|
14431
|
+
chalk48.cyan("Version"),
|
|
14432
|
+
chalk48.cyan("Downloads")
|
|
13864
14433
|
],
|
|
13865
14434
|
style: { head: [], border: ["gray"] },
|
|
13866
14435
|
colWidths: [25, 40, 10, 12]
|
|
@@ -13868,70 +14437,70 @@ function createSkillsFindCommand() {
|
|
|
13868
14437
|
for (const skill of results) {
|
|
13869
14438
|
const name = skill.filepath.replace("skills/", "");
|
|
13870
14439
|
table.push([
|
|
13871
|
-
|
|
13872
|
-
|
|
13873
|
-
|
|
13874
|
-
|
|
14440
|
+
chalk48.white(name),
|
|
14441
|
+
chalk48.dim((skill.description || "").slice(0, 38)),
|
|
14442
|
+
chalk48.green(skill.version || "-"),
|
|
14443
|
+
chalk48.dim(String(skill.downloads || 0))
|
|
13875
14444
|
]);
|
|
13876
14445
|
}
|
|
13877
|
-
console.log(
|
|
14446
|
+
console.log(chalk48.bold(`\u{1F4E6} Jai1 Server (${results.length} k\u1EBFt qu\u1EA3)`));
|
|
13878
14447
|
console.log(table.toString());
|
|
13879
14448
|
console.log();
|
|
13880
14449
|
}
|
|
13881
14450
|
}
|
|
13882
14451
|
if (searchNpm) {
|
|
13883
|
-
console.log(
|
|
14452
|
+
console.log(chalk48.cyan("\u{1F50D} \u0110ang t\xECm ki\u1EBFm tr\xEAn npm skills..."));
|
|
13884
14453
|
console.log();
|
|
13885
14454
|
const skillsService = new SkillsService();
|
|
13886
14455
|
try {
|
|
13887
14456
|
const output = await skillsService.npmSkillsFind(query);
|
|
13888
|
-
console.log(
|
|
14457
|
+
console.log(chalk48.bold("\u{1F310} npm Skills Registry"));
|
|
13889
14458
|
console.log(output);
|
|
13890
14459
|
} catch (error) {
|
|
13891
|
-
console.log(
|
|
14460
|
+
console.log(chalk48.yellow(
|
|
13892
14461
|
`Kh\xF4ng th\u1EC3 t\xECm ki\u1EBFm tr\xEAn npm: ${error instanceof Error ? error.message : String(error)}`
|
|
13893
14462
|
));
|
|
13894
14463
|
}
|
|
13895
14464
|
}
|
|
13896
14465
|
if (searchServer && !searchNpm) {
|
|
13897
|
-
console.log(
|
|
14466
|
+
console.log(chalk48.dim('\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t t\u1EEB server'));
|
|
13898
14467
|
} else if (searchNpm && !searchServer) {
|
|
13899
|
-
console.log(
|
|
14468
|
+
console.log(chalk48.dim('\u{1F4A1} D\xF9ng "j skills add <owner/repo@skill> --skillsh" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
|
|
13900
14469
|
} else {
|
|
13901
|
-
console.log(
|
|
13902
|
-
console.log(
|
|
13903
|
-
console.log(
|
|
14470
|
+
console.log(chalk48.dim("\u{1F4A1} C\xE0i \u0111\u1EB7t:"));
|
|
14471
|
+
console.log(chalk48.dim(" Server: j skills add <t\xEAn>"));
|
|
14472
|
+
console.log(chalk48.dim(" Skills.sh: j skills add <owner/repo@skill> --skillsh"));
|
|
13904
14473
|
}
|
|
13905
14474
|
});
|
|
13906
14475
|
}
|
|
13907
14476
|
|
|
13908
14477
|
// src/commands/skills/add.ts
|
|
13909
|
-
import { Command as
|
|
14478
|
+
import { Command as Command84 } from "commander";
|
|
13910
14479
|
import { join as join20 } from "path";
|
|
13911
|
-
import
|
|
14480
|
+
import chalk49 from "chalk";
|
|
13912
14481
|
import { checkbox as checkbox7 } from "@inquirer/prompts";
|
|
13913
14482
|
function createSkillsAddCommand() {
|
|
13914
|
-
return new
|
|
14483
|
+
return new Command84("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) => {
|
|
13915
14484
|
const skillsService = new SkillsService();
|
|
13916
14485
|
const projectRoot = process.cwd();
|
|
13917
14486
|
const headless = options.yes === true;
|
|
13918
14487
|
if (options.skillsh) {
|
|
13919
|
-
console.log(
|
|
14488
|
+
console.log(chalk49.cyan(`\u{1F310} \u0110ang c\xE0i \u0111\u1EB7t skill t\u1EEB npm: ${name}...`));
|
|
13920
14489
|
console.log();
|
|
13921
14490
|
const output = await skillsService.npmSkillsAdd(name, projectRoot);
|
|
13922
14491
|
console.log(output);
|
|
13923
|
-
console.log(
|
|
14492
|
+
console.log(chalk49.green("\u2705 C\xE0i \u0111\u1EB7t t\u1EEB npm th\xE0nh c\xF4ng!"));
|
|
13924
14493
|
} else {
|
|
13925
14494
|
const configService = new ConfigService();
|
|
13926
14495
|
const config = await configService.load();
|
|
13927
14496
|
if (!config) {
|
|
13928
14497
|
throw new ValidationError('Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.');
|
|
13929
14498
|
}
|
|
13930
|
-
console.log(
|
|
14499
|
+
console.log(chalk49.cyan(`\u{1F4E6} \u0110ang c\xE0i \u0111\u1EB7t skill: ${name}...`));
|
|
13931
14500
|
console.log();
|
|
13932
14501
|
const targetDir = join20(projectRoot, ".jai1");
|
|
13933
14502
|
await skillsService.installFromServer(config, name, targetDir);
|
|
13934
|
-
console.log(
|
|
14503
|
+
console.log(chalk49.green(`\u2705 \u0110\xE3 c\xE0i \u0111\u1EB7t skill "${name}" v\xE0o .jai1/skills/${name}/`));
|
|
13935
14504
|
}
|
|
13936
14505
|
console.log();
|
|
13937
14506
|
if (options.sync) {
|
|
@@ -13956,7 +14525,7 @@ function createSkillsAddCommand() {
|
|
|
13956
14525
|
});
|
|
13957
14526
|
}
|
|
13958
14527
|
if (selectedIdes.length > 0) {
|
|
13959
|
-
console.log(
|
|
14528
|
+
console.log(chalk49.cyan("\u{1F504} \u0110ang sync sang IDE(s)..."));
|
|
13960
14529
|
console.log();
|
|
13961
14530
|
const slug = name.includes("/") ? name.split("/").pop() : name;
|
|
13962
14531
|
const result = await skillsService.syncToIdes(
|
|
@@ -13969,24 +14538,24 @@ function createSkillsAddCommand() {
|
|
|
13969
14538
|
}
|
|
13970
14539
|
);
|
|
13971
14540
|
console.log();
|
|
13972
|
-
console.log(
|
|
14541
|
+
console.log(chalk49.green(`\u2705 Sync ho\xE0n t\u1EA5t! Created: ${result.created}, Updated: ${result.updated}`));
|
|
13973
14542
|
if (result.errors > 0) {
|
|
13974
|
-
console.log(
|
|
14543
|
+
console.log(chalk49.yellow(`\u26A0\uFE0F Errors: ${result.errors}`));
|
|
13975
14544
|
}
|
|
13976
14545
|
}
|
|
13977
14546
|
} else {
|
|
13978
|
-
console.log(
|
|
13979
|
-
console.log(
|
|
14547
|
+
console.log(chalk49.dim('\u{1F4A1} Ch\u1EA1y "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
|
|
14548
|
+
console.log(chalk49.dim(' ho\u1EB7c "j ide sync" \u0111\u1EC3 sync to\xE0n b\u1ED9 .jai1/'));
|
|
13980
14549
|
}
|
|
13981
14550
|
});
|
|
13982
14551
|
}
|
|
13983
14552
|
|
|
13984
14553
|
// src/commands/skills/list.ts
|
|
13985
|
-
import { Command as
|
|
13986
|
-
import
|
|
14554
|
+
import { Command as Command85 } from "commander";
|
|
14555
|
+
import chalk50 from "chalk";
|
|
13987
14556
|
import Table9 from "cli-table3";
|
|
13988
14557
|
function createSkillsListCommand() {
|
|
13989
|
-
return new
|
|
14558
|
+
return new Command85("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) => {
|
|
13990
14559
|
const skillsService = new SkillsService();
|
|
13991
14560
|
if (options.available) {
|
|
13992
14561
|
const configService = new ConfigService();
|
|
@@ -13994,19 +14563,19 @@ function createSkillsListCommand() {
|
|
|
13994
14563
|
if (!config) {
|
|
13995
14564
|
throw new ValidationError('Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.');
|
|
13996
14565
|
}
|
|
13997
|
-
console.log(
|
|
14566
|
+
console.log(chalk50.cyan("\u{1F4E6} \u0110ang t\u1EA3i danh s\xE1ch skills t\u1EEB server..."));
|
|
13998
14567
|
console.log();
|
|
13999
14568
|
const results = await skillsService.searchFromServer(config, options.search);
|
|
14000
14569
|
if (results.length === 0) {
|
|
14001
|
-
console.log(
|
|
14570
|
+
console.log(chalk50.yellow("Kh\xF4ng t\xECm th\u1EA5y skills n\xE0o."));
|
|
14002
14571
|
return;
|
|
14003
14572
|
}
|
|
14004
14573
|
const table = new Table9({
|
|
14005
14574
|
head: [
|
|
14006
|
-
|
|
14007
|
-
|
|
14008
|
-
|
|
14009
|
-
|
|
14575
|
+
chalk50.cyan("T\xEAn"),
|
|
14576
|
+
chalk50.cyan("M\xF4 t\u1EA3"),
|
|
14577
|
+
chalk50.cyan("Version"),
|
|
14578
|
+
chalk50.cyan("Downloads")
|
|
14010
14579
|
],
|
|
14011
14580
|
style: { head: [], border: ["gray"] },
|
|
14012
14581
|
colWidths: [28, 40, 10, 12]
|
|
@@ -14014,63 +14583,63 @@ function createSkillsListCommand() {
|
|
|
14014
14583
|
for (const skill of results) {
|
|
14015
14584
|
const name = skill.filepath.replace("skills/", "");
|
|
14016
14585
|
table.push([
|
|
14017
|
-
|
|
14018
|
-
|
|
14019
|
-
|
|
14020
|
-
|
|
14586
|
+
chalk50.white(name),
|
|
14587
|
+
chalk50.dim((skill.description || "").slice(0, 38)),
|
|
14588
|
+
chalk50.green(skill.version || "-"),
|
|
14589
|
+
chalk50.dim(String(skill.downloads || 0))
|
|
14021
14590
|
]);
|
|
14022
14591
|
}
|
|
14023
14592
|
console.log(table.toString());
|
|
14024
14593
|
console.log();
|
|
14025
|
-
console.log(
|
|
14026
|
-
console.log(
|
|
14594
|
+
console.log(chalk50.dim(`T\u1ED5ng c\u1ED9ng: ${results.length} skill(s)`));
|
|
14595
|
+
console.log(chalk50.dim('\n\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
|
|
14027
14596
|
} else {
|
|
14028
14597
|
const projectRoot = process.cwd();
|
|
14029
14598
|
const skills = await skillsService.listLocal(projectRoot);
|
|
14030
14599
|
if (skills.length === 0) {
|
|
14031
|
-
console.log(
|
|
14600
|
+
console.log(chalk50.yellow("Ch\u01B0a c\xF3 skills n\xE0o \u0111\u01B0\u1EE3c c\xE0i \u0111\u1EB7t."));
|
|
14032
14601
|
console.log();
|
|
14033
|
-
console.log(
|
|
14034
|
-
console.log(
|
|
14602
|
+
console.log(chalk50.dim('\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
|
|
14603
|
+
console.log(chalk50.dim(' ho\u1EB7c "j skills list --available" \u0111\u1EC3 xem skills c\xF3 s\u1EB5n'));
|
|
14035
14604
|
return;
|
|
14036
14605
|
}
|
|
14037
|
-
console.log(
|
|
14606
|
+
console.log(chalk50.bold.cyan("\u{1F6E0} Skills \u0111\xE3 c\xE0i \u0111\u1EB7t"));
|
|
14038
14607
|
console.log();
|
|
14039
14608
|
const table = new Table9({
|
|
14040
14609
|
head: [
|
|
14041
|
-
|
|
14042
|
-
|
|
14043
|
-
|
|
14610
|
+
chalk50.cyan("T\xEAn"),
|
|
14611
|
+
chalk50.cyan("M\xF4 t\u1EA3"),
|
|
14612
|
+
chalk50.cyan("Files")
|
|
14044
14613
|
],
|
|
14045
14614
|
style: { head: [], border: ["gray"] },
|
|
14046
14615
|
colWidths: [28, 45, 8]
|
|
14047
14616
|
});
|
|
14048
14617
|
for (const skill of skills) {
|
|
14049
14618
|
table.push([
|
|
14050
|
-
|
|
14051
|
-
|
|
14052
|
-
|
|
14619
|
+
chalk50.white(skill.slug),
|
|
14620
|
+
chalk50.dim(skill.description.slice(0, 43)),
|
|
14621
|
+
chalk50.dim(String(skill.fileCount))
|
|
14053
14622
|
]);
|
|
14054
14623
|
}
|
|
14055
14624
|
console.log(table.toString());
|
|
14056
14625
|
console.log();
|
|
14057
|
-
console.log(
|
|
14058
|
-
console.log(
|
|
14626
|
+
console.log(chalk50.dim(`T\u1ED5ng c\u1ED9ng: ${skills.length} skill(s)`));
|
|
14627
|
+
console.log(chalk50.dim('\n\u{1F4A1} D\xF9ng "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
|
|
14059
14628
|
}
|
|
14060
14629
|
});
|
|
14061
14630
|
}
|
|
14062
14631
|
|
|
14063
14632
|
// src/commands/skills/info.ts
|
|
14064
|
-
import { Command as
|
|
14065
|
-
import
|
|
14633
|
+
import { Command as Command86 } from "commander";
|
|
14634
|
+
import chalk51 from "chalk";
|
|
14066
14635
|
function createSkillsInfoCommand() {
|
|
14067
|
-
return new
|
|
14636
|
+
return new Command86("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) => {
|
|
14068
14637
|
const skillsService = new SkillsService();
|
|
14069
14638
|
if (options.server) {
|
|
14070
14639
|
const configService = new ConfigService();
|
|
14071
14640
|
const config = await configService.load();
|
|
14072
14641
|
if (!config) {
|
|
14073
|
-
console.log(
|
|
14642
|
+
console.log(chalk51.red('\u274C Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.'));
|
|
14074
14643
|
process.exit(1);
|
|
14075
14644
|
}
|
|
14076
14645
|
const filepath = name.startsWith("skills/") ? name : `skills/${name}`;
|
|
@@ -14079,7 +14648,7 @@ function createSkillsInfoCommand() {
|
|
|
14079
14648
|
try {
|
|
14080
14649
|
const component = await componentsService.get(config, filepath);
|
|
14081
14650
|
console.log(`
|
|
14082
|
-
\u{1F6E0} ${
|
|
14651
|
+
\u{1F6E0} ${chalk51.bold(component.name || name)}
|
|
14083
14652
|
`);
|
|
14084
14653
|
console.log(`Filepath: ${component.filepath}`);
|
|
14085
14654
|
console.log(`Version: ${component.version}`);
|
|
@@ -14092,47 +14661,47 @@ function createSkillsInfoCommand() {
|
|
|
14092
14661
|
}
|
|
14093
14662
|
console.log(`Type: ${component.contentType}`);
|
|
14094
14663
|
console.log();
|
|
14095
|
-
console.log(
|
|
14664
|
+
console.log(chalk51.dim('\u{1F4A1} D\xF9ng "j skills add ' + name + '" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
|
|
14096
14665
|
} catch (error) {
|
|
14097
|
-
console.log(
|
|
14666
|
+
console.log(chalk51.red(`\u274C Kh\xF4ng t\xECm th\u1EA5y skill "${name}" tr\xEAn server.`));
|
|
14098
14667
|
process.exit(1);
|
|
14099
14668
|
}
|
|
14100
14669
|
} else {
|
|
14101
14670
|
const projectRoot = process.cwd();
|
|
14102
14671
|
const skill = await skillsService.getSkillInfo(projectRoot, name);
|
|
14103
14672
|
if (!skill) {
|
|
14104
|
-
console.log(
|
|
14105
|
-
console.log(
|
|
14106
|
-
console.log(
|
|
14673
|
+
console.log(chalk51.red(`\u274C Skill "${name}" ch\u01B0a \u0111\u01B0\u1EE3c c\xE0i \u0111\u1EB7t.`));
|
|
14674
|
+
console.log(chalk51.dim('\u{1F4A1} D\xF9ng "j skills info ' + name + ' --server" \u0111\u1EC3 xem tr\xEAn server'));
|
|
14675
|
+
console.log(chalk51.dim(' ho\u1EB7c "j skills add ' + name + '" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
|
|
14107
14676
|
process.exit(1);
|
|
14108
14677
|
}
|
|
14109
14678
|
console.log(`
|
|
14110
|
-
\u{1F6E0} ${
|
|
14679
|
+
\u{1F6E0} ${chalk51.bold(skill.name)}
|
|
14111
14680
|
`);
|
|
14112
14681
|
console.log(`Slug: ${skill.slug}`);
|
|
14113
|
-
console.log(`Description: ${skill.description ||
|
|
14682
|
+
console.log(`Description: ${skill.description || chalk51.dim("(none)")}`);
|
|
14114
14683
|
console.log(`Path: ${skill.path}`);
|
|
14115
14684
|
console.log(`Files: ${skill.fileCount}`);
|
|
14116
14685
|
console.log();
|
|
14117
|
-
console.log(
|
|
14686
|
+
console.log(chalk51.dim('\u{1F4A1} D\xF9ng "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
|
|
14118
14687
|
}
|
|
14119
14688
|
});
|
|
14120
14689
|
}
|
|
14121
14690
|
|
|
14122
14691
|
// src/commands/skills/sync.ts
|
|
14123
|
-
import { Command as
|
|
14124
|
-
import
|
|
14125
|
-
import { confirm as
|
|
14692
|
+
import { Command as Command87 } from "commander";
|
|
14693
|
+
import chalk52 from "chalk";
|
|
14694
|
+
import { confirm as confirm19, checkbox as checkbox8 } from "@inquirer/prompts";
|
|
14126
14695
|
function createSkillsSyncCommand() {
|
|
14127
|
-
return new
|
|
14696
|
+
return new Command87("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) => {
|
|
14128
14697
|
const skillsService = new SkillsService();
|
|
14129
14698
|
const projectRoot = process.cwd();
|
|
14130
14699
|
const headless = options.yes === true;
|
|
14131
|
-
console.log(
|
|
14700
|
+
console.log(chalk52.bold.cyan("\n\u{1F504} Sync skills sang IDE(s)\n"));
|
|
14132
14701
|
const localSkills = await skillsService.listLocal(projectRoot);
|
|
14133
14702
|
if (localSkills.length === 0) {
|
|
14134
|
-
console.log(
|
|
14135
|
-
console.log(
|
|
14703
|
+
console.log(chalk52.yellow("\u26A0\uFE0F Kh\xF4ng c\xF3 skills n\xE0o trong .jai1/skills/"));
|
|
14704
|
+
console.log(chalk52.dim('\u{1F4A1} Ch\u1EA1y "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t skills tr\u01B0\u1EDBc'));
|
|
14136
14705
|
process.exit(1);
|
|
14137
14706
|
}
|
|
14138
14707
|
console.log(`\u{1F4C1} T\xECm th\u1EA5y ${localSkills.length} skill(s) trong .jai1/skills/`);
|
|
@@ -14164,7 +14733,7 @@ function createSkillsSyncCommand() {
|
|
|
14164
14733
|
theme: checkboxTheme
|
|
14165
14734
|
});
|
|
14166
14735
|
if (selectedIdes.length === 0) {
|
|
14167
|
-
console.log(
|
|
14736
|
+
console.log(chalk52.yellow("\n\u26A0\uFE0F Ch\u01B0a ch\u1ECDn IDE n\xE0o!"));
|
|
14168
14737
|
process.exit(0);
|
|
14169
14738
|
}
|
|
14170
14739
|
}
|
|
@@ -14179,34 +14748,34 @@ function createSkillsSyncCommand() {
|
|
|
14179
14748
|
console.log(` Total: ${totalFiles} skill folder(s) s\u1EBD \u0111\u01B0\u1EE3c sync
|
|
14180
14749
|
`);
|
|
14181
14750
|
if (options.dryRun) {
|
|
14182
|
-
console.log(
|
|
14751
|
+
console.log(chalk52.dim("\u{1F50D} DRY RUN - Kh\xF4ng c\xF3 file n\xE0o \u0111\u01B0\u1EE3c ghi\n"));
|
|
14183
14752
|
return;
|
|
14184
14753
|
}
|
|
14185
14754
|
if (!headless) {
|
|
14186
|
-
const confirmed = await
|
|
14755
|
+
const confirmed = await confirm19({
|
|
14187
14756
|
message: "Ti\u1EBFp t\u1EE5c sync?",
|
|
14188
14757
|
default: true
|
|
14189
14758
|
});
|
|
14190
14759
|
if (!confirmed) {
|
|
14191
|
-
console.log(
|
|
14760
|
+
console.log(chalk52.yellow("\n\u274C \u0110\xE3 h\u1EE7y sync.\n"));
|
|
14192
14761
|
process.exit(0);
|
|
14193
14762
|
}
|
|
14194
14763
|
}
|
|
14195
|
-
console.log(
|
|
14764
|
+
console.log(chalk52.cyan("\n\u{1F504} \u0110ang sync...\n"));
|
|
14196
14765
|
const result = await skillsService.syncToIdes(
|
|
14197
14766
|
projectRoot,
|
|
14198
14767
|
selectedIdes,
|
|
14199
14768
|
selectedSlugs,
|
|
14200
14769
|
(res) => {
|
|
14201
14770
|
const icon = res.status === "created" ? "\u2713" : res.status === "updated" ? "\u21BB" : "\u2717";
|
|
14202
|
-
const statusColor = res.status === "error" ?
|
|
14203
|
-
console.log(` ${statusColor(icon)} ${res.ide}: ${res.skill} \u2192 ${
|
|
14771
|
+
const statusColor = res.status === "error" ? chalk52.red : chalk52.green;
|
|
14772
|
+
console.log(` ${statusColor(icon)} ${res.ide}: ${res.skill} \u2192 ${chalk52.dim(res.path)}`);
|
|
14204
14773
|
if (res.status === "error" && res.error) {
|
|
14205
|
-
console.log(` ${
|
|
14774
|
+
console.log(` ${chalk52.red("Error:")} ${res.error}`);
|
|
14206
14775
|
}
|
|
14207
14776
|
}
|
|
14208
14777
|
);
|
|
14209
|
-
console.log(
|
|
14778
|
+
console.log(chalk52.green("\n\u2705 Sync ho\xE0n t\u1EA5t!\n"));
|
|
14210
14779
|
console.log(` Created: ${result.created}`);
|
|
14211
14780
|
console.log(` Updated: ${result.updated}`);
|
|
14212
14781
|
if (result.errors > 0) {
|
|
@@ -14219,27 +14788,27 @@ function createSkillsSyncCommand() {
|
|
|
14219
14788
|
// src/commands/skills/index.ts
|
|
14220
14789
|
function showSkillsHelp() {
|
|
14221
14790
|
const cli = getCliName();
|
|
14222
|
-
console.log(
|
|
14791
|
+
console.log(chalk53.bold.cyan("\u{1F6E0} " + cli + " skills") + chalk53.dim(" - Qu\u1EA3n l\xFD agent skills"));
|
|
14223
14792
|
console.log();
|
|
14224
|
-
console.log(
|
|
14225
|
-
console.log(` ${
|
|
14226
|
-
console.log(` ${
|
|
14227
|
-
console.log(` ${
|
|
14228
|
-
console.log(` ${
|
|
14229
|
-
console.log(` ${
|
|
14793
|
+
console.log(chalk53.bold("C\xE1c l\u1EC7nh:"));
|
|
14794
|
+
console.log(` ${chalk53.cyan("find")} T\xECm ki\u1EBFm skills tr\xEAn server ho\u1EB7c npm`);
|
|
14795
|
+
console.log(` ${chalk53.cyan("add")} C\xE0i \u0111\u1EB7t skill v\xE0o .jai1/skills/`);
|
|
14796
|
+
console.log(` ${chalk53.cyan("list")} Li\u1EC7t k\xEA skills \u0111\xE3 c\xE0i ho\u1EB7c c\xF3 s\u1EB5n`);
|
|
14797
|
+
console.log(` ${chalk53.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t skill`);
|
|
14798
|
+
console.log(` ${chalk53.cyan("sync")} \u0110\u1ED3ng b\u1ED9 skills sang c\xE1c IDE`);
|
|
14230
14799
|
console.log();
|
|
14231
|
-
console.log(
|
|
14232
|
-
console.log(
|
|
14233
|
-
console.log(
|
|
14234
|
-
console.log(
|
|
14235
|
-
console.log(
|
|
14236
|
-
console.log(
|
|
14237
|
-
console.log(
|
|
14800
|
+
console.log(chalk53.bold("V\xED d\u1EE5:"));
|
|
14801
|
+
console.log(chalk53.dim(` $ ${cli} skills find audit`));
|
|
14802
|
+
console.log(chalk53.dim(` $ ${cli} skills find "react" --skillsh`));
|
|
14803
|
+
console.log(chalk53.dim(` $ ${cli} skills add brainstorming`));
|
|
14804
|
+
console.log(chalk53.dim(` $ ${cli} skills add vercel/next-skills --skillsh`));
|
|
14805
|
+
console.log(chalk53.dim(` $ ${cli} skills list`));
|
|
14806
|
+
console.log(chalk53.dim(` $ ${cli} skills sync --all -y`));
|
|
14238
14807
|
console.log();
|
|
14239
|
-
console.log(
|
|
14808
|
+
console.log(chalk53.dim(`Ch\u1EA1y "${cli} skills <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt`));
|
|
14240
14809
|
}
|
|
14241
14810
|
function createSkillsCommand() {
|
|
14242
|
-
const cmd = new
|
|
14811
|
+
const cmd = new Command88("skills").alias("s").description("Manage agent skills (search, install, sync to IDEs)").action(() => {
|
|
14243
14812
|
showSkillsHelp();
|
|
14244
14813
|
});
|
|
14245
14814
|
cmd.addCommand(createSkillsFindCommand());
|
|
@@ -14251,8 +14820,8 @@ function createSkillsCommand() {
|
|
|
14251
14820
|
}
|
|
14252
14821
|
|
|
14253
14822
|
// src/commands/upgrade.ts
|
|
14254
|
-
import { Command as
|
|
14255
|
-
import { confirm as
|
|
14823
|
+
import { Command as Command89 } from "commander";
|
|
14824
|
+
import { confirm as confirm20 } from "@inquirer/prompts";
|
|
14256
14825
|
import { execSync as execSync5 } from "child_process";
|
|
14257
14826
|
var colors2 = {
|
|
14258
14827
|
yellow: "\x1B[33m",
|
|
@@ -14263,7 +14832,7 @@ var colors2 = {
|
|
|
14263
14832
|
bold: "\x1B[1m"
|
|
14264
14833
|
};
|
|
14265
14834
|
function createUpgradeCommand() {
|
|
14266
|
-
return new
|
|
14835
|
+
return new Command89("upgrade").description("Upgrade CLI client to the latest version").option("--check", "Only check for updates without installing").option("-y, --force", "Upgrade without confirmation prompt (skip if already latest)").action(async (options) => {
|
|
14267
14836
|
await handleUpgrade(options);
|
|
14268
14837
|
});
|
|
14269
14838
|
}
|
|
@@ -14312,7 +14881,7 @@ ${colors2.bold}Current version:${colors2.reset} ${currentVersion}`);
|
|
|
14312
14881
|
return;
|
|
14313
14882
|
}
|
|
14314
14883
|
if (!options.force) {
|
|
14315
|
-
const shouldUpdate = await
|
|
14884
|
+
const shouldUpdate = await confirm20({
|
|
14316
14885
|
message: "Update to the latest version now?",
|
|
14317
14886
|
default: true
|
|
14318
14887
|
});
|
|
@@ -14416,11 +14985,11 @@ function getInstallCommand(packageManager2) {
|
|
|
14416
14985
|
}
|
|
14417
14986
|
|
|
14418
14987
|
// src/commands/clean.ts
|
|
14419
|
-
import { Command as
|
|
14420
|
-
import { confirm as
|
|
14988
|
+
import { Command as Command90 } from "commander";
|
|
14989
|
+
import { confirm as confirm21, select as select6 } from "@inquirer/prompts";
|
|
14421
14990
|
import { join as join21 } from "path";
|
|
14422
14991
|
function createCleanCommand() {
|
|
14423
|
-
return new
|
|
14992
|
+
return new Command90("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) => {
|
|
14424
14993
|
await handleClean(options);
|
|
14425
14994
|
});
|
|
14426
14995
|
}
|
|
@@ -14516,7 +15085,7 @@ async function cleanTarget(target, skipConfirm) {
|
|
|
14516
15085
|
}
|
|
14517
15086
|
const countStr = info.count ? ` (${info.count} items)` : "";
|
|
14518
15087
|
if (!skipConfirm) {
|
|
14519
|
-
const confirmed = await
|
|
15088
|
+
const confirmed = await confirm21({
|
|
14520
15089
|
message: `Delete ${target.name}${countStr}?`,
|
|
14521
15090
|
default: false
|
|
14522
15091
|
});
|
|
@@ -14534,7 +15103,7 @@ async function cleanTarget(target, skipConfirm) {
|
|
|
14534
15103
|
}
|
|
14535
15104
|
|
|
14536
15105
|
// src/commands/redmine/check.ts
|
|
14537
|
-
import { Command as
|
|
15106
|
+
import { Command as Command91 } from "commander";
|
|
14538
15107
|
|
|
14539
15108
|
// src/services/redmine-config.service.ts
|
|
14540
15109
|
import { readFile as readFile7 } from "fs/promises";
|
|
@@ -14841,7 +15410,7 @@ async function checkConnectivity(config) {
|
|
|
14841
15410
|
|
|
14842
15411
|
// src/commands/redmine/check.ts
|
|
14843
15412
|
function createRedmineCheckCommand() {
|
|
14844
|
-
const cmd = new
|
|
15413
|
+
const cmd = new Command91("check").description("Check Redmine connectivity").option("-c, --config <path>", "Config file path", "redmine.config.yaml").option("--json", "Output as JSON").action(async (options) => {
|
|
14845
15414
|
await handleRedmineCheck(options);
|
|
14846
15415
|
});
|
|
14847
15416
|
return cmd;
|
|
@@ -14869,7 +15438,7 @@ async function handleRedmineCheck(options) {
|
|
|
14869
15438
|
}
|
|
14870
15439
|
|
|
14871
15440
|
// src/commands/redmine/sync-issue.ts
|
|
14872
|
-
import { Command as
|
|
15441
|
+
import { Command as Command92 } from "commander";
|
|
14873
15442
|
|
|
14874
15443
|
// src/sync-issue.ts
|
|
14875
15444
|
import { resolve as resolve3, relative } from "path";
|
|
@@ -15253,7 +15822,7 @@ function extractIssueIdFromUrl(url) {
|
|
|
15253
15822
|
|
|
15254
15823
|
// src/commands/redmine/sync-issue.ts
|
|
15255
15824
|
function createSyncIssueCommand() {
|
|
15256
|
-
const cmd = new
|
|
15825
|
+
const cmd = new Command92("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) => {
|
|
15257
15826
|
await handleSyncIssue(options);
|
|
15258
15827
|
});
|
|
15259
15828
|
return cmd;
|
|
@@ -15297,7 +15866,7 @@ async function handleSyncIssue(options) {
|
|
|
15297
15866
|
}
|
|
15298
15867
|
|
|
15299
15868
|
// src/commands/redmine/sync-project.ts
|
|
15300
|
-
import { Command as
|
|
15869
|
+
import { Command as Command93 } from "commander";
|
|
15301
15870
|
|
|
15302
15871
|
// src/sync-project.ts
|
|
15303
15872
|
async function syncProject(config, options = {}) {
|
|
@@ -15367,7 +15936,7 @@ async function syncProject(config, options = {}) {
|
|
|
15367
15936
|
|
|
15368
15937
|
// src/commands/redmine/sync-project.ts
|
|
15369
15938
|
function createSyncProjectCommand() {
|
|
15370
|
-
const cmd = new
|
|
15939
|
+
const cmd = new Command93("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) => {
|
|
15371
15940
|
await handleSyncProject(options);
|
|
15372
15941
|
});
|
|
15373
15942
|
return cmd;
|
|
@@ -15422,12 +15991,12 @@ async function handleSyncProject(options) {
|
|
|
15422
15991
|
}
|
|
15423
15992
|
|
|
15424
15993
|
// src/commands/framework/info.ts
|
|
15425
|
-
import { Command as
|
|
15994
|
+
import { Command as Command94 } from "commander";
|
|
15426
15995
|
import { promises as fs28 } from "fs";
|
|
15427
15996
|
import { join as join22 } from "path";
|
|
15428
15997
|
import { homedir as homedir5 } from "os";
|
|
15429
15998
|
function createInfoCommand() {
|
|
15430
|
-
const cmd = new
|
|
15999
|
+
const cmd = new Command94("info").description("Show client configuration and status").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
|
|
15431
16000
|
await handleInfo(options);
|
|
15432
16001
|
});
|
|
15433
16002
|
return cmd;
|
|
@@ -15483,8 +16052,8 @@ async function getProjectStatus2() {
|
|
|
15483
16052
|
}
|
|
15484
16053
|
|
|
15485
16054
|
// src/commands/self-update.ts
|
|
15486
|
-
import { Command as
|
|
15487
|
-
import { confirm as
|
|
16055
|
+
import { Command as Command95 } from "commander";
|
|
16056
|
+
import { confirm as confirm22 } from "@inquirer/prompts";
|
|
15488
16057
|
import { execSync as execSync6 } from "child_process";
|
|
15489
16058
|
var colors3 = {
|
|
15490
16059
|
yellow: "\x1B[33m",
|
|
@@ -15495,7 +16064,7 @@ var colors3 = {
|
|
|
15495
16064
|
bold: "\x1B[1m"
|
|
15496
16065
|
};
|
|
15497
16066
|
function createSelfUpdateCommand() {
|
|
15498
|
-
return new
|
|
16067
|
+
return new Command95("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) => {
|
|
15499
16068
|
await handleSelfUpdate(options);
|
|
15500
16069
|
});
|
|
15501
16070
|
}
|
|
@@ -15539,7 +16108,7 @@ ${colors3.bold}Current version:${colors3.reset} ${currentVersion}`);
|
|
|
15539
16108
|
return;
|
|
15540
16109
|
}
|
|
15541
16110
|
if (!options.force) {
|
|
15542
|
-
const shouldUpdate = await
|
|
16111
|
+
const shouldUpdate = await confirm22({
|
|
15543
16112
|
message: "Update to the latest version now?",
|
|
15544
16113
|
default: true
|
|
15545
16114
|
});
|
|
@@ -15635,10 +16204,10 @@ function getInstallCommand2(packageManager2) {
|
|
|
15635
16204
|
}
|
|
15636
16205
|
|
|
15637
16206
|
// src/commands/clear-backups.ts
|
|
15638
|
-
import { Command as
|
|
15639
|
-
import { confirm as
|
|
16207
|
+
import { Command as Command96 } from "commander";
|
|
16208
|
+
import { confirm as confirm23 } from "@inquirer/prompts";
|
|
15640
16209
|
function createClearBackupsCommand() {
|
|
15641
|
-
return new
|
|
16210
|
+
return new Command96("clear-backups").description("Clear backup files").option("-y, --yes", "Skip confirmation").action(async (options) => {
|
|
15642
16211
|
const service = new ComponentsService();
|
|
15643
16212
|
const backups = await service.listBackups(process.cwd());
|
|
15644
16213
|
if (backups.length === 0) {
|
|
@@ -15654,7 +16223,7 @@ function createClearBackupsCommand() {
|
|
|
15654
16223
|
}
|
|
15655
16224
|
console.log();
|
|
15656
16225
|
if (!options.yes) {
|
|
15657
|
-
const ok = await
|
|
16226
|
+
const ok = await confirm23({ message: "Delete all backups?", default: false });
|
|
15658
16227
|
if (!ok) return;
|
|
15659
16228
|
}
|
|
15660
16229
|
await service.clearBackups(process.cwd());
|
|
@@ -15663,8 +16232,8 @@ function createClearBackupsCommand() {
|
|
|
15663
16232
|
}
|
|
15664
16233
|
|
|
15665
16234
|
// src/commands/vscode/index.ts
|
|
15666
|
-
import { Command as
|
|
15667
|
-
import { checkbox as checkbox9, confirm as
|
|
16235
|
+
import { Command as Command97 } from "commander";
|
|
16236
|
+
import { checkbox as checkbox9, confirm as confirm24, select as select7 } from "@inquirer/prompts";
|
|
15668
16237
|
import fs29 from "fs/promises";
|
|
15669
16238
|
import path12 from "path";
|
|
15670
16239
|
import { existsSync as existsSync4 } from "fs";
|
|
@@ -15803,7 +16372,7 @@ var PERFORMANCE_GROUPS2 = {
|
|
|
15803
16372
|
}
|
|
15804
16373
|
};
|
|
15805
16374
|
function createVSCodeCommand() {
|
|
15806
|
-
const vscodeCommand = new
|
|
16375
|
+
const vscodeCommand = new Command97("vscode").description("Qu\u1EA3n l\xFD c\xE0i \u0111\u1EB7t VSCode cho d\u1EF1 \xE1n hi\u1EC7n t\u1EA1i");
|
|
15807
16376
|
vscodeCommand.action(async () => {
|
|
15808
16377
|
await interactiveMode2();
|
|
15809
16378
|
});
|
|
@@ -15909,7 +16478,7 @@ async function applyGroups2(groupKeys, action) {
|
|
|
15909
16478
|
console.log("\u{1F4C4} \u0110\xE3 \u0111\u1ECDc c\xE0i \u0111\u1EB7t hi\u1EC7n t\u1EA1i t\u1EEB settings.json");
|
|
15910
16479
|
} catch {
|
|
15911
16480
|
console.warn("\u26A0\uFE0F Kh\xF4ng th\u1EC3 \u0111\u1ECDc settings.json (c\xF3 th\u1EC3 ch\u1EE9a comments).");
|
|
15912
|
-
const confirmOverwrite = await
|
|
16481
|
+
const confirmOverwrite = await confirm24({
|
|
15913
16482
|
message: "Ghi \u0111\xE8 file settings.json hi\u1EC7n t\u1EA1i?",
|
|
15914
16483
|
default: false
|
|
15915
16484
|
});
|
|
@@ -15956,7 +16525,7 @@ async function resetSettings2(groupKeys) {
|
|
|
15956
16525
|
console.log("\n\u26A0\uFE0F Kh\xF4ng t\xECm th\u1EA5y file settings.json");
|
|
15957
16526
|
return;
|
|
15958
16527
|
}
|
|
15959
|
-
const confirmReset = await
|
|
16528
|
+
const confirmReset = await confirm24({
|
|
15960
16529
|
message: groupKeys.length === 0 ? "Reset T\u1EA4T C\u1EA2 settings v\u1EC1 m\u1EB7c \u0111\u1ECBnh (x\xF3a to\xE0n b\u1ED9 file)?" : `Reset c\xE1c nh\xF3m: ${groupKeys.join(", ")}?`,
|
|
15961
16530
|
default: false
|
|
15962
16531
|
});
|
|
@@ -15974,10 +16543,10 @@ async function resetSettings2(groupKeys) {
|
|
|
15974
16543
|
}
|
|
15975
16544
|
|
|
15976
16545
|
// src/commands/migrate-ide.ts
|
|
15977
|
-
import { Command as
|
|
15978
|
-
import { checkbox as checkbox10, confirm as
|
|
16546
|
+
import { Command as Command98 } from "commander";
|
|
16547
|
+
import { checkbox as checkbox10, confirm as confirm25 } from "@inquirer/prompts";
|
|
15979
16548
|
function createMigrateIdeCommand() {
|
|
15980
|
-
const cmd = new
|
|
16549
|
+
const cmd = new Command98("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) => {
|
|
15981
16550
|
await runMigrateIde(options);
|
|
15982
16551
|
});
|
|
15983
16552
|
return cmd;
|
|
@@ -16048,7 +16617,7 @@ async function runMigrateIde(options) {
|
|
|
16048
16617
|
if (options.dryRun) {
|
|
16049
16618
|
console.log("\u{1F50D} DRY RUN - No files will be written\n");
|
|
16050
16619
|
}
|
|
16051
|
-
const confirmed = await
|
|
16620
|
+
const confirmed = await confirm25({
|
|
16052
16621
|
message: "Proceed with migration?",
|
|
16053
16622
|
default: true
|
|
16054
16623
|
});
|
|
@@ -16086,20 +16655,20 @@ async function runMigrateIde(options) {
|
|
|
16086
16655
|
|
|
16087
16656
|
// src/utils/help-formatter.ts
|
|
16088
16657
|
import boxen4 from "boxen";
|
|
16089
|
-
import
|
|
16658
|
+
import chalk54 from "chalk";
|
|
16090
16659
|
import gradient from "gradient-string";
|
|
16091
16660
|
import figlet from "figlet";
|
|
16092
16661
|
function showCustomHelp(version) {
|
|
16093
16662
|
const title = figlet.textSync("JAI1", { font: "Small" });
|
|
16094
16663
|
console.log(gradient.pastel(title));
|
|
16095
16664
|
console.log(
|
|
16096
|
-
boxen4(
|
|
16665
|
+
boxen4(chalk54.cyan(`Agentic Coding CLI v${version}`), {
|
|
16097
16666
|
padding: { left: 1, right: 1, top: 0, bottom: 0 },
|
|
16098
16667
|
borderStyle: "round",
|
|
16099
16668
|
borderColor: "cyan"
|
|
16100
16669
|
})
|
|
16101
16670
|
);
|
|
16102
|
-
console.log(
|
|
16671
|
+
console.log(chalk54.bold("\n\u{1F527} Thi\u1EBFt l\u1EADp & Th\xF4ng tin"));
|
|
16103
16672
|
console.log(" auth X\xE1c th\u1EF1c v\xE0 c\u1EA5u h\xECnh client");
|
|
16104
16673
|
console.log(" status Hi\u1EC3n th\u1ECB tr\u1EA1ng th\xE1i c\u1EA5u h\xECnh");
|
|
16105
16674
|
console.log(" client-info T\u1EA1o th\xF4ng tin client \u0111\u1EC3 g\u1EEDi \u0111\u1ED9i ph\xE1t tri\u1EC3n");
|
|
@@ -16107,43 +16676,43 @@ function showCustomHelp(version) {
|
|
|
16107
16676
|
console.log(" guide H\u01B0\u1EDBng d\u1EABn s\u1EED d\u1EE5ng nhanh");
|
|
16108
16677
|
console.log(" quickstart B\u1EAFt \u0111\u1EA7u t\u1EEB \u0111\xE2u? (theo t\xECnh hu\u1ED1ng)");
|
|
16109
16678
|
console.log(" doctor Chu\u1EA9n \u0111o\xE1n project hi\u1EC7n t\u1EA1i");
|
|
16110
|
-
console.log(
|
|
16679
|
+
console.log(chalk54.bold("\n\u{1F4E6} Qu\u1EA3n l\xFD Components"));
|
|
16111
16680
|
console.log(" apply C\xE0i \u0111\u1EB7t components (interactive)");
|
|
16112
16681
|
console.log(" update C\u1EADp nh\u1EADt components \u0111\xE3 c\xE0i");
|
|
16113
16682
|
console.log(" check Ki\u1EC3m tra c\u1EADp nh\u1EADt t\u1EEB server");
|
|
16114
|
-
console.log(
|
|
16683
|
+
console.log(chalk54.bold("\n\u{1F5A5}\uFE0F IDE & T\xEDch h\u1EE3p"));
|
|
16115
16684
|
console.log(" ide L\u1EC7nh c\u1EA5u h\xECnh IDE");
|
|
16116
16685
|
console.log(" chat Chat AI v\u1EDBi Jai1 LLM Proxy");
|
|
16117
16686
|
console.log(" openai-keys Th\xF4ng tin API credentials");
|
|
16118
|
-
console.log(
|
|
16687
|
+
console.log(chalk54.bold("\n\u{1F916} AI Tools"));
|
|
16119
16688
|
console.log(" translate D\u1ECBch v\u0103n b\u1EA3n/file b\u1EB1ng AI");
|
|
16120
16689
|
console.log(" image T\u1EA1o \u1EA3nh (Coming Soon)");
|
|
16121
16690
|
console.log(" stats Th\u1ED1ng k\xEA s\u1EED d\u1EE5ng LLM");
|
|
16122
16691
|
console.log(" feedback G\u1EEDi b\xE1o c\xE1o/\u0111\u1EC1 xu\u1EA5t");
|
|
16123
|
-
console.log(
|
|
16692
|
+
console.log(chalk54.bold("\n\u{1F4C1} Project"));
|
|
16124
16693
|
console.log(" kit Qu\u1EA3n l\xFD starter kits");
|
|
16125
16694
|
console.log(" tasks (t) Qu\u1EA3n l\xFD tasks ph\xE1t tri\u1EC3n");
|
|
16126
16695
|
console.log(" rules Qu\u1EA3n l\xFD rule presets");
|
|
16127
16696
|
console.log(" deps Qu\u1EA3n l\xFD dependencies");
|
|
16128
16697
|
console.log(" redmine Redmine context sync");
|
|
16129
|
-
console.log(
|
|
16698
|
+
console.log(chalk54.bold("\n\u2699\uFE0F B\u1EA3o tr\xEC"));
|
|
16130
16699
|
console.log(" upgrade C\u1EADp nh\u1EADt CLI client");
|
|
16131
16700
|
console.log(" clean D\u1ECDn d\u1EB9p cache/backup");
|
|
16132
16701
|
console.log(" utils Developer utilities");
|
|
16133
16702
|
const name = getCliName();
|
|
16134
|
-
console.log(
|
|
16703
|
+
console.log(chalk54.dim(`
|
|
16135
16704
|
S\u1EED d\u1EE5ng: ${name} [l\u1EC7nh] --help \u0111\u1EC3 xem chi ti\u1EBFt`));
|
|
16136
16705
|
}
|
|
16137
16706
|
function showUnknownCommand(commandName) {
|
|
16138
|
-
console.error(
|
|
16707
|
+
console.error(chalk54.red(`\u274C L\u1EC7nh kh\xF4ng t\u1ED3n t\u1EA1i: ${commandName}`));
|
|
16139
16708
|
const name = getCliName();
|
|
16140
|
-
console.error(
|
|
16709
|
+
console.error(chalk54.dim(`
|
|
16141
16710
|
G\u1EE3i \xFD: Ch\u1EA1y ${name} --help \u0111\u1EC3 xem danh s\xE1ch l\u1EC7nh`));
|
|
16142
16711
|
}
|
|
16143
16712
|
|
|
16144
16713
|
// src/cli.ts
|
|
16145
16714
|
checkNodeVersion();
|
|
16146
|
-
var program = new
|
|
16715
|
+
var program = new Command99();
|
|
16147
16716
|
if (process.argv.includes("-v") || process.argv.includes("--version")) {
|
|
16148
16717
|
console.log(package_default.version);
|
|
16149
16718
|
if (!process.argv.includes("--skip-update-check")) {
|
|
@@ -16178,6 +16747,7 @@ program.addCommand(createClientInfoCommand());
|
|
|
16178
16747
|
program.addCommand(createErrorsCommand());
|
|
16179
16748
|
program.addCommand(createUtilsCommand());
|
|
16180
16749
|
program.addCommand(createDepsCommand());
|
|
16750
|
+
program.addCommand(createDevCommand());
|
|
16181
16751
|
program.addCommand(createTasksCommand());
|
|
16182
16752
|
program.addCommand(createKitCommand());
|
|
16183
16753
|
program.addCommand(createRulesCommand());
|
|
@@ -16185,9 +16755,9 @@ program.addCommand(createSkillsCommand());
|
|
|
16185
16755
|
program.addCommand(createHooksCommand());
|
|
16186
16756
|
program.addCommand(createUpgradeCommand());
|
|
16187
16757
|
program.addCommand(createCleanCommand());
|
|
16188
|
-
var redmineCommand = new
|
|
16758
|
+
var redmineCommand = new Command99("redmine").description("Redmine context sync commands");
|
|
16189
16759
|
redmineCommand.addCommand(createRedmineCheckCommand());
|
|
16190
|
-
var syncCommand = new
|
|
16760
|
+
var syncCommand = new Command99("sync").description("Sync Redmine issues to markdown files");
|
|
16191
16761
|
syncCommand.addCommand(createSyncIssueCommand());
|
|
16192
16762
|
syncCommand.addCommand(createSyncProjectCommand());
|
|
16193
16763
|
redmineCommand.addCommand(syncCommand);
|