@jvittechs/j 1.0.44 → 1.0.46
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 +1056 -491
- 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.46",
|
|
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: {
|
|
@@ -1700,7 +1700,9 @@ async function nonInteractiveApply(config, items, options) {
|
|
|
1700
1700
|
\u2705 Complete: ${added} added, ${updated} updated, ${skipped} skipped`);
|
|
1701
1701
|
console.log(`\u{1F4C1} Location: ${targetDir}`);
|
|
1702
1702
|
console.log(`
|
|
1703
|
-
\u{1F4A1}
|
|
1703
|
+
\u{1F4A1} B\u01B0\u1EDBc ti\u1EBFp theo:`);
|
|
1704
|
+
console.log(` \u2022 Ch\u1EA1y "${name} ide sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 n\u1ED9i dung sang c\xE1c IDE b\u1EA1n \u0111ang d\xF9ng.`);
|
|
1705
|
+
console.log(` \u2022 Ch\u1EA1y "${name} doctor" \u0111\u1EC3 ki\u1EC3m tra d\u1EF1 \xE1n \u0111\xE3 setup Jai1 \u0111\u1EA7y \u0111\u1EE7 ch\u01B0a.`);
|
|
1704
1706
|
}
|
|
1705
1707
|
|
|
1706
1708
|
// src/commands/apply-new.ts
|
|
@@ -10614,12 +10616,574 @@ function createDepsCommand() {
|
|
|
10614
10616
|
return depsCommand;
|
|
10615
10617
|
}
|
|
10616
10618
|
|
|
10617
|
-
// 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
|
|
10618
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(` Polling every ${intervalSec}s${timeoutSec ? ` \xB7 timeout ${timeoutSec}s` : ""} \xB7 Ctrl+C to stop
|
|
10941
|
+
`));
|
|
10942
|
+
}
|
|
10943
|
+
const init = await api.getInbox(address, 30);
|
|
10944
|
+
if (init.success && init.data) {
|
|
10945
|
+
init.data.forEach((m) => seenIds.add(m.id));
|
|
10946
|
+
}
|
|
10947
|
+
const poll = async () => {
|
|
10948
|
+
if (timeoutSec && (Date.now() - startTime) / 1e3 >= timeoutSec) {
|
|
10949
|
+
if (!options.json) console.log(chalk25.dim("\n\u23F1 Timeout reached. Stopped."));
|
|
10950
|
+
process.exit(0);
|
|
10951
|
+
}
|
|
10952
|
+
pollCount++;
|
|
10953
|
+
if (!options.json) {
|
|
10954
|
+
process.stdout.write(chalk25.dim(`\r \u{1F504} Poll #${pollCount} \u2014 ${(/* @__PURE__ */ new Date()).toLocaleTimeString("vi-VN")} `));
|
|
10955
|
+
}
|
|
10956
|
+
const result = await api.getInbox(address, 30);
|
|
10957
|
+
if (!result.success) {
|
|
10958
|
+
if (!options.json) {
|
|
10959
|
+
process.stdout.write(chalk25.red(`\r \u274C Error: ${result.error}
|
|
10960
|
+
`));
|
|
10961
|
+
}
|
|
10962
|
+
return;
|
|
10963
|
+
}
|
|
10964
|
+
const newMessages = (result.data ?? []).filter((m) => !seenIds.has(m.id));
|
|
10965
|
+
if (newMessages.length > 0) {
|
|
10966
|
+
for (const msg of newMessages) {
|
|
10967
|
+
seenIds.add(msg.id);
|
|
10968
|
+
if (options.json) {
|
|
10969
|
+
console.log(JSON.stringify({ event: "new_message", data: msg }));
|
|
10970
|
+
continue;
|
|
10971
|
+
}
|
|
10972
|
+
const detail = await api.getMessage(address, msg.id);
|
|
10973
|
+
process.stdout.write("\r" + " ".repeat(50) + "\r");
|
|
10974
|
+
console.log("\n" + chalk25.bold.green("\u{1F4E8} New Email!\n"));
|
|
10975
|
+
console.log(` ${chalk25.bold("From:")} ${msg.from.name || msg.from.address}`);
|
|
10976
|
+
console.log(` ${chalk25.bold("Subject:")} ${msg.subject || "(no subject)"}`);
|
|
10977
|
+
console.log(` ${chalk25.bold("Date:")} ${new Date(msg.createdAt).toLocaleString("vi-VN")}`);
|
|
10978
|
+
console.log(` ${chalk25.dim("ID:")} ${msg.id}`);
|
|
10979
|
+
if (detail.success && detail.data) {
|
|
10980
|
+
const body = detail.data.text || (detail.data.html?.length ? htmlToText2(detail.data.html) : "");
|
|
10981
|
+
if (body) {
|
|
10982
|
+
const preview = body.slice(0, 600) + (body.length > 600 ? "\n\u2026" : "");
|
|
10983
|
+
console.log("\n" + "\u2500".repeat(60) + "\n");
|
|
10984
|
+
console.log(preview);
|
|
10985
|
+
console.log("\u2500".repeat(60));
|
|
10986
|
+
}
|
|
10987
|
+
}
|
|
10988
|
+
console.log();
|
|
10989
|
+
}
|
|
10990
|
+
}
|
|
10991
|
+
};
|
|
10992
|
+
await poll();
|
|
10993
|
+
const timer = setInterval(poll, intervalSec * 1e3);
|
|
10994
|
+
process.on("SIGINT", () => {
|
|
10995
|
+
clearInterval(timer);
|
|
10996
|
+
if (!options.json) {
|
|
10997
|
+
console.log(chalk25.dim("\n\n Stopped watching. Goodbye!"));
|
|
10998
|
+
}
|
|
10999
|
+
process.exit(0);
|
|
11000
|
+
});
|
|
11001
|
+
}
|
|
11002
|
+
function createMailWatchCommand() {
|
|
11003
|
+
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("-j, --json", "Output s\u1EF1 ki\u1EC7n email m\u1EDBi d\u1EA1ng JSON (m\u1ED7i d\xF2ng 1 event)").addHelpText("after", `
|
|
11004
|
+
Examples:
|
|
11005
|
+
$ j dev mail watch user@dollicons.com
|
|
11006
|
+
$ j dev mail watch user@dollicons.com -i 30
|
|
11007
|
+
$ j dev mail watch user@dollicons.com -t 300
|
|
11008
|
+
$ j dev mail watch user@dollicons.com -j | jq .
|
|
11009
|
+
`).action(async (address, options) => {
|
|
11010
|
+
await handleWatch(address, options);
|
|
11011
|
+
});
|
|
11012
|
+
}
|
|
11013
|
+
|
|
11014
|
+
// src/commands/dev/mail/purge.ts
|
|
11015
|
+
import { Command as Command53 } from "commander";
|
|
11016
|
+
import chalk26 from "chalk";
|
|
11017
|
+
import { confirm as confirm9 } from "@inquirer/prompts";
|
|
11018
|
+
async function handlePurge(address, options) {
|
|
11019
|
+
const config = await new ConfigService().load();
|
|
11020
|
+
if (!config) {
|
|
11021
|
+
console.error('\u274C Not initialized. Run "jai1 auth" first.');
|
|
11022
|
+
process.exit(1);
|
|
11023
|
+
}
|
|
11024
|
+
if (!options.yes && !options.json) {
|
|
11025
|
+
const confirmed = await confirm9({
|
|
11026
|
+
message: `X\xF3a to\xE0n b\u1ED9 emails c\u1EE7a ${chalk26.cyan(address)}?`,
|
|
11027
|
+
default: false
|
|
11028
|
+
});
|
|
11029
|
+
if (!confirmed) {
|
|
11030
|
+
console.log(chalk26.dim("\u274C \u0110\xE3 h\u1EE7y."));
|
|
11031
|
+
return;
|
|
11032
|
+
}
|
|
11033
|
+
}
|
|
11034
|
+
const api = new TempMailApiService(config);
|
|
11035
|
+
if (!options.json) {
|
|
11036
|
+
console.log(chalk26.dim("\u23F3 \u0110ang x\xF3a t\u1EA5t c\u1EA3 emails..."));
|
|
11037
|
+
}
|
|
11038
|
+
const result = await api.purgeMessages(address);
|
|
11039
|
+
if (!result.success) {
|
|
11040
|
+
if (options.json) {
|
|
11041
|
+
console.log(JSON.stringify({ success: false, error: result.error }));
|
|
11042
|
+
} else {
|
|
11043
|
+
console.error(`\u274C ${result.error}`);
|
|
11044
|
+
}
|
|
11045
|
+
process.exit(1);
|
|
11046
|
+
}
|
|
11047
|
+
if (options.json) {
|
|
11048
|
+
console.log(JSON.stringify({ success: true, data: result.data }));
|
|
11049
|
+
return;
|
|
11050
|
+
}
|
|
11051
|
+
const deleted = result.data?.deleted ?? 0;
|
|
11052
|
+
if (deleted === 0) {
|
|
11053
|
+
console.log(chalk26.dim("Inbox \u0111\xE3 tr\u1ED1ng, kh\xF4ng c\xF3 g\xEC \u0111\u1EC3 x\xF3a."));
|
|
11054
|
+
} else {
|
|
11055
|
+
console.log(`\u2705 \u0110\xE3 x\xF3a ${chalk26.bold(deleted)} email.`);
|
|
11056
|
+
}
|
|
11057
|
+
}
|
|
11058
|
+
function createMailPurgeCommand() {
|
|
11059
|
+
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", `
|
|
11060
|
+
Examples:
|
|
11061
|
+
$ j dev mail purge user@dollicons.com
|
|
11062
|
+
$ j dev mail purge user@dollicons.com -y
|
|
11063
|
+
$ j dev mail purge user@dollicons.com -j
|
|
11064
|
+
`).action(async (address, options) => {
|
|
11065
|
+
await handlePurge(address, options);
|
|
11066
|
+
});
|
|
11067
|
+
}
|
|
11068
|
+
|
|
11069
|
+
// src/commands/dev/mail/delete.ts
|
|
11070
|
+
import { Command as Command54 } from "commander";
|
|
11071
|
+
import chalk27 from "chalk";
|
|
11072
|
+
import { confirm as confirm10 } from "@inquirer/prompts";
|
|
11073
|
+
async function handleDelete(address, options) {
|
|
11074
|
+
const config = await new ConfigService().load();
|
|
11075
|
+
if (!config) {
|
|
11076
|
+
console.error('\u274C Not initialized. Run "jai1 auth" first.');
|
|
11077
|
+
process.exit(1);
|
|
11078
|
+
}
|
|
11079
|
+
if (!options.yes && !options.json) {
|
|
11080
|
+
const confirmed = await confirm10({
|
|
11081
|
+
message: `X\xF3a account ${chalk27.cyan(address)}? H\xE0nh \u0111\u1ED9ng kh\xF4ng th\u1EC3 ho\xE0n t\xE1c.`,
|
|
11082
|
+
default: false
|
|
11083
|
+
});
|
|
11084
|
+
if (!confirmed) {
|
|
11085
|
+
console.log(chalk27.dim("\u274C \u0110\xE3 h\u1EE7y."));
|
|
11086
|
+
return;
|
|
11087
|
+
}
|
|
11088
|
+
}
|
|
11089
|
+
const api = new TempMailApiService(config);
|
|
11090
|
+
if (!options.json) {
|
|
11091
|
+
console.log(chalk27.dim("\u23F3 \u0110ang x\xF3a account..."));
|
|
11092
|
+
}
|
|
11093
|
+
const result = await api.deleteAccount(address);
|
|
11094
|
+
if (!result.success) {
|
|
11095
|
+
if (options.json) {
|
|
11096
|
+
console.log(JSON.stringify({ success: false, error: result.error }));
|
|
11097
|
+
} else {
|
|
11098
|
+
console.error(`\u274C ${result.error}`);
|
|
11099
|
+
}
|
|
11100
|
+
process.exit(1);
|
|
11101
|
+
}
|
|
11102
|
+
if (options.json) {
|
|
11103
|
+
console.log(JSON.stringify({ success: true }));
|
|
11104
|
+
return;
|
|
11105
|
+
}
|
|
11106
|
+
console.log(`\u2705 \u0110\xE3 x\xF3a account: ${chalk27.cyan(address)}`);
|
|
11107
|
+
}
|
|
11108
|
+
function createMailDeleteCommand() {
|
|
11109
|
+
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", `
|
|
11110
|
+
Examples:
|
|
11111
|
+
$ j dev mail delete user@dollicons.com
|
|
11112
|
+
$ j dev mail delete user@dollicons.com -y
|
|
11113
|
+
$ j dev mail delete user@dollicons.com -j
|
|
11114
|
+
`).action(async (address, options) => {
|
|
11115
|
+
await handleDelete(address, options);
|
|
11116
|
+
});
|
|
11117
|
+
}
|
|
11118
|
+
|
|
11119
|
+
// src/commands/dev/mail/index.ts
|
|
11120
|
+
function showMailHelp() {
|
|
11121
|
+
console.log(chalk28.bold.cyan("\u{1F4EC} j dev mail") + chalk28.dim(" - Temp email cho m\xF4i tr\u01B0\u1EDDng dev"));
|
|
11122
|
+
console.log();
|
|
11123
|
+
console.log(chalk28.bold("Qu\u1EA3n l\xFD accounts:"));
|
|
11124
|
+
console.log(` ${chalk28.cyan("create")} T\u1EA1o temp email account m\u1EDBi`);
|
|
11125
|
+
console.log(` ${chalk28.cyan("list")} Li\u1EC7t k\xEA t\u1EA5t c\u1EA3 accounts`);
|
|
11126
|
+
console.log(` ${chalk28.cyan("random")} L\u1EA5y ng\u1EABu nhi\xEAn 1 \u0111\u1ECBa ch\u1EC9`);
|
|
11127
|
+
console.log(` ${chalk28.cyan("delete")} X\xF3a 1 account`);
|
|
11128
|
+
console.log();
|
|
11129
|
+
console.log(chalk28.bold("Email:"));
|
|
11130
|
+
console.log(` ${chalk28.cyan("inbox")} Xem inbox (emails g\u1EA7n nh\u1EA5t)`);
|
|
11131
|
+
console.log(` ${chalk28.cyan("read")} \u0110\u1ECDc n\u1ED9i dung email`);
|
|
11132
|
+
console.log(` ${chalk28.cyan("watch")} L\u1EAFng nghe email m\u1EDBi (polling)`);
|
|
11133
|
+
console.log(` ${chalk28.cyan("purge")} X\xF3a to\xE0n b\u1ED9 emails c\u1EE7a 1 account`);
|
|
11134
|
+
console.log();
|
|
11135
|
+
console.log(chalk28.bold("V\xED d\u1EE5:"));
|
|
11136
|
+
console.log(chalk28.dim(" $ j dev mail create"));
|
|
11137
|
+
console.log(chalk28.dim(" $ j dev mail create --name test"));
|
|
11138
|
+
console.log(chalk28.dim(" $ j dev mail list"));
|
|
11139
|
+
console.log(chalk28.dim(" $ j dev mail watch user@dollicons.com"));
|
|
11140
|
+
console.log();
|
|
11141
|
+
console.log(chalk28.dim(" T\u1EA5t c\u1EA3 l\u1EC7nh \u0111\u1EC1u h\u1ED7 tr\u1EE3 -j / --json"));
|
|
11142
|
+
console.log(chalk28.dim(' Ch\u1EA1y "j dev mail <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
|
|
11143
|
+
}
|
|
11144
|
+
function createMailCommand() {
|
|
11145
|
+
const cmd = new Command55("mail").description("Temp email management cho dev environment").action(() => {
|
|
11146
|
+
showMailHelp();
|
|
11147
|
+
});
|
|
11148
|
+
cmd.addCommand(createMailCreateCommand());
|
|
11149
|
+
cmd.addCommand(createMailListCommand());
|
|
11150
|
+
cmd.addCommand(createMailRandomCommand());
|
|
11151
|
+
cmd.addCommand(createMailInboxCommand());
|
|
11152
|
+
cmd.addCommand(createMailReadCommand());
|
|
11153
|
+
cmd.addCommand(createMailWatchCommand());
|
|
11154
|
+
cmd.addCommand(createMailPurgeCommand());
|
|
11155
|
+
cmd.addCommand(createMailDeleteCommand());
|
|
11156
|
+
return cmd;
|
|
11157
|
+
}
|
|
11158
|
+
|
|
11159
|
+
// src/commands/dev/index.ts
|
|
11160
|
+
function showDevHelp() {
|
|
11161
|
+
console.log(chalk29.bold.cyan("\u{1F9EA} j dev") + chalk29.dim(" - Dev tools cho m\xF4i tr\u01B0\u1EDDng local"));
|
|
11162
|
+
console.log();
|
|
11163
|
+
console.log(chalk29.bold("Tools:"));
|
|
11164
|
+
console.log(` ${chalk29.cyan("mail")} Qu\u1EA3n l\xFD temp email accounts (via mail.tm)`);
|
|
11165
|
+
console.log();
|
|
11166
|
+
console.log(chalk29.bold("V\xED d\u1EE5:"));
|
|
11167
|
+
console.log(chalk29.dim(" $ j dev mail create"));
|
|
11168
|
+
console.log(chalk29.dim(" $ j dev mail list"));
|
|
11169
|
+
console.log(chalk29.dim(" $ j dev mail watch <address>"));
|
|
11170
|
+
console.log();
|
|
11171
|
+
console.log(chalk29.dim('Ch\u1EA1y "j dev <tool> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
|
|
11172
|
+
}
|
|
11173
|
+
function createDevCommand() {
|
|
11174
|
+
const cmd = new Command56("dev").alias("d").description("Developer tools for local development environments").action(() => {
|
|
11175
|
+
showDevHelp();
|
|
11176
|
+
});
|
|
11177
|
+
cmd.addCommand(createMailCommand());
|
|
11178
|
+
return cmd;
|
|
11179
|
+
}
|
|
11180
|
+
|
|
11181
|
+
// src/commands/hooks/index.ts
|
|
11182
|
+
import { Command as Command57 } from "commander";
|
|
10619
11183
|
import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync, unlinkSync, chmodSync, mkdirSync } from "fs";
|
|
10620
11184
|
import { join as join9 } from "path";
|
|
10621
11185
|
import { execSync as execSync4 } from "child_process";
|
|
10622
|
-
import
|
|
11186
|
+
import chalk30 from "chalk";
|
|
10623
11187
|
var MARKER_START = "# >>> jai1-hooks";
|
|
10624
11188
|
var MARKER_END = "# <<< jai1-hooks";
|
|
10625
11189
|
var SHEBANG = "#!/bin/sh";
|
|
@@ -10712,7 +11276,7 @@ function setupHooks() {
|
|
|
10712
11276
|
if (existsSync3(hookPath)) {
|
|
10713
11277
|
const existing = readFileSync2(hookPath, "utf-8");
|
|
10714
11278
|
if (hasJai1Section(existing)) {
|
|
10715
|
-
console.log(
|
|
11279
|
+
console.log(chalk30.dim(` \u23ED ${def.hookName} \u2014 \u0111\xE3 c\xF3, skip`));
|
|
10716
11280
|
skipped++;
|
|
10717
11281
|
continue;
|
|
10718
11282
|
}
|
|
@@ -10725,23 +11289,23 @@ ${section}
|
|
|
10725
11289
|
`);
|
|
10726
11290
|
}
|
|
10727
11291
|
chmodSync(hookPath, 493);
|
|
10728
|
-
console.log(
|
|
11292
|
+
console.log(chalk30.green(` \u2705 ${def.hookName} \u2014 ${def.description}`));
|
|
10729
11293
|
installed++;
|
|
10730
11294
|
} catch (err) {
|
|
10731
11295
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10732
11296
|
if (msg.includes("EACCES") || msg.includes("permission")) {
|
|
10733
|
-
console.log(
|
|
11297
|
+
console.log(chalk30.red(` \u274C ${def.hookName} \u2014 kh\xF4ng c\xF3 quy\u1EC1n ghi. Th\u1EED ch\u1EA1y v\u1EDBi sudo.`));
|
|
10734
11298
|
} else {
|
|
10735
|
-
console.log(
|
|
11299
|
+
console.log(chalk30.red(` \u274C ${def.hookName} \u2014 l\u1ED7i: ${msg}`));
|
|
10736
11300
|
}
|
|
10737
11301
|
}
|
|
10738
11302
|
}
|
|
10739
11303
|
console.log("");
|
|
10740
11304
|
if (installed > 0) {
|
|
10741
|
-
console.log(
|
|
10742
|
-
console.log(
|
|
11305
|
+
console.log(chalk30.green(`\u{1F389} \u0110\xE3 c\xE0i ${installed} hook(s).`));
|
|
11306
|
+
console.log(chalk30.dim(" Tasks s\u1EBD t\u1EF1 \u0111\u1ED9ng sync khi b\u1EA1n push/pull."));
|
|
10743
11307
|
} else {
|
|
10744
|
-
console.log(
|
|
11308
|
+
console.log(chalk30.dim("\u2139\uFE0F T\u1EA5t c\u1EA3 hooks \u0111\xE3 \u0111\u01B0\u1EE3c c\xE0i s\u1EB5n."));
|
|
10745
11309
|
}
|
|
10746
11310
|
}
|
|
10747
11311
|
function removeHooks() {
|
|
@@ -10760,37 +11324,37 @@ function removeHooks() {
|
|
|
10760
11324
|
const cleaned = removeJai1Section(content);
|
|
10761
11325
|
if (isEffectivelyEmpty(cleaned)) {
|
|
10762
11326
|
unlinkSync(hookPath);
|
|
10763
|
-
console.log(
|
|
11327
|
+
console.log(chalk30.yellow(` \u{1F5D1} ${def.hookName} \u2014 xo\xE1 file (ch\u1EC9 c\xF3 jai1 hooks)`));
|
|
10764
11328
|
} else {
|
|
10765
11329
|
writeFileSync(hookPath, cleaned);
|
|
10766
|
-
console.log(
|
|
11330
|
+
console.log(chalk30.yellow(` \u2702\uFE0F ${def.hookName} \u2014 g\u1EE1 ph\u1EA7n jai1`));
|
|
10767
11331
|
}
|
|
10768
11332
|
removed++;
|
|
10769
11333
|
} catch (err) {
|
|
10770
11334
|
const msg = err instanceof Error ? err.message : String(err);
|
|
10771
11335
|
if (msg.includes("EACCES") || msg.includes("permission")) {
|
|
10772
|
-
console.log(
|
|
11336
|
+
console.log(chalk30.red(` \u274C ${def.hookName} \u2014 kh\xF4ng c\xF3 quy\u1EC1n ghi/xo\xE1. Th\u1EED ch\u1EA1y v\u1EDBi sudo.`));
|
|
10773
11337
|
} else {
|
|
10774
|
-
console.log(
|
|
11338
|
+
console.log(chalk30.red(` \u274C ${def.hookName} \u2014 l\u1ED7i: ${msg}`));
|
|
10775
11339
|
}
|
|
10776
11340
|
}
|
|
10777
11341
|
}
|
|
10778
11342
|
console.log("");
|
|
10779
11343
|
if (removed > 0) {
|
|
10780
|
-
console.log(
|
|
11344
|
+
console.log(chalk30.green(`\u2705 \u0110\xE3 g\u1EE1 ${removed} hook(s).`));
|
|
10781
11345
|
} else {
|
|
10782
|
-
console.log(
|
|
11346
|
+
console.log(chalk30.dim("\u2139\uFE0F Kh\xF4ng c\xF3 jai1 hooks n\xE0o \u0111\u1EC3 g\u1EE1."));
|
|
10783
11347
|
}
|
|
10784
11348
|
}
|
|
10785
11349
|
function createHooksCommand() {
|
|
10786
|
-
const cmd = new
|
|
11350
|
+
const cmd = new Command57("hooks").description("Qu\u1EA3n l\xFD Git hooks t\xEDch h\u1EE3p cho jai1");
|
|
10787
11351
|
cmd.command("setup").description("C\xE0i \u0111\u1EB7t Git hooks (auto task sync on push/pull)").action(() => {
|
|
10788
|
-
console.log(
|
|
11352
|
+
console.log(chalk30.bold("\n\u{1F517} C\xE0i \u0111\u1EB7t jai1 Git hooks...\n"));
|
|
10789
11353
|
setupHooks();
|
|
10790
11354
|
console.log("");
|
|
10791
11355
|
});
|
|
10792
11356
|
cmd.command("remove").description("G\u1EE1 b\u1ECF jai1 Git hooks").action(() => {
|
|
10793
|
-
console.log(
|
|
11357
|
+
console.log(chalk30.bold("\n\u{1F517} G\u1EE1 b\u1ECF jai1 Git hooks...\n"));
|
|
10794
11358
|
removeHooks();
|
|
10795
11359
|
console.log("");
|
|
10796
11360
|
});
|
|
@@ -10801,17 +11365,17 @@ function createHooksCommand() {
|
|
|
10801
11365
|
}
|
|
10802
11366
|
|
|
10803
11367
|
// src/commands/tasks/index.ts
|
|
10804
|
-
import { Command as
|
|
11368
|
+
import { Command as Command71 } from "commander";
|
|
10805
11369
|
|
|
10806
11370
|
// src/commands/tasks/add.ts
|
|
10807
|
-
import { Command as
|
|
10808
|
-
import
|
|
11371
|
+
import { Command as Command58 } from "commander";
|
|
11372
|
+
import chalk31 from "chalk";
|
|
10809
11373
|
function createTaskAddCommand() {
|
|
10810
|
-
return new
|
|
11374
|
+
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) => {
|
|
10811
11375
|
const service = new TaskService();
|
|
10812
11376
|
const priority = Number(options.priority ?? 2);
|
|
10813
11377
|
if (priority < 0 || priority > 3) {
|
|
10814
|
-
console.error(
|
|
11378
|
+
console.error(chalk31.red("\u274C Priority must be 0-3"));
|
|
10815
11379
|
process.exit(1);
|
|
10816
11380
|
}
|
|
10817
11381
|
const tags = options.tags ? options.tags.split(",").map((t) => t.trim()) : [];
|
|
@@ -10827,23 +11391,23 @@ function createTaskAddCommand() {
|
|
|
10827
11391
|
}
|
|
10828
11392
|
const icon = PRIORITY_ICONS[task.priority] || "\u{1F7E1}";
|
|
10829
11393
|
const label = PRIORITY_LABELS[task.priority] || "Medium";
|
|
10830
|
-
console.log(
|
|
10831
|
-
console.log(` ${
|
|
10832
|
-
console.log(` ${
|
|
11394
|
+
console.log(chalk31.green(`\u2705 Task added: ${chalk31.bold(task.id)}`));
|
|
11395
|
+
console.log(` ${chalk31.dim("Title:")} ${task.title}`);
|
|
11396
|
+
console.log(` ${chalk31.dim("Priority:")} ${icon} ${label}`);
|
|
10833
11397
|
if (task.parent) {
|
|
10834
|
-
console.log(` ${
|
|
11398
|
+
console.log(` ${chalk31.dim("Parent:")} ${task.parent}`);
|
|
10835
11399
|
}
|
|
10836
11400
|
if (task.tags.length > 0) {
|
|
10837
|
-
console.log(` ${
|
|
11401
|
+
console.log(` ${chalk31.dim("Tags:")} ${task.tags.join(", ")}`);
|
|
10838
11402
|
}
|
|
10839
11403
|
});
|
|
10840
11404
|
}
|
|
10841
11405
|
|
|
10842
11406
|
// src/commands/tasks/list.ts
|
|
10843
|
-
import { Command as
|
|
10844
|
-
import
|
|
11407
|
+
import { Command as Command59 } from "commander";
|
|
11408
|
+
import chalk32 from "chalk";
|
|
10845
11409
|
function createTaskListCommand() {
|
|
10846
|
-
return new
|
|
11410
|
+
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) => {
|
|
10847
11411
|
await handleTaskList(options);
|
|
10848
11412
|
});
|
|
10849
11413
|
}
|
|
@@ -10862,12 +11426,12 @@ async function handleTaskList(options) {
|
|
|
10862
11426
|
return;
|
|
10863
11427
|
}
|
|
10864
11428
|
if (tasks.length === 0) {
|
|
10865
|
-
console.log(
|
|
11429
|
+
console.log(chalk32.dim("No tasks found."));
|
|
10866
11430
|
return;
|
|
10867
11431
|
}
|
|
10868
11432
|
const resolvedIds = new Set(allTasks.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id));
|
|
10869
11433
|
const header = options.parent ? `\u{1F4CB} ${options.parent} (${tasks.length} tasks)` : `\u{1F4CB} All tasks (${tasks.length})`;
|
|
10870
|
-
console.log(
|
|
11434
|
+
console.log(chalk32.bold(header));
|
|
10871
11435
|
console.log();
|
|
10872
11436
|
for (const task of tasks) {
|
|
10873
11437
|
printTaskLine(task, resolvedIds);
|
|
@@ -10877,25 +11441,25 @@ function printTaskLine(task, resolvedIds) {
|
|
|
10877
11441
|
const isBlocked = task.status === "todo" && task.depends_on.length > 0 && !task.depends_on.every((id) => resolvedIds.has(id));
|
|
10878
11442
|
const statusIcon = isBlocked ? BLOCKED_ICON : STATUS_ICONS[task.status] || "\u{1F4CB}";
|
|
10879
11443
|
const priorityIcon = PRIORITY_ICONS[task.priority] || "\u{1F7E1}";
|
|
10880
|
-
let line = ` ${statusIcon} ${
|
|
11444
|
+
let line = ` ${statusIcon} ${chalk32.dim(task.id)} P${task.priority}${priorityIcon} ${task.title}`;
|
|
10881
11445
|
if (task.status === "in_progress" && task.assigned_to) {
|
|
10882
|
-
line +=
|
|
11446
|
+
line += chalk32.cyan(` @${task.assigned_to}`);
|
|
10883
11447
|
}
|
|
10884
11448
|
if (isBlocked) {
|
|
10885
11449
|
const blockedBy = task.depends_on.filter((id) => !resolvedIds.has(id));
|
|
10886
|
-
line +=
|
|
11450
|
+
line += chalk32.red(` (blocked: ${blockedBy.join(", ")})`);
|
|
10887
11451
|
}
|
|
10888
11452
|
if (task.parent) {
|
|
10889
|
-
line +=
|
|
11453
|
+
line += chalk32.dim(` [${task.parent}]`);
|
|
10890
11454
|
}
|
|
10891
11455
|
console.log(line);
|
|
10892
11456
|
}
|
|
10893
11457
|
|
|
10894
11458
|
// src/commands/tasks/ready.ts
|
|
10895
|
-
import { Command as
|
|
10896
|
-
import
|
|
11459
|
+
import { Command as Command60 } from "commander";
|
|
11460
|
+
import chalk33 from "chalk";
|
|
10897
11461
|
function createTaskReadyCommand() {
|
|
10898
|
-
return new
|
|
11462
|
+
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) => {
|
|
10899
11463
|
const service = new TaskService();
|
|
10900
11464
|
const tasks = await service.getReady(options.parent);
|
|
10901
11465
|
if (options.json) {
|
|
@@ -10903,37 +11467,37 @@ function createTaskReadyCommand() {
|
|
|
10903
11467
|
return;
|
|
10904
11468
|
}
|
|
10905
11469
|
if (tasks.length === 0) {
|
|
10906
|
-
console.log(
|
|
10907
|
-
console.log(
|
|
11470
|
+
console.log(chalk33.dim("No tasks ready to pick."));
|
|
11471
|
+
console.log(chalk33.dim("\u{1F4A1} Check blocked tasks: jai1 t list -s todo"));
|
|
10908
11472
|
return;
|
|
10909
11473
|
}
|
|
10910
|
-
console.log(
|
|
11474
|
+
console.log(chalk33.bold(`\u{1F4CB} Ready to pick (${tasks.length} tasks):`));
|
|
10911
11475
|
console.log();
|
|
10912
11476
|
for (const task of tasks) {
|
|
10913
11477
|
const icon = PRIORITY_ICONS[task.priority] || "\u{1F7E1}";
|
|
10914
|
-
let line = ` P${task.priority}${icon} ${
|
|
11478
|
+
let line = ` P${task.priority}${icon} ${chalk33.dim(task.id)} ${task.title}`;
|
|
10915
11479
|
if (task.parent) {
|
|
10916
|
-
line +=
|
|
11480
|
+
line += chalk33.dim(` [${task.parent}]`);
|
|
10917
11481
|
}
|
|
10918
11482
|
console.log(line);
|
|
10919
11483
|
}
|
|
10920
11484
|
console.log();
|
|
10921
|
-
console.log(
|
|
11485
|
+
console.log(chalk33.dim("\u{1F4A1} Run: jai1 t pick"));
|
|
10922
11486
|
});
|
|
10923
11487
|
}
|
|
10924
11488
|
|
|
10925
11489
|
// src/commands/tasks/update.ts
|
|
10926
|
-
import { Command as
|
|
10927
|
-
import
|
|
11490
|
+
import { Command as Command61 } from "commander";
|
|
11491
|
+
import chalk34 from "chalk";
|
|
10928
11492
|
var VALID_STATUSES = ["todo", "in_progress", "done", "cancelled"];
|
|
10929
11493
|
function createTaskUpdateCommand() {
|
|
10930
|
-
return new
|
|
11494
|
+
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) => {
|
|
10931
11495
|
if (!options.status && !options.notes) {
|
|
10932
|
-
console.error(
|
|
11496
|
+
console.error(chalk34.red("\u274C At least one of --status or --notes is required"));
|
|
10933
11497
|
process.exit(1);
|
|
10934
11498
|
}
|
|
10935
11499
|
if (options.status && !VALID_STATUSES.includes(options.status)) {
|
|
10936
|
-
console.error(
|
|
11500
|
+
console.error(chalk34.red(`\u274C Invalid status. Must be: ${VALID_STATUSES.join(", ")}`));
|
|
10937
11501
|
process.exit(1);
|
|
10938
11502
|
}
|
|
10939
11503
|
const service = new TaskService();
|
|
@@ -10943,8 +11507,8 @@ function createTaskUpdateCommand() {
|
|
|
10943
11507
|
if (existingTask) {
|
|
10944
11508
|
const { blocked, blockedBy } = await service.isBlocked(existingTask);
|
|
10945
11509
|
if (blocked) {
|
|
10946
|
-
console.log(
|
|
10947
|
-
console.log(
|
|
11510
|
+
console.log(chalk34.yellow(`\u26A0\uFE0F Task ${id} is blocked by: ${blockedBy.join(", ")}`));
|
|
11511
|
+
console.log(chalk34.yellow(` Dependencies ch\u01B0a done. Ti\u1EBFp t\u1EE5c update...`));
|
|
10948
11512
|
}
|
|
10949
11513
|
}
|
|
10950
11514
|
}
|
|
@@ -10964,24 +11528,24 @@ function createTaskUpdateCommand() {
|
|
|
10964
11528
|
if (options.notes) {
|
|
10965
11529
|
parts.push(`\u{1F4DD} notes updated`);
|
|
10966
11530
|
}
|
|
10967
|
-
console.log(
|
|
11531
|
+
console.log(chalk34.green(`\u2705 ${task.id} \u2192 ${parts.join(" | ")}`));
|
|
10968
11532
|
} catch (error) {
|
|
10969
|
-
console.error(
|
|
11533
|
+
console.error(chalk34.red(`\u274C ${error instanceof Error ? error.message : String(error)}`));
|
|
10970
11534
|
process.exit(1);
|
|
10971
11535
|
}
|
|
10972
11536
|
});
|
|
10973
11537
|
}
|
|
10974
11538
|
|
|
10975
11539
|
// src/commands/tasks/show.ts
|
|
10976
|
-
import { Command as
|
|
10977
|
-
import
|
|
11540
|
+
import { Command as Command62 } from "commander";
|
|
11541
|
+
import chalk35 from "chalk";
|
|
10978
11542
|
function createTaskShowCommand() {
|
|
10979
|
-
return new
|
|
11543
|
+
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) => {
|
|
10980
11544
|
const service = new TaskService();
|
|
10981
11545
|
if (query.startsWith("T-")) {
|
|
10982
11546
|
const task = await service.findById(query);
|
|
10983
11547
|
if (!task) {
|
|
10984
|
-
console.error(
|
|
11548
|
+
console.error(chalk35.red(`\u274C Task ${query} not found`));
|
|
10985
11549
|
process.exit(1);
|
|
10986
11550
|
}
|
|
10987
11551
|
if (options.json) {
|
|
@@ -10992,34 +11556,34 @@ function createTaskShowCommand() {
|
|
|
10992
11556
|
const statusIcon = blocked ? BLOCKED_ICON : STATUS_ICONS[task.status] || "\u{1F4CB}";
|
|
10993
11557
|
const priIcon = PRIORITY_ICONS[task.priority] || "\u{1F7E1}";
|
|
10994
11558
|
const priLabel = PRIORITY_LABELS[task.priority] || "Medium";
|
|
10995
|
-
console.log(
|
|
11559
|
+
console.log(chalk35.bold(`
|
|
10996
11560
|
\u{1F4CC} ${task.id}: ${task.title}
|
|
10997
11561
|
`));
|
|
10998
|
-
console.log(` ${
|
|
10999
|
-
console.log(` ${
|
|
11562
|
+
console.log(` ${chalk35.dim("Status:")} ${statusIcon} ${task.status}${blocked ? chalk35.red(" (BLOCKED)") : ""}`);
|
|
11563
|
+
console.log(` ${chalk35.dim("Priority:")} ${priIcon} P${task.priority} ${priLabel}`);
|
|
11000
11564
|
if (task.parent) {
|
|
11001
|
-
console.log(` ${
|
|
11565
|
+
console.log(` ${chalk35.dim("Parent:")} ${task.parent}`);
|
|
11002
11566
|
}
|
|
11003
11567
|
if (task.assigned_to) {
|
|
11004
|
-
console.log(` ${
|
|
11568
|
+
console.log(` ${chalk35.dim("Assigned:")} @${task.assigned_to} (${task.claimed_at})`);
|
|
11005
11569
|
}
|
|
11006
11570
|
if (task.depends_on.length > 0) {
|
|
11007
|
-
console.log(` ${
|
|
11571
|
+
console.log(` ${chalk35.dim("Depends on:")} ${task.depends_on.join(", ")}`);
|
|
11008
11572
|
if (blocked) {
|
|
11009
|
-
console.log(` ${
|
|
11573
|
+
console.log(` ${chalk35.dim("Blocked by:")} ${chalk35.red(blockedBy.join(", "))}`);
|
|
11010
11574
|
}
|
|
11011
11575
|
}
|
|
11012
11576
|
if (task.tags.length > 0) {
|
|
11013
|
-
console.log(` ${
|
|
11577
|
+
console.log(` ${chalk35.dim("Tags:")} ${task.tags.join(", ")}`);
|
|
11014
11578
|
}
|
|
11015
11579
|
if (task.branch) {
|
|
11016
|
-
console.log(` ${
|
|
11580
|
+
console.log(` ${chalk35.dim("Branch:")} ${task.branch}`);
|
|
11017
11581
|
}
|
|
11018
11582
|
if (task.notes) {
|
|
11019
|
-
console.log(` ${
|
|
11583
|
+
console.log(` ${chalk35.dim("Notes:")} ${task.notes}`);
|
|
11020
11584
|
}
|
|
11021
|
-
console.log(` ${
|
|
11022
|
-
console.log(` ${
|
|
11585
|
+
console.log(` ${chalk35.dim("Created:")} ${task.created}`);
|
|
11586
|
+
console.log(` ${chalk35.dim("Updated:")} ${task.updated}`);
|
|
11023
11587
|
console.log();
|
|
11024
11588
|
} else {
|
|
11025
11589
|
const tasks = await service.filter({ parent: query });
|
|
@@ -11028,10 +11592,10 @@ function createTaskShowCommand() {
|
|
|
11028
11592
|
return;
|
|
11029
11593
|
}
|
|
11030
11594
|
if (tasks.length === 0) {
|
|
11031
|
-
console.log(
|
|
11595
|
+
console.log(chalk35.dim(`No tasks for parent: ${query}`));
|
|
11032
11596
|
return;
|
|
11033
11597
|
}
|
|
11034
|
-
console.log(
|
|
11598
|
+
console.log(chalk35.bold(`
|
|
11035
11599
|
\u{1F4CB} ${query} (${tasks.length} tasks)
|
|
11036
11600
|
`));
|
|
11037
11601
|
const allTasks = await service.readAll();
|
|
@@ -11039,11 +11603,11 @@ function createTaskShowCommand() {
|
|
|
11039
11603
|
for (const task of tasks) {
|
|
11040
11604
|
const isBlocked = task.status === "todo" && task.depends_on.length > 0 && !task.depends_on.every((id) => doneIds.has(id));
|
|
11041
11605
|
const icon = isBlocked ? BLOCKED_ICON : STATUS_ICONS[task.status] || "\u{1F4CB}";
|
|
11042
|
-
let line = ` ${icon} ${
|
|
11043
|
-
if (task.assigned_to) line +=
|
|
11606
|
+
let line = ` ${icon} ${chalk35.dim(task.id)} P${task.priority} ${task.title}`;
|
|
11607
|
+
if (task.assigned_to) line += chalk35.cyan(` @${task.assigned_to}`);
|
|
11044
11608
|
if (isBlocked) {
|
|
11045
11609
|
const bb = task.depends_on.filter((id) => !doneIds.has(id));
|
|
11046
|
-
line +=
|
|
11610
|
+
line += chalk35.red(` (blocked: ${bb.join(", ")})`);
|
|
11047
11611
|
}
|
|
11048
11612
|
console.log(line);
|
|
11049
11613
|
}
|
|
@@ -11053,11 +11617,11 @@ function createTaskShowCommand() {
|
|
|
11053
11617
|
}
|
|
11054
11618
|
|
|
11055
11619
|
// src/commands/tasks/pick.ts
|
|
11056
|
-
import { Command as
|
|
11057
|
-
import
|
|
11058
|
-
import { confirm as
|
|
11620
|
+
import { Command as Command63 } from "commander";
|
|
11621
|
+
import chalk36 from "chalk";
|
|
11622
|
+
import { confirm as confirm11 } from "@inquirer/prompts";
|
|
11059
11623
|
function createTaskPickCommand() {
|
|
11060
|
-
return new
|
|
11624
|
+
return new Command63("pick").description("Claim the next available task").option("-j, --json", "Output JSON").action(async (options) => {
|
|
11061
11625
|
const service = new TaskService();
|
|
11062
11626
|
const ready = await service.getReady();
|
|
11063
11627
|
if (ready.length === 0) {
|
|
@@ -11065,8 +11629,8 @@ function createTaskPickCommand() {
|
|
|
11065
11629
|
console.log(JSON.stringify({ picked: null, message: "No tasks ready" }));
|
|
11066
11630
|
return;
|
|
11067
11631
|
}
|
|
11068
|
-
console.log(
|
|
11069
|
-
console.log(
|
|
11632
|
+
console.log(chalk36.dim("No tasks ready to pick."));
|
|
11633
|
+
console.log(chalk36.dim('\u{1F4A1} Add tasks first: jai1 t add "..."'));
|
|
11070
11634
|
return;
|
|
11071
11635
|
}
|
|
11072
11636
|
const top = ready[0];
|
|
@@ -11076,34 +11640,34 @@ function createTaskPickCommand() {
|
|
|
11076
11640
|
console.log(JSON.stringify(picked2, null, 2));
|
|
11077
11641
|
return;
|
|
11078
11642
|
}
|
|
11079
|
-
console.log(
|
|
11080
|
-
console.log(` ${
|
|
11643
|
+
console.log(chalk36.bold("\n\u{1F4CC} Next available task:"));
|
|
11644
|
+
console.log(` ${chalk36.bold(top.id)} P${top.priority}${icon} ${top.title}`);
|
|
11081
11645
|
if (top.parent) {
|
|
11082
|
-
console.log(` ${
|
|
11646
|
+
console.log(` ${chalk36.dim("Parent:")} ${top.parent}`);
|
|
11083
11647
|
}
|
|
11084
11648
|
if (ready.length > 1) {
|
|
11085
|
-
console.log(
|
|
11649
|
+
console.log(chalk36.dim(`
|
|
11086
11650
|
+${ready.length - 1} more tasks ready`));
|
|
11087
11651
|
}
|
|
11088
|
-
const proceed = await
|
|
11652
|
+
const proceed = await confirm11({
|
|
11089
11653
|
message: "Claim this task?",
|
|
11090
11654
|
default: true
|
|
11091
11655
|
});
|
|
11092
11656
|
if (!proceed) {
|
|
11093
|
-
console.log(
|
|
11657
|
+
console.log(chalk36.dim("\nCancelled."));
|
|
11094
11658
|
return;
|
|
11095
11659
|
}
|
|
11096
11660
|
const picked = await service.pick(top.id);
|
|
11097
|
-
console.log(
|
|
11661
|
+
console.log(chalk36.green(`
|
|
11098
11662
|
\u2705 ${picked.id} assigned to @${picked.assigned_to}, status \u2192 in_progress`));
|
|
11099
11663
|
});
|
|
11100
11664
|
}
|
|
11101
11665
|
|
|
11102
11666
|
// src/commands/tasks/done.ts
|
|
11103
|
-
import { Command as
|
|
11104
|
-
import
|
|
11667
|
+
import { Command as Command64 } from "commander";
|
|
11668
|
+
import chalk37 from "chalk";
|
|
11105
11669
|
function createTaskDoneCommand() {
|
|
11106
|
-
return new
|
|
11670
|
+
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) => {
|
|
11107
11671
|
const service = new TaskService();
|
|
11108
11672
|
try {
|
|
11109
11673
|
const task = await service.markDone(id);
|
|
@@ -11111,19 +11675,19 @@ function createTaskDoneCommand() {
|
|
|
11111
11675
|
console.log(JSON.stringify(task, null, 2));
|
|
11112
11676
|
return;
|
|
11113
11677
|
}
|
|
11114
|
-
console.log(
|
|
11678
|
+
console.log(chalk37.green(`\u2705 ${task.id}: ${task.title} \u2192 done`));
|
|
11115
11679
|
} catch (error) {
|
|
11116
|
-
console.error(
|
|
11680
|
+
console.error(chalk37.red(`\u274C ${error instanceof Error ? error.message : String(error)}`));
|
|
11117
11681
|
process.exit(1);
|
|
11118
11682
|
}
|
|
11119
11683
|
});
|
|
11120
11684
|
}
|
|
11121
11685
|
|
|
11122
11686
|
// src/commands/tasks/dep.ts
|
|
11123
|
-
import { Command as
|
|
11124
|
-
import
|
|
11687
|
+
import { Command as Command65 } from "commander";
|
|
11688
|
+
import chalk38 from "chalk";
|
|
11125
11689
|
function createTaskDepCommand() {
|
|
11126
|
-
return new
|
|
11690
|
+
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) => {
|
|
11127
11691
|
const service = new TaskService();
|
|
11128
11692
|
try {
|
|
11129
11693
|
const task = await service.addDependency(childId, parentId);
|
|
@@ -11131,166 +11695,166 @@ function createTaskDepCommand() {
|
|
|
11131
11695
|
console.log(JSON.stringify(task, null, 2));
|
|
11132
11696
|
return;
|
|
11133
11697
|
}
|
|
11134
|
-
console.log(
|
|
11135
|
-
console.log(
|
|
11698
|
+
console.log(chalk38.green(`\u2705 ${childId} now depends on ${parentId}`));
|
|
11699
|
+
console.log(chalk38.dim(` ${task.title} \u2192 waits for ${parentId}`));
|
|
11136
11700
|
} catch (error) {
|
|
11137
|
-
console.error(
|
|
11701
|
+
console.error(chalk38.red(`\u274C ${error instanceof Error ? error.message : String(error)}`));
|
|
11138
11702
|
process.exit(1);
|
|
11139
11703
|
}
|
|
11140
11704
|
});
|
|
11141
11705
|
}
|
|
11142
11706
|
|
|
11143
11707
|
// src/commands/tasks/sync.ts
|
|
11144
|
-
import { Command as
|
|
11145
|
-
import
|
|
11708
|
+
import { Command as Command66 } from "commander";
|
|
11709
|
+
import chalk39 from "chalk";
|
|
11146
11710
|
function createTaskSyncCommand() {
|
|
11147
|
-
return new
|
|
11711
|
+
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) => {
|
|
11148
11712
|
const service = new TaskService();
|
|
11149
11713
|
const branch = service.getSyncBranch();
|
|
11150
11714
|
const doPull = options.pull || !options.pull && !options.push;
|
|
11151
11715
|
const doPush = options.push || !options.pull && !options.push;
|
|
11152
11716
|
if (doPull) {
|
|
11153
|
-
console.log(
|
|
11717
|
+
console.log(chalk39.dim(`\u23F3 Pulling tasks from origin/${branch}...`));
|
|
11154
11718
|
try {
|
|
11155
11719
|
const result = await service.syncPull();
|
|
11156
11720
|
if (result.merged > 0) {
|
|
11157
|
-
console.log(
|
|
11721
|
+
console.log(chalk39.green(` \u2193 ${result.merged} tasks merged`));
|
|
11158
11722
|
} else {
|
|
11159
|
-
console.log(
|
|
11723
|
+
console.log(chalk39.dim(` \u2193 Already up to date`));
|
|
11160
11724
|
}
|
|
11161
11725
|
} catch (error) {
|
|
11162
|
-
console.error(
|
|
11726
|
+
console.error(chalk39.red(`\u274C Pull failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
11163
11727
|
process.exit(1);
|
|
11164
11728
|
}
|
|
11165
11729
|
}
|
|
11166
11730
|
if (doPush) {
|
|
11167
|
-
console.log(
|
|
11731
|
+
console.log(chalk39.dim(`\u23F3 Pushing tasks to origin/${branch}...`));
|
|
11168
11732
|
try {
|
|
11169
11733
|
await service.syncPush();
|
|
11170
|
-
console.log(
|
|
11734
|
+
console.log(chalk39.green(` \u2191 Tasks pushed to origin/${branch}`));
|
|
11171
11735
|
} catch (error) {
|
|
11172
|
-
console.error(
|
|
11736
|
+
console.error(chalk39.red(`\u274C Push failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
11173
11737
|
process.exit(1);
|
|
11174
11738
|
}
|
|
11175
11739
|
}
|
|
11176
|
-
console.log(
|
|
11740
|
+
console.log(chalk39.green("\u2705 Sync complete"));
|
|
11177
11741
|
});
|
|
11178
11742
|
}
|
|
11179
11743
|
|
|
11180
11744
|
// src/commands/tasks/guide.ts
|
|
11181
|
-
import { Command as
|
|
11182
|
-
import
|
|
11745
|
+
import { Command as Command67 } from "commander";
|
|
11746
|
+
import chalk40 from "chalk";
|
|
11183
11747
|
var GUIDE_TEXT = `
|
|
11184
|
-
${
|
|
11185
|
-
|
|
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
|
-
1. ${
|
|
11254
|
-
2. ${
|
|
11255
|
-
3. ${
|
|
11256
|
-
4. ${
|
|
11257
|
-
|
|
11258
|
-
${
|
|
11259
|
-
1. ${
|
|
11748
|
+
${chalk40.cyan.bold("\u{1F4D6} Jai1 Task Management Guide")}
|
|
11749
|
+
|
|
11750
|
+
${chalk40.bold("\u2501\u2501\u2501 STATUSES \u2501\u2501\u2501")}
|
|
11751
|
+
${chalk40.dim("todo")} \u{1F4CB} Ch\u01B0a b\u1EAFt \u0111\u1EA7u
|
|
11752
|
+
${chalk40.dim("in_progress")} \u{1F535} \u0110ang l\xE0m (bao g\u1ED3m review)
|
|
11753
|
+
${chalk40.dim("done")} \u2705 Ho\xE0n th\xE0nh
|
|
11754
|
+
${chalk40.dim("cancelled")} \u26AB Hu\u1EF7
|
|
11755
|
+
${chalk40.dim("(blocked)")} \u{1F534} Computed: depends_on ch\u01B0a done
|
|
11756
|
+
|
|
11757
|
+
${chalk40.bold("\u2501\u2501\u2501 PRIORITY \u2501\u2501\u2501")}
|
|
11758
|
+
${chalk40.dim("0")} = \u{1F525} Critical \u2014 Prod down, security, block c\u1EA3 team
|
|
11759
|
+
${chalk40.dim("1")} = \u{1F534} High \u2014 Feature ch\xEDnh, deadline g\u1EA7n
|
|
11760
|
+
${chalk40.dim("2")} = \u{1F7E1} Medium \u2014 B\xECnh th\u01B0\u1EDDng (default)
|
|
11761
|
+
${chalk40.dim("3")} = \u{1F7E2} Low \u2014 Nice-to-have, docs, refactor
|
|
11762
|
+
|
|
11763
|
+
${chalk40.bold("\u2501\u2501\u2501 QUICK START \u2501\u2501\u2501")}
|
|
11764
|
+
${chalk40.cyan("jai1 t add")} "Fix login bug" -p 1 -P bug/login
|
|
11765
|
+
${chalk40.cyan("jai1 t ready")} Show tasks s\u1EB5n s\xE0ng
|
|
11766
|
+
${chalk40.cyan("jai1 t pick")} Claim & start working
|
|
11767
|
+
${chalk40.cyan("jai1 t done")} T-003 Mark complete
|
|
11768
|
+
|
|
11769
|
+
${chalk40.bold("\u2501\u2501\u2501 DAILY WORKFLOW \u2501\u2501\u2501")}
|
|
11770
|
+
${chalk40.cyan("jai1 t sync --pull")} Pull latest tasks
|
|
11771
|
+
${chalk40.cyan("jai1 t summary")} Dashboard t\u1ED5ng quan
|
|
11772
|
+
${chalk40.cyan("jai1 t ready")} Xem tasks s\u1EB5n s\xE0ng
|
|
11773
|
+
${chalk40.cyan("jai1 t pick")} Claim task m\u1EDBi
|
|
11774
|
+
${chalk40.cyan("jai1 t done")} T-xxx Ho\xE0n th\xE0nh task
|
|
11775
|
+
${chalk40.cyan("jai1 t sync --push")} Push l\xEAn git
|
|
11776
|
+
|
|
11777
|
+
${chalk40.bold("\u2501\u2501\u2501 ADDING TASKS \u2501\u2501\u2501")}
|
|
11778
|
+
${chalk40.yellow("\u26A0 Lu\xF4n ki\u1EC3m tra duplicate tr\u01B0\u1EDBc khi add:")}
|
|
11779
|
+
${chalk40.cyan("jai1 t list -P")} feature/xxx
|
|
11780
|
+
|
|
11781
|
+
${chalk40.dim("Add cho feature:")}
|
|
11782
|
+
${chalk40.cyan("jai1 t add")} "Setup DB schema" -p 1 -P feature/xxx
|
|
11783
|
+
${chalk40.cyan("jai1 t add")} "Create API" -p 1 -P feature/xxx
|
|
11784
|
+
${chalk40.cyan("jai1 t add")} "Build UI" -p 2 -P feature/xxx
|
|
11785
|
+
|
|
11786
|
+
${chalk40.dim("Add cho plan:")}
|
|
11787
|
+
${chalk40.cyan("jai1 t add")} "Refactor middleware" -p 2 -P plan/xxx
|
|
11788
|
+
|
|
11789
|
+
${chalk40.dim("Add standalone:")}
|
|
11790
|
+
${chalk40.cyan("jai1 t add")} "Fix README typo" -p 3
|
|
11791
|
+
|
|
11792
|
+
${chalk40.dim("Add bug fix:")}
|
|
11793
|
+
${chalk40.cyan("jai1 t add")} "Fix login redirect" -p 1 -P bug/xxx
|
|
11794
|
+
|
|
11795
|
+
${chalk40.bold("\u2501\u2501\u2501 DEPENDENCY \u2501\u2501\u2501")}
|
|
11796
|
+
${chalk40.dim("Task dependency:")}
|
|
11797
|
+
${chalk40.cyan("jai1 t dep")} T-002 T-001 T-002 ch\u1EDD T-001 done
|
|
11798
|
+
${chalk40.cyan("jai1 t dep")} T-003 T-002 T-003 ch\u1EDD T-002 done
|
|
11799
|
+
|
|
11800
|
+
${chalk40.dim("Feature-level dependency:")}
|
|
11801
|
+
${chalk40.dim("# N\u1EBFu feature/auth ph\u1EE5 thu\u1ED9c feature/user-model:")}
|
|
11802
|
+
${chalk40.cyan("jai1 t add")} "[DEP] Wait for feature/user-model" -p 1 -P feature/auth
|
|
11803
|
+
${chalk40.dim("# R\u1ED3i dep n\xF3 v\u1EDBi tasks cu\u1ED1i c\u1EE7a user-model")}
|
|
11804
|
+
|
|
11805
|
+
${chalk40.dim("View deps:")}
|
|
11806
|
+
${chalk40.cyan("jai1 t show")} T-002 Hi\u1EC7n depends_on
|
|
11807
|
+
|
|
11808
|
+
${chalk40.bold("\u2501\u2501\u2501 TEAM COLLABORATION \u2501\u2501\u2501")}
|
|
11809
|
+
${chalk40.yellow("\u26A0 Assignment ch\u1EC9 qua pick \u2014 kh\xF4ng set th\u1EE7 c\xF4ng.")}
|
|
11810
|
+
${chalk40.dim("Khi b\u1EA1n pick \u2192 team th\u1EA5y task \u0111\xE3 c\xF3 ng\u01B0\u1EDDi nh\u1EADn.")}
|
|
11811
|
+
|
|
11812
|
+
${chalk40.dim("Sync morning:")} ${chalk40.cyan("jai1 t sync --pull")}
|
|
11813
|
+
${chalk40.dim("Sync evening:")} ${chalk40.cyan("jai1 t sync --push")}
|
|
11814
|
+
|
|
11815
|
+
${chalk40.bold("\u2501\u2501\u2501 FOR AI AGENTS (Workflow Integration) \u2501\u2501\u2501")}
|
|
11816
|
+
${chalk40.dim("Khi t\u1EA1o tasks t\u1EEB feature/plan:")}
|
|
11817
|
+
1. ${chalk40.cyan("jai1 t list -P")} <parent> Check existing (tr\xE1nh duplicate)
|
|
11818
|
+
2. ${chalk40.cyan("jai1 t add")} "..." -p <0-3> -P <parent> Add t\u1EEBng task
|
|
11819
|
+
3. ${chalk40.cyan("jai1 t dep")} <child> <parent> Set dependencies
|
|
11820
|
+
4. ${chalk40.cyan("jai1 t done")} <id> Mark complete
|
|
11821
|
+
|
|
11822
|
+
${chalk40.dim("Khi implement task ti\u1EBFp theo:")}
|
|
11823
|
+
1. ${chalk40.cyan("jai1 t pick")} (ho\u1EB7c ${chalk40.cyan("jai1 t ready -P")} <parent>)
|
|
11260
11824
|
2. Implement task
|
|
11261
|
-
3. ${
|
|
11825
|
+
3. ${chalk40.cyan("jai1 t done")} <id>
|
|
11262
11826
|
|
|
11263
|
-
${
|
|
11827
|
+
${chalk40.dim("Status transitions:")}
|
|
11264
11828
|
add \u2192 todo (default)
|
|
11265
11829
|
pick \u2192 in_progress (auto assign)
|
|
11266
11830
|
done \u2192 done
|
|
11267
11831
|
update -s \u2192 any valid status
|
|
11268
11832
|
|
|
11269
|
-
${
|
|
11270
|
-
${
|
|
11271
|
-
${
|
|
11272
|
-
${
|
|
11273
|
-
${
|
|
11274
|
-
${
|
|
11275
|
-
${
|
|
11276
|
-
${
|
|
11277
|
-
${
|
|
11278
|
-
${
|
|
11279
|
-
${
|
|
11280
|
-
${
|
|
11281
|
-
${
|
|
11282
|
-
|
|
11283
|
-
${
|
|
11833
|
+
${chalk40.bold("\u2501\u2501\u2501 ALL COMMANDS \u2501\u2501\u2501")}
|
|
11834
|
+
${chalk40.cyan("jai1 t list")} [-s status] [-P parent] [-j]
|
|
11835
|
+
${chalk40.cyan("jai1 t ready")} [-P parent] [-j]
|
|
11836
|
+
${chalk40.cyan("jai1 t add")} <title> [-p 0-3] [-P parent] [-t tags] [-j]
|
|
11837
|
+
${chalk40.cyan("jai1 t update")} <id> [-s <status>] [-n <notes>] [-j]
|
|
11838
|
+
${chalk40.cyan("jai1 t show")} <id|parent> [-j]
|
|
11839
|
+
${chalk40.cyan("jai1 t pick")} [-j]
|
|
11840
|
+
${chalk40.cyan("jai1 t done")} <id> [-j]
|
|
11841
|
+
${chalk40.cyan("jai1 t dep")} <childId> <parentId> [-j]
|
|
11842
|
+
${chalk40.cyan("jai1 t sync")} [--pull] [--push]
|
|
11843
|
+
${chalk40.cyan("jai1 t summary")} [-j]
|
|
11844
|
+
${chalk40.cyan("jai1 t groups")} [-s status] [-j] ${chalk40.dim("(alias: parents)")}
|
|
11845
|
+
${chalk40.cyan("jai1 t guide")}
|
|
11846
|
+
|
|
11847
|
+
${chalk40.dim("-j / --json available on all commands (except guide, sync)")}
|
|
11284
11848
|
`;
|
|
11285
11849
|
function createTaskGuideCommand() {
|
|
11286
|
-
return new
|
|
11850
|
+
return new Command67("guide").description("Show full task management guide").action(() => {
|
|
11287
11851
|
console.log(GUIDE_TEXT);
|
|
11288
11852
|
});
|
|
11289
11853
|
}
|
|
11290
11854
|
|
|
11291
11855
|
// src/commands/tasks/parents.ts
|
|
11292
|
-
import { Command as
|
|
11293
|
-
import
|
|
11856
|
+
import { Command as Command68 } from "commander";
|
|
11857
|
+
import chalk41 from "chalk";
|
|
11294
11858
|
var PARENT_STATUS_ICONS = {
|
|
11295
11859
|
done: "\u2705",
|
|
11296
11860
|
in_progress: "\u{1F535}",
|
|
@@ -11300,17 +11864,17 @@ var PARENT_STATUS_ICONS = {
|
|
|
11300
11864
|
function formatProgress(p) {
|
|
11301
11865
|
const completed = p.done + p.cancelled;
|
|
11302
11866
|
if (p.status === "done") {
|
|
11303
|
-
return
|
|
11867
|
+
return chalk41.green(`${completed}/${p.total} done`);
|
|
11304
11868
|
}
|
|
11305
11869
|
const parts = [];
|
|
11306
11870
|
if (p.in_progress > 0) parts.push(`${p.in_progress} in_progress`);
|
|
11307
11871
|
if (p.ready > 0) parts.push(`${p.ready} ready`);
|
|
11308
|
-
if (p.blocked > 0) parts.push(
|
|
11872
|
+
if (p.blocked > 0) parts.push(chalk41.red(`${p.blocked} blocked`));
|
|
11309
11873
|
if (p.done > 0) parts.push(`${p.done} done`);
|
|
11310
11874
|
return `${completed}/${p.total} tasks` + (parts.length > 0 ? ` (${parts.join(", ")})` : "");
|
|
11311
11875
|
}
|
|
11312
11876
|
function createTaskParentsCommand() {
|
|
11313
|
-
return new
|
|
11877
|
+
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) => {
|
|
11314
11878
|
const service = new TaskService();
|
|
11315
11879
|
const parents = await service.getParents(options.status);
|
|
11316
11880
|
if (options.json) {
|
|
@@ -11319,44 +11883,44 @@ function createTaskParentsCommand() {
|
|
|
11319
11883
|
}
|
|
11320
11884
|
if (parents.length === 0) {
|
|
11321
11885
|
if (options.status) {
|
|
11322
|
-
console.log(
|
|
11886
|
+
console.log(chalk41.dim(`No groups with status: ${options.status}`));
|
|
11323
11887
|
} else {
|
|
11324
|
-
console.log(
|
|
11325
|
-
console.log(
|
|
11888
|
+
console.log(chalk41.dim("No task groups found."));
|
|
11889
|
+
console.log(chalk41.dim('\u{1F4A1} Add tasks with parent: jai1 t add "..." -P feature/xxx'));
|
|
11326
11890
|
}
|
|
11327
11891
|
return;
|
|
11328
11892
|
}
|
|
11329
11893
|
const header = options.status ? `\u{1F4E6} Task Groups \u2014 ${options.status} (${parents.length})` : `\u{1F4E6} Task Groups (${parents.length})`;
|
|
11330
|
-
console.log(
|
|
11894
|
+
console.log(chalk41.bold(header));
|
|
11331
11895
|
console.log();
|
|
11332
11896
|
for (const p of parents) {
|
|
11333
11897
|
const icon = PARENT_STATUS_ICONS[p.status] || "\u{1F4CB}";
|
|
11334
11898
|
const progress = formatProgress(p);
|
|
11335
|
-
console.log(` ${icon} ${
|
|
11899
|
+
console.log(` ${icon} ${chalk41.bold(p.name)} ${chalk41.dim("\u2014")} ${progress}`);
|
|
11336
11900
|
}
|
|
11337
11901
|
console.log();
|
|
11338
11902
|
});
|
|
11339
11903
|
}
|
|
11340
11904
|
|
|
11341
11905
|
// src/commands/tasks/cancel.ts
|
|
11342
|
-
import { Command as
|
|
11343
|
-
import
|
|
11344
|
-
import { confirm as
|
|
11906
|
+
import { Command as Command69 } from "commander";
|
|
11907
|
+
import chalk42 from "chalk";
|
|
11908
|
+
import { confirm as confirm12 } from "@inquirer/prompts";
|
|
11345
11909
|
function createTaskCancelCommand() {
|
|
11346
|
-
return new
|
|
11910
|
+
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) => {
|
|
11347
11911
|
const service = new TaskService();
|
|
11348
11912
|
try {
|
|
11349
11913
|
const task = await service.findById(id);
|
|
11350
11914
|
if (!task) {
|
|
11351
|
-
console.error(
|
|
11915
|
+
console.error(chalk42.red(`\u274C Task ${id} not found`));
|
|
11352
11916
|
process.exit(1);
|
|
11353
11917
|
}
|
|
11354
11918
|
if (task.status === "cancelled") {
|
|
11355
|
-
console.error(
|
|
11919
|
+
console.error(chalk42.yellow(`\u26A0\uFE0F Task ${id} is already cancelled`));
|
|
11356
11920
|
process.exit(0);
|
|
11357
11921
|
}
|
|
11358
11922
|
if (task.status === "done") {
|
|
11359
|
-
console.error(
|
|
11923
|
+
console.error(chalk42.red(`\u274C Task ${id} is already done \u2014 cannot cancel`));
|
|
11360
11924
|
process.exit(1);
|
|
11361
11925
|
}
|
|
11362
11926
|
const dependents = await service.getDependents(id);
|
|
@@ -11364,17 +11928,17 @@ function createTaskCancelCommand() {
|
|
|
11364
11928
|
(t) => t.status !== "done" && t.status !== "cancelled"
|
|
11365
11929
|
);
|
|
11366
11930
|
const icon = STATUS_ICONS[task.status] || "\u{1F4CB}";
|
|
11367
|
-
console.log(
|
|
11931
|
+
console.log(chalk42.bold(`
|
|
11368
11932
|
\u26AB Cancel: ${icon} ${task.id} \u2014 ${task.title}
|
|
11369
11933
|
`));
|
|
11370
11934
|
if (activeDependents.length > 0) {
|
|
11371
|
-
console.log(
|
|
11935
|
+
console.log(chalk42.yellow(`\u26A0\uFE0F ${activeDependents.length} task(s) ph\u1EE5 thu\u1ED9c v\xE0o ${id}:`));
|
|
11372
11936
|
for (const dep of activeDependents) {
|
|
11373
11937
|
const depIcon = STATUS_ICONS[dep.status] || "\u{1F4CB}";
|
|
11374
|
-
console.log(` ${depIcon} ${
|
|
11938
|
+
console.log(` ${depIcon} ${chalk42.dim(dep.id)} ${dep.title}`);
|
|
11375
11939
|
}
|
|
11376
11940
|
console.log(
|
|
11377
|
-
|
|
11941
|
+
chalk42.dim(
|
|
11378
11942
|
`
|
|
11379
11943
|
\u2192 Cancel s\u1EBD resolve dependency, c\xE1c task tr\xEAn c\xF3 th\u1EC3 \u0111\u01B0\u1EE3c unblock.`
|
|
11380
11944
|
)
|
|
@@ -11382,12 +11946,12 @@ function createTaskCancelCommand() {
|
|
|
11382
11946
|
console.log();
|
|
11383
11947
|
}
|
|
11384
11948
|
if (!options.yes) {
|
|
11385
|
-
const proceed = await
|
|
11949
|
+
const proceed = await confirm12({
|
|
11386
11950
|
message: `Cancel task ${id}?`,
|
|
11387
11951
|
default: false
|
|
11388
11952
|
});
|
|
11389
11953
|
if (!proceed) {
|
|
11390
|
-
console.log(
|
|
11954
|
+
console.log(chalk42.dim("\u0110\xE3 hu\u1EF7."));
|
|
11391
11955
|
return;
|
|
11392
11956
|
}
|
|
11393
11957
|
}
|
|
@@ -11396,10 +11960,10 @@ function createTaskCancelCommand() {
|
|
|
11396
11960
|
console.log(JSON.stringify(updated, null, 2));
|
|
11397
11961
|
return;
|
|
11398
11962
|
}
|
|
11399
|
-
console.log(
|
|
11963
|
+
console.log(chalk42.green(`\u2705 ${updated.id}: ${updated.title} \u2192 cancelled`));
|
|
11400
11964
|
} catch (error) {
|
|
11401
11965
|
console.error(
|
|
11402
|
-
|
|
11966
|
+
chalk42.red(`\u274C ${error instanceof Error ? error.message : String(error)}`)
|
|
11403
11967
|
);
|
|
11404
11968
|
process.exit(1);
|
|
11405
11969
|
}
|
|
@@ -11407,27 +11971,27 @@ function createTaskCancelCommand() {
|
|
|
11407
11971
|
}
|
|
11408
11972
|
|
|
11409
11973
|
// src/commands/tasks/delete.ts
|
|
11410
|
-
import { Command as
|
|
11411
|
-
import
|
|
11412
|
-
import { confirm as
|
|
11974
|
+
import { Command as Command70 } from "commander";
|
|
11975
|
+
import chalk43 from "chalk";
|
|
11976
|
+
import { confirm as confirm13 } from "@inquirer/prompts";
|
|
11413
11977
|
function createTaskDeleteCommand() {
|
|
11414
|
-
return new
|
|
11978
|
+
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) => {
|
|
11415
11979
|
const service = new TaskService();
|
|
11416
11980
|
try {
|
|
11417
11981
|
const task = await service.findById(id);
|
|
11418
11982
|
if (!task) {
|
|
11419
|
-
console.error(
|
|
11983
|
+
console.error(chalk43.red(`\u274C Task ${id} not found`));
|
|
11420
11984
|
process.exit(1);
|
|
11421
11985
|
}
|
|
11422
11986
|
const dependents = await service.getDependents(id);
|
|
11423
11987
|
const icon = STATUS_ICONS[task.status] || "\u{1F4CB}";
|
|
11424
|
-
console.log(
|
|
11988
|
+
console.log(chalk43.bold(`
|
|
11425
11989
|
\u{1F5D1}\uFE0F Delete: ${icon} ${task.id} \u2014 ${task.title}
|
|
11426
11990
|
`));
|
|
11427
|
-
console.log(` ${
|
|
11428
|
-
console.log(` ${
|
|
11991
|
+
console.log(` ${chalk43.dim("Status:")} ${task.status}`);
|
|
11992
|
+
console.log(` ${chalk43.dim("Parent:")} ${task.parent || "(none)"}`);
|
|
11429
11993
|
if (task.depends_on.length > 0) {
|
|
11430
|
-
console.log(` ${
|
|
11994
|
+
console.log(` ${chalk43.dim("Depends:")} ${task.depends_on.join(", ")}`);
|
|
11431
11995
|
}
|
|
11432
11996
|
console.log();
|
|
11433
11997
|
if (dependents.length > 0) {
|
|
@@ -11438,23 +12002,23 @@ function createTaskDeleteCommand() {
|
|
|
11438
12002
|
(t) => t.status === "done" || t.status === "cancelled"
|
|
11439
12003
|
);
|
|
11440
12004
|
console.log(
|
|
11441
|
-
|
|
12005
|
+
chalk43.yellow(
|
|
11442
12006
|
`\u26A0\uFE0F ${dependents.length} task(s) c\xF3 depends_on \u2192 ${id}:`
|
|
11443
12007
|
)
|
|
11444
12008
|
);
|
|
11445
12009
|
for (const dep of dependents) {
|
|
11446
12010
|
const depIcon = STATUS_ICONS[dep.status] || "\u{1F4CB}";
|
|
11447
|
-
console.log(` ${depIcon} ${
|
|
12011
|
+
console.log(` ${depIcon} ${chalk43.dim(dep.id)} ${dep.title}`);
|
|
11448
12012
|
}
|
|
11449
12013
|
console.log(
|
|
11450
|
-
|
|
12014
|
+
chalk43.dim(
|
|
11451
12015
|
`
|
|
11452
12016
|
\u2192 Xo\xE1 s\u1EBD remove ${id} kh\u1ECFi depends_on c\u1EE7a c\xE1c task tr\xEAn.`
|
|
11453
12017
|
)
|
|
11454
12018
|
);
|
|
11455
12019
|
if (activeDependents.length > 0) {
|
|
11456
12020
|
console.log(
|
|
11457
|
-
|
|
12021
|
+
chalk43.dim(
|
|
11458
12022
|
` \u2192 ${activeDependents.length} task(s) ch\u01B0a done c\xF3 th\u1EC3 b\u1ECB \u1EA3nh h\u01B0\u1EDFng (unblock).`
|
|
11459
12023
|
)
|
|
11460
12024
|
);
|
|
@@ -11462,12 +12026,12 @@ function createTaskDeleteCommand() {
|
|
|
11462
12026
|
console.log();
|
|
11463
12027
|
}
|
|
11464
12028
|
if (!options.yes) {
|
|
11465
|
-
const proceed = await
|
|
12029
|
+
const proceed = await confirm13({
|
|
11466
12030
|
message: `Xo\xE1 v\u0129nh vi\u1EC5n task ${id}?`,
|
|
11467
12031
|
default: false
|
|
11468
12032
|
});
|
|
11469
12033
|
if (!proceed) {
|
|
11470
|
-
console.log(
|
|
12034
|
+
console.log(chalk43.dim("\u0110\xE3 hu\u1EF7."));
|
|
11471
12035
|
return;
|
|
11472
12036
|
}
|
|
11473
12037
|
}
|
|
@@ -11476,10 +12040,10 @@ function createTaskDeleteCommand() {
|
|
|
11476
12040
|
}
|
|
11477
12041
|
await service.deleteTask(id);
|
|
11478
12042
|
if (!options.json) {
|
|
11479
|
-
console.log(
|
|
12043
|
+
console.log(chalk43.green(`\u2705 \u0110\xE3 xo\xE1 ${task.id}: ${task.title}`));
|
|
11480
12044
|
if (dependents.length > 0) {
|
|
11481
12045
|
console.log(
|
|
11482
|
-
|
|
12046
|
+
chalk43.dim(
|
|
11483
12047
|
` \u0110\xE3 c\u1EADp nh\u1EADt depends_on cho: ${dependents.map((d) => d.id).join(", ")}`
|
|
11484
12048
|
)
|
|
11485
12049
|
);
|
|
@@ -11487,7 +12051,7 @@ function createTaskDeleteCommand() {
|
|
|
11487
12051
|
}
|
|
11488
12052
|
} catch (error) {
|
|
11489
12053
|
console.error(
|
|
11490
|
-
|
|
12054
|
+
chalk43.red(`\u274C ${error instanceof Error ? error.message : String(error)}`)
|
|
11491
12055
|
);
|
|
11492
12056
|
process.exit(1);
|
|
11493
12057
|
}
|
|
@@ -11496,7 +12060,7 @@ function createTaskDeleteCommand() {
|
|
|
11496
12060
|
|
|
11497
12061
|
// src/commands/tasks/index.ts
|
|
11498
12062
|
function createTasksCommand() {
|
|
11499
|
-
const cmd = new
|
|
12063
|
+
const cmd = new Command71("tasks").alias("t").description("Task management \u2014 track, assign, and manage development tasks").hook("preAction", (thisCommand, actionCommand) => {
|
|
11500
12064
|
if (actionCommand.name() !== "guide") {
|
|
11501
12065
|
TaskService.ensureJai1Dir();
|
|
11502
12066
|
}
|
|
@@ -11523,12 +12087,12 @@ function createTasksCommand() {
|
|
|
11523
12087
|
}
|
|
11524
12088
|
|
|
11525
12089
|
// src/commands/kit/index.ts
|
|
11526
|
-
import { Command as
|
|
11527
|
-
import
|
|
12090
|
+
import { Command as Command75 } from "commander";
|
|
12091
|
+
import chalk45 from "chalk";
|
|
11528
12092
|
|
|
11529
12093
|
// src/commands/kit/list.ts
|
|
11530
|
-
import { Command as
|
|
11531
|
-
import
|
|
12094
|
+
import { Command as Command72 } from "commander";
|
|
12095
|
+
import chalk44 from "chalk";
|
|
11532
12096
|
import Table6 from "cli-table3";
|
|
11533
12097
|
|
|
11534
12098
|
// src/services/starter-kit.service.ts
|
|
@@ -11596,13 +12160,13 @@ var StarterKitService = class {
|
|
|
11596
12160
|
|
|
11597
12161
|
// src/commands/kit/list.ts
|
|
11598
12162
|
function createKitListCommand() {
|
|
11599
|
-
return new
|
|
12163
|
+
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) => {
|
|
11600
12164
|
const configService = new ConfigService();
|
|
11601
12165
|
const config = await configService.load();
|
|
11602
12166
|
if (!config) {
|
|
11603
12167
|
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
11604
12168
|
}
|
|
11605
|
-
console.log(
|
|
12169
|
+
console.log(chalk44.cyan("\u{1F4E6} \u0110ang t\u1EA3i danh s\xE1ch starter kits..."));
|
|
11606
12170
|
console.log();
|
|
11607
12171
|
const kitService = new StarterKitService();
|
|
11608
12172
|
const kits = await kitService.list(config, {
|
|
@@ -11610,9 +12174,9 @@ function createKitListCommand() {
|
|
|
11610
12174
|
search: options.search
|
|
11611
12175
|
});
|
|
11612
12176
|
if (kits.length === 0) {
|
|
11613
|
-
console.log(
|
|
12177
|
+
console.log(chalk44.yellow("Kh\xF4ng t\xECm th\u1EA5y starter kits n\xE0o."));
|
|
11614
12178
|
if (options.category || options.search) {
|
|
11615
|
-
console.log(
|
|
12179
|
+
console.log(chalk44.dim("Th\u1EED b\u1ECF filter \u0111\u1EC3 xem t\u1EA5t c\u1EA3."));
|
|
11616
12180
|
}
|
|
11617
12181
|
return;
|
|
11618
12182
|
}
|
|
@@ -11636,35 +12200,35 @@ function createKitListCommand() {
|
|
|
11636
12200
|
const categoryKits = byCategory[category];
|
|
11637
12201
|
const categoryIcon = category === "frontend" ? "\u{1F3A8}" : category === "backend" ? "\u2699\uFE0F" : category === "fullstack" ? "\u{1F680}" : "\u{1F4E6}";
|
|
11638
12202
|
console.log(
|
|
11639
|
-
|
|
12203
|
+
chalk44.bold(`${categoryIcon} ${category.charAt(0).toUpperCase() + category.slice(1)}`)
|
|
11640
12204
|
);
|
|
11641
12205
|
const table = new Table6({
|
|
11642
12206
|
head: [
|
|
11643
|
-
|
|
11644
|
-
|
|
11645
|
-
|
|
12207
|
+
chalk44.cyan("Slug"),
|
|
12208
|
+
chalk44.cyan("M\xF4 t\u1EA3"),
|
|
12209
|
+
chalk44.cyan("Version")
|
|
11646
12210
|
],
|
|
11647
12211
|
style: { head: [], border: ["gray"] }
|
|
11648
12212
|
});
|
|
11649
12213
|
for (const kit of categoryKits) {
|
|
11650
12214
|
table.push([
|
|
11651
|
-
|
|
11652
|
-
|
|
11653
|
-
|
|
12215
|
+
chalk44.white(kit.slug),
|
|
12216
|
+
chalk44.dim(kit.description.slice(0, 50)),
|
|
12217
|
+
chalk44.green(`v${kit.version}`)
|
|
11654
12218
|
]);
|
|
11655
12219
|
}
|
|
11656
12220
|
console.log(table.toString());
|
|
11657
12221
|
console.log();
|
|
11658
12222
|
}
|
|
11659
|
-
console.log(
|
|
11660
|
-
console.log(
|
|
12223
|
+
console.log(chalk44.dim(`T\u1ED5ng c\u1ED9ng: ${kits.length} starter kit(s)`));
|
|
12224
|
+
console.log(chalk44.dim('\n\u{1F4A1} Ch\u1EA1y "jai1 kit create <slug>" \u0111\u1EC3 t\u1EA1o project m\u1EDBi'));
|
|
11661
12225
|
});
|
|
11662
12226
|
}
|
|
11663
12227
|
|
|
11664
12228
|
// src/commands/kit/info.ts
|
|
11665
|
-
import { Command as
|
|
12229
|
+
import { Command as Command73 } from "commander";
|
|
11666
12230
|
function createKitInfoCommand() {
|
|
11667
|
-
return new
|
|
12231
|
+
return new Command73("info").description("Show detailed information about a starter kit").argument("<slug>", "Starter kit slug").action(async (slug) => {
|
|
11668
12232
|
const configService = new ConfigService();
|
|
11669
12233
|
const config = await configService.load();
|
|
11670
12234
|
if (!config) {
|
|
@@ -11713,7 +12277,7 @@ Post-Init Commands:`);
|
|
|
11713
12277
|
}
|
|
11714
12278
|
|
|
11715
12279
|
// src/commands/kit/create.ts
|
|
11716
|
-
import { Command as
|
|
12280
|
+
import { Command as Command74 } from "commander";
|
|
11717
12281
|
import { promises as fs20 } from "fs";
|
|
11718
12282
|
import { join as join11 } from "path";
|
|
11719
12283
|
import { select as select3, input as input2, checkbox as checkbox4 } from "@inquirer/prompts";
|
|
@@ -11758,7 +12322,7 @@ var HookExecutor = class {
|
|
|
11758
12322
|
|
|
11759
12323
|
// src/commands/kit/create.ts
|
|
11760
12324
|
function createKitCreateCommand() {
|
|
11761
|
-
return new
|
|
12325
|
+
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) => {
|
|
11762
12326
|
const configService = new ConfigService();
|
|
11763
12327
|
const config = await configService.load();
|
|
11764
12328
|
if (!config) {
|
|
@@ -11937,23 +12501,23 @@ async function getAllFiles(dir) {
|
|
|
11937
12501
|
|
|
11938
12502
|
// src/commands/kit/index.ts
|
|
11939
12503
|
function showKitHelp() {
|
|
11940
|
-
console.log(
|
|
12504
|
+
console.log(chalk45.bold.cyan("\u{1F4E6} jai1 kit") + chalk45.dim(" - Qu\u1EA3n l\xFD starter kits"));
|
|
11941
12505
|
console.log();
|
|
11942
|
-
console.log(
|
|
11943
|
-
console.log(` ${
|
|
11944
|
-
console.log(` ${
|
|
11945
|
-
console.log(` ${
|
|
12506
|
+
console.log(chalk45.bold("C\xE1c l\u1EC7nh:"));
|
|
12507
|
+
console.log(` ${chalk45.cyan("list")} Li\u1EC7t k\xEA c\xE1c starter kits c\xF3 s\u1EB5n`);
|
|
12508
|
+
console.log(` ${chalk45.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t starter kit`);
|
|
12509
|
+
console.log(` ${chalk45.cyan("create")} T\u1EA1o project m\u1EDBi t\u1EEB starter kit`);
|
|
11946
12510
|
console.log();
|
|
11947
|
-
console.log(
|
|
11948
|
-
console.log(
|
|
11949
|
-
console.log(
|
|
11950
|
-
console.log(
|
|
11951
|
-
console.log(
|
|
12511
|
+
console.log(chalk45.bold("V\xED d\u1EE5:"));
|
|
12512
|
+
console.log(chalk45.dim(" $ jai1 kit list"));
|
|
12513
|
+
console.log(chalk45.dim(" $ jai1 kit list --category frontend"));
|
|
12514
|
+
console.log(chalk45.dim(" $ jai1 kit info next-tw4-shadcn"));
|
|
12515
|
+
console.log(chalk45.dim(" $ jai1 kit create next-tw4-shadcn my-project"));
|
|
11952
12516
|
console.log();
|
|
11953
|
-
console.log(
|
|
12517
|
+
console.log(chalk45.dim('Ch\u1EA1y "jai1 kit <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
|
|
11954
12518
|
}
|
|
11955
12519
|
function createKitCommand() {
|
|
11956
|
-
const cmd = new
|
|
12520
|
+
const cmd = new Command75("kit").description("Manage starter kits for new projects").action(() => {
|
|
11957
12521
|
showKitHelp();
|
|
11958
12522
|
});
|
|
11959
12523
|
cmd.addCommand(createKitListCommand());
|
|
@@ -11963,21 +12527,21 @@ function createKitCommand() {
|
|
|
11963
12527
|
}
|
|
11964
12528
|
|
|
11965
12529
|
// src/commands/rules/index.ts
|
|
11966
|
-
import { Command as
|
|
11967
|
-
import
|
|
12530
|
+
import { Command as Command82 } from "commander";
|
|
12531
|
+
import chalk47 from "chalk";
|
|
11968
12532
|
|
|
11969
12533
|
// src/commands/rules/list.ts
|
|
11970
|
-
import { Command as
|
|
11971
|
-
import
|
|
12534
|
+
import { Command as Command76 } from "commander";
|
|
12535
|
+
import chalk46 from "chalk";
|
|
11972
12536
|
import Table7 from "cli-table3";
|
|
11973
12537
|
function createRulesListCommand() {
|
|
11974
|
-
return new
|
|
12538
|
+
return new Command76("list").description("List available rule presets").option("--json", "Output as JSON").action(async (options) => {
|
|
11975
12539
|
const configService = new ConfigService();
|
|
11976
12540
|
const config = await configService.load();
|
|
11977
12541
|
if (!config) {
|
|
11978
12542
|
throw new ValidationError('Not initialized. Run "jai1 auth" first.');
|
|
11979
12543
|
}
|
|
11980
|
-
console.log(
|
|
12544
|
+
console.log(chalk46.cyan("\u{1F4CB} \u0110ang t\u1EA3i danh s\xE1ch rule presets..."));
|
|
11981
12545
|
console.log();
|
|
11982
12546
|
try {
|
|
11983
12547
|
const response = await fetch(`${config.apiUrl}/api/rules/presets`, {
|
|
@@ -11994,23 +12558,23 @@ function createRulesListCommand() {
|
|
|
11994
12558
|
return;
|
|
11995
12559
|
}
|
|
11996
12560
|
if (data.total === 0) {
|
|
11997
|
-
console.log(
|
|
12561
|
+
console.log(chalk46.yellow("Kh\xF4ng c\xF3 presets n\xE0o."));
|
|
11998
12562
|
return;
|
|
11999
12563
|
}
|
|
12000
12564
|
console.log(
|
|
12001
|
-
|
|
12565
|
+
chalk46.green(`\u2713 T\xECm th\u1EA5y ${chalk46.bold(data.total)} preset${data.total > 1 ? "s" : ""}`)
|
|
12002
12566
|
);
|
|
12003
12567
|
console.log();
|
|
12004
12568
|
for (const preset of data.presets) {
|
|
12005
|
-
console.log(
|
|
12569
|
+
console.log(chalk46.bold.cyan(`\u{1F4E6} ${preset.slug}`));
|
|
12006
12570
|
const table = new Table7({
|
|
12007
12571
|
style: { head: [], border: ["gray"], compact: true },
|
|
12008
12572
|
colWidths: [15, 55]
|
|
12009
12573
|
});
|
|
12010
12574
|
table.push(
|
|
12011
|
-
[
|
|
12012
|
-
[
|
|
12013
|
-
[
|
|
12575
|
+
[chalk46.dim("T\xEAn"), chalk46.white(preset.name)],
|
|
12576
|
+
[chalk46.dim("M\xF4 t\u1EA3"), chalk46.white(preset.description)],
|
|
12577
|
+
[chalk46.dim("Version"), chalk46.green(`v${preset.version}`)]
|
|
12014
12578
|
);
|
|
12015
12579
|
const stackParts = [];
|
|
12016
12580
|
if (preset.stack.frontend) stackParts.push(preset.stack.frontend);
|
|
@@ -12018,16 +12582,16 @@ function createRulesListCommand() {
|
|
|
12018
12582
|
if (preset.stack.css) stackParts.push(preset.stack.css);
|
|
12019
12583
|
if (preset.stack.database) stackParts.push(preset.stack.database);
|
|
12020
12584
|
if (stackParts.length > 0) {
|
|
12021
|
-
table.push([
|
|
12585
|
+
table.push([chalk46.dim("Stack"), chalk46.yellow(stackParts.join(" + "))]);
|
|
12022
12586
|
}
|
|
12023
12587
|
table.push(
|
|
12024
|
-
[
|
|
12025
|
-
[
|
|
12588
|
+
[chalk46.dim("Tags"), chalk46.dim(preset.tags.join(", ") || "-")],
|
|
12589
|
+
[chalk46.dim("Downloads"), chalk46.white(preset.downloads.toString())]
|
|
12026
12590
|
);
|
|
12027
12591
|
console.log(table.toString());
|
|
12028
12592
|
console.log();
|
|
12029
12593
|
}
|
|
12030
|
-
console.log(
|
|
12594
|
+
console.log(chalk46.dim('\u{1F4A1} Ch\u1EA1y "jai1 rules apply <name>" \u0111\u1EC3 \xE1p d\u1EE5ng preset'));
|
|
12031
12595
|
} catch (error) {
|
|
12032
12596
|
throw new Error(
|
|
12033
12597
|
`L\u1ED7i khi t\u1EA3i presets: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -12037,10 +12601,10 @@ function createRulesListCommand() {
|
|
|
12037
12601
|
}
|
|
12038
12602
|
|
|
12039
12603
|
// src/commands/rules/init.ts
|
|
12040
|
-
import { Command as
|
|
12604
|
+
import { Command as Command77 } from "commander";
|
|
12041
12605
|
import { promises as fs22 } from "fs";
|
|
12042
12606
|
import { join as join13 } from "path";
|
|
12043
|
-
import { select as select4, confirm as
|
|
12607
|
+
import { select as select4, confirm as confirm14 } from "@inquirer/prompts";
|
|
12044
12608
|
|
|
12045
12609
|
// src/services/project-config.service.ts
|
|
12046
12610
|
import { promises as fs21 } from "fs";
|
|
@@ -12162,7 +12726,7 @@ var ProjectConfigService = class {
|
|
|
12162
12726
|
|
|
12163
12727
|
// src/commands/rules/init.ts
|
|
12164
12728
|
function createRulesInitCommand() {
|
|
12165
|
-
return new
|
|
12729
|
+
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) => {
|
|
12166
12730
|
const configService = new ConfigService();
|
|
12167
12731
|
const config = await configService.load();
|
|
12168
12732
|
if (!config) {
|
|
@@ -12224,7 +12788,7 @@ function createRulesInitCommand() {
|
|
|
12224
12788
|
});
|
|
12225
12789
|
}
|
|
12226
12790
|
if (!options.yes) {
|
|
12227
|
-
const proceed = await
|
|
12791
|
+
const proceed = await confirm14({
|
|
12228
12792
|
message: `Apply preset '${bundle.preset.name}' to current directory?`,
|
|
12229
12793
|
default: true
|
|
12230
12794
|
});
|
|
@@ -12294,10 +12858,10 @@ async function applyAgentsMdFormat(bundle) {
|
|
|
12294
12858
|
}
|
|
12295
12859
|
|
|
12296
12860
|
// src/commands/rules/apply.ts
|
|
12297
|
-
import { Command as
|
|
12861
|
+
import { Command as Command78 } from "commander";
|
|
12298
12862
|
import { promises as fs24 } from "fs";
|
|
12299
12863
|
import { join as join15 } from "path";
|
|
12300
|
-
import { search, confirm as
|
|
12864
|
+
import { search, confirm as confirm15, checkbox as checkbox5 } from "@inquirer/prompts";
|
|
12301
12865
|
|
|
12302
12866
|
// src/services/rules-generator.service.ts
|
|
12303
12867
|
var RulesGeneratorService = class {
|
|
@@ -12808,7 +13372,7 @@ Restoring backup from ${metadata.timestamp}...`);
|
|
|
12808
13372
|
|
|
12809
13373
|
// src/commands/rules/apply.ts
|
|
12810
13374
|
function createRulesApplyCommand() {
|
|
12811
|
-
return new
|
|
13375
|
+
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) => {
|
|
12812
13376
|
const configService = new ConfigService();
|
|
12813
13377
|
const config = await configService.load();
|
|
12814
13378
|
if (!config) {
|
|
@@ -12964,7 +13528,7 @@ function createRulesApplyCommand() {
|
|
|
12964
13528
|
if (backupPath) {
|
|
12965
13529
|
console.log(` Backup: ${backupPath}`);
|
|
12966
13530
|
}
|
|
12967
|
-
const proceed = await
|
|
13531
|
+
const proceed = await confirm15({
|
|
12968
13532
|
message: "Apply these rules to the current directory?",
|
|
12969
13533
|
default: true
|
|
12970
13534
|
});
|
|
@@ -13103,11 +13667,11 @@ function createRulesApplyCommand() {
|
|
|
13103
13667
|
}
|
|
13104
13668
|
|
|
13105
13669
|
// src/commands/rules/restore.ts
|
|
13106
|
-
import { Command as
|
|
13670
|
+
import { Command as Command79 } from "commander";
|
|
13107
13671
|
import { join as join16 } from "path";
|
|
13108
|
-
import { select as select5, confirm as
|
|
13672
|
+
import { select as select5, confirm as confirm16 } from "@inquirer/prompts";
|
|
13109
13673
|
function createRulesRestoreCommand() {
|
|
13110
|
-
return new
|
|
13674
|
+
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) => {
|
|
13111
13675
|
const backupService = new BackupService();
|
|
13112
13676
|
const backups = await backupService.listBackups();
|
|
13113
13677
|
if (backups.length === 0) {
|
|
@@ -13140,7 +13704,7 @@ function createRulesRestoreCommand() {
|
|
|
13140
13704
|
console.log(` IDEs: ${selectedBackup.ides.join(", ")}`);
|
|
13141
13705
|
console.log(` Files: ${selectedBackup.files.length}`);
|
|
13142
13706
|
if (!options.yes) {
|
|
13143
|
-
const proceed = await
|
|
13707
|
+
const proceed = await confirm16({
|
|
13144
13708
|
message: "This will overwrite current rules. Continue?",
|
|
13145
13709
|
default: false
|
|
13146
13710
|
});
|
|
@@ -13176,12 +13740,12 @@ function formatTimestamp(timestamp) {
|
|
|
13176
13740
|
}
|
|
13177
13741
|
|
|
13178
13742
|
// src/commands/rules/sync.ts
|
|
13179
|
-
import { Command as
|
|
13743
|
+
import { Command as Command80 } from "commander";
|
|
13180
13744
|
import { promises as fs25 } from "fs";
|
|
13181
13745
|
import { join as join17 } from "path";
|
|
13182
|
-
import { checkbox as checkbox6, confirm as
|
|
13746
|
+
import { checkbox as checkbox6, confirm as confirm17, Separator } from "@inquirer/prompts";
|
|
13183
13747
|
function createRulesSyncCommand() {
|
|
13184
|
-
return new
|
|
13748
|
+
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) => {
|
|
13185
13749
|
const rulePresetDir = join17(process.cwd(), ".jai1", "rule-preset");
|
|
13186
13750
|
const presetJsonPath = join17(rulePresetDir, "preset.json");
|
|
13187
13751
|
let presetExists = false;
|
|
@@ -13230,7 +13794,7 @@ Detected ${detected.length} active IDE(s):
|
|
|
13230
13794
|
console.log(` ${confidence} ${d.name} - ${d.ruleCount} rules`);
|
|
13231
13795
|
});
|
|
13232
13796
|
if (!options.yes) {
|
|
13233
|
-
const proceed = await
|
|
13797
|
+
const proceed = await confirm17({
|
|
13234
13798
|
message: "\nSync these detected IDEs?",
|
|
13235
13799
|
default: true
|
|
13236
13800
|
});
|
|
@@ -13399,11 +13963,11 @@ function buildIdeChoices(currentIdes, detected, suggestions) {
|
|
|
13399
13963
|
}
|
|
13400
13964
|
|
|
13401
13965
|
// src/commands/rules/info.ts
|
|
13402
|
-
import { Command as
|
|
13966
|
+
import { Command as Command81 } from "commander";
|
|
13403
13967
|
import { promises as fs26 } from "fs";
|
|
13404
13968
|
import { join as join18 } from "path";
|
|
13405
13969
|
function createRulesInfoCommand() {
|
|
13406
|
-
return new
|
|
13970
|
+
return new Command81("info").description("Show current preset information").option("--json", "Output as JSON").action(async (options) => {
|
|
13407
13971
|
const projectConfigService = new ProjectConfigService();
|
|
13408
13972
|
const rulesConfig = await projectConfigService.loadRules();
|
|
13409
13973
|
if (!rulesConfig) {
|
|
@@ -13506,26 +14070,26 @@ async function checkIdeFilesExist(ideId, format) {
|
|
|
13506
14070
|
|
|
13507
14071
|
// src/commands/rules/index.ts
|
|
13508
14072
|
function showRulesHelp() {
|
|
13509
|
-
console.log(
|
|
14073
|
+
console.log(chalk47.bold.cyan("\u{1F4CB} jai1 rules") + chalk47.dim(" - Qu\u1EA3n l\xFD rule presets cho AI agents"));
|
|
13510
14074
|
console.log();
|
|
13511
|
-
console.log(
|
|
13512
|
-
console.log(` ${
|
|
13513
|
-
console.log(` ${
|
|
13514
|
-
console.log(` ${
|
|
13515
|
-
console.log(` ${
|
|
13516
|
-
console.log(` ${
|
|
13517
|
-
console.log(` ${
|
|
14075
|
+
console.log(chalk47.bold("C\xE1c l\u1EC7nh:"));
|
|
14076
|
+
console.log(` ${chalk47.cyan("list")} Li\u1EC7t k\xEA c\xE1c presets c\xF3 s\u1EB5n`);
|
|
14077
|
+
console.log(` ${chalk47.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t preset`);
|
|
14078
|
+
console.log(` ${chalk47.cyan("init")} Kh\u1EDFi t\u1EA1o rules t\u1EEB preset`);
|
|
14079
|
+
console.log(` ${chalk47.cyan("apply")} \xC1p d\u1EE5ng preset v\xE0o project`);
|
|
14080
|
+
console.log(` ${chalk47.cyan("sync")} \u0110\u1ED3ng b\u1ED9 rules sang c\xE1c \u0111\u1ECBnh d\u1EA1ng IDE`);
|
|
14081
|
+
console.log(` ${chalk47.cyan("restore")} Kh\xF4i ph\u1EE5c rules t\u1EEB backup`);
|
|
13518
14082
|
console.log();
|
|
13519
|
-
console.log(
|
|
13520
|
-
console.log(
|
|
13521
|
-
console.log(
|
|
13522
|
-
console.log(
|
|
13523
|
-
console.log(
|
|
14083
|
+
console.log(chalk47.bold("V\xED d\u1EE5:"));
|
|
14084
|
+
console.log(chalk47.dim(" $ jai1 rules list"));
|
|
14085
|
+
console.log(chalk47.dim(" $ jai1 rules info react-typescript"));
|
|
14086
|
+
console.log(chalk47.dim(" $ jai1 rules init --preset=react-typescript"));
|
|
14087
|
+
console.log(chalk47.dim(" $ jai1 rules apply react-typescript"));
|
|
13524
14088
|
console.log();
|
|
13525
|
-
console.log(
|
|
14089
|
+
console.log(chalk47.dim('Ch\u1EA1y "jai1 rules <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt'));
|
|
13526
14090
|
}
|
|
13527
14091
|
function createRulesCommand() {
|
|
13528
|
-
const rulesCommand = new
|
|
14092
|
+
const rulesCommand = new Command82("rules").description("Manage rule presets for AI agents").action(() => {
|
|
13529
14093
|
showRulesHelp();
|
|
13530
14094
|
});
|
|
13531
14095
|
rulesCommand.addCommand(createRulesListCommand());
|
|
@@ -13538,12 +14102,12 @@ function createRulesCommand() {
|
|
|
13538
14102
|
}
|
|
13539
14103
|
|
|
13540
14104
|
// src/commands/skills/index.ts
|
|
13541
|
-
import { Command as
|
|
13542
|
-
import
|
|
14105
|
+
import { Command as Command88 } from "commander";
|
|
14106
|
+
import chalk53 from "chalk";
|
|
13543
14107
|
|
|
13544
14108
|
// src/commands/skills/find.ts
|
|
13545
|
-
import { Command as
|
|
13546
|
-
import
|
|
14109
|
+
import { Command as Command83 } from "commander";
|
|
14110
|
+
import chalk48 from "chalk";
|
|
13547
14111
|
import Table8 from "cli-table3";
|
|
13548
14112
|
|
|
13549
14113
|
// src/services/skills.service.ts
|
|
@@ -13837,7 +14401,7 @@ var SkillsService = class {
|
|
|
13837
14401
|
|
|
13838
14402
|
// src/commands/skills/find.ts
|
|
13839
14403
|
function createSkillsFindCommand() {
|
|
13840
|
-
return new
|
|
14404
|
+
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) => {
|
|
13841
14405
|
const searchNpm = options.skillsh || options.all;
|
|
13842
14406
|
const searchServer = !options.skillsh || options.all;
|
|
13843
14407
|
if (searchServer) {
|
|
@@ -13846,19 +14410,19 @@ function createSkillsFindCommand() {
|
|
|
13846
14410
|
if (!config) {
|
|
13847
14411
|
throw new ValidationError('Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.');
|
|
13848
14412
|
}
|
|
13849
|
-
console.log(
|
|
14413
|
+
console.log(chalk48.cyan("\u{1F50D} \u0110ang t\xECm ki\u1EBFm tr\xEAn Jai1 server..."));
|
|
13850
14414
|
console.log();
|
|
13851
14415
|
const skillsService = new SkillsService();
|
|
13852
14416
|
const results = await skillsService.searchFromServer(config, query);
|
|
13853
14417
|
if (results.length === 0) {
|
|
13854
|
-
console.log(
|
|
14418
|
+
console.log(chalk48.yellow("Kh\xF4ng t\xECm th\u1EA5y skills n\xE0o tr\xEAn server."));
|
|
13855
14419
|
} else {
|
|
13856
14420
|
const table = new Table8({
|
|
13857
14421
|
head: [
|
|
13858
|
-
|
|
13859
|
-
|
|
13860
|
-
|
|
13861
|
-
|
|
14422
|
+
chalk48.cyan("T\xEAn"),
|
|
14423
|
+
chalk48.cyan("M\xF4 t\u1EA3"),
|
|
14424
|
+
chalk48.cyan("Version"),
|
|
14425
|
+
chalk48.cyan("Downloads")
|
|
13862
14426
|
],
|
|
13863
14427
|
style: { head: [], border: ["gray"] },
|
|
13864
14428
|
colWidths: [25, 40, 10, 12]
|
|
@@ -13866,70 +14430,70 @@ function createSkillsFindCommand() {
|
|
|
13866
14430
|
for (const skill of results) {
|
|
13867
14431
|
const name = skill.filepath.replace("skills/", "");
|
|
13868
14432
|
table.push([
|
|
13869
|
-
|
|
13870
|
-
|
|
13871
|
-
|
|
13872
|
-
|
|
14433
|
+
chalk48.white(name),
|
|
14434
|
+
chalk48.dim((skill.description || "").slice(0, 38)),
|
|
14435
|
+
chalk48.green(skill.version || "-"),
|
|
14436
|
+
chalk48.dim(String(skill.downloads || 0))
|
|
13873
14437
|
]);
|
|
13874
14438
|
}
|
|
13875
|
-
console.log(
|
|
14439
|
+
console.log(chalk48.bold(`\u{1F4E6} Jai1 Server (${results.length} k\u1EBFt qu\u1EA3)`));
|
|
13876
14440
|
console.log(table.toString());
|
|
13877
14441
|
console.log();
|
|
13878
14442
|
}
|
|
13879
14443
|
}
|
|
13880
14444
|
if (searchNpm) {
|
|
13881
|
-
console.log(
|
|
14445
|
+
console.log(chalk48.cyan("\u{1F50D} \u0110ang t\xECm ki\u1EBFm tr\xEAn npm skills..."));
|
|
13882
14446
|
console.log();
|
|
13883
14447
|
const skillsService = new SkillsService();
|
|
13884
14448
|
try {
|
|
13885
14449
|
const output = await skillsService.npmSkillsFind(query);
|
|
13886
|
-
console.log(
|
|
14450
|
+
console.log(chalk48.bold("\u{1F310} npm Skills Registry"));
|
|
13887
14451
|
console.log(output);
|
|
13888
14452
|
} catch (error) {
|
|
13889
|
-
console.log(
|
|
14453
|
+
console.log(chalk48.yellow(
|
|
13890
14454
|
`Kh\xF4ng th\u1EC3 t\xECm ki\u1EBFm tr\xEAn npm: ${error instanceof Error ? error.message : String(error)}`
|
|
13891
14455
|
));
|
|
13892
14456
|
}
|
|
13893
14457
|
}
|
|
13894
14458
|
if (searchServer && !searchNpm) {
|
|
13895
|
-
console.log(
|
|
14459
|
+
console.log(chalk48.dim('\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t t\u1EEB server'));
|
|
13896
14460
|
} else if (searchNpm && !searchServer) {
|
|
13897
|
-
console.log(
|
|
14461
|
+
console.log(chalk48.dim('\u{1F4A1} D\xF9ng "j skills add <owner/repo@skill> --skillsh" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
|
|
13898
14462
|
} else {
|
|
13899
|
-
console.log(
|
|
13900
|
-
console.log(
|
|
13901
|
-
console.log(
|
|
14463
|
+
console.log(chalk48.dim("\u{1F4A1} C\xE0i \u0111\u1EB7t:"));
|
|
14464
|
+
console.log(chalk48.dim(" Server: j skills add <t\xEAn>"));
|
|
14465
|
+
console.log(chalk48.dim(" Skills.sh: j skills add <owner/repo@skill> --skillsh"));
|
|
13902
14466
|
}
|
|
13903
14467
|
});
|
|
13904
14468
|
}
|
|
13905
14469
|
|
|
13906
14470
|
// src/commands/skills/add.ts
|
|
13907
|
-
import { Command as
|
|
14471
|
+
import { Command as Command84 } from "commander";
|
|
13908
14472
|
import { join as join20 } from "path";
|
|
13909
|
-
import
|
|
14473
|
+
import chalk49 from "chalk";
|
|
13910
14474
|
import { checkbox as checkbox7 } from "@inquirer/prompts";
|
|
13911
14475
|
function createSkillsAddCommand() {
|
|
13912
|
-
return new
|
|
14476
|
+
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) => {
|
|
13913
14477
|
const skillsService = new SkillsService();
|
|
13914
14478
|
const projectRoot = process.cwd();
|
|
13915
14479
|
const headless = options.yes === true;
|
|
13916
14480
|
if (options.skillsh) {
|
|
13917
|
-
console.log(
|
|
14481
|
+
console.log(chalk49.cyan(`\u{1F310} \u0110ang c\xE0i \u0111\u1EB7t skill t\u1EEB npm: ${name}...`));
|
|
13918
14482
|
console.log();
|
|
13919
14483
|
const output = await skillsService.npmSkillsAdd(name, projectRoot);
|
|
13920
14484
|
console.log(output);
|
|
13921
|
-
console.log(
|
|
14485
|
+
console.log(chalk49.green("\u2705 C\xE0i \u0111\u1EB7t t\u1EEB npm th\xE0nh c\xF4ng!"));
|
|
13922
14486
|
} else {
|
|
13923
14487
|
const configService = new ConfigService();
|
|
13924
14488
|
const config = await configService.load();
|
|
13925
14489
|
if (!config) {
|
|
13926
14490
|
throw new ValidationError('Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.');
|
|
13927
14491
|
}
|
|
13928
|
-
console.log(
|
|
14492
|
+
console.log(chalk49.cyan(`\u{1F4E6} \u0110ang c\xE0i \u0111\u1EB7t skill: ${name}...`));
|
|
13929
14493
|
console.log();
|
|
13930
14494
|
const targetDir = join20(projectRoot, ".jai1");
|
|
13931
14495
|
await skillsService.installFromServer(config, name, targetDir);
|
|
13932
|
-
console.log(
|
|
14496
|
+
console.log(chalk49.green(`\u2705 \u0110\xE3 c\xE0i \u0111\u1EB7t skill "${name}" v\xE0o .jai1/skills/${name}/`));
|
|
13933
14497
|
}
|
|
13934
14498
|
console.log();
|
|
13935
14499
|
if (options.sync) {
|
|
@@ -13954,7 +14518,7 @@ function createSkillsAddCommand() {
|
|
|
13954
14518
|
});
|
|
13955
14519
|
}
|
|
13956
14520
|
if (selectedIdes.length > 0) {
|
|
13957
|
-
console.log(
|
|
14521
|
+
console.log(chalk49.cyan("\u{1F504} \u0110ang sync sang IDE(s)..."));
|
|
13958
14522
|
console.log();
|
|
13959
14523
|
const slug = name.includes("/") ? name.split("/").pop() : name;
|
|
13960
14524
|
const result = await skillsService.syncToIdes(
|
|
@@ -13967,24 +14531,24 @@ function createSkillsAddCommand() {
|
|
|
13967
14531
|
}
|
|
13968
14532
|
);
|
|
13969
14533
|
console.log();
|
|
13970
|
-
console.log(
|
|
14534
|
+
console.log(chalk49.green(`\u2705 Sync ho\xE0n t\u1EA5t! Created: ${result.created}, Updated: ${result.updated}`));
|
|
13971
14535
|
if (result.errors > 0) {
|
|
13972
|
-
console.log(
|
|
14536
|
+
console.log(chalk49.yellow(`\u26A0\uFE0F Errors: ${result.errors}`));
|
|
13973
14537
|
}
|
|
13974
14538
|
}
|
|
13975
14539
|
} else {
|
|
13976
|
-
console.log(
|
|
13977
|
-
console.log(
|
|
14540
|
+
console.log(chalk49.dim('\u{1F4A1} Ch\u1EA1y "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
|
|
14541
|
+
console.log(chalk49.dim(' ho\u1EB7c "j ide sync" \u0111\u1EC3 sync to\xE0n b\u1ED9 .jai1/'));
|
|
13978
14542
|
}
|
|
13979
14543
|
});
|
|
13980
14544
|
}
|
|
13981
14545
|
|
|
13982
14546
|
// src/commands/skills/list.ts
|
|
13983
|
-
import { Command as
|
|
13984
|
-
import
|
|
14547
|
+
import { Command as Command85 } from "commander";
|
|
14548
|
+
import chalk50 from "chalk";
|
|
13985
14549
|
import Table9 from "cli-table3";
|
|
13986
14550
|
function createSkillsListCommand() {
|
|
13987
|
-
return new
|
|
14551
|
+
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) => {
|
|
13988
14552
|
const skillsService = new SkillsService();
|
|
13989
14553
|
if (options.available) {
|
|
13990
14554
|
const configService = new ConfigService();
|
|
@@ -13992,19 +14556,19 @@ function createSkillsListCommand() {
|
|
|
13992
14556
|
if (!config) {
|
|
13993
14557
|
throw new ValidationError('Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.');
|
|
13994
14558
|
}
|
|
13995
|
-
console.log(
|
|
14559
|
+
console.log(chalk50.cyan("\u{1F4E6} \u0110ang t\u1EA3i danh s\xE1ch skills t\u1EEB server..."));
|
|
13996
14560
|
console.log();
|
|
13997
14561
|
const results = await skillsService.searchFromServer(config, options.search);
|
|
13998
14562
|
if (results.length === 0) {
|
|
13999
|
-
console.log(
|
|
14563
|
+
console.log(chalk50.yellow("Kh\xF4ng t\xECm th\u1EA5y skills n\xE0o."));
|
|
14000
14564
|
return;
|
|
14001
14565
|
}
|
|
14002
14566
|
const table = new Table9({
|
|
14003
14567
|
head: [
|
|
14004
|
-
|
|
14005
|
-
|
|
14006
|
-
|
|
14007
|
-
|
|
14568
|
+
chalk50.cyan("T\xEAn"),
|
|
14569
|
+
chalk50.cyan("M\xF4 t\u1EA3"),
|
|
14570
|
+
chalk50.cyan("Version"),
|
|
14571
|
+
chalk50.cyan("Downloads")
|
|
14008
14572
|
],
|
|
14009
14573
|
style: { head: [], border: ["gray"] },
|
|
14010
14574
|
colWidths: [28, 40, 10, 12]
|
|
@@ -14012,63 +14576,63 @@ function createSkillsListCommand() {
|
|
|
14012
14576
|
for (const skill of results) {
|
|
14013
14577
|
const name = skill.filepath.replace("skills/", "");
|
|
14014
14578
|
table.push([
|
|
14015
|
-
|
|
14016
|
-
|
|
14017
|
-
|
|
14018
|
-
|
|
14579
|
+
chalk50.white(name),
|
|
14580
|
+
chalk50.dim((skill.description || "").slice(0, 38)),
|
|
14581
|
+
chalk50.green(skill.version || "-"),
|
|
14582
|
+
chalk50.dim(String(skill.downloads || 0))
|
|
14019
14583
|
]);
|
|
14020
14584
|
}
|
|
14021
14585
|
console.log(table.toString());
|
|
14022
14586
|
console.log();
|
|
14023
|
-
console.log(
|
|
14024
|
-
console.log(
|
|
14587
|
+
console.log(chalk50.dim(`T\u1ED5ng c\u1ED9ng: ${results.length} skill(s)`));
|
|
14588
|
+
console.log(chalk50.dim('\n\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
|
|
14025
14589
|
} else {
|
|
14026
14590
|
const projectRoot = process.cwd();
|
|
14027
14591
|
const skills = await skillsService.listLocal(projectRoot);
|
|
14028
14592
|
if (skills.length === 0) {
|
|
14029
|
-
console.log(
|
|
14593
|
+
console.log(chalk50.yellow("Ch\u01B0a c\xF3 skills n\xE0o \u0111\u01B0\u1EE3c c\xE0i \u0111\u1EB7t."));
|
|
14030
14594
|
console.log();
|
|
14031
|
-
console.log(
|
|
14032
|
-
console.log(
|
|
14595
|
+
console.log(chalk50.dim('\u{1F4A1} D\xF9ng "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
|
|
14596
|
+
console.log(chalk50.dim(' ho\u1EB7c "j skills list --available" \u0111\u1EC3 xem skills c\xF3 s\u1EB5n'));
|
|
14033
14597
|
return;
|
|
14034
14598
|
}
|
|
14035
|
-
console.log(
|
|
14599
|
+
console.log(chalk50.bold.cyan("\u{1F6E0} Skills \u0111\xE3 c\xE0i \u0111\u1EB7t"));
|
|
14036
14600
|
console.log();
|
|
14037
14601
|
const table = new Table9({
|
|
14038
14602
|
head: [
|
|
14039
|
-
|
|
14040
|
-
|
|
14041
|
-
|
|
14603
|
+
chalk50.cyan("T\xEAn"),
|
|
14604
|
+
chalk50.cyan("M\xF4 t\u1EA3"),
|
|
14605
|
+
chalk50.cyan("Files")
|
|
14042
14606
|
],
|
|
14043
14607
|
style: { head: [], border: ["gray"] },
|
|
14044
14608
|
colWidths: [28, 45, 8]
|
|
14045
14609
|
});
|
|
14046
14610
|
for (const skill of skills) {
|
|
14047
14611
|
table.push([
|
|
14048
|
-
|
|
14049
|
-
|
|
14050
|
-
|
|
14612
|
+
chalk50.white(skill.slug),
|
|
14613
|
+
chalk50.dim(skill.description.slice(0, 43)),
|
|
14614
|
+
chalk50.dim(String(skill.fileCount))
|
|
14051
14615
|
]);
|
|
14052
14616
|
}
|
|
14053
14617
|
console.log(table.toString());
|
|
14054
14618
|
console.log();
|
|
14055
|
-
console.log(
|
|
14056
|
-
console.log(
|
|
14619
|
+
console.log(chalk50.dim(`T\u1ED5ng c\u1ED9ng: ${skills.length} skill(s)`));
|
|
14620
|
+
console.log(chalk50.dim('\n\u{1F4A1} D\xF9ng "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
|
|
14057
14621
|
}
|
|
14058
14622
|
});
|
|
14059
14623
|
}
|
|
14060
14624
|
|
|
14061
14625
|
// src/commands/skills/info.ts
|
|
14062
|
-
import { Command as
|
|
14063
|
-
import
|
|
14626
|
+
import { Command as Command86 } from "commander";
|
|
14627
|
+
import chalk51 from "chalk";
|
|
14064
14628
|
function createSkillsInfoCommand() {
|
|
14065
|
-
return new
|
|
14629
|
+
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) => {
|
|
14066
14630
|
const skillsService = new SkillsService();
|
|
14067
14631
|
if (options.server) {
|
|
14068
14632
|
const configService = new ConfigService();
|
|
14069
14633
|
const config = await configService.load();
|
|
14070
14634
|
if (!config) {
|
|
14071
|
-
console.log(
|
|
14635
|
+
console.log(chalk51.red('\u274C Ch\u01B0a x\xE1c th\u1EF1c. Ch\u1EA1y "jai1 auth" tr\u01B0\u1EDBc.'));
|
|
14072
14636
|
process.exit(1);
|
|
14073
14637
|
}
|
|
14074
14638
|
const filepath = name.startsWith("skills/") ? name : `skills/${name}`;
|
|
@@ -14077,7 +14641,7 @@ function createSkillsInfoCommand() {
|
|
|
14077
14641
|
try {
|
|
14078
14642
|
const component = await componentsService.get(config, filepath);
|
|
14079
14643
|
console.log(`
|
|
14080
|
-
\u{1F6E0} ${
|
|
14644
|
+
\u{1F6E0} ${chalk51.bold(component.name || name)}
|
|
14081
14645
|
`);
|
|
14082
14646
|
console.log(`Filepath: ${component.filepath}`);
|
|
14083
14647
|
console.log(`Version: ${component.version}`);
|
|
@@ -14090,47 +14654,47 @@ function createSkillsInfoCommand() {
|
|
|
14090
14654
|
}
|
|
14091
14655
|
console.log(`Type: ${component.contentType}`);
|
|
14092
14656
|
console.log();
|
|
14093
|
-
console.log(
|
|
14657
|
+
console.log(chalk51.dim('\u{1F4A1} D\xF9ng "j skills add ' + name + '" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
|
|
14094
14658
|
} catch (error) {
|
|
14095
|
-
console.log(
|
|
14659
|
+
console.log(chalk51.red(`\u274C Kh\xF4ng t\xECm th\u1EA5y skill "${name}" tr\xEAn server.`));
|
|
14096
14660
|
process.exit(1);
|
|
14097
14661
|
}
|
|
14098
14662
|
} else {
|
|
14099
14663
|
const projectRoot = process.cwd();
|
|
14100
14664
|
const skill = await skillsService.getSkillInfo(projectRoot, name);
|
|
14101
14665
|
if (!skill) {
|
|
14102
|
-
console.log(
|
|
14103
|
-
console.log(
|
|
14104
|
-
console.log(
|
|
14666
|
+
console.log(chalk51.red(`\u274C Skill "${name}" ch\u01B0a \u0111\u01B0\u1EE3c c\xE0i \u0111\u1EB7t.`));
|
|
14667
|
+
console.log(chalk51.dim('\u{1F4A1} D\xF9ng "j skills info ' + name + ' --server" \u0111\u1EC3 xem tr\xEAn server'));
|
|
14668
|
+
console.log(chalk51.dim(' ho\u1EB7c "j skills add ' + name + '" \u0111\u1EC3 c\xE0i \u0111\u1EB7t'));
|
|
14105
14669
|
process.exit(1);
|
|
14106
14670
|
}
|
|
14107
14671
|
console.log(`
|
|
14108
|
-
\u{1F6E0} ${
|
|
14672
|
+
\u{1F6E0} ${chalk51.bold(skill.name)}
|
|
14109
14673
|
`);
|
|
14110
14674
|
console.log(`Slug: ${skill.slug}`);
|
|
14111
|
-
console.log(`Description: ${skill.description ||
|
|
14675
|
+
console.log(`Description: ${skill.description || chalk51.dim("(none)")}`);
|
|
14112
14676
|
console.log(`Path: ${skill.path}`);
|
|
14113
14677
|
console.log(`Files: ${skill.fileCount}`);
|
|
14114
14678
|
console.log();
|
|
14115
|
-
console.log(
|
|
14679
|
+
console.log(chalk51.dim('\u{1F4A1} D\xF9ng "j skills sync" \u0111\u1EC3 \u0111\u1ED3ng b\u1ED9 sang IDE(s)'));
|
|
14116
14680
|
}
|
|
14117
14681
|
});
|
|
14118
14682
|
}
|
|
14119
14683
|
|
|
14120
14684
|
// src/commands/skills/sync.ts
|
|
14121
|
-
import { Command as
|
|
14122
|
-
import
|
|
14123
|
-
import { confirm as
|
|
14685
|
+
import { Command as Command87 } from "commander";
|
|
14686
|
+
import chalk52 from "chalk";
|
|
14687
|
+
import { confirm as confirm19, checkbox as checkbox8 } from "@inquirer/prompts";
|
|
14124
14688
|
function createSkillsSyncCommand() {
|
|
14125
|
-
return new
|
|
14689
|
+
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) => {
|
|
14126
14690
|
const skillsService = new SkillsService();
|
|
14127
14691
|
const projectRoot = process.cwd();
|
|
14128
14692
|
const headless = options.yes === true;
|
|
14129
|
-
console.log(
|
|
14693
|
+
console.log(chalk52.bold.cyan("\n\u{1F504} Sync skills sang IDE(s)\n"));
|
|
14130
14694
|
const localSkills = await skillsService.listLocal(projectRoot);
|
|
14131
14695
|
if (localSkills.length === 0) {
|
|
14132
|
-
console.log(
|
|
14133
|
-
console.log(
|
|
14696
|
+
console.log(chalk52.yellow("\u26A0\uFE0F Kh\xF4ng c\xF3 skills n\xE0o trong .jai1/skills/"));
|
|
14697
|
+
console.log(chalk52.dim('\u{1F4A1} Ch\u1EA1y "j skills add <t\xEAn>" \u0111\u1EC3 c\xE0i \u0111\u1EB7t skills tr\u01B0\u1EDBc'));
|
|
14134
14698
|
process.exit(1);
|
|
14135
14699
|
}
|
|
14136
14700
|
console.log(`\u{1F4C1} T\xECm th\u1EA5y ${localSkills.length} skill(s) trong .jai1/skills/`);
|
|
@@ -14162,7 +14726,7 @@ function createSkillsSyncCommand() {
|
|
|
14162
14726
|
theme: checkboxTheme
|
|
14163
14727
|
});
|
|
14164
14728
|
if (selectedIdes.length === 0) {
|
|
14165
|
-
console.log(
|
|
14729
|
+
console.log(chalk52.yellow("\n\u26A0\uFE0F Ch\u01B0a ch\u1ECDn IDE n\xE0o!"));
|
|
14166
14730
|
process.exit(0);
|
|
14167
14731
|
}
|
|
14168
14732
|
}
|
|
@@ -14177,34 +14741,34 @@ function createSkillsSyncCommand() {
|
|
|
14177
14741
|
console.log(` Total: ${totalFiles} skill folder(s) s\u1EBD \u0111\u01B0\u1EE3c sync
|
|
14178
14742
|
`);
|
|
14179
14743
|
if (options.dryRun) {
|
|
14180
|
-
console.log(
|
|
14744
|
+
console.log(chalk52.dim("\u{1F50D} DRY RUN - Kh\xF4ng c\xF3 file n\xE0o \u0111\u01B0\u1EE3c ghi\n"));
|
|
14181
14745
|
return;
|
|
14182
14746
|
}
|
|
14183
14747
|
if (!headless) {
|
|
14184
|
-
const confirmed = await
|
|
14748
|
+
const confirmed = await confirm19({
|
|
14185
14749
|
message: "Ti\u1EBFp t\u1EE5c sync?",
|
|
14186
14750
|
default: true
|
|
14187
14751
|
});
|
|
14188
14752
|
if (!confirmed) {
|
|
14189
|
-
console.log(
|
|
14753
|
+
console.log(chalk52.yellow("\n\u274C \u0110\xE3 h\u1EE7y sync.\n"));
|
|
14190
14754
|
process.exit(0);
|
|
14191
14755
|
}
|
|
14192
14756
|
}
|
|
14193
|
-
console.log(
|
|
14757
|
+
console.log(chalk52.cyan("\n\u{1F504} \u0110ang sync...\n"));
|
|
14194
14758
|
const result = await skillsService.syncToIdes(
|
|
14195
14759
|
projectRoot,
|
|
14196
14760
|
selectedIdes,
|
|
14197
14761
|
selectedSlugs,
|
|
14198
14762
|
(res) => {
|
|
14199
14763
|
const icon = res.status === "created" ? "\u2713" : res.status === "updated" ? "\u21BB" : "\u2717";
|
|
14200
|
-
const statusColor = res.status === "error" ?
|
|
14201
|
-
console.log(` ${statusColor(icon)} ${res.ide}: ${res.skill} \u2192 ${
|
|
14764
|
+
const statusColor = res.status === "error" ? chalk52.red : chalk52.green;
|
|
14765
|
+
console.log(` ${statusColor(icon)} ${res.ide}: ${res.skill} \u2192 ${chalk52.dim(res.path)}`);
|
|
14202
14766
|
if (res.status === "error" && res.error) {
|
|
14203
|
-
console.log(` ${
|
|
14767
|
+
console.log(` ${chalk52.red("Error:")} ${res.error}`);
|
|
14204
14768
|
}
|
|
14205
14769
|
}
|
|
14206
14770
|
);
|
|
14207
|
-
console.log(
|
|
14771
|
+
console.log(chalk52.green("\n\u2705 Sync ho\xE0n t\u1EA5t!\n"));
|
|
14208
14772
|
console.log(` Created: ${result.created}`);
|
|
14209
14773
|
console.log(` Updated: ${result.updated}`);
|
|
14210
14774
|
if (result.errors > 0) {
|
|
@@ -14217,27 +14781,27 @@ function createSkillsSyncCommand() {
|
|
|
14217
14781
|
// src/commands/skills/index.ts
|
|
14218
14782
|
function showSkillsHelp() {
|
|
14219
14783
|
const cli = getCliName();
|
|
14220
|
-
console.log(
|
|
14784
|
+
console.log(chalk53.bold.cyan("\u{1F6E0} " + cli + " skills") + chalk53.dim(" - Qu\u1EA3n l\xFD agent skills"));
|
|
14221
14785
|
console.log();
|
|
14222
|
-
console.log(
|
|
14223
|
-
console.log(` ${
|
|
14224
|
-
console.log(` ${
|
|
14225
|
-
console.log(` ${
|
|
14226
|
-
console.log(` ${
|
|
14227
|
-
console.log(` ${
|
|
14786
|
+
console.log(chalk53.bold("C\xE1c l\u1EC7nh:"));
|
|
14787
|
+
console.log(` ${chalk53.cyan("find")} T\xECm ki\u1EBFm skills tr\xEAn server ho\u1EB7c npm`);
|
|
14788
|
+
console.log(` ${chalk53.cyan("add")} C\xE0i \u0111\u1EB7t skill v\xE0o .jai1/skills/`);
|
|
14789
|
+
console.log(` ${chalk53.cyan("list")} Li\u1EC7t k\xEA skills \u0111\xE3 c\xE0i ho\u1EB7c c\xF3 s\u1EB5n`);
|
|
14790
|
+
console.log(` ${chalk53.cyan("info")} Xem chi ti\u1EBFt m\u1ED9t skill`);
|
|
14791
|
+
console.log(` ${chalk53.cyan("sync")} \u0110\u1ED3ng b\u1ED9 skills sang c\xE1c IDE`);
|
|
14228
14792
|
console.log();
|
|
14229
|
-
console.log(
|
|
14230
|
-
console.log(
|
|
14231
|
-
console.log(
|
|
14232
|
-
console.log(
|
|
14233
|
-
console.log(
|
|
14234
|
-
console.log(
|
|
14235
|
-
console.log(
|
|
14793
|
+
console.log(chalk53.bold("V\xED d\u1EE5:"));
|
|
14794
|
+
console.log(chalk53.dim(` $ ${cli} skills find audit`));
|
|
14795
|
+
console.log(chalk53.dim(` $ ${cli} skills find "react" --skillsh`));
|
|
14796
|
+
console.log(chalk53.dim(` $ ${cli} skills add brainstorming`));
|
|
14797
|
+
console.log(chalk53.dim(` $ ${cli} skills add vercel/next-skills --skillsh`));
|
|
14798
|
+
console.log(chalk53.dim(` $ ${cli} skills list`));
|
|
14799
|
+
console.log(chalk53.dim(` $ ${cli} skills sync --all -y`));
|
|
14236
14800
|
console.log();
|
|
14237
|
-
console.log(
|
|
14801
|
+
console.log(chalk53.dim(`Ch\u1EA1y "${cli} skills <l\u1EC7nh> --help" \u0111\u1EC3 xem chi ti\u1EBFt`));
|
|
14238
14802
|
}
|
|
14239
14803
|
function createSkillsCommand() {
|
|
14240
|
-
const cmd = new
|
|
14804
|
+
const cmd = new Command88("skills").alias("s").description("Manage agent skills (search, install, sync to IDEs)").action(() => {
|
|
14241
14805
|
showSkillsHelp();
|
|
14242
14806
|
});
|
|
14243
14807
|
cmd.addCommand(createSkillsFindCommand());
|
|
@@ -14249,8 +14813,8 @@ function createSkillsCommand() {
|
|
|
14249
14813
|
}
|
|
14250
14814
|
|
|
14251
14815
|
// src/commands/upgrade.ts
|
|
14252
|
-
import { Command as
|
|
14253
|
-
import { confirm as
|
|
14816
|
+
import { Command as Command89 } from "commander";
|
|
14817
|
+
import { confirm as confirm20 } from "@inquirer/prompts";
|
|
14254
14818
|
import { execSync as execSync5 } from "child_process";
|
|
14255
14819
|
var colors2 = {
|
|
14256
14820
|
yellow: "\x1B[33m",
|
|
@@ -14261,7 +14825,7 @@ var colors2 = {
|
|
|
14261
14825
|
bold: "\x1B[1m"
|
|
14262
14826
|
};
|
|
14263
14827
|
function createUpgradeCommand() {
|
|
14264
|
-
return new
|
|
14828
|
+
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) => {
|
|
14265
14829
|
await handleUpgrade(options);
|
|
14266
14830
|
});
|
|
14267
14831
|
}
|
|
@@ -14310,7 +14874,7 @@ ${colors2.bold}Current version:${colors2.reset} ${currentVersion}`);
|
|
|
14310
14874
|
return;
|
|
14311
14875
|
}
|
|
14312
14876
|
if (!options.force) {
|
|
14313
|
-
const shouldUpdate = await
|
|
14877
|
+
const shouldUpdate = await confirm20({
|
|
14314
14878
|
message: "Update to the latest version now?",
|
|
14315
14879
|
default: true
|
|
14316
14880
|
});
|
|
@@ -14414,11 +14978,11 @@ function getInstallCommand(packageManager2) {
|
|
|
14414
14978
|
}
|
|
14415
14979
|
|
|
14416
14980
|
// src/commands/clean.ts
|
|
14417
|
-
import { Command as
|
|
14418
|
-
import { confirm as
|
|
14981
|
+
import { Command as Command90 } from "commander";
|
|
14982
|
+
import { confirm as confirm21, select as select6 } from "@inquirer/prompts";
|
|
14419
14983
|
import { join as join21 } from "path";
|
|
14420
14984
|
function createCleanCommand() {
|
|
14421
|
-
return new
|
|
14985
|
+
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) => {
|
|
14422
14986
|
await handleClean(options);
|
|
14423
14987
|
});
|
|
14424
14988
|
}
|
|
@@ -14514,7 +15078,7 @@ async function cleanTarget(target, skipConfirm) {
|
|
|
14514
15078
|
}
|
|
14515
15079
|
const countStr = info.count ? ` (${info.count} items)` : "";
|
|
14516
15080
|
if (!skipConfirm) {
|
|
14517
|
-
const confirmed = await
|
|
15081
|
+
const confirmed = await confirm21({
|
|
14518
15082
|
message: `Delete ${target.name}${countStr}?`,
|
|
14519
15083
|
default: false
|
|
14520
15084
|
});
|
|
@@ -14532,7 +15096,7 @@ async function cleanTarget(target, skipConfirm) {
|
|
|
14532
15096
|
}
|
|
14533
15097
|
|
|
14534
15098
|
// src/commands/redmine/check.ts
|
|
14535
|
-
import { Command as
|
|
15099
|
+
import { Command as Command91 } from "commander";
|
|
14536
15100
|
|
|
14537
15101
|
// src/services/redmine-config.service.ts
|
|
14538
15102
|
import { readFile as readFile7 } from "fs/promises";
|
|
@@ -14839,7 +15403,7 @@ async function checkConnectivity(config) {
|
|
|
14839
15403
|
|
|
14840
15404
|
// src/commands/redmine/check.ts
|
|
14841
15405
|
function createRedmineCheckCommand() {
|
|
14842
|
-
const cmd = new
|
|
15406
|
+
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) => {
|
|
14843
15407
|
await handleRedmineCheck(options);
|
|
14844
15408
|
});
|
|
14845
15409
|
return cmd;
|
|
@@ -14867,7 +15431,7 @@ async function handleRedmineCheck(options) {
|
|
|
14867
15431
|
}
|
|
14868
15432
|
|
|
14869
15433
|
// src/commands/redmine/sync-issue.ts
|
|
14870
|
-
import { Command as
|
|
15434
|
+
import { Command as Command92 } from "commander";
|
|
14871
15435
|
|
|
14872
15436
|
// src/sync-issue.ts
|
|
14873
15437
|
import { resolve as resolve3, relative } from "path";
|
|
@@ -15251,7 +15815,7 @@ function extractIssueIdFromUrl(url) {
|
|
|
15251
15815
|
|
|
15252
15816
|
// src/commands/redmine/sync-issue.ts
|
|
15253
15817
|
function createSyncIssueCommand() {
|
|
15254
|
-
const cmd = new
|
|
15818
|
+
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) => {
|
|
15255
15819
|
await handleSyncIssue(options);
|
|
15256
15820
|
});
|
|
15257
15821
|
return cmd;
|
|
@@ -15295,7 +15859,7 @@ async function handleSyncIssue(options) {
|
|
|
15295
15859
|
}
|
|
15296
15860
|
|
|
15297
15861
|
// src/commands/redmine/sync-project.ts
|
|
15298
|
-
import { Command as
|
|
15862
|
+
import { Command as Command93 } from "commander";
|
|
15299
15863
|
|
|
15300
15864
|
// src/sync-project.ts
|
|
15301
15865
|
async function syncProject(config, options = {}) {
|
|
@@ -15365,7 +15929,7 @@ async function syncProject(config, options = {}) {
|
|
|
15365
15929
|
|
|
15366
15930
|
// src/commands/redmine/sync-project.ts
|
|
15367
15931
|
function createSyncProjectCommand() {
|
|
15368
|
-
const cmd = new
|
|
15932
|
+
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) => {
|
|
15369
15933
|
await handleSyncProject(options);
|
|
15370
15934
|
});
|
|
15371
15935
|
return cmd;
|
|
@@ -15420,12 +15984,12 @@ async function handleSyncProject(options) {
|
|
|
15420
15984
|
}
|
|
15421
15985
|
|
|
15422
15986
|
// src/commands/framework/info.ts
|
|
15423
|
-
import { Command as
|
|
15987
|
+
import { Command as Command94 } from "commander";
|
|
15424
15988
|
import { promises as fs28 } from "fs";
|
|
15425
15989
|
import { join as join22 } from "path";
|
|
15426
15990
|
import { homedir as homedir5 } from "os";
|
|
15427
15991
|
function createInfoCommand() {
|
|
15428
|
-
const cmd = new
|
|
15992
|
+
const cmd = new Command94("info").description("Show client configuration and status").option("--json", "Output as JSON").option("--verbose", "Show detailed information").action(async (options) => {
|
|
15429
15993
|
await handleInfo(options);
|
|
15430
15994
|
});
|
|
15431
15995
|
return cmd;
|
|
@@ -15481,8 +16045,8 @@ async function getProjectStatus2() {
|
|
|
15481
16045
|
}
|
|
15482
16046
|
|
|
15483
16047
|
// src/commands/self-update.ts
|
|
15484
|
-
import { Command as
|
|
15485
|
-
import { confirm as
|
|
16048
|
+
import { Command as Command95 } from "commander";
|
|
16049
|
+
import { confirm as confirm22 } from "@inquirer/prompts";
|
|
15486
16050
|
import { execSync as execSync6 } from "child_process";
|
|
15487
16051
|
var colors3 = {
|
|
15488
16052
|
yellow: "\x1B[33m",
|
|
@@ -15493,7 +16057,7 @@ var colors3 = {
|
|
|
15493
16057
|
bold: "\x1B[1m"
|
|
15494
16058
|
};
|
|
15495
16059
|
function createSelfUpdateCommand() {
|
|
15496
|
-
return new
|
|
16060
|
+
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) => {
|
|
15497
16061
|
await handleSelfUpdate(options);
|
|
15498
16062
|
});
|
|
15499
16063
|
}
|
|
@@ -15537,7 +16101,7 @@ ${colors3.bold}Current version:${colors3.reset} ${currentVersion}`);
|
|
|
15537
16101
|
return;
|
|
15538
16102
|
}
|
|
15539
16103
|
if (!options.force) {
|
|
15540
|
-
const shouldUpdate = await
|
|
16104
|
+
const shouldUpdate = await confirm22({
|
|
15541
16105
|
message: "Update to the latest version now?",
|
|
15542
16106
|
default: true
|
|
15543
16107
|
});
|
|
@@ -15633,10 +16197,10 @@ function getInstallCommand2(packageManager2) {
|
|
|
15633
16197
|
}
|
|
15634
16198
|
|
|
15635
16199
|
// src/commands/clear-backups.ts
|
|
15636
|
-
import { Command as
|
|
15637
|
-
import { confirm as
|
|
16200
|
+
import { Command as Command96 } from "commander";
|
|
16201
|
+
import { confirm as confirm23 } from "@inquirer/prompts";
|
|
15638
16202
|
function createClearBackupsCommand() {
|
|
15639
|
-
return new
|
|
16203
|
+
return new Command96("clear-backups").description("Clear backup files").option("-y, --yes", "Skip confirmation").action(async (options) => {
|
|
15640
16204
|
const service = new ComponentsService();
|
|
15641
16205
|
const backups = await service.listBackups(process.cwd());
|
|
15642
16206
|
if (backups.length === 0) {
|
|
@@ -15652,7 +16216,7 @@ function createClearBackupsCommand() {
|
|
|
15652
16216
|
}
|
|
15653
16217
|
console.log();
|
|
15654
16218
|
if (!options.yes) {
|
|
15655
|
-
const ok = await
|
|
16219
|
+
const ok = await confirm23({ message: "Delete all backups?", default: false });
|
|
15656
16220
|
if (!ok) return;
|
|
15657
16221
|
}
|
|
15658
16222
|
await service.clearBackups(process.cwd());
|
|
@@ -15661,8 +16225,8 @@ function createClearBackupsCommand() {
|
|
|
15661
16225
|
}
|
|
15662
16226
|
|
|
15663
16227
|
// src/commands/vscode/index.ts
|
|
15664
|
-
import { Command as
|
|
15665
|
-
import { checkbox as checkbox9, confirm as
|
|
16228
|
+
import { Command as Command97 } from "commander";
|
|
16229
|
+
import { checkbox as checkbox9, confirm as confirm24, select as select7 } from "@inquirer/prompts";
|
|
15666
16230
|
import fs29 from "fs/promises";
|
|
15667
16231
|
import path12 from "path";
|
|
15668
16232
|
import { existsSync as existsSync4 } from "fs";
|
|
@@ -15801,7 +16365,7 @@ var PERFORMANCE_GROUPS2 = {
|
|
|
15801
16365
|
}
|
|
15802
16366
|
};
|
|
15803
16367
|
function createVSCodeCommand() {
|
|
15804
|
-
const vscodeCommand = new
|
|
16368
|
+
const vscodeCommand = new Command97("vscode").description("Qu\u1EA3n l\xFD c\xE0i \u0111\u1EB7t VSCode cho d\u1EF1 \xE1n hi\u1EC7n t\u1EA1i");
|
|
15805
16369
|
vscodeCommand.action(async () => {
|
|
15806
16370
|
await interactiveMode2();
|
|
15807
16371
|
});
|
|
@@ -15907,7 +16471,7 @@ async function applyGroups2(groupKeys, action) {
|
|
|
15907
16471
|
console.log("\u{1F4C4} \u0110\xE3 \u0111\u1ECDc c\xE0i \u0111\u1EB7t hi\u1EC7n t\u1EA1i t\u1EEB settings.json");
|
|
15908
16472
|
} catch {
|
|
15909
16473
|
console.warn("\u26A0\uFE0F Kh\xF4ng th\u1EC3 \u0111\u1ECDc settings.json (c\xF3 th\u1EC3 ch\u1EE9a comments).");
|
|
15910
|
-
const confirmOverwrite = await
|
|
16474
|
+
const confirmOverwrite = await confirm24({
|
|
15911
16475
|
message: "Ghi \u0111\xE8 file settings.json hi\u1EC7n t\u1EA1i?",
|
|
15912
16476
|
default: false
|
|
15913
16477
|
});
|
|
@@ -15954,7 +16518,7 @@ async function resetSettings2(groupKeys) {
|
|
|
15954
16518
|
console.log("\n\u26A0\uFE0F Kh\xF4ng t\xECm th\u1EA5y file settings.json");
|
|
15955
16519
|
return;
|
|
15956
16520
|
}
|
|
15957
|
-
const confirmReset = await
|
|
16521
|
+
const confirmReset = await confirm24({
|
|
15958
16522
|
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(", ")}?`,
|
|
15959
16523
|
default: false
|
|
15960
16524
|
});
|
|
@@ -15972,10 +16536,10 @@ async function resetSettings2(groupKeys) {
|
|
|
15972
16536
|
}
|
|
15973
16537
|
|
|
15974
16538
|
// src/commands/migrate-ide.ts
|
|
15975
|
-
import { Command as
|
|
15976
|
-
import { checkbox as checkbox10, confirm as
|
|
16539
|
+
import { Command as Command98 } from "commander";
|
|
16540
|
+
import { checkbox as checkbox10, confirm as confirm25 } from "@inquirer/prompts";
|
|
15977
16541
|
function createMigrateIdeCommand() {
|
|
15978
|
-
const cmd = new
|
|
16542
|
+
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) => {
|
|
15979
16543
|
await runMigrateIde(options);
|
|
15980
16544
|
});
|
|
15981
16545
|
return cmd;
|
|
@@ -16046,7 +16610,7 @@ async function runMigrateIde(options) {
|
|
|
16046
16610
|
if (options.dryRun) {
|
|
16047
16611
|
console.log("\u{1F50D} DRY RUN - No files will be written\n");
|
|
16048
16612
|
}
|
|
16049
|
-
const confirmed = await
|
|
16613
|
+
const confirmed = await confirm25({
|
|
16050
16614
|
message: "Proceed with migration?",
|
|
16051
16615
|
default: true
|
|
16052
16616
|
});
|
|
@@ -16084,20 +16648,20 @@ async function runMigrateIde(options) {
|
|
|
16084
16648
|
|
|
16085
16649
|
// src/utils/help-formatter.ts
|
|
16086
16650
|
import boxen4 from "boxen";
|
|
16087
|
-
import
|
|
16651
|
+
import chalk54 from "chalk";
|
|
16088
16652
|
import gradient from "gradient-string";
|
|
16089
16653
|
import figlet from "figlet";
|
|
16090
16654
|
function showCustomHelp(version) {
|
|
16091
16655
|
const title = figlet.textSync("JAI1", { font: "Small" });
|
|
16092
16656
|
console.log(gradient.pastel(title));
|
|
16093
16657
|
console.log(
|
|
16094
|
-
boxen4(
|
|
16658
|
+
boxen4(chalk54.cyan(`Agentic Coding CLI v${version}`), {
|
|
16095
16659
|
padding: { left: 1, right: 1, top: 0, bottom: 0 },
|
|
16096
16660
|
borderStyle: "round",
|
|
16097
16661
|
borderColor: "cyan"
|
|
16098
16662
|
})
|
|
16099
16663
|
);
|
|
16100
|
-
console.log(
|
|
16664
|
+
console.log(chalk54.bold("\n\u{1F527} Thi\u1EBFt l\u1EADp & Th\xF4ng tin"));
|
|
16101
16665
|
console.log(" auth X\xE1c th\u1EF1c v\xE0 c\u1EA5u h\xECnh client");
|
|
16102
16666
|
console.log(" status Hi\u1EC3n th\u1ECB tr\u1EA1ng th\xE1i c\u1EA5u h\xECnh");
|
|
16103
16667
|
console.log(" client-info T\u1EA1o th\xF4ng tin client \u0111\u1EC3 g\u1EEDi \u0111\u1ED9i ph\xE1t tri\u1EC3n");
|
|
@@ -16105,43 +16669,43 @@ function showCustomHelp(version) {
|
|
|
16105
16669
|
console.log(" guide H\u01B0\u1EDBng d\u1EABn s\u1EED d\u1EE5ng nhanh");
|
|
16106
16670
|
console.log(" quickstart B\u1EAFt \u0111\u1EA7u t\u1EEB \u0111\xE2u? (theo t\xECnh hu\u1ED1ng)");
|
|
16107
16671
|
console.log(" doctor Chu\u1EA9n \u0111o\xE1n project hi\u1EC7n t\u1EA1i");
|
|
16108
|
-
console.log(
|
|
16672
|
+
console.log(chalk54.bold("\n\u{1F4E6} Qu\u1EA3n l\xFD Components"));
|
|
16109
16673
|
console.log(" apply C\xE0i \u0111\u1EB7t components (interactive)");
|
|
16110
16674
|
console.log(" update C\u1EADp nh\u1EADt components \u0111\xE3 c\xE0i");
|
|
16111
16675
|
console.log(" check Ki\u1EC3m tra c\u1EADp nh\u1EADt t\u1EEB server");
|
|
16112
|
-
console.log(
|
|
16676
|
+
console.log(chalk54.bold("\n\u{1F5A5}\uFE0F IDE & T\xEDch h\u1EE3p"));
|
|
16113
16677
|
console.log(" ide L\u1EC7nh c\u1EA5u h\xECnh IDE");
|
|
16114
16678
|
console.log(" chat Chat AI v\u1EDBi Jai1 LLM Proxy");
|
|
16115
16679
|
console.log(" openai-keys Th\xF4ng tin API credentials");
|
|
16116
|
-
console.log(
|
|
16680
|
+
console.log(chalk54.bold("\n\u{1F916} AI Tools"));
|
|
16117
16681
|
console.log(" translate D\u1ECBch v\u0103n b\u1EA3n/file b\u1EB1ng AI");
|
|
16118
16682
|
console.log(" image T\u1EA1o \u1EA3nh (Coming Soon)");
|
|
16119
16683
|
console.log(" stats Th\u1ED1ng k\xEA s\u1EED d\u1EE5ng LLM");
|
|
16120
16684
|
console.log(" feedback G\u1EEDi b\xE1o c\xE1o/\u0111\u1EC1 xu\u1EA5t");
|
|
16121
|
-
console.log(
|
|
16685
|
+
console.log(chalk54.bold("\n\u{1F4C1} Project"));
|
|
16122
16686
|
console.log(" kit Qu\u1EA3n l\xFD starter kits");
|
|
16123
16687
|
console.log(" tasks (t) Qu\u1EA3n l\xFD tasks ph\xE1t tri\u1EC3n");
|
|
16124
16688
|
console.log(" rules Qu\u1EA3n l\xFD rule presets");
|
|
16125
16689
|
console.log(" deps Qu\u1EA3n l\xFD dependencies");
|
|
16126
16690
|
console.log(" redmine Redmine context sync");
|
|
16127
|
-
console.log(
|
|
16691
|
+
console.log(chalk54.bold("\n\u2699\uFE0F B\u1EA3o tr\xEC"));
|
|
16128
16692
|
console.log(" upgrade C\u1EADp nh\u1EADt CLI client");
|
|
16129
16693
|
console.log(" clean D\u1ECDn d\u1EB9p cache/backup");
|
|
16130
16694
|
console.log(" utils Developer utilities");
|
|
16131
16695
|
const name = getCliName();
|
|
16132
|
-
console.log(
|
|
16696
|
+
console.log(chalk54.dim(`
|
|
16133
16697
|
S\u1EED d\u1EE5ng: ${name} [l\u1EC7nh] --help \u0111\u1EC3 xem chi ti\u1EBFt`));
|
|
16134
16698
|
}
|
|
16135
16699
|
function showUnknownCommand(commandName) {
|
|
16136
|
-
console.error(
|
|
16700
|
+
console.error(chalk54.red(`\u274C L\u1EC7nh kh\xF4ng t\u1ED3n t\u1EA1i: ${commandName}`));
|
|
16137
16701
|
const name = getCliName();
|
|
16138
|
-
console.error(
|
|
16702
|
+
console.error(chalk54.dim(`
|
|
16139
16703
|
G\u1EE3i \xFD: Ch\u1EA1y ${name} --help \u0111\u1EC3 xem danh s\xE1ch l\u1EC7nh`));
|
|
16140
16704
|
}
|
|
16141
16705
|
|
|
16142
16706
|
// src/cli.ts
|
|
16143
16707
|
checkNodeVersion();
|
|
16144
|
-
var program = new
|
|
16708
|
+
var program = new Command99();
|
|
16145
16709
|
if (process.argv.includes("-v") || process.argv.includes("--version")) {
|
|
16146
16710
|
console.log(package_default.version);
|
|
16147
16711
|
if (!process.argv.includes("--skip-update-check")) {
|
|
@@ -16176,6 +16740,7 @@ program.addCommand(createClientInfoCommand());
|
|
|
16176
16740
|
program.addCommand(createErrorsCommand());
|
|
16177
16741
|
program.addCommand(createUtilsCommand());
|
|
16178
16742
|
program.addCommand(createDepsCommand());
|
|
16743
|
+
program.addCommand(createDevCommand());
|
|
16179
16744
|
program.addCommand(createTasksCommand());
|
|
16180
16745
|
program.addCommand(createKitCommand());
|
|
16181
16746
|
program.addCommand(createRulesCommand());
|
|
@@ -16183,9 +16748,9 @@ program.addCommand(createSkillsCommand());
|
|
|
16183
16748
|
program.addCommand(createHooksCommand());
|
|
16184
16749
|
program.addCommand(createUpgradeCommand());
|
|
16185
16750
|
program.addCommand(createCleanCommand());
|
|
16186
|
-
var redmineCommand = new
|
|
16751
|
+
var redmineCommand = new Command99("redmine").description("Redmine context sync commands");
|
|
16187
16752
|
redmineCommand.addCommand(createRedmineCheckCommand());
|
|
16188
|
-
var syncCommand = new
|
|
16753
|
+
var syncCommand = new Command99("sync").description("Sync Redmine issues to markdown files");
|
|
16189
16754
|
syncCommand.addCommand(createSyncIssueCommand());
|
|
16190
16755
|
syncCommand.addCommand(createSyncProjectCommand());
|
|
16191
16756
|
redmineCommand.addCommand(syncCommand);
|